summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-10-05 22:20:10 -0700
committerScott Gasch <[email protected]>2026-10-05 22:20:10 -0700
commit5fbd9353fa9b1a80f37bcf0339f5546ac88091b8 (patch)
tree5ac60b40603fd8c6fd051a44a24fd0e5e21425a6 /src
parentbb7cdcda4c8c30ad0ed71d0f60427daf05977b24 (diff)
Cache good/bad-bishop pawn-structure score in the pawn hash
iBishopColorScore[color][complex] is a pure function of pawn placement (own/enemy, stationary/transient) on a bishop's color complex, so it doesn't need rescanning per-bishop -- computed once per pawn-hash miss in _EvalPawns and reused by every bishop of that color/complex in _EvalBishop, mirroring the same cache-the-per-structure-invariant idea already used elsewhere in eval.c. Also disables two speculative check-extension/futility-gating experiments in search.c (#if 0, not deleted -- kept for reference) that didn't hold up, and adds the BBLEFT/BBRIGHT bitboard constants eval.c's bishop-mobility rewrite needs. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src')
-rw-r--r--src/bitboard.c4
-rwxr-xr-xsrc/chess.h10
-rw-r--r--src/eval.c506
-rw-r--r--src/generate.c2
-rw-r--r--src/search.c45
-rw-r--r--src/searchsup.c20
6 files changed, 291 insertions, 296 deletions
diff --git a/src/bitboard.c b/src/bitboard.c
index 2832fdb..24e6ceb 100644
--- a/src/bitboard.c
+++ b/src/bitboard.c
@@ -164,6 +164,10 @@ BITBOARD BBFILE[8] = {
BBFILEH
};
+// Left / Right half of board.
+BITBOARD BBLEFT = BBFILEA | BBFILEB | BBFILEC | BBFILED;
+BITBOARD BBRIGHT = BBFILEE | BBFILEF | BBFILEG | BBFILEH;
+
//
// A way to select one rank
//
diff --git a/src/chess.h b/src/chess.h
index b9cba3d..fde5c6f 100755
--- a/src/chess.h
+++ b/src/chess.h
@@ -1746,6 +1746,14 @@ typedef struct _PAWN_HASH_ENTRY
UCHAR uCountPerFile[2][10];
UCHAR uNumRammedPawns;
UCHAR uNumUnmovedPawns[2];
+ SHORT iBishopColorScore[2][2]; // [color][IS_SQUARE_WHITE(bishop's
+ // square)] -- good/bad-bishop
+ // pawn-structure score, pure
+ // function of pawn placement (see
+ // _EvalBishop's cache-miss
+ // population in eval.c), reused by
+ // every bishop of that color/
+ // complex instead of rescanned.
}
PAWN_HASH_ENTRY;
@@ -3143,6 +3151,8 @@ extern MOVE MATEMOVE;
extern FLAG g_fIsWhiteSquare[128];
extern BITBOARD BBFILE[8];
extern BITBOARD BBRANK[9];
+extern BITBOARD BBLEFT;
+extern BITBOARD BBRIGHT;
extern BITBOARD BBWHITESQ;
extern BITBOARD BBBLACKSQ;
extern BITBOARD BBLEFTSIDE;
diff --git a/src/eval.c b/src/eval.c
index ae12f0a..18c3464 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -565,8 +565,6 @@ static SCORE ROOK_LEADS_PASSER_BY_PASSER_RANK[2][9] =
{ +0, +0, -3, -5, -9, -13, -16, -22, +0 } // white
};
-static SCORE KING_TRAPPING_ROOK = -40;
-
static SCORE ROOK_VALUE_AS_PAWNS_COME_OFF[17] =
{// 0 1 2 3 4 5 6 7 8
+55, +51, +44, +38, +33, +27, +22, +16, +7,
@@ -591,7 +589,7 @@ static SCORE ROOK_CONNECTED_HORIZ = +4; // x2
//
static SCORE ROOK_MOBILITY_BY_SQUARES[15] =
{// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
- -28, -24, -20, -14, -7, -2, +0, +4, +8, +12, +15, +17, +19, +21, +22
+ -32, -26, -20, -14, -7, -2, +0, +4, +8, +12, +15, +17, +19, +21, +22
};// ^ |
@@ -962,7 +960,6 @@ static DNA_BASE_SIZE g_EvalDNA[] = {
DNA_VAR(ROOK_ON_SEVENTH_BONUS),
DNA_MATRIX(ROOK_BEHIND_PASSER_BY_PASSER_RANK),
DNA_MATRIX(ROOK_LEADS_PASSER_BY_PASSER_RANK),
- DNA_VAR(KING_TRAPPING_ROOK),
DNA_ARRAY(ROOK_VALUE_AS_PAWNS_COME_OFF),
DNA_VAR(ROOK_CONNECTED_VERT),
DNA_VAR(ROOK_CONNECTED_HORIZ),
@@ -1549,12 +1546,12 @@ Return value:
bb &= BBPRECEEDING_RANKS[(c & 0x70) >> 4][uColor];
if (!bb)
{
- ASSERT(CountBits(bb) == 0);
+ ASSERT(FastCountBits(bb) == 0);
// There are no enemy sentries so this pawn is a passer.
pHash->bbPasserLocations[uColor] |= COOR_TO_BB(c);
- ASSERT(CountBits(pHash->bbPasserLocations[uColor]) > 0);
- ASSERT(CountBits(pHash->bbPasserLocations[uColor]) <= 8);
+ ASSERT(FastCountBits(pHash->bbPasserLocations[uColor]) > 0);
+ ASSERT(FastCountBits(pHash->bbPasserLocations[uColor]) <= 8);
EVAL_TERM(uColor,
PAWN,
c,
@@ -1574,7 +1571,7 @@ Return value:
}
return;
}
- uSentries = CountBits(bb);
+ uSentries = FastCountBits(bb);
// There's one or more sentry pawns so we'll look for friendly
// helpers to decide if this pawn is a candidate passer. First
@@ -1894,7 +1891,7 @@ Return value:
ASSERT(RANK(cRightmostPasser) > 1);
ASSERT(RANK(cLeftmostPasser) < 8);
ASSERT(RANK(cRightmostPasser) < 8);
- if (CountBits(pHash->bbPasserLocations[uColor]) == 1)
+ if (FastCountBits(pHash->bbPasserLocations[uColor]) == 1)
{
ASSERT(cLeftmostPasser == cRightmostPasser);
}
@@ -2220,18 +2217,18 @@ Return value:
// Pre-count number of pawns per file, unrolled.
ASSERT(pHash->uCountPerFile[uColor][0] == 0);
- pHash->uCountPerFile[uColor][A + 1] = CountBits(pos->bbPawns[uColor] & BBFILE[A]);
- pHash->uCountPerFile[uColor][B + 1] = CountBits(pos->bbPawns[uColor] & BBFILE[B]);
- pHash->uCountPerFile[uColor][C + 1] = CountBits(pos->bbPawns[uColor] & BBFILE[C]);
- pHash->uCountPerFile[uColor][D + 1] = CountBits(pos->bbPawns[uColor] & BBFILE[D]);
- pHash->uCountPerFile[uColor][E + 1] = CountBits(pos->bbPawns[uColor] & BBFILE[E]);
- pHash->uCountPerFile[uColor][F + 1] = CountBits(pos->bbPawns[uColor] & BBFILE[F]);
- pHash->uCountPerFile[uColor][G + 1] = CountBits(pos->bbPawns[uColor] & BBFILE[G]);
- pHash->uCountPerFile[uColor][H + 1] = CountBits(pos->bbPawns[uColor] & BBFILE[H]);
+ pHash->uCountPerFile[uColor][A + 1] = FastCountBits(pos->bbPawns[uColor] & BBFILE[A]);
+ pHash->uCountPerFile[uColor][B + 1] = FastCountBits(pos->bbPawns[uColor] & BBFILE[B]);
+ pHash->uCountPerFile[uColor][C + 1] = FastCountBits(pos->bbPawns[uColor] & BBFILE[C]);
+ pHash->uCountPerFile[uColor][D + 1] = FastCountBits(pos->bbPawns[uColor] & BBFILE[D]);
+ pHash->uCountPerFile[uColor][E + 1] = FastCountBits(pos->bbPawns[uColor] & BBFILE[E]);
+ pHash->uCountPerFile[uColor][F + 1] = FastCountBits(pos->bbPawns[uColor] & BBFILE[F]);
+ pHash->uCountPerFile[uColor][G + 1] = FastCountBits(pos->bbPawns[uColor] & BBFILE[G]);
+ pHash->uCountPerFile[uColor][H + 1] = FastCountBits(pos->bbPawns[uColor] & BBFILE[H]);
ASSERT(pHash->uCountPerFile[uColor][9] == 0);
// Count number of unmoved pawns.
- pHash->uNumUnmovedPawns[uColor] = CountBits(pos->bbPawns[uColor] &
+ pHash->uNumUnmovedPawns[uColor] = FastCountBits(pos->bbPawns[uColor] &
BBRANK[RANK(uUnmovedRank[uColor])]);
ASSERT(pHash->uNumUnmovedPawns[uColor] <= 8);
@@ -2247,7 +2244,7 @@ Return value:
(uColor == WHITE) ?
(pos->bbPawns[WHITE] & (bbAllPawns << 8)) :
(pos->bbPawns[BLACK] & (bbAllPawns >> 8));
- ASSERT(CountBits(pHash->bbStationaryPawns[uColor]) <= 8);
+ ASSERT(FastCountBits(pHash->bbStationaryPawns[uColor]) <= 8);
// Give central pawns a small bonus and rook pawns a small
// penalty.
@@ -2269,9 +2266,86 @@ Return value:
// (and no /2 fixup) is needed the way the old per-pawn,
// per-color loop required.
pHash->uNumRammedPawns =
- CountBits(pos->bbPawns[WHITE] & (pos->bbPawns[BLACK] << 8));
+ FastCountBits(pos->bbPawns[WHITE] & (pos->bbPawns[BLACK] << 8));
ASSERT(pHash->uNumRammedPawns <= 8);
+ // Good/bad bishop pawn-structure score, cached per (color, color-
+ // complex) instead of rescanned by every bishop of that color/
+ // complex in _EvalBishop (board_representation/EVAL.md-style note:
+ // every input here -- bbStationaryPawns, bbPawns, bbPawnAttacks --
+ // is pure pawn-structure data already keyed by this pawn-hash
+ // entry's signature, and the bishop's own square only ever selects
+ // *which* of these two precomputed totals to use, never feeds the
+ // computation itself. Semantics (corrected from the old per-bishop
+ // version, which silently ignored enemy stationary pawns on the
+ // bishop's complex entirely):
+ // - own stationary pawns on the complex: always count (full
+ // weight) -- they're not moving, they wall the bishop in for
+ // good.
+ // - enemy stationary pawns on the complex: count (full weight)
+ // only if defended by another enemy pawn -- an undefended
+ // stationary pawn is still a static target, not a durable
+ // obstruction, the same reasoning the transient case below
+ // already applied to enemy pawns.
+ // - own transient (non-stationary) pawns on the complex: always
+ // count (full weight) -- might move off later, hence the
+ // separate, lighter table.
+ // - enemy transient pawns on the complex: count (half weight)
+ // only if defended -- unsupported ones are likely to be traded
+ // off soon, not a durable obstruction.
+ {
+ static const BITBOARD bbColorSq[2] = { 0x55aa55aa55aa55aaULL,
+ 0xaa55aa55aa55aa55ULL };
+ ULONG uComplex;
+
+ FOREACH_COLOR(uColor)
+ {
+ ULONG xColor = FLIP(uColor);
+ BITBOARD bbNonStationary = ~(pHash->bbStationaryPawns[WHITE] |
+ pHash->bbStationaryPawns[BLACK]);
+
+ for (uComplex = 0; uComplex < 2; uComplex++)
+ {
+ BITBOARD bbMask = bbColorSq[uComplex];
+ SCORE i = 16;
+ BITBOARD bb;
+ COOR cSq;
+
+ bb = pHash->bbStationaryPawns[uColor] & bbMask;
+ while (IS_ON_BOARD(cSq = CoorFromBitBoardRank8ToRank1(&bb)))
+ {
+ i += STATIONARY_PAWN_ON_BISHOP_COLOR[cSq];
+ }
+
+ bb = pHash->bbStationaryPawns[xColor] & bbMask;
+ while (IS_ON_BOARD(cSq = CoorFromBitBoardRank8ToRank1(&bb)))
+ {
+ if (pos->bbPawnAttacks[xColor] & COOR_TO_BB(cSq))
+ {
+ i += STATIONARY_PAWN_ON_BISHOP_COLOR[cSq];
+ }
+ }
+
+ bb = pos->bbPawns[uColor] & bbNonStationary & bbMask;
+ while (IS_ON_BOARD(cSq = CoorFromBitBoardRank8ToRank1(&bb)))
+ {
+ i += TRANSIENT_PAWN_ON_BISHOP_COLOR[cSq];
+ }
+
+ bb = pos->bbPawns[xColor] & bbNonStationary & bbMask;
+ while (IS_ON_BOARD(cSq = CoorFromBitBoardRank8ToRank1(&bb)))
+ {
+ if (pos->bbPawnAttacks[xColor] & COOR_TO_BB(cSq))
+ {
+ i += TRANSIENT_PAWN_ON_BISHOP_COLOR[cSq] / 2;
+ }
+ }
+
+ pHash->iBishopColorScore[uColor][uComplex] = (SHORT)i;
+ }
+ }
+ }
+
// Second pass, requires pHash->uCountPerFile[] populated for both
// colors.
FOREACH_COLOR(uColor)
@@ -2279,7 +2353,7 @@ Return value:
ASSERT(IS_VALID_COLOR(uColor));
ULONG xColor = FLIP(uColor);
ASSERT(IS_VALID_COLOR(xColor));
- ASSERT(CountBits(pos->bbPawns[uColor]) <= 8);
+ ASSERT(FastCountBits(pos->bbPawns[uColor]) <= 8);
d1 = 16 * g_iAhead[uColor];
BITBOARD bbPawnsToWalk = pos->bbPawns[uColor];
@@ -2333,7 +2407,7 @@ Return value:
uPawnFile = FILE(c) + 1;
ASSERT((1 <= uPawnFile) && (uPawnFile <= 8));
- ASSERT(CountBits(pos->bbPawns[uColor] & BBFILE[FILE(c)])
+ ASSERT(FastCountBits(pos->bbPawns[uColor] & BBFILE[FILE(c)])
== pHash->uCountPerFile[uColor][uPawnFile]);
//
@@ -2391,7 +2465,7 @@ Return value:
// it is shielded or exposed.
//
pHash->bbStationaryPawns[uColor] |= COOR_TO_BB(c);
- ASSERT(CountBits(pHash->bbStationaryPawns[uColor]) <= 8);
+ ASSERT(FastCountBits(pHash->bbStationaryPawns[uColor]) <= 8);
if (pHash->uCountPerFile[xColor][uPawnFile])
{
ASSERT(BACKWARD_SHIELDED_BY_LOCATION[c] < 0);
@@ -4213,7 +4287,6 @@ Return value:
BITBOARD bb;
BITBOARD bbMask;
BITBOARD bbPc;
- COOR cSquare;
SCORE i;
ULONG u;
ULONG uTotalMobility;
@@ -4230,60 +4303,26 @@ Return value:
pos->uMinorsAtHome[uColor] += (c == cBishopAtHome[uColor][1]);
ASSERT(pos->uMinorsAtHome[uColor] <= 4);
- // Good and bad bishops given stationary pawn structure.
- i = 16;
+ // Good and bad bishops given pawn structure (own and enemy,
+ // stationary and transient) on this bishop's color complex --
+ // cached per (color, complex) in the pawn hash, since none of it
+ // depends on which square of that complex the bishop actually
+ // sits on. See the cache population in the pawn-hash-miss path
+ // above (_EvalPawns's FOREACH_COLOR block) for the full semantics.
bbMask = bbColorSq[IS_SQUARE_WHITE(c)];
- bb = pHash->bbStationaryPawns[uColor] & bbMask;
- ASSERT(CountBits(bb) <= 16);
- while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb)))
- {
- // Consider stationary (rammed or backward) pawns of the same
- // color as the bishop.
- ASSERT(pos->rgSquare[cSquare].pPiece);
- ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece));
- ASSERT(GET_COLOR(pos->rgSquare[cSquare].pPiece) == uColor);
- i += STATIONARY_PAWN_ON_BISHOP_COLOR[cSquare];
- }
+ i = pHash->iBishopColorScore[uColor][IS_SQUARE_WHITE(c)];
EVAL_TERM(uColor,
BISHOP,
c,
pos->iScore[uColor],
i,
- "good/bad stationary pawns ++");
+ "good/bad bishop pawns");
- i = 0;
+ // Still needed below: "non-stationary pawns of either color on
+ // this color complex," used by the mobility walk's
+ // BMOB_FRIEND_PAWN case (see its comment just below).
bbPc = ~(pHash->bbStationaryPawns[WHITE] |
- pHash->bbStationaryPawns[BLACK]);
- bbPc &= bbMask;
- bb = pos->bbPawns[uColor] & bbPc;
- while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb)))
- {
- ASSERT(pos->rgSquare[cSquare].pPiece);
- ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece));
- ASSERT(GET_COLOR(pos->rgSquare[cSquare].pPiece) == uColor);
- ASSERT(pos->bbPawns[uColor] & COOR_TO_BB(cSquare));
- i += TRANSIENT_PAWN_ON_BISHOP_COLOR[cSquare];
- }
-
- bb = pos->bbPawns[xColor] & bbPc;
- while(IS_ON_BOARD(cSquare = CoorFromBitBoardRank8ToRank1(&bb)))
- {
- // N.B. Only count enemy pawns that are supported by another
- // pawn.
- ASSERT(pos->rgSquare[cSquare].pPiece);
- ASSERT(IS_PAWN(pos->rgSquare[cSquare].pPiece));
- ASSERT(GET_COLOR(pos->rgSquare[cSquare].pPiece) == xColor);
- ASSERT(pos->bbPawns[xColor] & COOR_TO_BB(cSquare));
- i +=
- ((pos->bbPawnAttacks[xColor] & COOR_TO_BB(cSquare)) != 0) *
- TRANSIENT_PAWN_ON_BISHOP_COLOR[cSquare] / 2;
- }
- EVAL_TERM(uColor,
- BISHOP,
- c,
- pos->iScore[uColor],
- i,
- "good/bad transient pawns");
+ pHash->bbStationaryPawns[BLACK]) & bbMask;
//
// Bishop mobility (and update attack tables) -- board_representation/
@@ -4379,7 +4418,7 @@ Return value:
}
pos->bbMinorXrayAttacks[uColor] |= bbXrayAccum;
- uTotalMobility = CountBits(bbMobility);
+ uTotalMobility = FastCountBits(bbMobility);
ASSERT(uTotalMobility <= 13);
@@ -4652,7 +4691,7 @@ Return value:
bbMobility = (bbAttack & bbEnemyNonPawnOcc) |
(bbAttack & ~bbFriendOcc & ~bbEnemyNonPawnOcc &
~bbUnsafeForMinor);
- uMobilitySquares = CountBits(bbMobility);
+ uMobilitySquares = FastCountBits(bbMobility);
}
ASSERT(uMobilitySquares >= 0);
ASSERT(uMobilitySquares <= 8);
@@ -4708,7 +4747,6 @@ Return value:
ULONG uColor;
ULONG uTotalMobility;
BITBOARD bbMobility;
- COOR cSquare;
SCORE i;
ASSERT(IS_ON_BOARD(c));
@@ -4735,17 +4773,6 @@ Return value:
"file/passer bonus");
//
- // Removed 2026-08-30 ("rook trapping enemy king"): a rook on the
- // 7th/8th rank aligned with the enemy king is exactly the
- // geometric pattern CountKingSafetyDefects' CHECK_VECTOR
- // line-of-sight scan already picks up into
- // uPiecesPointingAtKing/king-safety -- this was pricing the same
- // king-danger fact a second time via a rook-specific bonus. Belongs
- // in king safety, not here. (pFriendRook, only ever assigned in
- // this block and never read, is gone too.)
- //
-
- //
// Rooks increase in value as pawns come off:
//
// "A further refinement would be to raise the knight's value by
@@ -4759,9 +4786,12 @@ Return value:
ROOK,
c,
pos->iScore[uColor],
- ROOK_VALUE_AS_PAWNS_COME_OFF[FastCountBits(pos->bbPawns[uColor]) +
- FastCountBits(pos->bbPawns[xColor])],
+ ROOK_VALUE_AS_PAWNS_COME_OFF[
+ FastCountBits(pos->bbPawns[BLACK] | pos->bbPawns[WHITE])
+ ],
"increase in value/pawns");
+ ASSERT((FastCountBits(pos->bbPawns[uColor]) + FastCountBits(pos->bbPawns[xColor])) ==
+ (FastCountBits(pos->bbPawns[uColor] | pos->bbPawns[xColor])));
//
// Rook mobility (and update attack tables) -- board_representation/
@@ -4788,106 +4818,103 @@ Return value:
// credit at the blocker itself, matching the old table); a
// friendly rook blocker also earns the connected-rook bonus.
//
- {
- BITBOARD bbAttack = _RookAttacksBB(c, pos->bbOccupied);
- BITBOARD bbEnemyOcc = _BuildFriendlySideBB(pos, xColor);
- BITBOARD bbEnemySame = pos->bbPieces[xColor][ROOK];
- BITBOARD bbEnemyGEContinue = pos->bbPieces[xColor][QUEEN] |
- COOR_TO_BB(KingPosition(pos, xColor));
- BITBOARD bbFriendRQ = pos->bbPieces[uColor][ROOK] |
- pos->bbPieces[uColor][QUEEN];
- BITBOARD bbUnsafeForRook = pos->bbPawnAttacks[xColor] |
- pos->bbMinorAttacks[xColor];
- BITBOARD bbExclude = 0;
- BITBOARD bbSeen = 0;
- BITBOARD bbLayer = bbAttack;
- BITBOARD bbXrayAccum = 0;
- BITBOARD bbFirstLayerMask = 0;
- bbMobility = 0;
- pos->bbRookAttacks[uColor] |= bbAttack;
+ BITBOARD bbAttack = _RookAttacksBB(c, pos->bbOccupied);
+ BITBOARD bbEnemyOcc = _BuildFriendlySideBB(pos, xColor);
+ BITBOARD bbEnemySame = pos->bbPieces[xColor][ROOK];
+ BITBOARD bbEnemyGEContinue = pos->bbPieces[xColor][QUEEN] |
+ COOR_TO_BB(KingPosition(pos, xColor));
+ BITBOARD bbFriendRQ = pos->bbPieces[uColor][ROOK] |
+ pos->bbPieces[uColor][QUEEN];
+ BITBOARD bbUnsafeForRook = pos->bbPawnAttacks[xColor] |
+ pos->bbMinorAttacks[xColor];
+ BITBOARD bbExclude = 0;
+ BITBOARD bbSeen = 0;
+ BITBOARD bbLayer = bbAttack;
+ BITBOARD bbXrayAccum = 0;
+ BITBOARD bbFirstLayerMask = 0;
- //
- // Unified chain walk over both mobility and x-ray population
- // in one pass, board_representation/EVAL.md section 9. The
- // old mailbox walk's RMOB_FRIEND_ROOK/_QUEEN and
- // RMOB_ENEMY_GREATER cases don't just x-ray past their blocker
- // for attack-bit purposes -- they keep walking (fStop=FALSE)
- // and keep crediting mobility for whatever safe/empty squares
- // and further captures lie beyond, however many such blockers
- // are stacked on one ray. A first version of this conversion
- // (committed, since fixed) only handled the near side (up to
- // the first blocker) for mobility and treated the chain purely
- // as an attack-bit population exercise -- caught by comparing
- // against the old mailbox walk directly on a battery position
- // (two same-color rooks on an open file: old code credited 13
- // mobility to the far rook's near companion via squares beyond
- // it, the near-side-only version credited only 9). Fixed by
- // reusing the exact same "recompute with blockers excluded"
- // chain used for x-ray population to also accumulate mobility
- // credit at every layer, not just the first.
- //
- // Each iteration classifies the newly-revealed blockers on
- // each still-open ray: friendly rook/queen and enemy queen/
- // king continue the chain (recompute with them excluded too);
- // everything else (friendly non-R/Q, enemy pawn/knight/bishop,
- // enemy rook, or the board edge) stops that ray. Bounded: each
- // iteration either terminates or consumes at least one new
- // piece into the exclusion set, so this can loop at most
- // once per piece on the board.
- //
- for (;;)
- {
- BITBOARD bbNew = bbLayer & ~bbSeen;
- BITBOARD bbBlockersHere = bbNew & pos->bbOccupied;
- BITBOARD bbEmptyHere = bbNew & ~pos->bbOccupied;
- BITBOARD bbFriendRQHere = bbBlockersHere & bbFriendRQ;
- BITBOARD bbEnemySameHere = bbBlockersHere & bbEnemySame;
- BITBOARD bbEnemyGEHere = bbBlockersHere & bbEnemyGEContinue;
- BITBOARD bbEnemyLEHere = bbBlockersHere & bbEnemyOcc &
- ~bbEnemySameHere & ~bbEnemyGEHere;
- BITBOARD bbFriendRookHere = bbBlockersHere &
- pos->bbPieces[uColor][ROOK];
- BITBOARD bbContinueHere;
+ bbMobility = 0;
+ pos->bbRookAttacks[uColor] |= bbAttack;
- bbSeen |= bbNew;
- bbXrayAccum |= (bbNew & bbFirstLayerMask);
- bbFirstLayerMask = ~(BITBOARD)0;
+ //
+ // Unified chain walk over both mobility and x-ray population
+ // in one pass, board_representation/EVAL.md section 9. The
+ // old mailbox walk's RMOB_FRIEND_ROOK/_QUEEN and
+ // RMOB_ENEMY_GREATER cases don't just x-ray past their blocker
+ // for attack-bit purposes -- they keep walking (fStop=FALSE)
+ // and keep crediting mobility for whatever safe/empty squares
+ // and further captures lie beyond, however many such blockers
+ // are stacked on one ray. A first version of this conversion
+ // (committed, since fixed) only handled the near side (up to
+ // the first blocker) for mobility and treated the chain purely
+ // as an attack-bit population exercise -- caught by comparing
+ // against the old mailbox walk directly on a battery position
+ // (two same-color rooks on an open file: old code credited 13
+ // mobility to the far rook's near companion via squares beyond
+ // it, the near-side-only version credited only 9). Fixed by
+ // reusing the exact same "recompute with blockers excluded"
+ // chain used for x-ray population to also accumulate mobility
+ // credit at every layer, not just the first.
+ //
+ // Each iteration classifies the newly-revealed blockers on
+ // each still-open ray: friendly rook/queen and enemy queen/
+ // king continue the chain (recompute with them excluded too);
+ // everything else (friendly non-R/Q, enemy pawn/knight/bishop,
+ // enemy rook, or the board edge) stops that ray. Bounded: each
+ // iteration either terminates or consumes at least one new
+ // piece into the exclusion set, so this can loop at most
+ // once per piece on the board.
+ //
+ for (;;)
+ {
+ BITBOARD bbNew = bbLayer & ~bbSeen;
+ BITBOARD bbBlockersHere = bbNew & pos->bbOccupied;
+ BITBOARD bbEmptyHere = bbNew & ~pos->bbOccupied;
+ BITBOARD bbFriendRQHere = bbBlockersHere & bbFriendRQ;
+ BITBOARD bbEnemySameHere = bbBlockersHere & bbEnemySame;
+ BITBOARD bbEnemyGEHere = bbBlockersHere & bbEnemyGEContinue;
+ BITBOARD bbEnemyLEHere = bbBlockersHere & bbEnemyOcc &
+ ~bbEnemySameHere & ~bbEnemyGEHere;
+ BITBOARD bbFriendRookHere = bbBlockersHere &
+ pos->bbPieces[uColor][ROOK];
+ BITBOARD bbContinueHere;
- bbMobility |= (bbEmptyHere & ~bbUnsafeForRook) |
- bbEnemySameHere | bbEnemyGEHere |
- (bbEnemyLEHere & ~bbUnsafeForRook);
+ bbSeen |= bbNew;
+ bbXrayAccum |= (bbNew & bbFirstLayerMask);
+ bbFirstLayerMask = ~(BITBOARD)0;
- //
- // Connected-rook bonus fires at every friendly rook found
- // along the chain, not just the first (matches the old
- // walk: each RMOB_FRIEND_ROOK hit its own EVAL_TERM call).
- //
- while (bbFriendRookHere)
- {
- COOR cBlocker = CoorFromBitBoardRank8ToRank1(&bbFriendRookHere);
- FLAG fHoriz = (RANK(cBlocker) == RANK(c));
- EVAL_TERM(uColor,
- ROOK,
- cBlocker,
- pos->iScore[uColor],
- (ROOK_CONNECTED_HORIZ * fHoriz +
- ROOK_CONNECTED_VERT * FLIP(fHoriz)),
- "rook connected");
- }
+ bbMobility |= (bbEmptyHere & ~bbUnsafeForRook) |
+ bbEnemySameHere | bbEnemyGEHere |
+ (bbEnemyLEHere & ~bbUnsafeForRook);
- bbContinueHere = bbFriendRQHere | bbEnemyGEHere;
- if (!bbContinueHere)
- {
- break;
- }
- bbExclude |= bbContinueHere;
- bbLayer = _RookAttacksBB(c, pos->bbOccupied & ~bbExclude);
+ // Connected-rook bonus fires at every friendly rook found
+ // along the chain, not just the first (matches the old
+ // walk: each RMOB_FRIEND_ROOK hit its own EVAL_TERM call).
+ while (bbFriendRookHere)
+ {
+ COOR cBlocker = CoorFromBitBoardRank8ToRank1(&bbFriendRookHere);
+ FLAG fHoriz = (RANK(cBlocker) == RANK(c));
+ EVAL_TERM(uColor,
+ ROOK,
+ cBlocker,
+ pos->iScore[uColor],
+ (ROOK_CONNECTED_HORIZ * fHoriz +
+ ROOK_CONNECTED_VERT * FLIP(fHoriz)),
+ "rooks connected");
}
- pos->bbRookXrayAttacks[uColor] |= bbXrayAccum;
- uTotalMobility = CountBits(bbMobility);
+ bbContinueHere = bbFriendRQHere | bbEnemyGEHere;
+ if (!bbContinueHere)
+ {
+ break;
+ }
+ bbExclude |= bbContinueHere;
+ bbLayer = _RookAttacksBB(c, pos->bbOccupied & ~bbExclude);
}
+ pos->bbRookXrayAttacks[uColor] |= bbXrayAccum;
+
+ uTotalMobility = FastCountBits(bbMobility);
ASSERT(uTotalMobility <= 14);
ASSERT(pos->uArmyScaler[xColor] <= 31);
@@ -4899,64 +4926,17 @@ Return value:
// nonzero count here is still liable to be an overcount.
if (uTotalMobility < EVAL_MOBILITY_CAREFULLY_BELOW)
{
- if (uTotalMobility == 0)
- {
- i = ROOK_MOBILITY_BY_SQUARES[0];
- i *= (int)(REDUCED_MATERIAL_UP_SCALER[pos->uArmyScaler[xColor]] + 1);
- i /= 8;
- EVAL_TERM(uColor,
- ROOK,
- c,
- pos->iScore[uColor],
- i,
- "rook mobility");
- }
_RecordTrappedCandidate(pos, uColor, c, bbMobility);
-
- // Is the rook trapped in the corner by a stupid friendly
- // king? -- purely geometric (unrelated to the mobility
- // deferral above), still gated on the same coarse "cramped"
- // raw-mobility threshold -- color-indexed instead of a
- // WHITE/BLACK branch over two mirrored blocks
- // (board_representation/EVAL.md section 9).
- ASSERT(IS_VALID_COLOR(uColor));
- ASSERT(IS_ON_BOARD(c));
-
- static const ULONG BACK_RANK_MASK[2] = { 0x00, 0x70 }; // BLACK, WHITE
- static const COOR E_CORNER[2] = { E8, E1 };
- static const COOR D_CORNER[2] = { D8, D1 };
-
- if ((c & 0xF0) == BACK_RANK_MASK[uColor])
- {
- cSquare = KingPosition(pos, uColor);
- ASSERT(IS_ON_BOARD(cSquare));
- if ((cSquare & 0xF0) == BACK_RANK_MASK[uColor])
- {
- if (((cSquare > E_CORNER[uColor]) && (c > cSquare)) ||
- ((cSquare < D_CORNER[uColor]) && (c < cSquare)))
- {
- EVAL_TERM(uColor,
- ROOK,
- c,
- pos->iScore[uColor],
- KING_TRAPPING_ROOK,
- "king trapping rook");
- }
- }
- }
- }
- else // reasonable mobility
- {
- i = ROOK_MOBILITY_BY_SQUARES[uTotalMobility];
- i *= (int)(REDUCED_MATERIAL_UP_SCALER[pos->uArmyScaler[xColor]] + 1);
- i /= 8;
- EVAL_TERM(uColor,
- ROOK,
- c,
- pos->iScore[uColor],
- i,
- "rook mobility");
}
+ i = ROOK_MOBILITY_BY_SQUARES[uTotalMobility];
+ i *= (int)(REDUCED_MATERIAL_UP_SCALER[pos->uArmyScaler[xColor]] + 1);
+ i /= 8;
+ EVAL_TERM(uColor,
+ ROOK,
+ c,
+ pos->iScore[uColor],
+ i,
+ "rook mobility");
}
@@ -5104,7 +5084,7 @@ Return value:
pos->bbQueenAttacks[uColor] |= bbAttackTotal;
pos->bbQueenXrayAttacks[uColor] |= bbXrayAccum;
- uTotalMobility = CountBits(bbMobility);
+ uTotalMobility = FastCountBits(bbMobility);
}
ASSERT(uTotalMobility <= 27);
@@ -5177,7 +5157,7 @@ Return value:
// instruction, since this is the hottest inner loop in eval.c and
// scaler*(A+B+C+...) is one multiply instead of scaler*A + scaler*B
// + ... Ring-square attack/defense/flight terms below are computed
- // via bitboard intersection + CountBits() against the whole front/
+ // via bitboard intersection + FastCountBits() against the whole front/
// back/side bucket at once (g_KingRingFrontBB/g_KingRingBackBB/
// g_KingRingSideBB, data.c), not a per-square branchy loop -- a
// fixed handful of AND+popcount ops regardless of how many ring
@@ -5266,7 +5246,7 @@ Return value:
(SCORE)KING_COUNTER_BY_ATTACK_PATTERN[bvPattern];
// Count how many ring squares are attacked by the enemy queen.
- uQueenNearKing = CountBits(
+ uQueenNearKing = FastCountBits(
bbRing & (pos->bbQueenAttacks[xColor] | pos->bbQueenXrayAttacks[xColor])
);
ASSERT(uQueenNearKing <= 8);
@@ -5274,45 +5254,45 @@ Return value:
// Minor attacks at front, side, back.
iSafety += KING_RING_ATTACK_MINOR[0] *
- (SCORE)CountBits(bbFront & pos->bbMinorAttacks[xColor]);
+ (SCORE)FastCountBits(bbFront & pos->bbMinorAttacks[xColor]);
iSafety += KING_RING_ATTACK_MINOR[1] *
- (SCORE)CountBits(bbBack & pos->bbMinorAttacks[xColor]);
+ (SCORE)FastCountBits(bbBack & pos->bbMinorAttacks[xColor]);
iSafety += KING_RING_ATTACK_MINOR[2] *
- (SCORE)CountBits(bbSide & pos->bbMinorAttacks[xColor]);
+ (SCORE)FastCountBits(bbSide & pos->bbMinorAttacks[xColor]);
// Rook attacks at front, side, back.
iSafety += KING_RING_ATTACK_ROOK[0] *
- (SCORE)CountBits(bbFront & pos->bbRookAttacks[xColor]);
+ (SCORE)FastCountBits(bbFront & pos->bbRookAttacks[xColor]);
iSafety += KING_RING_ATTACK_ROOK[1] *
- (SCORE)CountBits(bbBack & pos->bbRookAttacks[xColor]);
+ (SCORE)FastCountBits(bbBack & pos->bbRookAttacks[xColor]);
iSafety += KING_RING_ATTACK_ROOK[2] *
- (SCORE)CountBits(bbSide & pos->bbRookAttacks[xColor]);
+ (SCORE)FastCountBits(bbSide & pos->bbRookAttacks[xColor]);
// Pawn attacks.
iSafety += KING_RING_ATTACK_PAWN *
- (SCORE)CountBits(bbRing & pos->bbPawnAttacks[xColor]);
+ (SCORE)FastCountBits(bbRing & pos->bbPawnAttacks[xColor]);
// Undefended squares in front, side, back.
iSafety += KING_RING_UNDEFENDED[0] *
- (SCORE)CountBits(bbFront & bbUndefended);
+ (SCORE)FastCountBits(bbFront & bbUndefended);
iSafety += KING_RING_UNDEFENDED[1] *
- (SCORE)CountBits(bbBack & bbUndefended);
+ (SCORE)FastCountBits(bbBack & bbUndefended);
iSafety += KING_RING_UNDEFENDED[2] *
- (SCORE)CountBits(bbSide & bbUndefended);
+ (SCORE)FastCountBits(bbSide & bbUndefended);
// Only xray attack, no direct attack.
iSafety += KING_RING_XRAY_BONUS *
- (SCORE)CountBits(bbRing & bbEnemyXrayAny & ~bbEnemyAtkAny);
+ (SCORE)FastCountBits(bbRing & bbEnemyXrayAny & ~bbEnemyAtkAny);
// Flight squares are empty, unattacked squares around the king.
// In front of the king, though, these are gaps and a negative
// whereas behind the king they are legitimately small bonuses.
iSafety += KING_RING_FRONT_FLIGHT_PENALTY *
- (SCORE)CountBits(bbFront & bbEmpty & ~bbEnemyAtkAny);
+ (SCORE)FastCountBits(bbFront & bbEmpty & ~bbEnemyAtkAny);
// Small bonus for empty squares behind the king to retreat into.
iSafety += KING_BACK_FLIGHT_BONUS *
- (SCORE)CountBits(bbBack & bbEmpty & ~bbEnemyAtkAny);
+ (SCORE)FastCountBits(bbBack & bbEmpty & ~bbEnemyAtkAny);
#ifdef KING_DEFECT_DUMP
{
@@ -5992,7 +5972,7 @@ Return value:
bb = pHash->bbPasserLocations[uColor];
if (bb != 0)
{
- uNumPassers[uColor] = CountBits(bb);
+ uNumPassers[uColor] = FastCountBits(bb);
ASSERT(uNumPassers[uColor] > 0);
d1 = 16 * g_iAhead[uColor];
ASSERT((d1 * 2) == (32 * g_iAhead[uColor]));
@@ -6379,7 +6359,7 @@ Return value:
// EVAL_MOBILITY_CAREFULLY_BELOW's comment.
bbUnsafeNow = pos->bbKingAttacks[xColor] & ~bbOwnDefend;
bbStillSafe = pos->bbTrappedEscapes[uColor][u] & ~bbUnsafeNow;
- uFinalMobility = CountBits(bbStillSafe);
+ uFinalMobility = FastCountBits(bbStillSafe);
if (IS_BISHOP(p))
{
@@ -7078,13 +7058,13 @@ Return value:
**/
{
POSITION *pos = &(ctx->sPosition);
+ ULONG uColor = pos->uToMove;
+ ULONG xColor = FLIP(uColor);
SCORE iScoreForSideToMove;
SCORE iAlphaMargin, iBetaMargin;
PAWN_HASH_ENTRY *pHash;
COOR c;
ULONG u;
- ULONG uColor = pos->uToMove;
- ULONG xColor = FLIP(uColor);
BITBOARD bb;
FLAG fDeferred;
// Computed at most once per Eval() call, on first need, by
@@ -7129,17 +7109,8 @@ Return value:
#ifdef EVAL_TIME
UINT64 u64MaterialSetupTimer = SystemReadTimeStampCounter();
#endif
- pos->iScore[uColor] =
- (PawnMaterial(pos, uColor) + pos->uNonPawnMaterial[uColor]);
- pos->iScore[xColor] =
- (PawnMaterial(pos, xColor) + pos->uNonPawnMaterial[xColor]);
#ifdef EVAL_DUMP
EvalTraceClear();
- Trace("Material:\n%d\t\t%d\t\t(%d for %s)\n",
- pos->iScore[WHITE], pos->iScore[BLACK],
- (pos->iScore[uColor] - pos->iScore[xColor]),
- COLOR_NAME(uColor)
- );
#endif
// Initialize army scalers here (moved ahead of the super-lazy exit
@@ -7233,7 +7204,7 @@ Return value:
#ifdef EVAL_TIME
UINT64 uSuperLazyTimer = SystemReadTimeStampCounter();
#endif
- iScoreForSideToMove = (pos->iScore[uColor] -pos->iScore[xColor]);
+ iScoreForSideToMove = pos->iMaterialBalance[uColor];
ASSERT(IS_VALID_SCORE(iScoreForSideToMove));
// We have a super-rough eval -- material only at this point.
@@ -7302,6 +7273,7 @@ Return value:
goto end;
#endif
}
+
if (iScoreForSideToMove - iSuperLazySelfPessEnemyOptMargin > iBeta)
{
ASSERT(iScoreForSideToMove > iBeta);
@@ -7380,11 +7352,13 @@ Return value:
#endif
ASSERT(NULL != pHash);
ASSERT(IS_VALID_FLAG(fDeferred));
- pos->iScore[WHITE] += pHash->iScore[WHITE];
- pos->iScore[BLACK] += pHash->iScore[BLACK];
+ pos->iScore[uColor] =
+ (PawnMaterial(pos, uColor) + pos->uNonPawnMaterial[uColor] + pHash->iScore[uColor]);
+ pos->iScore[xColor] =
+ (PawnMaterial(pos, xColor) + pos->uNonPawnMaterial[xColor] + pHash->iScore[xColor]);
ASSERT(IS_VALID_SCORE(pos->iScore[WHITE] - pos->iScore[BLACK]));
#ifdef EVAL_DUMP
- Trace("After pawns:\n%d\t\t%d\t\t(%d for %s)\n",
+ Trace("After pawns + material:\n%d\t\t%d\t\t(%d for %s)\n",
pos->iScore[WHITE], pos->iScore[BLACK],
(pos->iScore[uColor] - pos->iScore[xColor]),
COLOR_NAME(uColor)
diff --git a/src/generate.c b/src/generate.c
index f41a3b6..a2fc9c4 100644
--- a/src/generate.c
+++ b/src/generate.c
@@ -853,7 +853,7 @@ _ComputeCheckTargetMaskBB(IN COOR cKing, IN COOR cAttacker,
// Non-static so testgenerate.c's harness can call it directly to set
// up MOVE_STACK.bbFriendlyOccupied when calling a _Generate*BB
// function outside of _GenerateAllMoves.
-BITBOARD
+BITBOARD INLINE
_BuildFriendlySideBB(IN POSITION *pos, IN ULONG uSide)
/**
diff --git a/src/search.c b/src/search.c
index ccaae74..e6249b0 100644
--- a/src/search.c
+++ b/src/search.c
@@ -938,6 +938,7 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
if (MOVE_COUNT(ctx, ctx->uPly))
{
// TODO: Experiment with removing these extensions.
+#if 0
if (NUM_CHECKING_PIECES(ctx, ctx->uPly) > 1)
{
iPositionalExtend += QUARTER_PLY;
@@ -948,7 +949,8 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
iPositionalExtend += QUARTER_PLY;
INC(ctx->sCounters.extension.uNoLegalKingMoves);
}
- else if (ONE_LEGAL_MOVE(ctx, ctx->uPly) &&
+#endif
+ if (ONE_LEGAL_MOVE(ctx, ctx->uPly) &&
CountKingSafetyDefects(pos, pos->uToMove) > 2)
{
iPositionalExtend += HALF_PLY;
@@ -1248,14 +1250,14 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
// extensions which are represented by iPositionalExtend).
iExtend = iPositionalExtend;
ComputeMoveExtension(ctx,
- iAlpha,
- iBeta,
- (x - 1), // Note: x==0 if doing mvHash
- iEval,
- uDepth,
- iMoveSee,
- fIsLeftoverMove,
- &iExtend);
+ iAlpha,
+ iBeta,
+ (x - 1), // Note: x==0 if doing mvHash
+ iEval,
+ uDepth,
+ iMoveSee,
+ fIsLeftoverMove,
+ &iExtend);
ASSERT((uLegalMoves > 1) ? (x != 0) : TRUE);
fMoveIsPruneReduceEligible = CanWeReduceOrPruneMove(ctx,
@@ -2670,26 +2672,27 @@ _ShouldWeConsiderThisMove(IN SEARCHER_THREAD_CONTEXT *ctx,
ASSERT(IS_CHECKING_MOVE(mv));
ASSERT(TRUE == fGeneratedChecks);
+#if 0
// IDEA: don't play obviously losing checks if we are already
// way below alpha.
if (iFutility < +VALUE_BISHOP)
{
return(TRUE);
}
- {
- FLAG fConsider = (GetMoveSee(ctx, mv, uMoveNum) >= 0);
-#ifdef CALIBRATE_QSEARCH_FUTILITY
- if (FALSE == fConsider)
- {
- _QFutDiagnoseReject(ctx, uMoveNum, iAlpha, iBeta,
- iPositional, uPositionalSource,
- QFUT_GATE_CHECK_BISHOP,
- iFutility - (SCORE)VALUE_BISHOP,
- fGeneratedChecks);
- }
#endif
- return(fConsider);
+ FLAG fConsider = (GetMoveSee(ctx, mv, uMoveNum) >= 0);
+#ifdef CALIBRATE_QSEARCH_FUTILITY
+ if (FALSE == fConsider)
+ {
+ _QFutDiagnoseReject(ctx, uMoveNum, iAlpha, iBeta,
+ iPositional, uPositionalSource,
+ QFUT_GATE_CHECK_BISHOP,
+ iFutility, // - (SCORE)VALUE_BISHOP,
+ fGeneratedChecks);
}
+#endif
+ return(fConsider);
+
}
return(FALSE);
}
diff --git a/src/searchsup.c b/src/searchsup.c
index 9e11631..ca75c92 100644
--- a/src/searchsup.c
+++ b/src/searchsup.c
@@ -1267,15 +1267,16 @@ Return value:
BITV bvFlags;
ASSERT(IS_CHECKING_MOVE(mv) || (mv.pCaptured != NULL));
- if (uMoveNum != (ULONG)-1)
+ if (uMoveNum == (ULONG)-1)
{
- ASSERT(uMoveNum < MAX_MOVE_STACK);
- ASSERT(IS_SAME_MOVE(mv, ctx->sMoveStack.mvf[uMoveNum].mv));
- bvFlags = ctx->sMoveStack.mvf[uMoveNum].bvFlags;
- if (bvFlags & MVF_SEE_KNOWN)
- {
- return((bvFlags & MVF_SEE_NONNEGATIVE) ? 0 : -1);
- }
+ return(SeeFastSign(&ctx->sPosition, mv) ? 0 : -1);
+ }
+ ASSERT(uMoveNum < MAX_MOVE_STACK);
+ ASSERT(IS_SAME_MOVE(mv, ctx->sMoveStack.mvf[uMoveNum].mv));
+ bvFlags = ctx->sMoveStack.mvf[uMoveNum].bvFlags;
+ if (bvFlags & MVF_SEE_KNOWN)
+ {
+ return((bvFlags & MVF_SEE_NONNEGATIVE) ? 0 : -1);
}
return(SeeFastSign(&ctx->sPosition, mv) ? 0 : -1);
}
@@ -2393,9 +2394,11 @@ TryNullmovePruning(SEARCHER_THREAD_CONTEXT *ctx,
{
ASSERT(GET_COLOR(mv.pCaptured) == FLIP(pos->uToMove));
ASSERT(mv.pCaptured == pos->rgSquare[mv.cTo].pPiece);
+
// Victim belongs to FLIP(pos->uToMove) here, i.e. the
// mover at ctx->uPly - 1 (see RecordEnprisePieceAtPly).
RecordEnprisePieceAtPly(ctx, ctx->uPly - 1, mv.cTo);
+#if 0
mvRef = ctx->mvNullmoveRefutations[ctx->uPly - 2];
if ((mvRef.uMove) &&
(mvRef.pCaptured == mv.pCaptured) &&
@@ -2410,6 +2413,7 @@ TryNullmovePruning(SEARCHER_THREAD_CONTEXT *ctx,
*piOrigExtend += _bm_by_piece[PIECE_TYPE(mv.pCaptured)];
INC(ctx->sCounters.extension.uBotvinnikMarkoff);
}
+#endif
ctx->mvNullmoveRefutations[ctx->uPly] = mv;
}
else if (!IS_CAPTURE_OR_PROMOTION(mv))