diff options
| author | Scott Gasch <[email protected]> | 2026-09-16 08:02:21 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-09-16 08:02:21 -0700 |
| commit | 966a0ef4da9cf3911eb3da0533179199808717b4 (patch) | |
| tree | e09363b574215ef0d8996155a5ab24314db2dd69 /src | |
| parent | b91c8453f3fe21c8ccd3568923d8e39141c9c945 (diff) | |
Retire cPawns/cNonPawns piece-location lists in favor of bbPawns/bbPieces/cKingSq, and hoist two redundant TotalNonPawnCount POPCNT calls out of per-node hot loops.
POSITION no longer stores cPawns[2][8], cNonPawns[2][16], uPawnCount[2],
uNonPawnCount[2][8], or uWhiteSqBishopCount[2] -- every reader now goes
through PawnMaterial/TotalNonPawnCount/WhiteSqBishopCount/KingPosition
(chess.h), computed from the already-incrementally-maintained bbPawns/
bbPieces/cKingSq bitboards via a single -mpopcnt POPCNT instead of a
stored counter. move.c's make/unmake loses the list-swap-on-removal
bookkeeping entirely. GNUmakefile now passes -mpopcnt so the popcount
builtins actually compile to one instruction instead of clang's ~15-op
SWAR fallback.
Measured a small but real nps regression from the naive swap (search.c's
QSearch move-filter and eval.c's _EvalKing were each recomputing the same
TotalNonPawnCount sum on every candidate move / twice per node); hoisted
both to a single call per node instead. Net result across a same-code
self-play match: ~1541.5 knps candidate vs ~1505.7 knps head_reference.
A third hoist (FindEnprisePiece out of SelectBestWithHistory) was tried
and reverted -- its cache isn't actually loop-invariant within a node
(populated from inside Eval(), which doesn't run before every selection
call), caught by a DEBUG-build ASSERT before it landed.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src')
| -rw-r--r-- | src/GNUmakefile | 8 | ||||
| -rw-r--r-- | src/board.c | 279 | ||||
| -rwxr-xr-x | src/chess.h | 188 | ||||
| -rw-r--r-- | src/eval.c | 261 | ||||
| -rw-r--r-- | src/fen.c | 72 | ||||
| -rw-r--r-- | src/generate.c | 104 | ||||
| -rw-r--r-- | src/move.c | 238 | ||||
| -rw-r--r-- | src/movesup.c | 57 | ||||
| -rw-r--r-- | src/probe.c | 78 | ||||
| -rw-r--r-- | src/recogn.c | 256 | ||||
| -rw-r--r-- | src/root.c | 31 | ||||
| -rw-r--r-- | src/search.c | 33 | ||||
| -rw-r--r-- | src/searchsup.c | 14 | ||||
| -rw-r--r-- | src/see.c | 8 | ||||
| -rw-r--r-- | src/testmove.c | 29 | ||||
| -rw-r--r-- | src/testsee.c | 8 | ||||
| -rw-r--r-- | src/testsup.c | 59 |
17 files changed, 882 insertions, 841 deletions
diff --git a/src/GNUmakefile b/src/GNUmakefile index 3613e40..e6d24d8 100644 --- a/src/GNUmakefile +++ b/src/GNUmakefile @@ -97,7 +97,13 @@ ifdef USE_READLINE endif ifdef SIXTYFOUR - PROFILE += -m64 + # -mpopcnt: every x86-64 chip built since ~2008 (and this box's Ryzen + # 9 3900X) has the POPCNT instruction, but clang won't emit it for + # __builtin_popcountll without this -- without it, the builtin lowers + # to a ~15-instruction SWAR bit-trick sequence instead (still faster + # than the old CDECL-call CountBits, but not the ~1-3 cycle single + # instruction the "fast" eval.c wrappers were assumed to get). + PROFILE += -m64 -mpopcnt NASMFLAGS += -d_X64_ else PROFILE += -m32 diff --git a/src/board.c b/src/board.c index 9ed2f03..2f2820a 100644 --- a/src/board.c +++ b/src/board.c @@ -22,6 +22,14 @@ Revision History: #include "chess.h" +// See eval.c's identical comment: routes the debug-dump/consistency- +// check bitboard-scan loops (added retiring cNonPawns/cPawns/ +// uPawnCount/uNonPawnCount) to the inlined FastCoorFromBitBoardRank8ToRank1 +// (chess.h) instead of the real out-of-line extern call. +#ifndef CROUTINES +#define CoorFromBitBoardRank8ToRank1 FastCoorFromBitBoardRank8ToRank1 +#endif + void SetRootToInitialPosition(void) /** @@ -220,8 +228,8 @@ Return value: } if (pos->iMaterialBalance[WHITE] != - ((SCORE)(pos->uPawnMaterial[WHITE] + pos->uNonPawnMaterial[WHITE]) - - (SCORE)(pos->uPawnMaterial[BLACK] + pos->uNonPawnMaterial[BLACK]))) + ((SCORE)(PawnMaterial(pos, WHITE) + pos->uNonPawnMaterial[WHITE]) - + (SCORE)(PawnMaterial(pos, BLACK) + pos->uNonPawnMaterial[BLACK]))) { uReason = 3; goto end; @@ -255,90 +263,70 @@ Return value: } // - // Make sure the pawns are where they should be + // Make sure the pawns are where they should be -- rebuilt by + // scanning the mailbox directly (there is no cPawns[] piece- + // location list anymore, see chess.h's bbPawns comment). // - for (v = 0; v < pos->uPawnCount[u]; v++) + FOREACH_SQUARE(c) { - c = pos->cPawns[u][v]; - if (IS_ON_BOARD(c)) + if (!IS_ON_BOARD(c)) { - if (pos->rgSquare[c].pPiece != ((PAWN << 1) | u)) - { - uReason = 6; - goto end; - } - - if (pos->rgSquare[c].uIndex != v) - { - uReason = 7; - goto end; - } - uPawnMaterial[u] += VALUE_PAWN; - uPawnCount[u]++; - bbPawns[u] |= COOR_TO_BB(c); - bbOccupied |= COOR_TO_BB(c); - bbOccupiedSide[u] |= COOR_TO_BB(c); + continue; + } + if (pos->rgSquare[c].pPiece != ((PAWN << 1) | u)) + { + continue; } + uPawnMaterial[u] += VALUE_PAWN; + uPawnCount[u]++; + bbPawns[u] |= COOR_TO_BB(c); + bbOccupied |= COOR_TO_BB(c); + bbOccupiedSide[u] |= COOR_TO_BB(c); } // - // Make sure the non-pawns are where they should be + // Make sure the non-pawns are where they should be -- rebuilt + // by scanning the mailbox directly (there is no cNonPawns[] + // piece-location list anymore, see chess.h's cKingSq/bbPieces + // comments) rather than trusting any incrementally-maintained + // state. // - for (v = 0; v < 16; v++) + FOREACH_SQUARE(c) { - c = pos->cNonPawns[u][v]; - if (v < pos->uNonPawnCount[u][0]) + if (!IS_ON_BOARD(c)) + { + continue; + } + p = pos->rgSquare[c].pPiece; + if ((EMPTY == p) || IS_PAWN(p) || (PIECE_COLOR(p) != u)) + { + continue; + } + if (!IS_VALID_PIECE(p)) { - if (!IS_ON_BOARD(c)) + uReason = 11; + goto end; + } + if (IS_KING(p)) + { + if (pos->cKingSq[u] != c) { - uReason = 8; + uReason = 10; goto end; } - else - { - p = pos->rgSquare[c].pPiece; - if (pos->rgSquare[c].uIndex != v) - { - uReason = 9; - goto end; - } - - // - // 0th spot must be a king - // - if ((v == 0) && (!IS_KING(p))) - { - uReason = 10; - goto end; - } - - if ((!IS_VALID_PIECE(p)) || (PIECE_COLOR(p) != u)) - { - uReason = 11; - goto end; - } - uNonPawnCount[u][PIECE_TYPE(p)]++; - if (!IS_KING(p)) - { - bbPieces[u][PIECE_TYPE(p)] |= COOR_TO_BB(c); - } - bbOccupied |= COOR_TO_BB(c); - bbOccupiedSide[u] |= COOR_TO_BB(c); - uNonPawnMaterial[u] += PIECE_VALUE(p); - if ((IS_BISHOP(p)) && - (IS_WHITE_SQUARE_COOR(c))) - { - uWhiteSqBishopCount[u]++; - } - } } else { - // - // The first off-board is the last, the list must not - // have gaps in it followed by good piece data again. - // - break; + bbPieces[u][PIECE_TYPE(p)] |= COOR_TO_BB(c); + } + uNonPawnCount[u][PIECE_TYPE(p)]++; + bbOccupied |= COOR_TO_BB(c); + bbOccupiedSide[u] |= COOR_TO_BB(c); + uNonPawnMaterial[u] += PIECE_VALUE(p); + if ((IS_BISHOP(p)) && + (IS_WHITE_SQUARE_COOR(c))) + { + uWhiteSqBishopCount[u]++; } } } @@ -349,22 +337,24 @@ Return value: // Make sure the materials and counts match up with what we // got when walking the piece lists // - if ((pos->uPawnMaterial[u] != uPawnMaterial[u]) || - (pos->uPawnCount[u] != uPawnCount[u]) || - (pos->uPawnMaterial[u] != pos->uPawnCount[u] * VALUE_PAWN)) + if (PawnMaterial(pos, u) != uPawnMaterial[u]) { uReason = 12; goto end; } + // + // Per-type counts no longer stored on POSITION (see chess.h's + // bbPieces comment) -- bbPieces itself is verified exactly + // against the independently-rebuilt bbPieces[u][v] just below + // (uReason 21), a stronger check than a population count would + // be, so uSigmaNonPawnCount just accumulates the local, + // freshly-rebuilt-from-the-mailbox-scan counts for the total + // check further down. + // for (v = KNIGHT; v <= KING; v++) { - if (pos->uNonPawnCount[u][v] != uNonPawnCount[u][v]) - { - uReason = 13; - goto end; - } - uSigmaNonPawnCount[u] += pos->uNonPawnCount[u][v]; + uSigmaNonPawnCount[u] += uNonPawnCount[u][v]; } for (v = KNIGHT; v < KING; v++) @@ -383,19 +373,22 @@ Return value: } // - // Note: the 0th spot in the array is the sum of all non pawns + // uSigmaNonPawnCount includes the king (see the KNIGHT..KING + // loop above), matching TotalNonPawnCount(pos, u). // - if (uSigmaNonPawnCount[u] != pos->uNonPawnCount[u][0]) + if (uSigmaNonPawnCount[u] != TotalNonPawnCount(pos, u)) { uReason = 14; goto end; } // - // You can't have more white-square bishops than you have - // bishops. + // uWhiteSqBishopCount[u] here is the from-scratch mailbox + // tally (see the FOREACH_SQUARE walk above) -- verify it + // matches WhiteSqBishopCount's bbPieces-derived value (chess.h), + // same mailbox/bitboard cross-check as bbPieces itself gets. // - if (pos->uWhiteSqBishopCount[u] > pos->uNonPawnCount[u][BISHOP]) + if (uWhiteSqBishopCount[u] != WhiteSqBishopCount(pos, u)) { uReason = 15; goto end; @@ -442,21 +435,24 @@ Return value: u = PIECE_COLOR(p); if (IS_PAWN(p)) { + // + // No per-square index to sanity-check here anymore + // (pawns don't carry a list index either -- see + // chess.h's bbPawns comment). + // uPawnMaterial[u] -= VALUE_PAWN; - if (pos->rgSquare[c].uIndex > pos->uPawnCount[u]) - { - uReason = 17; - goto end; - } } else { + // + // No per-square index to sanity-check here anymore + // (non-pawn pieces don't carry a list index -- + // see chess.h's cKingSq/bbPieces comments); the + // per-color/per-type census taken above already + // catches a piece present on the board but not + // accounted for. + // uNonPawnMaterial[u] -= PIECE_VALUE(p); - if (pos->rgSquare[c].uIndex > pos->uNonPawnCount[u][0]) - { - uReason = 18; - goto end; - } } } } @@ -536,7 +532,7 @@ Return value: FOREACH_COLOR(u) { if ((p1->fCastled[u] != p2->fCastled[u]) || - (p1->uWhiteSqBishopCount[u] != p2->uWhiteSqBishopCount[u])) + (WhiteSqBishopCount(p1, u) != WhiteSqBishopCount(p2, u))) { return(FALSE); } @@ -679,7 +675,7 @@ Return value: " +0x%03x fCastled[2] : W:0x%02x B:0x%02x\n" " +0x%03x bvCastleInfo : 0x%02x (%s)\n" " +0x%03x cEpSquare : 0x%02x (%s)\n" - " +0x%03x cPawns[2][8] : \n", + " +0x%03x bbPawns[2] (pawn locations): \n", p, pos, OFFSET_OF(u64PawnSig, POSITION), pos->u64PawnSig, @@ -691,59 +687,73 @@ Return value: OFFSET_OF(fCastled, POSITION), pos->fCastled[WHITE], pos->fCastled[BLACK], OFFSET_OF(bvCastleInfo, POSITION), pos->bvCastleInfo, CastleInfoString(pos->bvCastleInfo), OFFSET_OF(cEpSquare, POSITION), pos->cEpSquare, (IS_ON_BOARD(pos->cEpSquare) ? CoorToString(pos->cEpSquare) : "none"), - OFFSET_OF(cPawns, POSITION)); + OFFSET_OF(bbPawns, POSITION)); Trace(" W: "); - for (u = 0; u < pos->uPawnCount[WHITE]; u++) { - Trace("0x%02x[%s] ", - pos->cPawns[WHITE][u], - CoorToString(pos->cPawns[WHITE][u])); - ASSERT(IS_ON_BOARD(pos->cPawns[WHITE][u])); + BITBOARD bbDump = pos->bbPawns[WHITE]; + COOR cDump; + + while (IS_ON_BOARD(cDump = CoorFromBitBoardRank8ToRank1(&bbDump))) + { + Trace("0x%02x[%s] ", cDump, CoorToString(cDump)); + } } Trace("\n" " B: "); - for (u = 0; u < pos->uPawnCount[BLACK]; u++) { - Trace("0x%02x[%s] ", - pos->cPawns[BLACK][u], - CoorToString(pos->cPawns[BLACK][u])); - ASSERT(IS_ON_BOARD(pos->cPawns[BLACK][u])); + BITBOARD bbDump = pos->bbPawns[BLACK]; + COOR cDump; + + while (IS_ON_BOARD(cDump = CoorFromBitBoardRank8ToRank1(&bbDump))) + { + Trace("0x%02x[%s] ", cDump, CoorToString(cDump)); + } } Trace("\n" - " +0x%03x uPawnMaterial[2] : W:0x%02x (%u, pawn=%u)\n" - " : B:0x%02x (%u, pawn=%u)\n" - " +0x%03x uPawnCount[2] : W:%1u pawns B:%1u pawns\n", - OFFSET_OF(uPawnMaterial, POSITION), - pos->uPawnMaterial[WHITE], pos->uPawnMaterial[WHITE], VALUE_PAWN, - pos->uPawnMaterial[BLACK], pos->uPawnMaterial[BLACK], VALUE_PAWN, - OFFSET_OF(uPawnCount, POSITION), - pos->uPawnCount[WHITE], pos->uPawnCount[BLACK]); + " PawnMaterial() : W:0x%02x (%u, pawn=%u)\n" + " : B:0x%02x (%u, pawn=%u)\n" + " bbPawns popcount : W:%1u pawns B:%1u pawns\n", + PawnMaterial(pos, WHITE), PawnMaterial(pos, WHITE), VALUE_PAWN, + PawnMaterial(pos, BLACK), PawnMaterial(pos, BLACK), VALUE_PAWN, + FastCountBits(pos->bbPawns[WHITE]), FastCountBits(pos->bbPawns[BLACK])); - Trace(" +0x%03x cNonPawns[2][16] : \n" - " W: ", OFFSET_OF(cNonPawns, POSITION)); - for (u = 0; u < pos->uNonPawnCount[WHITE][0]; u++) + Trace(" +0x%03x cKingSq[2] + bbPieces[2][8] (non-pawn locations): \n" + " W: ", OFFSET_OF(cKingSq, POSITION)); { - Trace("0x%02x[%s] ", - pos->cNonPawns[WHITE][u], - CoorToString(pos->cNonPawns[WHITE][u])); - ASSERT(IS_ON_BOARD(pos->cNonPawns[WHITE][u])); + BITBOARD bbDump; + COOR cDump; + + Trace("0x%02x[%s](K) ", pos->cKingSq[WHITE], + CoorToString(pos->cKingSq[WHITE])); + ASSERT(IS_ON_BOARD(pos->cKingSq[WHITE])); + bbDump = NON_KING_PIECES_BB(pos, WHITE); + while (IS_ON_BOARD(cDump = CoorFromBitBoardRank8ToRank1(&bbDump))) + { + Trace("0x%02x[%s] ", cDump, CoorToString(cDump)); + } } Trace("\n" " B: "); - for (u = 0; u < pos->uNonPawnCount[BLACK][0]; u++) { - Trace("0x%02x[%s] ", - pos->cNonPawns[BLACK][u], - CoorToString(pos->cNonPawns[BLACK][u])); - ASSERT(IS_ON_BOARD(pos->cNonPawns[BLACK][u])); + BITBOARD bbDump; + COOR cDump; + + Trace("0x%02x[%s](K) ", pos->cKingSq[BLACK], + CoorToString(pos->cKingSq[BLACK])); + ASSERT(IS_ON_BOARD(pos->cKingSq[BLACK])); + bbDump = NON_KING_PIECES_BB(pos, BLACK); + while (IS_ON_BOARD(cDump = CoorFromBitBoardRank8ToRank1(&bbDump))) + { + Trace("0x%02x[%s] ", cDump, CoorToString(cDump)); + } } Trace("\n" " +0x%03x uNonPawnMaterial[2] : W: 0x%02x (%u, pawn=%u)\n" " : B: 0x%02x (%u, pawn=%u)\n" - " +0x%03x uNonPawnCount[2][7] : WHITE BLACK\n" + " bbPieces popcounts : WHITE BLACK\n" " KNIGHT : %u %u\n" " BISHOP : %u %u\n" " WHITE SQUARE BISHOP : %u %u\n" @@ -755,13 +765,12 @@ Return value: VALUE_PAWN, pos->uNonPawnMaterial[BLACK], pos->uNonPawnMaterial[BLACK], VALUE_PAWN, - OFFSET_OF(uNonPawnCount, POSITION), - pos->uNonPawnCount[WHITE][KNIGHT], pos->uNonPawnCount[BLACK][KNIGHT], - pos->uNonPawnCount[WHITE][BISHOP], pos->uNonPawnCount[BLACK][BISHOP], - pos->uWhiteSqBishopCount[WHITE], pos->uWhiteSqBishopCount[BLACK], - pos->uNonPawnCount[WHITE][ROOK], pos->uNonPawnCount[BLACK][ROOK], - pos->uNonPawnCount[WHITE][QUEEN], pos->uNonPawnCount[BLACK][QUEEN], - pos->uNonPawnCount[WHITE][0], pos->uNonPawnCount[BLACK][0]); + FastCountBits(pos->bbPieces[WHITE][KNIGHT]), FastCountBits(pos->bbPieces[BLACK][KNIGHT]), + FastCountBits(pos->bbPieces[WHITE][BISHOP]), FastCountBits(pos->bbPieces[BLACK][BISHOP]), + WhiteSqBishopCount(pos, WHITE), WhiteSqBishopCount(pos, BLACK), + FastCountBits(pos->bbPieces[WHITE][ROOK]), FastCountBits(pos->bbPieces[BLACK][ROOK]), + FastCountBits(pos->bbPieces[WHITE][QUEEN]), FastCountBits(pos->bbPieces[BLACK][QUEEN]), + TotalNonPawnCount(pos, WHITE), TotalNonPawnCount(pos, BLACK)); #else Trace("\n%s\n", DrawTextBoardFromPosition(pos)); #endif diff --git a/src/chess.h b/src/chess.h index 38fdb3f..349ff1e 100755 --- a/src/chess.h +++ b/src/chess.h @@ -603,24 +603,47 @@ typedef struct _POSITION BITV bvCastleInfo; // who can castle how? COOR cEpSquare; // en-passant capture square - COOR cPawns[2][8]; // location of pawns on the board - ULONG uPawnMaterial[2]; // pawn material of each side - ULONG uPawnCount[2]; // number of pawns for each side + // Pawn material of each side -- every pawn is worth the same, so + // this (like uPawnCount/uNonPawnCount before it) is just + // FastCountBits(bbPawns[color]) * VALUE_PAWN (see PawnMaterial() + // below), not a stored field. - COOR cNonPawns[2][16]; // location of pieces on the board ULONG uNonPawnMaterial[2]; // piece material of each side - ULONG uNonPawnCount[2][8]; // number of non-pawns / type - // 0 and 1 are the sum, - // 2..6 are per PIECE_TYPE + // (counts: FastCountBits( + // bbPieces[2][KNIGHT/BISHOP/ + // ROOK/QUEEN]) per type; + // FastCountBits( + // NON_KING_PIECES_BB(pos, + // color)) + 1 for the total; + // always exactly 1 for KING, + // no backing bitboard exists) + + // King location, per color -- a plain scalar read/write. Used to + // carry the location the old cNonPawns[color][0] convention slot + // held (2026-09-16 board_representation/MIGRATION.md-style + // retirement of the cNonPawns[2][16] piece-location list: every + // reader outside move.c's own list-maintenance code turned out to + // be either this fixed king slot or a full-population walk, both + // servable directly by bbPieces[2][8] below via + // NON_KING_PIECES_BB/CoorFromBitBoardRank8ToRank1 -- see git log + // for the migration). NOT redundant with bbPieces[color][KING]: + // that slot is deliberately never populated (fen.c's IS_KING + // branch skips the bbPieces OR, board.c's + // VerifyPositionConsistency rebuild loop guards `v < KING` / + // `!IS_KING(p)` to match) -- and even if it were, every read would + // cost a CTZ + COOR conversion versus this field's one load. + // Maintained by SlidePiece (move.c) and fen.c's/testsup.c's setup + // code. + COOR cKingSq[2]; // Per-color, per-piece-type location bitboards -- "where are my // knights/bishops/rooks/queens" -- indexed by PIECE_TYPE exactly // like uNonPawnCount above (slots 0/1/PAWN/KING unused, same // convention). Maintained incrementally in move.c at every site - // that already updates cNonPawns[]/uNonPawnCount[] (see + // that already updates uNonPawnCount[] (see // board_representation/MIGRATION.md), not rebuilt -- O(1) per - // move. Pawns use bbPawns[2] below instead; king is a single - // square (cNonPawns[color][0]), a bitboard adds nothing. + // move. Pawns use bbPawns[2] below instead; king uses cKingSq[2] + // above, not this array (see cKingSq's own comment for why). BITBOARD bbPieces[2][8]; // Per-color pawn location bitboard -- same incremental-maintenance @@ -722,7 +745,6 @@ typedef struct _POSITION // bitboard for both colors. BITBOARD bbKingAttacks[2]; - ULONG uWhiteSqBishopCount[2]; // num bishops on white squares SCORE iMaterialBalance[2]; // material balance // temporary storage space for use in eval @@ -743,7 +765,6 @@ typedef struct _POSITION SCORE iScore[2]; SCORE iReducedMaterialDownScaler[2]; ULONG uMinorsAtHome[2]; - BITBOARD bb; } POSITION; @@ -3664,6 +3685,71 @@ FastLastBit(IN BITBOARD bb) return bb ? (ULONG)(64 - __builtin_clzll(bb)) : 0; } +// Same rationale as FastFirstBit/FastLastBit above: __builtin_popcountll +// compiles to a single POPCNT instruction (verified against this +// build's actual object code, not assumed -- GNUmakefile's -mpopcnt is +// required for this; without it clang silently falls back to a +// ~15-instruction SWAR sequence, still faster than the extern CDECL +// CountBits below -- x64.asm's real implementation is a Kernighan +// bit-clearing loop, O(popcount) iterations plus call overhead, +// measured at ~23 cycles/op by gmake TEST=1's own bitboard benchmark +// -- but not the ~1-cycle single instruction this function gets with +// -mpopcnt). Measured (rdtsc, -O3 -mpopcnt, inlined): statistically +// identical cost to a plain scalar field read for a single popcnt, and +// ~2.5 cycles for an OR-several-bitboards-then-popcnt compound count -- +// this is what retired uPawnCount[2]/uNonPawnCount[2][8]'s stored +// counts in favor of computing them from bbPawns/bbPieces on read. +static ULONG INLINE +FastCountBits(IN BITBOARD bb) +{ + ULONG u = (ULONG)__builtin_popcountll(bb); + ASSERT(u == SlowCountBits(bb)); + return u; +} + +// Fast, globally-available equivalents of CoorFromBitBoardRank8ToRank1/ +// CoorFromBitBoardRank1ToRank8 (bitboard.c) -- those are real +// out-of-line extern CDECL calls from every translation unit except +// eval.c (which has had its own private copy of exactly this pair +// since the cNonPawns/cPawns/uPawnCount/uNonPawnCount retirement work +// started adding multi-bit bitboard-scan loops elsewhere -- confirmed +// by disassembly that recogn.o/board.o/generate.o/movesup.o/probe.o/ +// testmove.o were still emitting real callq+PLT relocations to the +// slow extern version). Promoted here so every caller gets the fast +// path, not just eval.c. _FastCoorFromBitBoardRank1ToRank8 bakes in +// the same wrong-bit-clear fix as bitboard.c's real +// CoorFromBitBoardRank1ToRank8 (clears the bit it actually reported, +// via BBSQUARE, not unconditionally the lowest set bit). +static COOR INLINE +FastCoorFromBitBoardRank8ToRank1(IN OUT BITBOARD *pbb) +{ + COOR c = ILLEGAL_COOR; + ULONG uBitIndex; + + if (*pbb) + { + uBitIndex = (ULONG)__builtin_ctzll(*pbb); + c = BIT_NUMBER_TO_COOR(uBitIndex); + *pbb &= (*pbb - 1); + } + return c; +} + +static COOR INLINE +FastCoorFromBitBoardRank1ToRank8(IN OUT BITBOARD *pbb) +{ + COOR c = ILLEGAL_COOR; + ULONG uBitIndex; + + if (*pbb) + { + uBitIndex = (ULONG)(63 - __builtin_clzll(*pbb)); + c = BIT_NUMBER_TO_COOR(uBitIndex); + *pbb &= ~BBSQUARE[uBitIndex]; + } + return c; +} + #ifdef CROUTINES #define CountBits SlowCountBits #define FirstBit SlowFirstBit @@ -3676,6 +3762,82 @@ CoorFromBitBoardRank8ToRank1(BITBOARD *pbb); COOR CoorFromBitBoardRank1ToRank8(BITBOARD *pbb); +// King location -- reads the dedicated cKingSq[2] scalar (see its +// declaration in POSITION) instead of the old cNonPawns[color][0] +// convention. +static COOR INLINE +KingPosition(IN POSITION *pos, IN ULONG color) +{ + return pos->cKingSq[color]; +} + +// Combined "every non-pawn, non-king piece of this color" bitboard -- +// replaces a cNonPawns[color][1..count-1] list walk (slot 0 is always +// the king, see cKingSq above) with a scan over this bitboard. Bit +// order (rank8->rank1 via CoorFromBitBoardRank8ToRank1) differs from +// the old list's insertion order; fine for callers that just +// sum/count/search, not for callers that depended on insertion order +// specifically. +// +// bbOccupiedSide[color] & ~bbPawns[color] is every non-pawn piece of +// this color INCLUDING the king (bbOccupiedSide covers pawns, king, +// and every other piece type; subtracting bbPawns leaves king + +// knight/bishop/rook/queen) -- clearing the king's own bit on top of +// that is cheaper (one AND, one ANDNOT, one more ANDNOT) than the +// previous OR of all four per-type bitboards (three ORs), and doesn't +// need bbPieces[color][KING] to exist (it deliberately doesn't -- see +// cKingSq's comment). +#define NON_KING_PIECES_BB(pos, color) \ + ((pos)->bbOccupiedSide[(color)] & \ + ~(pos)->bbPawns[(color)] & \ + ~COOR_TO_BB((pos)->cKingSq[(color)])) + +// Non-destructive single-bit-to-COOR conversion -- unlike +// CoorFromBitBoardRank8ToRank1, doesn't clear the bit or take a +// pointer, so it's safe to call directly on a POSITION field. Callers +// must know the bitboard has exactly one bit set (e.g. bbPieces[color] +// [KNIGHT] in a position with only one knight of that color) -- +// behavior on an empty or multi-bit board is the same +// "first/lowest set bit, or ILLEGAL_COOR-shaped garbage if none" +// contract FastFirstBit already has. +static COOR INLINE +CoorOfSingleBit(IN BITBOARD bb) +{ + return BIT_NUMBER_TO_COOR(FastFirstBit(bb) - 1); +} + +// Total non-pawn piece count (including the king), replacing the old +// stored uNonPawnCount[color][0] convention slot -- see chess.h's +// uNonPawnMaterial comment for why this became a popcnt instead of a +// stored field. bbOccupiedSide[color] & ~bbPawns[color] already +// includes the king (unlike NON_KING_PIECES_BB, which excludes it) -- +// cheaper than FastCountBits(NON_KING_PIECES_BB(pos, color)) + 1, +// same reasoning as NON_KING_PIECES_BB's own comment. +static ULONG INLINE +TotalNonPawnCount(IN POSITION *pos, IN ULONG color) +{ + return FastCountBits(pos->bbOccupiedSide[color] & ~pos->bbPawns[color]); +} + +// Number of this color's bishops on white squares, replacing the old +// stored uWhiteSqBishopCount[2] field -- one AND against the constant +// BBWHITESQ bitboard (bitboard.c) plus a popcnt, same cost class as +// the other counts retired alongside it. +static ULONG INLINE +WhiteSqBishopCount(IN POSITION *pos, IN ULONG color) +{ + return FastCountBits(pos->bbPieces[color][BISHOP] & BBWHITESQ); +} + +// Pawn material of this color, replacing the old stored +// uPawnMaterial[2] field -- every pawn is worth VALUE_PAWN, so this is +// just a popcnt and a multiply. +static ULONG INLINE +PawnMaterial(IN POSITION *pos, IN ULONG color) +{ + return FastCountBits(pos->bbPawns[color]) * VALUE_PAWN; +} + // // x86.asm // @@ -3753,8 +3915,6 @@ _WhoAttacksSquareBB(POSITION *pos, ASSERT(OFFSET_OF(pPiece, SEE_THREESOME) == 0); \ ASSERT(OFFSET_OF(cLoc, SEE_THREESOME) == 4); \ ASSERT(OFFSET_OF(uVal, SEE_THREESOME) == 8); \ - ASSERT(OFFSET_OF(cNonPawns, POSITION) == 0x478); \ - ASSERT(OFFSET_OF(uNonPawnCount, POSITION) == 0x500); \ ASSERT(OFFSET_OF(rgSquare, POSITION) == 0); \ ASSERT(sizeof(SQUARE) == 8); \ ASSERT(sizeof(VECTOR_DELTA) == 4); \ @@ -30,57 +30,24 @@ Revision History: // loop -- O(popcount) iterations plus call overhead. Redirected via // #define, the same mechanism chess.h already uses for the OTHER // direction (#ifdef CROUTINES routes CountBits/FirstBit/LastBit to the -// slow C fallbacks) -- this routes them to fast inline compiler -// builtins instead, for every existing call site below with no further -// edits. Gated on !CROUTINES so the CROUTINES debug/comparison build -// still gets the real (slow, cross-checked) implementations. -// -// _FastCoorFromBitBoardRank1ToRank8 also bakes in the wrong-bit-clear -// fix applied to bitboard.c's real CoorFromBitBoardRank1ToRank8 (that -// function used to clear the LOWEST set bit via `*pbb &= (*pbb - 1)` -// regardless of uLastBit, correct only when a single bit was set) -- -// this version clears the bit it actually just reported, via BBSQUARE. +// slow C fallbacks) -- this routes them to the fast inline +// FastCountBits/FastCoorFromBitBoardRank8ToRank1/ +// FastCoorFromBitBoardRank1ToRank8 wrappers (chess.h) instead, for +// every existing call site below with no further edits. Those used to +// be duplicated here as file-local statics; promoted to chess.h so +// every other translation unit's bitboard-scan loops (recogn.c, +// board.c, generate.c, movesup.c, probe.c, testmove.c -- added during +// the cNonPawns/cPawns/uPawnCount/uNonPawnCount retirement work) get +// the fast path too, not just eval.c -- confirmed by disassembly that +// those files were still emitting real callq+PLT relocations to the +// slow extern versions before this. Gated on !CROUTINES so the +// CROUTINES debug/comparison build still gets the real (slow, +// cross-checked) implementations. // #ifndef CROUTINES -static ULONG INLINE -_FastCountBits(IN BITBOARD bb) -{ - return (ULONG)__builtin_popcountll(bb); -} - -static COOR INLINE -_FastCoorFromBitBoardRank8ToRank1(IN OUT BITBOARD *pbb) -{ - COOR c = ILLEGAL_COOR; - ULONG uBitIndex; - - if (*pbb) - { - uBitIndex = (ULONG)__builtin_ctzll(*pbb); - c = BIT_NUMBER_TO_COOR(uBitIndex); - *pbb &= (*pbb - 1); - } - return c; -} - -static COOR INLINE -_FastCoorFromBitBoardRank1ToRank8(IN OUT BITBOARD *pbb) -{ - COOR c = ILLEGAL_COOR; - ULONG uBitIndex; - - if (*pbb) - { - uBitIndex = (ULONG)(63 - __builtin_clzll(*pbb)); - c = BIT_NUMBER_TO_COOR(uBitIndex); - *pbb &= ~BBSQUARE[uBitIndex]; - } - return c; -} - -#define CountBits _FastCountBits -#define CoorFromBitBoardRank8ToRank1 _FastCoorFromBitBoardRank8ToRank1 -#define CoorFromBitBoardRank1ToRank8 _FastCoorFromBitBoardRank1ToRank8 +#define CountBits FastCountBits +#define CoorFromBitBoardRank8ToRank1 FastCoorFromBitBoardRank8ToRank1 +#define CoorFromBitBoardRank1ToRank8 FastCoorFromBitBoardRank1ToRank8 #endif // !CROUTINES @@ -1762,7 +1729,7 @@ Return value: // CANDIDATE_PASSER_BY_RANK value (an exact duplicate of the // term just added above, not a different angle on it), now a // fractional modifier. - if (pos->uNonPawnCount[xColor][0] == 1) + if (TotalNonPawnCount(pos, xColor) == 1) { EVAL_TERM(uColor, PAWN, @@ -1809,7 +1776,7 @@ Return value: ASSERT(IS_VALID_COLOR(uColor)); if (pHash->bbPasserLocations[uColor]) { - ASSERT(pos->uPawnCount[uColor] > 0); + ASSERT(FastCountBits(pos->bbPawns[uColor]) > 0); pFriend = BLACK_PAWN | uColor; cLeftmostPasser = cRightmostPasser = ILLEGAL_COOR; // Leftmost/rightmost is just a min/max over FILE(c) -- no @@ -2068,7 +2035,6 @@ Return value: FOREACH_COLOR(uColor) { ASSERT(IS_VALID_COLOR(uColor)); - ASSERT(CountBits(pos->bbPawns[uColor]) == pos->uPawnCount[uColor]); for (u = A; u <= H; u++) { @@ -2133,15 +2099,14 @@ Return value: FOREACH_COLOR(uColor) { ASSERT(IS_VALID_COLOR(uColor)); - ASSERT(pos->uPawnCount[uColor] <= 8); ASSERT(CountBits(pos->bbPawns[uColor]) <= 8); d1 = 16 * g_iAhead[uColor]; - for (u = 0; - u < pos->uPawnCount[uColor]; - u++) { - c = pos->cPawns[uColor][u]; + BITBOARD bbPawnsToWalk = pos->bbPawns[uColor]; + + while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbPawnsToWalk))) + { ASSERT(IS_ON_BOARD(c)); ASSERT(IS_PAWN(pos->rgSquare[c].pPiece)); ASSERT((1 < RANK(c)) && (RANK(c) < 8)); @@ -2382,6 +2347,7 @@ Return value: // Handle passers / candidate passers // _EvaluateCandidatePasser(pos, pHash, c); + } } } @@ -2415,7 +2381,7 @@ Return value: // ASSERT((uDoubled[WHITE] >= 0) && (uDoubled[WHITE] <= 8)); ASSERT((uDoubled[BLACK] >= 0) && (uDoubled[BLACK] <= 8)); - u = pos->uNonPawnCount[BLACK][ROOK] + pos->uNonPawnCount[BLACK][QUEEN] * 2; + u = FastCountBits(pos->bbPieces[BLACK][ROOK]) + FastCountBits(pos->bbPieces[BLACK][QUEEN]) * 2; u = MINU(u, 3); EVAL_TERM(BLACK, PAWN, @@ -2423,7 +2389,7 @@ Return value: pHash->iScore[BLACK], DOUBLED_PAWN_PENALTY_BY_COUNT[u][uDoubled[BLACK]], "exponential doubled"); - u = pos->uNonPawnCount[WHITE][ROOK] + pos->uNonPawnCount[WHITE][QUEEN] * 2; + u = FastCountBits(pos->bbPieces[WHITE][ROOK]) + FastCountBits(pos->bbPieces[WHITE][QUEEN]) * 2; u = MINU(u, 3); EVAL_TERM(WHITE, PAWN, @@ -2494,10 +2460,9 @@ Return value: return 0; } - cKing = pos->cNonPawns[uColor][0]; + cKing = KingPosition(pos, uColor); uCounter = KING_INITIAL_COUNTER_BY_LOCATION[uColor][cKing] >> 1; ASSERT(IS_KING(pos->rgSquare[cKing].pPiece)); - ASSERT(pos->rgSquare[cKing].uIndex == 0); ASSERT(IS_ON_BOARD(cKing)); ASSERT(GET_COLOR(pos->rgSquare[cKing].pPiece) == uColor); @@ -2638,7 +2603,7 @@ Return value: // scaled by the same filestormopenfile-derived factor. static SCORE KING_FILE_EDGE_KICKER = -5; ULONG ufColor = FLIP(uColor); - COOR c = pos->cNonPawns[uColor][0]; + COOR c = KingPosition(pos, uColor); SCORE iScore = 0; COOR cFileSq, cSquare; ULONG u, v; @@ -2723,7 +2688,7 @@ Routine description: +4, +2, +0 }; ULONG ufColor = FLIP(uColor); - COOR c = pos->cNonPawns[uColor][0]; + COOR c = KingPosition(pos, uColor); COOR cFileSq, cSquare; ULONG u, v; BITBOARD bb; @@ -2815,8 +2780,8 @@ Return value: **/ { UINT64 u64Key = pos->u64PawnSig ^ - g_u64SigSeeds[pos->cNonPawns[WHITE][0]][KING][WHITE] ^ - g_u64SigSeeds[pos->cNonPawns[BLACK][0]][KING][BLACK]; + g_u64SigSeeds[KingPosition(pos, WHITE)][KING][WHITE] ^ + g_u64SigSeeds[KingPosition(pos, BLACK)][KING][BLACK]; ULONG uSlot = (ULONG)u64Key & (KING_SAFETY_HASH_TABLE_SIZE - 1); KING_SAFETY_HASH_ENTRY *pEntry = &ctx->rgKingSafetyHash[uSlot]; #ifdef PERF_COUNTERS @@ -3706,14 +3671,13 @@ Return value: // count, no x-ray -- simply excluded by the friendly-occupied // mask, nothing else needed. // - pos->bb = bbPc; { BITBOARD bbAttack = _BishopAttacksBB(c, pos->bbOccupied); BITBOARD bbEnemySame = pos->bbPieces[xColor][BISHOP] | pos->bbPieces[xColor][KNIGHT]; BITBOARD bbEnemyGEContinue = pos->bbPieces[xColor][ROOK] | pos->bbPieces[xColor][QUEEN] | - COOR_TO_BB(pos->cNonPawns[xColor][0]); + COOR_TO_BB(KingPosition(pos, xColor)); BITBOARD bbFriendBQ = pos->bbPieces[uColor][BISHOP] | pos->bbPieces[uColor][QUEEN]; BITBOARD bbUnsafeForMinor = pos->bbPawnAttacks[xColor]; @@ -3813,7 +3777,7 @@ Return value: ((IS_ON_BOARD(c + 15 * g_iBehind[uColor])) && (pos->rgSquare[c + 15 * g_iBehind[uColor]].pPiece==p)))); #endif - u = DISTANCE(c, pos->cNonPawns[xColor][0]); + u = DISTANCE(c, KingPosition(pos, xColor)); i = BISHOP_UNASSAILABLE_BY_DIST_FROM_EKING[u]; EVAL_TERM(uColor, BISHOP, @@ -3893,7 +3857,7 @@ Return value: // if so give bonus for outposted knight which increases the // closer it is to the enemy king and the more pawns it has // supporting it. - uDist = DISTANCE(c, pos->cNonPawns[xColor][0]); + uDist = DISTANCE(c, KingPosition(pos, xColor)); ASSERT((uDist > 0) && (uDist <= 8)); bb = pos->bbPawns[xColor] & (~pHash->bbStationaryPawns[xColor]); @@ -3926,7 +3890,7 @@ Return value: { if (IS_SQUARE_WHITE(c)) { - if (pos->uWhiteSqBishopCount[xColor]) + if (WhiteSqBishopCount(pos, xColor)) { EVAL_TERM(uColor, KNIGHT, @@ -3938,8 +3902,8 @@ Return value: } else { - if (pos->uNonPawnCount[xColor][BISHOP] - - pos->uWhiteSqBishopCount[xColor]) + if (FastCountBits(pos->bbPieces[xColor][BISHOP]) - + WhiteSqBishopCount(pos, xColor)) { EVAL_TERM(uColor, KNIGHT, @@ -4111,8 +4075,8 @@ Return value: ROOK, c, pos->iScore[uColor], - ROOK_VALUE_AS_PAWNS_COME_OFF[pos->uPawnCount[uColor] + - pos->uPawnCount[FLIP(uColor)]], + ROOK_VALUE_AS_PAWNS_COME_OFF[FastCountBits(pos->bbPawns[uColor]) + + FastCountBits(pos->bbPawns[FLIP(uColor)])], "increase in value/pawns"); // @@ -4145,7 +4109,7 @@ Return value: BITBOARD bbEnemyOcc = _BuildFriendlySideBB(pos, FLIP(uColor)); BITBOARD bbEnemySame = pos->bbPieces[FLIP(uColor)][ROOK]; BITBOARD bbEnemyGEContinue = pos->bbPieces[FLIP(uColor)][QUEEN] | - COOR_TO_BB(pos->cNonPawns[FLIP(uColor)][0]); + COOR_TO_BB(KingPosition(pos, FLIP(uColor))); BITBOARD bbFriendRQ = pos->bbPieces[uColor][ROOK] | pos->bbPieces[uColor][QUEEN]; BITBOARD bbUnsafeForRook = pos->bbPawnAttacks[FLIP(uColor)] | @@ -4274,7 +4238,7 @@ Return value: ASSERT(IS_ON_BOARD(c)); if ((c & 0xF0) == BACK_RANK_MASK[uColor]) { - cSquare = pos->cNonPawns[uColor][0]; + cSquare = KingPosition(pos, uColor); ASSERT(IS_ON_BOARD(cSquare)); if ((cSquare & 0xF0) == BACK_RANK_MASK[uColor]) { @@ -4323,7 +4287,7 @@ Return value: ASSERT(p && IS_QUEEN(p)); uColor = GET_COLOR(p); ASSERT(IS_VALID_COLOR(uColor)); - cKing = pos->cNonPawns[FLIP(uColor)][0]; + cKing = KingPosition(pos, FLIP(uColor)); ASSERT(IS_ON_BOARD(cKing)); ASSERT(IS_KING(pos->rgSquare[cKing].pPiece)); @@ -4444,7 +4408,8 @@ static void _EvalKing(IN OUT POSITION *pos, IN const COOR c, IN const PAWN_HASH_ENTRY *pHash, - IN SEARCHER_THREAD_CONTEXT *ctx) + IN SEARCHER_THREAD_CONTEXT *ctx, + IN const ULONG uTotalNonPawnsBothSides) /** Routine description: @@ -4454,6 +4419,12 @@ Parameters: POSITION *pos, COOR c, PAWN_HASH_ENTRY *pHash, + ULONG uTotalNonPawnsBothSides : TotalNonPawnCount(pos, WHITE) + + TotalNonPawnCount(pos, BLACK), hoisted up to Eval() (chess.h's + TotalNonPawnCount is a POPCNT now, not a stored field -- see + its comment) since this function runs twice per Eval() call + (once per king) and used to recompute the identical sum both + times. Return value: @@ -4834,7 +4805,7 @@ Return value: // // Special code for kings in the endgame // - u = pos->uNonPawnCount[WHITE][0] + pos->uNonPawnCount[BLACK][0]; + u = uTotalNonPawnsBothSides; if (u < 8) { // Encourage kings to come to the center when safe. @@ -4880,8 +4851,8 @@ Return value: bb = (pos->bbPawns[WHITE] | pos->bbPawns[BLACK]); while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb))) { - i += (DISTANCE(cSquare, pos->cNonPawns[BLACK][0]) - - DISTANCE(cSquare, pos->cNonPawns[WHITE][0])); + i += (DISTANCE(cSquare, KingPosition(pos, BLACK)) - + DISTANCE(cSquare, KingPosition(pos, WHITE))); } EVAL_TERM(WHITE, KING, @@ -4933,8 +4904,8 @@ Return value: { return(FALSE); } - ASSERT((pos->uNonPawnCount[WHITE][0] == 1) || - (pos->uNonPawnCount[BLACK][0] == 1)) + ASSERT((TotalNonPawnCount(pos, WHITE) == 1) || + (TotalNonPawnCount(pos, BLACK) == 1)) // Both deferred past the no-passer early return above -- no need to // pay for this on the common case (most positions have no passer at @@ -4943,13 +4914,13 @@ Return value: // the pre-lazy-exit segment whenever that's true). uRacerDist[BLACK] = 99; uRacerDist[WHITE] = 99; - fDontCountMeOut[BLACK] = ((RANK(pos->cNonPawns[BLACK][0]) <= 3) && - (pos->uPawnCount[BLACK] != 0)); - fDontCountMeOut[WHITE] = ((RANK(pos->cNonPawns[WHITE][0]) >= 6) && - (pos->uPawnCount[WHITE] != 0)); + fDontCountMeOut[BLACK] = ((RANK(KingPosition(pos, BLACK)) <= 3) && + (pos->bbPawns[BLACK] != 0)); + fDontCountMeOut[WHITE] = ((RANK(KingPosition(pos, WHITE)) >= 6) && + (pos->bbPawns[WHITE] != 0)); FOREACH_COLOR(uColor) { - if (pos->uNonPawnCount[FLIP(uColor)][0] == 1) + if (TotalNonPawnCount(pos, FLIP(uColor)) == 1) { d1 = 16 * g_iAhead[uColor]; ASSERT((d1 * 2) == (32 * g_iAhead[uColor])); @@ -4960,7 +4931,7 @@ Return value: { // If the enemy king can take this passer and is // on the move, all bets are off! - cKing = pos->cNonPawns[FLIP(uColor)][0]; + cKing = KingPosition(pos, FLIP(uColor)); ASSERT(IS_KING(pos->rgSquare[cKing].pPiece)); ASSERT(GET_COLOR(pos->rgSquare[cKing].pPiece) != uColor); if (pos->uToMove == FLIP(uColor) && @@ -4994,7 +4965,7 @@ Return value: uPawnDist); } - cKing = pos->cNonPawns[uColor][0]; + cKing = KingPosition(pos, uColor); ASSERT(IS_KING(pos->rgSquare[cKing].pPiece)); ASSERT(GET_COLOR(pos->rgSquare[cKing].pPiece) == uColor); @@ -5132,22 +5103,22 @@ Return value: // // Consider distance from friend king / enemy king // - i = (8 - DISTANCE(c, pos->cNonPawns[uColor][0])); + i = (8 - DISTANCE(c, KingPosition(pos, uColor))); if (((uColor == WHITE) && - ((pos->cNonPawns[BLACK][0] >> 4) <= (c >> 4))) || + ((KingPosition(pos, BLACK) >> 4) <= (c >> 4))) || ((uColor == BLACK) && - ((pos->cNonPawns[WHITE][0] >> 4) >= (c >> 4)))) { + ((KingPosition(pos, WHITE) >> 4) >= (c >> 4)))) { // // The enemy king is ahead of the passer // - i += FILE_DISTANCE(c, pos->cNonPawns[FLIP(uColor)][0]) * 4; + i += FILE_DISTANCE(c, KingPosition(pos, FLIP(uColor))) * 4; } else { // // The enemy king is behind the passer // - i += 10 + DISTANCE(c, pos->cNonPawns[FLIP(uColor)][0]) * 4; + i += 10 + DISTANCE(c, KingPosition(pos, FLIP(uColor))) * 4; } i *= PASSER_MATERIAL_UP_SCALER[pos->uArmyScaler[FLIP(uColor)]]; i /= 8; @@ -5162,8 +5133,8 @@ Return value: // If a side is a piece down then the other side's passers // are even more dangerous. // - if (pos->uNonPawnCount[uColor][0] > - pos->uNonPawnCount[FLIP(uColor)][0]) + if (TotalNonPawnCount(pos, uColor) > + TotalNonPawnCount(pos, FLIP(uColor))) { i = (PASSER_BY_RANK[uColor][RANK(c)] / 2); EVAL_TERM(uColor, @@ -5246,8 +5217,8 @@ Return value: // engine will not like positions like KNB vs KRP - the side with // the pawn has the winning chances. // - ULONG uAheadPawnCount = pos->uPawnCount[uAhead]; - ULONG uInverseBehindPieceCount = 9 - MINU(9, pos->uNonPawnCount[uBehind][0]); + ULONG uAheadPawnCount = FastCountBits(pos->bbPawns[uAhead]); + ULONG uInverseBehindPieceCount = 9 - MINU(9, TotalNonPawnCount(pos, uBehind)); EVAL_TERM(uAhead, 0, ILLEGAL_COOR, @@ -5299,18 +5270,16 @@ Return value: **/ { COOR c; - ULONG u; + BITBOARD bbNonKingPieces; POSITION *pos = &ctx->sPosition; ULONG uSide = pos->uToMove; #ifdef DEBUG PIECE p; #endif - for (u = 1; - u < pos->uNonPawnCount[uSide][0]; - u++) + bbNonKingPieces = NON_KING_PIECES_BB(pos, uSide); + while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbNonKingPieces))) { - c = pos->cNonPawns[uSide][u]; #ifdef DEBUG ASSERT(IS_ON_BOARD(c)); p = pos->rgSquare[c].pPiece; @@ -5436,9 +5405,9 @@ Return value: ULONG uColor; FOREACH_COLOR(uColor) { - ULONG uBishopCount = pos->uNonPawnCount[uColor][BISHOP]; + ULONG uBishopCount = FastCountBits(pos->bbPieces[uColor][BISHOP]); ASSERT(uBishopCount <= 10); - ULONG uWhiteSqBishopCount = pos->uWhiteSqBishopCount[uColor]; + ULONG uWhiteSqBishopCount = WhiteSqBishopCount(pos, uColor); ASSERT(uWhiteSqBishopCount <= 10); FLAG fPair = ((uBishopCount > 1) & (uWhiteSqBishopCount != 0) & @@ -5501,7 +5470,7 @@ Return value: // A pawn always gives some winning chances (it can always try to // queen with support) -- nothing below applies. // - if (pos->uPawnCount[uSide] > 0) + if (FastCountBits(pos->bbPawns[uSide]) > 0) { return(TRUE); } @@ -5511,9 +5480,9 @@ Return value: // never enough material to force mate (K+N or K+B vs anything is // never a forced win on material alone). // - if ((2 == pos->uNonPawnCount[uSide][0]) && - (1 == (pos->uNonPawnCount[uSide][KNIGHT] + - pos->uNonPawnCount[uSide][BISHOP]))) + if ((2 == TotalNonPawnCount(pos, uSide)) && + (1 == (FastCountBits(pos->bbPieces[uSide][KNIGHT]) + + FastCountBits(pos->bbPieces[uSide][BISHOP])))) { return(FALSE); } @@ -5524,16 +5493,16 @@ Return value: // force a win either -- e.g. KRB vs KR can be held by the // defender with correct play. // - iMajorDiff = (INT)(pos->uNonPawnCount[uSide][ROOK] + - 2 * pos->uNonPawnCount[uSide][QUEEN]) - - (INT)(pos->uNonPawnCount[uEnemy][ROOK] + - 2 * pos->uNonPawnCount[uEnemy][QUEEN]); + iMajorDiff = (INT)(FastCountBits(pos->bbPieces[uSide][ROOK]) + + 2 * FastCountBits(pos->bbPieces[uSide][QUEEN])) - + (INT)(FastCountBits(pos->bbPieces[uEnemy][ROOK]) + + 2 * FastCountBits(pos->bbPieces[uEnemy][QUEEN])); if ((1 == iMajorDiff) || (-1 == iMajorDiff)) { - iMinorDiff = (INT)(pos->uNonPawnCount[uEnemy][KNIGHT] + - pos->uNonPawnCount[uEnemy][BISHOP]) - - (INT)(pos->uNonPawnCount[uSide][KNIGHT] + - pos->uNonPawnCount[uSide][BISHOP]); + iMinorDiff = (INT)(FastCountBits(pos->bbPieces[uEnemy][KNIGHT]) + + FastCountBits(pos->bbPieces[uEnemy][BISHOP])) - + (INT)(FastCountBits(pos->bbPieces[uSide][KNIGHT]) + + FastCountBits(pos->bbPieces[uSide][BISHOP])); if (iMajorDiff == iMinorDiff) { return(FALSE); @@ -5548,12 +5517,12 @@ Return value: // excluded, since that has one bishop, not zero) -- both are // genuine, if sometimes technique-heavy, forced wins. // - if ((2 == pos->uNonPawnCount[uSide][KNIGHT]) && - (0 == pos->uNonPawnCount[uSide][BISHOP]) && - (0 == pos->uNonPawnCount[uSide][ROOK]) && - (0 == pos->uNonPawnCount[uSide][QUEEN]) && - (1 == pos->uNonPawnCount[uEnemy][0]) && - (0 == pos->uPawnCount[uEnemy])) + if ((2 == FastCountBits(pos->bbPieces[uSide][KNIGHT])) && + (0 == FastCountBits(pos->bbPieces[uSide][BISHOP])) && + (0 == FastCountBits(pos->bbPieces[uSide][ROOK])) && + (0 == FastCountBits(pos->bbPieces[uSide][QUEEN])) && + (1 == TotalNonPawnCount(pos, uEnemy)) && + (0 == FastCountBits(pos->bbPawns[uEnemy]))) { return(FALSE); } @@ -5603,11 +5572,11 @@ EvalLookForDrawishSituations(POSITION *pos, } // If this is a BOOC endgame, push towards draw score too. - if ((pos->uNonPawnCount[WHITE][0] == 2) && - (pos->uNonPawnCount[BLACK][0] == 2) && - (pos->uNonPawnCount[WHITE][BISHOP] == 1) && - (pos->uNonPawnCount[BLACK][BISHOP] == 1) && - (pos->uWhiteSqBishopCount[WHITE] != pos->uWhiteSqBishopCount[BLACK])) { + if ((TotalNonPawnCount(pos, WHITE) == 2) && + (TotalNonPawnCount(pos, BLACK) == 2) && + (FastCountBits(pos->bbPieces[WHITE][BISHOP]) == 1) && + (FastCountBits(pos->bbPieces[BLACK][BISHOP]) == 1) && + (WhiteSqBishopCount(pos, WHITE) != WhiteSqBishopCount(pos, BLACK))) { *piScoreForSideToMove = g_iDrawScore[uColor] + ((*piScoreForSideToMove - g_iDrawScore[uColor]) / 4); } @@ -5703,9 +5672,9 @@ Return value: UINT64 u64MaterialSetupTimer = SystemReadTimeStampCounter(); #endif pos->iScore[uColor] = - (pos->uPawnMaterial[uColor] + pos->uNonPawnMaterial[uColor]); + (PawnMaterial(pos, uColor) + pos->uNonPawnMaterial[uColor]); pos->iScore[xColor] = - (pos->uPawnMaterial[xColor] + pos->uNonPawnMaterial[xColor]); + (PawnMaterial(pos, xColor) + pos->uNonPawnMaterial[xColor]); #ifdef EVAL_DUMP EvalTraceClear(); Trace("Material:\n%d\t\t%d\n", pos->iScore[WHITE], pos->iScore[BLACK]); @@ -5887,8 +5856,8 @@ Return value: // Look for won passers races. Note: This code cannot trust the // state of the attack tables yet! // - if ((pos->uNonPawnCount[WHITE][0] == 1) || - (pos->uNonPawnCount[BLACK][0] == 1)) + if ((TotalNonPawnCount(pos, WHITE) == 1) || + (TotalNonPawnCount(pos, BLACK) == 1)) { (void)_EvalPasserRacesAgainstLoneKings(pos, pHash); #ifdef EVAL_DUMP @@ -6146,7 +6115,7 @@ Return value: // // This is a "position is closed|open" number. // - pos->uClosedScaler = (pos->uPawnCount[WHITE] + pos->uPawnCount[BLACK]) - + pos->uClosedScaler = (FastCountBits(pos->bbPawns[WHITE]) + FastCountBits(pos->bbPawns[BLACK])) - (pHash->uNumUnmovedPawns[WHITE] + pHash->uNumUnmovedPawns[BLACK]) + (pHash->uNumRammedPawns); ASSERT(pos->uClosedScaler >= 0); @@ -6305,8 +6274,15 @@ Return value: #endif } - // Evaluate the two kings last. - c = pos->cNonPawns[uColor][0]; + // Evaluate the two kings last. Compute both sides' non-pawn count + // once here rather than in _EvalKing itself -- it's called twice + // below (once per king) and used to recompute the identical + // TotalNonPawnCount(WHITE)+TotalNonPawnCount(BLACK) sum both times. + { + ULONG uTotalNonPawnsBothSides = + TotalNonPawnCount(pos, WHITE) + TotalNonPawnCount(pos, BLACK); + + c = KingPosition(pos, uColor); #ifdef DEBUG ASSERT(IS_ON_BOARD(c)); { @@ -6316,13 +6292,14 @@ Return value: ASSERT(IS_KING(pDebugKing)); } #endif - TIMED_EVAL_CALL(ctx, u64CyclesEvalKing, _EvalKing(pos, c, pHash, ctx)); + TIMED_EVAL_CALL(ctx, u64CyclesEvalKing, + _EvalKing(pos, c, pHash, ctx, uTotalNonPawnsBothSides)); #ifdef EVAL_DUMP Trace("After our K at %s:\n%d\t\t%d\n", CoorToString(c), pos->iScore[WHITE], pos->iScore[BLACK]); #endif - c = pos->cNonPawns[xColor][0]; + c = KingPosition(pos, xColor); #ifdef DEBUG ASSERT(IS_ON_BOARD(c)); { @@ -6332,7 +6309,9 @@ Return value: ASSERT(IS_KING(pDebugKing)); } #endif - TIMED_EVAL_CALL(ctx, u64CyclesEvalKing, _EvalKing(pos, c, pHash, ctx)); + TIMED_EVAL_CALL(ctx, u64CyclesEvalKing, + _EvalKing(pos, c, pHash, ctx, uTotalNonPawnsBothSides)); + } #ifdef EVAL_DUMP Trace("After enemy K at %s:\n%d\t\t%d\n", CoorToString(c), pos->iScore[WHITE], pos->iScore[BLACK]); @@ -6432,11 +6411,11 @@ Return value: { (*piPositional)[WHITE] = MAX(pos->iScore[WHITE] - - (SCORE)(pos->uPawnMaterial[WHITE] + pos->uNonPawnMaterial[WHITE]), + (SCORE)(PawnMaterial(pos, WHITE) + pos->uNonPawnMaterial[WHITE]), -(FUTILITY_BASE_MARGIN_FULL / 2)); (*piPositional)[BLACK] = MAX(pos->iScore[BLACK] - - (SCORE)(pos->uPawnMaterial[BLACK] + pos->uNonPawnMaterial[BLACK]), + (SCORE)(PawnMaterial(pos, BLACK) + pos->uNonPawnMaterial[BLACK]), -(FUTILITY_BASE_MARGIN_FULL / 2)); ctx->sPlyInfo[ctx->uPly].uLastPositionalSource = EVAL_POSITIONAL_SOURCE_FULL; } @@ -224,8 +224,6 @@ Return value: { char *pPieceIndex; ULONG uPieceIndex; - ULONG uPieceCounters[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - ULONG uNumNonPawns[2] = { 0, 0 }; ULONG uColor; ULONG uFile = 0; ULONG uIndex; @@ -295,43 +293,28 @@ Return value: if (IS_PAWN(p)) { - // - // The pawn list is not ordered at all, add the new pawn's - // location to the list and point from the pawn to the list. - // - pos->cPawns[uColor][uPieceCounters[uPieceIndex]] = cSquare; pos->bbPawns[uColor] |= COOR_TO_BB(cSquare); pos->bbOccupied |= COOR_TO_BB(cSquare); pos->bbOccupiedSide[uColor] |= COOR_TO_BB(cSquare); - pos->rgSquare[cSquare].uIndex = uPieceCounters[uPieceIndex]; + pos->rgSquare[cSquare].uIndex = INVALID_PIECE_INDEX; pos->rgSquare[cSquare].pPiece = p; - pos->uPawnMaterial[uColor] += VALUE_PAWN; } else { if (IS_KING(p)) { - if (pos->cNonPawns[uColor][0] != ILLEGAL_COOR) + if (pos->cKingSq[uColor] != ILLEGAL_COOR) { Trace("_FenToPosition: I can't handle positions with " "more than one king, sorry.\n"); return(FALSE); } - pos->cNonPawns[uColor][0] = cSquare; - uIndex = 0; + pos->cKingSq[uColor] = cSquare; + uIndex = INVALID_PIECE_INDEX; } else { - if (IS_BISHOP(p)) - { - if (IS_WHITE_SQUARE_COOR(cSquare)) - { - pos->uWhiteSqBishopCount[uColor]++; - } - } - uIndex = uNumNonPawns[uColor] + 1; - pos->cNonPawns[uColor][uIndex] = cSquare; - uNumNonPawns[uColor]++; + uIndex = INVALID_PIECE_INDEX; pos->bbPieces[uColor][PIECE_TYPE(p)] |= COOR_TO_BB(cSquare); } pos->bbOccupied |= COOR_TO_BB(cSquare); @@ -340,39 +323,13 @@ Return value: pos->rgSquare[cSquare].uIndex = uIndex; pos->uNonPawnMaterial[uColor] += PIECE_VALUE(p); } - uPieceCounters[uPieceIndex]++; cSquare++; } pPieces++; } - // - // Set the piece counts based on uPieceCounter contents - // - if (IS_ON_BOARD(pos->cNonPawns[WHITE][0])) - { - uNumNonPawns[WHITE]++; - } - pos->uNonPawnCount[WHITE][0] = uNumNonPawns[WHITE]; - if (IS_ON_BOARD(pos->cNonPawns[BLACK][0])) - { - uNumNonPawns[BLACK]++; - } - pos->uNonPawnCount[BLACK][0] = uNumNonPawns[BLACK]; - FOREACH_COLOR(uColor) - { - pos->uPawnCount[uColor] = uPieceCounters[(PAWN << 1) | uColor]; - for (p = KNIGHT; p <= KING; p++) - { - pos->uNonPawnCount[uColor][p] = - uPieceCounters[(p << 1) | uColor]; - } - } - ASSERT((pos->uPawnCount[WHITE] * VALUE_PAWN) == pos->uPawnMaterial[WHITE]); - ASSERT((pos->uPawnCount[BLACK] * VALUE_PAWN) == pos->uPawnMaterial[BLACK]); - - if ((ILLEGAL_COOR == pos->cNonPawns[BLACK][0]) || - (ILLEGAL_COOR == pos->cNonPawns[WHITE][0])) + if ((ILLEGAL_COOR == pos->cKingSq[BLACK]) || + (ILLEGAL_COOR == pos->cKingSq[WHITE])) { Trace("FenToPosition: Warning, missing king location.\n"); } @@ -705,7 +662,6 @@ Return value: { char *szCapturedFen = SystemAllocateMemory((ULONG)strlen(szFen) + 2); char *szPieces, *szToMove, *szCastle, *szEnPassant, *szFifty; - ULONG u; // // Initialize the captured fen buffer with a trailing whitespace char @@ -734,15 +690,7 @@ Return value: // Clear the position struct // memset(p, 0, sizeof(POSITION)); - for (u = 0; u < ARRAY_SIZE(p->cPawns[WHITE]); u++) - { - p->cPawns[WHITE][u] = p->cPawns[BLACK][u] = ILLEGAL_COOR; - } - - for (u = 0; u < ARRAY_SIZE(p->cNonPawns[WHITE]); u++) - { - p->cNonPawns[WHITE][u] = p->cNonPawns[BLACK][u] = ILLEGAL_COOR; - } + p->cKingSq[WHITE] = p->cKingSq[BLACK] = ILLEGAL_COOR; // // Do the pieces / board @@ -771,8 +719,8 @@ Return value: p->u64NonPawnSig = ComputeSig(p); p->u64PawnSig = ComputePawnSig(p); p->iMaterialBalance[WHITE] = - ((SCORE)(p->uNonPawnMaterial[WHITE] + p->uPawnMaterial[WHITE]) - - (SCORE)(p->uNonPawnMaterial[BLACK] + p->uPawnMaterial[BLACK])); + ((SCORE)(p->uNonPawnMaterial[WHITE] + PawnMaterial(p, WHITE)) - + (SCORE)(p->uNonPawnMaterial[BLACK] + PawnMaterial(p, BLACK))); p->iMaterialBalance[BLACK] = p->iMaterialBalance[WHITE] * -1; #ifdef DEBUG diff --git a/src/generate.c b/src/generate.c index e3db067..7e8f43e 100644 --- a/src/generate.c +++ b/src/generate.c @@ -40,6 +40,16 @@ Revision History: #include "chess.h" #include "psqt.h" +// See eval.c's identical comment: CoorFromBitBoardRank8ToRank1 +// (bitboard.c) is a real out-of-line extern call; this routes the +// bitboard-scan loops added during the cNonPawns/cPawns retirement +// (dead mailbox-fallback paths under this build's *_BITBOARD toggles, +// but still worth keeping fast for other toggle combinations) to the +// inlined FastCoorFromBitBoardRank8ToRank1 (chess.h) instead. +#ifndef CROUTINES +#define CoorFromBitBoardRank8ToRank1 FastCoorFromBitBoardRank8ToRank1 +#endif + // // Note: this order is important because of how EvalQueen works // @@ -83,7 +93,7 @@ Return value: **/ { register ULONG u; - COOR cKing = pos->cNonPawns[FLIP(pos->uToMove)][0]; + COOR cKing = KingPosition(pos, FLIP(pos->uToMove)); COOR c; ULONG uPly = pStack->uPly; #ifdef DEBUG @@ -182,7 +192,7 @@ Return value: COOR cTo = mv.cTo; COOR cFrom = mv.cFrom; PIECE pPiece = mv.pMoved; - COOR xKing = pos->cNonPawns[FLIP(GET_COLOR(pPiece))][0]; + COOR xKing = KingPosition(pos, FLIP(GET_COLOR(pPiece))); KEY_POINTER *kp = &(pStack->sUnblocked[ctx->uPly][0]); ULONG uUnblocked = pStack->uUnblockedKeyValue[ctx->uPly]; COOR cEpSquare; @@ -771,8 +781,8 @@ Return value: pos->bbPieces[BLACK][KNIGHT] | pos->bbPieces[BLACK][BISHOP] | pos->bbPieces[BLACK][ROOK] | pos->bbPieces[BLACK][QUEEN] | pos->bbPawns[WHITE] | pos->bbPawns[BLACK] | - COOR_TO_BB(pos->cNonPawns[WHITE][0]) | - COOR_TO_BB(pos->cNonPawns[BLACK][0])); + COOR_TO_BB(KingPosition(pos, WHITE)) | + COOR_TO_BB(KingPosition(pos, BLACK))); } // board_representation/MOVEGEN_MIGRATION.md section 6a Phase 2: raw @@ -3365,7 +3375,6 @@ Return value: GenerateWhiteKing // 13 (WHITE_KING) #endif }; - ULONG u; #ifdef DEBUG PIECE p; #endif @@ -3386,19 +3395,20 @@ Return value: pStack->bbOccupied = pos->bbOccupied; #endif - for(u = pos->uNonPawnCount[pos->uToMove][0] - 1; - u != (ULONG)-1; - u--) { - c = pos->cNonPawns[pos->uToMove][u]; + BITBOARD bbNonPawns = NON_KING_PIECES_BB(pos, pos->uToMove) | + COOR_TO_BB(KingPosition(pos, pos->uToMove)); + while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbNonPawns))) + { #ifdef DEBUG - ASSERT(IS_ON_BOARD(c)); - p = pos->rgSquare[c].pPiece; - ASSERT(!IS_EMPTY(p)); - ASSERT(!IS_PAWN(p)); - ASSERT(GET_COLOR(p) == pos->uToMove); + ASSERT(IS_ON_BOARD(c)); + p = pos->rgSquare[c].pPiece; + ASSERT(!IS_EMPTY(p)); + ASSERT(!IS_PAWN(p)); + ASSERT(GET_COLOR(p) == pos->uToMove); #endif - (JumpTable[pos->rgSquare[c].pPiece])(pStack, pos, c); + (JumpTable[pos->rgSquare[c].pPiece])(pStack, pos, c); + } } #if defined(GENERATE_PAWN_BITBOARD) @@ -3406,11 +3416,11 @@ Return value: #else if (pos->uToMove == BLACK) { - for(u = 0; u < pos->uPawnCount[BLACK]; u++) + BITBOARD bbPawnsToWalk = pos->bbPawns[BLACK]; + + while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbPawnsToWalk))) { - c = pos->cPawns[BLACK][u]; #ifdef DEBUG - ASSERT(IS_ON_BOARD(c)); p = pos->rgSquare[c].pPiece; ASSERT(!IS_EMPTY(p)); ASSERT(IS_PAWN(p)); @@ -3419,12 +3429,12 @@ Return value: GenerateBlackPawn(pStack, pos, c); } } else { + BITBOARD bbPawnsToWalk = pos->bbPawns[WHITE]; + ASSERT(pos->uToMove == WHITE); - for(u = 0; u < pos->uPawnCount[WHITE]; u++) + while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbPawnsToWalk))) { - c = pos->cPawns[WHITE][u]; #ifdef DEBUG - ASSERT(IS_ON_BOARD(c)); p = pos->rgSquare[c].pPiece; ASSERT(!IS_EMPTY(p)); ASSERT(IS_PAWN(p)); @@ -3504,9 +3514,6 @@ Return value: BITBOARD bb; ULONG uBitIndex; COOR c; -#if !defined(GENERATE_PAWN_BITBOARD) - ULONG u; -#endif #if defined(DEBUG) && !defined(GENERATE_PAWN_BITBOARD) PIECE p; #endif @@ -3558,7 +3565,7 @@ Return value: // side][0] -- see POSITION's bbPieces field comment in chess.h for // why a bitboard would add nothing here); still a direct, // statically-known call, same as the four loops above. - c = pos->cNonPawns[uSide][0]; + c = KingPosition(pos, uSide); ASSERT(IS_KING(pos->rgSquare[c].pPiece)); _GenerateKingBB(pStack, pos, c); @@ -3572,11 +3579,11 @@ Return value: #else if (uSide == BLACK) { - for(u = 0; u < pos->uPawnCount[BLACK]; u++) + BITBOARD bbPawnsToWalk = pos->bbPawns[BLACK]; + + while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbPawnsToWalk))) { - c = pos->cPawns[BLACK][u]; #ifdef DEBUG - ASSERT(IS_ON_BOARD(c)); p = pos->rgSquare[c].pPiece; ASSERT(!IS_EMPTY(p)); ASSERT(IS_PAWN(p)); @@ -3587,12 +3594,12 @@ Return value: } else { + BITBOARD bbPawnsToWalk = pos->bbPawns[WHITE]; + ASSERT(uSide == WHITE); - for(u = 0; u < pos->uPawnCount[WHITE]; u++) + while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbPawnsToWalk))) { - c = pos->cPawns[WHITE][u]; #ifdef DEBUG - ASSERT(IS_ON_BOARD(c)); p = pos->rgSquare[c].pPiece; ASSERT(!IS_EMPTY(p)); ASSERT(IS_PAWN(p)); @@ -3656,7 +3663,7 @@ Return value: #endif PIECE p; PIECE pKing; - COOR cKing = pos->cNonPawns[pos->uToMove][0]; + COOR cKing = KingPosition(pos, pos->uToMove); #if !defined(GENERATE_ESCAPES_KING_BITBOARD) int iIndex; #endif @@ -3920,27 +3927,29 @@ Return value: bbTargetMask); } #else - for (u = 1; // don't consider the king - u < pos->uNonPawnCount[pos->uToMove][0]; - u++) { - cDefender = pos->cNonPawns[pos->uToMove][u]; - ASSERT(IS_ON_BOARD(cDefender)); - p = pos->rgSquare[cDefender].pPiece; - ASSERT(p && !IS_PAWN(p)); - ASSERT(GET_COLOR(p) == pos->uToMove); + BITBOARD bbNonKingPieces = NON_KING_PIECES_BB(pos, pos->uToMove); - (JumpTable[p])(pStack, pos, cDefender, cKing, c); + 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) { - for (u = 0; u < pos->uPawnCount[BLACK]; u++) + BITBOARD bbPawnsToWalk = pos->bbPawns[BLACK]; + + while (IS_ON_BOARD(cDefender = + CoorFromBitBoardRank8ToRank1(&bbPawnsToWalk))) { - cDefender = pos->cPawns[BLACK][u]; #ifdef DEBUG - ASSERT(IS_ON_BOARD(cDefender)); p = pos->rgSquare[cDefender].pPiece; ASSERT(p && IS_PAWN(p)); ASSERT(GET_COLOR(p) == pos->uToMove); @@ -3948,12 +3957,13 @@ Return value: SaveMeBlackPawn(pStack, pos, cDefender, cKing, c); } } else { + BITBOARD bbPawnsToWalk = pos->bbPawns[WHITE]; + ASSERT(pos->uToMove == WHITE); - for (u = 0; u < pos->uPawnCount[WHITE]; u++) + while (IS_ON_BOARD(cDefender = + CoorFromBitBoardRank8ToRank1(&bbPawnsToWalk))) { - cDefender = pos->cPawns[WHITE][u]; #ifdef DEBUG - ASSERT(IS_ON_BOARD(cDefender)); p = pos->rgSquare[cDefender].pPiece; ASSERT(p && IS_PAWN(p)); ASSERT(GET_COLOR(p) == pos->uToMove); @@ -45,8 +45,7 @@ Return value: { register PIECE p; register ULONG c; - ULONG uIndex; - + #ifdef DEBUG VerifyPositionConsistency(pos, FALSE); ASSERT(IS_ON_BOARD(cFrom)); @@ -57,11 +56,12 @@ Return value: pos->rgSquare[cFrom].pPiece = EMPTY; ASSERT(!IS_PAWN(p)); ASSERT(IS_VALID_PIECE(p)); - uIndex = pos->rgSquare[cFrom].uIndex; c = GET_COLOR(p); ASSERT(IS_VALID_COLOR(c)); - ASSERT(pos->cNonPawns[c][uIndex] == cFrom); - pos->cNonPawns[c][uIndex] = cTo; + if (IS_KING(p)) + { + pos->cKingSq[c] = cTo; + } pos->bbPieces[c][PIECE_TYPE(p)] &= ~COOR_TO_BB(cFrom); pos->bbPieces[c][PIECE_TYPE(p)] |= COOR_TO_BB(cTo); pos->bbOccupied &= ~COOR_TO_BB(cFrom); @@ -77,7 +77,7 @@ Return value: } #endif pos->rgSquare[cTo].pPiece = p; - pos->rgSquare[cTo].uIndex = uIndex; + pos->rgSquare[cTo].uIndex = INVALID_PIECE_INDEX; #ifdef DEBUG pos->rgSquare[cFrom].uIndex = INVALID_PIECE_INDEX; VerifyPositionConsistency(pos, FALSE); @@ -108,8 +108,7 @@ Return value: { register PIECE p; register ULONG c; - ULONG uIndex; - + #ifdef DEBUG VerifyPositionConsistency(pos, FALSE); ASSERT(IS_ON_BOARD(cFrom)); @@ -120,11 +119,8 @@ Return value: pos->rgSquare[cFrom].pPiece = EMPTY; ASSERT(IS_PAWN(p)); ASSERT(IS_VALID_PIECE(p)); - uIndex = pos->rgSquare[cFrom].uIndex; c = GET_COLOR(p); ASSERT(IS_VALID_COLOR(c)); - ASSERT(pos->cPawns[c][uIndex] == cFrom); - pos->cPawns[c][uIndex] = cTo; pos->bbPawns[c] &= ~COOR_TO_BB(cFrom); pos->bbPawns[c] |= COOR_TO_BB(cTo); pos->bbOccupied &= ~COOR_TO_BB(cFrom); @@ -134,7 +130,7 @@ Return value: pos->u64PawnSig ^= g_u64PawnSigSeeds[cFrom][c]; pos->u64PawnSig ^= g_u64PawnSigSeeds[cTo][c]; pos->rgSquare[cTo].pPiece = p; - pos->rgSquare[cTo].uIndex = uIndex; + pos->rgSquare[cTo].uIndex = INVALID_PIECE_INDEX; #ifdef DEBUG pos->rgSquare[cFrom].uIndex = INVALID_PIECE_INDEX; VerifyPositionConsistency(pos, FALSE); @@ -167,8 +163,7 @@ Return value: { register PIECE p; register ULONG c; - ULONG uIndex; - + #ifdef DEBUG ASSERT(IS_ON_BOARD(cFrom)); ASSERT(IS_ON_BOARD(cTo)); @@ -178,11 +173,12 @@ Return value: pos->rgSquare[cFrom].pPiece = EMPTY; ASSERT(IS_VALID_PIECE(p)); ASSERT(!IS_PAWN(p)); - uIndex = pos->rgSquare[cFrom].uIndex; c = GET_COLOR(p); ASSERT(IS_VALID_COLOR(c)); - ASSERT(pos->cNonPawns[c][uIndex] == cFrom); - pos->cNonPawns[c][uIndex] = cTo; + if (IS_KING(p)) + { + pos->cKingSq[c] = cTo; + } pos->bbPieces[c][PIECE_TYPE(p)] &= ~COOR_TO_BB(cFrom); pos->bbPieces[c][PIECE_TYPE(p)] |= COOR_TO_BB(cTo); pos->bbOccupied &= ~COOR_TO_BB(cFrom); @@ -190,7 +186,7 @@ Return value: pos->bbOccupiedSide[c] &= ~COOR_TO_BB(cFrom); pos->bbOccupiedSide[c] |= COOR_TO_BB(cTo); pos->rgSquare[cTo].pPiece = p; - pos->rgSquare[cTo].uIndex = uIndex; + pos->rgSquare[cTo].uIndex = INVALID_PIECE_INDEX; #ifdef DEBUG pos->rgSquare[cFrom].uIndex = INVALID_PIECE_INDEX; #endif @@ -222,8 +218,7 @@ Return value: { register PIECE p; register ULONG c; - ULONG uIndex; - + #ifdef DEBUG ASSERT(IS_ON_BOARD(cFrom)); ASSERT(IS_ON_BOARD(cTo)); @@ -233,11 +228,8 @@ Return value: pos->rgSquare[cFrom].pPiece = EMPTY; ASSERT(IS_VALID_PIECE(p)); ASSERT(IS_PAWN(p)); - uIndex = pos->rgSquare[cFrom].uIndex; c = GET_COLOR(p); ASSERT(IS_VALID_COLOR(c)); - ASSERT(pos->cPawns[c][uIndex] == cFrom); - pos->cPawns[c][uIndex] = cTo; pos->bbPawns[c] &= ~COOR_TO_BB(cFrom); pos->bbPawns[c] |= COOR_TO_BB(cTo); pos->bbOccupied &= ~COOR_TO_BB(cFrom); @@ -245,7 +237,7 @@ Return value: pos->bbOccupiedSide[c] &= ~COOR_TO_BB(cFrom); pos->bbOccupiedSide[c] |= COOR_TO_BB(cTo); pos->rgSquare[cTo].pPiece = p; - pos->rgSquare[cTo].uIndex = uIndex; + pos->rgSquare[cTo].uIndex = INVALID_PIECE_INDEX; #ifdef DEBUG pos->rgSquare[cFrom].uIndex = INVALID_PIECE_INDEX; #endif @@ -275,9 +267,6 @@ Return value: register PIECE pLifted; ULONG color; register SCORE pv; - ULONG uLastIndex; - ULONG uIndex; - COOR c; ULONG u; #ifdef DEBUG VerifyPositionConsistency(pos, FALSE); @@ -293,11 +282,6 @@ Return value: ASSERT(!IS_KING(pLifted)); pos->bbOccupied &= ~COOR_TO_BB(cSquare); pos->bbOccupiedSide[GET_COLOR(pLifted)] &= ~COOR_TO_BB(cSquare); - uIndex = pos->rgSquare[cSquare].uIndex; -#ifdef DEBUG - ASSERT(IS_VALID_PIECE_INDEX(uIndex)); - pos->rgSquare[cSquare].uIndex = INVALID_PIECE_INDEX; -#endif color = GET_COLOR(pLifted); ASSERT(IS_VALID_COLOR(color)); pv = PIECE_VALUE(pLifted); @@ -308,61 +292,27 @@ Return value: if (IS_PAWN(pLifted)) { - ASSERT(pos->cPawns[color][uIndex] == cSquare); +#ifdef DEBUG + pos->rgSquare[cSquare].uIndex = INVALID_PIECE_INDEX; +#endif ASSERT(pv == VALUE_PAWN); - pos->uPawnMaterial[color] -= pv; - ASSERT(pos->uPawnMaterial[color] <= (7 * VALUE_PAWN)); pos->u64PawnSig ^= g_u64PawnSigSeeds[cSquare][color]; pos->bbPawns[color] &= ~COOR_TO_BB(cSquare); - - // - // Remove this pawn from the pawn list. - // - pos->uPawnCount[color]--; - ASSERT(pos->uPawnCount[color] < 8); - uLastIndex = pos->uPawnCount[color]; - - // - // Assume that the pawn being lifted is not the last pawn on - // the list. We can't have a hole in the list so swap the - // last pawn on the list into this guy's spot. - // - c = pos->cPawns[color][uLastIndex]; - pos->rgSquare[c].uIndex = uIndex; - pos->cPawns[color][uIndex] = c; -#ifdef DEBUG - pos->cPawns[color][uLastIndex] = ILLEGAL_COOR; -#endif } - else + else { // - // Piece is not a pawn + // Piece is not a pawn -- no piece-location list to maintain + // (see cKingSq/bbPieces in chess.h for how king/knight/bishop/ + // rook/queen locations are tracked instead), just the + // material/count/bitboard bookkeeping below. // - ASSERT(pos->cNonPawns[color][uIndex] == cSquare); ASSERT(pos->uNonPawnMaterial[color] >= VALUE_KING); ASSERT(pv > VALUE_PAWN); pos->uNonPawnMaterial[color] -= pv; ASSERT(pos->uNonPawnMaterial[color] <= VALUE_MAX_ARMY); - - // - // Remove this piece from the list. - // - pos->uNonPawnCount[color][0]--; - ASSERT(pos->uNonPawnCount[color] > 0); - uLastIndex = pos->uNonPawnCount[color][0]; // optimized... - // - // Assume that it is NOT the last piece in the list ... swap - // the last piece into this one's spot. - // - c = pos->cNonPawns[color][uLastIndex]; - pos->rgSquare[c].uIndex = uIndex; - pos->cNonPawns[color][uIndex] = c; -#ifdef DEBUG - pos->cNonPawns[color][uLastIndex] = ILLEGAL_COOR; -#endif // // Change per-piece counters. @@ -370,12 +320,7 @@ Return value: u = PIECE_TYPE(pLifted); ASSERT((u >= KNIGHT) && (u < KING)); pos->u64NonPawnSig ^= g_u64SigSeeds[cSquare][u][color]; - pos->uNonPawnCount[color][u]--; - ASSERT(pos->uNonPawnCount[color][u] <= 9); pos->bbPieces[color][u] &= ~COOR_TO_BB(cSquare); - pos->uWhiteSqBishopCount[color] -= (IS_BISHOP(pLifted) & - IS_SQUARE_WHITE(cSquare)); - ASSERT(pos->uWhiteSqBishopCount[color] <= 9); } #ifdef DEBUG @@ -414,9 +359,6 @@ Return value: register PIECE pLifted; ULONG color; register SCORE pv; - ULONG uLastIndex; - ULONG uIndex; - COOR c; ULONG u; ASSERT(IS_ON_BOARD(cSquare)); @@ -431,11 +373,6 @@ Return value: pos->bbOccupied &= ~COOR_TO_BB(cSquare); pos->bbOccupiedSide[GET_COLOR(pLifted)] &= ~COOR_TO_BB(cSquare); - uIndex = pos->rgSquare[cSquare].uIndex; -#ifdef DEBUG - ASSERT(IS_VALID_PIECE_INDEX(uIndex)); - pos->rgSquare[cSquare].uIndex = INVALID_PIECE_INDEX; -#endif color = GET_COLOR(pLifted); ASSERT(IS_VALID_COLOR(color)); pv = PIECE_VALUE(pLifted); @@ -446,72 +383,30 @@ Return value: if (IS_PAWN(pLifted)) { - ASSERT(pos->cPawns[color][uIndex] == cSquare); +#ifdef DEBUG + pos->rgSquare[cSquare].uIndex = INVALID_PIECE_INDEX; +#endif ASSERT(pv == VALUE_PAWN); - pos->uPawnMaterial[color] -= pv; - ASSERT(pos->uPawnMaterial[color] <= (7 * VALUE_PAWN)); pos->bbPawns[color] &= ~COOR_TO_BB(cSquare); - - // - // Remove this pawn from the pawn list. - // - pos->uPawnCount[color]--; - ASSERT(pos->uPawnCount[color] < 8); - uLastIndex = pos->uPawnCount[color]; // optimized... - - // - // Assume that the pawn being lifted is not the last pawn on - // the list. We can't have a hole in the list so swap the - // last pawn on the list into this guy's spot. - // - c = pos->cPawns[color][uLastIndex]; - pos->rgSquare[c].uIndex = uIndex; - pos->cPawns[color][uIndex] = c; -#ifdef DEBUG - pos->cPawns[color][uLastIndex] = ILLEGAL_COOR; -#endif } - else + else { // - // Piece is not a pawn + // Piece is not a pawn -- no piece-location list to maintain, + // just the material/count/bitboard bookkeeping below. // - ASSERT(pos->cNonPawns[color][uIndex] == cSquare); ASSERT(pos->uNonPawnMaterial[color] >= VALUE_KING); ASSERT(pv > VALUE_PAWN); pos->uNonPawnMaterial[color] -= pv; ASSERT(pos->uNonPawnMaterial[color] <= VALUE_MAX_ARMY); - - // - // Remove this piece from the list. - // - pos->uNonPawnCount[color][0]--; - ASSERT(pos->uNonPawnCount[color] > 0); - uLastIndex = pos->uNonPawnCount[color][0]; // optimized... - - // - // Assume that it is NOT the last piece in the list ... swap - // the last piece into this one's spot. - // - c = pos->cNonPawns[color][uLastIndex]; - pos->rgSquare[c].uIndex = uIndex; - pos->cNonPawns[color][uIndex] = c; -#ifdef DEBUG - pos->cNonPawns[color][uLastIndex] = ILLEGAL_COOR; -#endif // // Change per-piece counters. // u = PIECE_TYPE(pLifted); ASSERT((u >= KNIGHT) && (u < KING)); - pos->uNonPawnCount[color][u]--; - ASSERT(pos->uNonPawnCount[color][u] <= 9); pos->bbPieces[color][u] &= ~COOR_TO_BB(cSquare); - pos->uWhiteSqBishopCount[color] -= (IS_BISHOP(pLifted) & - IS_SQUARE_WHITE(cSquare)); - ASSERT(pos->uWhiteSqBishopCount[color] <= 9); } return(pLifted); @@ -539,10 +434,9 @@ Return value: **/ { register ULONG color = GET_COLOR(pPiece); - register ULONG uIndex; register SCORE pv; ULONG u; - + #ifdef DEBUG VerifyPositionConsistency(pos, FALSE); ASSERT(IS_EMPTY(pos->rgSquare[cSquare].pPiece)); @@ -559,50 +453,37 @@ Return value: if (IS_PAWN(pPiece)) { // - // Piece is a pawn + // Piece is a pawn -- no piece-location list to maintain (see + // bbPawns in chess.h), just the material/count/bitboard + // bookkeeping below. // ASSERT(pv == VALUE_PAWN); - pos->uPawnMaterial[color] += pv; - ASSERT(pos->uPawnMaterial[color] <= (8 * VALUE_PAWN)); - - uIndex = pos->uPawnCount[color]; - ASSERT((uIndex >= 0) && (uIndex <= 7)); - pos->uPawnCount[color]++; - ASSERT(pos->uPawnCount[color] <= 8); - pos->cPawns[color][uIndex] = cSquare; pos->bbPawns[color] |= COOR_TO_BB(cSquare); pos->u64PawnSig ^= g_u64PawnSigSeeds[cSquare][color]; + pos->rgSquare[cSquare].uIndex = INVALID_PIECE_INDEX; } else { // - // Piece is not a pawn + // Piece is not a pawn -- no piece-location list to maintain, + // just the material/count/bitboard bookkeeping below. // ASSERT(pv > VALUE_PAWN); pos->uNonPawnMaterial[color] += pv; ASSERT(pos->uNonPawnMaterial[color] <= VALUE_MAX_ARMY); - uIndex = pos->uNonPawnCount[color][0]; - pos->uNonPawnCount[color][0]++; - ASSERT(pos->uNonPawnCount[color][0] <= 16); - pos->cNonPawns[color][uIndex] = cSquare; - + u = PIECE_TYPE(pPiece); ASSERT((u >= KNIGHT) && (u < KING)); pos->u64NonPawnSig ^= g_u64SigSeeds[cSquare][u][color]; - pos->uNonPawnCount[color][u]++; - ASSERT(pos->uNonPawnCount[color][u] <= 10); pos->bbPieces[color][u] |= COOR_TO_BB(cSquare); - pos->uWhiteSqBishopCount[color] += (IS_BISHOP(pPiece) & - IS_SQUARE_WHITE(cSquare)); - ASSERT(pos->uWhiteSqBishopCount[color] <= 10); + pos->rgSquare[cSquare].uIndex = INVALID_PIECE_INDEX; } // // Place the piece on the board // pos->rgSquare[cSquare].pPiece = pPiece; - pos->rgSquare[cSquare].uIndex = uIndex; #ifdef DEBUG VerifyPositionConsistency(pos, FALSE); @@ -637,13 +518,12 @@ Return value: **/ { register ULONG color = GET_COLOR(pPiece); - register ULONG uIndex; register SCORE pv; ULONG u; - + ASSERT(IS_EMPTY(pos->rgSquare[cSquare].pPiece)); ASSERT(!IS_KING(pPiece)); - + pv = PIECE_VALUE(pPiece); pos->iMaterialBalance[color] += pv; pos->iMaterialBalance[FLIP(color)] -= pv; @@ -654,48 +534,34 @@ Return value: if (IS_PAWN(pPiece)) { // - // Piece is a pawn + // Piece is a pawn -- no piece-location list to maintain, just + // the material/count/bitboard bookkeeping below. // ASSERT(pv == VALUE_PAWN); - pos->uPawnMaterial[color] += pv; - ASSERT(pos->uPawnMaterial[color] <= (8 * VALUE_PAWN)); - - uIndex = pos->uPawnCount[color]; - ASSERT((uIndex >= 0) && (uIndex <= 7)); - pos->uPawnCount[color]++; - ASSERT(pos->uPawnCount[color] <= 8); - pos->cPawns[color][uIndex] = cSquare; pos->bbPawns[color] |= COOR_TO_BB(cSquare); + pos->rgSquare[cSquare].uIndex = INVALID_PIECE_INDEX; } else { // - // Piece is not a pawn + // Piece is not a pawn -- no piece-location list to maintain, + // just the material/count/bitboard bookkeeping below. // ASSERT(pv > VALUE_PAWN); pos->uNonPawnMaterial[color] += pv; ASSERT(pos->uNonPawnMaterial[color] <= VALUE_MAX_ARMY); - uIndex = pos->uNonPawnCount[color][0]; - pos->uNonPawnCount[color][0]++; - ASSERT(pos->uNonPawnCount[color][0] <= 16); - pos->cNonPawns[color][uIndex] = cSquare; - + u = PIECE_TYPE(pPiece); ASSERT((u >= KNIGHT) && (u < KING)); - pos->uNonPawnCount[color][u]++; - ASSERT(pos->uNonPawnCount[color][u] <= 10); pos->bbPieces[color][u] |= COOR_TO_BB(cSquare); - pos->uWhiteSqBishopCount[color] += (IS_BISHOP(pPiece) & - IS_SQUARE_WHITE(cSquare)); - ASSERT(pos->uWhiteSqBishopCount[color] <= 10); + pos->rgSquare[cSquare].uIndex = INVALID_PIECE_INDEX; } // // Place the piece on the board - // + // pos->rgSquare[cSquare].pPiece = pPiece; - pos->rgSquare[cSquare].uIndex = uIndex; } @@ -737,8 +603,8 @@ Return value: pi->uFifty = pos->uFifty; pi->fCastled[BLACK] = pos->fCastled[BLACK]; pi->fCastled[WHITE] = pos->fCastled[WHITE]; - pi->uTotalNonPawns = (pos->uNonPawnCount[BLACK][0] + - pos->uNonPawnCount[WHITE][0]); + pi->uTotalNonPawns = (FastCountBits(NON_KING_PIECES_BB(pos, BLACK)) + + FastCountBits(NON_KING_PIECES_BB(pos, WHITE)) + 2); ASSERT(IS_VALID_FLAG(pos->fCastled[WHITE]) && IS_VALID_FLAG(pos->fCastled[BLACK])); ASSERT((pos->bvCastleInfo & ~CASTLE_ALL_POSSIBLE) == 0); @@ -1064,7 +930,7 @@ Return value: // if (mv.uMove != 0) { - cKing = pos->cNonPawns[FLIP(pos->uToMove)][0]; + cKing = KingPosition(pos, FLIP(pos->uToMove)); ASSERT(IS_KING(pos->rgSquare[cKing].pPiece)); if (IS_KING(mv.pMoved) || IS_ESCAPING_CHECK(mv)) diff --git a/src/movesup.c b/src/movesup.c index 82ba10a..4a5f973 100644 --- a/src/movesup.c +++ b/src/movesup.c @@ -42,6 +42,14 @@ Revision History: #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 @@ -668,7 +676,7 @@ Return value: **/ { - COOR cKingLoc = pos->cNonPawns[uSide][0]; + COOR cKingLoc = KingPosition(pos, uSide); ASSERT(IS_VALID_COLOR(uSide)); ASSERT(IS_KING(pos->rgSquare[cKingLoc].pPiece)); @@ -697,28 +705,29 @@ Return value: **/ { - ULONG u; 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)); - ASSERT((pos->uNonPawnCount[uSide][0] - 1) < 16); - + // - // Begin at the end because the king is the 0th item in the list and - // we want to consider king moves last. - // - for (u = pos->uNonPawnCount[uSide][0] - 1; - u != (ULONG)-1; - u--) + // 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))) { - cLocation = pos->cNonPawns[uSide][u]; ASSERT(IS_ON_BOARD(cLocation)); pPiece = pos->rgSquare[cLocation].pPiece; ASSERT(!IS_EMPTY(pPiece)); @@ -810,7 +819,7 @@ Return value: ASSERT(IS_VALID_COLOR(uSide)); - cKingLoc = pos->cNonPawns[uSide][0]; + cKingLoc = KingPosition(pos, uSide); #ifdef DEBUG pKing = pos->rgSquare[cKingLoc].pPiece; ASSERT(IS_KING(pKing)); @@ -1024,7 +1033,7 @@ Return value: // pieces in the way... but it cannot expose its own king to check // by doing so! // - cKing = pos->cNonPawns[pos->uToMove][0]; + cKing = KingPosition(pos, pos->uToMove); cAttack = ExposesCheck(pos, cFrom, cKing); @@ -1421,6 +1430,20 @@ Return value: ASSERT(u >= ctx->sMoveStack.uBegin[ctx->uPly]); ASSERT(u < uEnd); + // Flee-to-safety: a small, same-tier selection-time nudge (not a + // GOOD_MOVE promotion -- that class was retired, see generate.c's + // RETIRED comment) for a quiet move whose origin square is + // currently en prise. NOT loop-invariant across repeated calls at + // the same node the way TotalNonPawnCount was in search.c/eval.c's + // recent hoists -- RecordEnprisePiece (dynamic.c) only populates + // this ply's cache from inside Eval(), which doesn't necessarily + // run before every single SelectBestWithHistory call at this node, + // so a later call in the same node can legitimately see a fresher + // answer than an earlier one. Recomputed every call (2026-09-16: + // briefly hoisted to the caller, reverted after a DEBUG-build + // ASSERT here caught the mismatch -- see git log). + cEnprise = FindEnprisePiece(ctx, ctx->sPosition.uToMove); + // Continuation history: "did a move like this tend to fail high // right after a move like that?", keyed by (previous move, this // move) at the same [piece][to] coarseness g_HistoryCounters @@ -1436,12 +1459,6 @@ Return value: fHaveContinuation = TRUE; } - // Flee-to-safety: a small, same-tier selection-time nudge (not a - // GOOD_MOVE promotion -- that class was retired, see generate.c's - // RETIRED comment) for a quiet move whose origin square is - // currently en prise. - cEnprise = FindEnprisePiece(ctx, ctx->sPosition.uToMove); - // // Linear search from u..ctx->sMoveStack.uEnd[ctx->uPly] for the // move with the best value. diff --git a/src/probe.c b/src/probe.c index 2d59e5a..5054142 100644 --- a/src/probe.c +++ b/src/probe.c @@ -33,6 +33,14 @@ Revision History: #include "chess.h" #include "fathom/tbprobe.h" +// See eval.c's identical comment: routes this file's Syzygy-bitboard- +// builder bitboard-scan loops to the inlined +// FastCoorFromBitBoardRank8ToRank1 (chess.h) instead of the real +// out-of-line extern call. +#ifndef CROUTINES +#define CoorFromBitBoardRank8ToRank1 FastCoorFromBitBoardRank8ToRank1 +#endif + ULONG g_uEgtbLock = 0; // @@ -147,9 +155,7 @@ Return value: **/ { ULONG uColor; - ULONG u; COOR c; - PIECE p; UINT64 bb; *pu64White = *pu64Black = 0ULL; @@ -160,37 +166,59 @@ Return value: { UINT64 *pu64Side = (uColor == WHITE) ? pu64White : pu64Black; - for (u = 0; u < pos->uPawnCount[uColor]; u++) { - c = pos->cPawns[uColor][u]; - ASSERT(IS_ON_BOARD(c)); - bb = (1ULL << (TO64(c))); - *pu64Side |= bb; - *pu64Pawns |= bb; + BITBOARD bbPawns = pos->bbPawns[uColor]; + + while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbPawns))) + { + bb = (1ULL << (TO64(c))); + *pu64Side |= bb; + *pu64Pawns |= bb; + } } - for (u = 0; u < pos->uNonPawnCount[uColor][0]; u++) { - c = pos->cNonPawns[uColor][u]; + c = KingPosition(pos, uColor); ASSERT(IS_ON_BOARD(c)); + ASSERT(IS_KING(pos->rgSquare[c].pPiece)); bb = (1ULL << (TO64(c))); *pu64Side |= bb; + *pu64Kings |= bb; + } + + { + BITBOARD bbType; + COOR cType; + + bbType = pos->bbPieces[uColor][KNIGHT]; + while (IS_ON_BOARD(cType = CoorFromBitBoardRank8ToRank1(&bbType))) + { + bb = (1ULL << (TO64(cType))); + *pu64Side |= bb; + *pu64Knights |= bb; + } + + bbType = pos->bbPieces[uColor][BISHOP]; + while (IS_ON_BOARD(cType = CoorFromBitBoardRank8ToRank1(&bbType))) + { + bb = (1ULL << (TO64(cType))); + *pu64Side |= bb; + *pu64Bishops |= bb; + } - if (0 == u) + bbType = pos->bbPieces[uColor][ROOK]; + while (IS_ON_BOARD(cType = CoorFromBitBoardRank8ToRank1(&bbType))) { - ASSERT(IS_KING(pos->rgSquare[c].pPiece)); - *pu64Kings |= bb; - continue; + bb = (1ULL << (TO64(cType))); + *pu64Side |= bb; + *pu64Rooks |= bb; } - p = pos->rgSquare[c].pPiece; - ASSERT(p && !IS_PAWN(p) && !IS_KING(p)); - if (IS_KNIGHT(p)) *pu64Knights |= bb; - else if (IS_BISHOP(p)) *pu64Bishops |= bb; - else if (IS_ROOK(p)) *pu64Rooks |= bb; - else + bbType = pos->bbPieces[uColor][QUEEN]; + while (IS_ON_BOARD(cType = CoorFromBitBoardRank8ToRank1(&bbType))) { - ASSERT(IS_QUEEN(p)); + bb = (1ULL << (TO64(cType))); + *pu64Side |= bb; *pu64Queens |= bb; } } @@ -243,8 +271,8 @@ Return value: return(FALSE); } - wcount = pos->uNonPawnCount[WHITE][0] + pos->uPawnCount[WHITE]; - bcount = pos->uNonPawnCount[BLACK][0] + pos->uPawnCount[BLACK]; + wcount = TotalNonPawnCount(pos, WHITE) + FastCountBits(pos->bbPawns[WHITE]); + bcount = TotalNonPawnCount(pos, BLACK) + FastCountBits(pos->bbPawns[BLACK]); if ((wcount + bcount) > TB_LARGEST) { return(FALSE); @@ -384,8 +412,8 @@ Return value: return(FALSE); } - wcount = pos->uNonPawnCount[WHITE][0] + pos->uPawnCount[WHITE]; - bcount = pos->uNonPawnCount[BLACK][0] + pos->uPawnCount[BLACK]; + wcount = TotalNonPawnCount(pos, WHITE) + FastCountBits(pos->bbPawns[WHITE]); + bcount = TotalNonPawnCount(pos, BLACK) + FastCountBits(pos->bbPawns[BLACK]); if ((wcount + bcount) > TB_LARGEST) { return(FALSE); diff --git a/src/recogn.c b/src/recogn.c index 32a618d..deb2285 100644 --- a/src/recogn.c +++ b/src/recogn.c @@ -23,6 +23,15 @@ Revision History: #include "chess.h" +// See eval.c's identical comment: routes this file's KBNK/KBKB +// adjacency-check bitboard-scan loops (called every node via +// RecognLookup in search.c) to the inlined +// FastCoorFromBitBoardRank8ToRank1 (chess.h) instead of the real +// out-of-line extern call. +#ifndef CROUTINES +#define CoorFromBitBoardRank8ToRank1 FastCoorFromBitBoardRank8ToRank1 +#endif + extern ULONG g_uIterateDepth; static COOR QUEENING_SQUARE_BY_COLOR_FILE[2][8] = { @@ -211,14 +220,14 @@ static FLAG if (!(p & PAWN)) { - if (pos->uPawnCount[uColor] > 0) return(FALSE); + if (FastCountBits(pos->bbPawns[uColor]) > 0) return(FALSE); } for (u = 0; u < ARRAY_LENGTH(q); u++) { if (!(p & q[u])) { - if (pos->uNonPawnCount[uColor][q[u]] > 0) return(FALSE); + if (FastCountBits(pos->bbPieces[uColor][q[u]]) > 0) return(FALSE); } } return(TRUE); @@ -276,8 +285,8 @@ Return value: ULONG uAdjacent; POSITION *pos = &ctx->sPosition; - ASSERT((pos->uNonPawnCount[WHITE][0] <= 3) && - (pos->uNonPawnCount[BLACK][0] <= 3)); + ASSERT((TotalNonPawnCount(pos, WHITE) <= 3) && + (TotalNonPawnCount(pos, BLACK) <= 3)); ASSERT(_NothingBut(pos, BISHOP, WHITE)); ASSERT(_NothingBut(pos, BISHOP, BLACK)); @@ -285,11 +294,11 @@ Return value: // Recognize KBKB as a draw unless there's a cornered king (in // which case it may be a mate-in-1) // - if ((pos->uNonPawnCount[WHITE][0] == 2) && - (pos->uNonPawnCount[BLACK][0] == 2)) + if ((TotalNonPawnCount(pos, WHITE) == 2) && + (TotalNonPawnCount(pos, BLACK) == 2)) { - if ((CORNER_DISTANCE(pos->cNonPawns[WHITE][0]) != 0) && - (CORNER_DISTANCE(pos->cNonPawns[BLACK][0]) != 0)) + if ((CORNER_DISTANCE(KingPosition(pos, WHITE)) != 0) && + (CORNER_DISTANCE(KingPosition(pos, BLACK)) != 0)) { *piScore = 0; return(RECOGN_EXACT); @@ -300,8 +309,8 @@ Return value: // Otherwise we want to deal with KB+ vs lone K. KBKBB etc are // too hard to recognize. // - if ((pos->uNonPawnCount[WHITE][0] != 1) && - (pos->uNonPawnCount[BLACK][0] != 1)) + if ((TotalNonPawnCount(pos, WHITE) != 1) && + (TotalNonPawnCount(pos, BLACK) != 1)) { return(UNRECOGNIZED); } @@ -310,21 +319,21 @@ Return value: // If we get here then one side has no pieces (except the king). // uStrong = BLACK; - if (pos->uNonPawnCount[WHITE][0] > 1) + if (TotalNonPawnCount(pos, WHITE) > 1) { uStrong = WHITE; } - ASSERT(pos->uNonPawnCount[uStrong][0] > 1); - ASSERT(pos->uNonPawnCount[FLIP(uStrong)][0] == 1); + ASSERT(TotalNonPawnCount(pos, uStrong) > 1); + ASSERT(TotalNonPawnCount(pos, FLIP(uStrong)) == 1); // // KB vs K is a draw, KB+ vs K is still a draw if all bishops are the // same color. // - uBishops = pos->uNonPawnCount[uStrong][BISHOP]; + uBishops = FastCountBits(pos->bbPieces[uStrong][BISHOP]); if ((uBishops == 1) || - (pos->uWhiteSqBishopCount[uStrong] == 0) || - (pos->uWhiteSqBishopCount[uStrong] == uBishops)) + (WhiteSqBishopCount(pos, uStrong) == 0) || + (WhiteSqBishopCount(pos, uStrong) == uBishops)) { *piScore = 0; return(RECOGN_EXACT); @@ -341,12 +350,16 @@ Return value: // move. Note: we allow the weak king to be adjacent to up to one // enemy bishop as long as it's the strong side's turn to move. // - cWeakKing = pos->cNonPawns[FLIP(uStrong)][0]; - ASSERT(DISTANCE(cWeakKing, pos->cNonPawns[uStrong][0]) > 1); + cWeakKing = KingPosition(pos, FLIP(uStrong)); + ASSERT(DISTANCE(cWeakKing, KingPosition(pos, uStrong)) > 1); uAdjacent = 0; - for (u = 1; u < pos->uNonPawnCount[uStrong][0]; u++) { - uAdjacent += (DISTANCE(cWeakKing, pos->cNonPawns[uStrong][u]) == 1); + BITBOARD bbPieces = NON_KING_PIECES_BB(pos, uStrong); + COOR c; + while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbPieces))) + { + uAdjacent += (DISTANCE(cWeakKing, c) == 1); + } } if (uAdjacent > 1) { @@ -357,9 +370,9 @@ Return value: // If it's the weak side's turn and the strong king is close // enough that the weak side may be stalemated, fail to recognize. // - ASSERT(IS_ON_BOARD(pos->cNonPawns[uStrong][0])); - ASSERT(IS_KING(pos->rgSquare[pos->cNonPawns[uStrong][0]].pPiece)); - u = DISTANCE(cWeakKing, pos->cNonPawns[uStrong][0]); + ASSERT(IS_ON_BOARD(KingPosition(pos, uStrong))); + ASSERT(IS_KING(pos->rgSquare[KingPosition(pos, uStrong)].pPiece)); + u = DISTANCE(cWeakKing, KingPosition(pos, uStrong)); ASSERT(u != 1); if (pos->uToMove != uStrong) { @@ -413,8 +426,8 @@ Return value: POSITION *pos = &ctx->sPosition; ULONG uStrong; - ASSERT((pos->uNonPawnCount[WHITE][0] <= 3) && - (pos->uNonPawnCount[BLACK][0] <= 3)); + ASSERT((TotalNonPawnCount(pos, WHITE) <= 3) && + (TotalNonPawnCount(pos, BLACK) <= 3)); ASSERT(_NothingBut(pos, KNIGHT, WHITE)); ASSERT(_NothingBut(pos, KNIGHT, BLACK)); @@ -422,11 +435,11 @@ Return value: // KNKN is a draw unless someone has a K in the corner (in which case, // with the friend knight in the way, there's a possible mate) // - if ((pos->uNonPawnCount[WHITE][0] == 2) && - (pos->uNonPawnCount[BLACK][0] == 2)) + if ((TotalNonPawnCount(pos, WHITE) == 2) && + (TotalNonPawnCount(pos, BLACK) == 2)) { - if ((CORNER_DISTANCE(pos->cNonPawns[WHITE][0]) != 0) && - (CORNER_DISTANCE(pos->cNonPawns[BLACK][0]) != 0)) + if ((CORNER_DISTANCE(KingPosition(pos, WHITE)) != 0) && + (CORNER_DISTANCE(KingPosition(pos, BLACK)) != 0)) { *piScore = 0; return(RECOGN_EXACT); @@ -438,8 +451,8 @@ Return value: // KNNKN etc... unrecognized. Heinz says "exceptional wins possible for // any side by mates in seven or less moves." TODO: add this knowledge. // - if ((pos->uNonPawnCount[WHITE][0] != 1) || - (pos->uNonPawnCount[BLACK][0] != 1)) + if ((TotalNonPawnCount(pos, WHITE) != 1) || + (TotalNonPawnCount(pos, BLACK) != 1)) { return(UNRECOGNIZED); } @@ -448,20 +461,20 @@ Return value: // If we get here somebody has no pieces (except a lone king). // uStrong = WHITE; - if (pos->uNonPawnCount[BLACK][0] > 1) + if (TotalNonPawnCount(pos, BLACK) > 1) { uStrong = BLACK; } - ASSERT(pos->uNonPawnCount[uStrong][0] > 1); - ASSERT(pos->uNonPawnCount[FLIP(uStrong)][0] == 1); + ASSERT(TotalNonPawnCount(pos, uStrong) > 1); + ASSERT(TotalNonPawnCount(pos, FLIP(uStrong)) == 1); // // Call KNNK a draw unless the weak K is on the edge of the board // where a checkmate is possible (e.g. 8/8/8/8/8/N2N4/4K3/2k5 b - -) // Everything else in here is a draw. // - ASSERT(pos->uNonPawnCount[uStrong][0] < 4); - if (ON_EDGE(pos->cNonPawns[FLIP(uStrong)][0])) + ASSERT(TotalNonPawnCount(pos, uStrong) < 4); + if (ON_EDGE(KingPosition(pos, FLIP(uStrong)))) { return(UNRECOGNIZED); } @@ -506,18 +519,18 @@ Return value: ULONG uAdjacent; POSITION *pos = &ctx->sPosition; - ASSERT((pos->uNonPawnCount[WHITE][0] <= 3) && - (pos->uNonPawnCount[BLACK][0] <= 3)); + ASSERT((TotalNonPawnCount(pos, WHITE) <= 3) && + (TotalNonPawnCount(pos, BLACK) <= 3)); ASSERT(_NothingBut(pos, BISHOP | KNIGHT, WHITE)); ASSERT(_NothingBut(pos, BISHOP | KNIGHT, BLACK)); - if ((pos->uNonPawnCount[WHITE][0] > 1) && - (pos->uNonPawnCount[BLACK][0] > 1)) + if ((TotalNonPawnCount(pos, WHITE) > 1) && + (TotalNonPawnCount(pos, BLACK) > 1)) { // // Do not recognize stuff like KNNKB or KNKBB etc... // - if (pos->uNonPawnCount[WHITE][0] + pos->uNonPawnCount[BLACK][0] > 4) + if (TotalNonPawnCount(pos, WHITE) + TotalNonPawnCount(pos, BLACK) > 4) { return(UNRECOGNIZED); } @@ -526,10 +539,10 @@ Return value: // This is KNKB; unless someone's king is on the edge, // recognize a draw. // - ASSERT((pos->uNonPawnCount[WHITE][0] == 2) && - (pos->uNonPawnCount[BLACK][0] == 2)); - if (ON_EDGE(pos->cNonPawns[WHITE][0]) || - ON_EDGE(pos->cNonPawns[BLACK][0])) + ASSERT((TotalNonPawnCount(pos, WHITE) == 2) && + (TotalNonPawnCount(pos, BLACK) == 2)); + if (ON_EDGE(KingPosition(pos, WHITE)) || + ON_EDGE(KingPosition(pos, BLACK))) { return(UNRECOGNIZED); } @@ -541,13 +554,13 @@ Return value: // If we get here we are in a KBNK endgame. // uStrong = WHITE; - if (pos->uNonPawnCount[BLACK][0] > 1) + if (TotalNonPawnCount(pos, BLACK) > 1) { uStrong = BLACK; } - ASSERT(pos->uNonPawnCount[uStrong][0] > 1); - ASSERT(pos->uNonPawnCount[FLIP(uStrong)][0] == 1); - cWeakKing = pos->cNonPawns[FLIP(uStrong)][0]; + ASSERT(TotalNonPawnCount(pos, uStrong) > 1); + ASSERT(TotalNonPawnCount(pos, FLIP(uStrong)) == 1); + cWeakKing = KingPosition(pos, FLIP(uStrong)); ASSERT(IS_ON_BOARD(cWeakKing)); ASSERT(IS_KING(pos->rgSquare[cWeakKing].pPiece)); @@ -556,7 +569,7 @@ Return value: // lone king. It's possible for the lone king to trap it there // thus drawing the game. // - cKnight = pos->cNonPawns[uStrong][KNIGHT]; + cKnight = CoorOfSingleBit(pos->bbPieces[uStrong][KNIGHT]); if ((CORNER_DISTANCE(cKnight) == 0) && (DISTANCE(cWeakKing, cKnight) == 1)) { @@ -568,9 +581,13 @@ Return value: // piece. // uAdjacent = 0; - for (u = 1; u < pos->uNonPawnCount[uStrong][0]; u++) { - uAdjacent += (DISTANCE(pos->cNonPawns[uStrong][u], cWeakKing) == 1); + BITBOARD bbPieces = NON_KING_PIECES_BB(pos, uStrong); + COOR c; + while (IS_ON_BOARD(c = CoorFromBitBoardRank8ToRank1(&bbPieces))) + { + uAdjacent += (DISTANCE(c, cWeakKing) == 1); + } } if (uAdjacent) { @@ -582,9 +599,9 @@ Return value: // that there might be a stalemate if the weak side is on move and // on the edge. // - ASSERT(IS_ON_BOARD(pos->cNonPawns[uStrong][0])); - ASSERT(IS_KING(pos->rgSquare[pos->cNonPawns[uStrong][0]].pPiece)); - u = DISTANCE(cWeakKing, pos->cNonPawns[uStrong][0]); + ASSERT(IS_ON_BOARD(KingPosition(pos, uStrong))); + ASSERT(IS_KING(pos->rgSquare[KingPosition(pos, uStrong)].pPiece)); + u = DISTANCE(cWeakKing, KingPosition(pos, uStrong)); ASSERT(u != 1); if (pos->uToMove != uStrong) { @@ -600,7 +617,7 @@ Return value: // progress towards driving the weak king to the correct corner to // mate him. // - if (pos->uWhiteSqBishopCount[uStrong] > 0) + if (WhiteSqBishopCount(pos, uStrong) > 0) { uDist = WHITE_CORNER_DISTANCE(cWeakKing); } @@ -666,8 +683,8 @@ Return value: ULONG uStrong; POSITION *pos = &ctx->sPosition; - ASSERT((pos->uNonPawnCount[WHITE][0] <= 3) && - (pos->uNonPawnCount[BLACK][0] <= 3)); + ASSERT((TotalNonPawnCount(pos, WHITE) <= 3) && + (TotalNonPawnCount(pos, BLACK) <= 3)); ASSERT(_NothingBut(pos, PAWN | KNIGHT, WHITE)); ASSERT(_NothingBut(pos, PAWN | KNIGHT, BLACK)); @@ -675,21 +692,21 @@ Return value: // Call the side with the knight "strong" // uStrong = WHITE; - if (pos->uNonPawnCount[BLACK][0] > 1) + if (TotalNonPawnCount(pos, BLACK) > 1) { uStrong = BLACK; } - ASSERT(pos->uNonPawnCount[uStrong][0] > 1); - ASSERT(pos->uNonPawnCount[FLIP(uStrong)][0] == 1); + ASSERT(TotalNonPawnCount(pos, uStrong) > 1); + ASSERT(TotalNonPawnCount(pos, FLIP(uStrong)) == 1); // // Exhaustively proven correct only for exactly one knight and // exactly one pawn (see the comment above) -- also still exclude // K on the edge, per the original ON_EDGE reasoning. // - if ((pos->uNonPawnCount[uStrong][KNIGHT] != 1) || - (pos->uPawnCount[FLIP(uStrong)] != 1) || - (ON_EDGE(pos->cNonPawns[FLIP(uStrong)][0]))) + if ((FastCountBits(pos->bbPieces[uStrong][KNIGHT]) != 1) || + (FastCountBits(pos->bbPawns[FLIP(uStrong)]) != 1) || + (ON_EDGE(KingPosition(pos, FLIP(uStrong))))) { return(UNRECOGNIZED); } @@ -730,13 +747,11 @@ Return value: { ULONG uStrong; BITBOARD bb; - ULONG u; - COOR c; COOR cWeakKing; POSITION *pos = &ctx->sPosition; - ASSERT((pos->uNonPawnCount[WHITE][0] <= 3) && - (pos->uNonPawnCount[BLACK][0] <= 3)); + ASSERT((TotalNonPawnCount(pos, WHITE) <= 3) && + (TotalNonPawnCount(pos, BLACK) <= 3)); ASSERT(_NothingBut(pos, PAWN | BISHOP, WHITE)); ASSERT(_NothingBut(pos, PAWN | BISHOP, BLACK)); @@ -744,36 +759,30 @@ Return value: // Call the side with bishop(s) "strong" // uStrong = WHITE; - if (pos->uNonPawnCount[BLACK][BISHOP] > 0) + if (FastCountBits(pos->bbPieces[BLACK][BISHOP]) > 0) { uStrong = BLACK; } - ASSERT(pos->uNonPawnCount[uStrong][0] > 1); - ASSERT(pos->uNonPawnCount[FLIP(uStrong)][0] == 1); + ASSERT(TotalNonPawnCount(pos, uStrong) > 1); + ASSERT(TotalNonPawnCount(pos, FLIP(uStrong)) == 1); - cWeakKing = pos->cNonPawns[FLIP(uStrong)][0]; + cWeakKing = KingPosition(pos, FLIP(uStrong)); ASSERT(IS_ON_BOARD(cWeakKing)); ASSERT(IS_KING(pos->rgSquare[cWeakKing].pPiece)); // - // Construct a strong side bitboard of pawn locations + // Strong side's pawn locations // - bb = 0ULL; - for (u = 0; u < pos->uPawnCount[uStrong]; u++) - { - c = pos->cPawns[uStrong][u]; - ASSERT(IS_ON_BOARD(c)); - bb |= COOR_TO_BB(c); - } + bb = pos->bbPawns[uStrong]; - if ((pos->uNonPawnCount[BLACK][0] + pos->uPawnCount[BLACK] > 1) && - (pos->uNonPawnCount[WHITE][0] + pos->uPawnCount[WHITE] > 1)) + if ((TotalNonPawnCount(pos, BLACK) + FastCountBits(pos->bbPawns[BLACK]) > 1) && + (TotalNonPawnCount(pos, WHITE) + FastCountBits(pos->bbPawns[WHITE]) > 1)) { // // Neither side has a lone king. This is either KBKP+ or // KBP+KP+. // - if (pos->uPawnCount[uStrong] > 0) + if (FastCountBits(pos->bbPawns[uStrong]) > 0) { // // Strong side can maybe take an adjacent pawn and survive the @@ -791,15 +800,15 @@ Return value: if (uStrong == WHITE) { if (!(bb & ~BBFILE[A]) && - (pos->uWhiteSqBishopCount[WHITE] == 0) && + (WhiteSqBishopCount(pos, WHITE) == 0) && (DISTANCE(cWeakKing, A8) <= 1)) { goto at_best_draw_for_strong; } if (!(bb & ~BBFILE[H]) && - (pos->uWhiteSqBishopCount[WHITE] == - pos->uNonPawnCount[WHITE][BISHOP]) && + (WhiteSqBishopCount(pos, WHITE) == + FastCountBits(pos->bbPieces[WHITE][BISHOP])) && (DISTANCE(cWeakKing, H8) <= 1)) { goto at_best_draw_for_strong; @@ -808,15 +817,15 @@ Return value: else { if (!(bb & ~BBFILE[A]) && - (pos->uWhiteSqBishopCount[BLACK] == - pos->uNonPawnCount[BLACK][BISHOP]) && + (WhiteSqBishopCount(pos, BLACK) == + FastCountBits(pos->bbPieces[BLACK][BISHOP])) && (DISTANCE(cWeakKing, A1) <= 1)) { goto at_best_draw_for_strong; } if (!(bb & ~BBFILE[H]) && - (pos->uWhiteSqBishopCount[BLACK] == 0) && + (WhiteSqBishopCount(pos, BLACK) == 0) && (DISTANCE(cWeakKing, H1) <= 1)) { goto at_best_draw_for_strong; @@ -830,7 +839,7 @@ Return value: // Strong side has no pawns... there's a mate-in-1 here if // the weak king is stuck in the corner. // - if ((pos->uNonPawnCount[uStrong][BISHOP] > 1) || + if ((FastCountBits(pos->bbPieces[uStrong][BISHOP]) > 1) || (CORNER_DISTANCE(cWeakKing) == 0)) { return(UNRECOGNIZED); @@ -844,20 +853,20 @@ Return value: // KBPK: make sure the bishop is the right color. This time // there is no need to check for on-move. // - ASSERT(pos->uNonPawnCount[FLIP(uStrong)][0] == 1); - ASSERT(pos->uNonPawnCount[uStrong][0] > 1); + ASSERT(TotalNonPawnCount(pos, FLIP(uStrong)) == 1); + ASSERT(TotalNonPawnCount(pos, uStrong) > 1); if (uStrong == WHITE) { if (!(bb & ~BBFILE[A]) && - (pos->uWhiteSqBishopCount[WHITE] == 0) && + (WhiteSqBishopCount(pos, WHITE) == 0) && (DISTANCE(cWeakKing, A8) <= 1)) { goto draw; } if (!(bb & ~BBFILE[H]) && - (pos->uWhiteSqBishopCount[WHITE] == - pos->uNonPawnCount[WHITE][BISHOP]) && + (WhiteSqBishopCount(pos, WHITE) == + FastCountBits(pos->bbPieces[WHITE][BISHOP])) && (DISTANCE(cWeakKing, H8) <= 1)) { goto draw; @@ -866,14 +875,14 @@ Return value: else { if (!(bb & ~BBFILE[A]) && - (pos->uWhiteSqBishopCount[BLACK] == - pos->uNonPawnCount[BLACK][BISHOP]) && + (WhiteSqBishopCount(pos, BLACK) == + FastCountBits(pos->bbPieces[BLACK][BISHOP])) && (DISTANCE(cWeakKing, A1) <= 1)) { goto draw; } if (!(bb & ~BBFILE[H]) && - (pos->uWhiteSqBishopCount[BLACK] == 0) && + (WhiteSqBishopCount(pos, BLACK) == 0) && (DISTANCE(cWeakKing, H1) <= 1)) { goto draw; @@ -983,6 +992,7 @@ Return value: } +// BUG: 7k/8/5KP1/8/8/8/8/8 w - - static ULONG _RecognizeKPK(IN SEARCHER_THREAD_CONTEXT *ctx, IN OUT SCORE *piScore) @@ -1012,8 +1022,8 @@ Return value: PAWN_HASH_ENTRY *pHash; POSITION *pos = &ctx->sPosition; - ASSERT(pos->uNonPawnCount[WHITE][0] == 1); - ASSERT(pos->uNonPawnCount[BLACK][0] == 1); + ASSERT(TotalNonPawnCount(pos, WHITE) == 1); + ASSERT(TotalNonPawnCount(pos, BLACK) == 1); ASSERT(_NothingBut(pos, PAWN, WHITE)); ASSERT(_NothingBut(pos, PAWN, BLACK)); @@ -1064,7 +1074,7 @@ Return value: // // No one has a winning passer or we did not find the hash entry. // - if ((pos->uPawnCount[WHITE] > 0) && (pos->uPawnCount[BLACK] > 0)) + if ((FastCountBits(pos->bbPawns[WHITE]) > 0) && (FastCountBits(pos->bbPawns[BLACK]) > 0)) { return(UNRECOGNIZED); } @@ -1076,15 +1086,15 @@ Return value: // draw for the weak side. // uStrong = WHITE; - if (pos->uPawnCount[WHITE] == 0) + if (FastCountBits(pos->bbPawns[WHITE]) == 0) { uStrong = BLACK; } - ASSERT(pos->uPawnCount[uStrong] > 0); + ASSERT(FastCountBits(pos->bbPawns[uStrong]) > 0); uWeak = FLIP(uStrong); - ASSERT(pos->uPawnCount[uWeak] == 0); + ASSERT(FastCountBits(pos->bbPawns[uWeak]) == 0); - if (pos->uPawnCount[uStrong] > 1) + if (FastCountBits(pos->bbPawns[uStrong]) > 1) { *piScore = 0; if (pos->uToMove == uStrong) @@ -1099,8 +1109,8 @@ Return value: // sophisticated analysis here to spot winning KPK // configurations earlier by using "critical squares" // - ASSERT(pos->uPawnCount[uStrong] == 1); - cPawn = pos->cPawns[uStrong][0]; + ASSERT(FastCountBits(pos->bbPawns[uStrong]) == 1); + cPawn = CoorOfSingleBit(pos->bbPawns[uStrong]); ASSERT(IS_ON_BOARD(cPawn)); ASSERT(IS_PAWN(pos->rgSquare[cPawn].pPiece)); @@ -1108,9 +1118,9 @@ Return value: // Step 1: the strong king must be closer to the pawn than // the weak king. // - uDist[uStrong] = DISTANCE(pos->cNonPawns[uStrong][0], cPawn); + uDist[uStrong] = DISTANCE(KingPosition(pos, uStrong), cPawn); ASSERT((1 <= uDist[uStrong]) && (uDist[uStrong] <= 7)); - uDist[uWeak] = DISTANCE(pos->cNonPawns[uWeak][0], cPawn); + uDist[uWeak] = DISTANCE(KingPosition(pos, uWeak), cPawn); ASSERT((1 <= uDist[uWeak]) && (uDist[uWeak] <= 7)); if (uDist[uStrong] <= uDist[uWeak]) { @@ -1121,10 +1131,10 @@ Return value: _GetPassersCriticalSquares(uStrong, cPawn, cCritical); for (u = 0; u < 3; u++) { - uDist[uStrong] = DISTANCE(pos->cNonPawns[uStrong][0], + uDist[uStrong] = DISTANCE(KingPosition(pos, uStrong), cCritical[u]); ASSERT((0 <= uDist[uStrong]) && (uDist[uStrong] <= 7)); - uDist[uWeak] = DISTANCE(pos->cNonPawns[uWeak][0], + uDist[uWeak] = DISTANCE(KingPosition(pos, uWeak), cCritical[u]); // @@ -1333,19 +1343,19 @@ Return value: // // Try interior node recognizers // - if ((pos->uNonPawnCount[WHITE][0] <= 3) && - (pos->uNonPawnCount[BLACK][0] <= 3)) + if ((TotalNonPawnCount(pos, WHITE) <= 3) && + (TotalNonPawnCount(pos, BLACK) <= 3)) { - uSig[BLACK] = _MakeMaterialSig((pos->uPawnCount[BLACK] > 0), - (pos->uNonPawnCount[BLACK][KNIGHT] > 0), - (pos->uNonPawnCount[BLACK][BISHOP] > 0), - (pos->uNonPawnCount[BLACK][ROOK] > 0), - (pos->uNonPawnCount[BLACK][QUEEN] > 0)); - uSig[WHITE] = _MakeMaterialSig((pos->uPawnCount[WHITE] > 0), - (pos->uNonPawnCount[WHITE][KNIGHT] > 0), - (pos->uNonPawnCount[WHITE][BISHOP] > 0), - (pos->uNonPawnCount[WHITE][ROOK] > 0), - (pos->uNonPawnCount[WHITE][QUEEN] > 0)); + uSig[BLACK] = _MakeMaterialSig((FastCountBits(pos->bbPawns[BLACK]) > 0), + (FastCountBits(pos->bbPieces[BLACK][KNIGHT]) > 0), + (FastCountBits(pos->bbPieces[BLACK][BISHOP]) > 0), + (FastCountBits(pos->bbPieces[BLACK][ROOK]) > 0), + (FastCountBits(pos->bbPieces[BLACK][QUEEN]) > 0)); + uSig[WHITE] = _MakeMaterialSig((FastCountBits(pos->bbPawns[WHITE]) > 0), + (FastCountBits(pos->bbPieces[WHITE][KNIGHT]) > 0), + (FastCountBits(pos->bbPieces[WHITE][BISHOP]) > 0), + (FastCountBits(pos->bbPieces[WHITE][ROOK]) > 0), + (FastCountBits(pos->bbPieces[WHITE][QUEEN]) > 0)); pRec = g_pRecognizers[RECOGN_INDEX(uSig[WHITE], uSig[BLACK])]; if (NULL != pRec) { @@ -1033,19 +1033,22 @@ _DetectPreMoveTerminalStates(SEARCHER_THREAD_CONTEXT *ctx, // Look for insufficient material. FOREACH_COLOR(u) { - uMajors[u] = (pos->uNonPawnCount[u][ROOK] + - pos->uNonPawnCount[u][QUEEN]); - uMinors[u] = (pos->uNonPawnCount[u][KNIGHT] + - pos->uNonPawnCount[u][BISHOP]); - uWhiteBishops[u] = pos->uWhiteSqBishopCount[u]; - uBlackBishops[u] = pos->uNonPawnCount[u][BISHOP] - uWhiteBishops[u]; + uMajors[u] = (FastCountBits(pos->bbPieces[u][ROOK]) + + FastCountBits(pos->bbPieces[u][QUEEN])); + uMinors[u] = (FastCountBits(pos->bbPieces[u][KNIGHT]) + + FastCountBits(pos->bbPieces[u][BISHOP])); + uWhiteBishops[u] = WhiteSqBishopCount(pos, u); + uBlackBishops[u] = FastCountBits(pos->bbPieces[u][BISHOP]) - uWhiteBishops[u]; } FOREACH_COLOR(u) { - if ((pos->uPawnCount[u] != 0) || - (pos->uPawnCount[FLIP(u)] != 0)) { + ULONG uNonKing = FastCountBits(NON_KING_PIECES_BB(pos, u)); + ULONG uNonKingFlip = FastCountBits(NON_KING_PIECES_BB(pos, FLIP(u))); + + if ((pos->bbPawns[u] != 0) || + (pos->bbPawns[FLIP(u)] != 0)) { continue; } - if (pos->uNonPawnCount[u][0] == 1) { + if (uNonKing == 0) { if (uMajors[FLIP(u)] == 0 && uMinors[FLIP(u)] == 1) { ret.eResult = RESULT_DRAW; @@ -1058,12 +1061,10 @@ _DetectPreMoveTerminalStates(SEARCHER_THREAD_CONTEXT *ctx, strcpy(ret.szDescription, "insufficient material"); return ret; } - } else if ((pos->uNonPawnCount[u][0] == 1 + uWhiteBishops[u] && - pos->uNonPawnCount[FLIP(u)][0] == - 1 + uWhiteBishops[FLIP(u)]) || - (pos->uNonPawnCount[u][0] == 1 + uBlackBishops[u] && - pos->uNonPawnCount[FLIP(u)][0] == - 1 + uBlackBishops[FLIP(u)])) { + } else if ((uNonKing == uWhiteBishops[u] && + uNonKingFlip == uWhiteBishops[FLIP(u)]) || + (uNonKing == uBlackBishops[u] && + uNonKingFlip == uBlackBishops[FLIP(u)])) { ret.eResult = RESULT_DRAW; strcpy(ret.szDescription, "insufficient material"); return ret; diff --git a/src/search.c b/src/search.c index 573f78c..91e3b2b 100644 --- a/src/search.c +++ b/src/search.c @@ -1450,7 +1450,21 @@ static FLAG INLINE _ShouldWeConsiderThisMove(IN SEARCHER_THREAD_CONTEXT *ctx, IN ULONG uMoveNum, IN SCORE iFutility, - IN FLAG fGeneratedChecks + IN FLAG fGeneratedChecks, + // TotalNonPawnCount(&ctx->sPosition, + // FLIP(ctx->sPosition.uToMove)) -- every + // capture's pCaptured is necessarily the + // opponent's piece, and uToMove doesn't + // change across the move loop this is called + // from (search.c's QSearch), so this is one + // value for the whole loop. Hoisted out to + // the caller instead of recomputed (a POPCNT + // each) on every candidate move -- see + // 2026-09-16 chess.h/search.c history for why + // this replaced a per-call cache-with-valid- + // flag branch that would have cost more than + // it saved. + IN ULONG uOpponentNonPawnCount #ifdef CALIBRATE_QSEARCH_FUTILITY , IN SCORE iAlpha , IN SCORE iBeta @@ -1517,10 +1531,13 @@ _ShouldWeConsiderThisMove(IN SEARCHER_THREAD_CONTEXT *ctx, // might save the game... uColor = GET_COLOR(mv.pCaptured); ASSERT(OPPOSITE_COLORS(ctx->sPosition.uToMove, uColor)); + ASSERT(uColor == FLIP(ctx->sPosition.uToMove)); + ASSERT(uOpponentNonPawnCount == + TotalNonPawnCount(&ctx->sPosition, uColor)); if ((IS_PAWN(mv.pCaptured) && - ctx->sPosition.uPawnCount[uColor] == 1) || + FastCountBits(ctx->sPosition.bbPawns[uColor]) == 1) || (!IS_PAWN(mv.pCaptured) && - ctx->sPosition.uNonPawnCount[uColor][0] == 2)) + uOpponentNonPawnCount == 2)) { return(TRUE); } @@ -1923,6 +1940,7 @@ QSearch(IN SEARCHER_THREAD_CONTEXT *ctx, SCORE iFutility; SCORE rgiPositional[2]; ULONG x; + ULONG uOpponentNonPawnCount; #ifdef PERF_COUNTERS ULONG uLegalMoves; #endif @@ -2092,6 +2110,12 @@ QSearch(IN SEARCHER_THREAD_CONTEXT *ctx, ); GenerateMoves(ctx, NULLMOVE, _WhatToGen[fIncludeChecks]); + // Hoisted out of _ShouldWeConsiderThisMove's per-move loop below: + // pos->uToMove is fixed for the whole loop, so the opponent's + // TotalNonPawnCount is one POPCNT for the entire node, not one per + // candidate move. + uOpponentNonPawnCount = TotalNonPawnCount(pos, FLIP(pos->uToMove)); + #ifdef PERF_COUNTERS uLegalMoves = 0; #endif @@ -2118,7 +2142,8 @@ QSearch(IN SEARCHER_THREAD_CONTEXT *ctx, if (FALSE == _ShouldWeConsiderThisMove(ctx, x, iFutility, - fIncludeChecks + fIncludeChecks, + uOpponentNonPawnCount #ifdef CALIBRATE_QSEARCH_FUTILITY , iAlpha , iBeta diff --git a/src/searchsup.c b/src/searchsup.c index 7ab8695..2ec001f 100644 --- a/src/searchsup.c +++ b/src/searchsup.c @@ -614,7 +614,7 @@ Return value: *piExtend += HALF_PLY; if (!fIsLeftoverMove || ((g_KingAttacksBB[mv.cTo] & - COOR_TO_BB(pos->cNonPawns[uPawnColor][0])) || + COOR_TO_BB(KingPosition(pos, uPawnColor))) || (_RookAttacksBB(mv.cTo, pos->bbOccupied) & (pos->bbPieces[uPawnColor][ROOK] | pos->bbPieces[uPawnColor][QUEEN])) || @@ -669,8 +669,8 @@ Return value: { // Endgame extension if ((ctx->sPlyInfo[1].uTotalNonPawns > 2) && - ((pos->uNonPawnCount[BLACK][0] + - pos->uNonPawnCount[WHITE][0]) == 2)) + ((TotalNonPawnCount(pos, BLACK) + + TotalNonPawnCount(pos, WHITE)) == 2)) { if ((mv.pCaptured && !IS_PAWN(mv.pCaptured)) || (mvLast.pCaptured && !IS_PAWN(mvLast.pCaptured))) @@ -1334,8 +1334,8 @@ SelectNullmoveRFactor(SEARCHER_THREAD_CONTEXT *ctx, INT uDepth) { ULONG u = TWO_PLY; - ULONG uMaxPiecesPerSide = MAXU(ctx->sPosition.uNonPawnCount[WHITE][0], - ctx->sPosition.uNonPawnCount[BLACK][0]); + ULONG uMaxPiecesPerSide = MAXU(TotalNonPawnCount(&ctx->sPosition, WHITE), + TotalNonPawnCount(&ctx->sPosition, BLACK)); ASSERT(uMaxPiecesPerSide <= 16); if ((uDepth > 8 * ONE_PLY) || @@ -1369,7 +1369,7 @@ FLAG WeShouldTryNullmovePruning(SEARCHER_THREAD_CONTEXT *ctx, ASSERT(uNullDepth < MAX_PLY_PER_SEARCH * ONE_PLY); if ((FALSE == pf->fAvoidNullmove) && - (pos->uNonPawnCount[pos->uToMove][0] > 2) && + (TotalNonPawnCount(pos, pos->uToMove) > 2) && (FALSE == pi->fInCheck) && (iBeta != +INFINITY) && (NODE_PV != pi->eNodeType)) @@ -1440,7 +1440,7 @@ TryNullmovePruning(SEARCHER_THREAD_CONTEXT *ctx, // If there are very few pieces on the board, launch a // separate "verification search". - if ((pos->uNonPawnCount[pos->uToMove][0] < 4) && + if ((TotalNonPawnCount(pos, pos->uToMove) < 4) && (pf->fVerifyNullmove == TRUE)) { ASSERT(pf->fAvoidNullmove == FALSE); @@ -112,9 +112,9 @@ Return value: ULONG u; bbAttackers = g_KnightAttacksBB[cSquare] & pos->bbPieces[uSide][KNIGHT]; - if (DISTANCE(cSquare, pos->cNonPawns[uSide][0]) == 1) + if (DISTANCE(cSquare, KingPosition(pos, uSide)) == 1) { - bbAttackers |= COOR_TO_BB(pos->cNonPawns[uSide][0]); + bbAttackers |= COOR_TO_BB(KingPosition(pos, uSide)); } // Measured slower: deriving the needed direction(s) directly from @@ -703,7 +703,7 @@ Return value: // would exposes his king to check, skip it and try the // next least valuable. // - cKing = pos->cNonPawns[uColor][0]; + cKing = KingPosition(pos, uColor); c = ExposesCheck(pos, pList->data[x].cLoc, cKing); // @@ -795,7 +795,7 @@ Return value: // would exposes his king to check, skip it and try the // next least valuable. // - cKing = pos->cNonPawns[uColor][0]; + cKing = KingPosition(pos, uColor); c = ExposesCheck(pos, pList->data[x].cLoc, cKing); // diff --git a/src/testmove.c b/src/testmove.c index 6711046..9dbee96 100644 --- a/src/testmove.c +++ b/src/testmove.c @@ -46,16 +46,33 @@ Return value: ULONG w; COOR c; - if ((TRUE == v) && (pos->uPawnCount[u])) + if ((TRUE == v) && (pos->bbPawns[u])) { - w = rand() % pos->uPawnCount[u]; - c = pos->cPawns[u][w]; + BITBOARD bbPawns = pos->bbPawns[u]; + + w = rand() % FastCountBits(pos->bbPawns[u]); + do + { + c = CoorFromBitBoardRank8ToRank1(&bbPawns); + ASSERT(IS_ON_BOARD(c)); + } + while (w-- > 0); } else { - ASSERT(pos->uNonPawnCount[u][0]); - w = rand() % pos->uNonPawnCount[u][0]; - c = pos->cNonPawns[u][w]; + COOR rgNonPawns[16]; + ULONG uCount = 0; + BITBOARD bb = NON_KING_PIECES_BB(pos, u); + COOR cBit; + + rgNonPawns[uCount++] = KingPosition(pos, u); + while (IS_ON_BOARD(cBit = CoorFromBitBoardRank8ToRank1(&bb))) + { + ASSERT(uCount < ARRAY_SIZE(rgNonPawns)); + rgNonPawns[uCount++] = cBit; + } + w = rand() % uCount; + c = rgNonPawns[w]; } ASSERT(IS_ON_BOARD(c)); return(c); diff --git a/src/testsee.c b/src/testsee.c index 978e1c6..ea8318e 100644 --- a/src/testsee.c +++ b/src/testsee.c @@ -64,9 +64,9 @@ DebugSEERecursive(SEARCHER_THREAD_CONTEXT *ctx, // Initialize MinMax to the stand pat score here so that the reply // can be "something else". // - iMinMax = ((ctx->sPosition.uPawnMaterial[g_uRootOnMove] + + iMinMax = ((PawnMaterial(&ctx->sPosition, g_uRootOnMove) + ctx->sPosition.uNonPawnMaterial[g_uRootOnMove]) - - (ctx->sPosition.uPawnMaterial[FLIP(g_uRootOnMove)] + + (PawnMaterial(&ctx->sPosition, FLIP(g_uRootOnMove)) + ctx->sPosition.uNonPawnMaterial[FLIP(g_uRootOnMove)])); // @@ -114,9 +114,9 @@ SCORE DebugSEE(POSITION *pos, MOVE mv) { - SCORE iRootBalance = ((pos->uPawnMaterial[pos->uToMove] + + SCORE iRootBalance = ((PawnMaterial(pos, pos->uToMove) + pos->uNonPawnMaterial[pos->uToMove]) - - (pos->uPawnMaterial[FLIP(pos->uToMove)] + + (PawnMaterial(pos, FLIP(pos->uToMove)) + pos->uNonPawnMaterial[FLIP(pos->uToMove)])); SCORE iAfterExchange = iRootBalance; SEARCHER_THREAD_CONTEXT *ctx = diff --git a/src/testsup.c b/src/testsup.c index 0cbc9a4..c5c62c2 100644 --- a/src/testsup.c +++ b/src/testsup.c @@ -67,7 +67,7 @@ void GenerateRandomLegalPosition(POSITION *pos) { COOR c, c1; - ULONG x, uIndex; + ULONG x; ULONG uColor; PIECE p; ULONG u; @@ -78,15 +78,7 @@ GenerateRandomLegalPosition(POSITION *pos) // Clear the board // memset(pos, 0, sizeof(POSITION)); - for (u = 0; u < ARRAY_SIZE(pos->cPawns[WHITE]); u++) - { - pos->cPawns[WHITE][u] = pos->cPawns[BLACK][u] = ILLEGAL_COOR; - } - - for (u = 0; u < ARRAY_SIZE(pos->cNonPawns[WHITE]); u++) - { - pos->cNonPawns[WHITE][u] = pos->cNonPawns[BLACK][u] = ILLEGAL_COOR; - } + pos->cKingSq[WHITE] = pos->cKingSq[BLACK] = ILLEGAL_COOR; // memset above zeroes cEpSquare to 0x00 (A8), a real on-board // square, not the "no en passant" sentinel -- every random @@ -112,10 +104,8 @@ GenerateRandomLegalPosition(POSITION *pos) c = RANDOM_COOR; ASSERT(IS_ON_BOARD(c)); pos->rgSquare[c].pPiece = WHITE_KING; - ASSERT(pos->cNonPawns[WHITE][0] == ILLEGAL_COOR); - pos->cNonPawns[WHITE][0] = c; - pos->uNonPawnCount[WHITE][KING] = 1; - pos->uNonPawnCount[WHITE][0] = 1; + ASSERT(pos->cKingSq[WHITE] == ILLEGAL_COOR); + pos->cKingSq[WHITE] = c; pos->uNonPawnMaterial[WHITE] = VALUE_KING; pos->bbOccupied |= COOR_TO_BB(c); pos->bbOccupiedSide[WHITE] |= COOR_TO_BB(c); @@ -127,10 +117,8 @@ GenerateRandomLegalPosition(POSITION *pos) while(DISTANCE(c, c1) < 2); ASSERT(IS_ON_BOARD(c1)); pos->rgSquare[c1].pPiece = BLACK_KING; - ASSERT(pos->cNonPawns[BLACK][0] == ILLEGAL_COOR); - pos->cNonPawns[BLACK][0] = c1; - pos->uNonPawnCount[BLACK][KING] = 1; - pos->uNonPawnCount[BLACK][0] = 1; + ASSERT(pos->cKingSq[BLACK] == ILLEGAL_COOR); + pos->cKingSq[BLACK] = c1; pos->uNonPawnMaterial[BLACK] = VALUE_KING; pos->bbOccupied |= COOR_TO_BB(c1); pos->bbOccupiedSide[BLACK] |= COOR_TO_BB(c1); @@ -169,20 +157,12 @@ GenerateRandomLegalPosition(POSITION *pos) ASSERT(GET_COLOR(p) == uColor); if (IS_PAWN(p) && - (pos->uPawnCount[uColor] < 8) && - (!RANK1(c)) && + (FastCountBits(pos->bbPawns[uColor]) < 8) && + (!RANK1(c)) && (!RANK8(c))) { - // - // The pawn list is not ordered at all, add - // the new pawn's location to the list and - // point from the pawn to the list. - // - pos->cPawns[uColor][pos->uPawnCount[uColor]] = c; - pos->rgSquare[c].uIndex = pos->uPawnCount[uColor]; + pos->rgSquare[c].uIndex = INVALID_PIECE_INDEX; pos->rgSquare[c].pPiece = p; - pos->uPawnCount[uColor]++; - pos->uPawnMaterial[uColor] += VALUE_PAWN; pos->bbPawns[uColor] |= COOR_TO_BB(c); pos->bbOccupied |= COOR_TO_BB(c); pos->bbOccupiedSide[uColor] |= COOR_TO_BB(c); @@ -191,23 +171,12 @@ GenerateRandomLegalPosition(POSITION *pos) else if (!IS_KING(p) && (IS_VALID_PIECE(p))) { - uIndex = pos->uNonPawnCount[uColor][0]; - pos->cNonPawns[uColor][uIndex] = c; - pos->uNonPawnCount[uColor][0]++; - pos->uNonPawnCount[uColor][PIECE_TYPE(p)]++; pos->rgSquare[c].pPiece = p; - pos->rgSquare[c].uIndex = uIndex; + pos->rgSquare[c].uIndex = INVALID_PIECE_INDEX; pos->uNonPawnMaterial[uColor] += PIECE_VALUE(p); pos->bbPieces[uColor][PIECE_TYPE(p)] |= COOR_TO_BB(c); pos->bbOccupied |= COOR_TO_BB(c); pos->bbOccupiedSide[uColor] |= COOR_TO_BB(c); - if (IS_BISHOP(p)) - { - if (IS_WHITE_SQUARE_COOR(c)) - { - pos->uWhiteSqBishopCount[uColor]++; - } - } break; } } @@ -216,19 +185,15 @@ GenerateRandomLegalPosition(POSITION *pos) } // next piece } // next color - ASSERT(pos->uNonPawnCount[WHITE][KING] == 1); - ASSERT(pos->uNonPawnCount[BLACK][KING] == 1); - //pos->uNonPawnCount[WHITE][0]--; - //pos->uNonPawnCount[BLACK][0]--; // // Do the rest of the position // pos->iMaterialBalance[WHITE] = ((SCORE)(pos->uNonPawnMaterial[WHITE] + - pos->uPawnMaterial[WHITE]) - + PawnMaterial(pos, WHITE)) - (SCORE)(pos->uNonPawnMaterial[BLACK] + - pos->uPawnMaterial[BLACK])); + PawnMaterial(pos, BLACK))); pos->iMaterialBalance[BLACK] = -pos->iMaterialBalance[WHITE]; pos->uToMove = RANDOM_COLOR; pos->uFifty = rand() % 100; |
