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
+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.engine import current_hand_sorted, decode_action, reset_from_order
from lost_cities_jax.opponents import (
HeuristicExpertConfig,
discard_only_action,
heuristic_balanced_action,
heuristic_cautious_action,
heuristic_expert_action,
make_heuristic_expert_policy,
)
from lost_cities_jax.ppo import (
JaxPPOConfig,
@@ -178,6 +180,76 @@ def test_expert_avoids_discarding_immediately_useful_opponent_card():
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(
"policy_name", ["discard_only", "heuristic_balanced", "heuristic_cautious"]
)