From ac5301acb1f4a1368fb8147efb19f92a4bf60208 Mon Sep 17 00:00:00 2001 From: Davide Cappellini Date: Mon, 14 Sep 2026 23:22:43 +0200 Subject: [PATCH] feat(SNNBot): exponential smoothing on aim angle to stop bin-flickering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adjacent 1° bins have similar scores, causing the winner to flip tick to tick. Circular EMA (α=0.6) on sin/cos dampens oscillation while preserving tracking response. Co-Authored-By: Claude Opus 4.6 --- SNNBot_garage/src/SNNBot.nim | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/SNNBot_garage/src/SNNBot.nim b/SNNBot_garage/src/SNNBot.nim index 4115626..3adc2ab 100644 --- a/SNNBot_garage/src/SNNBot.nim +++ b/SNNBot_garage/src/SNNBot.nim @@ -215,6 +215,8 @@ type velSpeed: float64 # speed (units/tick) from last scan delta lastDecideGunDir: float # gun heading captured at DECIDE time for EVALUATE lastBinInput: BitVec80 # DECIDE-time binary input, reused in EVALUATE + smoothSin: float # exponential smoothing of sin channel + smoothCos: float # exponential smoothing of cos channel # ── aimTo helper ────────────────────────────────────────────────────────────── @@ -377,6 +379,8 @@ method onRoundStarted*(bot: SNNBot, e: RoundStartedEvent) = method onGameStarted*(bot: SNNBot, e: GameStartedEventForBot) = initSNN(bot.snn) bot.res = initAimer() + bot.smoothSin = 0.0 + bot.smoothCos = 1.0 # ── Reservoir helpers ───────────────────────────────────────────────────────── @@ -439,10 +443,16 @@ method run*(bot: SNNBot) = let binInput = toBinaryInput(relBearing, bot.velDirDeg, bot.velSpeed, bot.hasLastPos) let winBin = bot.res.forward(binInput) let aimAngle = bot.res.interpolatedAngle(winBin) + const AIM_SMOOTH = 0.6 # 60% old, 40% new — dampens flicker + # ponytail: AIM_SMOOTH=0.6 balances tracking vs stability; lower if gun lags, raise if gun dances + let aimRad = degToRad(aimAngle) + bot.smoothSin = AIM_SMOOTH * bot.smoothSin + (1.0 - AIM_SMOOTH) * sin(aimRad) + bot.smoothCos = AIM_SMOOTH * bot.smoothCos + (1.0 - AIM_SMOOTH) * cos(aimRad) + let smoothedAim = radToDeg(arctan2(bot.smoothSin, bot.smoothCos)) bot.lastBinInput = binInput - bot.targetAngle = gunDir + aimAngle + bot.targetAngle = gunDir + smoothedAim bot.lastDecideGunDir = gunDir - echo "RES tick=" & $bot.tick & " bin=" & $winBin & " aim=" & formatFloat(aimAngle, ffDecimal, 1) + echo "RES tick=" & $bot.tick & " bin=" & $winBin & " aim=" & formatFloat(aimAngle, ffDecimal, 1) & " smoothed=" & formatFloat(smoothedAim, ffDecimal, 1) else: let inputs = encodeInputFull(relBearing, bot.velDirDeg, bot.velSpeed, bot.hasLastPos) # Multi-tick inference: accumulate sin/cos and spike counts over N_INFER ticks