From 7c0c304418a298e16f7e760d1595d427b220c397 Mon Sep 17 00:00:00 2001 From: Davide Cappellini Date: Wed, 16 Sep 2026 08:18:44 +0200 Subject: [PATCH] feat(SNNBot): track and log bullet hit rate statistics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds bulletsFired and bulletsHit fields to track shots, increments on fire and hit events, and logs hit rate (hits/shots × 100%) in both SNN and reservoir aiming paths. Co-Authored-By: Claude Opus 4.6 --- SNNBot_garage/src/SNNBot.nim | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/SNNBot_garage/src/SNNBot.nim b/SNNBot_garage/src/SNNBot.nim index 4895295..049cd4c 100644 --- a/SNNBot_garage/src/SNNBot.nim +++ b/SNNBot_garage/src/SNNBot.nim @@ -216,6 +216,8 @@ type lastBinInput: BitVec80 # DECIDE-time binary input, reused in EVALUATE lastInput: BitVec80 # previous EVALUATE-time input (for change detection) lastAbsBearing: float # absolute bearing to enemy captured at DECIDE time + bulletsFired: int # count of bullets fired (power > 0) + bulletsHit: int # count of bullets that hit # ── aimTo helper ────────────────────────────────────────────────────────────── @@ -281,6 +283,9 @@ method onGameStarted*(bot: SNNBot, e: GameStartedEventForBot) = initSNN(bot.snn) bot.res = initBinaryAimer() +method onBulletHitBotEvent*(bot: SNNBot, e: BulletHitBotEvent) = + inc bot.bulletsHit + # ── Reservoir helpers ───────────────────────────────────────────────────────── proc toBinaryInput(bearing: float, velDir: float, velSpeed: float, hasVel: bool): BitVec80 = @@ -345,14 +350,15 @@ method run*(bot: SNNBot) = bot.lastBinInput = binInput bot.lastDecideGunDir = gunDir bot.lastAbsBearing = absBearing + let rate = if bot.bulletsFired > 0: (float(bot.bulletsHit) / float(bot.bulletsFired) * 100.0) else: 0.0 if aimOffset <= -999.0: # cold start: aim directly at enemy (no lead) bot.targetAngle = absBearing - echo "RES tick=" & $bot.tick & " cold-start aim=" & formatFloat(absBearing, ffDecimal, 1) + echo "RES tick=" & $bot.tick & " cold-start aim=" & formatFloat(absBearing, ffDecimal, 1) & " fired=" & $bot.bulletsFired & " hit=" & $bot.bulletsHit & " rate=" & formatFloat(rate, ffDecimal, 1) & "%" else: # apply lead correction offset to current bearing bot.targetAngle = (absBearing + aimOffset + 360.0) mod 360.0 - echo "RES tick=" & $bot.tick & " offset=" & formatFloat(aimOffset, ffDecimal, 1) & " aim=" & formatFloat(bot.targetAngle, ffDecimal, 1) + echo "RES tick=" & $bot.tick & " offset=" & formatFloat(aimOffset, ffDecimal, 1) & " aim=" & formatFloat(bot.targetAngle, ffDecimal, 1) & " fired=" & $bot.bulletsFired & " hit=" & $bot.bulletsHit & " rate=" & formatFloat(rate, ffDecimal, 1) & "%" else: let inputs = encodeInputFull(absBearing, bot.velDirDeg, bot.velSpeed, bot.hasLastPos) # Multi-tick inference: accumulate sin/cos and spike counts over N_INFER ticks @@ -393,6 +399,7 @@ method run*(bot: SNNBot) = # Fire gate: only fire when error within adaptive dead zone if err < adaptiveDeadZone: discard setFire(FIRE_POWER) + inc bot.bulletsFired bot.phase = EVALUATE else: discard setFire(0.0) @@ -428,13 +435,15 @@ method run*(bot: SNNBot) = # Cold start: always learn bot.res.learn(bot.lastBinInput, correctOffset) let aimErr = if producedOffset > -999.0: abs(normalizeRelativeAngle(producedOffset - correctOffset)) else: -1.0 + let rate = if bot.bulletsFired > 0: (float(bot.bulletsHit) / float(bot.bulletsFired) * 100.0) else: 0.0 echo "RES tick=" & $bot.tick & " offset=" & (if producedOffset > -999.0: formatFloat(producedOffset, ffDecimal, 1) else: "cold") & " correctOffset=" & formatFloat(correctOffset, ffDecimal, 1) & " err=" & (if producedOffset > -999.0: formatFloat(aimErr, ffDecimal, 1) else: "n/a") & " hammingDist=" & $hammingDist & " inputChanged=" & $inputChanged & - " exemplars=" & $bot.res.count + " exemplars=" & $bot.res.count & + " fired=" & $bot.bulletsFired & " hit=" & $bot.bulletsHit & " rate=" & formatFloat(rate, ffDecimal, 1) & "%" else: let targetRel = normalizeRelativeAngle(correctAngle - bot.lastDecideGunDir) bot.snn.superSpikeUpdate(bot.lastSpikes, bot.lastVSnap, bot.snn.preTrace, targetRel) @@ -455,7 +464,8 @@ method run*(bot: SNNBot) = for w in bot.snn.wCos: meanWout += abs(w) meanWout /= float(2 * N_HID) - echo "tick=" & $bot.tick & " err=" & formatFloat(err, ffDecimal, 1) & "° infer_spk=" & $spikeCount & "/" & $(N_INFER * N_HID) & " maxV=" & formatFloat(maxV, ffDecimal, 3) & " |wih|=" & formatFloat(meanWih, ffDecimal, 4) & " |wOut|=" & formatFloat(meanWout, ffDecimal, 4) & " sin=" & formatFloat(bot.snn.lastSinOut, ffDecimal, 3) & " cos=" & formatFloat(bot.snn.lastCosOut, ffDecimal, 3) & " snnAngle=" & formatFloat(bot.snn.lastSnnAngle, ffDecimal, 1) + let rate = if bot.bulletsFired > 0: (float(bot.bulletsHit) / float(bot.bulletsFired) * 100.0) else: 0.0 + echo "tick=" & $bot.tick & " err=" & formatFloat(err, ffDecimal, 1) & "° infer_spk=" & $spikeCount & "/" & $(N_INFER * N_HID) & " maxV=" & formatFloat(maxV, ffDecimal, 3) & " |wih|=" & formatFloat(meanWih, ffDecimal, 4) & " |wOut|=" & formatFloat(meanWout, ffDecimal, 4) & " sin=" & formatFloat(bot.snn.lastSinOut, ffDecimal, 3) & " cos=" & formatFloat(bot.snn.lastCosOut, ffDecimal, 3) & " snnAngle=" & formatFloat(bot.snn.lastSnnAngle, ffDecimal, 1) & " fired=" & $bot.bulletsFired & " hit=" & $bot.bulletsHit & " rate=" & formatFloat(rate, ffDecimal, 1) & "%" # Update lastInput for next EVALUATE cycle's change detection bot.lastInput = bot.lastBinInput