Implements king-heals, poisoned-squares, capture-to-win,
last-piece-standing, explosive-rook, and queen-splits via the new
preset hook infrastructure. All six were registered but no-op stubs
with "// Full integration in ChessEngine (P3.11)" comments that
never got followed up.
New hooks on PresetDef
~~~~~~~~~~~~~~~~~~~~~~~
- onAfterMove(engine, moverColor)
Fires after applyMove`s turn switch but before game-result check.
Scope filtering is preset-side because rules have different
notions of "who does the hook target" (king-heals targets the
non-mover; poisoned-squares targets everyone on a poisoned
square).
- onCheckGameResult(engine) -> GameResult | undefined
Lets a preset override the engine`s default terminal-position
logic. First non-undefined return wins in registration order.
GameResult type extended with white-wins / black-wins so variant
rules can name the winner explicitly (checkmate implicitly means
"side to move loses" — insufficient for capture-to-win which can
end mid-move with either side winning).
Preset implementations
~~~~~~~~~~~~~~~~~~~~~~~
- king-heals: onAfterMove heals non-mover`s king +1 HP (max 3) when
not in check. Requires piece-hp.
- poisoned-squares: onAfterMove damages every piece standing on
d4/e4/d5/e5. Retracts pieces hitting 0 HP. Requires piece-hp.
Exports POISONED_SQUARES set for the UI overlay.
- capture-to-win: onBeforeCapture records the capturer`s color on
the game entity via Winner fact. onCheckGameResult converts it
into white-wins/black-wins. onDeactivate clears the Winner fact.
- last-piece-standing: onCheckGameResult counts pieces by color.
Zero on one side -> the other wins. Override returns "ongoing"
otherwise, suppressing default checkmate.
- explosive-rook: onBeforeCapture intercepts rook captures,
detonates all pieces within Chebyshev distance 2 on the rank
and file of the target (diagonals spared), moves the rook onto
the target square. Does not chain.
- queen-splits: onBeforeCapture intercepts queen captures,
retracts target and queen, spawns rook on target square and
bishop on first empty clockwise-from-N neighbour. Bishop is
forfeit if all 8 neighbours are occupied.
Board.tsx gains a per-SQUARE overlay registry (separate from per-
piece) so poisoned-squares can render a pulsing green tint on the
four central squares. The poisoned-squares.ui.tsx module registers
its overlay at load time via ui-overlays-index.ts.
GameView shows "White wins!"/"Black wins!" for the new result
variants; confetti fires on any decisive result; useChessEngine
and useMultiplayerGame play the checkmate sound on decisive results.
Server-side: GameEndReason extended with "variant-win" so the wire
protocol can signal decisive variant results without conflating
them with checkmate. MoveResult.gameOver exposes the correct
winner (not the mover) for white-wins/black-wins.
Testing
~~~~~~~
New fleshed-presets.test.ts: 13 integration tests covering each
preset`s canonical behaviour (healing cap, denial on check, poison
decrement + kill, first-capture-wins, annihilation override,
detonation AoE, queen fission spawn). 874 tests pass (+13); all 3
E2E specs green.