P0. Three of the five launchers ran ./<Bot> (a tracked binary at the bot root)
while config.nims sets outdir=out and both the test framework's compileBots and
a manual 'nim c src/<Bot>.nim' write to out/. SittingDuck and OscillatorBot
correctly ran ./out/<Bot>; RandomMover, PatternMover and WaveSurfer did not.
cmp confirms the root and out binaries differed for all three.
Consequence: the previous session's adversary fixes were compiled into out/ and
never executed. Every gauntlet and every capture ran the OLD code. This is
almost certainly why the user's instinct that these bots were still bugged was
correct while the code claimed otherwise.
Fixed by pointing all five launchers at ./out/<Bot>, and by deleting the three
stale root binaries so the trap cannot recur. Verified end to end through the
booter: WaveSurfer went from standing still 96.2% of ticks with a 1398-tick
longest standstill, to rest 12.3% / mean speed 6.69 / longest zero run 18 /
perpendicular 0.845.
Also honours GUN_STATS_PATH in test_gauntlet_5bots.nim (same knob ModularBot
reads) so pooled gauntlet runs append to one file instead of clobbering the
default.
NOTE for a follow-up: the out/ binaries are still TRACKED build artifacts, which
is the same class of hazard that caused this. Untracking them (as was done for
ModularBot_garage/ModularBot) would remove the failure mode entirely.
The user suspected these were bugged. They were, and the verdicts are not
uniform - three genuinely broken, one merely sloppy, one fine:
- WaveSurfer: GENUINELY BUGGED, worst of the five. (a) The enemy velocity
decomposition was sin/cos SWAPPED - enemyVx used sin and enemyVy used cos,
while Tank Royale is 0 deg = East, CCW+, so it must be cos for X and sin for
Y. Its linear-prediction gun was aiming at a reflected position. (b) The wall
escape flipped strafeDir on EVERY tick the bot was inside the wall margin,
so instead of turning away it flip-flopped in place: measured standing still
(speed < 0.5) for 96.2% of ticks with a longest continuous standstill of 1398
ticks. Fixed with a hysteretic wall-escape selection plus a corner escape,
dead enemyLastDir removed, and per-round state reset.
AFTER, measured through the booter: rest 12.3%, mean speed 6.69, full speed
79.7%, longest zero run 18, perpendicular 0.845 / radial 0.012 - it now
actually strafes. Gun sanity: lead error 1.0 px vs 106 px for head-on on a
constant-velocity target; lead gun 45.8% hits vs 29.3% for head-on.
- PatternMover: GENUINELY BUGGED. Real deadlock - it decremented its step
counter by the REQUESTED amount while issuing setTargetSpeed(8), so against a
wall the counter never reached 0, advanceStep never ran and it was stuck
forever (309-tick standstill). Now counts down by ACTUAL distance/turn with a
STALL_LIMIT watchdog and steers toward the arena centre. Standstill 309 -> 19
ticks; full-speed ticks 10.0% -> 28.4%.
- OscillatorBot: GENUINELY BUGGED, milder. No wall handling at all, so it
ground along walls 53.4% of ticks and could pin in a corner. Added wall
steering that preserves the fixed 25-tick reversal cadence. Wall-band 53.4%
-> 18.6%, mean wall distance 72 -> 119.
- RandomMover: merely sloppy, not broken. Its turn intent saturated against the
speed-dependent limit (18.4% of moving ticks clamped) and the fire gate was a
very loose 10 deg. Now clamps to calcMaxTurnRate and fires within 3 deg.
Saturation 18.4% -> 3.9%.
- SittingDuck: FINE. Speed 0 for 100% of ticks, zero shots. Left untouched -
it is a duck by design.
Adds test_wavesurfer_velocity.nim, a direct assertion that the decomposition is
cos/sin and explicitly NOT the swapped form (7 cases).
KNOWN ISSUE, not fixed: RandomMover/PatternMover/WaveSurfer import
tankroyale_botapi 1.0.1 and intermittently SIGSEGV in
tankroyale_botapi/event_queue.nim:89 addEvent, freezing the bot for the rest of
the battle. It reproduces on old and new code and never occurs for SittingDuck/
OscillatorBot, which import robocode_tankroyale_botapi 1.0.7. Migrating the
three to 1.0.7 would likely fix it and is worth doing - it is a real
reliability risk for these as sparring partners.
- 30-tick cooldown after ghost-stuck/timeout ram exit prevents re-entry loop
- enemy_tracker.update() skips dead bots to prevent same-tick scan resurrection
- TFIL graphics cleared when ramming is active movement
- [config] logs: white base with green-highlighted changes only
- [ram:enter] logs trigger reason and key values on false→true transition
- [death] and [target-invalid] logs retained for diagnostics
Renames PatternMover_garage → PatternMover, RandomMover_garage → RandomMover,
WaveSurfer_garage → WaveSurfer. Updates all .json, .sh, .nimble, and config.nims
files to match TR Booter naming convention (directory name = bot name).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>