A minimal Tank Royale bot that boots and stands still. Body, gun and radar are set to white once at init (team convention: white = not programmed yet); the tick loop only calls go(). - DevControlBot/ holds the single bot module, its metadata JSON, the BotLauncher entry script and the nimble file - DevControlBot.sh is a thin wrapper around the runBot nimble task: release build by default, --debug for the classic diagnostic build - dependencies resolve at run time from the global nimble store via `nimble path`; optional .env (gitignored) supplies env vars such as SERVER_URL, with existing environment variables taking precedence - build happens in a throwaway mktemp dir removed on exit; no artifacts
11 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.
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 on
ws://localhost:7654the 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 on 7654 (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