diff options
| author | Scott Gasch <[email protected]> | 2026-09-25 08:44:25 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-09-25 08:44:25 -0700 |
| commit | 9b28080f8b3dca4588cb2aaaf748451f45e3c626 (patch) | |
| tree | 54e0aff5d6ee1520c590c7c04a5ae2f8299e7497 /src/script.c | |
| parent | 66740dc89c7351c120c25788d0079055034f4942 (diff) | |
Two ideas:
1. Nullmove R=3 + depth/6 along with some ShouldWeDoNullmoveSearch
tweaking and calibrating.
2. Probe EGTB everywhere in the search as long as the table/position
is resident in memory.
Diffstat (limited to 'src/script.c')
| -rw-r--r-- | src/script.c | 137 |
1 files changed, 137 insertions, 0 deletions
diff --git a/src/script.c b/src/script.c index 532a0d5..f18ab7b 100644 --- a/src/script.c +++ b/src/script.c @@ -64,6 +64,24 @@ typedef struct _SUITE_COUNTERS UINT64 u64TotalLeftoverFH; UINT64 u64TotalCMEvidenceTries[CM_EVIDENCE_BUCKETS]; UINT64 u64TotalCMEvidenceFH[CM_EVIDENCE_BUCKETS]; +#ifdef TEST_NULL + // Null-move precision/recall calibration, summed across every + // position in the suite -- see _NullCalibBucketIndex's comment in + // search.c. Accumulated the same way as u64TotalCMEvidenceTries + // above (per-search counters are copied into g_Options at the end + // of each Think() call, then summed in here by _ProcessSuiteLine), + // and printed once by _PrintSuiteReport at true end-of-suite -- + // deliberately *not* dumped after every position, which would + // balloon the logfile (confirmed: 191MB after ~28K of 200K + // positions the first time this was tried that way). + UINT64 u64TotalNCAttempts[NC_TOTAL_BUCKETS]; + UINT64 u64TotalNCNullFH[NC_TOTAL_BUCKETS]; + UINT64 u64TotalNCRealFH[NC_TOTAL_BUCKETS]; + UINT64 u64TotalNCBothFH[NC_TOTAL_BUCKETS]; + UINT64 u64TotalNCNullProbeNodeSum[NC_TOTAL_BUCKETS]; + UINT64 u64TotalNCFirstMoveNodeSum[NC_TOTAL_BUCKETS]; + UINT64 u64TotalNCRealLoopNodeSum[NC_TOTAL_BUCKETS]; +#endif double dSigmaEBF; // sum of per-problem nodes^(1/depth) ULONG uEBFCount; // number of problems with depth > 0 double dAverageTimeToSolution; @@ -391,6 +409,28 @@ _ProcessSuiteLine(char *szLine, POSITION *pos) g_Options.u64CMEvidenceFH[v]; } } +#ifdef TEST_NULL + { + ULONG v; + for (v = 0; v < NC_TOTAL_BUCKETS; v++) + { + g_SuiteCounters.u64TotalNCAttempts[v] += + g_Options.u64NCAttempts[v]; + g_SuiteCounters.u64TotalNCNullFH[v] += + g_Options.u64NCNullFH[v]; + g_SuiteCounters.u64TotalNCRealFH[v] += + g_Options.u64NCRealFH[v]; + g_SuiteCounters.u64TotalNCBothFH[v] += + g_Options.u64NCBothFH[v]; + g_SuiteCounters.u64TotalNCNullProbeNodeSum[v] += + g_Options.u64NCNullProbeNodeSum[v]; + g_SuiteCounters.u64TotalNCFirstMoveNodeSum[v] += + g_Options.u64NCFirstMoveNodeSum[v]; + g_SuiteCounters.u64TotalNCRealLoopNodeSum[v] += + g_Options.u64NCRealLoopNodeSum[v]; + } + } +#endif } else { @@ -473,6 +513,103 @@ _PrintSuiteReport(double dSuiteStart) } } +#ifdef TEST_NULL + // Null-move precision/recall calibration, summed across the whole + // suite (see _NullCalibBucketIndex's comment in search.c for the + // encoding and what each dimension means). Decode each bucket + // index back into its component bands for a readable line; skip + // empty cells so this stays legible even though the full table has + // NC_TOTAL_BUCKETS cells. + { + static const char *rgszDepthBand[NC_DEPTH_BUCKETS] = + { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "11", "12", "13", "14", "15", "16+" }; + static const char *rgszDeficitBand[NC_DEFICIT_BUCKETS] = + { "<-300", "-300..-100", "-100..0", "0..100", "100..300", "300+" }; + static const char *rgszMaterialBand[NC_MATERIAL_BUCKETS] = + { "0-1", "2", "3-5", "6+" }; + ULONG uIdx, uDecode, uNullDepthB, uOuterDepthB, uDeficitB; + ULONG uMaxMaterialB, uMoverMaterialB; + UINT64 u64Attempts, u64NullFH, u64RealFH, u64BothFH; + UINT64 u64NullProbeNodeSum, u64FirstMoveNodeSum, u64RealLoopNodeSum; + double dAvgNullProbeNodes, dAvgFirstMoveNodes, dAvgRealLoopNodes; + double dNullSuccessRate, dEVFirstMoveOnly, dEV; + + Trace("\nNull-move calibration (attempts / nullFH / realFH / both / " + "precision / recall / nullSuccessRate / avgNullProbeNodes / " + "avgFirstMoveNodes / avgRealLoopNodes / EVFirstMoveOnly / " + "EV-per-attempt-in-nodes):\n"); + for (uIdx = 0; uIdx < NC_TOTAL_BUCKETS; uIdx++) + { + u64Attempts = g_SuiteCounters.u64TotalNCAttempts[uIdx]; + if (0 == u64Attempts) continue; + u64NullFH = g_SuiteCounters.u64TotalNCNullFH[uIdx]; + u64RealFH = g_SuiteCounters.u64TotalNCRealFH[uIdx]; + u64BothFH = g_SuiteCounters.u64TotalNCBothFH[uIdx]; + u64NullProbeNodeSum = g_SuiteCounters.u64TotalNCNullProbeNodeSum[uIdx]; + u64FirstMoveNodeSum = g_SuiteCounters.u64TotalNCFirstMoveNodeSum[uIdx]; + u64RealLoopNodeSum = g_SuiteCounters.u64TotalNCRealLoopNodeSum[uIdx]; + + // Decode uIdx (innermost/fastest-varying dimension first), + // matching _NullCalibBucketIndex's encoding in search.c. + // Use a scratch copy -- uIdx itself is still the array + // index for the lookups above/below. + uDecode = uIdx; + uMoverMaterialB = uDecode % NC_MATERIAL_BUCKETS; + uDecode /= NC_MATERIAL_BUCKETS; + uMaxMaterialB = uDecode % NC_MATERIAL_BUCKETS; + uDecode /= NC_MATERIAL_BUCKETS; + uDeficitB = uDecode % NC_DEFICIT_BUCKETS; + uDecode /= NC_DEFICIT_BUCKETS; + uOuterDepthB = uDecode % NC_DEPTH_BUCKETS; + uDecode /= NC_DEPTH_BUCKETS; + uNullDepthB = uDecode % NC_DEPTH_BUCKETS; + + dAvgNullProbeNodes = (double)u64NullProbeNodeSum / (double)u64Attempts; + dAvgFirstMoveNodes = (double)u64FirstMoveNodeSum / (double)u64Attempts; + dAvgRealLoopNodes = (double)u64RealLoopNodeSum / (double)u64Attempts; + dNullSuccessRate = (double)u64NullFH / (double)u64Attempts; + // EV of TRYING null vs. going straight to the real move loop, + // in nodes saved per attempt (positive = trying pays off): + // on the dNullSuccessRate fraction of attempts we pay the + // (cheap) null probe instead of the (expensive) real-loop + // cost; on the rest we pay the null probe *in addition* to + // still needing the real loop. dEV uses avgRealLoopNodes -- + // the true total cost of however the real search actually + // resolved the node (one move, several, or production's own + // separate null success) -- while dEVFirstMoveOnly keeps the + // cheaper move-1-only proxy alongside it for comparison. + // Both assume (per the user's simplifying assumption, + // matched by this dataset's ~99.96% overall precision) that + // a null-move fail-high is correct. + dEVFirstMoveOnly = + dNullSuccessRate * (dAvgFirstMoveNodes - dAvgNullProbeNodes) + - (1.0 - dNullSuccessRate) * dAvgNullProbeNodes; + dEV = dNullSuccessRate * (dAvgRealLoopNodes - dAvgNullProbeNodes) + - (1.0 - dNullSuccessRate) * dAvgNullProbeNodes; + + Trace(" nullDepth=%s outerDepth=%s deficit=%s " + "maxMaterial=%s moverMaterial=%s : " + "%"COMPILER_LONGLONG_UNSIGNED_FORMAT" / " + "%"COMPILER_LONGLONG_UNSIGNED_FORMAT" / " + "%"COMPILER_LONGLONG_UNSIGNED_FORMAT" / " + "%"COMPILER_LONGLONG_UNSIGNED_FORMAT" / " + "%5.3f / %5.3f / %5.3f / %10.1f / %10.1f / %10.1f / %10.1f / %10.1f\n", + rgszDepthBand[uNullDepthB], + rgszDepthBand[uOuterDepthB], + rgszDeficitBand[uDeficitB], + rgszMaterialBand[uMaxMaterialB], + rgszMaterialBand[uMoverMaterialB], + u64Attempts, u64NullFH, u64RealFH, u64BothFH, + (u64NullFH != 0) ? (100.0 * (double)u64BothFH / (double)u64NullFH) : 0.0, + (u64RealFH != 0) ? (100.0 * (double)u64BothFH / (double)u64RealFH) : 0.0, + 100.0 * dNullSuccessRate, + dAvgNullProbeNodes, dAvgFirstMoveNodes, dAvgRealLoopNodes, + dEVFirstMoveOnly, dEV); + } + } +#endif + // Histogram stuff if (g_SuiteCounters.uTotal > 0) { uMax = g_SuiteCounters.uHistogram[0]; |
