mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-11 00:14:00 +00:00
feat(training): add generation 5 curriculum
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
"""Offline checks for the generation-5 stage configuration and gates."""
|
||||
|
||||
import unittest
|
||||
|
||||
import generation5
|
||||
|
||||
|
||||
def flag_value(flags: list[str], name: str) -> str:
|
||||
index = flags.index(name)
|
||||
return flags[index + 1]
|
||||
|
||||
|
||||
class Generation5ConfigTests(unittest.TestCase):
|
||||
def test_stage_sequence_and_lineage(self) -> None:
|
||||
self.assertEqual([stage["number"] for stage in generation5.STAGES], [4, 5, 6])
|
||||
state = generation5.fresh_state()
|
||||
checkpoint = generation5.resume_checkpoint(
|
||||
state, stage_index=0, attempt=0, foundation=generation5.FOUNDATION_CHECKPOINT
|
||||
)
|
||||
self.assertEqual(checkpoint, generation5.FOUNDATION_CHECKPOINT)
|
||||
|
||||
def test_start_state_probabilities_leave_random_remainder(self) -> None:
|
||||
for stage in generation5.STAGES:
|
||||
flags = stage["flags"]
|
||||
total = sum(
|
||||
float(flag_value(flags, name))
|
||||
for name in (
|
||||
"--kickoff-chance",
|
||||
"--near-goal-chance",
|
||||
"--air-drill-chance",
|
||||
"--air-intercept-chance",
|
||||
)
|
||||
)
|
||||
with self.subTest(stage=stage["name"]):
|
||||
self.assertLessEqual(total, 1.0)
|
||||
|
||||
def test_only_league_stage_requests_pool(self) -> None:
|
||||
self.assertEqual(
|
||||
[stage["name"] for stage in generation5.STAGES if stage.get("league_pool")],
|
||||
["league"],
|
||||
)
|
||||
self.assertEqual(flag_value(generation5.STAGES[2]["flags"], "--opponent-mode"), "league")
|
||||
|
||||
def test_telemetry_floors_fail_closed_on_missing_metric(self) -> None:
|
||||
ok, failures = generation5.telemetry_passes(
|
||||
generation5.STAGES[0], {"rollout/upright_fraction": 1.0}
|
||||
)
|
||||
self.assertFalse(ok)
|
||||
self.assertIn("rollout/forward_motion_fraction missing", failures)
|
||||
|
||||
def test_match_gate_only_blocks_clear_regression(self) -> None:
|
||||
self.assertTrue(
|
||||
generation5.match_passes({"wins_a": 40, "wins_b": 54, "episodes": 100})
|
||||
)
|
||||
self.assertFalse(
|
||||
generation5.match_passes({"wins_a": 40, "wins_b": 55, "episodes": 100})
|
||||
)
|
||||
|
||||
def test_evaluation_goal_rate_counts_either_scorer(self) -> None:
|
||||
self.assertEqual(
|
||||
generation5.evaluation_goal_rate(
|
||||
{"wins_a": 45, "wins_b": 35, "draws": 20, "episodes": 100}
|
||||
),
|
||||
0.8,
|
||||
)
|
||||
|
||||
def test_physical_side_imbalance_exposes_broken_player_slot(self) -> None:
|
||||
self.assertEqual(
|
||||
generation5.physical_side_imbalance(
|
||||
{
|
||||
"physical_team_wins": {"team_0": 90, "team_1": 5},
|
||||
"episodes": 100,
|
||||
}
|
||||
),
|
||||
0.85,
|
||||
)
|
||||
self.assertEqual(
|
||||
generation5.physical_side_imbalance(
|
||||
{
|
||||
"physical_team_wins": {"team_0": 42, "team_1": 38},
|
||||
"episodes": 100,
|
||||
}
|
||||
),
|
||||
0.04,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user