diff options
| author | Scott Gasch <[email protected]> | 2026-09-05 00:19:59 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-09-05 00:19:59 -0700 |
| commit | 2ce3570dc6aa67f82dd6f02fc4e518be3e33aabf (patch) | |
| tree | 111f28a4b9587be62e3252aebc4c64b7d8350f4f | |
| parent | 5883f5a64f4f464b877b7637b8e13c25f7d208fc (diff) | |
Maintain pos->bbOccupied incrementally, dedupe its two from-scratch builders
chess.h: add POSITION::bbOccupied (full-board occupancy, both colors,
every piece including kings), maintained incrementally alongside
bbPieces/bbPawns rather than rebuilt on demand -- resolves the open
question in EVAL.md section 0 about whether this is worth doing given
both generate.c and the planned eval.c mobility rewrite need it.
move.c: SlidePiece/SlidePawn/LiftPiece/PlacePiece and their
WithoutSigs variants now maintain bbOccupied at the same choke points
that already maintain bbPieces/bbPawns -- unconditionally, since
occupancy doesn't care about piece type or color. Kings only ever
move through SlidePiece/SlidePieceWithoutSigs (never Lift/Place), so
no separate king-specific update site was needed.
fen.c: populate bbOccupied when parsing a FEN.
board.c: VerifyPositionConsistency cross-checks pos->bbOccupied
against a from-scratch rebuild, same pattern already used for
bbPieces/bbPawns.
generate.c/movesup.c/see.c: replace call sites that rebuilt full
occupancy via _BuildFullOccupiedBB/_BuildOccupiedBB with direct reads
of pos->bbOccupied; delete see.c's _BuildOccupiedBB, which was a
byte-for-byte duplicate of generate.c's _BuildFullOccupiedBB (kept
only as the from-scratch ground truth for the new consistency check
and testgenerate.c's benchmark harness).
testsup.c: GenerateRandomLegalPosition builds POSITIONs by poking
rgSquare/bbPieces/bbPawns directly, bypassing both move.c and fen.c --
a third construction path the above missed. It never set bbOccupied,
so the new VerifyPositionConsistency check failed on every generated
position, and since generation retries until a position verifies, the
self-test suite spun forever (100% CPU, no progress) instead of
crashing outright. Fixed by setting bbOccupied at all four placement
sites (both kings, pawn, non-pawn piece). Full self-test suite and
precommit_check.sh verified clean afterward.
board_representation/EVAL.md: record the bbOccupied decision and
rationale, resolving section 0's open question.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01P6g6iF6mD1Hau6nCZCzwYj
| -rwxr-xr-x | src/board.c | 10 | ||||
| -rw-r--r-- | src/board_representation/EVAL.md | 51 | ||||
| -rwxr-xr-x | src/chess.h | 21 | ||||
| -rwxr-xr-x | src/fen.c | 2 | ||||
| -rwxr-xr-x | src/generate.c | 37 | ||||
| -rwxr-xr-x | src/move.c | 32 | ||||
| -rwxr-xr-x | src/movesup.c | 8 | ||||
| -rwxr-xr-x | src/see.c | 43 | ||||
| -rw-r--r-- | src/testsup.c | 8 |
9 files changed, 137 insertions, 75 deletions
diff --git a/src/board.c b/src/board.c index 0b195be..814b045 100755 --- a/src/board.c +++ b/src/board.c @@ -170,6 +170,7 @@ Return value: "Fifty move counter is too high", "bbPieces bitboard doesn't match piece list", "bbPawns bitboard doesn't match pawn list", + "bbOccupied bitboard doesn't match piece list", }; ULONG u, v; COOR c; @@ -179,6 +180,7 @@ Return value: ULONG uNonPawnCount[2][7]; BITBOARD bbPieces[2][8]; BITBOARD bbPawns[2]; + BITBOARD bbOccupied = 0; ULONG uSigmaNonPawnCount[2] = {0, 0}; ULONG uWhiteSqBishopCount[2] = {0, 0}; UINT64 u64Computed; @@ -272,6 +274,7 @@ Return value: uPawnMaterial[u] += VALUE_PAWN; uPawnCount[u]++; bbPawns[u] |= COOR_TO_BB(c); + bbOccupied |= COOR_TO_BB(c); } } @@ -316,6 +319,7 @@ Return value: { bbPieces[u][PIECE_TYPE(p)] |= COOR_TO_BB(c); } + bbOccupied |= COOR_TO_BB(c); uNonPawnMaterial[u] += PIECE_VALUE(p); if ((IS_BISHOP(p)) && (IS_WHITE_SQUARE_COOR(c))) @@ -394,6 +398,12 @@ Return value: } } + if (pos->bbOccupied != bbOccupied) + { + uReason = 23; + goto end; + } + // // Now walk the actual board and reduce the material counts we got // by walking the piece lists. If everything is ok then the 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 diff --git a/src/chess.h b/src/chess.h index f39a956..41c8734 100755 --- a/src/chess.h +++ b/src/chess.h @@ -659,8 +659,8 @@ typedef struct _POSITION // Per-color pawn location bitboard -- same incremental-maintenance // idea as bbPieces above, but for pawns (which bbPieces - // deliberately excludes). Exists so _BuildOccupiedBB (see.c) can - // build full-board occupancy via two ORs instead of looping + // deliberately excludes). Exists so full-board occupancy + // (pos->bbOccupied below) can be built via two ORs instead of looping // cPawns[2][8] (up to 16 iterations) on every call -- see // board_representation/MIGRATION.md section 3. Also the single // source of truth for pawn locations used by pawn eval @@ -671,6 +671,23 @@ typedef struct _POSITION // this field directly instead. BITBOARD bbPawns[2]; + // Full-board occupancy (both colors, every piece including pawns + // and kings) -- same incremental-maintenance idea as bbPieces/ + // bbPawns above, added 2026-09-05 per board_representation/ + // EVAL.md section 0 (was previously only on MOVE_STACK, rebuilt + // on demand via _BuildFullOccupiedBB at many independent call + // sites in generate.c/movesup.c/see.c, and needed again by the + // planned eval.c mobility rewrite -- one incrementally-maintained + // copy here replaces all of that). Maintained by move.c's + // SlidePiece/SlidePawn/LiftPiece/PlacePiece (and their + // WithoutSigs variants) at the same sites that already maintain + // bbPieces/bbPawns, unconditionally (no per-color/per-type branch + // needed, unlike those). _BuildFullOccupiedBB(pos) (generate.c) + // remains as the from-scratch ground truth used to verify this + // field stays in sync (see VerifyPositionConsistency, board.c), + // not as something callers should call directly anymore. + BITBOARD bbOccupied; + ULONG uWhiteSqBishopCount[2]; // num bishops on white squares SCORE iMaterialBalance[2]; // material balance @@ -301,6 +301,7 @@ Return value: // pos->cPawns[uColor][uPieceCounters[uPieceIndex]] = cSquare; pos->bbPawns[uColor] |= COOR_TO_BB(cSquare); + pos->bbOccupied |= COOR_TO_BB(cSquare); pos->rgSquare[cSquare].uIndex = uPieceCounters[uPieceIndex]; pos->rgSquare[cSquare].pPiece = p; pos->uPawnMaterial[uColor] += VALUE_PAWN; @@ -332,6 +333,7 @@ Return value: uNumNonPawns[uColor]++; pos->bbPieces[uColor][PIECE_TYPE(p)] |= COOR_TO_BB(cSquare); } + pos->bbOccupied |= COOR_TO_BB(cSquare); pos->rgSquare[cSquare].pPiece = p; pos->rgSquare[cSquare].uIndex = uIndex; pos->uNonPawnMaterial[uColor] += PIECE_VALUE(p); diff --git a/src/generate.c b/src/generate.c index 69e431f..faecc2a 100755 --- a/src/generate.c +++ b/src/generate.c @@ -739,14 +739,14 @@ GenerateWhiteKnight(IN MOVE_STACK *pStack, // lookup needs no such color-specific shortcut, it just ANDs off // whichever side's occupancy pos->uToMove identifies. // -// Full-board occupancy, both sides -- same formula as see.c's static -// _BuildOccupiedBB (a separate copy, not shared, since that one is -// file-local to see.c and this module's own convention keeps its -// bitboard helpers together). Needed by the slider magic-bitboard -// generators (_GenerateRookBB/_GenerateBishopBB) to index into -// g_RookAttackTable/g_BishopAttackTable -- see MOVE_STACK's -// bbOccupied field comment in chess.h. Non-static so testgenerate.c's -// harness can call it directly. +// Full-board occupancy, both sides. Callers should prefer reading +// pos->bbOccupied directly (incrementally maintained, see chess.h and +// board_representation/EVAL.md section 0) -- this from-scratch +// rebuild now exists mainly as the ground truth used to verify that +// field stays in sync (VerifyPositionConsistency, board.c; the +// ASSERT(pStack->bbOccupied == _BuildFullOccupiedBB(pos)) calls +// below) and for testgenerate.c's benchmark harness. Non-static so +// both of those can call it directly. BITBOARD _BuildFullOccupiedBB(IN POSITION *pos) /** @@ -850,10 +850,11 @@ _BuildFriendlySideBB(IN POSITION *pos, IN ULONG uSide) Routine description: Full occupancy bitboard for one side only (all piece types - including pawns and king) -- see.c's _BuildOccupiedBB ORs both - sides together for a different purpose (SEE's "is this square - occupied at all" query); move generation needs just one side's - squares, to AND off as illegal (self-occupied) destinations. + including pawns and king) -- pos->bbOccupied ORs both sides + together for a different purpose (SEE's/generation's "is this + square occupied at all" query); move generation also needs just + one side's squares here, to AND off as illegal (self-occupied) + destinations. Parameters: @@ -2733,7 +2734,7 @@ Return value: **/ { BITBOARD bbPawns = pos->bbPawns[uSide]; - BITBOARD bbOccupied = _BuildFullOccupiedBB(pos); + BITBOARD bbOccupied = pos->bbOccupied; BITBOARD bbEmpty = ~bbOccupied; BITBOARD bbEnemy = bbOccupied & ~_BuildFriendlySideBB(pos, uSide); BITBOARD bbSinglePush, bbDoublePush, bbCapLeft, bbCapRight, bb; @@ -3072,7 +3073,7 @@ Return value: **/ { BITBOARD bbPawns = pos->bbPawns[uSide]; - BITBOARD bbOccupied = _BuildFullOccupiedBB(pos); + BITBOARD bbOccupied = pos->bbOccupied; BITBOARD bbEmpty = ~bbOccupied; BITBOARD bbEnemy = bbOccupied & ~_BuildFriendlySideBB(pos, uSide); BITBOARD bbSinglePush, bbDoublePush, bbCapLeft, bbCapRight, bb; @@ -3382,7 +3383,7 @@ Return value: #endif #if defined(GENERATE_ROOK_BITBOARD) || defined(GENERATE_BISHOP_BITBOARD) || \ defined(GENERATE_QUEEN_BITBOARD) - pStack->bbOccupied = _BuildFullOccupiedBB(pos); + pStack->bbOccupied = pos->bbOccupied; #endif for(u = pos->uNonPawnCount[pos->uToMove][0] - 1; @@ -3511,7 +3512,7 @@ Return value: #endif pStack->bbFriendlyOccupied = _BuildFriendlySideBB(pos, uSide); - pStack->bbOccupied = _BuildFullOccupiedBB(pos); + pStack->bbOccupied = pos->bbOccupied; bb = pos->bbPieces[uSide][KNIGHT]; while (bb) @@ -3728,7 +3729,7 @@ Return value: (void)pKing; BITBOARD bbFriendly = _BuildFriendlySideBB(pos, pos->uToMove); BITBOARD bbOccupiedWithoutKing = - _BuildFullOccupiedBB(pos) & ~COOR_TO_BB(cKing); + pos->bbOccupied & ~COOR_TO_BB(cKing); BITBOARD bbDest = g_KingAttacksBB[cKing] & ~bbFriendly; ULONG uBitIndex; @@ -3875,7 +3876,7 @@ Return value: // _SaveMe*BB call below to share (same amortization reasoning // as _GenerateAllMoves's own precompute block). pStack->bbFriendlyOccupied = _BuildFriendlySideBB(pos, pos->uToMove); - pStack->bbOccupied = _BuildFullOccupiedBB(pos); + pStack->bbOccupied = pos->bbOccupied; bbTargetMask = _ComputeCheckTargetMaskBB(cKing, c, pStack->bbOccupied); @@ -64,6 +64,8 @@ Return value: pos->cNonPawns[c][uIndex] = cTo; pos->bbPieces[c][PIECE_TYPE(p)] &= ~COOR_TO_BB(cFrom); pos->bbPieces[c][PIECE_TYPE(p)] |= COOR_TO_BB(cTo); + pos->bbOccupied &= ~COOR_TO_BB(cFrom); + pos->bbOccupied |= COOR_TO_BB(cTo); pos->u64NonPawnSig ^= g_u64SigSeeds[cFrom][PIECE_TYPE(p)][c]; pos->u64NonPawnSig ^= g_u64SigSeeds[cTo][PIECE_TYPE(p)][c]; #ifdef DEBUG @@ -123,6 +125,8 @@ Return value: pos->cPawns[c][uIndex] = cTo; pos->bbPawns[c] &= ~COOR_TO_BB(cFrom); pos->bbPawns[c] |= COOR_TO_BB(cTo); + pos->bbOccupied &= ~COOR_TO_BB(cFrom); + pos->bbOccupied |= COOR_TO_BB(cTo); pos->u64PawnSig ^= g_u64PawnSigSeeds[cFrom][c]; pos->u64PawnSig ^= g_u64PawnSigSeeds[cTo][c]; pos->rgSquare[cTo].pPiece = p; @@ -177,6 +181,8 @@ Return value: pos->cNonPawns[c][uIndex] = cTo; pos->bbPieces[c][PIECE_TYPE(p)] &= ~COOR_TO_BB(cFrom); pos->bbPieces[c][PIECE_TYPE(p)] |= COOR_TO_BB(cTo); + pos->bbOccupied &= ~COOR_TO_BB(cFrom); + pos->bbOccupied |= COOR_TO_BB(cTo); pos->rgSquare[cTo].pPiece = p; pos->rgSquare[cTo].uIndex = uIndex; #ifdef DEBUG @@ -185,7 +191,7 @@ Return value: } -void +void SlidePawnWithoutSigs(POSITION *pos, COOR cFrom, COOR cTo) /** @@ -228,6 +234,8 @@ Return value: pos->cPawns[c][uIndex] = cTo; pos->bbPawns[c] &= ~COOR_TO_BB(cFrom); pos->bbPawns[c] |= COOR_TO_BB(cTo); + pos->bbOccupied &= ~COOR_TO_BB(cFrom); + pos->bbOccupied |= COOR_TO_BB(cTo); pos->rgSquare[cTo].pPiece = p; pos->rgSquare[cTo].uIndex = uIndex; #ifdef DEBUG @@ -275,6 +283,7 @@ Return value: pos->rgSquare[cSquare].pPiece = EMPTY; ASSERT(IS_VALID_PIECE(pLifted)); ASSERT(!IS_KING(pLifted)); + pos->bbOccupied &= ~COOR_TO_BB(cSquare); uIndex = pos->rgSquare[cSquare].uIndex; #ifdef DEBUG ASSERT(IS_VALID_PIECE_INDEX(uIndex)); @@ -287,12 +296,12 @@ Return value: pos->iMaterialBalance[color] -= pv; pos->iMaterialBalance[FLIP(color)] += pv; ASSERT(pos->iMaterialBalance[WHITE] * -1 == pos->iMaterialBalance[BLACK]); - + if (IS_PAWN(pLifted)) { ASSERT(pos->cPawns[color][uIndex] == cSquare); ASSERT(pv == VALUE_PAWN); - + pos->uPawnMaterial[color] -= pv; ASSERT(pos->uPawnMaterial[color] <= (7 * VALUE_PAWN)); @@ -410,7 +419,8 @@ Return value: pos->rgSquare[cSquare].pPiece = EMPTY; ASSERT(IS_VALID_PIECE(pLifted)); ASSERT(!IS_KING(pLifted)); - + pos->bbOccupied &= ~COOR_TO_BB(cSquare); + uIndex = pos->rgSquare[cSquare].uIndex; #ifdef DEBUG ASSERT(IS_VALID_PIECE_INDEX(uIndex)); @@ -423,12 +433,12 @@ Return value: pos->iMaterialBalance[color] -= pv; pos->iMaterialBalance[FLIP(color)] += pv; ASSERT(pos->iMaterialBalance[WHITE] * -1 == pos->iMaterialBalance[BLACK]); - + if (IS_PAWN(pLifted)) { ASSERT(pos->cPawns[color][uIndex] == cSquare); ASSERT(pv == VALUE_PAWN); - + pos->uPawnMaterial[color] -= pv; ASSERT(pos->uPawnMaterial[color] <= (7 * VALUE_PAWN)); pos->bbPawns[color] &= ~COOR_TO_BB(cSquare); @@ -533,7 +543,8 @@ Return value: pos->iMaterialBalance[color] += pv; pos->iMaterialBalance[FLIP(color)] -= pv; ASSERT(pos->iMaterialBalance[WHITE] * -1 == pos->iMaterialBalance[BLACK]); - + pos->bbOccupied |= COOR_TO_BB(cSquare); + if (IS_PAWN(pPiece)) { // @@ -542,7 +553,7 @@ Return value: ASSERT(pv == VALUE_PAWN); pos->uPawnMaterial[color] += pv; ASSERT(pos->uPawnMaterial[color] <= (8 * VALUE_PAWN)); - + uIndex = pos->uPawnCount[color]; ASSERT((uIndex >= 0) && (uIndex <= 7)); pos->uPawnCount[color]++; @@ -626,7 +637,8 @@ Return value: pos->iMaterialBalance[color] += pv; pos->iMaterialBalance[FLIP(color)] -= pv; ASSERT(pos->iMaterialBalance[WHITE] * -1 == pos->iMaterialBalance[BLACK]); - + pos->bbOccupied |= COOR_TO_BB(cSquare); + if (IS_PAWN(pPiece)) { // @@ -635,7 +647,7 @@ Return value: ASSERT(pv == VALUE_PAWN); pos->uPawnMaterial[color] += pv; ASSERT(pos->uPawnMaterial[color] <= (8 * VALUE_PAWN)); - + uIndex = pos->uPawnCount[color]; ASSERT((uIndex >= 0) && (uIndex <= 7)); pos->uPawnCount[color]++; diff --git a/src/movesup.c b/src/movesup.c index 22bf0c1..82ba10a 100755 --- a/src/movesup.c +++ b/src/movesup.c @@ -220,7 +220,7 @@ Return value: iDelta = CHECK_DELTA_WITH_INDEX(iIndex); ASSERT(iDelta != 0); - bbOccupiedWithoutRemove = _BuildFullOccupiedBB(pos) & ~COOR_TO_BB(cRemove); + bbOccupiedWithoutRemove = pos->bbOccupied & ~COOR_TO_BB(cRemove); cBlocker = _NearestBlockerAlongRayBB(cLocation, iDelta, bbOccupiedWithoutRemove); return _ValidateExposedBlockerBB(pos, cBlocker, cLocation); @@ -270,7 +270,7 @@ Return value: } iDelta = CHECK_DELTA_WITH_INDEX(iIndex); - bbOccupiedWithoutRemove = _BuildFullOccupiedBB(pos) & ~COOR_TO_BB(cRemove); + bbOccupiedWithoutRemove = pos->bbOccupied & ~COOR_TO_BB(cRemove); cBlocker = _NearestBlockerAlongRayBB(cLocation, iDelta, bbOccupiedWithoutRemove); return _ValidateExposedBlockerBB(pos, cBlocker, cLocation); @@ -323,7 +323,7 @@ Return value: } iDelta = CHECK_DELTA_WITH_INDEX(iIndex); - bbOccupied = (_BuildFullOccupiedBB(pos) & + bbOccupied = (pos->bbOccupied & ~COOR_TO_BB(cTest) & ~COOR_TO_BB(cIgnore)) | COOR_TO_BB(cBlock); cBlocker = _NearestBlockerAlongRayBB(cKing, iDelta, bbOccupied); @@ -641,7 +641,7 @@ Return value: ASSERT(IS_ON_BOARD(cTest)); ASSERT(IS_VALID_COLOR(uSide)); - bbOccupied = _BuildFullOccupiedBB(pos); + bbOccupied = pos->bbOccupied; if (0 != _WhoAttacksSquareBB(pos, cTest, uSide, bbOccupied)) { return(TRUE); @@ -167,39 +167,12 @@ Return value: // out-of-line FirstBit/LastBit -- worth avoiding call overhead in a // per-move-generated, per-node hot path like this one. // -static BITBOARD -_BuildOccupiedBB(IN POSITION *pos) -/** - -Routine description: - - Full-board occupancy (both colors, every piece including pawns - and kings), built from the incrementally-maintained bbPieces[2][8] - and bbPawns[2] fields plus the king mailbox array - (cNonPawns[.][0], a single square per side -- a bitboard for that - adds nothing). All O(1) ORs now that bbPawns exists; this used to - loop cPawns[2][8] (up to 16 iterations) to build the pawn portion, - which ran on every single call regardless of how few pawns were - actually relevant. - -Parameters: - - POSITION *pos - -Return value: - - BITBOARD - -**/ -{ - return (pos->bbPieces[WHITE][KNIGHT] | pos->bbPieces[WHITE][BISHOP] | - pos->bbPieces[WHITE][ROOK] | pos->bbPieces[WHITE][QUEEN] | - pos->bbPieces[BLACK][KNIGHT] | pos->bbPieces[BLACK][BISHOP] | - pos->bbPieces[BLACK][ROOK] | pos->bbPieces[BLACK][QUEEN] | - pos->bbPawns[WHITE] | pos->bbPawns[BLACK] | - COOR_TO_BB(pos->cNonPawns[WHITE][0]) | - COOR_TO_BB(pos->cNonPawns[BLACK][0])); -} +// (This file used to have its own static _BuildOccupiedBB here, +// byte-for-byte identical to generate.c's _BuildFullOccupiedBB -- +// removed 2026-09-05 now that pos->bbOccupied is incrementally +// maintained directly on POSITION; see board_representation/ +// EVAL.md section 0. Callers below just read pos->bbOccupied.) +// // Non-static (unlike its historical file-local status) so // generate.c's Part B (_GenerateEscapes) bitboard work can call it @@ -249,7 +222,7 @@ Parameters: POSITION *pos, COOR cSquare : target square ULONG uSide : side whose attackers on cSquare we want - BITBOARD bbOccupied : full-board occupancy (see _BuildOccupiedBB) + BITBOARD bbOccupied : full-board occupancy (see pos->bbOccupied) Return value: @@ -412,7 +385,7 @@ Return value: // // Knights/bishops/rooks/queens/king, via the bitboard primitive. // - bbOccupied = _BuildOccupiedBB(pos); + bbOccupied = pos->bbOccupied; bbAttackers = _WhoAttacksSquareBB(pos, cSquare, uSide, bbOccupied); while (bbAttackers) { diff --git a/src/testsup.c b/src/testsup.c index d1b102d..852b6d9 100644 --- a/src/testsup.c +++ b/src/testsup.c @@ -117,7 +117,8 @@ GenerateRandomLegalPosition(POSITION *pos) pos->uNonPawnCount[WHITE][KING] = 1; pos->uNonPawnCount[WHITE][0] = 1; pos->uNonPawnMaterial[WHITE] = VALUE_KING; - + pos->bbOccupied |= COOR_TO_BB(c); + do { c1 = RANDOM_COOR; @@ -130,7 +131,8 @@ GenerateRandomLegalPosition(POSITION *pos) pos->uNonPawnCount[BLACK][KING] = 1; pos->uNonPawnCount[BLACK][0] = 1; pos->uNonPawnMaterial[BLACK] = VALUE_KING; - + pos->bbOccupied |= COOR_TO_BB(c1); + // // Place the rest of the armies // @@ -180,6 +182,7 @@ GenerateRandomLegalPosition(POSITION *pos) pos->uPawnCount[uColor]++; pos->uPawnMaterial[uColor] += VALUE_PAWN; pos->bbPawns[uColor] |= COOR_TO_BB(c); + pos->bbOccupied |= COOR_TO_BB(c); break; } else if (!IS_KING(p) && @@ -193,6 +196,7 @@ GenerateRandomLegalPosition(POSITION *pos) pos->rgSquare[c].uIndex = uIndex; pos->uNonPawnMaterial[uColor] += PIECE_VALUE(p); pos->bbPieces[uColor][PIECE_TYPE(p)] |= COOR_TO_BB(c); + pos->bbOccupied |= COOR_TO_BB(c); if (IS_BISHOP(p)) { if (IS_WHITE_SQUARE_COOR(c)) |
