- tests/: 38 pytest tests (under 10 s, no network) on synthetic races with
known answers: track snapping and lap counting, pit-stop timing (OpenF1's
pit time is the exit), Safety Car codes, the per-lap table, safe file
names, the race simulator (pace, pit loss, traffic, Safety Car laps), the
what-if engine, model data prep, and the server API. 8 deliberately
inserted bugs were each caught by a failing test.
- ci.yml: tests on every push and PR; tested pushes to master are pushed to
the Hugging Face Space (needs an HF_TOKEN secret; skipped with a warning
without it).
- refresh-data.yml: every Tuesday, downloads new races from OpenF1, commits
ml/laps_train.csv and deploys so the Space retrains. Refuses to commit if
the race count drops or the file outgrows Hugging Face's 10 MB limit.
- serve.py: no side effects on import (make_server/main), static files
served from the app folder, allow_reuse_address set before bind.
- fetch_data.py: pit-window rule extracted into pit…
Add the pit-stop what-if, a race switcher and shareable links
- Pit-stop what-if: follow a driver, press W, choose "pit at the end of
lap N for <compound>" or "stay out". The server simulates 10 laps with
the real strategy and with the alternative, and the panel shows the
projected position, the gap to each nearby rival, a calibrated chance
of being ahead, and what really happened; "Watch it" replays the
alternative as a ghost car.
- ml/: lap-time model (gradient boosting on every green-flag lap since
2023), lap-by-lap race simulator, backtests and calibration. On 30
unseen races the model beats "pace stays the same" in 27; on 451 real
pit stops it predicts the gap 10 laps later with 6.4 s error vs 8.8 s
for "gaps hold + pit loss" (Brier 0.059 vs 0.070). Full-race order
prediction does not beat the baselines yet, so the what-if looks 10
laps ahead.
- The Docker build trains the model from ml/laps_train.csv; race JSON
now embeds a per-lap table (shared lap_rows() in fetch_data.py).
- Race switcher: click the race title, the…
26 days ago
Add team radio, built-in showcase races and traced pit lanes
- Team radio: the replay plays each driver's real radio clip when the
race reaches it, with a pulsing ring on the car and a log entry. Plays
at 1x and 5x; while following a driver only their radio plays; R key
and a Radio button toggle it.
- precache.py downloads five showcase races into race_seed/ at Docker
build time so they load instantly; race data is served gzipped.
- Pit lanes are traced from a timed stop in the same session instead of
track_layouts.json, where 14 of 24 pit lanes jump across the circuit.
- Cache folders are writable by the Hugging Face runtime user (uid 1000);
backslashes are stripped from race file names.
- README: full version moves to .github/README.md (shown on GitHub); the
root README keeps the Hugging Face Space config.
- ml/: lap dataset builder and lap-time model script for the upcoming
strategy simulator (model evaluation still to run).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
F1's live-timing archive that FastF1 reads (livetiming.formula1.com/static)
now answers every request with 401 Unauthorized, so sessions loaded with no
laps or telemetry and no race could be downloaded.
- fetch_data.py: fetch positions, laps, stints, pit stops and race control
from the OpenF1 REST API (2023 onwards), keeping the same output JSON.
Race distance comes from snapping positions onto the circuit outline;
layouts that don't match the session's coordinates fall back to an
outline built from the session's fastest lap. Raise on failure instead
of returning silently.
- serve.py: schedule from OpenF1, redirect / to the simulator, report an
error when nothing was exported, ASCII startup message.
- simulator.html: years from 2023, show HTTP errors on data loads.
- Fix the prebuilt-layout lookup (it looked in race_cache/), the README
clone URL and port, and drop the fastf1 and requests-cache dependencies.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refactor code structure for improved readability and maintainability