diff --git a/SNNBot_garage/src/SNNBot.nim b/SNNBot_garage/src/SNNBot.nim index 24c004d..94f19f1 100644 --- a/SNNBot_garage/src/SNNBot.nim +++ b/SNNBot_garage/src/SNNBot.nim @@ -247,6 +247,11 @@ type lastDecideGunDir: float # gun heading captured at DECIDE time for EVALUATE lastVPerp: float # DECIDE-time vPerp, reused in EVALUATE lastAbsBearing: float # absolute bearing to enemy captured at DECIDE time + decideEnemyX: float # enemy X frozen at DECIDE time + decideEnemyY: float # enemy Y frozen at DECIDE time + decideVelDirDeg: float # enemy vel direction frozen at DECIDE time + decideVelSpeed: float # enemy speed frozen at DECIDE time + decideDist: float # distance to enemy frozen at DECIDE time roundTick: int # ticks elapsed in current round (reset each round) bulletsFired: int # count of bullets fired this round bulletsHit: int # count of bullets that hit this round @@ -379,6 +384,11 @@ method run*(bot: SNNBot) = bot.lastVPerp = vPerp bot.lastDecideGunDir = gunDir bot.lastAbsBearing = absBearing + bot.decideEnemyX = bot.lastEnemyX + bot.decideEnemyY = bot.lastEnemyY + bot.decideVelDirDeg = bot.velDirDeg + bot.decideVelSpeed = bot.velSpeed + bot.decideDist = bot.enemyDist let rate = if bot.bulletsFired > 0: float(bot.bulletsHit) / float(bot.bulletsFired) * 100.0 else: 0.0 bot.gridHasData = gridOffset > -999.0 if gridOffset <= -999.0: @@ -437,33 +447,33 @@ method run*(bot: SNNBot) = of EVALUATE: # Predictive error signal: extrapolate enemy position at bullet impact time. if bot.hasLastPos: - # correctOffset: correct lead angle at BULLET_SPEED - let travelTime = bot.enemyDist / BULLET_SPEED - let velRad = degToRad(bot.velDirDeg) - let futureX = bot.lastEnemyX + cos(velRad) * bot.velSpeed * travelTime - let futureY = bot.lastEnemyY + sin(velRad) * bot.velSpeed * travelTime + # correctOffset: correct lead angle at BULLET_SPEED, using DECIDE-time snapshot + let travelTime = bot.decideDist / BULLET_SPEED + let velRad = degToRad(bot.decideVelDirDeg) + let futureX = bot.decideEnemyX + cos(velRad) * bot.decideVelSpeed * travelTime + let futureY = bot.decideEnemyY + sin(velRad) * bot.decideVelSpeed * travelTime # correctAngle: absolute world-frame bearing [0,360) to predicted enemy position let correctAngle = directionTo(myX, myY, futureX, futureY) - # Adaptive learning dead zone: scale by enemy velocity - let t = (bot.velSpeed / 8.0).clamp(0.0, 1.0) + # Adaptive learning dead zone: scale by DECIDE-time enemy velocity + let t = (bot.decideVelSpeed / 8.0).clamp(0.0, 1.0) let adaptiveDeadZone = 0.5 + (0.3 - 0.5) * t # lerp(0.5, 0.3, t) when USE_RESERVOIR: # correctOffset: angle offset from bare bearing that a bullet at BULLET_SPEED needs let correctOffset = normalizeRelativeAngle(correctAngle - bot.lastAbsBearing) - let producedOffset = bot.res.forward(bot.lastVPerp, bot.enemyDist) + let producedOffset = bot.res.forward(bot.lastVPerp, bot.decideDist) # Bias diagnostics: log grid offset vs correct offset to detect systematic error let biasVal = producedOffset - correctOffset - appendLog(biasDebugLog, fmt"BIAS tick={bot.roundTick} gridOffset={producedOffset:.3f} correctOffset={correctOffset:.3f} bias={biasVal:.3f} vPerp={bot.lastVPerp:.3f} dist={bot.enemyDist:.1f}") + appendLog(biasDebugLog, fmt"BIAS tick={bot.roundTick} gridOffset={producedOffset:.3f} correctOffset={correctOffset:.3f} bias={biasVal:.3f} vPerp={bot.lastVPerp:.3f} dist={bot.decideDist:.1f}") # Learn if: cold start OR error > dead zone if producedOffset > -999.0: let aimErr = abs(normalizeRelativeAngle(producedOffset - correctOffset)) if aimErr > adaptiveDeadZone: - bot.res.learn(bot.lastVPerp, bot.enemyDist, correctOffset) + bot.res.learn(bot.lastVPerp, bot.decideDist, correctOffset) else: - bot.res.learn(bot.lastVPerp, bot.enemyDist, correctOffset) + bot.res.learn(bot.lastVPerp, bot.decideDist, 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 &