# Legacy champion bots as Tank Royale sparring partners The classic-Robocode bridge in `tools/robocode_shim/` is now **generalized**: it can host any unmodified classic robot jar, not just DrussGT. This document is the short version of how to use it and what was validated. The machine-readable manifest is [`robots.json`](robots.json). **No third-party jar is committed.** Every jar lives in `/tmp` and is referenced by absolute path; the repo only contains the shim, the manifest and this doc. ## Direct answer * **28 legacy champions are usable right now** (27 validated as effective + DrussGT itself). A further **5 play but are weak/degraded** (they fire and scan but landed 0 hits against the sample SpinBot). **2 fail** (Nene, Seraphim — see below). * Cost to add the top remaining failures: **~2–4 h** to add a second driver path for bots that implement `IBasicRobot` directly and return `null` from `getRobotRunnable()` (this is the only hard blocker left; it unlocks Nene and Seraphim, the two Azuma-framework melee bots), plus ~0.5–1 h to diagnose the divide-by-zero those bots hit during construction. * The weak-but-working bots need **~2–6 h of physics-divergence investigation** (they are not blocked on a missing API — see “Degraded bots” below). ## Add a bot in 3 commands ```bash # 1. download the jar (the RoboRumble archive is browsable) curl -Lo /tmp/bots/voidious.Diamond_1.8.28.jar \ http://robocode-archive.strangeautomata.com/robots/voidious.Diamond_1.8.28.jar # 2. generate the Tank Royale bot directory tools/robocode_shim/make_botdir.sh Diamond \ --class voidious.Diamond --jar /tmp/bots/voidious.Diamond_1.8.28.jar # 3. prove it plays a real Tank Royale battle (2 rounds vs the sample SpinBot) SHIM_BOTDIR=/tmp/tr_bots/Diamond tools/robocode_shim/run_bridge_battle.sh \ /home/davide/Projects/tank-royale/sample-bots/java/build/archive/SpinBot 2 ``` `make_botdir.sh` accepts `--class`, `--jar`, `--name`, `--version`, `--authors`, `--desc`, `--country`, `--gametypes`. The legacy forms still work unchanged: ```bash tools/robocode_shim/make_botdir.sh # -> /tmp/tr_bots/DrussGT (DrussGT defaults) tools/robocode_shim/make_botdir.sh /tmp/tr_bots/DrussGT # same, explicit path ``` ### GUI steps 1. Launch the Tank Royale GUI. 2. **Bots → Add bot**, and select the generated bot directory `/tmp/tr_bots/` (or its `.json`). The GUI reads the `.json` identity file. 3. Repeat for each `/tmp/tr_bots/` directory you want in the roster. 4. Start a classic 1v1 (or melee) battle and add the bots as participants. The generated `.sh` launcher selects the hosted classic robot via `SHIM_BOT_CLASS` / `SHIM_BOT_JAR` and creates its own data directory, so the GUI does not need any extra environment variables. ### One-line smoke battle for any generated bot ```bash SHIM_BOTDIR=/tmp/tr_bots/ tools/robocode_shim/run_bridge_battle.sh \ /home/davide/Projects/tank-royale/sample-bots/java/build/archive/SpinBot 2 ``` ### Offline triage (no server, ~2 s per bot) ```bash SHIM_BOT_JAR=/tmp/bots/ SHIM_BOT_CLASS= tools/robocode_shim/run_smoke.sh ``` > Caution: this synthetic harness has **false negatives** — Diamond, Dookious and > Lukious NPE there but play fine in a real battle. Only a real battle is > authoritative. ## Status table (MEASURED: 2-round battles vs sample SpinBot, embedded server) | # | Name | Style (inferred) | Status | Evidence (fired / hits / taken, 2 rounds) | |---|---|---|---|---| | — | DrussGT | DC gun + wave-surfing movement | **REFERENCE** | 141 / 17 / 0 — won 2/2 | | 1 | Diamond | pattern-matching gun + surf | **WORKS** | 50 / 18 / 1 — won 2/2 | | 2 | Dookious | surf movement + pattern/GF gun | **WORKS** | 86 / 16 / 1 — won 2/2 | | 3 | Lukious | wave-surfing movement | **WORKS** | 70 / 15 / 3 — won 2/2 | | 4 | Shadow | pattern matching (team-derived) | **WORKS** | 25 / 19 / 0 — won 2/2 | | 5 | Phoenix | pattern matching (team-derived) | **WORKS** | 50 / 17 / 0 — won 2/2 | | 6 | RougeDC | dynamic-clustering gun + surf | **WORKS** | 67 / 14 / 2 — won 2/2 | | 7 | DiamondStealer | rammer / brawler | **WORKS** | 18 / 12 / 1 — won 2/2 | | 8 | GresSuffurd | surf gun + movement | **WORKS** | 72 / 15 / 0 — won 2/2 | | 9 | KurtWaveSurfer | wave surfer | **WORKS** | 84 / 13 / 1 — won 2/2 | | 10 | CassiusClay | pattern-matching gun + surf | **WORKS** | 55 / 11 / 0 — won 2/2 | | 11 | Coriantumr | mini pattern matcher | **WORKS** | 41 / 14 / 1 — won 2/2 | | 12 | BrokenSword | mini pattern matcher | **WORKS** | 38 / 15 / 3 — won 2/2 | | 13 | WaveSurferGF | wave surfer (GF gun) | **WORKS** | 49 / 13 / 1 — won 2/2 | | 14 | WaveSurferPG | wave surfer (pattern gun) | **WORKS** | 57 / 16 / 3 — won 2/2 | | 15 | TripHammer | pattern matcher | **WORKS** | 52 / 19 / 1 — won 2/2 | | 16 | RetroGirl | perceptual pattern matcher | **WORKS** | 106 / 26 / 5 — won 2/2 | | 17 | Jen | micro wave surfer | **WORKS** | 71 / 13 / 2 — won 2/2 | | 18 | BlitzBat | micro brawler | **WORKS** | 34 / 12 / 1 — won 2/2 | | 19 | Ascendant | aggressive 1v1 mega | **WORKS** | 74 / 15 / 0 — won 2/2 | | 20 | DiamondHawk | pattern matcher | **WORKS** | 45 / 18 / 0 — won 2/2 | | 21 | YersiniaPestis | strong aggressive mega | **WORKS** | 52 / 12 / 0 — won 2/2 | | 22 | HawkOnFire | corner camper / low bot | **WORKS** | 74 / 12 / 0 — won 2/2 | | 23 | WallAvoider | micro wall-avoider (blocking style) | **WORKS** | 47 / 10 / 1 — won 2/2 | | 24 | Cigaret | mini surfer | **WORKS** | 46 / 5 / 5 — split 1-1 | | 25 | CigaretBH | mini bullet-hiding surfer | **WORKS** | 67 / 11 / 3 — split 1-1 | | 26 | Komarious | mini pattern matcher / surfer | **WORKS** | 62 / 20 / 10 — split 1-1 | | 27 | LionWWSVMvoid | wave-surfing SVM gun | **WORKS** | 62 / 12 / 12 — lost 0-2 | | 28 | FloodMini | mini flood-fill movement | WORKS_WEAK | 25 / 0 / 10 — lost 0-2 | | 29 | Aristocles | micro surfer | WORKS_WEAK | 39 / 0 / 10 — lost 0-2 | | 30 | PatternRobot | simple pattern robot | WORKS_WEAK | 8 / 0 / 2 — lost 0-2 | | 31 | LightningBug | pattern matcher | WORKS_WEAK | 36 / 0 / 10 — lost 0-2 | | 32 | Aurora | micro (team-derived) | WORKS_WEAK | 6 / 0 / 13 — lost 0-2 | | 33 | Nene | melee (Azuma framework) | **FAILS** | 0 / 0 / 14 — inert | | 34 | Seraphim | melee (Azuma framework) | **FAILS** | 0 / 0 / 14 — inert | `WORKS` = the battle completed, the bot scanned, moved and fired, and landed hits. `WORKS_WEAK` = the battle completed and the bot scanned/fired, but landed **0** hits against SpinBot; usable as a target/movement sparring partner, not as a gun-quality reference. Losing to SpinBot is not itself a failure (LionWWSVMvoid lost but is classified WORKS because it landed 12 hits and clearly fights). ## Missing-API summary Four classic-Robocode APIs were missing from the original shim. Three were **implemented in this change** because several bots failed on the same call — each is now a one-time cost of ~0.5–1 h (already paid): | API | Was missing | Cost | Unlocked | |---|---|---|---| | `robocode.robotinterfaces.peer.ITeamRobotPeer` (`getTeammates`, `isTeammate`, `broadcastMessage`, `sendMessage`, `getMessageEvents`) | `TeamRobot.setPeer()` casts the peer to it, so every `TeamRobot`-derived bot died with `ClassCastException` | **DONE** (~1 h) | Shadow, Phoenix, RougeDC, DiamondStealer, Ascendant, Aurora | | `robocode.StatusEvent` / `RobotStatus` synthesis each turn | never delivered; RougeDC busy-loops forever waiting for one (`ensureVersion` → “Got no status event! Dying now!”) | **DONE** (~1 h) | RougeDC; generally required by event-driven bots | | Non-advanced **immediate** semantics of `peer.move` / `turnBody` / `turnGun` / `turnRadar` | they only set the intent; classic performs the turn **and ends the turn** (`Robot.ahead`, `AdvancedRobot.turnRadarRightRadians`, …). Bots whose `run()` is `while (true) turnRadarRightRadians(1);` spun forever | **DONE** (~0.5 h) | Komarious, KurtWaveSurfer, Cigaret/CigaretBH, RetroGirl, Jen, WallAvoider, PatternRobot, … | | Re-entrancy guard for `execute()` called from inside an event handler | classic blocking robots call `Robot.fire()` inside `onScannedRobot`; the nested tick deadlocked | **DONE** (~0.5 h) | WallAvoider, PatternRobot | **Still missing (not implemented):** | Gap | Symptom | Bots | Cost estimate | |---|---|---|---| | Alternate driver for bots that implement `IBasicRobot`/`ITeamRobot` **directly** instead of extending `robocode.Robot` | `getRobotRunnable()` returns `null`; the bot is inert in battle. `cs.Azuma` even extends `java.io.PrintStream`. | Nene, Seraphim | **2–4 h** (second driver path: detect the null runnable and drive the bot’s own `execute()`/event listener instead). Plus **0.5–1 h** to diagnose the `ArithmeticException: divide by zero` Nene throws during construction. | | Degraded gun/movement for several micro/mini bots | they scan and fire but land 0 hits; not a missing API — almost certainly the documented physics divergences (move/turn ordering, distance bookkeeping; see README §5.9) | FloodMini, Aristocles, PatternRobot, LightningBug, Aurora | **2–6 h** of measurement/diagnosis; not blocking (they still play). | ## Isolation guarantees (parallel battles) * **Data directory:** the generated `.sh` sets `SHIM_DATA="${SHIM_DATA:-${DRUSSGT_DATA:-$(mktemp -d …)}}"` — a fresh per-process directory. Two bots, or two concurrent battles with the same bot, can never share or clobber a classic data directory. Callers can still pin a directory via `SHIM_DATA` (new) or `DRUSSGT_DATA` (legacy, e.g. `tools/ab/`). * **Bot selection:** `SHIM_BOT_CLASS` + `SHIM_BOT_JAR` (+ optional `SHIM_EXTRA_CP` for dependencies) are per-bot, so different legacy bots can run concurrently in different JVMs. * **Ports / processes:** the Tank Royale embedded server picks ephemeral ports and each battle is a separate process (the existing `tools/ab` machinery already relies on this and is unchanged). ## Generalizing the shim (what changed) * `src/robocode_shim/LegacyBotBridge.java` — the generalized bridge (the old DrussGT-specific logic, now configurable). Defaults reproduce DrussGT exactly: `SHIM_BOT_CLASS=jk.mega.DrussGT`, `SHIM_BOT_JAR=${DRUSSGT_JAR:-/tmp/drussgt/DrussGT.jar}`, `SHIM_DATA=${DRUSSGT_DATA:-/tmp/drussgt_bridge_data}`, `DRUSSGT_SHIELD=1` still keeps the EnergyDome shield on. * `src/robocode_shim/DrussGTBridge.java` — now a thin backwards-compatible alias so already-generated bot directories keep launching. * `src/robocode_shim/BotHost.java` — takes `(mainClass, jar, extraJars, dataDir)`; `disableShield()` generalized to `disableStaticBoolean(field)` (a silent no-op for bots without the field). * `src/robocode_shim/ClassicPeer.java` — `ITeamRobotPeer`, `StatusEvent` synthesis, immediate `move`/`turn*` semantics, re-entrant `execute()` guard, delivered events recorded in `getAllEvents()`. * `make_botdir.sh`, `run_bridge_battle.sh`, `run_smoke.sh` — generalized; all legacy invocations (no args, positional path, `DRUSSGT_BOTDIR`/`DRUSSGT_DATA`) still behave identically. ## Reproducing the validation ```bash export ROBOCODE_JAR=/tmp/robocode/install/libs/robocode.jar export TR_BOT_API_JAR=$HOME/Downloads/sample-bots-java-1.0.2/lib/robocode-tankroyale-bot-api-1.0.2.jar tools/robocode_shim/build.sh tools/robocode_shim/compile_drussgt.sh # DrussGT acceptance: 20/22 files, 43 classes tools/robocode_shim/run_smoke.sh # DrussGT default path unchanged: OK tools/robocode_shim/run_bridge_battle.sh \ /home/davide/Projects/tank-royale/sample-bots/java/build/archive/SpinBot 2 ``` ### MEASURED vs INFERRED * **MEASURED:** every status in the table, all fire/hit/taken counts, the missing-API symptoms (exception + method), the jar HTTP 200 downloads, the DrussGT default-path smoke/acceptance/battle. * **INFERRED:** the “style” column (from bot names/docs, not decompilation) and the cost estimates. The cause of the degraded micro/mini bots is inferred from the known physics divergences, not isolated to a single call.