Below is an example class to represent a Tic-Tac-Toe board demonstrating proper encapsulation, and Javadoc comments.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
/** * This class models the Tic-Tac-Toe board for our game. * All methods required to manipulate the board are found here. * * @author Vik Rao * @version May 5, 2022 */ public class Board { // Properly encapsulated (private) instance variables private int emptyCells; // Design Decision: I will represent the Board using an array of char(acters) rather than Strings. // Since char is a primitive data type it uses less memory and we can use == for comparisons. private char[][] board; /** * This no-argument constructor initializes all instance variables. */ public Board() { // No need to "reinvent the wheel" here. 8) clearBoard(); } /** * This method will create a new 2D array filled with '' empty characters. * * This method has no parameters and returns nothing. */ public void clearBoard() { board = new char[3][3]; // Fill the board with blank spaces for (int row = 0; row < 3; row++) { for (int col = 0; col < 3; col++) { board[row][col] = ' '; } } emptyCells = 9; } /** * This accessor returns the number of free cells left on the board. * * @return An integer between 1 and 9 representing the number of empty cells on the board */ public int getEmptyCells() { return emptyCells; } /** * This method attempts to place a given player token on the board. * It checks that the given row and col are valid indices and also that the chosen cell is ' ' empty. * If so, it places the given token in that cell, and returns true * Otherwise, it does not change the board and returns false * * @param row The row (integer between 0..2) to place the token * @param col The column (integer between 0..2) to place the token * @param token 'X' or 'O' to place at the given row and col. * * @return true if the location was valid and empty; (i.e., token was placed) or false if the move was invalid. */ public boolean placeToken(int row, int col, char token) { // Check that the row and col are in range (0..2) and the cell is empty before allowing the move if ( 0 <= row && row <= 2 && 0 <= col && col <= 2 && board[row][col] == ' ') { board[row][col] = token; emptyCells--; return true; } // Invalid move, row and/or col out of range (0..2) or the cell was not empty return false; } /** * This method checks if there are 3 'X' or 3 'O' characters in a row (vertically, horizontally, or diagonally) on the board. * * @return 'X' is returned if there are 3 X's in a row, 'O' is returned if there are 3 O's in a row, or ' ' is returned if there is no winner (yet). */ public char checkWinner() { // Check rows for winner for (int row = 0; row < 3; row++) { if (board[row][0] == board[row][1] && board[row][1] == board[row][2] && board[row][0] != ' ') { return board[row][0]; } } // Check columns for winner for (int col = 0; col < 3; col++) { if (board[0][col] == board[1][col] && board[1][col] == board[2][col] && board[0][col] != ' ') { return board[0][col]; } } // Check diagonal (top-left to bottom-right) for winner if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ') { return board[0][0]; } // Check diagonal (bottom-left to top-right) for winner if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[2][0] != ' ') { return board[2][0]; } // No winner: return a blank space character return ' '; } /** * Standard toString method. * * @return String representation of the current Tic-Tac-Toe Board */ public String toString() { return " 0 1 2\n" + "0: " + board[0][0] + " | " + board[0][1] + " | " + board[0][2] + "\n ---+---+---\n" + "1: " + board[1][0] + " | " + board[1][1] + " | " + board[1][2] + "\n ---+---+---\n" + "2: " + board[2][0] + " | " + board[2][1] + " | " + board[2][2] + "\n"; } } |
And here is the code for the main() game loop and additional helper functions. Notice that there are no global variables.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
/** * A Simple Tic-Tac-Toe Game Example * * @author Vik Rao * @version May 5, 2022 */ import java.util.Scanner; import java.util.Random; public class TicTacToe { /** * This method defines the mainline logic for our game loop. * It uses a Tic-Tac-Toe Board object and various helper methods to run the game. */ public static void main(String[] args) { Scanner input = new Scanner(System.in); Board ticTacToeBoard = new Board(); boolean playGame = true; while (playGame) { boolean usersTurn = true; System.out.println("\nWelcome to Tic-Tac-Toe!\n"); while (ticTacToeBoard.checkWinner() == ' ' && ticTacToeBoard.getEmptyCells() > 0) { System.out.println(ticTacToeBoard); if (usersTurn) { makeUserMove(ticTacToeBoard, input); } else { makeComputerMove(ticTacToeBoard); } usersTurn = !usersTurn; } System.out.println("*** G A M E O V E R ***"); System.out.println(ticTacToeBoard); if (ticTacToeBoard.checkWinner() == 'X') { System.out.println("YOU Won! Congratulations."); } else if (ticTacToeBoard.checkWinner() == 'O') { System.out.println("You LOSE! Muh-ha-ha-ha!"); } else { System.out.println("Tie! Play me again."); } playGame = askPlayAgain(input); ticTacToeBoard.clearBoard(); } System.out.println("See you next time!"); } /** * This method prompts the user for a move. * When this method exits, an 'X' will be placed on the board in a valid (empty) cell. * * @param tttBoard A Board object for our game. * @param input A Scanner object to get user input. */ public static void makeUserMove(Board tttBoard, Scanner input) { boolean validMove = false; // Prompt for a row and column with input validation while (!validMove) { System.out.print("What row would you like to move to (0-2): "); int row = input.nextInt(); System.out.print("What column would you like to move to (0-2): "); int col = input.nextInt(); // placeToken() will return false if the row and/or col are invalid or the cell is not empty validMove = tttBoard.placeToken(row, col, 'X'); if (!validMove) { System.out.println("Sorry, that location is not available to place an 'X'."); } } } /** * This method randomly generates a computer move. * When this method exits, an 'O' will be placed on the board in a valid (empty) cell. * * @param tttBoard A Board object for our game. */ public static void makeComputerMove(Board tttBoard) { Random numberGenerator = new Random(); boolean validMove = false; // Keep generating random moves until we find an empty cell to place an 'O' while (!validMove) { int row = numberGenerator.nextInt(3); int col = numberGenerator.nextInt(3); // placeToken() will return false if the cell is not empty (i.e., we need to try a new spot) validMove = tttBoard.placeToken(row, col, 'O'); } } /** * This method prompts the user if they would like to play again with some invalidation. * * @param input A Scanner object to get user input. * * @return true is returned if they would like another round, or false otherwise. */ public static boolean askPlayAgain(Scanner input) { // After reading integer values for row/col we need to consume the left-over <return> on the Scanner. input.nextLine(); System.out.println("Play again? [y/n]: "); String playAgain = input.nextLine(); while (!playAgain.equals("y") && !playAgain.equals("yes") && !playAgain.equals("n") && !playAgain.equals("no")) { System.out.println("Play again? [y/n]: "); playAgain = input.nextLine(); } if (playAgain.equals("y") || playAgain.equals("yes")) { return true; } return false; } } |