Tutorial to implement Tic Tac Toe in Java

Tutorial to implement Tic Tac Toe in Java

What is Tic Tac Toe?

Tic Tac Toe, also popularly known as X and 0?s is a two-player game. Generally, there is a 3 X 3 grid, in which players take turns to mark the spaces with their respective symbol i.e. ?X? or ?0?. The player that succeeds in placing three of their makes either in horizontal, vertical or in a diagonal row, wins.

Here are some rules for the game:

  • The player with symbol ?X? goes first
  • Players alternate placing Xs and Os on the board until either
  • one player has three in a row, horizontally, vertically or diagonally; or
  • all nine squares are filled.
  • The player who can draw three Xs or three Os in a row wins.
  • If all nine squares are filled and none of the players have three in a row, the game is a draw.

Java Tutorial for creating a Tic Tac Toe

Follow these steps to create a Tic Tac Toe program in java. Today, we will create a simple Tic Tac Toe program, without any GUI and can be played by two players. Please note that both the players must be human.

  1. Create a new Project

The very first step of creating our Tic Tac Toe game is to make a new project. Open your IDE (I am using Eclipse) and create a new Java Project by the name TicTacToe. Inside our project, we will create two classes, Main.java and MainGame.java, The Main.java class will contain the main() method and will be used to run the code in MainGame.java. The other class being MainGame, which will contain an object that will have the state of the board and methods to manipulate the game.

2. Working with the MainGame() class

Before writing the code to run our MainGame() object, we need to create a working object. Remember, our object is going to represent one game of TicTacToe, therefore it will contain two variables:

private char board;

This variable would be the 2D array that would represent the 3X3 board of the game. The state of the game would be held inside this board.

private char currentPlayer;

The value of this variable would either be ?X? or ?O? and would determine which player would be playing next. This variable would be used by the methods of our MainGame class to determine which mark (?X? or ?0?) would be placed next.

3. Initializing the method prototypes in the MainGame class:

In this section, I am going to discuss all the method headers that would be used in the MainGame.java class. To enjoy a full game of TicTacToe, these methods are necessary:

public MainGame():

This constructor would be responsible for ensuring that the board gets initialized properly. It will also decide which player would be playing first.

public void initializeBoard()

When this method is invoked, it will make sure that all the slots on the board are empty.

public void printBoard()

The Tic Tac Toe board will be printed in the standard format when this method is initialized.

public boolean isBoardFull()

We need this method to check whether the board is full or not. If the board is full, this method would return true, if not false.

public boolean isWinner()

If a player wins a game, this method would return true.

private boolean checkRows()

This method would check the rows for win

private boolean checkColumns()

This method will check the columns for the win.

private boolean checkDiagonals()

This method will check the diagonals for the win

private boolean checkRowCol(char c1, char c2, char c3)

This method will check if the 3 specified characters in the rows and columns contain a similar letter i.e. ?x? or ?o?. If yes, it will return true.

4. Initializing the board:

As we discussed earlier, this method will initialize the board and make sure that all slots are empty.

public void initializeBoard()

All the slots on the board will contain empty values, In this method, we will create 2 For loops, one to check for rows and another one to check for columns.

