summaryrefslogtreecommitdiff
path: root/src/searchsup.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-08-28 13:19:05 -0700
committerScott Gasch <[email protected]>2026-08-28 13:19:05 -0700
commit7e762b293d19b9c438a287f9d123e77ee3a5056d (patch)
tree6b3c8ab3d8109a6ef023796d1639b080fd2e2edd /src/searchsup.c
parent5dd4ef29dde8872bb4280328113e6f8b9254f5e8 (diff)
Replace SEARCH_SORT_LIMIT's pure move-count cutoff with a quality-based
one: always fully select "high performer" moves regardless of count, only apply the per-ply budget to leftover ordinary moves. The old gate (uLegalMoves < SEARCH_SORT_LIMIT(ply)) stopped selecting carefully after a fixed count, counting the hash move too -- so at ply 6+ (limit 5), a position with a hash move already used one of only 5 total slots before the cutoff hit. It had no way to tell "a handful of mediocre quiet moves" from "a hash move plus three winning captures and two killers" -- in the latter case, a real high-performer beyond the 4th/5th slot would get treated identically to a random leftover quiet move, even though generate.c had already tagged it as excellent. Checked what three real engines do here: Crafty always fully sorts the hash move, then MVV/LVA-ordered captures, then up to 4 killers -- its own cheap fallback (a move-count cutoff, gated by remaining depth) only ever applies to what's left after all of that, i.e. plain untested quiet moves. Stockfish uses a value threshold, not a position/count threshold, so a good move is never orphaned by where it happens to sit in the list, only by its own assessed quality. Berserk never gates at all -- full selection sort unconditionally, every node. New design: keep fully selecting for as long as every move found so far is >= GOOD_MOVE (a generate.c ordering-encoding constant that already sits, by construction, below every killer tier and SORT_THESE_FIRST's winning/even-capture range, and above ordinary quiet moves and losing captures -- a real quality floor already baked into the existing encoding, not a new one). The first selection that reveals a move below that floor marks the transition to "the rest of the team"; from there, SEARCH_SORT_LIMIT's existing table is reused (as an explicitly untuned starting point -- its old numbers were calibrated, if at all, against a different question: total selection budget from move 1, not a leftover-only budget) to decide how many more full selections are worth the cost before taking the remainder in place. On an IID-rescored ply, GOOD_MOVE is meaningless (iValue is a real eval-axis score there, not generate.c's encoding), so that ply type keeps its existing unconditional full-select behavior unchanged. Measured (ecm_ringers.ep_/ecm_confident_quick.ep_/ecm_hard_quick.ep_, sn=5M) against the prior baseline (10/88/9): 11/87/12, net +3 solves. EBF: unchanged on ringers, worse on confident_quick (the one suite that also lost a solve -- consistent single-suite regression, not a systemic pattern), better on hard_quick (paired with its solve gain). Not yet a fully validated result -- SEARCH_SORT_LIMIT's numbers (17/12/9/7/6/5) now need their own recalibration pass under this new "leftover budget" meaning, since whatever they were tuned against before doesn't apply to this role. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01YGSMkwjqiCk4XhbfN7ugD2
Diffstat (limited to 'src/searchsup.c')
-rw-r--r--src/searchsup.c4
1 files changed, 2 insertions, 2 deletions
diff --git a/src/searchsup.c b/src/searchsup.c
index 6f0d5f9..b532d9a 100644
--- a/src/searchsup.c
+++ b/src/searchsup.c
@@ -216,7 +216,7 @@ Return value:
ASSERT(mv.uMove);
ASSERT((uMoveNum > 0) || (uLegalMoves == 0));
if ((uRemainingDepth >= TWO_PLY) &&
- (iBeta == (iAlpha + 1)) &&
+ (FALSE == ctx->sPlyInfo[ctx->uPly - 1].fPvNode) &&
(uLegalMoves > 5) &&
(0 == iExtend) &&
(!IS_ESCAPING_CHECK(mv)) &&
@@ -1113,7 +1113,7 @@ WeShouldTryNullmovePruning(SEARCHER_THREAD_CONTEXT *ctx,
(pos->uNonPawnCount[pos->uToMove][0] > 2) &&
(FALSE == pi->fInCheck) &&
(iBeta != +INFINITY) &&
- (iBeta == iAlpha + 1)) // <--- TODO: test this one please...
+ (FALSE == pi->fPvNode)) // <--- TODO: test this one please...
{
if (uNullDepth <= 6 * ONE_PLY)
{