Files
SirStone 2cc2a3bd87 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
2026-09-20 20:44:45 +02:00

58 lines
2.3 KiB
Nim

## random_oscillator.nim — perpendicular strafing with randomized reversal timing.
## Harder to predict than fixed-period oscillation; same wall-lockout guard as oscillator.nim.
import std/math
import std/random
import std/times
import gun_harness/gun_interface
import movement_harness/movement_interface
const
MaxSpeed* = 8.0 ## Tank Royale max speed
MinPeriod* = 15 ## shortest reversal interval (ticks)
MaxPeriod* = 45 ## longest reversal interval (ticks)
WallMargin* = 80.0 ## px from wall to trigger forced reversal
WallLockout* = 20 ## ticks to suppress further wall-reversals after one fires
type RandomOscillatorModule* = object
sign: float ## +1 or -1
elapsed: int ## ticks since last reversal
nextPeriod: int ## random period chosen at last reversal
wallLockout: int ## remaining suppression ticks
rng: Rand
debugGraphics*: bool
proc initRandomOscillator*(): RandomOscillatorModule =
var rng = initRand(getTime().toUnix())
let period = rng.rand(MinPeriod..MaxPeriod)
RandomOscillatorModule(sign: 1.0, elapsed: 0, nextPeriod: period,
wallLockout: 0, rng: rng, debugGraphics: false)
proc computeMove*(m: var RandomOscillatorModule, ws: WorldState): MoveCommand =
inc m.elapsed
if m.wallLockout > 0: dec m.wallLockout
let nearWall = ws.selfX < WallMargin or ws.selfX > ws.arenaWidth - WallMargin or
ws.selfY < WallMargin or ws.selfY > ws.arenaHeight - WallMargin
if m.elapsed >= m.nextPeriod or (nearWall and m.wallLockout == 0):
m.sign *= -1.0
m.elapsed = 0
m.nextPeriod = m.rng.rand(MinPeriod..MaxPeriod)
if nearWall: m.wallLockout = WallLockout
# Perpendicular heading to enemy bearing (same dot-product trick as phantom_meteor)
let enemyBearingRad = arctan2(ws.enemyY - ws.selfY, ws.enemyX - ws.selfX)
let perpHeadingDeg = radToDeg(enemyBearingRad) + 90.0
var delta = perpHeadingDeg - ws.selfHeading
while delta > 180.0: delta -= 360.0
while delta < -180.0: delta += 360.0
# Dot-product trick: if going backward, flip turn to stay perpendicular
var normDelta = if m.sign < 0: delta - 180.0 else: delta
while normDelta > 180.0: normDelta -= 360.0
while normDelta < -180.0: normDelta += 360.0
(speed: m.sign * MaxSpeed, turnRate: normDelta.clamp(-10.0, 10.0))