feat(bot): vanilla CasualBrain delegates to js-chess-engine

The hand-rolled scoring heuristic lost to a random-move baseline 7-7 in
self-play — far below the spec's >=80% acceptance bar. Swap in a real
chess engine (js-chess-engine, MIT, ~400KB, no native deps) for vanilla
mode at level 2 with randomness=30 to break threefold cycles.

- BrainInput.fen added; driver populates it ONLY in vanilla mode.
  Blind mode omits the FEN so the engine path can't smuggle opponent
  positions past the view filter.
- CasualBrain in vanilla: convert FEN -> EngineGame -> ai({level: 2});
  validate the engine's move is in legalCandidates; fall back to
  heuristic on miss.
- Blind mode unchanged (engine isn't useful when only own pieces are
  visible — that's Phase 2 Recon's territory).

Self-play vs RandomBrain (100 games each direction, vanilla):
  - Casual(W) vs Random(B): W=97%
  - Random(W) vs Casual(B): B=96%
Casual-vs-Casual vanilla balanced, ~5-30ms/move. All 54 tests still pass.

Refresh .secrets.baseline (stale) to allow new pnpm-lock.yaml hashes.
This commit is contained in:
claude (blind_chess)
2026-04-28 15:14:12 -04:00
parent dc5e6678b9
commit 7c18725586
6 changed files with 438 additions and 4404 deletions
+9
View File
@@ -57,6 +57,9 @@ importers:
fastify:
specifier: ^5.2.0
version: 5.8.5
js-chess-engine:
specifier: ^2.4.6
version: 2.4.6
pino:
specifier: ^9.5.0
version: 9.14.0
@@ -936,6 +939,10 @@ packages:
resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==}
engines: {node: '>=10'}
js-chess-engine@2.4.6:
resolution: {integrity: sha512-OKvWKICifXLjUilGzT5RstUv9iGpk04PjGpTyVT0lMlxX2HptoXZ2Q9hNicidnYjFcR7FHpnXFVwreDSF6a5Ng==}
engines: {node: '>=24'}
js-tokens@9.0.1:
resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==}
@@ -2078,6 +2085,8 @@ snapshots:
joycon@3.1.1: {}
js-chess-engine@2.4.6: {}
js-tokens@9.0.1: {}
json-schema-ref-resolver@3.0.0: