feat(training): add airborne_penalty and a stage-6 "unmask" curriculum run

Stage 5 (aggression) passed (41-47 vs grounded curric-s2-defend, within
the lenient gate but not yet a clear win). Rather than keep the locomotion
mask on indefinitely, stage 6 reopens full 3D controls on top of the
aggression retune and pairs it with a new dense airborne_penalty (scaled
by height above the floor) so the policy learns to prefer staying grounded
through incentives instead of a hard mask — same regime shift that
regressed stage 3, but this time with a mitigation and ~12x the training
time (~240M timesteps / ~24h vs ~20M / ~2h) to actually re-converge
instead of stalling mid-shift.

airborne_penalty follows the existing SHIP_AI_OVERRIDES pattern: default
0 (off) on ship_ai_controller.gd, exposed via train.py's new
--airborne-penalty flag, added to training_mode.gd's allow-list. Also adds
a per-stage timesteps override in curriculum.py (STAGES[n]["timesteps"])
since this is the first stage to need a different budget than the rest.
This commit is contained in:
Josh Creek
2026-07-22 21:22:27 +01:00
parent 2de3085f13
commit 1afdc301ab
5 changed files with 71 additions and 17 deletions
+2 -1
View File
@@ -188,7 +188,7 @@ const SHIP_AI_OVERRIDES := [
"ball_touch_reward", "ball_touch_cooldown_ticks", "ball_touch_direction_floor",
"velocity_to_ball_weight", "ball_velocity_to_goal_weight", "ball_distance_penalty",
"wall_contact_penalty", "tilt_penalty", "speed_reward_weight", "time_penalty",
"allow_vertical", "allow_pitch_roll",
"airborne_penalty", "allow_vertical", "allow_pitch_roll",
]
@@ -238,6 +238,7 @@ func _ai_default(name: String) -> Variant:
"tilt_penalty": return 0.002
"speed_reward_weight": return 0.004
"time_penalty": return 0.001
"airborne_penalty": return 0.0
"allow_vertical", "allow_pitch_roll": return true
_: return null