Skip to Content
Course content

225: Building a Tic-Tac-Toe Game

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Up until now, we've been dealing with isolated concepts—loops here, arrays there. It's time to smash them all together. We're going to build a command-line Tic-Tac-Toe game. It's a classic for a reason: it forces you to think about state management and how to translate a visual grid into data the computer understands.

Mapping the Board to a 2D Array

First, we need a way to represent the 3x3 grid. While you could use a single array of 9 elements, a 2D array is much more intuitive here because it maps directly to the rows and columns of the actual game. I'll use a char array since we only care about 'X', 'O', or an empty space.

char board[3][3] = {
    {' ', ' ', ' '},
    {' ', ' ', ' '},
    {' ', ' ', ' '}
};

I'm initializing these with spaces rather than null terminators or zeros. It makes printing the board to the console much cleaner, as we can just print the character and a pipe symbol | to create a visual grid.

Handling Player Turns and the "Overwriting" Bug

The core of the game is a loop that keeps running until someone wins. I'll use a variable to track whose turn it is, swapping between 'X' and 'O' after every valid move. Here is how I initially approached the input logic:

int row, col;
printf("Enter row and col (0-2): ");
scanf("%d %d", &row, &col);
board[row][col] = currentPlayer;

I ran this version for a second and immediately realized I'd made a rookie mistake: I wasn't checking if the spot was already taken. My 'X' player could just move into 'O's square and overwrite it. That's not much of a game. I need to wrap that assignment in a conditional check to ensure the square is still a space before allowing the move.

if (board[row][col] == ' ') {
    board[row][col] = currentPlayer;
    // Now we can switch players
    currentPlayer = (currentPlayer == 'X') ? 'O' : 'X';
} else {
    printf("That spot is already taken! Try again.\n");
}

Logic for Detecting a Win

This is the part where most people get bogged down in nested loops. To keep things readable, I'm going to move the win-check logic into its own function. I need to check three things: every row, every column, and the two diagonals.

For the rows and columns, I can use a simple loop. For the diagonals, it's easier to just hard-code the indices since there are only two of them. Here is the logic I settled on:

int checkWin() {
    // Check rows and columns
    for (int i = 0; i < 3; i++) {
        if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ') return 1;
        if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ') return 1;
    }
    // Check diagonals
    if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ') return 1;
    if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[0][2] != ' ') return 1;

    return 0;
}

Note that I'm checking board[i][0] != ' '. If I don't do that, the function will return a win as soon as the game starts because three empty spaces technically "match."

Polishing the Game Loop

Now we just tie it all together. We need a function to print the board so the players can actually see where they are moving. I'll use a nested loop to iterate through the 2D array and print the characters with some basic formatting.

void printBoard() {
    for (int i = 0; i < 3; i++) {
        printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
        if (i < 2) printf("---|---|---\n");
    }
}

By combining the input loop, the overwrite check, the win condition, and the print function, we have a fully functional game. It's not flashy, but it's logically sound. You've now managed state, handled user input errors, and implemented a search algorithm for the win condition—all in one project.




📋 Practical Task

Implementing a Draw Condition for Tic-Tac-Toe

Currently, if no one wins and the board fills up, the game continues indefinitely or crashes depending on how the loop is structured. Your task is to modify the game logic to detect a "Draw".

Implement a mechanism (either a counter that tracks total moves or a function that scans the board for any remaining spaces) that triggers a "Draw!" message and terminates the game if all 9 squares are filled and checkWin() still returns 0.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.