diff options
| author | Scott Gasch <[email protected]> | 2026-10-25 10:39:58 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-10-25 10:39:58 -0700 |
| commit | f97b2710981e936cfb500ad0cdf1ceab233efad8 (patch) | |
| tree | 0004275d9dae8f7df4b5f86f3b750d80bc3f4a52 /src | |
| parent | 3927a4cac6ec254a8e34a5e864300f75c3efc9f3 (diff) | |
Make bitboard move generation unconditional; delete the mailbox fallback
All GENERATE_*_BITBOARD/EXPOSESCHECK_BITBOARD/ISATTACKED_BITBOARD/
UNBLOCKED_BITBOARD flags were already default-on (ifndef
DISABLE_BITBOARD_MOVEGEN); this removes that opt-out entirely (GNUmakefile)
and deletes the mailbox-era code the flags used to toggle between, since
there's no longer a build configuration that still needs it:
- generate.c: the mailbox per-piece generators and escape-generators
(GenerateKnight/GenerateWhiteKnight/GenerateBishop/GenerateRook/
GenerateQueen/GenerateBlackKing/GenerateWhiteKing/GenerateWhitePawn/
GenerateBlackPawn and their SaveMe* escape-generator counterparts),
_FindUnblockedSquares and its DEBUG-build cross-check inside
_FindUnblockedSquaresBB, and the dispatch-table #if/#else pairs that
chose between mailbox and bitboard functions (now unconditionally
bitboard). WouldGiveCheck simplified to its UNBLOCKED_BITBOARD-only
body. g_iBDeltas/g_iRDeltas (orphaned once their only consumers were
deleted) removed; g_iNDeltas (still used by data.c's InitMagic) moved
there, next to its real consumer.
- movesup.c: mailbox FasterExposesCheck/ExposesCheck/ExposesCheckEp/
IsAttacked/InCheck deleted; chess.h's three-way macro choice
simplified to a direct #define onto the BB versions (ExposesCheckBB/
IsAttackedBB/etc. are simply "the" implementation now).
- testsee.c/testgenerate.c: deleted the mailbox-vs-BB comparison/speed
harnesses that only existed to validate code just removed
(TestIsAttackedBB, TestGenerateKnightSpeed/KingSpeed/RookSpeed/
BishopSpeed/QueenSpeed, TestFindUnblockedSquaresSpeed's mailbox arm).
TestGetAttacks and the _GenerateAllMoves-vs-_GenerateAllMovesBB
dispatch-overhead benchmark are unaffected -- GetAttacks' mailbox/asm
predecessors were already retired independently, and that pair
compares two still-live BB-only dispatch strategies, not mailbox vs
BB.
Verified: clean release build and a clean DEBUG=1 TEST=1 self-test run
(move generation, perft-style legal-move checks, SAN/ICS parsing,
search smoke test) with no assertion failures.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src')
| -rw-r--r-- | src/GNUmakefile | 14 | ||||
| -rwxr-xr-x | src/chess.h | 132 | ||||
| -rw-r--r-- | src/data.c | 6 | ||||
| -rw-r--r-- | src/generate.c | 1853 | ||||
| -rw-r--r-- | src/main.c | 6 | ||||
| -rw-r--r-- | src/movesup.c | 430 | ||||
| -rw-r--r-- | src/testgenerate.c | 406 | ||||
| -rw-r--r-- | src/testsee.c | 125 |
8 files changed, 48 insertions, 2924 deletions
diff --git a/src/GNUmakefile b/src/GNUmakefile index 26c7b94..667495e 100644 --- a/src/GNUmakefile +++ b/src/GNUmakefile @@ -144,18 +144,18 @@ ifdef CROUTINES PROFILE += -DCROUTINES endif -ifdef GETATTACKS_BITBOARD - PROFILE += -DGETATTACKS_BITBOARD -endif - -ifndef DISABLE_BITBOARD_MOVEGEN - PROFILE += -DGENERATE_KNIGHT_BITBOARD -DGENERATE_KING_BITBOARD \ +# 2026-10-25: bitboard move generation is no longer optional -- this +# project always uses it (the mailbox fallback predates magic +# bitboards and is only still in the source tree as DEBUG/TEST +# cross-check code, see generate.c/chess.h). GETATTACKS_BITBOARD +# (checked nowhere in the source) and the DISABLE_BITBOARD_MOVEGEN +# opt-out are both retired; these defines are simply always on. +PROFILE += -DGENERATE_KNIGHT_BITBOARD -DGENERATE_KING_BITBOARD \ -DGENERATE_ROOK_BITBOARD -DGENERATE_BISHOP_BITBOARD \ -DGENERATE_QUEEN_BITBOARD -DGENERATE_PAWN_BITBOARD \ -DGENERATE_ESCAPES_KING_BITBOARD -DGENERATE_ESCAPES_BLOCK_BITBOARD \ -DEXPOSESCHECK_BITBOARD -DISATTACKED_BITBOARD \ -DUNBLOCKED_BITBOARD -endif ifdef EVERYTHING PROFILE += -DEVAL_DUMP -DEVAL_TIME -DPERF_COUNTERS -DMP -DSMP -DTEST_NULL -DTREEDUMP -fbounds-checking -DMOVE_ORDERING_EVIDENCE_COLLECTION diff --git a/src/chess.h b/src/chess.h index 85b4bd7..2942f0a 100755 --- a/src/chess.h +++ b/src/chess.h @@ -950,17 +950,12 @@ MOVE_STACK; #define GENERATE_DONT_SCORE (5) // generate.c's _GenerateCapturesAndPromotions (GENERATE_CAPTURES_PROMS's -// direct, non-filtered generator) exists only when every piece type is -// on its bitboard generator -- see that function's block comment in -// generate.c. Declared here, not in generate.c, so every translation -// unit that needs to know whether it exists (main.c's test-registration -// list, testgenerate.c's own tests) sees the same answer generate.c's -// GenerateMoves dispatch does. -#if defined(GENERATE_KNIGHT_BITBOARD) && defined(GENERATE_BISHOP_BITBOARD) && \ - defined(GENERATE_ROOK_BITBOARD) && defined(GENERATE_QUEEN_BITBOARD) && \ - defined(GENERATE_KING_BITBOARD) && defined(GENERATE_PAWN_BITBOARD) +// direct, non-filtered generator) -- every piece type is always on its +// bitboard generator now, so this is unconditional; kept as a named +// macro (rather than inlined away) so main.c's test-registration list +// and testgenerate.c's own tests keep one clearly-named thing to guard +// on, matching generate.c's own #if there. #define CAPTURES_PROMS_BITBOARD_PATH 1 -#endif #ifdef TEST #define GENERATE_ALL_MOVES_CHECK_OK (6) @@ -3446,24 +3441,9 @@ DumpMove(ULONG u); #define MOVE_TO_INDEX(mv) (((mv).uMove & 0xFFFF) + \ (0x10000 * GET_COLOR(mv.pMoved))) -COOR -FasterExposesCheck(POSITION *pos, - COOR cRemove, - COOR cLocation); - -COOR -ExposesCheck(POSITION *pos, - COOR cRemove, - COOR cLocation); - -COOR -ExposesCheckEp(POSITION *pos, - COOR cTest, - COOR cIgnore, - COOR cBlock, - COOR cKing); - -// board_representation/MOVEGEN_MIGRATION.md section 6b +// board_representation/MOVEGEN_MIGRATION.md section 6b. The mailbox +// originals (and the three-way #ifdef choice between them and these) +// are retired -- these are simply the real implementations now. COOR FasterExposesCheckBB(POSITION *pos, COOR cRemove, @@ -3481,29 +3461,9 @@ ExposesCheckEpBB(POSITION *pos, COOR cBlock, COOR cKing); -// Three-way choice matching GetAttacks's own pattern (MIGRATION.md -// section 6): EXPOSESCHECK_BITBOARD defined -> the BB versions above; -// else the mailbox versions declared right above this block. -#if defined(EXPOSESCHECK_BITBOARD) #define ExposesCheck ExposesCheckBB #define FasterExposesCheck FasterExposesCheckBB #define ExposesCheckEp ExposesCheckEpBB -#endif - -// Real mailbox declarations first, unconditionally, so the plain names -// "IsAttacked"/"InCheck" are always genuinely declared -- the #define -// below only affects *later* call sites in files that include this -// header after this point, matching GetAttacks's own three-way-choice -// pattern (chess.h, `GetAttacks` block) exactly. Declaring these after -// the #define instead (an earlier mistake here, caught by testsee.c's -// TestIsAttackedBB needing to call the real mailbox function by name) -// would macro-substitute this very declaration too, leaving the real -// name never actually declared anywhere. -FLAG -IsAttacked(COOR cTest, POSITION *pos, ULONG uSide); - -FLAG -InCheck(POSITION *pos, ULONG uSide); FLAG IsAttackedBB(COOR cTest, POSITION *pos, ULONG uSide); @@ -3511,13 +3471,8 @@ IsAttackedBB(COOR cTest, POSITION *pos, ULONG uSide); FLAG FORCEINLINE InCheckBB(POSITION *pos, ULONG uSide); -// ISATTACKED_BITBOARD defined -> the BB versions above resolve plain -// "IsAttacked"/"InCheck" call sites from here on; else the mailbox -// versions declared just above stay live. -#if defined(ISATTACKED_BITBOARD) #define IsAttacked IsAttackedBB #define InCheck InCheckBB -#endif FLAG SanityCheckMove(POSITION *pos, MOVE mv); @@ -3675,8 +3630,6 @@ TestMakeUnmakeMove(void); extern const int g_iQKDeltas[9]; extern const int g_iNDeltas[9]; -extern const int g_iBDeltas[5]; -extern const int g_iRDeltas[5]; void GenerateMoves(SEARCHER_THREAD_CONTEXT *ctx, @@ -3703,45 +3656,23 @@ _RookAttacksBB(COOR c, BITBOARD bbOccupied); BITBOARD _BishopAttacksBB(COOR c, BITBOARD bbOccupied); -// generate.c's bitboard replacement for _FindUnblockedSquares, exposed -// non-static so testgenerate.c's correctness/speed harness can call it -// directly regardless of whether UNBLOCKED_BITBOARD is defined for -// this build. +// generate.c's bitboard build of pStack->bbUnblocked -- the mailbox +// original (and its DEBUG-build cross-check against this) is retired. void _FindUnblockedSquaresBB(MOVE_STACK *pStack, POSITION *pos); -// The mailbox original, exposed non-static (compiled whenever -// !UNBLOCKED_BITBOARD, or under DEBUG/TEST for the cross-check and -// the test harness above) under the same build conditions generate.c -// itself uses -- see generate.c. -#if !defined(UNBLOCKED_BITBOARD) || defined(DEBUG) || defined(TEST) -void -_FindUnblockedSquares(MOVE_STACK *pStack, - POSITION *pos); -#endif - void _GenerateRookBB(MOVE_STACK *pStack, POSITION *pos, COOR cRook); void -GenerateRook(MOVE_STACK *pStack, - POSITION *pos, - COOR cRook); - -void _GenerateBishopBB(MOVE_STACK *pStack, POSITION *pos, COOR cBishop); void -GenerateBishop(MOVE_STACK *pStack, - POSITION *pos, - COOR cBishop); - -void _GenerateQueenBB(MOVE_STACK *pStack, POSITION *pos, COOR cQueen); @@ -3802,40 +3733,15 @@ _SaveMeAllPawnMovesBB(MOVE_STACK *pStack, BITBOARD bbTargetMask); void -GenerateQueen(MOVE_STACK *pStack, - POSITION *pos, - COOR cQueen); - -void _GenerateKnightBB(MOVE_STACK *pStack, POSITION *pos, COOR cKnight); void -GenerateKnight(MOVE_STACK *pStack, - POSITION *pos, - COOR cKnight); - -void -GenerateWhiteKnight(MOVE_STACK *pStack, - POSITION *pos, - COOR cKnight); - -void _GenerateKingBB(MOVE_STACK *pStack, POSITION *pos, COOR cKing); -void -GenerateBlackKing(MOVE_STACK *pStack, - POSITION *pos, - COOR cKing); - -void -GenerateWhiteKing(MOVE_STACK *pStack, - POSITION *pos, - COOR cKing); - FLAG FORCEINLINE WouldGiveCheck(IN SEARCHER_THREAD_CONTEXT *ctx, IN MOVE mv); @@ -3860,21 +3766,6 @@ void TestNnueAccumulator(void); void -TestGenerateKnightSpeed(void); - -void -TestGenerateKingSpeed(void); - -void -TestGenerateRookSpeed(void); - -void -TestGenerateBishopSpeed(void); - -void -TestGenerateQueenSpeed(void); - -void TestGenerateAllMovesSpeed(void); void @@ -4903,9 +4794,6 @@ void TestGetAttacks(void); void -TestIsAttackedBB(void); - -void TestSeeFastSign(void); void @@ -695,6 +695,12 @@ Return value: // BITBOARD g_KnightAttacksBB[128]; +// Knight move deltas used only by InitializeKnightAttackTables below -- +// the mailbox knight generators that used to be its other consumers +// are retired (see generate.c's MOVEGEN_MIGRATION.md history). +const INT g_iNDeltas[] = { + -33, -31, -18, -14, +14, +18, +31, +33, 0 }; + void InitializeKnightAttackTables(void) /** diff --git a/src/generate.c b/src/generate.c index cf31891..3411474 100644 --- a/src/generate.c +++ b/src/generate.c @@ -56,114 +56,6 @@ Revision History: const INT g_iQKDeltas[] = { -17, -16, -15, -1, +1, +17, +15, +16, 0 }; -// Only needed as a standalone call site when UNBLOCKED_BITBOARD is -// off; under that toggle it's still referenced (DEBUG builds only) by -// _FindUnblockedSquaresBB's cross-check, so it can't simply be -// deleted yet -- see board_representation/MOVEGEN_MIGRATION.md. -#if !defined(UNBLOCKED_BITBOARD) || defined(DEBUG) || defined(TEST) -// Exposed non-static (like generate.c's other migration-era helpers, -// e.g. _GenerateRookBB) whenever anything other than this file might -// need it: the DEBUG cross-check inside _FindUnblockedSquaresBB below, -// or testgenerate.c's correctness/speed harness under TEST. -void -_FindUnblockedSquares(IN MOVE_STACK *pStack, - IN POSITION *pos) -/** - -Routine description: - - Given a position, find, count and mark all squares that are - unblocked for the king of the side not on move. For example: - - +---+---+---+---+- - - The squares marked with *'s in this - |***| K |***|*Q*| diagram are unblocked for the K. As - +---+---+---+---+- - - you can see, there are nine of them. - |*P*|***|***| | - +---+---+---+---+- - - Note the piece at the end of an unblocked - | |***| |*P*| ray from the king is on an unblocked - +---+---+---+---+- - - square (and its color doesn't matter) - | |*R*| | | - +---+---+---+---+- - - This stuff is used to flag checking moves - | | | | | as they are generated. - . . . . . - - Also note: this is one of the most called routines in the engine; - speed is of the essence here. - -Parameters: - - MOVE_STACK *pStack : move stack pointer - POSITION *pos : position pointer - -Return value: - - void - -**/ -{ - register ULONG u; - COOR cKing = KingPosition(pos, FLIP(pos->uToMove)); - COOR c; - ULONG uPly = pStack->uPly; -#ifdef DEBUG - PIECE pKing = pos->rgSquare[cKing].pPiece; - ULONG uCount = 0; - COOR cx; - - ASSERT(IS_ON_BOARD(cKing)); - ASSERT(IS_KING(pKing)); - ASSERT(GET_COLOR(pKing) != pos->uToMove); -#endif - - // - // Adjust the unblocked key value for this ply... - // - pStack->uUnblockedKeyValue[uPly]++; -#ifdef DEBUG - FOREACH_SQUARE(c) - { - if (!IS_ON_BOARD(c)) continue; - ASSERT(pStack->sUnblocked[uPly][c].uKey != - pStack->uUnblockedKeyValue[uPly]); - } -#endif - - u = 0; - ASSERT(g_iQKDeltas[u] != 0); - do - { - c = cKing + g_iQKDeltas[u]; - while(IS_ON_BOARD(c)) - { - pStack->sUnblocked[uPly][c].uKey = - pStack->uUnblockedKeyValue[uPly]; - pStack->sUnblocked[uPly][c].iPointer = -1 * g_iQKDeltas[u]; -#ifdef DEBUG - ASSERT(-g_iQKDeltas[u] == DIRECTION_BETWEEN_SQUARES(c,cKing)); - ASSERT(pStack->sUnblocked[uPly][c].iPointer != 0); - cx = c; - do - { - cx += pStack->sUnblocked[uPly][c].iPointer; - } - while(IS_ON_BOARD(cx) && (IS_EMPTY(pos->rgSquare[cx].pPiece))); - ASSERT(cx == cKing); - uCount++; -#endif - if (!IS_EMPTY(pos->rgSquare[c].pPiece)) - { - break; - } - c += g_iQKDeltas[u]; - } - u++; - } - while(g_iQKDeltas[u] != 0); - ASSERT(uCount > 2); - ASSERT(uCount < 28); -} -#endif // !defined(UNBLOCKED_BITBOARD) || defined(DEBUG) - // board_representation/MOVEGEN_MIGRATION.md's never-started // _FindUnblockedSquares/WouldGiveCheck candidate. Membership (is this // square unblocked for the opposing king) is exactly the set of @@ -211,43 +103,8 @@ Return value: pStack->bbUnblocked[uPly] = _RookAttacksBB(cKing, pos->bbOccupied) | _BishopAttacksBB(cKing, pos->bbOccupied); - -#ifdef DEBUG - { - // Cross-check against the mailbox walk every single call -- - // this is the correctness gate for bring-up, not a one-time - // test: membership must match exactly, and for every square - // the bitboard claims is unblocked, the direction back to the - // king recomputed via DIRECTION_BETWEEN_SQUARES must match - // what the mailbox ray walk actually stored. - COOR c; - BITBOARD bb = pStack->bbUnblocked[uPly]; - - _FindUnblockedSquares(pStack, pos); - FOREACH_SQUARE(c) - { - if (!IS_ON_BOARD(c)) continue; - FLAG fBB = (0 != (bb & COOR_TO_BB(c))); - FLAG fMailbox = (pStack->sUnblocked[uPly][c].uKey == - pStack->uUnblockedKeyValue[uPly]); - ASSERT(fBB == fMailbox); - if (fBB) - { - ASSERT(DIRECTION_BETWEEN_SQUARES(c, cKing) == - pStack->sUnblocked[uPly][c].iPointer); - } - } - // Restore: the mailbox call above just bumped - // uUnblockedKeyValue[uPly] a second time this ply, which is - // harmless (it's just a generation counter) but leave the - // real bbUnblocked value as computed above, not whatever the - // mailbox call's side effects might suggest. - pStack->bbUnblocked[uPly] = bb; - } -#endif } -#ifdef UNBLOCKED_BITBOARD // GenerateMoves no longer builds bbUnblocked[uPly] eagerly -- it just // bumps this ply's generation ticket (uUnblockedKeyValue[uPly], see // its comment in chess.h) so WouldGiveCheck's own lazy check (below) @@ -262,16 +119,6 @@ Return value: (0 != (pStack->bbUnblocked[ctx->uPly] & COOR_TO_BB(c))) #define DIR_FROM_SQ_TO_KING(c) \ DIRECTION_BETWEEN_SQUARES((c), xKing) -#else -// Mailbox path has no lazy build to defer to -- its own per-square -// sUnblocked[][].uKey scheme (SQUARE_IS_UNBLOCKED/DIR_FROM_SQ_TO_KING -// below) is written directly by _FindUnblockedSquares itself, so this -// stays eager, same as it always was. -#define MARK_UNBLOCKED_SQUARES_STALE(pStack, pos, uPly) \ - ((void)(uPly), _FindUnblockedSquares((pStack), (pos))) -#define SQUARE_IS_UNBLOCKED(c) (uUnblocked == kp[(c)].uKey) -#define DIR_FROM_SQ_TO_KING(c) (kp[(c)].iPointer) -#endif FLAG FORCEINLINE WouldGiveCheck(IN SEARCHER_THREAD_CONTEXT *ctx, @@ -282,18 +129,12 @@ Routine description: Determine whether a move is a checking move or not. - Under UNBLOCKED_BITBOARD (this build's active path): self- - sufficient -- builds bbUnblocked[ctx->uPly] itself, lazily, on the - first call for a given generation (see the lazy-rebuild block + Self-sufficient -- builds bbUnblocked[ctx->uPly] itself, lazily, on + the first call for a given generation (see the lazy-rebuild block below and chess.h's uUnblockedBuiltFor comment). GenerateMoves no longer builds it eagerly, only marks the generation stale (MARK_UNBLOCKED_SQUARES_STALE); callers no longer need to remember - to call _FindUnblockedSquares[BB] first. - - Under the mailbox fallback (no UNBLOCKED_BITBOARD): the older - precondition still holds -- _FindUnblockedSquares must have been - called at the start of generation (MARK_UNBLOCKED_SQUARES_STALE - still does this eagerly for that path, same as it always did). + to call _FindUnblockedSquaresBB first. Note: this is one of the most called codepaths in the engine, speed is of the essence here. @@ -320,10 +161,6 @@ Return value: COOR cFrom = mv.cFrom; PIECE pPiece = mv.pMoved; COOR xKing = KingPosition(pos, FLIP(GET_COLOR(pPiece))); -#ifndef UNBLOCKED_BITBOARD - KEY_POINTER *kp = &(pStack->sUnblocked[ctx->uPly][0]); - ULONG uUnblocked = pStack->uUnblockedKeyValue[ctx->uPly]; -#endif COOR cEpSquare; int iDelta; @@ -335,7 +172,6 @@ Return value: ASSERT(IS_ON_BOARD(cFrom)); ASSERT(pPiece); -#ifdef UNBLOCKED_BITBOARD // Lazy rebuild: GenerateMoves only bumps uUnblockedKeyValue[uPly] // (MARK_UNBLOCKED_SQUARES_STALE above) instead of building // bbUnblocked[uPly] eagerly. The first WouldGiveCheck call for a @@ -355,7 +191,6 @@ Return value: _FindUnblockedSquaresBB(pStack, pos); pStack->uUnblockedBuiltFor[ctx->uPly] = pStack->uUnblockedKeyValue[ctx->uPly]; } -#endif // // Phase 1: Does this move directly attack the king? @@ -794,100 +629,6 @@ Return value: ASSERT(SanityCheckMove(pos, pMvf->mv)); } - -const INT g_iNDeltas[] = { - -33, -31, -18, -14, +14, +18, +31, +33, 0 }; - -void -GenerateKnight(IN MOVE_STACK *pStack, - IN POSITION *pos, - IN COOR cKnight) -/** - -Routine description: - - This function is called by GenerateMoves' JumpTable. Its job - is to generate and push pseudo-legal knight moves for the - knight sitting on square cKnight. - -Parameters: - - MOVE_STACK *pStack : the move stack - POSITION *pos : the board position - COOR cKnight : the knight's location - -Return value: - - void - -**/ -{ - COOR c; - ULONG u = 0; - PIECE p; - - ASSERT(IS_KNIGHT(pos->rgSquare[cKnight].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cKnight].pPiece) == pos->uToMove); - ASSERT(g_iNDeltas[u] != 0); - - do - { - c = cKnight + g_iNDeltas[u]; - u++; - if (IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - - // - // This logical OR replaced with bitwise OR; the effect is - // the same the the bitwise is marginally faster. - // - if (IS_EMPTY(p) | (OPPOSITE_COLORS(p, pos->uToMove))) - { - _AddNormalMove(pStack, pos, cKnight, c, p); - } - } - } - while(0 != g_iNDeltas[u]); -} - -void -GenerateWhiteKnight(IN MOVE_STACK *pStack, - IN POSITION *pos, - IN COOR cKnight) -{ - COOR c; - ULONG u = 0; - PIECE p; - - ASSERT(IS_KNIGHT(pos->rgSquare[cKnight].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cKnight].pPiece) == pos->uToMove); - ASSERT(g_iNDeltas[u] != 0); - - do - { - c = cKnight + g_iNDeltas[u]; - u++; - if (IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - - // - // Note: this test covers empty squares also -- it is just - // testing that the low order bit (color bit) of p is zero - // which is true for black pieces and empty squares. This - // is done for speed over readability. - // - if (GET_COLOR(p) == BLACK) - { - ASSERT(IS_EMPTY(p) || (GET_COLOR(p) == BLACK)); - _AddNormalMove(pStack, pos, cKnight, c, p); - } - } - } - while(0 != g_iNDeltas[u]); -} - // // board_representation/MOVEGEN_MIGRATION.md section 3 step 1: bitboard // knight generator, the pilot function for the whole migration -- @@ -1095,82 +836,6 @@ Return value: } // -// These logical AND/ORs replaced with bitwise AND/OR; the effect is -// the same the the bitwise is marginally faster. -// -#define BLOCKS_THE_CHECK(sq) \ - ((iAttackDelta != 0) & \ - (iAttackDelta == DIRECTION_BETWEEN_SQUARES(cAttacker, (sq))) & \ - (((cAttacker > cKing) & ((sq) > cKing)) | \ - ((cAttacker < cKing) & ((sq) < cKing)))) - -void -SaveMeKnight(IN MOVE_STACK *pStack, - IN POSITION *pos, - IN COOR cKnight, - IN COOR cKing, - IN COOR cAttacker) -/** - -Routine description: - - This routine is called by GenerateEscapes' JumpTable. Its job is - to generate moves by the knight on square cKnight that alleviate - check to the king on square cKing. The (lone) piece attacking the - king is on square cAttacker. - -Parameters: - - MOVE_STACK *pStack : the move stack - POSITION *pos : the board position - COOR cKnight : the location of the knight - COOR cKing : the location of the friendly king - COOR cAttacker : the location of the checking piece - -Return value: - - void - -**/ -{ - COOR c, cExposed; - int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing); - ULONG u = 0; - PIECE p; - - ASSERT(InCheck(pos, pos->uToMove)); - ASSERT(IS_KNIGHT(pos->rgSquare[cKnight].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cKnight].pPiece) == pos->uToMove); - ASSERT(GET_COLOR(pos->rgSquare[cKnight].pPiece) == - GET_COLOR(pos->rgSquare[cKing].pPiece)); - ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece, - pos->rgSquare[cKing].pPiece)); - ASSERT(g_iNDeltas[u] != 0); - - do - { - c = cKnight + g_iNDeltas[u]; - u++; - if (IS_ON_BOARD(c)) - { - if ((c == cAttacker) || BLOCKS_THE_CHECK(c)) - { - cExposed = ExposesCheck(pos, cKnight, cKing); - if (!IS_ON_BOARD(cExposed) || (cExposed == c)) - { - p = pos->rgSquare[c].pPiece; - ASSERT(!p || - OPPOSITE_COLORS(p, pos->rgSquare[cKnight].pPiece)); - ASSERT(!p || (c == cAttacker)); - _AddNormalMove(pStack, pos, cKnight, c, p); - } - } - } - } - while(0 != g_iNDeltas[u]); -} - -// // board_representation/MOVEGEN_MIGRATION.md section 6a Phase 2: // bitboard equivalent of SaveMeKnight -- one AND against a // precomputed bbTargetMask replaces the per-square (c == cAttacker) || @@ -1233,117 +898,6 @@ Return value: } -const INT g_iBDeltas[] = { - -17, -15, +15, +17, 0 }; - -void -GenerateBishop(IN MOVE_STACK *pStack, - IN POSITION *pos, - IN COOR cBishop) -/** - -Routine description: - - This routine is called by GenerateMoves' JumpTable to generate - pseudo-legal bishop moves by the piece at cBishop. - -Parameters: - - MOVE_STACK *pStack : the move stack - POSITION *pos : the board position - COOR cBishop : the bishop location - -Return value: - - void - -**/ -{ - COOR c; - PIECE p; - - ASSERT(IS_BISHOP(pos->rgSquare[cBishop].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cBishop].pPiece) == pos->uToMove); - - // - // Note: manually unrolled loop - // - c = cBishop - 17; - while(IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - if (IS_EMPTY(p)) - { - _AddNormalMove(pStack, pos, cBishop, c, 0); - } - else - { - if (OPPOSITE_COLORS(p, pos->uToMove)) - { - _AddNormalMove(pStack, pos, cBishop, c, p); - } - break; - } - c += -17; - } - - c = cBishop - 15; - while(IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - if (IS_EMPTY(p)) - { - _AddNormalMove(pStack, pos, cBishop, c, 0); - } - else - { - if (OPPOSITE_COLORS(p, pos->uToMove)) - { - _AddNormalMove(pStack, pos, cBishop, c, p); - } - break; - } - c += -15; - } - - c = cBishop + 15; - while(IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - if (IS_EMPTY(p)) - { - _AddNormalMove(pStack, pos, cBishop, c, 0); - } - else - { - if (OPPOSITE_COLORS(p, pos->uToMove)) - { - _AddNormalMove(pStack, pos, cBishop, c, p); - } - break; - } - c += 15; - } - - c = cBishop + 17; - while(IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - if (IS_EMPTY(p)) - { - _AddNormalMove(pStack, pos, cBishop, c, 0); - } - else - { - if (OPPOSITE_COLORS(p, pos->uToMove)) - { - _AddNormalMove(pStack, pos, cBishop, c, p); - } - break; - } - c += 17; - } -} // // board_representation/MOVEGEN_MIGRATION.md section 3 step 3: @@ -1418,78 +972,6 @@ Return value: } -void -SaveMeBishop(IN MOVE_STACK *pStack, - IN POSITION *pos, - IN COOR cBishop, - IN COOR cKing, - IN COOR cAttacker) -/** - -Routine description: - - This routine is called by GenerateEscapes' JumpTable in order to - generate moves by the bishop at cBishop to alleviate check on its - friendly king (at cKing) which is being checked by a piece at - cAttacker. - -Parameters: - - MOVE_STACK *pStack : the move stack - POSITION *pos : the board position - COOR cBishop : the bishop's location - COOR cKing : the friendly king's location - COOR cAttacker : the checking piece's location - -Return value: - - void - -**/ -{ - int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing); - COOR c; - COOR cExposed; - ULONG u = 0; - PIECE p; - - ASSERT(InCheck(pos, pos->uToMove)); - ASSERT(IS_BISHOP(pos->rgSquare[cBishop].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cBishop].pPiece) == pos->uToMove); - ASSERT(GET_COLOR(pos->rgSquare[cBishop].pPiece) == - GET_COLOR(pos->rgSquare[cKing].pPiece)); - ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece, - pos->rgSquare[cKing].pPiece)); - ASSERT(g_iBDeltas[u] != 0); - - do - { - c = cBishop + g_iBDeltas[u]; - while(IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - if ((c == cAttacker) || BLOCKS_THE_CHECK(c)) - { - ASSERT(!p || - OPPOSITE_COLORS(p, pos->rgSquare[cBishop].pPiece)); - ASSERT(!p || (c == cAttacker)); - cExposed = ExposesCheck(pos, cBishop, cKing); - if (!IS_ON_BOARD(cExposed) || (cExposed == c)) - { - _AddNormalMove(pStack, pos, cBishop, c, p); - break; - } - } - else if (!IS_EMPTY(p)) - { - break; - } - c += g_iBDeltas[u]; - } - u++; - } - while(0 != g_iBDeltas[u]); -} // // board_representation/MOVEGEN_MIGRATION.md section 6a Phase 2: @@ -1551,118 +1033,6 @@ Return value: } -const INT g_iRDeltas[] = { - -1, +1, +16, -16, 0 }; - - -void -GenerateRook(IN MOVE_STACK *pStack, - IN POSITION *pos, - IN COOR cRook) -/** - -Routine description: - - This routine is called by GenerateMoves' JumpTable in order to - generate pseudo-legal moves for the rook at position cRook. - -Parameters: - - MOVE_STACK *pStack : the move stack - POSITION *pos : the board position - COOR cRook : the rook's location - -Return value: - - void - -**/ -{ - COOR c; - PIECE p; - - ASSERT(IS_ROOK(pos->rgSquare[cRook].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cRook].pPiece) == pos->uToMove); - - // - // Note: manually unrolled loop - // - c = cRook - 1; - while(IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - if (IS_EMPTY(p)) - { - _AddNormalMove(pStack, pos, cRook, c, 0); - } - else - { - if (OPPOSITE_COLORS(p, pos->uToMove)) - { - _AddNormalMove(pStack, pos, cRook, c, p); - } - break; - } - c += -1; - } - - c = cRook + 1; - while(IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - if (IS_EMPTY(p)) - { - _AddNormalMove(pStack, pos, cRook, c, 0); - } - else - { - if (OPPOSITE_COLORS(p, pos->uToMove)) - { - _AddNormalMove(pStack, pos, cRook, c, p); - } - break; - } - c += 1; - } - - c = cRook + 16; - while(IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - if (IS_EMPTY(p)) - { - _AddNormalMove(pStack, pos, cRook, c, 0); - } - else - { - if (OPPOSITE_COLORS(p, pos->uToMove)) - { - _AddNormalMove(pStack, pos, cRook, c, p); - } - break; - } - c += 16; - } - - c = cRook - 16; - while(IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - if (IS_EMPTY(p)) - { - _AddNormalMove(pStack, pos, cRook, c, 0); - } - else - { - if (OPPOSITE_COLORS(p, pos->uToMove)) - { - _AddNormalMove(pStack, pos, cRook, c, p); - } - break; - } - c += -16; - } -} // // board_representation/MOVEGEN_MIGRATION.md section 3 step 3: magic- @@ -1735,78 +1105,6 @@ Return value: } } -void -SaveMeRook(IN MOVE_STACK *pStack, - IN POSITION *pos, - IN COOR cRook, - IN COOR cKing, - IN COOR cAttacker) -/** - -Routine description: - - This routine is called by GenerateEscapes' JumpTable in order to - generate moves by the rook at location cRook that alleviate check - to the king at cKing. The lone checking piece is at cAttacker. - -Parameters: - - MOVE_STACK *pStack : the move stack - POSITION *pos : the board position - COOR cRook : the rook's location - COOR cKing : the king's location - COOR cAttacker : the checker's location - -Return value: - - void - -**/ -{ - int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing); - COOR c; - COOR cExposed; - ULONG u = 0; - PIECE p; - - ASSERT(InCheck(pos, pos->uToMove)); - ASSERT(IS_ROOK(pos->rgSquare[cRook].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cRook].pPiece) == pos->uToMove); - ASSERT(GET_COLOR(pos->rgSquare[cRook].pPiece) == pos->uToMove); - ASSERT(GET_COLOR(pos->rgSquare[cRook].pPiece) == - GET_COLOR(pos->rgSquare[cKing].pPiece)); - ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece, - pos->rgSquare[cKing].pPiece)); - ASSERT(g_iRDeltas[u] != 0); - - do - { - c = cRook + g_iRDeltas[u]; - while(IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - if ((c == cAttacker) || BLOCKS_THE_CHECK(c)) - { - ASSERT(!p || - OPPOSITE_COLORS(p, pos->rgSquare[cRook].pPiece)); - ASSERT(!p || (c == cAttacker)); - cExposed = ExposesCheck(pos, cRook, cKing); - if (!IS_ON_BOARD(cExposed) || (cExposed == c)) - { - _AddNormalMove(pStack, pos, cRook, c, p); - break; - } - } - else if (!IS_EMPTY(p)) - { - break; - } - c += g_iRDeltas[u]; - } - u++; - } - while(0 != g_iRDeltas[u]); -} // // board_representation/MOVEGEN_MIGRATION.md section 6a Phase 2: @@ -1868,61 +1166,6 @@ Return value: } -void -GenerateQueen(IN MOVE_STACK *pStack, - IN POSITION *pos, - IN COOR cQueen) -/** - -Routine description: - - This routine is called by GenerateMoves' JumpTable in order to - generate pseudo-legal moves for the queen at position cQueen. - -Parameters: - - MOVE_STACK *pStack : the move stack - POSITION *pos : the board position - COOR cQueen : the queen's location - -Return value: - - void - -**/ -{ - COOR c; - ULONG u = 0; - PIECE p; - - ASSERT(IS_QUEEN(pos->rgSquare[cQueen].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cQueen].pPiece) == pos->uToMove); - ASSERT(g_iQKDeltas[u] != 0); - - do - { - c = cQueen + g_iQKDeltas[u]; - while(IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - if (IS_EMPTY(p)) - { - _AddNormalMove(pStack, pos, cQueen, c, 0); - } - else - { - if (OPPOSITE_COLORS(p, pos->uToMove)) - { - _AddNormalMove(pStack, pos, cQueen, c, p); - } - break; - } - c += g_iQKDeltas[u]; - } - u++; - } - while(0 != g_iQKDeltas[u]); -} // // board_representation/MOVEGEN_MIGRATION.md section 3 step 4: queen is @@ -2001,79 +1244,6 @@ Return value: } -void -SaveMeQueen(IN MOVE_STACK *pStack, - IN POSITION *pos, - IN COOR cQueen, - IN COOR cKing, - IN COOR cAttacker) -/** - -Routine description: - - This routine is called by code in GenerateEscapes in order to - generate moves by the queen at square cQueen that alleviate check - on the friendly king at square cKing. The lone enemy piece that - is checking the king is sitting on square cAttacker. - -Parameters: - - MOVE_STACK *pStack : the move stack - POSITION *pos : the board position - COOR cQueen : the queen's location - COOR cKing : the friendly king's location - COOR cAttacker : the attacker's location - -Return value: - - void - -**/ -{ - COOR c; - COOR cExposed; - int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing); - ULONG u = 0; - PIECE p; - - ASSERT(InCheck(pos, pos->uToMove)); - ASSERT(IS_QUEEN(pos->rgSquare[cQueen].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cQueen].pPiece) == pos->uToMove); - ASSERT(GET_COLOR(pos->rgSquare[cQueen].pPiece) == pos->uToMove); - ASSERT(GET_COLOR(pos->rgSquare[cQueen].pPiece) == - GET_COLOR(pos->rgSquare[cKing].pPiece)); - ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece, - pos->rgSquare[cKing].pPiece)); - ASSERT(g_iQKDeltas[u] != 0); - - do - { - c = cQueen + g_iQKDeltas[u]; - while(IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - if ((c == cAttacker) || BLOCKS_THE_CHECK(c)) - { - ASSERT(!p || - OPPOSITE_COLORS(p, pos->rgSquare[cQueen].pPiece)); - ASSERT(!p || (c == cAttacker)); - cExposed = ExposesCheck(pos, cQueen, cKing); - if (!IS_ON_BOARD(cExposed) || (cExposed == c)) - { - _AddNormalMove(pStack, pos, cQueen, c, p); - break; - } - } - else if (!IS_EMPTY(p)) - { - break; - } - c += g_iQKDeltas[u]; - } - u++; - } - while(0 != g_iQKDeltas[u]); -} // // board_representation/MOVEGEN_MIGRATION.md section 6a Phase 2: @@ -2135,178 +1305,6 @@ Return value: } } -void -GenerateBlackKing(IN MOVE_STACK *pStack, - IN POSITION *pos, - IN COOR cKing) -/** - -Routine description: - - This function is called by GenerateMoves in order to generate - pseudo-legal moves for the black king at square cKing. Note: this - function often generates illegal castling moves and relies on the - MakeMove code to decide about the legality of the castle. - -Parameters: - - MOVE_STACK *pStack : the move stack - POSITION *pos : the board position - COOR cKing : the king location - -Return value: - - void - -**/ -{ - COOR c; - ULONG u = 0; - PIECE p; - - ASSERT(IS_KING(pos->rgSquare[cKing].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cKing].pPiece) == pos->uToMove); - ASSERT(g_iQKDeltas[u] != 0); - - do - { - c = cKing + g_iQKDeltas[u]; - u++; - if (IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - - // - // This logical OR replaced with bitwise OR; the effect is - // the same the the bitwise is marginally faster. - // - if (IS_EMPTY(p) | (GET_COLOR(p))) - { - _AddNormalMove(pStack, pos, cKing, c, p); - } - } - } - while(0 != g_iQKDeltas[u]); - - if ((pos->bvCastleInfo & BLACK_CAN_CASTLE) == 0) return; -#ifdef DEBUG - p = pos->rgSquare[E8].pPiece; - ASSERT(IS_KING(p)); - ASSERT(cKing == E8); -#endif - - // - // Note: no castling through check is enforced at the time the - // move is played. This is a "pseudo-legal" move when generated. - // - if ((pos->bvCastleInfo & CASTLE_BLACK_SHORT) && - (IS_EMPTY(pos->rgSquare[G8].pPiece)) && - (IS_EMPTY(pos->rgSquare[F8].pPiece))) - { - ASSERT(pos->rgSquare[H8].pPiece == BLACK_ROOK); - _AddCastle(pStack, pos, E8, G8); - } - - if ((pos->bvCastleInfo & CASTLE_BLACK_LONG) && - (IS_EMPTY(pos->rgSquare[C8].pPiece)) && - (IS_EMPTY(pos->rgSquare[D8].pPiece)) && - (IS_EMPTY(pos->rgSquare[B8].pPiece))) - { - ASSERT(pos->rgSquare[A8].pPiece == BLACK_ROOK); - _AddCastle(pStack, pos, E8, C8); - } -} - -// -// N.B. There is no SaveYourselfBlackKing or SaveYourselfWhiteKing -// because this functionality is built into phase 1 of -// GenerateEscapes. -// - -void -GenerateWhiteKing(IN MOVE_STACK *pStack, - IN POSITION *pos, - IN COOR cKing) -/** - -Routine description: - - This function is called by GenerateMoves in order to generate - pseudo-legal king moves by the white king at square cKing. Note: - it often generates illegal castling moves and relies on the code - in MakeMove to determine the legality of castles. - -Parameters: - - MOVE_STACK *pStack : the move stack - POSITION *pos : the board position - COOR cKing : the king location - -Return value: - - void - -**/ -{ - COOR c; - ULONG u = 0; - PIECE p; - - ASSERT(IS_KING(pos->rgSquare[cKing].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cKing].pPiece) == pos->uToMove); - ASSERT(g_iQKDeltas[u] != 0); - - do - { - c = cKing + g_iQKDeltas[u]; - u++; - if (IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - - // - // Note: this test covers empty squares also -- it is just - // testing that the low order bit (color bit) of p is zero - // which is true for black pieces and empty squares. This - // is done for speed over readability. - // - if (GET_COLOR(p) == BLACK) - { - ASSERT(IS_EMPTY(p) || (GET_COLOR(p) == BLACK)); - _AddNormalMove(pStack, pos, cKing, c, p); - } - } - } - while(0 != g_iQKDeltas[u]); - - if ((pos->bvCastleInfo & WHITE_CAN_CASTLE) == 0) return; -#ifdef DEBUG - p = pos->rgSquare[E1].pPiece; - ASSERT(IS_KING(p)); - ASSERT(cKing == E1); -#endif - - // - // Note: no castling through check is enforced at the time the - // move is played. This is a "pseudo-legal" move when generated. - // - if ((pos->bvCastleInfo & CASTLE_WHITE_SHORT) && - (IS_EMPTY(pos->rgSquare[G1].pPiece)) && - (IS_EMPTY(pos->rgSquare[F1].pPiece))) - { - ASSERT(pos->rgSquare[H1].pPiece == WHITE_ROOK); - _AddCastle(pStack, pos, E1, G1); - } - - if ((pos->bvCastleInfo & CASTLE_WHITE_LONG) && - (IS_EMPTY(pos->rgSquare[C1].pPiece)) && - (IS_EMPTY(pos->rgSquare[B1].pPiece)) && - (IS_EMPTY(pos->rgSquare[D1].pPiece))) - { - ASSERT(pos->rgSquare[A1].pPiece == WHITE_ROOK); - _AddCastle(pStack, pos, E1, C1); - } -} // // board_representation/MOVEGEN_MIGRATION.md section 3 step 2: bitboard @@ -2425,406 +1423,6 @@ Return value: } } - -void -GenerateWhitePawn(IN MOVE_STACK *pStack, - IN POSITION *pos, - IN COOR cPawn) -/** - -Routine description: - - This function is called by GenerateMoves in order to generate - pseudo-legal pawn moves by the pawn at cPawn. It handles - en passant captures, double jumps, promotions, etc... - -Parameters: - - MOVE_STACK *pStack : the move stack - POSITION *pos : the board position - COOR cPawn : the pawn's location - -Return value: - - void - -**/ -{ - ULONG uRank = RANK(cPawn); - PIECE p; - COOR cTo; - - ASSERT(IS_PAWN(pos->rgSquare[cPawn].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove); - ASSERT((RANK(cPawn) >= 2) && (RANK(cPawn) <= 7)); - - if (uRank != 7) - { - cTo = cPawn - 16; - if (IS_EMPTY(pos->rgSquare[cTo].pPiece)) - { - _AddNormalMove(pStack, pos, cPawn, cTo, 0); - if (uRank == 2) - { - cTo = cPawn - 32; - if (IS_EMPTY(pos->rgSquare[cTo].pPiece)) - { - _AddDoubleJump(pStack, pos, cPawn, cTo); - } - } - } - cTo = cPawn - 15; - if (IS_ON_BOARD(cTo)) - { - if (cTo == pos->cEpSquare) - { - _AddEnPassant(pStack, pos, cPawn, cTo); - } - p = pos->rgSquare[cTo].pPiece; - - // - // This logical AND replaced with bitwise AND; the effect - // is the same the the bitwise is marginally faster. - // - if (!IS_EMPTY(p) & (GET_COLOR(p) == BLACK)) - { - _AddNormalMove(pStack, pos, cPawn, cTo, p); - } - } - cTo = cPawn - 17; - if (IS_ON_BOARD(cTo)) - { - if (cTo == pos->cEpSquare) - { - _AddEnPassant(pStack, pos, cPawn, cTo); - } - p = pos->rgSquare[cTo].pPiece; - - // - // This logical AND replaced with bitwise AND; the effect - // is the same the the bitwise is marginally faster. - // - if (!IS_EMPTY(p) & (GET_COLOR(p) == BLACK)) - { - _AddNormalMove(pStack, pos, cPawn, cTo, p); - } - } - } - else - { - ASSERT(RANK7(cPawn)); - cTo = cPawn - 16; - if (IS_EMPTY(pos->rgSquare[cTo].pPiece)) - { - _AddPromote(pStack, pos, cPawn, cTo); - } - cTo = cPawn - 15; - if (IS_ON_BOARD(cTo)) - { - p = pos->rgSquare[cTo].pPiece; - - // - // This logical AND replaced with bitwise AND; the effect - // is the same the the bitwise is marginally faster. - // - if (!IS_EMPTY(p) & (GET_COLOR(p) == BLACK)) - { - _AddPromote(pStack, pos, cPawn, cTo); - } - } - cTo = cPawn - 17; - if (IS_ON_BOARD(cTo)) - { - p = pos->rgSquare[cTo].pPiece; - - // - // This logical AND replaced with bitwise AND; the effect - // is the same the the bitwise is marginally faster. - // - if (!IS_EMPTY(p) & (GET_COLOR(p) == BLACK)) - { - _AddPromote(pStack, pos, cPawn, cTo); - } - } - } -} - - -void -SaveMeWhitePawn(IN MOVE_STACK *pStack, - IN POSITION *pos, - IN COOR cPawn, - IN COOR cKing, - IN COOR cAttacker) -/** - -Routine description: - - This function is called by the GenerateEscapes code in order to - ask for a pawn's help in alleviating check on the king at square - cKing. The lone attacker to said king is on square cAttacker. - -Parameters: - - MOVE_STACK *pStack : the move stack - POSITION *pos : the board position - COOR cPawn : the pawn's location - COOR cKing : the friendly king's location - COOR cAttacker : the location of the lone checker to the king - -Return value: - - void - -**/ -{ - ULONG uRank = RANK(cPawn); - PIECE p; - COOR cTo; - COOR cExposed; - int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing); - - ASSERT(InCheck(pos, pos->uToMove)); - ASSERT(IS_PAWN(pos->rgSquare[cPawn].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == WHITE); - ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove); - ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove); - ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == - GET_COLOR(pos->rgSquare[cKing].pPiece)); - ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece, - pos->rgSquare[cKing].pPiece)); - ASSERT((RANK(cPawn) >= 2) && (RANK(cPawn) <= 7)); - - if (uRank != 7) - { - cTo = cPawn - 16; - if (IS_EMPTY(pos->rgSquare[cTo].pPiece)) - { - if (BLOCKS_THE_CHECK(cTo) && - !IS_ON_BOARD(ExposesCheck(pos, cPawn, cKing))) - { - _AddNormalMove(pStack, pos, cPawn, cTo, 0); - } - - if (uRank == 2) - { - cTo = cPawn - 32; - if (IS_EMPTY(pos->rgSquare[cTo].pPiece) && - BLOCKS_THE_CHECK(cTo) && - !IS_ON_BOARD(ExposesCheck(pos, cPawn, cKing))) - { - _AddDoubleJump(pStack, pos, cPawn, cTo); - } - } - } - - cTo = cPawn - 15; - if (cTo == cAttacker) - { - cExposed = ExposesCheck(pos, cPawn, cKing); - if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker)) - { - p = pos->rgSquare[cTo].pPiece; - ASSERT(!IS_EMPTY(p)); - ASSERT(GET_COLOR(p) == BLACK); - ASSERT(IS_ON_BOARD(cTo)); - _AddNormalMove(pStack, pos, cPawn, cTo, p); - } - } - - // - // N.B. There is no possible way to block check with an - // en-passant capture because by definition the last move made - // by the other side was the pawn double jump. So only handle - // if the double jumping enemy pawn is the attacker and we can - // kill it en passant. - // - if ((cTo == pos->cEpSquare) && (cAttacker == cTo + 16)) - { - _AddEnPassant(pStack, pos, cPawn, cTo); - } - - cTo = cPawn - 17; - if (cTo == cAttacker) - { - cExposed = ExposesCheck(pos, cPawn, cKing); - if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker)) - { - p = pos->rgSquare[cTo].pPiece; - ASSERT(!IS_EMPTY(p)); - ASSERT(GET_COLOR(p) == BLACK); - ASSERT(IS_ON_BOARD(cTo)); - _AddNormalMove(pStack, pos, cPawn, cTo, p); - } - } - if ((cTo == pos->cEpSquare) && (cAttacker == cTo + 16)) - { - _AddEnPassant(pStack, pos, cPawn, cTo); - } - } - else - { - ASSERT(RANK7(cPawn)); - cTo = cPawn - 16; - if (BLOCKS_THE_CHECK(cTo) && IS_EMPTY(pos->rgSquare[cTo].pPiece)) - { - _AddPromote(pStack, pos, cPawn, cTo); - } - - cTo = cPawn - 15; - if (cTo == cAttacker) - { - cExposed = ExposesCheck(pos, cPawn, cKing); - if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker)) - { - p = pos->rgSquare[cTo].pPiece; - ASSERT(!IS_EMPTY(p)); - ASSERT(GET_COLOR(p) == BLACK); - ASSERT(IS_ON_BOARD(cTo)); - _AddPromote(pStack, pos, cPawn, cTo); - } - } - - cTo = cPawn - 17; - if (cTo == cAttacker) - { - cExposed = ExposesCheck(pos, cPawn, cKing); - if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker)) - { - p = pos->rgSquare[cTo].pPiece; - ASSERT(!IS_EMPTY(p)); - ASSERT(GET_COLOR(p) == BLACK); - ASSERT(IS_ON_BOARD(cTo)); - _AddPromote(pStack, pos, cPawn, cTo); - } - } - } -} - - -void -GenerateBlackPawn(IN MOVE_STACK *pStack, - IN POSITION *pos, - IN COOR cPawn) -/** - -Routine description: - - This code is called by GenerateMoves in order to handle move - generation for the black pawn on square cPawn. It handles - en passant captures, double jumps, and promotion. - -Parameters: - - MOVE_STACK *pStack : the move stack - POSITION *pos : the position - COOR cPawn : the pawn location - -Return value: - - void - -**/ -{ - ULONG uRank = RANK(cPawn); - PIECE p; - COOR cTo; - - ASSERT(IS_PAWN(pos->rgSquare[cPawn].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove); - ASSERT((RANK(cPawn) >= 2) && (RANK(cPawn) <= 7)); - - if (uRank != 2) - { - cTo = cPawn + 16; - if (IS_EMPTY(pos->rgSquare[cTo].pPiece)) - { - _AddNormalMove(pStack, pos, cPawn, cTo, 0); - if (uRank == 7) - { - cTo = cPawn + 32; - if (IS_EMPTY(pos->rgSquare[cTo].pPiece)) - { - _AddDoubleJump(pStack, pos, cPawn, cTo); - } - } - } - cTo = cPawn + 15; - if (IS_ON_BOARD(cTo)) - { - if (cTo == pos->cEpSquare) - { - _AddEnPassant(pStack, pos, cPawn, cTo); - } - p = pos->rgSquare[cTo].pPiece; - - // - // This logical AND replaced with bitwise AND; the effect - // is the same the the bitwise is marginally faster. - // - if (!IS_EMPTY(p) & (GET_COLOR(p))) - { - _AddNormalMove(pStack, pos, cPawn, cTo, p); - } - } - cTo = cPawn + 17; - if (IS_ON_BOARD(cTo)) - { - if (cTo == pos->cEpSquare) - { - _AddEnPassant(pStack, pos, cPawn, cTo); - } - p = pos->rgSquare[cTo].pPiece; - - // - // This logical AND replaced with bitwise AND; the effect - // is the same the the bitwise is marginally faster. - // - if (!IS_EMPTY(p) & (GET_COLOR(p))) - { - _AddNormalMove(pStack, pos, cPawn, cTo, p); - } - } - } - else - { - ASSERT(RANK2(cPawn)); - cTo = cPawn + 16; - if (IS_EMPTY(pos->rgSquare[cTo].pPiece)) - { - _AddPromote(pStack, pos, cPawn, cTo); - } - cTo = cPawn + 15; - if (IS_ON_BOARD(cTo)) - { - p = pos->rgSquare[cTo].pPiece; - - // - // This logical AND replaced with bitwise AND; the effect - // is the same the the bitwise is marginally faster. - // - if (!IS_EMPTY(p) & (GET_COLOR(p))) - { - _AddPromote(pStack, pos, cPawn, cTo); - } - } - cTo = cPawn + 17; - if (IS_ON_BOARD(cTo)) - { - p = pos->rgSquare[cTo].pPiece; - - // - // This logical AND replaced with bitwise AND; the effect - // is the same the the bitwise is marginally faster. - // - if (!IS_EMPTY(p) & (GET_COLOR(p))) - { - _AddPromote(pStack, pos, cPawn, cTo); - } - } - } -} - // // board_representation/MOVEGEN_MIGRATION.md section 3 step 5: bulk, // whole-side pawn generator using the classic shift-and-mask technique @@ -3033,160 +1631,6 @@ Return value: } -void -SaveMeBlackPawn(IN MOVE_STACK *pStack, - IN POSITION *pos, - IN COOR cPawn, - IN COOR cKing, - IN COOR cAttacker) -/** - -Routine description: - - This code is called to ask for a black pawn's help in alleviating - check to its king. The pawn is on square cPawn. The friendly - king is on square cKing. The lone enemy piece attacking it is on - square cAttacker. - -Parameters: - - MOVE_STACK *pStack : the move stack - POSITION *pos : the board position - COOR cPawn : the pawn location - COOR cKing : the friendly king location - COOR cAttacker : the lone checker location - -Return value: - - void - -**/ -{ - ULONG uRank = RANK(cPawn); - PIECE p; - COOR cTo; - COOR cExposed; - int iAttackDelta = DIRECTION_BETWEEN_SQUARES(cAttacker, cKing); - - ASSERT(InCheck(pos, pos->uToMove)); - ASSERT(IS_PAWN(pos->rgSquare[cPawn].pPiece)); - ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == BLACK); - ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove); - ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == pos->uToMove); - ASSERT(GET_COLOR(pos->rgSquare[cPawn].pPiece) == - GET_COLOR(pos->rgSquare[cKing].pPiece)); - ASSERT(OPPOSITE_COLORS(pos->rgSquare[cAttacker].pPiece, - pos->rgSquare[cKing].pPiece)); - ASSERT((RANK(cPawn) >= 2) && (RANK(cPawn) <= 7)); - - if (uRank != 2) - { - cTo = cPawn + 16; - if (IS_EMPTY(pos->rgSquare[cTo].pPiece)) - { - if (BLOCKS_THE_CHECK(cTo) && - !IS_ON_BOARD(ExposesCheck(pos, cPawn, cKing))) - { - _AddNormalMove(pStack, pos, cPawn, cTo, 0); - } - - if (uRank == 7) - { - cTo = cPawn + 32; - if (IS_EMPTY(pos->rgSquare[cTo].pPiece) && - BLOCKS_THE_CHECK(cTo) && - !IS_ON_BOARD(ExposesCheck(pos, cPawn, cKing))) - { - _AddDoubleJump(pStack, pos, cPawn, cTo); - } - } - } - - cTo = cPawn + 15; - if (cTo == cAttacker) - { - cExposed = ExposesCheck(pos, cPawn, cKing); - if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker)) - { - p = pos->rgSquare[cTo].pPiece; - ASSERT(!IS_EMPTY(p)); - ASSERT(GET_COLOR(p) == WHITE); - ASSERT(IS_ON_BOARD(cTo)); - _AddNormalMove(pStack, pos, cPawn, cTo, p); - } - } - - // - // N.B. There is no possible way to block check with an - // en-passant capture because by definition the last move made - // by the other side was the pawn double jump. So only handle - // if the double jumping enemy pawn is the attacker and we can - // kill it en passant. - // - if ((cTo == pos->cEpSquare) && - (cAttacker == cTo - 16)) - { - _AddEnPassant(pStack, pos, cPawn, cTo); - } - - cTo = cPawn + 17; - if (cTo == cAttacker) - { - cExposed = ExposesCheck(pos, cPawn, cKing); - if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker)) - { - p = pos->rgSquare[cTo].pPiece; - ASSERT(!IS_EMPTY(p)); - ASSERT(GET_COLOR(p) == WHITE); - ASSERT(IS_ON_BOARD(cTo)); - _AddNormalMove(pStack, pos, cPawn, cTo, p); - } - } - if ((cTo == pos->cEpSquare) && - (cAttacker == cTo - 16)) - { - _AddEnPassant(pStack, pos, cPawn, cTo); - } - } - else - { - ASSERT(RANK2(cPawn)); - cTo = cPawn + 16; - if (BLOCKS_THE_CHECK(cTo) && - IS_EMPTY(pos->rgSquare[cTo].pPiece)) - { - _AddPromote(pStack, pos, cPawn, cTo); - } - - cTo = cPawn + 15; - if (cTo == cAttacker) - { - cExposed = ExposesCheck(pos, cPawn, cKing); - if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker)) - { - p = pos->rgSquare[cTo].pPiece; - ASSERT(!IS_EMPTY(p)); - ASSERT(GET_COLOR(p) == WHITE); - ASSERT(IS_ON_BOARD(cTo)); - _AddPromote(pStack, pos, cPawn, cTo); - } - } - - cTo = cPawn + 17; - if (cTo == cAttacker) - { - cExposed = ExposesCheck(pos, cPawn, cKing); - if (!IS_ON_BOARD(cExposed) || (cExposed == cAttacker)) - { - p = pos->rgSquare[cTo].pPiece; - ASSERT(!IS_EMPTY(p)); - ASSERT(GET_COLOR(p) == WHITE); - ASSERT(IS_ON_BOARD(cTo)); - _AddPromote(pStack, pos, cPawn, cTo); - } - } - } -} // // board_representation/MOVEGEN_MIGRATION.md section 6a Phase 2: bulk @@ -3969,45 +2413,19 @@ Return value: InvalidGenerator, // EMPTY | WHITE InvalidGenerator, // 2 (BLACK_PAWN) InvalidGenerator, // 3 (WHITE_PAWN) - // MOVEGEN_MIGRATION.md section 6 toggle -- one #define per - // piece type, independent of the others. _GenerateKnightBB - // serves both slots; see its header comment for why the - // mailbox pair's color split doesn't carry over to bitboards. -#if defined(GENERATE_KNIGHT_BITBOARD) + // _GenerateKnightBB serves both slots; see its header comment + // for why the mailbox pair's old color split doesn't carry + // over to bitboards. _GenerateKnightBB, // 4 (BLACK_KNIGHT) _GenerateKnightBB, // 5 (WHITE_KNIGHT) -#else - GenerateKnight, // 4 (BLACK_KNIGHT) - GenerateWhiteKnight, // 5 (WHITE_KNIGHT) -#endif -#if defined(GENERATE_BISHOP_BITBOARD) _GenerateBishopBB, // 6 (BLACK_BISHOP) _GenerateBishopBB, // 7 (WHITE_BISHOP) -#else - GenerateBishop, // 6 (BLACK_BISHOP) - GenerateBishop, // 7 (WHITE_BISHOP) -#endif -#if defined(GENERATE_ROOK_BITBOARD) _GenerateRookBB, // 8 (BLACK_ROOK) _GenerateRookBB, // 9 (WHITE_ROOK) -#else - GenerateRook, // 8 (BLACK_ROOK) - GenerateRook, // 9 (WHITE_ROOK) -#endif -#if defined(GENERATE_QUEEN_BITBOARD) _GenerateQueenBB, // 10 (BLACK_QUEEN) _GenerateQueenBB, // 11 (WHITE_QUEEN) -#else - GenerateQueen, // 10 (BLACK_QUEEN) - GenerateQueen, // 11 (WHITE_QUEEN) -#endif -#if defined(GENERATE_KING_BITBOARD) _GenerateKingBB, // 12 (BLACK_KING) _GenerateKingBB // 13 (WHITE_KING) -#else - GenerateBlackKing, // 12 (BLACK_KING) - GenerateWhiteKing // 13 (WHITE_KING) -#endif }; #ifdef DEBUG PIECE p; @@ -4017,17 +2435,9 @@ Return value: // _GenerateKnightBB's header comment: computed once per node here, // before any piece-type dispatch, so every bitboard-backed // generator invoked below shares this build instead of each - // recomputing it -- extend this #if with each new - // GENERATE_*_BITBOARD toggle as piece types migrate. -#if defined(GENERATE_KNIGHT_BITBOARD) || defined(GENERATE_KING_BITBOARD) || \ - defined(GENERATE_ROOK_BITBOARD) || defined(GENERATE_BISHOP_BITBOARD) || \ - defined(GENERATE_QUEEN_BITBOARD) + // recomputing it. pStack->bbFriendlyOccupied = _BuildFriendlySideBB(pos, pos->uToMove); -#endif -#if defined(GENERATE_ROOK_BITBOARD) || defined(GENERATE_BISHOP_BITBOARD) || \ - defined(GENERATE_QUEEN_BITBOARD) pStack->bbOccupied = pos->bbOccupied; -#endif { BITBOARD bbNonPawns = NON_KING_PIECES_BB(pos, pos->uToMove) | @@ -4045,39 +2455,7 @@ Return value: } } -#if defined(GENERATE_PAWN_BITBOARD) _GenerateAllPawnMovesBB(pStack, pos, pos->uToMove); -#else - if (pos->uToMove == BLACK) - { - BITBOARD bbPawnsToWalk = pos->bbPawns[BLACK]; - - while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbPawnsToWalk))) - { -#ifdef DEBUG - p = pos->rgSquare[c].pPiece; - ASSERT(!IS_EMPTY(p)); - ASSERT(IS_PAWN(p)); - ASSERT(GET_COLOR(p) == pos->uToMove); -#endif - GenerateBlackPawn(pStack, pos, c); - } - } else { - BITBOARD bbPawnsToWalk = pos->bbPawns[WHITE]; - - ASSERT(pos->uToMove == WHITE); - while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbPawnsToWalk))) - { -#ifdef DEBUG - p = pos->rgSquare[c].pPiece; - ASSERT(!IS_EMPTY(p)); - ASSERT(IS_PAWN(p)); - ASSERT(GET_COLOR(p) == pos->uToMove); -#endif - GenerateWhitePawn(pStack, pos, c); - } - } -#endif } // @@ -4148,9 +2526,6 @@ Return value: BITBOARD bb; ULONG uBitIndex; COOR c; -#if defined(DEBUG) && !defined(GENERATE_PAWN_BITBOARD) - PIECE p; -#endif pStack->bbFriendlyOccupied = _BuildFriendlySideBB(pos, uSide); pStack->bbOccupied = pos->bbOccupied; @@ -4203,59 +2578,17 @@ Return value: ASSERT(IS_KING(pos->rgSquare[c].pPiece)); _GenerateKingBB(pStack, pos, c); - // Pawns: unchanged from _GenerateAllMoves -- not in scope for this - // migration (section 3 step 5's pawn note) unless - // GENERATE_PAWN_BITBOARD is also defined, in which case pawns get - // the same bulk treatment via _GenerateAllPawnMovesBB -- pawns' - // own toggle is independent of the five above (section 6). -#if defined(GENERATE_PAWN_BITBOARD) + // Pawns get the same bulk treatment via _GenerateAllPawnMovesBB. _GenerateAllPawnMovesBB(pStack, pos, uSide); -#else - if (uSide == BLACK) - { - BITBOARD bbPawnsToWalk = pos->bbPawns[BLACK]; - - while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbPawnsToWalk))) - { -#ifdef DEBUG - p = pos->rgSquare[c].pPiece; - ASSERT(!IS_EMPTY(p)); - ASSERT(IS_PAWN(p)); - ASSERT(GET_COLOR(p) == uSide); -#endif - GenerateBlackPawn(pStack, pos, c); - } - } - else - { - BITBOARD bbPawnsToWalk = pos->bbPawns[WHITE]; - - ASSERT(uSide == WHITE); - while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbPawnsToWalk))) - { -#ifdef DEBUG - p = pos->rgSquare[c].pPiece; - ASSERT(!IS_EMPTY(p)); - ASSERT(IS_PAWN(p)); - ASSERT(GET_COLOR(p) == uSide); -#endif - GenerateWhitePawn(pStack, pos, c); - } - } -#endif } // Whole-dispatch fork -- see _GenerateAllMovesBB's block comment for // why this is a #define swap of the entire function (matching // chess.h's GetAttacks precedent) rather than a branch nested inside -// _GenerateAllMoves. Only fires when every non-pawn, non-castling -// piece type is bitboard-backed; any partial mix still uses -// _GenerateAllMoves's cNonPawns/JumpTable path unchanged. -#if defined(GENERATE_KNIGHT_BITBOARD) && defined(GENERATE_KING_BITBOARD) && \ - defined(GENERATE_ROOK_BITBOARD) && defined(GENERATE_BISHOP_BITBOARD) && \ - defined(GENERATE_QUEEN_BITBOARD) +// _GenerateAllMoves. _GenerateAllMoves itself (the JumpTable/cNonPawns +// version above) is kept only as testgenerate.c's named comparison +// point -- production always goes through this redirect. #define _GenerateAllMoves _GenerateAllMovesBB -#endif static ULONG @@ -4285,47 +2618,12 @@ Return value: **/ { -#if !defined(GENERATE_ESCAPES_KING_BITBOARD) || !defined(GENERATE_ESCAPES_BLOCK_BITBOARD) - ULONG u; -#endif -#if !defined(GENERATE_ESCAPES_KING_BITBOARD) - ULONG v; -#endif COOR c; -#if !defined(GENERATE_ESCAPES_BLOCK_BITBOARD) - COOR cDefender; -#endif PIECE p; PIECE pKing; COOR cKing = KingPosition(pos, pos->uToMove); -#if !defined(GENERATE_ESCAPES_KING_BITBOARD) - int iIndex; -#endif SEE_LIST rgCheckers; -#if !defined(GENERATE_ESCAPES_KING_BITBOARD) - int iDelta; -#endif ULONG uReturn = 0; -#if !defined(GENERATE_ESCAPES_BLOCK_BITBOARD) - static void (*JumpTable[]) - (MOVE_STACK *, POSITION *, COOR, COOR, COOR) = - { - InvalidSaveMe, // EMPTY - InvalidSaveMe, // EMPTY | WHITE - InvalidSaveMe, // 2 (BLACK_PAWN) - InvalidSaveMe, // 3 (WHITE_PAWN) - SaveMeKnight, // 4 (BLACK_KNIGHT) - SaveMeKnight, // 5 (WHITE_KNIGHT) - SaveMeBishop, // 6 (BLACK_BISHOP) - SaveMeBishop, // 7 (WHITE_BISHOP) - SaveMeRook, // 8 (BLACK_ROOK) - SaveMeRook, // 9 (WHITE_ROOK) - SaveMeQueen, // 10 (BLACK_QUEEN) - SaveMeQueen, // 11 (WHITE_QUEEN) - InvalidSaveMe, // kings already considered - InvalidSaveMe // kings already considered - }; -#endif ASSERT(IS_KING(pos->rgSquare[cKing].pPiece)); ASSERT(TRUE == InCheck(pos, pos->uToMove)); @@ -4349,7 +2647,6 @@ Return value: ASSERT(GET_COLOR(pKing) == pos->uToMove); ASSERT(IS_KING(pKing)); ASSERT(OPPOSITE_COLORS(pKing, rgCheckers.data[0].pPiece)); -#if defined(GENERATE_ESCAPES_KING_BITBOARD) // // board_representation/MOVEGEN_MIGRATION.md section 6a Phase 1: // g_KingAttacksBB gives every candidate flight square in one @@ -4392,69 +2689,6 @@ Return value: } } } -#else - u = 0; - while(0 != g_iQKDeltas[u]) - { - c = cKing + g_iQKDeltas[u]; - u++; - if (IS_ON_BOARD(c)) - { - p = pos->rgSquare[c].pPiece; - - // - // This logical OR replaced with bitwise OR; the effect is - // the same the the bitwise is marginally faster. - // - if (IS_EMPTY(p) | (OPPOSITE_COLORS(p, pKing))) - { - if (FALSE == IsAttacked(c, pos, FLIP(pos->uToMove))) - { - // - // So we know the destination square is not under - // attack right now... but that could be because - // the present (pre-move) king position "blocks" - // the attack. Detect this. - // - for (v = 0; - v < rgCheckers.uCount; - v++) - { - ASSERT(OPPOSITE_COLORS(pKing, - rgCheckers.data[v].pPiece)); - if (!IS_PAWN(rgCheckers.data[v].pPiece) && - !IS_KNIGHT(rgCheckers.data[v].pPiece)) - { - iIndex = (int)rgCheckers.data[v].cLoc - - (int)cKing; - iDelta = CHECK_DELTA_WITH_INDEX(iIndex); - ASSERT(iDelta); - - iIndex = (int)rgCheckers.data[v].cLoc - - (int)c; - if (iDelta == CHECK_DELTA_WITH_INDEX(iIndex)) - { - goto loop; - } - } - } - ASSERT(v == rgCheckers.uCount); - - // - // If we get here the square is not under attack - // and it does not contain our own piece. It's - // either empty or contains an enemy piece. - // - ASSERT(!p || OPPOSITE_COLORS(pKing, p)); - _AddNormalMove(pStack, pos, cKing, c, p); - uReturn += 0x00010000; - } - } - } - loop: - ; - } -#endif // // N.B. If there is more than one piece checking the king then @@ -4493,19 +2727,16 @@ Return value: // checking piece. // c = rgCheckers.data[0].cLoc; -#if defined(GENERATE_ESCAPES_BLOCK_BITBOARD) // // board_representation/MOVEGEN_MIGRATION.md section 6a Phase 2: - // pos->cNonPawns[side][] mixes every non-pawn piece type together - // (same reason as _GenerateAllMoves's own loop -- no contiguous - // per-type range to slice), which is why the mailbox path above - // needs JumpTable[p], an indirect call whose target changes almost - // every iteration -- close to the worst case for a CPU's - // indirect-branch predictor. pos->bbPieces[side][KNIGHT/BISHOP/ - // ROOK/QUEEN] sidesteps this exactly like _GenerateAllMovesBB did: - // each piece type gets its own bit-extraction loop calling its - // specific _SaveMe*BB function BY NAME, no function pointer - // anywhere in this block. + // pos->cNonPawns[side][] mixed every non-pawn piece type together, + // which is why the old mailbox path needed JumpTable[p], an + // indirect call whose target changed almost every iteration -- + // close to the worst case for a CPU's indirect-branch predictor. + // pos->bbPieces[side][KNIGHT/BISHOP/ROOK/QUEEN] sidesteps this + // exactly like _GenerateAllMovesBB does: each piece type gets its + // own bit-extraction loop calling its specific _SaveMe*BB function + // BY NAME, no function pointer anywhere in this block. { BITBOARD bbTargetMask; BITBOARD bb; @@ -4560,52 +2791,6 @@ Return value: _SaveMeAllPawnMovesBB(pStack, pos, pos->uToMove, cKing, c, bbTargetMask); } -#else - { - BITBOARD bbNonKingPieces = NON_KING_PIECES_BB(pos, pos->uToMove); - - while (IS_ON_BOARD(cDefender = - CoorFromBitBoardRank8ToRank1(&bbNonKingPieces))) - { - p = pos->rgSquare[cDefender].pPiece; - ASSERT(p && !IS_PAWN(p)); - ASSERT(GET_COLOR(p) == pos->uToMove); - - (JumpTable[p])(pStack, pos, cDefender, cKing, c); - } - } - - // Consider all pawns too - if (pos->uToMove == BLACK) - { - BITBOARD bbPawnsToWalk = pos->bbPawns[BLACK]; - - while (IS_ON_BOARD(cDefender = - CoorFromBitBoardRank8ToRank1(&bbPawnsToWalk))) - { -#ifdef DEBUG - p = pos->rgSquare[cDefender].pPiece; - ASSERT(p && IS_PAWN(p)); - ASSERT(GET_COLOR(p) == pos->uToMove); -#endif - SaveMeBlackPawn(pStack, pos, cDefender, cKing, c); - } - } else { - BITBOARD bbPawnsToWalk = pos->bbPawns[WHITE]; - - ASSERT(pos->uToMove == WHITE); - while (IS_ON_BOARD(cDefender = - CoorFromBitBoardRank8ToRank1(&bbPawnsToWalk))) - { -#ifdef DEBUG - p = pos->rgSquare[cDefender].pPiece; - ASSERT(p && IS_PAWN(p)); - ASSERT(GET_COLOR(p) == pos->uToMove); -#endif - SaveMeWhitePawn(pStack, pos, cDefender, cKing, c); - } - } -#endif return(uReturn); } @@ -733,18 +733,12 @@ Return value: TestSan(); TestIcs(); TestGetAttacks(); - TestIsAttackedBB(); TestSeeFastSign(); TestSEENoDefenderFastPath(); TestWhoAttacksSquareMagic(); TestMoveGenerator(); TestLegalMoveGenerator(); TestNnueAccumulator(); - TestGenerateKnightSpeed(); - TestGenerateKingSpeed(); - TestGenerateRookSpeed(); - TestGenerateBishopSpeed(); - TestGenerateQueenSpeed(); TestGenerateAllMovesSpeed(); #if defined(CAPTURES_PROMS_BITBOARD_PATH) TestCapturesAndPromotionsGenerator(); diff --git a/src/movesup.c b/src/movesup.c index 7266177..ac12f10 100644 --- a/src/movesup.c +++ b/src/movesup.c @@ -35,33 +35,6 @@ Revision History: #define SKIP_SELECT_AFTER_N_MOVES (6) #endif -// Unlike GetAttacks (a real asm symbol in a different file, so -// chess.h's macro-swap never touches its own definition), -// ExposesCheck/FasterExposesCheck/ExposesCheckEp's mailbox -// implementations live in *this* file, right below their BB -// counterparts -- chess.h's #define would otherwise rename these -// functions' own definitions too, colliding with the real -// ExposesCheckBB/etc. symbols. #undef restores the real names for -// this file's own definitions; every other translation unit that -// includes chess.h still sees the macro-renamed calls. -#if defined(EXPOSESCHECK_BITBOARD) -#undef ExposesCheck -#undef FasterExposesCheck -#undef ExposesCheckEp -#endif -#if defined(ISATTACKED_BITBOARD) -#undef IsAttacked -#undef InCheck -#endif - -// See eval.c's identical comment: routes this file's (dead-under- -// ISATTACKED_BITBOARD) mailbox IsAttacked's bitboard-scan loop to the -// inlined FastCoorFromBitBoardRank8ToRank1 (chess.h) instead of the -// real out-of-line extern call. -#ifndef CROUTINES -#define CoorFromBitBoardRank8ToRank1 FastCoorFromBitBoardRank8ToRank1 -#endif - // board_representation/MOVEGEN_MIGRATION.md section 6b: bitboard // equivalents of ExposesCheck/FasterExposesCheck/ExposesCheckEp. // ExposesCheck is called from MakeMove (move.c) on essentially every @@ -360,265 +333,6 @@ Return value: return _ValidateExposedBlockerBB(pos, cBlocker, cKing); } -COOR -FasterExposesCheck(POSITION *pos, - COOR cRemove, - COOR cLocation) -/** - -Routine description: - - This is just like ExposesCheck (see below) but it does not bother - checking whether its possible to expose check with the piece at - cRemove because we already know it is. - -Parameters: - - POSITION *pos, - COOR cRemove, - COOR cLocation - -Return value: - - COOR - -**/ -{ - register int iDelta; - register COOR cBlockIndex; - int iIndex; - PIECE xPiece; - ULONG uColor = GET_COLOR(pos->rgSquare[cLocation].pPiece); - - ASSERT(IS_KING(pos->rgSquare[cLocation].pPiece)); - iIndex = (int)cLocation - (int)cRemove; - - ASSERT(IS_ON_BOARD(cRemove)); - ASSERT(IS_ON_BOARD(cLocation)); - ASSERT(!IS_EMPTY(pos->rgSquare[cLocation].pPiece)); - ASSERT(0 != (CHECK_VECTOR_WITH_INDEX(iIndex, BLACK) & (1 << QUEEN))); - - iDelta = CHECK_DELTA_WITH_INDEX(iIndex); - ASSERT(iDelta != 0); - - for (cBlockIndex = cRemove + iDelta; - IS_ON_BOARD(cBlockIndex); - cBlockIndex += iDelta) - { - ASSERT(cBlockIndex != cRemove); - ASSERT(cBlockIndex != cLocation); - xPiece = pos->rgSquare[cBlockIndex].pPiece; - if (!IS_EMPTY(xPiece)) - { - // - // Is it an enemy? - // - if (OPPOSITE_COLORS(xPiece, uColor)) - { - // - // Does it move the right way to attack? - // - iIndex = (int)cBlockIndex - (int)cLocation; - if (0 != (CHECK_VECTOR_WITH_INDEX(iIndex, WHITE) & - (1 << (PIECE_TYPE(xPiece))))) - { - return(cBlockIndex); - } - } - - // - // Either we found another friendly piece or an enemy piece - // that was unable to reach us. Either way we are safe. - // - return(ILLEGAL_COOR); - } - } - - // - // We fell off the board without seeing another piece. - // - return(ILLEGAL_COOR); -} - - - -COOR -ExposesCheck(POSITION *pos, - COOR cRemove, - COOR cLocation) -/** - -Routine description: - - Test if removing the piece at cRemove exposes the piece at - cLocation to attack by the other side. Note: there must be a - piece at cLocation because the color of that piece is used to - determine what side is "checking" and what side is "being - checked". - -Parameters: - - POSITION *pos : the board - COOR cRemove : the square where a piece hypothetically removed from - COOR cLocation : the square where the attackee is sitting - -Return value: - - COOR : the location of an attacker piece or 0x88 (!IS_ON_BOARD) if - the removal of cRemove does not expose check. - -**/ -{ - register int iDelta; - register COOR cBlockIndex; - int iIndex; - PIECE xPiece; - - ASSERT(IS_ON_BOARD(cRemove)); - ASSERT(IS_ON_BOARD(cLocation)); - ASSERT(!IS_EMPTY(pos->rgSquare[cLocation].pPiece)); - - iIndex = (int)cLocation - (int)cRemove; - - // - // If there is no way for a queen sitting at the square removed to - // reach the square we are testing (i.e. the two squares are not - // on the same rank, file, or diagonal) then there is no way - // removing it can expose cLocation to check. - // - if (0 == (CHECK_VECTOR_WITH_INDEX(iIndex, BLACK) & (1 << QUEEN))) - { - return(ILLEGAL_COOR); - } - iDelta = CHECK_DELTA_WITH_INDEX(iIndex); - for (cBlockIndex = cLocation + iDelta; - IS_ON_BOARD(cBlockIndex); - cBlockIndex += iDelta) - { - if (cBlockIndex == cRemove) - { - continue; - } - xPiece = pos->rgSquare[cBlockIndex].pPiece; - if (!IS_EMPTY(xPiece)) - { - // - // Is it an enemy? - // - if (OPPOSITE_COLORS(xPiece, pos->rgSquare[cLocation].pPiece)) - { - // - // Does it move the right way to attack? - // - iIndex = (int)cBlockIndex - (int)cLocation; - if (0 != (CHECK_VECTOR_WITH_INDEX(iIndex, GET_COLOR(xPiece)) & - (1 << (PIECE_TYPE(xPiece))))) - { - return(cBlockIndex); - } - } - - // - // Either we found another friendly piece or an enemy piece - // that was unable to reach us. Either way we are safe. - // - return(ILLEGAL_COOR); - } - } - - // - // We fell off the board without seeing another piece. - // - return(ILLEGAL_COOR); -} - - -COOR -ExposesCheckEp(POSITION *pos, - COOR cTest, - COOR cIgnore, - COOR cBlock, - COOR cKing) -/** - -Routine description: - -Parameters: - - POSITION *pos, - COOR cTest : The square from whence the attack comes - COOR cIgnore : Ignore this square, the pawn moved - COOR cBlock : This square is where the pawn moved to and now blocks - COOR cKing : The square under attack - -Return value: - - COOR - -**/ -{ - register int iDelta; - register COOR cBlockIndex; - int iIndex; - PIECE xPiece; - - ASSERT(IS_ON_BOARD(cTest)); - ASSERT(IS_ON_BOARD(cIgnore)); - ASSERT(IS_ON_BOARD(cBlock)); - ASSERT(IS_ON_BOARD(cKing)); - - iIndex = (int)cKing - (int)cTest; - - // - // If there is no way for a queen sitting at the square removed to - // reach the square we are testing (i.e. the two squares are not - // on the same rank, file, or diagonal) then no problemo. - // - if (0 == (CHECK_VECTOR_WITH_INDEX(iIndex, BLACK) & (1 << QUEEN))) - { - return(ILLEGAL_COOR); - } - - iDelta = CHECK_DELTA_WITH_INDEX(iIndex); - for (cBlockIndex = cKing + iDelta; - IS_ON_BOARD(cBlockIndex); - cBlockIndex += iDelta) - { - if (cBlockIndex == cTest) continue; - if (cBlockIndex == cIgnore) continue; - if (cBlockIndex == cBlock) return(ILLEGAL_COOR); - - xPiece = pos->rgSquare[cBlockIndex].pPiece; - if (!IS_EMPTY(xPiece)) - { - if (OPPOSITE_COLORS(xPiece, pos->rgSquare[cKing].pPiece)) - { - // - // Does it move the right way to attack? - // - iIndex = (int)cBlockIndex - (int)cKing; - if (0 != (CHECK_VECTOR_WITH_INDEX(iIndex, GET_COLOR(xPiece)) & - (1 << (PIECE_TYPE(xPiece))))) - { - return(cBlockIndex); - } - } - - // - // Either we found another friendly piece or an enemy piece - // that was unable to reach us. Either way we are safe. - // - return(ILLEGAL_COOR); - } - } - - // - // We fell off the board without seeing another piece. - // - return(ILLEGAL_COOR); -} - - // // board_representation/MOVEGEN_MIGRATION.md section 6b: bitboard // equivalent of IsAttacked/InCheck. IsAttacked's own direct callers @@ -697,150 +411,6 @@ Return value: return IsAttackedBB(cKingLoc, pos, FLIP(uSide)); } -FLAG -IsAttacked(COOR cTest, POSITION *pos, ULONG uSide) -/** - -Routine description: - - Determine whether the square cTest is attacked by the side uSide. - -Parameters: - - COOR cTest : the square we want to determine if is under attack - POSITION *pos : the board - ULONG uSide : the side we want to see if is attacking cTest - -Return value: - - FLAG : TRUE if uSide attacks cTest, FALSE otherwise - -**/ -{ - PIECE pPiece; - COOR cLocation; - COOR cBlockIndex; - int iIndex; - int iDelta; - BITBOARD bbNonPawns; - static int iPawnLoc[2] = { -17, +15 }; - static PIECE pPawn[2] = { BLACK_PAWN, WHITE_PAWN }; - - ASSERT(IS_ON_BOARD(cTest)); - ASSERT(IS_VALID_COLOR(uSide)); - - // - // Dead in this build (ISATTACKED_BITBOARD routes every caller to - // IsAttackedBB instead -- see this file's #undef block above), so - // iteration order here has no measured perf consequence; combining - // king + non-king pieces into one bitboard scan (instead of the - // old cNonPawns[uSide][] list, which deliberately visited the king - // -- always list slot 0 -- last) is simplest and just as correct. - // - bbNonPawns = NON_KING_PIECES_BB(pos, uSide) | COOR_TO_BB(KingPosition(pos, uSide)); - while (IS_ON_BOARD(cLocation = CoorFromBitBoardRank8ToRank1(&bbNonPawns))) - { - ASSERT(IS_ON_BOARD(cLocation)); - pPiece = pos->rgSquare[cLocation].pPiece; - ASSERT(!IS_EMPTY(pPiece)); - ASSERT(!IS_PAWN(pPiece)); - ASSERT(GET_COLOR(pPiece) == uSide); - iIndex = (int)cLocation - (int)cTest; - - if (!(CHECK_VECTOR_WITH_INDEX(iIndex, uSide) & - (1 << (PIECE_TYPE(pPiece))))) - { - continue; - } - - // - // These pieces do not slide, we don't need to look for - // blockers. If they can get there then there is nothing - // we can do to stop them. - // - if (IS_KNIGHT(pPiece) || IS_KING(pPiece)) - { - return(TRUE); - } - - // - // Well, here we have a sliding piece (bishop, rook or queen) - // that is on the same line (file, rank or diagonal) as the - // cTest. We now have to see if the attacker (pPiece) is - // blocked or is free to attack cTest. - // - iDelta = NEG_DELTA_WITH_INDEX(iIndex); - for (cBlockIndex = cTest + iDelta; - cBlockIndex != cLocation; - cBlockIndex += iDelta) - { - if (!IS_EMPTY(pos->rgSquare[cBlockIndex].pPiece)) - { - break; - } - } - if (cBlockIndex == cLocation) - { - return(TRUE); - } - } - - // - // Check the locations a pawn could attack cTest from. - // - cLocation = cTest + iPawnLoc[uSide]; - if (IS_ON_BOARD(cLocation) && - (pos->rgSquare[cLocation].pPiece == pPawn[uSide])) - { - return(TRUE); - } - - cLocation += 2; - if (IS_ON_BOARD(cLocation) && - (pos->rgSquare[cLocation].pPiece == pPawn[uSide])) - { - return(TRUE); - } - return(FALSE); -} - - -FLAG -InCheck(POSITION *pos, ULONG uSide) -/** - -Routine description: - - Determine if a side is in check or not. - -Parameters: - - POSITION *pos : the board - ULONG uSide : the side we want to determine if is in check - -Return value: - - FLAG : TRUE if side is in check, FALSE otherwise. - -**/ -{ - COOR cKingLoc; -#ifdef DEBUG - PIECE pKing; -#endif - - ASSERT(IS_VALID_COLOR(uSide)); - - cKingLoc = KingPosition(pos, uSide); -#ifdef DEBUG - pKing = pos->rgSquare[cKingLoc].pPiece; - ASSERT(IS_KING(pKing)); - ASSERT(IS_VALID_COLOR(uSide)); - ASSERT(GET_COLOR(pKing) == uSide); -#endif - return(IsAttacked(cKingLoc, pos, FLIP(uSide))); -} - static FLAG _SanityCheckPawnMove(POSITION *pos, MOVE mv) diff --git a/src/testgenerate.c b/src/testgenerate.c index 83c187d..30f26e9 100644 --- a/src/testgenerate.c +++ b/src/testgenerate.c @@ -245,384 +245,10 @@ TestLegalMoveGenerator(void) SystemFreeMemory(ctx); } -// -// board_representation/MOVEGEN_MIGRATION.md section 5's isolated -// cycles/call microbenchmark for the knight generator -- modeled -// directly on testsee.c's TestGetAttacks speed block (interleaved -// call-by-call across opening/middlegame/endgame positions, to cancel -// shared-box noise). Correctness for this piece type is already -// covered by TestMoveGenerator's perft counts (with -// GENERATE_KNIGHT_BITBOARD defined) -- this only answers "is it -// faster," the section 5 gate before a piece type's toggle is -// considered for default-on. -// -void -TestGenerateKnightSpeed(void) -{ - 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/3KN3/8/8/8/8 w - - 0 1", - }; - static const char *rgszLabel[3] = - { - "opening ", "middlegame", "endgame ", - }; - static const COOR rgcKnight[3] = { B1, C3, E5 }; - POSITION posBench; - SEARCHER_THREAD_CONTEXT *ctx; - UINT64 u64MailboxTotal, u64BBTotal, u64Start; - ULONG uIter, u; - ULONG uPly; - const ULONG uCallsPerPosition = 200000; - - Trace("Benchmarking knight move generation: mailbox vs " - "_GenerateKnightBB (interleaved, %lu calls/position)...\n", - uCallsPerPosition); - - ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); - ASSERT(ctx); - uPly = ctx->uPly; - - for (u = 0; u < 3; u++) - { - FenToPosition(&posBench, (char *)rgszFen[u]); - InitializeSearcherContext(&posBench, ctx); - ASSERT(IS_KNIGHT(posBench.rgSquare[rgcKnight[u]].pPiece)); - ASSERT(GET_COLOR(posBench.rgSquare[rgcKnight[u]].pPiece) == - posBench.uToMove); - - // _GenerateKnightBB relies on this being set by its caller - // (normally _GenerateAllMoves, once per node) -- see - // MOVE_STACK's bbFriendlyOccupied field comment in chess.h. - // Computed once per benchmark position, not per call, matching - // how the real call site amortizes it. - ctx->sMoveStack.bbFriendlyOccupied = - _BuildFriendlySideBB(&posBench, posBench.uToMove); - - u64MailboxTotal = 0; - u64BBTotal = 0; - for (uIter = 0; uIter < uCallsPerPosition; uIter++) - { - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - GenerateWhiteKnight(&ctx->sMoveStack, &posBench, rgcKnight[u]); - u64MailboxTotal += (SystemReadTimeStampCounter() - u64Start); - - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - _GenerateKnightBB(&ctx->sMoveStack, &posBench, rgcKnight[u]); - u64BBTotal += (SystemReadTimeStampCounter() - u64Start); - } - printf(" %s: mailbox %" COMPILER_LONGLONG_UNSIGNED_FORMAT - " cycles/call, _GenerateKnightBB %" - COMPILER_LONGLONG_UNSIGNED_FORMAT " cycles/call " - "(BB is %.2fx mailbox)\n", - rgszLabel[u], - u64MailboxTotal / uCallsPerPosition, - u64BBTotal / uCallsPerPosition, - (double)u64BBTotal / (double)u64MailboxTotal); - } - SystemFreeMemory(ctx); -} - -// -// Same shape as TestGenerateKnightSpeed, for the king. Opening position -// has castling rights but blocked by intervening pieces (exercises the -// emptiness-check branch's cost without ever actually reaching -// _AddCastle); middlegame is already castled (no rights, cheapest -// castling-tail exit); endgame is a bare king on an open board (no -// rights either, but the most normal-move destinations to enumerate). -// -void -TestGenerateKingSpeed(void) -{ - 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/4K3/8/8/8/8 w - - 0 1", - }; - static const char *rgszLabel[3] = - { - "opening ", "middlegame", "endgame ", - }; - static const COOR rgcKing[3] = { E1, G1, E5 }; - POSITION posBench; - SEARCHER_THREAD_CONTEXT *ctx; - UINT64 u64MailboxTotal, u64BBTotal, u64Start; - ULONG uIter, u; - ULONG uPly; - const ULONG uCallsPerPosition = 200000; - - Trace("Benchmarking king move generation: mailbox vs " - "_GenerateKingBB (interleaved, %lu calls/position)...\n", - uCallsPerPosition); - - ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); - ASSERT(ctx); - uPly = ctx->uPly; - - for (u = 0; u < 3; u++) - { - FenToPosition(&posBench, (char *)rgszFen[u]); - InitializeSearcherContext(&posBench, ctx); - ASSERT(IS_KING(posBench.rgSquare[rgcKing[u]].pPiece)); - ASSERT(GET_COLOR(posBench.rgSquare[rgcKing[u]].pPiece) == - posBench.uToMove); - - ctx->sMoveStack.bbFriendlyOccupied = - _BuildFriendlySideBB(&posBench, posBench.uToMove); - - u64MailboxTotal = 0; - u64BBTotal = 0; - for (uIter = 0; uIter < uCallsPerPosition; uIter++) - { - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - GenerateWhiteKing(&ctx->sMoveStack, &posBench, rgcKing[u]); - u64MailboxTotal += (SystemReadTimeStampCounter() - u64Start); - - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - _GenerateKingBB(&ctx->sMoveStack, &posBench, rgcKing[u]); - u64BBTotal += (SystemReadTimeStampCounter() - u64Start); - } - printf(" %s: mailbox %" COMPILER_LONGLONG_UNSIGNED_FORMAT - " cycles/call, _GenerateKingBB %" - COMPILER_LONGLONG_UNSIGNED_FORMAT " cycles/call " - "(BB is %.2fx mailbox)\n", - rgszLabel[u], - u64MailboxTotal / uCallsPerPosition, - u64BBTotal / uCallsPerPosition, - (double)u64BBTotal / (double)u64MailboxTotal); - } - SystemFreeMemory(ctx); -} - -// -// Same shape as TestGenerateKnightSpeed/TestGenerateKingSpeed, for the -// rook -- but unlike those two, this piece type is expected to -// actually win: rook a1 is fully blocked in the opening (0 -// destinations, cheapest case either way), partially open in the -// middlegame, and nearly fully open in the endgame (up to 13 -// destinations along an empty file/rank) -- exactly the case a -// 4-direction ray walk pays for and a magic lookup doesn't. -// -void -TestGenerateRookSpeed(void) -{ - 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/R7 w - - 0 1", - }; - static const char *rgszLabel[3] = - { - "opening ", "middlegame", "endgame ", - }; - static const COOR rgcRook[3] = { A1, A1, A1 }; - POSITION posBench; - SEARCHER_THREAD_CONTEXT *ctx; - UINT64 u64MailboxTotal, u64BBTotal, u64Start; - ULONG uIter, u; - ULONG uPly; - const ULONG uCallsPerPosition = 200000; - - Trace("Benchmarking rook move generation: mailbox vs " - "_GenerateRookBB (interleaved, %lu calls/position)...\n", - uCallsPerPosition); - - ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); - ASSERT(ctx); - uPly = ctx->uPly; - - for (u = 0; u < 3; u++) - { - FenToPosition(&posBench, (char *)rgszFen[u]); - InitializeSearcherContext(&posBench, ctx); - ASSERT(IS_ROOK(posBench.rgSquare[rgcRook[u]].pPiece)); - ASSERT(GET_COLOR(posBench.rgSquare[rgcRook[u]].pPiece) == - posBench.uToMove); - - ctx->sMoveStack.bbFriendlyOccupied = - _BuildFriendlySideBB(&posBench, posBench.uToMove); - ctx->sMoveStack.bbOccupied = _BuildFullOccupiedBB(&posBench); - - u64MailboxTotal = 0; - u64BBTotal = 0; - for (uIter = 0; uIter < uCallsPerPosition; uIter++) - { - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - GenerateRook(&ctx->sMoveStack, &posBench, rgcRook[u]); - u64MailboxTotal += (SystemReadTimeStampCounter() - u64Start); - - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - _GenerateRookBB(&ctx->sMoveStack, &posBench, rgcRook[u]); - u64BBTotal += (SystemReadTimeStampCounter() - u64Start); - } - printf(" %s: mailbox %" COMPILER_LONGLONG_UNSIGNED_FORMAT - " cycles/call, _GenerateRookBB %" - COMPILER_LONGLONG_UNSIGNED_FORMAT " cycles/call " - "(BB is %.2fx mailbox)\n", - rgszLabel[u], - u64MailboxTotal / uCallsPerPosition, - u64BBTotal / uCallsPerPosition, - (double)u64BBTotal / (double)u64MailboxTotal); - } - SystemFreeMemory(ctx); -} - -// -// Same shape as TestGenerateRookSpeed, for the bishop -- expect the -// same parity result (see MOVEGEN_MIGRATION.md section 3's rook -// entry for why), not a different outcome, since the underlying -// reason applies identically to diagonals. -// -void -TestGenerateBishopSpeed(void) -{ - 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/B7 w - - 0 1", - }; - static const char *rgszLabel[3] = - { - "opening ", "middlegame", "endgame ", - }; - static const COOR rgcBishop[3] = { C1, D3, A1 }; - POSITION posBench; - SEARCHER_THREAD_CONTEXT *ctx; - UINT64 u64MailboxTotal, u64BBTotal, u64Start; - ULONG uIter, u; - ULONG uPly; - const ULONG uCallsPerPosition = 200000; - - Trace("Benchmarking bishop move generation: mailbox vs " - "_GenerateBishopBB (interleaved, %lu calls/position)...\n", - uCallsPerPosition); - - ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); - ASSERT(ctx); - uPly = ctx->uPly; - - for (u = 0; u < 3; u++) - { - FenToPosition(&posBench, (char *)rgszFen[u]); - InitializeSearcherContext(&posBench, ctx); - ASSERT(IS_BISHOP(posBench.rgSquare[rgcBishop[u]].pPiece)); - ASSERT(GET_COLOR(posBench.rgSquare[rgcBishop[u]].pPiece) == - posBench.uToMove); - - ctx->sMoveStack.bbFriendlyOccupied = - _BuildFriendlySideBB(&posBench, posBench.uToMove); - ctx->sMoveStack.bbOccupied = _BuildFullOccupiedBB(&posBench); - - u64MailboxTotal = 0; - u64BBTotal = 0; - for (uIter = 0; uIter < uCallsPerPosition; uIter++) - { - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - GenerateBishop(&ctx->sMoveStack, &posBench, rgcBishop[u]); - u64MailboxTotal += (SystemReadTimeStampCounter() - u64Start); - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - _GenerateBishopBB(&ctx->sMoveStack, &posBench, rgcBishop[u]); - u64BBTotal += (SystemReadTimeStampCounter() - u64Start); - } - printf(" %s: mailbox %" COMPILER_LONGLONG_UNSIGNED_FORMAT - " cycles/call, _GenerateBishopBB %" - COMPILER_LONGLONG_UNSIGNED_FORMAT " cycles/call " - "(BB is %.2fx mailbox)\n", - rgszLabel[u], - u64MailboxTotal / uCallsPerPosition, - u64BBTotal / uCallsPerPosition, - (double)u64BBTotal / (double)u64MailboxTotal); - } - SystemFreeMemory(ctx); -} - -// -// Same shape as TestGenerateRookSpeed/TestGenerateBishopSpeed, for the -// queen -- expect the same parity-or-slightly-worse result, plus an -// extra fixed cost this time (two magic lookups instead of one) -- -// see MOVEGEN_MIGRATION.md section 3's rook entry for why a win was -// never really on the table for this piece type either. -// -void -TestGenerateQueenSpeed(void) -{ - static const char *rgszFen[3] = - { - "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", - "r1b2rk1/pp2bppp/2n1pn2/2pp4/3P4/1QNBPN2/PP3PPP/R3KB1R w - - 0 1", - "8/5k2/8/3K4/8/8/8/Q7 w - - 0 1", - }; - static const char *rgszLabel[3] = - { - "opening ", "middlegame", "endgame ", - }; - static const COOR rgcQueen[3] = { D1, B3, A1 }; - POSITION posBench; - SEARCHER_THREAD_CONTEXT *ctx; - UINT64 u64MailboxTotal, u64BBTotal, u64Start; - ULONG uIter, u; - ULONG uPly; - const ULONG uCallsPerPosition = 200000; - - Trace("Benchmarking queen move generation: mailbox vs " - "_GenerateQueenBB (interleaved, %lu calls/position)...\n", - uCallsPerPosition); - - ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); - ASSERT(ctx); - uPly = ctx->uPly; - for (u = 0; u < 3; u++) - { - FenToPosition(&posBench, (char *)rgszFen[u]); - InitializeSearcherContext(&posBench, ctx); - ASSERT(IS_QUEEN(posBench.rgSquare[rgcQueen[u]].pPiece)); - ASSERT(GET_COLOR(posBench.rgSquare[rgcQueen[u]].pPiece) == - posBench.uToMove); - ctx->sMoveStack.bbFriendlyOccupied = - _BuildFriendlySideBB(&posBench, posBench.uToMove); - ctx->sMoveStack.bbOccupied = _BuildFullOccupiedBB(&posBench); - - u64MailboxTotal = 0; - u64BBTotal = 0; - for (uIter = 0; uIter < uCallsPerPosition; uIter++) - { - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - GenerateQueen(&ctx->sMoveStack, &posBench, rgcQueen[u]); - u64MailboxTotal += (SystemReadTimeStampCounter() - u64Start); - ctx->sMoveStack.uEnd[uPly] = ctx->sMoveStack.uBegin[uPly]; - u64Start = SystemReadTimeStampCounter(); - _GenerateQueenBB(&ctx->sMoveStack, &posBench, rgcQueen[u]); - u64BBTotal += (SystemReadTimeStampCounter() - u64Start); - } - printf(" %s: mailbox %" COMPILER_LONGLONG_UNSIGNED_FORMAT - " cycles/call, _GenerateQueenBB %" - COMPILER_LONGLONG_UNSIGNED_FORMAT " cycles/call " - "(BB is %.2fx mailbox)\n", - rgszLabel[u], - u64MailboxTotal / uCallsPerPosition, - u64BBTotal / uCallsPerPosition, - (double)u64BBTotal / (double)u64MailboxTotal); - } - SystemFreeMemory(ctx); -} // // Whole-node dispatch benchmark: _GenerateAllMoves (mailbox @@ -700,21 +326,16 @@ TestGenerateAllMovesSpeed(void) // // board_representation/MOVEGEN_MIGRATION.md's never-started -// _FindUnblockedSquares/WouldGiveCheck candidate. Unlike every other +// _FindUnblockedSquaresBB/WouldGiveCheck candidate. Unlike every other // benchmark in this file, this does NOT measure the generator call in -// isolation: _FindUnblockedSquares writes ~20 square entries that may +// isolation: _FindUnblockedSquaresBB writes ~20 square entries that may // never be read, and WouldGiveCheck reads only 1-3 of them per move, // so a bitboard version that looks like a big win building the table // can give some of it back at query time. The real per-node cost is -// the pair -- one _FindUnblockedSquares(BB) call followed by +// the pair -- one _FindUnblockedSquaresBB call followed by // WouldGiveCheck over every move actually generated at that node -- // so that's what gets timed here, same interleaved-call methodology -// as the rest of this file. WouldGiveCheck's own body is selected at -// compile time by UNBLOCKED_BITBOARD (SQUARE_IS_UNBLOCKED/ -// DIR_FROM_SQ_TO_KING), so unlike TestGenerateAllMovesSpeed this can't -// compare both implementations by name in one binary -- run this test -// in two builds (with and without -DUNBLOCKED_BITBOARD) and compare -// the printed cycles/pair across them. +// as the rest of this file. // void TestFindUnblockedSquaresSpeed(void) @@ -737,15 +358,8 @@ TestFindUnblockedSquaresSpeed(void) MOVE_STACK *pStack; const ULONG uCallsPerPosition = 50000; - Trace("Benchmarking _FindUnblockedSquares%s + WouldGiveCheck over " - "a generated move list (%lu calls/position) -- compare this " - "build's numbers against a build with the opposite " - "-DUNBLOCKED_BITBOARD setting...\n", -#ifdef UNBLOCKED_BITBOARD - "BB", -#else - " (mailbox)", -#endif + Trace("Benchmarking _FindUnblockedSquaresBB + WouldGiveCheck over " + "a generated move list (%lu calls/position)...\n", uCallsPerPosition); ctx = SystemAllocateMemory(sizeof(SEARCHER_THREAD_CONTEXT)); @@ -763,11 +377,7 @@ TestFindUnblockedSquaresSpeed(void) // what's under test here, not generation itself. pStack->uEnd[uPly] = pStack->uBegin[uPly]; pStack->sGenFlags[uPly].uAllGenFlags = 0; -#ifdef UNBLOCKED_BITBOARD _FindUnblockedSquaresBB(pStack, &posBench); -#else - _FindUnblockedSquares(pStack, &posBench); -#endif _GenerateAllMoves(pStack, &posBench); uNumMoves = MOVE_COUNT(ctx, uPly); ASSERT(uNumMoves > 0); @@ -794,11 +404,7 @@ TestFindUnblockedSquaresSpeed(void) for (uIter = 0; uIter < uCallsPerPosition; uIter++) { u64Start = SystemReadTimeStampCounter(); -#ifdef UNBLOCKED_BITBOARD _FindUnblockedSquaresBB(pStack, &posBench); -#else - _FindUnblockedSquares(pStack, &posBench); -#endif for (uMove = pStack->uBegin[uPly]; uMove < pStack->uEnd[uPly]; uMove++) diff --git a/src/testsee.c b/src/testsee.c index 240ac39..4afe60f 100644 --- a/src/testsee.c +++ b/src/testsee.c @@ -26,19 +26,6 @@ Revision History: #include "chess.h" -// 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; @@ -264,118 +251,6 @@ 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); - } - } -} - // SeeFastSign (see.c) is a sign-only shortcut for SEE(pos, mv) >= 0 -- // GetMoveSee's only consumer (ComputeMoveExtension, searchsup.c) never // reads the magnitude, only the sign, so this skips SEE()'s list- |
