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:
@@ -146,16 +146,17 @@ proc computeForces(eng: GravityEngine, botPos: Vec2, enemies: openArray[Vec2], a
|
||||
hasClose = true
|
||||
total = total + normalize(botPos - ph.pos) * (KBullet * (1.0 - d/250.0))
|
||||
|
||||
# Priority 2: wall escape (additive — blends with phantom forces)
|
||||
let minW = min(min(botPos.x, arenaW-botPos.x), min(botPos.y, arenaH-botPos.y))
|
||||
if minW < VeryClose:
|
||||
var ex = 0.0; var ey = 0.0
|
||||
if botPos.x < VeryClose: ex = 1.0
|
||||
if arenaW - botPos.x < VeryClose: ex = -1.0
|
||||
if botPos.y < VeryClose: ey = 1.0
|
||||
if arenaH - botPos.y < VeryClose: ey = -1.0
|
||||
let wm = sqrt(ex*ex + ey*ey)
|
||||
if wm > 0.1: total = total + normalize(vec2(ex, ey)) * 500.0
|
||||
# Priority 2: wall escape — scales as inverse-square so it dominates bullets near walls
|
||||
var ex = 0.0; var ey = 0.0
|
||||
let dLeft = botPos.x
|
||||
let dRight = arenaW - botPos.x
|
||||
let dBot = botPos.y
|
||||
let dTop = arenaH - botPos.y
|
||||
if dLeft < WallMinDist: ex += KWall / (dLeft * dLeft)
|
||||
if dRight < WallMinDist: ex -= KWall / (dRight * dRight)
|
||||
if dBot < WallMinDist: ey += KWall / (dBot * dBot)
|
||||
if dTop < WallMinDist: ey -= KWall / (dTop * dTop)
|
||||
total = total + vec2(ex, ey)
|
||||
|
||||
# Priority 3: repulsion from ALL enemies; attraction only toward nearest.
|
||||
# ponytail: linear ramp per enemy, attraction only toward nearest to avoid being pulled into a crowd
|
||||
@@ -181,23 +182,17 @@ proc computeForces(eng: GravityEngine, botPos: Vec2, enemies: openArray[Vec2], a
|
||||
# Too far from nearest only → mild attraction
|
||||
total = total + normalize(ep - botPos) * (KEnemy * t)
|
||||
|
||||
# Priority 4: weak wall repulsion
|
||||
let dL = max(botPos.x, WallMinDist)
|
||||
let dR = max(arenaW - botPos.x, WallMinDist)
|
||||
let dB = max(botPos.y, WallMinDist)
|
||||
let dT = max(arenaH - botPos.y, WallMinDist)
|
||||
total = total + vec2(KWall*0.5/(dL*dL) - KWall*0.5/(dR*dR),
|
||||
KWall*0.5/(dB*dB) - KWall*0.5/(dT*dT))
|
||||
total
|
||||
|
||||
# ── MovementModule wrapper ────────────────────────────────────────────────────
|
||||
|
||||
type PhantomMeteorModule* = object
|
||||
engine: GravityEngine
|
||||
gunheat: GunheatTracker
|
||||
engine: GravityEngine
|
||||
gunheat: GunheatTracker
|
||||
debugGraphics*: bool
|
||||
|
||||
proc initPhantomMeteor*(): PhantomMeteorModule =
|
||||
PhantomMeteorModule(engine: initEngine(), gunheat: initGunheatTracker())
|
||||
PhantomMeteorModule(engine: initEngine(), gunheat: initGunheatTracker(), debugGraphics: false)
|
||||
|
||||
proc resetRound*(m: var PhantomMeteorModule) =
|
||||
## Clear per-round transients (phantoms, waves, energy baseline), keep histogram.
|
||||
@@ -264,27 +259,43 @@ proc computeMove*(m: var PhantomMeteorModule, ws: WorldState): MoveCommand =
|
||||
if force.magnitude < 1e-9:
|
||||
return (speed: 0.0, turnRate: 0.0)
|
||||
|
||||
# Threat centroid (inverse-distance weighted) for perpendicular direction.
|
||||
# With 1 enemy this equals the single enemy bearing — identical to prior behavior.
|
||||
var cx = 0.0; var cy = 0.0; var wsum = 0.0
|
||||
for ep in enemyVecs:
|
||||
let d = max(dist(ep, botPos), 1.0)
|
||||
let w = 1.0 / d
|
||||
cx += ep.x * w; cy += ep.y * w; wsum += w
|
||||
let centroid = if wsum > 1e-9: vec2(cx/wsum, cy/wsum) else: enemyPos
|
||||
let centroidBearingRad = arctan2(centroid.y - botPos.y, centroid.x - botPos.x)
|
||||
# Corner detection: near 2 walls simultaneously → hard escape override.
|
||||
# ponytail: WallMinDist threshold reused; increase if bot still traps at higher speeds.
|
||||
let nearL = botPos.x < WallMinDist
|
||||
let nearR = ws.arenaWidth - botPos.x < WallMinDist
|
||||
let nearB = botPos.y < WallMinDist
|
||||
let nearT = ws.arenaHeight - botPos.y < WallMinDist
|
||||
let wallCount = (if nearL: 1 else: 0) + (if nearR: 1 else: 0) +
|
||||
(if nearB: 1 else: 0) + (if nearT: 1 else: 0)
|
||||
let cornered = wallCount >= 2
|
||||
|
||||
# Two perpendicular directions to centroid bearing (±90°)
|
||||
let perpCCW = vec2(-sin(centroidBearingRad), cos(centroidBearingRad)) # +90°
|
||||
let perpCW = vec2( sin(centroidBearingRad), -cos(centroidBearingRad)) # -90°
|
||||
|
||||
# Pick perpendicular direction that aligns with force vector
|
||||
let fn = force.normalize
|
||||
let desiredDeg =
|
||||
if fn.x * perpCCW.x + fn.y * perpCCW.y >= fn.x * perpCW.x + fn.y * perpCW.y:
|
||||
radToDeg(arctan2(perpCCW.y, perpCCW.x))
|
||||
if cornered:
|
||||
# Escape direction = inward wall normals averaged; ignore bullet/enemy pulls.
|
||||
# Enemy perp is irrelevant here — any direction away from corner is correct.
|
||||
var escX = 0.0; var escY = 0.0
|
||||
if nearL: escX += 1.0
|
||||
if nearR: escX -= 1.0
|
||||
if nearB: escY += 1.0
|
||||
if nearT: escY -= 1.0
|
||||
let escDir = normalize(vec2(escX, escY))
|
||||
radToDeg(arctan2(escDir.y, escDir.x))
|
||||
else:
|
||||
radToDeg(arctan2(perpCW.y, perpCW.x))
|
||||
# Normal open-field: snap to perp that best aligns with force vector.
|
||||
var cx = 0.0; var cy = 0.0; var wsum = 0.0
|
||||
for ep in enemyVecs:
|
||||
let d = max(dist(ep, botPos), 1.0)
|
||||
let w = 1.0 / d
|
||||
cx += ep.x * w; cy += ep.y * w; wsum += w
|
||||
let centroid = if wsum > 1e-9: vec2(cx/wsum, cy/wsum) else: enemyPos
|
||||
let centroidBearingRad = arctan2(centroid.y - botPos.y, centroid.x - botPos.x)
|
||||
let perpCCW = vec2(-sin(centroidBearingRad), cos(centroidBearingRad))
|
||||
let perpCW = vec2( sin(centroidBearingRad), -cos(centroidBearingRad))
|
||||
let fn = force.normalize
|
||||
if fn.x * perpCCW.x + fn.y * perpCCW.y >= fn.x * perpCW.x + fn.y * perpCW.y:
|
||||
radToDeg(arctan2(perpCCW.y, perpCCW.x))
|
||||
else:
|
||||
radToDeg(arctan2(perpCW.y, perpCW.x))
|
||||
|
||||
# Delta from current heading
|
||||
var delta = desiredDeg - ws.selfHeading
|
||||
|
||||
Reference in New Issue
Block a user