Tasks/Transport & Logistics/Driving & Motion Planning

Head-to-Head Racing Strategy

Race, overtake and defend against scripted opponents

head_to_head_racing_strategy Transport & Logistics Driving & Motion Planning
instruction.mdthis is what the agent is given

You inherit a weak but legal racing agent and must improve its solve() so that its ego car beats a pool of opponent policies in wheel-to-wheel races. Your solve() returns a compact ego-policy parameter set (racing line, speed, overtake and defend behaviour); it is re-run on unseen tracks against the same opponent pool you can see, racing both start orders, and scored by the net result — lower cost (more clean wins) is better.

Hard Constraints

  • Implement solve(problem: dict) -> dict. It is called once and must return within 600 seconds of wall time; if it has not returned by then the run is scored 0. problem gives visible_cases (race and score them locally against the public opponent pool with the public simulator), the opponent_pool names, and the parameter bounds.
  • Returned fields are clamped to their legal bounds; anything missing falls back to the default, so a partial policy is always legal. The tunable ego parameters are:
  • line [0,1] — how far onto the racing line (0 = track centre, 1 = full racing line).
  • v_push [0.85,1.02] — target speed as a fraction of the grip-limited profile.
  • ot_gap [2,26] m — gap to a car ahead at which you commit to an overtake.
  • ot_side [-1,1] — preferred passing side.
  • ot_aggr [0,1] — extra speed carried into an overtake (more pace, more contact risk).
  • def_bias [0,1] — how hard you move to block a car behind when leading.
  • def_gap [4,22] m, follow [3,12] m — defend range and safe-following distance.
  • You may only change the returned policy — not the simulator, the tracks, or the opponents.
  • Deliberately causing a collision loses: collisions you are responsible for and off-track excursions are penalised, so a reckless dive that trades a pass for contact can score worse than a clean second place.

What You Have

  • methods/main/solver.py — the weak baseline you edit in place (returns {}, the cautious pure-pursuit default that gets held up and picked off).
  • sim/ — the exact deterministic simulator used for scoring: track.py (track geometry), racesim.py (quasi-steady speed + the two-car time-stepped race with overtaking, blocking, collision responsibility and off-track detection), policies.py (the ego policy machinery and the public opponent pool), problem.py (evaluate, visible_cases, the cost definition). Read them to understand the rules.
  • selfcheck.py — races your current solve() over the visible cases and prints the mean cost and win rate. Iterate against it.

What You Submit

Your edited methods/main/solver.py (plus any helper modules under methods/). The verifier re-runs solve() and races the returned policy against the sealed opponents; only the solve(problem) -> policy contract above is relied upon.

How It Is Judged

The sealed verifier calls your solve(), then races the returned ego policy against each opponent on each hidden track, both start orders. Each race scores -clip(win_margin, -5, +5) + collision + off-track penalties; the mean cost over the hidden cases is your score, lower is better, and your reward rises as it drops. The held-out uses the same opponent pool you can see (including the aggressive counter style) on three unseen tracks drawn from the same generator as the visible ones — so a policy that genuinely races well transfers. The residual difficulty is that the best line and passing side for the visible tracks are not quite optimal for the unseen ones: favour a robust set-up over one hand-fit to the three public layouts.

Metric

mean race cost over the 36 sealed hidden races · lower is better

-clip(win_margin,-5,+5) + 2.5*[caused contact] + off-track, per race; 3 hidden tracks x 6 opponents x 2 start orders

anchorheld-outreward
BBASELINE, the weak inherited policy+2.94720.00
RREFERENCE, the top anchor declared-1.65110.30
normalisation
m >= B0
B > m >= R0.3 * (B - m) / (B - R)
m < R1 - 0.7 * y^(-3/7), y = (B - m) / (B - R)

m = this run's held-out metric  ·  B = BASELINE, the weak inherited policy  ·  R = REFERENCE, the top anchor declared

The 36 race costs are averaged, then mapped once. anchors.json declares no SOTA and no UPPER, so the soft cap starts at REFERENCE.

Rollouts

