Creating a Simple Tic-Tac-Toe Game in C

CCBeginner
Practice Now

Introduction

In this project, you will learn how to create a simple Tic-Tac-Toe game in C. The game will be played between two players taking turns to mark the spaces in a 3x3 grid. The first player to have three of their marks in a row, column, or diagonal is the winner. If all spaces are filled and no player has three marks in a row, the game is a draw.

👀 Preview

Tic-Tac-Toe

ðŸŽŊ Tasks

In this project, you will learn:

  • How to create a game board and initialize it with empty spaces.
  • How to implement functions to clear the screen, display the game board, and check if the game is over.
  • How to determine the winner of the game.
  • How to implement the main game loop to allow players to take turns and interact with the game.

🏆 Achievements

After completing this project, you will be able to:

  • Create and manipulate arrays in C.
  • Use loops and conditionals to implement game logic.
  • Interact with the user through the command line interface.
  • Organize code into functions for better modularity and readability.

Create the Project Files

First, create a new file named tictactoe.c and open it in your preferred code editor.

cd ~/project
touch tictactoe.c
âœĻ Check Solution and Practice

Define Constants

Now, we need to write the C code. The first step is to include the header files:

#include <stdio.h>
#include <stdbool.h>

Declare board size:

char board[3][3];
âœĻ Check Solution and Practice

Initialize the Game Board

Initialize the game board with empty spaces.

void initializeBoard() {
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            board[i][j] = ' ';
        }
    }
}

The first for is an outer loop that uses the variable i to iterate from 0 to 2, representing the number of rows on the game board. The second for is an inner loop that uses the variable j to iterate from 0 to 2, representing the number of columns on the game board in turn.

board[i][j] = ' 'In the inner loop, sets the contents of the squares in row i and column j of the game board to the space character (' '). This means that the entire game board is initialized to an empty state, with no players playing on the board.

âœĻ Check Solution and Practice

Clear the Screen After Each Turn

Implement a function to clear the screen after each turn.

void clearScreen() {
    printf("\033[H\033[J");
}

The clearScreen function is primarily used to clear the contents of the terminal or console screen to clear the previous game state and output at the end of each turn. This is done by printing a special escape sequence to a standard output stream (usually a terminal window) that clears the text content on the screen.

printf("\033[H\033[J") uses ANSI escape sequences::

  • \033 is the octal representation of the ASCII escape character, which represents the beginning of the sequence.
  • [H indicates moving the cursor to the top left corner of the screen, equivalent to positioning the cursor to the first row and first column of the screen.
  • [J stands for clear screen. It will clear all text content after the cursor position, including previous game state and output.

This means that each time the clearScreen function is called, it will clear the contents of the current terminal or console screen in order to display the new game state before the next game session, resulting in a cleaner interface.

âœĻ Check Solution and Practice

Display the Game Board

Create the printBoard function to visually display the state of the current game board on the screen, allowing the player to understand the position of pieces on the board. This is a part of the game used in tic-tac-toe games to show the state of the game.

void printBoard() {
    printf("\n");
    printf("  1   2   3\n");
    printf("1 %c | %c | %c\n", board[0][0], board[0][1], board[0][2]);
    printf("  ---------\n");
    printf("2 %c | %c | %c\n", board[1][0], board[1][1], board[1][2]);
    printf("  ---------\n");
    printf("3 %c | %c | %c\n", board[2][0], board[2][1], board[2][2]);
}
âœĻ Check Solution and Practice

Check if the Game is Over

Implement a function called isGameOver is defined to determine whether the tic-tac-toe game has ended and return a Boolean value (true or false).

bool isGameOver() {
    // Check rows
    for (int i = 0; i < 3; i++) {
        if (board[i][0] != ' ' && board[i][0] == board[i][1] && board[i][0] == board[i][2]) {
            return true;
        }
    }

    // Check columns
    for (int j = 0; j < 3; j++) {
        if (board[0][j] != ' ' && board[0][j] == board[1][j] && board[0][j] == board[2][j]) {
            return true;
        }
    }

    // Check diagonals
    if (board[0][0] != ' ' && board[0][0] == board[1][1] && board[0][0] == board[2][2]) {
        return true;
    }
    if (board[0][2] != ' ' && board[0][2] == board[1][1] && board[0][2] == board[2][0]) {
        return true;
    }

    // Check for a draw
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            if (board[i][j] == ' ') {
                return false;
            }
        }
    }

    return true;
}

