diff options
| author | Scott Gasch <[email protected]> | 2026-10-13 11:12:35 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-10-13 11:12:35 -0700 |
| commit | 7400001fc2d43a507b54281f9860c60177860fe5 (patch) | |
| tree | 0ab8e3d1bd5fe54cc7f2883e38da67c3bfac36d8 /src/main.c | |
| parent | 78881b8e6422b7980e5f71780ae4bed862b93e72 (diff) | |
Add 5D FH% joint table (improve axis) + beta-side static-null-move
table, wire up static null-move pruning; misc searchsup.c tuning;
--cpus=4 fix; multi-pool match_play.py openings.
data.c: g_uJointFHThousandths grows a 5th "improve" axis
([12][11][11][15] -> [12][11][11][16][11]); new
g_uBetaFHThousandths[12][21][11][11] table added. chess.h/searchsup.c/
search.c/split.c: minimal consumer wiring for the improve axis
(ImproveBucket(), extended _JointTablePredictedFHThousandths
signature/callers).
search.c: new static (score-only, no verification search) null-move
cutoff ahead of the existing real null-move check -- when iEval > iBeta
by enough that the new beta-side table predicts a real null-move
search would fail high with >95% confidence, take the cutoff on the
table's say-so instead of paying for the verification search.
searchsup.c: EstimateHangingMaterialCp now gives half credit when the
mover has an unrelated piece already flagged trapped at node entry;
bumped the SEE>=0 check extension from 3/4 ply to a full ply when the
opponent has significant material left; removed the "entering the
endgame extension" bonus (recapture extension and its window check
untouched).
main.c: accept "--opt=value" in addition to "--opt value" on the
command line (a bare "--cpus=4" used to silently fail to match and
leave uNumProcessors at its default of 1); also fixes a latent
off-by-one that could read one past the end of argv for a value-taking
flag given as the last command-line argument.
unix.c: brace-scoping cleanup around signal handler setup, no behavior
change.
eval_tune/match_play.py, test_vs_head.sh: sample opening positions from
multiple PGN pools (twic_filtered.pgn + lichess*.pgn) instead of one,
for more opening diversity in match-play testing.
Brought out of a larger stashed changeset one piece at a time, each
verified independently against a --cpus 1 / --cpus 4 ratio benchmark
to rule out the MP scaling regression that changeset originally
triggered (see SCOTT_IS_A_MORON.md) -- no regression reproduced in any
of the pieces here. Playing-strength impact measured as neutral via
self-play (170 games, llr=-0.04, score=0.500) despite ~1 ply deeper
average search. The stashed changeset's EFP margin rewrite, LMP
threshold rewrite, and a disabled null-move-skip experiment are
deliberately excluded -- those need their own careful, separate
commits and testing. The stash's eval.c retuning is abandoned (measured
~even/worse over a ~700-game overnight self-play run).
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/main.c')
| -rw-r--r-- | src/main.c | 95 |
1 files changed, 67 insertions, 28 deletions
@@ -306,80 +306,119 @@ Return value: i = 1; while(i < argc) { + // Accept both "--opt value" (two argv tokens) and "--opt=value" + // (one token, '=' separated) -- a user typing --cpus=4 used to + // silently fail STRCMPI's exact-name match against "--cpus", + // fall through to the "unknown argument" branch, and leave + // uNumProcessors at its default of 1 with no indication why. + // Split any "--name=value" token into pszArg/pszInlineVal here + // so every branch below can keep comparing/consuming exactly + // as before; only the value-source (same token vs. next argv + // slot) changes. + char szArgNameBuf[SMALL_STRING_LEN_CHAR]; + char *pszArg = argv[i]; + char *pszInlineVal = NULL; + char *pEquals = strchr(argv[i], '='); + + if ((NULL != pEquals) && ('-' == argv[i][0])) + { + size_t uNameLen = (size_t)(pEquals - argv[i]); + if (uNameLen < sizeof(szArgNameBuf)) + { + strncpy(szArgNameBuf, argv[i], uNameLen); + szArgNameBuf[uNameLen] = '\0'; + pszArg = szArgNameBuf; + pszInlineVal = pEquals + 1; + } + } + // The "value" argument for a flag that takes one: either the + // inline "=value" just split off above, or the next argv + // token -- and whether that next token is even present. Must + // check argc > i+1, not argc > i -- the while loop's own + // condition already guarantees i < argc, so "argc > i" was + // always true and let the two-token branches below read + // argv[i+1] one past the end of argv when a value-taking flag + // was the last argument on the command line. + FLAG fHaveVal = (NULL != pszInlineVal) || (argc > i+1); + char *pszVal = (NULL != pszInlineVal) ? pszInlineVal : + ((argc > i+1) ? argv[i+1] : NULL); + // Only the two-token form consumes an extra argv slot. + FLAG fConsumedNextArg = (NULL == pszInlineVal); + #ifdef MP - if ((!STRCMPI(argv[i], "--cpus")) && (argc > i)) + if ((!STRCMPI(pszArg, "--cpus")) && fHaveVal) { - g_Options.uNumProcessors = (ULONG)atoi(argv[i+1]); + g_Options.uNumProcessors = (ULONG)atoi(pszVal); if ((g_Options.uNumProcessors == 0) || (g_Options.uNumProcessors > 64)) { g_Options.uNumProcessors = 2; } - i++; + if (fConsumedNextArg) i++; } else #endif - if ((!STRCMPI(argv[i], "--command")) && (argc > i)) + if ((!STRCMPI(pszArg, "--command")) && fHaveVal) { - ULONG uLen = (ULONG)strlen(argv[i+1]); + ULONG uLen = (ULONG)strlen(pszVal); g_szInitialCommand = (CHAR *)SystemAllocateMemory(uLen + 3); - strcpy(g_szInitialCommand, argv[i+1]); + strcpy(g_szInitialCommand, pszVal); strcat(g_szInitialCommand, "\r\n"); - i++; + if (fConsumedNextArg) i++; } - else if ((!STRCMPI(argv[i], "--hash")) && (argc > i)) + else if ((!STRCMPI(pszArg, "--hash")) && fHaveVal) { g_Options.uNumHashTableEntries = - _ParseHashOption(argv[i+1], + _ParseHashOption(pszVal, sizeof(HASH_ENTRY)); - i++; + if (fConsumedNextArg) i++; } - else if ((!STRCMPI(argv[i], "--egtbpath")) && (argc > i)) + else if ((!STRCMPI(pszArg, "--egtbpath")) && fHaveVal) { - if (!strcmp(argv[i+1], "-")) { + if (!strcmp(pszVal, "-")) { g_Options.szEGTBPath[0] = '\0'; } else { - strncpy(g_Options.szEGTBPath, argv[i+1], SMALL_STRING_LEN_CHAR); + strncpy(g_Options.szEGTBPath, pszVal, SMALL_STRING_LEN_CHAR); } - i++; + if (fConsumedNextArg) i++; } - else if (!STRCMPI(argv[i], "--book") && argc > i) + else if (!STRCMPI(pszArg, "--book") && fHaveVal) { - if (!strcmp(argv[i+1], "-")) { + if (!strcmp(pszVal, "-")) { g_Options.szBookName[0] = '\0'; } else { - strncpy(g_Options.szBookName, argv[i+1], SMALL_STRING_LEN_CHAR); + strncpy(g_Options.szBookName, pszVal, SMALL_STRING_LEN_CHAR); } - i++; + if (fConsumedNextArg) i++; } - else if (!STRCMPI(argv[i], "--batch")) + else if (!STRCMPI(pszArg, "--batch")) { g_Options.fNoInputThread = TRUE; } - else if ((!STRCMPI(argv[i], "--dnafile")) && (argc > i)) + else if ((!STRCMPI(pszArg, "--dnafile")) && fHaveVal) { - if (!ReadEvalDNA(argv[i+1])) + if (!ReadEvalDNA(pszVal)) { UtilPanic(INITIALIZATION_FAILURE, (void *)"Bad DNA file", - (void *)&(argv[i+1]), + (void *)&(pszVal), (void *)"read", NULL, __FILE__, __LINE__); } else { - Trace("Loaded dna file \"%s\"\n", argv[i+1]); + Trace("Loaded dna file \"%s\"\n", pszVal); } - i++; + if (fConsumedNextArg) i++; } - else if ((!STRCMPI(argv[i], "--logfile")) && (argc > i)) + else if ((!STRCMPI(pszArg, "--logfile")) && fHaveVal) { - strncpy(g_Options.szLogfile, argv[i+1], + strncpy(g_Options.szLogfile, pszVal, SMALL_STRING_LEN_CHAR); - i++; + if (fConsumedNextArg) i++; } - else if (!STRCMPI(argv[i], "--help")) { + else if (!STRCMPI(pszArg, "--help")) { Trace("Usage: %s [--batch] [--command arg] [--logfile arg] [--egtbpath arg]\n" " [--dnafile arg] [--cpus arg] [--hash arg]\n\n" " --batch : operate the engine without an input thread\n" |
