feat(tools): drive the real DrussGT jar outside the Robocode engine

The question was whether we can fight a genuine legacy leader bot in Tank
Royale. Answer: the API side is now PROVEN, not estimated.

KEY FINDING: the classic robocode.* API is a thin delegation layer over a
public seam. javap -c shows AdvancedRobot forwarding every call to
_RobotBase.peer (IBasicRobotPeer/IAdvancedRobotPeer), and _RobotBase.setPeer
is public final. So we do NOT need to reimplement the API: we reuse the
genuine robocode.jar and implement only the 75-method peer interface.

Consequences:
- DrussGT's 22 sources compile against the real API with ZERO unresolved
  symbols. (The literal 22-file javac fails only on two PRE-EXISTING
  duplicate classes - GFRange and Indice are declared both inline in
  DrussGunDC.java and as standalone files - and the 6 classes that ship
  without source. The jar supplies all of them, so a shim never cares.)
- Runtime smoke test PASSES: the unmodified DrussGT.jar is loaded through a
  child URLClassLoader and driven for 200 synthetic ticks, emitting movement
  intents every tick and 169 fire requests, with its thread surviving.

This is the cheap path to the 'final boss', and it also unlocks the whole
roborumble archive rather than one bot.

Traps found by measurement:
- ScannedRobotEvent's constructor order is (name, energy, bearing, distance,
  heading, velocity) - NOT heading-before-bearing. The wrong order silently
  yields distance=0, an immediate KD-tree insert and an NPE in
  EnemyMoves.predict; it hung the first smoke run.
- RobocodeFileOutputStream has a hard engine dependency (resolves
  IThreadManagerBase via ContainerBase) and throws 'ThreadManager cannot be
  null!' outside the engine, killing the bot thread. Reached from DrussGT's
  own contain() error logging, so it must be stubbed.
- robocode.RobotDeathEvent is required and was NOT in the predicted API list.
- Bullet.equals() is genuinely called for bullet identity, not just getters.

REMAINING WORK (README section 5): coordinate rotation DONE, execute()->tick
bridge DONE and proven, ThreadManager fix scoped. The main open item is the
classic motion model (setAhead distance semantics vs TR speed), estimated
1-3 days, plus event synthesis/ordering, gun heat, bullet identity, round
and radar cadence, and firing translation. NO API UNKNOWNS REMAIN.
Physics fidelity will still diverge from classic - expect to retune.

Jars stay out of git (blocked by tools/robocode_shim/.gitignore); the
genuine robocode.jar and DrussGT.jar are referenced from /tmp via
ROBOCODE_JAR / DRUSSGT_JAR.
This commit is contained in:
2026-09-20 23:52:51 +02:00
parent 76ae6170f8
commit e0bfa9b5e1
9 changed files with 1043 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env bash
# ACCEPTANCE TEST: compile the REAL, unmodified DrussGT sources against the
# genuine Robocode 1.9.5.5 API (Approach B reuses robocode.jar instead of
# re-implementing robocode.*).
#
# ROBOCODE_JAR=... DRUSSGT_SRC=... DRUSSGT_JAR=... ./compile_drussgt.sh
#
# Why two source files are excluded:
# GFRange.java and Indice.java are *decompilation artefacts*. The same two
# classes are ALSO declared inline at the bottom of DrussGunDC.java, so a
# single javac invocation over all 22 files fails with
# "duplicate class: jk.mega.dGun.GFRange/Indice" even with no shim at all.
# The original DrussGT.jar already ships GFRange.class/Indice.class, so we
# drop the redundant standalone files and let the jar supply them (together
# with the 6 genuinely-unsourced classes: JKDCUtils, NormalDistribution,
# StoreScan, DCRobotState, DCWave, Scan).
set -euo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROBOCODE_JAR="${ROBOCODE_JAR:-/tmp/robocode/install/libs/robocode.jar}"
DRUSSGT_SRC="${DRUSSGT_SRC:-/tmp/drussgt/src}"
DRUSSGT_JAR="${DRUSSGT_JAR:-/tmp/drussgt/DrussGT.jar}"
OUT="${OUT:-$HERE/out/drussgt}"
for f in "$ROBOCODE_JAR" "$DRUSSGT_JAR"; do
[[ -f "$f" ]] || { echo "missing: $f" >&2; exit 1; }
done
[[ -d "$DRUSSGT_SRC" ]] || { echo "missing source dir: $DRUSSGT_SRC" >&2; exit 1; }
mkdir -p "$OUT"
mapfile -t SRC < <(find "$DRUSSGT_SRC" -name '*.java' ! -name 'GFRange.java' ! -name 'Indice.java' | sort)
echo "javac -nowarn -cp \"$ROBOCODE_JAR:$DRUSSGT_JAR\" -d \"$OUT\" <${#SRC[@]} files>"
javac -nowarn -cp "$ROBOCODE_JAR:$DRUSSGT_JAR" -d "$OUT" "${SRC[@]}"
echo "OK: ${#SRC[@]}/22 source files compiled, $(find "$OUT" -name '*.class' | wc -l) classes emitted"
echo "(2/22 excluded as duplicate-class decompilation artefacts; supplied by DrussGT.jar)"