mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-11 00:14:00 +00:00
chore(training): add air_touch_bonus_weight and restart stage-5 intercepts
air_approach_weight alone didn't move productive_air_touch_fraction after a further 180M steps (360M cumulative across all six Stage-5 attempts): an unredirected air-intercept ball falls short of the goal from gravity and just lands on the floor, so the already-solved ground game collects the same episode reward whether or not anything touched the ball in the air. air_touch_bonus_weight adds a conjunctive event bonus on top of ball_touch_reward for a touch that's both genuinely aerial and goal-directed, targeting the actual measured behaviour instead of only the approach to it.
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
@@ -60,6 +60,29 @@ extends AIController3D
|
||||
# vector rather than the planar-only one, since a real aerial requires
|
||||
# pitching away from level.
|
||||
@export var air_approach_weight := 0.0
|
||||
# Event bonus, conjunctive with the same goal-direction alignment already
|
||||
# gating ball_touch_reward: extra payout for a touch that is BOTH genuinely
|
||||
# aerial (ball.y > AIR_TOUCH_HEIGHT, matching the productive_air_touch_
|
||||
# fraction telemetry definition exactly) AND goal-directed. air_approach_
|
||||
# weight alone did not move productive_air_touch_fraction (still 0.0 after
|
||||
# a further 180M steps, 360M cumulative) because nothing in the reward ever
|
||||
# made touching the ball while still airborne worth more than the
|
||||
# alternative every policy already had available for free: let gravity pull
|
||||
# an unredirected air-intercept ball back down (it falls well short of the
|
||||
# goal's ~0-1.5m height band over the required flight distance, so it does
|
||||
# not auto-score) and then collect the same goal_reward/ball_touch_reward
|
||||
# via the already-dominant, already-solved ground game once it lands. This
|
||||
# is deliberately NOT the standalone height-only bonus generation 4 ruled
|
||||
# out (see TRAINING.md's "why no air-touch reward" note, added to close the
|
||||
# RLGym wall-bounce exploit): it only pays scaled by the same alignment
|
||||
# dot-product as the base term, so batting the ball in a non-productive
|
||||
# direction earns nothing extra, same anti-farming shape as ball_touch_
|
||||
# reward itself. Also safe from that specific exploit on distributional
|
||||
# grounds: air-intercept spawns are central (x in [-8,8], z in [-10,10],
|
||||
# arena half-extents 18/27) and air-drill spawns keep AIR_DRILL_BALL_WALL_
|
||||
# CLEARANCE from every wall, so neither state can be solved by bouncing off
|
||||
# one.
|
||||
@export var air_touch_bonus_weight := 0.0
|
||||
@export var ball_velocity_to_goal_weight := 0.004
|
||||
# Per-tick penalty scaled by distance to the ball (full value at the arena's
|
||||
# far diagonal, 0 on top of the ball). Run04 lesson: with idling worth a flat
|
||||
@@ -504,14 +527,16 @@ func _on_ship_body_entered(body: Node) -> void:
|
||||
var to_goal := attack_goal_position - ball.global_position
|
||||
if to_goal.length_squared() > 0.0001 and ball.linear_velocity.length_squared() > 0.0001:
|
||||
alignment = clampf(ball.linear_velocity.normalized().dot(to_goal.normalized()), 0.0, 1.0)
|
||||
reward += ball_touch_reward * lerpf(ball_touch_direction_floor, 1.0, alignment)
|
||||
var touch_payout := ball_touch_reward * lerpf(ball_touch_direction_floor, 1.0, alignment)
|
||||
if air_touch_bonus_weight > 0.0 and ball.global_position.y > AIR_TOUCH_HEIGHT:
|
||||
touch_payout += air_touch_bonus_weight * alignment
|
||||
reward += touch_payout
|
||||
_ticks_since_ball_touch = 0
|
||||
|
||||
# Telemetry only (see get_info's air_touch_fraction) — not a reward term.
|
||||
# Generation 4 deliberately doesn't reward high touches directly (see
|
||||
# TRAINING.md's "why no air-touch reward" note); this just measures
|
||||
# whether the air-drill state setter is producing genuine aerial
|
||||
# contests, so a future decision to add one is data-driven.
|
||||
# air_touch_fraction/productive_air_touch_fraction (see get_info) share
|
||||
# their AIR_TOUCH_HEIGHT/alignment definitions 1:1 with air_touch_bonus_
|
||||
# weight above by design — the reward now targets exactly the behaviour
|
||||
# the telemetry measures.
|
||||
_touches += 1
|
||||
if ball.global_position.y > AIR_TOUCH_HEIGHT:
|
||||
_air_touches += 1
|
||||
|
||||
@@ -265,7 +265,7 @@ const TRAINING_MODE_OVERRIDES := [
|
||||
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", "air_approach_weight", "wall_contact_penalty", "tilt_penalty",
|
||||
"forward_velocity_to_ball_weight", "air_approach_weight", "air_touch_bonus_weight", "wall_contact_penalty", "tilt_penalty",
|
||||
"ground_tilt_penalty", "non_forward_penalty", "grounded_upright_reward",
|
||||
"speed_reward_weight", "time_penalty", "airborne_penalty",
|
||||
]
|
||||
@@ -324,6 +324,7 @@ func _ai_default(name: String) -> Variant:
|
||||
"velocity_to_ball_weight": return 0.02
|
||||
"forward_velocity_to_ball_weight": return 0.0
|
||||
"air_approach_weight": return 0.0
|
||||
"air_touch_bonus_weight": return 0.0
|
||||
"ball_velocity_to_goal_weight": return 0.004
|
||||
"ball_distance_penalty": return 0.002
|
||||
"wall_contact_penalty": return 0.0025
|
||||
|
||||
+28
@@ -662,6 +662,34 @@ were deleted and Stage 5 restarts from Stage 4's checkpoint with the new term,
|
||||
same reasoning as every previous mechanism change: don't resume a policy
|
||||
shaped by an absent term into one where it now exists.
|
||||
|
||||
**`air_approach_weight` alone did not fix it.** A further three attempts
|
||||
(180M more steps, 360M cumulative across all six Stage-5 attempts, 2026-08-18
|
||||
to 2026-08-19) closed with `productive_air_touch_fraction` still exactly 0.0
|
||||
and `air_touch_fraction` still at noise level, while `goal_rate` kept clearing
|
||||
its (lower) floor. Working out the actual physics instead of retuning another
|
||||
number found the real gap: an unredirected `_place_air_intercept` ball
|
||||
(spawned 6-12m up, aimed at a goal whose collision box sits at ~0-1.5m) sags
|
||||
well short of the goal from gravity alone over the required flight
|
||||
distance — it does not auto-score — so it just falls to the floor, where the
|
||||
already-solved ground game collects the exact same `goal_reward`/
|
||||
`ball_touch_reward` regardless of whether anything touched it in the air.
|
||||
Nothing in the reward ever made a genuinely aerial touch worth more than
|
||||
waiting the second or two for the ball to land, so `air_approach_weight`'s
|
||||
dense closing-speed shaping had nothing to reinforce — a second,
|
||||
independent missing-incentive gap in the same stage, not a training-duration
|
||||
problem. `air_touch_bonus_weight` (`ship_ai_controller.gd`) closes it
|
||||
directly: an event bonus on top of `ball_touch_reward`, paid only for a
|
||||
touch that is both above `AIR_TOUCH_HEIGHT` and goal-directed, scaled by the
|
||||
exact same alignment factor already gating the base touch reward —
|
||||
conjunctive, not standalone, so it targets exactly the behaviour
|
||||
`productive_air_touch_fraction` measures without reopening the RLGym
|
||||
wall-bounce exploit the original "no standalone air-touch reward" decision
|
||||
(above) was written to avoid: air-intercept/air-drill spawns are kept away
|
||||
from every wall by construction. Set to 0.5 (roughly `ball_touch_reward`'s
|
||||
own magnitude) and folded into `HANDLING_REWARD_FLAGS`. The three blocked
|
||||
attempts were deleted and Stage 5 restarts from Stage 4's checkpoint again
|
||||
with both terms active.
|
||||
|
||||
Stage 6's `league` opponent mode samples a historical exported policy at each
|
||||
episode reset. Each later stage preserves the preceding shaping and adds one
|
||||
new difficulty.
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -181,10 +181,40 @@ STANDING_ARGS = ["--ent-coef", "0.01", "--entropy-floor"]
|
||||
# than continuing retry2's, same reasoning as every previous mechanism
|
||||
# change in this file: don't resume a policy shaped by an absent term into
|
||||
# one where it now exists.
|
||||
#
|
||||
# Round 8 (2026-08-19): air_approach_weight alone did not move the needle
|
||||
# either -- another full 180M-step chain (3 more attempts, 360M cumulative
|
||||
# across all six Stage-5 attempts) closed with productive_air_touch_fraction
|
||||
# still exactly 0.0 and air_touch_fraction at noise level, while goal_rate
|
||||
# kept passing its (lower) floor. Working out the physics instead of just
|
||||
# re-tuning a number found why: an unredirected air-intercept ball (spawned
|
||||
# 6-12m up, aimed at a goal whose collision box sits at ~0-1.5m) sags well
|
||||
# short of the goal from gravity alone over the required flight distance --
|
||||
# it does not auto-score -- so it simply falls to the floor, and the
|
||||
# already-solved ground game (forward_velocity_to_ball_weight, ball_touch_
|
||||
# reward, goal_reward) collects the exact same total episode reward either
|
||||
# way. Nothing ever made touching the ball while it was still genuinely
|
||||
# airborne worth more than waiting the second or two for it to land, so
|
||||
# air_approach_weight's dense closing-speed shaping had nothing to reinforce
|
||||
# -- nowhere near a training-duration problem, a second missing-incentive
|
||||
# gap in the same stage.
|
||||
#
|
||||
# air_touch_bonus_weight (ship_ai_controller.gd) closes it directly: an
|
||||
# event bonus on top of ball_touch_reward, paid only for a touch that is
|
||||
# both above AIR_TOUCH_HEIGHT and goal-directed, scaled by the exact same
|
||||
# alignment factor already gating the base touch reward -- conjunctive, not
|
||||
# standalone, so it can't be farmed by batting the ball in a useless
|
||||
# direction, and it targets exactly the behaviour productive_air_touch_
|
||||
# fraction measures instead of only the approach to it. Set to 0.5 (roughly
|
||||
# ball_touch_reward's own magnitude, so a fully-aligned aerial touch pays
|
||||
# ~1.7x a fully-aligned ground one). Also folded into HANDLING_REWARD_FLAGS
|
||||
# so Stage 6 inherits it. Restarts Stage 5 from Stage 4's checkpoint again,
|
||||
# same reasoning as every prior mechanism change here.
|
||||
HANDLING_REWARD_FLAGS = [
|
||||
"--velocity-to-ball-weight", "0.04",
|
||||
"--forward-velocity-to-ball-weight", "0.15",
|
||||
"--air-approach-weight", "0.15",
|
||||
"--air-touch-bonus-weight", "0.5",
|
||||
"--ball-distance-penalty", "0.01",
|
||||
"--ball-touch-reward", "0.7",
|
||||
"--ball-velocity-to-goal-weight", "0.06",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"stage_index": 1,
|
||||
"attempt": 2,
|
||||
"status": "blocked",
|
||||
"attempt": 0,
|
||||
"status": "in_progress",
|
||||
"log": [
|
||||
{
|
||||
"stage_index": 0,
|
||||
@@ -258,254 +258,6 @@
|
||||
],
|
||||
"reason": "Human override. Only miss was the training goal-rate floor, at 0.7731 vs 0.80; every evaluation gate passed (65-22-13 vs promoted/easy.json, 87% non-draw, 12.6% physical-side imbalance) and the goal rate improved monotonically across all three attempts (0.537 -> 0.683 -> 0.773). Also promoted to Game/bots/promoted/medium.json on the same evidence. Stage 5 resumes from this checkpoint."
|
||||
}
|
||||
},
|
||||
{
|
||||
"stage_index": 1,
|
||||
"stage_number": 5,
|
||||
"stage_name": "intercepts",
|
||||
"experiment": "20260818-1903-gen5-s5-intercepts",
|
||||
"attempt": 0,
|
||||
"telemetry_tail": {
|
||||
"rollout/air_touch_fraction": 0.00011999999731779099,
|
||||
"rollout/airborne_fraction": 0.2547863810658455,
|
||||
"rollout/ep_len_mean": 97.30996008300781,
|
||||
"rollout/ep_rew_mean": 16.281804374694826,
|
||||
"rollout/forward_motion_fraction": 0.33076866614818573,
|
||||
"rollout/goal_rate": 0.8198399951457978,
|
||||
"rollout/grounded_upright_fraction": 0.1814400002658367,
|
||||
"rollout/mean_altitude": 3.249552144050598,
|
||||
"rollout/productive_air_touch_fraction": 0.0,
|
||||
"rollout/upright_fraction": 0.7921435704231262,
|
||||
"rollout/vertical_thrust_mean": 0.06410999808754772
|
||||
},
|
||||
"telemetry_failures": [
|
||||
"rollout/productive_air_touch_fraction=0.0000 < 0.0050"
|
||||
],
|
||||
"evaluation_goal_failures": [],
|
||||
"side_balance_failures": [],
|
||||
"eval": {
|
||||
"timestamp": "2026-08-19T03:12:42+00:00",
|
||||
"model_a": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260818-1903-gen5-s5-intercepts.json",
|
||||
"model_b": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260816-2126-gen5-s4-handling-retry2.json",
|
||||
"seed": 1,
|
||||
"episodes": 100,
|
||||
"wins_a": 55,
|
||||
"wins_b": 30,
|
||||
"draws": 15,
|
||||
"side_results": {
|
||||
"a_team_0": {
|
||||
"wins_a": 25,
|
||||
"wins_b": 17,
|
||||
"draws": 8
|
||||
},
|
||||
"a_team_1": {
|
||||
"wins_a": 30,
|
||||
"wins_b": 13,
|
||||
"draws": 7
|
||||
}
|
||||
},
|
||||
"physical_team_wins": {
|
||||
"team_0": 38,
|
||||
"team_1": 47
|
||||
},
|
||||
"win_rate_a": 0.55
|
||||
},
|
||||
"evals": [
|
||||
{
|
||||
"timestamp": "2026-08-19T03:12:42+00:00",
|
||||
"model_a": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260818-1903-gen5-s5-intercepts.json",
|
||||
"model_b": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260816-2126-gen5-s4-handling-retry2.json",
|
||||
"seed": 1,
|
||||
"episodes": 100,
|
||||
"wins_a": 55,
|
||||
"wins_b": 30,
|
||||
"draws": 15,
|
||||
"side_results": {
|
||||
"a_team_0": {
|
||||
"wins_a": 25,
|
||||
"wins_b": 17,
|
||||
"draws": 8
|
||||
},
|
||||
"a_team_1": {
|
||||
"wins_a": 30,
|
||||
"wins_b": 13,
|
||||
"draws": 7
|
||||
}
|
||||
},
|
||||
"physical_team_wins": {
|
||||
"team_0": 38,
|
||||
"team_1": 47
|
||||
},
|
||||
"win_rate_a": 0.55
|
||||
}
|
||||
],
|
||||
"decision": "fail"
|
||||
},
|
||||
{
|
||||
"stage_index": 1,
|
||||
"stage_number": 5,
|
||||
"stage_name": "intercepts",
|
||||
"experiment": "20260819-0412-gen5-s5-intercepts-retry1",
|
||||
"attempt": 1,
|
||||
"telemetry_tail": {
|
||||
"rollout/air_touch_fraction": 0.00011999999731779099,
|
||||
"rollout/airborne_fraction": 0.25744442877173424,
|
||||
"rollout/ep_len_mean": 112.18267991638183,
|
||||
"rollout/ep_rew_mean": 20.06156894302368,
|
||||
"rollout/forward_motion_fraction": 0.32834857150912283,
|
||||
"rollout/goal_rate": 0.7362400003671646,
|
||||
"rollout/grounded_upright_fraction": 0.17714000023156404,
|
||||
"rollout/mean_altitude": 3.2925854358673097,
|
||||
"rollout/productive_air_touch_fraction": 0.0,
|
||||
"rollout/upright_fraction": 0.7877685700654984,
|
||||
"rollout/vertical_thrust_mean": 0.09532099807504565
|
||||
},
|
||||
"telemetry_failures": [
|
||||
"rollout/goal_rate=0.7362 < 0.7500",
|
||||
"rollout/productive_air_touch_fraction=0.0000 < 0.0050"
|
||||
],
|
||||
"evaluation_goal_failures": [],
|
||||
"side_balance_failures": [],
|
||||
"eval": {
|
||||
"timestamp": "2026-08-19T12:21:42+00:00",
|
||||
"model_a": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260819-0412-gen5-s5-intercepts-retry1.json",
|
||||
"model_b": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260816-2126-gen5-s4-handling-retry2.json",
|
||||
"seed": 1,
|
||||
"episodes": 100,
|
||||
"wins_a": 58,
|
||||
"wins_b": 33,
|
||||
"draws": 9,
|
||||
"side_results": {
|
||||
"a_team_0": {
|
||||
"wins_a": 26,
|
||||
"wins_b": 22,
|
||||
"draws": 2
|
||||
},
|
||||
"a_team_1": {
|
||||
"wins_a": 32,
|
||||
"wins_b": 11,
|
||||
"draws": 7
|
||||
}
|
||||
},
|
||||
"physical_team_wins": {
|
||||
"team_0": 37,
|
||||
"team_1": 54
|
||||
},
|
||||
"win_rate_a": 0.58
|
||||
},
|
||||
"evals": [
|
||||
{
|
||||
"timestamp": "2026-08-19T12:21:42+00:00",
|
||||
"model_a": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260819-0412-gen5-s5-intercepts-retry1.json",
|
||||
"model_b": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260816-2126-gen5-s4-handling-retry2.json",
|
||||
"seed": 1,
|
||||
"episodes": 100,
|
||||
"wins_a": 58,
|
||||
"wins_b": 33,
|
||||
"draws": 9,
|
||||
"side_results": {
|
||||
"a_team_0": {
|
||||
"wins_a": 26,
|
||||
"wins_b": 22,
|
||||
"draws": 2
|
||||
},
|
||||
"a_team_1": {
|
||||
"wins_a": 32,
|
||||
"wins_b": 11,
|
||||
"draws": 7
|
||||
}
|
||||
},
|
||||
"physical_team_wins": {
|
||||
"team_0": 37,
|
||||
"team_1": 54
|
||||
},
|
||||
"win_rate_a": 0.58
|
||||
}
|
||||
],
|
||||
"decision": "fail"
|
||||
},
|
||||
{
|
||||
"stage_index": 1,
|
||||
"stage_number": 5,
|
||||
"stage_name": "intercepts",
|
||||
"experiment": "20260819-1321-gen5-s5-intercepts-retry2",
|
||||
"attempt": 2,
|
||||
"telemetry_tail": {
|
||||
"rollout/air_touch_fraction": 0.0,
|
||||
"rollout/airborne_fraction": 0.2547037144303322,
|
||||
"rollout/ep_len_mean": 108.22539994812011,
|
||||
"rollout/ep_rew_mean": 20.78616273498535,
|
||||
"rollout/forward_motion_fraction": 0.33834266650676725,
|
||||
"rollout/goal_rate": 0.7449199994802475,
|
||||
"rollout/grounded_upright_fraction": 0.22299999982118607,
|
||||
"rollout/mean_altitude": 3.228344327926636,
|
||||
"rollout/productive_air_touch_fraction": 0.0,
|
||||
"rollout/upright_fraction": 0.7867764747142791,
|
||||
"rollout/vertical_thrust_mean": 0.03713899875985459
|
||||
},
|
||||
"telemetry_failures": [
|
||||
"rollout/goal_rate=0.7449 < 0.7500",
|
||||
"rollout/productive_air_touch_fraction=0.0000 < 0.0050"
|
||||
],
|
||||
"evaluation_goal_failures": [],
|
||||
"side_balance_failures": [],
|
||||
"eval": {
|
||||
"timestamp": "2026-08-19T21:34:06+00:00",
|
||||
"model_a": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260819-1321-gen5-s5-intercepts-retry2.json",
|
||||
"model_b": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260816-2126-gen5-s4-handling-retry2.json",
|
||||
"seed": 1,
|
||||
"episodes": 100,
|
||||
"wins_a": 56,
|
||||
"wins_b": 31,
|
||||
"draws": 13,
|
||||
"side_results": {
|
||||
"a_team_0": {
|
||||
"wins_a": 25,
|
||||
"wins_b": 16,
|
||||
"draws": 9
|
||||
},
|
||||
"a_team_1": {
|
||||
"wins_a": 31,
|
||||
"wins_b": 15,
|
||||
"draws": 4
|
||||
}
|
||||
},
|
||||
"physical_team_wins": {
|
||||
"team_0": 40,
|
||||
"team_1": 47
|
||||
},
|
||||
"win_rate_a": 0.56
|
||||
},
|
||||
"evals": [
|
||||
{
|
||||
"timestamp": "2026-08-19T21:34:06+00:00",
|
||||
"model_a": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260819-1321-gen5-s5-intercepts-retry2.json",
|
||||
"model_b": "/home/jcreek/ai-training/CosmicClash/Game/bots/20260816-2126-gen5-s4-handling-retry2.json",
|
||||
"seed": 1,
|
||||
"episodes": 100,
|
||||
"wins_a": 56,
|
||||
"wins_b": 31,
|
||||
"draws": 13,
|
||||
"side_results": {
|
||||
"a_team_0": {
|
||||
"wins_a": 25,
|
||||
"wins_b": 16,
|
||||
"draws": 9
|
||||
},
|
||||
"a_team_1": {
|
||||
"wins_a": 31,
|
||||
"wins_b": 15,
|
||||
"draws": 4
|
||||
}
|
||||
},
|
||||
"physical_team_wins": {
|
||||
"team_0": 40,
|
||||
"team_1": 47
|
||||
},
|
||||
"win_rate_a": 0.56
|
||||
}
|
||||
],
|
||||
"decision": "fail"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -360,6 +360,12 @@ def parse_args():
|
||||
help="Aerial mirror of forward-velocity-to-ball-weight: high-altitude dense reward for "
|
||||
"nose-led 3D closing speed toward the ball",
|
||||
)
|
||||
curriculum.add_argument(
|
||||
"--air-touch-bonus-weight", type=float, default=None,
|
||||
help="Event bonus on top of ball-touch-reward for a touch that is both genuinely aerial "
|
||||
"(ball above AIR_TOUCH_HEIGHT) and goal-directed, scaled by the same alignment factor "
|
||||
"as the base touch reward",
|
||||
)
|
||||
curriculum.add_argument(
|
||||
"--ball-distance-penalty", type=float, default=None,
|
||||
help="Overrides ShipAIController.ball_distance_penalty (dense per-tick cost scaled by distance to the ball)",
|
||||
@@ -425,6 +431,7 @@ def _curriculum_kwargs(args) -> dict:
|
||||
"ai_velocity_to_ball_weight": args.velocity_to_ball_weight,
|
||||
"ai_forward_velocity_to_ball_weight": args.forward_velocity_to_ball_weight,
|
||||
"ai_air_approach_weight": args.air_approach_weight,
|
||||
"ai_air_touch_bonus_weight": args.air_touch_bonus_weight,
|
||||
"ai_ball_distance_penalty": args.ball_distance_penalty,
|
||||
"ai_ball_touch_reward": args.ball_touch_reward,
|
||||
"ai_airborne_penalty": args.airborne_penalty,
|
||||
|
||||
Reference in New Issue
Block a user