summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-10-05 13:05:34 -0700
committerScott Gasch <[email protected]>2026-10-05 13:05:34 -0700
commitbb7cdcda4c8c30ad0ed71d0f60427daf05977b24 (patch)
tree99a5a3f9fa6edcced73d966df0af07274ff67962 /src
parent5d9a5cf4036d07252e81d51e78e6dd36e65c9275 (diff)
New telemetry.
Diffstat (limited to 'src')
-rwxr-xr-xsrc/chess.h24
-rw-r--r--src/root.c44
-rw-r--r--src/search.c21
-rw-r--r--src/searchsup.c13
4 files changed, 96 insertions, 6 deletions
diff --git a/src/chess.h b/src/chess.h
index 420e313..b9cba3d 100755
--- a/src/chess.h
+++ b/src/chess.h
@@ -1014,6 +1014,14 @@ MOVE_STACK;
// which would have been a search-behavior change rather than a fix.
#define JOINT_EXTRA_TIER_COUNT (6)
+// Null-move R-factor breakdown (post-move dump, root.c) -- bucketed by
+// SelectNullmoveRFactor's whole-ply R value (searchsup.c). R is
+// currently TWO_PLY (sparse material) or THREE_PLY + depth/6 (Crafty-
+// style growth), so real traffic never gets near this bound; sized
+// with headroom and clamped at the top bucket rather than tied exactly
+// to a formula that might grow later.
+#define NULL_R_BUCKETS (12)
+
// 2026-10-03: PRUNE_VERIFY (GNUmakefile) -- shadow verification for the
// DEEP PRUNE gate (ShouldLatePrune), answering a question the LMR-side
// JOINT_EXTRA_VERIFY cannot: "this move was about to be skipped
@@ -1241,6 +1249,16 @@ typedef struct _COUNTERS
// leftover pool near the leaves. Counts every move this was
// applied to.
UINT64 u64LMPPruned;
+ // 2026-10-05: extended futility pruning (EFP, search.c) -- same
+ // shape as u64LMPPruned above, for the other overt-skip (not
+ // reduction) mechanism in the move loop.
+ UINT64 u64EFPPruned;
+ // 2026-10-05: null-move R-factor breakdown -- which whole-ply R
+ // SelectNullmoveRFactor chose, and how often the null-move
+ // search at that R actually failed high. Indexed by R/ONE_PLY,
+ // clamped to NULL_R_BUCKETS - 1.
+ UINT64 u64NullMoveTriesByR[NULL_R_BUCKETS];
+ UINT64 u64NullMoveSuccessByR[NULL_R_BUCKETS];
#ifdef PRUNE_VERIFY
// [predicted-FH band][shipped depth bucket] -- see
// PRUNE_VERIFY_BANDS above. Tries = moves the deep-prune gate
@@ -1985,6 +2003,9 @@ typedef struct _GAME_OPTIONS
UINT64 u64JointExtraTries[JOINT_EXTRA_TIER_COUNT];
UINT64 u64JointExtraResearch[JOINT_EXTRA_TIER_COUNT];
UINT64 u64LMPPruned;
+ UINT64 u64EFPPruned;
+ UINT64 u64NullMoveTriesByR[NULL_R_BUCKETS];
+ UINT64 u64NullMoveSuccessByR[NULL_R_BUCKETS];
#ifdef PRUNE_VERIFY
UINT64 u64PruneVerifyTries[PRUNE_VERIFY_BANDS][SHIP_DEPTH_BUCKETS];
UINT64 u64PruneVerifyFailHigh[PRUNE_VERIFY_BANDS][SHIP_DEPTH_BUCKETS];
@@ -3735,7 +3756,8 @@ TryNullmovePruning(IN OUT SEARCHER_THREAD_CONTEXT *ctx,
IN SCORE iBeta,
IN ULONG uNullDepth,
IN OUT INT *piOrigExtend,
- OUT SCORE *piNullScore);
+ OUT SCORE *piNullScore,
+ IN ULONG uNullRBucket);
void
UpdatePV(SEARCHER_THREAD_CONTEXT *ctx, MOVE mv);
diff --git a/src/root.c b/src/root.c
index bc6e3be..f9949a9 100644
--- a/src/root.c
+++ b/src/root.c
@@ -469,6 +469,45 @@ Return value:
Trace("Null move cutoff rate: %5.3f percent.\n",
((n / d) * 100.0));
#ifdef PERF_COUNTERS
+ Trace("Overt move skips: LMP=%llu EFP=%llu moves.\n",
+ ctx->sCounters.tree.u64LMPPruned,
+ ctx->sCounters.tree.u64EFPPruned);
+ {
+ ULONG uR;
+ UINT64 u64TotalTries = 0;
+ for (uR = 0; uR < NULL_R_BUCKETS; uR++)
+ {
+ u64TotalTries += ctx->sCounters.tree.u64NullMoveTriesByR[uR];
+ }
+ u64TotalTries += (0 == u64TotalTries); // avoid /0 if never tried
+ Trace("Null move (R): tries/successes, successes%% of ALL null-move "
+ "tries (sums to the overall cutoff rate above):\n");
+ for (uR = 0; uR < NULL_R_BUCKETS; uR++)
+ {
+ UINT64 u64Tries = ctx->sCounters.tree.u64NullMoveTriesByR[uR];
+ if (0 == u64Tries) continue;
+ Trace(" %u/%" COMPILER_LONGLONG_UNSIGNED_FORMAT
+ "/%" COMPILER_LONGLONG_UNSIGNED_FORMAT
+ "(%.1f%%)",
+ uR, u64Tries,
+ ctx->sCounters.tree.u64NullMoveSuccessByR[uR],
+ 100.0 * (double)ctx->sCounters.tree.u64NullMoveSuccessByR[uR] /
+ (double)u64TotalTries);
+ }
+ Trace("\n");
+ }
+ {
+ ULONG uTier;
+ Trace("LMR reductions (plies):");
+ for (uTier = 0; uTier < JOINT_EXTRA_TIER_COUNT; uTier++)
+ {
+ UINT64 u64Tries = ctx->sCounters.tree.u64JointExtraTries[uTier];
+ if (0 == u64Tries) continue;
+ Trace(" %u/%" COMPILER_LONGLONG_UNSIGNED_FORMAT,
+ uTier + 1, u64Tries);
+ }
+ Trace("\n");
+ }
// 2026-10-03: percentages added, each against the count of nodes of
// THAT TYPE (u64NodeTypeCount, incremented in CommonSearchInit),
// not against total nodes -- dividing by total nodes understates
@@ -1629,6 +1668,11 @@ Return value:
memcpy(g_Options.u64JointExtraResearch, ctx->sCounters.tree.u64JointExtraResearch,
sizeof(g_Options.u64JointExtraResearch));
g_Options.u64LMPPruned = ctx->sCounters.tree.u64LMPPruned;
+ g_Options.u64EFPPruned = ctx->sCounters.tree.u64EFPPruned;
+ memcpy(g_Options.u64NullMoveTriesByR, ctx->sCounters.tree.u64NullMoveTriesByR,
+ sizeof(g_Options.u64NullMoveTriesByR));
+ memcpy(g_Options.u64NullMoveSuccessByR, ctx->sCounters.tree.u64NullMoveSuccessByR,
+ sizeof(g_Options.u64NullMoveSuccessByR));
#ifdef PRUNE_VERIFY
memcpy(g_Options.u64PruneVerifyTries, ctx->sCounters.tree.u64PruneVerifyTries,
sizeof(g_Options.u64PruneVerifyTries));
diff --git a/src/search.c b/src/search.c
index ee055a4..ccaae74 100644
--- a/src/search.c
+++ b/src/search.c
@@ -458,6 +458,7 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
INT iPositionalExtend = 0;
INT iExtend;
ULONG uNextDepth;
+ ULONG uNullR;
ULONG uLegalMoves = 0;
FLAG fIsLeftoverMove = FALSE;
// Stamped once below, right after this node's own Eval() call and
@@ -631,7 +632,8 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
(!IS_CHECKING_MOVE(mvLast) && (FALSE == pi->fInCheck)));
// Prepare next depth for nullmove and hashtable lookup
- uNextDepth = uDepth - SelectNullmoveRFactor(ctx, uDepth) - ONE_PLY;
+ uNullR = SelectNullmoveRFactor(ctx, uDepth);
+ uNextDepth = uDepth - uNullR - ONE_PLY;
if (uNextDepth > MAX_DEPTH_PER_SEARCH) uNextDepth = 0;
// Check the transposition table. This may give us a cutoff
@@ -862,13 +864,22 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
iImprovement,
uNextDepth))
{
+ ULONG uNullRBucket = uNullR / ONE_PLY;
+ if (uNullRBucket >= NULL_R_BUCKETS)
+ {
+ uNullRBucket = NULL_R_BUCKETS - 1;
+ }
+#ifdef PERF_COUNTERS
+ INC(ctx->sCounters.tree.u64NullMoveTriesByR[uNullRBucket]);
+#endif
if (TryNullmovePruning(ctx,
&fThreat,
iAlpha,
iBeta,
uNextDepth,
&iPositionalExtend,
- &iScore))
+ &iScore,
+ uNullRBucket))
{
if (iScore > iBeta)
{
@@ -1407,6 +1418,12 @@ Search(IN SEARCHER_THREAD_CONTEXT *ctx,
#endif
}
}
+#ifdef PERF_COUNTERS
+ if (TRUE == fThisMoveEFPPruned)
+ {
+ INC(ctx->sCounters.tree.u64EFPPruned);
+ }
+#endif
// If neither LMP nor EFP fired, we are going to search below
// this move. But if the move is very late, we might reduce
diff --git a/src/searchsup.c b/src/searchsup.c
index 2bbf1ef..9e11631 100644
--- a/src/searchsup.c
+++ b/src/searchsup.c
@@ -999,8 +999,11 @@ Parameters:
Return value:
FLAG : TRUE iff mv should be pruned outright (skipped, not merely
- reduced) -- see search.c's call site comment for why that's
- currently audit-only, not wired to actually skip the move.
+ reduced). Live, not audit-only: search.c's move loop actually
+ `continue`s past mv when this returns TRUE (the same final check
+ that consumes fThisMoveEFPPruned), after INCrementing
+ u64LMPPruned for the script-summary "late-move pruning (overt
+ skip, not reduction)" line under PERF_COUNTERS.
**/
{
@@ -2306,7 +2309,8 @@ TryNullmovePruning(SEARCHER_THREAD_CONTEXT *ctx,
SCORE iBeta,
ULONG uNullDepth,
INT *piOrigExtend,
- SCORE *piNullScore)
+ SCORE *piNullScore,
+ ULONG uNullRBucket)
{
static INT _bm_by_piece[7] = {
-1, -1, QUARTER_PLY, QUARTER_PLY, HALF_PLY, HALF_PLY, -1
@@ -2367,6 +2371,9 @@ TryNullmovePruning(SEARCHER_THREAD_CONTEXT *ctx,
// Nullmove succeeded and either no need to verify or verify
// succeeded too.
INC(ctx->sCounters.tree.u64NullMoveSuccess);
+#ifdef PERF_COUNTERS
+ INC(ctx->sCounters.tree.u64NullMoveSuccessByR[uNullRBucket]);
+#endif
if (*piNullScore > +NMATE) *piNullScore = +NMATE;
return TRUE;
}