Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
06a7cd3
docs: refresh the roadmap against the code, and the stale notes it po…
isaac0804 Oct 21, 2026
9f2fe59
docs: fix the roadmap refresh's errors; cite roadmap entries by title
isaac0804 Oct 21, 2026
c84556e
fix(engine): skip the pinned goalkeeper when its robot is not on the …
isaac0804 Oct 21, 2026
b1516df
fix(tactics): hysteresis on the shot aim in LeadAndSupport and GiveAndGo
isaac0804 Oct 21, 2026
09cbcd6
feat(examples): one attacker vs one defender on real robots or in sim
isaac0804 Oct 21, 2026
d6eb4f4
fix(engine): no goalkeeper row in debug_status when its robot is absent
isaac0804 Oct 21, 2026
30d8574
feat(run): field check on camera runs, auto-HALT on real robots
isaac0804 Oct 21, 2026
88e34a0
feat(run): robot profiles: push without a kicker, gentle carry withou…
isaac0804 Oct 21, 2026
f0b25e4
feat(examples,dashboard): per-robot hardware and RULES switch; tidier…
isaac0804 Oct 21, 2026
ecc87ae
feat(run,analysis): record field tests as SSL game logs; measure robo…
isaac0804 Oct 21, 2026
1fbce32
feat(dashboard): match timeline in the Replay and Live tabs; 15x smal…
isaac0804 Oct 21, 2026
92ba5f9
feat(dashboard): Play list of situations; possession band, switch tic…
isaac0804 Oct 21, 2026
f25b1eb
fix(refiners): an unseen ball rolls to a stop inside vision's area
isaac0804 Oct 21, 2026
693fda1
docs(examples): a test day's measurements: calibration twice, floor f…
isaac0804 Oct 21, 2026
3a164f6
fix(examples): run as documented, `pixi run python examples/<name>.py`
isaac0804 Oct 21, 2026
12f96b1
feat(dashboard): ball placement by right-click; phone layout; lighter…
isaac0804 Oct 21, 2026
898d0a1
perf(dashboard): send a replay's ticks as columns of whole numbers
isaac0804 Oct 21, 2026
d9e6f2e
fix(dashboard): a client that stops reading no longer freezes the con…
isaac0804 Oct 21, 2026
ac30fb2
fix(runner): an auto-HALT is the command of the tick that forced it
isaac0804 Oct 21, 2026
15a33d2
fix(dashboard): the auto-HALT banner clears once play resumes; HALT i…
isaac0804 Oct 21, 2026
42adde9
fix(dashboard): a columnar replay's last Play row ends at its last tick
isaac0804 Oct 21, 2026
499bc88
fix(dashboard): a robot's heading is mirrored with the view
isaac0804 Oct 21, 2026
bb3049c
fix(dashboard): a dropped stream client is closed, so its browser rec…
isaac0804 Oct 21, 2026
3d80454
fix(dashboard): a ball placement must be inside the field and the are…
isaac0804 Oct 21, 2026
a6391e4
feat(dashboard): Live shows refused commands and a stalled stream; Ha…
isaac0804 Oct 21, 2026
18762aa
feat(dashboard): a held touch opens the ball-placement menu (iPhone)
isaac0804 Oct 21, 2026
ca4e5fa
fix(refiners): with smoothing off, a second ball loss starts where th…
isaac0804 Oct 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/STRATEGY_DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,9 @@ whoever runs it.
dark strip where it had the ball), each robot's distance to the ball, and goals, fouls and shots on
top. Use it for thrashing (a robot switching tactics every few ticks), a tactic stuck committed, or
everyone chasing the ball. Friendly side only has tactic lanes (the intentions log is config_a's).
The dashboard draws the same lanes under the Replay tab's controls (click to seek; hover for what
each mark is) and live in the Live tab. Beside a replay, its Play list reads the match as
situations: who had the ball from which restart or regain, for how long, and how it ended.
Thrashing in `split_shape` against `tiki_taka_plus`: robots 2-4 flip between the attack and defence
slots about every 1.5 s, in pairs of rows at the same tick. It is not split_shape's alone: most
strategies that split attack and defence on possession take a tactic for under 1 s and revert about
Expand Down
22 changes: 22 additions & 0 deletions docs/custom_referee_gui.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,28 @@ pixi run python examples/demo_referee_gui_rsim.py # then open http://localhost
The page shows the score, current/next command, stage and time left, `designated_position`, the
field, the active profile and an event log; a button issues each command.

## Field check (camera runs)

With vision from a camera (real robots or grSim) the Live tab also shows the field check
(`utama_core/run/field_check.py`), one row per question, red / amber / green:

| Row | Fails (red) when | Warns (amber) when |
|---|---|---|
| Camera | no vision packet for 0.5 s | |
| Field size | ssl-vision's geometry differs from `full_field_dims` | no geometry packet yet |
| `<colour> <id>` (each robot we drive) | never seen, lost by vision 0.5 s, or more than 0.3 m outside `field_bounds` | |
| `<colour> <id> radio` (real only) | it answered on the radio, then stopped | it has not answered yet (the firmware may send no feedback) |
| `<colour> <id>` (any other) | | seen inside `field_bounds` but not configured: our planners cannot see it, often a wrong ID |
| Ball | | not seen, or outside `field_bounds` |

