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/move.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/move.c')
| -rw-r--r-- | src/move.c | 238 |
1 files changed, 52 insertions, 186 deletions
@@ -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)) |
