SOURCE / PINNED RELEASE
Made of little things.
Pursuit PIT
- Release
- 565f4981bdc3…
- Author-recorded commit
- 197afdbed44b…
- License
- LICENSE
- Author’s source reference
- nostr://npub12lm3rxsva8kfempyjkxupt09e02n3fezurkyadyqfejdc5e0r2kscqty6r/wss%3A%2F%2Fgit.napplet.soy%2F/n-951414e7fd7
Archive hash verified: 3b0485589942fd5c…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
// Headless check of the core loop: a scripted pursuer chases the suspect and tries to PIT it.
// It also measures the "always a route" rule: the most highway lanes traffic ever blocks
// in any 50 m stretch near the chase. Run with: bun tools/bot.ts [runs]
import { LANES, ROUTE_BAND, Sim, forwardSpeed, wrapAngle, type Input } from '../src/game/sim.ts';
function bot(sim: Sim): Input {
const p = sim.player, t = sim.suspect;
const gap = t.s - p.s;
// Aim beside the suspect's rear quarter, then lean into it.
const side = p.x <= t.x ? -1 : 1;
const close = gap < 7 && gap > 1;
const targetX = close ? t.x + side * 1.2 : gap < 25 ? t.x + side * 2.2 : t.x;
const wantYaw = Math.max(-0.25, Math.min(0.25, (targetX - p.x) * 0.12 - p.vx * 0.05));
const steer = Math.max(-1, Math.min(1, (wantYaw - wrapAngle(p.yaw)) * 6));
const closing = forwardSpeed(p) - forwardSpeed(t);
const throttle = gap > 20 ? 1 : closing > (gap - 3) * 0.8 ? -0.4 : 1;
return { throttle, steer };
}
const runs = Number(process.argv[2] ?? 10);
const results: Record<string, number> = {};
let worstBlock = 0, blockedSamples = 0, samples = 0, rampMerged = 0;
for (let r = 0; r < runs; r++) {
const sim = new Sim(1);
let pits = 0, step = 0;
const seenRamp = new Set<number>();
while (sim.outcome === 'running' && sim.time < 240) {
sim.step(1 / 120, bot(sim));
for (const e of sim.events) if (e.type === 'pit') pits++;
sim.events.length = 0;
for (const c of sim.cars) if (c.ramp !== null) seenRamp.add(c.id);
if (step++ % 30 === 0) {
for (let s = sim.player.s - 50; s < sim.player.s + 300; s += 10) {
const n = sim.lanesUsed(s, s + ROUTE_BAND).size;
worstBlock = Math.max(worstBlock, n);
if (n > LANES - 2) blockedSamples++;
samples++;
}
}
}
rampMerged += [...seenRamp].filter((id) => sim.cars.some((c) => c.id === id && c.ramp === null && c.lane < LANES)).length;
const key = sim.time >= 240 ? 'timeout' : sim.outcome;
results[key] = (results[key] ?? 0) + 1;
const traffic = sim.cars.filter((c) => c.kind === 'traffic').length;
console.log(`run ${r}: ${key} at ${sim.time.toFixed(1)}s, pits=${pits}, gap=${sim.gap.toFixed(0)}m, traffic=${traffic}, rampCars=${seenRamp.size}`);
}
console.log(results);
console.log(`worst lanes blocked in any ROUTE_BAND stretch: ${worstBlock}/${LANES}; samples over ${LANES - 2}: ${blockedSamples}/${samples}; ramp cars merged and still present: ${rampMerged}`);
