summaryrefslogtreecommitdiff
path: root/src/search.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-10-07 08:17:51 -0700
committerScott Gasch <[email protected]>2026-10-07 08:17:51 -0700
commitd14dbe2a50c220b2adac758c3d4f1a1fa67ece0b (patch)
treecbf6d9928bcc41ef833b62b70479113f7d419925 /src/search.c
parent447130db3df87b40d080dc26429d45e9213c19f7 (diff)
Speed up eval/move-gen/SEE; remove EvalLookForDanger, overhaul _EvalKing
SPRT-confirmed improvement (1814 games, st 4.0): candidate nps=1498118 vs head=1378448, avg depth 14.30 vs 13.87, ELO=+24.2+/-16.0, LOWER95=0.5118, LLR=+3.126 crossing the H1 bound. Most of the diff is non-semantic speed work across eval.c, generate.c, movesup.c, see.c, probe.c and friends. Two real semantic changes: removed EvalLookForDanger and overhauled _EvalKing's king-safety scoring. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/search.c')
-rw-r--r--src/search.c37
1 files changed, 31 insertions, 6 deletions
diff --git a/src/search.c b/src/search.c
index d8ece8d..fcf8907 100644
--- a/src/search.c
+++ b/src/search.c
@@ -725,13 +725,19 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
case UNRECOGNIZED:
break;
case RECOGN_EXACT:
- case RECOGN_EGTB:
if ((iAlpha < iScore) && (iScore < iBeta))
{
UpdatePV(ctx, RECOGNMOVE);
}
iBestScore = iScore;
goto end;
+ case RECOGN_EGTB:
+ if ((iAlpha < iScore) && (iScore < iBeta))
+ {
+ UpdatePV(ctx, EGTBMOVE);
+ }
+ iBestScore = iScore;
+ goto end;
case RECOGN_LOWER:
if (iScore >= iBeta)
{
@@ -1073,14 +1079,18 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
// leftover (matches pre-retirement behavior).
if (MOVE_SCORING_IID_RESCORED == pi->eMoveScoringScheme)
{
- SelectBestNoHistory(ctx, x);
+ SelectBestNoHistory(ctx, x); // mv's score is search-based
+ // WouldGiveCheck already computed in RescoreMovesViaSearch
+ mv = ctx->sMoveStack.mvf[x].mv;
}
else
{
SelectBestWithHistory(ctx, x);
+ mv = ctx->sMoveStack.mvf[x].mv;
+ mv.bvFlags |= WouldGiveCheck(ctx, mv);
+ ctx->sMoveStack.mvf[x].mv = mv; // re-store WouldGiveCheck's result
fIsLeftoverMove = (ctx->sMoveStack.mvf[x].iValue < GOOD_MOVE);
}
- mv = ctx->sMoveStack.mvf[x].mv;
#ifdef DEBUG
ASSERT(0 == (ctx->sMoveStack.mvf[x].bvFlags &
MVF_MOVE_SEARCHED));
@@ -1107,7 +1117,6 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
uCMEvidenceBucket = _CMEvidenceBucket(uEvidence);
}
#endif
- mv.bvFlags |= WouldGiveCheck(ctx, mv);
// Note: x is the index of the NEXT move to be
// considered, this move's index is (x-1).
@@ -3021,13 +3030,19 @@ QSearch(IN SEARCHER_THREAD_CONTEXT *ctx,
case UNRECOGNIZED:
break;
case RECOGN_EXACT:
- case RECOGN_EGTB:
if ((iAlpha < iScore) && (iScore < iBeta))
{
UpdatePV(ctx, RECOGNMOVE);
}
iBestScore = iScore;
goto end;
+ case RECOGN_EGTB:
+ if ((iAlpha < iScore) && (iScore < iBeta))
+ {
+ UpdatePV(ctx, EGTBMOVE);
+ }
+ iBestScore = iScore;
+ goto end;
case RECOGN_LOWER:
if (iScore >= iBeta)
{
@@ -3238,7 +3253,17 @@ QSearch(IN SEARCHER_THREAD_CONTEXT *ctx,
// If fIncludeChecks is FALSE then we still need to see if
// this move is going to check the opponent; GenerateMoves
// didn't do it for us to save time in the event of a fail
- // high.
+ // high. Tried swapping this for a post-MakeMove InCheck call
+ // instead (the theory: at most one query, nothing to amortize
+ // WouldGiveCheck's table-build against) -- measured worse on
+ // 2 of 3 curated suites (ringers +7.2%, confident_quick +5.9%,
+ // hard_quick -1.2%, sd 10, node counts identical either way).
+ // The assumption was wrong: this loop tries more than one move
+ // per node whenever an earlier candidate raises alpha without
+ // failing high, and the lazy-built bbUnblocked[uPly] (see
+ // WouldGiveCheck) gets reused cheaply across all of those,
+ // while a fresh InCheck call pays full price every time with
+ // no reuse at all. Reverted -- WouldGiveCheck stays here.
if (FALSE == fIncludeChecks)
{
mv.bvFlags |= WouldGiveCheck(ctx, mv);