Files
SirRoboGarage/DevControlBot_garage/AGENTS.md
T
SirStone 305c3977a6 Add PLAN.md rule, garage .gitignore, remove hardcoded server port hint
- 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.
2026-10-03 16:39:02 +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/, tests/, or out/
  • 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

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 but go() until real logic is written.

Build artifacts / binaries

  • Rule: never write a binary, log or build output into this garage. The runBot task builds into a mktemp -d throwaway dir and removes it with an EXIT/INT/TERM trap, so no out/ directory is ever created here.
  • Rule: never commit binaries or nimble cache dirs (~/.nimble, nimbledeps/). nimbledeps/ only appears with nimble 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_botapi is the only setup step. (The package was renamed from tankroyale_botapi; the old name is gone.) Exception: the runBot task derives --path: flags at run time from nimble path <pkg>, because the compiler's bundled nimblepath="$home/.nimble/pkgs2/" only resolves when $HOME is 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 .gitignore claims "all builds go to *_garage/out/". That is no longer true for this garage — do not rely on that rule, and do not add an out/ directory just to satisfy it. (Root .gitignore is 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.sh performs a quiet RELEASE build and runs the bot with the bundled DevControlBot.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 --json flag: 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)). --debug selects the classic debug build (nim c, full hints/diagnostics). The flag is stripped by the wrapper in any position and reaches the runBot task 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 runBot must be invoked from DevControlBot/, the directory containing the .nimble. From DevControlBot_garage/ it fails with Could not find a file with a .nimble extension inside the specified directory.

  • compile/build are reserved Nimble builtin names — hence runBot.

  • Without a Tank Royale server at SERVER_URL the run fails with [start] Cannot connect ... Connection refused and 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 inside start(): SERVER_URL and SERVER_SECRET (robocode_tankroyale_botapi.nim:424-425, documented at :396-397) and BOT_NAME / BOT_VERSION / BOT_AUTHORS / … in bot_info.nim:117-130 (used when no JSON metadata is found). That is the supported mechanism the official BotLauncher uses; DevControlBot.nim calls plain start(bot, jsonPath) and needs no change.
  • The runBot task makes those variables present for local runs (see the .env section). 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 runBot task's shell, not in DevControlBot.sh, so it works for both ./DevControlBot.sh and a direct nimble runBot and the wrapper stays a thin wrapper. $DEVCONTROLBOT_ENV_FILE overrides 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: a NAME=value line is dropped when NAME is already set ([ -n "${NAME+x}" ], set-but-maybe-empty), and only the still-unset names are set -a-exported and sourced. Do not "simplify" this into a plain set -a; . .env — sourcing does the opposite and would let the file beat the launcher.
  • Accepts FOO=bar and export FOO=bar, strips CR (CRLF), skips blanks, # comments and any line that is not a plain NAME=value identifier.
  • Never print values and never use set -x: report variable names only.
  • Never commit it — it holds secrets. The root .gitignore covers ModularBot_garage/.env only and does not ignore this one; the rule to add (ask the user before touching the root file) is DevControlBot_garage/DevControlBot/.env.

Failure reporting / exit codes

  • Rule: an expected failure must never surface as a Nimble/NimScript exception. exec raises on any non-zero exit, printing a stack trace and an escaped copy of runScript. So the shell always exits 0 and writes the real code to $DEVCONTROLBOT_STATUS_FILE; DevControlBot.sh exports 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): 0 clean, 1 no game server at SERVER_URL (expected), 2 dependency not installed, 3 compile failure, anything else = the bot's own code. Each prints its own short, actionable line; compile failures keep the raw compiler errors.
  • Running nimble runBot directly (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, no tests/config.nims, and the .nimble has exactly one task (runBot) — the old test, compileBot and setupVendor tasks are gone. nimble test does not exist; do not invoke it and do not document it.
  • Convention when tests are added (repo-wide, see ../../AGENTS.md and common_libs/test_framework/README.md):
    • create DevControlBot_garage/tests/ with tests/config.nims containing --path:"../../common_libs" (depth must match the garage location), one test<concern>.nim per concern (e.g. test_basic_battle.nim);

    • add a test task to DevControlBot.nimble running nim 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) and OscillatorBot (fights back); standard call is runBattle(@[myBotDir, adversaryDir], rounds = 10).

    • env vars: TR_SERVER_JAR, TR_BATTLE_RUNNER. Failures surface as OSError (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 runBot compiles (fails only on "Cannot connect" without a server) and leaves the garage tree unchanged (ls DevControlBot_garage still shows only AGENTS.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