Tic Tac Toe: Low Level Design
Last Updated :
18 Sep, 2025
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 ToeThe 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
What is System Design
System Design Fundamentals
Scalability in System Design
Databases in Designing Systems
High Level Design(HLD)
Low Level Design(LLD)
Design Patterns
Interview Guide for System Design
System Design Interview Questions & Answers