Check if a row has a winner: If all three cells in a row are occupied by the same player, there is a winner and the function returns true.

Check if a column has a winner: If all three cells in a column are occupied by the same player, there is a winner and the function returns true.

Check if there is a winner on the diagonal: If all three squares on any diagonal are occupied by the same player, there is a winner and the function returns true.

Check for a draw: Finally, the function checks if all cells have been occupied, but there is no winner. If this is the case, the game is a draw and the function returns true.

âœĻ Check Solution and Practice

Determine the Winner

A function named getWinner is defined whose purpose is to determine whether there is a winner in a game of tic-tac-toe and return the winner's flag (X or O), or a space (' ') if there is no winner.

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

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

    // Check diagonals
    if (board[0][0] != ' ' && board[0][0] == board[1][1] && board[0][0] == board[2][2]) {
        return board[0][0];
    }
    if (board[0][2] != ' ' && board[0][2] == board[1][1] && board[0][2] == board[2][0]) {
        return board[0][2];
    }

    return ' '; // No winner
}
âœĻ Check Solution and Practice

Implement the Main Game Loop

Write the main game loop to allow players to take turns and interact with the game.

int main() {
    initializeBoard();
    int currentPlayer = 1;

    while (1) {
        clearScreen();
        printf("Current board state:\n");
        printBoard();

        int row, col;
        printf("Player %d, please enter a row and column (e.g., 1 2):", currentPlayer);

        while (scanf("%d %d", &row, &col) != 2) {
            printf("Invalid input, please try again: ");
            while (getchar() != '\n');
        }

        if (row < 1 || row > 3 || col < 1 || col > 3 || board[row - 1][col - 1] != ' ') {
            printf("Invalid move, please try again.\n");
        } else {
            if (currentPlayer == 1) {
                board[row - 1][col - 1] = 'X';
                currentPlayer = 2;
            } else {
                board[row - 1][col - 1] = 'O';
                currentPlayer = 1;
            }
        }

        if (isGameOver()) {
            clearScreen();
            printf("Game over!\n");
            printBoard();
            char winner = getWinner();
            if (winner != ' ') {
                printf("Player %c wins!\n", winner);
            } else {
                printf("It's a draw!\n");
            }
            break;
        }
    }

    return 0;
}

Call the initializeBoard function to initialize the game board, setting all squares to Spaces.

Create an integer variable currentPlayer, to keep track of whose turn it is. The initial setting is 1, indicating player 1.

In the while (1) main loop:

  • Call the clearScreen function to clear the screen so that the display is refreshed after each turn.
  • Call the printBoard function to print the current game board.

Player input: Prompts the current player to enter row and column coordinates via the scanf function, e.g. Player 1, please enter a row and column (e.g., 1 2): . If the input is Invalid (not two integers), the message Invalid input, please try again: is displayed and the input buffer is cleared until a valid input is obtained.

Input validation: Then, check that the entered row and column coordinates are valid (on a scale of 1 to 3) and that the selected position is a space. If the input is Invalid or the selected location is already occupied, the message Invalid move, please try again. is displayed and the player is asked to re-enter.

Chess: If the input is valid, the code places an X or an O at the corresponding position on the game board based on the current player (1 or 2).

Game end detection: The code then calls the isGameOver() function to check if the game is over. If the game ends, the screen clears and a message that the game is over is displayed, including a winner (if any) or a draw.

Game result: If there is a winner, the code will say Player X wins! Or Player O wins! , depending on which player the winner is. If there is no winner, the code will say It's a draw! Indicates a tie.

âœĻ Check Solution and Practice

Compile and Run the Project

Execute the gcc command to compile:

cd ~/project
gcc -o tictactoe tictactoe.c
./tictactoe
Tic-Tac-Toe
âœĻ Check Solution and Practice

Summary

Congratulations! You have successfully created a simple Tic-Tac-Toe game in C. Players can take turns and mark their positions on the 3x3 grid. The game checks for a winner or a draw after each turn and displays the result accordingly. This simple project demonstrates the basic concepts of C programming and can serve as a starting point for more complex game development.

Other C Tutorials you may like