How to Write a Tic Tac Toe Game in Java using classes

Posted by Marta on February 2, 2023 Viewed 24658 times

Card image cap

In this article you will learn to code a Tic Tac Toe game using java and classes. The tic tac toe is a really simple game and the rules are widely known. Our game will use a 2D array to represent the board, and it will be console based, meaning you will interact with the game using the console.

I have organised the code in three different classes each with different responsibilities. Separating different functionality in different classes is always a good idea. It will help to organise your code logically, and divide the main problem into smaller problems that are easier to solve.

Watch the tutorial on Youtube:

General consideration

Firs thing to consider when implementing a tic tac toe game is how you want to present the game board. The board is basically a 2D array with 3 rows, and 3 cells each row. The cells can have 3 states: empty cell, cell containing an X, or the cell can contain an O. In this example, I will represent each of those states with numbers:

  • 0 = empty cell
  • 1 = cell containing an X
  • 2 = cell containing an Y

Therefore I will use integers to represent the cells, and a matrix of integers to represent the board. In java you can create a matrix using a 2D array. See how this works below:

private int[][] board= new int[3][3];

Game mechanism

Let’s see how our game will work. First we need to display the board, showing to the user the mapping between the numbers and the cells. This way they know, if they like to place a game piece at the left-top corner, they should enter number 0 and so on.

Next the game will carry out the following steps: request the user to enter a number, place the X piece on the board in the chosen position, place the opponent piece, display the board with the new pieces, and finally check is anyone has won.

The game should keep asking for positions until someone wins or the board is full of pieces. See how the game will work below:

Overall coding approach

As I mentioned before I will organise the code in 3 classes. One will be the Board, which will have the code to manipulate the matrix. Another class called GameResolver which will has the code to check if there are 3 piece in line. And finally the TicTacToeGame class which will have the game logic. Let’s see how to implement each.

Step #1: Implementing the board

First step, I will create a class named Board that will contain all logic to manipulate the 2D array: display the matrix to the console, place a game pieces, and check if the board is full. See below the Board class and the methods I will need.

public class Board {
    // 0 means empty
    // 1 means X
    // 2 means O
    private int[][] board= new int[3][3];

    public void instructionBoard(){}

    public void displayBoard(){}

    private String printBoardRow(int row){ return "";}

    public boolean placePiece(int position, String pieceType){ return false; }

    public boolean placePieceRandomly(String pieceType){ return false; }

    public boolean isFull(){ return false; }
   
    public int[][] getBoard(){
        return board;
    }
}

As you can see above there is a method to display the board, two methods to place game pieces, randomly or in a given position, and a method to check if the matrix is full.

Find the source code here

Display the board using a 2D Array

I will create two methods to display the board. One that will display the board in “instruction mode” showing the position number for each cell. And another method displaying the real board with the X’s and O’s . See the code for each method below:

public class Board {
    // 0 means empty
    // 1 means X
    // 2 means O
    private int[][] board= new int[3][3];

    public void instructionBoard(){
        System.out.println("| - | - | - |");
        System.out.println("| 1 | 2 | 3 |");
        System.out.println("| - | - | - |");
        System.out.println("| 4 | 5 | 6 |");
        System.out.println("| - | - | - |");
        System.out.println("| 7 | 8 | 9 |");
        System.out.println("| - | - | - |");
    }

    public void displayBoard(){
        System.out.println("| - | - | - |");
        System.out.println(printBoardRow(0));
        System.out.println("| - | - | - |");
        System.out.println(printBoardRow(1));
        System.out.println("| - | - | - |");
        System.out.println(printBoardRow(2));
        System.out.println("| - | - | - |");

    }
  
  	private String printBoardRow(int row){
    	//implementation below
    }
  
}
private String printBoardRow(int row){
        StringBuilder rowBuilder = new StringBuilder("| ");
        for(int i=0;i<board[0].length;i++){
            if(board[row][i] == 0) rowBuilder.append(" ");
            if(board[row][i] == 1) rowBuilder.append("X");
            if(board[row][i] == 2) rowBuilder.append("O");
            rowBuilder.append(" | ");
        }
        rowBuilder.deleteCharAt(rowBuilder.lastIndexOf(" "));
        return rowBuilder.toString();
}

