Open In App

Low Level Design of Tic Tac Toe | System Design

Last Updated : 19 Oct, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

In classic games, few are as universally recognized and enjoyed as Tic Tac Toe. Despite its apparent simplicity, this game engages players of all ages in a battle of wits and strategy. In this article, we delve into the intricacies of designing a low-level structure for Tic Tac Toe that captures the essence of the game and highlights the underlying complexities.

The Essence of Tic Tac Toe

Tic Tac Toe, known colloquially as “Xs and Os,” is a two-player game typically played on a 3×3 grid. The objective is simple: be the first to form a horizontal, vertical, or diagonal line of three of your marks (either “X” or “O”). The elegance of the game lies in its deceptive complexity, while the rules are straightforward, devising an unbeatable strategy demands a keen understanding of the game’s dynamics.

Rules of the game

Firstly let’s understand the rules of the game:

  • Setup: The game is played on a 3 * 3 grid. One player uses ‘X’ another player uses ‘O’ and each player takes turns making their moves.
  • Winner: The game is won by the player placing his or her symbol in a row, column, or diagonal. The first player to get three symbols in a row wins the game. When the player reaches this, the game ends immediately.
  • Draw: If all the grid cells are filled and no player has three symbols in a row, the game will be a tie or a draw.
  • Illegal Moves: A player cannot place his or her symbol on a tile occupied by an opponent’s symbol or their own symbol. The move must be made to an empty cell.

Representing the Game Board

At the heart of every game lies the game board. In our low-level design for Tic Tac Toe, we choose a 2D array or matrix to represent the board. Each cell of the array can take three possible values: empty, “X,” or “O.” This array forms the canvas upon which players will make their moves.

Python3




# Initialize an empty 3x3 Tic Tac Toe board
board = [[' ' for _ in range(3)] for _ in range(3)]


Player Turns and Moves

In any game, managing player turns is crucial. We can represent the players using simple integers – player 1 and player 2. Their moves are translated to row and column indices on the game board.

Python3




current_player = 1  # Player 1 starts
 
row = 1
column = 2
 
# Placing the current player's mark on the board
board[row][column] = 'X' if current_player == 1 else 'O'


Validating Moves

Ensuring that players adhere to the rules is paramount. The low-level design includes a move validation process, verifying that the chosen cell is empty and within the bounds of the board.

Python3




def is_valid_move(row, column):
    return 0 <= row < 3 and 0 <= column < 3 and board[row][column] == ' '
 
# Example usage
if is_valid_move(1, 2):
    # Perform the move
else:
    print("Invalid move. Please choose an empty cell within the board.")


Determining the Winner

The climax of any Tic Tac Toe game is the declaration of a winner. Our low-level design accommodates a function to check for victory conditions after each move.

Python3




def check_winner(player):
    # Check rows, columns, and diagonals for three marks in a row
    # Return True if the player wins, False otherwise
 
# Example usage
if check_winner(1):
    print("Player 1 wins!")
elif check_winner(2):
    print("Player 2 wins!")


Tying It All Together

Tic Tac Toe’s low-level design is a harmonious interplay of game mechanics, data structures, and decision-making logic. From managing the game board to validating moves and determining victory, every element contributes to the immersive experience of the game.

So, the next time you engage in a friendly match of Tic Tac Toe, remember that behind the Xs and Os lies a thoughtfully crafted low-level design that brings a timeless game to life. As you strategize to outwit your opponent, you’re also experiencing the result of careful planning and design – a testament to the enduring appeal of this classic pastime.

Complete Tic Tac Toe implementation

C++




#include <iostream>
#include <vector>
 
using namespace std;
 
class TicTacToe {
private:
    vector<vector<char> > board; // 3x3 game board
    int currentPlayer; // 1 for Player 1, 2 for Player 2
 
public:
    TicTacToe()
    {
        board = vector<vector<char> >(3,
                                      vector<char>(3, ' '));
        currentPlayer = 1;
    }
 
    void printBoard()
    {
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                cout << board[i][j];
                if (j < 2) {
                    cout << " | ";
                }
            }
            cout << endl;
            if (i < 2) {
                cout << "---------" << endl;
            }
        }
    }
 
    bool isBoardFull()
    {
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                if (board[i][j] == ' ') {
                    return false;
                }
            }
        }
        return true;
    }
 
    bool makeMove(int row, int column)
    {
        if (row < 0 || row >= 3 || column < 0 || column >= 3
            || board[row][column] != ' ') {
            return false; // Invalid move
        }
        board[row][column]
            = (currentPlayer == 1) ? 'X' : 'O';
        currentPlayer
            = 3 - currentPlayer; // Switch player (1 to 2 or
                                 // 2 to 1)
        return true;
    }
 
    bool checkWinner()
    {
        // Check rows, columns, and diagonals for a win
        for (int i = 0; i < 3; ++i) {
            if (board[i][0] != ' '
                && board[i][0] == board[i][1]
                && board[i][1] == board[i][2]) {
                return true; // Row win
            }
            if (board[0][i] != ' '
                && board[0][i] == board[1][i]
                && board[1][i] == board[2][i]) {
                return true; // Column win
            }
        }
        if (board[0][0] != ' ' && board[0][0] == board[1][1]
            && board[1][1] == board[2][2]) {
            return true; // Diagonal win
        }
        if (board[0][2] != ' ' && board[0][2] == board[1][1]
            && board[1][1] == board[2][0]) {
            return true; // Diagonal win
        }
        return false;
    }
};
 
