Design Tic-Tac-Toe (LLD)
1. Requirements
-
Functional
- NxN grid (classic 3x3) with two marks alternating; win by line of length matching rule set.
- Reject illegal moves (occupied cell, play after terminal state).
- Expose current player and game outcome: win, draw, in progress.
-
Non-Functional
- O(1) move application; optional O(n) win check on small boards acceptable.
- Extensible to M,N,K generalized tic-tac-toe.
-
Assumptions / Out of Scope
- No AI in core domain; AI via separate player agent.
2. Core Entities
| Entity | Responsibility | Key Attributes |
|---|---|---|
| Game | Controls flow | grid, currentMark, status |
| Board | Cell storage | rows, columns, cells |
| Move | Row and column choice | rowIndex, columnIndex |
| WinChecker | Evaluates lines | winLength |
| Mark | Side enumeration | cross, nought |
3. Class Diagram
Loading diagram…
4. State / Sequence Diagram (where relevant)
Loading diagram…
5. Design Patterns Applied
- State — Game lifecycle (
InProgress,Won,Draw). State pattern. - Null Object — Empty cell representation vs optional pointers. See the design patterns index; Null Object is not a standalone note here.
6. Implementation
Go
package tictactoe
type Mark int
type Board struct {
Rows, Columns int
Cells [][]Mark
}
type Move struct {
RowIndex, ColumnIndex int
}
type Game struct {
Board Board
Current Mark
Status string
WinLength int
}
func (game *Game) PlayMove(move Move) error { /* bounds, occupancy, terminal */ }JavaScript
const Mark = { Empty: 0, Cross: 1, Nought: 2 };
class Board {
constructor({ rows, columns }) {
this.rows = rows;
this.columns = columns;
this.cells = Array.from({ length: rows }, () =>
Array(columns).fill(Mark.Empty)
);
}
}
class Game {
constructor({ rows, columns, winLength }) {
this.board = new Board({ rows, columns });
this.winLength = winLength;
this.currentMark = Mark.Cross;
this.status = 'inProgress';
}
playMove({ rowIndex, columnIndex }) { /* ... */ }
}7. Concurrency / Thread Safety
- Collisions: Two clicks submitting move simultaneously on web UI.
- Granularity: Debounce in UI; server uses per-game lock or actor mailbox.
- Go:
sync.MutexonGameif referenced by multiple handlers.
8. Extensibility & Followups
- Bitmask board for fast win masks on 3x3.
- Generalized K-in-a-row on larger grids with directional sweep from last move only.
- Undo stack via small command list.
- Edge cases: mis-sized winLength greater than board dimensions.
Last updated on
Spotted something unclear or wrong on this page?