## ModularBot — plugin gun architecture tracer bullet. ## Guns: HeadOnGun (0), LinearGun (1), TsetlinGun (2), CircularGun (3), GFGun (4), PatternMatcherGun (5), AntiSurferGun (6) via GunHarness. ## Radar: RadarLockModule via radar harness. ## Movement: OscillatorModule (perpendicular strafing). import std/[math, os, strformat] import robocode_tankroyale_botapi import radar_harness/radar_interface import radars/radar_lock_module import gun_harness/gun_interface import gun_harness/virtual_bullets as vb import gun_harness/selector import guns/head_on import guns/linear import guns/circular import guns/tsetlin import guns/guess_factor import guns/pattern_matcher import guns/anti_surfer import guns/wall_bounce import movements/phantom_meteor const botJsonPath = currentSourcePath().parentDir / "ModularBot.json" const DebugVBullets = false const DebugCircular = false const GunNames = ["HeadOn", "Linear", "Tsetlin", "Circular", "GuessFactor", "Pattern", "AntiSurf", "WallBounce"] type ModularBot = ref object of Bot hasContact: bool enemyBearing: float lastState: WorldState radar: RadarLockModule tracker: VirtualTracker headOn: HeadOnGun linear: LinearGun circular: CircularGun tsetlin: TsetlinGun guessFactor: GFGun patternMatcher: PatternMatcherGun antiSurfer: AntiSurferGun wallBounce: WallBounceGun mover: PhantomMeteorModule virtualHits: int virtualMiss: int tick: int currentGun: int proc buildState(bot: ModularBot, ex, ey, espeed, eheading, eenergy: float): WorldState = WorldState( enemyX: ex, enemyY: ey, enemySpeed: espeed, enemyHeading: eheading, enemyEnergy: eenergy, selfX: getX(), selfY: getY(), selfSpeed: getSpeed(), selfHeading: getDirection(), selfRadarHeading: getRadarDirection(), selfEnergy: getEnergy(), arenaWidth: getArenaWidth().float, arenaHeight: getArenaHeight().float, tick: bot.tick, ) method onScannedBot*(bot: ModularBot, e: ScannedBotEvent) = bot.enemyBearing = directionTo(getX(), getY(), e.x, e.y) bot.hasContact = true bot.lastState = bot.buildState(e.x, e.y, e.speed, e.direction, e.energy) # Collect predictions for all power bins from all guns var headsUp: array[len(PowerBins), GunPrediction] var linPreds: array[len(PowerBins), GunPrediction] var tmPreds: array[len(PowerBins), GunPrediction] var circPreds: array[len(PowerBins), GunPrediction] var gfPreds: array[len(PowerBins), GunPrediction] var pmPreds: array[len(PowerBins), GunPrediction] var asPreds: array[len(PowerBins), GunPrediction] var wbPreds: array[len(PowerBins), GunPrediction] for i in 0.. 0: bot.virtualHits.float / total.float * 100.0 else: 0.0 echo fmt"[vbullet] gun={gunId} bin={binIdx} miss={fe.missDistance:.1f}px hit={fe.hit} | total hits={bot.virtualHits}/{total} ({pct:.1f}%)" ) # Gun selection + fire let (selectedGun, _, power) = selectShot(bot.tracker) # Log gun switch + update turret/bullet colors if selectedGun != bot.currentGun: echo fmt"[gun] switched: {GunNames[selectedGun]}" bot.currentGun = selectedGun case selectedGun of 0: setTurretColor("#FF3333"); setBulletColor("#FF6666") # HeadOn: warm red of 1: setTurretColor("#3366FF"); setBulletColor("#6699FF") # Linear: electric blue of 2: setTurretColor("#9933FF"); setBulletColor("#CC66FF") # Tsetlin: violet of 3: setTurretColor("#33CC33"); setBulletColor("#66FF66") # Circular: emerald of 4: setTurretColor("#FFCC00"); setBulletColor("#FFE066") # GuessFactor: yellow of 5: setTurretColor("#FF6600"); setBulletColor("#FF9944") # Pattern: orange of 6: setTurretColor("#00CCFF"); setBulletColor("#66DDFF") # AntiSurf: cyan of 7: setTurretColor("#CCCCCC"); setBulletColor("#EEEEEE") # WallBounce: silver/white else: discard let pred = case selectedGun of 1: bot.linear.predict(bot.lastState, bulletSpeed(power)) of 2: bot.tsetlin.predict(bot.lastState, bulletSpeed(power)) of 3: bot.circular.predict(bot.lastState, bulletSpeed(power)) of 4: bot.guessFactor.predict(bot.lastState, bulletSpeed(power)) of 5: bot.patternMatcher.predict(bot.lastState, bulletSpeed(power)) of 6: bot.antiSurfer.predict(bot.lastState, bulletSpeed(power)) of 7: bot.wallBounce.predict(bot.lastState, bulletSpeed(power)) else: bot.headOn.predict(bot.lastState, bulletSpeed(power)) let target = aimAngle(getX(), getY(), pred.x, pred.y) let gunDir = getGunDirection() let gunHeat = getGunHeat() let gunDelta = (target - gunDir) mod 360.0 var normDelta = gunDelta if normDelta > 180.0: normDelta -= 360.0 elif normDelta < -180.0: normDelta += 360.0 if shouldFire(gunDir, target, gunHeat): discard setFire(power) # Turn gun toward predicted point every scan setGunTurnRate(normDelta) method onRoundEnded*(bot: ModularBot, e: RoundEndedEventForBot) = discard # Skip detailed logging for now method onRoundStarted*(bot: ModularBot, e: RoundStartedEvent) = setAdjustGunForBodyTurn(true) setAdjustRadarForBodyTurn(true) setAdjustRadarForGunTurn(true) setBodyColor("#FF9900") # PhantomMeteor: deep orange/gold setRadarColor("#00CCCC") # RadarLock: cyan/teal setScanColor("#33FFFF") # RadarLock scan arc: bright teal bot.radar.init() bot.hasContact = false bot.tick = 0 bot.currentGun = -1 # Reset to trigger initial log on first selectShot bot.mover.resetRound() echo fmt"[gun] starting: {GunNames[0]}" # tracker fitness persists across rounds (rolling window carries over) method onGameStarted*(bot: ModularBot, e: GameStartedEventForBot) = discard method run*(bot: ModularBot) = while isRunning(): inc bot.tick if not bot.hasContact: setTargetSpeed(0.0) setTurnRate(0.0) setRadarTurnRate(45.0) go() continue # Movement harness: oscillate perpendicular to enemy let (spd, tr) = bot.mover.computeMove(bot.lastState) setTargetSpeed(spd) setTurnRate(tr) setRadarTurnRate(bot.radar.computeScan(bot.lastState)) go() when isMainModule: var bot = ModularBot( tracker: vb.initTracker(8), # 0: HeadOn, 1: Linear, 2: Tsetlin, 3: Circular, 4: GuessFactor, 5: Pattern, 6: AntiSurf, 7: WallBounce headOn: HeadOnGun(), linear: LinearGun(), circular: CircularGun(), tsetlin: initTsetlinGun(), guessFactor: initGFGun(), patternMatcher: PatternMatcherGun(), antiSurfer: initAntiSurferGun(), wallBounce: initWallBounceGun(), radar: RadarLockModule(), mover: initPhantomMeteor(), currentGun: -1, ) start(bot, botJsonPath)