summaryrefslogtreecommitdiff
path: root/src/chess.h
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2026-10-23 13:19:17 -0700
committerScott Gasch <[email protected]>2026-10-23 13:19:17 -0700
commit69525eb2f42ea9cca8255f6cc3b85e6b1312fd05 (patch)
treed78420b6c45d584a3f09b4dcbaf4f1edb36e8d5d /src/chess.h
parent6b883e83935f5e8110788565cfcb77cd6fbf8950 (diff)
NNUE search integration: FH% tables, NODE_TYPE refinement, TREEDUMP rewrite
- data.c/chess.h: widen the joint FH% table's late axis 16->17, dedicating the new bucket 0 to a whole-node ("will something here fail high at all") sample rather than sharing it with the first-leftover-move curve. Add an NNUE-eval counterpart table (g_uJointFHThousandths_NNUE) and an NNUE counterpart of the beta-side table, selected at runtime based on whether NNUE eval is active. - search.c/searchsup.c: add _RefineNodeTypeGuess, which uses the FH% tables (via g_NodeTypeGuessTable) to refine a node's NODE_TYPE guess instead of the old heuristic; thread uParentMoveFHEstimate through PLY_INFO so a node can see its parent's prediction for the move that led to it. - generate.c: initialize sKillers[2]/[3].uBonus unconditionally (an uninitialized-stack read caught by valgrind under LTO; harmless today but a real bug). - dumptree.c/chess.h/GNUmakefile: replace the long-dead DUMP_TREE XML tree dump (written for a GUI this project no longer has) with TREEDUMP, a flat, path-keyed move/result trace meant to diff against an identically-instrumented Stockfish build -- see docs/TREEDUMP_FORMAT.md. - docs/DEEPER_PRUNING.md: notes on closing the search-efficiency gap vs Stockfish. - crafty_egtb/stockfish_xboard/typhoon_nnue: wrapper scripts so match_play.py's Engine class can drive Crafty, Stockfish (via PolyGlot), and an NNUE-enabled typhoon the same way it drives HCE typhoon. - tests/lop: rename lop.txt -> lop.ep_; update compare_lop.cpp. - annotate_pgns: add aggregate_marginsafety.py, dedup_pgn.py, fit_lazy_probe.py; update annotate.py/run_nnue_pipeline.py for the NNUE training pipeline. - .gitignore: ignore compile_commands.json (clangd build DB) and eval.nnue (a symlink into the untracked head_reference/ comparison baseline), neither of which belongs in the repo. Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/chess.h')
-rwxr-xr-xsrc/chess.h156
1 files changed, 130 insertions, 26 deletions
diff --git a/src/chess.h b/src/chess.h
index 8c5d86c..189e0a4 100755
--- a/src/chess.h
+++ b/src/chess.h
@@ -1537,6 +1537,40 @@ typedef enum
}
NODE_TYPE;
+// Sentinels for PLY_INFO.uParentMoveFHEstimate (below). A node's
+// PARENT stashes, before recursing into it, its own prediction of
+// "how likely is this specific move to fail high" -- which is
+// identically this node's own predicted FAIL-LOW probability (for mv
+// to fail high at the parent, every move here, including mv's
+// continuation, must resolve to a fail-low). Two cases produce no
+// real number:
+// - MOVE_FH_ESTIMATE_NONLEFTOVER: mv was NOT a leftover move (hash
+// move, good capture, killer, etc.) -- the dynamic-ordering/joint
+// tables are only ever consulted for leftover moves
+// (searchsup.c's SearchOneMove), so there is no table lookup to
+// report. This is NOT "no information": trying a move specifically
+// BECAUSE it's expected to be strong is itself evidence the parent
+// expects a HIGHER fail-high chance than even the best leftover
+// move would get, so this sentinel should bucket as a
+// high-confidence band, not as "unknown".
+// - MOVE_FH_ESTIMATE_NO_DATA: mv WAS a leftover move and the table
+// was consulted, but the cell was below the sample floor (same
+// value as searchsup.c's file-local JOINT_TABLE_SENTINEL -- kept
+// numerically identical on purpose, see that macro's own comment).
+// This case really is "no information".
+#define MOVE_FH_ESTIMATE_NONLEFTOVER (0xFFFFFFFE)
+#define MOVE_FH_ESTIMATE_NO_DATA (0xFFFF)
+
+// Band count for FHThousandthsToBand (searchsup.c) and
+// g_NodeTypeGuessTable (data.c) -- see both for the banding scheme
+// and how the table was baked.
+#define NODE_TYPE_FH_BANDS (7)
+
+ULONG
+FHThousandthsToBand(IN ULONG uFHThousandths);
+
+extern const NODE_TYPE g_NodeTypeGuessTable[2][NODE_TYPE_FH_BANDS][NODE_TYPE_FH_BANDS];
+
// ----------------------------------------------------------------------
//
// Searcher thread record
@@ -1704,6 +1738,19 @@ typedef struct _PLY_INFO
FLAG fInQsearch;
NODE_TYPE eNodeType; // classic PVS node typing --
// see the enum's own comment
+
+ // 2026-10-22: the PARENT's own predicted FH% for the move that
+ // led to this node (identically this node's predicted FAIL-LOW
+ // probability -- see the sentinel macros' comment above). Written
+ // by searchsup.c's SearchOneMove right after MakeMove succeeds,
+ // i.e. for THIS ply, by the ply ABOVE, before recursing in; read
+ // back from here once this node's own Search()/CommonSearchInit
+ // runs. One slot per ply, same convention as every other
+ // per-ply-indexed field in this struct (iExtensionAmount, etc) --
+ // a child overwrites it on its own next move, so it must be read
+ // before this ply recurses again itself.
+ ULONG uParentMoveFHEstimate;
+
MOVE_SCORING_SCHEME eMoveScoringScheme; // which convention scored
// sMoveStack[uPly] -- see
// the enum's own comment
@@ -3694,9 +3741,32 @@ extern BITBOARD g_KingAttacksBB[128];
// (thousandths of a percent), 0xFFFF sentinel = below this run's
// 500-try sample floor, don't trust yet. [depth][dumb][gap][late],
// depth outermost for cache locality -- a whole node's move loop shares
-// one depth-slice (11*11*15 = 1,815 cells, ~3.5KB). Not yet consumed by
+// one depth-slice (11*11*17 = 2,057 cells, ~4KB). Not yet consumed by
// search.c -- phase 2 (LMR replacement) is a separate, later step.
-extern USHORT g_uJointFHThousandths[12][11][11][16][11];
+//
+// 2026-10-21: late axis widened 16 -> 17. Shipped late bucket 0 is now
+// dedicated SOLELY to the whole-node sample (raw late == 0, "will
+// something at this node fail high" -- no specific move), not shared
+// with the real first-leftover-move curve the way the old 16-bucket
+// shape coincidentally did. See searchsup.c's _ShipLateBucket and
+// dual_table_run_nnue/README.md's "whole-node alpha sample" section.
+// g_uJointFHThousandths (HCE) carries a PLACEHOLDER in that bucket,
+// duplicated from g_uJointFHThousandths_NNUE's real measurement, since
+// this table's own collection build predates the whole-node
+// instrumentation -- see data.c's header comment on this array, and
+// dual_table_run/README.md, for how/when that gets replaced with a
+// real HCE-side measurement.
+extern USHORT g_uJointFHThousandths[12][11][11][17][11];
+
+// 2026-10-21: NNUE-eval counterpart of g_uJointFHThousandths above --
+// same shape, collected against the NNUE evaluator instead of HCE
+// (see ../dual_table_run_nnue/). Both tables are compiled in; which
+// one a lookup reads is chosen at runtime by
+// _JointTablePredictedFHThousandths (searchsup.c) based on
+// (g_fUseNnueEval && g_fNnueWeightsLoaded) -- NOT a build-time
+// #ifdef, since NNUE is itself already a runtime switch in this
+// binary (see eval.c, command.c's "nnue on"/"nnue off").
+extern USHORT g_uJointFHThousandths_NNUE[12][11][11][17][11];
// 2026-10-10: beta-side (static-null-move) 4D FH% table -- see data.c's
// header. [depth][gapBeta][ownImprove][enemyImprove]. Cell stores the
@@ -3704,6 +3774,12 @@ extern USHORT g_uJointFHThousandths[12][11][11][16][11];
// itself -- see _BetaTablePredictedFHThousandths's comment for why.
extern USHORT g_uBetaFHThousandths[12][21][11][11];
+// 2026-10-21: NNUE-eval counterpart of g_uBetaFHThousandths above --
+// same shape/encoding, collected against the NNUE evaluator. Runtime-
+// selected the same way as g_uJointFHThousandths_NNUE above (see
+// _BetaTablePredictedFHThousandths, searchsup.c).
+extern USHORT g_uBetaFHThousandths_NNUE[12][21][11][11];
+
// 2026-09-15: king-ring bitboard split for _EvalKing's linear-SCORE
// rewrite -- front/back mirror EmptyAttackedSquare's old color-dependent
// asymmetry and eval_tune/train_king_safety_model.py's BUCKET_SLOTS
@@ -4329,6 +4405,15 @@ _BetaTablePredictedFHThousandths(IN ULONG uRawDepthBucket,
IN ULONG uOwnImproveBucket,
IN ULONG uEnemyImproveBucket);
+ULONG
+_RefineNodeTypeGuess(IN SEARCHER_THREAD_CONTEXT *ctx,
+ IN SCORE iEval,
+ IN SCORE iAlpha,
+ IN SCORE iBeta,
+ IN ULONG uDepth,
+ IN SCORE iEnemyImprovement,
+ IN ULONG uRawImproveB);
+
FLAG
WeShouldTryNullmovePruning(IN SEARCHER_THREAD_CONTEXT *ctx,
IN SCORE iAlpha,
@@ -5407,45 +5492,64 @@ void
CleanupEGTB(void);
//
-// dumptree.c
+// dumptree.c: TREEDUMP -- flat, line-oriented, path-keyed node/move
+// trace of the search tree, meant to be diffed against an
+// identically-instrumented Stockfish build on the same FEN -- see
+// TREEDUMP_FORMAT.md. Build-time-only ("gmake TREEDUMP=1 ..."),
+// always-on-when-built. There is no runtime toggle/filter -- keep a
+// dump small by using a shallow "sd N"/"go depth N", not by
+// restricting what gets written.
//
-#ifdef DUMP_TREE
+// Replaces a 2004-era, long-unused XML tree dump written for a Windows
+// GUI (typhoonui.exe) that this project no longer has; that mechanism
+// (DUMP_TREE, InitializeTreeDump/CleanupTreeDump/DTEnterNode/
+// DTLeaveNode/DTTrace) has been removed outright rather than kept
+// alongside this one, on the assumption that a mechanism nothing has
+// exercised in two decades is broken.
+//
+// TDMove logs one decision about a move already made at the CURRENT
+// (child) ply -- i.e. call it AFTER MakeMove and BEFORE UnmakeMove, so
+// the path builder (which walks ctx->sPlyInfo[0..ctx->uPly-1].mv) picks
+// up this move as the path's last element for free. TDResult logs the
+// node's own final outcome and must be called at the node's OWN ply
+// (e.g. search.c's "end:"), before any further move has been made.
+#ifdef TREEDUMP
void
-InitializeTreeDump(void);
+TDOpen(void);
void
-CleanupTreeDump(void);
+TDClose(void);
void
-DTEnterNode(SEARCHER_THREAD_CONTEXT *ctx,
- ULONG uDepth,
- FLAG fIsQNode,
- SCORE iAlpha,
- SCORE iBeta);
+TDMove(SEARCHER_THREAD_CONTEXT *ctx,
+ ULONG uDepth,
+ SCORE iAlpha,
+ SCORE iBeta,
+ CHAR *szKind,
+ CHAR *szAct,
+ double dReducedPlies,
+ double dExtendedPlies,
+ SCORE iScore,
+ FLAG fSkipped);
void
-DTLeaveNode(SEARCHER_THREAD_CONTEXT *ctx,
- FLAG fQNode,
- SCORE iBestScore,
- MOVE mvBestMove);
-
-void CDECL
-DTTrace(ULONG uPly, CHAR *szMessage, ...);
-
-#define DTTRACE(...) DTTrace(ctx->uPly, __VA_ARGS__)
+TDResult(SEARCHER_THREAD_CONTEXT *ctx,
+ ULONG uDepth,
+ SCORE iAlpha,
+ SCORE iBeta,
+ SCORE iEval,
+ SCORE iBestScore);
#else
-#define DTTRACE(...)
-
-#define DTEnterNode(...)
+#define TDOpen(...)
-#define DTLeaveNode(...)
+#define TDClose(...)
-#define InitializeTreeDump(...)
+#define TDMove(...)
-#define CleanupTreeDump(...)
+#define TDResult(...)
#endif