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

12 KiB

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 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 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. Repo-wide rules live in ../../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:

cd DevControlBot_garage/DevControlBot
nimble runBot                                  # quiet release build + run

Or via the wrapper, which works from any cwd:

./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 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.

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.

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.