mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-10 16:04:04 +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:
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
@@ -9,14 +9,14 @@ friction = 0.1
|
||||
bounce = 0.15
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_dsjou"]
|
||||
size = Vector3(1, 1, 4)
|
||||
size = Vector3(1.6, 0.6, 4)
|
||||
|
||||
[node name="Ship" type="RigidBody3D"]
|
||||
collision_layer = 1
|
||||
collision_mask = 7
|
||||
mass = 5.0
|
||||
physics_material_override = SubResource("PhysicsMaterial_ship")
|
||||
inertia = Vector3(1, 1, 1)
|
||||
inertia = Vector3(7, 1, 7)
|
||||
script = ExtResource("1_efag7")
|
||||
|
||||
[node name="Nose" type="MeshInstance3D" parent="."]
|
||||
|
||||
@@ -26,6 +26,21 @@ extends RigidBody3D
|
||||
@export var ceiling_pull_strength = 11.5 # Ceiling grav-plating strength; nets above gravity so a ship can hold a ceiling
|
||||
@export var ceiling_pull_range = 3.0 # Metres from the ceiling where pull begins
|
||||
|
||||
# Grav-plating righting torque: a spring-damper that rolls/pitches the hull
|
||||
# back toward belly-down, strongest at floor level and faded to nothing by
|
||||
# righting_range so genuine aerials keep full attitude freedom. Without it
|
||||
# "upright" is not a physically distinguished state at all — the hull is a
|
||||
# box with no restoring torque, so belly-down and rolled-90 are equally
|
||||
# stable and a policy has no dynamics-level reason to prefer either. Six
|
||||
# rounds of RL reward shaping (see TRAINING.md) failed to buy upright
|
||||
# ground handling for exactly this reason; the fix belongs in the physics,
|
||||
# not the reward. Same idea as the wall/ceiling pull above — the plating
|
||||
# orients you, not just attracts you — and it helps human pilots land
|
||||
# cleanly too.
|
||||
@export var righting_strength: float = 20.0 # Righting spring gain (0 disables)
|
||||
@export var righting_damping: float = 6.0 # Opposes tumble while righting
|
||||
@export var righting_range: float = 3.0 # Metres above the floor where righting fades out
|
||||
|
||||
# Non-tinted hull meshes, runtime-merged into one ArrayMesh by
|
||||
# _build_merged_hull() (Nose/TailFin stay separate MeshInstance3Ds since
|
||||
# _apply_team_color() retints them per-team and must keep addressing them by
|
||||
@@ -337,6 +352,7 @@ func _integrate_forces(state):
|
||||
|
||||
# === ROTATION (Turning) ===
|
||||
apply_rotation_forces(state, _current_action.rotation)
|
||||
apply_righting_torque(state)
|
||||
|
||||
# === DRAG AND LIMITS ===
|
||||
apply_drag_and_limits(state, _current_action.rotation)
|
||||
@@ -385,6 +401,31 @@ func apply_surface_pull(state: PhysicsDirectBodyState3D) -> void:
|
||||
state.apply_central_force(pull * mass)
|
||||
|
||||
|
||||
# Spring-damper torque toward belly-down (see righting_strength). The spring
|
||||
# term basis.y x UP is a world-space axis whose magnitude is sin(tilt) and
|
||||
# whose direction is the shortest rotation back to upright, so it is zero
|
||||
# when already level, peaks on its side, and — being a cross product —
|
||||
# vanishes again when perfectly inverted. The damping term is applied only
|
||||
# about that same righting axis, so it bleeds off tumble without taxing
|
||||
# deliberate yaw. Fades linearly to nothing by righting_range metres up,
|
||||
# matching every other ground-handling term's altitude ramp (see
|
||||
# ShipAIController.GROUND_HANDLING_HEIGHT).
|
||||
func apply_righting_torque(state: PhysicsDirectBodyState3D) -> void:
|
||||
if righting_strength <= 0.0:
|
||||
return
|
||||
var height_factor := 1.0 - clampf(global_position.y / righting_range, 0.0, 1.0)
|
||||
if height_factor <= 0.0:
|
||||
return
|
||||
|
||||
var righting_axis := global_transform.basis.y.cross(Vector3.UP)
|
||||
var torque := righting_axis * righting_strength
|
||||
# Damp only the component of spin around the righting axis.
|
||||
if righting_axis.length_squared() > 0.0001:
|
||||
var axis := righting_axis.normalized()
|
||||
torque -= axis * state.angular_velocity.dot(axis) * righting_damping
|
||||
state.apply_torque(torque * height_factor)
|
||||
|
||||
|
||||
func apply_rotation_forces(state: PhysicsDirectBodyState3D, rotation_input: Vector3):
|
||||
if rotation_input.length() < 0.01:
|
||||
return
|
||||
|
||||
@@ -69,6 +69,15 @@ extends GameMode
|
||||
# a real goal and ships start low behind/lateral to it, so a useful touch is
|
||||
# naturally reinforced by the existing goal-directed ball rewards.
|
||||
@export_range(0.0, 1.0) var air_intercept_chance := 0.0
|
||||
# Ground-start branch for the generation-5 handling stage: ships spawn level
|
||||
# and resting on the floor with a low, floor-level ball. Every other branch
|
||||
# samples ship Y uniformly across the full 18m volume (see _random_position),
|
||||
# so ~65% of episodes previously began at a mean altitude near 8.7m — the
|
||||
# measured airborne_fraction ~0.44 was largely that spawn distribution rather
|
||||
# than a policy preference, and the ground-handling reward terms (which all
|
||||
# fade out above 3m) barely ever applied. A stage that means to teach driving
|
||||
# has to actually start the ship on the ground.
|
||||
@export_range(0.0, 1.0) var ground_start_chance := 0.0
|
||||
|
||||
# Ships per team. Default 1 preserves every existing curriculum script's 1v1
|
||||
# behaviour unchanged; up to 5 matches ShipObservations.MAX_TEAMMATES/
|
||||
@@ -77,13 +86,19 @@ extends GameMode
|
||||
@export_range(1, 5) var team_size: int = 1
|
||||
|
||||
# Placement bounds for randomized episode starts, derived from the standard
|
||||
# enclosure (ArenaBoundary). The inset keeps a randomly oriented ship (1x1x4
|
||||
# box, worst-case half-extent ~2.05) from spawning intersecting the walls,
|
||||
# ceiling, or goal sensors.
|
||||
# enclosure (ArenaBoundary). The inset keeps a randomly oriented ship
|
||||
# (1.6x0.6x4 box, worst-case half-extent ~2.18) from spawning intersecting
|
||||
# the walls, ceiling, or goal sensors.
|
||||
const SPAWN_INSET := 2.5
|
||||
const FIELD_HALF_X := ArenaBoundary.INNER_HALF_X - SPAWN_INSET
|
||||
const FIELD_HALF_Z := ArenaBoundary.GOAL_LINE_Z - SPAWN_INSET
|
||||
const FIELD_MIN_Y := 1.5
|
||||
# Resting heights for the ground-start branch: half the ship hull's 0.6 height
|
||||
# and the ball's 0.5 radius (Godot's SphereShape3D default, see ball.tscn),
|
||||
# each plus a little clearance so bodies settle onto the floor instead of
|
||||
# spawning interpenetrated with it.
|
||||
const GROUND_START_Y := 0.35
|
||||
const GROUND_START_BALL_Y := 0.55
|
||||
const FIELD_MAX_Y := ArenaBoundary.INNER_HEIGHT - SPAWN_INSET
|
||||
# The corner curves reach at most their chord plane |x| + |z| = INNER_HALF_X
|
||||
# + INNER_HALF_Z - CORNER_RADIUS; spawns keep the same SPAWN_INSET clearance
|
||||
@@ -243,7 +258,7 @@ func _parse_eval_args() -> void:
|
||||
const TRAINING_MODE_OVERRIDES := [
|
||||
"goal_reward", "draw_penalty", "kickoff_state_chance",
|
||||
"ball_near_goal_chance", "attack_goal_bias", "air_drill_chance",
|
||||
"air_intercept_chance", "team_size",
|
||||
"air_intercept_chance", "ground_start_chance", "team_size",
|
||||
]
|
||||
# ShipAIController @export names a curriculum run may override, read as
|
||||
# --ai_<name>=<value> to avoid colliding with the names above.
|
||||
@@ -273,6 +288,7 @@ func _parse_curriculum_args() -> void:
|
||||
set(name, _typed_like(args[name], get(name)))
|
||||
var start_probability := kickoff_state_chance + ball_near_goal_chance \
|
||||
+ air_drill_chance + air_intercept_chance
|
||||
start_probability += ground_start_chance
|
||||
if start_probability > 1.0:
|
||||
push_error("TrainingMode: episode-start probabilities sum to %.3f (> 1.0)" % start_probability)
|
||||
|
||||
@@ -428,6 +444,9 @@ func _reset_episode() -> void:
|
||||
_place_air_drill()
|
||||
elif roll < kickoff_state_chance + ball_near_goal_chance + air_drill_chance + air_intercept_chance:
|
||||
_place_air_intercept()
|
||||
elif roll < kickoff_state_chance + ball_near_goal_chance + air_drill_chance \
|
||||
+ air_intercept_chance + ground_start_chance:
|
||||
_place_ground_start()
|
||||
else:
|
||||
_place_ships_random()
|
||||
_place_ball_random()
|
||||
@@ -497,6 +516,46 @@ func _place_air_drill() -> void:
|
||||
_place_body(ship, Transform3D(orientation, ship_position), Vector3.ZERO, Vector3.ZERO)
|
||||
|
||||
|
||||
# Ground start (see ground_start_chance): ships resting level on the floor,
|
||||
# yaw-only so they begin belly-down rather than needing to recover attitude
|
||||
# first, and a floor-level ball rolling slowly. This is the state the
|
||||
# handling stage's rewards are actually written for — every ground term
|
||||
# (ground_tilt_penalty, non_forward_penalty, the nose-led approach bonus)
|
||||
# fades out by GROUND_HANDLING_HEIGHT, so they only bite in states like this
|
||||
# one. The ball gets a modest planar-only velocity so it stays reachable
|
||||
# without a climb.
|
||||
func _place_ground_start() -> void:
|
||||
var ball_velocity := _random_direction()
|
||||
ball_velocity.y = 0.0
|
||||
ball_velocity = ball_velocity.normalized() * randf_range(0.0, MAX_RANDOM_BALL_SPEED * 0.5)
|
||||
var ball_position := Vector3(
|
||||
randf_range(-FIELD_HALF_X, FIELD_HALF_X),
|
||||
GROUND_START_BALL_Y,
|
||||
randf_range(-FIELD_HALF_Z, FIELD_HALF_Z)
|
||||
)
|
||||
_place_body(ball, Transform3D(Basis.IDENTITY, ball_position), ball_velocity, Vector3.ZERO)
|
||||
|
||||
var placed: Array[Vector3] = []
|
||||
for ship in ships:
|
||||
if ship in _inert_ships:
|
||||
continue
|
||||
var ship_position := Vector3.ZERO
|
||||
for _attempt in 20:
|
||||
ship_position = Vector3(
|
||||
randf_range(-FIELD_HALF_X, FIELD_HALF_X),
|
||||
GROUND_START_Y,
|
||||
randf_range(-FIELD_HALF_Z, FIELD_HALF_Z)
|
||||
)
|
||||
if _spawn_position_clear(ship_position) and _far_enough_from(ship_position, placed):
|
||||
break
|
||||
placed.append(ship_position)
|
||||
var yaw := randf_range(-PI, PI)
|
||||
_place_body(
|
||||
ship, Transform3D(Basis.from_euler(Vector3(0.0, yaw, 0.0)), ship_position),
|
||||
Vector3.ZERO, Vector3.ZERO
|
||||
)
|
||||
|
||||
|
||||
# Goal-relevant aerial intercept: a high ball is already travelling toward a
|
||||
# randomly selected goal, while ships begin low and behind/lateral to its
|
||||
# path. The generous wall clearance prevents rebound farming and an upright
|
||||
|
||||
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