Only the red and amber rows are listed; the green ones fold into an "N ok" line that opens on
click. On real robots any red row forces `HALT`, whatever the command and whether or not the page is
open; the red banner says why. Pressing Force Start while a row is red halts again at once, so
the check is also the pre-start gate. The field is drawn as ssl-vision sees it (+x to the right),
with everything outside `field_bounds` dimmed, so a half-field test shows which half the runner
expects. Space halts; nothing on the keyboard starts the robots. The Kickoff and Free Kick
buttons are hidden when the profile has no rule that sets up a restart (goal detection only).

## Commands and what robots do

| Button | Command | Use | Robots |
Expand Down
305 changes: 116 additions & 189 deletions docs/roadmap.md

Large diffs are not rendered by default.

11 changes: 8 additions & 3 deletions docs/sim_realism.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Sim realism: closing the gap between rsim and real matches

Status: plan, 2026-10-14 (priorities added 2026-10-16). Nothing below is built yet unless it
says so.
Status: plan, 2026-10-14 (priorities added 2026-10-16; Phases 0-2 are built, 2026-10-16/17).
Nothing below is built yet unless it says so.

## Where this leads

Expand Down Expand Up @@ -89,7 +89,7 @@ friction). **Within** means kick-to-kick noise for the same robot.

| Parameter | Why it matters | From official logs? | rsim today |
|---|---|---|---|
| Sensing-to-action latency (vision, decision, radio, motor response) | Likeliest cause of the keeper gap | Only as other teams' total reaction time: from a kick to their keeper accelerating | None: commands act on the next tick |
| Sensing-to-action latency (vision, decision, radio, motor response) | Likeliest cause of the keeper gap | Only as other teams' total reaction time: from a kick to their keeper accelerating; for our robots, `tools/robot_calibration.py` on a recorded field test (command sent to motion seen) | None: commands act on the next tick |
| Ball rolling deceleration (and the switch from sliding to rolling) | Pass and shot timing; keeper reaction time | Yes: fit free-rolling segments, per match (venue carpet) | Fixed in the C++ physics (`vendor/rSim`) |
| Kick speed: between and within | How much time the keeper has; pass weight | Yes: peak ball speed after each kick, per team | Fixed |
| Kick direction error | Wide rate (6% vs 22%) | Partly: shot miss distance, pass error relative to the receiver | None: exact |
Expand Down Expand Up @@ -338,6 +338,11 @@ direction SD 3° (per robot 1.5°), ball slide 2.5–4.0 then roll 0.27–0.33 m

### Phase 2: Knobs in the sim (1–2 weeks)

All of these are built (2026-10-17): `SimNoise` draws each knob per match from its range, seeded
and recorded in the replay, and reaches `run_match`, the round-robin option, `evaluate.py` and
`pool_v1` (the v3 ranges, played as `v3_seed2` in the 2026-10-17 round-robin). A knob at zero
reproduces the old behaviour, so results played without `--sim-noise` are unchanged.

Each knob is off by default, so current results are unchanged, and each comes with a test that
pins its effect:

Expand Down
12 changes: 8 additions & 4 deletions docs/testing_gaps.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@ symptom. Adopting mypy/pyright is undecided given the codebase isn't typed to th

**6. New rules never systematically fuzzed against real match dynamics** — partly open.
`pushing` (closed, `a3f3795`) and `keeper_held_ball` (fired 10 times in 6 full matches) are
confirmed live. `ball_placement_interference` has never fired live: across ~200 placements the
longest non-placer dwell in the 0.5m stadium was 1.0s, under the rulebook's 2s grace. Not
treated as a bug; recheck if a match produces a ≥2s linger, or build an adversarial scenario.
confirmed live. `ball_placement_interference` has never fired in real play: across ~200
placements the longest non-placer dwell in the 0.5m stadium was 1.0s, under the rulebook's 2s
grace. Not treated as a bug; recheck if a real match produces a ≥2s linger, or build an
adversarial scenario. Our own sim does reach it, so it is no longer untested:
`tests/moments/placement_in_corner_traps_defender.json` (round-robin
`tournament_20261017_002337`) pins a corner placement that trapped a defender inside the
keep-out circle, and must keep passing.

## Closed (one line each)

