Deep Dive
low level designoopdesign patterns

Designing Tic-Tac-Toe: A Low-Level Design Walkthrough

A complete object-oriented design for Tic-Tac-Toe — a playable board, the full class model (players, board, pluggable rules), an O(1) win check, a codeable interface you can implement, and the patterns that scale it to N×N, K-in-a-row, and bots.

·12 min read

Tic-Tac-Toe is the “hello world” of low-level design interviews — deceptively small, but a great lens for what they’re really testing: can you turn fuzzy requirements into clean, extensible objects without over-engineering?

Before we model anything, play a few rounds. This is the exact system we’re about to design — two players, alternating turns, first to get three in a line wins.

X to move

Nail the requirements first

Before a single class, pin down what “done” means. Splitting functional from non-functional is what separates a design that works from one that scales.

Functional
  • Two players alternate turns
  • Place a mark on an empty cell
  • Reject moves on taken / out-of-range cells
  • Detect a win (row, column, diagonal)
  • Detect a draw (board full, no winner)
Non-functional
  • Board size configurable (3×3 → N×N)
  • Win length K configurable (not always N)
  • Win-detection cheap per move
  • Rules & players pluggable without touching core

Separate the components

The single most important decision is drawing boundaries. Group by responsibility, and every future change lands in exactly one place.

Layer Owns Classes
Entities Value types, identity Symbol, Position, Player
State The mutable grid Board
Rules When is the game won? WinningStrategy (+ implementations)
Orchestration Turn flow, status TicTacToe (the game controller)
I/O Input & rendering console / web / the playable board above
has 2ownsusesTicTacToePlayer«abstract»BoardWinningStrategy«interface»HumanPlayerBotPlayerRowColDiagonalKInARow
Class relationships: one game controller orchestrates Players, a Board, and a pluggable WinningStrategy. Arrows flow from owner to owned; the animated edges show the direction of dependency.

Entities: the vocabulary

Start with the small, dumb value types. They carry no logic — just meaning.

Entities
python

Each class should have one reason to change. is the rule that produced these types — and the whole layering above.

Players — and why they’re a hierarchy

A Player isn’t just a name + a symbol. Making it abstract with a next_move() method is what lets a human and a computer be interchangeable — the game loop never needs to know which is playing.

Player hierarchy
python

State: the Board

The Board owns the grid and nothing else. It knows how to place a mark and report its own shape, but it does not know the rules of winning.

Board
python

Rules: the win check, where design actually matters

The naive approach re-scans every row, column, and both diagonals after each move: O(N²) work per move. Fine for 3×3, wasteful at scale. The elegant move is to track counts incrementally.

Naive: rescan the board
  • O(N²) work every single move
  • Re-reads cells that never changed
  • Simple to write, poor to scale
Incremental counters
  • O(1) per move (amortized O(N))
  • Only the affected line is touched
  • One counter per row, col, diagonal

Behind an interface so the rule is swappable — this is the Strategy pattern:

WinningStrategy
python

Orchestration: the game controller

TicTacToe is the conductor. It holds players and turn order, delegates storage to Board and rule-checking to a WinningStrategy, and drives one turn at a time.

TicTacToe controller
python

The codeable interface

Here’s the whole contract on one screen — the four seams a reader (or an interviewer) can implement against. If you can fill these in, you’ve understood the design:

The codeable interface
python

Wiring it all together is a few lines — note how nothing here knows how a player decides or how winning is computed:

Wiring it together
python

Extensibility, for free

What changes to add…
  • N×N board → Board(size=n)
  • K-in-a-row → new WinningStrategy
  • Computer player → new Player subclass
  • 3+ players → TicTacToe turn logic only
What stays untouched
  • Board (storage is size-agnostic)
  • Player contract (next_move)
  • The game loop (depends on interfaces)
  • Entities (Symbol, Position)

Edge cases worth stating out loud

Calling these out unprompted is a strong signal in an interview:

Practice

Reading a design is easy; reproducing it is where it sticks. Try the challenge, then check yourself.

The key idea: only lines through the just-placed cell can change, so scan outward in the four axes and keep the longest run. Write it in your language below — the interfaces are pre-filled — then reveal the reference. Cost is O(k) per move, and TicTacToe never learns this rule exists: that’s the Strategy pattern paying off.

The best LLD answers aren’t the ones with the most classes — they’re the ones where every class has an obvious job and the design bends exactly where the requirements said it would.

Go Premium

Enjoyed this post?

Unlock every deep-dive on system design & distributed systems, and keep your reading streak alive — from ₹499/month.

View plans

Discussion

Sign in to join the conversation.