diff options
Diffstat (limited to 'src/eval_tune/dna_diff.py')
| -rwxr-xr-x | src/eval_tune/dna_diff.py | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/src/eval_tune/dna_diff.py b/src/eval_tune/dna_diff.py new file mode 100755 index 0000000..ecdd6bb --- /dev/null +++ b/src/eval_tune/dna_diff.py @@ -0,0 +1,123 @@ +#!/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.""" +import sys + +# Exact order from eval.c:604-659 (g_EvalDNA[] initializer). +DNA_NAMES = [ + "TRADE_PIECES", "DONT_TRADE_PAWNS", "REDUCED_MATERIAL_DOWN_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", + "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", + "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", + "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_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", + "KING_TO_CENTER", "KING_SAFETY_BY_COUNTER", + "KING_MISSING_ONE_CASTLE_OPTION", +] + +# 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 +# top-to-bottom as rank 8 -> rank 1 like the board is shown elsewhere. +BOARD128_NAMES = { + "PAWN_CENTRALITY_BONUS", "BACKWARD_SHIELDED_BY_LOCATION", + "BACKWARD_EXPOSED_BY_LOCATION", "STATIONARY_PAWN_ON_BISHOP_COLOR", + "TRANSIENT_PAWN_ON_BISHOP_COLOR", "KNIGHT_CENTRALITY_BONUS", + "KING_TO_CENTER", +} +# KING_INITIAL_COUNTER_BY_LOCATION is [2][128] -- one 128-board per color. +BOARD128_PAIR_NAMES = {"KING_INITIAL_COUNTER_BY_LOCATION"} + +FILES = "ABCDEFGH" + + +def read_dna_file(path): + rows = [] + with open(path) as f: + for line in f: + line = line.strip() + if not line: + continue + rows.append([int(x) for x in line.split(",")]) + return rows + + +def diff_board128(old_row, new_row): + lines = [] + for rank8_from_top in range(8): + old_cells = old_row[rank8_from_top * 16: rank8_from_top * 16 + 8] + new_cells = new_row[rank8_from_top * 16: rank8_from_top * 16 + 8] + rank_label = 8 - rank8_from_top + cell_strs = [] + for o, n in zip(old_cells, new_cells): + if o == n: + cell_strs.append(f"{n:4d}") + else: + cell_strs.append(f"{o:+d}->{n:+d}") + lines.append(f" {rank_label} " + " ".join(f"{s:>9s}" for s in cell_strs)) + lines.append(" " + " ".join(f"{f}" for f in FILES)) + return lines + + +def report(baseline_rows, tuned_rows, names=DNA_NAMES, only_changed=True): + assert len(baseline_rows) == len(tuned_rows) == len(names), ( + f"row count mismatch: baseline={len(baseline_rows)} " + f"tuned={len(tuned_rows)} names={len(names)}" + ) + any_change = False + for name, old_row, new_row in zip(names, baseline_rows, tuned_rows): + if old_row == new_row: + if not only_changed: + print(f"{name}: unchanged") + continue + any_change = True + print(f"\n=== {name} ===") + if name in BOARD128_PAIR_NAMES: + half = len(old_row) // 2 + for color, lo, hi in (("BLACK", 0, half), ("WHITE", half, len(old_row))): + if old_row[lo:hi] != new_row[lo:hi]: + print(f" -- {color} --") + for line in diff_board128(old_row[lo:hi], new_row[lo:hi]): + print(" ", line) + elif name in BOARD128_NAMES and len(old_row) == 128: + for line in diff_board128(old_row, new_row): + print(" ", line) + else: + diffs = [ + (i, o, n) for i, (o, n) in enumerate(zip(old_row, new_row)) if o != n + ] + print(f" old: {old_row}") + print(f" new: {new_row}") + print(f" changed cells: {diffs}") + if not any_change: + print("No differences -- tuned DNA is identical to baseline.") + + +if __name__ == "__main__": + baseline_path, tuned_path = sys.argv[1], sys.argv[2] + report(read_dna_file(baseline_path), read_dna_file(tuned_path)) |