Expand Down Expand Up @@ -56,4 +60,4 @@ treated as a bug; recheck if a match produces a ≥2s linger, or build an advers
finish timeout (`ae6a6f3`), `ball_is_loose` ignoring a legally barred enemy near our
defense area (`10fc89c`); and a same-team two-robot ball scrum
(`_teammate_already_has_ball()`, `a59a8e5`). Cross-tactic convergence in general is
roadmap item 7.
the roadmap's "Cross-tactic ball convergence".
2 changes: 2 additions & 0 deletions docs/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,5 +112,7 @@ described in [STRATEGY_DEVELOPMENT.md](STRATEGY_DEVELOPMENT.md).
| `examples/demo_referee_gui_rsim.py` | `CustomReferee` with the dashboard's referee tab, over rsim |
| `examples/demo_referee_feedback_gui.py` | The referee UI's controller-feedback panel with fake feedback, no hardware |
| `examples/demo_exhibition_road.py` | The Exhibition Road festival demo on the 4 m x 3 m field, over rsim |
| `examples/real_attacker_defender.py` | One attacker against one defender on real robots (or `--mode rsim`/`grsim`): field, camera half, IDs and each robot's working hardware in one config block; the Live tab's field check gates the start. Records every camera run as an SSL game log in `replays/field/`; `--calibrate attacker\|defender` plays fixed moves for measuring that robot |
| `tools/robot_calibration.py` | A robot's numbers from a recorded field test (`replays/field/*.log`): loop delay, speed gain and acceleration per axis (the sim's move model fitted to the camera's positions), vision noise at rest and missed detections, kick speed. Writes `<log>.calibration.json`. The same log converts to a replay with `tools/convert_ssl_log.py` |
| `examples/demo_dribbler_test.py` | One robot dribbling round a rectangle on the Exhibition Road field |
| `examples/demo_split_shape_match.py` | Visible grSim 6v6 of `split_shape` against itself |
7 changes: 7 additions & 0 deletions examples/demo_custom_referee.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# ruff: noqa: E402
"""examples/demo_custom_referee.py — visual demonstration of the CustomReferee system.

Runs entirely in-process (no network, no RSim binary needed). A scripted
Expand All @@ -23,6 +24,12 @@

from __future__ import annotations

import sys
from pathlib import Path

# Run as `pixi run python examples/<this>.py`: utama_core is not installed, so put the repo on the path.
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

import dataclasses
import math
import sys
Expand Down
7 changes: 7 additions & 0 deletions examples/demo_dribbler_test.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# ruff: noqa: E402
"""examples/demo_dribbler_test.py — Test the dribbler with one robot on the Exhibition Road field.

Run:
Expand All @@ -10,6 +11,12 @@
rectangle, releasing and reacquiring the ball each segment
"""

import sys
from pathlib import Path

# Run as `pixi run python examples/<this>.py`: utama_core is not installed, so put the repo on the path.
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

from utama_core.config.field_params import GREAT_EXHIBITION_FIELD_DIMS
from utama_core.engine.abstract_strategy import AbstractStrategy
from utama_core.engine.context import TickContext
Expand Down
7 changes: 7 additions & 0 deletions examples/demo_exhibition_road.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# ruff: noqa: E402
"""examples/demo_exhibition_road.py — Exhibition Road Festival demo.

Run:
Expand Down Expand Up @@ -27,6 +28,12 @@
advance play (human auto-advance profile).
"""

import sys
from pathlib import Path

# Run as `pixi run python examples/<this>.py`: utama_core is not installed, so put the repo on the path.
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

from utama_core.config.field_params import GREAT_EXHIBITION_FIELD_DIMS
from utama_core.custom_referee import CustomReferee
from utama_core.custom_referee.profiles.profile_loader import (
Expand Down
7 changes: 7 additions & 0 deletions examples/demo_referee_feedback_gui.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# ruff: noqa: E402
"""Preview the referee web UI controller-feedback panel without hardware.

Run:
Expand All @@ -11,6 +12,12 @@

from __future__ import annotations

import sys
from pathlib import Path

# Run as `pixi run python examples/<this>.py`: utama_core is not installed, so put the repo on the path.
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

import argparse
import math
import time
Expand Down
7 changes: 7 additions & 0 deletions examples/demo_referee_gui_rsim.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# ruff: noqa: E402
"""examples/demo_referee_gui_rsim.py — CustomReferee + web GUI + StrategyRunner (RSim).

Run:
Expand Down Expand Up @@ -26,6 +27,12 @@
5. With the human profile, the referee stays in STOP after a goal until the operator advances play.
"""

import sys
from pathlib import Path

# Run as `pixi run python examples/<this>.py`: utama_core is not installed, so put the repo on the path.
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

from utama_core.custom_referee import CustomReferee
from utama_core.custom_referee.profiles.profile_loader import load_profile
from utama_core.dashboard import attach_dashboard
Expand Down
7 changes: 7 additions & 0 deletions examples/demo_split_shape_match.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# ruff: noqa: E402
"""Visible grsim 6v6: both teams run the split-shape kernel Strategy against each other.

Not a test — a manual/visual smoke run for reviewing LeadAndSupportTactic /
Expand All @@ -14,6 +15,12 @@
Open http://localhost:8080 while this is running.
"""

import sys
from pathlib import Path

# Run as `pixi run python examples/<this>.py`: utama_core is not installed, so put the repo on the path.
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))

import time

from utama_core.custom_referee import CustomReferee
Expand Down
Loading
Loading