Files
SirRoboGarage/tools/robocode_shim/LEGACY_BOTS.md
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

12 KiB
Raw Permalink Blame History

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 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

# 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

  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

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>.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

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.