int main()
{
    TicTacToe game;
    int row, column;
 
    while (!game.isBoardFull() && !game.checkWinner()) {
        game.printBoard();
 
        cout << "Player " << game.getCurrentPlayer()
             << ", enter your move (row and column): ";
        cin >> row >> column;
 
        if (game.makeMove(row, column)) {
            cout << "Move successful!" << endl;
        }
        else {
            cout << "Invalid move. Try again." << endl;
        }
    }
 
    game.printBoard();
 
    if (game.checkWinner()) {
        cout << "Player " << (3 - game.getCurrentPlayer())
             << " wins!" << endl;
    }
    else {
        cout << "It's a draw!" << endl;
    }
 
    return 0;
}


Java




import java.util.Scanner;
 
public class TicTacToe {
    private char[][] board; // 3x3 game board
    private int
        currentPlayer; // 1 for Player 1, 2 for Player 2
 
    public TicTacToe()
    {
        board = new char[3][3];
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                board[i][j] = ' ';
            }
        }
        currentPlayer = 1;
    }
 
    public void printBoard()
    {
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                System.out.print(board[i][j]);
                if (j < 2) {
                    System.out.print(" | ");
                }
            }
            System.out.println();
            if (i < 2) {
                System.out.println("---------");
            }
        }
    }
 
    public boolean isBoardFull()
    {
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                if (board[i][j] == ' ') {
                    return false;
                }
            }
        }
        return true;
    }
 
    public boolean makeMove(int row, int column)
    {
        if (row < 0 || row >= 3 || column < 0 || column >= 3
            || board[row][column] != ' ') {
            return false; // Invalid move
        }
        board[row][column]
            = (currentPlayer == 1) ? 'X' : 'O';
        currentPlayer
            = 3 - currentPlayer; // Switch player (1 to 2 or
                                 // 2 to 1)
        return true;
    }
 
    public boolean checkWinner()
    {
        // Check rows, columns, and diagonals for a win
        for (int i = 0; i < 3; ++i) {
            if (board[i][0] != ' '
                && board[i][0] == board[i][1]
                && board[i][1] == board[i][2]) {
                return true; // Row win
            }
            if (board[0][i] != ' '
                && board[0][i] == board[1][i]
                && board[1][i] == board[2][i]) {
                return true; // Column win
            }
        }
        if (board[0][0] != ' ' && board[0][0] == board[1][1]
            && board[1][1] == board[2][2]) {
            return true; // Diagonal win
        }
        if (board[0][2] != ' ' && board[0][2] == board[1][1]
            && board[1][1] == board[2][0]) {
            return true; // Diagonal win
        }
        return false;
    }
 
    public static void main(String[] args)
    {
        TicTacToe game = new TicTacToe();
        Scanner scanner = new Scanner(System.in);
        int row, column;
 
        while (!game.isBoardFull() && !game.checkWinner()) {
            game.printBoard();
 
            System.out.print(
                "Player " + game.currentPlayer
                + ", enter your move (row and column): ");
            row = scanner.nextInt();
            column = scanner.nextInt();
 
            if (game.makeMove(row, column)) {
                System.out.println("Move successful!");
            }
            else {
                System.out.println(
                    "Invalid move. Try again.");
            }
        }
 
        game.printBoard();
 
        if (game.checkWinner()) {
            System.out.println("Player "
                               + (3 - game.currentPlayer)
                               + " wins!");
        }
        else {
            System.out.println("It's a draw!");
        }
 
        scanner.close();
    }
}


Python3




