diff options
| author | Scott Gasch <[email protected]> | 2026-09-04 16:46:27 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-09-04 16:46:27 -0700 |
| commit | 92fc41226f784b251f41eab7e75c13075e980a54 (patch) | |
| tree | 201d61c44dea1925b30c1d78ce2a3ac9d98e688e /src/testsee.c | |
| parent | 550ea81a5a2ee2561c3feb91dc55686f4d3cb872 (diff) | |
Land bitboard move generation (Part A+B) and movesup.c bitboard queries; default on
Implements the full board_representation/MOVEGEN_MIGRATION.md scope:
bitboard-backed generators for all six not-in-check piece types plus
the JumpTable-avoiding whole-node dispatch fork (_GenerateAllMovesBB),
the in-check escape path (king flight + block/capture), and
movesup.c's ExposesCheck/FasterExposesCheck/ExposesCheckEp/IsAttacked/
InCheck bitboard equivalents. Nine toggles total
(GENERATE_{KNIGHT,KING,ROOK,BISHOP,QUEEN,PAWN}_BITBOARD,
GENERATE_ESCAPES_{KING,BLOCK}_BITBOARD, EXPOSESCHECK_BITBOARD,
ISATTACKED_BITBOARD), all now on by default in GNUmakefile --
DISABLE_BITBOARD_MOVEGEN=1 opts back into the mailbox path, which
remains fully present and compiled either way.
Correctness verified via perft (Kiwipete, Position 4), the move-set
comparison harness across 20,000 random positions, all nine toggles
combined cleanly (15/15 runs, after fixing a GenerateRandomLegalPosition
en-passant-sentinel bug in the test harness), and sd10 on all three
curated suites showing zero solve-count regression vs head_reference
(the ecm_hard_quick delta traced to unrelated intervening commits).
Speed: most individual generators land near parity by design (mailbox's
per-square walk was already close to O(destination count)); the real,
consistent wins are the dispatch-layer fork (up to 23% in dense
positions) and IsAttackedBB (0.73x-0.93x of mailbox).
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01AbHkVrm5KUyzLwWd3GHmo6
Diffstat (limited to 'src/testsee.c')
| -rw-r--r-- | src/testsee.c | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/src/testsee.c b/src/testsee.c index 2106494..d196d54 100644 --- a/src/testsee.c +++ b/src/testsee.c @@ -39,6 +39,19 @@ Revision History: #undef GetAttacks #endif +// Same reasoning, for IsAttacked/InCheck (board_representation/ +// MOVEGEN_MIGRATION.md section 6b's ISATTACKED_BITBOARD toggle): +// TestIsAttackedBB below must always be able to call the real mailbox +// IsAttacked by name and compare it against IsAttackedBB explicitly, +// regardless of which one chess.h's macro currently routes plain +// "IsAttacked(...)" calls to elsewhere in the engine. +#ifdef IsAttacked +#undef IsAttacked +#endif +#ifdef InCheck +#undef InCheck +#endif + #ifdef TEST_BROKEN ULONG g_uRootOnMove; @@ -320,4 +333,116 @@ TestGetAttacks(void) } } } + +// board_representation/MOVEGEN_MIGRATION.md section 6b: direct +// comparison harness for IsAttacked/IsAttackedBB, same pattern as +// TestGetAttacks above -- mailbox vs. bitboard, same inputs, must +// match exactly. This is the piece ExposesCheckBB didn't get before +// shipping and had to be debugged the hard way instead (two real bugs +// found via TestSan/perft/DEBUG-assert failures rather than a direct +// comparison); doing it properly here from the start. +void +TestIsAttackedBB(void) +{ + POSITION pos; + ULONG u; + COOR c; + ULONG uSide; + FLAG fMailbox, fBB; + + Trace("Testing IsAttacked...\n"); + for (u = 0; u < 20000; u++) + { + GenerateRandomLegalPosition(&pos); + FOREACH_SQUARE(c) + { + if (!IS_ON_BOARD(c)) continue; + for (uSide = BLACK; uSide <= WHITE; uSide++) + { + fMailbox = IsAttacked(c, &pos, uSide); + fBB = IsAttackedBB(c, &pos, uSide); + if (fMailbox != fBB) + { + UtilPanic(TESTCASE_FAILURE, + &pos, + "IsAttacked/IsAttackedBB mismatch", + NULL, NULL, + __FILE__, __LINE__); + } + } + } + for (uSide = BLACK; uSide <= WHITE; uSide++) + { + fMailbox = InCheck(&pos, uSide); + fBB = InCheckBB(&pos, uSide); + if (fMailbox != fBB) + { + UtilPanic(TESTCASE_FAILURE, + &pos, + "InCheck/InCheckBB mismatch", + NULL, NULL, + __FILE__, __LINE__); + } + } + } + + // + // Speed: same isolated cycles/call methodology as TestGetAttacks + // above, interleaved call-by-call to cancel shared-box noise. + // + { + static const char *rgszFen[3] = + { + "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", + "r1bq1rk1/pp2bppp/2n1pn2/2pp4/3P4/2NBPN2/PP3PPP/R1BQ1RK1 w - - 0 1", + "8/5k2/8/3K4/8/8/8/4R3 w - - 0 1", + }; + static const char *rgszLabel[3] = + { + "opening ", "middlegame", "endgame ", + }; + POSITION posBench; + UINT64 u64MailboxTotal, u64BBTotal, u64Start; + ULONG uIter; + ULONG uSq; + COOR cBench; + ULONG uSideBench; + const ULONG uCallsPerPosition = 200000; + FLAG fSink; + + Trace("Benchmarking IsAttacked: mailbox vs IsAttackedBB " + "(interleaved, %lu calls/position)...\n", + uCallsPerPosition); + for (u = 0; u < 3; u++) + { + FenToPosition(&posBench, (char *)rgszFen[u]); + u64MailboxTotal = 0; + u64BBTotal = 0; + for (uIter = 0; uIter < uCallsPerPosition; uIter++) + { + uSq = uIter % 64; + cBench = BIT_NUMBER_TO_COOR(uSq); + uSideBench = uIter & 1; + if (!IS_ON_BOARD(cBench)) continue; + + u64Start = SystemReadTimeStampCounter(); + fSink = IsAttacked(cBench, &posBench, uSideBench); + u64MailboxTotal += (SystemReadTimeStampCounter() - u64Start); + + u64Start = SystemReadTimeStampCounter(); + fSink = IsAttackedBB(cBench, &posBench, uSideBench); + u64BBTotal += (SystemReadTimeStampCounter() - u64Start); + (void)fSink; + } + printf(" %s: mailbox %" COMPILER_LONGLONG_UNSIGNED_FORMAT + " cycles/call, IsAttackedBB %" + COMPILER_LONGLONG_UNSIGNED_FORMAT " cycles/call " + "(BB is %.2fx mailbox)\n", + rgszLabel[u], + u64MailboxTotal / uCallsPerPosition, + u64BBTotal / uCallsPerPosition, + (double)u64BBTotal / (double)u64MailboxTotal); + } + } +} #endif // TEST |
