summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-09-05 00:19:59 -0700
committerScott Gasch <[email protected]>2026-09-05 00:19:59 -0700
commit2ce3570dc6aa67f82dd6f02fc4e518be3e33aabf (patch)
tree111f28a4b9587be62e3252aebc4c64b7d8350f4f /src
parent5883f5a64f4f464b877b7637b8e13c25f7d208fc (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
Diffstat (limited to 'src')
-rwxr-xr-xsrc/board.c10
-rw-r--r--src/board_representation/EVAL.md51
-rwxr-xr-xsrc/chess.h21
-rwxr-xr-xsrc/fen.c2
-rwxr-xr-xsrc/generate.c37
-rwxr-xr-xsrc/move.c32
-rwxr-xr-xsrc/movesup.c8
-rwxr-xr-xsrc/see.c43
-rw-r--r--src/testsup.c8
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
diff --git a/src/fen.c b/src/fen.c
index 2baa05d..37ce8da 100755
--- a/src/fen.c
+++ b/src/fen.c
@@ -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);
diff --git a/src/move.c b/src/move.c
index 44e7073..27d86df 100755
--- a/src/move.c
+++ b/src/move.c
@@ -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);
diff --git a/src/see.c b/src/see.c
index 4ba62ad..dc308d5 100755
--- a/src/see.c
+++ b/src/see.c
@@ -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))