feat(engine): shouldAdvanceTurn hook + HalfMovesThisTurn fact
Phase A.3 of the rule-variants epic — preset-controlled turn flip gating. Enables double-move (white plays 2, then black plays 2) and monster (scope-aware: white plays 2, black plays 1) without engine changes at the preset layer. - Adds HalfMovesThisTurn: number to ChessAttrMap. Seeded to 0 in applyLayout. Distinct from HalfmoveClock (FIDE 50-move rule) — this counter is within-turn and resets on flip. - Adds TurnAdvanceContext + shouldAdvanceTurn hook to PresetDef. Hook receives post-increment count via ctx.halfMovesThisTurn so 'play N half-moves before flipping' reads as the predicate 'halfMovesThisTurn < N -> false'. - engine.applyMove: increments HalfMovesThisTurn BEFORE polling shouldAdvanceTurn; first false wins; on flip resets to 0 and increments FullmoveNumber after black; onTurnStart is gated on shouldAdvance (vetoed flips don't fire turn-start hooks, so mid-turn stamina regen bugs are prevented by construction). - starting-position.test.ts updated with HalfMovesThisTurn=0 seed assertion. - New turn-advance.test.ts (7 tests): seeding, baseline unchanged, never-flip veto, flip-after-two (double-move surface), onTurnStart gating across vetoed + non-vetoed flips, FullmoveNumber increments only on actual flips after black. Tests: 1441 passing (was 1434, +7 new). Blocks: unblocks B.2 (double-move), B.3 (monster-rules), B.4 (first-promotion-wins).
This commit is contained in:
parent
db8145fb97
commit
f9475e9739
7 changed files with 395 additions and 12 deletions
|
|
@ -153,3 +153,44 @@ section).
|
|||
would be a visible diff. Keeping enforcement out for now
|
||||
because the 1 call site per legal-move request is hot-path-
|
||||
adjacent and a `Set`-based prune would show up.
|
||||
|
||||
## [2026-04-20 20:13] Task: A.3 — shouldAdvanceTurn hook + HalfMovesThisTurn fact
|
||||
|
||||
**Shipped**:
|
||||
- `HalfMovesThisTurn: number` added to `ChessAttrMap`
|
||||
(`packages/chess/src/schema.ts`). Seeded to 0 alongside `Turn` in
|
||||
`applyLayout` (`packages/chess/src/starting-position.ts`).
|
||||
- `TurnAdvanceContext` type + `shouldAdvanceTurn` hook on
|
||||
`PresetDef`. Hook receives post-increment count. First `false`
|
||||
wins; iteration stops.
|
||||
- `engine.applyMove`:
|
||||
1. Increments `HalfMovesThisTurn` BEFORE polling (so hooks see
|
||||
count including the move just committed).
|
||||
2. Polls every active preset's `shouldAdvanceTurn`; first `false`
|
||||
cancels the flip.
|
||||
3. On flip: updates `Turn`, resets `HalfMovesThisTurn` to 0,
|
||||
increments `FullmoveNumber` after black.
|
||||
4. On skip: leaves `Turn` and `HalfMovesThisTurn` alone (count
|
||||
accumulates).
|
||||
5. `onTurnStart` is GATED on `shouldAdvance === true`. Vetoed
|
||||
flips don't fire turn-start (so double-move stamina doesn't
|
||||
regen mid-turn).
|
||||
- `starting-position.test.ts` updated to assert the new fact seed.
|
||||
- New `turn-advance.test.ts` — 7 tests: seeding, baseline unchanged,
|
||||
never-flip veto accumulates count, flip-after-two double-move
|
||||
surface, `onTurnStart` gating against vetoes, FullmoveNumber only
|
||||
on real flips.
|
||||
|
||||
**Verification**: `bun run check` green. 1441 tests (was 1434, +7).
|
||||
|
||||
**Gotchas**:
|
||||
- Two DISTINCT half-move counters now exist. `HalfmoveClock`
|
||||
(FIDE 50-move rule) vs `HalfMovesThisTurn` (this epic's
|
||||
within-turn counter). Documented in schema comment; hook docs
|
||||
emphasize the distinction.
|
||||
- `tickAfterMove(color)` runs on the mover's color regardless of
|
||||
flip decision — correct: a 3-turn buff ticks per actual move
|
||||
made, not per Turn flip. Matches existing semantics.
|
||||
- `session.allFacts()` automatically carries the new fact across
|
||||
serialization boundaries (snapshot/restore) without protocol
|
||||
changes — no server-side work needed for the A.3 addition.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue