feat(SNNBot): track and log bullet hit rate statistics
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 <noreply@anthropic.com>
This commit is contained in:
@@ -216,6 +216,8 @@ type
|
|||||||
lastBinInput: BitVec80 # DECIDE-time binary input, reused in EVALUATE
|
lastBinInput: BitVec80 # DECIDE-time binary input, reused in EVALUATE
|
||||||
lastInput: BitVec80 # previous EVALUATE-time input (for change detection)
|
lastInput: BitVec80 # previous EVALUATE-time input (for change detection)
|
||||||
lastAbsBearing: float # absolute bearing to enemy captured at DECIDE time
|
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 ──────────────────────────────────────────────────────────────
|
# ── aimTo helper ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -281,6 +283,9 @@ method onGameStarted*(bot: SNNBot, e: GameStartedEventForBot) =
|
|||||||
initSNN(bot.snn)
|
initSNN(bot.snn)
|
||||||
bot.res = initBinaryAimer()
|
bot.res = initBinaryAimer()
|
||||||
|
|
||||||
|
method onBulletHitBotEvent*(bot: SNNBot, e: BulletHitBotEvent) =
|
||||||
|
inc bot.bulletsHit
|
||||||
|
|
||||||
# ── Reservoir helpers ─────────────────────────────────────────────────────────
|
# ── Reservoir helpers ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
proc toBinaryInput(bearing: float, velDir: float, velSpeed: float, hasVel: bool): BitVec80 =
|
proc toBinaryInput(bearing: float, velDir: float, velSpeed: float, hasVel: bool): BitVec80 =
|
||||||
@@ -345,14 +350,15 @@ method run*(bot: SNNBot) =
|
|||||||
bot.lastBinInput = binInput
|
bot.lastBinInput = binInput
|
||||||
bot.lastDecideGunDir = gunDir
|
bot.lastDecideGunDir = gunDir
|
||||||
bot.lastAbsBearing = absBearing
|
bot.lastAbsBearing = absBearing
|
||||||
|
let rate = if bot.bulletsFired > 0: (float(bot.bulletsHit) / float(bot.bulletsFired) * 100.0) else: 0.0
|
||||||
if aimOffset <= -999.0:
|
if aimOffset <= -999.0:
|
||||||
# cold start: aim directly at enemy (no lead)
|
# cold start: aim directly at enemy (no lead)
|
||||||
bot.targetAngle = absBearing
|
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:
|
else:
|
||||||
# apply lead correction offset to current bearing
|
# apply lead correction offset to current bearing
|
||||||
bot.targetAngle = (absBearing + aimOffset + 360.0) mod 360.0
|
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:
|
else:
|
||||||
let inputs = encodeInputFull(absBearing, bot.velDirDeg, bot.velSpeed, bot.hasLastPos)
|
let inputs = encodeInputFull(absBearing, bot.velDirDeg, bot.velSpeed, bot.hasLastPos)
|
||||||
# Multi-tick inference: accumulate sin/cos and spike counts over N_INFER ticks
|
# 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
|
# Fire gate: only fire when error within adaptive dead zone
|
||||||
if err < adaptiveDeadZone:
|
if err < adaptiveDeadZone:
|
||||||
discard setFire(FIRE_POWER)
|
discard setFire(FIRE_POWER)
|
||||||
|
inc bot.bulletsFired
|
||||||
bot.phase = EVALUATE
|
bot.phase = EVALUATE
|
||||||
else:
|
else:
|
||||||
discard setFire(0.0)
|
discard setFire(0.0)
|
||||||
@@ -428,13 +435,15 @@ method run*(bot: SNNBot) =
|
|||||||
# Cold start: always learn
|
# Cold start: always learn
|
||||||
bot.res.learn(bot.lastBinInput, correctOffset)
|
bot.res.learn(bot.lastBinInput, correctOffset)
|
||||||
let aimErr = if producedOffset > -999.0: abs(normalizeRelativeAngle(producedOffset - correctOffset)) else: -1.0
|
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 &
|
echo "RES tick=" & $bot.tick &
|
||||||
" offset=" & (if producedOffset > -999.0: formatFloat(producedOffset, ffDecimal, 1) else: "cold") &
|
" offset=" & (if producedOffset > -999.0: formatFloat(producedOffset, ffDecimal, 1) else: "cold") &
|
||||||
" correctOffset=" & formatFloat(correctOffset, ffDecimal, 1) &
|
" correctOffset=" & formatFloat(correctOffset, ffDecimal, 1) &
|
||||||
" err=" & (if producedOffset > -999.0: formatFloat(aimErr, ffDecimal, 1) else: "n/a") &
|
" err=" & (if producedOffset > -999.0: formatFloat(aimErr, ffDecimal, 1) else: "n/a") &
|
||||||
" hammingDist=" & $hammingDist &
|
" hammingDist=" & $hammingDist &
|
||||||
" inputChanged=" & $inputChanged &
|
" inputChanged=" & $inputChanged &
|
||||||
" exemplars=" & $bot.res.count
|
" exemplars=" & $bot.res.count &
|
||||||
|
" fired=" & $bot.bulletsFired & " hit=" & $bot.bulletsHit & " rate=" & formatFloat(rate, ffDecimal, 1) & "%"
|
||||||
else:
|
else:
|
||||||
let targetRel = normalizeRelativeAngle(correctAngle - bot.lastDecideGunDir)
|
let targetRel = normalizeRelativeAngle(correctAngle - bot.lastDecideGunDir)
|
||||||
bot.snn.superSpikeUpdate(bot.lastSpikes, bot.lastVSnap, bot.snn.preTrace, targetRel)
|
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:
|
for w in bot.snn.wCos:
|
||||||
meanWout += abs(w)
|
meanWout += abs(w)
|
||||||
meanWout /= float(2 * N_HID)
|
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
|
# Update lastInput for next EVALUATE cycle's change detection
|
||||||
bot.lastInput = bot.lastBinInput
|
bot.lastInput = bot.lastBinInput
|
||||||
|
|||||||
Reference in New Issue
Block a user