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