summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-09-15 23:17:33 -0700
committerScott Gasch <[email protected]>2026-09-15 23:17:33 -0700
commitc0ac3d7845f60c7f6f2c3bfc306ceb0e4ac045ba (patch)
treee2b7427e212bd5b557f15131b815517a86b20486 /src
parent0bb4ae3052cb8ff9ffcf951b3ef0e6f50a7efdfa (diff)
Rework check/pawn-push/recapture extension gating on real SEE (GetMoveSee, generalized from GetCheckSee), restrict LMR to leftover moves only, and tighten recapture's window back to 1x after measuring a 6.8x node blowup from sharing the wider window.
Head-to-head match at 1170 games confirms parity with the prior head (score=0.510, LLR still between bounds) -- landing as a neutral-or-better baseline rather than a regression.
Diffstat (limited to 'src')
-rwxr-xr-xsrc/chess.h8
-rw-r--r--src/generate.c25
-rw-r--r--src/root.c15
-rw-r--r--src/search.c36
-rw-r--r--src/searchsup.c169
-rw-r--r--src/split.c21
6 files changed, 196 insertions, 78 deletions
diff --git a/src/chess.h b/src/chess.h
index 4d7015e..16cdca9 100755
--- a/src/chess.h
+++ b/src/chess.h
@@ -1113,10 +1113,13 @@ typedef struct _COUNTERS
struct
{
ULONG uPawnPush;
+ ULONG uPawnPushByQuarterPly[5]; // indexed by granted amount / QUARTER_PLY
ULONG uCheck;
+ ULONG uCheckByQuarterPly[5]; // indexed by granted amount / QUARTER_PLY
ULONG uMateThreat;
ULONG uRecapture;
ULONG uOneLegalMove;
+ ULONG uOneLegalMoveByQuarterPly[5]; // indexed by granted amount / QUARTER_PLY
ULONG uNoLegalKingMoves;
ULONG uMultiCheck;
ULONG uSingularMove;
@@ -3247,7 +3250,7 @@ ComputeReactionToCheckExtension(IN OUT SEARCHER_THREAD_CONTEXT *ctx,
IN OUT INT *piOrigExtend);
SCORE
-GetCheckSee(IN SEARCHER_THREAD_CONTEXT *ctx,
+GetMoveSee(IN SEARCHER_THREAD_CONTEXT *ctx,
IN MOVE mv,
IN ULONG uMoveNum);
@@ -3258,7 +3261,8 @@ ComputeMoveExtension(IN OUT SEARCHER_THREAD_CONTEXT *ctx,
IN ULONG uMoveNum,
IN SCORE iRoughEval,
IN ULONG uDepth,
- IN SCORE iCheckSee,
+ IN SCORE iMoveSee,
+ IN FLAG fIsLeftoverMove,
IN OUT INT *piExtend);
SCORE
diff --git a/src/generate.c b/src/generate.c
index a04b4c6..e3db067 100644
--- a/src/generate.c
+++ b/src/generate.c
@@ -4171,6 +4171,21 @@ Return value:
pStack->mvf[u].bvFlags |= MVF_SEE_KNOWN |
((s >= 0) ? MVF_SEE_NONNEGATIVE : 0);
}
+ else
+ {
+ // s > 0 here: the captured piece is worth strictly
+ // more than the piece making the capture. The real
+ // SEE(pos, mv) is mathematically guaranteed >= this
+ // material floor (the side to move can always stop
+ // the exchange right after this capture), so its
+ // sign is already known to be nonnegative without
+ // paying for the actual exchange walk -- avoids a
+ // later GetMoveSee() cache miss (and a redundant
+ // SEE() call) on exactly the most common "good
+ // capture" case.
+ pStack->mvf[u].bvFlags |= MVF_SEE_KNOWN |
+ MVF_SEE_NONNEGATIVE;
+ }
if (s >= 0)
{
@@ -4384,7 +4399,15 @@ Return value:
pStack->mvf[u].bvFlags |= MVF_SEE_KNOWN |
((s >= 0) ? MVF_SEE_NONNEGATIVE : 0);
}
-
+ else
+ {
+ // Same reasoning as _ScoreAllMoves' identical
+ // shortcut: s > 0 here is a guaranteed floor on the
+ // real SEE, so its sign is already known.
+ pStack->mvf[u].bvFlags |= MVF_SEE_KNOWN |
+ MVF_SEE_NONNEGATIVE;
+ }
+
if (s > 0)
{
// Winning captures: take with pieces before with king
diff --git a/src/root.c b/src/root.c
index 46a7a80..f5fe4c1 100644
--- a/src/root.c
+++ b/src/root.c
@@ -598,6 +598,9 @@ Return value:
#endif
Trace("Extensions: (%u +, %u q+, %u 1mv, %u !kmvs, %u mult+, %u pawn\n"
" %u threat, %u zug, %u sing, %u endg, %u bm, %u recap)\n"
+ " check by amount (1/4,1/2,3/4,1 ply): %u %u %u %u\n"
+ " 1mv by amount (1/4,1/2,3/4,1 ply): %u %u %u %u\n"
+ " pawn push by amount (1/4,1/2,3/4,1 ply): %u %u %u %u\n"
" %u / %llu qsearch nodes (%.1f%%) were in-check "
"evasion nodes\n"
" too-deep bailout: %llu ply-limit, %llu "
@@ -614,6 +617,18 @@ Return value:
ctx->sCounters.extension.uEndgame,
ctx->sCounters.extension.uBotvinnikMarkoff,
ctx->sCounters.extension.uRecapture,
+ ctx->sCounters.extension.uCheckByQuarterPly[1],
+ ctx->sCounters.extension.uCheckByQuarterPly[2],
+ ctx->sCounters.extension.uCheckByQuarterPly[3],
+ ctx->sCounters.extension.uCheckByQuarterPly[4],
+ ctx->sCounters.extension.uOneLegalMoveByQuarterPly[1],
+ ctx->sCounters.extension.uOneLegalMoveByQuarterPly[2],
+ ctx->sCounters.extension.uOneLegalMoveByQuarterPly[3],
+ ctx->sCounters.extension.uOneLegalMoveByQuarterPly[4],
+ ctx->sCounters.extension.uPawnPushByQuarterPly[1],
+ ctx->sCounters.extension.uPawnPushByQuarterPly[2],
+ ctx->sCounters.extension.uPawnPushByQuarterPly[3],
+ ctx->sCounters.extension.uPawnPushByQuarterPly[4],
ctx->sCounters.extension.uQExtendChaseNodes,
ctx->sCounters.tree.u64QNodeCount,
(ctx->sCounters.tree.u64QNodeCount ?
diff --git a/src/search.c b/src/search.c
index 4e89a2c..573f78c 100644
--- a/src/search.c
+++ b/src/search.c
@@ -189,7 +189,7 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
ULONG uFutilityMargin = 0;
FLAG fAnyMoveEFPPruned = FALSE;
FLAG fThisMoveEFPPruned = FALSE;
- SCORE iCheckSee;
+ SCORE iMoveSee;
#ifdef DEBUG
ASSERT(IS_VALID_SCORE(iAlpha));
ASSERT(IS_VALID_SCORE(iBeta));
@@ -745,14 +745,22 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
#endif
// SEE must be computed on the PRE-move position -- see.c's
- // exchange walk needs the piece still sitting on cFrom. Only
- // needed for checking moves, where ComputeMoveExtension uses it
- // to gate the check extension on soundness (Crafty-style: don't
- // extend a checking move that's really just a losing sacrifice).
- iCheckSee = 0;
- if (IS_CHECKING_MOVE(mv))
+ // exchange walk needs the piece still sitting on cFrom.
+ // ComputeMoveExtension uses it for two soundness gates: the
+ // check extension (Crafty-style: don't extend a checking move
+ // that's really just a losing sacrifice) and the recapture
+ // extension (don't extend a "recapture" that actually loses
+ // material). uDepth <= THREE_PLY mirrors the recapture
+ // extension's own depth gate -- cheap to test here since
+ // uDepth is already loop-invariant, and it keeps this from
+ // computing a SEE on every single non-pawn capture at every
+ // depth. One call per move regardless of which gate(s) end up
+ // wanting it -- SEE is too hot a routine to pay for twice.
+ iMoveSee = 0;
+ if (IS_CHECKING_MOVE(mv) ||
+ (mv.pCaptured && !IS_PAWN(mv.pCaptured) && (uDepth <= THREE_PLY)))
{
- iCheckSee = GetCheckSee(ctx,
+ iMoveSee = GetMoveSee(ctx,
mv,
(uStage == TRY_GENERATED_MOVES) ?
(x - 1) : (ULONG)-1);
@@ -773,7 +781,8 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
(x - 1), // Note: x==0 if doing mvHash
iEval,
uDepth,
- iCheckSee,
+ iMoveSee,
+ fIsLeftoverMove,
&iExtend);
// Note: MAX_EXTEND_PER_LINE (a flat, non-depth-relative cap on
@@ -786,8 +795,9 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
// Decide how much (if any) to reduce this move's depth --
// graded LMR.
if ((uDepth > TWO_PLY) &&
- (NODE_PV != pi->eNodeType) &&
+ (pi->eNodeType != NODE_PV) &&
(uLegalMoves > 5) &&
+ (fIsLeftoverMove) &&
(0 == iExtend) &&
(!IS_ESCAPING_CHECK(mv)) &&
(!IS_CAPTURE_OR_PROMOTION(mv)) &&
@@ -1570,7 +1580,7 @@ _ShouldWeConsiderThisMove(IN SEARCHER_THREAD_CONTEXT *ctx,
// a real (winning/even) iMoveValue gets the same value-plus-
// flat-bonus treatment as any other capture; a move with no
// usable value (SEE already called it losing) falls back to
- // GetCheckSee's real tactical judgment, exactly like the
+ // GetMoveSee's real tactical judgment, exactly like the
// quiet-check (VALUE_BISHOP) branch already does below.
if (IS_CHECKING_MOVE(mv) && (TRUE == fGeneratedChecks))
{
@@ -1590,7 +1600,7 @@ _ShouldWeConsiderThisMove(IN SEARCHER_THREAD_CONTEXT *ctx,
return(fConsider);
}
{
- FLAG fConsider = (GetCheckSee(ctx, mv, uMoveNum) >= 0);
+ FLAG fConsider = (GetMoveSee(ctx, mv, uMoveNum) >= 0);
#ifdef CALIBRATE_QSEARCH_FUTILITY
if (FALSE == fConsider)
{
@@ -1640,7 +1650,7 @@ _ShouldWeConsiderThisMove(IN SEARCHER_THREAD_CONTEXT *ctx,
return(TRUE);
}
{
- FLAG fConsider = (GetCheckSee(ctx, mv, uMoveNum) >= 0);
+ FLAG fConsider = (GetMoveSee(ctx, mv, uMoveNum) >= 0);
#ifdef CALIBRATE_QSEARCH_FUTILITY
if (FALSE == fConsider)
{
diff --git a/src/searchsup.c b/src/searchsup.c
index 32e1be8..7ab8695 100644
--- a/src/searchsup.c
+++ b/src/searchsup.c
@@ -437,17 +437,25 @@ ComputeMoveScore(IN SEARCHER_THREAD_CONTEXT *ctx,
SCORE
-GetCheckSee(IN SEARCHER_THREAD_CONTEXT *ctx,
+GetMoveSee(IN SEARCHER_THREAD_CONTEXT *ctx,
IN MOVE mv,
IN ULONG uMoveNum)
/**
Routine description:
- SEE for a checking move, used by ComputeMoveExtension's callers
- (Search, HelpSearch, and eventually QSearch) to decide how much to
- extend/reduce a check. Must be computed on the PRE-move position --
- see.c's exchange walk needs the piece still sitting on cFrom.
+ SEE for a checking move or a non-pawn capture, used by
+ ComputeMoveExtension's callers (Search, HelpSearch, and eventually
+ QSearch) to decide how much to extend/reduce a check, and (as of
+ the recapture-extension soundness gate) to judge a recapture too.
+ One call site, one SEE per move regardless of how many of
+ ComputeMoveExtension's gates end up wanting it -- SEE is one of
+ the hottest routines in the tree, so callers compute this at most
+ once per move and pass the single result into ComputeMoveExtension
+ via iMoveSee, rather than each gate calling it independently. Must
+ be computed on the PRE-move position -- see.c's exchange walk needs
+ the piece still sitting on cFrom, and by the time
+ ComputeMoveExtension runs the move has already been played.
Move generation/scoring (_ScoreAllMoves, _ScoreAllEscapes in
generate.c) already runs a real SEE on this same (pos, mv) pair
@@ -460,7 +468,7 @@ Routine description:
Parameters:
SEARCHER_THREAD_CONTEXT *ctx : searcher thread context
- MOVE mv : the checking move
+ MOVE mv : the checking move or capture needing a soundness read
ULONG uMoveNum : mv's index in ctx->sMoveStack.mvf[], or (ULONG)-1
if mv did not come from the move stack (e.g. the hash move)
@@ -472,7 +480,7 @@ Return value:
{
BITV bvFlags;
- ASSERT(IS_CHECKING_MOVE(mv));
+ ASSERT(IS_CHECKING_MOVE(mv) || (mv.pCaptured != NULL));
if (uMoveNum != (ULONG)-1)
{
ASSERT(uMoveNum < MAX_MOVE_STACK);
@@ -493,7 +501,8 @@ void ComputeMoveExtension(IN SEARCHER_THREAD_CONTEXT *ctx,
IN ULONG uMoveNum,
IN SCORE iRoughEval,
IN ULONG uDepth,
- IN SCORE iCheckSee,
+ IN SCORE iMoveSee,
+ IN FLAG fIsLeftoverMove,
IN OUT INT *piExtend)
/**
@@ -561,36 +570,65 @@ Return value:
{
ASSERT(InCheck(pos, pos->uToMove));
INC(ctx->sCounters.extension.uCheck);
- if (iCheckSee < 0)
+ if (iMoveSee < 0)
{
*piExtend += QUARTER_PLY;
- goto enforce_ceiling;
+ INC(ctx->sCounters.extension.uCheckByQuarterPly[QUARTER_PLY / QUARTER_PLY]);
}
else
{
- *piExtend += THREE_QUARTERS_PLY;
if (pos->uNonPawnMaterial[FLIP(pos->uToMove)] >
(VALUE_KING + VALUE_BISHOP))
{
- goto enforce_ceiling;
+ *piExtend += THREE_QUARTERS_PLY;
+ INC(ctx->sCounters.extension.uCheckByQuarterPly[THREE_QUARTERS_PLY / QUARTER_PLY]);
+ }
+ else
+ {
+ *piExtend += QUARTER_PLY;
+ INC(ctx->sCounters.extension.uCheckByQuarterPly[QUARTER_PLY / QUARTER_PLY]);
}
- *piExtend -= HALF_PLY;
}
}
- // Pawn push extension
- if (IS_PAWN(mv.pMoved))
+ // Pawn push extension. RANK7/RANK2 already mean "one step from
+ // promotion", so the pawn is a passer by definition -- no separate
+ // passed-pawn test needed. Two cheap, eval-independent refinements
+ // (both read only incrementally-maintained POSITION bitboards, no
+ // Eval()/pawn-hash dependency, no sig-check needed):
+ //
+ // 1. Skip entirely if the promotion square itself is already
+ // occupied (friend or foe) -- a push that can just be captured
+ // or that's blocked from ever queening isn't worth the ply.
+ // 2. The size of the bonus depends on whether the pawn's own
+ // king/rook/queen actually defends it on cTo, not just
+ // whether the king happens to be nearby (a king 2 squares
+ // away that can't recapture doesn't help this pawn at all).
+ if (IS_PAWN(mv.pMoved) && (RANK7(mv.cTo) || RANK2(mv.cTo)))
{
- if (((GET_COLOR(mv.pMoved) == WHITE) && RANK7(mv.cTo)) ||
- ((GET_COLOR(mv.pMoved) == BLACK) && RANK2(mv.cTo)))
+ ULONG uPawnColor = GET_COLOR(mv.pMoved);
+ COOR cPromo = mv.cTo + ((uPawnColor == WHITE) ? 0x10 : -0x10);
+
+ if (!(pos->bbOccupied & COOR_TO_BB(cPromo)))
{
- *piExtend += THREE_QUARTERS_PLY;
- if (DISTANCE(pos->cNonPawns[GET_COLOR(mv.pMoved)][0], mv.cTo) < 3)
+ *piExtend += HALF_PLY;
+ if (!fIsLeftoverMove ||
+ ((g_KingAttacksBB[mv.cTo] &
+ COOR_TO_BB(pos->cNonPawns[uPawnColor][0])) ||
+ (_RookAttacksBB(mv.cTo, pos->bbOccupied) &
+ (pos->bbPieces[uPawnColor][ROOK] |
+ pos->bbPieces[uPawnColor][QUEEN])) ||
+ (_BishopAttacksBB(mv.cTo, pos->bbOccupied) &
+ pos->bbPieces[uPawnColor][QUEEN])))
{
*piExtend += QUARTER_PLY;
+ INC(ctx->sCounters.extension.uPawnPushByQuarterPly[THREE_QUARTERS_PLY / QUARTER_PLY]);
+ }
+ else
+ {
+ INC(ctx->sCounters.extension.uPawnPushByQuarterPly[HALF_PLY / QUARTER_PLY]);
}
INC(ctx->sCounters.extension.uPawnPush);
- goto enforce_ceiling;
}
}
@@ -600,28 +638,27 @@ Return value:
ASSERT(IS_ESCAPING_CHECK(mv));
iMoveScore = iRoughEval + ComputeMoveScore(ctx, mv, uMoveNum);
- // One legal move in reply to check... threshold doubled
- // 2026-09-06, same stopgap reasoning as search.c's qsearch
- // check-extension gate (board_representation/EVAL.md section
- // 9) -- was ">1", stale against CountKingSafetyDefects' old
- // CHECK_VECTOR-based scale.
+ // This is forced and cheap (1 move at the parent node).
if (ONE_LEGAL_MOVE(ctx, ctx->uPly - 1) &&
CountKingSafetyDefects(&ctx->sPosition, uColor) > 2)
{
- *piExtend += (QUARTER_PLY +
- HALF_PLY *
- (iMoveScore + VALUE_KNIGHT > iAlpha));
+ FLAG fBigBonus = (iMoveScore + VALUE_KNIGHT > iAlpha);
+ *piExtend += (QUARTER_PLY + HALF_PLY * fBigBonus);
INC(ctx->sCounters.extension.uOneLegalMove);
+ INC(ctx->sCounters.extension.uOneLegalMoveByQuarterPly[
+ fBigBonus ?
+ (THREE_QUARTERS_PLY / QUARTER_PLY) :
+ (QUARTER_PLY / QUARTER_PLY)]);
}
- // Singular response to check...
- if ((mv.pCaptured) &&
- (iRoughEval + 400 < iAlpha) &&
- (iMoveScore + 75 > iAlpha))
+ // One "good" way to response to check? Kinda dodgy but "works".
+ if (!fIsLeftoverMove &&
+ mv.pCaptured &&
+ (iRoughEval + 300 < iAlpha) &&
+ (iMoveScore + 50 > iAlpha))
{
*piExtend += ONE_PLY;
INC(ctx->sCounters.extension.uSingularReplyToCheck);
- goto enforce_ceiling;
}
}
@@ -643,30 +680,34 @@ Return value:
}
}
- // Recapture extension
+ // Recapture extension. Own tighter (1x) window restored --
+ // measured on ECM.721 (ringers): sharing the outer 2x window
+ // grew this position's recapture-fire count 6.8x vs HEAD
+ // (17,569,874 -> 34,337,674 total nodes), far more than the
+ // ~2x growth every other extension category saw from stacking
+ // alone -- all in refuted alternative lines, not the winning
+ // PV (which has zero recaptures on it). The 2x window did
+ // recover ECM.735/ECM.443 by making trade-dense *correct*
+ // lines eligible, but it's a net loss on positions where the
+ // expensive trades are all in the lines being ruled out
+ // instead. Back to the tighter window as the better tradeoff.
if ((iRoughEval >= (g_iRootScore[uColor] - _window[ctx->uPly])) &&
- (iRoughEval <= (g_iRootScore[uColor] + _window[ctx->uPly])))
+ (iRoughEval <= (g_iRootScore[uColor] + _window[ctx->uPly])) &&
+ (uDepth < THREE_PLY))
{
- if (uDepth <= THREE_PLY)
+ if (!fIsLeftoverMove &&
+ mv.pCaptured &&
+ !IS_PAWN(mv.pCaptured) &&
+ (iMoveSee >= 0) &&
+ ((PIECE_VALUE(mv.pCaptured) == PIECE_VALUE(mvLast.pCaptured)) ||
+ ((mvLast.pPromoted) && (mv.cTo == mvLast.cTo))))
{
- if ((mv.pCaptured) && !IS_PAWN(mv.pCaptured) &&
- ((PIECE_VALUE(mv.pCaptured) ==
- PIECE_VALUE(mvLast.pCaptured)) ||
- ((mvLast.pPromoted) && (mv.cTo == mvLast.cTo))))
- {
- iMoveScore = ComputeMoveScore(ctx, mv, uMoveNum);
- if (iMoveScore >= 0)
- {
- *piExtend += HALF_PLY;
- INC(ctx->sCounters.extension.uRecapture);
- }
- }
+ *piExtend += HALF_PLY;
+ INC(ctx->sCounters.extension.uRecapture);
}
}
}
- enforce_ceiling:
-
// Don't let extensions add up to > 1 ply
#ifdef DEBUG
iM = MIN(ONE_PLY, *piExtend);
@@ -675,13 +716,29 @@ Return value:
*piExtend = MINU(ONE_PLY, *piExtend);
ASSERT(iM == *piExtend);
- // Scale back extensions that are very far from the root:
+ // Scale back extensions that are very far from the root. Table
+ // built in root.c from g_uIterateDepth (see its own construction
+ // there for the exact arithmetic -- this is just a summary, keep
+ // the two in sync):
+ //
+ // ┌─────────────────────────┬───────────┬────────────────────────────────┐
+ // │ ply range (× iterate │ reduction │ net effect │
+ // │ depth) │ │ │
+ // ├─────────────────────────┼───────────┼────────────────────────────────┤
+ // │ u < 1.5× │ 0 │ full extension kept │
+ // ├─────────────────────────┼───────────┼────────────────────────────────┤
+ // │ 1.5× ≤ u < 1.875× │ -1/4 ply │ │
+ // ├─────────────────────────┼───────────┼────────────────────────────────┤
+ // │ 1.875× ≤ u < 2.25× │ -1/2 ply │ │
+ // ├─────────────────────────┼───────────┼────────────────────────────────┤
+ // │ 2.25× ≤ u < 3× │ -3/4 ply │ only a +1 ply extension │
+ // │ │ │ can survive here │
+ // ├─────────────────────────┼───────────┼────────────────────────────────┤
+ // │ u ≥ 3× (hard limit) │ -all ply │ no extensions beyond here │
+ // └─────────────────────────┴───────────┴────────────────────────────────┘
+ //
+ // NOTE: check extension in qsearch is handled differently, see search.c.
//
- // Iterate g_Soft g_Hard
- // | | | | | | | | |
- // 0 1 2 3/2 3 4 (x Iterate)
- // |-------------------|----|----|---------|----...
- // |full full full full|-1/4|-1/2|-3/4 -3/4|none...
ASSERT(ctx->uPly != 0);
ASSERT(ctx->uPly <= MAX_PLY_PER_SEARCH);
*piExtend -= g_uExtensionReduction[ctx->uPly-1];
diff --git a/src/split.c b/src/split.c
index 3a69ab4..679d26d 100644
--- a/src/split.c
+++ b/src/split.c
@@ -1081,7 +1081,7 @@ Return value:
INT iOrigExtend;
INT iExtend;
MOVE mv;
- SCORE iCheckSee;
+ SCORE iMoveSee;
#ifdef DEBUG
POSITION board;
@@ -1120,10 +1120,11 @@ Return value:
ASSERT(iExtend <= +ONE_PLY);
// SEE on the PRE-move position -- see search.c's identical comment.
- iCheckSee = 0;
- if (IS_CHECKING_MOVE(mv))
+ iMoveSee = 0;
+ if (IS_CHECKING_MOVE(mv) ||
+ (mv.pCaptured && !IS_PAWN(mv.pCaptured) && (uDepth <= THREE_PLY)))
{
- iCheckSee = GetCheckSee(ctx,
+ iMoveSee = GetMoveSee(ctx,
mv,
ctx->sMoveStack.uBegin[ctx->uPly] +
uMoveNum);
@@ -1134,14 +1135,22 @@ Return value:
ASSERT((IS_CHECKING_MOVE(mv) && InCheck(&ctx->sPosition, ctx->sPosition.uToMove)) ||
(!IS_CHECKING_MOVE(mv) && !InCheck(&ctx->sPosition, ctx->sPosition.uToMove)));
- // Compute extension
+ // Compute extension. fIsLeftoverMove is unconditionally TRUE
+ // here: search.c only ever starts a split when the move it
+ // was about to try was itself already classified as a
+ // leftover move (see its own fIsLeftoverMove-gated split
+ // condition) -- every move HelpSearch pulls from the shared
+ // pool for that split is drawn from that same leftover
+ // range, so there's no "early, well-ordered move" case to
+ // distinguish here.
ComputeMoveExtension(ctx,
iAlpha,
iBeta,
(ctx->sMoveStack.uBegin[ctx->uPly - 1] + uMoveNum),
iEval,
uDepth,
- iCheckSee,
+ iMoveSee,
+ TRUE,
&iExtend);
//