Add gates 1-2 audit and repair workflow

This commit is contained in:
2026-07-05 17:55:22 +09:00
parent 94e9ac1854
commit fa9a1c5286
15 changed files with 1602 additions and 3 deletions
@@ -0,0 +1,40 @@
run:
experiment_name: gates-1-2-long-random-exploiter
seed: 20260705
learner_seat: 0
total_updates: 1200
log_every: 10
checkpoint_every: 100
artifact_root: /mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/exploiters
opponent:
name: league_v1_update_500_frozen
network:
hidden_size: 512
num_layers: 3
ppo:
batch_games: 8192
rollout_steps: 400
gamma: 1.0
gae_lambda: 0.95
clip_epsilon: 0.2
entropy_coef: 0.01
value_coef: 0.5
max_grad_norm: 0.5
learning_rate: 0.0003
epochs: 4
minibatches: 128
reward:
terminal_scale: 50.0
potential_shaping_initial: 1.0
potential_shaping_final: 0.0
potential_shaping_anneal_steps: 5000000
evaluation:
games: 2000
duplicate: true
shuffle_bank_seed: 20260704
batch_games: 8192
@@ -0,0 +1,40 @@
run:
experiment_name: gates-1-2-replay-exploiter
seed: 20260705
learner_seat: 0
total_updates: 900
log_every: 10
checkpoint_every: 100
artifact_root: /mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/exploiters
opponent:
name: league_v1_update_500_frozen
network:
hidden_size: 512
num_layers: 3
ppo:
batch_games: 8192
rollout_steps: 400
gamma: 1.0
gae_lambda: 0.95
clip_epsilon: 0.2
entropy_coef: 0.01
value_coef: 0.5
max_grad_norm: 0.5
learning_rate: 0.0003
epochs: 4
minibatches: 128
reward:
terminal_scale: 50.0
potential_shaping_initial: 0.0
potential_shaping_final: 0.0
potential_shaping_anneal_steps: 0
evaluation:
games: 2000
duplicate: true
shuffle_bank_seed: 20260704
batch_games: 8192
@@ -0,0 +1,40 @@
run:
experiment_name: gates-1-2-smoke-exploiter
seed: 20260705
learner_seat: 0
total_updates: 1
log_every: 1
checkpoint_every: 1
artifact_root: /mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2-smoke/exploiters
opponent:
name: league_v1_update_500_frozen
network:
hidden_size: 512
num_layers: 3
ppo:
batch_games: 8
rollout_steps: 16
gamma: 1.0
gae_lambda: 0.95
clip_epsilon: 0.2
entropy_coef: 0.01
value_coef: 0.5
max_grad_norm: 0.5
learning_rate: 0.0003
epochs: 1
minibatches: 2
reward:
terminal_scale: 50.0
potential_shaping_initial: 1.0
potential_shaping_final: 0.0
potential_shaping_anneal_steps: 5000000
evaluation:
games: 2
duplicate: true
shuffle_bank_seed: 20260704
batch_games: 2
@@ -0,0 +1,40 @@
run:
experiment_name: gates-1-2-warmstart-gate3-exploiter
seed: 20260705
learner_seat: 0
total_updates: 900
log_every: 10
checkpoint_every: 100
artifact_root: /mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/exploiters
opponent:
name: league_v1_update_500_frozen
network:
hidden_size: 512
num_layers: 3
ppo:
batch_games: 8192
rollout_steps: 400
gamma: 1.0
gae_lambda: 0.95
clip_epsilon: 0.2
entropy_coef: 0.01
value_coef: 0.5
max_grad_norm: 0.5
learning_rate: 0.0003
epochs: 4
minibatches: 128
reward:
terminal_scale: 50.0
potential_shaping_initial: 0.0
potential_shaping_final: 0.0
potential_shaping_anneal_steps: 0
evaluation:
games: 2000
duplicate: true
shuffle_bank_seed: 20260704
batch_games: 8192
+35
View File
@@ -0,0 +1,35 @@
run:
artifact_root: /mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2-smoke
report_path: /mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2-smoke/report.md
summary_path: /mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2-smoke/summary.jsonl
shuffle_bank_seed: 20260704
gpu_budget_hours: 1.0
target:
config: /mnt/2tbhdd/coolrl-lost-cities-artifacts/league/2026-07-05_052325_jax-ppo-league-v1/main_ppo_config.json
checkpoint: /mnt/2tbhdd/coolrl-lost-cities-artifacts/league/2026-07-05_052325_jax-ppo-league-v1/snapshots/cycle_01_update_000500
gate1:
games: 2
batch_games: 2
delta_games: 2
delta_target_events: 2
delta_pairs: 2
delta_batch_events: 1
gate2:
eval_games: 2
pass_threshold: 0.55
exploiters:
- name: long_random_smoke
config: configs/jax_ppo/gates-1-2-exploiter-smoke.yaml
repair:
max_cycles: 1
league_template: configs/jax_ppo/league-smoke.yaml
experiment_name: jax-ppo-gates-1-2-repair-smoke
artifact_subdir: gate2c
evaluation_games: 2
evaluation_batch_games: 2
guard_expert_ci_low: -1000.0
guard_max_steps_rate: 1.0
+44
View File
@@ -0,0 +1,44 @@
run:
artifact_root: /mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2
report_path: docs/reports/gates-1-2-2026-07-05.md
summary_path: docs/reports/gates-1-2-2026-07-05-summary.jsonl
shuffle_bank_seed: 20260704
gpu_budget_hours: 8.0
target:
config: /mnt/2tbhdd/coolrl-lost-cities-artifacts/league/2026-07-05_052325_jax-ppo-league-v1/main_ppo_config.json
checkpoint: /mnt/2tbhdd/coolrl-lost-cities-artifacts/league/2026-07-05_052325_jax-ppo-league-v1/snapshots/cycle_01_update_000500
gate1:
games: 2000
batch_games: 8192
delta_games: 200
delta_target_events: 500
delta_pairs: 64
delta_batch_events: 16
gate2:
eval_games: 2000
pass_threshold: 0.55
exploiters:
- name: long_random
config: configs/jax_ppo/gates-1-2-exploiter-long-random.yaml
notes: random init + shaping anneal, extended budget
- name: warmstart_gate3
config: configs/jax_ppo/gates-1-2-exploiter-warmstart.yaml
resume: /mnt/2tbhdd/coolrl-lost-cities-artifacts/ladder-v2/2026-07-05_013223_jax-ppo-ladder-v2-expert/latest
notes: ladder v2 gate-3 warm start, shaping disabled
- name: replay_exploiter
config: configs/jax_ppo/gates-1-2-exploiter-replay.yaml
resume: /mnt/2tbhdd/coolrl-lost-cities-artifacts/league/2026-07-05_052325_jax-ppo-league-v1/exploiters/2026-07-05_060727_jax-ppo-league-v1-cycle-1-exploiter/latest
notes: league v1 exploiter warm start, shaping disabled
repair:
max_cycles: 3
league_template: configs/jax_ppo/league-v1.yaml
experiment_name: jax-ppo-gates-1-2-repair
artifact_subdir: gate2c
evaluation_games: 2000
evaluation_batch_games: 8192
guard_expert_ci_low: 0.0
guard_max_steps_rate: 0.02
@@ -0,0 +1,19 @@
{"event": "gate1_tournament", "games": 4000, "json": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate1/expert_cap2_vs_expert_cap3.json", "learner": "expert_cap2", "losses": 2102.0, "max_steps_rate": 0.0, "mean_game_length": 46.549, "mean_score_diff": -1.89325, "opened_colors_per_game": 1.77925, "opponent": "expert_cap3", "play_action_rate": 0.2414775169804832, "positive_expeditions_per_game": 0.94175, "score_diff_ci95_high": -1.4203123965597364, "score_diff_ci95_low": -2.366187603440264, "score_diff_std": 15.261096936571331, "ties": 132.0, "wilson_high": 0.45693730157215046, "wilson_low": 0.42617495329274474, "win_rate": 0.4415, "wins": 1766.0}
{"event": "gate1_tournament", "games": 4000, "json": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate1/expert_cap2_vs_expert_capN.json", "learner": "expert_cap2", "losses": 2092.0, "max_steps_rate": 0.0, "mean_game_length": 46.354, "mean_score_diff": -1.88075, "opened_colors_per_game": 1.7815, "opponent": "expert_capN", "play_action_rate": 0.24223387949830538, "positive_expeditions_per_game": 0.93725, "score_diff_ci95_high": -1.4070318876291175, "score_diff_ci95_low": -2.3544681123708826, "score_diff_std": 15.286282970338549, "ties": 130.0, "wilson_high": 0.45994507591745637, "wilson_low": 0.4291614222877006, "win_rate": 0.4445, "wins": 1778.0}
{"event": "gate1_tournament", "games": 4000, "json": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate1/expert_cap3_vs_expert_capN.json", "learner": "expert_cap3", "losses": 1927.0, "max_steps_rate": 0.0, "mean_game_length": 45.35975, "mean_score_diff": 0.0285, "opened_colors_per_game": 2.21525, "opponent": "expert_capN", "play_action_rate": 0.3101634934459303, "positive_expeditions_per_game": 1.20625, "score_diff_ci95_high": 0.5258735279417411, "score_diff_ci95_low": -0.46887352794174114, "score_diff_std": 16.04961324366783, "ties": 137.0, "wilson_high": 0.49949486790031544, "wilson_low": 0.4685358342849551, "win_rate": 0.484, "wins": 1936.0}
{"event": "gate1_vs_league", "games": 4000, "json": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate1/expert_cap2_vs_league_v1_update_500.json", "learner": "expert_cap2", "losses": 2871.0, "max_steps_rate": 0.00025, "mean_game_length": 55.372, "mean_score_diff": -20.88625, "opened_colors_per_game": 1.9735, "opponent": "league_v1_update_500", "play_action_rate": 0.22526015834941476, "positive_expeditions_per_game": 1.09725, "score_diff_ci95_high": -19.787867065349186, "score_diff_ci95_low": -21.984632934650815, "score_diff_std": 35.44342491958145, "ties": 39.0, "wilson_high": 0.2865114339331819, "wilson_low": 0.2589251127636324, "win_rate": 0.2725, "wins": 1090.0}
{"event": "gate1_vs_league", "games": 4000, "json": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate1/expert_cap3_vs_league_v1_update_500.json", "learner": "expert_cap3", "losses": 2590.0, "max_steps_rate": 0.0, "mean_game_length": 53.52525, "mean_score_diff": -14.35125, "opened_colors_per_game": 2.7385, "opponent": "league_v1_update_500", "play_action_rate": 0.320897958422324, "positive_expeditions_per_game": 1.52675, "score_diff_ci95_high": -13.256641796659636, "score_diff_ci95_low": -15.445858203340364, "score_diff_std": 35.32161912528796, "ties": 50.0, "wilson_high": 0.3548273870268761, "wilson_low": 0.32547963482582853, "win_rate": 0.34, "wins": 1360.0}
{"event": "gate1_vs_league", "games": 4000, "json": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate1/expert_capN_vs_league_v1_update_500.json", "learner": "expert_capN", "losses": 2585.0, "max_steps_rate": 0.0, "mean_game_length": 53.29175, "mean_score_diff": -13.967, "opened_colors_per_game": 3.004, "opponent": "league_v1_update_500", "play_action_rate": 0.3410701746172932, "positive_expeditions_per_game": 1.56475, "score_diff_ci95_high": -12.879677994093045, "score_diff_ci95_low": -15.054322005906956, "score_diff_std": 35.086502770569304, "ties": 49.0, "wilson_high": 0.3563415432992672, "wilson_low": 0.3269626002235683, "win_rate": 0.3415, "wins": 1366.0}
{"ci95_high": 0.3559042062015777, "ci95_low": -0.32834170620157765, "deltas_npy": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate1/delta_open_deltas.npy", "event": "gate1_delta_open", "histogram": {"counts": [107, 1643, 5209, 7192, 10627, 5447, 1662, 113], "edges": [-200.0, -100.0, -50.0, -20.0, 0.0, 20.0, 50.0, 100.0, 200.0]}, "histogram_png": "docs/reports/gates-1-2-delta-open-hist.png", "judgment": "near_zero", "mean_delta": 0.01378125, "pairs": 64, "quantiles": {"max": 149.0, "min": -167.0, "p05": -52.0, "p25": -17.0, "p50": 0.0, "p75": 17.0, "p95": 51.0}, "samples": 32000, "states": 500, "std_delta": 31.225479356323433}
{"checkpoint": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/exploiters/2026-07-05_094311_gates-1-2-long-random-exploiter/latest", "event": "gate2_exploiter", "exploiter": "long_random", "games": 4000, "json": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/exploiters/2026-07-05_094311_gates-1-2-long-random-exploiter/eval_vs_league_v1_update_500_duplicate.json", "losses": 1610.0, "max_steps_rate": 0.0, "mean_game_length": 50.74375, "mean_score_diff": 10.95125, "notes": "random init + shaping anneal, extended budget", "opened_colors_per_game": 4.99575, "play_action_rate": 0.8010700772505124, "positive_expeditions_per_game": 2.53625, "resume": null, "score_diff_ci95_high": 12.423541316332845, "score_diff_ci95_low": 9.478958683667155, "score_diff_std": 47.50897440589277, "ties": 42.0, "train_dir": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/exploiters/2026-07-05_094311_gates-1-2-long-random-exploiter", "wilson_high": 0.6021679468487495, "wilson_low": 0.5716651100188423, "win_rate": 0.587, "wins": 2348.0}
{"checkpoint": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/exploiters/2026-07-05_105902_gates-1-2-warmstart-gate3-exploiter/latest", "event": "gate2_exploiter", "exploiter": "warmstart_gate3", "games": 4000, "json": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/exploiters/2026-07-05_105902_gates-1-2-warmstart-gate3-exploiter/eval_vs_league_v1_update_500_duplicate.json", "losses": 1605.0, "max_steps_rate": 0.0, "mean_game_length": 50.8715, "mean_score_diff": 11.38075, "notes": "ladder v2 gate-3 warm start, shaping disabled", "opened_colors_per_game": 4.9945, "play_action_rate": 0.798003736847281, "positive_expeditions_per_game": 2.5195, "resume": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/ladder-v2/2026-07-05_013223_jax-ppo-ladder-v2-expert/latest", "score_diff_ci95_high": 12.872234578510747, "score_diff_ci95_low": 9.889265421489254, "score_diff_std": 48.128316645747006, "ties": 35.0, "train_dir": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/exploiters/2026-07-05_105902_gates-1-2-warmstart-gate3-exploiter", "wilson_high": 0.6051483732088389, "wilson_low": 0.5746789269990148, "win_rate": 0.59, "wins": 2360.0}
{"checkpoint": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/exploiters/2026-07-05_115602_gates-1-2-replay-exploiter/latest", "event": "gate2_exploiter", "exploiter": "replay_exploiter", "games": 4000, "json": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/exploiters/2026-07-05_115602_gates-1-2-replay-exploiter/eval_vs_league_v1_update_500_duplicate.json", "losses": 1638.0, "max_steps_rate": 0.0, "mean_game_length": 51.4305, "mean_score_diff": 10.42525, "notes": "league v1 exploiter warm start, shaping disabled", "opened_colors_per_game": 4.99775, "play_action_rate": 0.7904596181514173, "positive_expeditions_per_game": 2.54225, "resume": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/league/2026-07-05_052325_jax-ppo-league-v1/exploiters/2026-07-05_060727_jax-ppo-league-v1-cycle-1-exploiter/latest", "score_diff_ci95_high": 11.913719629678091, "score_diff_ci95_low": 8.936780370321909, "score_diff_std": 48.0310280688623, "ties": 34.0, "train_dir": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/exploiters/2026-07-05_115602_gates-1-2-replay-exploiter", "wilson_high": 0.5971992743148633, "wilson_low": 0.5666433769856256, "win_rate": 0.582, "wins": 2328.0}
{"event": "gate2_judgment", "passed": false, "threshold": 0.55, "worst_exploiter": "warmstart_gate3", "worst_win_rate": 0.59}
{"elapsed_seconds": 11425.08623591601, "event": "gates_complete", "run_dir": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2"}
{"cycle": 1, "event": "gate2c_league_cycle", "league_config": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/cycle_01/league_config.yaml", "league_run_dir": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/league/2026-07-05_125709_jax-ppo-gates-1-2-repair-c01", "target_checkpoint": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/league/2026-07-05_125709_jax-ppo-gates-1-2-repair-c01/snapshots/cycle_01_update_000500", "target_config": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/league/2026-07-05_125709_jax-ppo-gates-1-2-repair-c01/main_ppo_config.json", "worst_protocol": "warmstart_gate3"}
{"cycle": 1, "event": "gate2c_guard_expert", "games": 4000, "json": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/cycle_01/guard_vs_heuristic_expert.json", "losses": 1188.0, "max_steps_rate": 0.0, "mean_game_length": 54.61975, "mean_score_diff": 19.654, "opened_colors_per_game": 4.7015, "passed": true, "play_action_rate": 0.6708126417051431, "positive_expeditions_per_game": 2.1505, "score_diff_ci95_high": 20.81558482366198, "score_diff_ci95_low": 18.49241517633802, "score_diff_std": 37.482869759149246, "target_checkpoint": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/league/2026-07-05_125709_jax-ppo-gates-1-2-repair-c01/snapshots/cycle_01_update_000500", "target_config": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/league/2026-07-05_125709_jax-ppo-gates-1-2-repair-c01/main_ppo_config.json", "ties": 41.0, "wilson_high": 0.7068566468456556, "wilson_low": 0.6782734877661645, "win_rate": 0.69275, "wins": 2771.0}
{"checkpoint": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/cycle_01/battery/2026-07-05_143807_gates-1-2-long-random-exploiter/latest", "cycle": 1, "event": "gate2c_exploiter", "exploiter": "long_random", "games": 4000, "json": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/cycle_01/battery/2026-07-05_143807_gates-1-2-long-random-exploiter/eval_vs_repair_cycle_01_duplicate.json", "losses": 1810.0, "max_steps_rate": 0.0, "mean_game_length": 51.4185, "mean_score_diff": 6.27925, "notes": "random init + shaping anneal, extended budget", "opened_colors_per_game": 4.99425, "play_action_rate": 0.7816023796562719, "positive_expeditions_per_game": 2.52, "resume": null, "score_diff_ci95_high": 7.76660187726116, "score_diff_ci95_low": 4.79189812273884, "score_diff_std": 47.99495961530356, "target_checkpoint": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/league/2026-07-05_125709_jax-ppo-gates-1-2-repair-c01/snapshots/cycle_01_update_000500", "target_config": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/league/2026-07-05_125709_jax-ppo-gates-1-2-repair-c01/main_ppo_config.json", "target_name": "repair_cycle_01", "ties": 34.0, "train_dir": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/cycle_01/battery/2026-07-05_143807_gates-1-2-long-random-exploiter", "wilson_high": 0.5544028831760525, "wilson_low": 0.5235222802473508, "win_rate": 0.539, "wins": 2156.0}
{"checkpoint": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/cycle_01/battery/2026-07-05_155430_gates-1-2-warmstart-gate3-exploiter/latest", "cycle": 1, "event": "gate2c_exploiter", "exploiter": "warmstart_gate3", "games": 4000, "json": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/cycle_01/battery/2026-07-05_155430_gates-1-2-warmstart-gate3-exploiter/eval_vs_repair_cycle_01_duplicate.json", "losses": 1763.0, "max_steps_rate": 0.0, "mean_game_length": 50.434, "mean_score_diff": 7.0285, "notes": "ladder v2 gate-3 warm start, shaping disabled", "opened_colors_per_game": 4.9935, "play_action_rate": 0.8006960073764884, "positive_expeditions_per_game": 2.4845, "resume": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/ladder-v2/2026-07-05_013223_jax-ppo-ladder-v2-expert/latest", "score_diff_ci95_high": 8.490821414519859, "score_diff_ci95_low": 5.5661785854801415, "score_diff_std": 47.18725831288327, "target_checkpoint": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/league/2026-07-05_125709_jax-ppo-gates-1-2-repair-c01/snapshots/cycle_01_update_000500", "target_config": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/league/2026-07-05_125709_jax-ppo-gates-1-2-repair-c01/main_ppo_config.json", "target_name": "repair_cycle_01", "ties": 41.0, "train_dir": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/cycle_01/battery/2026-07-05_155430_gates-1-2-warmstart-gate3-exploiter", "wilson_high": 0.5643659496573598, "wilson_low": 0.5335400249002497, "win_rate": 0.549, "wins": 2196.0}
{"checkpoint": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/cycle_01/battery/2026-07-05_165151_gates-1-2-replay-exploiter/latest", "cycle": 1, "event": "gate2c_exploiter", "exploiter": "replay_exploiter", "games": 4000, "json": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/cycle_01/battery/2026-07-05_165151_gates-1-2-replay-exploiter/eval_vs_repair_cycle_01_duplicate.json", "losses": 1798.0, "max_steps_rate": 0.0, "mean_game_length": 51.116, "mean_score_diff": 6.10225, "notes": "league v1 exploiter warm start, shaping disabled", "opened_colors_per_game": 4.99425, "play_action_rate": 0.7845902569268536, "positive_expeditions_per_game": 2.50025, "resume": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/league/2026-07-05_052325_jax-ppo-league-v1/exploiters/2026-07-05_060727_jax-ppo-league-v1-cycle-1-exploiter/latest", "score_diff_ci95_high": 7.583110013023179, "score_diff_ci95_low": 4.62138998697682, "score_diff_std": 47.785475385853, "target_checkpoint": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/league/2026-07-05_125709_jax-ppo-gates-1-2-repair-c01/snapshots/cycle_01_update_000500", "target_config": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/league/2026-07-05_125709_jax-ppo-gates-1-2-repair-c01/main_ppo_config.json", "target_name": "repair_cycle_01", "ties": 45.0, "train_dir": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/cycle_01/battery/2026-07-05_165151_gates-1-2-replay-exploiter", "wilson_high": 0.5546520360903574, "wilson_low": 0.523772647611401, "win_rate": 0.53925, "wins": 2157.0}
{"cycle": 1, "event": "gate2c_judgment", "passed": true, "threshold": 0.55, "worst_exploiter": "warmstart_gate3", "worst_win_rate": 0.549}
{"elapsed_seconds_total": 28945.184503759025, "event": "gates_repair_complete", "run_dir": "/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2"}
+67
View File
@@ -0,0 +1,67 @@
# Gates 1-2 Report - 2026-07-05
**Run dir:** `/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2`.
**Target:** `/mnt/2tbhdd/coolrl-lost-cities-artifacts/league/2026-07-05_052325_jax-ppo-league-v1/snapshots/cycle_01_update_000500`.
## Gate 1A - Policy Class Tournament
| Learner | Opponent | Win rate | Mean diff | CI low | Opened colors | Max-step |
| --- | --- | ---: | ---: | ---: | ---: | ---: |
| `expert_cap2` | `expert_cap3` | 0.4415 | -1.8933 | -2.3662 | 1.7792 | 0.0000 |
| `expert_cap2` | `expert_capN` | 0.4445 | -1.8807 | -2.3545 | 1.7815 | 0.0000 |
| `expert_cap3` | `expert_capN` | 0.4840 | +0.0285 | -0.4689 | 2.2153 | 0.0000 |
## Gate 1A - Variants vs League v1
| Learner | Opponent | Win rate | Mean diff | CI low | Opened colors | Max-step |
| --- | --- | ---: | ---: | ---: | ---: | ---: |
| `expert_cap2` | `league_v1_update_500` | 0.2725 | -20.8863 | -21.9846 | 1.9735 | 0.0003 |
| `expert_cap3` | `league_v1_update_500` | 0.3400 | -14.3513 | -15.4459 | 2.7385 | 0.0000 |
| `expert_capN` | `league_v1_update_500` | 0.3415 | -13.9670 | -15.0543 | 3.0040 | 0.0000 |
## Gate 1B - Delta Open Audit
- States: 500
- Paired samples: 32000
- Mean delta: +0.0138
- CI95: [-0.3283, +0.3559]
- Judgment: `near_zero`
- Histogram: `docs/reports/gates-1-2-delta-open-hist.png`
## Gate 1C - Selectivity Judgment
통념 기각/미결: capN은 집중 변형보다 유의하게 나쁘지 않고, 리그 정책의 4번째+ 오픈 delta는 0 근처다. selectivity는 현재 주요 성능 병목으로 보이지 않는다.
## Gate 2A - Strengthened Exploiter Battery
| Exploiter | Win rate | Mean diff | CI low | Opened colors | Max-step | Run |
| --- | ---: | ---: | ---: | ---: | ---: | --- |
| `long_random` | 0.5870 | +10.9512 | +9.4790 | 4.9958 | 0.0000 | `/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/exploiters/2026-07-05_094311_gates-1-2-long-random-exploiter` |
| `warmstart_gate3` | 0.5900 | +11.3808 | +9.8893 | 4.9945 | 0.0000 | `/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/exploiters/2026-07-05_105902_gates-1-2-warmstart-gate3-exploiter` |
| `replay_exploiter` | 0.5820 | +10.4253 | +8.9368 | 4.9977 | 0.0000 | `/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/exploiters/2026-07-05_115602_gates-1-2-replay-exploiter` |
## Gate 2B - Robustness Judgment
보수 필요: worst exploiter `warmstart_gate3` win rate 0.5900 vs threshold 0.5500.
## Gate 2C - Conditional Repair League
| Cycle | Worst protocol used | Guard pass | Battery worst | Passed | Checkpoint |
| ---: | --- | ---: | ---: | ---: | --- |
| 1 | `warmstart_gate3` | True | 0.5490 | True | `/mnt/2tbhdd/coolrl-lost-cities-artifacts/gates-1-2/2026-07-05_094238_gates-1-2/gate2c/league/2026-07-05_125709_jax-ppo-gates-1-2-repair-c01/snapshots/cycle_01_update_000500` |
| Cycle | Exploiter | Win rate | Mean diff | CI low | Opened colors | Max-step |
| ---: | --- | ---: | ---: | ---: | ---: | ---: |
| 1 | `long_random` | 0.5390 | +6.2793 | +4.7919 | 4.9943 | 0.0000 |
| 1 | `warmstart_gate3` | 0.5490 | +7.0285 | +5.5662 | 4.9935 | 0.0000 |
| 1 | `replay_exploiter` | 0.5393 | +6.1022 | +4.6214 | 4.9943 | 0.0000 |
## Human Play Recommendation
조건부 예: 강화 exploiter 관문은 통과했다. 다만 selectivity 관문이 미결/부분 지지이면 인간 대전은 실력 인증이 아니라 행동 양식 진단으로 시작해야 한다.
## Decisions
- Existing `heuristic_expert` remains unchanged. The cap variants add only a hard `max_open_colors` gate around new-color openings.
- `expert_capN` means no hard cap; EV thresholds and the existing soft concentration penalties are retained.
- Warm-started exploiters use shaping coefficient 0 to measure target-specific exploitation without reintroducing early shaping rewards.
Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because it is too large Load Diff
+3
View File
@@ -72,6 +72,7 @@ class LeagueConfig:
evaluation_shuffle_bank_seed: int = 20260704 evaluation_shuffle_bank_seed: int = 20260704
recent_snapshot_evals: int = 3 recent_snapshot_evals: int = 3
exploiter_config: str = "configs/jax_ppo/ladder-v2-exploiter.yaml" exploiter_config: str = "configs/jax_ppo/ladder-v2-exploiter.yaml"
exploiter_resume: str | None = None
exploiter_updates: int = 250 exploiter_updates: int = 250
expert_guard_ci_low: float = 0.0 expert_guard_ci_low: float = 0.0
max_steps_guard: float = 0.02 max_steps_guard: float = 0.02
@@ -115,6 +116,7 @@ def load_league_config(path: str | Path) -> LeagueConfig:
evaluation_shuffle_bank_seed=evaluation.get("shuffle_bank_seed", 20260704), evaluation_shuffle_bank_seed=evaluation.get("shuffle_bank_seed", 20260704),
recent_snapshot_evals=evaluation.get("recent_snapshot_evals", 3), recent_snapshot_evals=evaluation.get("recent_snapshot_evals", 3),
exploiter_config=exploiter.get("config", "configs/jax_ppo/ladder-v2-exploiter.yaml"), exploiter_config=exploiter.get("config", "configs/jax_ppo/ladder-v2-exploiter.yaml"),
exploiter_resume=exploiter.get("resume"),
exploiter_updates=exploiter.get("updates", 250), exploiter_updates=exploiter.get("updates", 250),
expert_guard_ci_low=guards.get("expert_ci_low", 0.0), expert_guard_ci_low=guards.get("expert_ci_low", 0.0),
max_steps_guard=guards.get("max_steps_rate", 0.02), max_steps_guard=guards.get("max_steps_rate", 0.02),
@@ -392,6 +394,7 @@ def run_exploiter_cycle(
exploiter_cfg, exploiter_cfg,
ppo_cfg, ppo_cfg,
target.checkpoint or cfg.warm_start_checkpoint, target.checkpoint or cfg.warm_start_checkpoint,
resume=cfg.exploiter_resume,
) )
result = evaluate_checkpoint_match( result = evaluate_checkpoint_match(
exploiter_cfg, exploiter_cfg,
+17 -2
View File
@@ -34,6 +34,7 @@ ACTION_DRAW_SOURCE = ACTION_IDS % 6
@dataclass(frozen=True) @dataclass(frozen=True)
class HeuristicExpertConfig: class HeuristicExpertConfig:
max_open_colors: int | None = None
target_open_colors: int = 3 target_open_colors: int = 3
fourth_color_margin: float = 38.0 fourth_color_margin: float = 38.0
extra_color_margin: float = 58.0 extra_color_margin: float = 58.0
@@ -64,7 +65,7 @@ class HeuristicExpertConfig:
def policy_by_name(name: str) -> ActionPolicy: def policy_by_name(name: str) -> ActionPolicy:
normalized = name.replace("-", "_") normalized = name.replace("-", "_").lower()
if normalized == "discard_only": if normalized == "discard_only":
return discard_only_action return discard_only_action
if normalized == "heuristic_balanced": if normalized == "heuristic_balanced":
@@ -73,6 +74,12 @@ def policy_by_name(name: str) -> ActionPolicy:
return heuristic_cautious_action return heuristic_cautious_action
if normalized == "heuristic_expert": if normalized == "heuristic_expert":
return heuristic_expert_action return heuristic_expert_action
if normalized in {"expert_cap2", "heuristic_expert_cap2"}:
return make_heuristic_expert_policy(HeuristicExpertConfig(max_open_colors=2))
if normalized in {"expert_cap3", "heuristic_expert_cap3"}:
return make_heuristic_expert_policy(HeuristicExpertConfig(max_open_colors=3))
if normalized in {"expert_capn", "expert_cap_n", "heuristic_expert_capn"}:
return make_heuristic_expert_policy(HeuristicExpertConfig(max_open_colors=None))
msg = f"unknown opponent policy: {name}" msg = f"unknown opponent policy: {name}"
raise ValueError(msg) raise ValueError(msg)
@@ -197,6 +204,11 @@ def _heuristic_expert_action(
progress = (state.draw_ptr - 16).astype(jnp.float32) / DECK_DRAWS progress = (state.draw_ptr - 16).astype(jnp.float32) / DECK_DRAWS
active_colors = jnp.sum(state.col_len[player] > 0).astype(jnp.float32) active_colors = jnp.sum(state.col_len[player] > 0).astype(jnp.float32)
hard_cap_allows_new_color = (
jnp.asarray(True)
if cfg.max_open_colors is None
else active_colors < jnp.asarray(cfg.max_open_colors, dtype=jnp.float32)
)
open_threshold = ( open_threshold = (
cfg.base_open_threshold cfg.base_open_threshold
+ progress * cfg.late_open_threshold + progress * cfg.late_open_threshold
@@ -259,11 +271,14 @@ def _heuristic_expert_action(
defensive_sacrifice = ( defensive_sacrifice = (
(discard_danger > 22.0) (discard_danger > 22.0)
& ~own_open & ~own_open
& hard_cap_allows_new_color
& (active_colors < cfg.target_open_colors) & (active_colors < cfg.target_open_colors)
& (rank_i <= 5) & (rank_i <= 5)
& (color_open_value > -18.0) & (color_open_value > -18.0)
) )
new_color_allowed = jnp.where(is_handshake, hs_open_allowed, open_allowed) new_color_allowed = (
jnp.where(is_handshake, hs_open_allowed, open_allowed) & hard_cap_allows_new_color
)
play_score = jnp.where( play_score = jnp.where(
own_open, own_open,
jnp.where( jnp.where(
+20 -1
View File
@@ -1290,10 +1290,18 @@ def cli_main(argv: list[str] | None = None) -> None:
league_run_parser = league_sub.add_parser("run") league_run_parser = league_sub.add_parser("run")
league_run_parser.add_argument("--config", required=True) league_run_parser.add_argument("--config", required=True)
gates_parser = sub.add_parser("gates")
gates_sub = gates_parser.add_subparsers(dest="gates_command", required=True)
gates_run_parser = gates_sub.add_parser("run")
gates_run_parser.add_argument("--config", required=True)
gates_repair_parser = gates_sub.add_parser("repair")
gates_repair_parser.add_argument("--config", required=True)
gates_repair_parser.add_argument("--run-dir", required=True)
args = parser.parse_args(argv) args = parser.parse_args(argv)
cfg = ( cfg = (
load_config(args.config, overrides=parse_overrides(args.set)) load_config(args.config, overrides=parse_overrides(args.set))
if hasattr(args, "config") and args.command != "league" if hasattr(args, "config") and args.command not in {"league", "gates"}
else None else None
) )
if args.command == "rollout-smoke": if args.command == "rollout-smoke":
@@ -1378,6 +1386,17 @@ def cli_main(argv: list[str] | None = None) -> None:
run_dir = run_league(args.config) run_dir = run_league(args.config)
print(run_dir) print(run_dir)
elif args.command == "gates":
if args.gates_command == "run":
from lost_cities_jax.gates import run_gates
run_dir = run_gates(args.config)
print(run_dir)
elif args.gates_command == "repair":
from lost_cities_jax.gates import repair_gates
run_dir = repair_gates(args.config, args.run_dir)
print(run_dir)
def _create_run_dir(cfg: JaxPPOConfig) -> Path: def _create_run_dir(cfg: JaxPPOConfig) -> Path:
+19
View File
@@ -0,0 +1,19 @@
from __future__ import annotations
from lost_cities_jax.gates import load_gates_config
def test_gates_config_loads_repair_settings():
cfg = load_gates_config("configs/jax_ppo/gates-1-2.yaml")
assert cfg.gate2_pass_threshold == 0.55
assert len(cfg.exploiters) == 3
assert cfg.repair.max_cycles == 3
assert cfg.repair.league_template == "configs/jax_ppo/league-v1.yaml"
assert cfg.repair.evaluation_games == 2000
def test_gates_smoke_config_uses_smoke_repair_template():
cfg = load_gates_config("configs/jax_ppo/gates-1-2-smoke.yaml")
assert cfg.repair.max_cycles == 1
assert cfg.repair.league_template == "configs/jax_ppo/league-smoke.yaml"
assert cfg.repair.guard_max_steps_rate == 1.0
+72
View File
@@ -10,10 +10,12 @@ import pytest
from lost_cities_jax import legal_action_mask, reset from lost_cities_jax import legal_action_mask, reset
from lost_cities_jax.engine import current_hand_sorted, decode_action, reset_from_order from lost_cities_jax.engine import current_hand_sorted, decode_action, reset_from_order
from lost_cities_jax.opponents import ( from lost_cities_jax.opponents import (
HeuristicExpertConfig,
discard_only_action, discard_only_action,
heuristic_balanced_action, heuristic_balanced_action,
heuristic_cautious_action, heuristic_cautious_action,
heuristic_expert_action, heuristic_expert_action,
make_heuristic_expert_policy,
) )
from lost_cities_jax.ppo import ( from lost_cities_jax.ppo import (
JaxPPOConfig, JaxPPOConfig,
@@ -178,6 +180,76 @@ def test_expert_avoids_discarding_immediately_useful_opponent_card():
assert hand[hand_slot] != dangerous assert hand[hand_slot] != dangerous
def test_expert_cap2_blocks_third_open_color():
state = _manual_state(
p0_hand=[
_hs_card(2),
_rank_card(2, 7),
_rank_card(2, 8),
_rank_card(2, 9),
_rank_card(2, 10),
_rank_card(3, 2),
_rank_card(3, 3),
_rank_card(4, 2),
],
p0_board=[_rank_card(0, 2), _rank_card(1, 2)],
to_move=0,
)
policy = make_heuristic_expert_policy(HeuristicExpertConfig(max_open_colors=2))
action = int(policy(state, jnp.int32(0), jax.random.PRNGKey(1)))
hand_slot, place_type, _ = [int(x) for x in decode_action(jnp.asarray(action))]
hand = [int(x) for x in current_hand_sorted(state, 0)]
opened_new_color = (
place_type == PLAY and state.col_len[0, hand[hand_slot] // CARDS_PER_COLOR] == 0
)
assert not bool(opened_new_color)
def test_expert_cap3_allows_third_but_blocks_fourth_open_color():
cap3 = make_heuristic_expert_policy(HeuristicExpertConfig(max_open_colors=3))
third_state = _manual_state(
p0_hand=[
_hs_card(2),
_rank_card(2, 7),
_rank_card(2, 8),
_rank_card(2, 9),
_rank_card(2, 10),
_rank_card(3, 2),
_rank_card(3, 3),
_rank_card(4, 2),
],
p0_board=[_rank_card(0, 2), _rank_card(1, 2)],
to_move=0,
)
action = int(cap3(third_state, jnp.int32(0), jax.random.PRNGKey(1)))
hand_slot, place_type, _ = [int(x) for x in decode_action(jnp.asarray(action))]
hand = [int(x) for x in current_hand_sorted(third_state, 0)]
assert place_type == PLAY
assert third_state.col_len[0, hand[hand_slot] // CARDS_PER_COLOR] == 0
fourth_state = _manual_state(
p0_hand=[
_hs_card(3),
_rank_card(3, 7),
_rank_card(3, 8),
_rank_card(3, 9),
_rank_card(3, 10),
_rank_card(4, 2),
_rank_card(4, 3),
_rank_card(4, 4),
],
p0_board=[_rank_card(0, 2), _rank_card(1, 2), _rank_card(2, 2)],
to_move=0,
)
action = int(cap3(fourth_state, jnp.int32(0), jax.random.PRNGKey(1)))
hand_slot, place_type, _ = [int(x) for x in decode_action(jnp.asarray(action))]
hand = [int(x) for x in current_hand_sorted(fourth_state, 0)]
opened_new_color = (
place_type == PLAY and fourth_state.col_len[0, hand[hand_slot] // CARDS_PER_COLOR] == 0
)
assert not bool(opened_new_color)
@pytest.mark.parametrize( @pytest.mark.parametrize(
"policy_name", ["discard_only", "heuristic_balanced", "heuristic_cautious"] "policy_name", ["discard_only", "heuristic_balanced", "heuristic_cautious"]
) )