diff options
| author | Scott Gasch <[email protected]> | 2026-09-15 13:31:36 -0700 |
|---|---|---|
| committer | Scott Gasch <[email protected]> | 2026-09-15 13:31:36 -0700 |
| commit | 390e675849ff42c8b7e1806f75552de988741fff (patch) | |
| tree | 17b8792aeaf74373bbaef3bac6b71bb5c0d74c69 /src/eval_tune/dna_diff.py | |
| parent | ca73728f7dbf01c4b39c7496729f81b1fbffa00b (diff) | |
Rewrite _EvalKing as a linear ring-scan and promote its terms to DNA-tunable constants; retune via mate-mining regression, ElasticNet/Ridge, and logistic-vs-material refits.
Splits _EvalKing's own king-location table from CountKingSafetyDefects' search-gate copy so future eval retunes can't silently perturb extension/reduction gate frequency. Bumps KING_BACK_FLIGHT_BONUS (+1 -> +3) and reorders/nudges the ring-scan sub-terms (minor/pawn/xray/undefended) to match relative rankings that replicated across three independent calibration methods, while leaving KING_QUEEN_PROXIMITY_DANGER and KING_MISSING_ONE_CASTLE_OPTION untouched pending further discussion.
Self-play (640 games) is roughly even (score 0.509, llr=+0.17, far from either SPRT bound) but consistently faster (nps candidate=1362140 vs head=1333682) at equal average depth -- landing as a wash/speed-neutral checkpoint, not a confirmed tactical improvement, for further tuning to build on.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Diffstat (limited to 'src/eval_tune/dna_diff.py')
| -rwxr-xr-x | src/eval_tune/dna_diff.py | 94 |
1 files changed, 69 insertions, 25 deletions
diff --git a/src/eval_tune/dna_diff.py b/src/eval_tune/dna_diff.py index ecdd6bb..1b44280 100755 --- a/src/eval_tune/dna_diff.py +++ b/src/eval_tune/dna_diff.py @@ -1,45 +1,89 @@ #!/usr/bin/env python3 """Human-readable diff between a baseline DNA dump and a tuned .dna -file: names each of g_EvalDNA's 56 arrays (order taken directly from -eval.c's g_EvalDNA[] table) and, for the 128-cell board-shaped -location tables, renders an actual 8x8 grid diff instead of a wall of -raw numbers.""" +file: names each of g_EvalDNA's arrays and, for the 128-cell board- +shaped location tables, renders an actual 8x8 grid diff instead of a +wall of raw numbers. + +2026-09-15: DNA_NAMES used to be a hand-maintained list "exact order +from eval.c:604-659" -- found to have drifted badly (wrong from index +0: eval.c's g_EvalDNA[] no longer even contains TRADE_PIECES/ +DONT_TRADE_PAWNS, the list's first two entries, and roughly a dozen +other names had also renamed/moved/vanished over past sessions without +this list being updated). Every consumer of this list (this script, +dna_trend.py, tune_eval_dna.py's --freeze) had been silently mislabeling +or misdirecting against the wrong array for a while -- plausibly the +real explanation for at least some past "Texel tuning produced +nonsensical results" reports, since a diff/freeze targeting the wrong +array by name looks exactly like that from the output alone. + +Fixed by deriving this list from eval.c directly at import time instead +of hand-maintaining a parallel copy that can silently drift again. Only +falls back to the hardcoded snapshot below (kept only as a last resort +for running this script somewhere eval.c isn't reachable) with a loud +warning -- never silently. +""" +import re import sys +from pathlib import Path -# Exact order from eval.c:604-659 (g_EvalDNA[] initializer). -DNA_NAMES = [ - "TRADE_PIECES", "DONT_TRADE_PAWNS", "REDUCED_MATERIAL_DOWN_SCALER", +# Last-resort fallback only -- see module docstring. Regenerate by +# running _derive_dna_names() with eval.c present and pasting the result +# here if this ever needs to be updated by hand again. +_FALLBACK_DNA_NAMES = [ + "REDUCED_MATERIAL_DOWN_SCALER", "CASTLE_AND_DEVELOPMENT_SCALER", "REDUCED_MATERIAL_UP_SCALER", "PASSER_MATERIAL_UP_SCALER", "PAWN_CENTRALITY_BONUS", "BACKWARD_SHIELDED_BY_LOCATION", "BACKWARD_EXPOSED_BY_LOCATION", "DOUBLED_PAWN_PENALTY_BY_COUNT", - "ISOLATED_PAWN_PENALTY_BY_COUNT", "ISOLATED_PAWN_BY_PAWNFILE", - "ISOLATED_EXPOSED_PAWN", "ISOLATED_DOUBLED_PAWN", "PASSER_BY_RANK", - "CANDIDATE_PASSER_BY_RANK", "CONNECTED_PASSERS_BY_RANK", - "SUPPORTED_PASSER_BY_RANK", "OUTSIDE_PASSER_BY_DISTANCE", + "ISOLATED_PAWN_BY_PAWNFILE", "ISOLATED_EXPOSED_PAWN", + "ISOLATED_DOUBLED_PAWN", "PASSER_BY_RANK", "CANDIDATE_PASSER_BY_RANK", + "CONNECTED_PASSERS_BY_RANK", "SUPPORTED_PASSER_BY_RANK", + "KING_SUPPORTING_OWN_PASSER_BY_RANK", "OUTSIDE_PASSER_BY_DISTANCE", "PASSER_BONUS_AS_MATERIAL_COMES_OFF", "RACER_WINS_RACE", "UNDEVELOPED_MINORS_IN_OPENING", "BISHOP_OVER_KNIGHT_IN_ENDGAME", - "BISHOP_PAIR", "STATIONARY_PAWN_ON_BISHOP_COLOR", + "BISHOP_PAIR_BONUS", "STATIONARY_PAWN_ON_BISHOP_COLOR", "TRANSIENT_PAWN_ON_BISHOP_COLOR", "BISHOP_MOBILITY_BY_SQUARES", - "BISHOP_MAX_MOBILITY_IN_A_ROW_BONUS", - "BISHOP_UNASSAILABLE_BY_DIST_FROM_EKING", "BISHOP_IN_CLOSED_POSITION", - "KNIGHT_CENTRALITY_BONUS", "KNIGHT_KING_TROPISM_BONUS", - "KNIGHT_UNASSAILABLE_BY_DIST_FROM_EKING", + "BISHOP_UNASSAILABLE_BY_DIST_FROM_EKING", "KNIGHT_CENTRALITY_BONUS", + "KNIGHT_KING_TROPISM_BONUS", "KNIGHT_UNASSAILABLE_BY_DIST_FROM_EKING", "KNIGHT_ON_INTERESTING_SQUARE_BY_RANK", "KNIGHT_MOBILITY_BY_COUNT", "KNIGHT_WITH_N_PAWNS_SUPPORTING", "KNIGHT_IN_CLOSED_POSITION", - "ROOK_ON_FULL_OPEN_BY_DIST_FROM_EKING", - "ROOK_ON_HALF_OPEN_WITH_ENEMY_BY_DIST_FROM_EKING", - "ROOK_ON_HALF_OPEN_WITH_FRIEND_BY_DIST_FROM_EKING", - "ROOK_BEHIND_PASSER_BY_PASSER_RANK", "ROOK_LEADS_PASSER_BY_PASSER_RANK", - "KING_TRAPPING_ROOK", "ROOK_TRAPPING_EKING", + "ROOK_ON_FULL_OPEN", "ROOK_ON_HALF_OPEN_WITH_ENEMY", + "ROOK_ON_HALF_OPEN_WITH_FRIEND", "ROOK_WITH_ENEMY_PASSER", + "ROOK_WITH_FRIEND_PASSER", "ROOK_BEHIND_PASSER_BY_PASSER_RANK", + "ROOK_LEADS_PASSER_BY_PASSER_RANK", "KING_TRAPPING_ROOK", "ROOK_VALUE_AS_PAWNS_COME_OFF", "ROOK_CONNECTED_VERT", "ROOK_CONNECTED_HORIZ", "ROOK_MOBILITY_BY_SQUARES", - "ROOK_MAX_MOBILITY_IN_A_ROW_BONUS", "QUEEN_MOBILITY_BY_SQUARES", - "QUEEN_OUT_EARLY", "QUEEN_KING_TROPISM", - "QUEEN_ATTACKS_SQ_NEXT_TO_KING", "KING_INITIAL_COUNTER_BY_LOCATION", + "QUEEN_MOBILITY_BY_SQUARES", "QUEEN_KING_TROPISM", + "KING_INITIAL_COUNTER_BY_LOCATION", "KING_EVAL_INITIAL_LOCATION", "KING_TO_CENTER", "KING_SAFETY_BY_COUNTER", - "KING_MISSING_ONE_CASTLE_OPTION", + "KING_QUEEN_PROXIMITY_DANGER", "KING_MISSING_ONE_CASTLE_OPTION", + "KING_BACK_FLIGHT_BONUS", "KING_RING_ATTACK_MINOR", + "KING_RING_ATTACK_ROOK", "KING_RING_ATTACK_PAWN", + "KING_RING_UNDEFENDED", "KING_RING_XRAY_BONUS", + "KING_RING_FRONT_FLIGHT_PENALTY", "KING_INITIAL_LOCATION_SCALE", + "KING_ATTACK_PATTERN_SCALE", "KING_DANGER_ACCELERATOR", ] + +def _derive_dna_names(eval_c_path="../eval.c"): + """Parse g_EvalDNA[]'s initializer directly out of eval.c, in + order -- the only way this list can't silently drift out of sync + with the real array again.""" + text = Path(eval_c_path).read_text() + m = re.search(r'static DNA_BASE_SIZE g_EvalDNA\[\] = \{(.*?)\n\};', + text, re.DOTALL) + if not m: + return None + return re.findall(r'DNA_(?:ARRAY|MATRIX|VAR)\(([A-Za-z0-9_]+)\)', m.group(1)) + + +DNA_NAMES = _derive_dna_names() +if DNA_NAMES is None: + print("WARNING: could not find/parse g_EvalDNA[] in ../eval.c -- " + "falling back to a hardcoded DNA_NAMES snapshot that WILL " + "silently drift out of date. Run this from eval_tune/ with " + "the real eval.c reachable if at all possible.", file=sys.stderr) + DNA_NAMES = _FALLBACK_DNA_NAMES + # Arrays laid out as a 128-cell "0x88-style" board: 8 files + 8 padding # zeros per rank, 8 ranks (see eval.c's literal formatting -- each row # of the C initializer is one rank, padded to 16 slots). Rendered |
