Files
SirRoboGarage/tools/robocode_shim/LEGACY_BOTS.md
T
SirStone ef16dd982d robocode_shim: generalize bridge to any legacy bot; validate 28 champions as sparring partners
Generalize the classic-Robocode shim so the hosted bot's main class, jar, extra
classpath and data directory are configurable (SHIM_BOT_CLASS / SHIM_BOT_JAR /
SHIM_EXTRA_CP / SHIM_DATA), with DrussGT kept as the default so every existing
script, fixture and generated bot dir behaves identically.

- LegacyBotBridge: generalized bridge (DrussGTBridge kept as an alias).
- BotHost: (mainClass, jar, extraJars, dataDir); disableShield -> generic
  disableStaticBoolean.
- ClassicPeer: implement ITeamRobotPeer; synthesize StatusEvent each turn;
  make move/turnBody/turnGun/turnRadar the immediate (turn-ending) variants;
  guard re-entrant execute() from event handlers; record delivered events.
- make_botdir.sh / run_bridge_battle.sh / run_smoke.sh generalized, legacy
  invocations unchanged; generated launcher uses a per-process mktemp data dir
  so concurrent bots/battles cannot clobber one classic data directory.

Validated 35 legacy bots against a real Tank Royale battle (2 rounds vs sample
SpinBot): 28 usable (27 effective + DrussGT), 5 weak-but-playing, 2 failing.
Adds robots.json (manifest) and LEGACY_BOTS.md (how-to, status, missing-API
costs). No third-party jar is committed.
2026-09-26 00:25:33 +02:00

197 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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/<Name>`
(or its `<Name>.json`). The GUI reads the `.json` identity file.
3. Repeat for each `/tmp/tr_bots/<Name>` directory you want in the roster.
4. Start a classic 1v1 (or melee) battle and add the bots as participants.
The generated `<Name>.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/<Name> 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/<jar> SHIM_BOT_CLASS=<fqcn> 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 `<Name>.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.