fix(ModularBot): ram loop prevention, dead-target guards, cleaner logging
- 30-tick cooldown after ghost-stuck/timeout ram exit prevents re-entry loop - enemy_tracker.update() skips dead bots to prevent same-tick scan resurrection - TFIL graphics cleared when ramming is active movement - [config] logs: white base with green-highlighted changes only - [ram:enter] logs trigger reason and key values on false→true transition - [death] and [target-invalid] logs retained for diagnostics
This commit is contained in:
@@ -9,17 +9,18 @@ import gun_harness/gun_interface
|
||||
const MaxSpeed = 8.0 ## TR bot max speed (px/tick)
|
||||
|
||||
type AccelGun* = object
|
||||
prevSpeed: float
|
||||
prevHeading: float
|
||||
prevPrevSpeed: float
|
||||
prevTick: int
|
||||
frames: int ## how many observations we have
|
||||
cachedAccel: float ## accel (px/tick²) cached per tick
|
||||
prevSpeed: float
|
||||
prevHeading: float
|
||||
prevPrevSpeed: float
|
||||
prevTick: int
|
||||
frames: int ## how many observations we have
|
||||
cachedAccel: float ## accel (px/tick²) cached per tick
|
||||
cachedTurnRate: float ## turn rate (deg/tick) cached per tick
|
||||
cachedTick: int
|
||||
cachedTick: int
|
||||
debugGraphics*: bool
|
||||
|
||||
proc initAccelGun*(): AccelGun =
|
||||
AccelGun()
|
||||
AccelGun(debugGraphics: false)
|
||||
|
||||
proc predict*(g: var AccelGun, state: WorldState, bulletSpeed: float): GunPrediction =
|
||||
if bulletSpeed <= 0.0:
|
||||
|
||||
@@ -21,9 +21,11 @@ type
|
||||
waves: seq[ASWave]
|
||||
cachedTick: int
|
||||
cachedWaveStored: bool
|
||||
debugGraphics*: bool
|
||||
|
||||
proc initAntiSurferGun*(): AntiSurferGun =
|
||||
result.cachedTick = -1
|
||||
result.debugGraphics = false
|
||||
# Same triangular head-on prior as GFGun — cold-start aims head-on.
|
||||
let center = (ASBins - 1) div 2 # = 15
|
||||
for i in 0..<ASBins:
|
||||
|
||||
@@ -7,14 +7,15 @@ import guns/circular
|
||||
import guns/wall_bounce
|
||||
|
||||
type AveragedLeadGun* = object
|
||||
linear: LinearGun
|
||||
circular: CircularGun
|
||||
wallBounce: WallBounceGun
|
||||
cachedTick: int
|
||||
cachedPred: GunPrediction # per-tick cache; ponytail: single cache, extend if multi-power needed
|
||||
linear: LinearGun
|
||||
circular: CircularGun
|
||||
wallBounce: WallBounceGun
|
||||
cachedTick: int
|
||||
cachedPred: GunPrediction # per-tick cache; ponytail: single cache, extend if multi-power needed
|
||||
debugGraphics*: bool
|
||||
|
||||
proc initAveragedLeadGun*(): AveragedLeadGun =
|
||||
AveragedLeadGun(wallBounce: initWallBounceGun())
|
||||
AveragedLeadGun(wallBounce: initWallBounceGun(), debugGraphics: false)
|
||||
|
||||
proc predict*(g: var AveragedLeadGun, state: WorldState, bulletSpeed: float): GunPrediction =
|
||||
if state.tick == g.cachedTick:
|
||||
|
||||
@@ -6,11 +6,12 @@ import std/math
|
||||
import gun_harness/gun_interface
|
||||
|
||||
type CircularGun* = object
|
||||
prevHeading: float ## enemy heading from the tick before current
|
||||
prevTick: int ## tick of prevHeading observation
|
||||
hasPrev: bool
|
||||
cachedOmega: float ## omega (rad/tick) computed on first call this tick
|
||||
cachedTick: int ## tick for which cachedOmega was computed
|
||||
prevHeading: float ## enemy heading from the tick before current
|
||||
prevTick: int ## tick of prevHeading observation
|
||||
hasPrev: bool
|
||||
cachedOmega: float ## omega (rad/tick) computed on first call this tick
|
||||
cachedTick: int ## tick for which cachedOmega was computed
|
||||
debugGraphics*: bool
|
||||
|
||||
proc predict*(g: var CircularGun, state: WorldState, bulletSpeed: float): GunPrediction =
|
||||
if bulletSpeed <= 0.0:
|
||||
|
||||
@@ -20,9 +20,11 @@ type
|
||||
waves: seq[DWave]
|
||||
cachedTick: int
|
||||
cachedWaveStored: bool
|
||||
debugGraphics*: bool
|
||||
|
||||
proc initDecayGFGun*(): DecayGFGun =
|
||||
result.cachedTick = -1
|
||||
result.debugGraphics = false
|
||||
let center = (GFBins - 1) div 2
|
||||
for i in 0..<GFBins:
|
||||
let d = abs(i - center)
|
||||
|
||||
@@ -10,13 +10,14 @@ const WindowSize = 15 # ticks; N+1 frames stored
|
||||
|
||||
type
|
||||
DisplacementGun* = object
|
||||
posX: array[WindowSize + 1, float]
|
||||
posY: array[WindowSize + 1, float]
|
||||
count: int # frames collected so far
|
||||
head: int # ring-buffer head
|
||||
lastTick: int # for per-tick cache
|
||||
cacheSpeed: float
|
||||
cachePred: GunPrediction
|
||||
posX: array[WindowSize + 1, float]
|
||||
posY: array[WindowSize + 1, float]
|
||||
count: int # frames collected so far
|
||||
head: int # ring-buffer head
|
||||
lastTick: int # for per-tick cache
|
||||
cacheSpeed: float
|
||||
cachePred: GunPrediction
|
||||
debugGraphics*: bool
|
||||
|
||||
proc predict*(g: var DisplacementGun, state: WorldState, bulletSpeed: float): GunPrediction =
|
||||
# Per-tick cache: same tick + same speed => same prediction
|
||||
@@ -62,4 +63,4 @@ proc onResult*(g: var DisplacementGun, e: FeedbackEvent) =
|
||||
discard # analytical gun — no learning
|
||||
|
||||
proc initDisplacementGun*(): DisplacementGun =
|
||||
DisplacementGun(count: 0, head: 0, lastTick: -1)
|
||||
DisplacementGun(count: 0, head: 0, lastTick: -1, debugGraphics: false)
|
||||
|
||||
@@ -17,14 +17,16 @@ type
|
||||
# mea not stored — recomputed from FeedbackEvent.bulletPower at resolution time
|
||||
|
||||
GFGun* = object
|
||||
bins: array[GFBins, float]
|
||||
waves: seq[Wave] # pending unresolved waves
|
||||
bins: array[GFBins, float]
|
||||
waves: seq[Wave] # pending unresolved waves
|
||||
# per-tick cache: store wave only once across multiple power-bin calls
|
||||
cachedTick: int
|
||||
cachedTick: int
|
||||
cachedWaveStored: bool
|
||||
debugGraphics*: bool
|
||||
|
||||
proc initGFGun*(): GFGun =
|
||||
result.cachedTick = -1
|
||||
result.debugGraphics = false
|
||||
# Seed with a head-on prior: triangular bump at bin 15 (GF=0).
|
||||
# Prevents the cold-start tie-break to GF=-1 (bin 0) that poisons early fitness.
|
||||
let center = (GFBins - 1) div 2 # = 15
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import gun_harness/gun_interface
|
||||
|
||||
type HeadOnGun* = object
|
||||
discard
|
||||
debugGraphics*: bool
|
||||
|
||||
proc predict*(g: var HeadOnGun, state: WorldState, bulletSpeed: float): GunPrediction =
|
||||
GunPrediction(x: state.enemyX, y: state.enemyY)
|
||||
|
||||
@@ -23,23 +23,25 @@ type
|
||||
feat: array[7, float]
|
||||
|
||||
KNNGun* = object
|
||||
obs: seq[Obs]
|
||||
obsHead: int # ring-buffer write index
|
||||
waves: seq[KNNWave]
|
||||
obs: seq[Obs]
|
||||
obsHead: int # ring-buffer write index
|
||||
waves: seq[KNNWave]
|
||||
# per-tick cache
|
||||
cachedTick: int
|
||||
cachedWaveStored: bool
|
||||
cachedTick: int
|
||||
cachedWaveStored: bool
|
||||
# rolling normalization ranges
|
||||
featMin: array[7, float]
|
||||
featMax: array[7, float]
|
||||
featMin: array[7, float]
|
||||
featMax: array[7, float]
|
||||
# state for feature extraction
|
||||
lastSpeed: float
|
||||
lastDirection: float # +1 or -1
|
||||
lastSpeed: float
|
||||
lastDirection: float # +1 or -1
|
||||
timeSinceDirChange: int
|
||||
debugGraphics*: bool
|
||||
|
||||
proc initKNNGun*(): KNNGun =
|
||||
result.cachedTick = -1
|
||||
result.cachedTick = -1
|
||||
result.lastDirection = 1.0
|
||||
result.debugGraphics = false
|
||||
for i in 0..6:
|
||||
result.featMin[i] = 1e18
|
||||
result.featMax[i] = -1e18
|
||||
|
||||
@@ -5,7 +5,7 @@ import std/math
|
||||
import gun_harness/gun_interface
|
||||
|
||||
type LinearGun* = object
|
||||
discard
|
||||
debugGraphics*: bool
|
||||
|
||||
proc predict*(g: var LinearGun, state: WorldState, bulletSpeed: float): GunPrediction =
|
||||
let dist = hypot(state.enemyX - state.selfX, state.enemyY - state.selfY)
|
||||
|
||||
@@ -16,18 +16,19 @@ type
|
||||
headingDelta: float ## heading change in radians this tick
|
||||
|
||||
PatternMatcherGun* = object
|
||||
buf: array[HistorySize, MoveTick]
|
||||
head: int ## next write index (circular)
|
||||
count: int ## filled entries (capped at HistorySize)
|
||||
prevHeading: float
|
||||
prevSpeed: float
|
||||
prevTick: int
|
||||
hasPrev: bool
|
||||
buf: array[HistorySize, MoveTick]
|
||||
head: int ## next write index (circular)
|
||||
count: int ## filled entries (capped at HistorySize)
|
||||
prevHeading: float
|
||||
prevSpeed: float
|
||||
prevTick: int
|
||||
hasPrev: bool
|
||||
# per-tick cache — avoid re-searching for multiple power bins
|
||||
cacheTick: int
|
||||
cacheX: float
|
||||
cacheY: float
|
||||
cacheValid: bool
|
||||
cacheTick: int
|
||||
cacheX: float
|
||||
cacheY: float
|
||||
cacheValid: bool
|
||||
debugGraphics*: bool
|
||||
|
||||
# --- circular buffer helpers ---
|
||||
|
||||
|
||||
@@ -11,15 +11,16 @@ const BrakeDecel = 2.0 ## same-direction stop
|
||||
const CoastDecel = 1.0 ## cross-zero coasting stop
|
||||
|
||||
type StopShotGun* = object
|
||||
prevSpeed: float
|
||||
prevHeading: float
|
||||
prevTick: int
|
||||
frames: int
|
||||
cachedTick: int
|
||||
cachedPredX: float
|
||||
cachedPredY: float
|
||||
prevSpeed: float
|
||||
prevHeading: float
|
||||
prevTick: int
|
||||
frames: int
|
||||
cachedTick: int
|
||||
cachedPredX: float
|
||||
cachedPredY: float
|
||||
debugGraphics*: bool
|
||||
|
||||
proc initStopShotGun*(): StopShotGun = StopShotGun()
|
||||
proc initStopShotGun*(): StopShotGun = StopShotGun(debugGraphics: false)
|
||||
|
||||
proc predict*(g: var StopShotGun, state: WorldState, bulletSpeed: float): GunPrediction =
|
||||
if bulletSpeed <= 0.0:
|
||||
|
||||
@@ -200,17 +200,19 @@ type
|
||||
alive: bool
|
||||
|
||||
TsetlinGun* = object
|
||||
net: TmNet
|
||||
frameBuffer: array[TM_WINDOW_SIZE, TmFrameEncoded]
|
||||
bufferCount: int
|
||||
traces: array[TM_TRACE_SLOTS, TmTrace]
|
||||
traceHead: int
|
||||
shotCount: int ## total onResult calls received
|
||||
net: TmNet
|
||||
frameBuffer: array[TM_WINDOW_SIZE, TmFrameEncoded]
|
||||
bufferCount: int
|
||||
traces: array[TM_TRACE_SLOTS, TmTrace]
|
||||
traceHead: int
|
||||
shotCount: int ## total onResult calls received
|
||||
debugGraphics*: bool
|
||||
|
||||
proc initTsetlinGun*(): TsetlinGun =
|
||||
# states init at 0 (boundary); one Type I step crosses into Include
|
||||
for s in result.net.states.mitems: s = 0'i16
|
||||
randomize()
|
||||
result.debugGraphics = false
|
||||
|
||||
proc isWarmedUp*(g: TsetlinGun): bool {.inline.} =
|
||||
g.bufferCount >= TM_WINDOW_SIZE
|
||||
|
||||
@@ -6,9 +6,9 @@ import std/math
|
||||
import gun_harness/gun_interface
|
||||
|
||||
type WallBounceGun* = object
|
||||
discard
|
||||
debugGraphics*: bool
|
||||
|
||||
proc initWallBounceGun*(): WallBounceGun = WallBounceGun()
|
||||
proc initWallBounceGun*(): WallBounceGun = WallBounceGun(debugGraphics: false)
|
||||
|
||||
proc simulateBounce(ex, ey, heading, speed: float, ticks: int,
|
||||
arenaW, arenaH: float): (float, float) =
|
||||
|
||||
Reference in New Issue
Block a user