class TicTacToe:
    def __init__(self):
        self.board = [[' ' for _ in range(3)] for _ in range(3)]
        self.current_player = 1
 
    def print_board(self):
        for row in self.board:
            print('|'.join(row))
            print('-' * 5)
 
    def is_board_full(self):
        return all(cell != ' ' for row in self.board for cell in row)
 
    def make_move(self, row, column):
        if 0 <= row < 3 and 0 <= column < 3 and self.board[row][column] == ' ':
            self.board[row][column] = 'X' if self.current_player == 1 else 'O'
            # Switch player (1 to 2 or 2 to 1)
            self.current_player = 3 - self.current_player
            return True
        return False
 
    def check_winner(self):
        for i in range(3):
            if self.board[i][0] == self.board[i][1] == self.board[i][2] != ' ':
                return True  # Row win
            if self.board[0][i] == self.board[1][i] == self.board[2][i] != ' ':
                return True  # Column win
        if self.board[0][0] == self.board[1][1] == self.board[2][2] != ' ':
            return True  # Diagonal win
        if self.board[0][2] == self.board[1][1] == self.board[2][0] != ' ':
            return True  # Diagonal win
        return False
 
 
# Main game loop
if __name__ == "__main__":
    game = TicTacToe()
 
    while not game.is_board_full() and not game.check_winner():
        game.print_board()
 
        row, column = map(int, input(
            f"Player {game.current_player}, enter your move (row and column): ").split())
 
        if game.make_move(row, column):
            print("Move successful!")
        else:
            print("Invalid move. Try again.")
 
    game.print_board()
 
    if game.check_winner():
        print(f"Player {3 - game.current_player} wins!")
    else:
        print("It's a draw!")


Javascript




class TicTacToe {
    constructor() {
        this.board = Array.from(Array(3), () => Array(3).fill(' ')); // 3x3 game board
        this.currentPlayer = 1; // 1 for Player 1, 2 for Player 2
    }
 
    printBoard() {
        for (let i = 0; i < 3; ++i) {
            for (let j = 0; j < 3; ++j) {
                process.stdout.write(this.board[i][j]);
                if (j < 2) {
                    process.stdout.write(' | ');
                }
            }
            console.log();
            if (i < 2) {
                console.log('---------');
            }
        }
    }
 
    isBoardFull() {
        for (let i = 0; i < 3; ++i) {
            for (let j = 0; j < 3; ++j) {
                if (this.board[i][j] === ' ') {
                    return false;
                }
            }
        }
        return true;
    }
 
    makeMove(row, column) {
        if (
            row < 0 ||
            row >= 3 ||
            column < 0 ||
            column >= 3 ||
            this.board[row][column] !== ' '
        ) {
            return false; // Invalid move
        }
        this.board[row][column] = this.currentPlayer === 1 ? 'X' : 'O';
        this.currentPlayer = 3 - this.currentPlayer; // Switch player (1 to 2 or 2 to 1)
        return true;
    }
 
    checkWinner() {
        // Check rows, columns, and diagonals for a win
        for (let i = 0; i < 3; ++i) {
            if (
                this.board[i][0] !== ' ' &&
                this.board[i][0] === this.board[i][1] &&
                this.board[i][1] === this.board[i][2]
            ) {
                return true; // Row win
            }
            if (
                this.board[0][i] !== ' ' &&
                this.board[0][i] === this.board[1][i] &&
                this.board[1][i] === this.board[2][i]
            ) {
                return true; // Column win
            }
        }
        if (
            this.board[0][0] !== ' ' &&
            this.board[0][0] === this.board[1][1] &&
            this.board[1][1] === this.board[2][2]
        ) {
            return true; // Diagonal win
        }
        if (
            this.board[0][2] !== ' ' &&
            this.board[0][2] === this.board[1][1] &&
            this.board[1][1] === this.board[2][0]
        ) {
            return true; // Diagonal win
        }
        return false;
    }
}
 
function main() {
    const game = new TicTacToe();
    const readline = require('readline-sync');
    let row, column;
 
    while (!game.isBoardFull() && !game.checkWinner()) {
        game.printBoard();
 
        console.log(`Player ${game.currentPlayer}, enter your move (row and column): `);
        row = parseInt(readline.question('Row: '));
        column = parseInt(readline.question('Column: '));
 
        if (game.makeMove(row, column)) {
            console.log('Move successful!');
        } else {
            console.log('Invalid move. Try again.');
        }
    }
 
    game.printBoard();
 
    if (game.checkWinner()) {
        console.log(`Player ${3 - game.currentPlayer} wins!`);
    } else {
        console.log("It's a draw!");
    }
}
 
main();
// This code is contributed by Vikram_Shirsat


Conclusion

The game of Tic Tac Toe, often deemed simplistic, hides layers of intriguing design choices beneath its surface. From crafting the game board to managing turns and deciphering wins, every aspect intertwines to deliver an immersive gaming experience. As we delve into the low-level design using C++, we uncover the harmony between code and gameplay, reminding us that even seemingly uncomplicated games hold within them a realm of thoughtful intricacies.

So the next time you engage in a round of Tic Tac Toe, take a moment to appreciate the elegance of its design—a design that transforms an ordinary grid into a battlefield of strategic brilliance.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads