DEV Community

Cover image for Let's Build Tic Tac Toe in Java
Thomas(Tripp) White
Thomas(Tripp) White

Posted on

Let's Build Tic Tac Toe in Java

Recently, I have wanted to broaden my horizon and have started to learn Java. I’ve watched online lectures, read forums, and practice small bits of code. This has been a great start but it is time to move on to a small project. I built a very simple command line Tic Tac Toe game in Java using a tutorial on youtube as a reference. To get the most out of this learning experience I want to summarize the project in my own words. Let's dive in to see how it works.

Setup

To get started I downloaded the Java SE Development Kit 16.0.2 and IntelliJ IDE for my local development. I created a new project with the command line template. From there I went coded everything out in one file if you would like to follow along. In the main we will use a while loop to handle our game. We need to do this so that way the game will keep working till we have a winner and then stop.

Game Board

First, let's start by creating a game board to use. A 2D array will represent our game board. A 2D array is just an Array of Arrays. Let's make it with the datatype char. This allows us to easily place X and O on the board. The gameBoard array has 5 elements. We need to have 3 rows for our X and O input and then we need to have the other two rows as lines to make our grid show.

char[][] gameBoard = {{' ','|',' ', '|', ' '},
        {'-','+','-', '+', '-'},
        {' ','|',' ', '|', ' '},
        {'-','+','-', '+', '-'},
        {' ','|',' ', '|', ' }};
Enter fullscreen mode Exit fullscreen mode

Now that we have our 2D array we need to print this out to the console. The printGameBoard(char[][] gameBoard) will handle this. This method takes in the gameBoard as a parameter. This way we can pass in the new game board every time it's updated with a move. Printing the game board is very easy. All we have to do is iterate through our gameBoard array. The first iteration will give us access to the row and the second gives us access to the each char in the row. All we have to do is print them to the console and it shows up nicely.

public static void printGameBoard(char[][] gameBoard) {
    for(char[] row: gameBoard){
        for(char c: row){
            System.out.print(c);
        }
        System.out.println();
    }
}
Enter fullscreen mode Exit fullscreen mode

Player input

Great, we now have our game board showing on our console. It is now time to get input from the user so we can start filling our board. The first thing we need to do is create a static variable for both playerPositions and cpuPositions. We need this to record each move. Having this record will allow us to check for winning combinations and to also make sure we don’t have duplicate moves.

static ArrayList<Integer> playerPositions = new ArrayList<Integer>();
static ArrayList<Integer> cpuPositions = new ArrayList<Integer>();
Enter fullscreen mode Exit fullscreen mode

To get input from the user lets imported the Scanner class and print a message to the console asking for their input. Once we received the input, add it to playerPos variable. Then take this variable to check to see if it's valid. To do this let's created a while loop that takes this position and checks to see if this position is in the playerPositions or cpuPositions ArrayList. If it is, prompt a message asking for a new move. Once we have a unique move, we then call our placePiece() to place the piece on the board.

Scanner scan = new Scanner(System.in);
System.out.println("Enter your placement (1-9):");
int playerPos = scan.nextInt();
while(playerPositions.contains(playerPos) || cpuPositions.contains(playerPos)){
    System.out.println("Position taken. Try again!");
    playerPos =  scan.nextInt();
}

placePiece(gameBoard, playerPos, "player");
Enter fullscreen mode Exit fullscreen mode

X Marks the Spot

Now that we have the input we need to place an X on the corresponding place on the gameBoard. Let's do this with a method called placePiece(). This will be the method for placing both the users and computers turns on the board. This method takes in the parameters for gameBoard, position, and user. These parameters allows this method to be the only method needed to place both X and O on the gameBoard. The variable symbol depends on the user parameter. If it is a player it will change the symbol to 'X' and if it's a cpu then the symbol will be 'O'. Then the switch statement will look at the position parameter. Depending on the position it will change the char in the 2D gameBoard array to the correct char. Now we just have to call our printGameBoard() method to update the gameBoard with our players move.

public static void placePiece(char[][] gameBoard, int pos, String user){
    char symbol = ' ';
    if(user.equals("player")){
        symbol = 'X';
        playerPositions.add(pos);
    } else if(user.equals("cpu")){
        symbol = 'O';
        cpuPositions.add(pos);
    }

    switch (pos){
        case 1:
            gameBoard[0][0] = symbol;
            break;
        case 2:
            gameBoard[0][2] = symbol;
            break;
        case 3:
            gameBoard[0][4] = symbol;
            break;
        case 4:
            gameBoard[2][0] = symbol;
            break;
        case 5:
            gameBoard[2][2] = symbol;
            break;
        case 6:
            gameBoard[2][4] = symbol;
            break;
        case 7:
            gameBoard[4][0] = symbol;
            break;
        case 8:
            gameBoard[4][2] = symbol;
            break;
        case 9:
            gameBoard[4][4] = symbol;
            break;
    }
}

Enter fullscreen mode Exit fullscreen mode

For the computers move we need to import the Random class and create a random number between 1 and 9. At first this works great till the random number is the same number as a previous users turn and it overwrites their spot on the board! To fix this we used the same while loop that we used to check the validation for the users move.

Random rand = new Random();
int cpuPos = rand.nextInt(9) + 1;

while(playerPositions.contains(cpuPos) || cpuPositions.contains(cpuPos)){
    System.out.println("");
    cpuPos = rand.nextInt(9)+1;
}

placePiece(gameBoard, cpuPos, "cpu");

Enter fullscreen mode Exit fullscreen mode

Winner Winner

So now we have a game board, input from the user, cpu generating random placement, and a check system to make sure the inputs are unique. How do we know when to stop the program and declare a winner? The checkWinner() method will check to see if there is a winner or if the game continues. In this method we have all the possible winning conditions. Then we add these conditions to a List of List so we can iterate through the List and search for these conditions in the playerPositions and cpuPositions. If the playerPositions or cpuPositions contains all three positions then they are the winner. If the number of positions for both cpu and player add up to 9 and they do not have any of the winning conditions then that means the Cat won. If there is no winner and there is still room on the board it will just return an empty string.

public static String checkWinner() {
    List tRow = Arrays.asList(1, 2, 3);
    List mRow = Arrays.asList(4, 5, 6);
    List bRow = Arrays.asList(7, 8, 9);
    List lCol = Arrays.asList(1, 4, 7);
    List cCol = Arrays.asList(2, 5, 8);
    List rCol = Arrays.asList(3, 6, 9);
    List fDiagonal = Arrays.asList(1, 5, 9);
    List bDiagonal = Arrays.asList(3, 5, 7);

    List<List> winningConditions = new ArrayList<List>();
    winningConditions.add(tRow);
    winningConditions.add(mRow);
    winningConditions.add(bRow);
    winningConditions.add(cCol);
    winningConditions.add(lCol);
    winningConditions.add(rCol);
    winningConditions.add(fDiagonal);
    winningConditions.add(bDiagonal);

    for (List l : winningConditions) {
        if (playerPositions.containsAll(l)) {
            return "Yay you won!";
        } else if (cpuPositions.containsAll(l)) {
            return "Sorry better luck next time!";
        } else if (playerPositions.size() + cpuPositions.size() == 9) {
            return "Cat won!";
        }

    }
    return "";
}

Enter fullscreen mode Exit fullscreen mode

Now in the while loop of our Main we check for winner after every player and cpu move. A conditional checks to see if checkWinner() returns a string longer than 0. If the length is zero that means we still have moves to make, but if it's greater than 0 that means we have some sort of winner. When there is a winner checkWinner() prints out the message and then uses the break keyword to stop the program.

String result = checkWinner();

if(result.length()>0){
    System.out.println(result);
    break;
}
Enter fullscreen mode Exit fullscreen mode

Summary

There you have it, a nice simple Tic Tac Toe game made with Java. This was a great first project for me to create with Java. When I was learning Ruby and Javascript I followed a tutorial and created this same game. Following tutorials can be helpful when starting, but to get the most of them, don’t just mindlessly follow along. Take notes and write summaries like this to help you fully grasp the code. This method has helped me tremendously on expanding my knowledge base. I had a great time with this project and I hope others do as well! Now its time for me to build something from scratch! If you want to see the my code for this project go check it out at my Github https://github.com/turpp/JavaTicTacToe.

Top comments (1)

Collapse
 
scodile profile image
Scodile

very nice tutorial,
I tried the code and found a little bug.

for (List l : winningConditions) {
if (playerPositions.containsAll(l)) {
return "Yay you won!";
} else if (cpuPositions.containsAll(l)) {
return "Sorry better luck next time!";
} else if (playerPositions.size() + cpuPositions.size() == 9) {
return "Cat won!";
}

If you use this code and the game ends in a win/loss on turn 9 it will return "Cat won!" even tough you want it to return win/loss.

I made a quick fix:
for (List l : winConditions) {

if (playerPositions.containsAll(l)){
return "player";
}else if (cpuPositions.containsAll(l)){
return "cpu";
}
}
if(playerPositions.size() + cpuPositions.size() == 9){
return "draw";
}

This allows the lists of possible winning combinations to be checked completely before you give the option for "draw" to be the returned value.