5
\$\begingroup\$

This program runs the game tic-tac-toe within the console. The players, X and O, take turns placing their character onto the 3x3 grid. When a player successfully makes 3 in a row, either vertically, horizontally, or diagonally, they win. If no winner is decided upon all squares being filled, it is a stalemate.

I'm wondering how efficient this code is. Are there any redundancies, formatting issues, legibility issues, etc.?

TicTacToe.java

package tictactoe;

public class TicTacToe {

    public static void main(String[] args) {
        GameBoard myGame = new GameBoard();
        myGame.displayBoard();

        int counter = 1;

        while (myGame.gameActive() && counter < 10) {
            if (counter % 2 == 0)
                myGame.askPlayer('O');
            else
                myGame.askPlayer('X');
            counter++;

            System.out.println("\n");
            myGame.displayBoard();
            myGame.checkForWinner();

            if (counter == 10 && myGame.gameOnGoing() == true)
                System.out.print("Stalemate!\n");
        }

    }

}

GameBoard.java

package tictactoe;

import java.util.Arrays;
import java.util.Scanner;

public class GameBoard {
    private char[][] gameBoard;
    private boolean gameOnGoing = true;

    //This is the constructor for the GameBoard class.
    public GameBoard() {
        gameBoard = new char[3][3];

        for (int row=0; row < gameBoard.length; row++) {
            Arrays.fill(gameBoard[row], ' ');
        }
    }   //end of constructor


    //This method will display the gameBoard to the screen.
    public void displayBoard() {
        for (int row=0; row < gameBoard.length; row++) {
            for (int col=0; col < gameBoard[0].length; col++) {
                System.out.print("\t" + gameBoard[row][col]);
                if (col == 0 || col == 1)
                    System.out.print("|");
            }
            if (row == 0 || row == 1)
                System.out.print("\n----------------------------\n");
        }
        System.out.println();
    }   //end of method displayBoard

    //This method will return true if the game is still active.
    public boolean gameActive() {
        return gameOnGoing;
    } //end of method gameActive

    //This method will ask the user to pick a row and column, validate
    //the inputs, and call the method makeMove().
    public void askPlayer(char player) {
        Scanner keyboard = new Scanner(System.in);
        int row, col;
        do {
            System.out.printf("Player %s, please enter a row (1-3): ",player);
            row = keyboard.nextInt();

            System.out.printf("Player %s, please enter a column (1-3): ",player);
            col = keyboard.nextInt();
        }   
            while (notValid(row,col));

        makeMove(player,row-1,col-1);

    }   //end of askPlayer method

    //This method will check to see if there are 3 x's or o's in a row
    //and return true if there is a winner, false otherwise
    public void checkForWinner() {
        //loop over each row and check for a winner
        for (int row =0; row < gameBoard.length; row++) {
            if (gameBoard[row][0] == gameBoard[row][1] && gameBoard[row][1] == gameBoard[row][2]
                    && gameBoard[row][0] != ' ') {
                System.out.print("The winner is " + gameBoard[row][0] + "!");
                gameOnGoing = false;
            }

        }

        //loop over each column and check for a winner
        for (int col=0; col < gameBoard[0].length; col++) {
            if (gameBoard[0][col] == gameBoard[1][col] && gameBoard[1][col] == gameBoard[2][col]
                    && gameBoard[0][col] != ' ') {
                System.out.print("The winner is " + gameBoard[0][col] + "!");
                gameOnGoing = false;
            }

        }

        //check the diagonals
        if (gameBoard[0][0] == gameBoard[1][1] && gameBoard[1][1] == gameBoard[2][2]
                && gameBoard[0][0] != ' ') {
            System.out.print("The winner is " + gameBoard[0][0] + "!");
            gameOnGoing = false;
        }

        if (gameBoard[2][0] == gameBoard[1][1] && gameBoard[1][1] == gameBoard[0][2]
                && gameBoard[2][0] != ' ') {
            System.out.print("The winner is " + gameBoard[2][0] + "!");
            gameOnGoing = false;
        }
    }

