diff options
Diffstat (limited to 'src/board_representation')
| -rw-r--r-- | src/board_representation/EVAL.md | 51 |
1 files changed, 47 insertions, 4 deletions
diff --git a/src/board_representation/EVAL.md b/src/board_representation/EVAL.md index 34fb9c3..697ea38 100644 --- a/src/board_representation/EVAL.md +++ b/src/board_representation/EVAL.md @@ -31,10 +31,53 @@ Two things changed since the first draft: attack that number directly, not as a speculative "bitboards are modern, let's use them" exercise. -Open question: both move gen (generate.c) and eval (eval.c) need a -bbOccupied. Is this worth maintaining incrementally (in -MakeMove/LiftPiece/SlidePiece/etc...) so that it will always be on -POSITION and up-to-date? +**Resolved 2026-09-05 (was an open question): yes, move `bbOccupied` +onto `POSITION`, incrementally maintained.** Investigated by reading +`move.c` end to end for how `bbPieces[2][8]`/`bbPawns[2]` -- the +precedent for exactly this kind of incrementally-maintained bitboard +-- actually get updated: + +- Every mutation funnels through a small, fixed set of primitives + (`SlidePiece`, `SlidePawn`, `LiftPiece`, `PlacePiece`, and their + `WithoutSigs` variants) -- `MakeMove`/`UnmakeMove` never touch these + bitboards directly. `bbOccupied` would touch the identical choke + points, not a new set of call sites. +- It's actually *simpler* to maintain than `bbPieces`/`bbPawns`: + those are keyed by piece type and color (an array lookup before the + OR/AND), but occupancy doesn't care what's on a square or whose it + is -- one unconditional clear-from-bit/set-to-bit pair per primitive, + no branch, and it uniformly covers kings too (which today have no + bitboard at all, only the `cNonPawns[.][0]` mailbox slot). +- **Currently it lives on `MOVE_STACK`** (`chess.h`'s `bbOccupied` + field), rebuilt on demand via `_BuildFullOccupiedBB(pos)` + (`generate.c`) -- itself already cheap (11 ORs of `bbPieces`/ + `bbPawns`/king squares, no mailbox scan, per that function's own + comment) -- but called fresh at many independent sites: several in + `generate.c`, four in `movesup.c`, one in `see.c`. Worse, the exact + same 11-OR logic is duplicated verbatim as `_BuildOccupiedBB` + (static, `see.c`) and `_BuildFullOccupiedBB` (non-static, + `generate.c`) -- same function, two names, two files. +- Moving it to `POSITION`, incrementally maintained, means every one + of those call sites (plus this plan's own future `eval.c` mobility + rewrite, which needs the same value for + `_RookAttacksBB`/`_BishopAttacksBB(c, bbOccupied)`) reads one + already-current field instead of independently re-deriving it -- + and dedupes the two identical builder functions into one. +- **Verification is close to free**: `generate.c` already has + `ASSERT(pStack->bbOccupied == _BuildFullOccupiedBB(pos))` at three + call sites, cross-checking the `MOVE_STACK`-cached copy against a + from-scratch rebuild today. The identical assert, repointed at + `pos->bbOccupied`, becomes the DEBUG-build safety net for the + incremental version -- the same pattern that already validated + `bbPieces`/`bbPawns` when they were added. +- **Scoped as standalone, low-risk work, not gated on the rest of + this plan**: move generation benefits from it immediately regardless + of `Eval()`'s progress, so it's worth landing (add the field, touch + the handful of `move.c` primitives, dedupe the two builder + functions, repoint the existing assert, run + `debug_smoke_test.sh`/`precommit_check.sh`) on its own, ahead of or + alongside section 4's toggle work -- not bundled into any single + piece type's toggle. **The movegen project's own findings are the load-bearing precedent here, and they cut both ways -- worth stating plainly before |
