Free preview

Why this matters: this problem's design act is graded on placement, not size. There are only four pieces — board, movement rule, die, game loop — and interviewers watch where each rule lands. Chains belong to the board; overshoot belongs to a swappable rule; randomness hides behind an interface so tests can control it. A candidate who puts all four rules in one playTurn method has written the same program with none of the design.

The model in words

Say it before you draw it: "The board owns the geometry — size, jumps, and the resolution of a landing cell through any chain of jumps. The movement rule — what happens on overshoot — is its own small strategy, because it's a house rule with variants. The die is an interface so the game is testable. The game loop owns turn order and win detection, and orchestrates the other three without containing any rule itself."

Four owners, one rule each. Here is each decision earning its place.

The board: geometry plus resolution

The board holds the size and a single map of jumps — a snake and a ladder are the same shape, a (start → end) pair; only the direction differs. That observation collapses two config lists into one mechanism and is worth saying out loud.

The board's real job is resolution. Because chains are allowed, "where do I end up if I land on cell c?" is a loop:

resolve(c):
    while jumps contains c:
        c = jumps[c]
    return c

Putting resolve on the board — rather than having the game loop peek into the jump map — is cohesion: the data and the only nontrivial operation on it live together, and the chain rule exists in exactly one place. If chains were ever disallowed, one method changes.

Resolution as written can loop forever on a cyclic configuration (two jumps forming a ring). That's not a movement problem — it's a validity problem, which is the next decision.

Validation at the door, a guard in play

A configured board can be invalid: a jump starting or ending on cell 1 or the final cell, two jumps sharing a start cell, a cycle. The clean design validates at construction — a board that constructs is a board whose invariants hold, and nothing downstream ever re-checks them. Stating that principle ("validate at the boundary, then trust the object") earns more than the validation code itself.

The defensive complement: resolve still bounds its loop (no more than jumps.size() hops is a natural limit) and fails loudly if exceeded. Not because a validated board can cycle — it can't — but because a guard that converts "impossible" into a crisp error is cheap insurance against the validator and resolver drifting apart under future edits.

The overshoot rule: small, swappable, named

Overshoot — stay put in v1 — is deliberately not an if inside the turn logic. It's a tiny strategy: given (current position, roll, board size), produce the target position, or no move. Lesson 01 flagged why: it's a house rule with known variants, which is precisely the profile of logic that earns a seam. This is open/closed applied with judgment — one interface, one v1 implementation, no speculative zoo of rule classes. The variant table from the requirements conversation is the evidence; the strategy is the response.

The die: an interface, because of tests

A die is one method returning 1–6. The reason it's an interface is not ceremony — it's that this game cannot be tested through a real die. Reaching a snake, a chain, an exact win requires specific rolls; the test needs a die that produces a script. Design for that on day one and every rule above becomes deterministically testable. This is the problem's quiet dependency-inversion moment, and interviewers specifically look for whether randomness is injectable or welded in.

The game loop: orchestration only

Players are position + identity. The loop cycles them in fixed order; a turn is: roll, ask the movement rule for the target, ask the board to resolve it, update the player, check for the win. Note what the loop doesn't know: how chains work, what overshoot does, how the die behaves. Every rule was placed elsewhere; the loop is pure sequencing. That's separation of concerns with a test: if any rule changes, the loop's diff is empty.

Invariants worth stating

- every player's position is 0 or a plain cell: never mid-jump,
  never past the final cell
- resolve() terminates: validated boards are acyclic, and the
  in-play guard bounds the loop regardless
- turn order is fixed; exactly one player moves per turn
- the game ends the moment any player reaches the final cell

The first invariant is the design's heart: positions are always post-resolution. No stored state ever sits on a snake's head.

What we rejected

Cell objects with behavior. A board of 100 objects where snake-cells "act on" arriving players is heavy machinery for what is, in v1, a lookup table with a loop. The map earns the simpler model; the object-per-cell design is the classic over-engineering trap in this problem.

Rules inlined in the loop. Overshoot as an if, chain-following in playTurn, Random called directly — the same behavior, but every future variation lands as an edit to the one method everything flows through, and none of it is testable in isolation.

Key takeaway

Four owners, one rule each: the board owns jumps and their chain-following resolution (validated at construction, guarded in play), the overshoot rule is a small swappable strategy because it's a house rule with variants, the die is an interface because the game is untestable through real randomness, and the game loop is pure orchestration that would show an empty diff if any rule changed. Placement — not size — is what this design 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