fix(SNNBot): freeze DECIDE-time snapshot for EVALUATE — eliminates training signal noise
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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 &
|
||||
|
||||
Reference in New Issue
Block a user