mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-12 21:32:03 +00:00
fix(physics): make upright a real state, and actually start ships on the floor
Six rounds of reward shaping (~700M steps) failed to produce upright ground driving. A critical review of the simulation rather than the reward found why: 1. The hull was a 1x1x4 box with inertia (1,1,1) and no restoring torque anywhere, so belly-down and rolled-90 were geometrically identical resting states. "Upright" was not a physically distinguished state at all - the reward was paying for a property the simulation did not have. 2. ~65% of episodes spawned ships via _random_position, which samples Y uniformly over the full 18m volume (mean ~8.7m). The measured airborne_fraction ~0.44 was largely that spawn distribution, and every ground-handling term fades out above 3m, so the shaping being tuned barely ever applied. 3. air_drill_chance 0.20 spawned deliberately unreachable-without-climbing states in the stage meant to teach ground driving, and its own air_touch_fraction (0.0002) shows the drills were never solved. Fixes land in the physics and the task distribution, not the reward: - ship.tscn: hull 1x1x4 -> 1.6x0.6x4 so it has one stable resting face; inertia (1,1,1) -> (7,1,7), physically correct for the hull, making tumbling reluctant while keeping yaw snappy. - ship.gd: new altitude-faded righting torque (spring-damper toward belly-down, faded out by 3m so aerials keep full attitude freedom). This is the grav-plating analogue of Rocket League's auto-righting and helps human pilots land cleanly too. - training_mode.gd: new ground_start_chance branch spawning ships level and resting on the floor with a floor-level ball - the state the handling stage's rewards are actually written for. - generation5.py: ground-start-chance 0.50, air-drill-chance 0.20 -> 0.0. Reward terms are left exactly as they were; they should finally pull in a direction the ship can go.
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
+31
-1
@@ -122,6 +122,35 @@ STANDING_ARGS = ["--ent-coef", "0.01", "--entropy-floor"]
|
||||
# so it likely cannot measure the behaviour the 0.45 floor was meant to
|
||||
# capture. Re-baseline that floor from what the new signal reports rather
|
||||
# than from another round of reshaping.
|
||||
#
|
||||
# Round 6 (2026-08-16): round 5 read grounded_upright_fraction 0.050 /
|
||||
# 0.069 / 0.052 — when the ship touches the floor it is upright about 1
|
||||
# time in 17 — and the user's own observation was "it spends the vast
|
||||
# majority of the time on its side, driving upwards towards the ball". A
|
||||
# critical review of the *simulation* rather than the reward found why six
|
||||
# rounds of shaping could never work:
|
||||
#
|
||||
# 1. The hull was a 1x1x4 box with inertia (1,1,1) and no restoring
|
||||
# torque anywhere, so belly-down and rolled-90 were geometrically
|
||||
# identical resting states. "Upright" was not a physically
|
||||
# distinguished state at all — the reward was paying for a property
|
||||
# the simulation did not have.
|
||||
# 2. ~65% of episodes spawned ships from _random_position, which samples
|
||||
# Y uniformly over the full 18m volume (mean ~8.7m). The measured
|
||||
# airborne_fraction ~0.44 was largely that spawn distribution, and
|
||||
# every ground-handling term fades out above 3m, so the shaping
|
||||
# being tuned barely ever applied.
|
||||
# 3. air_drill_chance 0.20 spawned deliberately unreachable-without-
|
||||
# climbing states in the stage meant to teach ground driving, and its
|
||||
# own air_touch_fraction (0.0002) shows the drills were never solved.
|
||||
#
|
||||
# Fixes land in the physics and the task distribution instead of the
|
||||
# reward: an altitude-faded righting torque plus a flat-bottomed hull and
|
||||
# realistic inertia (ship.gd / ship.tscn) make belly-down a genuine
|
||||
# attractor, ground_start_chance 0.50 actually starts the ship on the
|
||||
# floor, and air-drill-chance goes to 0. The reward terms already built
|
||||
# are left exactly as they were — they should finally pull in a direction
|
||||
# the ship can go.
|
||||
HANDLING_REWARD_FLAGS = [
|
||||
"--velocity-to-ball-weight", "0.04",
|
||||
"--forward-velocity-to-ball-weight", "0.15",
|
||||
@@ -145,8 +174,9 @@ STAGES = [
|
||||
"--opponent-mode", "self_play",
|
||||
"--kickoff-chance", "0.15",
|
||||
"--near-goal-chance", "0.25",
|
||||
"--air-drill-chance", "0.20",
|
||||
"--air-drill-chance", "0.0",
|
||||
"--air-intercept-chance", "0.0",
|
||||
"--ground-start-chance", "0.50",
|
||||
*HANDLING_REWARD_FLAGS,
|
||||
],
|
||||
# Conservative catastrophe floors, not claims of mastery. Tail values
|
||||
|
||||
@@ -1,263 +1,6 @@
|
||||
{
|
||||
"stage_index": 0,
|
||||
"attempt": 2,
|
||||
"status": "blocked",
|
||||
"log": [
|
||||
{
|
||||
"stage_index": 0,
|
||||
"stage_number": 4,
|
||||
"stage_name": "handling",
|
||||
"experiment": "20260814-1939-gen5-s4-handling",
|
||||
"attempt": 0,
|
||||
"telemetry_tail": {
|
||||
"rollout/air_touch_fraction": 0.0004599999897181988,
|
||||
"rollout/airborne_fraction": 0.43958361899852755,
|
||||
"rollout/ep_len_mean": 137.3482799835205,
|
||||
"rollout/ep_rew_mean": -9.079601858139037,
|
||||
"rollout/forward_motion_fraction": 0.2240593806952238,
|
||||
"rollout/goal_rate": 0.5860000020265579,
|
||||
"rollout/grounded_upright_fraction": 0.049944999784231184,
|
||||
"rollout/mean_altitude": 4.377092701435089,
|
||||
"rollout/productive_air_touch_fraction": 3.999999910593033e-05,
|
||||
"rollout/upright_fraction": 0.2692298571616411,
|
||||
"rollout/vertical_thrust_mean": 0.11654999937745743
|
||||
},
|
||||
"telemetry_failures": [
|
||||
"rollout/goal_rate=0.5860 < 0.8000",
|
||||
"rollout/upright_fraction=0.2692 < 0.4500",
|
||||
"rollout/forward_motion_fraction=0.2241 < 0.2500"
|
||||
],
|
||||
"evaluation_goal_failures": [
|
||||
"easy.json: goal_rate=0.780 < 0.800"
|
||||
],
|
||||
"side_balance_failures": [],
|
||||
"eval": {
|
||||
"timestamp": "2026-08-15T00:56:25+00:00",
|
||||
"model_a": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260814-1939-gen5-s4-handling.json",
|
||||
"model_b": "/home/jcreek/ai-training/CosmicClash/Game/bots/promoted/easy.json",
|
||||
"seed": 1,
|
||||
"episodes": 100,
|
||||
"wins_a": 47,
|
||||
"wins_b": 31,
|
||||
"draws": 22,
|
||||
"side_results": {
|
||||
"a_team_0": {
|
||||
"wins_a": 26,
|
||||
"wins_b": 16,
|
||||
"draws": 8
|
||||
},
|
||||
"a_team_1": {
|
||||
"wins_a": 21,
|
||||
"wins_b": 15,
|
||||
"draws": 14
|
||||
}
|
||||
},
|
||||
"physical_team_wins": {
|
||||
"team_0": 41,
|
||||
"team_1": 37
|
||||
},
|
||||
"win_rate_a": 0.47
|
||||
},
|
||||
"evals": [
|
||||
{
|
||||
"timestamp": "2026-08-15T00:56:25+00:00",
|
||||
"model_a": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260814-1939-gen5-s4-handling.json",
|
||||
"model_b": "/home/jcreek/ai-training/CosmicClash/Game/bots/promoted/easy.json",
|
||||
"seed": 1,
|
||||
"episodes": 100,
|
||||
"wins_a": 47,
|
||||
"wins_b": 31,
|
||||
"draws": 22,
|
||||
"side_results": {
|
||||
"a_team_0": {
|
||||
"wins_a": 26,
|
||||
"wins_b": 16,
|
||||
"draws": 8
|
||||
},
|
||||
"a_team_1": {
|
||||
"wins_a": 21,
|
||||
"wins_b": 15,
|
||||
"draws": 14
|
||||
}
|
||||
},
|
||||
"physical_team_wins": {
|
||||
"team_0": 41,
|
||||
"team_1": 37
|
||||
},
|
||||
"win_rate_a": 0.47
|
||||
}
|
||||
],
|
||||
"decision": "fail"
|
||||
},
|
||||
{
|
||||
"stage_index": 0,
|
||||
"stage_number": 4,
|
||||
"stage_name": "handling",
|
||||
"experiment": "20260815-0156-gen5-s4-handling-retry1",
|
||||
"attempt": 1,
|
||||
"telemetry_tail": {
|
||||
"rollout/air_touch_fraction": 0.0001599999964237213,
|
||||
"rollout/airborne_fraction": 0.4403798085451126,
|
||||
"rollout/ep_len_mean": 142.34064038085938,
|
||||
"rollout/ep_rew_mean": -5.766011684894562,
|
||||
"rollout/forward_motion_fraction": 0.22267090499401093,
|
||||
"rollout/goal_rate": 0.5380800016522408,
|
||||
"rollout/grounded_upright_fraction": 0.06886999988555909,
|
||||
"rollout/mean_altitude": 4.340883392333985,
|
||||
"rollout/productive_air_touch_fraction": 3.999999910593033e-05,
|
||||
"rollout/upright_fraction": 0.300410619109869,
|
||||
"rollout/vertical_thrust_mean": 0.060687998470733875
|
||||
},
|
||||
"telemetry_failures": [
|
||||
"rollout/goal_rate=0.5381 < 0.8000",
|
||||
"rollout/upright_fraction=0.3004 < 0.4500",
|
||||
"rollout/forward_motion_fraction=0.2227 < 0.2500"
|
||||
],
|
||||
"evaluation_goal_failures": [],
|
||||
"side_balance_failures": [],
|
||||
"eval": {
|
||||
"timestamp": "2026-08-15T07:12:56+00:00",
|
||||
"model_a": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260815-0156-gen5-s4-handling-retry1.json",
|
||||
"model_b": "/home/jcreek/ai-training/CosmicClash/Game/bots/promoted/easy.json",
|
||||
"seed": 1,
|
||||
"episodes": 100,
|
||||
"wins_a": 48,
|
||||
"wins_b": 32,
|
||||
"draws": 20,
|
||||
"side_results": {
|
||||
"a_team_0": {
|
||||
"wins_a": 22,
|
||||
"wins_b": 17,
|
||||
"draws": 11
|
||||
},
|
||||
"a_team_1": {
|
||||
"wins_a": 26,
|
||||
"wins_b": 15,
|
||||
"draws": 9
|
||||
}
|
||||
},
|
||||
"physical_team_wins": {
|
||||
"team_0": 37,
|
||||
"team_1": 43
|
||||
},
|
||||
"win_rate_a": 0.48
|
||||
},
|
||||
"evals": [
|
||||
{
|
||||
"timestamp": "2026-08-15T07:12:56+00:00",
|
||||
"model_a": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260815-0156-gen5-s4-handling-retry1.json",
|
||||
"model_b": "/home/jcreek/ai-training/CosmicClash/Game/bots/promoted/easy.json",
|
||||
"seed": 1,
|
||||
"episodes": 100,
|
||||
"wins_a": 48,
|
||||
"wins_b": 32,
|
||||
"draws": 20,
|
||||
"side_results": {
|
||||
"a_team_0": {
|
||||
"wins_a": 22,
|
||||
"wins_b": 17,
|
||||
"draws": 11
|
||||
},
|
||||
"a_team_1": {
|
||||
"wins_a": 26,
|
||||
"wins_b": 15,
|
||||
"draws": 9
|
||||
}
|
||||
},
|
||||
"physical_team_wins": {
|
||||
"team_0": 37,
|
||||
"team_1": 43
|
||||
},
|
||||
"win_rate_a": 0.48
|
||||
}
|
||||
],
|
||||
"decision": "fail"
|
||||
},
|
||||
{
|
||||
"stage_index": 0,
|
||||
"stage_number": 4,
|
||||
"stage_name": "handling",
|
||||
"experiment": "20260815-0812-gen5-s4-handling-retry2",
|
||||
"attempt": 2,
|
||||
"telemetry_tail": {
|
||||
"rollout/air_touch_fraction": 0.0002199999950826168,
|
||||
"rollout/airborne_fraction": 0.4502894285917282,
|
||||
"rollout/ep_len_mean": 141.1317198638916,
|
||||
"rollout/ep_rew_mean": -4.833696460247039,
|
||||
"rollout/forward_motion_fraction": 0.2373267139494419,
|
||||
"rollout/goal_rate": 0.5526000013351441,
|
||||
"rollout/grounded_upright_fraction": 0.051729999739676714,
|
||||
"rollout/mean_altitude": 4.42696008014679,
|
||||
"rollout/productive_air_touch_fraction": 7.999999821186065e-05,
|
||||
"rollout/upright_fraction": 0.2735673332810402,
|
||||
"rollout/vertical_thrust_mean": 0.08471799899730831
|
||||
},
|
||||
"telemetry_failures": [
|
||||
"rollout/goal_rate=0.5526 < 0.8000",
|
||||
"rollout/upright_fraction=0.2736 < 0.4500",
|
||||
"rollout/forward_motion_fraction=0.2373 < 0.2500"
|
||||
],
|
||||
"evaluation_goal_failures": [
|
||||
"easy.json: goal_rate=0.740 < 0.800"
|
||||
],
|
||||
"side_balance_failures": [],
|
||||
"eval": {
|
||||
"timestamp": "2026-08-15T13:29:47+00:00",
|
||||
"model_a": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260815-0812-gen5-s4-handling-retry2.json",
|
||||
"model_b": "/home/jcreek/ai-training/CosmicClash/Game/bots/promoted/easy.json",
|
||||
"seed": 1,
|
||||
"episodes": 100,
|
||||
"wins_a": 44,
|
||||
"wins_b": 30,
|
||||
"draws": 26,
|
||||
"side_results": {
|
||||
"a_team_0": {
|
||||
"wins_a": 21,
|
||||
"wins_b": 18,
|
||||
"draws": 11
|
||||
},
|
||||
"a_team_1": {
|
||||
"wins_a": 23,
|
||||
"wins_b": 12,
|
||||
"draws": 15
|
||||
}
|
||||
},
|
||||
"physical_team_wins": {
|
||||
"team_0": 33,
|
||||
"team_1": 41
|
||||
},
|
||||
"win_rate_a": 0.44
|
||||
},
|
||||
"evals": [
|
||||
{
|
||||
"timestamp": "2026-08-15T13:29:47+00:00",
|
||||
"model_a": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260815-0812-gen5-s4-handling-retry2.json",
|
||||
"model_b": "/home/jcreek/ai-training/CosmicClash/Game/bots/promoted/easy.json",
|
||||
"seed": 1,
|
||||
"episodes": 100,
|
||||
"wins_a": 44,
|
||||
"wins_b": 30,
|
||||
"draws": 26,
|
||||
"side_results": {
|
||||
"a_team_0": {
|
||||
"wins_a": 21,
|
||||
"wins_b": 18,
|
||||
"draws": 11
|
||||
},
|
||||
"a_team_1": {
|
||||
"wins_a": 23,
|
||||
"wins_b": 12,
|
||||
"draws": 15
|
||||
}
|
||||
},
|
||||
"physical_team_wins": {
|
||||
"team_0": 33,
|
||||
"team_1": 41
|
||||
},
|
||||
"win_rate_a": 0.44
|
||||
}
|
||||
],
|
||||
"decision": "fail"
|
||||
}
|
||||
]
|
||||
"attempt": 0,
|
||||
"status": "in_progress",
|
||||
"log": []
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -334,6 +334,11 @@ def parse_args():
|
||||
"--air-intercept-chance", type=float, default=None,
|
||||
help="Moving high-ball interception starts aimed at a real goal (generation-5 aerial stage)",
|
||||
)
|
||||
curriculum.add_argument(
|
||||
"--ground-start-chance", type=float, default=None,
|
||||
help="Fraction of resets that spawn ships level and resting on the floor with a floor-level "
|
||||
"ball — the state the handling stage's ground rewards are written for",
|
||||
)
|
||||
curriculum.add_argument(
|
||||
"--team-size", type=int, choices=range(1, 6), default=None,
|
||||
help="Ships per team (1-5); generation-5 automated stages remain 1v1 until 2v2 evaluation exists",
|
||||
@@ -406,6 +411,7 @@ def _curriculum_kwargs(args) -> dict:
|
||||
"ball_near_goal_chance": args.near_goal_chance,
|
||||
"air_drill_chance": args.air_drill_chance,
|
||||
"air_intercept_chance": args.air_intercept_chance,
|
||||
"ground_start_chance": args.ground_start_chance,
|
||||
"team_size": args.team_size,
|
||||
"ai_tilt_penalty": args.tilt_penalty,
|
||||
"ai_ground_tilt_penalty": args.ground_tilt_penalty,
|
||||
|
||||
Reference in New Issue
Block a user