Open In App

Tic Tac Toe: Low Level Design

Last Updated : 18 Sep, 2025
Comments
Improve
Suggest changes
4 Likes
Like
Report

Tic Tac Toe is a simple yet strategic game that has entertained players for generations. Its low-level design showcases how basic rules, structured logic, and player interactions come together to create an engaging gameplay experience.

Let's understand this with the help of Diagram:

tic_tac_toe
Tic Tac Toe

The Essence of Tic Tac Toe

Tic Tac Toe, or "Xs and Os," is a two-player game on a 3x3 grid where the goal is to align three marks horizontally, vertically, or diagonally. Simple in rules yet rich in strategy, its charm lies in the balance of clarity and challenge.

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.

C++
# Initialize an empty 3x3 Tic Tac Toe board
board = [[' ' for _ in range(3)] for _ in range(3)]
Java
/* Initialize an empty 3x3 Tic Tac Toe board */
String[][] board = new String[3][3];
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        board[i][j] = " ";
    }
}
Python
# Initialize an empty 3x3 Tic Tac Toe board
board = [[' ' for _ in range(3)] for _ in range(3)]
JavaScript
// Initialize an empty 3x3 Tic Tac Toe board
let board = Array.from({ length: 3 }, () => Array(3).fill(' '));

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.

C++
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'
Java
int current_player = 1;  // Player 1 starts

int row = 1;
int column = 2;

// Placing the current player's mark on the board
char[][] board = new char[3][3];
board[row][column] = (current_player == 1) ? 'X' : 'O';
Python
current_player = 1  # Player 1 starts

row = 1
column = 2

# Placing the current player's mark on the board
board = [[' ' for _ in range(3)] for _ in range(3)]
board[row][column] = 'X' if current_player == 1 else 'O'
JavaScript
let current_player = 1;  // Player 1 starts

let row = 1;
let column = 2;

// Placing the current player's mark on the board
let board = Array.from({ length: 3 }, () => Array(3).fill(' '));
board[row][column] = current_player === 1 ? 'X' : '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.

C++
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.")
Java
public class TicTacToe {
    private static final int SIZE = 3;
    private static char[][] board = new char[SIZE][SIZE];

    public static boolean isValidMove(int row, int column) {
        return 0 <= row && row < SIZE && 0 <= column && column < SIZE && board[row][column] == ' ';
    }

    public static void main(String[] args) {
        if (isValidMove(1, 2)) {
            // Perform the move
        } else {
            System.out.println("Invalid move. Please choose an empty cell within the board.");
        }
    }
}
Python
def is_valid_move(row, column):
    board = [[' ' for _ in range(3)] for _ in range(3)]
    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.")
JavaScript
const board = Array.from({ length: 3 }, () => Array.from({ length: 3 }, () => ' '));

function isValidMove(row, column) {
    return 0 <= row && row < 3 && 0 <= column && column < 3 && board[row][column] === ' ';
}

// Example usage
if (isValidMove(1, 2)) {
    // Perform the move
} else {
    console.log('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.

C++
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!")
Java
public boolean checkWinner(int player) {
    // Check rows, columns, and diagonals for three marks in a row
    // Return true if the player wins, false otherwise
}
Python
def check_winner(player):
    # Check rows, columns, and diagonals for three marks in a row
    # Return True if the player wins, False otherwise
    pass
JavaScript
function checkWinner(player) {
    // Check rows, columns, and diagonals for three marks in a row
    // Return true if the player wins, false otherwise
}

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

Here the Driver code of Tic-Tac-Toe:

C++
#include <iostream>
#include <vector>
using namespace std;

class TicTacToe {
private:
    vector<vector<char>> board;
    int currentPlayer;

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 (auto &row : board)
            for (char cell : row)
                if (cell == ' ') return false;
        return true;
    }

    bool makeMove(int row, int column) {
        if (row < 0 || row >= 3 || column < 0 || column >= 3 || board[row][column] != ' ')
            return false;
        board[row][column] = (currentPlayer == 1) ? 'X' : 'O';
        currentPlayer = 3 - currentPlayer;
        return true;
    }

    bool checkWinner() {
        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;
            if (board[0][i] != ' ' && board[0][i] == board[1][i] && board[1][i] == board[2][i])
                return true;
        }
        if (board[0][0] != ' ' && board[0][0] == board[1][1] && board[1][1] == board[2][2])
            return true;
        if (board[0][2] != ' ' && board[0][2] == board[1][1] && board[1][1] == board[2][0])
            return true;
        return false;
    }

    int getCurrentPlayer() { return currentPlayer; }
};

int main() {
    TicTacToe game;

    // Predefined sequence of moves (row, col)
    vector<pair<int,int>> moves = {
        {0,0}, {1,1}, {0,1}, {1,0}, {0,2}  // Player 1 wins
    };

    for (auto move : moves) {
        game.printBoard();
        cout << "Player " << game.getCurrentPlayer()
             << " plays (" << move.first << "," << move.second << ")\n";
        game.makeMove(move.first, move.second);
        if (game.checkWinner()) break;
    }

    game.printBoard();
    if (game.checkWinner()) {
        cout << "Player " << (3 - game.getCurrentPlayer()) << " wins!\n";
    } else {
        cout << "It's a draw!\n";
    }
    return 0;
}
Java
class TicTacToe {
    private char[][] board;
    private int currentPlayer;

    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 makeMove(int row, int col) {
        if (row < 0 || row >= 3 || col < 0 || col >= 3 || board[row][col] != ' ')
            return false;
        board[row][col] = (currentPlayer == 1) ? 'X' : 'O';
        currentPlayer = 3 - currentPlayer;
        return true;
    }

    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 checkWinner() {
        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;
            if (board[0][i] != ' ' && board[0][i] == board[1][i] && board[1][i] == board[2][i])
                return true;
        }
        if (board[0][0] != ' ' && board[0][0] == board[1][1] && board[1][1] == board[2][2])
            return true;
        if (board[0][2] != ' ' && board[0][2] == board[1][1] && board[1][1] == board[2][0])
            return true;
        return false;
    }

    public int getCurrentPlayer() {
        return currentPlayer;
    }
}

