# Tic-Tac-Toe — decision log

The Phase 5 answer key. Every entry is a question an interviewer actually asks ("why is that a
separate class?") and the answer that earns the mark: **what change the seam anticipates, and what
the change would cost without it.**

Nine classes, none over ~90 lines. `TicTacToe` holds the *sequence* of a move and no rule that a
requirement could reach.

| File | One reason to change |
|---|---|
| `TicTacToe` | the *sequence* of playing a move changes |
| `TicTacToeFactory` | what a standard game is made of changes |
| `Grid` / `StandardGrid` | how the board is stored, or where a requested move actually lands |
| `WinRule` | what "N in a row" means changes (it hasn't, in this corpus — see below) |
| `TurnOrder` | how a turn passes to the next player changes (it hasn't either — see below) |
| `Placement` | what we record about where a mark landed |
| `Demo` | what we show when we run it |

## The measurement, first, because it is the argument

Three requirement changes, each absorbed with the reference's own diff measured by
`server/lib/diff.mjs`:

| Curveball | Where it landed | Measured cost |
|---|---|---|
| a 4x4 board where 4 in a row wins | nowhere — the existing entry point, unmodified | **0 lines** |
| a third player, taking turns in order | one factory overload, delegating from the old one | **6 lines** |
| gravity: a mark drops to the lowest free row | one new file, plus one factory method | **8 lines** |

`TicTacToe` — the class that plays a move, checks for a win, and decides whose turn is next —
was **not opened by any of the three.** That is the claim composition makes here, and it is
measurable rather than aesthetic.

---

## C4 · composition over inheritance, and the design this problem is trying to catch

The tempting wrong design for tic-tac-toe is one of two shapes, and both are worth naming out
loud because an interviewer will ask why neither was chosen:

**A `Game` base class with `TicTacToeGame extends Game`.** There is no second kind of game in
this problem waiting to inherit from a shared base — "game" is not a genus with species here, it
is one concept with three interchangeable parts. Inheritance would answer a question nobody
asked ("what varies *about being a game*?") while leaving the real question — "what varies about
this board, this win rule, this turn order?" — unaddressed. Worse, the moment a second board
shape or turn policy needs a genuinely different sequence of operations (not just different
answers to the same three questions), the base class has to grow a hook for it, and every
subclass inherits a method it did not ask for.

**A `Board extends ArrayList<Mark>`, or some collection subclass standing in for the grid.**
This is the more seductive version, because a board *looks* like a list of cells. It fails for
the reason inheriting from a concrete collection always fails: `ArrayList` brings `add`, `remove`,
`sort`, `subList` and forty other methods that make no sense on a board and that nothing stops a
caller from calling. A board is not a list that happens to hold marks; it is a fixed-size grid
with exactly one legal operation from the outside — place a mark and find out where it landed —
and modelling that as "is a list" instead of "has storage, and answers exactly two questions
about it" is precisely the inheritance-for-code-reuse trap A2/A7 exist to catch.

**What `TicTacToe` actually holds:** a `Grid`, a `WinRule`, and a `TurnOrder` — three fields, not
three superclasses. `TicTacToe implements TicTacToeApi` and nothing else; `StandardGrid
implements Grid` and nothing else. The gravity curveball is the proof this is not merely
stylistic: it swaps the grid for one that lands a mark somewhere other than the cell that was
asked for, and **not one line of `TicTacToe` changes**, because `TicTacToe` never assumed
anything about `Grid` beyond the interface it was given. An inheritance-based design — `TicTacToe
extends StandardBoard` — would have had to either duplicate the whole move-playing sequence into
a `GravityTicTacToe extends GravityBoard`, or retrofit a hook into the base class for a rule
nobody had anticipated. Composition made the third option available for free: a new class that
implements the same three-method interface as the one it replaces.

## Seam 1 · `Grid` — an interface with one implementation in the base problem, backed by a second

**Anticipates:** any board that stores or places marks differently — a board read from a file, a
board with holes in it, and, concretely, a board with gravity.

This is the one interface in the design, and it is worth being honest about why the other two
composed things are not: `Grid` earns the B1 tag because the gravity curveball supplies a real
second implementation, `GravityGrid`, that the reference never anticipated needing to touch
`TicTacToe` or `WinRule` to add. Without the interface, gravity would have meant either a
boolean flag threaded through every method that touches the board, or a subclass of whatever
concrete board class existed — both of which put the decision about *where a mark lands* back
inside the class that is supposed to just ask.

**Why the interface is three methods, not one.** `size()`, `at(row, col)` and `isFull()` are
answered identically by every grid this corpus has; only `place` differs. A narrower interface
with just `place` would have made `TicTacToe` reach around the grid for its size and its cells,
which is the same mistake as a `ShippingPolicy` that took the whole cart instead of the two
numbers it needed — asking for more than the question requires.

## Seams that were *not* made interfaces, stated honestly

**`WinRule` is a plain final class.** Every variant of tic-tac-toe in this corpus — the standard
game, the bigger board, the third player, gravity — plays exactly one win-detection rule: N marks
of the same value in a row, in one of four directions, counted from the cell that was just
played. Nothing in the suggested curveballs, and nothing plausible beyond them, asks for a
*different* way to decide a win; they only ask for different values of N and a different board
shape, both of which `WinRule` already takes as data. Making this a `WinCondition` interface
with one implementation and no second one anywhere in sight would repeat the exact mistake
shopping-cart's decision log calls out for a `StackingRule`: a seam with nothing to show for
itself. If a future requirement wanted a different rule — "any four corners", "no diagonals
count" — *that* is where the interface would go, and the cost of adding it then is one new class
and one changed constructor call in `TicTacToeFactory`, not a rewrite.

**`TurnOrder` is a plain final class for the same reason.** There is one turn-taking policy in
this whole problem: strict round robin over however many distinct marks the game was built with.
The third-player curveball does not need a *different* policy — it needs this same class handed
a list of three marks instead of two, which is composition (a longer list) doing the work a
Strategy interface would have been invented to do without ever getting a second implementation.

**This is the honestly weakest tag in this problem's syllabus.** `C3` (dependency inversion) is
tagged on the strength of `TicTacToe` depending on `Grid`, `WinRule` and `TurnOrder` through its
constructor rather than constructing them itself — real dependency injection, and the mechanism
that lets the gravity curveball substitute a grid without a rewrite. But this problem has no
clock and no source of randomness, so it never gets to make the sharper version of the C3
argument other corpus problems make (injecting a `Dice` because a game that rolls its own is
untestable, or injecting a `Clock` because "now" cannot be a hidden global). Constructor
injection of collaborators that happen to have only one implementation each is a real but
modest instance of the principle, and it is named as such rather than inflated.

## Errors · one state check, one argument check, checked in that order

- **A bad situation is `IllegalStateException`.** Once `status` is not `IN_PROGRESS`, `play`
  throws before it looks at `row` or `col` at all — the same convention other problems in this
  corpus use for "the sku is looked up first, always." A move that is both on an occupied cell
  and made after the game ended is reported as "the game is over," never as "that cell is taken,"
  because the caller's mistake about the game's state is the one worth reporting first.
- **A bad argument is `IllegalArgumentException`.** An occupied cell, a cell off the board, an
  illegal `size`/`winLength` pair at construction. The caller passed something wrong; the game
  was otherwise willing to play.
- **A rejected move costs nothing.** `Grid.place` either succeeds and returns where the mark
  landed, or throws before touching any state at all — there is no partial placement to roll
  back, and `TicTacToe.current` is only ever reassigned after a placement has already succeeded.

## Immutability at the boundary

`GameState` and `MoveResult` are records that validate in their compact constructors: a
`MoveResult` cannot claim a winner other than the mark that just moved, and neither record can
claim `WON` without a winner or claim a winner without `WON`. `GameState.cells()` is
`List.copyOf`, so a caller can read the whole board and change none of it, and a snapshot taken
before a move stays exactly as it was after one — `GameState` does not hold a reference into
`TicTacToe`'s live board, it is built fresh, once, on the way out of `state()`.

## What was deliberately *not* built

- **No `Player` type.** A player is a `Mark`; nothing about a player has state beyond which mark
  it is. Inventing a `Player` class to hold a `Mark` and nothing else would be a wrapper earning
  nothing.
- **No listener or observer.** Nothing in this problem's requirements asks anything to be told
  about a move as it happens — there is no commentary, no move log, no external audience.
  `state()` after the fact is sufficient for everything asked, and a `GameListener` here would be
  the exact speculative seam this corpus's own honesty rule exists to refuse.
- **No locks.** The contract states the grader calls one method at a time. The shared mutable
  state is named rather than guarded: `current`, `status` and `winner` inside `TicTacToe`, and the
  cell array inside whichever `Grid` it holds. A game shared across threads wants a lock on the
  `TicTacToe` instance, not on the grid alone, because `play` reads and writes all three together
  and a caller must never see them mid-update.
- **No board rendering.** `Demo` prints one for its own sake; nothing in `GameState` is shaped
  for display, because nothing in the requirements asked it to be.
- **No sealed hierarchy for `Mark`.** Three players' worth of marks were needed for exactly one
  curveball, added by revising the enum, not by inventing a `sealed interface Player permits X,
  O, Z` up front. Sealing something the base problem never hinted might grow is the same
  over-anticipation this design refuses elsewhere.
