summaryrefslogtreecommitdiff
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
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]>
-rw-r--r--src/GNUmakefile8
-rw-r--r--src/board.c279
-rwxr-xr-xsrc/chess.h188
-rw-r--r--src/eval.c261
-rw-r--r--src/fen.c72
-rw-r--r--src/generate.c104
-rw-r--r--src/move.c238
-rw-r--r--src/movesup.c57
-rw-r--r--src/probe.c78
-rw-r--r--src/recogn.c256
-rw-r--r--src/root.c31
-rw-r--r--src/search.c33
-rw-r--r--src/searchsup.c14
-rw-r--r--src/see.c8
-rw-r--r--src/testmove.c29
-rw-r--r--src/testsee.c8
-rw-r--r--src/testsup.c59
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); \
diff --git a/src/eval.c b/src/eval.c
index d310817..6764349 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -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;
}
diff --git a/src/fen.c b/src/fen.c
index 60ff4dc..13c2563 100644
--- a/src/fen.c
+++ b/src/fen.c
@@ -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);
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))
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)
{
diff --git a/src/root.c b/src/root.c
index f5fe4c1..314395a 100644
--- a/src/root.c
+++ b/src/root.c
@@ -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);
diff --git a/src/see.c b/src/see.c
index 893a9bb..47efd30 100644
--- a/src/see.c
+++ b/src/see.c
@@ -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;