public class Main {
    public static void main(String[] args) {
        TicTacToe game = new TicTacToe();

        // Predefined sequence of moves
        int[][] moves = {
            {0, 0}, {1, 1}, {0, 1}, {1, 0}, {0, 2} // Player 1 wins
        };

        for (int[] move : moves) {
            game.printBoard();
            System.out.println("Player " + game.getCurrentPlayer() +
                               " plays (" + move[0] + "," + move[1] + ")");
            game.makeMove(move[0], move[1]);
            if (game.checkWinner()) break;
        }

        game.printBoard();
        if (game.checkWinner()) {
            System.out.println("Player " + (3 - game.getCurrentPlayer()) + " wins!");
        } else {
            System.out.println("It's a draw!");
        }
    }
}
Python
class TicTacToe:
    def __init__(self):
        self.board = [[' ' for _ in range(3)] for _ in range(3)]
        self.current_player = 1

    def print_board(self):
        for i in range(3):
            print(" | ".join(self.board[i]))
            if i < 2:
                print("---------")

    def make_move(self, row, col):
        if row < 0 or row >= 3 or col < 0 or col >= 3 or self.board[row][col] != ' ':
            return False
        self.board[row][col] = 'X' if self.current_player == 1 else 'O'
        self.current_player = 3 - self.current_player
        return True

    def is_board_full(self):
        return all(cell != ' ' for row in self.board for cell in row)

    def check_winner(self):
        for i in range(3):
            if self.board[i][0] != ' ' and self.board[i][0] == self.board[i][1] == self.board[i][2]:
                return True
            if self.board[0][i] != ' ' and self.board[0][i] == self.board[1][i] == self.board[2][i]:
                return True
        if self.board[0][0] != ' ' and self.board[0][0] == self.board[1][1] == self.board[2][2]:
            return True
        if self.board[0][2] != ' ' and self.board[0][2] == self.board[1][1] == self.board[2][0]:
            return True
        return False

    def get_current_player(self):
        return self.current_player


def main():
    game = TicTacToe()

    # Predefined moves (Player 1 wins)
    moves = [(0, 0), (1, 1), (0, 1), (1, 0), (0, 2)]

    for row, col in moves:
        game.print_board()
        print(f"Player {game.get_current_player()} plays ({row},{col})")
        game.make_move(row, col)
        if game.check_winner():
            break

    game.print_board()
    if game.check_winner():
        print(f"Player {3 - game.get_current_player()} wins!")
    else:
        print("It's a draw!")


if __name__ == "__main__":
    main()
JavaScript
class TicTacToe {
  constructor() {
    this.board = Array.from({ length: 3 }, () => Array(3).fill(" "));
    this.currentPlayer = 1; // 1 = Player 1, 2 = Player 2
  }

  printBoard() {
    for (let i = 0; i < 3; i++) {
      console.log(this.board[i].join(" | "));
      if (i < 2) console.log("---------");
    }
  }

  isBoardFull() {
    return this.board.every(row => row.every(cell => cell !== " "));
  }

  makeMove(row, col) {
    if (
      row < 0 || row >= 3 ||
      col < 0 || col >= 3 ||
      this.board[row][col] !== " "
    ) {
      return false;
    }
    this.board[row][col] = this.currentPlayer === 1 ? "X" : "O";
    this.currentPlayer = 3 - this.currentPlayer; // switch player
    return true;
  }

  checkWinner() {
    // Rows & Columns
    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;

      if (
        this.board[0][i] !== " " &&
        this.board[0][i] === this.board[1][i] &&
        this.board[1][i] === this.board[2][i]
      ) return true;
    }
    // Diagonals
    if (
      this.board[0][0] !== " " &&
      this.board[0][0] === this.board[1][1] &&
      this.board[1][1] === this.board[2][2]
    ) return true;

    if (
      this.board[0][2] !== " " &&
      this.board[0][2] === this.board[1][1] &&
      this.board[1][1] === this.board[2][0]
    ) return true;

    return false;
  }

  getCurrentPlayer() {
    return this.currentPlayer;
  }
}

// Example with readline (Node.js)
const readline = require("readline").createInterface({
  input: process.stdin,
  output: process.stdout
});

const game = new TicTacToe();

function playTurn() {
  if (game.isBoardFull() || game.checkWinner()) {
    game.printBoard();
    if (game.checkWinner()) {
      console.log(`Player ${3 - game.getCurrentPlayer()} wins!`);
    } else {
      console.log("It's a draw!");
    }
    readline.close();
    return;
  }

  game.printBoard();
  readline.question(`Player ${game.getCurrentPlayer()}, enter row and column (e.g. "0 1"): `, input => {
    const [row, col] = input.split(" ").map(Number);
    if (game.makeMove(row, col)) {
      console.log("Move successful!\n");
    } else {
      console.log("Invalid move. Try again.\n");
    }
    playTurn();
  });
}

playTurn();



Output:


Explore