Merge branch 'worktree-agent-ac811b59' (ticket #39 radar lock)
This commit is contained in:
@@ -0,0 +1,38 @@
|
|||||||
|
## Standalone radar lock module for Robocode Tank Royale (1v1).
|
||||||
|
## No bot-specific imports — takes plain floats, returns radarTurnRate.
|
||||||
|
##
|
||||||
|
## Tank Royale radar uses standard math convention: 0° = east, CCW positive.
|
||||||
|
## Angles are in degrees.
|
||||||
|
|
||||||
|
import std/math
|
||||||
|
|
||||||
|
const
|
||||||
|
MaxRadarTurn* = 45.0
|
||||||
|
DefaultOvershootDeg* = 5.0
|
||||||
|
|
||||||
|
var overShootDeg* = DefaultOvershootDeg
|
||||||
|
|
||||||
|
proc init*() =
|
||||||
|
## Reset module to defaults.
|
||||||
|
## In your bot's constructor set:
|
||||||
|
## adjustRadarForBodyTurn = true
|
||||||
|
## adjustRadarForGunTurn = true
|
||||||
|
overShootDeg = DefaultOvershootDeg
|
||||||
|
|
||||||
|
proc normalizeRelative(angle: float64): float64 {.inline.} =
|
||||||
|
result = angle mod 360.0
|
||||||
|
if result >= 180.0: result -= 360.0
|
||||||
|
elif result < -180.0: result += 360.0
|
||||||
|
|
||||||
|
proc doRadar*(currentRadarHeading, enemyBearing: float64): float64 =
|
||||||
|
## Returns radarTurnRate (degrees/tick, positive = clockwise).
|
||||||
|
##
|
||||||
|
## currentRadarHeading: current radar direction in degrees (0=east, CCW+).
|
||||||
|
## enemyBearing: absolute bearing to enemy in same coordinate system.
|
||||||
|
##
|
||||||
|
## Handles angle wrapping, clamps to [-45, +45], adds overshoot sweep.
|
||||||
|
var turn = normalizeRelative(enemyBearing - currentRadarHeading)
|
||||||
|
# Add overshoot in the same direction as the turn to maintain lock
|
||||||
|
if turn < 0.0: turn -= overShootDeg
|
||||||
|
else: turn += overShootDeg
|
||||||
|
result = turn.clamp(-MaxRadarTurn, MaxRadarTurn)
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Package
|
||||||
|
version = "1.0.0"
|
||||||
|
author = "Davide Cappellini"
|
||||||
|
description = "Standalone radar lock module for Robocode Tank Royale"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
|
||||||
|
# Dependencies
|
||||||
|
requires "nim >= 2.0.0"
|
||||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,74 @@
|
|||||||
|
import std/unittest
|
||||||
|
import std/math
|
||||||
|
import ../radar_lock
|
||||||
|
|
||||||
|
suite "radar_lock":
|
||||||
|
|
||||||
|
setup:
|
||||||
|
init()
|
||||||
|
|
||||||
|
# Basic lock: radar already on enemy — overshoot pushes it slightly
|
||||||
|
test "radar on enemy — returns overshoot only":
|
||||||
|
let rate = doRadar(90.0, 90.0)
|
||||||
|
check rate == DefaultOvershootDeg
|
||||||
|
|
||||||
|
# Cardinal directions
|
||||||
|
test "lock from 0 degrees":
|
||||||
|
let rate = doRadar(0.0, 0.0)
|
||||||
|
check rate == DefaultOvershootDeg
|
||||||
|
|
||||||
|
test "lock from 90 degrees":
|
||||||
|
let rate = doRadar(90.0, 90.0)
|
||||||
|
check rate == DefaultOvershootDeg
|
||||||
|
|
||||||
|
test "lock from 180 degrees":
|
||||||
|
let rate = doRadar(180.0, 180.0)
|
||||||
|
check rate == DefaultOvershootDeg
|
||||||
|
|
||||||
|
test "lock from 270 degrees":
|
||||||
|
let rate = doRadar(270.0, 270.0)
|
||||||
|
check rate == DefaultOvershootDeg
|
||||||
|
|
||||||
|
# Angle wrapping: radar at 350°, enemy at 10° → shortest path is +20°
|
||||||
|
test "wrapping 350 to 10 — turns right 20 + overshoot":
|
||||||
|
let rate = doRadar(350.0, 10.0)
|
||||||
|
check abs(rate - (20.0 + DefaultOvershootDeg)) < 1e-9
|
||||||
|
|
||||||
|
# Angle wrapping: radar at 10°, enemy at 350° → shortest path is -20°
|
||||||
|
test "wrapping 10 to 350 — turns left 20 + overshoot":
|
||||||
|
let rate = doRadar(10.0, 350.0)
|
||||||
|
check abs(rate - (-20.0 - DefaultOvershootDeg)) < 1e-9
|
||||||
|
|
||||||
|
# Clamping: enemy 100° away → raw turn+overshoot > 45°, must clamp
|
||||||
|
test "clamp positive — large gap":
|
||||||
|
let rate = doRadar(0.0, 100.0)
|
||||||
|
check rate == 45.0
|
||||||
|
|
||||||
|
test "clamp negative — large gap":
|
||||||
|
let rate = doRadar(100.0, 0.0)
|
||||||
|
check rate == -45.0
|
||||||
|
|
||||||
|
# Overshoot direction: turn right → positive overshoot
|
||||||
|
test "overshoot direction right":
|
||||||
|
let rate = doRadar(0.0, 30.0) # needs +30°, overshoot adds +5°
|
||||||
|
check abs(rate - 35.0) < 1e-9
|
||||||
|
|
||||||
|
# Overshoot direction: turn left → negative overshoot
|
||||||
|
test "overshoot direction left":
|
||||||
|
let rate = doRadar(30.0, 0.0) # needs -30°, overshoot adds -5°
|
||||||
|
check abs(rate - (-35.0)) < 1e-9
|
||||||
|
|
||||||
|
# Output never exceeds ±45
|
||||||
|
test "output bounded above":
|
||||||
|
let rate = doRadar(0.0, 179.0)
|
||||||
|
check rate <= 45.0
|
||||||
|
|
||||||
|
test "output bounded below":
|
||||||
|
let rate = doRadar(179.0, 0.0)
|
||||||
|
check rate >= -45.0
|
||||||
|
|
||||||
|
# init() resets overShootDeg
|
||||||
|
test "init resets overshoot":
|
||||||
|
overShootDeg = 20.0
|
||||||
|
init()
|
||||||
|
check overShootDeg == DefaultOvershootDeg
|
||||||
Reference in New Issue
Block a user