Problem statement
Design the object model and core APIs for a two-player Tic-Tac-Toe game engine that a single process runs to drive a match from empty board to a decided outcome. The engine owns the rules; whatever sits on top of it (a console loop, a GUI, a test harness) merely feeds moves in and reads state back.
Operating context. One match at a time, two players who alternate placing their mark on a 3x3 grid. A cell holds at most one mark and is never overwritten. A move is legal only when it is the current player's turn and the target cell is empty and the game is still running. The match ends the moment a player fills a full line (row, column, or either diagonal) with their mark, or in a draw when every cell is filled with no such line. All input arrives as (player, row, column) calls; there is no clock, no network, and no concurrent access — the engine is driven by one caller on one thread.
Out of scope. Rendering / drawing the board, any input parsing or console I/O, an AI opponent or move-search, persistence or save/resume, and networking or matchmaking. Design only the in-process rules engine and its object model, not the UI or transport around it.
What to produce. The class hierarchy (entities such as Board, Cell, Player, Move, Game, and any value objects like Mark or Position), the public API each class exposes, and the state transitions for the game (e.g. not-started, in-progress, won, drawn) and for a cell (empty, marked). Be explicit about: how illegal moves are rejected without corrupting state, how a win or draw is detected after each move, how turn ownership is tracked and enforced, and how the same rules core would extend to an NxN board (win = a full line of N) without rewriting move handling.
Functional requirements
- Apply a move given (player, row, column): validate it, place the mark, and advance the turn to the other player.
- Reject an illegal move (wrong turn, occupied or out-of-range cell, or a game that has already ended) without mutating the board or turn.
- Detect a win immediately after a move when the mover completes a full row, column, or diagonal.
- Detect a draw when every cell is filled and no line has been completed.
- Expose read-only queries for the current board state, whose turn it is, and the match outcome so far.
Non-functional requirements
- Win/draw detection after a move runs in O(N) for an NxN board by checking only the affected row, column, and diagonals, not the whole grid.
- Illegal-move rejection is total: every rejected call leaves board, turn, and status exactly as before (no partial mutation).
- The rules core is testable with no I/O, no wall clock, and no rendering layer — moves in, state out.
- The board size is a single parameter: supporting NxN (win = a line of N) must not change the move-application or turn logic.
- The win-condition check is pluggable enough that a variant rule (e.g. a different winning line set) swaps in without touching Move or Board mutation code.
- Public state is immutable to callers: reading the board or outcome cannot let a caller bypass validation and alter a cell.
Topics
- System Design LLD
- Oop Solid
- Statemachine
- Patterns Strategy
- Extensibility