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/board.c | |
| 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/board.c')
| -rw-r--r-- | src/board.c | 279 |
1 files changed, 144 insertions, 135 deletions
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 |