    //Same as above, but does not print winner or change state of gameOnGoing.
    public boolean gameOnGoing() {
        //loop over each row and check for a winner
        for (int row =0; row < gameBoard.length; row++) {
            if (gameBoard[row][0] == gameBoard[row][1] && gameBoard[row][1] == gameBoard[row][2]
                    && gameBoard[row][0] != ' ') {
                return false;
            }

        }

        //loop over each column and check for a winner
        for (int col=0; col < gameBoard[0].length; col++) {
            if (gameBoard[0][col] == gameBoard[1][col] && gameBoard[1][col] == gameBoard[2][col]
                    && gameBoard[0][col] != ' ') {
                return false;
            }

        }

        //check the diagonals
        if (gameBoard[0][0] == gameBoard[1][1] && gameBoard[1][1] == gameBoard[2][2]
                && gameBoard[0][0] != ' ') {
            return false;
        }

        if (gameBoard[2][0] == gameBoard[1][1] && gameBoard[1][1] == gameBoard[0][2]
                && gameBoard[2][0] != ' ') {
            return false;
        }
        return true;
    }   //end of method gameOnGoing

    //This method will validate if the row and column are between 1-3
    //and if the position is currently empty.
    public boolean notValid(int row, int col) {
        if (row > 3 || row < 1 || col > 3 || col < 1 || !isEmpty(row, col))
            return true;
        else    
            return false;
    } //end of method notValid

    //This method will check if a position is empty and return true
    //if the position is empty, false otherwise
    public boolean isEmpty(int row, int col) {
        if (gameBoard[row-1][col-1] == ' ')
            return true;
        else {
            System.out.print("That position is taken.\n");
            return false;
        }
    }

    //This method will validate if a player's move is allowed and return true
    //if the move was completed
    public boolean makeMove(char player, int row, int col) {
        if (row >=0 && row <=2 && col >=0 && col <=2) {
            if (gameBoard[row][col] != ' ')
                return false;
            else {
                gameBoard[row][col] = player;
                return true;
            }
        }
        else
            return false;
    }   //end of method makeMove

}
\$\endgroup\$
  • \$\begingroup\$ Hi, I think you should improve your question by removing "How would you fix these issues, personally?". Code Reviewers are not supposed to fix broken code. If your code is not broken, and you are sure about that, this phrase may generates a wrong interpretation about your question. \$\endgroup\$ – cpicanco Oct 13 '15 at 1:35
  • \$\begingroup\$ @SirPython Thank you. I've modified the post to address the points you have brought to my attention. \$\endgroup\$ – aebkea Oct 13 '15 at 1:41
3
\$\begingroup\$

Below are just some quick points. I didn't get into the meat of the GameBoard class.

TicTacToe.java

  • You're using a Magic Number here for your counter. This should be extracted as a constant.

  • The stalemate conditional can be placed outside of the while block, as you already know that counter = 10.

  • I would also extract the X and O chars as constants. I'm curious about what will happen when an unexpected char is passed to askPlayer.

  • There's rarely a point in having a \n in a print or println statement. In your finaly line, you can just have `System.out.println("Stalemate!");

  • You have all this game logic in a main method, when it should properly encapsulated into its own separate method.

GameBoard.java

  • Minor point, but I wouldn't initialize gameOnGoing to true by default. Rather, I'd have it set after everything was initialized in the constructor.

  • Extract the gameboard size, 3 as a constant.

  • Regarding comments, you should use proper JavaDoc comments for your methods, and there's no need to comment the end of every method. So this,

    //This is the constructor for the GameBoard class.
    public GameBoard() {
        gameBoard = new char[3][3];
        for (int row=0; row < gameBoard.length; row++) {
           Arrays.fill(gameBoard[row], ' ');
       }
     }   //end of constructor
    

    should be this:

    /**
     * This is the constructor for the GameBoard class.
     */
    public GameBoard() {
        gameBoard = new char[3][3];
    
        for (int row=0; row < gameBoard.length; row++) {
            Arrays.fill(gameBoard[row], ' ');
        }
    }
    
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.