Download this source file

 
/* TTT.java - A Tic-tac-toe Applet.
 *
 * <APPLET CODE="TTT" WIDTH="325" HEIGHT="385"> </APPLET>
 *
 * (C) 1999 by Wayne Pollock, Tampa Florida USA.  All Rights Reserved.
 */

import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class TTT extends Applet implements ActionListener {

   private Panel controls       = new Panel();
   private Panel scores         = new Panel();
   private Panel board          = new Panel();
   private Button[] squares     = new Button[9+1];
   private Button newGame       = new Button( "New Game" );
   private Button clearScores   = new Button( "Clear Scores" );
   private TextField myScore    = new TextField( "0" );
   private TextField yourScore  = new TextField( "0" );
   private Label title          = new Label( "  Tic-Tac-Toe" );
   private Label caption        = new Label();
   private Label IWon           = new Label( "   I Won" );
   private Label youWon         = new Label( "You Won" );
   private boolean IAmX         = false;  // X goes first!
   private Font XOFont          = new Font("SansSerif", Font.BOLD, 64);
   private int[][] possibleWins = {
      { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 },  // rows 1, 2, and 3
      { 1, 4, 7 }, { 2, 5, 8 }, { 3, 6, 9 },  // cols 1, 2, and 3
      { 1, 5, 9 }, { 3, 5, 7 }                // diagonals 1 and 2
   };

public void init () {
   setBackground( Color.gray );
   setLayout( new BorderLayout(5, 5) );

   board.setLayout( new GridLayout(3, 3, 4, 4) );
   board.setBackground( Color.black );
   for (int i=1; i<=9; ++i) {
      squares[i] = new Button( " " );
      squares[i].setBackground( Color.white );
      squares[i].setFont( XOFont );
      squares[i].setActionCommand( "" + i );
      squares[i].addActionListener(this);
      board.add( squares[i] );
   }
   add( board, "Center" );

   yourScore.setBackground ( Color.white );
   yourScore.setEnabled( false );
   myScore.setBackground ( Color.white );
   myScore.setEnabled( false );
   title.setFont( new Font("Serif", Font.BOLD, 18) );
   title.setForeground( Color.yellow );
   IWon.setForeground( Color.black );
   youWon.setForeground( Color.black );
   scores.setLayout( new FlowLayout() );
   scores.add( youWon );
   scores.add( yourScore );
   scores.add( title );
   scores.add( myScore );
   scores.add( IWon );
   add( scores, "North" );

   Panel p = new Panel();
     p.setLayout( new BorderLayout(1,1) );

     newGame.addActionListener(this);
     newGame.setForeground( Color.green.darker().darker() );
     clearScores.addActionListener(this);
     clearScores.setForeground( Color.red.darker() );
     controls.setLayout( new FlowLayout() );
     controls.add( newGame );
     controls.add( new Label("   ") );  // Just a spacer
     controls.add( clearScores );
     p.add( controls, "North" );

     caption.setForeground( Color.yellow );
     caption.setText( "\u00A9 1999 by Wayne Pollock, " +
                      "Tampa Florida USA." );
     Panel capP = new Panel();
     capP.setLayout( new FlowLayout() );
     capP.add( caption );
     p.add( capP, "South" );
     add( p, "South" );
}

public void actionPerformed( ActionEvent e ) {
   String whichButton = e.getActionCommand();
   if ( whichButton.equals( "Clear Scores" ) )
      clearScores();
   else if ( whichButton.equals( "New Game" ) )
      newGame();
   else    // User clicked on the board
      makeYourMove( Integer.parseInt(whichButton) );
   caption.requestFocus();  // Hides the ugly focus indicator
}

private void clearScores () {
   myScore.setText( "0" );
   yourScore.setText( "0" );
   newGame();
}

private void newGame () {
   IWon.setForeground( Color.black );
   youWon.setForeground( Color.black );

   for (int i=1; i<=9; ++i) {
      squares[i].setLabel( " " );
      squares[i].setEnabled( true );
   }
   IAmX = ! IAmX;   // Take turns going first.

   if ( IAmX )
      makeMyMove();
}

private void makeYourMove ( int where ) {
   if ( IAmX )
      squares[where].setLabel( "O" );
   else
      squares[where].setLabel( "X" );

   squares[where].setEnabled( false );
   makeMyMove();
}

private void makeMyMove () {
   int move = -1, possibilities[] = new int[8];
   update( possibilities );

   // First check for win (3 in a row):

   for (int i=0; i<possibilities.length; ++i) {
      if ( possibilities[i] == 3 || possibilities[i] == -3) {
         gameOver( "Player" );  // A win for the player
         return;
      }
   }

   // With a total of 2 or -2, either the computer can win
   // on this move, or else the computer must block the Player.

   for (int i=0; i<possibilities.length; ++i) {
      switch ( possibilities[i] ) {
      case  2:
         move = findEmpty( possibleWins[i] );
         if ( IAmX ) {
            squares[move].setLabel( "X" );
            gameOver( "Computer" );
            return;
         }
         break;

      case -2:
         move = findEmpty( possibleWins[i] );
         if ( ! IAmX ) {
            squares[move].setLabel( "O" );
            gameOver( "Computer" );
            return;
         }
         break;
      }
   }

   // If we can't win, check if a blocking move was found:
   if ( move >= 0 ) {
      if ( IAmX )
         squares[move].setLabel( "X" );
      else
         squares[move].setLabel( "O" );
      squares[move].setEnabled( false );
      return;
   }

   // If we get to here, no wins or blocks are possible
   // so make the best available move:

   int bestMoves[] =
      { 5, 1, 3, 7, 9, 2, 8, 4, 6 };  // In order from best to worst

   for (int i=0; i<bestMoves.length; ++i) {
      String status = squares[ bestMoves[i] ].getLabel();
      if ( status.equals( " " ) ) {    // Found an empty square!
         if ( IAmX )
            squares[ bestMoves[i] ].setLabel( "X" );
         else
            squares[ bestMoves[i] ].setLabel( "O" );
         squares[ bestMoves[i] ].setEnabled( false );
         return;
      }
   }

   // If we get to here, the game is a draw!
   IWon.setForeground( Color.black );
   youWon.setForeground( Color.black );
}


// When game ends, update winner's score and disable the game grid:

private void gameOver ( String winner ) {
   TextField scoreToUpdate;
   if ( winner.equals( "Player" ) ) {
      scoreToUpdate = yourScore;
      youWon.setForeground( Color.cyan );
      IWon.setForeground( Color.black );
   } else {
      scoreToUpdate = myScore;
      youWon.setForeground( Color.black );
      IWon.setForeground( Color.cyan );
   }
   int score = Integer.parseInt( scoreToUpdate.getText() ) + 1;
   scoreToUpdate.setText( "" + score );

   for (int i=1; i<=9; ++i)
      squares[i].setEnabled( false );
}


// Locate the empty square in a row (or column or diagonal).
// (This method is never called unless exactly one square is empty.)

private int findEmpty ( int[] aRow ) {
   int i;
   for (i=0; i<aRow.length; ++i)
      if ( squares[ aRow[i] ].getLabel().equals( " " ) )
         break;
   return aRow[i];
}


// There are eight possible ways to score three in a row.  This
// method calculates a score for each possibility by adding one
// for each X and adding -1 for each O.  So a score of 3 (or -3)
// would show X (or O) has three in a row.  A score of 2 (or -2)
// would show two in a row (and one empty square) for X (or O).
// Scores of zero or 1 (or -1) have different possibile meanings,
// but this program only needs to check for a 3 (-3) or a 2 (-2).

private void update ( int[] possibilities ) {
   int val=0;
   for (int i=0; i<possibilities.length; ++i)
      possibilities[i] = 0;

   for (int squareNum=1; squareNum<=9; ++squareNum) {
      switch ( squares[squareNum].getLabel().charAt(0) ) {
         case 'X':   val =  1;  break;
         case 'O':   val = -1;  break;
         case ' ':   val =  0;  break;
      }

      for (int which=0; which<possibleWins.length; ++which) {
         if ( isPartOf( squareNum, possibleWins[which] ) )
            possibilities[which] += val;
      }
   }
}


private boolean isPartOf (int num, int[] aRow ) {
   for (int i=0; i<aRow.length; ++i)
      if ( num == aRow[i] )
         return true;
   return false;
}

}