Running /tmp/tr_bots/DrussGT/DrussGT.sh manually failed with:
BotException: Required bot property 'name' is missing.
The Tank Royale Java bot API reads bot identity from BOT_* environment
variables (EnvVars: BOT_NAME, BOT_VERSION, BOT_AUTHORS, BOT_DESCRIPTION,
BOT_HOMEPAGE, BOT_COUNTRY_CODES, BOT_GAME_TYPES, BOT_PLATFORM, BOT_PROG_LANG,
BOT_INITIAL_POS). When the TR booter launches the directory it supplies those,
derived from the .json - which is why every headless battle worked - but
launching the script directly supplies nothing, so the API rejects the
handshake.
make_botdir.sh now exports them in the generated <dir>.sh (kept in sync with
the .json it writes), so the script works standalone as well as under the
booter.
Verified against a server started for the test: before, the exact error above;
after, 'Connected to: ws://localhost:4599' and the server logs
'Bot joined: DrussGT 3.1.4159'.
The unmodified DrussGT.jar connects, wave-surfs, fires and beats every
adversary we have. 5 rounds each, all rounds won:
SpinBot 542-16, Corners 823-4, Crazy 604-0, RamFire 900-0,
ModularBot 506-72.
It is genuinely surfing, not drifting or stalling. Movement statistics
against the classic captures, same metrics, same analyzer:
opp perp TR/classic reversing TR/cl median range TR/cl
SpinBot 0.967 / 0.962 0.466 / 0.464 462 / 402
Corners 0.894 / 0.920 0.462 / 0.419 520 / 504
Crazy 0.841 / 0.829 0.527 / 0.439 370 / 378
RamFire 0.762 / 0.651 0.533 / 0.417 326 / 283
ModularBot 0.956 / - 0.466 / - 453 / -
Every round starts moving within 3-11 ticks and runs at 75-82% full speed.
Implemented: the classic remaining-quantity motion model (delegated to the
TR Bot's own Nat-Pavasant model - identical constants: accel 1, decel -2,
max 8, body turn 10-0.75|v|, gun 20, radar 45 - so getDistanceRemaining and
getTurnRemaining are exactly self-consistent with what is emulated); event
synthesis with classic ordering; bullet identity via object identity; gun
heat; rounds; radar cadence; firing translation; and the ThreadManager
landmine is killed by installing a no-op IThreadManagerBase in
ContainerBase.instance (verified: without it 'RobotException: ThreadManager
cannot be null!' kills the bot thread; with it the write succeeds).
Also adds TrBattleCapture, an observer that dumps per-tick state in the SAME
JSONL fixture format as the classic capture, so legacy bots can be captured
from Tank Royale battles too.
HONEST DIVERGENCES (README section 5.9): the TR server moves along the
PRE-turn heading and then turns, while classic aligns displacement with the
POST-turn heading, so the analyzer's conversion error is ~1.5 deg rather than
0.000 deg; distanceRemaining decrements by target speed rather than actual
distance; collision clamping differs; BulletMissedEvent can fire less often
because age-expiry has no TR event; bulletId is a local temp id;
StatusEvent/onPaint/SkippedTurnEvent are never delivered. Physics fidelity
diverges by construction - expect to retune.
EnergyDomeWorker (the bullet shield) is off by default: its precise
bullet-detection warm-up makes round 1 up to 79% stationary vs 35% in
classic. Rounds 2+ match classic closely with it on, but the pure surfer is
the consistent path. DRUSSGT_SHIELD=1 re-enables it.
Jars remain out of git.