chore(training): retune stage-4 handling penalties and restart from Stage-3 foundation

Stage 4's upright/forward-motion telemetry plateaued flat across all three
blocked attempts because ground_tilt_penalty (0.003) was too weak to matter
and nothing penalized sideways/reverse motion at all. Raise
ground_tilt_penalty to 0.05 and add a new non_forward_penalty term
(ship_ai_controller.gd) that directly costs non-forward planar velocity near
the floor, independent of the ball. Delete the three blocked attempts'
checkpoints/logs/exports and reset generation5_state.json so the next run
starts fresh from the Stage-3 foundation checkpoint instead of continuing
from the drifted retry2 weights.
This commit is contained in:
Josh Creek
2026-08-09 13:09:12 +01:00
parent 005cd0c66e
commit c56f5ed1a3
14 changed files with 41 additions and 263 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+19
View File
@@ -74,6 +74,11 @@ extends AIController3D
# pitch/roll during a real aerial. Generation 5 uses this instead of raising
# the global tilt_penalty back to its pre-flight value.
@export var ground_tilt_penalty := 0.0
# Per-tick penalty on the planar-velocity component not pointed along the
# nose (sideways or reverse), independent of the ball — the mirror image of
# forward_velocity_to_ball_weight's ball-conditioned bonus. Same
# GROUND_HANDLING_HEIGHT altitude fade as ground_tilt_penalty.
@export var non_forward_penalty := 0.0
# Per-tick bonus for own speed: 0 stationary, full value (+0.24/s) at
# max_speed. Run07 lesson: after the kickoff flurry both ships parked next to
# a cornered ball — with every other dense term near zero there, standing
@@ -324,6 +329,20 @@ func _physics_process(delta):
var tilt_ground_factor: float = 1.0 - clampf(ship.global_position.y / GROUND_HANDLING_HEIGHT, 0.0, 1.0)
reward -= ground_tilt_penalty * (1.0 - ground_uprightness) * 0.5 * tilt_ground_factor
# Dense penalty: any planar velocity component not pointed along the nose
# (sideways or reverse), independent of the ball — the mirror image of
# forward_velocity_to_ball_weight's ball-conditioned bonus. Fades out with
# altitude via the same GROUND_HANDLING_HEIGHT ramp as ground_tilt_penalty.
if non_forward_penalty > 0.0 and ship.global_position.y < GROUND_HANDLING_HEIGHT:
var non_forward_planar_velocity := Vector3(ship.linear_velocity.x, 0.0, ship.linear_velocity.z)
var non_forward_planar_speed := non_forward_planar_velocity.length()
var non_forward_planar_forward := Vector3(-ship.global_transform.basis.z.x, 0.0, -ship.global_transform.basis.z.z)
if non_forward_planar_speed > 0.0001 and non_forward_planar_forward.length_squared() > 0.0001:
var forward_component: float = maxf(non_forward_planar_velocity.dot(non_forward_planar_forward.normalized()), 0.0)
var non_forward_speed: float = non_forward_planar_speed - forward_component
var non_forward_ground_factor: float = 1.0 - clampf(ship.global_position.y / GROUND_HANDLING_HEIGHT, 0.0, 1.0)
reward -= non_forward_penalty * (non_forward_speed / ship.max_speed) * non_forward_ground_factor
# Dense penalty: height above the floor (see airborne_penalty). The
# floor sits at world y = 0 (see training_mode.gd's FIELD_MIN_Y/
# _escaped bounds); normalized so the worst case is pinned at the
+2 -1
View File
@@ -251,7 +251,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",
"forward_velocity_to_ball_weight", "wall_contact_penalty", "tilt_penalty",
"ground_tilt_penalty", "speed_reward_weight", "time_penalty",
"ground_tilt_penalty", "non_forward_penalty", "speed_reward_weight", "time_penalty",
"airborne_penalty",
]
@@ -312,6 +312,7 @@ func _ai_default(name: String) -> Variant:
"wall_contact_penalty": return 0.0025
"tilt_penalty": return 0.0005
"ground_tilt_penalty": return 0.0
"non_forward_penalty": return 0.0
"speed_reward_weight": return 0.004
"time_penalty": return 0.001
"airborne_penalty": return 0.0