j134 fire fix: share ONE fire_tracker across tfil/ring/strafe/learned/surf (TR_FIRE_FIX, default on); env-gated TR_FIRE_DIAG alignment trace

This commit is contained in:
2026-09-26 12:40:19 +02:00
parent bd57db6d63
commit 6ad5d99922
11 changed files with 435 additions and 214 deletions
+52 -46
View File
@@ -84,6 +84,7 @@ import std/[math, os]
from std/strutils import parseFloat, parseInt, strip, toLowerAscii
import gun_harness/gun_interface
import movement_harness/movement_interface
import movement_harness/fire_tracker
import bitbrain/sbc
const
@@ -163,6 +164,9 @@ var
LearnedLabel* = llHistogram
LearnedRealEvents* = false
LearnedLog* = false
## j134: the shared fire-detection correction (`TR_FIRE_FIX`, default on).
## Off = the shipped `prev - energy` detector byte-for-byte.
LearnedFireFix* = true
proc getEnvFloat(name: string, default: float): float =
let s = getEnv(name, "")
@@ -195,6 +199,7 @@ proc loadLearnedEnv*() =
LearnedWallMargin = max(0.0, getEnvFloat(LearnedWallMarginEnv, 48.0))
LearnedGlobal = envOn(LearnedGlobalEnv)
LearnedRealEvents = envOn(LearnedRealEventsEnv)
LearnedFireFix = envOn("TR_FIRE_FIX", true)
LearnedLog = envOn(LearnedLogEnv)
LearnedLabel =
case getEnv(LearnedLabelEnv, "").strip().toLowerAscii()
@@ -272,7 +277,7 @@ type
missGlobal: int ## llOutcome: non-hits seen (for the prior)
scores: seq[float]
waves: seq[LSWave]
prevEnergy: seq[tuple[id: int, energy: float]]
fire: FireTracker ## shared energy-drop detector (j134)
strafeDir: float64
dir: float64 ## direction commanded last tick (+-1)
prevX, prevY: float64 ## our position one tick ago
@@ -290,7 +295,7 @@ proc resetRound*(m: var LearnedSurferModule) =
## it survives a round boundary without becoming a battle-long static average
## (the j115 defect). Use `resetBattle` for a hard wipe.
m.waves = @[]
m.prevEnergy = @[]
m.fire.reset()
m.strafeDir = 1.0
m.dir = 1.0
m.prevX = 0.0
@@ -307,6 +312,7 @@ proc resetRound*(m: var LearnedSurferModule) =
proc initLearnedSurfer*(): LearnedSurferModule =
result.debugGraphics = false
result.fire = initFireTracker(LearnedFireFix)
result.sbc = initCountedSbc(LS_NADE, LS_BINS,
max(0, LearnedDecayEvery),
max(0, LearnedDecayShift))
@@ -485,58 +491,57 @@ proc predictState*(m: var LearnedSurferModule, row, col: int,
# ── fire detection + state ──────────────────────────────────────────────────
proc prevEnergyGet(m: LearnedSurferModule, id: int): float =
for e in m.prevEnergy:
if e.id == id: return e.energy
100.0
proc prevEnergyGet(m: LearnedSurferModule, id: int): float = m.fire.prevEnergyGet(id)
proc prevEnergySet(m: var LearnedSurferModule, id: int, energy: float) =
for i in 0..<m.prevEnergy.len:
if m.prevEnergy[i].id == id:
m.prevEnergy[i].energy = energy
return
m.prevEnergy.add((id: id, energy: energy))
m.fire.prevEnergySet(id, energy)
proc noteEnemyBulletHit*(m: var LearnedSurferModule, power: float) =
## `onHitByBullet` -> the shooter's `3*power` bonus (no-op when off).
m.fire.noteEnemyBulletHit(power)
proc noteDamageDealt*(m: var LearnedSurferModule, damage: float) =
## `onBulletHit` -> our same-tick damage to the enemy (no-op when off).
m.fire.noteDamageDealt(damage)
proc detectFire(m: var LearnedSurferModule, id: int, ex, ey, eenergy: float,
ws: WorldState) =
## One enemy's energy sample. A plausible one-tick firepower drop IS a wave;
## the state is the wave-relative state at THIS tick (the fire tick).
let prev = m.prevEnergyGet(id)
let drop = prev - eenergy
m.prevEnergySet(id, eenergy)
if drop < 0.09 or drop > 3.01: return
## One enemy's energy sample. A plausible (corrected) one-tick firepower
## drop IS a wave; the state is the wave-relative state at THIS tick (the
## fire tick). Window 0.09..3.01, LEARNED's shipped window; the shared
## tracker corrects the delta and splits an over-cap drop across waves.
for drop in m.fire.detect(id, eenergy, 0.09, 3.01):
let botX = ws.selfX
let botY = ws.selfY
let bspeed = 20.0 - 3.0 * drop
let d = hypot(botX - ex, botY - ey)
let bearing = arctan2(botY - ey, botX - ex) # centre line
let ux = cos(bearing)
let uy = sin(bearing)
let botX = ws.selfX
let botY = ws.selfY
let bspeed = 20.0 - 3.0 * drop
let d = hypot(botX - ex, botY - ey)
let bearing = arctan2(botY - ey, botX - ex) # centre line
let ux = cos(bearing)
let uy = sin(bearing)
# vlat: lateral velocity in the wave frame. The centre line passes through us
# at this tick, so lat(now) == 0 and vlat == -lat(prev).
let dxp = m.prevX - ex
let dyp = m.prevY - ey
var vlat = -(dxp * (-uy) + dyp * ux)
if m.prevX == 0.0 and m.prevY == 0.0 and m.prevHeading == 0.0: vlat = 0.0
let roomDx = if vlat >= 0.0: -uy else: uy
let roomDy = if vlat >= 0.0: ux else: -ux
let room = roomToWall(botX, botY, roomDx, roomDy, ws.arenaWidth, ws.arenaHeight)
let turn = wrap180(float(ws.selfHeading) - float(m.prevHeading))
# vlat: lateral velocity in the wave frame. The centre line passes through us
# at this tick, so lat(now) == 0 and vlat == -lat(prev).
let dxp = m.prevX - ex
let dyp = m.prevY - ey
var vlat = -(dxp * (-uy) + dyp * ux)
if m.prevX == 0.0 and m.prevY == 0.0 and m.prevHeading == 0.0: vlat = 0.0
let roomDx = if vlat >= 0.0: -uy else: uy
let roomDy = if vlat >= 0.0: ux else: -ux
let room = roomToWall(botX, botY, roomDx, roomDy, ws.arenaWidth, ws.arenaHeight)
let turn = wrap180(float(ws.selfHeading) - float(m.prevHeading))
let row = code(vlat, VlatEdges) * LS_Q + code(d, DistEdges)
let col = code(room, RoomEdges) * LS_Q + code(turn, TurnEdges)
let row = code(vlat, VlatEdges) * LS_Q + code(d, DistEdges)
let col = code(room, RoomEdges) * LS_Q + code(turn, TurnEdges)
m.waves.add LSWave(
ownerId: id, fireTick: ws.tick,
originX: ex, originY: ey, bearing: bearing, speed: bspeed,
startDist: d, power: drop,
ticksLeft: max(1, int(ceil(d / max(bspeed, 1e-9)))),
fresh: true,
selfEnergyAtFire: ws.selfEnergy,
stateRow: row, stateCol: col,
)
m.waves.add LSWave(
ownerId: id, fireTick: ws.tick,
originX: ex, originY: ey, bearing: bearing, speed: bspeed,
startDist: d, power: drop,
ticksLeft: max(1, int(ceil(d / max(bspeed, 1e-9)))),
fresh: true,
selfEnergyAtFire: ws.selfEnergy,
stateRow: row, stateCol: col,
)
# ── the mover ───────────────────────────────────────────────────────────────
@@ -551,6 +556,7 @@ proc computeMove*(m: var LearnedSurferModule, ws: WorldState): MoveCommand =
m.detectFire(ei.id, ei.x, ei.y, ei.energy, ws)
if seen == 0 and (ws.enemyX != 0.0 or ws.enemyY != 0.0):
m.detectFire(-1, ws.enemyX, ws.enemyY, ws.enemyEnergy, ws)
m.fire.endScan()
# ── 2. advance + resolve waves; every resolution is a training sample ────
var i = 0