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.
12 KiB
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.
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
IBasicRobotdirectly and returnnullfromgetRobotRunnable()(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
# 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:
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
- Launch the Tank Royale GUI.
- Bots → Add bot, and select the generated bot directory
/tmp/tr_bots/<Name>(or its<Name>.json). The GUI reads the.jsonidentity file. - Repeat for each
/tmp/tr_bots/<Name>directory you want in the roster. - 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
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)
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>.shsetsSHIM_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 viaSHIM_DATA(new) orDRUSSGT_DATA(legacy, e.g.tools/ab/). - Bot selection:
SHIM_BOT_CLASS+SHIM_BOT_JAR(+ optionalSHIM_EXTRA_CPfor 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/abmachinery 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=1still 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 todisableStaticBoolean(field)(a silent no-op for bots without the field).src/robocode_shim/ClassicPeer.java—ITeamRobotPeer,StatusEventsynthesis, immediatemove/turn*semantics, re-entrantexecute()guard, delivered events recorded ingetAllEvents().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
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.