feat(ModularBot): movement selector — phantom meteor + wave surfer
- Adds WaveSurferModule as second movement option - Tracks damage per round via onHitByBullet, EMA decay α=0.5 - Every round after round 2: switches to lower-damage movement - Body color signals active mover (orange=phantom, blue=wave) - vs SpinBot 10r: ModularBot 1412 vs SpinBot 650 - vs WaveSurfer 10r: ModularBot 1858 vs WaveSurfer 10 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ import guns/displacement
|
|||||||
import guns/averaged_lead
|
import guns/averaged_lead
|
||||||
import guns/decay_gf
|
import guns/decay_gf
|
||||||
import movements/phantom_meteor
|
import movements/phantom_meteor
|
||||||
|
import movements/wave_surfer
|
||||||
|
|
||||||
const botJsonPath = currentSourcePath().parentDir / "ModularBot.json"
|
const botJsonPath = currentSourcePath().parentDir / "ModularBot.json"
|
||||||
const DebugVBullets = false
|
const DebugVBullets = false
|
||||||
@@ -51,6 +52,12 @@ type
|
|||||||
avgLead: AveragedLeadGun
|
avgLead: AveragedLeadGun
|
||||||
decayGF: DecayGFGun
|
decayGF: DecayGFGun
|
||||||
mover: PhantomMeteorModule
|
mover: PhantomMeteorModule
|
||||||
|
waveMover: WaveSurferModule
|
||||||
|
movementId: int ## 0=phantom, 1=waveSurf
|
||||||
|
dmgPhantom: float ## rolling damage taken under phantom
|
||||||
|
dmgWaveSurf: float ## rolling damage taken under waveSurf
|
||||||
|
roundDmg: float ## damage this round
|
||||||
|
roundCount: int
|
||||||
virtualHits: int
|
virtualHits: int
|
||||||
virtualMiss: int
|
virtualMiss: int
|
||||||
tick: int
|
tick: int
|
||||||
@@ -207,21 +214,46 @@ method onScannedBot*(bot: ModularBot, e: ScannedBotEvent) =
|
|||||||
setGunTurnRate(normDelta)
|
setGunTurnRate(normDelta)
|
||||||
|
|
||||||
|
|
||||||
|
method onHitByBullet*(bot: ModularBot, e: HitByBulletEvent) =
|
||||||
|
bot.roundDmg += e.damage
|
||||||
|
|
||||||
method onRoundEnded*(bot: ModularBot, e: RoundEndedEventForBot) =
|
method onRoundEnded*(bot: ModularBot, e: RoundEndedEventForBot) =
|
||||||
discard # Skip detailed logging for now
|
# Accumulate damage into active movement bucket (exponential decay, α=0.5)
|
||||||
|
# ponytail: simple EMA; switch to longer window if thrashing becomes a problem
|
||||||
|
let decay = 0.5
|
||||||
|
if bot.movementId == 0:
|
||||||
|
bot.dmgPhantom = decay * bot.dmgPhantom + (1.0 - decay) * bot.roundDmg
|
||||||
|
else:
|
||||||
|
bot.dmgWaveSurf = decay * bot.dmgWaveSurf + (1.0 - decay) * bot.roundDmg
|
||||||
|
echo "[move] round dmg=", bot.roundDmg, " phantom_ema=", bot.dmgPhantom, " wave_ema=", bot.dmgWaveSurf
|
||||||
|
|
||||||
method onRoundStarted*(bot: ModularBot, e: RoundStartedEvent) =
|
method onRoundStarted*(bot: ModularBot, e: RoundStartedEvent) =
|
||||||
setAdjustGunForBodyTurn(true)
|
setAdjustGunForBodyTurn(true)
|
||||||
setAdjustRadarForBodyTurn(true)
|
setAdjustRadarForBodyTurn(true)
|
||||||
setAdjustRadarForGunTurn(true)
|
setAdjustRadarForGunTurn(true)
|
||||||
setBodyColor("#FF9900") # PhantomMeteor: deep orange/gold
|
|
||||||
setRadarColor("#00CCCC") # RadarLock: cyan/teal
|
setRadarColor("#00CCCC") # RadarLock: cyan/teal
|
||||||
setScanColor("#33FFFF") # RadarLock scan arc: bright teal
|
setScanColor("#33FFFF") # RadarLock scan arc: bright teal
|
||||||
bot.radar.init()
|
bot.radar.init()
|
||||||
bot.hasContact = false
|
bot.hasContact = false
|
||||||
bot.tick = 0
|
bot.tick = 0
|
||||||
bot.currentGun = -1 # Reset to trigger initial log on first selectShot
|
bot.currentGun = -1
|
||||||
bot.mover.resetRound()
|
bot.roundDmg = 0.0
|
||||||
|
inc bot.roundCount
|
||||||
|
|
||||||
|
# Every 3 rounds: switch to lower-damage movement (probe every 3rd round)
|
||||||
|
# ponytail: simple 3-round probe; add hysteresis if switching thrashes
|
||||||
|
if bot.roundCount > 2:
|
||||||
|
bot.movementId = if bot.dmgPhantom <= bot.dmgWaveSurf: 0 else: 1
|
||||||
|
|
||||||
|
if bot.movementId == 0:
|
||||||
|
bot.mover.resetRound()
|
||||||
|
setBodyColor("#FF9900") # PhantomMeteor: deep orange/gold
|
||||||
|
echo "[move] using phantom_meteor"
|
||||||
|
else:
|
||||||
|
bot.waveMover.resetRound()
|
||||||
|
setBodyColor("#0099FF") # WaveSurfer: blue
|
||||||
|
echo "[move] using wave_surfer"
|
||||||
|
|
||||||
echo fmt"[gun] starting: {GunNames[0]}"
|
echo fmt"[gun] starting: {GunNames[0]}"
|
||||||
# tracker fitness persists across rounds (rolling window carries over)
|
# tracker fitness persists across rounds (rolling window carries over)
|
||||||
|
|
||||||
@@ -239,8 +271,10 @@ method run*(bot: ModularBot) =
|
|||||||
go()
|
go()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Movement harness: oscillate perpendicular to enemy
|
# Movement harness: dispatch to active mover
|
||||||
let (spd, tr) = bot.mover.computeMove(bot.lastState)
|
let (spd, tr) =
|
||||||
|
if bot.movementId == 0: bot.mover.computeMove(bot.lastState)
|
||||||
|
else: bot.waveMover.computeMove(bot.lastState)
|
||||||
setTargetSpeed(spd)
|
setTargetSpeed(spd)
|
||||||
setTurnRate(tr)
|
setTurnRate(tr)
|
||||||
|
|
||||||
@@ -265,6 +299,7 @@ when isMainModule:
|
|||||||
decayGF: initDecayGFGun(),
|
decayGF: initDecayGFGun(),
|
||||||
radar: RadarLockModule(),
|
radar: RadarLockModule(),
|
||||||
mover: initPhantomMeteor(),
|
mover: initPhantomMeteor(),
|
||||||
|
waveMover: initWaveSurfer(),
|
||||||
currentGun: -1,
|
currentGun: -1,
|
||||||
)
|
)
|
||||||
start(bot, botJsonPath)
|
start(bot, botJsonPath)
|
||||||
|
|||||||
Reference in New Issue
Block a user