Free preview

Why this matters: the two-layer core is the answer to the prompt, but senior candidates get graded on the space around their answer — what the design grows into, what they'd change under different pressures, and how they'd know it still works. This lesson tours the sanctioned neighborhood.

Game-end detection: a free consequence of the design

"Can you detect checkmate?" sounds like a new feature; in this design it is a query over what already exists. Checkmate is: the side to move is in check AND has zero legal moves. Stalemate: not in check AND zero legal moves. Both reduce to one new function — generate every candidate move for the side to move and count how many pass isLegal.

hasAnyLegalMove(p):
  for each own piece, for each candidate its rules accept:
    if isLegal(p, move): return true
  return false

It is honest O(pieces × candidate squares) with a simulation per candidate — dozens of copies per call. Say the cost, then the calibration: at interactive rates, called once per completed move, it is nothing. The design lesson to speak aloud: because legality has one owner, game-end logic composes on top without touching any layer below. That's the separation paying rent a second time.

The optimization path: attack maps

isSquareAttacked scans all 64 squares per query. The next rung is an attack map: after each real move, precompute the set of squares each side attacks, and answer step 4 by lookup. Costs: the map must be maintained on every applied move (including simulations), and incremental maintenance is genuinely tricky — a moved piece changes not only its own attacks but those of every slider whose ray it blocked or unblocked. The honest framing for an interview: name the option, name the maintenance burden, and say what would trigger the investment — validation moving from interactive rates to engine-adjacent workloads (millions of positions per second), which the requirements explicitly did not ask for. Declining an optimization with its trigger stated scores higher than performing it.

Board representations: objects, 0x88, bitboards

The 8×8 object grid optimizes for readability — the right default in a design round. Two variations worth knowing by name:

  • 0x88 layout: a 128-cell one-dimensional board where off-board detection becomes a single bit test (square & 0x88). Kills a whole class of bounds-check code in ray walking; costs some readability.
  • Bitboards: one 64-bit word per piece type per color; attacks become bitwise operations. This is engine territory — spectacular throughput, painful debuggability — and in this round it earns exactly one sentence: "if this validator had to live inside an engine, boards become bitboards, and the movement-rules interface is the seam that lets that happen without touching legality."

That last clause is the point: representation is hidden behind the layers, so swapping it is contained. Open/closed, demonstrated at the storage level.

Undo/redo: the apply-revert pair grows up

A UI wants takebacks. The scratch-copy design extends naturally: keep a stack of applied moves with enough captured-state to revert (the taken piece, at minimum). This is the disciplined apply/revert pair from lesson 03, promoted from an optimization to a feature — and the reason to have mentioned it. A stack of full position copies also works and is simpler; at typical game lengths the memory is trivial. Offer both, pick by simplicity, move on.

Testing: perft, the validator's truth serum

Chess has a canonical test technique worth naming in any round: perft — from a known position, count all legal move sequences to a fixed depth and compare against published reference counts. One wrong rule anywhere and the count diverges. It composes directly from hasAnyLegalMove's generator, and saying "I'd validate the module with perft counts before trusting it" signals you know how correctness is actually established in this domain — tests derived from the design, not bolted on.

Key takeaway

Around the core: game-end detection composes from legal-move existence without touching lower layers; attack maps are the named-but-declined optimization with an explicit trigger; representations from 8×8 objects to bitboards swap behind the movement seam; undo grows from the apply/revert pair; and perft is how this domain proves correctness. Knowing the neighborhood — and the cost of moving into it — is what the trade-offs act grades.

Enjoying the preview?

Create a free account to unlock the rest of this course, the in-browser judge, and live AI mock interviews.

Sign up free to continue