summaryrefslogtreecommitdiff
path: root/src/board.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/board.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/board.c')
-rw-r--r--src/board.c279
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