# PatternMover — repeats a fixed movement sequence, head-on firing. # Purpose: test pattern-matching gun. import std/[math, os] import robocode_tankroyale_botapi const botJsonPath = currentSourcePath().parentDir / "PatternMover.json" # Sequence: (distance, turn_deg) pairs type Step = tuple[dist: float, turn: float] const STEPS: array[6, Step] = [ (100.0, 0.0), ( 0.0, 45.0), ( 50.0, 0.0), ( 0.0, -90.0), ( 75.0, 0.0), ( 0.0, 30.0), ] const MOVE_SPEED = 8.0 STALL_LIMIT = 20 # ticks without progress before the step is abandoned type PatternMover = ref object of Bot stepIdx: int remaining: float # distance left in current move step, or turn degrees inTurn: bool # true while executing a turn sub-step lastX: float lastY: float lastDir: float haveLast: bool stallTicks: int method onRoundStarted*(bot: PatternMover, e: RoundStartedEvent) = setAdjustGunForBodyTurn(true) setAdjustRadarForBodyTurn(true) setAdjustRadarForGunTurn(true) bot.stepIdx = 0 bot.remaining = STEPS[0].dist bot.inTurn = false bot.haveLast = false bot.stallTicks = 0 method onScannedBot*(bot: PatternMover, e: ScannedBotEvent) = let bearing = directionTo(getX(), getY(), e.x, e.y) let gunDelta = normalizeRelativeAngle(bearing - getGunDirection()) setGunTurnRate(gunDelta.clamp(-MAX_GUN_TURN_RATE, MAX_GUN_TURN_RATE)) if abs(gunDelta) < 10.0 and getGunHeat() <= 0.0: discard setFire(1.0) proc advanceStep(bot: PatternMover) = bot.stepIdx = (bot.stepIdx + 1) mod STEPS.len let s = STEPS[bot.stepIdx] if s.dist > 0.0: bot.inTurn = false bot.remaining = s.dist else: bot.inTurn = true bot.remaining = abs(s.turn) bot.stallTicks = 0 method run*(bot: PatternMover) = while isRunning(): setRadarTurnRate(45.0) # Progress is measured from real movement / real turning, never from the # requested intent, so a blocked bot cannot run its step counter down. var moved = 0.0 var turned = 0.0 if bot.haveLast: moved = distanceTo(bot.lastX, bot.lastY, getX(), getY()) turned = abs(normalizeRelativeAngle(getDirection() - bot.lastDir)) if bot.inTurn: let s = STEPS[bot.stepIdx] let sign = if s.turn >= 0: 1.0 else: -1.0 let rate = min(bot.remaining, MAX_TURN_RATE) setTurnRate(sign * rate) setTargetSpeed(0.0) bot.remaining -= turned if turned < 0.05: inc bot.stallTicks else: bot.stallTicks = 0 else: # If the forward move is actually blocked (e.g. against a wall), steer # towards the arena centre until it can move again, so the pattern makes # real progress instead of deadlocking against the wall. var headingCorrection = 0.0 if bot.stallTicks >= 2: let arenaW = getArenaWidth().float let arenaH = getArenaHeight().float let toCenter = directionTo(getX(), getY(), arenaW * 0.5, arenaH * 0.5) headingCorrection = normalizeRelativeAngle(toCenter - getDirection()) setTurnRate(headingCorrection.clamp(-MAX_TURN_RATE, MAX_TURN_RATE)) setTargetSpeed(MOVE_SPEED) bot.remaining -= moved if moved < 0.05: inc bot.stallTicks else: bot.stallTicks = 0 bot.lastX = getX() bot.lastY = getY() bot.lastDir = getDirection() bot.haveLast = true # Advance on completion OR after a bounded stall (e.g. pinned against a # wall), so the repeating pattern can never deadlock. if bot.remaining <= 0.5 or bot.stallTicks > STALL_LIMIT: advanceStep(bot) go() when isMainModule: var bot = PatternMover() start(bot, botJsonPath)