import java.util.Scanner; /** * Implements the game of Tic Tac Toe * @author alchambers */ public class TicTacToe { private static final int SIZE = 3; private static final int CELL_SIZE = 3; private static final int NUM_VERT_BARS = 4; private int row; // the row of the current player's move private int col; // the column of the current player's move private Scanner scan; private String[][] board; /** * Starts a new game of Tic Tac Toe */ public TicTacToe(){ row = -1; col = -1; scan = new Scanner(System.in); board = new String[SIZE][SIZE]; } /** * Plays a game of Tic Tac Toe */ public void play(){ printWelcomeMessage(); drawBoard(); int numMoves; String currPlayer = "x"; // If the number of moves reaches SIZE*SIZE, then we know we have a tie for(numMoves = 0; numMoves < SIZE*SIZE; numMoves++){ System.out.println("Player " + currPlayer + " turn"); takeTurn(currPlayer); if(win(currPlayer)){ break; } // Switch to the next player if(currPlayer.equals("x")){ currPlayer = "o"; } else{ currPlayer = "x"; } } if(numMoves == SIZE*SIZE){ System.out.println("\nIt was a tie!"); } else{ printWinMessage(currPlayer); } } /*********************************************************** * Private Methods for implementing each turn of the game ***********************************************************/ /** * Reads the user's move from the keyboard and stores the * row and column in instance variables. */ private void getMove(){ while(!validIndices(row, col) || board[row][col] != null){ System.out.print("Enter row: "); row = scan.nextInt(); System.out.print("Enter col: "); col = scan.nextInt(); System.out.println(); } } /** * Implements one turn in the game * @param mark the player's mark */ private void takeTurn(String mark){ getMove(); board[row][col] = mark; drawBoard(); } /*************************************** * Private Methods for Detecting Wins ***************************************/ /** * Detects a win * @param mark the player's mark * @return true if the player has won, false otherwise */ private boolean win(String mark){ return winRow(mark) || winCol(mark) || winDiag(mark); } private boolean winCol(String mark){ for(int col = 0; col < SIZE; col++){ boolean win = true; for(int row = 0; row < SIZE; row++){ if(board[row][col] == null || !board[row][col].equals(mark)){ win = false; } } if(win){ return true; } } return false; } /** * Checks each row for a win * @param mark the player's mark */ private boolean winRow(String mark){ for(int row = 0; row < SIZE; row++){ boolean win = true; for(int col = 0; col < SIZE; col++){ if(board[row][col] == null || !board[row][col].equals(mark)){ win = false; } } if(win){ return true; } } return false; } /** * Checks for a win in a diagonal position * @param mark player's mark * @return true if player won in main diagonal or off diagonal, false otherwise */ private boolean winDiag(String mark){ // Since the variable j is declared *outside* of the for-loop, we can // cleverly use it to first count up and then count down boolean won = true; int j = 0; for(int i = 0; i < SIZE; i++){ if(!mark.equals(board[i][j])){ won = false; } j++; } if(won){ return won; } won = true; j--; for(int i = 0; i < SIZE; i++){ if(!mark.equals(board[i][j])){ won = false; } j--; } return won; } /** * An alternative version of winDiag that uses only a single for loop */ private boolean winDiag_version2(String mark){ boolean mainDiag = true; boolean offDiag = true; for(int i = 0; i < SIZE; i++){ if(board[i][i] == null || !board[i][i].equals(mark)){ mainDiag = false; } if(board[i][SIZE-i-1] == null || !board[i][SIZE-i-1].equals(mark)){ offDiag = false; } } return (mainDiag || offDiag); } /** * An alternative version of winDiag that exploits the following * pattern: * - (row-column) = 0 for any entry on the main diagonal * - (row+column) = SIZE-1 for any entry on the off diagonal */ private boolean winDiag_version3(String mark){ boolean mainDiag = true; boolean offDiag = true; for(int i = 0; i < SIZE; i++){ for(int j = 0; j < SIZE; j++){ if(i-j == 0){ if(!mark.equals(board[i][j])){ mainDiag = false; } } if(i+j == SIZE-1){ if(!mark.equals(board[i][j])){ offDiag = false; } } } } return (mainDiag || offDiag); } /***************************************** * Private Methods for Validating Input *****************************************/ private boolean validMark(String mark){ return mark.equals("x") || mark.equals("o"); } private boolean validIndices(int row, int col){ return (row >= 0 && row < SIZE && col >= 0 && col