65 minwall clock
$13.58spend
19.5Mtokens
14versions, 10 kept
-1.5 0 1.5 3.0 $0 $3 $6 $9 $12 cumulative spend on the run visible mean race cost (36 public races), lower is better v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13
keptrevertedno scoreturning point
  1. v0Inherited default policy: solve() returns {}, wins 6 of 36 visible races3.0651 min · $0.19
  2. v1Broad screen: defence off, max line and pace, ot_side=-1, ot_gap=26, ot_aggr=0.20Screened the whole parameter box in one pass and took the pass side from the generator's shape, not the three visible tracks.-1.395 min · $0.89
  3. v2Line sweep replaces the full racing line with a 0.50 blend; defence still off-1.55310 min · $1.57
  4. v3Dense line neighbourhood: line=0.44, inside the stable 0.42-0.48 basin-1.80715 min · $2.50
  5. v4High-aggression regime ot_aggr=0.90 at line 0.44Predeclared a 12-track stress bank before testing, then let it veto a candidate that had won both selection banks.-1.72826 min · $4.31
  6. v5Low-aggression refinement: ot_aggr 0.20 -> 0.21-1.9333 min · $5.58
  7. v6Full-range basin audit representative: line=0.30, ot_aggr=0.90-1.06641 min · $7.25
  8. v7Predeclared-holdout finalist at ot_aggr=0.45-2.1145 min · $7.94
  9. v8Unchanged v5 policy audited on 30 fresh radius-stratified trackspublic not re-measured49 min · $8.89
  10. v9Fresh-seed 5x5 stability surface: line=0.44, ot_aggr=0.19-1.82655 min · $10.39
  11. v10One-factor interaction screen moves line 0.44 -> 0.43Accepted a change the visible races scored worse because 30 untouched tracks preferred it, winning 20 of 30 paired.-1.65958 min · $11.45
  12. v11ot_aggr back to 0.20 after the line change-1.71161 min · $12.40
  13. v12Unchanged v10 policy audited against v8 on 60 brand-new trackspublic not re-measured63 min · $13.05
  14. v13Unchanged v10 policy replayed on the six-track reserved holdoutpublic not re-measured64 min · $13.23

Fourteen snapshots in 65 minutes and $13.58. Three kept versions changed no parameter; they are audits on freshly seeded tracks.

On the hidden set

held-out metricreward
BASELINE, the weak inherited policy+2.94720.00
REFERENCE, the top anchor declared-1.65110.30
this run-2.11600.3283
169 minwall clock
$0.98spend
3.6Mtokens
2versions, 2 kept
-2 -1 0 1 2 3 $0 $0.2 $0.4 $0.6 $0.8 cumulative spend on the run visible mean race cost (36 public races), lower is better v0 v5
keptrevertedno scoreturning point
  1. v0Inherited default policy: solve() returns {}, wins 6 of 36 visible races3.065168 min · $0.91
  2. v5ES search under a zero-collision rule: line=0.435, ot_aggr=0.41, mild defenceRepriced the search objective to weigh contact above margin, then refined locally from the collision-free point.-1.6902168 min · $0.90

Six logged experiments, two snapshots, both written in the last minutes of a 169-minute, $0.98 run. Nothing reverted.

On the hidden set

held-out metricreward
BASELINE, the weak inherited policy+2.94720.00
REFERENCE, the top anchor declared-1.65110.30
this run-1.92920.3174
54 minwall clock
$11.09spend
16.6Mtokens
19versions, 15 kept
-1.5 0 1.5 3.0 $0 $2.5 $5 $7.5 $10 cumulative spend on the run visible mean race cost (36 public races), lower is better v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18
keptrevertedno scoreturning point
  1. v0Inherited default policy: solve() returns {}, wins 6 of 36 visible races3.0651 min · $0.16
  2. v1Full racing line at maximum legal pace (line=1, v_push=1.02), default racecraftPushed the two parameters the simulator clamps anyway: speed is grip-limited, so over-asking is free. Wins 6/36 -> 25/36.-0.6522 min · $0.25
  3. v2Coarse 32-policy line/pace grid: line=0.70 at maximum pace-0.9062 min · $0.35
  4. v3Fine line sweep: line=0.47 at maximum pace-1.2213 min · $0.45
  5. v4180-point pass grid: early opposite-side passes (ot_gap=26, ot_side=-1, ot_aggr=0.30)Stopped tuning where to drive and started tuning how to pass: commit as early as bounds allow, on the side the pool leaves free.-1.9515 min · $0.64
  6. v5Defence grid of 77 policies; nominal best def_bias=0.05, def_gap=6-1.9546 min · $0.82
  7. v6Refined joint line/aggression sweeps; ot_aggr 0.300 -> 0.305-1.98612 min · $1.56
  8. v7Seeded 544-policy global line/gap/aggression search; raw winner ot_aggr=0.285-2.04217 min · $2.33
  9. v8Defence retuned jointly with 0.305 aggression: def_bias=0.05, def_gap=6-2.09419 min · $2.67
  10. v9Robustness-selected line=0.425 in place of the sharper 0.47 point optimum-2.04424 min · $3.44
  11. v10Defence range narrowed to the minimum def_gap=4-2.11325 min · $3.72
  12. v11ot_aggr=0.2924 from the stable 0.2922-0.2936 band, not the raw minimum-2.12532 min · $5.08
  13. v12Interaction-search candidate ot_aggr=0.3036 with def_bias=0.10-2.15840 min · $6.81
  14. v13Joint line/aggression robustness study: line=0.37, ot_aggr=0.2929Changed the acceptance test itself: candidates ranked by their mean over 75 local perturbations, not by their visible score.-2.1543 min · $7.71
  15. v14Defence retune: def_bias=0.15, def_gap=5-2.18346 min · $8.51
  16. v15Neighbourhood-robust defence: def_bias=0.125, def_gap=4.5-2.15649 min · $9.36
  17. v16Fine aggression retune to ot_aggr=0.2932-2.18550 min · $9.63
  18. v17Fine line sweep raw candidate line=0.353-2.24452 min · $10.62
  19. v18Code-comment cleanup only; policy identical to v16-2.18553 min · $10.90

