fix(PPO_Bot): radar lock oscillation bug — arctan2 arg order wrong for Tank Royale coords
arctan2(dy, dx) gives east-based math bearing; Tank Royale uses north=0°, CW+. Swapping to arctan2(dx, dy) gives the correct game-space bearing. Symptom: radar commanded 45°/tick away from a target directly ahead. Adds test_radar_lock.nim as regression test (20-tick lock, ±15° tolerance). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -82,9 +82,10 @@ proc getRadarTurnRate*(tracker: var EnemyTracker;
|
|||||||
return 45.0
|
return 45.0
|
||||||
|
|
||||||
# Bearing from radar to enemy
|
# Bearing from radar to enemy
|
||||||
|
# Tank Royale: 0°=north, CW+. arctan2(dx, dy) gives north-based CW bearing.
|
||||||
let dx = tracker.current.x - botX
|
let dx = tracker.current.x - botX
|
||||||
let dy = tracker.current.y - botY
|
let dy = tracker.current.y - botY
|
||||||
let absoluteDir = (180.0 * arctan2(dy, dx) / PI + 360.0) mod 360.0
|
let absoluteDir = (180.0 * arctan2(dx, dy) / PI + 360.0) mod 360.0
|
||||||
let radarBearing = normalizeRelative(absoluteDir - radarDirection)
|
let radarBearing = normalizeRelative(absoluteDir - radarDirection)
|
||||||
|
|
||||||
# Overshoot by 10°, alternate direction each tick
|
# Overshoot by 10°, alternate direction each tick
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
## Regression test: radar lock must hold on a stationary target.
|
||||||
|
## Run: nim c -r tests/test_radar_lock.nim
|
||||||
|
|
||||||
|
import std/[math, strformat]
|
||||||
|
import "../enemy_tracker"
|
||||||
|
|
||||||
|
template check(cond: bool, msg: string) =
|
||||||
|
if not cond:
|
||||||
|
quit("FAIL: " & msg, 1)
|
||||||
|
|
||||||
|
# Bot at arena center; stationary enemy due north (same x, higher y).
|
||||||
|
# Tank Royale: y increases northward.
|
||||||
|
# True bearing to enemy = 0° (north = 0° in game coords).
|
||||||
|
|
||||||
|
let botX = 400.0
|
||||||
|
let botY = 300.0
|
||||||
|
let enemyX = 400.0 # same x → dx = 0
|
||||||
|
let enemyY = 500.0 # north of bot → dy > 0
|
||||||
|
let trueBearing = 0.0 # north
|
||||||
|
|
||||||
|
# Radar starts pointing at the enemy (radarDirection = 0°, due north).
|
||||||
|
var radarDir = 0.0
|
||||||
|
|
||||||
|
var tracker = initEnemyTracker()
|
||||||
|
# Prime with contact at the known position
|
||||||
|
tracker.update(enemyX, enemyY, 0.0, 0.0, 100.0)
|
||||||
|
|
||||||
|
echo "Tick | radarDir | trueBearing | bearingErr"
|
||||||
|
for tick in 1 .. 20:
|
||||||
|
let rate = tracker.getRadarTurnRate(botX, botY, 0.0, radarDir)
|
||||||
|
radarDir = (radarDir + rate + 360.0) mod 360.0
|
||||||
|
|
||||||
|
# Simulate a successful scan every tick (enemy is stationary)
|
||||||
|
tracker.update(enemyX, enemyY, 0.0, 0.0, 100.0)
|
||||||
|
|
||||||
|
# Bearing error: signed difference, wrapped to [-180, 180]
|
||||||
|
let err = ((radarDir - trueBearing) + 540.0) mod 360.0 - 180.0
|
||||||
|
echo &" {tick:2d} | {radarDir:8.3f}° | {trueBearing:8.3f}° | {err:+.3f}°"
|
||||||
|
|
||||||
|
check abs(err) <= 15.0, &"tick {tick}: radar {radarDir:.1f}° drifted > 15° from target {trueBearing:.1f}°"
|
||||||
|
|
||||||
|
echo "All radar lock tests passed"
|
||||||
Reference in New Issue
Block a user