The printBoardRow method above will build a string representing the state of the matrix row. To do so, I will check each cell of the matrix row. If there is a 0 in the matrix, it means the cell is empty, so it will append a white space to the matrix. If the cell contains a 1, it means there is an X piece in that cell, so it will append an X to our final row string.

Find the source code here

Place game pieces

Once we asked the user to enter a position, we will need to place X or O in the board. Placing the piece in the game board, basically means updating a given cell in our 2D array. For instance, if we would like to place a X piece in position (0,0), the top-left corner, that is equivalent to performing the following operations:

board[0][0] = 1 // 1 means X

For our game I will need two methods. One that place a game piece in a given position, and another that place a piece randomly, simulating an opponent.

When placing a piece randomly, consider you can’t place a piece in a cell already occupied. Let’s have a look at the code. Please note you should add these two methods to the Board class:

	public boolean placePiece(int position, String pieceType){
        int row = (position-1)/3;
        int col = (position - (row*3))-1;
        if(board[row][col] == 0) {
            if (pieceType.equals("X")) board[row][col] = 1;
            if (pieceType.equals("O")) board[row][col] = 2;
            return true;
        }
        return false;

	}

The code above will place a piece in a given position. First it will calculate the mapping cell for the selected position. And secondly it will update the resulting cell with 1 or 2, depending if I want to play an X piece or an ) piece.

	public boolean placePieceRandomly(String pieceType){
        int row = new Random().nextInt(3);
        int col = new Random().nextInt(3);
        boolean wasPiecePlace = false;
        while(!wasPiecePlace && !isFull()) {
            if(board[row][col]==0) {
                wasPiecePlace=true;
                if (pieceType.equals("X")) board[row][col] = 1;
                if (pieceType.equals("O")) board[row][col] = 2;
            }else{
                row = new Random().nextInt(3);
                col = new Random().nextInt(3);
            }
        }
        return wasPiecePlace;
    }

The code above will place a game piece in any remaining free cell. First it will pick a random cell, check if the cell is zero, meaning empty. Then if the cell is empty, it will update the cell with 1 or 2. In case the cell is not zero, it means the cell is occupied, so the code needs to try with another random cell.

Find the source code here

Check if the board is full

In case the board is full of X’s and O’s or someone won, the game is over. Therefore our board class should have a method to check if the board is full. Here is the method you need to add to the Board class to get this functionality:

    public boolean isFull(){
		for(int row=0;row<board.length;row++){
            for(int col=0;col<board[0].length;col++){
                if(board[row][col]==0) return false;
            }
        }
        return true;
    }

The code above will use two for loops, that will check every cell of the 2D array. As soon as, you find one cell that is empty, it means the board is not full, therefore the code returns false. If the code reaches the end it means that there is no empty cells, so the code returns true, indicating the board is full.

Step #2: Resolve Game: Check if someone won

As soon as there are 3 consecutive pieces, someone won and consequently the game is over. Therefore we need some code that check if there are three pieces in line, and if so stop the game. To do so, you could create a java class called GameResolver including the functionality necessary for our tic tac toe game.

Find Three Consecutive pieces

Let’s think how you will do this manually. You will go through every cell and check if there are 3 pieces in line for every possible direction. You can write exactly this mechanism in java.

First let’s see how to do the simplest task, which is given one cell check if there are 3 pieces in line in one single direction. For instance, check if there are 3 pieces inline starting from cell (0,0) in the right direction. Here is how to do this check:

        isCellOutOfBoard = (position.col+1>board[0].length-1);
        if ( !isCellOutOfBoard && (board[position.row][position.col+1] == gamePiece)){
            isCellOutOfBoard = (position.col+2>board[0].length-1);
            if(!isCellOutOfBoard && (board[position.row][position.col+2] == gamePiece)){
                return gamePiece==1?GameState.X_WON:GameState.O_WON;
            }
        }

