Files
SirRoboGarage/DevControlBot_garage/AGENTS.md
T
SirStone de0dd50d6e Purge AGENTS.md of code explanation; keep inventory and diagrams
AGENTS.md is now an inventory of what exists plus the mermaid behaviour
diagrams — not a narration of the code. The source and its doc comments are
the carrier of truth for how the bot works; a prose retelling in AGENTS.md
only goes stale against them.

Removed: blocks that duplicated what the source already says, a stale line
that contradicted DevControlBot.sh's actual no-argument behaviour, and the
prior bug-fix history (which is a record of the past, not guidance for the
next change). 384 -> 238 lines.

Kept byte-identical: the behaviour diagrams, the rules (git, version/tag,
colour convention), the build and run commands, and the physics and
coordinates references.

Two constraints stay as one-liners, because they are the two ways this bot
has actually been broken: command the radar BEFORE go(), and keep all radar
logic in modules/radar.nim.
2026-10-04 16:55:05 +02:00

239 lines
12 KiB
Markdown

# DevControlBot — Garage Agent Guide
> ## ⚠️ READ THIS FIRST — THIS GARAGE IS *INTENTIONALLY* NOT THE ROOT TEMPLATE
>
> **The layout and format described in the repo-root [`../../AGENTS.md`](../../AGENTS.md)
> template do NOT apply to `DevControlBot_garage/`.** The root template documents a
> `src/<bot>.nim` / `tests/` / `out/` convention for garages. **This garage deviates
> from it on purpose and must stay that way.**
>
> **Do NOT, even to "fix" a perceived inconsistency:**
> - create `src/` or `out/`, or put anything inside `DevControlBot/` for testing
> (the one exception is the intended `DevControlBot_garage/tests/`, see
> "Layout of this folder" — it does not exist yet, create it only when the
> first test is actually written)
> - add a `config.nims`, `nimble.paths`, `--path` flags, or a vendor/ directory
> - restructure `DevControlBot/` to match the root template
>
> Builds go to a `mktemp -d` throwaway dir that is removed on exit — nothing is
> ever written here. The root `AGENTS.md` and its folder conventions are for the
> **other** garages; here it is context only (issue tracker, Nim conventions), and
> is **NOT a layout contract for this folder**. If the root template and this file
> disagree about layout, **this file wins.**
> ## ⚠️ DO NOT TOUCH `PLAN.md`
>
> **[`PLAN.md`](PLAN.md) is the user's personal working notes file.** The user
> maintains it and edits it exclusively.
>
> - Agents must **NEVER** modify, rewrite, reformat, move, delete or commit
> changes to `PLAN.md` — unless the user explicitly asks in that conversation.
> - Reading it is **not** assumed or expected; do not open it by default.
> - It **IS** tracked in git and is intended to be committed (deliberately added
> in commit `59fad00b`). Do **NOT** gitignore it and do **NOT**
> `git rm --cached` it.
## Start here
The code is the source of truth; this file is an inventory of what exists and how
the pieces interact — not a description of what the code does. If the two
disagree, the code wins and this file is stale.
The human-facing description lives in [README.md](README.md). Repo-wide rules live
in [`../../AGENTS.md`](../../AGENTS.md) (issue tracker, Nim conventions, test
framework) — read it for that context, but note again: **its folder-layout
template does not govern this garage.**
## Conventions
### Git rule for this garage
- **EVERYTHING** under `DevControlBot_garage/` gets committed — sources, modules,
docs, `PLAN.md` — **unless it is explicitly listed in a `.gitignore`**.
- This includes files that look like scratch or generated output, and files an agent
did not create: if not gitignored, they belong in the commit.
- Do not silently leave a file untracked, and do **not** add entries to
`.gitignore` to avoid committing something. The only things that stay out are
those the user has deliberately gitignored (e.g. `.env`).
### Version bumps and tags — the user's decision
Agents **MUST NOT** bump the version in `DevControlBot.nimble` or
`DevControlBot.json`, and **MUST NOT** create git tags. A version bump and its tag
are honoured **only** when the user explicitly asks in that conversation; a bump is
never a side effect of ordinary work or of a commit. Never amend, rewrite or retag
an existing commit or tag.
### Scope and colour
- This bot's code lives **only** inside `DevControlBot_garage/`. Do not edit other
garages to make this bot work, and do not reuse another bot's game logic
without explicit permission; shared, bot-agnostic code belongs in
`common_libs/`, and only with permission.
- Team colour convention: **white = not programmed, black = radar active**.
- Provenance: ModularBot / common_libs (Davide Cappellini, Apache-2.0).
### Constraints worth remembering
Not explanations — real invariants that broke things when violated:
- **Command the radar BEFORE `go()`** — a real bug the other way round.
- All radar logic is `modules/radar.nim`, in one piece. Do not split it back into
harness/lock/melee modules.
## Layout of this folder
```
DevControlBot_garage/
├── AGENTS.md # this file
├── README.md # human-facing description
├── PLAN.md # the user's own notes — never edit (see header)
├── .gitignore # ignores .env
├── DevControlBot/
│ ├── DevControlBot.nim # bot type + entry point (isMainModule)
│ ├── DevControlBot.json # bot metadata
│ ├── DevControlBot.nimble # single task: runBot
│ ├── DevControlBot.sh # BotLauncher entry point (thin wrapper)
│ ├── .env # OPTIONAL local config; secrets, never commit
│ └── modules/
│ └── radar.nim # all radar logic
└── tests/ # INTENDED, does not exist yet — create it on
# the first real test
```
- `DevControlBot/` holds only what the bot needs to run.
- `tests/` is a sibling of `DevControlBot/`, never inside it, and holds **test
code worth keeping**: drivers, harnesses, reusable battle/probe scripts.
- **Throwaway output does NOT go here or anywhere in the repo — it goes to
`/tmp`**: logs, temporary binaries, dumps, telemetry, scratch output.
- No `out/`, no binaries, no `nimbledeps/` here. Builds are throwaway by design.
## Build and run
Use the folder task, never raw `nim c`:
```sh
cd DevControlBot_garage/DevControlBot
nimble runBot # quiet release build + run
```
Or via the wrapper, which works from any cwd:
```sh
./DevControlBot/DevControlBot.sh # bundled metadata
./DevControlBot/DevControlBot.sh my.json # first positional arg, must end in .json
./DevControlBot/DevControlBot.sh --debug # classic debug build (hints + diagnostics)
./DevControlBot/DevControlBot.sh --help # usage, exits 0 without building
```
- **No arguments means RUN, not usage.** Usage prints only for `-h` / `--help`.
There is no `--json` flag; a first positional ending in `.json` overrides the
metadata, anything else is forwarded to the bot verbatim.
- **Gotcha:** `nimble runBot` must run from `DevControlBot/` (where the
`.nimble` lives); from `DevControlBot_garage/` it fails with
`Could not find a file with a .nimble extension...`. `compile`/`build` are
reserved Nimble builtin names — hence `runBot`.
- **Config is environment-only.** The bot API's `start()` reads `SERVER_URL` /
`SERVER_SECRET` itself; **never add env-reading code to `DevControlBot.nim`.**
The optional `.env` (loaded by the `runBot` task, `$DEVCONTROLBOT_ENV_FILE`
overrides the path) is local-runs-only and **never overrides a variable already
present in the environment** — the BotLauncher's values always win. Never print
its values, never `set -x`, never commit it.
- Dependency setup is `nimble install robocode_tankroyale_botapi`; dependencies
come from the global nimble store. Do not vendor, do not add
`nimble.paths` / `nimble develop`.
- Without a game server at `SERVER_URL` the run fails with
`[start] Cannot connect ... Connection refused`. That is expected locally, not a
bug.
- Exit codes are distinct because BotLauncher branches on them: `0` clean,
`1` no server (expected), `2` dependency missing, `3` compile failure,
anything else = the bot's own code.
## Tests
There are **no tests yet** — no `tests/` directory and no `test` task; `nimble test`
does not exist. When the first test is written, follow the repo-wide convention in
[`../../AGENTS.md`](../../AGENTS.md) and
`common_libs/test_framework/README.md`: `tests/config.nims` with
`--path:"../../common_libs"`, one `test<concern>.nim` per concern, a `test` task
in the `.nimble`, and a guard-first block so tests pass with no Java present.
## Coordinates & angles (source: robocode.dev/articles/coordinates-and-angles.html)
Source: https://robocode.dev/articles/coordinates-and-angles.html (Tank Royale docs)
- Cartesian coordinate system; (0, 0) is the bottom-left corner of the arena.
- Y up is implied by the origin being bottom-left (page never states it explicitly); X east is confirmed via the angle rules below.
- Angles follow classic trig (unlike original Robocode, whose 0/360 was north and 90 east).
- 0°/360° = east; 90° = north; 180° = west; 270° = south.
- Positive angles go counterclockwise; turning right decreases the angle (clockwise).
- Full turn = 360°.
- NOT stated on this page: distance formula, bearing/angle-between-two-points formula, angle normalization/wrapping, worked example numbers.
## Behaviour diagrams
**CURRENT STATE, not a design sketch.** These describe the code as it is today.
**Diagram 1 — one tick. Owned by `DevControlBot/DevControlBot.nim` (`run`, lines
34-40) and `DevControlBot/modules/radar.nim` (`onScan`, `commandRadar`) — update
this diagram when either file changes.** The ordering is the whole point: the
command is issued BEFORE `go()` so it travels with this tick's intent, and
events arrive AFTER, so a scan can only ever steer the NEXT tick.
```mermaid
sequenceDiagram
participant Run as run loop
participant Radar as radar.nim
participant Bot as bot + API
participant Srv as server
Run->>Radar: commandRadar<br/># one command, every tick, no idle branch
Radar-->>Bot: setRadarTurnRate<br/>LOCK turn + 5deg overshoot,<br/>or +45deg sweep
Run->>Bot: go<br/># intent sent here, FIRST
Bot->>Srv: tick intent<br/>radar turn only,<br/>no move, no fire
Srv-->>Bot: tick result<br/>+ pending ScannedBotEvent
Bot->>Radar: onScannedBot -> onScan x, y<br/># AFTER go: remembers only
Radar-->>Radar: target position stored,<br/>tick counter reset
Note over Run,Radar: that remembered target is used<br/>by the NEXT commandRadar
```
**Diagram 2 — radar behaviour. Owned by `DevControlBot/modules/radar.nim`
(`commandRadar`, lines 70-81) — update this diagram when that file changes.**
SWEEP is not a separate lifecycle: it is the same single command the lock
replaces, so switching costs no ticks.
```mermaid
stateDiagram-v2
[*] --> SWEEP
state "SWEEP<br/>360 sweep at 45 deg/tick,<br/>full revolution every 8 ticks" as SWEEP
state "LOCKED<br/>servo onto target bearing<br/>PLUS 5 deg overshoot" as LOCKED
SWEEP --> LOCKED: "getEnemyCount is 1<br/>AND target seen within<br/>FreshScanTurns 1"
LOCKED --> SWEEP: "enemy count is not 1,<br/>or target stale beyond 10 ticks,<br/>or lost"
LOCKED --> LOCKED: "scan every tick<br/>because the radar crosses<br/>the bearing"
```
The 5 deg overshoot is the reason the lock works: the radar sweeps ACROSS the
bearing instead of parking on it, so a fresh `ScannedBotEvent` arrives every
tick. Turning by the bare error, or holding at rate 0 inside a deadzone, stops
the scans and loses the target until a sweep re-finds it. Never replace the
overshoot with a deadzone hold.
## Physics (source: robocode.dev/articles/physics.html)
Source: https://robocode.dev/articles/physics.html (Tank Royale docs)
- Turns/rounds 1-based; each round restarts at turn 1. Distance units: floating-point double.
- Acceleration: +1 unit/turn; deceleration: 2 units/turn (braking 2x faster than accelerating).
- Speed: v = a * t; max speed 8 units/turn. Velocity direction == bot heading.
- Distance: d = v * t.
- Rotation in degrees. Base max turn rate: 10 - 3/4*|v| deg/turn (10 deg/turn standing still; 4 deg/turn at max speed 8).
- Gun rotation max 20 deg/turn (added to bot's current rate).
- Radar rotation max 45 deg/turn (added to gun's current rate).
- Firepower: min 0.1, max 3; energy cost subtracted from bot energy.
- Bullet damage: 4 * firepower, plus 2 * (firepower - 1) if firepower > 1.
- Bullet speed: 20 - 3 * firepower units/turn (19.7 at fp 0.1; 11 at fp 3); constant per shot.
- Gun heat gain: 1 + firepower/5. Cannot fire while heat > 0. Gun starts at heat 3 each round.
- Energy gain on bullet hit: 3 * firepower.
- Collision with bot/wall stops the bot, except when moving away from the bot that hit it.
- Bot-vs-bot collision: 0.6 damage to each.
- Ramming (moving forward into another bot): both take damage; rammer gets ramming kill bonus. Wall damage: |v|/2 - 1, clamped at 0 if negative.
- NOT stated on this page: coordinate system/axes/units, Y up or down, angle origin & clockwise/CCW sense, distance/bearing formulas, angle normalization/wrapping, max energy, per-turn energy regain from inactivity.