summaryrefslogtreecommitdiff
path: root/src/search.c
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-08-29 19:26:49 -0700
committerScott Gasch <[email protected]>2026-08-29 19:26:49 -0700
commit9632634b648109765955e89ec07f788388d8170f (patch)
tree7854105dd63ae6eb95f776700048e824f9650575 /src/search.c
parent4b99e893a2b423247fd20ab69c0346fcbe6bca81 (diff)
Fix batch-mode exit code, killer-table backfill collision, impossible QSearch mate-magnitude asserts, MATEMOVE PV display, --command truncation, and test.sh's stale egtbpath.
Several small, independent correctness fixes bundled together since they were all exercised together through today's precommit_check.sh and curated-suite runs: - command.c: batch-mode's "Exhausted input" exit was exit(-1), which truncates to 255 (an 8-bit status) and is indistinguishable from a real crash's nonzero exit. Changed to exit(0) so debug_smoke_test.sh can reliably tell a clean batch run apart from a crash by exit status alone. - dynamic.c: _NewKillerMove's slot[1] backfill from mvNullmoveQuietRefutations[uPly] had no check that the backfilled move differed from the move just placed in slot[0]. When they coincided, both slots held the identical move, silently wasting a killer slot in release builds (ASSERT is a no-op there) and tripping _NewKillerMove's own IS_SAME_MOVE invariant in DEBUG builds. Fixed by skipping the backfill on collision. - search.c: removed two ASSERT(iBestScore > -NMATE) calls in QSearch that encoded an invariant that isn't actually guaranteed -- at an early full-width root iteration, or after aspiration-window widening following repeated fail-highs, an ancestor frame's iAlpha/iBeta can itself already be more extreme than -NMATE with no mate anywhere in the line, so a legitimate fail-low placeholder or fail-high score can land in mate-magnitude territory purely as a window artifact. hash.c's storage path already treats any value <= -NMATE as a sound upper bound regardless of origin, so this was a false invariant, not a caught bug. Also: minor whitespace cleanup, an added ASSERT documenting the futility-margin depth precondition it replaced a redundant runtime check for, and PV/leaf-count bookkeeping on the mate/draw-at-root leaf paths that was previously skipped. - util.c: MATEMOVE sentinel moves weren't handled in PV-to-string conversion, so a PV ending in a detected mate would either display garbage or hit the same-move assert. Added an explicit "<#>" marker. - test.sh: --egtbpath pointed at a nonexistent /egtb/three;/egtb/four; /egtb/five; corrected to /zscratch/egtb, this box's actual EGTB location. - main.c/input.c: --command's initial-command buffer (g_szInitialCommand) was a fixed 256-byte array; strncpy(..., SMALL_STRING_LEN_CHAR - 2) silently truncated any longer --command string, and -- worse -- when the source was long enough not to fit, strncpy doesn't null-terminate the destination, so the immediately-following strcat(..., "\r\n") could read/write past the buffer. Long move-replay command strings used during this session's debugging hit the truncation directly (a ~600 char move list silently cut off mid-token, desyncing the input queue). Changed g_szInitialCommand to a heap allocation sized to the actual input length instead of a fixed cap. - CLAUDE.md: documents the above (this file's own diff is prior session's writeup of these same fixes, committed now alongside the code). All exercised together via precommit_check.sh (self-test suite + DEBUG smoke test against random ecm.ep_ samples) and the sd10/sn5m curated suite sweep run for the eval.c hand-tuning commit just before this one. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01M9ZDiJhiUajUxh95mTXCFJ
Diffstat (limited to 'src/search.c')
-rwxr-xr-xsrc/search.c26
1 files changed, 17 insertions, 9 deletions
diff --git a/src/search.c b/src/search.c
index 61fa428..ab7d122 100755
--- a/src/search.c
+++ b/src/search.c
@@ -358,7 +358,7 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
if (iScore > iBeta) {
StoreLowerBound(mvHash, pos, iScore, uDepth, FALSE);
}
- iBestScore = iScore; // TODO: try just beta here
+ iBestScore = iScore;
goto end;
}
}
@@ -516,8 +516,8 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
(ctx->uPly >= 2) &&
(ctx->sPlyInfo[ctx->uPly - 2].iExtensionAmount <= 0))
{
- if ((uDepth > THREE_QUARTERS_PLY) &&
- (uDepth <= ONE_PLY + THREE_QUARTERS_PLY) &&
+ ASSERT(uDepth >= THREE_QUARTERS_PLY);
+ if ((uDepth <= ONE_PLY + THREE_QUARTERS_PLY) &&
(iRoughEval + VALUE_KNIGHT <= iAlpha))
{
uFutilityMargin = (iAlpha - iRoughEval) / 2;
@@ -762,9 +762,9 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
(uFutilityMargin) &&
(TRUE == fIsLeftoverMove) &&
(iExtend <= 0) &&
- (!IS_ESCAPING_CHECK(mv)) &&
+ (!IS_ESCAPING_CHECK(mv)) &&
(!IS_CAPTURE_OR_PROMOTION(mv)) &&
- (!IS_CHECKING_MOVE(mv)) &&
+ (!IS_CHECKING_MOVE(mv)) &&
(!fThreat) &&
(ComputeMoveScore(ctx, mv, (x - 1)) < uFutilityMargin))
{
@@ -843,7 +843,7 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
iBestScore = iScore;
mvBest = mv;
pi->mvBest = mv;
-
+
if (iScore > iAlpha)
{
if (iScore >= iBeta)
@@ -919,6 +919,11 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
ASSERT(IS_CHECKING_MOVE(mvLast));
ASSERT(InCheck(pos, pos->uToMove));
iBestScore = MATED_SCORE(ctx->uPly);
+ if ((iAlpha < iBestScore) && (iBestScore < iBeta))
+ {
+ INC(ctx->sCounters.tree.u64LeafCount);
+ UpdatePV(ctx, MATEMOVE);
+ }
ASSERT(iBestScore <= -NMATE);
goto end;
}
@@ -927,6 +932,7 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
iBestScore = 0;
if ((iAlpha < iBestScore) && (iBestScore < iBeta))
{
+ INC(ctx->sCounters.tree.u64LeafCount);
UpdatePV(ctx, DRAWMOVE);
}
goto end;
@@ -1460,7 +1466,6 @@ QSearch(IN SEARCHER_THREAD_CONTEXT *ctx,
// If we see a move whose value is zero, the rest of the moves
// in this ply can be tossed.
ASSERT(SanityCheckMoves(ctx, x, VERIFY_BEFORE | VERIFY_AFTER));
- ASSERT(iBestScore > -NMATE);
goto end;
}
mv = ctx->sMoveStack.mvf[x].mv;
@@ -1522,7 +1527,6 @@ QSearch(IN SEARCHER_THREAD_CONTEXT *ctx,
mv.cTo);
}
KEEP_TRACK_OF_FIRST_MOVE_FHs(uLegalMoves == 1);
- ASSERT(iBestScore > -NMATE);
ASSERT(SanityCheckMoves(ctx, x, VERIFY_BEFORE));
goto end;
}
@@ -1546,7 +1550,6 @@ QSearch(IN SEARCHER_THREAD_CONTEXT *ctx,
}
}
}
- ASSERT(iBestScore > -NMATE);
ASSERT(SanityCheckMoves(ctx, x, VERIFY_BEFORE));
end:
@@ -1554,5 +1557,10 @@ QSearch(IN SEARCHER_THREAD_CONTEXT *ctx,
ASSERT(PositionsAreEquivalent(pos, &pi->sPosition));
ASSERT(IS_VALID_SCORE(iBestScore) || WE_SHOULD_STOP_SEARCHING);
DTLeaveNode(ctx, TRUE, iBestScore, pi->mvBest);
+
+ // Note: iBestScore can be +INFINITY or -INFINITY here even in the
+ // absence of a legitimate mate detected if we disallowed stand
+ // pat due to perceived danger early on, when the a..b window had
+ // an extreme bound. This is "legitimate" but weird.
return(iBestScore);
}