for (int i = 0; i < 3; i++) {

Inside this for loop, we will create a second for loop, with an integer ?j?, to denote the column that we are observing.

for (int j = 0; j < 3; j++)

Inside this second loop, we will set the location of the board to ?-?. With both loops completed and nested properly, we can iterate through every place inside of the board 2D array.

This is how the loop would look:

// Loop through rows

for (int i = 0; i < 3; i++) {

{

// Loop through columns

for (int j = 0; j < 3; j++) {

board[i][j] = ?-?;

}

5. Printing the board:

The initially printed board would look like

Image for post

The method public void printBoard(), from the MainGame() class, would be handling this. To print the board, we need to have an access to every spot on the 2D array.

We will handle this using nested loops.

Initially, we just need to print a line of ?-?(In this case, 13 of these), which would represent the top of the board. Beneath that, we need to create a ?for loop? that will be looping through each of the three rows. This loop will call to print a ?|? character, we will put a second ?for loop?, to loop through the columns, and a call to the System.out.println() function. This function would print a new line and the remaining ?-? on the screen.

The inner for loop will also loop through three columns. Since our outer for loop has already printed the first ?|? character of each row of the board, we can now print the characters that belong in the box. To do this, we?ll print the character at that row and column using board[i][j], here ?i? is the variable which was used for the outer ?for loop?, which is the row, and j being the variable used for the inner loop, which is the column. This print statement also needs to have a concatenated ?|? character, that will separate the boxes.

We now need to make the last call to print the new line to a separate row followed by ?-? (in this case 13).

This is the syntax and code for printing the board:

public void printBoard()

{

System.out.println(? ? ? ? ? ? ? -?);

for (int i = 0; i < 3; i++)

{

System.out.print(?| ?);

for (int j = 0; j < 3; j++)

{

System.out.print(board[i][j] + ? | ?);

}

System.out.println();

System.out.println(? ? ? ? ? ? ? -?);

}

}

6. Determining whether a player has won

A player could be a winner in 3 ways: rows, columns, and diagonals. Our program needs to separate these into different conditions because they are all different in terms of arrays. In this case, the isWinner() function will be will be utilized to test all these three functions

isWinner() :

We will simply write a return statement that calls upon these three functions.

At first, we will check the rows for the win, if it doesn?t return true, we will check the columns, and finally the diagonals.

This is how our functions would look checkRows() || checkColumns() || checkDiagonals()

Syntax:

public boolean isWinner()

{

return (checkRows() || checkColumns() || checkDiagonals());

}

checkRows():

We would loop through the rows to see if we have any winner. This will contain one loop with an if statement inside it. The for loop will be incrementing through the integer ?I?.

for (int i = 0; i < 3; i++).

The if statement would compare each space in a row to each other and return a true value if they all are ?equal?. For e.g. if a player has three ?o?s? in a row, this method would return true.

if (checkRowCol(board[i][0], board[i][1], board[i][2]) == true)

Within this ?if statement?, there should be a: ?return true?; and if the method has not stopped after the ?For Loop?, then the method needs have a return which would state that this row does not have 3 consecutive matching symbols. To achieve this, we will write: return false, before closing this statement.

Syntax:

private boolean checkRows()

{

for (int i = 0; i < 3; i++)

{

if (checkRowCol(board[i][0], board[i][1], board[i][2]) == true)

{

return true;

}

}

return false;

}

checkColumns()

This method would be very similar to the checkRows() method, with a slight change. We will be making changes, inside the ?if? statement. We will be incrementing through the coloumns, instead of the rows. This means, that checkColumns() will have an if statement that says: if (checkRowCol(board[0][i], board[1][i], board[2][i]) == true).

Syntax:

private boolean checkColumns()

{

for (int i = 0; i < 3; i++)

{

if (checkRowCol(board[0][i], board[1][i], board[2][i]) == true)

{

return true;

}

}

return false;

}

checkDiagonals():

Here, the first check we will perform would be on the diagonal from the top left corner to the bottom right corner. To do this, we check all the spaces that would be included in this section. checkRowCol(board[0][0], board[1][1], board[2][2]) == true) Then we will have one more statement, but we will separate the two by an OR symbol: ?||? The second statement will check from the top right corner to the bottom left corner. checkRowCol(board[0][2], board[1][1], board[2][0]) == true) So your final product of the checkDiagonals() method should be a return(), and inside it should contain the first statement OR the second statement.

Syntax:

private boolean checkDiagonals()

{

return ((checkRowCol(board[0][0], board[1][1], board[2][2]) == true) || (checkRowCol(board[0][2], board[1][1], board[2][0]) == true));

}

checkRowCol() method

In this method, we would make sure if the first value is not an empty one or ?-?. We would then compare the first value to the second, second to the third and so on, and if and only if all the values match with each other and they are not empty, this method will return true. So basically, the first statement will check if it?s not empty (c1 != ?-?). If it?s true, then it will check whether the first value is equal to the second value and if this is true, it would check if the third value is equal to the second value (c2 == c3).

Here is the syntax:

private boolean checkRowCol(char c1, char c2, char c3)

{

return ((c1 != ?-?) && (c1 == c2) && (c2 == c3));

}

7. Allowing the second player to take the turn

The changePlayer() method will swap the variable ?currentplayer? between ?x? and ?o?. To do this, we need to check, what the variable currently holds. If the variable is holding an ?x?, then change it to an ?o?. Otherwise, change it to an ?x?.

Syntax:

public void changePlayer()

{

if (currentPlayer == ?x?)

{

currentPlayer = ?o?;

}

else

{

currentPlayer = ?x?;

}

}

public boolean placeMark(int row, int col)

This method will place the correct letter onto the specified row and col in the board variable (taken in as parameters, for e.g. to place x in the first row and the first column, the user will have to put 1 1). This will be true if the placement is valid. If not, the player would be prompted to enter the parameters again.

To accomplish this behavior, we need to check few things:

We need to use and if statement to see whether the row argument was between 0 and 2. Next, we would check that the col argument was between 0 and 2. Finally, we need to see if the spot entered by the users is empty or not. If all the conditions are true, we place the at the location specified by row and col and then return true. If any of the three conditions are not met, then noting should happen and false should be returned.

Syntax:

public boolean placeMark(int row, int col)

{

if ((row >= 0) && (row = 0) && (col < 3)) {

if (board[row][col] == ?-?)

{

board[row][col] = currentPlayer;

return true;

}

}

8. Getting the player input and playing the game:

All the methods of our MainGame class are complete. We now need to create a main() method which will run through the entire game by using the MainGame object.

We will create a Scanner object to take input from System.in. This object must also instantiate a MainGame() object to play the Tic-Tac-Toe game with. After these things, it must initialize the board of the MainGame() object by calling it?s initializeBoard() method.

After these steps are completed, actual gameplay must be accounted for. To go through turns, we need to use a do-while loop. If we have a winner or the board is full, this loop should break. We will print the current board state before the turn so that the player knows which spaces are occupied and which spaces are free. When the player has provided the inputs, we take them and mark them on the board. After this input is taken in, the mark will be placed using the MainGame object?s method, and the players would switch turns.

This is how to code will look:

do

{

System.out.println(?Current board layout:?);

game.printBoard();

int row;

int col;

do

{

System.out.println(?Player ? + game.getCurrentPlayer() + ?, enter an empty row and column to place your mark!?);

row = scan.nextInt()-1;

col = scan.nextInt()-1;

}

while (!game.placeMark(row, col));

game.changePlayer();

}

To decide whether the game had a winner (or it was a tie) we will check if the game was a tie first by checking if both the board was full and there was no winner. If these things are the case, then print out that the game was a tie. Otherwise, print out who won by printing out the opposite of the current state of the MainGame object?s currentplayer variable. This can be accomplished by first calling the MainGame object?s changePlayer() method and then using the MainGame object?s getCurrentplayer() method to get the state of the currentplayer variable. Finally, we will print the board for one last time to show the final state.

This is the snippet to perform the above action:

while(!game.isWinner() && !game.isBoardFull());

if (game.isBoardFull() && !game.isWinner())

{

System.out.println(?The game was a tie!?);

}

else

{

System.out.println(?Current board layout:?);

game.printBoard();

game.changePlayer();

System.out.println(Character.toUpperCase(game.getCurrentPlayer()) + ? Wins!?);

}

9. Play the Game:

At this point, we have completed all the steps. Go ahead and compile the file (it is highly recommended that you use an IDE). Once compiled, click on the run button and play the game. If you have find errors while compiling or want to see the code, you can find that here.

Future Enhancements:

  1. Adding GUI:This game will be more interactive if GUI is added to it. Instead of having players enter the location (here the players need to define, where to place their symbols like 2 1 or 3 3), the symbols can be added to the by just clicking on the matrices, which would be buttons if GUI is used.
  2. Allow the players to define the metrics:An option to increase the board to size to NxN, where N can be any positive number (>0). This will increase the complexity of the code and when added with artificial intelligence will greatly make the game robust. Also, we can add a feature where k(<N) consecutive marks/dots (filled by the user) is a point/score. Or we can go even further and use a range of number n.
  3. Introduction of Artificial IntelligenceAn option of playing against the computer (bot) can be added too. An algorithm can be implemented for the bot based on actual rules a player uses to fill the Tic-Tac-Toe board. This can pose a challenge to the user when playing.
  4. User profiles and scoresOne of the other enhancements could be that users can create their profiles and save their scores.
  5. In-Game assistantAn option to include an Assistant which will provide hints and suggestions to the user when playing against a bot.
20