feat(*): add staged curriculum training with an automated stage-by-stage orchestrator

This commit is contained in:
Josh Creek
2026-07-21 12:38:51 +01:00
parent 1d539cc8c7
commit 8e3fafcc8b
7 changed files with 581 additions and 18 deletions
+52
View File
@@ -51,9 +51,60 @@ def parse_args():
parser.add_argument("--checkpoint-every", type=int, default=100_000, help="Timesteps between checkpoints")
parser.add_argument("--viz", action="store_true", help="Show game windows (debugging; slow)")
parser.add_argument("--wandb", action="store_true", help="Also log to Weights & Biases")
curriculum = parser.add_argument_group(
"curriculum", "Stage the training run — see TRAINING.md's Curriculum training section"
)
curriculum.add_argument(
"--opponent-mode",
choices=["self_play", "inert", "frozen"],
default=None,
help="self_play (default): both ships are live trainees. inert: team 1 is a "
"do-nothing placeholder (isolated scoring practice). frozen: team 1 runs a "
"fixed exported policy (--opponent-model)",
)
curriculum.add_argument("--opponent-model", default=None, help="Exported policy .json for --opponent-mode=frozen")
curriculum.add_argument(
"--draw-penalty", type=float, default=None, help="One-time penalty when an episode times out with no goal"
)
curriculum.add_argument(
"--attack-goal-bias",
type=float,
default=None,
help="0.5 = uniform between both goals (default); 1.0 = near-goal resets always target the goal team 0 attacks",
)
curriculum.add_argument("--kickoff-chance", type=float, default=None, help="Overrides kickoff_state_chance")
curriculum.add_argument("--near-goal-chance", type=float, default=None, help="Overrides ball_near_goal_chance")
curriculum.add_argument(
"--allow-vertical", action=argparse.BooleanOptionalAction, default=None, help="Allow vertical thrust (default true)"
)
curriculum.add_argument(
"--allow-pitch-roll",
action=argparse.BooleanOptionalAction,
default=None,
help="Allow pitch/roll rotation (default true)",
)
return parser.parse_args()
def _curriculum_kwargs(args) -> dict:
"""Maps train.py's curriculum flags to the --key=value args training_mode.gd's
_parse_curriculum_args() reads, omitting anything not explicitly passed so
unset flags leave Godot's own @export defaults in place."""
mapping = {
"opponent_mode": args.opponent_mode,
"opponent_model": args.opponent_model,
"draw_penalty": args.draw_penalty,
"attack_goal_bias": args.attack_goal_bias,
"kickoff_state_chance": args.kickoff_chance,
"ball_near_goal_chance": args.near_goal_chance,
"ai_allow_vertical": args.allow_vertical,
"ai_allow_pitch_roll": args.allow_pitch_roll,
}
return {key: value for key, value in mapping.items() if value is not None}
def main():
args = parse_args()
log_dir = TRAINING_DIR / "logs"
@@ -72,6 +123,7 @@ def main():
port=args.port,
show_window=args.viz,
speedup=args.speedup,
**_curriculum_kwargs(args),
)
env = VecMonitor(env)