Find the source code here

The code above need to check the cell on the right, which you can access staying in the same row and moving to the next column. You also need to avoid accessing cells that are out of the board, which is the aim of the isCellOutOfBoard variable.

In case 3 pieces are found in any direction, the code returns an enum indicating someone won. Please note you should add this piece of code to the GameResolver class:

public enum GameState{
	IN_PROGRESS,X_WON,O_WON;
}

Now that we know how to do the check in one direction, we need to apply the same mechanism for every direction. See how this works below:

public static GameState calculateGameState(int[][] board, Board.Cell position){

        int gamePiece = board[position.row][position.col];
        if (gamePiece == 0) return GameState.IN_PROGRESS;
        // DOWN DIRECTION
        boolean isCellOutOfBoard = (position.row-1 < 0);
        if (!isCellOutOfBoard && (board[position.row-1][position.col] == gamePiece)){
            isCellOutOfBoard = (position.row-2<0);
            if(!isCellOutOfBoard && (board[position.row-2][position.col] == gamePiece)){
                return gamePiece==1?GameState.X_WON:GameState.O_WON;
            }
        }
        // RIGHT UP DIRECTION
        isCellOutOfBoard = (position.row-1<0) || (position.col+1>board[0].length-1);
        if ( !isCellOutOfBoard && (board[position.row-1][position.col+1] == gamePiece)){
            isCellOutOfBoard = (position.row-2<0) || (position.col+2>board[0].length-1);
            if(!isCellOutOfBoard && (board[position.row-2][position.col+2] == gamePiece)){
                return gamePiece==1?GameState.X_WON:GameState.O_WON;
            }
        }

        // RIGHT DIRECTION
        isCellOutOfBoard = (position.col+1>board[0].length-1);
        if ( !isCellOutOfBoard && (board[position.row][position.col+1] == gamePiece)){
            isCellOutOfBoard = (position.col+2>board[0].length-1);
            if(!isCellOutOfBoard && (board[position.row][position.col+2] == gamePiece)){
                return gamePiece==1?GameState.X_WON:GameState.O_WON;
            }
        }

        // RIGHT DOWN DIRECTION
        isCellOutOfBoard =position.row+1>board.length-1 || (position.col+1>board[0].length-1);
        if ( !isCellOutOfBoard && (board[position.row+1][position.col+1] == gamePiece)){
            isCellOutOfBoard = position.row+2>board.length-1 || (position.col+2>board[0].length-1);
            if(!isCellOutOfBoard && (board[position.row+2][position.col+2] == gamePiece)){
                return gamePiece==1?GameState.X_WON:GameState.O_WON;
            }
        }

        // DOWN DIRECTION
        isCellOutOfBoard =position.row+1>board.length-1;
        if ( !isCellOutOfBoard && (board[position.row+1][position.col] == gamePiece)){
            isCellOutOfBoard = position.row+2>board.length-1;
            if(!isCellOutOfBoard && (board[position.row+2][position.col] == gamePiece)){
                return gamePiece==1?GameState.X_WON:GameState.O_WON;
            }
        }

        // LEFT DOWN DIRECTION
        isCellOutOfBoard =position.row+1>board.length-1 || (position.col-1<0);
        if ( !isCellOutOfBoard && (board[position.row+1][position.col] == gamePiece)){
            isCellOutOfBoard =position.row+2>board.length-1 || (position.col-2<0);
            if(!isCellOutOfBoard && (board[position.row+2][position.col-2] == gamePiece)){
                return gamePiece==1?GameState.X_WON:GameState.O_WON;
            }
        }

        // LEFT DIRECTION
        isCellOutOfBoard = (position.col-1<0);
        if ( !isCellOutOfBoard && (board[position.row][position.col-1] == gamePiece)){
            isCellOutOfBoard =(position.col-2<0);
            if(!isCellOutOfBoard && (board[position.row][position.col-2] == gamePiece)){
                return gamePiece==1?GameState.X_WON:GameState.O_WON;
            }
        }

        // LEFT UP DIRECTION
        isCellOutOfBoard = (position.row-1<0) || (position.col-1<0);
        if ( !isCellOutOfBoard && (board[position.row-1][position.col-1] == gamePiece)){
            isCellOutOfBoard = (position.row-2<0) || (position.col-2<0);
            if(!isCellOutOfBoard && (board[position.row-2][position.col-2] == gamePiece)){
                return gamePiece==1?GameState.X_WON:GameState.O_WON;
            }
        }

        return GameState.IN_PROGRESS;
    }

