- AGENTS.md: mark PLAN.md as user-owned (never modify; it stays tracked) - move the .env ignore rule to the garage root so it covers any depth - drop the hardcoded "port 7654" hint from the no-server message; the target is now reported from the resolved SERVER_URL. The 7654 fallback remains only as the upstream TR default.
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.mdtemplate do NOT apply toDevControlBot_garage/. The root template documents asrc/<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/,tests/, orout/- add a
config.nims,nimble.paths,--pathflags, or a vendor/ directory- restructure
DevControlBot/to match the root templateBuilds go to a
mktemp -dthrowaway dir that is removed on exit — nothing is ever written here. The rootAGENTS.mdand 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.mdis 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 NOTgit rm --cachedit.
Start here
This file is the working rules for the bot. The human-facing description lives in README.md — read it first.
Garage-specific rules for DevControlBot_garage/. 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
- This bot's code lives only inside
DevControlBot_garage/. Do not edit other garages to make this bot work. - Do not reuse or copy another bot's game logic (movement/scanning/firing, radar
tables, targeting heuristics) without explicit permission from the user. Shared,
bot-agnostic code belongs in
common_libs/, and only with permission. - Body, gun and radar are
WHITE(team convention: that part is not programmed yet). Colors are set once at initialization, never in the tick loop. Keep it that way: the tick loop must do nothing butgo()until real logic is written.
Build artifacts / binaries
- Rule: never write a binary, log or build output into this garage. The
runBottask builds into amktemp -dthrowaway dir and removes it with anEXIT/INT/TERMtrap, so noout/directory is ever created here. - Rule: never commit binaries or nimble cache dirs (
~/.nimble,nimbledeps/).nimbledeps/only appears withnimble develop; ignore it. - Rule: dependencies come from the global nimble store. Do not vendor a
copy or add
nimble.paths/nimble develop—nimble install robocode_tankroyale_botapiis the only setup step. (The package was renamed fromtankroyale_botapi; the old name is gone.) Exception: therunBottask derives--path:flags at run time fromnimble path <pkg>, because the compiler's bundlednimblepath="$home/.nimble/pkgs2/"only resolves when$HOMEis set. That is resolution, not vendoring; keep it. - Build/run with the folder task, never raw
nim c:
cd DevControlBot_garage/DevControlBot
nimble runBot
- Note: the root
.gitignoreclaims "all builds go to*_garage/out/". That is no longer true for this garage — do not rely on that rule, and do not add anout/directory just to satisfy it. (Root.gitignoreis not this garage's file; leave it alone unless the user asks.)
Running the bot
DevControlBot/DevControlBot.sh is the canonical entry point and works from any
cwd — it resolves SCRIPT_DIR from BASH_SOURCE, cds to the .nimble dir,
forwards args and signals to nimble runBot, and propagates the exit code.
./DevControlBot/DevControlBot.sh # from the garage root: bundled metadata
./DevControlBot/DevControlBot.sh /path.json # alternate metadata (first arg, must end in .json)
./DevControlBot/DevControlBot.sh --debug # classic debug build
./DevControlBot/DevControlBot.sh --help # usage, exits 0 without building
-
Rule: no arguments means RUN, not usage. A bare
./DevControlBot.shperforms a quiet RELEASE build and runs the bot with the bundledDevControlBot.json, exactly as if that path had been passed. Usage is printed only for an explicit-h/--help, which exits 0 without building. (An earlier version printed usage and exited 0 with no arguments — that was the bug, and it is fixed.) -
There is no
--jsonflag: the metadata path is the first positional argument and is used when it ends in.json. Anything else is forwarded to the bot verbatim. -
Build mode: default is release (
nim c -d:release, quiet: no compiler hints, no dot-progress line, just[devcontrolbot] build ok (release)).--debugselects the classic debug build (nim c, full hints/diagnostics). The flag is stripped by the wrapper in any position and reaches therunBottask through the exported env var$DEVCONTROLBOT_DEBUG(debug/release), never as a bot argument. Compiler errors are shown in both modes; a failed release build replays the captured compiler log, so it is never silent. -
With no arguments (or
--help) the wrapper prints usage and exits 0 without building. -
Gotcha:
nimble runBotmust be invoked fromDevControlBot/, the directory containing the.nimble. FromDevControlBot_garage/it fails withCould not find a file with a .nimble extension inside the specified directory. -
compile/buildare reserved Nimble builtin names — hencerunBot. -
Without a Tank Royale server at
SERVER_URLthe run fails with[start] Cannot connect ... Connection refusedand exit code 1. That is expected during local verification, not a bug.
Configuration = environment variables (the API's own mechanism)
- Rule: never add env-reading code to
DevControlBot.nim. The bot API already reads its configuration from the process environment insidestart():SERVER_URLandSERVER_SECRET(robocode_tankroyale_botapi.nim:424-425, documented at:396-397) andBOT_NAME/BOT_VERSION/BOT_AUTHORS/ … inbot_info.nim:117-130(used when no JSON metadata is found). That is the supported mechanism the official BotLauncher uses;DevControlBot.nimcalls plainstart(bot, jsonPath)and needs no change. - The
runBottask makes those variables present for local runs (see the.envsection). That is all it does; it does not interpret them.
Optional .env (local runs only)
DevControlBot/.env holds the local configuration (typically SERVER_URL and
SERVER_SECRET). Rules:
- It is optional. If it is missing the run is unchanged: no error, no non-zero exit, just one informational line and the API defaults.
- Loaded in the
runBottask's shell, not inDevControlBot.sh, so it works for both./DevControlBot.shand a directnimble runBotand the wrapper stays a thin wrapper.$DEVCONTROLBOT_ENV_FILEoverrides the path. - Precedence — CRITICAL: the environment always wins. A variable already
present in the environment is never overridden by
.env, so the official BotLauncher's values keep winning. It is achieved by filtering: aNAME=valueline is dropped whenNAMEis already set ([ -n "${NAME+x}" ], set-but-maybe-empty), and only the still-unset names areset -a-exported and sourced. Do not "simplify" this into a plainset -a; . .env— sourcing does the opposite and would let the file beat the launcher. - Accepts
FOO=barandexport FOO=bar, stripsCR(CRLF), skips blanks,#comments and any line that is not a plainNAME=valueidentifier. - Never print values and never use
set -x: report variable names only. - Never commit it — it holds secrets. The root
.gitignorecoversModularBot_garage/.envonly and does not ignore this one; the rule to add (ask the user before touching the root file) isDevControlBot_garage/DevControlBot/.env.
Failure reporting / exit codes
- Rule: an expected failure must never surface as a Nimble/NimScript
exception.
execraises on any non-zero exit, printing a stack trace and an escaped copy ofrunScript. So the shell always exits 0 and writes the real code to$DEVCONTROLBOT_STATUS_FILE;DevControlBot.shexports that path and re-exits with the code. Never mask it to 0, never let the shell's code be swallowed. - Exit codes (keep them distinct, they are what BotLauncher branches on):
0clean,1no game server atSERVER_URL(expected),2dependency not installed,3compile failure, anything else = the bot's own code. Each prints its own short, actionable line; compile failures keep the raw compiler errors. - Running
nimble runBotdirectly (no status file) intentionally falls back to the legacy behaviour: the shell exits with the real code and Nimble raises.
Test organization
- There are currently no tests in this garage. No
tests/directory, notests/config.nims, and the.nimblehas exactly one task (runBot) — the oldtest,compileBotandsetupVendortasks are gone.nimble testdoes not exist; do not invoke it and do not document it. - Convention when tests are added (repo-wide, see
../../AGENTS.mdandcommon_libs/test_framework/README.md):-
create
DevControlBot_garage/tests/withtests/config.nimscontaining--path:"../../common_libs"(depth must match the garage location), onetest<concern>.nimper concern (e.g.test_basic_battle.nim); -
add a
testtask toDevControlBot.nimblerunningnim c -r --path:../common_libs tests/test_basic_battle.nim; -
guard first, import second — tests must pass with no Java present:
import std/os if not existsEnv("TR_SERVER_JAR") or not existsEnv("TR_BATTLE_RUNNER"): echo "Skipping: TR_SERVER_JAR / TR_BATTLE_RUNNER not set" quit(0) import test_framework/test_framework -
shared adversaries:
common_libs/test_framework/adversaries/SittingDuck(passive) andOscillatorBot(fights back); standard call isrunBattle(@[myBotDir, adversaryDir], rounds = 10). -
env vars:
TR_SERVER_JAR,TR_BATTLE_RUNNER. Failures surface asOSError(bot compile failed),IOError(runner non-zero),TimeoutError(server 15s / compile 30s per bot / battle runner).
-
Onboarding — full sequence
# 1. dependency (once; already global if nimble install says "already installed")
nimble install robocode_tankroyale_botapi
# 2. run the bot (compiles to a temp dir, runs, cleans up; nothing left behind)
./DevControlBot/DevControlBot.sh
# 2b. same thing via the task — must be run from DevControlBot/
cd DevControlBot && nimble runBot
# 3. tests — none exist yet; see "Test organization" above
Checklist after touching build config:
cd DevControlBot && nimble runBotcompiles (fails only on "Cannot connect" without a server) and leaves the garage tree unchanged (ls DevControlBot_garagestill shows onlyAGENTS.md,README.md,DevControlBot/).
Layout of this folder
DevControlBot_garage/
├── AGENTS.md # this file
├── README.md # human-facing description
└── 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