c214abcfa8
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.
54 lines
1.8 KiB
Nim
54 lines
1.8 KiB
Nim
## Direct assertion that WaveSurfer decomposes an enemy's (heading, speed) into
|
|
## a world velocity with the Tank Royale angle convention: 0 degrees = EAST,
|
|
## angles increase counter-clockwise, so vx = cos(h)*s and vy = sin(h)*s.
|
|
##
|
|
## The classic-Robocode convention (0 = north, clockwise) would swap sin/cos;
|
|
## this test fails if anyone reintroduces that bug.
|
|
|
|
import std/[math, unittest]
|
|
import ../test_framework/adversaries/WaveSurfer/src/WaveSurfer
|
|
|
|
const eps = 1e-9
|
|
|
|
proc close(a, b: float): bool = abs(a - b) < eps
|
|
|
|
suite "WaveSurfer enemy velocity decomposition (TR: 0=east, CCW+)":
|
|
test "east heading is +x":
|
|
let v = enemyVelocity(0.0, 10.0)
|
|
check close(v.vx, 10.0)
|
|
check close(v.vy, 0.0)
|
|
|
|
test "north heading (90 deg) is +y":
|
|
let v = enemyVelocity(90.0, 10.0)
|
|
check close(v.vx, 0.0)
|
|
check close(v.vy, 10.0)
|
|
|
|
test "west heading (180 deg) is -x":
|
|
let v = enemyVelocity(180.0, 10.0)
|
|
check close(v.vx, -10.0)
|
|
check close(v.vy, 0.0)
|
|
|
|
test "south heading (270 deg) is -y":
|
|
let v = enemyVelocity(270.0, 10.0)
|
|
check close(v.vx, 0.0)
|
|
check close(v.vy, -10.0)
|
|
|
|
test "diagonal 45 deg splits evenly":
|
|
let v = enemyVelocity(45.0, 10.0)
|
|
check close(v.vx, 10.0 / sqrt(2.0))
|
|
check close(v.vy, 10.0 / sqrt(2.0))
|
|
|
|
test "negative speed reverses the velocity":
|
|
let v = enemyVelocity(0.0, -5.0)
|
|
check close(v.vx, -5.0)
|
|
check close(v.vy, 0.0)
|
|
|
|
test "explicitly differs from the classic sin/cos swap":
|
|
# The old (buggy) decomposition. If the implementation ever matches this
|
|
# at a heading where cos != sin, the test fails.
|
|
for heading in [30.0, 60.0, 90.0, 120.0]:
|
|
let v = enemyVelocity(heading, 8.0)
|
|
let swappedX = sin(degToRad(heading)) * 8.0
|
|
let swappedY = cos(degToRad(heading)) * 8.0
|
|
check not (close(v.vx, swappedX) and close(v.vy, swappedY))
|