import java.util.Random; import java.util.Scanner; /** * Simulates the Tic Tac Toe game. * The game is based on a 3X3 board and 2 players. * The game is turn-based, each player, at their turn, place 1 piece on * the board, whether X or O. * The goal is to get to 3 consecutive squares of the same piece: horizontal, * vertical or diagonal. * * @author IDC CS Class of 2010-2011 */ public class TicTacToe { /** * Runs the game between a human and the computer. * @param args unused. */ public static void main(String[] args) { TicTacToe game = new TicTacToe(); System.out.println(game); do { game.playMove(); System.out.println(game); game.switchTurns(); } while (!game.isGameOver()); switch (game.getWinner()) { case COMPUTER: System.out.println("Computer wins.. :("); break; case HUMAN: System.out.println("Human wins! ~8^)"); break; default: System.out.println("No one wins. :-/"); break; } } /** * Represents the type a board cell can hold. */ private enum CellType { EMPTY, // if no piece is placed on that cell PIECE_X, // if X is placed on that cell PIECE_O // if O is placed on that cell } /** * Represents supported game players. */ public enum Player { HUMAN, COMPUTER, NONE } /** Denotes the size of the board (the board is 3X3) */ private static final int BOARD_SIZE = 3; // instance variables private CellType[][] board = new CellType[BOARD_SIZE][BOARD_SIZE]; private Player currPlayer = Player.NONE; private Player winner = Player.NONE; private int totalPiecesOnBoard = 0; /** * Constructs a new game with an empty board and sets the first player. */ public TicTacToe() { // iterate through the entire board for (int currRow = 0; currRow < board.length; ++currRow) { for (int currCol = 0; currCol < board[currRow].length; ++currCol) { // reset each cell board[currRow][currCol] = CellType.EMPTY; } } // let the human play first currPlayer = Player.HUMAN; } /** * Gets the game winner. * @return the game winner */ public Player getWinner() { return this.winner; } /** * Plays a single game move by the current player. */ public void playMove() { // run the move according to the current player switch (currPlayer) { case COMPUTER: // it's the human's turn runComputerTurn(); break; case HUMAN: // it's the computer's turn runHumanTurn(); break; } } /** * Checks whether the game is over or not. * A game is over in one of two cases: * - someone won * - the board is full * @return true if the game is over, otherwise false */ public boolean isGameOver() { // 8 options of winning // cases 1-3, horizontal for (int row = 0; row < 3; row++) { if (board[row][0] == board[row][1] && board[row][1] == board[row][2]) { if (board[row][0] != CellType.EMPTY) { this.winner = pieceToPlayer(board[row][0]); return true; } } } // cases 4-6, vertical for (int col = 0; col < 3; col++) { if (board[0][col] == board[1][col] && board[1][col] == board[2][col]) { if (board[0][col] != CellType.EMPTY) { this.winner = pieceToPlayer(board[0][col]); return true; } } } // cases 7-8, diagonals if (board[0][0] == board[1][1] && board[1][1] == board[2][2]) { if (board[1][1] != CellType.EMPTY) { this.winner = pieceToPlayer(board[1][1]); return true; } } // case 8, opposite diagonal if (board[0][2] == board[1][1] && board[1][1] == board[2][0]) { if (board[1][1] != CellType.EMPTY) { this.winner = pieceToPlayer(board[1][1]); return true; } } // check the case of full board and no winners. if (totalPiecesOnBoard == BOARD_SIZE * BOARD_SIZE) { this.winner = Player.NONE; return true; } return false; } /** * Returns a String representation of the current board and the pieces on it. */ public String toString() { String result = separatorLineToString(); // iterate on the rows for (CellType[] row : board) { // iterate on the cells of each row for (CellType cell : row) { // get a String representation of each cell switch (cell) { case EMPTY: result += "| "; break; case PIECE_O: result += "|O"; break; case PIECE_X: result += "|X"; break; } } result += "|\n"; result += separatorLineToString(); } return result; } /** * Switches the turns among the 2 players. */ public void switchTurns() { currPlayer = Player.values()[1 - currPlayer.ordinal()]; } /*------------------ PRIVATE MEMBERS -------------------------/* /** * Runs the computer's turn. * The computer is pretty dumb and selects a random (valid) * cell to place its piece on. */ private void runComputerTurn() { System.out.println("Computer move"); // select a random cell Random randomizer = new Random(); int targetX = randomizer.nextInt(BOARD_SIZE); int targetY = randomizer.nextInt(BOARD_SIZE); // keep choosing a cell until valid while (board[targetX][targetY] != CellType.EMPTY) { targetX = randomizer.nextInt(BOARD_SIZE); targetY = randomizer.nextInt(BOARD_SIZE); } // set the piece on that cell board[targetX][targetY] = CellType.PIECE_O; // this will be used to check if the game is over this.totalPiecesOnBoard++; } /** * Run the human turn. * The game player can choose where to put their piece, using the console. */ private void runHumanTurn() { System.out.println("Player move"); // let the user set the cell Scanner scanner = new Scanner(System.in); System.out.print("Enter coordinates, human [X Y]: "); int targetX = scanner.nextInt(); int targetY = scanner.nextInt(); // repeat until the human returns to his senses while (board[targetX][targetY] != CellType.EMPTY) { System.out.println("Invalid coordinates, human!"); System.out.print("Enter coordinates, human [X Y]: "); targetX = scanner.nextInt(); targetY = scanner.nextInt(); } // set the piece board[targetX][targetY] = CellType.PIECE_X; // this will be used to check if the game is over this.totalPiecesOnBoard++; } /** * Returns a String representation of the board separator line. */ private String separatorLineToString() { String result = ""; // draw the line for (int i = 0; i < BOARD_SIZE; ++i) { result += "+-"; } result += "+\n"; return result; } /** * Converts a cell type to a player, according to the cell content: if * the piece is X, it's a human, if O it's a computer. * @param type the cell type * @return the player type that put the piece on that cell, or null if * no piece is on that cell. */ private static Player pieceToPlayer(CellType type) { switch (type) { case PIECE_O: return Player.COMPUTER; case PIECE_X: return Player.HUMAN; default: return null; } } }