## Virtual body tracker — mirrors virtual_bullets.nim but for movement modules. ## Tracks simulated positions for each movement candidate under enemy waves. ## Scores modules by how often they sit in high-danger GF bins. ## Caller feeds actual hit data via registerHit() to build the danger profile. import std/math import gun_harness/gun_interface import movement_harness/movement_interface import movement_harness/gunheat_tracker import movement_harness/bullet_shadows const VB_BINS* = 31 ## GF bins from -1 to +1 MaxWaves* = 64 ## hard cap; ponytail: ring buffer, resize if needed MaxModules* = 8 ## max movement modules tracked type VBWave = object fireX, fireY: float fireBearingRad: float ## bearing from fireOrigin to REAL bot at fire time (radians) speed: float radius: float fireTick: int startDist: float VirtualBody = object x, y: float heading: float ## degrees (Tank Royale / bot API) speed: float ## current speed px/tick VirtualBodyTracker* = object numModules: int bodies: array[MaxModules, VirtualBody] dangerScore: array[MaxModules, float] ## accumulated danger hits dangerBins: array[VB_BINS, float] ## populated from registerHit waves: array[MaxWaves, VBWave] waveCount: int waveHead: int ## ring buffer head gunheat: GunheatTracker shadows*: ShadowTracker ## our bullets in flight for shadow computation arenaW, arenaH: float proc initVirtualBodyTracker*(numModules: int): VirtualBodyTracker = result.numModules = numModules result.gunheat = initGunheatTracker() # Seed bins so we have a uniform prior before any real hits for i in 0.. MaxModules: {.error: "cmds array exceeds MaxModules".} if i < N: advanceBody(t.bodies[i], cmds[i], state.arenaWidth, state.arenaHeight) # --- Advance our bullets (for shadow tracking) --- t.shadows.tick() # --- Advance waves and score --- # ponytail: O(waves * modules), small counts, fine for wi in 0.. PI: off -= 2.0 * PI while off < -PI: off += 2.0 * PI let maxA = mea(w.speed) if maxA >= 1e-9: let gf = clamp(off / maxA, -1.0, 1.0) let bin = gfToBin(gf) # Shadow zones are guaranteed safe — reduce danger by 90% let shadowMul = if isShadowed(waveShadows, gf): 0.1 else: 1.0 t.dangerScore[mi] += t.dangerBins[bin] * shadowMul proc bestMovement*(t: VirtualBodyTracker): int = ## Returns index of module with lowest accumulated danger score. result = 0 var best = t.dangerScore[0] for i in 1..