diff options
| author | Scott Gasch <[email protected]> | 2026-09-18 23:43:55 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-09-18 23:43:55 -0700 |
| commit | b70be6fde0fef0694e404e16d70f00c73dd971a2 (patch) | |
| tree | d5523636bf76bd53f8ac4a60e508c458fa3c2a51 /src/see.c | |
| parent | 004a5736c6d41747d8fc0e15d230ab80bd6cced6 (diff) | |
see.c: replace _WhoAttacksSquareBB's ray-walk with magic-bitboard lookups
_RookAttacksBB/_BishopAttacksBB (generate.c) are already O(1)
FORCEINLINE magic-table lookups, proven and already used everywhere
else in this codebase (eval.c, searchsup.c, generate.c's own move
generation) -- this function alone predated that migration and was
left on a manual per-direction ray-walk (4 rook + 4 bishop directions,
nearest-blocker isolation via FastFirstBit/FastLastBit). King handling
is also branchless now: king adjacency is symmetric, so
g_KingAttacksBB[cSquare] doubles as "every square a king standing
there would attack cSquare from," same O(1)-table-lookup shape the
knight line already uses -- no DISTANCE()==1 branch needed.
Old ray-walk kept as _WhoAttacksSquareRayBB, testsee.c's
TestWhoAttacksSquareMagic correctness-diff reference (20000 random
positions x every square x both colors, clean) -- same convention as
_AddXRaysMailbox next to _AddXRaysBB from an earlier SEE investigation
this session.
Isolated benchmark: magic is 0.95x-0.99x ray-walk in a standalone
3-position microbenchmark (small, since the ray-walk already has its
own early-out for "no slider anywhere near this square," per its own
header comment). Measured end-to-end (sd10 + sn5M, all three curated
suites, before vs. after): node counts and solve counts bit-for-bit
identical (pure algorithmic swap, zero behavior change). Wall-clock
~0.8% faster overall, all 6 measurements faster, none slower.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/see.c')
| -rw-r--r-- | src/see.c | 95 |
1 files changed, 85 insertions, 10 deletions
@@ -47,17 +47,24 @@ Revision History: // 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 -// directly for king-flight safety testing, passing an occupancy -// bitboard with the king itself removed to correctly account for -// x-ray/discovered attacks when the king steps out of a slider's way -// -- see board_representation/MOVEGEN_MIGRATION.md section 6a. +// Ray-walk reference implementation -- superseded 2026-09-19 by the +// magic-bitboard version below (_WhoAttacksSquareBB itself now uses +// magic lookups; this name is kept only as testsee.c's +// TestWhoAttacksSquareMagic correctness-diff reference, same +// convention as _AddXRaysMailbox next to _AddXRaysBB). Non-static +// (unlike its historical file-local status) so generate.c's Part B +// (_GenerateEscapes) bitboard work could call it directly for +// king-flight safety testing, passing an occupancy bitboard with the +// king itself removed to correctly account for x-ray/discovered +// attacks when the king steps out of a slider's way -- see +// board_representation/MOVEGEN_MIGRATION.md section 6a. (That real +// call site, and _GetAttacksBB's, now go through the magic version +// below instead.) BITBOARD -_WhoAttacksSquareBB(IN POSITION *pos, - IN COOR cSquare, - IN ULONG uSide, - IN BITBOARD bbOccupied) +_WhoAttacksSquareRayBB(IN POSITION *pos, + IN COOR cSquare, + IN ULONG uSide, + IN BITBOARD bbOccupied) /** Routine description: @@ -183,6 +190,74 @@ Return value: return bbAttackers; } +// Magic-bitboard implementation -- the real _WhoAttacksSquareBB as of +// 2026-09-19, all production callers (movesup.c, generate.c, +// _GetAttacksBB/SeeFastSign below) go through this name. +// _RookAttacksBB/_BishopAttacksBB (generate.c) are already O(1) +// FORCEINLINE magic-table lookups (one multiply, one shift, one table +// read), proven and already used everywhere else in this codebase +// (eval.c, searchsup.c, generate.c's own move generation) -- this +// function alone predated that migration and was left on the manual +// per-direction ray-walk (_WhoAttacksSquareRayBB above, kept as +// testsee.c's correctness-diff reference). King handling is branchless: +// king adjacency is symmetric, so g_KingAttacksBB[cSquare] (every +// square one king-step from cSquare) doubles as "every square a king +// standing there would attack cSquare from" -- ANDing it against a +// single-bit bitboard of the actual king square is either that bit or +// zero, same O(1)-table-lookup shape the knight line already uses, no +// DISTANCE()==1 branch needed. +BITBOARD +_WhoAttacksSquareBB(IN POSITION *pos, + IN COOR cSquare, + IN ULONG uSide, + IN BITBOARD bbOccupied) +/** + +Routine description: + + Return a bitboard of every uSide knight/bishop/rook/queen/king + that attacks cSquare in the current position, blockers included. + Pawns are deliberately excluded -- see _GetAttacksBB. Magic- + bitboard equivalent of the historical ray-walk implementation + (_WhoAttacksSquareRayBB above) -- see that function's description + for what this replaces. + +Parameters: + + POSITION *pos, + COOR cSquare : target square + ULONG uSide : side whose attackers on cSquare we want + BITBOARD bbOccupied : full-board occupancy (see pos->bbOccupied) + +Return value: + + BITBOARD + +**/ +{ + BITBOARD bbAttackers; + BITBOARD bbRookSliders; + BITBOARD bbBishopSliders; + + // Branchless: king adjacency is symmetric, so g_KingAttacksBB[cSquare] + // (every square one king-step from cSquare) doubles as "every square + // a king standing there would attack cSquare from" -- ANDing it + // against a single-bit bitboard of the actual king square is either + // that bit or zero, exactly matching what the DISTANCE()==1 branch + // computed, same O(1)-table-lookup shape the knight line above + // already uses. + bbAttackers = g_KnightAttacksBB[cSquare] & pos->bbPieces[uSide][KNIGHT]; + bbAttackers |= g_KingAttacksBB[cSquare] & COOR_TO_BB(KingPosition(pos, uSide)); + + bbRookSliders = pos->bbPieces[uSide][ROOK] | pos->bbPieces[uSide][QUEEN]; + bbAttackers |= _RookAttacksBB(cSquare, bbOccupied) & bbRookSliders; + + bbBishopSliders = pos->bbPieces[uSide][BISHOP] | pos->bbPieces[uSide][QUEEN]; + bbAttackers |= _BishopAttacksBB(cSquare, bbOccupied) & bbBishopSliders; + + return bbAttackers; +} + void CDECL _GetAttacksBB(IN OUT SEE_LIST *pList, IN POSITION *pos, |
