houserules/packages/chess/src/hooks
Joey Yakimowich-Payne 1e292d6c00
feat(chess): flesh out all six remaining stub presets
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.
2026-04-17 16:16:11 -06:00
..
useChessEngine.ts feat(chess): flesh out all six remaining stub presets 2026-04-17 16:16:11 -06:00
useMultiplayerGame.ts feat(chess): flesh out all six remaining stub presets 2026-04-17 16:16:11 -06:00