summaryrefslogtreecommitdiff
path: root/src/move.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-09-16 08:02:21 -0700
committerScott Gasch <[email protected]>2026-09-16 08:02:21 -0700
commit966a0ef4da9cf3911eb3da0533179199808717b4 (patch)
treee09363b574215ef0d8996155a5ab24314db2dd69 /src/move.c
parentb91c8453f3fe21c8ccd3568923d8e39141c9c945 (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.c238
1 files changed, 52 insertions, 186 deletions
diff --git a/src/move.c b/src/move.c
index f5dab1f..28e376c 100644
--- a/src/move.c
+++ b/src/move.c
@@ -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))