// file Ttst.java /* Author: Peter Loeb Date: July, 1999 This file contains the GUI for a Tic Tac Toe game. The "application" code resides in Tutil.java. The game works as either an application or an applet. It is based on a PERL/CGI script I wrote a few years ago. I have also "borrowed" a bit of code from Bruce Eckel, from his book "Thinking in Java", in particular, ToeTestNew.java */ package com.palserv.ttt; import java.applet.*; import java.awt.*; import java.awt.event.*; /** This is the applet which implements the game. @author Peter Loeb, July 1999 */ public class Ttst extends Applet { int tsiz = 100; String bord = "BBBBBBBBB"; String oppcol; String xo; Tutil tut = new Tutil(); Tbut tb[] = new Tbut[9]; Label ln = new Label(" "); Label ls = new Label(" "); Label l1 = new Label("New Game: do you want to "); Label l2 = new Label(" or "); Button bx = new Button("play X"); Button bo = new Button("play O"); Panel pp = new Panel(); Panel p0 = new Panel(); Panel p1 = new Panel(); Panel p2 = new Panel(); String s0 = new String("Panel0"); String s1 = new String("Panel1"); ML nML = new ML(); CardLayout CL = new CardLayout(); /** This method allows the applet to be used as an application. */ public static void main(String args[]) { Ttst applet = new Ttst(); Frame aFrame = new Frame("Ttst"); aFrame.addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } } ); aFrame.add(applet, BorderLayout.CENTER); aFrame.setSize(400,400); applet.init(); aFrame.setVisible(true); } public void init() { /* The primary layout is a card layout. there are two cards, p0 and p1. the "background" on which they occur is the pp panel. */ add(pp); pp.setLayout(CL); /* Here we do the "p0" panel This is the panel which comes up first. It asks whether the opponent (user) wants to play "X" or "O". */ pp.add(s0,p0); p0.setLayout(new FlowLayout()); p0.add(l1); bx.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent ae) { newGame("X"); } } ); bo.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent ae) { newGame("O"); } } ); p0.add(bx); p0.add(l2); p0.add(bo); /* now we do the "p1" panel This is the game "proper". It uses a GridLayout within a BorderLayout. The "North" (ln) and "South" (ls) portions of the BorderLayout are used for messages to the user. The "Center" (p2) portion of the BorderLayout contains the panel p2 which uses a GridLayout which is filled with 9 instances of the Tbut class. */ pp.add(s1,p1); p1.setLayout(new BorderLayout()); p1.add("North", ln); p1.add("Center", p2); p1.add("South", ls); p2.setLayout(new GridLayout(3,3)); for (int i = 0; i < 9; i ++) { tb[i] = new Tbut(i + 1, this); tb[i].setSize(tsiz, tsiz); p2.add(tb[i]); } setUp(); } void setUp() { // select the p0 panel ((CardLayout) CL).show(pp,s0); } void newGame(String oc) { oppcol = oc; xo = tut.getDiffCol(oc); // select the p1 panel ((CardLayout) CL).show(pp,s1); // initialize the game ln.setText("Your move; you are playing " + oppcol); for (int i = 0; i < 9; i ++) { tb[i].setSt(0); // set all squares to blank } ls.setText(" "); // set "South" text to blank setBord("BBBBBBBBB"); // new board (all blank) if (xo == "X") { // I move first int mov = tut.figMov(bord,xo); // get a move bord = tut.aplyMov(bord,mov,xo); // apply it to the board tb[mov - 1].setSt(1); // set the state of the Tbut tb[mov - 1].repaint(); // repaint the Tbut } } String getBord() { return bord; } void setBord(String b) { bord = b; } void setNorth(String s) { ln.setText(s); } void setSouth(String s) { ls.setText(s); } void setSouthOn() { ls.addMouseListener(nML); } String getOppCol() { return oppcol; } Tbut getTbut(int i) { return tb[i]; } class ML extends MouseAdapter { public void mousePressed(MouseEvent e) { ls.removeMouseListener(nML); setUp(); } } Tutil getTut() { return tut; } } class Tbut extends Canvas { int square; // which square does this Tbut occupy (1-9) int state = 0; // state: 0-blank, 1-"X", 2-"O" Ttst tt; // handle to the applet so we can send it messages /* The only constructor. As the Tbut's are created in init(), they are each passed both the square number that they will occupy and a handle to the applet. */ public Tbut(int t, Ttst tst) { square = t; // set square so we can know which square tt = tst; // set the handle /* add a listener so we can pick up when the user clicks on a Tbut */ addMouseListener(new Mousey()); } public int getSq() { return square; } public void setSt(int s) { state = s; } public int getSt() { return state; } public void paint (Graphics g) { int x1 = 0; int y1 = 0; int x2 = getSize().width-1; int y2 = getSize().height-1; g.drawRect(x1,y1,x2,y2); // make both border and board x1 = x2 / 4; y1 = y2 / 4; int wide = x2 / 2; int high = y2 / 2; if (state == 1) { // make an "X" g.drawLine(x1, y1, x1 + wide, y1 + high); g.drawLine(x1, y1 + high, x1 + wide, y1); } if (state == 2) { // make an "O" g.drawOval(x1, y1, x1 + wide/2, y1 + high/2); } } /* This is the class which "listens" for events in Tbut. if a user clicks on a Tbut (a square on the board) then this will pick it up */ class Mousey extends MouseAdapter { public void mousePressed(MouseEvent e) { /* there is nothing to stop the user from clicking on a Tbut which is not blank. However, we want to ignore this action, so we check to make sure that the state is 0. */ if (state == 0) { String brd = tt.getBord(); // brd is the board // ocol is opponent's color ("X" or "O") String ocol = tt.getOppCol(); // mycol is program's color ("X" or "O") String mycol = tt.getTut().getDiffCol(ocol); /* since the opponent has just moved to this square, we want to set the state to indicate this. */ state = "XO".indexOf(ocol) + 1; // apply opponent's move to the board brd = tt.getTut().aplyMov(brd, square, ocol); tt.setBord(brd); // update the board // check to see if the game is over int go = tt.getTut().gamOvr(brd,ocol); if (go == 1) { tt.setNorth("You win!"); tt.setSouth("Click here for new game"); tt.setSouthOn(); repaint(); return; } if (go == 2) { tt.setNorth("You lose!"); tt.setSouth("Click here for new game"); tt.setSouthOn(); repaint(); return; } if (go == 3) { tt.setNorth("It's a tie!"); tt.setSouth("Click here for new game"); tt.setSouthOn(); repaint(); return; } /* game is not yet over; program must make a move. First, we call figMov to figure out a move. */ int mov = tt.getTut().figMov(brd, ocol); // apply the move to the board brd = tt.getTut().aplyMov(brd, mov, mycol); // get a handle for the Tbut at that square Tbut sqr = tt.getTbut(mov - 1); // update the state to indicate the move sqr.state = "XO".indexOf(mycol) + 1; // get the graphics context for that Tbut Graphics gs = sqr.getGraphics(); // paint the square sqr.paint(gs); // update the board tt.setBord(brd); // check to see if the game is over int ngo = tt.getTut().gamOvr(brd,ocol); if (ngo == 1) { tt.setNorth("You win!"); tt.setSouth("Click here for new game"); tt.setSouthOn(); repaint(); return; } if (ngo == 2) { tt.setNorth("You lose!"); tt.setSouth("Click here for new game"); tt.setSouthOn(); repaint(); return; } if (ngo == 3) { tt.setNorth("It's a tie!"); tt.setSouth("Click here for new game"); tt.setSouthOn(); repaint(); return; } repaint(); } } } }