/* Tic-Tac-Toe Game written by Keith Fenske Sunday, 17 March 2002 Copyright (c) 2002 by Keith Fenske. All rights reserved. This is a simple implementation of Tic-Tac-Toe (X's and O's) to show the general control structure for a game. The algorithms in this Java program are not optimal. They were chosen because they are simple to implement, produce correct results, and are easy to understand. */ import cs1.Keyboard; // import Lewis/Loftus keyboard class // http://duke.csc.villanova.edu/jss/keyboard.html public class TicTacToe { final static int EMPTY = 0; // internal code for empty square final static int USER = 1; // internal code for user's squares final static int COMPUTER = 2; // internal code for computer's squares final static int SIZE = 3; // board grid is 3x3 static int[][] board; // data array for internal game board /* The main() method introduces the game and says good-bye. The actual work of playing the game is done by the playGame() method. Later, main() can be changed to play more than one game each time the program is run. This sample program has no way to end a game early. */ public static void main(String[] args) { System.out.println("\nWelcome to Tic-Tac-Toe!"); playGame(); System.out.println("\nThank you for playing Tic-Tac-Toe."); } // ------------------------------------------------------------------------- // For Java applications, put the main() method first, then put the other // methods in alphabetical order. // ------------------------------------------------------------------------- /* The clearBoard() method fills the game board with empty squares. */ static void clearBoard() { int col, row; board = new int[SIZE][SIZE]; for (row = 0; row < SIZE; row ++) for (col = 0; col < SIZE; col ++) board[row][col] = EMPTY; } /* The computerMoves() method makes a move for the computer. The computer's strategy in this example program is very poor: the computer picks an empty square at random! There should be at least one empty square, because the endGame() method checks if the board is full. */ static void computerMoves() { int col, row; int count; // number of empty squares int square; // index of random empty square // Count the number of empty squares. count = 0; for (row = 0; row < SIZE; row ++) for (col = 0; col < SIZE; col ++) if (board[row][col] == EMPTY) count ++; if (count == 0) { System.out.println("mistake in computerMoves() method"); return; } // Pick one of the empty squares at random. We are actually picking the // index (ordinal position) of the empty square, not the exact row and // column numbers. square = (int) (Math.random() * count); // Now go back and find the empty square. This loop is not efficient, // since it repeats the work of the first loop, and it rescans the // entire board, even if the randomly chosen square is found early in // the loop. count = 0; for (row = 0; row < SIZE; row ++) for (col = 0; col < SIZE; col ++) if (board[row][col] == EMPTY) { if (count == square) { board[row][col] = COMPUTER; System.out.println("\nMy move is row " + (row+1) + " column " + (col+1) + "."); } count ++; } } /* The displayBoard() method displays the game board by converting the internal representation (integers) into formatted text. */ static void displayBoard() { int col, row; System.out.println(); // blank line before printing board // display top of board with column numbers System.out.print(" "); // column numbers for (col = 0; col < SIZE; col ++) System.out.print(" " + (col+1)); System.out.println(); System.out.print(" "); // top line in board for (col = 0; col < SIZE; col ++) System.out.print("+-"); System.out.println("+"); // Each row has two lines of output: board squares and a dividing line. for (row = 0; row < SIZE; row ++) { // display line with board squares System.out.print((row+1) + "|"); for (col = 0; col < SIZE; col ++) { if (board[row][col] == EMPTY) System.out.print(" "); else if (board[row][col] == USER) System.out.print("X"); else if (board[row][col] == COMPUTER) System.out.print("O"); else System.out.print("mistake in displayBoard() method"); System.out.print("|"); } System.out.println(); // start new line // display dividing line between board rows System.out.print(" "); for (col = 0; col < SIZE; col ++) System.out.print("+-"); System.out.println("+"); } } /* The endGame() method checks if a player has won or if all the board squares are now occupied. This method returns true if the game is finished, and false if the game can be continued. */ static boolean endGame() { int col, row; // column, row int count; // number of squares with same player int winner; // who wins the game (if anyone) winner = EMPTY; // nobody has won yet // Check all rows to see if same player has occupied every square. for (row = 0; row < SIZE; row ++) { count = 0; if (board[row][0] != EMPTY) for (col = 0; col < SIZE; col ++) if (board[row][0] == board[row][col]) count ++; if (count == SIZE) winner = board[row][0]; } // Check all columns to see if same player has occupied every square. for (col = 0; col < SIZE; col ++) { count = 0; if (board[0][col] != EMPTY) for (row = 0; row < SIZE; row ++) if (board[0][col] == board[row][col]) count ++; if (count == SIZE) winner = board[0][col]; } // Check diagonal from top-left to bottom-right. count = 0; if (board[0][0] != EMPTY) for (row = 0; row < SIZE; row ++) if (board[0][0] == board[row][row]) count ++; if (count == SIZE) winner = board[0][0]; // Check diagonal from top-right to bottom-left. count = 0; if (board[0][SIZE-1] != EMPTY) for (row = 0; row < SIZE; row ++) if (board[0][SIZE-1] == board[row][SIZE-row-1]) count ++; if (count == SIZE) winner = board[0][SIZE-1]; // Did we find a winner? if (winner != EMPTY) { if (winner == USER) System.out.println("\nCongratulations! You win!"); else if (winner == COMPUTER) System.out.println("\nSorry, the computer wins this game."); else System.out.println("mistake in endGame() method"); return true; // return from endGame() method } // There was no winner. Now check if the game board is full. count = 0; // number of empty squares for (row = 0; row < SIZE; row ++) for (col = 0; col < SIZE; col ++) if (board[row][col] == EMPTY) count ++; if (count == 0) { System.out.println("\nSorry, this game is tied. Better luck next time!"); return true; // return from endGame() method } // Nobody won. Nobody tied. There is still a game to be played. return false; } /* The playGame() method controls the playing of a single game. */ static void playGame() { boolean end; // true when game is finished clearBoard(); // start with an empty board end = false; // play until we can't play anymore while (!end) { displayBoard(); // show user the game board userMoves(); // ask user for a move displayBoard(); // show updated game board end = endGame(); // check if game is finished if (!end) { computerMoves(); // computer makes a move end = endGame(); // check if game is finished if (end) displayBoard(); // show final board if computer wins } } } /* The userMoves() method asks the user (person) for his or her move using the keyboard. We ask for a row number and a column number. Java starts numbering rows and columns from zero; people prefer to start from one. */ static void userMoves() { boolean asking; // true until we get valid input int col, row; asking = true; while (asking) { System.out.println("\nWhat is your move? Please type a row number\n" + "from 1 to " + SIZE + " and a column number from 1 to " + SIZE + "."); row = Keyboard.readInt(); col = Keyboard.readInt(); if ((row < 1) || (row > SIZE) || (col < 1) || (col > SIZE)) { System.out.println("Sorry, row " + row + " or column " + col + " must be from 1 to " + SIZE + "."); } else { row --; // first Java row is numbered zero col --; // first Java column is numbered zero if (board[row][col] != EMPTY) { System.out.println("Sorry, that board square is occupied."); displayBoard(); // show user the game board (again) } else { board[row][col] = USER; asking = false; // we are finished the user's input } } } } } // end of class