And finally we need to do this check for every board cell, so we can determine if anyone has won. To do so, I will add the following method to the class GameResolver:

    public static GameState resolve(int[][] board) {
        for(int row=0;row<board.length;row++){
            for(int col=0;col<board[0].length;col++){
                GameState gameState = calculateGameState(board,new Board.Cell(row,col));
                if(gameState!=GameState.IN_PROGRESS){
                    return gameState;
                }
            }
        }

        return GameState.IN_PROGRESS;
    }

Find the source code here

Step #3: Glue all code snippets together

So far we have seen the separate bits of code to program and tic tac toe game in java. Some code to manipulate the board, and also the code is able to resolve the game. Therefore it is time to put all code together and make our game works.

Now I will add the code that will capture the mechanism or logic the game should follow. First I need to create a Board, display the instructions with the numbers available to use, and request a number to the user.

Once the code received the position, it will place an X piece and it will start loop. The game should stay in the loop until the someone won the game or the board is full. This will be our exit loop condition. While this condition is not met, the game will keep placing the opponent piece randomly, displaying the board and asking the user to add another piece in a new position. See how this will works below:

public class TicTacToeGame {

    public static void main(String[] args) {
        Scanner userInputReader = new Scanner(System.in);

        Board board = new Board();
        board.instructionBoard();
        System.out.println("Please enter a position:");
        int position = Integer.parseInt(userInputReader.nextLine());
        board.placePiece(position, "X");

        boolean isGameInProgress = GameResolver.resolve(board.getBoard())==GameResolver.GameState.IN_PROGRESS;

        while( isGameInProgress && !board.isFull()){

            board.placePieceRandomly("O");
            board.displayBoard();
            System.out.println("Please enter a position:");
            position = Integer.parseInt(userInputReader.nextLine());
            board.placePiece(position, "X");

            isGameInProgress = GameResolver.resolve(board.getBoard())==GameResolver.GameState.IN_PROGRESS;

        }
        if(!isGameInProgress) {
            board.displayBoard();
            System.out.print(" GAME OVER!!!");
        }
        else board.displayBoard();
    }
}

The TicTacToeGame class is the entry point of our game since it has the main function. To run the game you can run this class from your IDE, either eclipse or Intellij.

Find the source code here

Conclusion

To summarise, in this tutorial I have covered how you can create a console based tic tac toe game in java. You basically need three classes: Board, GameResolver and TicTacToeGame. These three classes contains logic for manipulating the board, check the game status, and the game logic.

I hope you find the article useful and thanks for reading and supporting this blog. Happy coding! 🙂

Recommended articles

Project-Based Programming Introduction

Steady pace book with lots of worked examples. Starting with the basics, and moving to projects, data visualisation, and web applications

100% Recommended book for Java Beginners

Unique lay-out and teaching programming style helping new concepts stick in your memory

90 Specific Ways to Write Better Python

Great guide for those who want to improve their skills when writing python code. Easy to understand. Many practical examples

Grow Your Java skills as a developer

Perfect Boook for anyone who has an alright knowledge of Java and wants to take it to the next level.

Write Code as a Professional Developer

Excellent read for anyone who already know how to program and want to learn Best Practices

Every Developer should read this

Perfect book for anyone transitioning into the mid/mid-senior developer level

Great preparation for interviews

Great book and probably the best way to practice for interview. Some really good information on how to perform an interview. Code Example in Java