## Acceleration predictor gun: tracks velocity and heading changes across frames, ## extrapolates with accel+turn for bots that speed up/slow down. ## Needs 3+ frames for stable accel; falls back to linear (2 frames) or head-on (1 frame). ## Coordinate system: 0° = East, CCW positive (Tank Royale standard). import std/math 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 cachedTurnRate: float ## turn rate (deg/tick) cached per tick cachedTick: int proc initAccelGun*(): AccelGun = AccelGun() proc predict*(g: var AccelGun, state: WorldState, bulletSpeed: float): GunPrediction = if bulletSpeed <= 0.0: return GunPrediction(x: state.enemyX, y: state.enemyY) # Update observation window on new tick if state.tick > g.cachedTick: if g.frames >= 1: # Compute turn rate (normalised to [-180, 180]) var tr = state.enemyHeading - g.prevHeading if tr > 180.0: tr -= 360.0 elif tr < -180.0: tr += 360.0 let tickDelta = max(1, state.tick - g.prevTick).float g.cachedTurnRate = tr / tickDelta if g.frames >= 2: # Compute acceleration g.cachedAccel = (state.enemySpeed - g.prevSpeed) / tickDelta else: g.cachedAccel = 0.0 else: g.cachedTurnRate = 0.0 g.cachedAccel = 0.0 # Advance window g.prevPrevSpeed = g.prevSpeed g.prevSpeed = state.enemySpeed g.prevHeading = state.enemyHeading g.prevTick = state.tick g.cachedTick = state.tick inc g.frames # fallback: not enough history if g.frames < 2: return GunPrediction(x: state.enemyX, y: state.enemyY) let dx = state.enemyX - state.selfX let dy = state.enemyY - state.selfY let dist = sqrt(dx*dx + dy*dy) var t = dist / bulletSpeed # initial ticks estimate let accel = g.cachedAccel let turnRate = g.cachedTurnRate # deg/tick # Iterative convergence (same pattern as circular gun) var ex = state.enemyX var ey = state.enemyY for _ in 0..4: var vx = state.enemySpeed var hd = state.enemyHeading # degrees var px = state.enemyX var py = state.enemyY let iters = int(t + 0.5) for _ in 0..