## 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 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) 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))