From be420bb8d1d5d16a4e24ab6fd706a5ae898eaa85 Mon Sep 17 00:00:00 2001 From: Scott Gasch Date: Fri, 4 Sep 2026 09:22:46 -0700 Subject: Add bitboard-backed GetAttacks (section 2/3), verified faster than asm Board-representation migration, sections 2-3 (GetAttacks half): - board.c: VerifyPositionConsistency's bbPieces consistency check (migration section 2), verified clean via gmake TEST=1 with the assert live. - POSITION.bbPawns[2]: new incrementally-maintained per-color pawn location bitboard (chess.h), maintained at the same 6 move.c sites as bbPieces, populated from scratch in fen.c. Distinct from the pawn-hash-keyed bbPawnLocations; this one needs no SEARCHER_THREAD_CONTEXT, so it's reachable from GetAttacks's actual call sites (which only ever have a POSITION*). - data.c/chess.h/main.c: g_RookRayAll/g_BishopRayAll (all 4 per-square ray directions pre-ORed) and g_PawnAttackOriginBB[2][128] startup tables, plus FastFirstBit/FastLastBit (static inline bsf/bsr wrappers, chess.h) -- supporting tables/helpers for the primitive below. - see.c: _WhoAttacksSquareBB (bitboard "who attacks square X" query) and _GetAttacksBB (SEE_LIST-populating PoC wrapping it), side by side with the existing SlowGetAttacks/asm GetAttacks -- not wired into the GetAttacks macro yet (section 6), pure addition. - testsee.c: SeeListsAreEqual made order-independent (SEE() sorts the list right after GetAttacks returns, so order was never semantically significant); TestGetAttacks extended to run _GetAttacksBB as a third comparison across the existing 20,000-random-position sweep; added an interleaved asm/Slow/BB cycles-per-call benchmark across opening/middlegame/endgame positions. - testsup.c: fixed GenerateRandomLegalPosition (used by the sweep above) to maintain bbPieces/bbPawns at its two hand-placement sites -- a latent gap since section 1 that made its own VerifyPositionConsistency legality gate almost always reject generated positions, causing large, variable retry-loop slowdowns. Verified: 20,000-position x every-square x both-colors correctness sweep passes (gmake TEST=1), precommit_check.sh clean (self-test + DEBUG smoke test). Benchmark: _GetAttacksBB is ~0.53-0.55x asm GetAttacks's cycles/call (opening/middlegame) and ~0.89x (endgame) -- faster, not just equivalent, primarily from replacing bbOccupied's up-to-16-iteration pawn loop with two bbPawns ORs, plus a g_PawnAttackOriginBB table lookup replacing per-call pawn-delta arithmetic and per-direction/per-side-group early-outs in the slider walk. See board_representation/MIGRATION.md section 3 for the full writeup, including a reverted approach that measured slower and why, and the CountKingSafetyDefects half's re-scoped (not yet implemented) design. Also confirmed (not caused by this work, not fixed here): a pre-existing non-deterministic MP-race assertion in util.c:1093's PV printing, reproduced independently on a clean HEAD checkout. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01Jntky4yGUTyQVaGCXms4F2 --- src/move.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'src/move.c') diff --git a/src/move.c b/src/move.c index 1ed1485..44e7073 100755 --- a/src/move.c +++ b/src/move.c @@ -121,6 +121,8 @@ Return value: ASSERT(IS_VALID_COLOR(c)); ASSERT(pos->cPawns[c][uIndex] == cFrom); pos->cPawns[c][uIndex] = cTo; + pos->bbPawns[c] &= ~COOR_TO_BB(cFrom); + pos->bbPawns[c] |= COOR_TO_BB(cTo); pos->u64PawnSig ^= g_u64PawnSigSeeds[cFrom][c]; pos->u64PawnSig ^= g_u64PawnSigSeeds[cTo][c]; pos->rgSquare[cTo].pPiece = p; @@ -224,6 +226,8 @@ Return value: ASSERT(IS_VALID_COLOR(c)); ASSERT(pos->cPawns[c][uIndex] == cFrom); pos->cPawns[c][uIndex] = cTo; + pos->bbPawns[c] &= ~COOR_TO_BB(cFrom); + pos->bbPawns[c] |= COOR_TO_BB(cTo); pos->rgSquare[cTo].pPiece = p; pos->rgSquare[cTo].uIndex = uIndex; #ifdef DEBUG @@ -293,10 +297,11 @@ Return value: ASSERT(pos->uPawnMaterial[color] <= (7 * VALUE_PAWN)); pos->u64PawnSig ^= g_u64PawnSigSeeds[cSquare][color]; + pos->bbPawns[color] &= ~COOR_TO_BB(cSquare); // // Remove this pawn from the pawn list. - // + // pos->uPawnCount[color]--; ASSERT(pos->uPawnCount[color] < 8); uLastIndex = pos->uPawnCount[color]; @@ -426,10 +431,11 @@ Return value: pos->uPawnMaterial[color] -= pv; ASSERT(pos->uPawnMaterial[color] <= (7 * VALUE_PAWN)); - + pos->bbPawns[color] &= ~COOR_TO_BB(cSquare); + // // Remove this pawn from the pawn list. - // + // pos->uPawnCount[color]--; ASSERT(pos->uPawnCount[color] < 8); uLastIndex = pos->uPawnCount[color]; // optimized... @@ -542,6 +548,7 @@ Return value: pos->uPawnCount[color]++; ASSERT(pos->uPawnCount[color] <= 8); pos->cPawns[color][uIndex] = cSquare; + pos->bbPawns[color] |= COOR_TO_BB(cSquare); pos->u64PawnSig ^= g_u64PawnSigSeeds[cSquare][color]; } else @@ -634,6 +641,7 @@ Return value: pos->uPawnCount[color]++; ASSERT(pos->uPawnCount[color] <= 8); pos->cPawns[color][uIndex] = cSquare; + pos->bbPawns[color] |= COOR_TO_BB(cSquare); } else { -- cgit v1.3