Implements the piece-hp (Hit Points) preset end-to-end and, more
importantly, sets up the infrastructure for rules that need state,
capture interception, or custom UI. Adding a new "guns" rule, a
"poison-cloud" visual, or similar now requires zero changes to
engine.ts, Board.tsx, or protocol.ts.
Engine / preset registry
~~~~~~~~~~~~~~~~~~~~~~~~
PresetDef gains three new optional hooks:
- onActivate(engine) \u2014 fires once on active-set transition
(inactive \u2192 active). Idempotent by
convention so sync paths that re-apply
from server state don\`t stomp values.
- onDeactivate(engine) \u2014 symmetric cleanup. Also fires when a
turn-limited preset expires via
tickAfterMove.
- onBeforeCapture(engine, attacker, target)
Fires immediately before the engine\`s default capture path.
Returns `{ consume: true }` to short-circuit: engine skips
target retraction AND attacker move. Used by piece-hp for
non-lethal damage; future rules can override however they like.
New ChessEngine.setActivePresets(requests) is the single entry point
that diffs old vs new and fires lifecycle hooks in deterministic
order (deactivate-then-activate). replaceAll stays public for tests
that want to bypass hooks.
ActivePresetSet.tickAfterMove now returns the list of expired ids
so the engine can fire onDeactivate on them.
piece-hp preset
~~~~~~~~~~~~~~~
- onActivate seeds Hp=2 on every piece.
- onDeactivate retracts Hp from all pieces.
- onBeforeCapture decrements Hp; if > 0 consumes the capture (attacker
stays, target survives, turn advances). At 0, returns without
consuming so the engine\`s default retract-and-move fires normally.
All capture sites intercepted: regular captures (engine.ts:200) AND
en-passant captures (engine.ts:194). The check-simulation path in
check.ts does NOT fire the hook \u2014 it uses an isolated snapshot
session, so lifecycle side effects don\`t leak into legal-move
filtering.
UI overlay registry
~~~~~~~~~~~~~~~~~~~
New packages/chess/src/ui/preset-overlays.tsx: a module-level registry
mapping preset id \u2192 React component that renders above each piece.
Board.tsx loads the registry via side-effect import and renders any
registered overlays for every active preset.
New packages/chess/src/presets/piece-hp.ui.tsx registers HealthBarPips:
2 pip dots above each piece, filled = remaining HP, empty = lost HP.
Pip color contrasts piece color for readability on either square.
Adding a new visual rule now costs 3 files and zero engine changes:
presets/foo.ts (mechanic + lifecycle hooks)
presets/foo.ui.tsx (overlay component + registry call)
presets/ui-overlays-index.ts (single-line import)
Testing
~~~~~~~
New piece-hp.test.ts: 8 integration tests covering lifecycle (seed,
retract, idempotent re-apply, no-fire on scope-only change) and
capture resolution (non-lethal decrement, lethal retract at HP=0,
HP drain over multiple captures, deactivate mid-game leaves damage
but retracts Hp). Total 861 tests pass (+8), 3/3 E2E green.
|
||
|---|---|---|
| .. | ||
| src | ||
| package.json | ||
| PROTOCOL.md | ||
| README.md | ||
| tsconfig.json | ||
| vitest.config.ts | ||