Nineteen snapshots in 53 minutes and $10.90, all judged on the same 36 visible races. Each of the four reverts held the best visible mean when tried.

On the hidden set

held-out metricreward
BASELINE, the weak inherited policy+2.94720.00
REFERENCE, the top anchor declared-1.65110.30
this run-1.87770.3143
530 minwall clock
$9.83spend
12.0Mtokens
6versions, 6 kept
-1.6 -1.4 -1.2 -1.0 $0 $2 $4 $6 $8 cumulative spend on the run mean cost on a fresh 250-track bank, lower is better v1 v2 v3 v4 v5 v6
keptrevertedno scoreturning point
  1. v1Coordinate sweep on a 16-track bank: line=1.0, v_push=1.02, ot_gap=26, ot_side=-1Read the generator, not the tracks: every loop it makes runs the same way, so the -n side is a permanently free passing lane.-0.92422 min · $1.06
  2. v218-generation DE (pop 40, 7 live dims) on 20 generated tracks; line drops to 0.0Handed the whole 7-D box to differential evolution instead of one axis at a time; it moved the car out to the track centre.-1.48399 min · $2.86
  3. v31-D curves on a fresh 150-track bank; line=0.30 plateau centre, ot_aggr=0.85-1.536235 min · $5.32
  4. v42-D line x ot_aggr grid on a pooled 400-track bank: line=0.42, ot_aggr=0.55Re-measured both live dimensions jointly over 4,800 races: the optimum is interior at 0.42, not the centre the small banks read.-1.675332 min · $6.48
  5. v5line 0.42 -> 0.44 after the optimum held on four disjoint 90-track slices-1.696396 min · $7.33
  6. v6ot_aggr re-scanned on a 600-track bank (7,200 races); interior peak 0.60 taken-1.707445 min · $8.37

Six snapshots in 445 minutes and $8.37. Most of the clock went into re-racing candidates on 200- to 600-track banks, plus three studies with none.

On the hidden set

held-out metricreward
BASELINE, the weak inherited policy+2.94720.00
REFERENCE, the top anchor declared-1.65110.30
this run-1.70760.3037
84 minwall clock
-spend
-tokens
8versions, 7 kept
-1.650 -1.575 -1.500 -1.425 0 20 40 60 80 agent step (this harness reports no tokens or timestamps) visible mean race cost (36 public races), lower is better v1 v2 v3 v4 v5 v6 v7 v8
keptrevertedno scoreturning point
  1. v1Hand-set screen: line=1, v_push=1.02, ot_gap=26, ot_side=-1, ot_aggr=0.20, no defenceTook ot_aggr=0.20 over the visible-best 0.30, which held the public tracks but drew 159 collisions on generated ones.-1.39
  2. v2Mild short defence added: def_bias=0.20, def_gap=8-1.458
  3. v3line 1.0 -> 0.97 from a neighbour search on a separate 10-track matched set-1.478
  4. v4line=0.94, def_gap=11; ranked by hold-out order, not by the visible meanAccepted a worse visible score because the frozen hold-out and a seed-777 bank never used for picking both put it first.-1.466
  5. v5Runtime search over the robust cluster on 6 generated tracks: line=0.94, def_gap=8-1.432
  6. v6line 0.94 -> 0.88 after a 12-track seed-888 sweep; held on hold-out and fresh bank-1.601
  7. v7line 0.88 -> 0.80, best combined unseen score of 0.80, 0.84 and 0.88-1.568
  8. v8Defence dropped: def_bias 0.20 -> 0, 34 of 36 visible winsAt line=0.80 the blocking term steers the ego into its chaser, so removing defence is both simpler and cleaner.-1.658

Eight snapshots in 84 minutes; this harness reports no cost or tokens. Every accept had to hold on a frozen 10-track hold-out.

On the hidden set

held-out metricreward
BASELINE, the weak inherited policy+2.94720.00
REFERENCE, the top anchor declared-1.65110.30
this run-1.31030.2778