fix(SNNBot): fast recovery on target move + bidirectional fire gate

Problem 1: Dead zone (skip learn when error < 1.5°) means stale exemplars
linger in ring buffer after target moves. Fix: detect error jump >5° and
reset ring buffer (aimer.count, aimer.nextSlot = 0) for fresh start.

Problem 2: Fire gate only checked start condition (err < threshold), never
stopped firing when error worsened. Fix: explicit setFire(0.0) when error
exceeds tolerance, making gate bidirectional (start and stop).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 08:35:10 +02:00
parent 94fbde2934
commit 87bcd3df6b
+14
View File
@@ -215,6 +215,7 @@ 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
lastError: float # last aiming error (for jump detection)
# ── aimTo helper ──────────────────────────────────────────────────────────────
@@ -273,6 +274,7 @@ method onRoundStarted*(bot: SNNBot, e: RoundStartedEvent) =
bot.velSpeed = 0.0
bot.phase = DECIDE
bot.tick = 0
bot.lastError = -1.0 # sentinel: no error yet
setTargetSpeed(0.0)
setTurnRate(0.0)
@@ -382,9 +384,21 @@ method run*(bot: SNNBot) =
of WAITING:
aimTo(bot.targetAngle, gunDir)
let err = abs(normalizeRelativeAngle(bot.targetAngle - gunDir))
# Bidirectional fire gate: fire only when error within tolerance
if err < AIM_TOL:
discard setFire(FIRE_POWER)
bot.phase = EVALUATE
else:
discard setFire(0.0)
# Fast recovery: detect error jump (regime change) and reset ring buffer
# ponytail: >5° jump triggers reset; tune threshold if target moves frequently
if bot.lastError >= 0.0 and err - bot.lastError > 5.0:
bot.res.count = 0
bot.res.nextSlot = 0
bot.lastError = err
of EVALUATE:
# Predictive error signal: extrapolate enemy position at bullet impact time.