@vibemancer/core 0.1.3 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/rules.ts","../src/engine-version.ts","../src/engine/physics.ts","../src/engine/spells.ts","../src/engine/hooks-runtime.ts","../src/utils/angles.ts","../src/utils/random.ts","../src/engine/simulation.ts","../src/engine/manual-match.ts","../src/engine/missile-templates.ts","../src/utils/distance.ts","../src/utils/movement.ts","../src/utils/targeting.ts","../src/utils/spatial.ts","../src/utils/combat.ts","../src/bots/standalone/TargetDummy.ts","../src/bots/standalone/Rookie.ts","../src/bots/standalone/Critter.ts","../src/hooks/threat-analysis.ts","../src/bots/shared.ts","../src/bots/standalone/Hogger.ts","../src/bots/standalone/Doombringer.ts","../src/engine/params-runtime.ts","../src/bots/defensive/01_Turtle.ts","../src/bots/defensive/02_Sentinel.ts","../src/bots/defensive/03_Golem.ts","../src/bots/melee/01_Shadowblade.ts","../src/bots/melee/02_Nightblade.ts","../src/bots/melee/03_Voidblade.ts","../src/bots/homing/01_Bonemancer.ts","../src/bots/homing/02_Lich.ts","../src/bots/homing/03_Archlich.ts","../src/bots/caster/01_Flamecaller.ts","../src/bots/caster/02_Pyromancer.ts","../src/bots/caster/03_Infernalist.ts","../src/bots/sniper/01_Spellshot.ts","../src/bots/sniper/02_Spelltracer.ts","../src/bots/sniper/03_Spellseeker.ts","../src/bots/duelist/01_Battlemage.ts","../src/bots/duelist/02_Warmage.ts","../src/bots/duelist/03_Archmage.ts","../src/bots/berserker/01_Stormchaser.ts","../src/bots/berserker/02_Stormcaller.ts","../src/bots/berserker/03_Stormforger.ts","../src/bots/kiter/01_Spellspinner.ts","../src/bots/kiter/02_Spellweaver.ts","../src/bots/kiter/03_Spellbinder.ts","../src/bots/registry.ts","../src/bots/index.ts","../src/hooks/state-hooks.ts","../src/hooks/action-builders.ts","../src/hooks/bot-wrapper.ts","../src/engine/optimizer.ts","../src/engine/sandbox-browser.ts","../src/stats.ts","../src/testing.ts","../src/trace.ts"],"sourcesContent":["/**\n * VIBEMANCER - GAME RULES\n *\n * This is the single source of truth for all game constants.\n * All game logic imports from here. Read this to understand the game.\n */\n\nimport {MissileConfig} from './types.js';\n\n// === TIMING ===\nexport const TICKS_PER_SECOND = 100;\nexport const TICK_DURATION_MS = 10;\n\n// === WIZARD ===\nexport const WIZARD_HEALTH = 60;\nexport const WIZARD_RADIUS = 5; // units\nexport const MOVEMENT_SPEED = 1; // units per tick (100 units/sec)\nexport const CASTING_MOVEMENT_MULT = 0.5; // 50% speed while casting\n\n// === GCD ===\nexport const GCD_DURATION = 100; // ticks (1 second)\n\n// === SHIELD ===\nexport const SHIELD_CAST_TIME = 20; // ticks (0.2s)\nexport const SHIELD_MAX_BLOCK = 0.9; // 90%\nexport const SHIELD_DECAY_PER_SECOND = 0.2; // loses 20% per second\nexport const SHIELD_MIN_BLOCK = 0.3; // 30% minimum\n// Note: Shield block % is applied directly to damage (no additional scaling)\n\n// === BLINK ===\nexport const BLINK_CAST_TIME = 10; // ticks (0.1s)\nexport const BLINK_RANGE = 300; // units\nexport const BLINK_MAX_COOLDOWN = 2000; // ticks (20 seconds) — full-range blink\nexport const BLINK_MIN_COOLDOWN = 100; // ticks (1 second) — micro-blink floor\n/** @deprecated Use BLINK_MAX_COOLDOWN */\nexport const BLINK_COOLDOWN = BLINK_MAX_COOLDOWN;\n\n// === ARENA ===\nexport const ARENA_WIDTH = 800;\nexport const ARENA_HEIGHT = 800;\nexport const ARENA_SIZE = ARENA_WIDTH; // Arena is square; alias for bot convenience\nexport const SPAWN_DISTANCE = 600;\n\n// === MISSILE CONSTRAINTS ===\nexport const MISSILE_MIN_DAMAGE = 1;\nexport const MISSILE_MIN_SPEED = 1.5; // must be faster than player\nexport const MISSILE_MIN_DURATION = 10; // ticks (0.1 seconds)\nexport const MISSILE_MIN_CAST_TIME = 0.1; // seconds\n\n// === MISSILE HITBOX ===\n// Missile hitbox scales with damage (design doc line 1122)\n// Visual size reference (design doc lines 349-354):\n// - 1-5 damage: Tiny\n// - 6-15 damage: Small-Medium\n// - 16-30 damage: Large\n// - 31+ damage: Huge\nexport const MISSILE_BASE_RADIUS = 2; // base radius in units\nexport const MISSILE_DAMAGE_RADIUS_SCALE = 0.1; // additional radius per damage point\n\n/**\n * Calculate missile hitbox radius based on damage.\n */\nexport function calculateMissileRadius(damage: number): number\n{\n\treturn MISSILE_BASE_RADIUS + damage * MISSILE_DAMAGE_RADIUS_SCALE;\n}\n\n// === MISSILE CAST TIME FORMULA ===\n// cast_time = BASE + SCALE × damage^POWER + delivery costs\n//\n// Power-law scaling: DPS always increases with damage (higher damage = more\n// DPS but longer casts that are easier to shield/dodge). Fast casts (low\n// damage) can't be shielded — that's their advantage over raw DPS.\n//\n// Reference points (stab, no delivery cost):\n// d=10: ~1.15s cast → DPS 4.65, TTK 11.9s (fast, unshieldable)\n// d=20: ~1.77s cast → DPS 7.23, TTK 7.3s (medium, borderline shieldable)\n// d=30: ~2.28s cast → DPS 9.14, TTK 5.6s (slow, shieldable)\n// d=60: ~3.57s cast → DPS 13.1, TTK 3.6s (very slow, easily shielded)\nexport const MISSILE_BASE_CAST = 0.1;\nexport const MISSILE_DAMAGE_SCALE = 0.226;\nexport const MISSILE_DAMAGE_POWER = 2 / 3; // sublinear: DPS always increases with damage\nexport const MISSILE_HOMING_COEFF = 0.12;\nexport const MISSILE_TURN_DURATION_COEFF = 0.10;\nexport const MISSILE_SPEED_DURATION_BASELINE = 1.5;\nexport const MISSILE_SPEED_DURATION_COEFF = 0.025;\n\n/**\n * Maps turnRate to effective cost for the cast time formula.\n * - Positive: linear (unchanged behavior)\n * - Negative: diminishing returns via -|t|/(1+|t|), saturating at -1.\n * turnRate -0.5 → -0.33, -1 → -0.5, -5 → -0.83, -10 → -0.91\n * Big cast speed gains from 0 to -1, worthwhile to -5, negligible after.\n * No hard floor — the curve naturally caps the benefit.\n */\nexport function effectiveTurnRateCost(turnRate: number): number\n{\n\tif (turnRate >= 0) return turnRate;\n\tconst abs = Math.abs(turnRate);\n\treturn -(abs / (1 + abs));\n}\n\n/**\n * Validate and sanitize missile config.\n * Ensures all values meet minimum requirements (Lesson #9).\n */\nexport function validateMissileConfig(config: MissileConfig): MissileConfig\n{\n\treturn {\n\t\tdamage: Math.max(MISSILE_MIN_DAMAGE, Number.isFinite(config.damage) ? config.damage : MISSILE_MIN_DAMAGE),\n\t\tspeed: Math.max(MISSILE_MIN_SPEED, Number.isFinite(config.speed) ? config.speed : MISSILE_MIN_SPEED),\n\t\tturnRate: Number.isFinite(config.turnRate) ? config.turnRate : 0, // no floor — diminishing returns via effectiveTurnRateCost\n\t\tduration: Math.max(MISSILE_MIN_DURATION, Number.isFinite(config.duration) ? Math.floor(config.duration) : MISSILE_MIN_DURATION),\n\t};\n}\n\n/**\n * Calculate missile cast time from config.\n *\n * Base formula:\n * cast_time = 0.1\n * + 0.226 × damage^(2/3)\n * + 0.12 × effectiveTurnRateCost(turnRate)\n * + 0.10 × (effectiveTurnRateCost(turnRate) × durationSeconds)\n * + 0.025 × (speed × durationSeconds - 1.5)\n *\n * effectiveTurnRateCost: linear for positive, -ln(1+|t|) for negative (diminishing returns).\n *\n * If lastMissileConfig is a MissileConfig, applies warmup multiplier:\n * - Similar to previous: up to 20% faster\n * - Very different: up to 20% slower (switching penalty)\n */\nexport function calculateMissileCastTime(config: MissileConfig, lastMissileConfig: MissileConfig | undefined | null = null): number\n{\n\tconst {damage, speed, turnRate, duration} = config;\n\tconst durationSeconds = duration / TICKS_PER_SECOND;\n\tconst turnCost = effectiveTurnRateCost(turnRate);\n\n\tconst raw = MISSILE_BASE_CAST\n\t\t+ MISSILE_DAMAGE_SCALE * Math.pow(damage, MISSILE_DAMAGE_POWER)\n\t\t+ MISSILE_HOMING_COEFF * turnCost\n\t\t+ MISSILE_TURN_DURATION_COEFF * (turnCost * durationSeconds)\n\t\t+ MISSILE_SPEED_DURATION_COEFF * (speed * durationSeconds - MISSILE_SPEED_DURATION_BASELINE);\n\n\tconst baseCast = Math.max(MISSILE_MIN_CAST_TIME, raw);\n\n\t// Warmup is applied when lastMissileConfig is an actual MissileConfig.\n\t// Note: passing undefined triggers the default (null) due to JS semantics,\n\t// so in practice only an explicit MissileConfig activates warmup.\n\t// - null/undefined/omitted: return base cast time (no warmup)\n\t// - MissileConfig: apply warmup multiplier based on similarity to previous\n\tif (lastMissileConfig !== null)\n\t{\n\t\tconst multiplier = calculateWarmupMultiplier(lastMissileConfig, config);\n\t\treturn Math.max(MISSILE_MIN_CAST_TIME, baseCast * multiplier);\n\t}\n\n\treturn baseCast;\n}\n\n// === CAST WARMUP ===\n// Repeated similar missile casts get faster (\"muscle memory\"), while\n// switching to a very different config incurs a penalty (slower cast).\n// This rewards specialization: melee bots that always stab have faster\n// casts than ranged bots that switch to stabs opportunistically.\n//\n// The warmup multiplier ranges from (1 - MAX_BONUS) to (1 + MAX_PENALTY):\n// Similar to previous: cast × 0.80 (20% faster)\n// First cast (no previous): cast × 0.80 (full warmup, no conflict)\n// Very different from previous: cast × 1.20 (20% slower, switching penalty)\nexport const WARMUP_MAX_BONUS = 0.20; // 20% cast time reduction at full warmup\nexport const WARMUP_MAX_PENALTY = 0.20; // 20% cast time increase when switching\n// Tolerances for similarity calculation (differences within tolerance are \"similar\"):\n// Note: damage is intentionally excluded — it doesn't define playstyle.\n// A melee bot stabbing at d=10 and d=25 is the same style, just different power.\nexport const WARMUP_SPEED_TOLERANCE = 3; // speed can vary ±3\nexport const WARMUP_TURN_TOLERANCE = 1; // turnRate can vary ±1\nexport const WARMUP_DURATION_TOLERANCE = 50; // duration (ticks) can vary ±50\n\n/**\n * Calculate how similar two missile configs are (0 to 1).\n * Returns 1 for identical configs, 0 for very different ones.\n * Used by the warmup system to determine cast time multiplier.\n *\n * Only compares speed, turnRate, and duration — these define the missile\n * \"style\" (melee stab vs ranged homing vs fast snipe). Damage is excluded\n * because varying power doesn't change playstyle.\n */\nexport function calculateMissileSimilarity(prev: MissileConfig | undefined, current: MissileConfig): number\n{\n\tif (!prev) return 1; // No previous missile = no conflicting muscle memory = full warmup\n\n\tconst dist = Math.abs(prev.speed - current.speed) / WARMUP_SPEED_TOLERANCE\n\t\t+ Math.abs(prev.turnRate - current.turnRate) / WARMUP_TURN_TOLERANCE\n\t\t+ Math.abs(prev.duration - current.duration) / WARMUP_DURATION_TOLERANCE;\n\n\treturn Math.max(0, 1 - dist / 3);\n}\n\n/**\n * Calculate the cast time multiplier from the warmup system.\n * Returns < 1 for bonus (faster), > 1 for penalty (slower), 1 for neutral.\n *\n * Similar to previous cast → multiplier approaches (1 - MAX_BONUS) = 0.80\n * Very different from previous → multiplier approaches (1 + MAX_PENALTY) = 1.20\n * No previous cast → full bonus (1 - MAX_BONUS) = 0.80\n */\nexport function calculateWarmupMultiplier(prev: MissileConfig | undefined, current: MissileConfig): number\n{\n\tconst similarity = calculateMissileSimilarity(prev, current);\n\t// similarity=1 → bonus (faster): 1 - MAX_BONUS\n\t// similarity=0 → penalty (slower): 1 + MAX_PENALTY\n\t// Linear interpolation between penalty and bonus\n\treturn (1 + WARMUP_MAX_PENALTY) - similarity * (WARMUP_MAX_BONUS + WARMUP_MAX_PENALTY);\n}\n\n// === MATCH ===\nexport const MATCH_DURATION = 30000; // ticks (5 minutes)\n\n// ============================================================\n// NEW API CONSTANT ALIASES\n// These are the preferred names for the new hooks-based API\n// ============================================================\n\n// Health & Combat\nexport const MAX_HEALTH = WIZARD_HEALTH;\nexport const COLLISION_RADIUS = WIZARD_RADIUS;\n\n// Movement\nexport const MOVE_SPEED = MOVEMENT_SPEED;\n\n// Missiles\nexport const MISSILE_RADIUS_PER_DAMAGE = MISSILE_DAMAGE_RADIUS_SCALE;\n\n// Shield (aliases for consistency)\nexport const SHIELD_MAX_STRENGTH = SHIELD_MAX_BLOCK;\nexport const SHIELD_MIN_STRENGTH = SHIELD_MIN_BLOCK;\nexport const SHIELD_DECAY_RATE = SHIELD_DECAY_PER_SECOND;\n\n// Blink (aliases for consistency)\nexport const BLINK_MAX_RANGE = BLINK_RANGE;\n\n/**\n * Calculate blink cooldown based on distance traveled.\n * Short blinks get short cooldowns, full-range blinks get the maximum.\n */\nexport function calculateBlinkCooldown(distance: number): number\n{\n\tconst ratio = Math.min(1, distance / BLINK_MAX_RANGE);\n\treturn Math.max(BLINK_MIN_COOLDOWN, Math.round(ratio * BLINK_MAX_COOLDOWN));\n}\n\n// Arena\nexport const ARENA_WATER_BUFFER = 200; // units - missiles can fly this far past arena bounds\n","/**\n * Engine version — milliseconds since the Unix epoch.\n *\n * Bumped automatically by `scripts/update-engine-version.mjs` on every build.\n * The same value is propagated to `packages/functions/src/engine-version.generated.ts`\n * so cloud functions and the core engine always agree on a single version per deploy.\n *\n * Used to gate spectator replays: a recorded match can only be re-simulated when\n * the runtime engine version matches the version that produced the match.\n */\nexport const ENGINE_VERSION = 1777650763203;\n","import {Position, WizardState, ProjectileState} from '../types.js';\nimport {ARENA_WIDTH, ARENA_HEIGHT, MOVEMENT_SPEED, CASTING_MOVEMENT_MULT, WIZARD_RADIUS} from '../rules.js';\n\n/**\n * Move a wizard based on world-space input direction.\n *\n * Movement model (from design doc):\n * - x: +100 = right, -100 = left\n * - y: +100 = down, -100 = up\n * - Values are clamped to [-100, 100]\n * - Diagonal movement is normalized (magnitude capped at 100)\n * - No rotation tracking - just output (x, y) direction\n */\nexport function moveWizard(wizard: WizardState, move: {x: number; y: number}, deltaTicks: number): Position\n{\n\t// Clamp input values to [-100, 100]\n\tlet dx = Math.max(-100, Math.min(100, move.x || 0));\n\tlet dy = Math.max(-100, Math.min(100, move.y || 0));\n\n\t// Normalize diagonal movement (cap magnitude at 100)\n\tconst magnitude = Math.sqrt(dx * dx + dy * dy);\n\tif (magnitude > 100)\n\t{\n\t\tdx = dx * 100 / magnitude;\n\t\tdy = dy * 100 / magnitude;\n\t}\n\n\t// Convert from [-100, 100] to [-1, 1] direction\n\tdx = dx / 100;\n\tdy = dy / 100;\n\n\tlet speed = MOVEMENT_SPEED;\n\tif (wizard.state === 'casting')\n\t{\n\t\tspeed *= CASTING_MOVEMENT_MULT;\n\t}\n\telse if (wizard.state === 'channeling')\n\t{\n\t\t// Movement keys ignored during channeling (design doc line 197, 1120)\n\t\tspeed = 0;\n\t}\n\n\tconst newPos = {\n\t\tx: wizard.position.x + dx * speed * deltaTicks,\n\t\ty: wizard.position.y + dy * speed * deltaTicks,\n\t};\n\n\treturn clampToArena(newPos, WIZARD_RADIUS);\n}\n\n/**\n * Move a projectile in its current rotation direction.\n */\nexport function moveProjectile(projectile: ProjectileState, deltaTicks: number): Position\n{\n\tconst radians = projectile.rotation * (Math.PI / 180);\n\treturn {\n\t\tx: projectile.position.x + Math.cos(radians) * projectile.speed * deltaTicks,\n\t\ty: projectile.position.y + Math.sin(radians) * projectile.speed * deltaTicks,\n\t};\n}\n\n/**\n * Clamp a position to the arena boundaries.\n */\nexport function clampToArena(position: Position, radius: number): Position\n{\n\treturn {\n\t\tx: Math.max(radius, Math.min(ARENA_WIDTH - radius, position.x)),\n\t\ty: Math.max(radius, Math.min(ARENA_HEIGHT - radius, position.y)),\n\t};\n}\n\n/**\n * Resolve body collision between two wizards.\n * Pushes both apart equally so they don't overlap. Neither is blocked — they just can't stack.\n * Iterates until stable: wizard push → wall clamp → re-check overlap → repeat.\n */\nexport function resolveWizardCollision(wizard1: WizardState, wizard2: WizardState): void\n{\n\tconst minDist = WIZARD_RADIUS * 2;\n\tconst maxIterations = 5; // Prevents infinite loops in edge cases\n\n\tfor (let i = 0; i < maxIterations; i++)\n\t{\n\t\tconst dx = wizard2.position.x - wizard1.position.x;\n\t\tconst dy = wizard2.position.y - wizard1.position.y;\n\t\tconst dist = Math.sqrt(dx * dx + dy * dy);\n\n\t\tif (dist >= minDist) return; // No overlap — done\n\n\t\tif (dist === 0)\n\t\t{\n\t\t\t// Exactly overlapping — push apart along arbitrary axis\n\t\t\twizard1.position = {x: wizard1.position.x - WIZARD_RADIUS, y: wizard1.position.y};\n\t\t\twizard2.position = {x: wizard2.position.x + WIZARD_RADIUS, y: wizard2.position.y};\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconst overlap = minDist - dist;\n\t\t\tconst pushX = (dx / dist) * (overlap / 2);\n\t\t\tconst pushY = (dy / dist) * (overlap / 2);\n\n\t\t\twizard1.position = {x: wizard1.position.x - pushX, y: wizard1.position.y - pushY};\n\t\t\twizard2.position = {x: wizard2.position.x + pushX, y: wizard2.position.y + pushY};\n\t\t}\n\n\t\t// Clamp both to arena — this may re-introduce overlap if one is pushed into a wall\n\t\twizard1.position = clampToArena(wizard1.position, WIZARD_RADIUS);\n\t\twizard2.position = clampToArena(wizard2.position, WIZARD_RADIUS);\n\t}\n}\n\n/**\n * Perform swept circle collision detection between a moving point (projectile) and a stationary circle (wizard).\n * Returns true if a collision occurred during the movement from oldPos to newPos.\n */\nexport function sweptCircleCollision(oldPos: Position, newPos: Position, radius: number, targetPos: Position, targetRadius: number): boolean\n{\n\t// Distance from point to line segment\n\tconst totalRadius = radius + targetRadius;\n\n\t// Vector from oldPos to newPos\n\tconst dx = newPos.x - oldPos.x;\n\tconst dy = newPos.y - oldPos.y;\n\tconst d2 = dx * dx + dy * dy;\n\n\tif (d2 === 0)\n\t{\n\t\tconst dist2 = (oldPos.x - targetPos.x) ** 2 + (oldPos.y - targetPos.y) ** 2;\n\t\treturn dist2 <= totalRadius * totalRadius;\n\t}\n\n\t// Projection of targetPos onto the line segment (oldPos, newPos)\n\tlet t = ((targetPos.x - oldPos.x) * dx + (targetPos.y - oldPos.y) * dy) / d2;\n\tt = Math.max(0, Math.min(1, t));\n\n\tconst closestX = oldPos.x + t * dx;\n\tconst closestY = oldPos.y + t * dy;\n\n\tconst dist2 = (targetPos.x - closestX) ** 2 + (targetPos.y - closestY) ** 2;\n\treturn dist2 <= totalRadius * totalRadius;\n}\n","import {WizardState, MissileConfig, ProjectileState} from '../types.js';\nimport {\n\tSHIELD_MAX_BLOCK,\n\tSHIELD_DECAY_PER_SECOND,\n\tSHIELD_MIN_BLOCK,\n\tTICKS_PER_SECOND,\n\tGCD_DURATION,\n\tSHIELD_CAST_TIME,\n\tBLINK_CAST_TIME,\n\tcalculateMissileCastTime,\n\tcalculateWarmupMultiplier,\n} from '../rules.js';\n\n/**\n * Start casting a spell.\n * For missiles, applies warmup system (bonus for similar, penalty for switching).\n */\nexport function startCast(wizard: WizardState, spell: 'missile' | 'shield' | 'blink', config?: MissileConfig): void\n{\n\twizard.state = 'casting';\n\twizard.castingSpell = spell;\n\twizard.castProgress = 0;\n\n\tif (spell === 'missile' && config)\n\t{\n\t\t// calculateMissileCastTime includes warmup when lastMissileConfig is passed\n\t\tconst castTimeSec = calculateMissileCastTime(config, wizard.lastMissileConfig);\n\t\twizard.castDuration = Math.max(1, Math.round(castTimeSec * TICKS_PER_SECOND));\n\t\t// Store warmup multiplier for visualization\n\t\twizard.warmupMultiplier = calculateWarmupMultiplier(wizard.lastMissileConfig, config);\n\t}\n\telse if (spell === 'shield')\n\t{\n\t\twizard.castDuration = SHIELD_CAST_TIME;\n\t}\n\telse if (spell === 'blink')\n\t{\n\t\twizard.castDuration = BLINK_CAST_TIME;\n\t}\n}\n\n/**\n * Cancel the current cast.\n */\nexport function cancelCast(wizard: WizardState): void\n{\n\twizard.state = 'idle';\n\tdelete wizard.castingSpell;\n\tdelete wizard.castProgress;\n\tdelete wizard.castDuration;\n}\n\n/**\n * Complete the current cast and trigger the spell effect.\n */\nexport function completeCast(wizard: WizardState): void\n{\n\tconst spell = wizard.castingSpell;\n\n\tif (spell === 'shield')\n\t{\n\t\twizard.state = 'channeling';\n\t\twizard.channelingSpell = 'shield';\n\t\twizard.channelDuration = 0;\n\t}\n\telse if (spell === 'missile')\n\t{\n\t\twizard.state = 'gcd_locked';\n\t\twizard.gcdRemaining = GCD_DURATION;\n\t}\n\telse\n\t{\n\t\t// Blink: no GCD — can act immediately after teleporting\n\t\twizard.state = 'idle';\n\t}\n\n\tdelete wizard.castingSpell;\n\tdelete wizard.castProgress;\n\tdelete wizard.castDuration;\n}\n\n/**\n * Update the shield channel state.\n */\nexport function updateShield(wizard: WizardState, deltaTicks: number): void\n{\n\tif (wizard.state === 'channeling' && wizard.channelingSpell === 'shield')\n\t{\n\t\twizard.channelDuration = (wizard.channelDuration ?? 0) + deltaTicks;\n\t}\n}\n\n/**\n * Calculate the current block percentage of a shield based on channel duration.\n */\nexport function calculateShieldBlock(channelDurationTicks: number): number\n{\n\tconst channelSeconds = channelDurationTicks / TICKS_PER_SECOND;\n\tconst decay = channelSeconds * SHIELD_DECAY_PER_SECOND;\n\treturn Math.max(SHIELD_MIN_BLOCK, SHIELD_MAX_BLOCK - decay);\n}\n\n/**\n * Apply damage to a wizard, considering shield mitigation.\n *\n * Shield blocks a percentage of damage based on channel duration:\n * - Fresh shield (0s): 90% block → 10% damage through\n * - Decayed shield: block % decreases over time (20%/sec)\n * - Minimum: 30% block → 70% damage through\n *\n * (Design doc lines 196-212)\n */\nexport function applyDamage(wizard: WizardState, damage: number, _projectile?: ProjectileState): number\n{\n\tif (wizard.invincible) return 0;\n\n\tlet finalDamage = damage;\n\n\tif (wizard.state === 'channeling' && wizard.channelingSpell === 'shield')\n\t{\n\t\tconst blockPercent = calculateShieldBlock(wizard.channelDuration ?? 0);\n\t\t// Simple block: damage through = damage * (1 - blockPercent)\n\t\tfinalDamage = damage * (1 - blockPercent);\n\t}\n\n\twizard.health = Math.max(0, wizard.health - finalDamage);\n\treturn finalDamage;\n}\n","/**\n * VIBEMANCER - HOOKS RUNTIME\n *\n * This file implements a minimal React-like hooks runtime for AI programming.\n * It supports useState, useEffect, useMemo, useRef, and useParam with entity isolation.\n *\n * RULES OF HOOKS (same as React):\n * - Hooks must be called at the top level of the bot function\n * - Hooks must be called in the same order every tick\n * - Hooks must NOT be called conditionally\n *\n * Violating these rules throws an error (detected via hook index validation).\n */\n\nimport type {BotContext} from '../hooks/types.js';\n\nexport interface HookState\n{\n\tvalues: unknown[];\n\teffects: {\n\t\tcallback: () => void | (() => void);\n\t\tdeps?: unknown[];\n\t\tcleanup?: () => void;\n\t}[];\n\tmemos: {\n\t\tvalue: unknown;\n\t\tdeps?: unknown[];\n\t}[];\n\t/** Type of each hook call in order (for validation). */\n\thookTypes: string[];\n\t/** Total hooks called on first successful tick. */\n\thookCount: number;\n\t/** Whether the first tick has completed successfully (hook pattern established). */\n\tinitialized: boolean;\n}\n\n// Use globalThis for hooks state so that multiple copies of the hooks runtime\n// (e.g., engine template + bot bundle in sandboxed execution) share the same state.\n// Without this, bot bundles that inline their own copy of the hooks runtime would\n// have separate currentEntityId etc., causing \"useParam can only be used inside\n// a bot function\" errors.\nconst _g = globalThis as Record<string, unknown>;\n// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- we own __vibemancer_entityHooks\nconst entityHooks: Map<string, HookState> = (_g.__vibemancer_entityHooks as Map<string, HookState>)\n\t?? (_g.__vibemancer_entityHooks = new Map<string, HookState>());\n\n// Accessor functions for shared mutable state on globalThis\nfunction _getCurrentEntityId(): string | null\n{\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- we own __vibemancer_currentEntityId\n\treturn (_g.__vibemancer_currentEntityId as string | null) ?? null;\n}\nfunction _setCurrentEntityId(id: string | null): void\n{\n\t_g.__vibemancer_currentEntityId = id;\n}\nfunction _getCurrentHookIndex(): number\n{\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- we own __vibemancer_currentHookIndex\n\treturn (_g.__vibemancer_currentHookIndex as number) ?? 0;\n}\nfunction _setCurrentHookIndex(idx: number): void\n{\n\t_g.__vibemancer_currentHookIndex = idx;\n}\nfunction _getCurrentBotContext(): BotContext | null\n{\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- we own __vibemancer_currentBotContext\n\treturn (_g.__vibemancer_currentBotContext as BotContext | null) ?? null;\n}\nfunction _setCurrentBotContext(ctx: BotContext | null): void\n{\n\t_g.__vibemancer_currentBotContext = ctx;\n}\n\n/**\n * Get or create the hook state for an entity.\n */\nfunction getHookState(entityId: string): HookState\n{\n\tlet state = entityHooks.get(entityId);\n\tif (!state)\n\t{\n\t\tstate = {\n\t\t\tvalues: [],\n\t\t\teffects: [],\n\t\t\tmemos: [],\n\t\t\thookTypes: [],\n\t\t\thookCount: 0,\n\t\t\tinitialized: false,\n\t\t};\n\t\tentityHooks.set(entityId, state);\n\t}\n\treturn state;\n}\n\n/**\n * Validate a hook call and return its sequential index.\n * Ensures hooks are called in the same order every tick.\n *\n * On the first tick: records the hook type at this index.\n * On subsequent ticks: validates the hook type matches.\n *\n * @param type - The hook type name (e.g., 'useState', 'useEffect', 'useParam')\n * @returns The sequential hook index\n * @throws If called outside runWithHooks or if hook order changed\n */\nexport function validateHookCall(type: string): number\n{\n\tif (!_getCurrentEntityId())\n\t{\n\t\tthrow new Error(`${type} can only be used inside a bot function (runWithHooks)`);\n\t}\n\n\tconst state = getHookState(_getCurrentEntityId()!);\n\tconst index = _getCurrentHookIndex();\n\t_setCurrentHookIndex(index + 1);\n\n\tif (state.initialized)\n\t{\n\t\t// Validate: not more hooks than first tick\n\t\tif (index >= state.hookCount)\n\t\t{\n\t\t\tthrow new Error(\n\t\t\t\t`Hook \"${type}\" called at index ${index}, but only ${state.hookCount} hooks were registered on the first tick. ` +\n\t\t\t\t'This likely means a hook was called conditionally. Hooks must be called in the same order every tick.',\n\t\t\t);\n\t\t}\n\t\t// Validate: same hook type at this index\n\t\tif (state.hookTypes[index] !== type)\n\t\t{\n\t\t\tthrow new Error(\n\t\t\t\t`Hook order changed at index ${index}: expected \"${state.hookTypes[index]}\", got \"${type}\". ` +\n\t\t\t\t'Hooks must be called in the same order every tick.',\n\t\t\t);\n\t\t}\n\t}\n\telse\n\t{\n\t\t// First tick: record hook type\n\t\tstate.hookTypes[index] = type;\n\t}\n\n\treturn index;\n}\n\n/**\n * Finalize hook count after a successful tick.\n * On first tick: establishes the hook pattern.\n * On subsequent ticks: validates the count matches.\n */\nfunction finalizeHookCount(entityId: string): void\n{\n\tconst state = entityHooks.get(entityId);\n\tif (!state) return;\n\n\tif (state.initialized)\n\t{\n\t\tif (_getCurrentHookIndex() < state.hookCount)\n\t\t{\n\t\t\tthrow new Error(\n\t\t\t\t`Fewer hooks were called (${_getCurrentHookIndex()}) than on the first tick (${state.hookCount}). ` +\n\t\t\t\t'This likely means a hook was called conditionally. Hooks must be called in the same order every tick.',\n\t\t\t);\n\t\t}\n\t}\n\telse\n\t{\n\t\tstate.hookCount = _getCurrentHookIndex();\n\t\tstate.initialized = true;\n\t}\n}\n\n/**\n * Run a function with a specific entity's hook context.\n */\nexport function runWithHooks<T>(entityId: string, fn: () => T): T\n{\n\tconst previousEntityId = _getCurrentEntityId();\n\tconst previousHookIndex = _getCurrentHookIndex();\n\n\t_setCurrentEntityId(entityId);\n\t_setCurrentHookIndex(0);\n\n\t// If the first tick failed, reset hook type tracking so next attempt starts clean\n\tconst state = entityHooks.get(entityId);\n\tif (state && !state.initialized)\n\t{\n\t\tstate.hookTypes.length = 0;\n\t}\n\n\ttry\n\t{\n\t\tconst result = fn();\n\t\tfinalizeHookCount(entityId);\n\t\treturn result;\n\t}\n\tfinally\n\t{\n\t\t_setCurrentEntityId(previousEntityId);\n\t\t_setCurrentHookIndex(previousHookIndex);\n\t}\n}\n\n/**\n * Run a bot function with full context (game state + persistence hooks).\n * Use this when you need to set BOTH the entity ID and the game context.\n */\nexport function runBotWithContext<T>(entityId: string, context: BotContext, fn: () => T): T\n{\n\tconst previousContext = _getCurrentBotContext();\n\t_setCurrentBotContext(context);\n\n\ttry\n\t{\n\t\treturn runWithHooks(entityId, fn);\n\t}\n\tfinally\n\t{\n\t\t_setCurrentBotContext(previousContext);\n\t}\n}\n\n/**\n * Run a function with game state context only, preserving the current entity ID.\n * Use this inside wrapNewBot where the entity ID is already set by the outer runWithHooks.\n */\nexport function withBotContext<T>(context: BotContext, fn: () => T): T\n{\n\tconst previousContext = _getCurrentBotContext();\n\t_setCurrentBotContext(context);\n\n\ttry\n\t{\n\t\treturn fn();\n\t}\n\tfinally\n\t{\n\t\t_setCurrentBotContext(previousContext);\n\t}\n}\n\n/**\n * Get the current bot context. Throws if called outside bot execution.\n */\nexport function getBotContext(): BotContext\n{\n\tif (!_getCurrentBotContext())\n\t{\n\t\tthrow new Error('Game state hooks can only be used inside a bot function');\n\t}\n\treturn _getCurrentBotContext()!;\n}\n\n/**\n * Persist state between ticks.\n */\nexport function useState<T>(initialValue: T | (() => T)): [T, (newValue: T | ((prev: T) => T)) => void]\n{\n\tconst index = validateHookCall('useState');\n\tconst state = getHookState(_getCurrentEntityId()!);\n\n\tif (state.values.length <= index)\n\t{\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\n\t\tstate.values[index] = (typeof initialValue === 'function') ? (initialValue as () => T)() : initialValue;\n\t}\n\n\tconst setter = (newValue: T | ((prev: T) => T)): void =>\n\t{\n\t\tif (typeof newValue === 'function')\n\t\t{\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\n\t\t\tstate.values[index] = (newValue as (prev: T) => T)(state.values[index] as T);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstate.values[index] = newValue;\n\t\t}\n\t};\n\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\n\treturn [state.values[index] as T, setter];\n}\n\n/**\n * React to state changes.\n */\nexport function useEffect(callback: () => void | (() => void), deps?: unknown[]): void\n{\n\tconst index = validateHookCall('useEffect');\n\tconst state = getHookState(_getCurrentEntityId()!);\n\n\tconst oldEffect = state.effects[index];\n\tconst hasChanged = !oldEffect || !deps ||\n\t\tdeps.length !== oldEffect.deps?.length ||\n\t\t!deps.every((dep, i) => dep === oldEffect.deps?.[i]);\n\n\tif (hasChanged)\n\t{\n\t\t// Cleanup old effect if it exists\n\t\tif (oldEffect?.cleanup)\n\t\t{\n\t\t\toldEffect.cleanup();\n\t\t}\n\n\t\t// Run new effect\n\t\tconst cleanup = callback();\n\t\tstate.effects[index] = {\n\t\t\tcallback,\n\t\t\tdeps,\n\t\t\tcleanup: typeof cleanup === 'function' ? cleanup : undefined,\n\t\t};\n\t}\n}\n\n/**\n * Memoize expensive calculations.\n */\nexport function useMemo<T>(factory: () => T, deps?: unknown[]): T\n{\n\tconst index = validateHookCall('useMemo');\n\tconst state = getHookState(_getCurrentEntityId()!);\n\n\tconst oldMemo = state.memos[index];\n\tconst hasChanged = !oldMemo || !deps ||\n\t\tdeps.length !== oldMemo.deps?.length ||\n\t\t!deps.every((dep, i) => dep === oldMemo.deps?.[i]);\n\n\tif (hasChanged)\n\t{\n\t\tconst value = factory();\n\t\tstate.memos[index] = {\n\t\t\tvalue,\n\t\t\tdeps,\n\t\t};\n\t\treturn value;\n\t}\n\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\n\treturn oldMemo.value as T;\n}\n\n/**\n * Mutable reference that persists across ticks.\n * Unlike useState, mutations don't need a setter - just modify .current directly.\n */\nexport interface RefObject<T>\n{\n\tcurrent: T;\n}\n\nexport function useRef<T>(initialValue: T): RefObject<T>\n{\n\tconst index = validateHookCall('useRef');\n\tconst state = getHookState(_getCurrentEntityId()!);\n\n\tif (state.values.length <= index)\n\t{\n\t\tstate.values[index] = {current: initialValue};\n\t}\n\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\n\treturn state.values[index] as RefObject<T>;\n}\n\n/**\n * Clear hook state for an entity (e.g., when it dies).\n */\nexport function clearHooks(entityId: string): void\n{\n\tconst state = entityHooks.get(entityId);\n\tif (state)\n\t{\n\t\tstate.effects.forEach((effect) =>\n\t\t{\n\t\t\tif (effect.cleanup)\n\t\t\t{\n\t\t\t\teffect.cleanup();\n\t\t\t}\n\t\t});\n\t\tentityHooks.delete(entityId);\n\t}\n}\n\n/**\n * Reset all hook states (e.g., when a match restarts).\n */\nexport function resetAllHooks(): void\n{\n\tentityHooks.forEach((_, entityId) => clearHooks(entityId));\n\tentityHooks.clear();\n}\n\n","import {Position} from '../types.js';\r\n\r\n/**\r\n * Calculate the angle from one point to another in degrees.\r\n * 0° = right, 90° = down, 180° = left, 270° = up.\r\n */\r\nexport function angleTo(from: Position, to: Position): number\r\n{\r\n\tconst dx = to.x - from.x;\r\n\tconst dy = to.y - from.y;\r\n\tconst radians = Math.atan2(dy, dx);\r\n\treturn normalizeAngle(radians * (180 / Math.PI));\r\n}\r\n\r\n/**\r\n * Normalize an angle to the 0-360 range.\r\n */\r\nexport function normalizeAngle(angle: number): number\r\n{\r\n\tlet normalized = angle % 360;\r\n\tif (normalized < 0)\r\n\t{\r\n\t\tnormalized += 360;\r\n\t}\r\n\treturn normalized;\r\n}\r\n\r\n/**\r\n * Calculate the shortest difference between two angles (-180 to +180).\r\n */\r\nexport function angleDiff(angleA: number, angleB: number): number\r\n{\r\n\tlet diff = (angleB - angleA + 180) % 360 - 180;\r\n\tif (diff < -180)\r\n\t{\r\n\t\tdiff += 360;\r\n\t}\r\n\treturn diff;\r\n}\r\n\r\n/**\r\n * Check if an angle is within a certain range of a target angle.\r\n */\r\nexport function angleInRange(angle: number, target: number, range: number): boolean\r\n{\r\n\treturn Math.abs(angleDiff(angle, target)) <= range;\r\n}\r\n","/**\r\n * Seeded PRNG utilities for deterministic randomness.\r\n *\r\n * Each entity (wizard, missile) gets its own random sequence that:\r\n * - Is deterministic: same seed = same sequence\r\n * - Is isolated: one entity's calls don't affect another's\r\n * - Advances state: each call produces a different value\r\n */\r\n\r\nconst MAX_INT = 0x7fffffff;\r\n\r\n/**\r\n * Combine two seeds into one using a simple hash.\r\n */\r\nexport function hashCombine(a: number, b: number): number\r\n{\r\n\t// Simple hash combining two integers\r\n\tlet hash = a;\r\n\thash = ((hash << 5) + hash) ^ b;\r\n\treturn hash & MAX_INT;\r\n}\r\n\r\n/**\r\n * Advance the random state using a Linear Congruential Generator.\r\n * Parameters from glibc (widely tested).\r\n */\r\nexport function nextRandom(state: number): number\r\n{\r\n\treturn (state * 1103515245 + 12345) & MAX_INT;\r\n}\r\n\r\n/**\r\n * Create a seeded random number generator.\r\n * Returns a function that produces values in [0, 1) and advances internal state.\r\n */\r\nexport function createRandom(seed: number): () => number\r\n{\r\n\tlet state = seed & MAX_INT;\r\n\r\n\treturn (): number =>\r\n\t{\r\n\t\tstate = nextRandom(state);\r\n\t\treturn state / MAX_INT;\r\n\t};\r\n}\r\n\r\n/**\r\n * Create a deterministic seed for an entity based on match seed, entity ID, and tick.\r\n * This ensures reproducibility: same match + same entity + same tick = same random sequence.\r\n */\r\nexport function createEntitySeed(matchSeed: number, entityId: string, tick: number): number\r\n{\r\n\t// Hash the entity ID string into a number\r\n\tlet entityHash = 0;\r\n\tfor (let i = 0; i < entityId.length; i++)\r\n\t{\r\n\t\tentityHash = ((entityHash << 5) + entityHash) ^ entityId.charCodeAt(i);\r\n\t}\r\n\r\n\t// Combine match seed, entity hash, and tick\r\n\tlet combined = hashCombine(matchSeed, entityHash & MAX_INT);\r\n\tcombined = hashCombine(combined, tick);\r\n\r\n\treturn combined;\r\n}\r\n","import {\n\tGameState,\n\tWizardState,\n\tProjectileState,\n\tWizardFunction,\n\tWizardActions,\n\tMissileAIFunction,\n\tMissileActions,\n\tGameConfig,\n\tMissileConfig,\n} from '../types.js';\nimport {\n\tARENA_WIDTH,\n\tARENA_HEIGHT,\n\tSPAWN_DISTANCE,\n\tWIZARD_HEALTH,\n\tTICKS_PER_SECOND,\n\tcalculateBlinkCooldown,\n\tBLINK_RANGE,\n\tWIZARD_RADIUS,\n\tMATCH_DURATION,\n\tvalidateMissileConfig,\n\tcalculateMissileRadius,\n\tARENA_WATER_BUFFER,\n} from '../rules.js';\nimport {moveWizard, moveProjectile, sweptCircleCollision, clampToArena, resolveWizardCollision} from './physics.js';\nimport {applyDamage, updateShield, startCast, completeCast} from './spells.js';\nimport {runWithHooks, resetAllHooks, clearHooks} from './hooks-runtime.js';\nimport {angleTo, normalizeAngle, angleDiff} from '../utils/angles.js';\nimport {createRandom, createEntitySeed} from '../utils/random.js';\n\nexport interface InternalWizardState extends WizardState\n{\n\tmissileConfig?: MissileConfig;\n\tmissileAI?: MissileAIFunction;\n\tblinkTarget?: {x: number; y: number};\n\t// Combat tracking\n\tdamageDealt: number;\n\tdamageTaken: number;\n\tlastHitTick: number;\n}\n\n/**\n * Initialize a new match state.\n */\nexport function createInitialState(_seed: number, spawnDist: number = SPAWN_DISTANCE): GameState\n{\n\tconst center = {x: ARENA_WIDTH / 2, y: ARENA_HEIGHT / 2};\n\tconst offset = spawnDist / 2;\n\n\tconst wizard1: WizardState = {\n\t\tid: 'wizard-1',\n\t\tposition: {x: center.x - offset, y: center.y},\n\t\trotation: 0,\n\t\thealth: WIZARD_HEALTH,\n\t\tmaxHealth: WIZARD_HEALTH,\n\t\tstate: 'idle',\n\t\tblinkCooldown: 0,\n\t\tvelocity: {x: 0, y: 0},\n\t};\n\n\tconst wizard2: WizardState = {\n\t\tid: 'wizard-2',\n\t\tposition: {x: center.x + offset, y: center.y},\n\t\trotation: 180,\n\t\thealth: WIZARD_HEALTH,\n\t\tmaxHealth: WIZARD_HEALTH,\n\t\tstate: 'idle',\n\t\tblinkCooldown: 0,\n\t\tvelocity: {x: 0, y: 0},\n\t};\n\n\treturn {\n\t\ttick: 0,\n\t\tposition: wizard1.position,\n\t\trotation: wizard1.rotation,\n\t\thealth: wizard1.health,\n\t\tmaxHealth: wizard1.maxHealth,\n\t\tstate: wizard1.state,\n\t\tblinkCooldown: wizard1.blinkCooldown,\n\t\tvelocity: wizard1.velocity,\n\t\tenemies: [wizard2],\n\t\tprojectiles: [],\n\t\tmyProjectiles: [],\n\t\tdamageDealt: 0,\n\t\tdamageTaken: 0,\n\t\tlastHitTick: 0,\n\t};\n}\n\n/**\n * Process one game tick.\n */\nexport function tick(\n\tcurrentTick: number,\n\twizard1AI: WizardFunction,\n\twizard2AI: WizardFunction,\n\tconfig: GameConfig,\n\twizards: InternalWizardState[],\n\tprojectiles: ProjectileState[],\n\tmissileAIs: Map<string, MissileAIFunction>,\n\tmatchSeed: number,\n): {\n\tnextTick: number;\n\twizards: InternalWizardState[];\n\tprojectiles: ProjectileState[];\n\terrors: BotError[];\n}\n{\n\tconst nextTick = currentTick + 1;\n\tconst errors: BotError[] = [];\n\n\t// 1. Run wizard AIs with isolated random generators\n\t// Each wizard gets a fresh random generator seeded by (matchSeed, wizardId, tick)\n\tconst random1 = createRandom(createEntitySeed(matchSeed, wizards[0]!.id, nextTick));\n\tconst random2 = createRandom(createEntitySeed(matchSeed, wizards[1]!.id, nextTick));\n\n\t// Wrap AI calls in try-catch - if AI throws, wizard does nothing (Lesson #21)\n\tlet actions1: WizardActions = {move: {x: 0, y: 0}};\n\ttry\n\t{\n\t\tactions1 = runWithHooks(wizards[0]!.id, () => wizard1AI({\n\t\t\tstate: getPlayerStateView(0, wizards, projectiles, nextTick),\n\t\t\tconfig,\n\t\t\trandom: random1,\n\t\t})) ?? {move: {x: 0, y: 0}};\n\t}\n\tcatch(e)\n\t{\n\t\terrors.push({tick: nextTick, entityId: 'wizard-1', message: e instanceof Error ? e.message : String(e)});\n\t}\n\n\tlet actions2: WizardActions = {move: {x: 0, y: 0}};\n\ttry\n\t{\n\t\tactions2 = runWithHooks(wizards[1]!.id, () => wizard2AI({\n\t\t\tstate: getPlayerStateView(1, wizards, projectiles, nextTick),\n\t\t\tconfig,\n\t\t\trandom: random2,\n\t\t})) ?? {move: {x: 0, y: 0}};\n\t}\n\tcatch(e)\n\t{\n\t\terrors.push({tick: nextTick, entityId: 'wizard-2', message: e instanceof Error ? e.message : String(e)});\n\t}\n\n\tconst actions = [actions1, actions2];\n\n\t// 2. Update wizards\n\twizards.forEach((wizard, i) =>\n\t{\n\t\tconst action = actions[i]!;\n\n\t\t// Handle cooldowns\n\t\tif (wizard.blinkCooldown > 0)\n\t\t{\n\t\t\twizard.blinkCooldown--;\n\t\t}\n\t\tif (wizard.state === 'gcd_locked' && wizard.gcdRemaining !== undefined)\n\t\t{\n\t\t\twizard.gcdRemaining--;\n\t\t\tif (wizard.gcdRemaining <= 0)\n\t\t\t{\n\t\t\t\twizard.state = 'idle';\n\t\t\t\tdelete wizard.gcdRemaining;\n\t\t\t}\n\t\t}\n\n\t\t// Auto-aim: face the enemy by default, bot can override with aimDirection\n\t\tconst enemy = wizards[1 - i]!;\n\t\tif (action.aimDirection !== undefined && Number.isFinite(action.aimDirection))\n\t\t{\n\t\t\twizard.rotation = action.aimDirection;\n\t\t}\n\t\telse\n\t\t{\n\t\t\twizard.rotation = angleTo(wizard.position, enemy.position);\n\t\t}\n\n\t\t// Handle casting\n\t\tif (wizard.state === 'casting' && wizard.castProgress !== undefined && wizard.castDuration !== undefined)\n\t\t{\n\t\t\twizard.castProgress++;\n\t\t\tif (wizard.castProgress >= wizard.castDuration)\n\t\t\t{\n\t\t\t\tconst spell = wizard.castingSpell;\n\n\t\t\t\tif (spell === 'missile' && wizard.missileConfig && wizard.missileAI)\n\t\t\t\t{\n\t\t\t\t\t// Validate missile config to enforce minimums (Lesson #9)\n\t\t\t\t\tconst validConfig = validateMissileConfig(wizard.missileConfig);\n\t\t\t\t\tconst id = `missile-${wizard.id}-${nextTick}`;\n\t\t\t\t\tconst projectile: ProjectileState = {\n\t\t\t\t\t\tid,\n\t\t\t\t\t\ttype: 'missile',\n\t\t\t\t\t\townerId: wizard.id,\n\t\t\t\t\t\tposition: {...wizard.position},\n\t\t\t\t\t\trotation: wizard.rotation, // Use wizard's rotation at time of firing\n\t\t\t\t\t\tspeed: validConfig.speed,\n\t\t\t\t\t\tturnRate: validConfig.turnRate,\n\t\t\t\t\t\tdamage: validConfig.damage,\n\t\t\t\t\t\tremainingTicks: validConfig.duration,\n\t\t\t\t\t};\n\t\t\t\t\tprojectiles.push(projectile);\n\t\t\t\t\tmissileAIs.set(id, wizard.missileAI);\n\t\t\t\t\t// Track last missile for warmup system\n\t\t\t\t\twizard.lastMissileConfig = {...validConfig};\n\t\t\t\t\tdelete wizard.missileConfig;\n\t\t\t\t\tdelete wizard.missileAI;\n\t\t\t\t}\n\t\t\t\telse if (spell === 'blink' && wizard.blinkTarget)\n\t\t\t\t{\n\t\t\t\t\t// Clamp blink target to BLINK_RANGE from current position\n\t\t\t\t\tconst dx = wizard.blinkTarget.x - wizard.position.x;\n\t\t\t\t\tconst dy = wizard.blinkTarget.y - wizard.position.y;\n\t\t\t\t\tconst distance = Math.sqrt(dx * dx + dy * dy);\n\n\t\t\t\t\tlet targetPos = wizard.blinkTarget;\n\t\t\t\t\tif (distance > BLINK_RANGE)\n\t\t\t\t\t{\n\t\t\t\t\t\t// Clamp to max range in the same direction\n\t\t\t\t\t\tconst scale = BLINK_RANGE / distance;\n\t\t\t\t\t\ttargetPos = {\n\t\t\t\t\t\t\tx: wizard.position.x + dx * scale,\n\t\t\t\t\t\t\ty: wizard.position.y + dy * scale,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Clamp to arena bounds and calculate actual distance traveled\n\t\t\t\t\tconst oldPos = wizard.position;\n\t\t\t\t\twizard.position = clampToArena(targetPos, WIZARD_RADIUS);\n\t\t\t\t\tconst actualDx = wizard.position.x - oldPos.x;\n\t\t\t\t\tconst actualDy = wizard.position.y - oldPos.y;\n\t\t\t\t\tconst actualDistance = Math.sqrt(actualDx * actualDx + actualDy * actualDy);\n\t\t\t\t\twizard.blinkCooldown = calculateBlinkCooldown(actualDistance);\n\t\t\t\t\tdelete wizard.blinkTarget;\n\t\t\t\t}\n\n\t\t\t\tcompleteCast(wizard);\n\t\t\t}\n\t\t}\n\n\t\t// Handle movement (default to no movement if bot didn't provide move)\n\t\tconst oldPos = wizard.position;\n\t\tconst move = action.move ?? {x: 0, y: 0};\n\t\twizard.position = moveWizard(wizard, move, 1);\n\t\twizard.velocity = {\n\t\t\tx: wizard.position.x - oldPos.x,\n\t\t\ty: wizard.position.y - oldPos.y,\n\t\t};\n\n\t\t// Handle shield — cancel goes to idle (no GCD)\n\t\tif (wizard.state === 'channeling')\n\t\t{\n\t\t\tupdateShield(wizard, 1);\n\t\t\tif (action.cancel)\n\t\t\t{\n\t\t\t\twizard.state = 'idle';\n\t\t\t\tdelete wizard.channelingSpell;\n\t\t\t\tdelete wizard.channelDuration;\n\t\t\t}\n\t\t}\n\n\t\t// Handle start cast\n\t\tif (wizard.state === 'idle' && action.startCast)\n\t\t{\n\t\t\tif (action.startCast.spell === 'blink' && wizard.blinkCooldown > 0)\n\t\t\t{\n\t\t\t\t// Cannot blink yet\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstartCast(wizard, action.startCast.spell, action.startCast.spell === 'missile' ? action.startCast.config : undefined);\n\t\t\t\tif (action.startCast.spell === 'missile')\n\t\t\t\t{\n\t\t\t\t\twizard.missileConfig = action.startCast.config;\n\t\t\t\t\twizard.missileAI = action.startCast.missileAI;\n\t\t\t\t\tif (action.startCast.direction !== undefined)\n\t\t\t\t\t{\n\t\t\t\t\t\twizard.rotation = action.startCast.direction;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (action.startCast.spell === 'blink')\n\t\t\t\t{\n\t\t\t\t\twizard.blinkTarget = action.startCast.target;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle cancel\n\t\tif (wizard.state === 'casting' && action.cancel)\n\t\t{\n\t\t\twizard.state = 'idle';\n\t\t\tdelete wizard.castingSpell;\n\t\t\tdelete wizard.castProgress;\n\t\t\tdelete wizard.castDuration;\n\t\t\tdelete wizard.missileConfig;\n\t\t\tdelete wizard.missileAI;\n\t\t\tdelete wizard.blinkTarget;\n\t\t}\n\t});\n\n\t// 2b. Resolve wizard body collision (push apart if overlapping)\n\tresolveWizardCollision(wizards[0]!, wizards[1]!);\n\n\t// 3. Update projectiles\n\tconst remainingProjectiles: ProjectileState[] = [];\n\tprojectiles.forEach((projectile) =>\n\t{\n\t\tconst ai = missileAIs.get(projectile.id);\n\t\tif (ai)\n\t\t{\n\t\t\tconst ownerIndex = wizards.findIndex((w) => w.id === projectile.ownerId);\n\t\t\t// Each missile gets its own isolated random generator\n\t\t\tconst missileRandom = createRandom(createEntitySeed(matchSeed, projectile.id, nextTick));\n\n\t\t\t// Wrap missile AI in try-catch - if it throws, missile continues straight (Lesson #21)\n\t\t\tlet missileActions: MissileActions = {};\n\t\t\ttry\n\t\t\t{\n\t\t\t\tmissileActions = runWithHooks(projectile.id, () => ai({\n\t\t\t\t\tmissileState: projectileView(projectile),\n\t\t\t\t\tworldState: getPlayerStateView(ownerIndex, wizards, projectiles, nextTick),\n\t\t\t\t\trandom: missileRandom,\n\t\t\t\t})) ?? {};\n\t\t\t}\n\t\t\tcatch(e)\n\t\t\t{\n\t\t\t\terrors.push({tick: nextTick, entityId: projectile.id, message: e instanceof Error ? e.message : String(e)});\n\t\t\t}\n\n\t\t\tif (missileActions.turnToward)\n\t\t\t{\n\t\t\t\tconst targetAngle = angleTo(projectile.position, missileActions.turnToward);\n\t\t\t\tconst diff = angleDiff(projectile.rotation, targetAngle);\n\t\t\t\t// Negative turnRate = anti-homing (turns away from target)\n\t\t\t\tconst absTurnRate = Math.abs(projectile.turnRate);\n\t\t\t\tconst effectiveDiff = projectile.turnRate >= 0 ? diff : -diff;\n\t\t\t\tconst turn = Math.max(-absTurnRate, Math.min(absTurnRate, effectiveDiff));\n\t\t\t\tprojectile.rotation = normalizeAngle(projectile.rotation + turn);\n\t\t\t}\n\t\t}\n\n\t\tconst oldPos = projectile.position;\n\t\tprojectile.position = moveProjectile(projectile, 1);\n\t\tprojectile.remainingTicks--;\n\n\t\t// Check collisions\n\t\tlet hit = false;\n\t\tconst missileRadius = calculateMissileRadius(projectile.damage);\n\t\twizards.forEach((wizard) =>\n\t\t{\n\t\t\tif (wizard.id !== projectile.ownerId && !hit)\n\t\t\t{\n\t\t\t\tif (sweptCircleCollision(oldPos, projectile.position, missileRadius, wizard.position, WIZARD_RADIUS))\n\t\t\t\t{\n\t\t\t\t\tconst actualDamage = applyDamage(wizard, projectile.damage);\n\t\t\t\t\thit = true;\n\n\t\t\t\t\t// Track damage for combat tracking hooks\n\t\t\t\t\twizard.damageTaken += actualDamage;\n\t\t\t\t\twizard.lastHitTick = nextTick;\n\n\t\t\t\t\t// Find the owner and track their damage dealt\n\t\t\t\t\tconst owner = wizards.find((w) => w.id === projectile.ownerId);\n\t\t\t\t\tif (owner)\n\t\t\t\t\t{\n\t\t\t\t\t\towner.damageDealt += actualDamage;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t// Check if projectile is beyond the water buffer\n\t\tconst isOutOfBounds =\n\t\t\tprojectile.position.x < -ARENA_WATER_BUFFER ||\n\t\t\tprojectile.position.x > ARENA_WIDTH + ARENA_WATER_BUFFER ||\n\t\t\tprojectile.position.y < -ARENA_WATER_BUFFER ||\n\t\t\tprojectile.position.y > ARENA_HEIGHT + ARENA_WATER_BUFFER;\n\n\t\tif (!hit && projectile.remainingTicks > 0 && !isOutOfBounds)\n\t\t{\n\t\t\tremainingProjectiles.push(projectile);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmissileAIs.delete(projectile.id);\n\t\t\tclearHooks(projectile.id);\n\t\t}\n\t});\n\n\treturn {\n\t\tnextTick,\n\t\twizards,\n\t\tprojectiles: remainingProjectiles,\n\t\terrors,\n\t};\n}\n\n/**\n * Deep clone a wizard state to prevent mutation.\n */\nfunction cloneWizard(wizard: WizardState): WizardState\n{\n\treturn {\n\t\tid: wizard.id,\n\t\tposition: {...wizard.position},\n\t\trotation: wizard.rotation,\n\t\thealth: wizard.health,\n\t\tmaxHealth: wizard.maxHealth,\n\t\tstate: wizard.state,\n\t\tcastingSpell: wizard.castingSpell,\n\t\tcastProgress: wizard.castProgress,\n\t\tcastDuration: wizard.castDuration,\n\t\tchannelingSpell: wizard.channelingSpell,\n\t\tchannelDuration: wizard.channelDuration,\n\t\tgcdRemaining: wizard.gcdRemaining,\n\t\tblinkCooldown: wizard.blinkCooldown,\n\t\tvelocity: {...wizard.velocity},\n\t};\n}\n\n/**\n * Deep clone a projectile state to prevent mutation.\n */\nfunction cloneProjectile(projectile: ProjectileState): ProjectileState\n{\n\treturn {\n\t\tid: projectile.id,\n\t\ttype: projectile.type,\n\t\townerId: projectile.ownerId,\n\t\tposition: {...projectile.position},\n\t\trotation: projectile.rotation,\n\t\tspeed: projectile.speed,\n\t\tturnRate: projectile.turnRate,\n\t\tdamage: projectile.damage,\n\t\tremainingTicks: projectile.remainingTicks,\n\t};\n}\n\n/**\n * Get the game state from a specific player's perspective.\n * Returns a deep clone to prevent mutation of history entries.\n * Used for history recording where independent snapshots are needed.\n */\nexport function getPlayerState(playerIndex: number, wizards: InternalWizardState[], projectiles: ProjectileState[], tick: number): GameState\n{\n\tconst me = wizards[playerIndex]!;\n\tconst enemies = wizards.filter((_, i) => i !== playerIndex).map(cloneWizard);\n\tconst clonedProjectiles = projectiles.map(cloneProjectile);\n\n\treturn {\n\t\ttick,\n\t\tposition: {...me.position},\n\t\trotation: me.rotation,\n\t\thealth: me.health,\n\t\tmaxHealth: me.maxHealth,\n\t\tstate: me.state,\n\t\tcastingSpell: me.castingSpell,\n\t\tcastProgress: me.castProgress,\n\t\tcastDuration: me.castDuration,\n\t\tchannelingSpell: me.channelingSpell,\n\t\tchannelDuration: me.channelDuration,\n\t\tgcdRemaining: me.gcdRemaining,\n\t\tblinkCooldown: me.blinkCooldown,\n\t\tvelocity: {...me.velocity},\n\t\tlastMissileConfig: me.lastMissileConfig ? {...me.lastMissileConfig} : undefined,\n\t\twarmupMultiplier: me.warmupMultiplier,\n\t\tenemies,\n\t\tprojectiles: clonedProjectiles,\n\t\tmyProjectiles: clonedProjectiles.filter((p) => p.ownerId === me.id),\n\t\t// Combat tracking\n\t\tdamageDealt: me.damageDealt,\n\t\tdamageTaken: me.damageTaken,\n\t\tlastHitTick: me.lastHitTick,\n\t};\n}\n\n/**\n * Create a lightweight read-only view of a projectile.\n * Clones only the mutable sub-objects (position) to prevent\n * bot code from corrupting internal simulation state.\n * Scalar fields are safe since they're copied by value.\n */\nfunction projectileView(p: ProjectileState): ProjectileState\n{\n\treturn {\n\t\tid: p.id,\n\t\ttype: p.type,\n\t\townerId: p.ownerId,\n\t\tposition: {x: p.position.x, y: p.position.y},\n\t\trotation: p.rotation,\n\t\tspeed: p.speed,\n\t\tturnRate: p.turnRate,\n\t\tdamage: p.damage,\n\t\tremainingTicks: p.remainingTicks,\n\t};\n}\n\n/**\n * Extract public WizardState fields from an InternalWizardState.\n * Clones position and velocity ({x,y} spreads) to prevent bot code\n * from mutating internal simulation state. Scalar fields are safe.\n */\nfunction extractWizardView(wizard: InternalWizardState): WizardState\n{\n\treturn {\n\t\tid: wizard.id,\n\t\tposition: {x: wizard.position.x, y: wizard.position.y},\n\t\trotation: wizard.rotation,\n\t\thealth: wizard.health,\n\t\tmaxHealth: wizard.maxHealth,\n\t\tstate: wizard.state,\n\t\tcastingSpell: wizard.castingSpell,\n\t\tcastProgress: wizard.castProgress,\n\t\tcastDuration: wizard.castDuration,\n\t\tchannelingSpell: wizard.channelingSpell,\n\t\tchannelDuration: wizard.channelDuration,\n\t\tgcdRemaining: wizard.gcdRemaining,\n\t\tblinkCooldown: wizard.blinkCooldown,\n\t\tvelocity: {x: wizard.velocity.x, y: wizard.velocity.y},\n\t};\n}\n\n/**\n * Get a lightweight read-only view of game state for AI calls.\n * Clones only the small mutable sub-objects (position, velocity as {x,y})\n * to prevent bot code from corrupting internal simulation state.\n * Scalar fields are copied by value and safe from mutation.\n *\n * This is much faster than getPlayerState() (which deep-clones everything)\n * because it only spreads the tiny {x,y} objects, not full deep clones.\n */\nfunction getPlayerStateView(playerIndex: number, wizards: InternalWizardState[], projectiles: ProjectileState[], tick: number): GameState\n{\n\tconst me = wizards[playerIndex]!;\n\tconst enemies = wizards.filter((_, i) => i !== playerIndex).map(extractWizardView);\n\tconst projViews = projectiles.map(projectileView);\n\n\treturn {\n\t\ttick,\n\t\tposition: {x: me.position.x, y: me.position.y},\n\t\trotation: me.rotation,\n\t\thealth: me.health,\n\t\tmaxHealth: me.maxHealth,\n\t\tstate: me.state,\n\t\tcastingSpell: me.castingSpell,\n\t\tcastProgress: me.castProgress,\n\t\tcastDuration: me.castDuration,\n\t\tchannelingSpell: me.channelingSpell,\n\t\tchannelDuration: me.channelDuration,\n\t\tgcdRemaining: me.gcdRemaining,\n\t\tblinkCooldown: me.blinkCooldown,\n\t\tvelocity: {x: me.velocity.x, y: me.velocity.y},\n\t\tlastMissileConfig: me.lastMissileConfig ? {...me.lastMissileConfig} : undefined,\n\t\twarmupMultiplier: me.warmupMultiplier,\n\t\tenemies,\n\t\tprojectiles: projViews,\n\t\tmyProjectiles: projViews.filter((p) => p.ownerId === me.id),\n\t\t// Combat tracking\n\t\tdamageDealt: me.damageDealt,\n\t\tdamageTaken: me.damageTaken,\n\t\tlastHitTick: me.lastHitTick,\n\t};\n}\n\n/** Winner of a single match: a wizard ID, 'draw' (simultaneous kill), or null (timeout). */\nexport type MatchWinner = 'wizard-1' | 'wizard-2' | 'draw' | null;\n\n/** Winner of a fight (aggregate): a wizard ID or 'draw'. Never null. */\nexport type FightWinner = 'wizard-1' | 'wizard-2' | 'draw';\n\n/**\n * Result of a simulation.\n */\n/** A runtime error captured from a bot or missile AI function. */\nexport interface BotError\n{\n\ttick: number;\n\tentityId: string;\n\tmessage: string;\n}\n\nexport interface SimulateResult\n{\n\t/** 'wizard-1'/'wizard-2' = killed opponent, 'draw' = simultaneous kill, null = timeout */\n\twinner: MatchWinner;\n\tticks: number;\n\tfinalState: GameState;\n\thistory: GameState[];\n\t/** Runtime errors thrown by bot or missile AI functions (empty if no errors). */\n\terrors: BotError[];\n}\n\n/**\n * Result of a fight (best-of-5 at different spawn distances).\n */\nexport interface FightResult\n{\n\twizard1Wins: number;\n\twizard2Wins: number;\n\tdraws: number;\n\t/** Winner of the fight: 'wizard-1', 'wizard-2', or 'draw' (never null) */\n\twinner: FightWinner;\n\t/**\n\t * Individual match results (one per spawn distance, non-swapped only).\n\t * Used for visual playback in the web viewer. Scoring includes both sides.\n\t */\n\tmatches: SimulateResult[];\n}\n\n/** Spawn distances for the 5 matches in a fight (creates butterfly effect variation) */\nexport const FIGHT_SPAWN_DISTANCES = [500, 550, 600, 650, 700];\n\n/**\n * Run a fight: 10 matches (5 spawn distances × 2 sides) between two bots.\n * Each spawn distance is played twice — once with each bot on each side —\n * to ensure results are independent of starting position.\n *\n * The `matches` array contains only the 5 non-swapped matches (for visual playback).\n * The scoring aggregates (wizard1Wins, wizard2Wins, draws) include all 10 matches.\n *\n * This is the standard way to determine who wins a matchup.\n * Used by both the tournament system and the visual UI.\n */\nexport function fight(\n\twizard1AI: WizardFunction,\n\twizard2AI: WizardFunction,\n\toptions: {\n\t\tseed?: number;\n\t\tmaxTicks?: number;\n\t} = {},\n): FightResult\n{\n\tconst matches: SimulateResult[] = [];\n\tlet wizard1Wins = 0;\n\tlet wizard2Wins = 0;\n\tlet draws = 0;\n\n\tfor (const spawnDistance of FIGHT_SPAWN_DISTANCES)\n\t{\n\t\t// Normal side: wizard1 on left, wizard2 on right\n\t\tconst result = simulate(wizard1AI, wizard2AI, {\n\t\t\tseed: options.seed,\n\t\t\tmaxTicks: options.maxTicks,\n\t\t\tspawnDistance,\n\t\t});\n\n\t\tmatches.push(result);\n\n\t\tif (result.winner === 'wizard-1')\n\t\t{\n\t\t\twizard1Wins++;\n\t\t}\n\t\telse if (result.winner === 'wizard-2')\n\t\t{\n\t\t\twizard2Wins++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdraws++;\n\t\t}\n\n\t\t// Swapped side: wizard2 on left, wizard1 on right\n\t\t// skipHistory: swapped matches are only for scoring, not visual playback\n\t\tconst swapped = simulate(wizard2AI, wizard1AI, {\n\t\t\tseed: options.seed,\n\t\t\tmaxTicks: options.maxTicks,\n\t\t\tspawnDistance,\n\t\t\tskipHistory: true,\n\t\t});\n\n\t\t// Don't push swapped match to matches array (it's only for scoring)\n\t\tif (swapped.winner === 'wizard-1')\n\t\t{\n\t\t\t// wizard-1 in swapped match = wizard2 from original perspective\n\t\t\twizard2Wins++;\n\t\t}\n\t\telse if (swapped.winner === 'wizard-2')\n\t\t{\n\t\t\twizard1Wins++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdraws++;\n\t\t}\n\t}\n\n\tconst winner: FightWinner = wizard1Wins > wizard2Wins ? 'wizard-1'\n\t\t: wizard2Wins > wizard1Wins ? 'wizard-2'\n\t\t\t: 'draw';\n\n\treturn {wizard1Wins, wizard2Wins, draws, winner, matches};\n}\n\n/**\n * Run a full match simulation.\n *\n * @param options.skipHistory - When true, skips recording per-tick history snapshots.\n * This dramatically improves performance (no deep cloning per tick) and is used\n * by the optimizer and fight() scoring. The returned history array will be empty\n * and finalState will still be populated.\n */\nexport function simulate(\n\twizard1AI: WizardFunction,\n\twizard2AI: WizardFunction,\n\toptions: {\n\t\tmaxTicks?: number;\n\t\tseed?: number;\n\t\tspawnDistance?: number;\n\t\tskipHistory?: boolean;\n\t} = {},\n): SimulateResult\n{\n\t// Validate AI functions (Lesson #1)\n\tif (typeof wizard1AI !== 'function')\n\t{\n\t\tthrow new Error('wizard1AI must be a function');\n\t}\n\tif (typeof wizard2AI !== 'function')\n\t{\n\t\tthrow new Error('wizard2AI must be a function');\n\t}\n\n\t// Sanitize options (Lesson #3)\n\tconst {maxTicks: rawMaxTicks = MATCH_DURATION, seed: rawSeed = Date.now(), spawnDistance: rawSpawnDistance, skipHistory = false} = options;\n\n\t// Sanitize seed - ensure valid 32-bit integer\n\tconst seed = Number.isFinite(rawSeed) ? Math.floor(rawSeed) & 0x7FFFFFFF : Date.now() & 0x7FFFFFFF;\n\n\t// Sanitize maxTicks - ensure positive integer with reasonable limit\n\tconst maxTicks = Number.isFinite(rawMaxTicks) && rawMaxTicks > 0\n\t\t? Math.min(Math.floor(rawMaxTicks), 100000) // Cap at 100k ticks (~16 min)\n\t\t: MATCH_DURATION;\n\n\t// Sanitize spawnDistance - must be positive and fit in arena\n\tconst spawnDistance = Number.isFinite(rawSpawnDistance) && rawSpawnDistance! > 0\n\t\t? Math.min(rawSpawnDistance!, Math.min(ARENA_WIDTH, ARENA_HEIGHT) - 100) // Leave room for wizards\n\t\t: SPAWN_DISTANCE;\n\n\tconst config: GameConfig = Object.freeze({\n\t\tarenaSize: Object.freeze({width: ARENA_WIDTH, height: ARENA_HEIGHT}),\n\t\ttickRate: TICKS_PER_SECOND,\n\t\tmaxTicks,\n\t});\n\n\tresetAllHooks();\n\n\tconst initialState = createInitialState(seed, spawnDistance);\n\tconst history: GameState[] = skipHistory ? [] : [initialState];\n\tlet currentTick = 0;\n\tlet wizards: InternalWizardState[] = [\n\t\t{\n\t\t\tid: 'wizard-1',\n\t\t\tposition: {x: ARENA_WIDTH / 2 - spawnDistance / 2, y: ARENA_HEIGHT / 2},\n\t\t\trotation: 0,\n\t\t\thealth: WIZARD_HEALTH,\n\t\t\tmaxHealth: WIZARD_HEALTH,\n\t\t\tstate: 'idle',\n\t\t\tblinkCooldown: 0,\n\t\t\tvelocity: {x: 0, y: 0},\n\t\t\tdamageDealt: 0,\n\t\t\tdamageTaken: 0,\n\t\t\tlastHitTick: 0,\n\t\t},\n\t\t{\n\t\t\tid: 'wizard-2',\n\t\t\tposition: {x: ARENA_WIDTH / 2 + spawnDistance / 2, y: ARENA_HEIGHT / 2},\n\t\t\trotation: 180,\n\t\t\thealth: WIZARD_HEALTH,\n\t\t\tmaxHealth: WIZARD_HEALTH,\n\t\t\tstate: 'idle',\n\t\t\tblinkCooldown: 0,\n\t\t\tvelocity: {x: 0, y: 0},\n\t\t\tdamageDealt: 0,\n\t\t\tdamageTaken: 0,\n\t\t\tlastHitTick: 0,\n\t\t},\n\t];\n\tlet projectiles: ProjectileState[] = [];\n\tconst missileAIs = new Map<string, MissileAIFunction>();\n\tconst allErrors: BotError[] = [];\n\n\twhile (currentTick < maxTicks)\n\t{\n\t\tconst result = tick(currentTick, wizard1AI, wizard2AI, config, wizards, projectiles, missileAIs, seed);\n\t\tcurrentTick = result.nextTick;\n\t\twizards = result.wizards;\n\t\tprojectiles = result.projectiles;\n\t\tif (result.errors.length > 0) allErrors.push(...result.errors);\n\n\t\tif (!skipHistory)\n\t\t{\n\t\t\t// Deep-clone snapshot for history (needed for replay)\n\t\t\thistory.push(getPlayerState(0, wizards, projectiles, currentTick));\n\t\t}\n\n\t\tif (wizards[0]!.health <= 0 || wizards[1]!.health <= 0)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tlet winner: MatchWinner = null;\n\tif (wizards[0]!.health <= 0 && wizards[1]!.health <= 0)\n\t{\n\t\twinner = 'draw';\n\t}\n\telse if (wizards[1]!.health <= 0)\n\t{\n\t\twinner = 'wizard-1';\n\t}\n\telse if (wizards[0]!.health <= 0)\n\t{\n\t\twinner = 'wizard-2';\n\t}\n\n\t// Always create final state snapshot (needed for result)\n\tconst finalState = getPlayerState(0, wizards, projectiles, currentTick);\n\n\treturn {\n\t\twinner,\n\t\tticks: currentTick,\n\t\tfinalState,\n\t\thistory: skipHistory ? [] : history,\n\t\terrors: allErrors,\n\t};\n}\n","/**\n * VIBEMANCER - MANUAL MATCH\n *\n * Tick-by-tick match runner used by manual play mode. Wraps the same\n * `tick()` primitive that `simulate()` uses, so stepping forward\n * produces the same trajectory as a batched `simulate()` with the\n * same seed.\n *\n * Extra capabilities over `simulate()`:\n * - step(N) advances N ticks at a time (default 1) — caller controls pacing\n * - replaceMissileAI(id, ai) hot-swaps a missile's AI mid-flight (used by\n * the missile-hijack feature in manual play)\n * - setInvincible(wizardIndex, on) toggles damage immunity per wizard\n */\n\nimport {\n\tARENA_HEIGHT,\n\tARENA_WIDTH,\n\tMATCH_DURATION,\n\tSPAWN_DISTANCE,\n\tTICKS_PER_SECOND,\n\tWIZARD_HEALTH,\n} from '../rules.js';\nimport type {\n\tGameConfig,\n\tGameState,\n\tMissileAIFunction,\n\tProjectileState,\n\tWizardFunction,\n} from '../types.js';\nimport {clearHooks} from './hooks-runtime.js';\nimport {\n\ttype BotError,\n\ttype InternalWizardState,\n\ttype MatchWinner,\n\ttype SimulateResult,\n\tgetPlayerState,\n\ttick,\n} from './simulation.js';\n\nexport interface ManualMatchOptions\n{\n\tseed?: number;\n\tspawnDistance?: number;\n\tmaxTicks?: number;\n}\n\nexport interface StepResult\n{\n\tgameState: GameState;\n\terrors: BotError[];\n\tdone: boolean;\n}\n\n/**\n * Build the initial pair of internal wizards for a match. Mirrors the\n * inline setup in simulate() so both code paths produce identical state.\n */\nfunction createInitialWizards(spawnDistance: number): InternalWizardState[]\n{\n\treturn [\n\t\t{\n\t\t\tid: 'wizard-1',\n\t\t\tposition: {x: ARENA_WIDTH / 2 - spawnDistance / 2, y: ARENA_HEIGHT / 2},\n\t\t\trotation: 0,\n\t\t\thealth: WIZARD_HEALTH,\n\t\t\tmaxHealth: WIZARD_HEALTH,\n\t\t\tstate: 'idle',\n\t\t\tblinkCooldown: 0,\n\t\t\tvelocity: {x: 0, y: 0},\n\t\t\tdamageDealt: 0,\n\t\t\tdamageTaken: 0,\n\t\t\tlastHitTick: 0,\n\t\t},\n\t\t{\n\t\t\tid: 'wizard-2',\n\t\t\tposition: {x: ARENA_WIDTH / 2 + spawnDistance / 2, y: ARENA_HEIGHT / 2},\n\t\t\trotation: 180,\n\t\t\thealth: WIZARD_HEALTH,\n\t\t\tmaxHealth: WIZARD_HEALTH,\n\t\t\tstate: 'idle',\n\t\t\tblinkCooldown: 0,\n\t\t\tvelocity: {x: 0, y: 0},\n\t\t\tdamageDealt: 0,\n\t\t\tdamageTaken: 0,\n\t\t\tlastHitTick: 0,\n\t\t},\n\t];\n}\n\nexport class ManualMatch\n{\n\tprivate readonly wizard1AI: WizardFunction;\n\tprivate readonly wizard2AI: WizardFunction;\n\tprivate readonly seed: number;\n\tprivate readonly maxTicks: number;\n\tprivate readonly config: GameConfig;\n\n\tprivate wizards: InternalWizardState[];\n\tprivate projectiles: ProjectileState[] = [];\n\tprivate missileAIs: Map<string, MissileAIFunction> = new Map();\n\t/** First-replacement originals for hijacked missiles. Used by restoreMissileAI. */\n\tprivate originalMissileAIs: Map<string, MissileAIFunction> = new Map();\n\tprivate currentTick = 0;\n\tprivate done = false;\n\tprivate allErrors: BotError[] = [];\n\tprivate history: GameState[];\n\n\tconstructor(wizard1AI: WizardFunction, wizard2AI: WizardFunction, options: ManualMatchOptions = {})\n\t{\n\t\tif (typeof wizard1AI !== 'function')\n\t\t{\n\t\t\tthrow new Error('wizard1AI must be a function');\n\t\t}\n\t\tif (typeof wizard2AI !== 'function')\n\t\t{\n\t\t\tthrow new Error('wizard2AI must be a function');\n\t\t}\n\n\t\tthis.wizard1AI = wizard1AI;\n\t\tthis.wizard2AI = wizard2AI;\n\n\t\tconst {\n\t\t\tseed: rawSeed = Date.now(),\n\t\t\tspawnDistance: rawSpawn,\n\t\t\tmaxTicks: rawMaxTicks = MATCH_DURATION,\n\t\t} = options;\n\n\t\tthis.seed = Number.isFinite(rawSeed) ? Math.floor(rawSeed) & 0x7FFFFFFF : Date.now() & 0x7FFFFFFF;\n\t\tthis.maxTicks = Number.isFinite(rawMaxTicks) && rawMaxTicks > 0\n\t\t\t? Math.min(Math.floor(rawMaxTicks), 100000)\n\t\t\t: MATCH_DURATION;\n\t\tconst spawnDistance = Number.isFinite(rawSpawn) && rawSpawn! > 0\n\t\t\t? Math.min(rawSpawn!, Math.min(ARENA_WIDTH, ARENA_HEIGHT) - 100)\n\t\t\t: SPAWN_DISTANCE;\n\n\t\tthis.config = Object.freeze({\n\t\t\tarenaSize: Object.freeze({width: ARENA_WIDTH, height: ARENA_HEIGHT}),\n\t\t\ttickRate: TICKS_PER_SECOND,\n\t\t\tmaxTicks: this.maxTicks,\n\t\t});\n\n\t\t// Hook state must start clean — bot useState/useRef from a previous\n\t\t// match would otherwise leak in.\n\t\tclearHooks('wizard-1');\n\t\tclearHooks('wizard-2');\n\n\t\tthis.wizards = createInitialWizards(spawnDistance);\n\t\tthis.history = [getPlayerState(0, this.wizards, this.projectiles, 0)];\n\t}\n\n\t/**\n\t * Advance the match by `count` ticks (default 1). Stops early if the\n\t * match completes mid-batch.\n\t */\n\tstep(count = 1): StepResult\n\t{\n\t\tconst errorsThisCall: BotError[] = [];\n\t\tfor (let i = 0; i < count; i++)\n\t\t{\n\t\t\tif (this.done) break;\n\t\t\tconst result = tick(\n\t\t\t\tthis.currentTick,\n\t\t\t\tthis.wizard1AI,\n\t\t\t\tthis.wizard2AI,\n\t\t\t\tthis.config,\n\t\t\t\tthis.wizards,\n\t\t\t\tthis.projectiles,\n\t\t\t\tthis.missileAIs,\n\t\t\t\tthis.seed,\n\t\t\t);\n\t\t\tthis.currentTick = result.nextTick;\n\t\t\tthis.wizards = result.wizards;\n\t\t\tthis.projectiles = result.projectiles;\n\t\t\tif (result.errors.length > 0)\n\t\t\t{\n\t\t\t\tthis.allErrors.push(...result.errors);\n\t\t\t\terrorsThisCall.push(...result.errors);\n\t\t\t}\n\n\t\t\tthis.history.push(getPlayerState(0, this.wizards, this.projectiles, this.currentTick));\n\n\t\t\tif (\n\t\t\t\tthis.wizards[0]!.health <= 0\n\t\t\t\t|| this.wizards[1]!.health <= 0\n\t\t\t\t|| this.currentTick >= this.maxTicks\n\t\t\t)\n\t\t\t{\n\t\t\t\tthis.done = true;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tgameState: this.getGameState(),\n\t\t\terrors: errorsThisCall,\n\t\t\tdone: this.done,\n\t\t};\n\t}\n\n\t/**\n\t * Hot-swap a missile's AI function. Used by the missile-hijack feature\n\t * in manual play. The first replacement remembers the original AI so\n\t * `restoreMissileAI` can put it back. Subsequent replacements update\n\t * the active AI but leave the remembered original alone.\n\t *\n\t * No-op if the projectile id doesn't exist.\n\t */\n\treplaceMissileAI(projectileId: string, ai: MissileAIFunction): void\n\t{\n\t\tconst existing = this.missileAIs.get(projectileId);\n\t\tif (!existing) return;\n\t\tif (!this.originalMissileAIs.has(projectileId))\n\t\t{\n\t\t\tthis.originalMissileAIs.set(projectileId, existing);\n\t\t}\n\t\tthis.missileAIs.set(projectileId, ai);\n\t}\n\n\t/**\n\t * Restore a previously hijacked missile's original AI function.\n\t * No-op if the projectile id doesn't exist or was never hijacked.\n\t */\n\trestoreMissileAI(projectileId: string): void\n\t{\n\t\tconst original = this.originalMissileAIs.get(projectileId);\n\t\tif (!original) return;\n\t\tif (!this.missileAIs.has(projectileId)) return;\n\t\tthis.missileAIs.set(projectileId, original);\n\t\tthis.originalMissileAIs.delete(projectileId);\n\t}\n\n\t/**\n\t * Set the invincibility flag for a wizard. Invincible wizards take 0\n\t * damage from all sources.\n\t */\n\tsetInvincible(wizardIndex: 0 | 1, on: boolean): void\n\t{\n\t\tconst wizard = this.wizards[wizardIndex];\n\t\tif (!wizard) return;\n\t\twizard.invincible = on;\n\t}\n\n\t/**\n\t * Get the current game state from wizard-1's perspective.\n\t * Returned object is a deep clone — safe to mutate.\n\t */\n\tgetGameState(): GameState\n\t{\n\t\treturn getPlayerState(0, this.wizards, this.projectiles, this.currentTick);\n\t}\n\n\tgetCurrentTick(): number\n\t{\n\t\treturn this.currentTick;\n\t}\n\n\tisComplete(): {done: boolean; winner: MatchWinner}\n\t{\n\t\treturn {done: this.done, winner: this.done ? this.computeWinner() : null};\n\t}\n\n\tgetResult(): SimulateResult\n\t{\n\t\treturn {\n\t\t\twinner: this.computeWinner(),\n\t\t\tticks: this.currentTick,\n\t\t\tfinalState: this.getGameState(),\n\t\t\thistory: this.history,\n\t\t\terrors: this.allErrors,\n\t\t};\n\t}\n\n\tdispose(): void\n\t{\n\t\tclearHooks('wizard-1');\n\t\tclearHooks('wizard-2');\n\t\tthis.history = [];\n\t}\n\n\tprivate computeWinner(): MatchWinner\n\t{\n\t\tconst w1 = this.wizards[0]!;\n\t\tconst w2 = this.wizards[1]!;\n\t\tif (w1.health <= 0 && w2.health <= 0) return 'draw';\n\t\tif (w2.health <= 0) return 'wizard-1';\n\t\tif (w1.health <= 0) return 'wizard-2';\n\t\treturn null;\n\t}\n}\n","/**\n * VIBEMANCER - MISSILE TEMPLATES\n *\n * Reusable missile config + AI factories for manual play and bot examples.\n * Each factory clamps its inputs to rules.ts minimums and returns a pure\n * AI function that's safe to register with the engine.\n */\n\nimport {\n\tMISSILE_MIN_DAMAGE,\n\tMISSILE_MIN_SPEED,\n\tMISSILE_MIN_DURATION,\n} from '../rules.js';\nimport type {MissileAIFunction, MissileConfig} from '../types.js';\n\ninterface BaseParams\n{\n\tdamage: number;\n\tspeed: number;\n\tduration: number;\n}\n\ninterface TurningParams extends BaseParams\n{\n\tturnRate: number;\n}\n\nexport type StraightParams = BaseParams;\n\nexport type HomingParams = TurningParams;\n\nexport type AntiHomingParams = TurningParams;\n\nexport interface SpiralParams extends BaseParams\n{\n\tspiralRadius: number;\n\tspiralFreq: number;\n}\n\nexport interface SeekerParams extends TurningParams\n{\n\tminLockDistance: number;\n}\n\nexport interface MissileTemplate\n{\n\tconfig: MissileConfig;\n\tai: MissileAIFunction;\n}\n\nfunction clampBase(p: BaseParams): {damage: number; speed: number; duration: number}\n{\n\treturn {\n\t\tdamage: Math.max(MISSILE_MIN_DAMAGE, p.damage),\n\t\tspeed: Math.max(MISSILE_MIN_SPEED, p.speed),\n\t\tduration: Math.max(MISSILE_MIN_DURATION, Math.floor(p.duration)),\n\t};\n}\n\n/**\n * Fire-and-forget missile. No steering — flies in a straight line.\n */\nexport function straightMissile(p: StraightParams): MissileTemplate\n{\n\tconst base = clampBase(p);\n\tconst config: MissileConfig = {...base, turnRate: 0};\n\tconst ai: MissileAIFunction = () => ({});\n\treturn {config, ai};\n}\n\n/**\n * Homing missile. Steers toward the enemy each tick.\n */\nexport function homingMissile(p: HomingParams): MissileTemplate\n{\n\tconst base = clampBase(p);\n\tconst config: MissileConfig = {...base, turnRate: p.turnRate};\n\tconst ai: MissileAIFunction = ({worldState}) =>\n\t{\n\t\tconst enemy = worldState.enemies[0];\n\t\tif (!enemy) return {};\n\t\treturn {turnToward: enemy.position};\n\t};\n\treturn {config, ai};\n}\n\n/**\n * Anti-homing missile. Steers AWAY from the enemy — useful as a feint or\n * area-denial pattern.\n */\nexport function antiHomingMissile(p: AntiHomingParams): MissileTemplate\n{\n\tconst base = clampBase(p);\n\tconst config: MissileConfig = {...base, turnRate: p.turnRate};\n\tconst ai: MissileAIFunction = ({missileState, worldState}) =>\n\t{\n\t\tconst enemy = worldState.enemies[0];\n\t\tif (!enemy) return {};\n\t\t// Reflect the enemy position through the missile to get an \"away\" target.\n\t\tconst target = {\n\t\t\tx: missileState.position.x * 2 - enemy.position.x,\n\t\t\ty: missileState.position.y * 2 - enemy.position.y,\n\t\t};\n\t\treturn {turnToward: target};\n\t};\n\treturn {config, ai};\n}\n\n/**\n * Spiral missile. Continuously orbits its current heading while advancing.\n * Uses missileState.remainingTicks as a deterministic phase counter so the\n * AI is fully pure (no closure state).\n */\nexport function spiralMissile(p: SpiralParams): MissileTemplate\n{\n\tconst base = clampBase(p);\n\t// turnRate must be high enough for the spiral to manifest visibly\n\tconst config: MissileConfig = {...base, turnRate: 12};\n\tconst radius = Math.max(1, p.spiralRadius);\n\tconst freq = p.spiralFreq;\n\tconst ai: MissileAIFunction = ({missileState}) =>\n\t{\n\t\tconst phase = -missileState.remainingTicks * freq;\n\t\tconst target = {\n\t\t\tx: missileState.position.x + Math.cos(phase) * radius,\n\t\t\ty: missileState.position.y + Math.sin(phase) * radius,\n\t\t};\n\t\treturn {turnToward: target};\n\t};\n\treturn {config, ai};\n}\n\n/**\n * Seeker missile. Homes toward the enemy, but only when farther away than\n * minLockDistance — closer than that, the missile coasts straight (so it\n * doesn't whirl around a target it's about to hit).\n */\nexport function seekerMissile(p: SeekerParams): MissileTemplate\n{\n\tconst base = clampBase(p);\n\tconst config: MissileConfig = {...base, turnRate: p.turnRate};\n\tconst minDist = Math.max(0, p.minLockDistance);\n\tconst minDistSq = minDist * minDist;\n\tconst ai: MissileAIFunction = ({missileState, worldState}) =>\n\t{\n\t\tconst enemy = worldState.enemies[0];\n\t\tif (!enemy) return {};\n\t\tconst dx = enemy.position.x - missileState.position.x;\n\t\tconst dy = enemy.position.y - missileState.position.y;\n\t\tconst distSq = dx * dx + dy * dy;\n\t\tif (distSq < minDistSq) return {};\n\t\treturn {turnToward: enemy.position};\n\t};\n\treturn {config, ai};\n}\n","import {Position} from '../types.js';\r\n\r\n/**\r\n * Calculate the distance between two points.\r\n */\r\nexport function distanceTo(a: Position, b: Position): number\r\n{\r\n\tconst dx = b.x - a.x;\r\n\tconst dy = b.y - a.y;\r\n\treturn Math.sqrt(dx * dx + dy * dy);\r\n}\r\n\r\n/**\r\n * Check if two points are within a certain range of each other.\r\n */\r\nexport function inRange(a: Position, b: Position, range: number): boolean\r\n{\r\n\tconst dx = b.x - a.x;\r\n\tconst dy = b.y - a.y;\r\n\treturn (dx * dx + dy * dy) <= range * range;\r\n}\r\n","import {Position, Velocity} from '../types.js';\r\nimport {normalizeAngle} from './angles.js';\r\n\r\n/**\r\n * Get the position after moving a certain distance in a direction.\r\n */\r\nexport function moveInDirection(position: Position, angle: number, distance: number): Position\r\n{\r\n\tconst radians = angle * (Math.PI / 180);\r\n\treturn {\r\n\t\tx: position.x + Math.cos(radians) * distance,\r\n\t\ty: position.y + Math.sin(radians) * distance,\r\n\t};\r\n}\r\n\r\n/**\r\n * Predict the position after N ticks given current velocity.\r\n */\r\nexport function predictPosition(position: Position, velocity: Velocity, ticks: number): Position\r\n{\r\n\treturn {\r\n\t\tx: position.x + velocity.x * ticks,\r\n\t\ty: position.y + velocity.y * ticks,\r\n\t};\r\n}\r\n\r\n/**\r\n * Calculate the intercept angle for a target moving at a certain velocity.\r\n * Returns null if no intercept solution exists.\r\n */\r\nexport function interceptAngle(\r\n\tshooterPosition: Position,\r\n\ttargetPosition: Position,\r\n\ttargetVelocity: Velocity,\r\n\tprojectileSpeed: number,\r\n): number | null\r\n{\r\n\tconst dx = targetPosition.x - shooterPosition.x;\r\n\tconst dy = targetPosition.y - shooterPosition.y;\r\n\r\n\tconst vx = targetVelocity.x;\r\n\tconst vy = targetVelocity.y;\r\n\r\n\tconst a = vx * vx + vy * vy - projectileSpeed * projectileSpeed;\r\n\tconst b = 2 * (vx * dx + vy * dy);\r\n\tconst c = dx * dx + dy * dy;\r\n\r\n\tlet t: number | null = null;\r\n\r\n\t// Handle the linear case when a is approximately zero\r\n\t// This happens when |targetVelocity| === projectileSpeed\r\n\tif (Math.abs(a) < 1e-10)\r\n\t{\r\n\t\t// Linear equation: b*t + c = 0 => t = -c/b\r\n\t\tif (Math.abs(b) < 1e-10)\r\n\t\t{\r\n\t\t\t// No solution possible\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\tconst linearT = -c / b;\r\n\t\tif (linearT > 0)\r\n\t\t{\r\n\t\t\tt = linearT;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\t// Quadratic case\r\n\t\tconst discriminant = b * b - 4 * a * c;\r\n\r\n\t\tif (discriminant < 0)\r\n\t\t{\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\tconst sqrtD = Math.sqrt(discriminant);\r\n\t\tconst t1 = (-b + sqrtD) / (2 * a);\r\n\t\tconst t2 = (-b - sqrtD) / (2 * a);\r\n\r\n\t\tif (t1 > 0 && t2 > 0)\r\n\t\t{\r\n\t\t\tt = Math.min(t1, t2);\r\n\t\t}\r\n\t\telse if (t1 > 0)\r\n\t\t{\r\n\t\t\tt = t1;\r\n\t\t}\r\n\t\telse if (t2 > 0)\r\n\t\t{\r\n\t\t\tt = t2;\r\n\t\t}\r\n\t}\r\n\r\n\tif (t === null)\r\n\t{\r\n\t\treturn null;\r\n\t}\r\n\r\n\tconst interceptPos = {\r\n\t\tx: targetPosition.x + vx * t,\r\n\t\ty: targetPosition.y + vy * t,\r\n\t};\r\n\r\n\tconst dx2 = interceptPos.x - shooterPosition.x;\r\n\tconst dy2 = interceptPos.y - shooterPosition.y;\r\n\r\n\treturn normalizeAngle(Math.atan2(dy2, dx2) * (180 / Math.PI));\r\n}\r\n","import {Position} from '../types.js';\r\nimport {distanceTo} from './distance.js';\r\n\r\n/**\r\n * Find the nearest entity from a list.\r\n */\r\nexport function findNearest<T extends {position: Position}>(from: Position, entities: T[]): T | null\r\n{\r\n\tif (entities.length === 0)\r\n\t{\r\n\t\treturn null;\r\n\t}\r\n\r\n\tlet nearest = entities[0]!;\r\n\tlet minDistance = distanceTo(from, nearest.position);\r\n\r\n\tfor (let i = 1; i < entities.length; i++)\r\n\t{\r\n\t\tconst entity = entities[i]!;\r\n\t\tconst distance = distanceTo(from, entity.position);\r\n\t\tif (distance < minDistance)\r\n\t\t{\r\n\t\t\tminDistance = distance;\r\n\t\t\tnearest = entity;\r\n\t\t}\r\n\t}\r\n\r\n\treturn nearest;\r\n}\r\n\r\n/**\r\n * Find all entities within a certain range.\r\n */\r\nexport function findInRange<T extends {position: Position}>(from: Position, entities: T[], range: number): T[]\r\n{\r\n\treturn entities.filter((entity) => distanceTo(from, entity.position) <= range);\r\n}\r\n\r\n/**\r\n * Sort entities by distance (closest first).\r\n */\r\nexport function sortByDistance<T extends {position: Position}>(from: Position, entities: T[]): T[]\r\n{\r\n\treturn [...entities].sort((a, b) => distanceTo(from, a.position) - distanceTo(from, b.position));\r\n}\r\n","/**\r\n * VIBEMANCER - SPATIAL UTILITIES\r\n *\r\n * Direction vectors and arena bounds utilities.\r\n */\r\n\r\nimport {Position} from '../types.js';\r\nimport {ARENA_WIDTH, ARENA_HEIGHT} from '../rules.js';\r\n\r\n/**\r\n * Normalize a vector to unit length.\r\n * Returns {x: 0, y: 0} for zero-length vectors.\r\n */\r\nexport function normalize(vector: Position): Position\r\n{\r\n\tconst len = Math.sqrt(vector.x * vector.x + vector.y * vector.y);\r\n\tif (len === 0)\r\n\t{\r\n\t\treturn {x: 0, y: 0};\r\n\t}\r\n\treturn {x: vector.x / len, y: vector.y / len};\r\n}\r\n\r\n/**\r\n * Get normalized direction vector from one position toward another.\r\n * Returns {x: 0, y: 0} if positions are identical.\r\n */\r\nexport function directionTo(from: Position, to: Position): Position\r\n{\r\n\tconst dx = to.x - from.x;\r\n\tconst dy = to.y - from.y;\r\n\treturn normalize({x: dx, y: dy});\r\n}\r\n\r\n/**\r\n * Get normalized direction vector from one position away from another.\r\n * Returns {x: 0, y: 0} if positions are identical.\r\n */\r\nexport function directionAway(from: Position, to: Position): Position\r\n{\r\n\tconst dir = directionTo(from, to);\r\n\treturn {x: -dir.x, y: -dir.y};\r\n}\r\n\r\n/**\r\n * Clamp a position to valid arena bounds.\r\n * Note: For wizard-specific clamping with radius, use clampToArena from physics.ts\r\n */\r\nexport function clampPositionToArena(position: Position): Position\r\n{\r\n\treturn {\r\n\t\tx: Math.max(0, Math.min(ARENA_WIDTH, position.x)),\r\n\t\ty: Math.max(0, Math.min(ARENA_HEIGHT, position.y)),\r\n\t};\r\n}\r\n\r\n/**\r\n * Get the length/magnitude of a vector.\r\n */\r\nexport function magnitude(vector: Position): number\r\n{\r\n\treturn Math.sqrt(vector.x * vector.x + vector.y * vector.y);\r\n}\r\n","/**\r\n * VIBEMANCER - COMBAT UTILITIES\r\n *\r\n * Utilities for combat calculations.\r\n */\r\n\r\nimport {Position, Velocity, MissileConfig} from '../types.js';\r\nimport {\r\n\tcalculateMissileCastTime,\r\n\tcalculateWarmupMultiplier,\r\n\tvalidateMissileConfig,\r\n\tTICKS_PER_SECOND,\r\n\tWIZARD_HEALTH,\r\n\tMISSILE_BASE_CAST,\r\n\tMISSILE_DAMAGE_SCALE,\r\n\tMISSILE_DAMAGE_POWER,\r\n\tMISSILE_HOMING_COEFF,\r\n\tMISSILE_TURN_DURATION_COEFF,\r\n\tMISSILE_SPEED_DURATION_COEFF,\r\n\tMISSILE_SPEED_DURATION_BASELINE,\r\n\tMISSILE_MIN_CAST_TIME,\r\n\tMISSILE_MIN_DURATION,\r\n\tMISSILE_MIN_DAMAGE,\r\n\teffectiveTurnRateCost,\r\n} from '../rules.js';\r\n\r\n/**\r\n * Get cast time in ticks for a missile configuration.\r\n * Applies the same clamps/validation as the engine before calculating,\r\n * so the result matches the actual cast time that will be used in-game.\r\n *\r\n * If lastMissileConfig is provided, includes warmup multiplier.\r\n * Pass undefined for first cast (full warmup) or null for base time only.\r\n */\r\nexport function getMissileCastTime(config: MissileConfig, lastMissileConfig?: MissileConfig | null): number\r\n{\r\n\tconst validated = validateMissileConfig(config);\r\n\treturn Math.ceil(calculateMissileCastTime(validated, lastMissileConfig) * TICKS_PER_SECOND);\r\n}\r\n\r\n/**\r\n * Calculate the position to aim at to hit a moving target.\r\n * Returns the intercept point where a missile would hit the target.\r\n *\r\n * @param targetPos - Current target position\r\n * @param targetVel - Target velocity (units per tick)\r\n * @param missileSpeed - Missile speed (units per tick)\r\n * @param myPos - Shooter position\r\n * @returns The position to aim at\r\n */\r\nexport function getLeadPosition(\r\n\ttargetPos: Position,\r\n\ttargetVel: Velocity,\r\n\tmissileSpeed: number,\r\n\tmyPos: Position,\r\n): Position\r\n{\r\n\t// Vector from shooter to target\r\n\tconst dx = targetPos.x - myPos.x;\r\n\tconst dy = targetPos.y - myPos.y;\r\n\r\n\t// Target velocity\r\n\tconst vx = targetVel.x;\r\n\tconst vy = targetVel.y;\r\n\r\n\t// Quadratic coefficients for intercept time\r\n\t// |target + velocity * t - shooter|^2 = (missileSpeed * t)^2\r\n\t// Expands to: (vx^2 + vy^2 - speed^2) * t^2 + 2*(vx*dx + vy*dy) * t + (dx^2 + dy^2) = 0\r\n\tconst a = vx * vx + vy * vy - missileSpeed * missileSpeed;\r\n\tconst b = 2 * (vx * dx + vy * dy);\r\n\tconst c = dx * dx + dy * dy;\r\n\r\n\tlet t: number | null = null;\r\n\r\n\t// Handle the linear case (|velocity| === missileSpeed)\r\n\tif (Math.abs(a) < 1e-10)\r\n\t{\r\n\t\tif (Math.abs(b) > 1e-10)\r\n\t\t{\r\n\t\t\tconst linearT = -c / b;\r\n\t\t\tif (linearT > 0)\r\n\t\t\t{\r\n\t\t\t\tt = linearT;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\t// Quadratic case\r\n\t\tconst discriminant = b * b - 4 * a * c;\r\n\r\n\t\tif (discriminant >= 0)\r\n\t\t{\r\n\t\t\tconst sqrtD = Math.sqrt(discriminant);\r\n\t\t\tconst t1 = (-b + sqrtD) / (2 * a);\r\n\t\t\tconst t2 = (-b - sqrtD) / (2 * a);\r\n\r\n\t\t\tif (t1 > 0 && t2 > 0)\r\n\t\t\t{\r\n\t\t\t\tt = Math.min(t1, t2);\r\n\t\t\t}\r\n\t\t\telse if (t1 > 0)\r\n\t\t\t{\r\n\t\t\t\tt = t1;\r\n\t\t\t}\r\n\t\t\telse if (t2 > 0)\r\n\t\t\t{\r\n\t\t\t\tt = t2;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t// If no intercept solution, just aim at current position\r\n\tif (t === null)\r\n\t{\r\n\t\treturn {x: targetPos.x, y: targetPos.y};\r\n\t}\r\n\r\n\t// Calculate intercept position\r\n\treturn {\r\n\t\tx: targetPos.x + vx * t,\r\n\t\ty: targetPos.y + vy * t,\r\n\t};\r\n}\r\n\r\n/**\r\n * Calculate optimal missile configuration based on target behavior.\r\n */\r\nexport function getAdaptiveMissileConfig(\r\n\ttargetVelocity: Position,\r\n\tdistance: number,\r\n): {speed: number; turnRate: number; damage: number; duration: number}\r\n{\r\n\tconst targetSpeed = Math.sqrt(targetVelocity.x ** 2 + targetVelocity.y ** 2);\r\n\r\n\t// Fast moving target = more homing\r\n\t// Slow/stationary target = faster missile, light homing for correction\r\n\tif (targetSpeed > 0.8)\r\n\t{\r\n\t\t// Moving target - use significant homing\r\n\t\tconst turnRate = Math.min(3, 1 + targetSpeed * 1.5);\r\n\t\treturn {\r\n\t\t\tdamage: 10,\r\n\t\t\tspeed: 5,\r\n\t\t\tturnRate,\r\n\t\t\tduration: Math.min(250, 100 + distance * 0.5),\r\n\t\t};\r\n\t}\r\n\telse\r\n\t{\r\n\t\t// Stationary/slow target - faster missile with light homing for accuracy\r\n\t\treturn {\r\n\t\t\tdamage: 10,\r\n\t\t\tspeed: 6,\r\n\t\t\tturnRate: 1, // Light homing to correct any aiming errors\r\n\t\t\tduration: 180,\r\n\t\t};\r\n\t}\r\n}\r\n\r\n/**\r\n * Given a cast-time budget (in ticks) and a target distance, find the best\r\n * missile config that fits. Maximizes damage while ensuring the missile\r\n * can reach the target and finishes casting in time.\r\n *\r\n * Returns null if no useful missile fits in the budget.\r\n *\r\n * How it works: tries several speed/turnRate templates. For each, calculates\r\n * the minimum duration to reach `distance`, then solves the cast-time formula\r\n * for the maximum damage that fits within `budgetTicks`.\r\n *\r\n * If `lastMissileConfig` is provided, accounts for warmup bonus: similar\r\n * missiles cast faster, so more damage can fit in the same budget.\r\n */\r\nexport function fitMissileToBudget(\r\n\tbudgetTicks: number,\r\n\tdistance: number,\r\n\toptions?: {minTurnRate?: number; maxDamage?: number; lastMissileConfig?: MissileConfig},\r\n): MissileConfig | null\r\n{\r\n\tconst budgetSec = budgetTicks / TICKS_PER_SECOND;\r\n\tconst minTurnRate = options?.minTurnRate ?? 0;\r\n\tconst maxDamage = options?.maxDamage ?? WIZARD_HEALTH;\r\n\t// Warmup is active when caller explicitly passes lastMissileConfig in options.\r\n\t// Value can be undefined (first cast = full warmup) or MissileConfig (similarity-based).\r\n\t// When key is absent (old callers), no warmup is applied.\r\n\tconst warmupActive = options !== undefined && 'lastMissileConfig' in options;\r\n\tconst lastMissile = options?.lastMissileConfig;\r\n\r\n\t// Minimum useful cast time is 10 ticks (0.1s)\r\n\tif (budgetSec < MISSILE_MIN_CAST_TIME)\r\n\t{\r\n\t\treturn null;\r\n\t}\r\n\r\n\t// Templates: [speed, turnRate] combos to try, ordered by preference.\r\n\t// Lower turnRate = cheaper cast. Higher speed = shorter duration needed.\r\n\t// Negative turnRate = anti-homing (turns away from target) but cheaper cast time.\r\n\tconst templates: [number, number][] = [\r\n\t\t[7, -1], // Fast anti-homing: cheapest cast, curves away from target\r\n\t\t[7, -0.5],// Fast light anti-homing\r\n\t\t[7, 0], // Fast straight: cheapest cast, only hits stationary\r\n\t\t[5, 0], // Medium straight\r\n\t\t[6, 0.5], // Fast with light tracking\r\n\t\t[5, 1], // Medium with tracking\r\n\t\t[4, 1], // Slow with tracking (long range)\r\n\t\t[4, 1.5], // Slow with strong tracking\r\n\t\t[3, 2], // Very slow with heavy tracking (for aggressive homing)\r\n\t];\r\n\r\n\tlet best: MissileConfig | null = null;\r\n\tlet bestDamage = 0;\r\n\r\n\tfor (const [speed, turnRate] of templates)\r\n\t{\r\n\t\tif (turnRate < minTurnRate) continue;\r\n\t\t// Minimum duration to reach target (with 20% margin for homing curve)\r\n\t\tconst margin = turnRate > 0 ? 1.2 : 1.05;\r\n\t\tconst minDurationTicks = Math.max(\r\n\t\t\tMISSILE_MIN_DURATION,\r\n\t\t\tMath.ceil(distance * margin / speed),\r\n\t\t);\r\n\t\tconst durationSec = minDurationTicks / TICKS_PER_SECOND;\r\n\r\n\t\t// Solve cast formula for damage:\r\n\t\t// budgetSec = BASE + SCALE * d^POWER + delivery costs\r\n\t\t// → d = ((budgetSec - fixedCost) / SCALE)^(1/POWER)\r\n\t\tconst turnCost = effectiveTurnRateCost(turnRate);\r\n\t\tconst fixedCost = MISSILE_BASE_CAST\r\n\t\t\t+ MISSILE_HOMING_COEFF * turnCost\r\n\t\t\t+ MISSILE_TURN_DURATION_COEFF * (turnCost * durationSec)\r\n\t\t\t+ MISSILE_SPEED_DURATION_COEFF * (speed * durationSec - MISSILE_SPEED_DURATION_BASELINE);\r\n\r\n\t\t// Estimate warmup multiplier for this template (depends on speed/turnRate/duration,\r\n\t\t// not damage, so we can estimate before solving for damage)\r\n\t\tlet effectiveBudgetSec = budgetSec;\r\n\t\tif (warmupActive && lastMissile)\r\n\t\t{\r\n\t\t\tconst probe: MissileConfig = {damage: lastMissile.damage, speed, turnRate, duration: minDurationTicks};\r\n\t\t\tconst multiplier = calculateWarmupMultiplier(lastMissile, probe);\r\n\t\t\t// multiplier < 1 = faster (more budget), > 1 = slower (less budget)\r\n\t\t\teffectiveBudgetSec = budgetSec / multiplier;\r\n\t\t}\r\n\r\n\t\tconst remaining = effectiveBudgetSec - fixedCost;\r\n\t\tif (remaining <= 0)\r\n\t\t{\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tconst maxDamageFloat = Math.pow(remaining / MISSILE_DAMAGE_SCALE, 1 / MISSILE_DAMAGE_POWER);\r\n\t\tconst damage = Math.min(maxDamage, Math.floor(maxDamageFloat));\r\n\r\n\t\tif (damage < MISSILE_MIN_DAMAGE)\r\n\t\t{\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\t// Verify actual cast time with real warmup (using final damage)\r\n\t\tconst candidate: MissileConfig = {damage, speed, turnRate, duration: minDurationTicks};\r\n\t\tconst actualCastSec = (warmupActive && lastMissile)\r\n\t\t\t? calculateMissileCastTime(candidate, lastMissile)\r\n\t\t\t: calculateMissileCastTime(candidate);\r\n\t\tconst actualCastTicks = Math.max(1, Math.ceil(actualCastSec * TICKS_PER_SECOND));\r\n\r\n\t\tif (actualCastTicks > budgetTicks)\r\n\t\t{\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tif (damage > bestDamage)\r\n\t\t{\r\n\t\t\tbestDamage = damage;\r\n\t\t\tbest = candidate;\r\n\t\t}\r\n\t}\r\n\r\n\treturn best;\r\n}\r\n","import {WizardFunction} from '../../types.js';\n\n/**\n * Bot: TargetDummy\n *\n * BEHAVIOR: Does absolutely nothing. No movement, no spells, no AI.\n *\n * NAMING RATIONALE: \"Target Dummy\" is universal MMO player vocabulary for the\n * practice objects found in capital cities. Every WoW/FFXIV player has beaten\n * on a target dummy to test DPS rotations. That's exactly what this bot is —\n * a punching bag for testing missile mechanics and baseline damage output.\n * Nobody calls them \"training dummies\"; the player term is always \"target dummy.\"\n *\n * STANDALONE — no tier progression. It's a test fixture, not a combatant.\n */\nexport const TargetDummy: WizardFunction = (_props) =>\n{\n\treturn {\n\t\tmove: {x: 0, y: 0},\n\t};\n};\n","import {WizardFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\n\n/**\n * Bot: Rookie\n *\n * BEHAVIOR: Stands perfectly still and fires straight (non-homing) missiles at\n * the enemy. No movement, no dodging, no shielding. Knows one spell and uses\n * it on cooldown. The wizard equivalent of an FPS player who stands in the open\n * and holds left-click.\n *\n * NAMING RATIONALE: \"Rookie\" is the universal term for a first-timer who barely\n * knows what they're doing. This bot is a day-one player who learned how to cast\n * missile and nothing else. No movement, no defense, just raw \"I press the button.\"\n * We considered \"Noob\" (more accurate) but Rookie is less abrasive while conveying\n * the same thing — a beginner who doesn't know any better.\n *\n * STANDALONE — no tier progression. Rookies either learn to play a real class\n * or quit. This bot represents the rock-bottom of \"at least it shoots.\"\n */\nexport const Rookie: WizardFunction = ({state}) => \n{\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tif (state.state === 'idle') \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {\n\t\t\t\tspell: 'missile',\n\t\t\t\tconfig: {\n\t\t\t\t\tdamage: 10,\n\t\t\t\t\tspeed: 5,\n\t\t\t\t\tturnRate: 0,\n\t\t\t\t\tduration: 200,\n\t\t\t\t},\n\t\t\t\tmissileAI: () => ({}),\n\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t},\n\t\t};\n\t}\n\n\treturn {\n\t\tmove: {x: 0, y: 0},\n\t};\n};\n","import {WizardFunction} from '../../types.js';\nimport {ARENA_WIDTH, ARENA_HEIGHT} from '../../rules.js';\n\n/**\n * Bot: Critter\n *\n * BEHAVIOR: Picks random valid actions each tick — random movement, random spells,\n * random missile configs, random directions. Occasionally cancels its own casts.\n * Uses engine-provided seeded random for deterministic behavior. Useful for\n * finding edge cases in the engine, but completely useless in combat.\n *\n * NAMING RATIONALE: In WoW, critters are the 1-HP ambient mobs (rabbits, squirrels,\n * prairie dogs) that wander around doing nothing useful and die to literally anything.\n * This bot is the wizard equivalent — it flails around randomly and gets destroyed by\n * anyone with a plan. The word \"Critter\" immediately tells any gamer \"this thing is\n * helpless and exists only to fill space.\"\n *\n * STANDALONE — no tier progression. Critters don't level up. However, a future\n * \"Hogger\" bot could be an elite critter: same chaotic spirit but actually dangerous\n * (like the famous WoW elite that wipes unprepared lowbies).\n */\nexport const Critter: WizardFunction = ({state, random}) => \n{\n\t// Use the engine-provided random function (seeded, isolated, deterministic)\n\t// World-space movement: random x/y in [-100, 100] range\n\tconst move = {\n\t\tx: random() * 200 - 100, // -100 to 100\n\t\ty: random() * 200 - 100, // -100 to 100\n\t};\n\n\tif (state.state === 'idle' && random() > 0.95) \n\t{\n\t\tconst spellRoll = random();\n\t\tif (spellRoll > 0.6) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: {\n\t\t\t\t\t\tdamage: Math.floor(random() * 20) + 5,\n\t\t\t\t\t\tspeed: random() * 5 + 2,\n\t\t\t\t\t\tturnRate: random() * 3,\n\t\t\t\t\t\tduration: Math.floor(random() * 200) + 100,\n\t\t\t\t\t},\n\t\t\t\t\t// Missile AI gets its own random from engine - use it for random targeting\n\t\t\t\t\tmissileAI: ({random: missileRandom}) => ({\n\t\t\t\t\t\tturnToward:\n missileRandom() > 0.5\n \t? {\n \t\tx: missileRandom() * ARENA_WIDTH,\n \t\ty: missileRandom() * ARENA_HEIGHT,\n \t}\n \t: undefined,\n\t\t\t\t\t}),\n\t\t\t\t\tdirection: random() * 360,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\telse if (spellRoll > 0.3) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t\telse if (state.blinkCooldown === 0) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'blink',\n\t\t\t\t\ttarget: {x: random() * ARENA_WIDTH, y: random() * ARENA_HEIGHT},\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\tif (\n\t\t(state.state === 'casting' || state.state === 'channeling') &&\n random() > 0.98\n\t) \n\t{\n\t\treturn {move, cancel: true};\n\t}\n\n\t// Override auto-aim with random direction — Critter doesn't know where anything is\n\treturn {move, aimDirection: random() * 360};\n};\n","/**\r\n * VIBEMANCER - THREAT ANALYSIS\r\n *\r\n * Pre-computes threat information for incoming projectiles.\r\n * This handles the \"subconscious\" perception of missile trajectories.\r\n */\r\n\r\nimport {Position, ProjectileState} from '../types.js';\r\nimport {\r\n\tCOLLISION_RADIUS,\r\n\tMISSILE_BASE_RADIUS,\r\n\tMISSILE_RADIUS_PER_DAMAGE,\r\n\tMOVE_SPEED,\r\n\tSHIELD_CAST_TIME,\r\n\tARENA_WIDTH,\r\n\tARENA_HEIGHT,\r\n\tARENA_WATER_BUFFER,\r\n} from '../rules.js';\r\nimport {distanceTo} from '../utils/distance.js';\r\nimport {angleTo, normalizeAngle, angleDiff} from '../utils/angles.js';\r\nimport type {AnalyzedThreat} from './types.js';\r\n\r\n// Maximum distance at which a missile is still tracked as a potential threat\r\n// (even if simulation says it will miss — homing missiles can change course)\r\nconst THREAT_RELEVANCE_DISTANCE = 500;\r\n\r\n/**\r\n * Analyze all threats from enemy projectiles.\r\n *\r\n * @param myPos - Current position of the wizard\r\n * @param projectiles - All projectiles in the game\r\n * @param myProjectiles - Only the bot's own projectiles (used for filtering)\r\n * @param ticksUntilReady - Ticks until wizard can start a new action\r\n * @returns Array of analyzed threats sorted by ticksToImpact (soonest first)\r\n */\r\nexport function analyzeThreats(\r\n\tmyPos: Position,\r\n\tprojectiles: ProjectileState[],\r\n\tmyProjectiles: ProjectileState[],\r\n\tticksUntilReady: number,\r\n): AnalyzedThreat[]\r\n{\r\n\t// Filter to enemy projectiles by excluding our own\r\n\tconst myProjectileIds = new Set(myProjectiles.map((p) => p.id));\r\n\tconst enemyProjectiles = projectiles.filter((p) => !myProjectileIds.has(p.id));\r\n\r\n\t// Analyze each projectile\r\n\tconst threats: AnalyzedThreat[] = [];\r\n\r\n\tfor (const projectile of enemyProjectiles)\r\n\t{\r\n\t\tconst analysis = analyzeOneThreat(myPos, projectile, ticksUntilReady);\r\n\t\t// Only include if it will hit or missile is within reasonable distance\r\n\t\tconst missileDistance = Math.sqrt(\r\n\t\t\t(projectile.position.x - myPos.x) ** 2 +\r\n\t\t\t(projectile.position.y - myPos.y) ** 2,\r\n\t\t);\r\n\t\tif (analysis.willHit || missileDistance < THREAT_RELEVANCE_DISTANCE)\r\n\t\t{\r\n\t\t\tthreats.push(analysis);\r\n\t\t}\r\n\t}\r\n\r\n\t// Sort by ticksToImpact (soonest first)\r\n\tthreats.sort((a, b) => a.ticksToImpact - b.ticksToImpact);\r\n\r\n\treturn threats;\r\n}\r\n\r\n/**\r\n * Analyze a single projectile threat.\r\n */\r\nfunction analyzeOneThreat(\r\n\ttargetPos: Position,\r\n\tprojectile: ProjectileState,\r\n\tticksUntilReady: number,\r\n): AnalyzedThreat\r\n{\r\n\t// Calculate collision radius\r\n\tconst missileRadius = MISSILE_BASE_RADIUS + projectile.damage * MISSILE_RADIUS_PER_DAMAGE;\r\n\tconst collisionDist = COLLISION_RADIUS + missileRadius;\r\n\r\n\t// Dodge simulation uses a larger collision distance to account for swept circle collision.\r\n\t// The real game checks the entire missile path each tick (swept), but the simulation only\r\n\t// checks endpoints. Adding half the missile speed compensates for this gap.\r\n\tconst dodgeCollisionDist = collisionDist + projectile.speed * 0.5;\r\n\r\n\t// Simulate missile trajectory toward stationary target\r\n\tconst {willHit, ticksToImpact} = simulateMissileToTarget(\r\n\t\tprojectile,\r\n\t\ttargetPos,\r\n\t\tcollisionDist,\r\n\t);\r\n\r\n\t// Only simulate dodges for missiles that will actually hit (saves ~75% simulation work)\r\n\tlet canDodgeLeft = true;\r\n\tlet canDodgeRight = true;\r\n\tlet canOutrun = true;\r\n\tlet bestDodgeDirection: Position | null = null;\r\n\r\n\tif (willHit)\r\n\t{\r\n\t\tcanDodgeLeft = simulateDodge(projectile, targetPos, 'left', dodgeCollisionDist);\r\n\t\tcanDodgeRight = simulateDodge(projectile, targetPos, 'right', dodgeCollisionDist);\r\n\t\tcanOutrun = simulateDodge(projectile, targetPos, 'away', dodgeCollisionDist);\r\n\r\n\t\tbestDodgeDirection = calculateBestDodgeDirection(\r\n\t\t\tprojectile,\r\n\t\t\ttargetPos,\r\n\t\t\tcanDodgeLeft,\r\n\t\t\tcanDodgeRight,\r\n\t\t\tcanOutrun,\r\n\t\t);\r\n\t}\r\n\r\n\t// Calculate shield timing\r\n\t// Can block if: ticksUntilReady + shield cast time < ticksToImpact\r\n\tconst canBlockInTime = ticksToImpact > ticksUntilReady + SHIELD_CAST_TIME + 1;\r\n\r\n\t// When to start casting shield (leave 1 tick buffer)\r\n\tconst ticksToStartShield = Math.max(0, ticksToImpact - SHIELD_CAST_TIME - 1);\r\n\r\n\treturn {\r\n\t\tid: projectile.id,\r\n\t\tprojectile,\r\n\t\tticksToImpact,\r\n\t\twillHit,\r\n\t\tcanDodgeLeft,\r\n\t\tcanDodgeRight,\r\n\t\tcanOutrun,\r\n\t\tbestDodgeDirection,\r\n\t\tcanBlockInTime,\r\n\t\tticksToStartShield,\r\n\t};\r\n}\r\n\r\n/**\r\n * Simulate a missile moving toward a stationary target.\r\n * Accounts for homing (turn rate) and expiration.\r\n */\r\nfunction simulateMissileToTarget(\r\n\tprojectile: ProjectileState,\r\n\ttargetPos: Position,\r\n\tcollisionDist: number,\r\n): {willHit: boolean; ticksToImpact: number}\r\n{\r\n\tlet pos = {...projectile.position};\r\n\tlet rotation = projectile.rotation;\r\n\tconst maxTicks = projectile.remainingTicks;\r\n\tconst speed = projectile.speed;\r\n\tconst turnRate = projectile.turnRate;\r\n\r\n\tfor (let tick = 1; tick <= maxTicks; tick++)\r\n\t{\r\n\t\t// Apply homing (turn toward target)\r\n\t\tif (turnRate > 0)\r\n\t\t{\r\n\t\t\tconst desiredAngle = angleTo(pos, targetPos);\r\n\t\t\tconst diff = angleDiff(rotation, desiredAngle);\r\n\t\t\tconst maxTurn = turnRate;\r\n\r\n\t\t\tif (Math.abs(diff) <= maxTurn)\r\n\t\t\t{\r\n\t\t\t\trotation = desiredAngle;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\trotation = normalizeAngle(rotation + Math.sign(diff) * maxTurn);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Move forward\r\n\t\tconst rad = rotation * (Math.PI / 180);\r\n\t\tpos = {\r\n\t\t\tx: pos.x + Math.cos(rad) * speed,\r\n\t\t\ty: pos.y + Math.sin(rad) * speed,\r\n\t\t};\r\n\r\n\t\t// Check if missile is out of bounds (with water buffer)\r\n\t\tif (isOutOfBounds(pos))\r\n\t\t{\r\n\t\t\treturn {willHit: false, ticksToImpact: Infinity};\r\n\t\t}\r\n\r\n\t\t// Check collision\r\n\t\tconst dist = distanceTo(pos, targetPos);\r\n\t\tif (dist <= collisionDist)\r\n\t\t{\r\n\t\t\treturn {willHit: true, ticksToImpact: tick};\r\n\t\t}\r\n\t}\r\n\r\n\t// Missile expired without hitting\r\n\treturn {willHit: false, ticksToImpact: Infinity};\r\n}\r\n\r\n/**\r\n * Simulate a dodge attempt.\r\n * Returns true if the dodge avoids the missile.\r\n */\r\nfunction simulateDodge(\r\n\tprojectile: ProjectileState,\r\n\tstartPos: Position,\r\n\tdirection: 'left' | 'right' | 'away',\r\n\tcollisionDist: number,\r\n): boolean\r\n{\r\n\t// Calculate dodge direction vector\r\n\tconst dodgeDir = getDodgeDirection(projectile, startPos, direction);\r\n\r\n\t// If we can't determine a dodge direction, fail\r\n\tif (dodgeDir.x === 0 && dodgeDir.y === 0)\r\n\t{\r\n\t\treturn false;\r\n\t}\r\n\r\n\tlet targetPos = {...startPos};\r\n\tlet missilePos = {...projectile.position};\r\n\tlet missileRotation = projectile.rotation;\r\n\tconst maxTicks = projectile.remainingTicks;\r\n\tconst speed = projectile.speed;\r\n\tconst turnRate = projectile.turnRate;\r\n\r\n\tfor (let tick = 1; tick <= maxTicks; tick++)\r\n\t{\r\n\t\t// Move target\r\n\t\tconst newX = targetPos.x + dodgeDir.x * MOVE_SPEED;\r\n\t\tconst newY = targetPos.y + dodgeDir.y * MOVE_SPEED;\r\n\r\n\t\t// Clamp to arena bounds\r\n\t\ttargetPos = {\r\n\t\t\tx: Math.max(COLLISION_RADIUS, Math.min(ARENA_WIDTH - COLLISION_RADIUS, newX)),\r\n\t\t\ty: Math.max(COLLISION_RADIUS, Math.min(ARENA_HEIGHT - COLLISION_RADIUS, newY)),\r\n\t\t};\r\n\r\n\t\t// Apply missile homing toward new target position\r\n\t\tif (turnRate > 0)\r\n\t\t{\r\n\t\t\tconst desiredAngle = angleTo(missilePos, targetPos);\r\n\t\t\tconst diff = angleDiff(missileRotation, desiredAngle);\r\n\t\t\tconst maxTurn = turnRate;\r\n\r\n\t\t\tif (Math.abs(diff) <= maxTurn)\r\n\t\t\t{\r\n\t\t\t\tmissileRotation = desiredAngle;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tmissileRotation = normalizeAngle(missileRotation + Math.sign(diff) * maxTurn);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Move missile\r\n\t\tconst rad = missileRotation * (Math.PI / 180);\r\n\t\tmissilePos = {\r\n\t\t\tx: missilePos.x + Math.cos(rad) * speed,\r\n\t\t\ty: missilePos.y + Math.sin(rad) * speed,\r\n\t\t};\r\n\r\n\t\t// Check if missile is out of bounds\r\n\t\tif (isOutOfBounds(missilePos))\r\n\t\t{\r\n\t\t\treturn true; // Dodged - missile left arena\r\n\t\t}\r\n\r\n\t\t// Check collision\r\n\t\tconst dist = distanceTo(missilePos, targetPos);\r\n\t\tif (dist <= collisionDist)\r\n\t\t{\r\n\t\t\treturn false; // Got hit while dodging\r\n\t\t}\r\n\t}\r\n\r\n\t// Missile expired without hitting\r\n\treturn true;\r\n}\r\n\r\n/**\r\n * Get the dodge direction vector based on direction type.\r\n */\r\nfunction getDodgeDirection(\r\n\tprojectile: ProjectileState,\r\n\ttargetPos: Position,\r\n\tdirection: 'left' | 'right' | 'away',\r\n): Position\r\n{\r\n\t// Get missile direction vector\r\n\tconst missileAngleRad = projectile.rotation * (Math.PI / 180);\r\n\tconst missileDir = {\r\n\t\tx: Math.cos(missileAngleRad),\r\n\t\ty: Math.sin(missileAngleRad),\r\n\t};\r\n\r\n\tswitch (direction)\r\n\t{\r\n\t\tcase 'left':\r\n\t\t\t// Perpendicular to missile direction (left = counter-clockwise)\r\n\t\t\treturn {x: missileDir.y, y: -missileDir.x};\r\n\r\n\t\tcase 'right':\r\n\t\t\t// Perpendicular to missile direction (right = clockwise)\r\n\t\t\treturn {x: -missileDir.y, y: missileDir.x};\r\n\r\n\t\tcase 'away':\r\n\t\t{\r\n\t\t\t// Away from missile current position\r\n\t\t\tconst dx = targetPos.x - projectile.position.x;\r\n\t\t\tconst dy = targetPos.y - projectile.position.y;\r\n\t\t\tconst len = Math.sqrt(dx * dx + dy * dy);\r\n\t\t\tif (len === 0) return {x: 0, y: 0};\r\n\t\t\treturn {x: dx / len, y: dy / len};\r\n\t\t}\r\n\r\n\t\tdefault:\r\n\t\t\treturn {x: 0, y: 0};\r\n\t}\r\n}\r\n\r\n/**\r\n * Calculate the best dodge direction based on available options.\r\n */\r\nfunction calculateBestDodgeDirection(\r\n\tprojectile: ProjectileState,\r\n\ttargetPos: Position,\r\n\tcanDodgeLeft: boolean,\r\n\tcanDodgeRight: boolean,\r\n\tcanOutrun: boolean,\r\n): Position | null\r\n{\r\n\t// Prefer sidestep over outrunning (faster result)\r\n\tif (canDodgeLeft && canDodgeRight)\r\n\t{\r\n\t\t// Both sides work - pick the one closer to where we want to be\r\n\t\t// For simplicity, just return left\r\n\t\treturn getDodgeDirection(projectile, targetPos, 'left');\r\n\t}\r\n\telse if (canDodgeLeft)\r\n\t{\r\n\t\treturn getDodgeDirection(projectile, targetPos, 'left');\r\n\t}\r\n\telse if (canDodgeRight)\r\n\t{\r\n\t\treturn getDodgeDirection(projectile, targetPos, 'right');\r\n\t}\r\n\telse if (canOutrun)\r\n\t{\r\n\t\treturn getDodgeDirection(projectile, targetPos, 'away');\r\n\t}\r\n\r\n\t// No dodge available\r\n\treturn null;\r\n}\r\n\r\n/**\r\n * Check if a position is out of bounds (past the water buffer).\r\n */\r\nfunction isOutOfBounds(pos: Position): boolean\r\n{\r\n\treturn (\r\n\t\tpos.x < -ARENA_WATER_BUFFER ||\r\n\t\tpos.x > ARENA_WIDTH + ARENA_WATER_BUFFER ||\r\n\t\tpos.y < -ARENA_WATER_BUFFER ||\r\n\t\tpos.y > ARENA_HEIGHT + ARENA_WATER_BUFFER\r\n\t);\r\n}\r\n","/**\r\n * VIBEMANCER - SHARED BOT UTILITIES\r\n *\r\n * Common helpers used by multiple bots for threat analysis and defense.\r\n */\r\n\r\nimport {GameState, Position, ProjectileState} from '../types.js';\r\nimport {analyzeThreats} from '../hooks/threat-analysis.js';\r\nimport type {AnalyzedThreat} from '../hooks/types.js';\r\nimport {distanceTo} from '../utils/distance.js';\r\nimport {ARENA_SIZE, BLINK_RANGE, GCD_DURATION, SHIELD_CAST_TIME, WIZARD_RADIUS} from '../rules.js';\r\n\r\n// Re-export public utility so bots can import from shared\r\nexport {fitMissileToBudget} from '../utils/combat.js';\r\n\r\n// T2 bots: max cast time when no threats are incoming (~2s)\r\nexport const MAX_CAST_BUDGET = 200;\r\n\r\n// Minimum useful damage — d=1 to d=4 missiles waste cast time for negligible output.\r\n// When budget only allows this, fall back to firing a bigger missile and accept the hit.\r\nexport const MIN_USEFUL_DAMAGE = 5;\r\n\r\n/**\r\n * Compute how many ticks until the wizard can start a new action.\r\n */\r\nexport function computeTicksUntilReady(state: GameState): number\r\n{\r\n\tswitch (state.state)\r\n\t{\r\n\t\tcase 'idle':\r\n\t\t\treturn 0;\r\n\t\tcase 'casting':\r\n\t\t\treturn (state.castDuration ?? 0) - (state.castProgress ?? 0);\r\n\t\tcase 'channeling':\r\n\t\t\treturn 0; // can cancel instantly\r\n\t\tcase 'gcd_locked':\r\n\t\t\treturn state.gcdRemaining ?? 0;\r\n\t\tdefault:\r\n\t\t\treturn 0;\r\n\t}\r\n}\r\n\r\n/**\r\n * Get all threats from enemy projectiles using the simulation-based system.\r\n */\r\nexport function getThreats(state: GameState): AnalyzedThreat[]\r\n{\r\n\tconst ticksUntilReady = computeTicksUntilReady(state);\r\n\treturn analyzeThreats(state.position, state.projectiles, state.myProjectiles, ticksUntilReady);\r\n}\r\n\r\n/**\r\n * Get the most urgent threat that will actually hit us.\r\n * Returns null if no threats will hit.\r\n */\r\nexport function getMostUrgentThreat(threats: AnalyzedThreat[]): AnalyzedThreat | null\r\n{\r\n\t// Threats are already sorted by ticksToImpact (soonest first)\r\n\treturn threats.find((t) => t.willHit) ?? null;\r\n}\r\n\r\n/**\r\n * Determine if a threat should force a shield even when a dodge direction exists.\r\n * High-damage homing missiles are too risky to dodge — the missile tracks the wizard\r\n * and the dodge often fails. Shield instead to guarantee survival.\r\n */\r\nexport function shouldForceShield(threat: AnalyzedThreat): boolean\r\n{\r\n\treturn threat.projectile.damage >= 25 && threat.projectile.turnRate > 0;\r\n}\r\n\r\n/**\r\n * Get a dodge direction for movement, even if no threat will directly hit.\r\n * Homing missiles continuously adjust course, so even missiles the simulation\r\n * says will \"miss\" should be dodged if they're close.\r\n *\r\n * Uses the missile's heading to determine which side it will pass the wizard,\r\n * then dodges perpendicular to the heading, AWAY from the approach side.\r\n * When the missile is heading dead-center at us (ambiguous), uses wall\r\n * avoidance as a stable tiebreaker.\r\n */\r\nexport function getDodgeDirectionForMovement(\r\n\tthreats: AnalyzedThreat[],\r\n\tmyPos: Position,\r\n): Position | null\r\n{\r\n\t// Only dodge missiles the simulation predicts will hit\r\n\tconst target = threats.find((t) => t.willHit) ?? null;\r\n\r\n\tif (!target) return null;\r\n\r\n\t// Missile heading direction (from its rotation)\r\n\tconst rad = target.projectile.rotation * (Math.PI / 180);\r\n\tconst missileDir = {x: Math.cos(rad), y: Math.sin(rad)};\r\n\r\n\t// Vector from missile to wizard\r\n\tconst toWizard = {\r\n\t\tx: myPos.x - target.projectile.position.x,\r\n\t\ty: myPos.y - target.projectile.position.y,\r\n\t};\r\n\tconst len = Math.sqrt(toWizard.x * toWizard.x + toWizard.y * toWizard.y);\r\n\tif (len < 0.01) return null;\r\n\r\n\t// Cross product: which side of the missile's path is the wizard on?\r\n\t// cross > 0 → wizard is clockwise from heading → missile passes CCW → dodge CW (away)\r\n\t// cross < 0 → wizard is counter-clockwise → missile passes CW → dodge CCW (away)\r\n\tconst cross = missileDir.x * toWizard.y - missileDir.y * toWizard.x;\r\n\r\n\t// Perpendicular options to missile heading\r\n\tconst dodgeCW = {x: missileDir.y, y: -missileDir.x};\r\n\tconst dodgeCCW = {x: -missileDir.y, y: missileDir.x};\r\n\r\n\t// Deadzone: missile heading nearly straight at us (~8° cone).\r\n\t// Both sides are equally good, so use a stable tiebreaker.\r\n\tconst DEADZONE = 0.15 * len;\r\n\r\n\tif (cross > DEADZONE)\r\n\t{\r\n\t\treturn dodgeCCW;\r\n\t}\r\n\tif (cross < -DEADZONE)\r\n\t{\r\n\t\treturn dodgeCW;\r\n\t}\r\n\r\n\t// Tiebreaker for dead-center missiles: pick the side further from walls\r\n\tconst futureA = {x: myPos.x + dodgeCCW.x * 100, y: myPos.y + dodgeCCW.y * 100};\r\n\tconst futureB = {x: myPos.x + dodgeCW.x * 100, y: myPos.y + dodgeCW.y * 100};\r\n\tconst scoreA = Math.min(futureA.x, ARENA_SIZE - futureA.x, futureA.y, ARENA_SIZE - futureA.y);\r\n\tconst scoreB = Math.min(futureB.x, ARENA_SIZE - futureB.x, futureB.y, ARENA_SIZE - futureB.y);\r\n\r\n\treturn scoreA >= scoreB ? dodgeCCW : dodgeCW;\r\n}\r\n\r\n/**\r\n * Check if it's safe to start an attack (no imminent undodgeable threats).\r\n *\r\n * @param threats - Analyzed threats\r\n * @param castTicks - How many ticks the attack cast will take\r\n * @param gcdTicks - GCD ticks after cast (default 100)\r\n * @param shieldCastTicks - Shield cast time (default 20)\r\n */\r\nexport function isSafeToAttack(\r\n\tthreats: AnalyzedThreat[],\r\n\tcastTicks: number,\r\n\tgcdTicks: number = GCD_DURATION,\r\n\tshieldCastTicks: number = SHIELD_CAST_TIME,\r\n): boolean\r\n{\r\n\t// No threats = safe\r\n\tconst hittingThreats = threats.filter((t) => t.willHit);\r\n\r\n\tfor (const threat of hittingThreats)\r\n\t{\r\n\t\tif (threat.bestDodgeDirection !== null)\r\n\t\t{\r\n\t\t\t// Homing missiles track during GCD, but the wizard moves at full speed\r\n\t\t\t// during GCD and can dodge normally. Only the cast (half speed) plus a\r\n\t\t\t// transition buffer is truly dangerous for dodgeable homing missiles.\r\n\t\t\t// Non-homing missiles only need dodging during the cast itself.\r\n\t\t\tif (threat.projectile.turnRate > 0)\r\n\t\t\t{\r\n\t\t\t\tif (threat.ticksToImpact < castTicks + HOMING_DODGE_BUFFER)\r\n\t\t\t\t{\r\n\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if (threat.ticksToImpact < castTicks)\r\n\t\t\t{\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\t// Undodgeable threat: we need time to finish cast + GCD + shield cast\r\n\t\tconst timeNeeded = castTicks + gcdTicks + shieldCastTicks + 1;\r\n\t\tif (threat.ticksToImpact < timeNeeded)\r\n\t\t{\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n/**\r\n * Calculate a blink target toward arena center (for emergency escapes).\r\n */\r\nexport function getBlinkToCenter(pos: Position): Position\r\n{\r\n\tconst centerX = ARENA_SIZE / 2;\r\n\tconst centerY = ARENA_SIZE / 2;\r\n\tconst dx = centerX - pos.x;\r\n\tconst dy = centerY - pos.y;\r\n\tconst dist = Math.sqrt(dx * dx + dy * dy);\r\n\r\n\tif (dist === 0) return {x: centerX, y: centerY};\r\n\r\n\treturn {\r\n\t\tx: pos.x + (dx / dist) * BLINK_RANGE,\r\n\t\ty: pos.y + (dy / dist) * BLINK_RANGE,\r\n\t};\r\n}\r\n\r\n// ============================================================================\r\n// BLINK DISTANCE FUNCTIONS\r\n// ============================================================================\r\n\r\n// Post-blink vulnerability: shield cast time before wizard can defend (blink has no GCD)\r\nconst POST_BLINK_VULNERABILITY = SHIELD_CAST_TIME;\r\n\r\n// Approximate hit radius for missile safety (wizard radius + typical missile radius)\r\nconst HIT_RADIUS = WIZARD_RADIUS + 3;\r\n\r\n// Homing missiles need extra buffer beyond cast time because they track during GCD\r\nconst HOMING_DODGE_BUFFER = 20;\r\n\r\n// Minimum wall margin when evaluating blink targets (prevents blinking into walls)\r\nconst BLINK_WALL_MARGIN = 20;\r\n\r\n// Minimum post-blink distance from enemy for escape blinks to be worthwhile\r\nexport const MIN_BLINK_ESCAPE_DISTANCE = 250;\r\n\r\n// Scoring constants\r\nconst ANGLE_CANDIDATES = 24; // every 15°\r\nconst DISTANCE_FRACTIONS = [1.0, 0.7, 0.4];\r\n\r\n\r\n/**\r\n * Find the best blink target to increase distance from an enemy.\r\n * Avoids positions where missiles could arrive during the post-blink GCD window.\r\n * Returns null if minResultDistance can't be achieved.\r\n */\r\nexport function getBlinkToIncreaseDistance(\r\n\tmyPos: Position,\r\n\tenemyPos: Position,\r\n\tprojectiles: ProjectileState[],\r\n\tminResultDistance?: number,\r\n): Position | null\r\n{\r\n\treturn findBestBlinkTarget(myPos, enemyPos, projectiles, 'increase', minResultDistance);\r\n}\r\n\r\n/**\r\n * Find the best blink target to decrease distance to an enemy.\r\n * Avoids positions where missiles could arrive during the post-blink GCD window.\r\n */\r\nexport function getBlinkToDecreaseDistance(\r\n\tmyPos: Position,\r\n\tenemyPos: Position,\r\n\tprojectiles: ProjectileState[],\r\n): Position | null\r\n{\r\n\treturn findBestBlinkTarget(myPos, enemyPos, projectiles, 'decrease');\r\n}\r\n\r\n/**\r\n * Core blink target finder. Evaluates candidates across multiple angles and distances,\r\n * scoring by distance goal + wall safety + missile avoidance.\r\n */\r\nfunction findBestBlinkTarget(\r\n\tmyPos: Position,\r\n\tenemyPos: Position,\r\n\tprojectiles: ProjectileState[],\r\n\tgoal: 'increase' | 'decrease',\r\n\tminResultDistance?: number,\r\n): Position | null\r\n{\r\n\tconst currentDist = distanceTo(myPos, enemyPos);\r\n\r\n\t// Base direction: away from enemy (increase) or toward enemy (decrease)\r\n\tlet baseAngle: number;\r\n\tif (currentDist < 0.01)\r\n\t{\r\n\t\t// Overlapping: fallback to toward center\r\n\t\tbaseAngle = Math.atan2(ARENA_SIZE / 2 - myPos.y, ARENA_SIZE / 2 - myPos.x);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tconst dx = goal === 'increase'\r\n\t\t\t? myPos.x - enemyPos.x\r\n\t\t\t: enemyPos.x - myPos.x;\r\n\t\tconst dy = goal === 'increase'\r\n\t\t\t? myPos.y - enemyPos.y\r\n\t\t\t: enemyPos.y - myPos.y;\r\n\t\tbaseAngle = Math.atan2(dy, dx);\r\n\t}\r\n\r\n\t// For decrease: don't overshoot past enemy\r\n\tconst maxBlinkDist = goal === 'decrease'\r\n\t\t? Math.min(BLINK_RANGE, currentDist)\r\n\t\t: BLINK_RANGE;\r\n\r\n\tlet bestTarget: Position | null = null;\r\n\tlet bestScore = -Infinity;\r\n\r\n\tfor (const fraction of DISTANCE_FRACTIONS)\r\n\t{\r\n\t\tconst blinkDist = maxBlinkDist * fraction;\r\n\t\tif (blinkDist < 10) continue;\r\n\r\n\t\tfor (let i = 0; i < ANGLE_CANDIDATES; i++)\r\n\t\t{\r\n\t\t\tconst angle = baseAngle + (i * 2 * Math.PI) / ANGLE_CANDIDATES;\r\n\r\n\t\t\t// Candidate position (clamped with wall buffer to prevent wall-hugging)\r\n\t\t\tconst cx = Math.max(BLINK_WALL_MARGIN, Math.min(ARENA_SIZE - BLINK_WALL_MARGIN, myPos.x + Math.cos(angle) * blinkDist));\r\n\t\t\tconst cy = Math.max(BLINK_WALL_MARGIN, Math.min(ARENA_SIZE - BLINK_WALL_MARGIN, myPos.y + Math.sin(angle) * blinkDist));\r\n\r\n\t\t\t// Skip if clamping made this a non-blink\r\n\t\t\tif (distanceTo(myPos, {x: cx, y: cy}) < 10) continue;\r\n\r\n\t\t\tconst candidate = {x: cx, y: cy};\r\n\r\n\t\t\t// Score: distance from enemy (maximize for increase, minimize for decrease)\r\n\t\t\tconst enemyDist = distanceTo(candidate, enemyPos);\r\n\t\t\tconst distScore = goal === 'increase' ? enemyDist : -enemyDist;\r\n\r\n\t\t\t// Penalize distance from arena center (prevents corner-trapping)\r\n\t\t\tconst center = ARENA_SIZE / 2;\r\n\t\t\tconst distFromCenter = distanceTo(candidate, {x: center, y: center});\r\n\r\n\t\t\t// Missile danger penalty\r\n\t\t\tconst missilePenalty = getMissileDangerAtPosition(candidate, projectiles);\r\n\r\n\t\t\tconst score = distScore - distFromCenter * 0.5 - missilePenalty;\r\n\r\n\t\t\tif (score > bestScore)\r\n\t\t\t{\r\n\t\t\t\tbestScore = score;\r\n\t\t\t\tbestTarget = candidate;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t// Check minResultDistance threshold\r\n\tif (minResultDistance !== undefined && bestTarget !== null)\r\n\t{\r\n\t\tif (distanceTo(bestTarget, enemyPos) < minResultDistance)\r\n\t\t{\r\n\t\t\treturn null;\r\n\t\t}\r\n\t}\r\n\r\n\treturn bestTarget;\r\n}\r\n\r\n/**\r\n * Estimate missile danger at a position during the post-blink vulnerability window.\r\n * Returns a penalty score (0 = safe, high = dangerous).\r\n *\r\n * Uses the missile's actual heading, speed, and turn rate to estimate arrival time.\r\n * During GCD the wizard moves at full speed (1 u/tick), so missiles only threaten\r\n * the blink destination if they arrive quickly enough that the wizard can't dodge.\r\n */\r\nfunction getMissileDangerAtPosition(\r\n\tpos: Position,\r\n\tprojectiles: ProjectileState[],\r\n): number\r\n{\r\n\tlet danger = 0;\r\n\r\n\tfor (const proj of projectiles)\r\n\t{\r\n\t\tconst dx = pos.x - proj.position.x;\r\n\t\tconst dy = pos.y - proj.position.y;\r\n\t\tconst dist = Math.sqrt(dx * dx + dy * dy);\r\n\r\n\t\t// Can the missile physically reach this position in time?\r\n\t\tif (dist / proj.speed > POST_BLINK_VULNERABILITY) continue;\r\n\r\n\t\tif (proj.turnRate > 0)\r\n\t\t{\r\n\t\t\t// Homing: estimate arrival using heading + turn rate + distance.\r\n\t\t\t// The missile must turn toward the position AND travel to it.\r\n\t\t\t// Minimum arrival ≈ max(turnTime, travelTime) since they overlap.\r\n\t\t\tconst angleToPos = Math.atan2(dy, dx) * (180 / Math.PI);\r\n\t\t\tlet angleDiff = angleToPos - proj.rotation;\r\n\t\t\twhile (angleDiff > 180) angleDiff -= 360;\r\n\t\t\twhile (angleDiff < -180) angleDiff += 360;\r\n\r\n\t\t\tconst turnTicks = Math.abs(angleDiff) / proj.turnRate;\r\n\t\t\tconst travelTicks = dist / proj.speed;\r\n\t\t\tconst estimatedArrival = Math.max(turnTicks, travelTicks);\r\n\r\n\t\t\t// Only dangerous if it arrives before the wizard can move away.\r\n\t\t\t// At full speed (1 u/tick), the wizard needs ~HIT_RADIUS ticks to\r\n\t\t\t// sidestep out of the collision zone. Add a buffer for reaction.\r\n\t\t\tif (estimatedArrival < HIT_RADIUS * 3)\r\n\t\t\t{\r\n\t\t\t\tdanger += 1000;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// Non-homing: check if straight-line path passes near the position\r\n\t\t\tconst rad = proj.rotation * (Math.PI / 180);\r\n\t\t\tconst mx = Math.cos(rad);\r\n\t\t\tconst my = Math.sin(rad);\r\n\r\n\t\t\t// Project position onto missile path (dot product)\r\n\t\t\tconst dot = dx * mx + dy * my;\r\n\t\t\tif (dot < 0) continue; // missile heading away\r\n\r\n\t\t\t// Closest point on missile path to candidate\r\n\t\t\tconst closestX = proj.position.x + mx * dot;\r\n\t\t\tconst closestY = proj.position.y + my * dot;\r\n\t\t\tconst closestDist = Math.sqrt(\r\n\t\t\t\t(pos.x - closestX) ** 2 + (pos.y - closestY) ** 2,\r\n\t\t\t);\r\n\r\n\t\t\t// Check if path passes close enough to hit, within time window\r\n\t\t\tif (closestDist < HIT_RADIUS * 3 && dot / proj.speed <= POST_BLINK_VULNERABILITY)\r\n\t\t\t{\r\n\t\t\t\tdanger += 1000;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\treturn danger;\r\n}\r\n\r\n\r\n// ============================================================================\r\n// T3 SHARED HELPERS\r\n// ============================================================================\r\n\r\n/**\r\n * Compute how fast the enemy is closing distance (positive = approaching).\r\n * Projects enemy velocity onto the vector from enemy to us.\r\n */\r\nexport function getClosingRate(\r\n\tmyPos: {x: number; y: number},\r\n\tenemyPos: {x: number; y: number},\r\n\tenemyVel: {x: number; y: number},\r\n): number\r\n{\r\n\tconst dx = myPos.x - enemyPos.x;\r\n\tconst dy = myPos.y - enemyPos.y;\r\n\tconst dist = Math.sqrt(dx * dx + dy * dy);\r\n\tif (dist < 0.01) return 0;\r\n\treturn (enemyVel.x * dx + enemyVel.y * dy) / dist;\r\n}\r\n\r\n/**\r\n * Compute how many ticks the enemy remains unable to shield.\r\n * Casting missile → remaining cast + GCD. Casting blink/shield → remaining cast only.\r\n * GCD-locked → remaining GCD. Idle/channeling → 0.\r\n */\r\nexport function getEnemyVulnerabilityTicks(enemy: {\r\n\tstate: string;\r\n\tcastingSpell?: string;\r\n\tcastProgress?: number;\r\n\tcastDuration?: number;\r\n\tgcdRemaining?: number;\r\n}): number\r\n{\r\n\tif (enemy.state === 'casting')\r\n\t{\r\n\t\tconst remainingCast = (enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\r\n\t\tif (enemy.castingSpell === 'missile')\r\n\t\t{\r\n\t\t\treturn remainingCast + GCD_DURATION;\r\n\t\t}\r\n\t\treturn remainingCast;\r\n\t}\r\n\tif (enemy.state === 'gcd_locked')\r\n\t{\r\n\t\treturn enemy.gcdRemaining ?? 0;\r\n\t}\r\n\treturn 0;\r\n}\r\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n} from '../shared.js';\nconst WALL_BUFFER = 60;\nconst WALL_PUSH = 30; // How hard to push away from walls\n\n/**\n * Homing AI that sometimes forgets what it's doing.\n */\nconst ChaoticHoming: MissileAIFunction = ({worldState, random}) => \n{\n\tconst enemy = worldState.enemies[0];\n\t// 80% chance to actually track, 20% chance to just fly straight\n\tif (random() < 0.8) \n\t{\n\t\treturn {turnToward: enemy?.position};\n\t}\n\treturn {};\n};\n\n/**\n * Bot: Hogger\n *\n * BEHAVIOR: The elite critter. Chaotic and unpredictable but genuinely dangerous.\n * Randomly varies missile configs each cast (damage 7-15, speed 3-8, random homing),\n * moves erratically but still somewhat toward/away from the enemy, shields when\n * in real danger, and blinks unpredictably. The randomness makes Hogger hard to\n * predict — you never know if the next missile will be a slow tracker or a fast\n * snipe. Unlike Critter's pure randomness, Hogger has enough combat awareness\n * to actually win fights.\n *\n * NAMING RATIONALE: In WoW, Hogger is the iconic level 11 elite gnoll in Elwynn\n * Forest who infamously kills unprepared lowbies. He's technically a basic mob\n * but hits way harder than expected. This bot is the Critter that learned to\n * fight — still chaotic, still a bit dumb, but capable of ending you if you\n * underestimate it. \"Hogger\" is one of WoW's most recognizable references and\n * perfectly captures \"deceptively dangerous chaos.\"\n *\n * STANDALONE — no tier progression. There's only one Hogger.\n */\nexport const Hogger: WizardFunction = ({state, random}) => \n{\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// Erratic movement: mostly toward/away from enemy with random strafe\n\tconst deltaX = enemy.position.x - state.position.x;\n\tconst deltaY = enemy.position.y - state.position.y;\n\tconst normalizedDistance = distance > 0 ? distance : 1;\n\n\t// Dodge if there's a real threat\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\n\tlet moveX: number;\n\tlet moveY: number;\n\n\tif (dodgeDirection) \n\t{\n\t\t// Actually dodge (Hogger isn't stupid)\n\t\tmoveX = dodgeDirection.x * 100;\n\t\tmoveY = dodgeDirection.y * 100;\n\t}\n\telse \n\t{\n\t\t// Random strafe component\n\t\tconst perpX = -deltaY / normalizedDistance;\n\t\tconst perpY = deltaX / normalizedDistance;\n\t\tconst strafeIntensity = random() * 80 - 40; // -40 to 40\n\n\t\t// Approach/retreat: prefer medium range (250-400), but sometimes just wander\n\t\tlet approachIntensity = 0;\n\t\tif (distance > 400) \n\t\t{\n\t\t\tapproachIntensity = 40 + random() * 30; // approach\n\t\t}\n\t\telse if (distance < 200) \n\t\t{\n\t\t\tapproachIntensity = -(30 + random() * 30); // retreat\n\t\t}\n\t\telse \n\t\t{\n\t\t\tapproachIntensity = random() * 60 - 30; // random walk\n\t\t}\n\n\t\tmoveX =\n\t\t\tperpX * strafeIntensity +\n (deltaX / normalizedDistance) * approachIntensity;\n\t\tmoveY =\n\t\t\tperpY * strafeIntensity +\n (deltaY / normalizedDistance) * approachIntensity;\n\n\t\t// Wall avoidance (Hogger doesn't like corners)\n\t\tif (state.position.x < WALL_BUFFER) moveX += WALL_PUSH;\n\t\tif (state.position.x > ARENA_SIZE - WALL_BUFFER) moveX -= WALL_PUSH;\n\t\tif (state.position.y < WALL_BUFFER) moveY += WALL_PUSH;\n\t\tif (state.position.y > ARENA_SIZE - WALL_BUFFER) moveY -= WALL_PUSH;\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\tconst move = {x: moveX, y: moveY};\n\n\t// === DEFENSE: Shield if in real danger ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > 120) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection\n\t) \n\t{\n\t\tif (urgentThreat.canBlockInTime && urgentThreat.ticksToStartShield <= 3) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t\telse if (!urgentThreat.canBlockInTime && state.blinkCooldown === 0) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === OFFENSE: Random blink (sometimes offensive, sometimes random) ===\n\tif (state.state === 'idle' && state.blinkCooldown === 0 && random() < 0.03) \n\t{\n\t\t// 60% chance blink toward enemy, 40% chance random blink\n\t\tif (random() < 0.6 && distance > 300) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: enemy.position},\n\t\t\t};\n\t\t}\n\t\telse \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'blink',\n\t\t\t\t\ttarget: {\n\t\t\t\t\t\tx: 100 + random() * 600,\n\t\t\t\t\t\ty: 100 + random() * 600,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === OFFENSE: Chaotic missiles ===\n\tif (state.state === 'idle' && distance < 500) \n\t{\n\t\t// Random missile config each time\n\t\tconst roll = random();\n\t\tlet damage: number;\n\t\tlet speed: number;\n\t\tlet turnRate: number;\n\t\tlet duration: number;\n\n\t\tif (roll < 0.3) \n\t\t{\n\t\t\t// Fast snipe\n\t\t\tdamage = 8 + Math.floor(random() * 5); // 8-12\n\t\t\tspeed = 6 + random() * 2; // 6-8\n\t\t\tturnRate = 0;\n\t\t\tduration = 80 + Math.floor(random() * 40); // 80-120\n\t\t}\n\t\telse if (roll < 0.7) \n\t\t{\n\t\t\t// Homing tracker\n\t\t\tdamage = 7 + Math.floor(random() * 5); // 7-11\n\t\t\tspeed = 3 + random() * 2; // 3-5\n\t\t\tturnRate = 1 + random() * 2; // 1-3\n\t\t\tduration = 150 + Math.floor(random() * 100); // 150-250\n\t\t}\n\t\telse \n\t\t{\n\t\t\t// Big slow bomb\n\t\t\tdamage = 12 + Math.floor(random() * 4); // 12-15\n\t\t\tspeed = 2 + random() * 2; // 2-4\n\t\t\tturnRate = 0.5 + random() * 1.5; // 0.5-2\n\t\t\tduration = 200 + Math.floor(random() * 100); // 200-300\n\t\t}\n\n\t\treturn {\n\t\t\tmove,\n\t\t\tstartCast: {\n\t\t\t\tspell: 'missile',\n\t\t\t\tconfig: {damage, speed, turnRate, duration},\n\t\t\t\tmissileAI: ChaoticHoming,\n\t\t\t\tdirection:\n angleTo(state.position, enemy.position) + (random() * 20 - 10), // ±10° aim wobble\n\t\t\t},\n\t\t};\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {fitMissileToBudget} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetBlinkToCenter,\n} from '../shared.js';\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\n/**\n * Bot: Doombringer\n *\n * BEHAVIOR: Fires a single maximum-damage homing missile with infinite budget.\n * No damage cap — goes for the biggest possible hit. Exists as a benchmark to\n * demonstrate why lower-damage + shield play is superior. Has basic shield\n * defense but no sophisticated tactics. One fat cast, one fat hit.\n *\n * STANDALONE — no tier progression. Benchmark/test bot.\n */\nexport const Doombringer: WizardFunction = ({state}) => \n{\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > 150) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Shield undodgeable threats ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection\n\t) \n\t{\n\t\tif (urgentThreat.canBlockInTime && urgentThreat.ticksToStartShield <= 3) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t\telse if (!urgentThreat.canBlockInTime && state.blinkCooldown === 0) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === OFFENSE: Fire max-damage homing missile (no damage cap) ===\n\tif (state.state === 'idle') \n\t{\n\t\tconst config = fitMissileToBudget(Infinity, distance, {minTurnRate: 0.5});\n\t\tif (config) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {move: {x: 0, y: 0}};\n};\n","/**\r\n * VIBEMANCER - PARAMETER RUNTIME\r\n *\r\n * Provides the useParam() hook for bots to declare tunable parameters,\r\n * and the infrastructure for the optimizer to inject/discover parameter values.\r\n *\r\n * Design: Module-level state (JS is single-threaded, no race conditions).\r\n * The optimizer sets param values before running a bot, and clears them after.\r\n * During discovery, all useParam calls are recorded.\r\n *\r\n * ## useParam API\r\n *\r\n * ```typescript\r\n * // Basic: just a value, no optimizer config\r\n * const damage = useParam('damage', 15);\r\n *\r\n * // With range: optimizer searches value ± range (sliding window)\r\n * const distance = useParam('distance', 350, {range: 150, min: 0});\r\n *\r\n * // With fixed min/max: optimizer searches [min, max] (fixed bounds)\r\n * const damage = useParam('damage', 15, {min: 5, max: 25});\r\n *\r\n * // With all: range defines search radius, min/max clamp it\r\n * const fraction = useParam('fraction', 0.25, {range: 0.2, min: 0, max: 1});\r\n *\r\n * // With custom step count: optimizer tests 20 values instead of default 10\r\n * const distance = useParam('distance', 500, {range: 200, min: 0, steps: 20});\r\n * ```\r\n *\r\n * - **Arg 1** `name` — unique parameter name (must be consistent across ticks)\r\n * - **Arg 2** `value` — the actual value used in gameplay. This is what your bot\r\n * uses during matches. The optimizer script automatically updates this value.\r\n * - **Arg 3** `config` (optional) — optimizer search configuration:\r\n * - `range` — search radius: optimizer checks `value ± range`. The auto-optimizer\r\n * rewrites `value` after each run, so the search window slides automatically.\r\n * - `min` / `max` — hard constraints (e.g., distance ≥ 0, fraction ≤ 1).\r\n * When `range` is omitted, these define fixed search bounds (old-style).\r\n * - `steps` — how many evenly-spaced values the optimizer tests per pass (default: 10).\r\n * - `substeps` — steps to use in refinement passes (passes 2+). Set to 0 to freeze\r\n * after pass 1 (ideal for boolean params). Defaults to `steps` if not specified.\r\n * - At least `range` or both `min` + `max` must be provided.\r\n *\r\n * Without optimizer config, useParam simply returns `value` every tick.\r\n * With optimizer config, the offline optimizer script can override the value during search.\r\n *\r\n * ## Rules of Hooks\r\n * useParam follows the same rules as useState/useEffect/etc:\r\n * - Must be called at the top level of your bot function (not inside conditionals)\r\n * - Must be called in the same order every tick\r\n * - Violations are detected and throw errors\r\n */\r\n\r\nimport type {WizardFunction} from '../types.js';\r\nimport {validateHookCall} from './hooks-runtime.js';\r\n\r\n/**\r\n * Declaration of a tunable parameter, as discovered by the optimizer.\r\n */\r\nexport interface ParamDeclaration\r\n{\r\n\tname: string;\r\n\tvalue: number;\r\n\trange?: number;\r\n\tmin?: number;\r\n\tmax?: number;\r\n\tsteps: number;\r\n\t/** Steps to use in refinement passes (passes 2+). 0 = freeze after pass 1. Defaults to `steps`. */\r\n\tsubsteps?: number;\r\n}\r\n\r\n// --- Module-level state ---\r\n\r\n/** Current parameter values injected by the optimizer. null = use defaults. */\r\nlet paramValues: Map<string, number> | null = null;\r\n\r\n/** Params discovered during discovery mode. */\r\nconst discoveredParams = new Map<string, ParamDeclaration>();\r\n\r\n/** Whether we're in discovery mode (collecting param declarations). */\r\nlet discovering = false;\r\n\r\n// --- Public API for bot authors ---\r\n\r\n/**\r\n * Declare a tunable parameter. Returns the current value (optimizer-injected or the provided value).\r\n *\r\n * @param name - Unique parameter name (consistent across ticks)\r\n * @param value - The gameplay value. The auto-optimizer rewrites this in source code.\r\n * @param config - Optional optimizer search configuration\r\n * @returns The optimizer-injected value during optimization, or `value` during normal play\r\n *\r\n * @example\r\n * // Simple: no optimizer config\r\n * const damage = useParam('damage', 15);\r\n *\r\n * // With range: optimizer searches value ± range (sliding window)\r\n * const distance = useParam('distance', 350, {range: 150, min: 0});\r\n *\r\n * // With fixed min/max: optimizer searches [min, max]\r\n * const damage = useParam('damage', 15, {min: 5, max: 25});\r\n */\r\nexport function useParam(name: string, value: number, config?: {\r\n\trange?: number;\r\n\tmin?: number;\r\n\tmax?: number;\r\n\tsteps?: number;\r\n\tsubsteps?: number;\r\n}): number\r\n{\r\n\t// Participate in hook index validation (same rules as useState, useEffect, etc.)\r\n\tvalidateHookCall('useParam');\r\n\r\n\t// Register for discovery (only if optimizer config is provided)\r\n\tif (discovering && config)\r\n\t{\r\n\t\tdiscoveredParams.set(name, {\r\n\t\t\tname,\r\n\t\t\tvalue,\r\n\t\t\trange: config.range,\r\n\t\t\tmin: config.min,\r\n\t\t\tmax: config.max,\r\n\t\t\tsteps: config.steps ?? 10,\r\n\t\t\tsubsteps: config.substeps,\r\n\t\t});\r\n\t}\r\n\r\n\t// Return optimizer value or the provided value\r\n\treturn paramValues?.get(name) ?? value;\r\n}\r\n\r\n// --- Optimizer API ---\r\n\r\n/**\r\n * Inject parameter values for the next bot execution.\r\n * The wrapped bot will read these values via useParam().\r\n */\r\nexport function setParamValues(values: Record<string, number>): void\r\n{\r\n\tparamValues = new Map(Object.entries(values));\r\n}\r\n\r\n/**\r\n * Clear injected parameter values. useParam() will return its provided value.\r\n */\r\nexport function clearParamValues(): void\r\n{\r\n\tparamValues = null;\r\n}\r\n\r\n/**\r\n * Start discovery mode. All subsequent useParam() calls with optimizer config\r\n * will register their declarations.\r\n */\r\nexport function startDiscovery(): void\r\n{\r\n\tdiscovering = true;\r\n\tdiscoveredParams.clear();\r\n}\r\n\r\n/**\r\n * Stop discovery mode and return all discovered parameter declarations.\r\n */\r\nexport function stopDiscovery(): ParamDeclaration[]\r\n{\r\n\tdiscovering = false;\r\n\tconst result = [...discoveredParams.values()];\r\n\tdiscoveredParams.clear();\r\n\treturn result;\r\n}\r\n\r\n/**\r\n * Wrap a bot function to inject specific parameter values.\r\n * The returned function sets params before calling the bot and clears them after.\r\n */\r\nexport function wrapWithParams(bot: WizardFunction, params: Record<string, number>): WizardFunction\r\n{\r\n\treturn (props) =>\r\n\t{\r\n\t\tsetParamValues(params);\r\n\t\ttry\r\n\t\t{\r\n\t\t\treturn bot(props);\r\n\t\t}\r\n\t\tfinally\r\n\t\t{\r\n\t\t\tclearParamValues();\r\n\t\t}\r\n\t};\r\n}\r\n","import {WizardFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {getMissileCastTime} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetBlinkToCenter,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\n/**\n * Bot: Turtle\n *\n * BEHAVIOR: Stationary tank with a simple defense-first loop: shield → missile\n * → wait → shield → repeat. Never moves. Always prioritizes having shield\n * ready over dealing damage.\n *\n * COMBAT LOOP:\n * 1. Incoming missile? → Shield immediately (cancel anything else)\n * 2. Hold shield until missile has actually impacted (don't cancel early!)\n * 3. Cancel shield → GCD → Fire one missile → GCD → idle\n * 4. Wait idle for next threat\n * 5. Goto 1\n *\n * CRITICAL: Does NOT cancel shield until incoming missiles have impacted.\n * Shield must be held through impact to actually block damage.\n *\n * Uses damage 10, speed 5, turnRate 1, duration 180 (~81 tick cast).\n * Emergency blinks toward center when caught in GCD with unavoidable hit.\n *\n * NAMING RATIONALE: \"Turtling\" is the universal gaming term for extreme\n * defensive play. Sits still and blocks everything.\n *\n * PROGRESSION LINE: Turtle → Sentinel → Golem\n * - Turtle (tier 1): Stationary, fixed missiles, always-shield-first\n * - Sentinel (tier 2): Stationary, adaptive missile fitting (fitMissileToBudget)\n * - Golem (tier 3): Future — immovable fortress, perfect timing, punishing hits\n *\n * TIER: 1 (base)\n */\n\n// Homing missile: damage 12, speed 5, turnRate 1, duration 180.\n// Range = 5 × 180 = 900 units.\nconst MISSILE_CONFIG = {damage: 12, speed: 5, turnRate: 1, duration: 180};\n\nexport const Turtle: WizardFunction = ({state}) => \n{\n\tconst shieldBuffer = useParam('shieldBuffer', 3.1, {\n\t\trange: 5,\n\t\tmin: 0,\n\t\tsteps: 5,\n\t});\n\tconst shieldHoldTicks = useParam('shieldHoldTicks', 5, {\n\t\trange: 5,\n\t\tmin: 0,\n\t\tsteps: 5,\n\t});\n\tconst ticksToReshield = SHIELD_CAST_TIME + shieldBuffer;\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) return {move: {x: 0, y: 0}};\n\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\tconst shouldStartShield =\n\t\turgentThreat !== null &&\n urgentThreat.ticksToImpact <= SHIELD_CAST_TIME + shieldBuffer;\n\n\t// === CHANNELING SHIELD ===\n\t// Hold shield until the imminent missile(s) have actually impacted.\n\tif (state.state === 'channeling' && state.channelingSpell === 'shield') \n\t{\n\t\t// Don't cancel while a missile is about to hit\n\t\tconst imminentThreat = threats.find(\n\t\t\t(t) => t.willHit && t.ticksToImpact <= shieldHoldTicks,\n\t\t);\n\t\tif (imminentThreat) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}};\n\t\t}\n\n\t\t// Safe to cancel if no next threat, or next threat is far enough to reshield\n\t\tconst nextThreat = threats.find((t) => t.willHit);\n\t\tif (!nextThreat || nextThreat.ticksToImpact > ticksToReshield) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\t// Next threat too soon — keep channeling\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === CASTING SHIELD: let it finish ===\n\tif (state.state === 'casting' && state.castingSpell === 'shield') \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === NEED TO SHIELD NOW: cancel anything else ===\n\tif (shouldStartShield) \n\t{\n\t\tif (state.state === 'casting') \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\tif (state.state === 'idle') \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, startCast: {spell: 'shield'}};\n\t\t}\n\t}\n\n\t// === EMERGENCY BLINK: missile too close for shield, blink available ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n urgentThreat.ticksToImpact < SHIELD_CAST_TIME + shieldBuffer &&\n urgentThreat.ticksToImpact >= 10 &&\n state.blinkCooldown === 0\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t};\n\t}\n\n\t// === ATTACK: one missile if safe window exists ===\n\t// Dynamic safe window: actual cast time (with warmup) + GCD + shield + buffer\n\tconst castTime = getMissileCastTime(MISSILE_CONFIG, state.lastMissileConfig);\n\tconst safeWindow = castTime + GCD_DURATION + SHIELD_CAST_TIME + shieldBuffer;\n\tif (\n\t\tstate.state === 'idle' &&\n (!urgentThreat || urgentThreat.ticksToImpact > safeWindow)\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {\n\t\t\t\tspell: 'missile',\n\t\t\t\tconfig: MISSILE_CONFIG,\n\t\t\t\tmissileAI: ({worldState}) => ({\n\t\t\t\t\tturnToward: worldState.enemies[0]?.position,\n\t\t\t\t}),\n\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t},\n\t\t};\n\t}\n\n\treturn {move: {x: 0, y: 0}};\n};\n","import {WizardFunction, MissileActions} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {getMissileCastTime} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetBlinkToCenter,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\n/**\n * Bot: Sentinel\n *\n * BEHAVIOR: Stationary tank with last-moment shielding AND two-tier offense.\n * Like Turtle, never moves and shields at the last moment. Unlike Turtle,\n * fires bigger missiles (damage 20) when the safe window is large enough,\n * falling back to Turtle's fast missile (damage 12) when pressed.\n *\n * PROGRESSION LINE: Turtle → Sentinel → Golem\n * - Turtle (tier 1): Stationary, fixed missiles, reactive shield timing\n * - Sentinel (tier 2): Stationary, two-tier offense (big + fast missiles)\n * - Golem (tier 3): Immovable fortress, perfect shield timing\n *\n * TIER: 2 (enhanced Turtle)\n */\nexport const Sentinel: WizardFunction = ({state}) =>\n{\n\tconst shieldHoldTicks = useParam('shieldHoldTicks', 2, {\n\t\trange: 5,\n\t\tmin: 0,\n\t\tmax: 15,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\tconst SHIELD_BUFFER = 3;\n\tconst shouldStartShield =\n\t\turgentThreat !== null &&\n urgentThreat.ticksToImpact <= SHIELD_CAST_TIME + SHIELD_BUFFER;\n\n\tconst TICKS_TO_RESHIELD = SHIELD_CAST_TIME + SHIELD_BUFFER;\n\tconst nextThreat = threats.filter((t) => t.willHit)[1] ?? null;\n\tconst safeToCancel =\n\t\t!nextThreat || nextThreat.ticksToImpact > TICKS_TO_RESHIELD;\n\n\tif (state.state === 'channeling' && state.channelingSpell === 'shield')\n\t{\n\t\tconst imminentThreat = threats.find(\n\t\t\t(t) => t.willHit && t.ticksToImpact <= shieldHoldTicks,\n\t\t);\n\t\tif (imminentThreat) return {move: {x: 0, y: 0}};\n\t\tif (safeToCancel) return {move: {x: 0, y: 0}, cancel: true};\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tif (state.state === 'casting' && state.castingSpell === 'shield')\n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tif (shouldStartShield)\n\t{\n\t\tif (state.state === 'casting') return {move: {x: 0, y: 0}, cancel: true};\n\t\tif (state.state === 'idle')\n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, startCast: {spell: 'shield'}};\n\t\t}\n\t}\n\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n urgentThreat.ticksToImpact < SHIELD_CAST_TIME + SHIELD_BUFFER &&\n urgentThreat.ticksToImpact >= 10 &&\n state.blinkCooldown === 0\n\t)\n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t};\n\t}\n\n\t// Two-tier offense: big missile when safe, fast missile otherwise.\n\t// Same speed/turnRate/duration → no warmup switching penalty.\n\tif (state.state === 'idle' && enemy)\n\t{\n\t\tconst FAST_MISSILE = {damage: 12, speed: 5, turnRate: 1, duration: 180};\n\t\tconst BIG_MISSILE = {damage: 20, speed: 5, turnRate: 1, duration: 180};\n\n\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\tconst bigCast = getMissileCastTime(BIG_MISSILE, state.lastMissileConfig);\n\t\tconst bigWindow = bigCast + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tif (!urgentThreat || nextThreatTime > bigWindow)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: BIG_MISSILE,\n\t\t\t\t\tmissileAI: ({worldState}): MissileActions => ({\n\t\t\t\t\t\tturnToward: worldState.enemies[0]?.position,\n\t\t\t\t\t}),\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst fastCast = getMissileCastTime(FAST_MISSILE, state.lastMissileConfig);\n\t\tconst fastWindow = fastCast + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tif (!urgentThreat || nextThreatTime > fastWindow)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: FAST_MISSILE,\n\t\t\t\t\tmissileAI: ({worldState}): MissileActions => ({\n\t\t\t\t\t\tturnToward: worldState.enemies[0]?.position,\n\t\t\t\t\t}),\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {move: {x: 0, y: 0}};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {\n\tGCD_DURATION,\n\tSHIELD_CAST_TIME,\n\tBLINK_CAST_TIME,\n} from '../../rules.js';\nimport {getMissileCastTime, getLeadPosition} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetBlinkToCenter,\n\tfitMissileToBudget,\n\tisSafeToAttack,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n\tgetEnemyVulnerabilityTicks,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst SHIELD_BUFFER = 3;\nconst SHIELD_HOLD_TICKS = 5;\n\n/**\n * Standard homing missile AI — tracks enemy position.\n */\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\n/**\n * Straight missile AI — no homing, flies in initial direction.\n */\nconst StraightAI: MissileAIFunction = () => ({});\n\n/**\n * Bot: Golem\n *\n * BEHAVIOR: Stationary fortress with perfect shield timing and devastating\n * counterattacks during enemy vulnerability windows. Reads enemy cast/GCD\n * state to time punish missiles that land when the enemy can't shield.\n * Handles multi-missile volleys by holding shield through consecutive impacts.\n * Uses progressive cast-cancel thresholds for optimal damage trading.\n *\n * KEY IMPROVEMENTS OVER SENTINEL:\n * - Counterattack punish: fires during enemy cast/GCD recovery\n * - Multi-threat volley awareness: holds shield through consecutive hits\n * - Progressive cast-cancel: graduated damage thresholds\n * - Perfect shield timing: uses ticksToStartShield precisely\n *\n * PROGRESSION LINE: Turtle → Sentinel → Golem\n * TIER: 3 (elite Defensive line)\n */\nexport const Golem: WizardFunction = ({state}) => \n{\n\t// Positive turn rate ensures homing missiles that hit moving targets.\n\t// Hardcoded — optimizer incorrectly pushes this negative for stationary matchups.\n\tconst minTurnRate = 0.5;\n\tconst preemptiveShieldRange = useParam('preemptiveShieldRange', 150, {\n\t\trange: 85,\n\t\tmin: 0,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 100, {\n\t\trange: 75,\n\t\tmin: 10,\n\t});\n\tconst punishMinVulnerability = useParam('punishMinVulnerability', 310, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tsteps: 5,\n\t});\n\tconst preferBlinkDodge =\n\t\tuseParam('preferBlinkDodge', 1, {min: 0, max: 1, steps: 2}) >= 0.5;\n\tconst cancelHeavyDmg = useParam('cancelHeavyDmg', 14, {\n\t\trange: 15,\n\t\tmin: 5,\n\t\tmax: 40,\n\t\tsteps: 5,\n\t});\n\tconst cancelMediumDmg = useParam('cancelMediumDmg', 12, {\n\t\trange: 10,\n\t\tmin: 3,\n\t\tmax: 25,\n\t\tsteps: 5,\n\t});\n\tconst cancelCastRatio = useParam('cancelCastRatio', 0.8, {\n\t\trange: 0.4,\n\t\tmin: 0.3,\n\t\tmax: 1.0,\n\t\tsteps: 5,\n\t});\n\tconst cancelLightDmg = useParam('cancelLightDmg', 8, {\n\t\trange: 5,\n\t\tmin: 1,\n\t\tmax: 15,\n\t\tsteps: 5,\n\t});\n\tconst cancelEarlyCastRatio = useParam('cancelEarlyCastRatio', 0.7, {\n\t\trange: 0.3,\n\t\tmin: 0.0,\n\t\tmax: 0.7,\n\t\tsteps: 5,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 50, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst preemptiveShieldBuffer = useParam('preemptiveShieldBuffer', 11.3, {\n\t\trange: 5,\n\t\tmin: 0,\n\t\tmax: 15,\n\t\tsteps: 5,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 9.5, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\tconst hittingThreats = threats.filter((t) => t.willHit);\n\n\t// === STEP 1: CHANNELING — hold through volleys, cancel when safe ===\n\tif (state.state === 'channeling' && state.channelingSpell === 'shield') \n\t{\n\t\t// Hold if any missile is about to hit\n\t\tconst imminentThreat = hittingThreats.find(\n\t\t\t(t) => t.ticksToImpact <= SHIELD_HOLD_TICKS,\n\t\t);\n\t\tif (imminentThreat) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}};\n\t\t}\n\n\t\t// Hold if next threat will arrive before we can reshield\n\t\tconst nextHitting = hittingThreats[0];\n\t\tconst ticksToReshield = SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tif (nextHitting && nextHitting.ticksToImpact <= ticksToReshield) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}};\n\t\t}\n\n\t\t// Cancel shield if no threat or threat is far enough\n\t\tif (!nextHitting || nextHitting.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// If casting shield, let it finish\n\tif (state.state === 'casting' && state.castingSpell === 'shield') \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === STEP 2: PROGRESSIVE CAST-CANCEL — evaluate damage tradeoff ===\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat\n\t) \n\t{\n\t\t// Sentinel is stationary — can't dodge. Must always consider canceling.\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tconst defenseTime =\n\t\t\tstate.blinkCooldown === 0\n\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tconst willArriveInWindow =\n\t\t\turgentThreat.ticksToImpact <= remainingCast + GCD_DURATION + defenseTime;\n\n\t\tif (willArriveInWindow) \n\t\t{\n\t\t\tconst incomingDamage = urgentThreat.projectile.damage;\n\t\t\tconst castRatio = (state.castProgress ?? 0) / (state.castDuration ?? 1);\n\n\t\t\t// Always cancel for lethal or heavy damage\n\t\t\tif (incomingDamage >= state.health || incomingDamage >= cancelHeavyDmg) \n\t\t\t{\n\t\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t\t}\n\t\t\t// Significant damage + early/mid cast: cancel\n\t\t\tif (incomingDamage >= cancelMediumDmg && castRatio < cancelCastRatio) \n\t\t\t{\n\t\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t\t}\n\t\t\t// Medium damage + very early cast: cancel\n\t\t\tif (\n\t\t\t\tincomingDamage >= cancelLightDmg &&\n castRatio < cancelEarlyCastRatio\n\t\t\t) \n\t\t\t{\n\t\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === STEP 3: BLINK-DODGE — 100% damage avoidance (optimizer-controlled) ===\n\tif (\n\t\tpreferBlinkDodge &&\n state.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n urgentThreat.ticksToImpact >= BLINK_CAST_TIME &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t};\n\t}\n\n\t// === STEP 3b: SHIELD — fallback when blink unavailable (90% block) ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= SHIELD_BUFFER\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'shield'},\n\t\t};\n\t}\n\n\t// === STEP 4: PREEMPTIVE SHIELD — enemy casting at close range ===\n\tif (\n\t\tstate.state === 'idle' &&\n enemy &&\n enemy.state === 'casting' &&\n enemy.castingSpell === 'missile'\n\t) \n\t{\n\t\tconst distance = distanceTo(state.position, enemy.position);\n\t\tif (distance < preemptiveShieldRange) \n\t\t{\n\t\t\tconst remainingCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTicks = distance / flightTimeDivisor;\n\t\t\tconst ticksUntilHit = remainingCast + estimatedFlightTicks;\n\t\t\tif (\n\t\t\t\tticksUntilHit <\n SHIELD_CAST_TIME + SHIELD_BUFFER + preemptiveShieldBuffer\n\t\t\t) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === STEP 5: OFFENSE — punish mode + standard counterattack ===\n\tif (state.state === 'idle' && enemy) \n\t{\n\t\tconst offenseDefenseTime =\n\t\t\tstate.blinkCooldown === 0\n\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tconst distance = distanceTo(state.position, enemy.position);\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst vulnerabilityTicks = getEnemyVulnerabilityTicks(enemy);\n\n\t\t// --- PUNISH MODE: exploit enemy cast/GCD vulnerability ---\n\t\tif (vulnerabilityTicks >= punishMinVulnerability) \n\t\t{\n\t\t\tconst flightTimeEstimate = distance / flightTimeDivisor;\n\t\t\tconst punishBudget = vulnerabilityTicks - flightTimeEstimate;\n\t\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\t\tconst safeBudget = nextThreatTime - GCD_DURATION - offenseDefenseTime;\n\t\t\tconst effectiveBudget = Math.min(\n\t\t\t\tpunishBudget,\n\t\t\t\tsafeBudget,\n\t\t\t\tMAX_CAST_BUDGET,\n\t\t\t);\n\n\t\t\tif (effectiveBudget > 0) \n\t\t\t{\n\t\t\t\tconst config = fitMissileToBudget(effectiveBudget, distance, {\n\t\t\t\t\tminTurnRate: 0,\n\t\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t\t});\n\n\t\t\t\tif (config && config.damage >= MIN_USEFUL_DAMAGE) \n\t\t\t\t{\n\t\t\t\t\tconst castTime = getMissileCastTime(config, state.lastMissileConfig);\n\t\t\t\t\tconst actualFlight = distance / config.speed;\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tcastTime + actualFlight < vulnerabilityTicks &&\n isSafeToAttack(threats, castTime)\n\t\t\t\t\t) \n\t\t\t\t\t{\n\t\t\t\t\t\tconst aimTarget =\n\t\t\t\t\t\t\tconfig.turnRate > 0\n\t\t\t\t\t\t\t\t? enemy.position\n\t\t\t\t\t\t\t\t: getLeadPosition(\n\t\t\t\t\t\t\t\t\tenemy.position,\n\t\t\t\t\t\t\t\t\tenemy.velocity,\n\t\t\t\t\t\t\t\t\tconfig.speed,\n\t\t\t\t\t\t\t\t\tstate.position,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\t\t\tstartCast: {\n\t\t\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\tmissileAI: config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\t\t\t\tdirection: angleTo(state.position, aimTarget),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// --- STANDARD MODE: adaptive homing in safe windows ---\n\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\tconst safeBudget = nextThreatTime - GCD_DURATION - offenseDefenseTime;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (config && config.damage >= Math.min(MIN_USEFUL_DAMAGE, enemyHP)) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {move: {x: 0, y: 0}};\n};\n","import {WizardFunction} from '../../types.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToDecreaseDistance,\n\tshouldForceShield,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 50;\n\n// Melee stab: 20 damage, speed 8, 3-hit kill.\nconst STAB_CONFIG = {damage: 20, speed: 8, turnRate: 0, duration: 10};\n\n/**\n * Aggressive movement: dodge missiles using threat analysis, close distance, light strafe.\n */\nfunction aggressiveMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\t// When actively dodging, commit fully\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\t// Light strafe + aggressive closing\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\t// Always close distance aggressively (melee wants to be in the enemy's face)\n\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Shadowblade\n *\n * BEHAVIOR: Melee assassin. Blinks to the enemy, then lands devastating point-blank\n * stab attacks (15 damage, 30u range, ~60 tick cast = 2-hit kill). Runs directly\n * at the enemy with minimal strafe, shields undodgeable threats. The entire\n * strategy is: get close, stab, kill. Simple and brutal.\n *\n * PROGRESSION LINE: Shadowblade → Nightblade → Voidblade\n * - Shadowblade (tier 1): Offensive blink, melee stabs, basic shield\n * - Nightblade (tier 2): + missile-aware blinks, adaptive stabs, timed defense\n * - Voidblade (tier 3): Future — perfect assassination timing, inescapable engages\n *\n * TIER: 1 (base)\n */\nexport const Shadowblade: WizardFunction = ({state}) => \n{\n\tconst stabRange = useParam('stabRange', 76, {\n\t\trange: 30,\n\t\tmin: 20,\n\t\tmax: 80,\n\t\tsteps: 5,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 252, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 55, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 61, {\n\t\trange: 20,\n\t\tmin: 50,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 23, {\n\t\trange: 30,\n\t\tmin: 15,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// Cancel shield if no longer needed\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tif (state.state === 'idle') \n\t{\n\t\t// === BLINK: Dodge missile + close gap simultaneously (first priority) ===\n\t\t// Wait until the missile is close (<=40 ticks) so the blink actually dodges it.\n\t\t// Blinking too early means the missile can still re-track. Too late (<10) and the\n\t\t// blink cast won't complete in time.\n\t\tif (\n\t\t\turgentThreat &&\n state.blinkCooldown === 0 &&\n distance > stabRange &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t\t) \n\t\t{\n\t\t\tconst blinkTarget =\n\t\t\t\tgetBlinkToDecreaseDistance(\n\t\t\t\t\tstate.position,\n\t\t\t\t\tenemy.position,\n\t\t\t\t\tstate.projectiles,\n\t\t\t\t) ?? enemy.position;\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t};\n\t\t}\n\n\t\t// Pre-stab shield: already at melee range, survival beats landing another stab.\n\t\t// Gated to stab range so out-of-range threats wait for the blink window.\n\t\tif (\n\t\t\turgentThreat &&\n distance < stabRange &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3\n\t\t)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\n\t\t// Melee stab when close enough (offense over defense at melee range)\n\t\tif (distance < stabRange)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: STAB_CONFIG,\n\t\t\t\t\tmissileAI: () => ({}), // Non-homing, point blank\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// === DEFENSE: Shield undodgeable threats (only when blink can't handle it) ===\n\t\t// Don't shield if blink is available — wait for the blink window instead.\n\t\tif (\n\t\t\turgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3 &&\n (state.blinkCooldown > 0 || urgentThreat.ticksToImpact < 10)\n\t\t)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// Aggressive movement — always close distance\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = aggressiveMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t);\n\treturn {move};\n};\n","import {WizardFunction} from '../../types.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToDecreaseDistance,\n\tshouldForceShield,\n\tfitMissileToBudget,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 50;\nconst SHIELD_BUFFER = 3;\n\n/**\n * Aggressive movement: dodge missiles, close distance, light strafe.\n */\nfunction aggressiveMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\t// When actively dodging, commit fully\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\t// Light strafe + aggressive closing\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\t// Always close distance aggressively\n\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Nightblade\n *\n * BEHAVIOR: Enhanced melee assassin. Same aggressive engagement as Shadowblade —\n * blinks directly to the enemy and stabs for 15 damage (2-hit kill). The tier 2\n * upgrade is PREEMPTIVE DEFENSE: Nightblade watches the enemy's cast bar and\n * shields before a point-blank missile is even launched. At melee range, missiles\n * arrive almost instantly after launch — too fast to react. Nightblade anticipates\n * the threat. Also has emergency blink and proper channeling management.\n *\n * PROGRESSION LINE: Shadowblade → Nightblade → Voidblade\n * - Shadowblade (tier 1): Offensive blink, melee stabs, basic shield (reactive only)\n * - Nightblade (tier 2): + preemptive shield vs enemy casts, emergency blink\n * - Voidblade (tier 3): Future — perfect assassination timing, inescapable engages\n *\n * TIER: 2 (enhanced Shadowblade)\n */\nexport const Nightblade: WizardFunction = ({state}) => \n{\n\tconst stabRange = useParam('stabRange', 263, {range: 38, min: 0, steps: 5});\n\tconst stabRangeVsCasting = stabRange + 20;\n\tconst preemptiveShieldRange = useParam('preemptiveShieldRange', 261, {\n\t\trange: 85,\n\t\tmin: 0,\n\t\tsteps: 7,\n\t});\n\tconst acceptHitFraction = useParam('acceptHitFraction', 0, {\n\t\trange: 0.2,\n\t\tmin: 0,\n\t\tmax: 1,\n\t\tsteps: 5,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', -11.4, {range: 5.25, steps: 5});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 300, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 22, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 92, {\n\t\trange: 20,\n\t\tmin: 50,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 50, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 19, {\n\t\trange: 30,\n\t\tmin: 15,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 12, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst enemyFiringSoonThreshold = useParam('enemyFiringSoonThreshold', 50, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst meleeAbortRange = useParam('meleeAbortRange', 200, {\n\t\trange: 50,\n\t\tmin: 50,\n\t\tmax: 200,\n\t\tsteps: 7,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = aggressiveMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t);\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\t// Keep shield up if enemy is about to fire at close range (preemptive shield scenario)\n\t\tconst enemyFiringSoon =\n\t\t\tdistance < preemptiveShieldRange &&\n enemy.state === 'casting' &&\n enemy.castingSpell === 'missile' &&\n (enemy.castDuration ?? 0) -\n (enemy.castProgress ?? 0) +\n distance / flightTimeDivisor <\n enemyFiringSoonThreshold;\n\t\tif (\n\t\t\t(!urgentThreat && !enemyFiringSoon) ||\n (urgentThreat && urgentThreat.ticksToImpact > shieldCancelThreshold)\n\t\t) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast for LETHAL incoming damage ===\n\t// DPS trade: we accepted the hit when we started casting. Only cancel to survive.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER\n\t\t) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === CANCEL: Abort melee cast if target escaped (non-homing missile won't reach) ===\n\t// Melee missiles are non-homing with short range (~63u). If the enemy blinked away,\n\t// continuing the cast is wasted time while we're vulnerable.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n distance > meleeAbortRange\n\t) \n\t{\n\t\treturn {move, cancel: true};\n\t}\n\n\t// === BLINK: Dodge missile + close gap simultaneously (first priority) ===\n\t// Blink both dodges the incoming missile AND closes distance — better than shielding.\n\t// Only shield when blink is on cooldown.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n distance > stabRange &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\tconst blinkTarget =\n\t\t\tgetBlinkToDecreaseDistance(\n\t\t\t\tstate.position,\n\t\t\t\tenemy.position,\n\t\t\t\tstate.projectiles,\n\t\t\t) ?? enemy.position;\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t};\n\t}\n\n\t// === DEFENSE: Shield lethal/high-damage undodgeable threats — survival over offense ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&\n urgentThreat.projectile.damage >= state.health &&\n urgentThreat.canBlockInTime\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'shield'},\n\t\t};\n\t}\n\n\t// === OFFENSE: Budget-based melee stab when close enough ===\n\t// Tier 2 upgrade: fits the highest-damage stab into the available time window\n\t// before needing to shield. Also stabs from farther when enemy is casting/channeling\n\t// (they move at 0.5 or 0 speed, so the cast delay doesn't let them dodge).\n\tconst effectiveStabRange =\n\t\tenemy.state === 'casting' || enemy.state === 'channeling'\n\t\t\t? stabRangeVsCasting\n\t\t\t: stabRange;\n\t// Skip offense when a lethal missile could reach us during cast+GCD — dodge at full speed instead\n\tconst myProjectileIds = new Set(state.myProjectiles.map((p) => p.id));\n\tconst hasNearbyLethalMissile = state.projectiles.some(\n\t\t(p) =>\n\t\t\t!myProjectileIds.has(p.id) &&\n p.damage >= state.health &&\n distanceTo(state.position, p.position) / p.speed <\n MAX_CAST_BUDGET + GCD_DURATION,\n\t);\n\tif (\n\t\tstate.state === 'idle' &&\n distance < effectiveStabRange &&\n !hasNearbyLethalMissile\n\t) \n\t{\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Account for enemy's active cast — their missile will arrive after cast completes + flight time\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTime = distance / flightTimeDivisor;\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t);\n\t\t}\n\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tlet config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\t// If budget too small for useful stab, fire bigger and accept the hit\n\t\t// Only accept small hits (< 25% HP) — don't trade away large chunks of health\n\t\t// Never accept when enemy is actively casting (their missile WILL arrive during our long cast)\n\t\tconst enemyCasting =\n\t\t\tenemy.state === 'casting' && enemy.castingSpell === 'missile';\n\t\tif (\n\t\t\t(!config || config.damage < minDmg) &&\n !enemyCasting &&\n (!urgentThreat ||\n urgentThreat.projectile.damage < state.health * acceptHitFraction)\n\t\t) \n\t\t{\n\t\t\tconfig = fitMissileToBudget(MAX_CAST_BUDGET, distance, {\n\t\t\t\tminTurnRate,\n\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t});\n\t\t}\n\n\t\tif (config) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: () => ({}),\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats (only when blink can't handle it) ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (\n\t\t\turgentThreat.canBlockInTime &&\n (state.blinkCooldown > 0 || urgentThreat.ticksToImpact < 10)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === DEFENSE: Preemptive shield when enemy is casting a missile at close range ===\n\t// At melee range, missiles arrive almost instantly after launch — too fast to react.\n\t// Shield BEFORE the missile launches. Trigger when shield will finish BEFORE missile hits.\n\tif (\n\t\tstate.state === 'idle' &&\n distance < preemptiveShieldRange &&\n enemy.state === 'casting' &&\n enemy.castingSpell === 'missile'\n\t) \n\t{\n\t\tconst remainingCast = (enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\tconst estimatedFlightTicks = distance / flightTimeDivisor;\n\t\tconst ticksUntilHit = remainingCast + estimatedFlightTicks;\n\t\t// Shield when hit is close enough that we MUST start now, but far enough that\n\t\t// the shield finishes in time. The window: SHIELD_CAST_TIME <= ticksUntilHit < SHIELD_CAST_TIME+15\n\t\tif (\n\t\t\tticksUntilHit >= SHIELD_CAST_TIME &&\n ticksUntilHit < SHIELD_CAST_TIME + 15\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction} from '../../types.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {\n\tARENA_SIZE,\n\tGCD_DURATION,\n\tSHIELD_CAST_TIME,\n\tBLINK_CAST_TIME,\n} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToDecreaseDistance,\n\tshouldForceShield,\n\tfitMissileToBudget,\n\tMIN_USEFUL_DAMAGE,\n\tgetEnemyVulnerabilityTicks,\n} from '../shared.js';\nimport {getMissileCastTime} from '../../utils/combat.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst WALL_BUFFER = 50;\nconst SHIELD_BUFFER = 3;\n\n/**\n * Aggressive movement: dodge missiles, close distance, light strafe.\n */\nfunction aggressiveMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Voidblade\n *\n * BEHAVIOR: Reactive counter-puncher. Shields everything, then fires quick stabs\n * during enemy vulnerability windows (GCD/casting) when they can't shield back.\n * At melee range, the shield blocks ~90% of incoming damage while Voidblade's\n * counter-stabs land at full damage — winning through attrition.\n *\n * CORE LOOP (melee range):\n * 1. Enemy casts missile → Voidblade blink-dodges (100% avoid) or shields (90% block)\n * 2. Enemy enters GCD → Voidblade fires quick stab (lands unblocked)\n * 3. Voidblade enters GCD → enemy recovers → repeat\n *\n * KEY IMPROVEMENTS OVER NIGHTBLADE:\n * - Blink-dodge priority: avoids 100% of damage when blink available, shields as fallback\n * - Reads enemy vulnerability to time counter-stabs perfectly\n * - Cancel-into-defense: aborts own cast if enemy missile incoming\n * - Punish budget: sizes stabs to fit exactly in the vulnerability window\n *\n * PROGRESSION LINE: Shadowblade → Nightblade → Voidblade\n * TIER: 3 (elite Melee line)\n */\nexport const Voidblade: WizardFunction = ({state}) => \n{\n\tconst preemptiveShieldRange = useParam('preemptiveShieldRange', 348, {\n\t\trange: 85,\n\t\tmin: 50,\n\t\tsteps: 7,\n\t});\n\tconst shieldWindow = useParam('shieldWindow', 18, {\n\t\trange: 10,\n\t\tmin: 5,\n\t\tmax: 30,\n\t\tsteps: 5,\n\t});\n\tconst punishMinVulnerability = useParam('punishMinVulnerability', 10, {\n\t\trange: 15,\n\t\tmin: 10,\n\t\tsteps: 5,\n\t});\n\tconst preferBlinkDodge =\n\t\tuseParam('preferBlinkDodge', 0, {min: 0, max: 1, steps: 2}) >= 0.5;\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 231, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 250,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 45, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 92, {\n\t\trange: 20,\n\t\tmin: 50,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 40, {\n\t\trange: 30,\n\t\tmin: 15,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst blinkPastMargin = useParam('blinkPastMargin', 50, {\n\t\trange: 30,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst blinkPastDistance = useParam('blinkPastDistance', 200, {\n\t\trange: 100,\n\t\tmin: 50,\n\t\tmax: 350,\n\t\tsteps: 7,\n\t});\n\tconst engageBlinkRange = useParam('engageBlinkRange', 30, {\n\t\trange: 75,\n\t\tmin: 30,\n\t\tmax: 200,\n\t\tsteps: 7,\n\t});\n\tconst counterStabRange = useParam('counterStabRange', 27, {\n\t\trange: 20,\n\t\tmin: 10,\n\t\tmax: 60,\n\t\tsteps: 7,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 7.5, {\n\t\trange: 4,\n\t\tmin: 3,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst enemyFlightTimeDivisor = useParam('enemyFlightTimeDivisor', 10, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 10,\n\t\tsteps: 5,\n\t});\n\tconst idleProvokeBudget = useParam('idleProvokeBudget', 37, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst meleeAbortRange = useParam('meleeAbortRange', 50, {\n\t\trange: 50,\n\t\tmin: 50,\n\t\tmax: 200,\n\t\tsteps: 7,\n\t});\n\tconst enemyFiringSoonThreshold = useParam('enemyFiringSoonThreshold', 50, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst vulnerabilityTicks = getEnemyVulnerabilityTicks(enemy);\n\n\tconst move = aggressiveMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t);\n\n\t// Precompute blink targets — used by blink-dodge and engage-blink.\n\t// blinkCloseTarget: from getBlinkToDecreaseDistance (dodge + close gap). Null at melee range.\n\t// blinkPastTarget: blink ~200u past enemy (dodge only, loses position). For in-flight missiles only.\n\tlet blinkCloseTarget: {x: number; y: number} | null = null;\n\tlet blinkPastTarget: {x: number; y: number} | null = null;\n\tif (state.blinkCooldown === 0) \n\t{\n\t\tblinkCloseTarget = getBlinkToDecreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t);\n\t\tif (!blinkCloseTarget) \n\t\t{\n\t\t\tconst dx = enemy.position.x - state.position.x;\n\t\t\tconst dy = enemy.position.y - state.position.y;\n\t\t\tconst norm = Math.sqrt(dx * dx + dy * dy) || 1;\n\t\t\tblinkPastTarget = {\n\t\t\t\tx: Math.max(\n\t\t\t\t\tblinkPastMargin,\n\t\t\t\t\tMath.min(\n\t\t\t\t\t\tARENA_SIZE - blinkPastMargin,\n\t\t\t\t\t\tstate.position.x + (dx / norm) * blinkPastDistance,\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\ty: Math.max(\n\t\t\t\t\tblinkPastMargin,\n\t\t\t\t\tMath.min(\n\t\t\t\t\t\tARENA_SIZE - blinkPastMargin,\n\t\t\t\t\t\tstate.position.y + (dy / norm) * blinkPastDistance,\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 1: CHANNELING — hold shield until missile passes ===\n\tif (state.state === 'channeling') \n\t{\n\t\t// Hold shield while there's an active threat OR enemy is about to fire\n\t\tconst enemyFiringSoon =\n\t\t\tdistance < preemptiveShieldRange &&\n enemy.state === 'casting' &&\n enemy.castingSpell === 'missile' &&\n (enemy.castDuration ?? 0) -\n (enemy.castProgress ?? 0) +\n distance / enemyFlightTimeDivisor <\n enemyFiringSoonThreshold;\n\n\t\tif (\n\t\t\t(!urgentThreat && !enemyFiringSoon) ||\n (urgentThreat && urgentThreat.ticksToImpact > shieldCancelThreshold)\n\t\t) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === STEP 2: CAST-CANCEL INTO DEFENSE ===\n\t// If we're casting a stab and a missile is incoming, cancel so we can\n\t// blink-dodge (if available) or shield. Defense time depends on blink availability.\n\tif (state.state === 'casting' && state.castingSpell === 'missile') \n\t{\n\t\t// Cancel if target escaped melee range\n\t\tif (distance > meleeAbortRange) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\n\t\t// Defense time: blink (10t) is faster than shield (20t + 3t buffer)\n\t\tconst defenseTime =\n\t\t\tstate.blinkCooldown === 0\n\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\n\t\t// Cancel for any undodgeable incoming threat we can't finish casting before\n\t\tif (urgentThreat && !urgentThreat.bestDodgeDirection) \n\t\t{\n\t\t\tconst remainingCast =\n\t\t\t\t(state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\t\tconst willArriveInWindow =\n\t\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + defenseTime;\n\t\t\tif (willArriveInWindow) \n\t\t\t{\n\t\t\t\treturn {move, cancel: true};\n\t\t\t}\n\t\t}\n\n\t\t// Cancel when enemy starts casting a missile at close range —\n\t\t// at melee range their missile will arrive too fast to react after our cast finishes\n\t\tif (\n\t\t\tdistance < preemptiveShieldRange &&\n enemy.state === 'casting' &&\n enemy.castingSpell === 'missile'\n\t\t) \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst remainingOurCast =\n\t\t\t\t(state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\t\tconst estimatedFlightTicks = distance / enemyFlightTimeDivisor;\n\t\t\tconst ticksUntilEnemyHit = remainingEnemyCast + estimatedFlightTicks;\n\t\t\tconst ticksAfterOurCast = remainingOurCast + GCD_DURATION + defenseTime;\n\n\t\t\t// If enemy missile will arrive before we can defend after our cast, cancel now\n\t\t\tif (ticksUntilEnemyHit < ticksAfterOurCast) \n\t\t\t{\n\t\t\t\treturn {move, cancel: true};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === STEP 3: BLINK-DODGE — 100% damage avoidance (preferred over shield) ===\n\tif (state.state === 'idle' && state.blinkCooldown === 0) \n\t{\n\t\t// Blink target for dodging: close-gap target if available, else blink-past for in-flight only\n\t\tconst dodgeBlinkTarget = blinkCloseTarget ?? blinkPastTarget;\n\n\t\t// 3a: Blink-dodge an in-flight missile (missile already fired, can't be cancelled)\n\t\tif (\n\t\t\tpreferBlinkDodge &&\n dodgeBlinkTarget &&\n urgentThreat &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: dodgeBlinkTarget},\n\t\t\t};\n\t\t}\n\n\t\t// 3b: Preemptive blink-dodge — only when we have a close-gap target.\n\t\t// At melee range, preemptive blink loses positioning (200u walk back) and\n\t\t// the enemy can cancel their cast. Shield is better for preemptive defense.\n\t\tif (\n\t\t\tpreferBlinkDodge &&\n blinkCloseTarget &&\n distance < preemptiveShieldRange &&\n enemy.state === 'casting' &&\n enemy.castingSpell === 'missile'\n\t\t) \n\t\t{\n\t\t\tconst remainingCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTicks = distance / enemyFlightTimeDivisor;\n\t\t\tconst ticksUntilHit = remainingCast + estimatedFlightTicks;\n\n\t\t\tif (\n\t\t\t\tticksUntilHit >= BLINK_CAST_TIME &&\n ticksUntilHit < BLINK_CAST_TIME + shieldWindow\n\t\t\t) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'blink', target: blinkCloseTarget},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// 3c: Engage blink — close distance during enemy vulnerability (they can't react)\n\t\tif (\n\t\t\tblinkCloseTarget &&\n distance > engageBlinkRange &&\n vulnerabilityTicks >= punishMinVulnerability &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkCloseTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 4: SHIELD — fallback when blink can't help (90% block) ===\n\n\t// 4a: Shield in-flight missiles\n\t// Shield when: no blink target at all, or missile too close for blink (<10 ticks)\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&\n urgentThreat.canBlockInTime &&\n ((!blinkCloseTarget && !blinkPastTarget) || urgentThreat.ticksToImpact < 10)\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'shield'},\n\t\t};\n\t}\n\n\t// 4b: Preemptive shield — when blink can't close gap (melee range or cooldown)\n\tif (\n\t\tstate.state === 'idle' &&\n !blinkCloseTarget &&\n distance < preemptiveShieldRange &&\n enemy.state === 'casting' &&\n enemy.castingSpell === 'missile'\n\t) \n\t{\n\t\tconst remainingCast = (enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\tconst estimatedFlightTicks = distance / enemyFlightTimeDivisor;\n\t\tconst ticksUntilHit = remainingCast + estimatedFlightTicks;\n\n\t\tif (\n\t\t\tticksUntilHit >= SHIELD_CAST_TIME &&\n ticksUntilHit < SHIELD_CAST_TIME + shieldWindow\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 5: COUNTER-STAB — fire during enemy vulnerability ===\n\t// Only fire when enemy CAN'T shield (in GCD or mid-cast).\n\t// Use small, fast stabs that fit within the vulnerability window.\n\tif (state.state === 'idle' && distance < counterStabRange) \n\t{\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst flightTime = Math.ceil(distance / flightTimeDivisor) + 1;\n\n\t\tif (vulnerabilityTicks >= punishMinVulnerability) \n\t\t{\n\t\t\t// Budget: stab must land while enemy is still vulnerable\n\t\t\t// castTime + flightTime < vulnerabilityTicks\n\t\t\tconst punishBudget = vulnerabilityTicks - flightTime;\n\n\t\t\t// Also need time to shield if another missile is incoming\n\t\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t\t// Account for enemy's active cast — their missile will arrive after cast completes + flight time\n\t\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t\t{\n\t\t\t\tconst remainingEnemyCast =\n\t\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\t\tconst estimatedFlightTime = distance / enemyFlightTimeDivisor;\n\t\t\t\tnextThreatTime = Math.min(\n\t\t\t\t\tnextThreatTime,\n\t\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Defense time: blink (10t) is faster than shield (20t + 3t buffer)\n\t\t\tconst defenseTime =\n\t\t\t\tstate.blinkCooldown === 0\n\t\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\t\tconst safeBudget = nextThreatTime - GCD_DURATION - defenseTime;\n\t\t\tconst effectiveBudget = Math.min(punishBudget, safeBudget);\n\n\t\t\tif (effectiveBudget > 0) \n\t\t\t{\n\t\t\t\tconst config = fitMissileToBudget(effectiveBudget, distance, {\n\t\t\t\t\tminTurnRate: 0,\n\t\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t\t});\n\n\t\t\t\tif (config && config.damage >= MIN_USEFUL_DAMAGE) \n\t\t\t\t{\n\t\t\t\t\tconst castTime = getMissileCastTime(config, state.lastMissileConfig);\n\t\t\t\t\tif (castTime + flightTime <= vulnerabilityTicks) \n\t\t\t\t\t{\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tmove,\n\t\t\t\t\t\t\tstartCast: {\n\t\t\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\tmissileAI: () => ({}),\n\t\t\t\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Enemy is idle or channeling shield at close range — fire a quick stab to provoke.\n\t\t// Idle: they must shield or eat damage.\n\t\t// Channeling: shield blocks ~90% but 10% still lands. Stabs whittle HP so they\n\t\t// can't just hold shield forever. Why: otherwise the bot waits for a\n\t\t// vulnerability window that a channeling player never opens.\n\t\tif ((enemy.state === 'idle' || enemy.state === 'channeling') && !urgentThreat)\n\t\t{\n\t\t\t// Use a small budget to keep stabs fast and stay reactive\n\t\t\tconst quickBudget = idleProvokeBudget;\n\t\t\tconst config = fitMissileToBudget(quickBudget, distance, {\n\t\t\t\tminTurnRate: 0,\n\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t});\n\n\t\t\tif (config) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove,\n\t\t\t\t\tstartCast: {\n\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\tmissileAI: () => ({}),\n\t\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\n\nconst SimpleHoming: MissileAIFunction = ({worldState}) => \n{\n\t// Note: random is available but not used by this simple homing AI\n\tconst enemy = worldState.enemies[0];\n\treturn {\n\t\tturnToward: enemy?.position,\n\t};\n};\n\n/**\n * Bot: Bonemancer\n *\n * BEHAVIOR: Stands still and fires slow, homing missiles constantly. Every missile\n * tracks the enemy with turnRate 2 — they curve relentlessly toward the target.\n * No movement, no shields, just an unending stream of seeking projectiles. The\n * missiles are slow (speed 3) but long-lived (300 ticks) and will chase you across\n * the entire arena.\n *\n * NAMING RATIONALE: Named after Diablo 2's Bone Necromancer (\"Bonemancer\"), whose\n * signature spell Bone Spirit is a slow-moving, auto-tracking projectile that hunts\n * enemies relentlessly. That's exactly what this bot does — it stands in place and\n * spams seeking missiles. The homing behavior is the key identity: these aren't\n * aimed shots, they're heat-seeking spirits that chase you down. Every D2 player\n * knows the Bonemancer — it's one of the most iconic builds.\n *\n * PROGRESSION LINE: Bonemancer → Lich → Archlich\n * - Bonemancer (tier 1): Stationary, spams slow homing missiles\n * - Lich (tier 2): Future — enhanced homing with adaptive missiles and defense\n * - Archlich (tier 3): Future — master of tracking magic, undodgeable death swarm\n * The progression follows the D2 necromancer power fantasy: from bone apprentice\n * to undead overlord, each tier's missiles become harder to escape.\n *\n * TIER: 1 (base)\n */\nexport const Bonemancer: WizardFunction = ({state}) => \n{\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tif (state.state === 'idle') \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {\n\t\t\t\tspell: 'missile',\n\t\t\t\tconfig: {\n\t\t\t\t\tdamage: 12,\n\t\t\t\t\tspeed: 3,\n\t\t\t\t\tturnRate: 2,\n\t\t\t\t\tduration: 300,\n\t\t\t\t},\n\t\t\t\tmissileAI: SimpleHoming,\n\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t},\n\t\t};\n\t}\n\n\treturn {\n\t\tmove: {x: 0, y: 0},\n\t};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n\tfitMissileToBudget,\n\tshouldForceShield,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst WALL_BUFFER = 60;\n\nconst SHIELD_BUFFER = 3;\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\nfunction smartStrafe(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\tif (normalizedDistance === 0) return {x: 0, y: 0};\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDir: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDir = dodgeDirection;\n\t}\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDir =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDir = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tconst intensity = dodgeDirection ? 100 : strafeIntensity;\n\treturn {x: strafeDir.x * intensity, y: strafeDir.y * intensity};\n}\n\n/**\n * Bot: Lich\n *\n * BEHAVIOR: Homing missile specialist with strong-tracking adaptive missiles.\n * Uses fitMissileToBudget with minTurnRate 1.0 — higher than other bots (0.5) —\n * producing missiles with superior tracking at the cost of some damage/speed.\n * Strafing launches missiles from different angles, creating multi-angle pressure.\n *\n * Shields undodgeable/critical threats, emergency blinks. Cancels missile cast\n * only for lethal incoming damage.\n *\n * KEY DIFFERENCES FROM BONEMANCER:\n * - Bonemancer: stationary, no defense, fixed d=7/s=3/t=2/dur=300\n * - Lich: mobile, full defense, adaptive strong-tracking homing missiles\n *\n * PROGRESSION LINE: Bonemancer → Lich → Archlich\n * - Bonemancer (tier 1): Stationary, spams fixed slow homing missiles, no defense\n * - Lich (tier 2): Mobile + defense, adaptive strong-tracking missiles (minTurnRate 1.0)\n * - Archlich (tier 3): Future — converging web patterns, impossible to escape\n *\n * TIER: 2 (enhanced Bonemancer)\n */\nexport const Lich: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 768, {\n\t\trange: 175,\n\t\tmin: 0,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', 0.4, {range: 1.5, steps: 5});\n\tconst shieldHealthThreshold = useParam('shieldHealthThreshold', 99, {\n\t\trange: 23,\n\t\tmin: 1,\n\t\tsteps: 7,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 138, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 100, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 62, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 7, {\n\t\trange: 30,\n\t\tmin: 5,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 27, {\n\t\trange: 30,\n\t\tmin: 15,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 5.8, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst strafe = smartStrafe(\n\t\tstate.position,\n\t\tenemy,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t);\n\n\t// Distance management (retreat if too close, approach if too far)\n\tlet moveX = strafe.x;\n\tlet moveY = strafe.y;\n\n\tif (!dodgeDirection) \n\t{\n\t\tconst deltaX = enemy.position.x - state.position.x;\n\t\tconst deltaY = enemy.position.y - state.position.y;\n\t\tconst normalizedDistance = distance > 0 ? distance : 1;\n\n\t\tif (distance > targetDistance + distanceDeadZone) \n\t\t{\n\t\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t\telse if (distance < targetDistance - distanceDeadZone) \n\t\t{\n\t\t\tmoveX -= (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY -= (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\tconst move = {x: moveX, y: moveY};\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast only for LETHAL incoming damage ===\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER\n\t\t) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK: Dodge missile + reposition (first priority) ===\n\t// Blink dodges the incoming missile — no GCD after, so can immediately resume casting.\n\t// Only shield when blink is on cooldown.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t};\n\t}\n\n\t// === DEFENSE: Shield threats (when blink on cooldown) ===\n\t// Shield undodgeable, high-damage homing, or when health is critical\n\tif (state.state === 'idle' && urgentThreat) \n\t{\n\t\tconst forceShield = shouldForceShield(urgentThreat);\n\t\tconst undodgeable = !urgentThreat.bestDodgeDirection;\n\t\tconst healthLow = state.health < shieldHealthThreshold;\n\t\tconst healthCritical =\n\t\t\tstate.health <= urgentThreat.projectile.damage * 2 + 1;\n\t\tconst doShield =\n\t\t\tforceShield || (undodgeable && healthLow) || healthCritical;\n\n\t\tif (\n\t\t\tdoShield &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= SHIELD_BUFFER\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === OFFENSE: Adaptive strong-tracking homing missiles ===\n\t// minTurnRate 1.0 gives better tracking than other bots (0.5) at the cost of damage/speed.\n\t// Skip offense when a lethal missile could reach us during cast+GCD — dodge at full speed instead\n\tconst myProjectileIds = new Set(state.myProjectiles.map((p) => p.id));\n\tconst hasNearbyLethalMissile = state.projectiles.some(\n\t\t(p) =>\n\t\t\t!myProjectileIds.has(p.id) &&\n p.damage >= state.health &&\n distanceTo(state.position, p.position) / p.speed <\n MAX_CAST_BUDGET + GCD_DURATION,\n\t);\n\tif (state.state === 'idle' && !hasNearbyLethalMissile) \n\t{\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Account for enemy's active cast — their missile will arrive after cast completes + flight time\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTime = distance / flightTimeDivisor;\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t);\n\t\t}\n\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Lich specializes in strong tracking (minTurnRate 1.0 vs typical 0.5)\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (config && config.damage >= minDmg) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Budget too small for useful missile — wait for threat to pass, then fire\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {getMissileCastTime, getLeadPosition} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n\tfitMissileToBudget,\n\tshouldForceShield,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n\tgetEnemyVulnerabilityTicks,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst WALL_BUFFER = 60;\nconst SHIELD_BUFFER = 3;\n\nconst HomingAI: MissileAIFunction = ({worldState}) =>\n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\nconst StraightAI: MissileAIFunction = () => ({});\n\nfunction isNearWall(position: {x: number; y: number}): boolean\n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\nfunction smartStrafe(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n): {x: number; y: number}\n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\tif (normalizedDistance === 0) return {x: 0, y: 0};\n\n\tconst cw = {x: -deltaY / normalizedDistance, y: deltaX / normalizedDistance};\n\tconst ccw = {x: deltaY / normalizedDistance, y: -deltaX / normalizedDistance};\n\n\tlet dir: {x: number; y: number};\n\n\tif (dodgeDirection)\n\t{\n\t\tdir = dodgeDirection;\n\t}\n\telse if (isNearWall(position))\n\t{\n\t\tconst fCw = {x: position.x + cw.x * 100, y: position.y + cw.y * 100};\n\t\tconst fCcw = {x: position.x + ccw.x * 100, y: position.y + ccw.y * 100};\n\t\tconst sCw = Math.min(fCw.x, ARENA_SIZE - fCw.x, fCw.y, ARENA_SIZE - fCw.y);\n\t\tconst sCcw = Math.min(fCcw.x, ARENA_SIZE - fCcw.x, fCcw.y, ARENA_SIZE - fCcw.y);\n\t\tdir = sCw > sCcw ? cw : ccw;\n\t}\n\telse\n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tdir = cycle === 0 ? cw : ccw;\n\t}\n\n\tconst intensity = dodgeDirection ? 100 : strafeIntensity;\n\treturn {x: dir.x * intensity, y: dir.y * intensity};\n}\n\n/**\n * Bot: Archlich\n *\n * BEHAVIOR: Lich's proven core (mobile homing specialist) plus vulnerability\n * exploitation. Defense, movement, and standard offense are identical to Lich.\n * The T3 advantage: when the enemy is locked in GCD/cast, fires fast straight\n * punish missiles that land during the vulnerability window.\n *\n * PROGRESSION LINE: Bonemancer → Lich → Archlich\n * TIER: 3 (elite Homing line)\n */\nexport const Archlich: WizardFunction = ({state}) =>\n{\n\t// Same params as Lich — Archlich is Lich + punish mode (disabled by default, optimizer tunes)\n\tconst targetDistance = useParam('targetDistance', 737, {range: 175, min: 0});\n\tconst minTurnRate = useParam('minTurnRate', 0.4, {range: 1.5, steps: 5});\n\tconst shieldHealthThreshold = useParam('shieldHealthThreshold', 99, {range: 23, min: 1, steps: 7});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 300, {range: 75, min: 80, max: 300});\n\tconst strafeIntensity = useParam('strafeIntensity', 96, {range: 40, min: 20, max: 100, steps: 7});\n\tconst approachSpeed = useParam('approachSpeed', 65, {range: 30, min: 10, max: 70, steps: 7});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 7, {range: 30, min: 5, max: 80, steps: 7});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {range: 75, min: 50, max: 300});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 38, {range: 30, min: 15, max: 80, steps: 7});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 5.8, {range: 4, min: 2, max: 12, steps: 5});\n\tconst punishMinVulnerability = useParam('punishMinVulnerability', 500, {range: 80, min: 30, steps: 7});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) return {move: {x: 0, y: 0}};\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst strafe = smartStrafe(state.position, enemy, dodgeDirection, state.tick, strafeCyclePeriod, strafeIntensity);\n\n\tlet moveX = strafe.x;\n\tlet moveY = strafe.y;\n\tif (!dodgeDirection)\n\t{\n\t\tconst dx = enemy.position.x - state.position.x;\n\t\tconst dy = enemy.position.y - state.position.y;\n\t\tconst nd = distance > 0 ? distance : 1;\n\t\tif (distance > targetDistance + distanceDeadZone)\n\t\t{\n\t\t\tmoveX += (dx / nd) * approachSpeed;\n\t\t\tmoveY += (dy / nd) * approachSpeed;\n\t\t}\n\t\telse if (distance < targetDistance - distanceDeadZone)\n\t\t{\n\t\t\tmoveX -= (dx / nd) * approachSpeed;\n\t\t\tmoveY -= (dy / nd) * approachSpeed;\n\t\t}\n\t}\n\tconst mag = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (mag > 100) \n\t{\n\t\tmoveX = (moveX / mag) * 100; moveY = (moveY / mag) * 100; \n\t}\n\tconst move = {x: moveX, y: moveY};\n\n\t// === DEFENSE: identical to Lich ===\n\tif (state.state === 'channeling')\n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) return {move, cancel: true};\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tif (state.state === 'casting' && state.castingSpell === 'missile' && urgentThreat && urgentThreat.projectile.damage >= state.health)\n\t{\n\t\tconst rem = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (urgentThreat.ticksToImpact <= rem + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER) return {move, cancel: true};\n\t}\n\n\tif (state.state === 'idle' && urgentThreat && state.blinkCooldown === 0 && urgentThreat.ticksToImpact >= 10 && urgentThreat.ticksToImpact <= blinkWindowMax)\n\t{\n\t\treturn {move: {x: 0, y: 0}, startCast: {spell: 'blink', target: getBlinkToCenter(state.position)}};\n\t}\n\n\tif (state.state === 'idle' && urgentThreat)\n\t{\n\t\tconst forceShield = shouldForceShield(urgentThreat);\n\t\tconst undodgeable = !urgentThreat.bestDodgeDirection;\n\t\tconst healthLow = state.health < shieldHealthThreshold;\n\t\tconst healthCritical = state.health <= urgentThreat.projectile.damage * 2 + 1;\n\t\tif ((forceShield || (undodgeable && healthLow) || healthCritical) && urgentThreat.canBlockInTime && urgentThreat.ticksToStartShield <= SHIELD_BUFFER)\n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, startCast: {spell: 'shield'}};\n\t\t}\n\t}\n\n\t// === OFFENSE ===\n\tconst myIds = new Set(state.myProjectiles.map((p) => p.id));\n\tconst hasLethal = state.projectiles.some((p) => !myIds.has(p.id) && p.damage >= state.health && distanceTo(state.position, p.position) / p.speed < MAX_CAST_BUDGET + GCD_DURATION);\n\tif (state.state === 'idle' && !hasLethal)\n\t{\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\n\t\t// T3 PUNISH: fast straight missiles during vulnerability\n\t\tconst vulnTicks = getEnemyVulnerabilityTicks(enemy);\n\t\tif (vulnTicks >= punishMinVulnerability && distance < 300)\n\t\t{\n\t\t\tconst flight = distance / flightTimeDivisor;\n\t\t\tconst pBudget = vulnTicks - flight;\n\t\t\tconst ntt = urgentThreat?.ticksToImpact ?? Infinity;\n\t\t\tconst sBudget = ntt - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\t\tconst eBudget = Math.min(pBudget, sBudget, MAX_CAST_BUDGET);\n\t\t\tif (eBudget > 0)\n\t\t\t{\n\t\t\t\tconst cfg = fitMissileToBudget(eBudget, distance, {minTurnRate: 0, maxDamage: enemyHP, lastMissileConfig: state.lastMissileConfig});\n\t\t\t\tif (cfg && cfg.damage >= MIN_USEFUL_DAMAGE)\n\t\t\t\t{\n\t\t\t\t\tconst ct = getMissileCastTime(cfg, state.lastMissileConfig);\n\t\t\t\t\tif (ct + distance / cfg.speed < vulnTicks)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst aim = cfg.turnRate > 0 ? enemy.position : getLeadPosition(enemy.position, enemy.velocity, cfg.speed, state.position);\n\t\t\t\t\t\treturn {move, startCast: {spell: 'missile', config: cfg, missileAI: cfg.turnRate > 0 ? HomingAI : StraightAI, direction: angleTo(state.position, aim)}};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// STANDARD MODE: identical to Lich\n\t\tlet ntt = urgentThreat?.ticksToImpact ?? Infinity;\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile')\n\t\t{\n\t\t\tconst rem = (enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tntt = Math.min(ntt, rem + distance / flightTimeDivisor);\n\t\t}\n\t\tconst sBudget = ntt - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budget = Math.min(sBudget, MAX_CAST_BUDGET);\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tconst cfg = fitMissileToBudget(budget, distance, {minTurnRate, lastMissileConfig: state.lastMissileConfig});\n\t\tif (cfg && cfg.damage >= minDmg)\n\t\t{\n\t\t\treturn {move, startCast: {spell: 'missile', config: cfg, missileAI: HomingAI, direction: angleTo(state.position, enemy.position)}};\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {getMissileCastTime} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tisSafeToAttack,\n\tgetBlinkToCenter,\n\tshouldForceShield,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst WALL_BUFFER = 60;\n\nconst SHIELD_BUFFER = 3;\n\n// Fixed homing missile: damage 12, speed 5, turnRate 1, duration 180\n// Range: 5 × 180 = 900 units (covers arena)\nconst MISSILE_CONFIG = {damage: 12, speed: 5, turnRate: 1, duration: 180};\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\nfunction smartStrafe(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\tif (normalizedDistance === 0) return {x: 0, y: 0};\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDir: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDir = dodgeDirection;\n\t}\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDir =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDir = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tconst intensity = dodgeDirection ? 100 : strafeIntensity;\n\treturn {x: strafeDir.x * intensity, y: strafeDir.y * intensity};\n}\n\n/**\n * Bot: Flamecaller\n *\n * BEHAVIOR: Long-range homing missile caster with fixed missile config.\n * Maintains 350u distance, strafes to dodge, and fires standard homing\n * missiles (d=10, s=5, t=1, dur=180). Shields undodgeable threats,\n * emergency blinks. A straightforward ranged caster that trades\n * consistency for adaptability.\n *\n * PROGRESSION LINE: Flamecaller → Pyromancer → Infernalist\n * - Flamecaller (tier 1): Fixed homing missiles, basic strafe and defense\n * - Pyromancer (tier 2): + adaptive fitting, cast canceling, smart fallbacks\n * - Infernalist (tier 3): Future — overwhelming adaptive fire\n *\n * TIER: 1 (base)\n */\nexport const Flamecaller: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 753, {\n\t\trange: 150,\n\t\tmin: 0,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 273, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 20, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 40, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 56, {\n\t\trange: 30,\n\t\tmin: 5,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst safeAttackCastEstimate = useParam('safeAttackCastEstimate', 35, {\n\t\trange: 25,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst strafe = smartStrafe(\n\t\tstate.position,\n\t\tenemy,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t);\n\n\t// Distance management (retreat if too close, approach if too far)\n\tlet moveX = strafe.x;\n\tlet moveY = strafe.y;\n\n\tif (!dodgeDirection) \n\t{\n\t\tconst deltaX = enemy.position.x - state.position.x;\n\t\tconst deltaY = enemy.position.y - state.position.y;\n\t\tconst normalizedDistance = distance > 0 ? distance : 1;\n\n\t\tif (distance > targetDistance + distanceDeadZone) \n\t\t{\n\t\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t\telse if (distance < targetDistance - distanceDeadZone) \n\t\t{\n\t\t\tmoveX -= (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY -= (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\tconst move = {x: moveX, y: moveY};\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Let shield cast finish ===\n\tif (state.state === 'casting' && state.castingSpell === 'shield') \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (urgentThreat.canBlockInTime) \n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= SHIELD_BUFFER) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\telse if (state.blinkCooldown === 0) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === OFFENSE: Fire fixed homing missile when safe ===\n\tif (state.state === 'idle') \n\t{\n\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\tconst castTime = getMissileCastTime(\n\t\t\tMISSILE_CONFIG,\n\t\t\tstate.lastMissileConfig,\n\t\t);\n\t\tconst safeWindow =\n\t\t\tcastTime + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER;\n\n\t\tif (\n\t\t\tnextThreatTime > safeWindow &&\n isSafeToAttack(threats, safeAttackCastEstimate)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: MISSILE_CONFIG,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tisSafeToAttack,\n\tgetBlinkToCenter,\n\tfitMissileToBudget,\n\tshouldForceShield,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 60;\n\nconst SHIELD_BUFFER = 3;\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\nfunction smartStrafe(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\tif (normalizedDistance === 0) return {x: 0, y: 0};\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDir: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDir = dodgeDirection;\n\t}\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDir =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDir = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tconst intensity = dodgeDirection ? 100 : strafeIntensity;\n\treturn {x: strafeDir.x * intensity, y: strafeDir.y * intensity};\n}\n\n/**\n * Bot: Pyromancer\n *\n * BEHAVIOR: Adaptive homing missile specialist at long range. Maintains 400u\n * distance, strafes to dodge, and uses fitMissileToBudget with minTurnRate 0.5\n * to fire the highest-damage homing missile that fits in the safe window.\n * Shields undodgeable threats, emergency blinks. A versatile ranged caster\n * that adapts its missiles to the situation.\n *\n * PROGRESSION LINE: Flamecaller → Pyromancer → Infernalist\n * - Flamecaller (tier 1): Fixed homing missiles, basic strafe and defense\n * - Pyromancer (tier 2): + adaptive fitting, cast canceling, smart fallbacks\n * - Infernalist (tier 3): Future — overwhelming adaptive fire\n *\n * TIER: 2 (enhanced Flamecaller)\n */\nexport const Pyromancer: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 643, {\n\t\trange: 225,\n\t\tmin: 0,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', 0.2, {range: 1.5, steps: 5});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 129, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 33, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 69, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 18, {\n\t\trange: 30,\n\t\tmin: 5,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 26, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 5.3, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst safeAttackCastEstimate = useParam('safeAttackCastEstimate', 35, {\n\t\trange: 25,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst strafe = smartStrafe(\n\t\tstate.position,\n\t\tenemy,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t);\n\n\t// Distance management (retreat if too close, approach if too far)\n\tlet moveX = strafe.x;\n\tlet moveY = strafe.y;\n\n\tif (!dodgeDirection) \n\t{\n\t\tconst deltaX = enemy.position.x - state.position.x;\n\t\tconst deltaY = enemy.position.y - state.position.y;\n\t\tconst normalizedDistance = distance > 0 ? distance : 1;\n\n\t\tif (distance > targetDistance + distanceDeadZone) \n\t\t{\n\t\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t\telse if (distance < targetDistance - distanceDeadZone) \n\t\t{\n\t\t\tmoveX -= (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY -= (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\tconst move = {x: moveX, y: moveY};\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast only for LETHAL incoming damage ===\n\t// DPS trade: we accepted the hit when we started casting. Only cancel to survive.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER\n\t\t) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK: Dodge missile + reposition ===\n\t// Blink dodges the incoming missile — no GCD after, so can immediately fire back.\n\t// Wide window (10-80) prevents idle gaps where budget is too small for a missile\n\t// but the threat is too far for the old narrow blink window.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t};\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats (when blink on cooldown) ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (urgentThreat.canBlockInTime) \n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= SHIELD_BUFFER) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === OFFENSE: Adaptive homing missiles ===\n\t// Skip offense when a lethal missile could reach us during cast+GCD — dodge at full speed instead\n\tconst myProjectileIds = new Set(state.myProjectiles.map((p) => p.id));\n\tconst hasNearbyLethalMissile = state.projectiles.some(\n\t\t(p) =>\n\t\t\t!myProjectileIds.has(p.id) &&\n p.damage >= state.health &&\n distanceTo(state.position, p.position) / p.speed <\n MAX_CAST_BUDGET + GCD_DURATION,\n\t);\n\tif (state.state === 'idle' && !hasNearbyLethalMissile) \n\t{\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Account for enemy's active cast — their missile will arrive after cast completes + flight time\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTime = distance / flightTimeDivisor;\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t);\n\t\t}\n\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Pyromancer wants homing missiles — no maxDamage cap so missiles punch through shields\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (\n\t\t\tconfig &&\n config.damage >= minDmg &&\n isSafeToAttack(threats, safeAttackCastEstimate)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Budget too small for useful missile — wait for threat to pass, then fire\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {\n\tARENA_SIZE,\n\tGCD_DURATION,\n\tSHIELD_CAST_TIME,\n\tBLINK_CAST_TIME,\n} from '../../rules.js';\nimport {getMissileCastTime, getLeadPosition} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tisSafeToAttack,\n\tgetBlinkToIncreaseDistance,\n\tfitMissileToBudget,\n\tshouldForceShield,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n\tgetClosingRate,\n\tgetEnemyVulnerabilityTicks,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst WALL_BUFFER = 60;\nconst SHIELD_BUFFER = 3;\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\nconst StraightAI: MissileAIFunction = () => ({});\n\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\nfunction smartStrafe(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst len = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\tif (len === 0) return {x: 0, y: 0};\n\n\tconst cw = {x: -deltaY / len, y: deltaX / len};\n\tconst ccw = {x: deltaY / len, y: -deltaX / len};\n\n\tlet dir: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tdir = dodgeDirection;\n\t}\n\telse if (isNearWall(position)) \n\t{\n\t\tconst fCw = {x: position.x + cw.x * 100, y: position.y + cw.y * 100};\n\t\tconst fCcw = {x: position.x + ccw.x * 100, y: position.y + ccw.y * 100};\n\t\tconst sCw = Math.min(fCw.x, ARENA_SIZE - fCw.x, fCw.y, ARENA_SIZE - fCw.y);\n\t\tconst sCcw = Math.min(\n\t\t\tfCcw.x,\n\t\t\tARENA_SIZE - fCcw.x,\n\t\t\tfCcw.y,\n\t\t\tARENA_SIZE - fCcw.y,\n\t\t);\n\t\tdir = sCw > sCcw ? cw : ccw;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tdir = cycle === 0 ? cw : ccw;\n\t}\n\n\tconst intensity = dodgeDirection ? 100 : strafeIntensity;\n\treturn {x: dir.x * intensity, y: dir.y * intensity};\n}\n\n/**\n * Bot: Infernalist\n *\n * BEHAVIOR: Rapid-fire caster that exploits warmup bonus for accelerating DPS.\n * Fires consistent homing missiles to build warmup, punishes vulnerability windows\n * with warmup-boosted fast casts. Proactive blink kiting when enemy closes.\n *\n * KEY IMPROVEMENTS OVER PYROMANCER:\n * - Warmup exploitation: always passes lastMissileConfig for bonus\n * - Punish mode: straight missiles during enemy vulnerability\n * - Proactive blink kiting: monitors closing rate\n * - Progressive cast-cancel: graduated thresholds\n *\n * PROGRESSION LINE: Flamecaller → Pyromancer → Infernalist\n * TIER: 3 (elite Caster line)\n */\nexport const Infernalist: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 285, {range: 225, min: 0});\n\tconst minTurnRate = useParam('minTurnRate', 0.2, {range: 1.5, steps: 5});\n\tconst dangerDistance = useParam('dangerDistance', 222, {\n\t\trange: 150,\n\t\tmin: 0,\n\t});\n\tconst closingRateThreshold = useParam('closingRateThreshold', 0.94, {\n\t\trange: 0.65,\n\t\tmin: 0,\n\t\tsteps: 5,\n\t});\n\tconst punishMinVulnerability = useParam('punishMinVulnerability', 340, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tsteps: 5,\n\t});\n\tconst preferBlinkDodge =\n\t\tuseParam('preferBlinkDodge', 0, {min: 0, max: 1, steps: 2}) >= 0.5;\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 109, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 250,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 23, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 53, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 34, {\n\t\trange: 30,\n\t\tmin: 5,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst cancelHeavyDmg = useParam('cancelHeavyDmg', 40, {\n\t\trange: 15,\n\t\tmin: 5,\n\t\tmax: 40,\n\t\tsteps: 5,\n\t});\n\tconst cancelMediumDmg = useParam('cancelMediumDmg', 9, {\n\t\trange: 10,\n\t\tmin: 3,\n\t\tmax: 25,\n\t\tsteps: 5,\n\t});\n\tconst cancelCastRatio = useParam('cancelCastRatio', 0.78, {\n\t\trange: 0.4,\n\t\tmin: 0.3,\n\t\tmax: 1.0,\n\t\tsteps: 5,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 100, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 75, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst proactiveBlinkRange = useParam('proactiveBlinkRange', 100, {\n\t\trange: 150,\n\t\tmin: 100,\n\t\tmax: 500,\n\t\tsteps: 7,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 5.4, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst safeAttackCastEstimate = useParam('safeAttackCastEstimate', 35, {\n\t\trange: 25,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst strafe = smartStrafe(\n\t\tstate.position,\n\t\tenemy,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t);\n\n\t// Distance management with wall-aware retreat\n\tlet moveX = strafe.x;\n\tlet moveY = strafe.y;\n\n\tif (!dodgeDirection) \n\t{\n\t\tconst deltaX = enemy.position.x - state.position.x;\n\t\tconst deltaY = enemy.position.y - state.position.y;\n\t\tconst norm = distance > 0 ? distance : 1;\n\n\t\tif (distance > targetDistance + distanceDeadZone) \n\t\t{\n\t\t\tmoveX += (deltaX / norm) * approachSpeed;\n\t\t\tmoveY += (deltaY / norm) * approachSpeed;\n\t\t}\n\t\telse if (distance < targetDistance - distanceDeadZone) \n\t\t{\n\t\t\tlet rx = -(deltaX / norm) * approachSpeed;\n\t\t\tlet ry = -(deltaY / norm) * approachSpeed;\n\t\t\tif (state.position.x < WALL_BUFFER && rx < 0) rx = 0;\n\t\t\tif (state.position.x > ARENA_SIZE - WALL_BUFFER && rx > 0) rx = 0;\n\t\t\tif (state.position.y < WALL_BUFFER && ry < 0) ry = 0;\n\t\t\tif (state.position.y > ARENA_SIZE - WALL_BUFFER && ry > 0) ry = 0;\n\t\t\tmoveX += rx;\n\t\t\tmoveY += ry;\n\t\t}\n\t}\n\n\tconst mag = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (mag > 100) \n\t{\n\t\tmoveX = (moveX / mag) * 100;\n\t\tmoveY = (moveY / mag) * 100;\n\t}\n\n\tconst move = {x: moveX, y: moveY};\n\n\t// === STEP 1: CHANNELING ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === STEP 2: PROGRESSIVE CAST-CANCEL ===\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tconst defenseTime =\n\t\t\tstate.blinkCooldown === 0\n\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tconst willArriveInWindow =\n\t\t\turgentThreat.ticksToImpact <= remainingCast + GCD_DURATION + defenseTime;\n\n\t\tif (willArriveInWindow) \n\t\t{\n\t\t\tconst incomingDamage = urgentThreat.projectile.damage;\n\t\t\tconst castRatio = (state.castProgress ?? 0) / (state.castDuration ?? 1);\n\n\t\t\tif (incomingDamage >= state.health || incomingDamage >= cancelHeavyDmg) \n\t\t\t{\n\t\t\t\treturn {move, cancel: true};\n\t\t\t}\n\t\t\tif (incomingDamage >= cancelMediumDmg && castRatio < cancelCastRatio) \n\t\t\t{\n\t\t\t\treturn {move, cancel: true};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === BLINK-DODGE — optimizer-controlled alternative to shield ===\n\tif (\n\t\tpreferBlinkDodge &&\n state.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&\n urgentThreat.ticksToImpact >= BLINK_CAST_TIME &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (blinkTarget) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 3: SHIELD — undodgeable or high-damage homing threats ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (\n\t\t\turgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= SHIELD_BUFFER\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 5: PROACTIVE BLINK KITING ===\n\tif (\n\t\tstate.state === 'idle' &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst closingRate = getClosingRate(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tenemy.velocity,\n\t\t);\n\t\tif (\n\t\t\t(closingRate > closingRateThreshold && distance < proactiveBlinkRange) ||\n distance < dangerDistance\n\t\t) \n\t\t{\n\t\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\t\tstate.position,\n\t\t\t\tenemy.position,\n\t\t\t\tstate.projectiles,\n\t\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t\t);\n\t\t\tif (blinkTarget) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === STEP 6: OFFENSE ===\n\tif (state.state === 'idle') \n\t{\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst vulnerabilityTicks = getEnemyVulnerabilityTicks(enemy);\n\n\t\t// --- PUNISH MODE: warmup-accelerated missiles during vulnerability ---\n\t\tif (vulnerabilityTicks >= punishMinVulnerability && distance < 500) \n\t\t{\n\t\t\tconst flightTimeEstimate = distance / flightTimeDivisor;\n\t\t\tconst punishBudget = vulnerabilityTicks - flightTimeEstimate;\n\t\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\t\tconst safeBudget =\n\t\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\t\tconst effectiveBudget = Math.min(\n\t\t\t\tpunishBudget,\n\t\t\t\tsafeBudget,\n\t\t\t\tMAX_CAST_BUDGET,\n\t\t\t);\n\n\t\t\tif (effectiveBudget > 0) \n\t\t\t{\n\t\t\t\tconst config = fitMissileToBudget(effectiveBudget, distance, {\n\t\t\t\t\tminTurnRate: 0,\n\t\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t\t});\n\n\t\t\t\tif (config && config.damage >= MIN_USEFUL_DAMAGE) \n\t\t\t\t{\n\t\t\t\t\tconst castTime = getMissileCastTime(config, state.lastMissileConfig);\n\t\t\t\t\tconst actualFlight = distance / config.speed;\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tcastTime + actualFlight < vulnerabilityTicks &&\n isSafeToAttack(threats, castTime)\n\t\t\t\t\t) \n\t\t\t\t\t{\n\t\t\t\t\t\tconst aimTarget =\n\t\t\t\t\t\t\tconfig.turnRate > 0\n\t\t\t\t\t\t\t\t? enemy.position\n\t\t\t\t\t\t\t\t: getLeadPosition(\n\t\t\t\t\t\t\t\t\tenemy.position,\n\t\t\t\t\t\t\t\t\tenemy.velocity,\n\t\t\t\t\t\t\t\t\tconfig.speed,\n\t\t\t\t\t\t\t\t\tstate.position,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tmove,\n\t\t\t\t\t\t\tstartCast: {\n\t\t\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\tmissileAI: config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\t\t\t\tdirection: angleTo(state.position, aimTarget),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// --- STANDARD MODE: warmup-consistent homing missiles ---\n\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tlet config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (\n\t\t\tconfig &&\n config.damage >= minDmg &&\n isSafeToAttack(threats, safeAttackCastEstimate)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Fallback: fire bigger missile, accept incoming hit\n\t\tif (!config || config.damage < minDmg) \n\t\t{\n\t\t\tconfig = fitMissileToBudget(MAX_CAST_BUDGET, distance, {\n\t\t\t\tminTurnRate,\n\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t});\n\t\t\tif (config) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove,\n\t\t\t\t\tstartCast: {\n\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {getMissileCastTime} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tisSafeToAttack,\n\tgetBlinkToCenter,\n\tgetBlinkToIncreaseDistance,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tshouldForceShield,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\nconst WALL_BUFFER = 50;\n\n/**\n * Smart combat movement: dodge using threat analysis, avoid walls, commit to direction.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\tminDistance: number,\n\tmaxDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\t// Priority 1: Use dodge direction (from threat analysis or close missile avoidance)\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\t// Priority 2: Avoid walls\n\telse if (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\t// Priority 3: Alternate strafe direction\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\t// When actively dodging, commit fully — no approach/retreat dilution\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\t// Distance management (only when not dodging)\n\tif (currentDistance > maxDistance) \n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < minDistance) \n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Spellshot\n *\n * BEHAVIOR: Uses interceptAngle to calculate where the enemy will be and fires\n * fast, non-homing missiles (speed 8, turnRate 0) along the predicted path.\n * Strafes at medium range (300-400), shields undodgeable threats, emergency\n * blinks when shield isn't available. The key mechanic is PREDICTION — these\n * missiles don't track, they go exactly where you calculated the enemy would be.\n *\n * NAMING RATIONALE: \"Spellshot\" — a spell that is a single, precisely aimed shot.\n * Like a sniper's \"called shot\" but magical. The defining feature is the intercept\n * calculation: this bot doesn't fire tracking missiles, it calculates the exact\n * angle needed to hit a moving target. \"Shot\" implies precision, singular impact,\n * and skill-based aiming — everything this bot is about.\n *\n * PROGRESSION LINE: Spellshot → Spelltracer → Spellseeker\n * - Spellshot (tier 1): Basic intercept prediction, non-homing missiles\n * - Spelltracer (tier 2): Future — predictive homing (missiles that lead AND track)\n * - Spellseeker (tier 3): Future — perfect prediction, multi-angle attacks\n * The naming progression: shot (single bullet) → tracer (bullet that tracks a path)\n * → seeker (actively hunts). Each tier adds more intelligence to the projectile,\n * evolving from \"I calculate where you'll be\" to \"my missile calculates where you'll be.\"\n *\n * NOTE: A separate future archetype \"Spellslinger\" (volume-of-fire) is reserved\n * for a rapid-fire bot that prioritizes quantity over prediction.\n *\n * TIER: 1 (base)\n */\nexport const Spellshot: WizardFunction = ({state}) => \n{\n\tconst dangerDistance = useParam('dangerDistance', 45, {range: 100, min: 0});\n\tconst targetDistance = useParam('targetDistance', 339, {\n\t\trange: 150,\n\t\tmin: 0,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 22, {\n\t\trange: 50,\n\t\tmin: 0,\n\t});\n\tconst minCombatDistance = targetDistance - distanceDeadZone;\n\tconst maxCombatDistance = targetDistance + distanceDeadZone;\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 230, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 36, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 78, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst missileDamage = useParam('missileDamage', 30, {\n\t\trange: 10,\n\t\tmin: 5,\n\t\tmax: 30,\n\t\tsteps: 5,\n\t});\n\tconst missileSpeed = useParam('missileSpeed', 12, {\n\t\trange: 3,\n\t\tmin: 3,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst missileTurnRate = useParam('missileTurnRate', 0.3, {\n\t\trange: 1.5,\n\t\tsteps: 5,\n\t});\n\tconst missileMinDuration = useParam('missileMinDuration', 55, {\n\t\trange: 30,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst durationRangeFactor = useParam('durationRangeFactor', 0.67, {\n\t\trange: 0.5,\n\t\tmin: 0.5,\n\t\tmax: 2.0,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast if dangerous threat will arrive before we can shield ===\n\t// Only cancel for: high-damage homing (always) or undodgeable when health is at risk\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tconst mustCancel =\n\t\t\tshouldForceShield(urgentThreat) ||\n state.health <= urgentThreat.projectile.damage * 2 + 1;\n\t\tif (mustCancel) \n\t\t{\n\t\t\tconst remainingCast =\n\t\t\t\t(state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\t\tif (\n\t\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + 3\n\t\t\t) \n\t\t\t{\n\t\t\t\tconst dodgeDir = getDodgeDirectionForMovement(threats, state.position);\n\t\t\t\tconst cancelMove = combatMove(\n\t\t\t\t\tstate.position,\n\t\t\t\t\tenemy,\n\t\t\t\t\tdistance,\n\t\t\t\t\tminCombatDistance,\n\t\t\t\t\tmaxCombatDistance,\n\t\t\t\t\tdodgeDir,\n\t\t\t\t\tstate.tick,\n\t\t\t\t\tstrafeCyclePeriod,\n\t\t\t\t\tstrafeIntensity,\n\t\t\t\t\tapproachSpeed,\n\t\t\t\t);\n\t\t\t\treturn {move: cancelMove, cancel: true};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (urgentThreat.canBlockInTime) \n\t\t{\n\t\t\t// Start shield at the right time (small buffer for safety)\n\t\t\tif (urgentThreat.ticksToStartShield <= 3) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\t// Can't dodge, can't shield → blink away (emergency)\n\t\telse if (state.blinkCooldown === 0) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === DISTANCE BLINK: enemy too close, blink to reestablish kiting range ===\n\tif (\n\t\tstate.state === 'idle' &&\n distance < dangerDistance &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (blinkTarget) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// Smart combat movement\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\tminCombatDistance,\n\t\tmaxCombatDistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t);\n\n\t// === OFFENSE: Fast homing missiles ===\n\tif (state.state === 'idle' && distance < 600) \n\t{\n\t\tconst missileConfig = {\n\t\t\tdamage: missileDamage,\n\t\t\tspeed: missileSpeed,\n\t\t\tturnRate: missileTurnRate,\n\t\t\tduration: Math.max(\n\t\t\t\tmissileMinDuration,\n\t\t\t\tMath.ceil((distance * durationRangeFactor) / missileSpeed),\n\t\t\t),\n\t\t};\n\t\tconst castTime = getMissileCastTime(missileConfig, state.lastMissileConfig);\n\t\tif (isSafeToAttack(threats, castTime)) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: missileConfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n\tgetBlinkToIncreaseDistance,\n\tfitMissileToBudget,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tshouldForceShield,\n\tMAX_CAST_BUDGET,\n\tgetEnemyVulnerabilityTicks,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\nconst WALL_BUFFER = 50;\n\nconst SHIELD_BUFFER = 3;\n\n/**\n * Smart combat movement: dodge using threat analysis, avoid walls, commit to direction.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\tminDistance: number,\n\tmaxDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\t// When actively dodging, commit fully\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\t// Distance management\n\tif (currentDistance > maxDistance) \n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < minDistance) \n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Spelltracer\n *\n * BEHAVIOR: Enhanced ranged sniper with adaptive missile fitting and intercept\n * prediction. Uses fitMissileToBudget to find the highest-damage fast missile\n * that fits the safe window, then fires it along the predicted intercept angle.\n * Maintains medium-long range (300-450), shields undodgeable threats with proper\n * timing, emergency blinks, and distance blinks when cornered. The key mechanic\n * is still PREDICTION — but now with adaptive damage optimization.\n *\n * PROGRESSION LINE: Spellshot → Spelltracer → Spellseeker\n * - Spellshot (tier 1): Fixed config intercept prediction, non-homing missiles\n * - Spelltracer (tier 2): + adaptive fitting, timed defense, distance management\n * - Spellseeker (tier 3): Future — perfect prediction, multi-angle attacks\n *\n * TIER: 2 (enhanced Spellshot)\n */\nexport const Spelltracer: WizardFunction = ({state}) => \n{\n\t// Tunable parameters (run optimizer script to find optimal values)\n\tconst dangerDistance = useParam('dangerDistance', 199, {\n\t\trange: 175,\n\t\tmin: 0,\n\t});\n\tconst targetDistance = useParam('targetDistance', 555, {range: 200, min: 0});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 12, {\n\t\trange: 50,\n\t\tmin: 0,\n\t});\n\tconst rangeMin = targetDistance - distanceDeadZone;\n\tconst rangeMax = targetDistance + distanceDeadZone;\n\tconst minTurnRate = useParam('minTurnRate', 0.9, {range: 1.5, steps: 5});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 140, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 48, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 80, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 23, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 12, {\n\t\trange: 4,\n\t\tmin: 3,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst enemyResponseEstimate = useParam('enemyResponseEstimate', 217, {\n\t\trange: 100,\n\t\tmin: 50,\n\t\tmax: 300,\n\t\tsteps: 7,\n\t});\n\tconst threatFallbackThreshold = useParam('threatFallbackThreshold', 250, {\n\t\trange: 100,\n\t\tmin: 100,\n\t\tmax: 400,\n\t\tsteps: 7,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast only for LETHAL incoming damage ===\n\t// DPS trade: we accepted the hit when we started casting. Only cancel to survive.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER\n\t\t) \n\t\t{\n\t\t\tconst dodgeDir = getDodgeDirectionForMovement(threats, state.position);\n\t\t\tconst cancelMove = combatMove(\n\t\t\t\tstate.position,\n\t\t\t\tenemy,\n\t\t\t\tdistance,\n\t\t\t\trangeMin,\n\t\t\t\trangeMax,\n\t\t\t\tdodgeDir,\n\t\t\t\tstate.tick,\n\t\t\t\tstrafeCyclePeriod,\n\t\t\t\tstrafeIntensity,\n\t\t\t\tapproachSpeed,\n\t\t\t);\n\t\t\treturn {move: cancelMove, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK: Dodge undodgeable/forced-shield threats (offensive alternative to shielding) ===\n\t// When we would otherwise shield, blink instead — no GCD after blink, so can fire back immediately.\n\t// Only for threats that can't be dodged by movement (homing/undodgeable).\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {\n\t\t\t\tspell: 'blink',\n\t\t\t\ttarget:\n distance < targetDistance\n \t? (getBlinkToIncreaseDistance(\n \t\tstate.position,\n \t\tenemy.position,\n \t\tstate.projectiles,\n \t) ?? getBlinkToCenter(state.position))\n \t: getBlinkToCenter(state.position),\n\t\t\t},\n\t\t};\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats (when blink on cooldown) ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (urgentThreat.canBlockInTime) \n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= SHIELD_BUFFER) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === DISTANCE BLINK: enemy too close, blink to reestablish range ===\n\tif (\n\t\tstate.state === 'idle' &&\n distance < dangerDistance &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (blinkTarget) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// Smart combat movement\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\trangeMin,\n\t\trangeMax,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t);\n\n\t// === OFFENSE: Adaptive homing missiles ===\n\t// Skip offense when a lethal missile could reach us during cast+GCD — dodge at full speed instead\n\tconst myProjectileIds = new Set(state.myProjectiles.map((p) => p.id));\n\tconst hasNearbyLethalMissile = state.projectiles.some(\n\t\t(p) =>\n\t\t\t!myProjectileIds.has(p.id) &&\n p.damage >= state.health &&\n distanceTo(state.position, p.position) / p.speed <\n MAX_CAST_BUDGET + GCD_DURATION,\n\t);\n\tif (state.state === 'idle' && distance < 600 && !hasNearbyLethalMissile) \n\t{\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Anticipate enemy's next missile based on their current state\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + distance / flightTimeDivisor,\n\t\t\t);\n\t\t}\n\t\t// No imminent threat — estimate enemy's likely response time\n\t\tif (nextThreatTime > threatFallbackThreshold) \n\t\t{\n\t\t\tconst enemyLockTime = getEnemyVulnerabilityTicks(enemy);\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tenemyLockTime + enemyResponseEstimate + distance / flightTimeDivisor,\n\t\t\t);\n\t\t}\n\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Try adaptive fitting for the safe budget\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\t// HIGH-DAMAGE PRIORITY: Sniper missiles need to chip through shields.\n\t\t// Damage 5 through 90% shield = 0.5 chip (useless).\n\t\t// Damage 25 through 90% shield = 2.5 chip (kills in ~24 hits).\n\t\t// Always prefer the adaptive config if damage >= 15; otherwise use a\n\t\t// fixed big-shot fallback that guarantees meaningful shield chip.\n\t\tconst BIG_SHOT = {damage: 25, speed: 10, turnRate: 0.5, duration: 80};\n\n\t\tif (config && config.damage >= 15)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Adaptive fitting produced weak missile or failed — fire big shot instead\n\t\t// This ensures Spelltracer always does meaningful damage, even if it takes\n\t\t// a hit during the longer cast (trade is worthwhile at high damage).\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > 50)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: BIG_SHOT,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Imminent threat — wait for it to pass, then fire\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {\n\tARENA_SIZE,\n\tGCD_DURATION,\n\tSHIELD_CAST_TIME,\n\tBLINK_CAST_TIME,\n} from '../../rules.js';\nimport {getMissileCastTime, getLeadPosition} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n\tgetBlinkToIncreaseDistance,\n\tfitMissileToBudget,\n\tshouldForceShield,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n\tgetClosingRate,\n\tgetEnemyVulnerabilityTicks,\n\tisSafeToAttack,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\nconst StraightAI: MissileAIFunction = () => ({});\n\nconst WALL_BUFFER = 50;\nconst SHIELD_BUFFER = 3;\n\n/**\n * Smart combat movement: dodge, avoid walls, maintain sniper range.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\tminDistance: number,\n\tmaxDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\tif (currentDistance > maxDistance) \n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < minDistance) \n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Spellseeker\n *\n * BEHAVIOR: Elite sniper that uses intercept-aimed straight missiles during vulnerability\n * windows. Combines Spelltracer's adaptive fitting with precise lead-position aiming\n * and vulnerability exploitation. Straight punish missiles at sniper range are nearly\n * unavoidable. Proactive distance control via closing rate detection.\n *\n * KEY IMPROVEMENTS OVER SPELLTRACER:\n * - Intercept-aimed punish: getLeadPosition + straight missiles during vulnerability\n * - Proactive distance blink: monitors closing rate, blinks before danger zone\n * - Progressive cast-cancel: graduated damage thresholds\n * - Warmup exploitation: always passes lastMissileConfig\n *\n * PROGRESSION LINE: Spellshot → Spelltracer → Spellseeker\n * TIER: 3 (elite Sniper line)\n */\nexport const Spellseeker: WizardFunction = ({state}) => \n{\n\tconst dangerDistance = useParam('dangerDistance', 65, {\n\t\trange: 175,\n\t\tmin: 0,\n\t});\n\tconst targetDistance = useParam('targetDistance', 289, {\n\t\trange: 200,\n\t\tmin: 0,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 22, {\n\t\trange: 50,\n\t\tmin: 0,\n\t});\n\tconst rangeMin = targetDistance - distanceDeadZone;\n\tconst rangeMax = targetDistance + distanceDeadZone;\n\tconst minTurnRate = useParam('minTurnRate', 3.5, {range: 1.5, steps: 5});\n\tconst closingRateThreshold = useParam('closingRateThreshold', 1.79, {\n\t\trange: 0.65,\n\t\tmin: 0,\n\t\tsteps: 5,\n\t});\n\tconst punishMinVulnerability = useParam('punishMinVulnerability', 279, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tsteps: 5,\n\t});\n\tconst preferBlinkDodge =\n\t\tuseParam('preferBlinkDodge', 1, {min: 0, max: 1, steps: 2}) >= 0.5;\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 231, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 250,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 87, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 80, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 100, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 88, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst proactiveBlinkRange = useParam('proactiveBlinkRange', 433, {\n\t\trange: 150,\n\t\tmin: 100,\n\t\tmax: 500,\n\t\tsteps: 7,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 12, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst castingFlightTimeDivisor = useParam('castingFlightTimeDivisor', 12, {\n\t\trange: 4,\n\t\tmin: 3,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst enemyResponseEstimate = useParam('enemyResponseEstimate', 168, {\n\t\trange: 100,\n\t\tmin: 50,\n\t\tmax: 300,\n\t\tsteps: 7,\n\t});\n\tconst threatFallbackThreshold = useParam('threatFallbackThreshold', 250, {\n\t\trange: 100,\n\t\tmin: 100,\n\t\tmax: 400,\n\t\tsteps: 7,\n\t});\n\tconst punishAttackRange = useParam('punishAttackRange', 213, {\n\t\trange: 150,\n\t\tmin: 200,\n\t\tmax: 700,\n\t\tsteps: 7,\n\t});\n\tconst safeAttackCastEstimate = useParam('safeAttackCastEstimate', 35, {\n\t\trange: 25,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\trangeMin,\n\t\trangeMax,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t);\n\n\t// === STEP 1: CHANNELING — cancel shield when safe ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === STEP 2: PROGRESSIVE CAST-CANCEL ===\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tconst defenseTime =\n\t\t\tstate.blinkCooldown === 0\n\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tconst willArriveInWindow =\n\t\t\turgentThreat.ticksToImpact <= remainingCast + GCD_DURATION + defenseTime;\n\n\t\tif (willArriveInWindow) \n\t\t{\n\t\t\tconst incomingDamage = urgentThreat.projectile.damage;\n\t\t\tconst castRatio = (state.castProgress ?? 0) / (state.castDuration ?? 1);\n\n\t\t\tif (incomingDamage >= state.health || incomingDamage >= 20) \n\t\t\t{\n\t\t\t\treturn {move, cancel: true};\n\t\t\t}\n\t\t\tif (incomingDamage >= 10 && castRatio < 0.8) \n\t\t\t{\n\t\t\t\treturn {move, cancel: true};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === STEP 3: BLINK-DODGE — 100% damage avoidance (optimizer-controlled) ===\n\tif (\n\t\tpreferBlinkDodge &&\n state.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&\n urgentThreat.ticksToImpact >= BLINK_CAST_TIME &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\tconst midRange = targetDistance;\n\t\tconst target =\n\t\t\tdistance < midRange\n\t\t\t\t? (getBlinkToIncreaseDistance(\n\t\t\t\t\tstate.position,\n\t\t\t\t\tenemy.position,\n\t\t\t\t\tstate.projectiles,\n\t\t\t\t) ?? getBlinkToCenter(state.position))\n\t\t\t\t: getBlinkToCenter(state.position);\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target},\n\t\t};\n\t}\n\n\t// === STEP 4: SHIELD — undodgeable or critical threats ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (\n\t\t\turgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= SHIELD_BUFFER\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 5: PROACTIVE DISTANCE BLINK ===\n\tif (\n\t\tstate.state === 'idle' &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst closingRate = getClosingRate(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tenemy.velocity,\n\t\t);\n\t\tif (\n\t\t\t(closingRate > closingRateThreshold && distance < proactiveBlinkRange) ||\n distance < dangerDistance\n\t\t) \n\t\t{\n\t\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\t\tstate.position,\n\t\t\t\tenemy.position,\n\t\t\t\tstate.projectiles,\n\t\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t\t);\n\t\t\tif (blinkTarget) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === STEP 6: OFFENSE ===\n\tif (state.state === 'idle' && distance < 600) \n\t{\n\t\tconst offenseDefenseTime =\n\t\t\tstate.blinkCooldown === 0\n\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst vulnerabilityTicks = getEnemyVulnerabilityTicks(enemy);\n\n\t\t// --- PUNISH MODE: intercept-aimed straight missiles during vulnerability ---\n\t\tif (\n\t\t\tvulnerabilityTicks >= punishMinVulnerability &&\n distance < punishAttackRange\n\t\t) \n\t\t{\n\t\t\tconst flightTimeEstimate = distance / flightTimeDivisor;\n\t\t\tconst punishBudget = vulnerabilityTicks - flightTimeEstimate;\n\t\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\t\tconst safeBudget = nextThreatTime - GCD_DURATION - offenseDefenseTime;\n\t\t\tconst effectiveBudget = Math.min(\n\t\t\t\tpunishBudget,\n\t\t\t\tsafeBudget,\n\t\t\t\tMAX_CAST_BUDGET,\n\t\t\t);\n\n\t\t\tif (effectiveBudget > 0) \n\t\t\t{\n\t\t\t\tconst config = fitMissileToBudget(effectiveBudget, distance, {\n\t\t\t\t\tminTurnRate: 0,\n\t\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t\t});\n\n\t\t\t\tif (config && config.damage >= MIN_USEFUL_DAMAGE) \n\t\t\t\t{\n\t\t\t\t\tconst castTime = getMissileCastTime(config, state.lastMissileConfig);\n\t\t\t\t\tconst actualFlight = distance / config.speed;\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tcastTime + actualFlight < vulnerabilityTicks &&\n isSafeToAttack(threats, castTime)\n\t\t\t\t\t) \n\t\t\t\t\t{\n\t\t\t\t\t\tconst aimTarget =\n\t\t\t\t\t\t\tconfig.turnRate > 0\n\t\t\t\t\t\t\t\t? enemy.position\n\t\t\t\t\t\t\t\t: getLeadPosition(\n\t\t\t\t\t\t\t\t\tenemy.position,\n\t\t\t\t\t\t\t\t\tenemy.velocity,\n\t\t\t\t\t\t\t\t\tconfig.speed,\n\t\t\t\t\t\t\t\t\tstate.position,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tmove,\n\t\t\t\t\t\t\tstartCast: {\n\t\t\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\tmissileAI: config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\t\t\t\tdirection: angleTo(state.position, aimTarget),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// --- STANDARD MODE: adaptive homing missiles ---\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Anticipate enemy's next missile based on their current state\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + distance / castingFlightTimeDivisor,\n\t\t\t);\n\t\t}\n\t\t// No imminent threat — estimate enemy's likely response time\n\t\tif (nextThreatTime > threatFallbackThreshold) \n\t\t{\n\t\t\tconst enemyLockTime = getEnemyVulnerabilityTicks(enemy);\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tenemyLockTime +\n enemyResponseEstimate +\n distance / castingFlightTimeDivisor,\n\t\t\t);\n\t\t}\n\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\t// HIGH-DAMAGE PRIORITY: Sniper missiles need meaningful shield chip.\n\t\t// Fixed big-shot fallback ensures Spellseeker always does significant damage.\n\t\tconst BIG_SHOT = {damage: 28, speed: 10, turnRate: 0.5, duration: 80};\n\n\t\tif (\n\t\t\tconfig &&\n config.damage >= 15 &&\n isSafeToAttack(threats, safeAttackCastEstimate)\n\t\t)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Adaptive fitting weak or failed — fire big shot for meaningful damage\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > 50)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: BIG_SHOT,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {getMissileCastTime} from '../../utils/combat.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tisSafeToAttack,\n\tgetBlinkToDecreaseDistance,\n\tshouldForceShield,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 60;\n\nconst QUICK_CONFIG = {damage: 15, speed: 5, turnRate: 1, duration: 70};\nconst HEAVY_CONFIG = {damage: 15, speed: 5, turnRate: 1, duration: 150};\n\n// Missile effective range (speed * duration). Use HEAVY when QUICK can't reach.\nconst QUICK_RANGE = QUICK_CONFIG.speed * QUICK_CONFIG.duration; // 350\n\n/**\n * Standard homing missile AI.\n */\nconst StandardHoming: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {\n\t\tturnToward: enemy?.position,\n\t};\n};\n\n/**\n * Check if near wall.\n */\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart combat movement with threat-aware dodging.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\ttargetDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n\tdistanceDeadZone: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\t// Perpendicular directions\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\t// Priority 1: Use dodge direction (from threat analysis or close missile avoidance)\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\t// Priority 2: Avoid walls\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\t// Priority 3: Commit to direction\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\t// When actively dodging, commit fully — no approach/retreat dilution\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\t// Distance management (only when not dodging)\n\tif (currentDistance > targetDistance + distanceDeadZone) \n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < targetDistance - distanceDeadZone) \n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\t// Normalize\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Battlemage\n *\n * BEHAVIOR: Balanced mid-range duelist. Shields undodgeable threats, interrupts\n * enemy casts with quick missiles, saves blink for emergencies OR gap-closing.\n * Switches between quick (10 dmg, fast) and heavy (15 dmg, slow) missile configs\n * based on safety window and range. Will aggressively trade hits when health allows.\n * The unique trait is cast-interruption: fires quick missiles specifically when the\n * enemy is casting, punishing long cast times.\n *\n * PROGRESSION LINE: Battlemage → Warmage → Archmage\n * - Battlemage (tier 1): Quick/heavy fixed configs, cast interruption, basic defense\n * - Warmage (tier 2): Adaptive missile fitting (fitMissileToBudget), smarter attacks\n * - Archmage (tier 3): Future — supreme duelist, perfect tactical mastery\n *\n * TIER: 1 (base)\n */\nexport const Battlemage: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 274, {\n\t\trange: 150,\n\t\tmin: 150,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 219, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 36, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 80, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 31, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 35, {\n\t\trange: 30,\n\t\tmin: 15,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst blinkDistanceOffset = useParam('blinkDistanceOffset', 100, {\n\t\trange: 40,\n\t\tmin: 0,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst cancelMinRemainingCast = useParam('cancelMinRemainingCast', 19, {\n\t\trange: 30,\n\t\tmin: 5,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst interruptRange = useParam('interruptRange', 324, {\n\t\trange: 150,\n\t\tmin: 150,\n\t\tmax: 600,\n\t\tsteps: 7,\n\t});\n\tconst attackRange = useParam('attackRange', 556, {\n\t\trange: 150,\n\t\tmin: 200,\n\t\tmax: 700,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\t// Cancel shield if no imminent threats\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel long missile casts if undodgeable threat approaches ===\n\t// Only cancel when health is too low to trade (aggressive casts accepted the risk).\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\tremainingCast > cancelMinRemainingCast &&\n urgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + 1 &&\n state.health <= urgentThreat.projectile.damage * 2 + 1\n\t\t) \n\t\t{\n\t\t\tconst dodgeDirection = getDodgeDirectionForMovement(\n\t\t\t\tthreats,\n\t\t\t\tstate.position,\n\t\t\t);\n\t\t\tconst move = combatMove(\n\t\t\t\tstate.position,\n\t\t\t\tenemy,\n\t\t\t\tdistance,\n\t\t\t\ttargetDistance,\n\t\t\t\tdodgeDirection,\n\t\t\t\tstate.tick,\n\t\t\t\tstrafeCyclePeriod,\n\t\t\t\tstrafeIntensity,\n\t\t\t\tapproachSpeed,\n\t\t\t\tdistanceDeadZone,\n\t\t\t);\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK: Dodge missile + close gap simultaneously (first priority) ===\n\t// Blink both dodges the incoming missile AND closes distance — better than shielding.\n\t// Only shield when blink is on cooldown.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n distance > targetDistance - blinkDistanceOffset &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\tconst blinkTarget =\n\t\t\tgetBlinkToDecreaseDistance(\n\t\t\t\tstate.position,\n\t\t\t\tenemy.position,\n\t\t\t\tstate.projectiles,\n\t\t\t) ?? enemy.position;\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t};\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats (only when blink can't handle it) ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (\n\t\t\turgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3 &&\n (state.blinkCooldown > 0 || urgentThreat.ticksToImpact < 10)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// Movement: smart strafe + maintain optimal distance\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\ttargetDistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t\tdistanceDeadZone,\n\t);\n\n\t// === OFFENSE ===\n\tif (state.state === 'idle') \n\t{\n\t\t// Interrupt if enemy is casting (quick attack)\n\t\tif (enemy.state === 'casting' && distance < interruptRange) \n\t\t{\n\t\t\tconst castTime = getMissileCastTime(QUICK_CONFIG);\n\t\t\tif (isSafeToAttack(threats, castTime)) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove,\n\t\t\t\t\tstartCast: {\n\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\tconfig: QUICK_CONFIG,\n\t\t\t\t\t\tmissileAI: StandardHoming,\n\t\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// Standard attacks at combat range\n\t\tif (distance < attackRange) \n\t\t{\n\t\t\tconst heavyCastTime = getMissileCastTime(HEAVY_CONFIG);\n\t\t\tif (isSafeToAttack(threats, heavyCastTime)) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove,\n\t\t\t\t\tstartCast: {\n\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\tconfig: HEAVY_CONFIG,\n\t\t\t\t\t\tmissileAI: StandardHoming,\n\t\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Fallback: quick homing missile when heavy isn't safe (only if in range)\n\t\t\tconst quickCastTime = getMissileCastTime(QUICK_CONFIG);\n\t\t\tif (\n\t\t\t\tdistance < QUICK_RANGE * 0.9 &&\n isSafeToAttack(threats, quickCastTime)\n\t\t\t) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove,\n\t\t\t\t\tstartCast: {\n\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\tconfig: QUICK_CONFIG,\n\t\t\t\t\t\tmissileAI: StandardHoming,\n\t\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Not safe to attack — wait for threat to pass, then fire\n\t\t}\n\t}\n\n\t// === FALLBACK DEFENSE: Shield when no attack window exists ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'shield'},\n\t\t};\n\t}\n\n\treturn {move};\n};\n","import {\n\tWizardFunction,\n\tMissileAIFunction,\n\tMissileActions,\n} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tfitMissileToBudget,\n\tgetBlinkToDecreaseDistance,\n\tgetBlinkToIncreaseDistance,\n\tshouldForceShield,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 60;\n\nconst SHIELD_BUFFER = 3;\n\n/**\n * Standard homing missile AI.\n */\nconst StandardHoming: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {\n\t\tturnToward: enemy?.position,\n\t};\n};\n\n/**\n * Check if near wall.\n */\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart combat movement with threat-aware dodging.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\ttargetDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n\tdistanceDeadZone: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\t// Perpendicular directions\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\tif (currentDistance > targetDistance + distanceDeadZone) \n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < targetDistance - distanceDeadZone) \n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Warmage\n *\n * BEHAVIOR: Enhanced Battlemage with adaptive missile fitting. Uses fitMissileToBudget\n * to maximize damage within safe attack windows instead of fixed quick/heavy configs.\n * Same close-range playstyle: shields undodgeable threats, blinks to close distance\n * or escape, aggressive hit-trading when health allows. The adaptive fitting means\n * every attack is optimized for the current situation — no wasted cast time.\n *\n * PROGRESSION LINE: Battlemage → Warmage → Archmage\n * - Battlemage (tier 1): Quick/heavy fixed configs, cast interruption, basic defense\n * - Warmage (tier 2): Adaptive missile fitting, optimized damage windows\n * - Archmage (tier 3): Future — supreme duelist, perfect tactical mastery\n *\n * TIER: 2 (enhanced Battlemage)\n */\nexport const Warmage: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 188, {\n\t\trange: 150,\n\t\tmin: 0,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', 0.4, {range: 1.5, steps: 5});\n\tconst blinkEscapeDistance = useParam('blinkEscapeDistance', 42, {\n\t\trange: 190,\n\t\tmin: 0,\n\t\tsteps: 7,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 299, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 33, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 80, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 33, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 37, {\n\t\trange: 30,\n\t\tmin: 15,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst blinkDistanceOffset = useParam('blinkDistanceOffset', 100, {\n\t\trange: 40,\n\t\tmin: 0,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst attackRange = useParam('attackRange', 300, {\n\t\trange: 150,\n\t\tmin: 200,\n\t\tmax: 700,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 9.5, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast only for LETHAL incoming damage ===\n\t// DPS trade: we accepted the hit when we started casting. Only cancel to survive.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + 1\n\t\t) \n\t\t{\n\t\t\tconst dodgeDirection = getDodgeDirectionForMovement(\n\t\t\t\tthreats,\n\t\t\t\tstate.position,\n\t\t\t);\n\t\t\tconst move = combatMove(\n\t\t\t\tstate.position,\n\t\t\t\tenemy,\n\t\t\t\tdistance,\n\t\t\t\ttargetDistance,\n\t\t\t\tdodgeDirection,\n\t\t\t\tstate.tick,\n\t\t\t\tstrafeCyclePeriod,\n\t\t\t\tstrafeIntensity,\n\t\t\t\tapproachSpeed,\n\t\t\t\tdistanceDeadZone,\n\t\t\t);\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK: Dodge missile + close gap simultaneously (first priority) ===\n\t// Blink both dodges the incoming missile AND closes distance — better than shielding.\n\t// Only shield when blink is on cooldown.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n distance > targetDistance - blinkDistanceOffset &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\tconst blinkTarget =\n\t\t\tgetBlinkToDecreaseDistance(\n\t\t\t\tstate.position,\n\t\t\t\tenemy.position,\n\t\t\t\tstate.projectiles,\n\t\t\t) ?? enemy.position;\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t};\n\t}\n\n\t// === BLINK ESCAPE: Enemy too close, blink away to reestablish dueling range ===\n\t// Warmage is a duelist at 280 range — if enemy closes to melee, blink out.\n\tif (\n\t\tstate.state === 'idle' &&\n distance < blinkEscapeDistance &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst escapeTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (escapeTarget) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: escapeTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats (only when blink can't handle it) ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (\n\t\t\turgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3 &&\n (state.blinkCooldown > 0 || urgentThreat.ticksToImpact < 10)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// Movement\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\ttargetDistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t\tdistanceDeadZone,\n\t);\n\n\t// === OFFENSE: Adaptive missile fitting ===\n\tif (state.state === 'idle' && distance < attackRange) \n\t{\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Account for enemy's active cast — their missile will arrive after cast completes + flight time\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTime = distance / flightTimeDivisor;\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t);\n\t\t}\n\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Warmage moves while casting, so always need homing to compensate\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (config && config.damage >= minDmg) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI:\n config.turnRate > 0 ? StandardHoming : (): MissileActions => ({}),\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Budget too small for useful missile — wait for threat to pass, then fire\n\t}\n\n\t// === FALLBACK DEFENSE: Shield when no attack window exists ===\n\t// If all offense checks failed and there's an undodgeable threat, shield it.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'shield'},\n\t\t};\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {\n\tARENA_SIZE,\n\tGCD_DURATION,\n\tSHIELD_CAST_TIME,\n\tBLINK_CAST_TIME,\n} from '../../rules.js';\nimport {getMissileCastTime, getLeadPosition} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tfitMissileToBudget,\n\tgetBlinkToDecreaseDistance,\n\tgetBlinkToIncreaseDistance,\n\tshouldForceShield,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n\tgetEnemyVulnerabilityTicks,\n\tisSafeToAttack,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst WALL_BUFFER = 60;\nconst SHIELD_BUFFER = 3;\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\nconst StraightAI: MissileAIFunction = () => ({});\n\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart combat movement with threat-aware dodging.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\ttargetDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n\tdistanceDeadZone: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\tif (currentDistance > targetDistance + distanceDeadZone) \n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < targetDistance - distanceDeadZone) \n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Archmage\n *\n * BEHAVIOR: Versatile duelist that adapts missile choice based on distance and HP.\n * Close range → straight missiles (no turn cost = more damage). Mid/far range → homing.\n * Uses dual-blink aggressively (gap-close during vulnerability, escape when trade is bad).\n * HP-aware: ahead → aggressive close range; behind → defensive ranged kiting.\n *\n * KEY IMPROVEMENTS OVER WARMAGE:\n * - Range-adaptive missiles: straight close, homing far\n * - Vulnerability-timed blinks: gap-close during enemy cast/GCD\n * - HP-aware aggression: adjusts distance + risk tolerance based on HP differential\n * - Progressive cast-cancel: graduated thresholds\n *\n * PROGRESSION LINE: Battlemage → Warmage → Archmage\n * TIER: 3 (elite Duelist line)\n */\nexport const Archmage: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 214, {\n\t\trange: 150,\n\t\tmin: 0,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', -14.6, {range: 1.5, steps: 5});\n\tconst blinkEscapeDistance = useParam('blinkEscapeDistance', 442, {\n\t\trange: 190,\n\t\tmin: 0,\n\t\tsteps: 7,\n\t});\n\tconst punishMinVulnerability = useParam('punishMinVulnerability', 85, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tsteps: 5,\n\t});\n\tconst aggressiveHPThreshold = useParam('aggressiveHPThreshold', 4, {\n\t\trange: 20,\n\t\tmin: 0,\n\t\tsteps: 5,\n\t});\n\tconst preferBlinkDodge =\n\t\tuseParam('preferBlinkDodge', 1, {min: 0, max: 1, steps: 2}) >= 0.5;\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 248, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 250,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 100, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 57, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 37, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst aggressiveDistanceOffset = useParam('aggressiveDistanceOffset', 99, {\n\t\trange: 50,\n\t\tmin: 0,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst cautiousDistanceOffset = useParam('cautiousDistanceOffset', 42, {\n\t\trange: 50,\n\t\tmin: 0,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst cancelHeavyDmg = useParam('cancelHeavyDmg', 40, {\n\t\trange: 15,\n\t\tmin: 5,\n\t\tmax: 40,\n\t\tsteps: 5,\n\t});\n\tconst cancelMediumDmg = useParam('cancelMediumDmg', 20, {\n\t\trange: 10,\n\t\tmin: 3,\n\t\tmax: 25,\n\t\tsteps: 5,\n\t});\n\tconst cancelCastRatio = useParam('cancelCastRatio', 0.74, {\n\t\trange: 0.4,\n\t\tmin: 0.3,\n\t\tmax: 1.0,\n\t\tsteps: 5,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 100, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 29, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst blinkGapThreshold = useParam('blinkGapThreshold', 80, {\n\t\trange: 75,\n\t\tmin: 20,\n\t\tmax: 200,\n\t\tsteps: 7,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 4.5, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst closeRangeStraightThreshold = useParam(\n\t\t'closeRangeStraightThreshold',\n\t\t50,\n\t\t{range: 100, min: 50, max: 300, steps: 7},\n\t);\n\tconst safeAttackCastEstimate = useParam('safeAttackCastEstimate', 35, {\n\t\trange: 25,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\n\t// HP-aware distance: ahead → fight closer, behind → kite at range\n\tconst hpDiff = state.health - enemy.health;\n\tconst isAggressive = hpDiff >= -aggressiveHPThreshold;\n\tconst effectiveTargetDistance = isAggressive\n\t\t? targetDistance - aggressiveDistanceOffset\n\t\t: targetDistance + cautiousDistanceOffset;\n\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\teffectiveTargetDistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t\tdistanceDeadZone,\n\t);\n\n\t// === STEP 1: CHANNELING ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === STEP 2: PROGRESSIVE CAST-CANCEL ===\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tconst defenseTime =\n\t\t\tstate.blinkCooldown === 0\n\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tconst willArriveInWindow =\n\t\t\turgentThreat.ticksToImpact <= remainingCast + GCD_DURATION + defenseTime;\n\n\t\tif (willArriveInWindow) \n\t\t{\n\t\t\tconst incomingDamage = urgentThreat.projectile.damage;\n\t\t\tconst castRatio = (state.castProgress ?? 0) / (state.castDuration ?? 1);\n\n\t\t\tif (incomingDamage >= state.health || incomingDamage >= cancelHeavyDmg) \n\t\t\t{\n\t\t\t\treturn {move, cancel: true};\n\t\t\t}\n\t\t\tif (incomingDamage >= cancelMediumDmg && castRatio < cancelCastRatio) \n\t\t\t{\n\t\t\t\treturn {move, cancel: true};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === STEP 3: BLINK-DODGE — 100% damage avoidance (optimizer-controlled) ===\n\tif (\n\t\tpreferBlinkDodge &&\n state.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&\n urgentThreat.ticksToImpact >= BLINK_CAST_TIME &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\t// Aggressive: blink toward enemy to dodge + close gap\n\t\t// Defensive: blink away to dodge + increase distance\n\t\tif (isAggressive && distance > effectiveTargetDistance) \n\t\t{\n\t\t\tconst blinkTarget = getBlinkToDecreaseDistance(\n\t\t\t\tstate.position,\n\t\t\t\tenemy.position,\n\t\t\t\tstate.projectiles,\n\t\t\t);\n\t\t\tif (blinkTarget) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (blinkTarget) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 4: BLINK ESCAPE — enemy too close ===\n\tif (\n\t\tstate.state === 'idle' &&\n distance < blinkEscapeDistance &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst escapeTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (escapeTarget) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: escapeTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 5: SHIELD ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (\n\t\t\turgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= SHIELD_BUFFER &&\n (state.blinkCooldown > 0 || urgentThreat.ticksToImpact < 10)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 6: VULNERABILITY BLINK — gap-close during enemy cast/GCD ===\n\tif (\n\t\tstate.state === 'idle' &&\n state.blinkCooldown === 0 &&\n isAggressive &&\n distance > effectiveTargetDistance + blinkGapThreshold &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst vulnerabilityTicks = getEnemyVulnerabilityTicks(enemy);\n\t\tif (vulnerabilityTicks >= punishMinVulnerability) \n\t\t{\n\t\t\tconst blinkTarget = getBlinkToDecreaseDistance(\n\t\t\t\tstate.position,\n\t\t\t\tenemy.position,\n\t\t\t\tstate.projectiles,\n\t\t\t);\n\t\t\tif (blinkTarget) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === STEP 7: OFFENSE ===\n\tif (state.state === 'idle' && distance < 500) \n\t{\n\t\tconst offenseDefenseTime =\n\t\t\tstate.blinkCooldown === 0\n\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst vulnerabilityTicks = getEnemyVulnerabilityTicks(enemy);\n\n\t\t// --- PUNISH MODE: range-adaptive missiles during vulnerability ---\n\t\tif (vulnerabilityTicks >= punishMinVulnerability) \n\t\t{\n\t\t\tconst flightTimeEstimate = distance / flightTimeDivisor;\n\t\t\tconst punishBudget = vulnerabilityTicks - flightTimeEstimate;\n\t\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\t\tconst safeBudget = nextThreatTime - GCD_DURATION - offenseDefenseTime;\n\t\t\tconst effectiveBudget = Math.min(\n\t\t\t\tpunishBudget,\n\t\t\t\tsafeBudget,\n\t\t\t\tMAX_CAST_BUDGET,\n\t\t\t);\n\n\t\t\tif (effectiveBudget > 0) \n\t\t\t{\n\t\t\t\t// Close range → straight for max damage, far → light homing\n\t\t\t\tconst punishTurnRate = distance < closeRangeStraightThreshold ? 0 : 0.2;\n\t\t\t\tconst config = fitMissileToBudget(effectiveBudget, distance, {\n\t\t\t\t\tminTurnRate: punishTurnRate,\n\t\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t\t});\n\n\t\t\t\tif (config && config.damage >= MIN_USEFUL_DAMAGE) \n\t\t\t\t{\n\t\t\t\t\tconst castTime = getMissileCastTime(config, state.lastMissileConfig);\n\t\t\t\t\tconst actualFlight = distance / config.speed;\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tcastTime + actualFlight < vulnerabilityTicks &&\n isSafeToAttack(threats, castTime)\n\t\t\t\t\t) \n\t\t\t\t\t{\n\t\t\t\t\t\tconst aimTarget =\n\t\t\t\t\t\t\tconfig.turnRate > 0\n\t\t\t\t\t\t\t\t? enemy.position\n\t\t\t\t\t\t\t\t: getLeadPosition(\n\t\t\t\t\t\t\t\t\tenemy.position,\n\t\t\t\t\t\t\t\t\tenemy.velocity,\n\t\t\t\t\t\t\t\t\tconfig.speed,\n\t\t\t\t\t\t\t\t\tstate.position,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tmove,\n\t\t\t\t\t\t\tstartCast: {\n\t\t\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\tmissileAI: config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\t\t\t\tdirection: angleTo(state.position, aimTarget),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// --- STANDARD MODE: range-adaptive missiles ---\n\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\tconst safeBudget = nextThreatTime - GCD_DURATION - offenseDefenseTime;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Adapt turn rate to distance: close → straight, far → homing\n\t\tconst effectiveMinTurnRate =\n\t\t\tdistance < closeRangeStraightThreshold ? 0 : minTurnRate;\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tlet config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate: effectiveMinTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (\n\t\t\tconfig &&\n config.damage >= minDmg &&\n isSafeToAttack(threats, safeAttackCastEstimate)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Fallback: fire bigger missile, accept incoming hit\n\t\tif (!config || config.damage < minDmg) \n\t\t{\n\t\t\tconfig = fitMissileToBudget(MAX_CAST_BUDGET, distance, {\n\t\t\t\tminTurnRate: effectiveMinTurnRate,\n\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t});\n\t\t\tif (config) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove,\n\t\t\t\t\tstartCast: {\n\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\tmissileAI: config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {interceptAngle, moveInDirection} from '../../utils/movement.js';\nimport {getMissileCastTime} from '../../utils/combat.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tisSafeToAttack,\n\tgetBlinkToCenter,\n\tshouldForceShield,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 80;\n\n/**\n * Standard homing missile: higher damage for faster kills. s=5 × dur=150 = 750u range.\n */\nconst STANDARD_CONFIG = {damage: 15, speed: 5, turnRate: 1, duration: 150};\n\n/**\n * Quick attack config for use under pressure (shorter cast time).\n * Same low turn/duration for warmup compatibility.\n */\nconst QUICK_CONFIG = {damage: 10, speed: 5, turnRate: 1, duration: 80};\n\n/**\n * Predictive homing missile AI - leads the target.\n */\nconst StormchaserMissileAI: MissileAIFunction = ({\n\tworldState,\n\tmissileState,\n}) => \n{\n\tconst enemy = worldState.enemies[0];\n\tif (!enemy) return {};\n\n\tconst angle = interceptAngle(\n\t\tmissileState.position,\n\t\tenemy.position,\n\t\tenemy.velocity,\n\t\tmissileState.speed,\n\t);\n\tif (angle !== null) \n\t{\n\t\treturn {turnToward: moveInDirection(missileState.position, angle, 100)};\n\t}\n\treturn {turnToward: enemy.position};\n};\n\n/**\n * Check if position is near a wall.\n */\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart combat movement with threat-aware dodging.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\ttargetDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n\tdistanceDeadZone: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\t// Perpendicular directions for strafing\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\t// Priority 1: Use dodge direction (from threat analysis or close missile avoidance)\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\t// Priority 2: Avoid walls\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\t// Priority 3: Commit to strafe direction (unpredictability)\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\t// When actively dodging, commit fully — no approach/retreat dilution\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\t// Distance management (tighter band than other bots, only when not dodging)\n\tif (currentDistance > targetDistance + distanceDeadZone) \n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < targetDistance - distanceDeadZone) \n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\t// Normalize to max speed\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Stormchaser\n *\n * BEHAVIOR: Fights aggressively while managing defense intelligently. Uses\n * two fixed missile configs (standard homing + quick attack) with predictive\n * homing missile AI (interceptAngle on the missile itself). Blink-dodges\n * incoming threats, shields when blink is on cooldown. Tight distance\n * management (350 units, ±30 band).\n *\n * PROGRESSION LINE: Stormchaser → Stormcaller → Stormforger\n * - Stormchaser (tier 1): Fixed missiles, predictive homing AI, blink-dodge\n * - Stormcaller (tier 2): + fitMissileToBudget, predictive homing, optimized damage\n * - Stormforger (tier 3): Future — supreme berserker, perfect aggression\n *\n * TIER: 1 (base)\n */\nexport const Stormchaser: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 191, {\n\t\trange: 150,\n\t\tmin: 0,\n\t});\n\tconst healthLowThreshold = useParam('healthLowThreshold', 45, {\n\t\trange: 20,\n\t\tmin: 1,\n\t\tsteps: 5,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 130, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 42, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 68, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 26, {\n\t\trange: 25,\n\t\tmin: 10,\n\t\tmax: 60,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 25, {\n\t\trange: 30,\n\t\tmin: 15,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst cancelMinRemainingCast = useParam('cancelMinRemainingCast', 5, {\n\t\trange: 50,\n\t\tmin: 5,\n\t\tmax: 150,\n\t\tsteps: 7,\n\t});\n\tconst attackRange = useParam('attackRange', 676, {\n\t\trange: 200,\n\t\tmin: 300,\n\t\tmax: 800,\n\t});\n\tconst healthDamageRatio = useParam('healthDamageRatio', 5, {\n\t\trange: 2,\n\t\tmin: 0.5,\n\t\tmax: 5,\n\t\tsteps: 7,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// === DEFENSE: Handle channeling state ===\n\tif (state.state === 'channeling') \n\t{\n\t\t// Cancel shield if no imminent danger\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast for threats arriving during vulnerability window ===\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tconst incomingDamage = urgentThreat.projectile.damage;\n\t\tconst arrivesInGCD =\n\t\t\turgentThreat.ticksToImpact > remainingCast &&\n urgentThreat.ticksToImpact <= remainingCast + GCD_DURATION;\n\n\t\t// Cancel for undodgeable threats when trade isn't worth it\n\t\tif (!urgentThreat.bestDodgeDirection) \n\t\t{\n\t\t\tconst canSurviveHit =\n\t\t\t\tstate.health > incomingDamage * healthDamageRatio + 1;\n\t\t\tconst tradeWorthIt =\n\t\t\t\tcanSurviveHit && incomingDamage <= QUICK_CONFIG.damage;\n\t\t\tif (\n\t\t\t\t!tradeWorthIt &&\n remainingCast > cancelMinRemainingCast &&\n urgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + 1\n\t\t\t) \n\t\t\t{\n\t\t\t\tconst dodgeDir = getDodgeDirectionForMovement(threats, state.position);\n\t\t\t\tconst cancelMove = combatMove(\n\t\t\t\t\tstate.position,\n\t\t\t\t\tenemy,\n\t\t\t\t\tdistance,\n\t\t\t\t\ttargetDistance,\n\t\t\t\t\tdodgeDir,\n\t\t\t\t\tstate.tick,\n\t\t\t\t\tstrafeCyclePeriod,\n\t\t\t\t\tstrafeIntensity,\n\t\t\t\t\tapproachSpeed,\n\t\t\t\t\tdistanceDeadZone,\n\t\t\t\t);\n\t\t\t\treturn {move: cancelMove, cancel: true};\n\t\t\t}\n\t\t}\n\n\t\t// Cancel for ANY threat arriving during GCD when health is critical\n\t\t// At low health, surviving is more important than finishing the attack.\n\t\tif (\n\t\t\tarrivesInGCD &&\n state.health <= incomingDamage + 1 &&\n remainingCast > 10\n\t\t) \n\t\t{\n\t\t\tconst dodgeDir = getDodgeDirectionForMovement(threats, state.position);\n\t\t\tconst cancelMove = combatMove(\n\t\t\t\tstate.position,\n\t\t\t\tenemy,\n\t\t\t\tdistance,\n\t\t\t\ttargetDistance,\n\t\t\t\tdodgeDir,\n\t\t\t\tstate.tick,\n\t\t\t\tstrafeCyclePeriod,\n\t\t\t\tstrafeIntensity,\n\t\t\t\tapproachSpeed,\n\t\t\t\tdistanceDeadZone,\n\t\t\t);\n\t\t\treturn {move: cancelMove, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK: Dodge missile + reposition (first priority) ===\n\t// Blink dodges the incoming missile — no GCD after, so can immediately fire back.\n\t// Only shield when blink is on cooldown.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t};\n\t}\n\n\t// === DEFENSE: Shield threats (when blink is on cooldown) ===\n\tif (state.state === 'idle' && urgentThreat) \n\t{\n\t\tconst forceShield = shouldForceShield(urgentThreat);\n\t\tconst undodgeable = !urgentThreat.bestDodgeDirection;\n\t\tconst healthLow = state.health < healthLowThreshold;\n\t\tconst healthCritical =\n\t\t\tstate.health <= urgentThreat.projectile.damage * healthDamageRatio + 1;\n\t\tconst shouldShield =\n\t\t\tforceShield || (undodgeable && healthLow) || healthCritical;\n\n\t\tif (shouldShield && urgentThreat.canBlockInTime) \n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= 3) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === MOVEMENT: Smart combat movement ===\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\ttargetDistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t\tdistanceDeadZone,\n\t);\n\n\t// === OFFENSE: Attack aggressively while moving ===\n\tif (state.state === 'idle' && distance < attackRange) \n\t{\n\t\t// Primary: standard homing missile\n\t\tconst standardCastTime = getMissileCastTime(STANDARD_CONFIG);\n\t\tif (isSafeToAttack(threats, standardCastTime)) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: STANDARD_CONFIG,\n\t\t\t\t\tmissileAI: StormchaserMissileAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Fallback: quick missile under pressure (shorter cast = smaller vulnerability window)\n\t\tconst quickCastTime = getMissileCastTime(QUICK_CONFIG);\n\t\tif (isSafeToAttack(threats, quickCastTime)) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: QUICK_CONFIG,\n\t\t\t\t\tmissileAI: StormchaserMissileAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Not safe to attack — wait for threat to pass, then fire\n\t}\n\n\t// === FALLBACK DEFENSE: Shield when no attack window exists ===\n\t// If all offense checks failed and there's an undodgeable threat, shield it\n\t// rather than sitting idle and taking the hit.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'shield'},\n\t\t};\n\t}\n\n\t// Default: just move (dodging/positioning)\n\treturn {move};\n};\n","import {\n\tWizardFunction,\n\tMissileAIFunction,\n\tMissileActions,\n} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {interceptAngle, moveInDirection} from '../../utils/movement.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n\tfitMissileToBudget,\n\tshouldForceShield,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 80;\n\nconst SHIELD_BUFFER = 3;\n\n/**\n * Predictive homing missile AI - leads the target.\n */\nconst StormcallerMissileAI: MissileAIFunction = ({\n\tworldState,\n\tmissileState,\n}) => \n{\n\tconst enemy = worldState.enemies[0];\n\tif (!enemy) return {};\n\n\tconst angle = interceptAngle(\n\t\tmissileState.position,\n\t\tenemy.position,\n\t\tenemy.velocity,\n\t\tmissileState.speed,\n\t);\n\tif (angle !== null) \n\t{\n\t\treturn {turnToward: moveInDirection(missileState.position, angle, 100)};\n\t}\n\treturn {turnToward: enemy.position};\n};\n\n/**\n * Check if position is near a wall.\n */\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart combat movement with threat-aware dodging.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\ttargetDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n\tdistanceDeadZone: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\tif (currentDistance > targetDistance + distanceDeadZone) \n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < targetDistance - distanceDeadZone) \n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Stormcaller\n *\n * BEHAVIOR: Enhanced Stormchaser with adaptive missile fitting (fitMissileToBudget)\n * AND predictive homing missiles. Combines aggressive fighting philosophy with\n * optimized damage output. Uses budget-based missile fitting to maximize damage\n * within safe windows. Falls back to quick missiles under pressure. Same smart\n * trade/shield decisions as Stormchaser but with better resource usage.\n *\n * PROGRESSION LINE: Stormchaser → Stormcaller → Stormforger\n * - Stormchaser (tier 1): Adaptive missiles, smart trading, aggressive defense\n * - Stormcaller (tier 2): + fitMissileToBudget, predictive homing, optimized damage\n * - Stormforger (tier 3): Future — supreme berserker, perfect aggression\n *\n * TIER: 2 (enhanced Stormchaser)\n */\nexport const Stormcaller: WizardFunction = ({state}) => \n{\n\t// Tunable parameters (run optimizer script to find optimal values)\n\tconst targetDistance = useParam('targetDistance', 129, {\n\t\trange: 200,\n\t\tmin: 0,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', 0.5, {range: 1.5, steps: 5});\n\tconst shieldHealthThreshold = useParam('shieldHealthThreshold', 20, {\n\t\trange: 23,\n\t\tmin: 1,\n\t\tsteps: 7,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 281, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 69, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 69, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 18, {\n\t\trange: 25,\n\t\tmin: 10,\n\t\tmax: 60,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 70, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst attackRange = useParam('attackRange', 430, {\n\t\trange: 200,\n\t\tmin: 300,\n\t\tmax: 800,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 10.8, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// === DEFENSE: Handle channeling state ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast only for LETHAL incoming damage ===\n\t// DPS trade: we accepted the hit when we started casting. Only cancel to survive.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + 1\n\t\t) \n\t\t{\n\t\t\tconst dodgeDirection = getDodgeDirectionForMovement(\n\t\t\t\tthreats,\n\t\t\t\tstate.position,\n\t\t\t);\n\t\t\tconst move = combatMove(\n\t\t\t\tstate.position,\n\t\t\t\tenemy,\n\t\t\t\tdistance,\n\t\t\t\ttargetDistance,\n\t\t\t\tdodgeDirection,\n\t\t\t\tstate.tick,\n\t\t\t\tstrafeCyclePeriod,\n\t\t\t\tstrafeIntensity,\n\t\t\t\tapproachSpeed,\n\t\t\t\tdistanceDeadZone,\n\t\t\t);\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK: Dodge missile + reposition (first priority) ===\n\t// Blink dodges the incoming missile — no GCD after, so can immediately fire back.\n\t// Only shield when blink is on cooldown.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t};\n\t}\n\n\t// === DEFENSE: Shield threats (when blink is on cooldown) ===\n\tif (state.state === 'idle' && urgentThreat) \n\t{\n\t\tconst forceShield = shouldForceShield(urgentThreat);\n\t\tconst undodgeable = !urgentThreat.bestDodgeDirection;\n\t\tconst healthLow = state.health < shieldHealthThreshold;\n\t\tconst healthCritical =\n\t\t\tstate.health <= urgentThreat.projectile.damage * 2 + 1;\n\t\tconst doShield =\n\t\t\tforceShield || (undodgeable && healthLow) || healthCritical;\n\n\t\tif (doShield && urgentThreat.canBlockInTime) \n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= 3) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === MOVEMENT: Smart combat movement ===\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\ttargetDistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t\tdistanceDeadZone,\n\t);\n\n\t// === OFFENSE: Adaptive missile fitting with predictive homing ===\n\tif (state.state === 'idle' && distance < attackRange) \n\t{\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Account for enemy's active cast — their missile will arrive after cast completes + flight time\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTime = distance / flightTimeDivisor;\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t);\n\t\t}\n\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Predictive homing compensates for movement during cast\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (config && config.damage >= minDmg) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI:\n config.turnRate > 0\n \t? StormcallerMissileAI\n \t: (): MissileActions => ({}),\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Budget too small for useful missile — wait for threat to pass, then fire\n\t}\n\n\t// === FALLBACK DEFENSE: Shield when no attack window exists ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'shield'},\n\t\t};\n\t}\n\n\treturn {move};\n};\n","import {\n\tWizardFunction,\n\tMissileAIFunction,\n\tMissileActions,\n} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {interceptAngle, moveInDirection} from '../../utils/movement.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {getMissileCastTime, getLeadPosition} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n\tfitMissileToBudget,\n\tshouldForceShield,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n\tgetEnemyVulnerabilityTicks,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst WALL_BUFFER = 80;\n\nconst SHIELD_BUFFER = 3;\n\n/**\n * Predictive homing missile AI - leads the target.\n */\nconst HomingAI: MissileAIFunction = ({\n\tworldState,\n\tmissileState,\n}) =>\n{\n\tconst enemy = worldState.enemies[0];\n\tif (!enemy) return {};\n\n\tconst angle = interceptAngle(\n\t\tmissileState.position,\n\t\tenemy.position,\n\t\tenemy.velocity,\n\t\tmissileState.speed,\n\t);\n\tif (angle !== null)\n\t{\n\t\treturn {turnToward: moveInDirection(missileState.position, angle, 100)};\n\t}\n\treturn {turnToward: enemy.position};\n};\n\nconst StraightAI: MissileAIFunction = () => ({});\n\n/**\n * Check if position is near a wall.\n */\nfunction isNearWall(position: {x: number; y: number}): boolean\n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart combat movement with threat-aware dodging.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\ttargetDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n\tdistanceDeadZone: number,\n): {x: number; y: number}\n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection)\n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (isNearWall(position))\n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse\n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tif (dodgeDirection)\n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\tif (currentDistance > targetDistance + distanceDeadZone)\n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < targetDistance - distanceDeadZone)\n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100)\n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Stormforger\n *\n * BEHAVIOR: Enhanced Stormcaller with vulnerability exploitation. Takes the exact\n * Stormcaller foundation (adaptive missile fitting + predictive homing) and adds\n * a punish mode that fires fast straight missiles when the enemy is locked in\n * GCD or cast animation. During vulnerability windows, uses getLeadPosition for\n * accurate straight shots that arrive before the enemy can react.\n *\n * PROGRESSION LINE: Stormchaser → Stormcaller → Stormforger\n * - Stormchaser (tier 1): Adaptive missiles, smart trading, aggressive defense\n * - Stormcaller (tier 2): + fitMissileToBudget, predictive homing, optimized damage\n * - Stormforger (tier 3): + vulnerability exploitation, punish missiles during enemy GCD\n *\n * TIER: 3 (elite Berserker line)\n */\nexport const Stormforger: WizardFunction = ({state}) =>\n{\n\t// Tunable parameters (run optimizer script to find optimal values)\n\tconst targetDistance = useParam('targetDistance', 271, {\n\t\trange: 200,\n\t\tmin: 0,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', 0.5, {range: 1.5, steps: 5});\n\tconst shieldHealthThreshold = useParam('shieldHealthThreshold', 20, {\n\t\trange: 23,\n\t\tmin: 1,\n\t\tsteps: 7,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 300, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 72, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 69, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 21, {\n\t\trange: 25,\n\t\tmin: 10,\n\t\tmax: 60,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 81, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst attackRange = useParam('attackRange', 339, {\n\t\trange: 200,\n\t\tmin: 300,\n\t\tmax: 800,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 11.4, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst punishMinVulnerability = useParam('punishMinVulnerability', 30, {\n\t\trange: 80,\n\t\tmin: 30,\n\t\tsteps: 7,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy)\n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// === DEFENSE: Handle channeling state ===\n\tif (state.state === 'channeling')\n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold)\n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast only for LETHAL incoming damage ===\n\t// DPS trade: we accepted the hit when we started casting. Only cancel to survive.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t)\n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + 1\n\t\t)\n\t\t{\n\t\t\tconst dodgeDirection = getDodgeDirectionForMovement(\n\t\t\t\tthreats,\n\t\t\t\tstate.position,\n\t\t\t);\n\t\t\tconst move = combatMove(\n\t\t\t\tstate.position,\n\t\t\t\tenemy,\n\t\t\t\tdistance,\n\t\t\t\ttargetDistance,\n\t\t\t\tdodgeDirection,\n\t\t\t\tstate.tick,\n\t\t\t\tstrafeCyclePeriod,\n\t\t\t\tstrafeIntensity,\n\t\t\t\tapproachSpeed,\n\t\t\t\tdistanceDeadZone,\n\t\t\t);\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK: Dodge missile + reposition (first priority) ===\n\t// Blink dodges the incoming missile — no GCD after, so can immediately fire back.\n\t// Only shield when blink is on cooldown.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t)\n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t};\n\t}\n\n\t// === DEFENSE: Shield threats (when blink is on cooldown) ===\n\tif (state.state === 'idle' && urgentThreat)\n\t{\n\t\tconst forceShield = shouldForceShield(urgentThreat);\n\t\tconst undodgeable = !urgentThreat.bestDodgeDirection;\n\t\tconst healthLow = state.health < shieldHealthThreshold;\n\t\tconst healthCritical =\n\t\t\tstate.health <= urgentThreat.projectile.damage * 2 + 1;\n\t\tconst doShield =\n\t\t\tforceShield || (undodgeable && healthLow) || healthCritical;\n\n\t\tif (doShield && urgentThreat.canBlockInTime)\n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= 3)\n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === MOVEMENT: Smart combat movement ===\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\ttargetDistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t\tdistanceDeadZone,\n\t);\n\n\t// === OFFENSE: Vulnerability punish + adaptive missile fitting ===\n\tif (state.state === 'idle' && distance < attackRange)\n\t{\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst vulnerabilityTicks = getEnemyVulnerabilityTicks(enemy);\n\n\t\t// --- PUNISH MODE: fast straight missiles during enemy GCD/cast ---\n\t\tif (\n\t\t\tvulnerabilityTicks >= punishMinVulnerability &&\n distance < 500\n\t\t)\n\t\t{\n\t\t\tconst punishBudget = vulnerabilityTicks - distance / flightTimeDivisor;\n\n\t\t\tif (punishBudget > 0)\n\t\t\t{\n\t\t\t\tconst config = fitMissileToBudget(\n\t\t\t\t\tMath.min(punishBudget, MAX_CAST_BUDGET),\n\t\t\t\t\tdistance,\n\t\t\t\t\t{\n\t\t\t\t\t\tminTurnRate: 0,\n\t\t\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\tif (config && config.damage >= MIN_USEFUL_DAMAGE)\n\t\t\t\t{\n\t\t\t\t\tconst castTime = getMissileCastTime(config, state.lastMissileConfig);\n\t\t\t\t\tconst flightTime = distance / config.speed;\n\n\t\t\t\t\tif (castTime + flightTime < vulnerabilityTicks)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst aimTarget =\n\t\t\t\t\t\t\tconfig.turnRate > 0\n\t\t\t\t\t\t\t\t? enemy.position\n\t\t\t\t\t\t\t\t: getLeadPosition(\n\t\t\t\t\t\t\t\t\tenemy.position,\n\t\t\t\t\t\t\t\t\tenemy.velocity,\n\t\t\t\t\t\t\t\t\tconfig.speed,\n\t\t\t\t\t\t\t\t\tstate.position,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tmove,\n\t\t\t\t\t\t\tstartCast: {\n\t\t\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\tmissileAI:\n config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\t\t\t\tdirection: angleTo(state.position, aimTarget),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// --- STANDARD MODE: adaptive missile fitting with predictive homing ---\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Account for enemy's active cast — their missile will arrive after cast completes + flight time\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile')\n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTime = distance / flightTimeDivisor;\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t);\n\t\t}\n\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Predictive homing compensates for movement during cast\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (config && config.damage >= minDmg)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI:\n config.turnRate > 0\n \t? HomingAI\n \t: (): MissileActions => ({}),\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Budget too small for useful missile — wait for threat to pass, then fire\n\t}\n\n\t// === FALLBACK DEFENSE: Shield when no attack window exists ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3\n\t)\n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'shield'},\n\t\t};\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {getMissileCastTime} from '../../utils/combat.js';\nimport {ARENA_SIZE, GCD_DURATION} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tisSafeToAttack,\n\tgetBlinkToCenter,\n\tgetBlinkToIncreaseDistance,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tshouldForceShield,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 60;\n\n/**\n * Standard homing missile: higher damage, good range. s=5 × dur=150 = 750u range.\n */\nconst STANDARD_CONFIG = {damage: 15, speed: 5, turnRate: 1, duration: 150};\n\n/**\n * Quick homing missile for firing under pressure.\n * Similar speed/turn for warmup compatibility.\n */\nconst QUICK_CONFIG = {damage: 10, speed: 5, turnRate: 1, duration: 80};\n\n/**\n * Standard homing missile AI.\n */\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {\n\t\tturnToward: enemy?.position,\n\t};\n};\n\n/**\n * Check if near wall.\n */\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart strafe with threat-aware dodging and wall avoidance.\n */\nfunction smartStrafe(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\tif (normalizedDistance === 0) return {x: 0, y: 0};\n\n\t// Perpendicular directions\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDir: {x: number; y: number};\n\n\t// Priority 1: Use dodge direction (from threat analysis or close missile avoidance)\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDir = dodgeDirection;\n\t}\n\t// Priority 2: Avoid walls\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDir =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\t// Priority 3: Commit to direction (switch every ~200 ticks)\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDir = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\t// When actively dodging, commit fully — no approach/retreat dilution\n\tconst intensity = dodgeDirection ? 100 : strafeIntensity;\n\treturn {x: strafeDir.x * intensity, y: strafeDir.y * intensity};\n}\n\n/**\n * Bot: Spellspinner\n *\n * BEHAVIOR: Maintains medium range (350 units), strafes constantly to dodge\n * missiles, and fires homing missiles (damage 10, speed 4, turnRate 2). Heavy\n * emphasis on movement — 80% strafe intensity when not dodging, 100% when dodging.\n * Shields only undodgeable threats, emergency blinks when shield isn't available.\n * The constant circular strafing motion traces patterns like thread being spun.\n *\n * NAMING RATIONALE: Like a spider spinning a web of projectiles while circling its\n * prey. The constant strafing movement pattern traces circles — spinning thread\n * around the arena. \"Spell\" + \"spinner\" = a wizard who spins spells around the\n * battlefield. The kiting behavior (maintaining distance while attacking) creates\n * a web-like pattern of missiles and movement that traps opponents.\n *\n * PROGRESSION LINE: Spellspinner → Spellweaver → Spellbinder\n * - Spellspinner (tier 1): Fixed homing missiles, constant strafe, basic defense\n * - Spellweaver (tier 2): + adaptive missile fitting, more sophisticated patterns\n * - Spellbinder (tier 3): Future — inescapable web of magic, perfect distance control\n * The progression: spinner (raw thread) → weaver (creates patterns) → binder\n * (constrains and traps). Each tier's projectile web becomes harder to escape.\n *\n * TIER: 1 (base)\n */\nexport const Spellspinner: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 338, {\n\t\trange: 150,\n\t\tmin: 150,\n\t\tmax: 450,\n\t});\n\tconst dangerDistance = useParam('dangerDistance', 242, {\n\t\trange: 100,\n\t\tmin: 100,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 210, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 31, {\n\t\trange: 40,\n\t\tmin: 30,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 25, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 40, {\n\t\trange: 30,\n\t\tmin: 5,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 68, {\n\t\trange: 30,\n\t\tmin: 15,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// Smart strafe (dodge direction includes close homing missile avoidance)\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst strafe = smartStrafe(\n\t\tstate.position,\n\t\tenemy,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t);\n\n\t// Distance management (only when not dodging)\n\tlet moveX = strafe.x;\n\tlet moveY = strafe.y;\n\n\tif (!dodgeDirection) \n\t{\n\t\tconst deltaX = enemy.position.x - state.position.x;\n\t\tconst deltaY = enemy.position.y - state.position.y;\n\t\tconst normalizedDistance = distance > 0 ? distance : 1;\n\n\t\tif (distance > targetDistance + distanceDeadZone) \n\t\t{\n\t\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t\telse if (distance < targetDistance - distanceDeadZone) \n\t\t{\n\t\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\t\tif (state.position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\t\tif (state.position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0)\n\t\t\t\tretreatX = 0;\n\t\t\tif (state.position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\t\tif (state.position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0)\n\t\t\t\tretreatY = 0;\n\t\t\tmoveX += retreatX;\n\t\t\tmoveY += retreatY;\n\t\t}\n\t}\n\n\t// Normalize\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\tconst move = {x: moveX, y: moveY};\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\t// Cancel shield if no imminent threats\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}}; // Hold shield if still threatened\n\t}\n\n\t// === DEFENSE: Cancel missile cast if lethal threat arrives during GCD ===\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tconst arrivesInGCD =\n\t\t\turgentThreat.ticksToImpact > remainingCast &&\n urgentThreat.ticksToImpact <= remainingCast + GCD_DURATION;\n\n\t\tif (\n\t\t\tarrivesInGCD &&\n state.health <= urgentThreat.projectile.damage + 1 &&\n remainingCast > 10\n\t\t) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK-DODGE: For undodgeable threats OR to create attack window ===\n\t// Blink to center (stays in combat range) and dodges the current missile.\n\t// Also blink when persistent threats block all offense (creates safe firing window).\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\tconst undodgeableOrForced =\n\t\t\t!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat);\n\t\tconst cannotAttack = !isSafeToAttack(\n\t\t\tthreats,\n\t\t\tgetMissileCastTime(QUICK_CONFIG),\n\t\t);\n\n\t\tif (undodgeableOrForced || cannotAttack) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === DEFENSE: Shield undodgeable/high-damage threats (when blink on cooldown) ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (urgentThreat.canBlockInTime) \n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= 3) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === DISTANCE BLINK: enemy too close, blink to reestablish kiting range ===\n\tif (\n\t\tstate.state === 'idle' &&\n distance < dangerDistance &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (blinkTarget) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === OFFENSE: Attack when safe ===\n\tif (state.state === 'idle' && distance < 500) \n\t{\n\t\t// Primary: standard homing missile\n\t\tconst standardCastTime = getMissileCastTime(STANDARD_CONFIG);\n\t\tif (isSafeToAttack(threats, standardCastTime)) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: STANDARD_CONFIG,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Fallback: quick missile under pressure\n\t\tconst quickCastTime = getMissileCastTime(QUICK_CONFIG);\n\t\tif (isSafeToAttack(threats, quickCastTime)) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: QUICK_CONFIG,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n\tgetBlinkToIncreaseDistance,\n\tfitMissileToBudget,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tshouldForceShield,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 60;\n\nconst SHIELD_BUFFER = 3;\n\n/**\n * Standard homing missile AI.\n */\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {\n\t\tturnToward: enemy?.position,\n\t};\n};\n\n/**\n * Check if near wall.\n */\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart strafe with threat-aware dodging and wall avoidance.\n */\nfunction smartStrafe(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\tif (normalizedDistance === 0) return {x: 0, y: 0};\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDir: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDir = dodgeDirection;\n\t}\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDir =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDir = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tconst intensity = dodgeDirection ? 100 : strafeIntensity;\n\treturn {x: strafeDir.x * intensity, y: strafeDir.y * intensity};\n}\n\n/**\n * Bot: Spellweaver\n *\n * BEHAVIOR: Enhanced Spellspinner with adaptive missile fitting. Same medium-range\n * kiting playstyle — maintains distance, strafes heavily — but uses fitMissileToBudget\n * to maximize damage within safe attack windows. Always uses homing missiles since\n * kiting means enemies are always moving. More sophisticated than Spellspinner's\n * fixed damage/speed/turnRate configuration.\n *\n * NAMING RATIONALE: A weaver creates intricate patterns from raw thread. Where the\n * Spellspinner produces raw threads of magic (fixed missiles), the Spellweaver\n * combines them into optimized patterns (adaptive fitting). The name suggests\n * craftsmanship and sophistication — the same kiting web, but deliberately woven\n * rather than chaotically spun.\n *\n * PROGRESSION LINE: Spellspinner → Spellweaver → Spellbinder\n * - Spellspinner (tier 1): Fixed homing missiles, constant strafe, basic defense\n * - Spellweaver (tier 2): + adaptive missile fitting, more sophisticated patterns\n * - Spellbinder (tier 3): Future — inescapable web, perfect distance control\n *\n * TIER: 2 (enhanced Spellspinner)\n */\nexport const Spellweaver: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 322, {\n\t\trange: 150,\n\t\tmin: 0,\n\t});\n\tconst dangerDistance = useParam('dangerDistance', 272, {\n\t\trange: 125,\n\t\tmin: 0,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', 0.2, {range: 1.5, steps: 5});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 296, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 42, {\n\t\trange: 40,\n\t\tmin: 30,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 68, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 12, {\n\t\trange: 30,\n\t\tmin: 5,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 2, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// Smart strafe\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst strafe = smartStrafe(\n\t\tstate.position,\n\t\tenemy,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t);\n\n\t// Distance management (only when not dodging)\n\tlet moveX = strafe.x;\n\tlet moveY = strafe.y;\n\n\tif (!dodgeDirection) \n\t{\n\t\tconst deltaX = enemy.position.x - state.position.x;\n\t\tconst deltaY = enemy.position.y - state.position.y;\n\t\tconst normalizedDistance = distance > 0 ? distance : 1;\n\n\t\tif (distance > targetDistance + distanceDeadZone) \n\t\t{\n\t\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t\telse if (distance < targetDistance - distanceDeadZone) \n\t\t{\n\t\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\t\tif (state.position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\t\tif (state.position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0)\n\t\t\t\tretreatX = 0;\n\t\t\tif (state.position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\t\tif (state.position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0)\n\t\t\t\tretreatY = 0;\n\t\t\tmoveX += retreatX;\n\t\t\tmoveY += retreatY;\n\t\t}\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\tconst move = {x: moveX, y: moveY};\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast only for LETHAL incoming damage ===\n\t// DPS trade: we accepted the hit when we started casting. Only cancel to survive.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER\n\t\t) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (urgentThreat.canBlockInTime) \n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= SHIELD_BUFFER) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\telse if (state.blinkCooldown === 0) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === DISTANCE BLINK: enemy too close, blink to reestablish kiting range ===\n\tif (\n\t\tstate.state === 'idle' &&\n distance < dangerDistance &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (blinkTarget) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === OFFENSE: Adaptive missile fitting ===\n\t// Skip offense when a lethal missile could reach us during cast+GCD — dodge at full speed instead\n\tconst myProjectileIds = new Set(state.myProjectiles.map((p) => p.id));\n\tconst hasNearbyLethalMissile = state.projectiles.some(\n\t\t(p) =>\n\t\t\t!myProjectileIds.has(p.id) &&\n p.damage >= state.health &&\n distanceTo(state.position, p.position) / p.speed <\n MAX_CAST_BUDGET + GCD_DURATION,\n\t);\n\tif (state.state === 'idle' && distance < 600 && !hasNearbyLethalMissile) \n\t{\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Account for enemy's active cast\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTime = distance / flightTimeDivisor;\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t);\n\t\t}\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Spellweaver always uses homing (kiting means enemies are always moving)\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (config && config.damage >= minDmg) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Budget too small for useful missile — wait for threat to pass, then fire\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {getMissileCastTime, getLeadPosition} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n\tgetBlinkToIncreaseDistance,\n\tfitMissileToBudget,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tshouldForceShield,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n\tgetEnemyVulnerabilityTicks,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 60;\n\nconst SHIELD_BUFFER = 3;\n\n/**\n * Standard homing missile AI.\n */\nconst HomingAI: MissileAIFunction = ({worldState}) =>\n{\n\tconst enemy = worldState.enemies[0];\n\treturn {\n\t\tturnToward: enemy?.position,\n\t};\n};\n\n/**\n * Straight missile AI — no homing, flies in initial direction.\n */\nconst StraightAI: MissileAIFunction = () => ({});\n\n/**\n * Check if near wall.\n */\nfunction isNearWall(position: {x: number; y: number}): boolean\n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart strafe with threat-aware dodging and wall avoidance.\n */\nfunction smartStrafe(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n): {x: number; y: number}\n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\tif (normalizedDistance === 0) return {x: 0, y: 0};\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDir: {x: number; y: number};\n\n\tif (dodgeDirection)\n\t{\n\t\tstrafeDir = dodgeDirection;\n\t}\n\telse if (isNearWall(position))\n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDir =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse\n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDir = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tconst intensity = dodgeDirection ? 100 : strafeIntensity;\n\treturn {x: strafeDir.x * intensity, y: strafeDir.y * intensity};\n}\n\n/**\n * Bot: Spellbinder\n *\n * BEHAVIOR: Enhanced Spellweaver with vulnerability exploitation. Same medium-range\n * kiting playstyle — maintains distance, strafes heavily, uses fitMissileToBudget\n * for adaptive homing missiles. The T3 upgrade adds a punish mode that fires fast\n * straight missiles timed to land while the enemy is locked in a cast or GCD,\n * when they cannot shield. Defense, movement, and standard offense are identical\n * to Spellweaver.\n *\n * NAMING RATIONALE: A binder constrains and locks down opponents. Where the\n * Spellweaver optimizes missile patterns (adaptive fitting), the Spellbinder\n * reads the enemy's state and punishes vulnerability windows — binding them\n * to their commitments with unavoidable damage.\n *\n * PROGRESSION LINE: Spellspinner → Spellweaver → Spellbinder\n * - Spellspinner (tier 1): Fixed homing missiles, constant strafe, basic defense\n * - Spellweaver (tier 2): + adaptive missile fitting, more sophisticated patterns\n * - Spellbinder (tier 3): + vulnerability punish mode with fast straight missiles\n *\n * TIER: 3 (elite Spellspinner line)\n */\nexport const Spellbinder: WizardFunction = ({state}) =>\n{\n\tconst targetDistance = useParam('targetDistance', 134, {\n\t\trange: 150,\n\t\tmin: 0,\n\t});\n\tconst dangerDistance = useParam('dangerDistance', 389, {\n\t\trange: 125,\n\t\tmin: 0,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', 0.2, {range: 1.5, steps: 5});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 123, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 30, {\n\t\trange: 40,\n\t\tmin: 30,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 50, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 7, {\n\t\trange: 30,\n\t\tmin: 5,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 10.8, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst punishMinVulnerability = useParam('punishMinVulnerability', 30, {\n\t\trange: 80,\n\t\tmin: 30,\n\t\tsteps: 7,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy)\n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// Smart strafe\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst strafe = smartStrafe(\n\t\tstate.position,\n\t\tenemy,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t);\n\n\t// Distance management (only when not dodging)\n\tlet moveX = strafe.x;\n\tlet moveY = strafe.y;\n\n\tif (!dodgeDirection)\n\t{\n\t\tconst deltaX = enemy.position.x - state.position.x;\n\t\tconst deltaY = enemy.position.y - state.position.y;\n\t\tconst normalizedDistance = distance > 0 ? distance : 1;\n\n\t\tif (distance > targetDistance + distanceDeadZone)\n\t\t{\n\t\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t\telse if (distance < targetDistance - distanceDeadZone)\n\t\t{\n\t\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\t\tif (state.position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\t\tif (state.position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0)\n\t\t\t\tretreatX = 0;\n\t\t\tif (state.position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\t\tif (state.position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0)\n\t\t\t\tretreatY = 0;\n\t\t\tmoveX += retreatX;\n\t\t\tmoveY += retreatY;\n\t\t}\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100)\n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\tconst move = {x: moveX, y: moveY};\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling')\n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold)\n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast only for LETHAL incoming damage ===\n\t// DPS trade: we accepted the hit when we started casting. Only cancel to survive.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t)\n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER\n\t\t)\n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t)\n\t{\n\t\tif (urgentThreat.canBlockInTime)\n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= SHIELD_BUFFER)\n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\telse if (state.blinkCooldown === 0)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === DISTANCE BLINK: enemy too close, blink to reestablish kiting range ===\n\tif (\n\t\tstate.state === 'idle' &&\n distance < dangerDistance &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t)\n\t{\n\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (blinkTarget)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === OFFENSE: Punish mode — fast straight missiles during enemy vulnerability ===\n\tif (\n\t\tstate.state === 'idle' &&\n getEnemyVulnerabilityTicks(enemy) >= punishMinVulnerability &&\n distance < 500\n\t)\n\t{\n\t\tconst vulnerabilityTicks = getEnemyVulnerabilityTicks(enemy);\n\t\tconst flightTimeEstimate = distance / flightTimeDivisor;\n\t\tconst punishBudget = vulnerabilityTicks - flightTimeEstimate;\n\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst effectiveBudget = Math.min(\n\t\t\tpunishBudget,\n\t\t\tsafeBudget,\n\t\t\tMAX_CAST_BUDGET,\n\t\t);\n\n\t\tif (effectiveBudget > 0)\n\t\t{\n\t\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\t\tconst config = fitMissileToBudget(effectiveBudget, distance, {\n\t\t\t\tminTurnRate: 0,\n\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t});\n\n\t\t\tif (config && config.damage >= MIN_USEFUL_DAMAGE)\n\t\t\t{\n\t\t\t\tconst castTime = getMissileCastTime(config, state.lastMissileConfig);\n\t\t\t\tconst actualFlight = distance / config.speed;\n\n\t\t\t\tif (castTime + actualFlight < vulnerabilityTicks)\n\t\t\t\t{\n\t\t\t\t\tconst aimTarget =\n\t\t\t\t\t\tconfig.turnRate > 0\n\t\t\t\t\t\t\t? enemy.position\n\t\t\t\t\t\t\t: getLeadPosition(\n\t\t\t\t\t\t\t\tenemy.position,\n\t\t\t\t\t\t\t\tenemy.velocity,\n\t\t\t\t\t\t\t\tconfig.speed,\n\t\t\t\t\t\t\t\tstate.position,\n\t\t\t\t\t\t\t);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tmove,\n\t\t\t\t\t\tstartCast: {\n\t\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\tmissileAI: config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\t\t\tdirection: angleTo(state.position, aimTarget),\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// === OFFENSE: Adaptive missile fitting (identical to Spellweaver) ===\n\t// Skip offense when a lethal missile could reach us during cast+GCD — dodge at full speed instead\n\tconst myProjectileIds = new Set(state.myProjectiles.map((p) => p.id));\n\tconst hasNearbyLethalMissile = state.projectiles.some(\n\t\t(p) =>\n\t\t\t!myProjectileIds.has(p.id) &&\n p.damage >= state.health &&\n distanceTo(state.position, p.position) / p.speed <\n MAX_CAST_BUDGET + GCD_DURATION,\n\t);\n\tif (state.state === 'idle' && distance < 600 && !hasNearbyLethalMissile)\n\t{\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Account for enemy's active cast\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile')\n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTime = distance / flightTimeDivisor;\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t);\n\t\t}\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Spellbinder always uses homing in standard mode (kiting means enemies are always moving)\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (config && config.damage >= minDmg)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Budget too small for useful missile — wait for threat to pass, then fire\n\t}\n\n\treturn {move};\n};\n","/**\r\n * VIBEMANCER - BOT REGISTRY\r\n *\r\n * Single source of truth for all bots, ordered from weakest to strongest.\r\n * Run the tournament test to determine the correct ordering.\r\n *\r\n * To reorder: run `npx vitest run tests/bots/tournament.test.ts`\r\n * and update the list below based on the results.\r\n *\r\n * BOT NAMING SCHEME (3-tier progression):\r\n *\r\n * | Group | Tier 1 (base) | Tier 2 (enhanced) | Tier 3 (elite) |\r\n * |------------|----------------|-------------------|-----------------|\r\n * | Defensive | Turtle | Sentinel | Golem |\r\n * | Duelist | Battlemage | Warmage | Archmage |\r\n * | Homing | Bonemancer | Lich | Archlich |\r\n * | Caster | Flamecaller | Pyromancer | Infernalist |\r\n * | Melee | Shadowblade | Nightblade | Voidblade |\r\n * | Sniper | Spellshot | Spelltracer | Spellseeker |\r\n * | Berserker | Stormchaser | Stormcaller | Stormforger |\r\n * | Kiter | Spellspinner | Spellweaver | Spellbinder |\r\n *\r\n * Standalone: TargetDummy, Critter, Hogger, Rookie, Doombringer\r\n * Reserved: Spellslinger (future volume-of-fire archetype)\r\n *\r\n */\r\n\r\nimport {WizardFunction} from '../types.js';\r\nimport {TargetDummy} from './standalone/TargetDummy.js';\r\nimport {Critter} from './standalone/Critter.js';\r\nimport {Rookie} from './standalone/Rookie.js';\r\nimport {Hogger} from './standalone/Hogger.js';\r\nimport {Doombringer} from './standalone/Doombringer.js';\r\nimport {Turtle} from './defensive/01_Turtle.js';\r\nimport {Sentinel} from './defensive/02_Sentinel.js';\r\nimport {Golem} from './defensive/03_Golem.js';\r\nimport {Battlemage} from './duelist/01_Battlemage.js';\r\nimport {Warmage} from './duelist/02_Warmage.js';\r\nimport {Archmage} from './duelist/03_Archmage.js';\r\nimport {Bonemancer} from './homing/01_Bonemancer.js';\r\nimport {Lich} from './homing/02_Lich.js';\r\nimport {Archlich} from './homing/03_Archlich.js';\r\nimport {Flamecaller} from './caster/01_Flamecaller.js';\r\nimport {Pyromancer} from './caster/02_Pyromancer.js';\r\nimport {Infernalist} from './caster/03_Infernalist.js';\r\nimport {Shadowblade} from './melee/01_Shadowblade.js';\r\nimport {Nightblade} from './melee/02_Nightblade.js';\r\nimport {Voidblade} from './melee/03_Voidblade.js';\r\nimport {Spellshot} from './sniper/01_Spellshot.js';\r\nimport {Spelltracer} from './sniper/02_Spelltracer.js';\r\nimport {Spellseeker} from './sniper/03_Spellseeker.js';\r\nimport {Stormchaser} from './berserker/01_Stormchaser.js';\r\nimport {Stormcaller} from './berserker/02_Stormcaller.js';\r\nimport {Stormforger} from './berserker/03_Stormforger.js';\r\nimport {Spellspinner} from './kiter/01_Spellspinner.js';\r\nimport {Spellweaver} from './kiter/02_Spellweaver.js';\r\nimport {Spellbinder} from './kiter/03_Spellbinder.js';\r\n\r\nexport interface BotEntry\r\n{\r\n\tname: string;\r\n\tai: WizardFunction;\r\n\tdescription: string;\r\n\ttier?: number; // 1, 2, 3 for progression bots; undefined for standalone\r\n\tgroup: string; // progression line name or 'Standalone'\r\n}\r\n\r\nexport interface BotGroup\r\n{\r\n\tlabel: string;\r\n\tbots: BotEntry[];\r\n}\r\n\r\n/**\r\n * All bots organized by progression line.\r\n * Each group contains bots from the same archetype, ordered by tier.\r\n */\r\nexport const BOT_GROUPS: BotGroup[] = [\r\n\t{\r\n\t\tlabel: 'Standalone',\r\n\t\tbots: [\r\n\t\t\t{name: 'TargetDummy', ai: TargetDummy, description: 'Does nothing (for testing)', group: 'Standalone'},\r\n\t\t\t{name: 'Critter', ai: Critter, description: 'Random actions (chaos test)', group: 'Standalone'},\r\n\t\t\t{name: 'Rookie', ai: Rookie, description: 'Basic homing missiles', group: 'Standalone'},\r\n\t\t\t{name: 'Hogger', ai: Hogger, description: 'Chaotic elite — random but dangerous', group: 'Standalone'},\r\n\t\t\t{name: 'Doombringer', ai: Doombringer, description: 'Single max-damage shot benchmark', group: 'Standalone'},\r\n\t\t],\r\n\t},\r\n\t{\r\n\t\tlabel: 'Defensive',\r\n\t\tbots: [\r\n\t\t\t{name: 'Turtle', ai: Turtle, description: 'Stationary tank, last-moment shields', tier: 1, group: 'Defensive'},\r\n\t\t\t{name: 'Sentinel', ai: Sentinel, description: 'Two-tier offense (big + fast missiles) + timed shields', tier: 2, group: 'Defensive'},\r\n\t\t\t{name: 'Golem', ai: Golem, description: 'Stationary fortress, counterattack punish', tier: 3, group: 'Defensive'},\r\n\t\t],\r\n\t},\r\n\t{\r\n\t\tlabel: 'Melee',\r\n\t\tbots: [\r\n\t\t\t{name: 'Shadowblade', ai: Shadowblade, description: 'Blink-in melee assassin', tier: 1, group: 'Melee'},\r\n\t\t\t{name: 'Nightblade', ai: Nightblade, description: '+ preemptive shields, smart stabs', tier: 2, group: 'Melee'},\r\n\t\t\t{name: 'Voidblade', ai: Voidblade, description: '+ vulnerability engage, disengage mode', tier: 3, group: 'Melee'},\r\n\t\t],\r\n\t},\r\n\t{\r\n\t\tlabel: 'Homing',\r\n\t\tbots: [\r\n\t\t\t{name: 'Bonemancer', ai: Bonemancer, description: 'Slow homing missile spam', tier: 1, group: 'Homing'},\r\n\t\t\t{name: 'Lich', ai: Lich, description: 'Slow missile swarm + defense', tier: 2, group: 'Homing'},\r\n\t\t\t{name: 'Archlich', ai: Archlich, description: '+ vulnerability punish missiles during enemy GCD', tier: 3, group: 'Homing'},\r\n\t\t],\r\n\t},\r\n\t{\r\n\t\tlabel: 'Caster',\r\n\t\tbots: [\r\n\t\t\t{name: 'Flamecaller', ai: Flamecaller, description: 'Fixed homing missiles, long range', tier: 1, group: 'Caster'},\r\n\t\t\t{name: 'Pyromancer', ai: Pyromancer, description: '+ adaptive fitting, cast canceling', tier: 2, group: 'Caster'},\r\n\t\t\t{name: 'Infernalist', ai: Infernalist, description: '+ warmup exploitation, punish mode', tier: 3, group: 'Caster'},\r\n\t\t],\r\n\t},\r\n\t{\r\n\t\tlabel: 'Sniper',\r\n\t\tbots: [\r\n\t\t\t{name: 'Spellshot', ai: Spellshot, description: 'Intercept prediction shots', tier: 1, group: 'Sniper'},\r\n\t\t\t{name: 'Spelltracer', ai: Spelltracer, description: '+ adaptive fitting, full defense', tier: 2, group: 'Sniper'},\r\n\t\t\t{name: 'Spellseeker', ai: Spellseeker, description: '+ intercept-aimed punish, proactive kiting', tier: 3, group: 'Sniper'},\r\n\t\t],\r\n\t},\r\n\t{\r\n\t\tlabel: 'Duelist',\r\n\t\tbots: [\r\n\t\t\t{name: 'Battlemage', ai: Battlemage, description: 'Close-range fighter, cast interrupts', tier: 1, group: 'Duelist'},\r\n\t\t\t{name: 'Warmage', ai: Warmage, description: '+ adaptive missile fitting', tier: 2, group: 'Duelist'},\r\n\t\t\t{name: 'Archmage', ai: Archmage, description: '+ range-adaptive, HP-aware aggression', tier: 3, group: 'Duelist'},\r\n\t\t],\r\n\t},\r\n\t{\r\n\t\tlabel: 'Berserker',\r\n\t\tbots: [\r\n\t\t\t{name: 'Stormchaser', ai: Stormchaser, description: 'Aggressive trader, predictive homing', tier: 1, group: 'Berserker'},\r\n\t\t\t{name: 'Stormcaller', ai: Stormcaller, description: '+ budget-fitted homing, smarter defense', tier: 2, group: 'Berserker'},\r\n\t\t\t{name: 'Stormforger', ai: Stormforger, description: '+ vulnerability punish missiles during enemy GCD', tier: 3, group: 'Berserker'},\r\n\t\t],\r\n\t},\r\n\t{\r\n\t\tlabel: 'Kiter',\r\n\t\tbots: [\r\n\t\t\t{name: 'Spellspinner', ai: Spellspinner, description: 'Kiting with homing missiles', tier: 1, group: 'Kiter'},\r\n\t\t\t{name: 'Spellweaver', ai: Spellweaver, description: '+ budget-fitted adaptive missiles', tier: 2, group: 'Kiter'},\r\n\t\t\t{name: 'Spellbinder', ai: Spellbinder, description: '+ vulnerability punish missiles during enemy GCD', tier: 3, group: 'Kiter'},\r\n\t\t],\r\n\t},\r\n];\r\n\r\n// Flat map for lookup by name\r\nconst BOT_MAP = new Map<string, BotEntry>();\r\nfor (const group of BOT_GROUPS)\r\n{\r\n\tfor (const bot of group.bots)\r\n\t{\r\n\t\tBOT_MAP.set(bot.name, bot);\r\n\t}\r\n}\r\n\r\n/**\r\n * All bots ordered from weakest (#1) to strongest (#29).\r\n * Based on round-robin tournament results.\r\n * To update: run `npm run tournament`\r\n */\r\nconst ALL_BOTS_ORDER = [\r\n\t'TargetDummy', // #1 (1 pts, 0W)\r\n\t'Critter', // #2 (1 pts, 0W)\r\n\t'Hogger', // #3 (9 pts, 29W)\r\n\t'Bonemancer', // #4 (9 pts, 32W)\r\n\t'Rookie', // #5 (9 pts, 38W)\r\n\t'Turtle', // #6 (27 pts, 90W)\r\n\t'Spellspinner', // #7 (27 pts, 104W)\r\n\t'Flamecaller', // #8 (28 pts, 103W)\r\n\t'Doombringer', // #9 (30 pts, 108W)\r\n\t'Golem', // #10 (30 pts, 126W)\r\n\t'Sentinel', // #11 (30 pts, 98W)\r\n\t'Shadowblade', // #12 (33 pts, 120W)\r\n\t'Stormchaser', // #13 (36 pts, 116W)\r\n\t'Warmage', // #14 (39 pts, 144W)\r\n\t'Battlemage', // #15 (45 pts, 141W)\r\n\t'Spellweaver', // #16 (45 pts, 164W)\r\n\t'Stormcaller', // #17 (48 pts, 170W)\r\n\t'Pyromancer', // #18 (48 pts, 161W)\r\n\t'Lich', // #19 (51 pts, 165W)\r\n\t'Archlich', // #20 (58 pts, 184W)\r\n\t'Infernalist', // #21 (60 pts, 197W)\r\n\t'Stormforger', // #22 (60 pts, 211W)\r\n\t'Spellshot', // #23 (63 pts, 196W)\r\n\t'Spelltracer', // #24 (63 pts, 186W)\r\n\t'Spellbinder', // #25 (66 pts, 199W)\r\n\t'Archmage', // #26 (72 pts, 229W)\r\n\t'Nightblade', // #27 (72 pts, 230W)\r\n\t'Spellseeker', // #28 (76 pts, 243W)\r\n\t'Voidblade', // #29 (78 pts, 258W)\r\n];\r\n\r\nexport const ALL_BOTS: BotEntry[] = ALL_BOTS_ORDER.map((name) =>\r\n{\r\n\tconst bot = BOT_MAP.get(name);\r\n\tif (!bot) throw new Error(`Bot \"${name}\" not found in BOT_GROUPS`);\r\n\treturn bot;\r\n});\r\n","export * from './standalone/TargetDummy.js';\r\nexport * from './standalone/Rookie.js';\r\nexport * from './standalone/Critter.js';\r\nexport * from './standalone/Hogger.js';\r\nexport * from './standalone/Doombringer.js';\r\nexport * from './defensive/01_Turtle.js';\r\nexport * from './defensive/02_Sentinel.js';\r\nexport * from './defensive/03_Golem.js';\r\nexport * from './melee/01_Shadowblade.js';\r\nexport * from './melee/02_Nightblade.js';\r\nexport * from './melee/03_Voidblade.js';\r\nexport * from './homing/01_Bonemancer.js';\r\nexport * from './homing/02_Lich.js';\r\nexport * from './homing/03_Archlich.js';\r\nexport * from './caster/01_Flamecaller.js';\r\nexport * from './caster/02_Pyromancer.js';\r\nexport * from './caster/03_Infernalist.js';\r\nexport * from './sniper/01_Spellshot.js';\r\nexport * from './sniper/02_Spelltracer.js';\r\nexport * from './sniper/03_Spellseeker.js';\r\nexport * from './duelist/01_Battlemage.js';\r\nexport * from './duelist/02_Warmage.js';\r\nexport * from './duelist/03_Archmage.js';\r\nexport * from './berserker/01_Stormchaser.js';\r\nexport * from './berserker/02_Stormcaller.js';\r\nexport * from './berserker/03_Stormforger.js';\r\nexport * from './kiter/01_Spellspinner.js';\r\nexport * from './kiter/02_Spellweaver.js';\r\nexport * from './kiter/03_Spellbinder.js';\r\nexport * from './registry.js';\r\n","/**\r\n * VIBEMANCER - STATE HOOKS\r\n *\r\n * React-style hooks for reading game state.\r\n * These handle the \"subconscious\" perception that humans do instinctively.\r\n *\r\n * UNITS REFERENCE (100 ticks = 1 second):\r\n * Position: absolute world coordinates, 0-800 on each axis (800×800 arena)\r\n * Velocity: units per tick on each axis (player max speed = 1 u/t)\r\n * Health: hit points (max 60)\r\n * Ticks: game ticks (10ms each, 100/sec). Divide by 100 for seconds.\r\n * Shield: block multiplier 0.0-0.9 (0.9 = blocks 90% damage)\r\n */\r\n\r\nimport {Position, Velocity, ProjectileState} from '../types.js';\r\nimport {getBotContext, useMemo} from '../engine/hooks-runtime.js';\r\nimport {SHIELD_MAX_BLOCK, SHIELD_DECAY_PER_SECOND, SHIELD_MIN_BLOCK, TICKS_PER_SECOND} from '../rules.js';\r\nimport type {EnemyState, AnalyzedThreat} from './types.js';\r\nimport {analyzeThreats} from './threat-analysis.js';\r\n\r\n// ============================================================\r\n// SELF STATE HOOKS\r\n// ============================================================\r\n\r\n/**\r\n * Get your current health (0-60). Wizard dies at 0.\r\n */\r\nexport function useHealth(): number\r\n{\r\n\treturn getBotContext().health;\r\n}\r\n\r\n/**\r\n * Get your current position as {x, y} in world coordinates (0-800).\r\n * Position is clamped to [5, 795] (arena bounds minus wizard radius).\r\n */\r\nexport function usePosition(): Position\r\n{\r\n\tconst ctx = getBotContext();\r\n\treturn {x: ctx.position.x, y: ctx.position.y};\r\n}\r\n\r\n/**\r\n * Get your current velocity as {x, y} in units/tick.\r\n * Max magnitude is 1 u/t when idle, 0.5 u/t when casting, 0 when channeling shield.\r\n */\r\nexport function useVelocity(): Velocity\r\n{\r\n\tconst ctx = getBotContext();\r\n\treturn {x: ctx.velocity.x, y: ctx.velocity.y};\r\n}\r\n\r\n/**\r\n * Get your current status:\r\n * - 'idle': free to act\r\n * - 'casting': casting a spell (missile or blink). Can move at 50% speed.\r\n * - 'channeling': channeling shield. Cannot move. Cancel anytime with cancel().\r\n * - 'gcd_locked': global cooldown after spell. Can move at full speed, but cannot cast.\r\n */\r\nexport function useStatus(): 'idle' | 'casting' | 'channeling' | 'gcd_locked'\r\n{\r\n\treturn getBotContext().state;\r\n}\r\n\r\n/**\r\n * Get ticks until you can start a new spell.\r\n *\r\n * Returns 0 when idle or channeling (shield can be canceled immediately).\r\n * During casting: remaining cast ticks. During GCD: remaining GCD ticks.\r\n *\r\n * Note: 100 ticks = 1 second.\r\n */\r\nexport function useTicksUntilReady(): number\r\n{\r\n\tconst ctx = getBotContext();\r\n\r\n\tswitch (ctx.state)\r\n\t{\r\n\t\tcase 'idle':\r\n\t\t\treturn 0;\r\n\t\tcase 'casting':\r\n\t\t\t// Remaining cast time\r\n\t\t\tif (ctx.castDuration !== undefined && ctx.castProgress !== undefined)\r\n\t\t\t{\r\n\t\t\t\treturn ctx.castDuration - ctx.castProgress;\r\n\t\t\t}\r\n\t\t\treturn 0;\r\n\t\tcase 'channeling':\r\n\t\t\t// Can cancel anytime, so 0\r\n\t\t\treturn 0;\r\n\t\tcase 'gcd_locked':\r\n\t\t\treturn ctx.gcdRemaining ?? 0;\r\n\t\tdefault:\r\n\t\t\treturn 0;\r\n\t}\r\n}\r\n\r\n/**\r\n * Get current shield block multiplier.\r\n *\r\n * Returns 0 if not channeling shield.\r\n * Returns 0.3-0.9 if channeling: starts at 0.9 (blocks 90%), decays by 0.2/sec,\r\n * minimum 0.3 (blocks 30%). The remaining damage gets through:\r\n * actualDamage = incomingDamage × (1 - shieldStrength).\r\n */\r\nexport function useShieldStrength(): number\r\n{\r\n\tconst ctx = getBotContext();\r\n\r\n\tif (ctx.state !== 'channeling' || ctx.channelingSpell !== 'shield')\r\n\t{\r\n\t\treturn 0;\r\n\t}\r\n\r\n\t// Calculate shield strength based on channel duration\r\n\tconst channelDuration = ctx.channelDuration ?? 0;\r\n\tconst decayPerTick = SHIELD_DECAY_PER_SECOND / TICKS_PER_SECOND;\r\n\tconst strength = SHIELD_MAX_BLOCK - (channelDuration * decayPerTick);\r\n\treturn Math.max(SHIELD_MIN_BLOCK, strength);\r\n}\r\n\r\n/**\r\n * Get blink cooldown remaining in ticks. Returns 0 if ready to blink.\r\n *\r\n * Cooldown scales with distance used:\r\n * - 100 units → ~100 ticks (1s)\r\n * - 300 units (max range) → 2000 ticks (20s)\r\n *\r\n * Note: 100 ticks = 1 second.\r\n */\r\nexport function useBlinkCooldown(): number\r\n{\r\n\treturn getBotContext().blinkCooldown;\r\n}\r\n\r\n/**\r\n * Get currently casting spell, or null if not casting.\r\n * Returns 'missile', 'shield', or 'blink'.\r\n */\r\nexport function useCastingSpell(): 'missile' | 'shield' | 'blink' | null\r\n{\r\n\tconst ctx = getBotContext();\r\n\tif (ctx.state !== 'casting')\r\n\t{\r\n\t\treturn null;\r\n\t}\r\n\treturn ctx.castingSpell ?? null;\r\n}\r\n\r\n/**\r\n * Get cast progress as {current, total} in ticks, or null if not casting.\r\n *\r\n * current = ticks elapsed, total = ticks needed. When current >= total, spell fires.\r\n * Note: 100 ticks = 1 second.\r\n */\r\nexport function useCastProgress(): {current: number; total: number} | null\r\n{\r\n\tconst ctx = getBotContext();\r\n\tif (ctx.state !== 'casting')\r\n\t{\r\n\t\treturn null;\r\n\t}\r\n\tif (ctx.castProgress === undefined || ctx.castDuration === undefined)\r\n\t{\r\n\t\treturn null;\r\n\t}\r\n\treturn {current: ctx.castProgress, total: ctx.castDuration};\r\n}\r\n\r\n// ============================================================\r\n// ENEMY STATE HOOK\r\n// ============================================================\r\n\r\n/**\r\n * Get enemy wizard state.\r\n *\r\n * Returns position, velocity, health, status, casting spell, and shield strength.\r\n * Note: you cannot see the enemy's missile configs or exact cooldown timers —\r\n * only their status and what's visible on the field.\r\n */\r\nexport function useEnemy(): EnemyState\r\n{\r\n\tconst ctx = getBotContext();\r\n\tconst enemy = ctx.enemies[0];\r\n\r\n\tif (!enemy)\r\n\t{\r\n\t\t// No enemy - return default state\r\n\t\treturn {\r\n\t\t\tposition: {x: 0, y: 0},\r\n\t\t\tvelocity: {x: 0, y: 0},\r\n\t\t\thealth: 0,\r\n\t\t\tstatus: 'idle',\r\n\t\t\tcastingSpell: null,\r\n\t\t\tshieldStrength: 0,\r\n\t\t};\r\n\t}\r\n\r\n\t// Calculate enemy shield strength\r\n\tlet shieldStrength = 0;\r\n\tif (enemy.state === 'channeling' && enemy.channelingSpell === 'shield')\r\n\t{\r\n\t\tconst channelDuration = enemy.channelDuration ?? 0;\r\n\t\tconst decayPerTick = SHIELD_DECAY_PER_SECOND / TICKS_PER_SECOND;\r\n\t\tshieldStrength = Math.max(SHIELD_MIN_BLOCK, SHIELD_MAX_BLOCK - (channelDuration * decayPerTick));\r\n\t}\r\n\r\n\treturn {\r\n\t\tposition: {x: enemy.position.x, y: enemy.position.y},\r\n\t\tvelocity: {x: enemy.velocity.x, y: enemy.velocity.y},\r\n\t\thealth: enemy.health,\r\n\t\tstatus: enemy.state,\r\n\t\tcastingSpell: enemy.state === 'casting' ? (enemy.castingSpell ?? null) : null,\r\n\t\tshieldStrength,\r\n\t};\r\n}\r\n\r\n// ============================================================\r\n// PROJECTILE HOOKS\r\n// ============================================================\r\n\r\n/**\r\n * Get all your active (in-flight) projectiles.\r\n * Each has position, rotation (degrees), speed (u/t), turnRate, remainingTicks.\r\n */\r\nexport function useMyProjectiles(): ProjectileState[]\r\n{\r\n\treturn getBotContext().myProjectiles;\r\n}\r\n\r\n// ============================================================\r\n// COMBAT TRACKING HOOKS\r\n// ============================================================\r\n\r\n/**\r\n * Get total damage you've dealt this match.\r\n */\r\nexport function useDamageDealt(): number\r\n{\r\n\treturn getBotContext().damageDealt;\r\n}\r\n\r\n/**\r\n * Get total damage you've taken this match.\r\n */\r\nexport function useDamageTaken(): number\r\n{\r\n\treturn getBotContext().damageTaken;\r\n}\r\n\r\n/**\r\n * Get the tick number when you last took damage. Returns 0 if never hit.\r\n * Compare with useTick() to get ticks since last hit.\r\n */\r\nexport function useLastHitTick(): number\r\n{\r\n\treturn getBotContext().lastHitTick;\r\n}\r\n\r\n// ============================================================\r\n// ARENA HOOKS\r\n// ============================================================\r\n\r\n/**\r\n * Get arena dimensions. Default: {width: 800, height: 800}.\r\n * Wizards are clamped to [5, 795] on each axis (radius = 5).\r\n */\r\nexport function useArenaSize(): {width: number; height: number}\r\n{\r\n\tconst ctx = getBotContext();\r\n\treturn {width: ctx.arenaWidth, height: ctx.arenaHeight};\r\n}\r\n\r\n/**\r\n * Get current game tick (starts at 0, increments each tick).\r\n * 100 ticks = 1 second. Match ends at 30,000 ticks (5 minutes).\r\n */\r\nexport function useTick(): number\r\n{\r\n\treturn getBotContext().tick;\r\n}\r\n\r\n// ============================================================\r\n// THREAT ANALYSIS HOOKS\r\n// ============================================================\r\n\r\n/**\r\n * Get analyzed threats from all incoming enemy projectiles.\r\n * Sorted by ticksToImpact (soonest first). Only includes missiles within 500 units\r\n * or that are predicted to hit.\r\n *\r\n * Each threat includes:\r\n * - ticksToImpact: ticks until hit (Infinity if will miss)\r\n * - willHit: true if missile hits your current position\r\n * - canDodgeLeft/Right: whether strafing perpendicular to missile heading works\r\n * - canOutrun: whether moving away from missile escapes it\r\n * - bestDodgeDirection: {x, y} unit vector to dodge optimally, or null if undodgeable\r\n * - canBlockInTime: whether you can raise shield before impact\r\n * - ticksToStartShield: when to START channeling shield to block in time\r\n */\r\nexport function useThreats(): AnalyzedThreat[]\r\n{\r\n\tconst ctx = getBotContext();\r\n\tconst ticksUntilReady = useTicksUntilReady();\r\n\r\n\t// Memoize to avoid recalculating every call\r\n\treturn useMemo(() =>\r\n\t{\r\n\t\treturn analyzeThreats(\r\n\t\t\tctx.position,\r\n\t\t\tctx.projectiles,\r\n\t\t\tctx.myProjectiles,\r\n\t\t\tticksUntilReady,\r\n\t\t);\r\n\t}, [ctx.projectiles, ctx.myProjectiles, ctx.position.x, ctx.position.y, ticksUntilReady]);\r\n}\r\n\r\n/**\r\n * Get the most imminent threat, or null if no threats.\r\n * Shorthand for useThreats()[0].\r\n */\r\nexport function useClosestThreat(): AnalyzedThreat | null\r\n{\r\n\tconst threats = useThreats();\r\n\treturn threats[0] ?? null;\r\n}\r\n\r\n/**\r\n * Get your missiles analyzed from the enemy's perspective.\r\n * Useful to predict when enemy will shield/dodge your attacks.\r\n */\r\nexport function useMyThreatsToEnemy(): AnalyzedThreat[]\r\n{\r\n\tconst ctx = getBotContext();\r\n\tconst enemy = ctx.enemies[0];\r\n\r\n\tif (!enemy)\r\n\t{\r\n\t\treturn [];\r\n\t}\r\n\r\n\t// Memoize to avoid recalculating every call\r\n\treturn useMemo(() =>\r\n\t{\r\n\t\t// Analyze from enemy's perspective\r\n\t\t// Pass empty array for \"enemy's own projectiles\" since we're analyzing our missiles as threats TO them\r\n\t\treturn analyzeThreats(\r\n\t\t\tenemy.position,\r\n\t\t\tctx.myProjectiles, // Our projectiles are their threats\r\n\t\t\t[], // Enemy has no \"own projectiles\" in this context\r\n\t\t\t0, // We don't know enemy's ticksUntilReady\r\n\t\t);\r\n\t}, [ctx.myProjectiles, enemy.position.x, enemy.position.y]);\r\n}\r\n","/**\r\n * VIBEMANCER - ACTION BUILDERS\r\n *\r\n * Fluent API for constructing bot actions with type-safe chaining.\r\n *\r\n * UNITS REFERENCE (100 ticks = 1 second):\r\n * Position: absolute world coordinates, 0-800 on each axis (800×800 arena)\r\n * Movement: direction vector, magnitude auto-normalized to max 100\r\n * Speed: units per tick (player moves at 1 unit/tick = 100 units/sec)\r\n * Duration: ticks (divide by 100 for seconds)\r\n * Angles: degrees (0°=right, 90°=down, 180°=left, 270°=up)\r\n * Damage: raw HP removed on hit (wizard has 60 HP)\r\n * Turn rate: degrees per tick the missile can rotate\r\n */\r\n\r\nimport {WizardActions, MissileConfig, MissileAIFunction} from '../types.js';\r\nimport type {FinalAction, ActionBuilder} from './types.js';\r\n\r\n/**\r\n * Create a FinalAction (no further chaining possible).\r\n */\r\nfunction createFinalAction(action: WizardActions): FinalAction\r\n{\r\n\treturn {\r\n\t\t_toAction: () => action,\r\n\t};\r\n}\r\n\r\n/**\r\n * Create an ActionBuilder (can chain .move()).\r\n */\r\nfunction createActionBuilder(baseAction: Omit<WizardActions, 'move'>): ActionBuilder\r\n{\r\n\tconst action: WizardActions = {\r\n\t\t...baseAction,\r\n\t\tmove: {x: 0, y: 0},\r\n\t};\r\n\r\n\treturn {\r\n\t\t_toAction: () => action,\r\n\t\tmove(x: number, y: number): FinalAction\r\n\t\t{\r\n\t\t\treturn createFinalAction({\r\n\t\t\t\t...action,\r\n\t\t\t\tmove: {x, y},\r\n\t\t\t});\r\n\t\t},\r\n\t};\r\n}\r\n\r\n// ============================================================\r\n// ACTION BUILDERS\r\n// ============================================================\r\n\r\n/**\r\n * Channel a shield that blocks incoming damage.\r\n *\r\n * Starts at 90% block, decays by 20% per second, minimum 30%.\r\n * Takes 20 ticks (0.2s) to activate. Movement is disabled while channeling.\r\n * Cancel anytime with cancel(). Triggers 100-tick (1s) GCD after cancel.\r\n *\r\n * Can chain .move() — movement applies during the 20-tick cast, NOT during channel.\r\n *\r\n * @example\r\n * return shield(); // shield and stay still\r\n * return shield().move(100, 0); // move right while cast starts\r\n */\r\nexport function shield(): ActionBuilder\r\n{\r\n\treturn createActionBuilder({\r\n\t\tstartCast: {spell: 'shield'},\r\n\t});\r\n}\r\n\r\n/**\r\n * Cast a missile spell.\r\n *\r\n * Cast time scales with damage, speed, duration, and turn rate — bigger missiles\r\n * take longer to cast. While casting you move at 50% speed. After firing, 100-tick\r\n * (1s) GCD before next spell.\r\n *\r\n * Repeated similar missiles cast 20% faster (warmup bonus). Switching styles\r\n * incurs a 20% penalty.\r\n *\r\n * Can chain .move() for simultaneous movement while casting.\r\n *\r\n * @param config - Missile stats:\r\n * - damage: HP removed on hit (1-60 typical). Also sets hitbox: radius = 2 + 0.1×damage.\r\n * - speed: units/tick (min 1.5). Player moves at 1 u/t, so 5 = 5× player speed.\r\n * - duration: ticks the missile lives (min 10). Range ≈ speed × duration.\r\n * - turnRate: degrees/tick of homing (0 = straight line, 3 = moderate homing, 5+ = strong).\r\n * Negative = no homing + minor speed cost reduction.\r\n * @param ai - Called every tick to control missile steering. Receives:\r\n * - missileState: the missile's position, rotation (degrees), speed, remainingTicks\r\n * - worldState: full game state (all wizards, projectiles)\r\n * - random(): seeded PRNG [0, 1)\r\n * Return { turnToward: {x, y} } to home toward a position, or {} to fly straight.\r\n * @param direction - Launch angle in degrees (0°=right, 90°=down, 180°=left, 270°=up).\r\n * Tip: use Math.atan2(dy, dx) * (180 / Math.PI) to aim at a target.\r\n *\r\n * @example\r\n * // Straight missile aimed at enemy\r\n * const angle = Math.atan2(dy, dx) * (180 / Math.PI);\r\n * return missile({damage: 15, speed: 6, duration: 200, turnRate: 0}, () => ({}), angle);\r\n *\r\n * // Homing missile that tracks enemy\r\n * return missile(\r\n * {damage: 10, speed: 5, duration: 300, turnRate: 3},\r\n * ({worldState}) => ({turnToward: worldState.enemies[0]?.position}),\r\n * angle,\r\n * );\r\n */\r\nexport function missile(config: MissileConfig, ai: MissileAIFunction, direction: number): ActionBuilder\r\n{\r\n\treturn createActionBuilder({\r\n\t\tstartCast: {\r\n\t\t\tspell: 'missile',\r\n\t\t\tconfig,\r\n\t\t\tmissileAI: ai,\r\n\t\t\tdirection,\r\n\t\t},\r\n\t});\r\n}\r\n\r\n/**\r\n * Teleport to an absolute position on the arena.\r\n *\r\n * Max range: 300 units from current position (clamped by engine if further).\r\n * Cast time: 10 ticks (0.1s). Cooldown scales with distance:\r\n * - 100 units → 100 ticks (1s)\r\n * - 300 units → 2000 ticks (20s)\r\n *\r\n * Cannot chain .move() — blink IS the movement.\r\n *\r\n * @param x - Target X position (0-800, absolute world coordinate)\r\n * @param y - Target Y position (0-800, absolute world coordinate)\r\n *\r\n * @example\r\n * return blink(400, 400); // blink to center\r\n * return blink(enemy.position.x, enemy.position.y); // blink to enemy\r\n */\r\nexport function blink(x: number, y: number): FinalAction\r\n{\r\n\treturn createFinalAction({\r\n\t\tmove: {x: 0, y: 0}, // No manual movement with blink\r\n\t\tstartCast: {\r\n\t\t\tspell: 'blink',\r\n\t\t\ttarget: {x, y},\r\n\t\t},\r\n\t});\r\n}\r\n\r\n/**\r\n * Cancel current cast or channel (e.g. stop shielding to attack).\r\n *\r\n * Canceling a cast/channel triggers 100-tick (1s) GCD.\r\n * Can chain .move() for simultaneous movement.\r\n *\r\n * @example\r\n * return cancel().move(-100, 0); // cancel and dodge left\r\n */\r\nexport function cancel(): ActionBuilder\r\n{\r\n\treturn createActionBuilder({\r\n\t\tcancel: true,\r\n\t});\r\n}\r\n\r\n/**\r\n * Move in a direction without casting any spell.\r\n *\r\n * This is a **direction vector**, not a target position. The engine normalizes\r\n * the magnitude to max 100, then moves at 1 unit/tick (100 units/sec).\r\n * Positive X = right, positive Y = down.\r\n *\r\n * To move toward a target position, subtract your position:\r\n * move(target.x - myPos.x, target.y - myPos.y)\r\n *\r\n * @param x - Horizontal direction (positive = right, negative = left)\r\n * @param y - Vertical direction (positive = down, negative = up)\r\n *\r\n * @example\r\n * return move(100, 0); // move right\r\n * return move(enemy.position.x - myPos.x, enemy.position.y - myPos.y); // move toward enemy\r\n */\r\nexport function move(x: number, y: number): FinalAction\r\n{\r\n\treturn createFinalAction({\r\n\t\tmove: {x, y},\r\n\t});\r\n}\r\n\r\n/**\r\n * Do nothing — no action, no movement.\r\n */\r\nexport function idle(): FinalAction\r\n{\r\n\treturn createFinalAction({\r\n\t\tmove: {x: 0, y: 0},\r\n\t});\r\n}\r\n\r\n/**\r\n * Extract WizardActions from a FinalAction.\r\n * Used by the engine to get the actual action.\r\n */\r\nexport function extractAction(finalAction: FinalAction): WizardActions\r\n{\r\n\treturn finalAction._toAction();\r\n}\r\n","/**\r\n * VIBEMANCER - BOT WRAPPER\r\n *\r\n * Utilities to convert between old-style (WizardFunction) and new-style (BotFunction) bots.\r\n */\r\n\r\nimport {WizardFunction, WizardActions, GameState, GameConfig} from '../types.js';\r\nimport {withBotContext} from '../engine/hooks-runtime.js';\r\nimport {extractAction} from './action-builders.js';\r\nimport type {BotFunction, BotContext} from './types.js';\r\n\r\n/**\r\n * Convert a new-style bot (using hooks) to an old-style bot (WizardFunction).\r\n *\r\n * This allows new bots to work with the existing simulation.\r\n *\r\n * @example\r\n * const NewBot: BotFunction = () => {\r\n * const health = useHealth();\r\n * return health < 10 ? shield() : idle();\r\n * };\r\n *\r\n * // Convert to work with simulate()\r\n * const oldStyleBot = wrapNewBot(NewBot);\r\n * simulate(oldStyleBot, opponent);\r\n */\r\nexport function wrapNewBot(newBot: BotFunction): WizardFunction\r\n{\r\n\treturn ({state, config}: {state: GameState; config: GameConfig; random: () => number}): WizardActions =>\r\n\t{\r\n\t\t// Create the BotContext from GameState\r\n\t\t// Note: entityId is inherited from the simulation's outer runWithHooks call\r\n\t\tconst botContext: BotContext = {\r\n\t\t\tentityId: 'unused', // Not used - entity ID comes from simulation\r\n\t\t\ttick: state.tick,\r\n\t\t\tposition: state.position,\r\n\t\t\tvelocity: state.velocity,\r\n\t\t\thealth: state.health,\r\n\t\t\tmaxHealth: state.maxHealth,\r\n\t\t\tstate: state.state,\r\n\t\t\tcastingSpell: state.castingSpell,\r\n\t\t\tcastProgress: state.castProgress,\r\n\t\t\tcastDuration: state.castDuration,\r\n\t\t\tchannelingSpell: state.channelingSpell,\r\n\t\t\tchannelDuration: state.channelDuration,\r\n\t\t\tgcdRemaining: state.gcdRemaining,\r\n\t\t\tblinkCooldown: state.blinkCooldown,\r\n\t\t\tenemies: state.enemies.map((e) => ({\r\n\t\t\t\tid: e.id,\r\n\t\t\t\tposition: e.position,\r\n\t\t\t\tvelocity: e.velocity,\r\n\t\t\t\thealth: e.health,\r\n\t\t\t\tstate: e.state,\r\n\t\t\t\tcastingSpell: e.castingSpell,\r\n\t\t\t\tchannelingSpell: e.channelingSpell,\r\n\t\t\t\tchannelDuration: e.channelDuration,\r\n\t\t\t})),\r\n\t\t\tprojectiles: state.projectiles,\r\n\t\t\tmyProjectiles: state.myProjectiles,\r\n\t\t\tarenaWidth: config.arenaSize.width,\r\n\t\t\tarenaHeight: config.arenaSize.height,\r\n\t\t\tdamageDealt: state.damageDealt,\r\n\t\t\tdamageTaken: state.damageTaken,\r\n\t\t\tlastHitTick: state.lastHitTick,\r\n\t\t};\r\n\r\n\t\t// Run the new-style bot with context\r\n\t\t// Use withBotContext (not runBotWithContext) to preserve the entity ID\r\n\t\t// set by the simulation's outer runWithHooks call\r\n\t\tconst finalAction = withBotContext(botContext, newBot);\r\n\r\n\t\t// Extract WizardActions from FinalAction\r\n\t\treturn extractAction(finalAction);\r\n\t};\r\n}\r\n\r\n/**\r\n * Type guard to check if a bot is a new-style BotFunction.\r\n * New-style bots have 0 parameters, old-style bots have at least 1.\r\n */\r\nexport function isNewStyleBot(bot: WizardFunction | BotFunction): bot is BotFunction\r\n{\r\n\treturn bot.length === 0;\r\n}\r\n","/**\r\n * VIBEMANCER - OPTIMIZER UTILITIES\r\n *\r\n * Core functions used by the offline parameter optimizer script.\r\n * These handle combo generation, range narrowing, and fight scoring.\r\n *\r\n * The optimizer uses multi-pass coordinate descent:\r\n * - Pass 1: Coarse search across the effective range for each param\r\n * - Pass 2+: Fine search zoomed into the neighborhood of the best result\r\n *\r\n * Parameters can define their search window two ways:\r\n * - `range`: sliding window centered on current value (value ± range)\r\n * - `min`/`max`: fixed bounds (old-style)\r\n *\r\n * Both can be combined: range defines the search radius, min/max clamp it.\r\n */\r\n\r\nimport type {ParamDeclaration} from './params-runtime.js';\r\nimport type {FightResult} from './simulation.js';\r\nimport {WIZARD_HEALTH} from '../rules.js';\r\n\r\n/**\r\n * Compute the effective min/max search window for a parameter.\r\n *\r\n * - If `range` is set: window is `[value - range, value + range]`, clamped by optional min/max\r\n * - If only `min`/`max` are set: window is `[min, max]` directly\r\n * - If neither: returns `[value, value]` (no search)\r\n */\r\nexport function getEffectiveRange(p: ParamDeclaration): {min: number; max: number}\r\n{\r\n\tif (p.range != null)\r\n\t{\r\n\t\treturn {\r\n\t\t\tmin: Math.max(p.value - p.range, p.min ?? -Infinity),\r\n\t\t\tmax: Math.min(p.value + p.range, p.max ?? Infinity),\r\n\t\t};\r\n\t}\r\n\treturn {\r\n\t\tmin: p.min ?? p.value,\r\n\t\tmax: p.max ?? p.value,\r\n\t};\r\n}\r\n\r\n/**\r\n * Generate evenly-spaced candidate values for a single parameter.\r\n *\r\n * Divides [min, max] into `steps` evenly-spaced values. For example,\r\n * min=0, max=100, steps=5 produces [0, 25, 50, 75, 100].\r\n *\r\n * @param min - Lower bound of search range\r\n * @param max - Upper bound of search range\r\n * @param steps - Number of evenly-spaced values to generate (minimum 2)\r\n * @returns Array of candidate values, sorted ascending\r\n */\r\nexport function generateCandidates(min: number, max: number, steps: number): number[]\r\n{\r\n\tif (steps < 2) steps = 2;\r\n\tconst values: number[] = [];\r\n\tfor (let i = 0; i < steps; i++)\r\n\t{\r\n\t\tvalues.push(min + (max - min) * i / (steps - 1));\r\n\t}\r\n\treturn values;\r\n}\r\n\r\n/**\r\n * Generate all combinations (cartesian product) of candidate values for multiple params.\r\n *\r\n * For N params with S1, S2, ... SN steps each, produces S1 × S2 × ... × SN combinations.\r\n * Each combination is a Record<string, number> mapping param name to value.\r\n *\r\n * @param params - Parameter declarations with search ranges and step counts\r\n * @returns Array of all parameter combinations to evaluate\r\n */\r\nexport function generateCombos(params: ParamDeclaration[]): Record<string, number>[]\r\n{\r\n\tif (params.length === 0) return [{}];\r\n\r\n\t// Generate candidates per param\r\n\tconst candidateArrays = params.map((p) =>\r\n\t{\r\n\t\tconst {min, max} = getEffectiveRange(p);\r\n\t\treturn {\r\n\t\t\tname: p.name,\r\n\t\t\tvalues: generateCandidates(min, max, p.steps),\r\n\t\t};\r\n\t});\r\n\r\n\t// Cartesian product\r\n\tlet combos: Record<string, number>[] = [{}];\r\n\tfor (const {name, values} of candidateArrays)\r\n\t{\r\n\t\tconst next: Record<string, number>[] = [];\r\n\t\tfor (const combo of combos)\r\n\t\t{\r\n\t\t\tfor (const value of values)\r\n\t\t\t{\r\n\t\t\t\tnext.push({...combo, [name]: value});\r\n\t\t\t}\r\n\t\t}\r\n\t\tcombos = next;\r\n\t}\r\n\r\n\treturn combos;\r\n}\r\n\r\n/**\r\n * Narrow parameter ranges around the best combo found in the previous pass.\r\n *\r\n * Centers each param on its best value and shrinks the search range to one gap width.\r\n * This provides finer resolution in subsequent passes. Hard min/max constraints are preserved.\r\n *\r\n * @param params - Original parameter declarations\r\n * @param best - Best parameter combination from the previous pass\r\n * @returns New parameter declarations with narrowed ranges for the next pass\r\n */\r\nexport function narrowRange(params: ParamDeclaration[], best: Record<string, number>): ParamDeclaration[]\r\n{\r\n\treturn params.map((p) =>\r\n\t{\r\n\t\tconst bestVal = best[p.name] ?? p.value;\r\n\t\tconst effectiveSubsteps = p.substeps ?? p.steps;\r\n\r\n\t\t// substeps: 0 — freeze this param at its best value (skip in subsequent passes)\r\n\t\tif (effectiveSubsteps === 0)\r\n\t\t{\r\n\t\t\treturn {...p, value: bestVal, min: bestVal, max: bestVal, range: undefined, steps: 2};\r\n\t\t}\r\n\r\n\t\tconst {min: effMin, max: effMax} = getEffectiveRange(p);\r\n\t\tconst gap = (effMax - effMin) / (p.steps - 1);\r\n\t\tconst narrowedSteps = effectiveSubsteps;\r\n\r\n\t\tif (p.range != null)\r\n\t\t{\r\n\t\t\t// Range mode: center on best, shrink range to one gap\r\n\t\t\treturn {...p, value: bestVal, range: gap, steps: narrowedSteps};\r\n\t\t}\r\n\t\t// Fixed min/max mode: narrow bounds around best, clamped to original\r\n\t\treturn {\r\n\t\t\t...p,\r\n\t\t\tmin: Math.max(p.min ?? bestVal, bestVal - gap),\r\n\t\t\tmax: Math.min(p.max ?? bestVal, bestVal + gap),\r\n\t\t\tsteps: narrowedSteps,\r\n\t\t};\r\n\t});\r\n}\r\n\r\n/**\r\n * Score a FightResult from one bot's perspective.\r\n *\r\n * Returns a continuous score that provides gradient information beyond binary win/loss:\r\n * - Win: 3.0 base + up to 0.5 HP bonus (higher remaining HP = better)\r\n * - Draw: 1.0\r\n * - Loss: 0.0 base + up to 0.5 bonus for low enemy HP (closer fights = better)\r\n *\r\n * For a mirrored fight set (bot as wizard-1 AND wizard-2), call this twice\r\n * and sum the scores for a balanced evaluation.\r\n *\r\n * @param result - The fight result to score\r\n * @returns Continuous score in range [0, 3.5] per match\r\n */\r\nexport function scoreFight(result: FightResult): number\r\n{\r\n\tlet score = 0;\r\n\r\n\tfor (const match of result.matches)\r\n\t{\r\n\t\tif (match.winner === 'wizard-1')\r\n\t\t{\r\n\t\t\t// Won: 3.0 base + HP bonus\r\n\t\t\tscore += 3.0 + (match.finalState.health / WIZARD_HEALTH) * 0.5;\r\n\t\t}\r\n\t\telse if (match.winner === 'draw' || match.winner === null)\r\n\t\t{\r\n\t\t\tscore += 1.0;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// Lost: small bonus for low enemy HP\r\n\t\t\tconst enemyHP = match.finalState.enemies[0]?.health ?? WIZARD_HEALTH;\r\n\t\t\tscore += (1 - enemyHP / WIZARD_HEALTH) * 0.5;\r\n\t\t}\r\n\t}\r\n\r\n\treturn score;\r\n}\r\n\r\n/**\r\n * Score a FightResult from wizard-2's perspective.\r\n * Same scoring logic as scoreFight but with roles reversed.\r\n *\r\n * Use this for tournaments where both sides of a pairing need scoring.\r\n */\r\nexport function scoreFightAsWizard2(result: FightResult): number\r\n{\r\n\tlet score = 0;\r\n\r\n\tfor (const match of result.matches)\r\n\t{\r\n\t\tif (match.winner === 'wizard-2')\r\n\t\t{\r\n\t\t\t// Won: 3.0 base + HP bonus (wizard-2's remaining HP)\r\n\t\t\tconst hp = match.finalState.enemies[0]?.health ?? WIZARD_HEALTH;\r\n\t\t\tscore += 3.0 + (hp / WIZARD_HEALTH) * 0.5;\r\n\t\t}\r\n\t\telse if (match.winner === 'draw' || match.winner === null)\r\n\t\t{\r\n\t\t\tscore += 1.0;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// Lost: small bonus for low wizard-1 HP\r\n\t\t\tconst enemyHP = match.finalState.health;\r\n\t\t\tscore += (1 - enemyHP / WIZARD_HEALTH) * 0.5;\r\n\t\t}\r\n\t}\r\n\r\n\treturn score;\r\n}\r\n","/**\r\n * VIBEMANCER — BROWSER SANDBOX\r\n *\r\n * Provides Web Worker-based sandboxing for bot code execution in the browser.\r\n * Same compiled bundles as the isolated-vm sandbox (MatchSandbox), but runs\r\n * in a Web Worker instead of a V8 isolate.\r\n *\r\n * Architecture:\r\n * - Host: creates Worker from Blob URL, communicates via postMessage\r\n * - Worker: loads compiled bundle (sets globalThis.__fight/__simulate),\r\n * dispatches fight/simulate calls, posts results back\r\n *\r\n * Safety:\r\n * - Timeout via setTimeout + worker.terminate() catches infinite loops\r\n * - No memory limit (browser manages worker memory; worst case = tab crash)\r\n * - Prototype freeze prevents cross-bot sabotage (same banner as isolated-vm)\r\n * - No Node.js APIs available in Web Workers\r\n *\r\n * NOTE: This file has ZERO Node.js dependencies. It works in any JS environment.\r\n */\r\n\r\nimport type {GameState, MissileAIFunction, ProjectileState, WizardActions} from '../types.js';\r\nimport type {BotError, FightResult, MatchWinner, SimulateResult} from './simulation.js';\r\nimport type {StepResult} from './manual-match.js';\r\n\r\n// ============================================================\r\n// WORKER PROTOCOL\r\n// ============================================================\r\n\r\n/**\r\n * Minimal Worker interface for dependency injection.\r\n * Matches the browser Worker API subset we need.\r\n * For tests, a Node.js worker_threads adapter can implement this.\r\n */\r\nexport interface WorkerLike\r\n{\r\n\tpostMessage(data: unknown): void;\r\n\tterminate(): void;\r\n\taddEventListener(type: string, listener: (ev: unknown) => void): void;\r\n\tremoveEventListener(type: string, listener: (ev: unknown) => void): void;\r\n}\r\n\r\n/**\r\n * Factory function that creates a WorkerLike from a JavaScript code string.\r\n * Default: creates a browser Web Worker via Blob URL.\r\n * Override in options.createWorker for testing with Node.js worker_threads.\r\n */\r\nexport type WorkerFactory = (code: string) => {worker: WorkerLike; cleanup?: () => void};\r\n\r\n/**\r\n * Options for browser sandbox creation.\r\n */\r\nexport interface BrowserSandboxOptions\r\n{\r\n\t/** Timeout in ms for fight/simulate calls (default: 30000). */\r\n\ttimeoutMs?: number;\r\n\t/** Custom worker factory for dependency injection (testing). */\r\n\tcreateWorker?: WorkerFactory;\r\n}\r\n\r\n// ============================================================\r\n// WORKER BOOTSTRAP\r\n// ============================================================\r\n\r\n/**\r\n * JavaScript bootstrap code appended to the compiled bundle.\r\n * Runs inside the worker after the IIFE sets up the relevant globals\r\n * (e.g., __fight/__simulate for fight bundles, __manualMatchInit/Step/etc\r\n * for manual-play bundles).\r\n *\r\n * The dispatcher is generic: any method name `foo` resolves to\r\n * `globalThis.__foo`. This allows new bundle types to expose new method\r\n * names without changing the bootstrap.\r\n *\r\n * Environment-adaptive: works in both browser Web Workers and\r\n * Node.js worker_threads (for testing).\r\n */\r\nconst WORKER_BOOTSTRAP = `\r\n;(function() {\r\n\tvar _port = null;\r\n\ttry { _port = require('worker_threads').parentPort; } catch(e) {}\r\n\r\n\tfunction send(data) {\r\n\t\tif (_port) _port.postMessage(data);\r\n\t\telse self.postMessage(data);\r\n\t}\r\n\r\n\tfunction listen(fn) {\r\n\t\tif (_port) _port.on('message', fn);\r\n\t\telse self.addEventListener('message', function(e) { fn(e.data); });\r\n\t}\r\n\r\n\tlisten(function(data) {\r\n\t\tif (data && data.method) {\r\n\t\t\ttry {\r\n\t\t\t\tvar fn = globalThis['__' + data.method];\r\n\t\t\t\tif (typeof fn !== 'function') {\r\n\t\t\t\t\tthrow new Error('Unknown sandbox method: ' + data.method);\r\n\t\t\t\t}\r\n\t\t\t\tvar result = fn(data.options);\r\n\t\t\t\tsend({id: data.id, result: result});\r\n\t\t\t} catch(err) {\r\n\t\t\t\tsend({id: data.id, error: (err && err.message) || String(err)});\r\n\t\t\t}\r\n\t\t}\r\n\t});\r\n\r\n\tsend({type: 'ready'});\r\n})();\r\n`;\r\n\r\n/**\r\n * Create the full worker script from a compiled match bundle.\r\n * Appends the message-handling bootstrap to the bundle IIFE.\r\n */\r\nexport function createWorkerScript(bundle: string): string\r\n{\r\n\treturn bundle + '\\n' + WORKER_BOOTSTRAP;\r\n}\r\n\r\n// ============================================================\r\n// BROWSER MATCH SANDBOX\r\n// ============================================================\r\n\r\ninterface PendingCall\r\n{\r\n\tresolve: (value: unknown) => void;\r\n\treject: (error: Error) => void;\r\n\ttimer: ReturnType<typeof setTimeout>;\r\n}\r\n\r\n/**\r\n * Browser-compatible sandboxed match runner using Web Workers.\r\n *\r\n * Same compiled bundles as MatchSandbox (isolated-vm), but runs in a\r\n * Web Worker instead. All fight/simulate calls are async (postMessage-based).\r\n *\r\n * Usage:\r\n * ```ts\r\n * // Bundle is compiled server-side or at build time (Node.js only)\r\n * const bundle = await MatchSandbox.compile(bot1, bot2);\r\n *\r\n * // Run in browser via Web Worker\r\n * const sandbox = await BrowserMatchSandbox.fromBundle(bundle);\r\n * const result = await sandbox.fight({ seed: 42 });\r\n * sandbox.dispose();\r\n * ```\r\n */\r\nexport class BrowserMatchSandbox\r\n{\r\n\tprivate worker: WorkerLike;\r\n\tprivate workerCleanup: (() => void) | undefined;\r\n\tprivate timeout: number;\r\n\tprivate disposed = false;\r\n\tprivate nextId = 0;\r\n\tprivate pending = new Map<number, PendingCall>();\r\n\tprivate messageHandler: ((ev: unknown) => void) | null = null;\r\n\tprivate errorHandler: ((ev: unknown) => void) | null = null;\r\n\r\n\tprivate constructor(\r\n\t\tworker: WorkerLike,\r\n\t\tworkerCleanup: (() => void) | undefined,\r\n\t\ttimeout: number,\r\n\t)\r\n\t{\r\n\t\tthis.worker = worker;\r\n\t\tthis.workerCleanup = workerCleanup;\r\n\t\tthis.timeout = timeout;\r\n\t}\r\n\r\n\t/**\r\n\t * Create a browser sandbox from a pre-compiled bundle string.\r\n\t * The bundle should be the output of MatchSandbox.compile() (or equivalent IIFE\r\n\t * that sets globalThis.__fight and globalThis.__simulate).\r\n\t */\r\n\tstatic async fromBundle(\r\n\t\tbundle: string,\r\n\t\toptions?: BrowserSandboxOptions,\r\n\t): Promise<BrowserMatchSandbox>\r\n\t{\r\n\t\tconst timeout = options?.timeoutMs ?? 30000;\r\n\t\tconst code = createWorkerScript(bundle);\r\n\r\n\t\tlet worker: WorkerLike;\r\n\t\tlet cleanup: (() => void) | undefined;\r\n\r\n\t\tif (options?.createWorker)\r\n\t\t{\r\n\t\t\tconst created = options.createWorker(code);\r\n\t\t\tworker = created.worker;\r\n\t\t\tcleanup = created.cleanup;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// Browser default: Blob URL Worker\r\n\t\t\t// Uses globalThis to avoid TypeScript error in Node.js builds (no DOM lib)\r\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- runtime browser check\r\n\t\t\tconst BrowserWorker = (globalThis as Record<string, unknown>).Worker as\r\n\t\t\t\t{new (url: string | URL): WorkerLike} | undefined;\r\n\t\t\tif (!BrowserWorker)\r\n\t\t\t{\r\n\t\t\t\tthrow new Error(\r\n\t\t\t\t\t'Web Worker API not available. Pass a createWorker factory for Node.js usage.',\r\n\t\t\t\t);\r\n\t\t\t}\r\n\t\t\tconst blob = new Blob([code], {type: 'application/javascript'});\r\n\t\t\tconst blobUrl = URL.createObjectURL(blob);\r\n\t\t\tcleanup = (): void => URL.revokeObjectURL(blobUrl);\r\n\t\t\tworker = new BrowserWorker(blobUrl);\r\n\t\t}\r\n\r\n\t\tconst sandbox = new BrowserMatchSandbox(worker, cleanup, timeout);\r\n\r\n\t\ttry\r\n\t\t{\r\n\t\t\tawait sandbox.waitForReady(timeout);\r\n\t\t}\r\n\t\tcatch(error)\r\n\t\t{\r\n\t\t\tsandbox.dispose();\r\n\t\t\tthrow error;\r\n\t\t}\r\n\r\n\t\treturn sandbox;\r\n\t}\r\n\r\n\t/**\r\n\t * Wait for the worker to post {type: 'ready'}, then attach permanent handlers.\r\n\t */\r\n\tprivate waitForReady(timeout: number): Promise<void>\r\n\t{\r\n\t\treturn new Promise<void>((resolve, reject) =>\r\n\t\t{\r\n\t\t\tconst timer = setTimeout(() =>\r\n\t\t\t{\r\n\t\t\t\treject(new Error('Worker initialization timed out'));\r\n\t\t\t}, timeout);\r\n\r\n\t\t\tconst readyListener = (ev: unknown): void =>\r\n\t\t\t{\r\n\t\t\t\t// Unwrap MessageEvent (browser) or raw data (Node.js adapter)\r\n\t\t\t\tconst data = this.unwrapEvent(ev);\r\n\t\t\t\tif (data && typeof data === 'object' && 'type' in data && data.type === 'ready')\r\n\t\t\t\t{\r\n\t\t\t\t\tclearTimeout(timer);\r\n\t\t\t\t\tthis.worker.removeEventListener('message', readyListener);\r\n\t\t\t\t\tthis.attachHandlers();\r\n\t\t\t\t\tresolve();\r\n\t\t\t\t}\r\n\t\t\t};\r\n\r\n\t\t\tthis.worker.addEventListener('message', readyListener);\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * Extract message data from a browser MessageEvent or raw Node.js data.\r\n\t */\r\n\tprivate unwrapEvent(ev: unknown): Record<string, unknown> | null\r\n\t{\r\n\t\tif (ev == null) return null;\r\n\t\tif (typeof ev !== 'object') return null;\r\n\r\n\t\t// Browser MessageEvent has .data\r\n\t\tconst maybeEvent = ev as {data?: unknown};\r\n\t\tconst raw = maybeEvent.data !== undefined ? maybeEvent.data : ev;\r\n\r\n\t\tif (raw != null && typeof raw === 'object')\r\n\t\t{\r\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- runtime type-checked above\r\n\t\t\treturn raw as Record<string, unknown>;\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/**\r\n\t * Attach permanent message and error handlers for fight/simulate responses.\r\n\t */\r\n\tprivate attachHandlers(): void\r\n\t{\r\n\t\tthis.messageHandler = (ev: unknown): void =>\r\n\t\t{\r\n\t\t\tconst data = this.unwrapEvent(ev);\r\n\t\t\tif (!data || data.id == null) return;\r\n\r\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- id is always a number from our protocol\r\n\t\t\tconst id = data.id as number;\r\n\t\t\tconst handler = this.pending.get(id);\r\n\t\t\tif (!handler) return;\r\n\r\n\t\t\tclearTimeout(handler.timer);\r\n\t\t\tthis.pending.delete(id);\r\n\r\n\t\t\tif (data.error)\r\n\t\t\t{\r\n\t\t\t\thandler.reject(new Error(String(data.error)));\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\thandler.resolve(data.result);\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tthis.errorHandler = (ev: unknown): void =>\r\n\t\t{\r\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- ErrorEvent shape from Worker\r\n\t\t\tconst message = (ev as {message?: string}).message ?? 'Worker error';\r\n\t\t\t// Reject all pending calls\r\n\t\t\tfor (const [, handler] of this.pending)\r\n\t\t\t{\r\n\t\t\t\tclearTimeout(handler.timer);\r\n\t\t\t\thandler.reject(new Error(message));\r\n\t\t\t}\r\n\t\t\tthis.pending.clear();\r\n\t\t};\r\n\r\n\t\tthis.worker.addEventListener('message', this.messageHandler);\r\n\t\tthis.worker.addEventListener('error', this.errorHandler);\r\n\t}\r\n\r\n\t// ============================================================\r\n\t// PUBLIC API\r\n\t// ============================================================\r\n\r\n\t/**\r\n\t * Run a full fight (10 matches: 5 spawn distances x 2 sides).\r\n\t * Returns a Promise because Worker communication is async.\r\n\t */\r\n\tasync fight(options?: {seed?: number; maxTicks?: number}): Promise<FightResult>\r\n\t{\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- worker returns FightResult shape\r\n\t\treturn this.call('fight', options ?? {}) as Promise<FightResult>;\r\n\t}\r\n\r\n\t/**\r\n\t * Run a single simulation. Returns a Promise.\r\n\t *\r\n\t * @param options.params1 - useParam overrides for bot 1 (wizard-1)\r\n\t * @param options.params2 - useParam overrides for bot 2 (wizard-2)\r\n\t */\r\n\tasync simulate(options?: {\r\n\t\tseed?: number;\r\n\t\tmaxTicks?: number;\r\n\t\tspawnDistance?: number;\r\n\t\tskipHistory?: boolean;\r\n\t\tparams1?: Record<string, number>;\r\n\t\tparams2?: Record<string, number>;\r\n\t}): Promise<SimulateResult>\r\n\t{\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- worker returns SimulateResult shape\r\n\t\treturn this.call('simulate', options ?? {}) as Promise<SimulateResult>;\r\n\t}\r\n\r\n\t/**\r\n\t * Dispose the worker and free all resources.\r\n\t * The sandbox cannot be used after disposal.\r\n\t */\r\n\tdispose(): void\r\n\t{\r\n\t\tif (!this.disposed)\r\n\t\t{\r\n\t\t\tthis.disposed = true;\r\n\r\n\t\t\t// Reject all pending calls\r\n\t\t\tfor (const [, handler] of this.pending)\r\n\t\t\t{\r\n\t\t\t\tclearTimeout(handler.timer);\r\n\t\t\t\thandler.reject(new Error('BrowserMatchSandbox has been disposed'));\r\n\t\t\t}\r\n\t\t\tthis.pending.clear();\r\n\r\n\t\t\t// Remove event listeners\r\n\t\t\tif (this.messageHandler)\r\n\t\t\t{\r\n\t\t\t\tthis.worker.removeEventListener('message', this.messageHandler);\r\n\t\t\t}\r\n\t\t\tif (this.errorHandler)\r\n\t\t\t{\r\n\t\t\t\tthis.worker.removeEventListener('error', this.errorHandler);\r\n\t\t\t}\r\n\r\n\t\t\t// Terminate worker\r\n\t\t\tthis.worker.terminate();\r\n\r\n\t\t\t// Clean up Blob URL (or other resources)\r\n\t\t\tthis.workerCleanup?.();\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Whether this sandbox has been disposed.\r\n\t */\r\n\tget isDisposed(): boolean\r\n\t{\r\n\t\treturn this.disposed;\r\n\t}\r\n\r\n\t// ============================================================\r\n\t// INTERNAL\r\n\t// ============================================================\r\n\r\n\t/**\r\n\t * Send a generic method call to the worker. Used by sibling sandboxes\r\n\t * (e.g. BrowserManualMatchSandbox) that need to dispatch to method\r\n\t * names other than fight/simulate. The worker bootstrap looks up\r\n\t * `globalThis['__' + method]` and calls it with `options`.\r\n\t */\r\n\tcallRaw(method: string, options: unknown): Promise<unknown>\r\n\t{\r\n\t\treturn this.call(method, options);\r\n\t}\r\n\r\n\t/**\r\n\t * Send a method call to the worker and wait for the response.\r\n\t * Times out and terminates the worker if no response within timeout.\r\n\t */\r\n\tprivate call(method: string, options: unknown): Promise<unknown>\r\n\t{\r\n\t\tthis.ensureNotDisposed();\r\n\t\tconst id = this.nextId++;\r\n\r\n\t\treturn new Promise((resolve, reject) =>\r\n\t\t{\r\n\t\t\tconst timer = setTimeout(() =>\r\n\t\t\t{\r\n\t\t\t\tthis.pending.delete(id);\r\n\t\t\t\t// Worker is stuck — terminate and clean up all resources\r\n\t\t\t\tthis.worker.terminate();\r\n\t\t\t\tthis.workerCleanup?.();\r\n\t\t\t\tthis.disposed = true;\r\n\t\t\t\treject(new Error(`BrowserMatchSandbox timed out after ${this.timeout}ms`));\r\n\t\t\t}, this.timeout);\r\n\r\n\t\t\tthis.pending.set(id, {resolve, reject, timer});\r\n\t\t\tthis.worker.postMessage({id, method, options});\r\n\t\t});\r\n\t}\r\n\r\n\tprivate ensureNotDisposed(): void\r\n\t{\r\n\t\tif (this.disposed)\r\n\t\t{\r\n\t\t\tthrow new Error('BrowserMatchSandbox has been disposed');\r\n\t\t}\r\n\t}\r\n}\r\n\r\n// ============================================================\r\n// CONVENIENCE FUNCTIONS\r\n// ============================================================\r\n\r\n/**\r\n * One-shot browser-sandboxed fight. Creates worker, runs fight, disposes.\r\n */\r\nexport async function browserSandboxFight(\r\n\tbundle: string,\r\n\toptions?: {seed?: number; maxTicks?: number} & BrowserSandboxOptions,\r\n): Promise<FightResult>\r\n{\r\n\tconst sandbox = await BrowserMatchSandbox.fromBundle(bundle, options);\r\n\ttry\r\n\t{\r\n\t\treturn await sandbox.fight({seed: options?.seed, maxTicks: options?.maxTicks});\r\n\t}\r\n\tfinally\r\n\t{\r\n\t\tsandbox.dispose();\r\n\t}\r\n}\r\n\r\n/**\r\n * One-shot browser-sandboxed simulate. Creates worker, runs simulate, disposes.\r\n */\r\nexport async function browserSandboxSimulate(\r\n\tbundle: string,\r\n\toptions?: {\r\n\t\tseed?: number;\r\n\t\tmaxTicks?: number;\r\n\t\tspawnDistance?: number;\r\n\t\tskipHistory?: boolean;\r\n\t\tparams1?: Record<string, number>;\r\n\t\tparams2?: Record<string, number>;\r\n\t} & BrowserSandboxOptions,\r\n): Promise<SimulateResult>\r\n{\r\n\tconst sandbox = await BrowserMatchSandbox.fromBundle(bundle, options);\r\n\ttry\r\n\t{\r\n\t\treturn await sandbox.simulate({\r\n\t\t\tseed: options?.seed,\r\n\t\t\tmaxTicks: options?.maxTicks,\r\n\t\t\tspawnDistance: options?.spawnDistance,\r\n\t\t\tskipHistory: options?.skipHistory,\r\n\t\t\tparams1: options?.params1,\r\n\t\t\tparams2: options?.params2,\r\n\t\t});\r\n\t}\r\n\tfinally\r\n\t{\r\n\t\tsandbox.dispose();\r\n\t}\r\n}\r\n\r\n// ============================================================\r\n// BROWSER MANUAL-MATCH SANDBOX\r\n// ============================================================\r\n\r\n/**\r\n * Options accepted by `__manualMatchInit` (worker-side). Mirrors\r\n * `ManualMatchOptions` from manual-match.ts, but without the constructor's\r\n * AI parameters since the player AI is a worker-local stub.\r\n */\r\nexport interface ManualMatchInitOptions\r\n{\r\n\tseed?: number;\r\n\tspawnDistance?: number;\r\n\tmaxTicks?: number;\r\n\tinitialHumanActions?: WizardActions;\r\n}\r\n\r\nexport interface ManualMatchStepRequest\r\n{\r\n\thumanActions?: WizardActions;\r\n\thumanMissileTargets?: Record<string, {x: number; y: number}>;\r\n\tcount?: number;\r\n}\r\n\r\n/**\r\n * Long-lived Web Worker sandbox holding a single ManualMatch instance.\r\n *\r\n * Unlike BrowserMatchSandbox (which runs one batched fight/simulate per\r\n * worker), BrowserManualMatchSandbox keeps the worker alive across many\r\n * step calls so the engine state and hook state persist between ticks.\r\n * This is what manual play mode uses: one worker per session, disposed\r\n * when the user leaves the page or starts a new match.\r\n *\r\n * Usage:\r\n * ```ts\r\n * const sandbox = await BrowserManualMatchSandbox.fromBundle(opponentBundle);\r\n * await sandbox.init({seed: 42});\r\n * for (let i = 0; i < 100; i++) {\r\n * await sandbox.step({humanActions: {move: {x: 100, y: 0}}, count: 1});\r\n * }\r\n * await sandbox.dispose();\r\n * ```\r\n */\r\nexport class BrowserManualMatchSandbox\r\n{\r\n\tprivate readonly inner: BrowserMatchSandbox;\r\n\r\n\tprivate constructor(inner: BrowserMatchSandbox)\r\n\t{\r\n\t\tthis.inner = inner;\r\n\t}\r\n\r\n\t/**\r\n\t * Create a manual-match sandbox from a pre-compiled bundle. The bundle\r\n\t * must be the output of `compileManualMatchBundle()` — the regular\r\n\t * `compileMatchBundle()` output won't work since it doesn't expose the\r\n\t * `__manualMatch*` globals.\r\n\t */\r\n\tstatic async fromBundle(\r\n\t\tbundle: string,\r\n\t\toptions?: BrowserSandboxOptions,\r\n\t): Promise<BrowserManualMatchSandbox>\r\n\t{\r\n\t\tconst inner = await BrowserMatchSandbox.fromBundle(bundle, options);\r\n\t\treturn new BrowserManualMatchSandbox(inner);\r\n\t}\r\n\r\n\t/**\r\n\t * Initialize the worker-side ManualMatch instance.\r\n\t * Returns the initial GameState (tick 0).\r\n\t */\r\n\tasync init(options?: ManualMatchInitOptions): Promise<GameState>\r\n\t{\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- worker returns GameState shape\r\n\t\treturn this.inner.callRaw('manualMatchInit', options ?? {}) as Promise<GameState>;\r\n\t}\r\n\r\n\t/**\r\n\t * Advance the match by `request.count` ticks (default 1), updating\r\n\t * the player's human actions and any hijacked missile targets first.\r\n\t */\r\n\tasync step(request: ManualMatchStepRequest = {}): Promise<StepResult>\r\n\t{\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- worker returns StepResult shape\r\n\t\treturn this.inner.callRaw('manualMatchStep', request) as Promise<StepResult>;\r\n\t}\r\n\r\n\t/**\r\n\t * Replace a missile's AI with a worker-local hijack stub that reads\r\n\t * from `latestHumanMissileTargets[projectileId]`. Subsequent step()\r\n\t * calls with `humanMissileTargets` populated for this id steer the\r\n\t * missile.\r\n\t */\r\n\tasync hijackMissile(projectileId: string): Promise<void>\r\n\t{\r\n\t\tawait this.inner.callRaw('manualMatchHijack', {projectileId});\r\n\t}\r\n\r\n\t/**\r\n\t * Restore a hijacked missile's original AI.\r\n\t */\r\n\tasync releaseMissile(projectileId: string): Promise<void>\r\n\t{\r\n\t\tawait this.inner.callRaw('manualMatchRelease', {projectileId});\r\n\t}\r\n\r\n\t/**\r\n\t * Toggle invincibility for a wizard.\r\n\t */\r\n\tasync setInvincible(wizardIndex: 0 | 1, on: boolean): Promise<void>\r\n\t{\r\n\t\tawait this.inner.callRaw('manualMatchSetInvincible', {wizardIndex, on});\r\n\t}\r\n\r\n\t/**\r\n\t * Get the current game state without advancing.\r\n\t */\r\n\tasync getState(): Promise<GameState>\r\n\t{\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- worker returns GameState shape\r\n\t\treturn this.inner.callRaw('manualMatchGetState', {}) as Promise<GameState>;\r\n\t}\r\n\r\n\t/**\r\n\t * Get the full SimulateResult-compatible result object (history + winner).\r\n\t */\r\n\tasync getResult(): Promise<SimulateResult>\r\n\t{\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- worker returns SimulateResult shape\r\n\t\treturn this.inner.callRaw('manualMatchGetResult', {}) as Promise<SimulateResult>;\r\n\t}\r\n\r\n\t/**\r\n\t * Dispose the worker-side ManualMatch instance. Does NOT terminate the\r\n\t * worker — call dispose() for that.\r\n\t */\r\n\tasync resetMatch(): Promise<void>\r\n\t{\r\n\t\tawait this.inner.callRaw('manualMatchDispose', {});\r\n\t}\r\n\r\n\t/**\r\n\t * Terminate the worker and free all resources.\r\n\t */\r\n\tdispose(): void\r\n\t{\r\n\t\tthis.inner.dispose();\r\n\t}\r\n\r\n\tget isDisposed(): boolean\r\n\t{\r\n\t\treturn this.inner.isDisposed;\r\n\t}\r\n}\r\n\r\n/**\r\n * Re-import-friendly types (re-exported here so consumers don't need to\r\n * cherry-pick from internal modules).\r\n */\r\nexport type {\r\n\tBotError,\r\n\tGameState,\r\n\tMatchWinner,\r\n\tMissileAIFunction,\r\n\tProjectileState,\r\n\tStepResult,\r\n\tWizardActions,\r\n};\r\n","/**\r\n * VIBEMANCER - FIGHT STATISTICS\r\n *\r\n * Extracts detailed per-bot statistics from a SimulateResult history.\r\n * Used by testBot().simulate() and vibemancer trace for debugging.\r\n */\r\n\r\nimport type {GameState} from './types.js';\r\nimport type {SimulateResult} from './engine/simulation.js';\r\nimport {TICKS_PER_SECOND, SHIELD_MAX_BLOCK, SHIELD_DECAY_PER_SECOND, SHIELD_MIN_BLOCK} from './rules.js';\r\n\r\n/** Detailed statistics for one bot in a single match. */\r\nexport interface FightStats\r\n{\r\n\t/** Total damage dealt to the opponent. */\r\n\tdamageDealt: number;\r\n\t/** Total damage taken from the opponent. */\r\n\tdamageTaken: number;\r\n\t/** Number of missiles launched. */\r\n\tmissilesLaunched: number;\r\n\t/** Number of missiles that dealt damage (hit the opponent). */\r\n\tmissileHits: number;\r\n\t/** Hit rate (0-1). NaN if no missiles fired. */\r\n\thitRate: number;\r\n\t/** Number of our missiles that hit while enemy was shielding. */\r\n\thitsEnemyShielded: number;\r\n\t/** Number of our missiles that hit while enemy had no shield. */\r\n\thitsEnemyUnshielded: number;\r\n\t/** Total raw damage our missiles would have dealt without shields. */\r\n\trawDamageDealt: number;\r\n\t/** Damage blocked by enemy shields. */\r\n\tdamageBlockedByEnemy: number;\r\n\t/** Percentage of our raw damage blocked by enemy shields (0-1). */\r\n\tenemyBlockRate: number;\r\n\t/** Number of times we were hit by enemy missiles. */\r\n\thitsReceived: number;\r\n\t/** Number of hits received while shield was channeling. */\r\n\thitsShielded: number;\r\n\t/** Number of hits received without shield. */\r\n\thitsUnshielded: number;\r\n\t/** Total raw damage that hit us (before shield reduction). */\r\n\trawDamageReceived: number;\r\n\t/** Total damage blocked by shields. */\r\n\tdamageBlocked: number;\r\n\t/** Percentage of incoming raw damage that was blocked (0-1). */\r\n\tblockRate: number;\r\n\t/** Ticks spent in 'casting' state. */\r\n\tcastingTicks: number;\r\n\t/** Ticks spent channeling shield. */\r\n\tshieldTicks: number;\r\n\t/** Ticks spent in GCD lockout. */\r\n\tgcdTicks: number;\r\n\t/** Ticks spent idle (not casting, channeling, or in GCD). */\r\n\tidleTicks: number;\r\n\t/** Number of times shield was activated. */\r\n\tshieldCount: number;\r\n\t/** Number of times blink was used. */\r\n\tblinkCount: number;\r\n\t/** Total match duration in ticks. */\r\n\ttotalTicks: number;\r\n}\r\n\r\n/**\r\n * Compute the shield block percentage at a given channel duration.\r\n * Same formula as the engine: starts at 90%, decays 20%/sec, min 30%.\r\n */\r\nfunction shieldBlockPct(channelDurationTicks: number): number\r\n{\r\n\tconst channelSec = channelDurationTicks / TICKS_PER_SECOND;\r\n\treturn Math.max(SHIELD_MIN_BLOCK, SHIELD_MAX_BLOCK - SHIELD_DECAY_PER_SECOND * channelSec);\r\n}\r\n\r\n/**\r\n * Extract fight statistics from a SimulateResult.\r\n * Returns stats for wizard-1 (the bot under test).\r\n */\r\nexport function extractStats(result: SimulateResult): FightStats\r\n{\r\n\tconst history = result.history;\r\n\tif (history.length === 0)\r\n\t{\r\n\t\treturn emptyStats(result.ticks);\r\n\t}\r\n\r\n\tlet damageDealt = 0;\r\n\tlet damageTaken = 0;\r\n\tlet missilesLaunched = 0;\r\n\tlet missileHits = 0;\r\n\tlet hitsEnemyShielded = 0;\r\n\tlet hitsEnemyUnshielded = 0;\r\n\tlet rawDamageDealt = 0;\r\n\tlet damageBlockedByEnemy = 0;\r\n\tlet hitsReceived = 0;\r\n\tlet hitsShielded = 0;\r\n\tlet hitsUnshielded = 0;\r\n\tlet rawDamageReceived = 0;\r\n\tlet damageBlocked = 0;\r\n\tlet castingTicks = 0;\r\n\tlet shieldTicks = 0;\r\n\tlet gcdTicks = 0;\r\n\tlet idleTicks = 0;\r\n\tlet shieldCount = 0;\r\n\tlet blinkCount = 0;\r\n\r\n\tlet prevState: GameState | null = null;\r\n\r\n\tfor (const state of history)\r\n\t{\r\n\t\tif (prevState)\r\n\t\t{\r\n\t\t\t// Damage dealt (enemy HP decreased)\r\n\t\t\tconst prevEnemy = prevState.enemies?.[0];\r\n\t\t\tconst curEnemy = state.enemies?.[0];\r\n\t\t\tconst prevEnemyHp = prevEnemy?.health ?? 0;\r\n\t\t\tconst curEnemyHp = curEnemy?.health ?? 0;\r\n\t\t\tif (curEnemyHp < prevEnemyHp)\r\n\t\t\t{\r\n\t\t\t\tconst dmg = prevEnemyHp - curEnemyHp;\r\n\t\t\t\tdamageDealt += dmg;\r\n\t\t\t\tmissileHits++;\r\n\r\n\t\t\t\tif (prevEnemy?.channelingSpell === 'shield')\r\n\t\t\t\t{\r\n\t\t\t\t\t// Our missile hit while enemy was shielding\r\n\t\t\t\t\thitsEnemyShielded++;\r\n\t\t\t\t\tconst enemyBlockPct = shieldBlockPct(prevEnemy.channelDuration ?? 0);\r\n\t\t\t\t\tconst rawDmg = dmg / (1 - enemyBlockPct);\r\n\t\t\t\t\trawDamageDealt += rawDmg;\r\n\t\t\t\t\tdamageBlockedByEnemy += rawDmg - dmg;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t// Hit without shield — raw = actual\r\n\t\t\t\t\thitsEnemyUnshielded++;\r\n\t\t\t\t\trawDamageDealt += dmg;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Damage taken (our HP decreased)\r\n\t\t\tif (state.health < prevState.health)\r\n\t\t\t{\r\n\t\t\t\tconst dmg = prevState.health - state.health;\r\n\t\t\t\tdamageTaken += dmg;\r\n\t\t\t\thitsReceived++;\r\n\r\n\t\t\t\tif (prevState.channelingSpell === 'shield')\r\n\t\t\t\t{\r\n\t\t\t\t\t// Hit while shielding — compute actual block amount\r\n\t\t\t\t\thitsShielded++;\r\n\t\t\t\t\tconst blockPct = shieldBlockPct(prevState.channelDuration ?? 0);\r\n\t\t\t\t\t// damageTaken = rawDamage * (1 - blockPct)\r\n\t\t\t\t\t// rawDamage = damageTaken / (1 - blockPct)\r\n\t\t\t\t\tconst rawDmg = dmg / (1 - blockPct);\r\n\t\t\t\t\tconst blocked = rawDmg - dmg;\r\n\t\t\t\t\trawDamageReceived += rawDmg;\r\n\t\t\t\t\tdamageBlocked += blocked;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t// Hit without shield — raw damage = damage taken\r\n\t\t\t\t\thitsUnshielded++;\r\n\t\t\t\t\trawDamageReceived += dmg;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Missile launches (new projectiles appearing)\r\n\t\t\tconst prevMyProj = prevState.myProjectiles?.length ?? 0;\r\n\t\t\tconst curMyProj = state.myProjectiles?.length ?? 0;\r\n\t\t\tif (curMyProj > prevMyProj)\r\n\t\t\t{\r\n\t\t\t\tmissilesLaunched += curMyProj - prevMyProj;\r\n\t\t\t}\r\n\r\n\t\t\t// State tracking\r\n\t\t\tif (state.castingSpell) castingTicks++;\r\n\t\t\telse if (state.channelingSpell === 'shield') shieldTicks++;\r\n\t\t\telse if (state.gcdRemaining && state.gcdRemaining > 0) gcdTicks++;\r\n\t\t\telse idleTicks++;\r\n\r\n\t\t\t// Shield activations\r\n\t\t\tif (state.channelingSpell === 'shield' && prevState.channelingSpell !== 'shield')\r\n\t\t\t{\r\n\t\t\t\tshieldCount++;\r\n\t\t\t}\r\n\r\n\t\t\t// Blink usage\r\n\t\t\tif (prevState.castingSpell === 'blink' && state.castingSpell !== 'blink' && state.gcdRemaining && state.gcdRemaining > 0)\r\n\t\t\t{\r\n\t\t\t\tblinkCount++;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprevState = state;\r\n\t}\r\n\r\n\tconst totalTicks = result.ticks;\r\n\r\n\treturn {\r\n\t\tdamageDealt,\r\n\t\tdamageTaken,\r\n\t\tmissilesLaunched,\r\n\t\tmissileHits,\r\n\t\thitRate: missilesLaunched > 0 ? missileHits / missilesLaunched : NaN,\r\n\t\thitsEnemyShielded,\r\n\t\thitsEnemyUnshielded,\r\n\t\trawDamageDealt,\r\n\t\tdamageBlockedByEnemy,\r\n\t\tenemyBlockRate: rawDamageDealt > 0 ? damageBlockedByEnemy / rawDamageDealt : 0,\r\n\t\thitsReceived,\r\n\t\thitsShielded,\r\n\t\thitsUnshielded,\r\n\t\trawDamageReceived,\r\n\t\tdamageBlocked,\r\n\t\tblockRate: rawDamageReceived > 0 ? damageBlocked / rawDamageReceived : 0,\r\n\t\tcastingTicks,\r\n\t\tshieldTicks,\r\n\t\tgcdTicks,\r\n\t\tidleTicks,\r\n\t\tshieldCount,\r\n\t\tblinkCount,\r\n\t\ttotalTicks,\r\n\t};\r\n}\r\n\r\nfunction emptyStats(totalTicks: number): FightStats\r\n{\r\n\treturn {\r\n\t\tdamageDealt: 0, damageTaken: 0,\r\n\t\tmissilesLaunched: 0, missileHits: 0, hitRate: NaN,\r\n\t\thitsEnemyShielded: 0, hitsEnemyUnshielded: 0,\r\n\t\trawDamageDealt: 0, damageBlockedByEnemy: 0, enemyBlockRate: 0,\r\n\t\thitsReceived: 0, hitsShielded: 0, hitsUnshielded: 0,\r\n\t\trawDamageReceived: 0, damageBlocked: 0, blockRate: 0,\r\n\t\tcastingTicks: 0, shieldTicks: 0, gcdTicks: 0, idleTicks: totalTicks,\r\n\t\tshieldCount: 0, blinkCount: 0, totalTicks,\r\n\t};\r\n}\r\n\r\n/** Format stats as a human-readable summary string. */\r\nexport function formatStats(stats: FightStats, botName: string): string\r\n{\r\n\tconst lines: string[] = [];\r\n\tconst pct = (n: number): string => ((n / stats.totalTicks) * 100).toFixed(1);\r\n\tconst sec = (n: number): string => (n / TICKS_PER_SECOND).toFixed(1);\r\n\r\n\tlines.push(` ${botName} stats:`);\r\n\tlines.push(' Offense:');\r\n\tlines.push(` Missiles: ${stats.missilesLaunched} fired, ${stats.missileHits} hit (${isNaN(stats.hitRate) ? 'N/A' : (stats.hitRate * 100).toFixed(0) + '%'})`);\r\n\tlines.push(` Damage: ${stats.damageDealt.toFixed(1)} dealt${stats.damageBlockedByEnemy > 0 ? ` (${stats.rawDamageDealt.toFixed(1)} raw, ${stats.damageBlockedByEnemy.toFixed(1)} blocked by enemy = ${(stats.enemyBlockRate * 100).toFixed(0)}%)` : ''}`);\r\n\tif (stats.missileHits > 0)\r\n\t{\r\n\t\tlines.push(` Hits: ${stats.hitsEnemyUnshielded} clean, ${stats.hitsEnemyShielded} shielded`);\r\n\t}\r\n\tlines.push(' Defense:');\r\n\tlines.push(` Hits taken: ${stats.hitsReceived} — ${stats.hitsUnshielded} unshielded, ${stats.hitsShielded} shielded`);\r\n\tlines.push(` Damage: ${stats.damageTaken.toFixed(1)} taken${stats.damageBlocked > 0 ? ` (${stats.rawDamageReceived.toFixed(1)} raw, ${stats.damageBlocked.toFixed(1)} blocked = ${(stats.blockRate * 100).toFixed(0)}%)` : ''}`);\r\n\tlines.push(` Shield: ${stats.shieldCount} activations, ${sec(stats.shieldTicks)}s channeled`);\r\n\tlines.push(` Blinks: ${stats.blinkCount}`);\r\n\tlines.push(` Time: ${pct(stats.castingTicks)}% casting, ${pct(stats.shieldTicks)}% shielding, ${pct(stats.gcdTicks)}% GCD, ${pct(stats.idleTicks)}% idle`);\r\n\r\n\treturn lines.join('\\n');\r\n}\r\n","/**\r\n * VIBEMANCER - TESTING UTILITIES\r\n *\r\n * Helpers for writing automated tests for your bot.\r\n * Import from '@vibemancer/core' in your test files.\r\n *\r\n * @example\r\n * import {testBot} from '@vibemancer/core';\r\n * import {MyWizard} from '../src/bot';\r\n *\r\n * test('beats TargetDummy', async () => {\r\n * const result = await testBot(MyWizard).fight('TargetDummy');\r\n * expect(result.won).toBe(true);\r\n * });\r\n */\r\n\r\nimport type {BotFunction} from './hooks/types.js';\r\nimport type {WizardFunction} from './types.js';\r\nimport type {FightResult, SimulateResult, MatchWinner, FightWinner} from './engine/simulation.js';\r\nimport {fight, simulate, FIGHT_SPAWN_DISTANCES} from './engine/simulation.js';\r\nimport {isNewStyleBot, wrapNewBot} from './hooks/bot-wrapper.js';\r\nimport {extractStats} from './stats.js';\r\nimport type {FightStats} from './stats.js';\r\n\r\n// Import all built-in bots\r\nimport * as allBots from './bots/index.js';\r\n\r\n/** Result of a testBot().fight() call with convenience accessors. */\r\nexport interface TestFightResult\r\n{\r\n\t/** The raw FightResult from the simulation engine. */\r\n\traw: FightResult;\r\n\t/** Overall winner: 'wizard-1' | 'wizard-2' | 'draw'. */\r\n\twinner: FightWinner;\r\n\t/** True if your bot won the fight. */\r\n\twon: boolean;\r\n\t/** True if your bot lost the fight. */\r\n\tlost: boolean;\r\n\t/** True if the fight was a draw. */\r\n\tdrawn: boolean;\r\n\t/** Number of individual matches your bot won (out of 10). */\r\n\twins: number;\r\n\t/** Number of individual matches your bot lost (out of 10). */\r\n\tlosses: number;\r\n\t/** Number of individual matches that were draws. */\r\n\tdraws: number;\r\n}\r\n\r\n/** Result of a testBot().simulate() call with convenience accessors. */\r\nexport interface TestSimulateResult\r\n{\r\n\t/** The raw SimulateResult from the simulation engine. */\r\n\traw: SimulateResult;\r\n\t/** Match winner. */\r\n\twinner: MatchWinner;\r\n\t/** True if your bot won. */\r\n\twon: boolean;\r\n\t/** True if your bot lost. */\r\n\tlost: boolean;\r\n\t/** True if the match was a draw or timeout. */\r\n\tdrawn: boolean;\r\n\t/** Number of ticks the match lasted. */\r\n\tticks: number;\r\n\t/** Your bot's remaining HP. */\r\n\tmyHealth: number;\r\n\t/** Enemy's remaining HP. */\r\n\tenemyHealth: number;\r\n\t/** Detailed fight statistics (missiles, damage, shield usage, time breakdown). */\r\n\tstats: FightStats;\r\n\t/** Runtime errors thrown by bot or missile AI (empty if none). */\r\n\terrors: Array<{tick: number; entityId: string; message: string}>;\r\n}\r\n\r\n/** Builder returned by testBot(). */\r\nexport interface TestBotBuilder\r\n{\r\n\t/**\r\n\t * Run a full fight (10 matches) against a named built-in bot.\r\n\t * @param opponent - Built-in bot name (e.g., 'Battlemage', 'TargetDummy').\r\n\t */\r\n\tfight(opponent: string, options?: {seed?: number}): TestFightResult;\r\n\r\n\t/**\r\n\t * Run a single match against a named built-in bot.\r\n\t * @param opponent - Built-in bot name.\r\n\t */\r\n\tsimulate(opponent: string, options?: {\r\n\t\tseed?: number;\r\n\t\tspawnDistance?: number;\r\n\t\tmaxTicks?: number;\r\n\t}): TestSimulateResult;\r\n}\r\n\r\n/**\r\n * Resolve a built-in bot name to a WizardFunction.\r\n */\r\nfunction resolveBuiltinBot(name: string): WizardFunction\r\n{\r\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- dynamic bot lookup\r\n\tconst botMap = allBots as unknown as Record<string, WizardFunction | BotFunction>;\r\n\tconst bot = botMap[name];\r\n\tif (!bot)\r\n\t{\r\n\t\tconst available = Object.keys(botMap).filter((k) => typeof botMap[k] === 'function').join(', ');\r\n\t\tthrow new Error(`Unknown opponent \"${name}\". Available bots: ${available}`);\r\n\t}\r\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- branching on length+shape detection\r\n\treturn typeof bot === 'function' && bot.length === 0 && isNewStyleBot(bot as BotFunction)\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- guarded by isNewStyleBot above\r\n\t\t? wrapNewBot(bot as BotFunction)\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- legacy style bot after guard\r\n\t\t: bot as WizardFunction;\r\n}\r\n\r\n/**\r\n * Wrap a bot function for use with the simulation engine.\r\n */\r\nfunction wrapBot(bot: WizardFunction | BotFunction): WizardFunction\r\n{\r\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- runtime shape detection\r\n\tif (isNewStyleBot(bot as BotFunction))\r\n\t{\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- guarded by isNewStyleBot above\r\n\t\treturn wrapNewBot(bot as BotFunction);\r\n\t}\r\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- legacy style bot after guard\r\n\treturn bot as WizardFunction;\r\n}\r\n\r\n/**\r\n * Create a test builder for your bot.\r\n *\r\n * @param bot - Your bot function (the same function you export from src/bot.ts).\r\n * @returns A builder with .fight() and .simulate() methods.\r\n *\r\n * @example\r\n * ```ts\r\n * import {testBot} from '@vibemancer/core';\r\n * import {MyWizard} from '../src/bot';\r\n *\r\n * test('beats TargetDummy', () => {\r\n * const result = testBot(MyWizard).fight('TargetDummy');\r\n * expect(result.won).toBe(true);\r\n * });\r\n *\r\n * test('survives 10 seconds against Battlemage', () => {\r\n * const result = testBot(MyWizard).simulate('Battlemage', {maxTicks: 1000});\r\n * expect(result.myHealth).toBeGreaterThan(0);\r\n * });\r\n *\r\n * test('kills at close range', () => {\r\n * const result = testBot(MyWizard).simulate('TargetDummy', {spawnDistance: 200});\r\n * expect(result.won).toBe(true);\r\n * expect(result.ticks).toBeLessThan(1000);\r\n * });\r\n * ```\r\n */\r\nexport function testBot(bot: WizardFunction | BotFunction): TestBotBuilder\r\n{\r\n\tconst wrappedBot = wrapBot(bot);\r\n\r\n\treturn {\r\n\t\tfight(opponent: string, options?: {seed?: number}): TestFightResult\r\n\t\t{\r\n\t\t\tconst opponentBot = resolveBuiltinBot(opponent);\r\n\t\t\tconst raw = fight(wrappedBot, opponentBot, {seed: options?.seed});\r\n\r\n\t\t\treturn {\r\n\t\t\t\traw,\r\n\t\t\t\twinner: raw.winner,\r\n\t\t\t\twon: raw.winner === 'wizard-1',\r\n\t\t\t\tlost: raw.winner === 'wizard-2',\r\n\t\t\t\tdrawn: raw.winner === 'draw',\r\n\t\t\t\twins: raw.wizard1Wins,\r\n\t\t\t\tlosses: raw.wizard2Wins,\r\n\t\t\t\tdraws: raw.draws,\r\n\t\t\t};\r\n\t\t},\r\n\r\n\t\tsimulate(opponent: string, options?: {\r\n\t\t\tseed?: number;\r\n\t\t\tspawnDistance?: number;\r\n\t\t\tmaxTicks?: number;\r\n\t\t}): TestSimulateResult\r\n\t\t{\r\n\t\t\tconst opponentBot = resolveBuiltinBot(opponent);\r\n\t\t\tconst raw = simulate(wrappedBot, opponentBot, {\r\n\t\t\t\tseed: options?.seed,\r\n\t\t\t\tspawnDistance: options?.spawnDistance ?? FIGHT_SPAWN_DISTANCES[2], // 600 (middle distance)\r\n\t\t\t\tmaxTicks: options?.maxTicks,\r\n\t\t\t});\r\n\r\n\t\t\treturn {\r\n\t\t\t\traw,\r\n\t\t\t\twinner: raw.winner,\r\n\t\t\t\twon: raw.winner === 'wizard-1',\r\n\t\t\t\tlost: raw.winner === 'wizard-2',\r\n\t\t\t\tdrawn: raw.winner === null || raw.winner === 'draw',\r\n\t\t\t\tticks: raw.ticks,\r\n\t\t\t\tmyHealth: raw.finalState.health,\r\n\t\t\t\tenemyHealth: raw.finalState.enemies?.[0]?.health ?? 0,\r\n\t\t\t\tstats: extractStats(raw),\r\n\t\t\t\terrors: raw.errors,\r\n\t\t\t};\r\n\t\t},\r\n\t};\r\n}\r\n","/**\r\n * VIBEMANCER - FIGHT TRACE\r\n *\r\n * Extracts a structured event log from a SimulateResult history.\r\n * Both bots' actions are tracked: state changes, missile launches with\r\n * full config, hits, damage, dodge proximity, movement patterns.\r\n *\r\n * Used by:\r\n * - vibemancer trace (CLI debug command)\r\n * - scripts/fight-trace.ts (internal diagnostic)\r\n * - User tests that want event-level analysis\r\n */\r\n\r\nimport type {GameState, ProjectileState} from './types.js';\r\nimport type {SimulateResult, BotError} from './engine/simulation.js';\r\nimport {distanceTo} from './utils/distance.js';\r\n\r\n// ─── Types ───────────────────────────────────────────────────────────────────\r\n\r\nexport interface TraceEvent\r\n{\r\n\ttick: number;\r\n\t/** 'W1' = wizard-1, 'W2' = wizard-2 */\r\n\tactor: 'W1' | 'W2';\r\n\ttype: TraceEventType;\r\n\tdetail: string;\r\n}\r\n\r\nexport type TraceEventType =\r\n\t| 'STATE' // State transition (idle→casting, etc.)\r\n\t| 'FIRE' // Missile launched (includes config + range)\r\n\t| 'HIT' // Damage dealt to opponent\r\n\t| 'HURT' // Damage taken from opponent\r\n\t| 'DODGE_START' // Enemy missile enters 150u proximity\r\n\t| 'DODGE_CLOSE' // Enemy missile enters 50u proximity\r\n\t| 'MOVE' // Movement summary (every 100 ticks)\r\n\t| 'ERROR' // Bot or missile AI threw a runtime error\r\n\t| 'WARNING'; // Common mistake detected (move/blink confusion, etc.)\r\n\r\nexport interface TraceSummary\r\n{\r\n\twinner: string;\r\n\tticks: number;\r\n\tw1Name: string;\r\n\tw2Name: string;\r\n\tw1FinalHp: number;\r\n\tw2FinalHp: number;\r\n\tw1: TraceBotSummary;\r\n\tw2: TraceBotSummary;\r\n}\r\n\r\nexport interface TraceBotSummary\r\n{\r\n\tmissilesLaunched: number;\r\n\thits: number;\r\n\tdamageDealt: number;\r\n\tdamageReceived: number;\r\n\tshields: number;\r\n\tblinks: number;\r\n\tdodgeEncounters: number;\r\n\tmovement: {strafe: number; approach: number; retreat: number; still: number};\r\n}\r\n\r\n// ─── Event Extraction ────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Extract a structured event log from simulation history.\r\n * Tracks both bots' state changes, missile launches (with full config + range),\r\n * hits, damage, dodge proximity, movement patterns, and runtime errors.\r\n */\r\nexport function extractTraceEvents(history: GameState[], errors?: BotError[]): TraceEvent[]\r\n{\r\n\tconst events: TraceEvent[] = [];\r\n\r\n\tlet prevW1State = '';\r\n\tlet prevW2State = '';\r\n\tlet prevW1Health = history[0]?.health ?? 60;\r\n\tlet prevW2Health = history[0]?.enemies[0]?.health ?? 60;\r\n\tlet prevW1Spell = '';\r\n\tlet prevW2Spell = '';\r\n\tconst prevProjectileIds = new Set<string>();\r\n\r\n\tfor (let i = 0; i < history.length; i++)\r\n\t{\r\n\t\tconst state = history[i]!;\r\n\t\tconst tick = state.tick;\r\n\t\tconst enemy = state.enemies[0]!;\r\n\t\tconst distance = distanceTo(state.position, enemy.position);\r\n\r\n\t\t// --- W1 state transitions ---\r\n\t\tconst w1State = state.state;\r\n\t\tconst w1Spell = state.castingSpell ?? state.channelingSpell ?? '';\r\n\t\tif (w1State !== prevW1State || w1Spell !== prevW1Spell)\r\n\t\t{\r\n\t\t\tlet detail = `${prevW1State || '?'}→${w1State}`;\r\n\t\t\tif (w1Spell) detail += ` (${w1Spell})`;\r\n\t\t\tif (w1State === 'casting' && state.castDuration) detail += ` [${state.castDuration}t]`;\r\n\t\t\tdetail += ` dist=${distance.toFixed(0)}`;\r\n\t\t\tevents.push({tick, actor: 'W1', type: 'STATE', detail});\r\n\t\t}\r\n\r\n\t\t// --- W2 state transitions ---\r\n\t\tconst w2State = enemy.state;\r\n\t\tconst w2Spell = enemy.castingSpell ?? enemy.channelingSpell ?? '';\r\n\t\tif (w2State !== prevW2State || w2Spell !== prevW2Spell)\r\n\t\t{\r\n\t\t\tlet detail = `${prevW2State || '?'}→${w2State}`;\r\n\t\t\tif (w2Spell) detail += ` (${w2Spell})`;\r\n\t\t\tif (w2State === 'casting' && enemy.castDuration) detail += ` [${enemy.castDuration}t]`;\r\n\t\t\tdetail += ` dist=${distance.toFixed(0)}`;\r\n\t\t\tevents.push({tick, actor: 'W2', type: 'STATE', detail});\r\n\t\t}\r\n\r\n\t\t// --- Damage events ---\r\n\t\tif (enemy.health < prevW2Health)\r\n\t\t{\r\n\t\t\tconst dmg = prevW2Health - enemy.health;\r\n\t\t\tevents.push({\r\n\t\t\t\ttick, actor: 'W1', type: 'HIT',\r\n\t\t\t\tdetail: `dealt ${dmg.toFixed(1)} → enemy HP=${enemy.health.toFixed(1)} dist=${distance.toFixed(0)}`,\r\n\t\t\t});\r\n\t\t}\r\n\t\tif (state.health < prevW1Health)\r\n\t\t{\r\n\t\t\tconst dmg = prevW1Health - state.health;\r\n\t\t\tconst stateTag = state.state === 'channeling' ? ' (SHIELDED)'\r\n\t\t\t\t: state.state === 'casting' ? ` (CASTING ${state.castingSpell})`\r\n\t\t\t\t\t: state.state === 'gcd_locked' ? ' (GCD)'\r\n\t\t\t\t\t\t: ` (${state.state})`;\r\n\t\t\tevents.push({\r\n\t\t\t\ttick, actor: 'W1', type: 'HURT',\r\n\t\t\t\tdetail: `took ${dmg.toFixed(1)} → HP=${state.health.toFixed(1)}${stateTag} dist=${distance.toFixed(0)}`,\r\n\t\t\t});\r\n\t\t}\r\n\t\t// W2 damage taken = W1 damage dealt (already covered above as HIT)\r\n\t\t// W2 damage dealt = W1 damage taken (already covered above as HURT)\r\n\r\n\t\t// --- Missile launches (with full config) ---\r\n\t\tconst currentIds = new Set<string>();\r\n\t\tfor (const proj of state.projectiles)\r\n\t\t{\r\n\t\t\tcurrentIds.add(proj.id);\r\n\t\t\tif (!prevProjectileIds.has(proj.id))\r\n\t\t\t{\r\n\t\t\t\tconst owner: 'W1' | 'W2' = proj.ownerId === 'wizard-1' ? 'W1' : 'W2';\r\n\t\t\t\tconst range = (proj.speed * proj.remainingTicks).toFixed(0);\r\n\t\t\t\tevents.push({\r\n\t\t\t\t\ttick, actor: owner, type: 'FIRE',\r\n\t\t\t\t\tdetail: `dmg=${proj.damage} spd=${proj.speed} turn=${proj.turnRate} life=${proj.remainingTicks} range=${range} dist=${distance.toFixed(0)}`,\r\n\t\t\t\t});\r\n\t\t\t\t// Warn if missile range < distance (will expire before reaching target)\r\n\t\t\t\tif (proj.speed * proj.remainingTicks < distance * 0.8 && proj.turnRate >= 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tevents.push({\r\n\t\t\t\t\t\ttick, actor: owner, type: 'WARNING',\r\n\t\t\t\t\t\tdetail: `missile range (${range}) may be too short for distance (${distance.toFixed(0)}) — consider increasing duration`,\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tprevProjectileIds.clear();\r\n\t\tfor (const id of currentIds) prevProjectileIds.add(id);\r\n\r\n\t\t// --- Dodge proximity events (enemy missiles approaching W1) ---\r\n\t\tif (i > 0)\r\n\t\t{\r\n\t\t\tconst speed = Math.sqrt(state.velocity.x ** 2 + state.velocity.y ** 2);\r\n\t\t\tfor (const proj of state.projectiles)\r\n\t\t\t{\r\n\t\t\t\tif (proj.ownerId === 'wizard-1') continue;\r\n\t\t\t\tconst missileDistance = distanceTo(state.position, proj.position);\r\n\t\t\t\tconst prevProj = history[i - 1]!.projectiles.find((p) => p.id === proj.id);\r\n\t\t\t\tconst prevMissileDist = prevProj ? distanceTo(history[i - 1]!.position, prevProj.position) : Infinity;\r\n\r\n\t\t\t\tif (missileDistance <= 150 && prevMissileDist > 150)\r\n\t\t\t\t{\r\n\t\t\t\t\tevents.push({\r\n\t\t\t\t\t\ttick, actor: 'W1', type: 'DODGE_START',\r\n\t\t\t\t\t\tdetail: formatDodgeDetail(state, proj, missileDistance, speed),\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t\tif (missileDistance <= 50 && prevMissileDist > 50)\r\n\t\t\t\t{\r\n\t\t\t\t\tevents.push({\r\n\t\t\t\t\t\ttick, actor: 'W1', type: 'DODGE_CLOSE',\r\n\t\t\t\t\t\tdetail: formatDodgeDetail(state, proj, missileDistance, speed),\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// --- Movement summary every 100 ticks ---\r\n\t\tif (tick > 0 && tick % 100 === 0)\r\n\t\t{\r\n\t\t\tconst speed = Math.sqrt(state.velocity.x ** 2 + state.velocity.y ** 2);\r\n\t\t\tconst moveType = classifyMovement(state, enemy, speed);\r\n\t\t\tconst incomingMissiles = state.projectiles.filter((p) => p.ownerId !== 'wizard-1').length;\r\n\t\t\tevents.push({\r\n\t\t\t\ttick, actor: 'W1', type: 'MOVE',\r\n\t\t\t\tdetail: `${moveType} spd=${speed.toFixed(2)} dist=${distance.toFixed(0)} missiles=${incomingMissiles} ${state.state} hp=${state.health.toFixed(1)}`,\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tprevW1State = w1State;\r\n\t\tprevW2State = w2State;\r\n\t\tprevW1Health = state.health;\r\n\t\tprevW2Health = enemy.health;\r\n\t\tprevW1Spell = w1Spell;\r\n\t\tprevW2Spell = w2Spell;\r\n\t}\r\n\r\n\t// --- Inject runtime errors as ERROR events ---\r\n\tif (errors && errors.length > 0)\r\n\t{\r\n\t\tfor (const err of errors)\r\n\t\t{\r\n\t\t\tconst actor: 'W1' | 'W2' = err.entityId === 'wizard-1' ? 'W1'\r\n\t\t\t\t: err.entityId === 'wizard-2' ? 'W2'\r\n\t\t\t\t\t: err.entityId.startsWith('missile') ? (/* missile errors attributed to owner */ 'W1') : 'W1';\r\n\t\t\tevents.push({\r\n\t\t\t\ttick: err.tick,\r\n\t\t\t\tactor,\r\n\t\t\t\ttype: 'ERROR',\r\n\t\t\t\tdetail: err.message,\r\n\t\t\t});\r\n\t\t}\r\n\t\t// Re-sort by tick so errors appear in chronological order\r\n\t\tevents.sort((a, b) => a.tick - b.tick || (a.type === 'ERROR' ? 1 : 0));\r\n\t}\r\n\r\n\treturn events;\r\n}\r\n\r\n// ─── Summary ─────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Generate a summary from trace events and the simulation result.\r\n */\r\nexport function summarizeTrace(\r\n\tevents: TraceEvent[],\r\n\tresult: SimulateResult,\r\n\tw1Name: string,\r\n\tw2Name: string,\r\n): TraceSummary\r\n{\r\n\tconst final = result.history[result.history.length - 1];\r\n\r\n\tfunction botSummary(actor: 'W1' | 'W2'): TraceBotSummary\r\n\t{\r\n\t\tconst myEvents = events.filter((e) => e.actor === actor);\r\n\t\tconst moveEvents = myEvents.filter((e) => e.type === 'MOVE');\r\n\t\treturn {\r\n\t\t\tmissilesLaunched: myEvents.filter((e) => e.type === 'FIRE').length,\r\n\t\t\thits: myEvents.filter((e) => e.type === 'HIT').length,\r\n\t\t\tdamageDealt: myEvents.filter((e) => e.type === 'HIT').reduce((sum, e) =>\r\n\t\t\t{\r\n\t\t\t\tconst m = e.detail.match(/dealt ([\\d.]+)/);\r\n\t\t\t\treturn sum + (m ? parseFloat(m[1]!) : 0);\r\n\t\t\t}, 0),\r\n\t\t\tdamageReceived: myEvents.filter((e) => e.type === 'HURT').reduce((sum, e) =>\r\n\t\t\t{\r\n\t\t\t\tconst m = e.detail.match(/took ([\\d.]+)/);\r\n\t\t\t\treturn sum + (m ? parseFloat(m[1]!) : 0);\r\n\t\t\t}, 0),\r\n\t\t\tshields: myEvents.filter((e) => e.type === 'STATE' && e.detail.includes('shield')).length,\r\n\t\t\tblinks: myEvents.filter((e) => e.type === 'STATE' && e.detail.includes('blink')).length,\r\n\t\t\tdodgeEncounters: myEvents.filter((e) => e.type.startsWith('DODGE')).length,\r\n\t\t\tmovement: {\r\n\t\t\t\tstrafe: moveEvents.filter((e) => e.detail.startsWith('STRAFE')).length,\r\n\t\t\t\tapproach: moveEvents.filter((e) => e.detail.startsWith('APPROACH')).length,\r\n\t\t\t\tretreat: moveEvents.filter((e) => e.detail.startsWith('RETREAT')).length,\r\n\t\t\t\tstill: moveEvents.filter((e) => e.detail.startsWith('STILL')).length,\r\n\t\t\t},\r\n\t\t};\r\n\t}\r\n\r\n\tconst winnerName = result.winner === 'wizard-1' ? w1Name\r\n\t\t: result.winner === 'wizard-2' ? w2Name\r\n\t\t\t: result.winner === 'draw' ? 'draw'\r\n\t\t\t\t: 'timeout';\r\n\r\n\treturn {\r\n\t\twinner: winnerName,\r\n\t\tticks: result.ticks,\r\n\t\tw1Name,\r\n\t\tw2Name,\r\n\t\tw1FinalHp: final?.health ?? 0,\r\n\t\tw2FinalHp: final?.enemies[0]?.health ?? 0,\r\n\t\tw1: botSummary('W1'),\r\n\t\tw2: botSummary('W2'),\r\n\t};\r\n}\r\n\r\n// ─── Formatting ──────────────────────────────────────────────────────────────\r\n\r\n/** Format trace events as a human-readable string. */\r\nexport function formatTraceEvents(events: TraceEvent[]): string\r\n{\r\n\treturn events.map((e) =>\r\n\t{\r\n\t\tconst t = String(e.tick).padStart(5);\r\n\t\tconst a = e.actor.padEnd(2);\r\n\t\tconst ty = e.type.padEnd(12);\r\n\t\treturn ` T${t} ${a} ${ty} ${e.detail}`;\r\n\t}).join('\\n');\r\n}\r\n\r\n/** Format a full trace summary as a human-readable string. */\r\nexport function formatTraceSummary(summary: TraceSummary): string\r\n{\r\n\tconst {w1Name, w2Name, w1, w2} = summary;\r\n\tconst lines = [\r\n\t\t` Winner: ${summary.winner} @ T${summary.ticks}`,\r\n\t\t` Final HP: ${w1Name}=${summary.w1FinalHp.toFixed(1)}, ${w2Name}=${summary.w2FinalHp.toFixed(1)}`,\r\n\t\t'',\r\n\t\t` ${w1Name}: ${w1.missilesLaunched} missiles, ${w1.hits} hits (${w1.missilesLaunched > 0 ? ((w1.hits / w1.missilesLaunched) * 100).toFixed(0) : 'N/A'}%), ${w1.damageDealt.toFixed(1)} dmg dealt, ${w1.damageReceived.toFixed(1)} dmg taken, ${w1.shields} shields, ${w1.blinks} blinks`,\r\n\t\t` ${w2Name}: ${w2.missilesLaunched} missiles, ${w2.hits} hits (${w2.missilesLaunched > 0 ? ((w2.hits / w2.missilesLaunched) * 100).toFixed(0) : 'N/A'}%), ${w2.damageDealt.toFixed(1)} dmg dealt, ${w2.damageReceived.toFixed(1)} dmg taken, ${w2.shields} shields, ${w2.blinks} blinks`,\r\n\t\t'',\r\n\t\t` ${w1Name} movement: ${w1.movement.approach} approach, ${w1.movement.strafe} strafe, ${w1.movement.retreat} retreat, ${w1.movement.still} still | ${w1.dodgeEncounters} dodge encounters`,\r\n\t];\r\n\treturn lines.join('\\n');\r\n}\r\n\r\n/**\r\n * Generate diagnostic tips based on trace analysis.\r\n * Identifies common problems and suggests fixes.\r\n * Returns an array of human-readable tips (empty if no issues found).\r\n */\r\nexport function diagnoseTrace(events: TraceEvent[], summary: TraceSummary): string[]\r\n{\r\n\tconst tips: string[] = [];\r\n\tconst {w1} = summary;\r\n\r\n\t// Check for runtime errors\r\n\tconst errors = events.filter((e) => e.actor === 'W1' && e.type === 'ERROR');\r\n\tif (errors.length > 0)\r\n\t{\r\n\t\tconst firstErr = errors[0]!;\r\n\t\ttips.push(`BOT ERROR: Your bot threw \"${firstErr.detail}\" on tick ${firstErr.tick}. The bot does nothing when it throws. Fix this first.`);\r\n\t\tif (errors.length > 1) tips.push(` (${errors.length} total errors — likely the same bug repeating every tick)`);\r\n\t}\r\n\r\n\t// Check for range warnings\r\n\tconst rangeWarnings = events.filter((e) => e.actor === 'W1' && e.type === 'WARNING' && e.detail.includes('range'));\r\n\tif (rangeWarnings.length > 0)\r\n\t{\r\n\t\ttips.push(`RANGE: ${rangeWarnings.length} of your missiles had range shorter than target distance. Increase missile duration or get closer before firing.`);\r\n\t}\r\n\r\n\t// No missiles fired\r\n\tif (w1.missilesLaunched === 0 && errors.length === 0)\r\n\t{\r\n\t\ttips.push('NO OFFENSE: Your bot fired 0 missiles. Check that you return missile() when useTicksUntilReady() === 0.');\r\n\t}\r\n\r\n\t// Very low hit rate\r\n\tif (w1.missilesLaunched >= 3 && w1.hits === 0)\r\n\t{\r\n\t\ttips.push(`ACCURACY: Fired ${w1.missilesLaunched} missiles but 0 hit. Consider: more homing (higher turnRate), or use getLeadPosition() for straight missiles, or get closer.`);\r\n\t}\r\n\telse if (w1.missilesLaunched >= 5 && w1.hits / w1.missilesLaunched < 0.25)\r\n\t{\r\n\t\ttips.push(`LOW ACCURACY: Only ${((w1.hits / w1.missilesLaunched) * 100).toFixed(0)}% hit rate. Try increasing turnRate for more homing, or use getLeadPosition() for prediction.`);\r\n\t}\r\n\r\n\t// No shields against incoming damage\r\n\tif (w1.damageReceived > 20 && w1.shields === 0)\r\n\t{\r\n\t\ttips.push(`NO DEFENSE: Took ${w1.damageReceived.toFixed(0)} damage with 0 shields. Use useThreats() to detect incoming missiles and shield() when you can't dodge.`);\r\n\t}\r\n\r\n\t// Too much idle time\r\n\tif (summary.ticks > 500)\r\n\t{\r\n\t\tconst idleMovements = w1.movement.still;\r\n\t\tconst totalMovements = idleMovements + w1.movement.approach + w1.movement.strafe + w1.movement.retreat;\r\n\t\tif (totalMovements > 0 && idleMovements / totalMovements > 0.5)\r\n\t\t{\r\n\t\t\ttips.push(`IDLE: Your bot was stationary ${((idleMovements / totalMovements) * 100).toFixed(0)}% of the time. Move to dodge missiles and close distance.`);\r\n\t\t}\r\n\t}\r\n\r\n\treturn tips;\r\n}\r\n\r\n/** Format diagnostic tips as a human-readable string. */\r\nexport function formatDiagnosis(tips: string[]): string\r\n{\r\n\tif (tips.length === 0) return '';\r\n\tconst lines = [' Diagnosis:'];\r\n\tfor (const tip of tips)\r\n\t{\r\n\t\tlines.push(` - ${tip}`);\r\n\t}\r\n\treturn lines.join('\\n');\r\n}\r\n\r\n// ─── Helpers ─────────────────────────────────────────────────────────────────\r\n\r\nfunction formatDodgeDetail(state: GameState, proj: ProjectileState, missileDistance: number, speed: number): string\r\n{\r\n\tconst angle = dodgeAngle(state.velocity, proj.rotation);\r\n\treturn `missile at ${missileDistance.toFixed(0)}u rot=${proj.rotation.toFixed(0)}° dodge_angle=${angle.toFixed(0)}° ` +\r\n\t\t`spd=${speed.toFixed(2)} ${state.state}`;\r\n}\r\n\r\nfunction dodgeAngle(wizardVelocity: {x: number; y: number}, missileRotation: number): number\r\n{\r\n\tconst speed = Math.sqrt(wizardVelocity.x ** 2 + wizardVelocity.y ** 2);\r\n\tif (speed < 0.01) return 0;\r\n\tconst missileRad = missileRotation * (Math.PI / 180);\r\n\tconst dot = Math.cos(missileRad) * (wizardVelocity.x / speed) + Math.sin(missileRad) * (wizardVelocity.y / speed);\r\n\treturn Math.acos(Math.max(-1, Math.min(1, Math.abs(dot)))) * (180 / Math.PI);\r\n}\r\n\r\nfunction classifyMovement(state: GameState, enemy: GameState['enemies'][0], speed: number): string\r\n{\r\n\tif (speed < 0.1) return 'STILL';\r\n\tconst toEnemyAngle = Math.atan2(enemy.position.y - state.position.y, enemy.position.x - state.position.x);\r\n\tconst moveAngle = Math.atan2(state.velocity.y, state.velocity.x);\r\n\tlet angleDiff = ((moveAngle - toEnemyAngle) * 180 / Math.PI) % 360;\r\n\tif (angleDiff > 180) angleDiff -= 360;\r\n\tif (angleDiff < -180) angleDiff += 360;\r\n\tconst absAngle = Math.abs(angleDiff);\r\n\tif (absAngle < 30) return 'APPROACH';\r\n\tif (absAngle > 150) return 'RETREAT';\r\n\tif (absAngle >= 60 && absAngle <= 120) return 'STRAFE';\r\n\tif (absAngle < 60) return 'APPROACH+STRAFE';\r\n\treturn 'RETREAT+STRAFE';\r\n}\r\n"],"mappings":"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","names":["move","magnitude","dist2","tick","oldPos","move","isOutOfBounds","tick","move","tick","angleDiff","magnitude","move","SHIELD_BUFFER","HomingAI","nextThreatTime","safeBudget","config","WALL_BUFFER","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","aggressiveMove","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","aggressiveMove","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","HomingAI","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","HomingAI","StraightAI","isNearWall","smartStrafe","tick","move","ntt","sBudget","cfg","WALL_BUFFER","SHIELD_BUFFER","MISSILE_CONFIG","HomingAI","isNearWall","smartStrafe","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","HomingAI","isNearWall","smartStrafe","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","HomingAI","StraightAI","isNearWall","smartStrafe","tick","move","nextThreatTime","safeBudget","config","HomingAI","WALL_BUFFER","tick","magnitude","move","HomingAI","WALL_BUFFER","SHIELD_BUFFER","combatMove","tick","magnitude","move","HomingAI","StraightAI","WALL_BUFFER","SHIELD_BUFFER","combatMove","tick","magnitude","move","nextThreatTime","safeBudget","config","WALL_BUFFER","isNearWall","combatMove","tick","magnitude","dodgeDirection","move","WALL_BUFFER","SHIELD_BUFFER","StandardHoming","isNearWall","combatMove","tick","magnitude","dodgeDirection","move","WALL_BUFFER","SHIELD_BUFFER","HomingAI","StraightAI","isNearWall","combatMove","tick","magnitude","move","blinkTarget","nextThreatTime","safeBudget","config","WALL_BUFFER","QUICK_CONFIG","isNearWall","combatMove","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","isNearWall","combatMove","tick","magnitude","dodgeDirection","move","WALL_BUFFER","SHIELD_BUFFER","HomingAI","StraightAI","isNearWall","combatMove","tick","magnitude","dodgeDirection","move","config","WALL_BUFFER","STANDARD_CONFIG","QUICK_CONFIG","HomingAI","isNearWall","smartStrafe","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","HomingAI","isNearWall","smartStrafe","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","HomingAI","StraightAI","isNearWall","smartStrafe","tick","magnitude","move","tick","angleDiff"]}
1
+ {"version":3,"sources":["../src/rules.ts","../src/engine-version.ts","../src/engine/physics.ts","../src/engine/spells.ts","../src/engine/hooks-runtime.ts","../src/utils/angles.ts","../src/utils/random.ts","../src/engine/simulation.ts","../src/engine/manual-match.ts","../src/engine/missile-templates.ts","../src/utils/distance.ts","../src/utils/movement.ts","../src/utils/targeting.ts","../src/utils/spatial.ts","../src/utils/combat.ts","../src/bots/standalone/TargetDummy.ts","../src/bots/standalone/Rookie.ts","../src/bots/standalone/Critter.ts","../src/hooks/threat-analysis.ts","../src/bots/shared.ts","../src/bots/standalone/Hogger.ts","../src/bots/standalone/Doombringer.ts","../src/engine/params-runtime.ts","../src/bots/defensive/01_Turtle.ts","../src/bots/defensive/02_Sentinel.ts","../src/bots/defensive/03_Golem.ts","../src/bots/melee/01_Shadowblade.ts","../src/bots/melee/02_Nightblade.ts","../src/bots/melee/03_Voidblade.ts","../src/bots/homing/01_Bonemancer.ts","../src/bots/homing/02_Lich.ts","../src/bots/homing/03_Archlich.ts","../src/bots/caster/01_Flamecaller.ts","../src/bots/caster/02_Pyromancer.ts","../src/bots/caster/03_Infernalist.ts","../src/bots/sniper/01_Spellshot.ts","../src/bots/sniper/02_Spelltracer.ts","../src/bots/sniper/03_Spellseeker.ts","../src/bots/duelist/01_Battlemage.ts","../src/bots/duelist/02_Warmage.ts","../src/bots/duelist/03_Archmage.ts","../src/bots/berserker/01_Stormchaser.ts","../src/bots/berserker/02_Stormcaller.ts","../src/bots/berserker/03_Stormforger.ts","../src/bots/kiter/01_Spellspinner.ts","../src/bots/kiter/02_Spellweaver.ts","../src/bots/kiter/03_Spellbinder.ts","../src/bots/registry.ts","../src/bots/index.ts","../src/hooks/state-hooks.ts","../src/hooks/action-builders.ts","../src/hooks/bot-wrapper.ts","../src/engine/optimizer.ts","../src/engine/sandbox-browser.ts","../src/stats.ts","../src/testing.ts","../src/trace.ts"],"sourcesContent":["/**\n * VIBEMANCER - GAME RULES\n *\n * This is the single source of truth for all game constants.\n * All game logic imports from here. Read this to understand the game.\n */\n\nimport {MissileConfig} from './types.js';\n\n// === TIMING ===\nexport const TICKS_PER_SECOND = 100;\nexport const TICK_DURATION_MS = 10;\n\n// === WIZARD ===\nexport const WIZARD_HEALTH = 60;\nexport const WIZARD_RADIUS = 5; // units\nexport const MOVEMENT_SPEED = 1; // units per tick (100 units/sec)\nexport const CASTING_MOVEMENT_MULT = 0.5; // 50% speed while casting\n\n// === GCD ===\nexport const GCD_DURATION = 100; // ticks (1 second)\n\n// === SHIELD ===\nexport const SHIELD_CAST_TIME = 20; // ticks (0.2s)\nexport const SHIELD_MAX_BLOCK = 0.9; // 90%\nexport const SHIELD_DECAY_PER_SECOND = 0.2; // loses 20% per second\nexport const SHIELD_MIN_BLOCK = 0.3; // 30% minimum\n// Note: Shield block % is applied directly to damage (no additional scaling)\n\n// === BLINK ===\nexport const BLINK_CAST_TIME = 10; // ticks (0.1s)\nexport const BLINK_RANGE = 300; // units\nexport const BLINK_MAX_COOLDOWN = 2000; // ticks (20 seconds) — full-range blink\nexport const BLINK_MIN_COOLDOWN = 100; // ticks (1 second) — micro-blink floor\n/** @deprecated Use BLINK_MAX_COOLDOWN */\nexport const BLINK_COOLDOWN = BLINK_MAX_COOLDOWN;\n\n// === ARENA ===\nexport const ARENA_WIDTH = 800;\nexport const ARENA_HEIGHT = 800;\nexport const ARENA_SIZE = ARENA_WIDTH; // Arena is square; alias for bot convenience\nexport const SPAWN_DISTANCE = 600;\n\n// === MISSILE CONSTRAINTS ===\nexport const MISSILE_MIN_DAMAGE = 1;\nexport const MISSILE_MIN_SPEED = 1.5; // must be faster than player\nexport const MISSILE_MIN_DURATION = 10; // ticks (0.1 seconds)\nexport const MISSILE_MIN_CAST_TIME = 0.1; // seconds\n\n// === MISSILE HITBOX ===\n// Missile hitbox scales with damage (design doc line 1122)\n// Visual size reference (design doc lines 349-354):\n// - 1-5 damage: Tiny\n// - 6-15 damage: Small-Medium\n// - 16-30 damage: Large\n// - 31+ damage: Huge\nexport const MISSILE_BASE_RADIUS = 2; // base radius in units\nexport const MISSILE_DAMAGE_RADIUS_SCALE = 0.1; // additional radius per damage point\n\n/**\n * Calculate missile hitbox radius based on damage.\n */\nexport function calculateMissileRadius(damage: number): number\n{\n\treturn MISSILE_BASE_RADIUS + damage * MISSILE_DAMAGE_RADIUS_SCALE;\n}\n\n// === MISSILE CAST TIME FORMULA ===\n// cast_time = BASE + SCALE × damage^POWER + delivery costs\n//\n// Power-law scaling: DPS always increases with damage (higher damage = more\n// DPS but longer casts that are easier to shield/dodge). Fast casts (low\n// damage) can't be shielded — that's their advantage over raw DPS.\n//\n// Reference points (stab, no delivery cost):\n// d=10: ~1.15s cast → DPS 4.65, TTK 11.9s (fast, unshieldable)\n// d=20: ~1.77s cast → DPS 7.23, TTK 7.3s (medium, borderline shieldable)\n// d=30: ~2.28s cast → DPS 9.14, TTK 5.6s (slow, shieldable)\n// d=60: ~3.57s cast → DPS 13.1, TTK 3.6s (very slow, easily shielded)\nexport const MISSILE_BASE_CAST = 0.1;\nexport const MISSILE_DAMAGE_SCALE = 0.226;\nexport const MISSILE_DAMAGE_POWER = 2 / 3; // sublinear: DPS always increases with damage\nexport const MISSILE_HOMING_COEFF = 0.12;\nexport const MISSILE_TURN_DURATION_COEFF = 0.10;\nexport const MISSILE_SPEED_DURATION_BASELINE = 1.5;\nexport const MISSILE_SPEED_DURATION_COEFF = 0.025;\n\n/**\n * Maps turnRate to effective cost for the cast time formula.\n * - Positive: linear (unchanged behavior)\n * - Negative: diminishing returns via -|t|/(1+|t|), saturating at -1.\n * turnRate -0.5 → -0.33, -1 → -0.5, -5 → -0.83, -10 → -0.91\n * Big cast speed gains from 0 to -1, worthwhile to -5, negligible after.\n * No hard floor — the curve naturally caps the benefit.\n */\nexport function effectiveTurnRateCost(turnRate: number): number\n{\n\tif (turnRate >= 0) return turnRate;\n\tconst abs = Math.abs(turnRate);\n\treturn -(abs / (1 + abs));\n}\n\n/**\n * Validate and sanitize missile config.\n * Ensures all values meet minimum requirements (Lesson #9).\n */\nexport function validateMissileConfig(config: MissileConfig): MissileConfig\n{\n\treturn {\n\t\tdamage: Math.max(MISSILE_MIN_DAMAGE, Number.isFinite(config.damage) ? config.damage : MISSILE_MIN_DAMAGE),\n\t\tspeed: Math.max(MISSILE_MIN_SPEED, Number.isFinite(config.speed) ? config.speed : MISSILE_MIN_SPEED),\n\t\tturnRate: Number.isFinite(config.turnRate) ? config.turnRate : 0, // no floor — diminishing returns via effectiveTurnRateCost\n\t\tduration: Math.max(MISSILE_MIN_DURATION, Number.isFinite(config.duration) ? Math.floor(config.duration) : MISSILE_MIN_DURATION),\n\t};\n}\n\n/**\n * Calculate missile cast time from config.\n *\n * Base formula:\n * cast_time = 0.1\n * + 0.226 × damage^(2/3)\n * + 0.12 × effectiveTurnRateCost(turnRate)\n * + 0.10 × (effectiveTurnRateCost(turnRate) × durationSeconds)\n * + 0.025 × (speed × durationSeconds - 1.5)\n *\n * effectiveTurnRateCost: linear for positive, -ln(1+|t|) for negative (diminishing returns).\n *\n * If lastMissileConfig is a MissileConfig, applies warmup multiplier:\n * - Similar to previous: up to 20% faster\n * - Very different: up to 20% slower (switching penalty)\n */\nexport function calculateMissileCastTime(config: MissileConfig, lastMissileConfig: MissileConfig | undefined | null = null): number\n{\n\tconst {damage, speed, turnRate, duration} = config;\n\tconst durationSeconds = duration / TICKS_PER_SECOND;\n\tconst turnCost = effectiveTurnRateCost(turnRate);\n\n\tconst raw = MISSILE_BASE_CAST\n\t\t+ MISSILE_DAMAGE_SCALE * Math.pow(damage, MISSILE_DAMAGE_POWER)\n\t\t+ MISSILE_HOMING_COEFF * turnCost\n\t\t+ MISSILE_TURN_DURATION_COEFF * (turnCost * durationSeconds)\n\t\t+ MISSILE_SPEED_DURATION_COEFF * (speed * durationSeconds - MISSILE_SPEED_DURATION_BASELINE);\n\n\tconst baseCast = Math.max(MISSILE_MIN_CAST_TIME, raw);\n\n\t// Warmup is applied when lastMissileConfig is an actual MissileConfig.\n\t// Note: passing undefined triggers the default (null) due to JS semantics,\n\t// so in practice only an explicit MissileConfig activates warmup.\n\t// - null/undefined/omitted: return base cast time (no warmup)\n\t// - MissileConfig: apply warmup multiplier based on similarity to previous\n\tif (lastMissileConfig !== null)\n\t{\n\t\tconst multiplier = calculateWarmupMultiplier(lastMissileConfig, config);\n\t\treturn Math.max(MISSILE_MIN_CAST_TIME, baseCast * multiplier);\n\t}\n\n\treturn baseCast;\n}\n\n// === CAST WARMUP ===\n// Repeated similar missile casts get faster (\"muscle memory\"), while\n// switching to a very different config incurs a penalty (slower cast).\n// This rewards specialization: melee bots that always stab have faster\n// casts than ranged bots that switch to stabs opportunistically.\n//\n// The warmup multiplier ranges from (1 - MAX_BONUS) to (1 + MAX_PENALTY):\n// Similar to previous: cast × 0.80 (20% faster)\n// First cast (no previous): cast × 0.80 (full warmup, no conflict)\n// Very different from previous: cast × 1.20 (20% slower, switching penalty)\nexport const WARMUP_MAX_BONUS = 0.20; // 20% cast time reduction at full warmup\nexport const WARMUP_MAX_PENALTY = 0.20; // 20% cast time increase when switching\n// Tolerances for similarity calculation (differences within tolerance are \"similar\"):\n// Note: damage is intentionally excluded — it doesn't define playstyle.\n// A melee bot stabbing at d=10 and d=25 is the same style, just different power.\nexport const WARMUP_SPEED_TOLERANCE = 3; // speed can vary ±3\nexport const WARMUP_TURN_TOLERANCE = 1; // turnRate can vary ±1\nexport const WARMUP_DURATION_TOLERANCE = 50; // duration (ticks) can vary ±50\n\n/**\n * Calculate how similar two missile configs are (0 to 1).\n * Returns 1 for identical configs, 0 for very different ones.\n * Used by the warmup system to determine cast time multiplier.\n *\n * Only compares speed, turnRate, and duration — these define the missile\n * \"style\" (melee stab vs ranged homing vs fast snipe). Damage is excluded\n * because varying power doesn't change playstyle.\n */\nexport function calculateMissileSimilarity(prev: MissileConfig | undefined, current: MissileConfig): number\n{\n\tif (!prev) return 1; // No previous missile = no conflicting muscle memory = full warmup\n\n\tconst dist = Math.abs(prev.speed - current.speed) / WARMUP_SPEED_TOLERANCE\n\t\t+ Math.abs(prev.turnRate - current.turnRate) / WARMUP_TURN_TOLERANCE\n\t\t+ Math.abs(prev.duration - current.duration) / WARMUP_DURATION_TOLERANCE;\n\n\treturn Math.max(0, 1 - dist / 3);\n}\n\n/**\n * Calculate the cast time multiplier from the warmup system.\n * Returns < 1 for bonus (faster), > 1 for penalty (slower), 1 for neutral.\n *\n * Similar to previous cast → multiplier approaches (1 - MAX_BONUS) = 0.80\n * Very different from previous → multiplier approaches (1 + MAX_PENALTY) = 1.20\n * No previous cast → full bonus (1 - MAX_BONUS) = 0.80\n */\nexport function calculateWarmupMultiplier(prev: MissileConfig | undefined, current: MissileConfig): number\n{\n\tconst similarity = calculateMissileSimilarity(prev, current);\n\t// similarity=1 → bonus (faster): 1 - MAX_BONUS\n\t// similarity=0 → penalty (slower): 1 + MAX_PENALTY\n\t// Linear interpolation between penalty and bonus\n\treturn (1 + WARMUP_MAX_PENALTY) - similarity * (WARMUP_MAX_BONUS + WARMUP_MAX_PENALTY);\n}\n\n// === MATCH ===\nexport const MATCH_DURATION = 30000; // ticks (5 minutes)\n\n// ============================================================\n// NEW API CONSTANT ALIASES\n// These are the preferred names for the new hooks-based API\n// ============================================================\n\n// Health & Combat\nexport const MAX_HEALTH = WIZARD_HEALTH;\nexport const COLLISION_RADIUS = WIZARD_RADIUS;\n\n// Movement\nexport const MOVE_SPEED = MOVEMENT_SPEED;\n\n// Missiles\nexport const MISSILE_RADIUS_PER_DAMAGE = MISSILE_DAMAGE_RADIUS_SCALE;\n\n// Shield (aliases for consistency)\nexport const SHIELD_MAX_STRENGTH = SHIELD_MAX_BLOCK;\nexport const SHIELD_MIN_STRENGTH = SHIELD_MIN_BLOCK;\nexport const SHIELD_DECAY_RATE = SHIELD_DECAY_PER_SECOND;\n\n// Blink (aliases for consistency)\nexport const BLINK_MAX_RANGE = BLINK_RANGE;\n\n/**\n * Calculate blink cooldown based on distance traveled.\n * Short blinks get short cooldowns, full-range blinks get the maximum.\n */\nexport function calculateBlinkCooldown(distance: number): number\n{\n\tconst ratio = Math.min(1, distance / BLINK_MAX_RANGE);\n\treturn Math.max(BLINK_MIN_COOLDOWN, Math.round(ratio * BLINK_MAX_COOLDOWN));\n}\n\n// Arena\nexport const ARENA_WATER_BUFFER = 200; // units - missiles can fly this far past arena bounds\n","/**\n * Engine version — milliseconds since the Unix epoch.\n *\n * Bumped automatically by `scripts/update-engine-version.mjs` on every build.\n * The same value is propagated to `packages/functions/src/engine-version.generated.ts`\n * so cloud functions and the core engine always agree on a single version per deploy.\n *\n * Used to gate spectator replays: a recorded match can only be re-simulated when\n * the runtime engine version matches the version that produced the match.\n */\nexport const ENGINE_VERSION = 1777687052301;\n","import {Position, WizardState, ProjectileState} from '../types.js';\nimport {ARENA_WIDTH, ARENA_HEIGHT, MOVEMENT_SPEED, CASTING_MOVEMENT_MULT, WIZARD_RADIUS} from '../rules.js';\n\n/**\n * Move a wizard based on world-space input direction.\n *\n * Movement model (from design doc):\n * - x: +100 = right, -100 = left\n * - y: +100 = down, -100 = up\n * - Values are clamped to [-100, 100]\n * - Diagonal movement is normalized (magnitude capped at 100)\n * - No rotation tracking - just output (x, y) direction\n */\nexport function moveWizard(wizard: WizardState, move: {x: number; y: number}, deltaTicks: number): Position\n{\n\t// Clamp input values to [-100, 100]\n\tlet dx = Math.max(-100, Math.min(100, move.x || 0));\n\tlet dy = Math.max(-100, Math.min(100, move.y || 0));\n\n\t// Normalize diagonal movement (cap magnitude at 100)\n\tconst magnitude = Math.sqrt(dx * dx + dy * dy);\n\tif (magnitude > 100)\n\t{\n\t\tdx = dx * 100 / magnitude;\n\t\tdy = dy * 100 / magnitude;\n\t}\n\n\t// Convert from [-100, 100] to [-1, 1] direction\n\tdx = dx / 100;\n\tdy = dy / 100;\n\n\tlet speed = MOVEMENT_SPEED;\n\tif (wizard.state === 'casting')\n\t{\n\t\tspeed *= CASTING_MOVEMENT_MULT;\n\t}\n\telse if (wizard.state === 'channeling')\n\t{\n\t\t// Movement keys ignored during channeling (design doc line 197, 1120)\n\t\tspeed = 0;\n\t}\n\n\tconst newPos = {\n\t\tx: wizard.position.x + dx * speed * deltaTicks,\n\t\ty: wizard.position.y + dy * speed * deltaTicks,\n\t};\n\n\treturn clampToArena(newPos, WIZARD_RADIUS);\n}\n\n/**\n * Move a projectile in its current rotation direction.\n */\nexport function moveProjectile(projectile: ProjectileState, deltaTicks: number): Position\n{\n\tconst radians = projectile.rotation * (Math.PI / 180);\n\treturn {\n\t\tx: projectile.position.x + Math.cos(radians) * projectile.speed * deltaTicks,\n\t\ty: projectile.position.y + Math.sin(radians) * projectile.speed * deltaTicks,\n\t};\n}\n\n/**\n * Clamp a position to the arena boundaries.\n */\nexport function clampToArena(position: Position, radius: number): Position\n{\n\treturn {\n\t\tx: Math.max(radius, Math.min(ARENA_WIDTH - radius, position.x)),\n\t\ty: Math.max(radius, Math.min(ARENA_HEIGHT - radius, position.y)),\n\t};\n}\n\n/**\n * Resolve body collision between two wizards.\n * Pushes both apart equally so they don't overlap. Neither is blocked — they just can't stack.\n * Iterates until stable: wizard push → wall clamp → re-check overlap → repeat.\n */\nexport function resolveWizardCollision(wizard1: WizardState, wizard2: WizardState): void\n{\n\tconst minDist = WIZARD_RADIUS * 2;\n\tconst maxIterations = 5; // Prevents infinite loops in edge cases\n\n\tfor (let i = 0; i < maxIterations; i++)\n\t{\n\t\tconst dx = wizard2.position.x - wizard1.position.x;\n\t\tconst dy = wizard2.position.y - wizard1.position.y;\n\t\tconst dist = Math.sqrt(dx * dx + dy * dy);\n\n\t\tif (dist >= minDist) return; // No overlap — done\n\n\t\tif (dist === 0)\n\t\t{\n\t\t\t// Exactly overlapping — push apart along arbitrary axis\n\t\t\twizard1.position = {x: wizard1.position.x - WIZARD_RADIUS, y: wizard1.position.y};\n\t\t\twizard2.position = {x: wizard2.position.x + WIZARD_RADIUS, y: wizard2.position.y};\n\t\t}\n\t\telse\n\t\t{\n\t\t\tconst overlap = minDist - dist;\n\t\t\tconst pushX = (dx / dist) * (overlap / 2);\n\t\t\tconst pushY = (dy / dist) * (overlap / 2);\n\n\t\t\twizard1.position = {x: wizard1.position.x - pushX, y: wizard1.position.y - pushY};\n\t\t\twizard2.position = {x: wizard2.position.x + pushX, y: wizard2.position.y + pushY};\n\t\t}\n\n\t\t// Clamp both to arena — this may re-introduce overlap if one is pushed into a wall\n\t\twizard1.position = clampToArena(wizard1.position, WIZARD_RADIUS);\n\t\twizard2.position = clampToArena(wizard2.position, WIZARD_RADIUS);\n\t}\n}\n\n/**\n * Perform swept circle collision detection between a moving point (projectile) and a stationary circle (wizard).\n * Returns true if a collision occurred during the movement from oldPos to newPos.\n */\nexport function sweptCircleCollision(oldPos: Position, newPos: Position, radius: number, targetPos: Position, targetRadius: number): boolean\n{\n\t// Distance from point to line segment\n\tconst totalRadius = radius + targetRadius;\n\n\t// Vector from oldPos to newPos\n\tconst dx = newPos.x - oldPos.x;\n\tconst dy = newPos.y - oldPos.y;\n\tconst d2 = dx * dx + dy * dy;\n\n\tif (d2 === 0)\n\t{\n\t\tconst dist2 = (oldPos.x - targetPos.x) ** 2 + (oldPos.y - targetPos.y) ** 2;\n\t\treturn dist2 <= totalRadius * totalRadius;\n\t}\n\n\t// Projection of targetPos onto the line segment (oldPos, newPos)\n\tlet t = ((targetPos.x - oldPos.x) * dx + (targetPos.y - oldPos.y) * dy) / d2;\n\tt = Math.max(0, Math.min(1, t));\n\n\tconst closestX = oldPos.x + t * dx;\n\tconst closestY = oldPos.y + t * dy;\n\n\tconst dist2 = (targetPos.x - closestX) ** 2 + (targetPos.y - closestY) ** 2;\n\treturn dist2 <= totalRadius * totalRadius;\n}\n","import {WizardState, MissileConfig, ProjectileState} from '../types.js';\nimport {\n\tSHIELD_MAX_BLOCK,\n\tSHIELD_DECAY_PER_SECOND,\n\tSHIELD_MIN_BLOCK,\n\tTICKS_PER_SECOND,\n\tGCD_DURATION,\n\tSHIELD_CAST_TIME,\n\tBLINK_CAST_TIME,\n\tcalculateMissileCastTime,\n\tcalculateWarmupMultiplier,\n} from '../rules.js';\n\n/**\n * Start casting a spell.\n * For missiles, applies warmup system (bonus for similar, penalty for switching).\n */\nexport function startCast(wizard: WizardState, spell: 'missile' | 'shield' | 'blink', config?: MissileConfig): void\n{\n\twizard.state = 'casting';\n\twizard.castingSpell = spell;\n\twizard.castProgress = 0;\n\n\tif (spell === 'missile' && config)\n\t{\n\t\t// calculateMissileCastTime includes warmup when lastMissileConfig is passed\n\t\tconst castTimeSec = calculateMissileCastTime(config, wizard.lastMissileConfig);\n\t\twizard.castDuration = Math.max(1, Math.round(castTimeSec * TICKS_PER_SECOND));\n\t\t// Store warmup multiplier for visualization\n\t\twizard.warmupMultiplier = calculateWarmupMultiplier(wizard.lastMissileConfig, config);\n\t}\n\telse if (spell === 'shield')\n\t{\n\t\twizard.castDuration = SHIELD_CAST_TIME;\n\t}\n\telse if (spell === 'blink')\n\t{\n\t\twizard.castDuration = BLINK_CAST_TIME;\n\t}\n}\n\n/**\n * Cancel the current cast.\n */\nexport function cancelCast(wizard: WizardState): void\n{\n\twizard.state = 'idle';\n\tdelete wizard.castingSpell;\n\tdelete wizard.castProgress;\n\tdelete wizard.castDuration;\n}\n\n/**\n * Complete the current cast and trigger the spell effect.\n */\nexport function completeCast(wizard: WizardState): void\n{\n\tconst spell = wizard.castingSpell;\n\n\tif (spell === 'shield')\n\t{\n\t\twizard.state = 'channeling';\n\t\twizard.channelingSpell = 'shield';\n\t\twizard.channelDuration = 0;\n\t}\n\telse if (spell === 'missile')\n\t{\n\t\twizard.state = 'gcd_locked';\n\t\twizard.gcdRemaining = GCD_DURATION;\n\t}\n\telse\n\t{\n\t\t// Blink: no GCD — can act immediately after teleporting\n\t\twizard.state = 'idle';\n\t}\n\n\tdelete wizard.castingSpell;\n\tdelete wizard.castProgress;\n\tdelete wizard.castDuration;\n}\n\n/**\n * Update the shield channel state.\n */\nexport function updateShield(wizard: WizardState, deltaTicks: number): void\n{\n\tif (wizard.state === 'channeling' && wizard.channelingSpell === 'shield')\n\t{\n\t\twizard.channelDuration = (wizard.channelDuration ?? 0) + deltaTicks;\n\t}\n}\n\n/**\n * Calculate the current block percentage of a shield based on channel duration.\n */\nexport function calculateShieldBlock(channelDurationTicks: number): number\n{\n\tconst channelSeconds = channelDurationTicks / TICKS_PER_SECOND;\n\tconst decay = channelSeconds * SHIELD_DECAY_PER_SECOND;\n\treturn Math.max(SHIELD_MIN_BLOCK, SHIELD_MAX_BLOCK - decay);\n}\n\n/**\n * Apply damage to a wizard, considering shield mitigation.\n *\n * Shield blocks a percentage of damage based on channel duration:\n * - Fresh shield (0s): 90% block → 10% damage through\n * - Decayed shield: block % decreases over time (20%/sec)\n * - Minimum: 30% block → 70% damage through\n *\n * (Design doc lines 196-212)\n */\nexport function applyDamage(wizard: WizardState, damage: number, _projectile?: ProjectileState): number\n{\n\tif (wizard.invincible) return 0;\n\n\tlet finalDamage = damage;\n\n\tif (wizard.state === 'channeling' && wizard.channelingSpell === 'shield')\n\t{\n\t\tconst blockPercent = calculateShieldBlock(wizard.channelDuration ?? 0);\n\t\t// Simple block: damage through = damage * (1 - blockPercent)\n\t\tfinalDamage = damage * (1 - blockPercent);\n\t}\n\n\twizard.health = Math.max(0, wizard.health - finalDamage);\n\treturn finalDamage;\n}\n","/**\n * VIBEMANCER - HOOKS RUNTIME\n *\n * This file implements a minimal React-like hooks runtime for AI programming.\n * It supports useState, useEffect, useMemo, useRef, and useParam with entity isolation.\n *\n * RULES OF HOOKS (same as React):\n * - Hooks must be called at the top level of the bot function\n * - Hooks must be called in the same order every tick\n * - Hooks must NOT be called conditionally\n *\n * Violating these rules throws an error (detected via hook index validation).\n */\n\nimport type {BotContext} from '../hooks/types.js';\n\nexport interface HookState\n{\n\tvalues: unknown[];\n\teffects: {\n\t\tcallback: () => void | (() => void);\n\t\tdeps?: unknown[];\n\t\tcleanup?: () => void;\n\t}[];\n\tmemos: {\n\t\tvalue: unknown;\n\t\tdeps?: unknown[];\n\t}[];\n\t/** Type of each hook call in order (for validation). */\n\thookTypes: string[];\n\t/** Total hooks called on first successful tick. */\n\thookCount: number;\n\t/** Whether the first tick has completed successfully (hook pattern established). */\n\tinitialized: boolean;\n}\n\n// Use globalThis for hooks state so that multiple copies of the hooks runtime\n// (e.g., engine template + bot bundle in sandboxed execution) share the same state.\n// Without this, bot bundles that inline their own copy of the hooks runtime would\n// have separate currentEntityId etc., causing \"useParam can only be used inside\n// a bot function\" errors.\nconst _g = globalThis as Record<string, unknown>;\n// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- we own __vibemancer_entityHooks\nconst entityHooks: Map<string, HookState> = (_g.__vibemancer_entityHooks as Map<string, HookState>)\n\t?? (_g.__vibemancer_entityHooks = new Map<string, HookState>());\n\n// Accessor functions for shared mutable state on globalThis\nfunction _getCurrentEntityId(): string | null\n{\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- we own __vibemancer_currentEntityId\n\treturn (_g.__vibemancer_currentEntityId as string | null) ?? null;\n}\nfunction _setCurrentEntityId(id: string | null): void\n{\n\t_g.__vibemancer_currentEntityId = id;\n}\nfunction _getCurrentHookIndex(): number\n{\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- we own __vibemancer_currentHookIndex\n\treturn (_g.__vibemancer_currentHookIndex as number) ?? 0;\n}\nfunction _setCurrentHookIndex(idx: number): void\n{\n\t_g.__vibemancer_currentHookIndex = idx;\n}\nfunction _getCurrentBotContext(): BotContext | null\n{\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- we own __vibemancer_currentBotContext\n\treturn (_g.__vibemancer_currentBotContext as BotContext | null) ?? null;\n}\nfunction _setCurrentBotContext(ctx: BotContext | null): void\n{\n\t_g.__vibemancer_currentBotContext = ctx;\n}\n\n/**\n * Get or create the hook state for an entity.\n */\nfunction getHookState(entityId: string): HookState\n{\n\tlet state = entityHooks.get(entityId);\n\tif (!state)\n\t{\n\t\tstate = {\n\t\t\tvalues: [],\n\t\t\teffects: [],\n\t\t\tmemos: [],\n\t\t\thookTypes: [],\n\t\t\thookCount: 0,\n\t\t\tinitialized: false,\n\t\t};\n\t\tentityHooks.set(entityId, state);\n\t}\n\treturn state;\n}\n\n/**\n * Validate a hook call and return its sequential index.\n * Ensures hooks are called in the same order every tick.\n *\n * On the first tick: records the hook type at this index.\n * On subsequent ticks: validates the hook type matches.\n *\n * @param type - The hook type name (e.g., 'useState', 'useEffect', 'useParam')\n * @returns The sequential hook index\n * @throws If called outside runWithHooks or if hook order changed\n */\nexport function validateHookCall(type: string): number\n{\n\tif (!_getCurrentEntityId())\n\t{\n\t\tthrow new Error(`${type} can only be used inside a bot function (runWithHooks)`);\n\t}\n\n\tconst state = getHookState(_getCurrentEntityId()!);\n\tconst index = _getCurrentHookIndex();\n\t_setCurrentHookIndex(index + 1);\n\n\tif (state.initialized)\n\t{\n\t\t// Validate: not more hooks than first tick\n\t\tif (index >= state.hookCount)\n\t\t{\n\t\t\tthrow new Error(\n\t\t\t\t`Hook \"${type}\" called at index ${index}, but only ${state.hookCount} hooks were registered on the first tick. ` +\n\t\t\t\t'This likely means a hook was called conditionally. Hooks must be called in the same order every tick.',\n\t\t\t);\n\t\t}\n\t\t// Validate: same hook type at this index\n\t\tif (state.hookTypes[index] !== type)\n\t\t{\n\t\t\tthrow new Error(\n\t\t\t\t`Hook order changed at index ${index}: expected \"${state.hookTypes[index]}\", got \"${type}\". ` +\n\t\t\t\t'Hooks must be called in the same order every tick.',\n\t\t\t);\n\t\t}\n\t}\n\telse\n\t{\n\t\t// First tick: record hook type\n\t\tstate.hookTypes[index] = type;\n\t}\n\n\treturn index;\n}\n\n/**\n * Finalize hook count after a successful tick.\n * On first tick: establishes the hook pattern.\n * On subsequent ticks: validates the count matches.\n */\nfunction finalizeHookCount(entityId: string): void\n{\n\tconst state = entityHooks.get(entityId);\n\tif (!state) return;\n\n\tif (state.initialized)\n\t{\n\t\tif (_getCurrentHookIndex() < state.hookCount)\n\t\t{\n\t\t\tthrow new Error(\n\t\t\t\t`Fewer hooks were called (${_getCurrentHookIndex()}) than on the first tick (${state.hookCount}). ` +\n\t\t\t\t'This likely means a hook was called conditionally. Hooks must be called in the same order every tick.',\n\t\t\t);\n\t\t}\n\t}\n\telse\n\t{\n\t\tstate.hookCount = _getCurrentHookIndex();\n\t\tstate.initialized = true;\n\t}\n}\n\n/**\n * Run a function with a specific entity's hook context.\n */\nexport function runWithHooks<T>(entityId: string, fn: () => T): T\n{\n\tconst previousEntityId = _getCurrentEntityId();\n\tconst previousHookIndex = _getCurrentHookIndex();\n\n\t_setCurrentEntityId(entityId);\n\t_setCurrentHookIndex(0);\n\n\t// If the first tick failed, reset hook type tracking so next attempt starts clean\n\tconst state = entityHooks.get(entityId);\n\tif (state && !state.initialized)\n\t{\n\t\tstate.hookTypes.length = 0;\n\t}\n\n\ttry\n\t{\n\t\tconst result = fn();\n\t\tfinalizeHookCount(entityId);\n\t\treturn result;\n\t}\n\tfinally\n\t{\n\t\t_setCurrentEntityId(previousEntityId);\n\t\t_setCurrentHookIndex(previousHookIndex);\n\t}\n}\n\n/**\n * Run a bot function with full context (game state + persistence hooks).\n * Use this when you need to set BOTH the entity ID and the game context.\n */\nexport function runBotWithContext<T>(entityId: string, context: BotContext, fn: () => T): T\n{\n\tconst previousContext = _getCurrentBotContext();\n\t_setCurrentBotContext(context);\n\n\ttry\n\t{\n\t\treturn runWithHooks(entityId, fn);\n\t}\n\tfinally\n\t{\n\t\t_setCurrentBotContext(previousContext);\n\t}\n}\n\n/**\n * Run a function with game state context only, preserving the current entity ID.\n * Use this inside wrapNewBot where the entity ID is already set by the outer runWithHooks.\n */\nexport function withBotContext<T>(context: BotContext, fn: () => T): T\n{\n\tconst previousContext = _getCurrentBotContext();\n\t_setCurrentBotContext(context);\n\n\ttry\n\t{\n\t\treturn fn();\n\t}\n\tfinally\n\t{\n\t\t_setCurrentBotContext(previousContext);\n\t}\n}\n\n/**\n * Get the current bot context. Throws if called outside bot execution.\n */\nexport function getBotContext(): BotContext\n{\n\tif (!_getCurrentBotContext())\n\t{\n\t\tthrow new Error('Game state hooks can only be used inside a bot function');\n\t}\n\treturn _getCurrentBotContext()!;\n}\n\n/**\n * Persist state between ticks.\n */\nexport function useState<T>(initialValue: T | (() => T)): [T, (newValue: T | ((prev: T) => T)) => void]\n{\n\tconst index = validateHookCall('useState');\n\tconst state = getHookState(_getCurrentEntityId()!);\n\n\tif (state.values.length <= index)\n\t{\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\n\t\tstate.values[index] = (typeof initialValue === 'function') ? (initialValue as () => T)() : initialValue;\n\t}\n\n\tconst setter = (newValue: T | ((prev: T) => T)): void =>\n\t{\n\t\tif (typeof newValue === 'function')\n\t\t{\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\n\t\t\tstate.values[index] = (newValue as (prev: T) => T)(state.values[index] as T);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tstate.values[index] = newValue;\n\t\t}\n\t};\n\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\n\treturn [state.values[index] as T, setter];\n}\n\n/**\n * React to state changes.\n */\nexport function useEffect(callback: () => void | (() => void), deps?: unknown[]): void\n{\n\tconst index = validateHookCall('useEffect');\n\tconst state = getHookState(_getCurrentEntityId()!);\n\n\tconst oldEffect = state.effects[index];\n\tconst hasChanged = !oldEffect || !deps ||\n\t\tdeps.length !== oldEffect.deps?.length ||\n\t\t!deps.every((dep, i) => dep === oldEffect.deps?.[i]);\n\n\tif (hasChanged)\n\t{\n\t\t// Cleanup old effect if it exists\n\t\tif (oldEffect?.cleanup)\n\t\t{\n\t\t\toldEffect.cleanup();\n\t\t}\n\n\t\t// Run new effect\n\t\tconst cleanup = callback();\n\t\tstate.effects[index] = {\n\t\t\tcallback,\n\t\t\tdeps,\n\t\t\tcleanup: typeof cleanup === 'function' ? cleanup : undefined,\n\t\t};\n\t}\n}\n\n/**\n * Memoize expensive calculations.\n */\nexport function useMemo<T>(factory: () => T, deps?: unknown[]): T\n{\n\tconst index = validateHookCall('useMemo');\n\tconst state = getHookState(_getCurrentEntityId()!);\n\n\tconst oldMemo = state.memos[index];\n\tconst hasChanged = !oldMemo || !deps ||\n\t\tdeps.length !== oldMemo.deps?.length ||\n\t\t!deps.every((dep, i) => dep === oldMemo.deps?.[i]);\n\n\tif (hasChanged)\n\t{\n\t\tconst value = factory();\n\t\tstate.memos[index] = {\n\t\t\tvalue,\n\t\t\tdeps,\n\t\t};\n\t\treturn value;\n\t}\n\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\n\treturn oldMemo.value as T;\n}\n\n/**\n * Mutable reference that persists across ticks.\n * Unlike useState, mutations don't need a setter - just modify .current directly.\n */\nexport interface RefObject<T>\n{\n\tcurrent: T;\n}\n\nexport function useRef<T>(initialValue: T): RefObject<T>\n{\n\tconst index = validateHookCall('useRef');\n\tconst state = getHookState(_getCurrentEntityId()!);\n\n\tif (state.values.length <= index)\n\t{\n\t\tstate.values[index] = {current: initialValue};\n\t}\n\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion\n\treturn state.values[index] as RefObject<T>;\n}\n\n/**\n * Clear hook state for an entity (e.g., when it dies).\n */\nexport function clearHooks(entityId: string): void\n{\n\tconst state = entityHooks.get(entityId);\n\tif (state)\n\t{\n\t\tstate.effects.forEach((effect) =>\n\t\t{\n\t\t\tif (effect.cleanup)\n\t\t\t{\n\t\t\t\teffect.cleanup();\n\t\t\t}\n\t\t});\n\t\tentityHooks.delete(entityId);\n\t}\n}\n\n/**\n * Reset all hook states (e.g., when a match restarts).\n */\nexport function resetAllHooks(): void\n{\n\tentityHooks.forEach((_, entityId) => clearHooks(entityId));\n\tentityHooks.clear();\n}\n\n","import {Position} from '../types.js';\r\n\r\n/**\r\n * Calculate the angle from one point to another in degrees.\r\n * 0° = right, 90° = down, 180° = left, 270° = up.\r\n */\r\nexport function angleTo(from: Position, to: Position): number\r\n{\r\n\tconst dx = to.x - from.x;\r\n\tconst dy = to.y - from.y;\r\n\tconst radians = Math.atan2(dy, dx);\r\n\treturn normalizeAngle(radians * (180 / Math.PI));\r\n}\r\n\r\n/**\r\n * Normalize an angle to the 0-360 range.\r\n */\r\nexport function normalizeAngle(angle: number): number\r\n{\r\n\tlet normalized = angle % 360;\r\n\tif (normalized < 0)\r\n\t{\r\n\t\tnormalized += 360;\r\n\t}\r\n\treturn normalized;\r\n}\r\n\r\n/**\r\n * Calculate the shortest difference between two angles (-180 to +180).\r\n */\r\nexport function angleDiff(angleA: number, angleB: number): number\r\n{\r\n\tlet diff = (angleB - angleA + 180) % 360 - 180;\r\n\tif (diff < -180)\r\n\t{\r\n\t\tdiff += 360;\r\n\t}\r\n\treturn diff;\r\n}\r\n\r\n/**\r\n * Check if an angle is within a certain range of a target angle.\r\n */\r\nexport function angleInRange(angle: number, target: number, range: number): boolean\r\n{\r\n\treturn Math.abs(angleDiff(angle, target)) <= range;\r\n}\r\n","/**\r\n * Seeded PRNG utilities for deterministic randomness.\r\n *\r\n * Each entity (wizard, missile) gets its own random sequence that:\r\n * - Is deterministic: same seed = same sequence\r\n * - Is isolated: one entity's calls don't affect another's\r\n * - Advances state: each call produces a different value\r\n */\r\n\r\nconst MAX_INT = 0x7fffffff;\r\n\r\n/**\r\n * Combine two seeds into one using a simple hash.\r\n */\r\nexport function hashCombine(a: number, b: number): number\r\n{\r\n\t// Simple hash combining two integers\r\n\tlet hash = a;\r\n\thash = ((hash << 5) + hash) ^ b;\r\n\treturn hash & MAX_INT;\r\n}\r\n\r\n/**\r\n * Advance the random state using a Linear Congruential Generator.\r\n * Parameters from glibc (widely tested).\r\n */\r\nexport function nextRandom(state: number): number\r\n{\r\n\treturn (state * 1103515245 + 12345) & MAX_INT;\r\n}\r\n\r\n/**\r\n * Create a seeded random number generator.\r\n * Returns a function that produces values in [0, 1) and advances internal state.\r\n */\r\nexport function createRandom(seed: number): () => number\r\n{\r\n\tlet state = seed & MAX_INT;\r\n\r\n\treturn (): number =>\r\n\t{\r\n\t\tstate = nextRandom(state);\r\n\t\treturn state / MAX_INT;\r\n\t};\r\n}\r\n\r\n/**\r\n * Create a deterministic seed for an entity based on match seed, entity ID, and tick.\r\n * This ensures reproducibility: same match + same entity + same tick = same random sequence.\r\n */\r\nexport function createEntitySeed(matchSeed: number, entityId: string, tick: number): number\r\n{\r\n\t// Hash the entity ID string into a number\r\n\tlet entityHash = 0;\r\n\tfor (let i = 0; i < entityId.length; i++)\r\n\t{\r\n\t\tentityHash = ((entityHash << 5) + entityHash) ^ entityId.charCodeAt(i);\r\n\t}\r\n\r\n\t// Combine match seed, entity hash, and tick\r\n\tlet combined = hashCombine(matchSeed, entityHash & MAX_INT);\r\n\tcombined = hashCombine(combined, tick);\r\n\r\n\treturn combined;\r\n}\r\n","import {\n\tGameState,\n\tWizardState,\n\tProjectileState,\n\tWizardFunction,\n\tWizardActions,\n\tMissileAIFunction,\n\tMissileActions,\n\tGameConfig,\n\tMissileConfig,\n} from '../types.js';\nimport {\n\tARENA_WIDTH,\n\tARENA_HEIGHT,\n\tSPAWN_DISTANCE,\n\tWIZARD_HEALTH,\n\tTICKS_PER_SECOND,\n\tcalculateBlinkCooldown,\n\tBLINK_RANGE,\n\tWIZARD_RADIUS,\n\tMATCH_DURATION,\n\tvalidateMissileConfig,\n\tcalculateMissileRadius,\n\tARENA_WATER_BUFFER,\n} from '../rules.js';\nimport {moveWizard, moveProjectile, sweptCircleCollision, clampToArena, resolveWizardCollision} from './physics.js';\nimport {applyDamage, updateShield, startCast, completeCast} from './spells.js';\nimport {runWithHooks, resetAllHooks, clearHooks} from './hooks-runtime.js';\nimport {angleTo, normalizeAngle, angleDiff} from '../utils/angles.js';\nimport {createRandom, createEntitySeed} from '../utils/random.js';\n\nexport interface InternalWizardState extends WizardState\n{\n\tmissileConfig?: MissileConfig;\n\tmissileAI?: MissileAIFunction;\n\tblinkTarget?: {x: number; y: number};\n\t// Combat tracking\n\tdamageDealt: number;\n\tdamageTaken: number;\n\tlastHitTick: number;\n}\n\n/**\n * Initialize a new match state.\n */\nexport function createInitialState(_seed: number, spawnDist: number = SPAWN_DISTANCE): GameState\n{\n\tconst center = {x: ARENA_WIDTH / 2, y: ARENA_HEIGHT / 2};\n\tconst offset = spawnDist / 2;\n\n\tconst wizard1: WizardState = {\n\t\tid: 'wizard-1',\n\t\tposition: {x: center.x - offset, y: center.y},\n\t\trotation: 0,\n\t\thealth: WIZARD_HEALTH,\n\t\tmaxHealth: WIZARD_HEALTH,\n\t\tstate: 'idle',\n\t\tblinkCooldown: 0,\n\t\tvelocity: {x: 0, y: 0},\n\t};\n\n\tconst wizard2: WizardState = {\n\t\tid: 'wizard-2',\n\t\tposition: {x: center.x + offset, y: center.y},\n\t\trotation: 180,\n\t\thealth: WIZARD_HEALTH,\n\t\tmaxHealth: WIZARD_HEALTH,\n\t\tstate: 'idle',\n\t\tblinkCooldown: 0,\n\t\tvelocity: {x: 0, y: 0},\n\t};\n\n\treturn {\n\t\ttick: 0,\n\t\tposition: wizard1.position,\n\t\trotation: wizard1.rotation,\n\t\thealth: wizard1.health,\n\t\tmaxHealth: wizard1.maxHealth,\n\t\tstate: wizard1.state,\n\t\tblinkCooldown: wizard1.blinkCooldown,\n\t\tvelocity: wizard1.velocity,\n\t\tenemies: [wizard2],\n\t\tprojectiles: [],\n\t\tmyProjectiles: [],\n\t\tdamageDealt: 0,\n\t\tdamageTaken: 0,\n\t\tlastHitTick: 0,\n\t};\n}\n\n/**\n * Process one game tick.\n */\nexport function tick(\n\tcurrentTick: number,\n\twizard1AI: WizardFunction,\n\twizard2AI: WizardFunction,\n\tconfig: GameConfig,\n\twizards: InternalWizardState[],\n\tprojectiles: ProjectileState[],\n\tmissileAIs: Map<string, MissileAIFunction>,\n\tmatchSeed: number,\n): {\n\tnextTick: number;\n\twizards: InternalWizardState[];\n\tprojectiles: ProjectileState[];\n\terrors: BotError[];\n}\n{\n\tconst nextTick = currentTick + 1;\n\tconst errors: BotError[] = [];\n\n\t// 1. Run wizard AIs with isolated random generators\n\t// Each wizard gets a fresh random generator seeded by (matchSeed, wizardId, tick)\n\tconst random1 = createRandom(createEntitySeed(matchSeed, wizards[0]!.id, nextTick));\n\tconst random2 = createRandom(createEntitySeed(matchSeed, wizards[1]!.id, nextTick));\n\n\t// Wrap AI calls in try-catch - if AI throws, wizard does nothing (Lesson #21)\n\tlet actions1: WizardActions = {move: {x: 0, y: 0}};\n\ttry\n\t{\n\t\tactions1 = runWithHooks(wizards[0]!.id, () => wizard1AI({\n\t\t\tstate: getPlayerStateView(0, wizards, projectiles, nextTick),\n\t\t\tconfig,\n\t\t\trandom: random1,\n\t\t})) ?? {move: {x: 0, y: 0}};\n\t}\n\tcatch(e)\n\t{\n\t\terrors.push({tick: nextTick, entityId: 'wizard-1', message: e instanceof Error ? e.message : String(e)});\n\t}\n\n\tlet actions2: WizardActions = {move: {x: 0, y: 0}};\n\ttry\n\t{\n\t\tactions2 = runWithHooks(wizards[1]!.id, () => wizard2AI({\n\t\t\tstate: getPlayerStateView(1, wizards, projectiles, nextTick),\n\t\t\tconfig,\n\t\t\trandom: random2,\n\t\t})) ?? {move: {x: 0, y: 0}};\n\t}\n\tcatch(e)\n\t{\n\t\terrors.push({tick: nextTick, entityId: 'wizard-2', message: e instanceof Error ? e.message : String(e)});\n\t}\n\n\tconst actions = [actions1, actions2];\n\n\t// 2. Update wizards\n\twizards.forEach((wizard, i) =>\n\t{\n\t\tconst action = actions[i]!;\n\n\t\t// Handle cooldowns\n\t\tif (wizard.blinkCooldown > 0)\n\t\t{\n\t\t\twizard.blinkCooldown--;\n\t\t}\n\t\tif (wizard.state === 'gcd_locked' && wizard.gcdRemaining !== undefined)\n\t\t{\n\t\t\twizard.gcdRemaining--;\n\t\t\tif (wizard.gcdRemaining <= 0)\n\t\t\t{\n\t\t\t\twizard.state = 'idle';\n\t\t\t\tdelete wizard.gcdRemaining;\n\t\t\t}\n\t\t}\n\n\t\t// Auto-aim: face the enemy by default, bot can override with aimDirection\n\t\tconst enemy = wizards[1 - i]!;\n\t\tif (action.aimDirection !== undefined && Number.isFinite(action.aimDirection))\n\t\t{\n\t\t\twizard.rotation = action.aimDirection;\n\t\t}\n\t\telse\n\t\t{\n\t\t\twizard.rotation = angleTo(wizard.position, enemy.position);\n\t\t}\n\n\t\t// Handle casting\n\t\tif (wizard.state === 'casting' && wizard.castProgress !== undefined && wizard.castDuration !== undefined)\n\t\t{\n\t\t\twizard.castProgress++;\n\t\t\tif (wizard.castProgress >= wizard.castDuration)\n\t\t\t{\n\t\t\t\tconst spell = wizard.castingSpell;\n\n\t\t\t\tif (spell === 'missile' && wizard.missileConfig && wizard.missileAI)\n\t\t\t\t{\n\t\t\t\t\t// Validate missile config to enforce minimums (Lesson #9)\n\t\t\t\t\tconst validConfig = validateMissileConfig(wizard.missileConfig);\n\t\t\t\t\tconst id = `missile-${wizard.id}-${nextTick}`;\n\t\t\t\t\tconst projectile: ProjectileState = {\n\t\t\t\t\t\tid,\n\t\t\t\t\t\ttype: 'missile',\n\t\t\t\t\t\townerId: wizard.id,\n\t\t\t\t\t\tposition: {...wizard.position},\n\t\t\t\t\t\trotation: wizard.rotation, // Use wizard's rotation at time of firing\n\t\t\t\t\t\tspeed: validConfig.speed,\n\t\t\t\t\t\tturnRate: validConfig.turnRate,\n\t\t\t\t\t\tdamage: validConfig.damage,\n\t\t\t\t\t\tremainingTicks: validConfig.duration,\n\t\t\t\t\t};\n\t\t\t\t\tprojectiles.push(projectile);\n\t\t\t\t\tmissileAIs.set(id, wizard.missileAI);\n\t\t\t\t\t// Track last missile for warmup system\n\t\t\t\t\twizard.lastMissileConfig = {...validConfig};\n\t\t\t\t\tdelete wizard.missileConfig;\n\t\t\t\t\tdelete wizard.missileAI;\n\t\t\t\t}\n\t\t\t\telse if (spell === 'blink' && wizard.blinkTarget)\n\t\t\t\t{\n\t\t\t\t\t// Clamp blink target to BLINK_RANGE from current position\n\t\t\t\t\tconst dx = wizard.blinkTarget.x - wizard.position.x;\n\t\t\t\t\tconst dy = wizard.blinkTarget.y - wizard.position.y;\n\t\t\t\t\tconst distance = Math.sqrt(dx * dx + dy * dy);\n\n\t\t\t\t\tlet targetPos = wizard.blinkTarget;\n\t\t\t\t\tif (distance > BLINK_RANGE)\n\t\t\t\t\t{\n\t\t\t\t\t\t// Clamp to max range in the same direction\n\t\t\t\t\t\tconst scale = BLINK_RANGE / distance;\n\t\t\t\t\t\ttargetPos = {\n\t\t\t\t\t\t\tx: wizard.position.x + dx * scale,\n\t\t\t\t\t\t\ty: wizard.position.y + dy * scale,\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\n\t\t\t\t\t// Clamp to arena bounds and calculate actual distance traveled\n\t\t\t\t\tconst oldPos = wizard.position;\n\t\t\t\t\twizard.position = clampToArena(targetPos, WIZARD_RADIUS);\n\t\t\t\t\tconst actualDx = wizard.position.x - oldPos.x;\n\t\t\t\t\tconst actualDy = wizard.position.y - oldPos.y;\n\t\t\t\t\tconst actualDistance = Math.sqrt(actualDx * actualDx + actualDy * actualDy);\n\t\t\t\t\twizard.blinkCooldown = calculateBlinkCooldown(actualDistance);\n\t\t\t\t\tdelete wizard.blinkTarget;\n\t\t\t\t}\n\n\t\t\t\tcompleteCast(wizard);\n\t\t\t}\n\t\t}\n\n\t\t// Handle movement (default to no movement if bot didn't provide move)\n\t\tconst oldPos = wizard.position;\n\t\tconst move = action.move ?? {x: 0, y: 0};\n\t\twizard.position = moveWizard(wizard, move, 1);\n\t\twizard.velocity = {\n\t\t\tx: wizard.position.x - oldPos.x,\n\t\t\ty: wizard.position.y - oldPos.y,\n\t\t};\n\n\t\t// Handle shield — cancel goes to idle (no GCD)\n\t\tif (wizard.state === 'channeling')\n\t\t{\n\t\t\tupdateShield(wizard, 1);\n\t\t\tif (action.cancel)\n\t\t\t{\n\t\t\t\twizard.state = 'idle';\n\t\t\t\tdelete wizard.channelingSpell;\n\t\t\t\tdelete wizard.channelDuration;\n\t\t\t}\n\t\t}\n\n\t\t// Handle start cast\n\t\tif (wizard.state === 'idle' && action.startCast)\n\t\t{\n\t\t\tif (action.startCast.spell === 'blink' && wizard.blinkCooldown > 0)\n\t\t\t{\n\t\t\t\t// Cannot blink yet\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tstartCast(wizard, action.startCast.spell, action.startCast.spell === 'missile' ? action.startCast.config : undefined);\n\t\t\t\tif (action.startCast.spell === 'missile')\n\t\t\t\t{\n\t\t\t\t\twizard.missileConfig = action.startCast.config;\n\t\t\t\t\twizard.missileAI = action.startCast.missileAI;\n\t\t\t\t\tif (action.startCast.direction !== undefined)\n\t\t\t\t\t{\n\t\t\t\t\t\twizard.rotation = action.startCast.direction;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (action.startCast.spell === 'blink')\n\t\t\t\t{\n\t\t\t\t\twizard.blinkTarget = action.startCast.target;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Handle cancel\n\t\tif (wizard.state === 'casting' && action.cancel)\n\t\t{\n\t\t\twizard.state = 'idle';\n\t\t\tdelete wizard.castingSpell;\n\t\t\tdelete wizard.castProgress;\n\t\t\tdelete wizard.castDuration;\n\t\t\tdelete wizard.missileConfig;\n\t\t\tdelete wizard.missileAI;\n\t\t\tdelete wizard.blinkTarget;\n\t\t}\n\t});\n\n\t// 2b. Resolve wizard body collision (push apart if overlapping)\n\tresolveWizardCollision(wizards[0]!, wizards[1]!);\n\n\t// 3. Update projectiles\n\tconst remainingProjectiles: ProjectileState[] = [];\n\tprojectiles.forEach((projectile) =>\n\t{\n\t\tconst ai = missileAIs.get(projectile.id);\n\t\tif (ai)\n\t\t{\n\t\t\tconst ownerIndex = wizards.findIndex((w) => w.id === projectile.ownerId);\n\t\t\t// Each missile gets its own isolated random generator\n\t\t\tconst missileRandom = createRandom(createEntitySeed(matchSeed, projectile.id, nextTick));\n\n\t\t\t// Wrap missile AI in try-catch - if it throws, missile continues straight (Lesson #21)\n\t\t\tlet missileActions: MissileActions = {};\n\t\t\ttry\n\t\t\t{\n\t\t\t\tmissileActions = runWithHooks(projectile.id, () => ai({\n\t\t\t\t\tmissileState: projectileView(projectile),\n\t\t\t\t\tworldState: getPlayerStateView(ownerIndex, wizards, projectiles, nextTick),\n\t\t\t\t\trandom: missileRandom,\n\t\t\t\t})) ?? {};\n\t\t\t}\n\t\t\tcatch(e)\n\t\t\t{\n\t\t\t\terrors.push({tick: nextTick, entityId: projectile.id, message: e instanceof Error ? e.message : String(e)});\n\t\t\t}\n\n\t\t\tif (missileActions.turnToward)\n\t\t\t{\n\t\t\t\tconst targetAngle = angleTo(projectile.position, missileActions.turnToward);\n\t\t\t\tconst diff = angleDiff(projectile.rotation, targetAngle);\n\t\t\t\t// Negative turnRate = anti-homing (turns away from target)\n\t\t\t\tconst absTurnRate = Math.abs(projectile.turnRate);\n\t\t\t\tconst effectiveDiff = projectile.turnRate >= 0 ? diff : -diff;\n\t\t\t\tconst turn = Math.max(-absTurnRate, Math.min(absTurnRate, effectiveDiff));\n\t\t\t\tprojectile.rotation = normalizeAngle(projectile.rotation + turn);\n\t\t\t}\n\t\t}\n\n\t\tconst oldPos = projectile.position;\n\t\tprojectile.position = moveProjectile(projectile, 1);\n\t\tprojectile.remainingTicks--;\n\n\t\t// Check collisions\n\t\tlet hit = false;\n\t\tconst missileRadius = calculateMissileRadius(projectile.damage);\n\t\twizards.forEach((wizard) =>\n\t\t{\n\t\t\tif (wizard.id !== projectile.ownerId && !hit)\n\t\t\t{\n\t\t\t\tif (sweptCircleCollision(oldPos, projectile.position, missileRadius, wizard.position, WIZARD_RADIUS))\n\t\t\t\t{\n\t\t\t\t\tconst actualDamage = applyDamage(wizard, projectile.damage);\n\t\t\t\t\thit = true;\n\n\t\t\t\t\t// Track damage for combat tracking hooks\n\t\t\t\t\twizard.damageTaken += actualDamage;\n\t\t\t\t\twizard.lastHitTick = nextTick;\n\n\t\t\t\t\t// Find the owner and track their damage dealt\n\t\t\t\t\tconst owner = wizards.find((w) => w.id === projectile.ownerId);\n\t\t\t\t\tif (owner)\n\t\t\t\t\t{\n\t\t\t\t\t\towner.damageDealt += actualDamage;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\t// Check if projectile is beyond the water buffer\n\t\tconst isOutOfBounds =\n\t\t\tprojectile.position.x < -ARENA_WATER_BUFFER ||\n\t\t\tprojectile.position.x > ARENA_WIDTH + ARENA_WATER_BUFFER ||\n\t\t\tprojectile.position.y < -ARENA_WATER_BUFFER ||\n\t\t\tprojectile.position.y > ARENA_HEIGHT + ARENA_WATER_BUFFER;\n\n\t\tif (!hit && projectile.remainingTicks > 0 && !isOutOfBounds)\n\t\t{\n\t\t\tremainingProjectiles.push(projectile);\n\t\t}\n\t\telse\n\t\t{\n\t\t\tmissileAIs.delete(projectile.id);\n\t\t\tclearHooks(projectile.id);\n\t\t}\n\t});\n\n\treturn {\n\t\tnextTick,\n\t\twizards,\n\t\tprojectiles: remainingProjectiles,\n\t\terrors,\n\t};\n}\n\n/**\n * Deep clone a wizard state to prevent mutation.\n */\nfunction cloneWizard(wizard: WizardState): WizardState\n{\n\treturn {\n\t\tid: wizard.id,\n\t\tposition: {...wizard.position},\n\t\trotation: wizard.rotation,\n\t\thealth: wizard.health,\n\t\tmaxHealth: wizard.maxHealth,\n\t\tstate: wizard.state,\n\t\tcastingSpell: wizard.castingSpell,\n\t\tcastProgress: wizard.castProgress,\n\t\tcastDuration: wizard.castDuration,\n\t\tchannelingSpell: wizard.channelingSpell,\n\t\tchannelDuration: wizard.channelDuration,\n\t\tgcdRemaining: wizard.gcdRemaining,\n\t\tblinkCooldown: wizard.blinkCooldown,\n\t\tvelocity: {...wizard.velocity},\n\t};\n}\n\n/**\n * Deep clone a projectile state to prevent mutation.\n */\nfunction cloneProjectile(projectile: ProjectileState): ProjectileState\n{\n\treturn {\n\t\tid: projectile.id,\n\t\ttype: projectile.type,\n\t\townerId: projectile.ownerId,\n\t\tposition: {...projectile.position},\n\t\trotation: projectile.rotation,\n\t\tspeed: projectile.speed,\n\t\tturnRate: projectile.turnRate,\n\t\tdamage: projectile.damage,\n\t\tremainingTicks: projectile.remainingTicks,\n\t};\n}\n\n/**\n * Get the game state from a specific player's perspective.\n * Returns a deep clone to prevent mutation of history entries.\n * Used for history recording where independent snapshots are needed.\n */\nexport function getPlayerState(playerIndex: number, wizards: InternalWizardState[], projectiles: ProjectileState[], tick: number): GameState\n{\n\tconst me = wizards[playerIndex]!;\n\tconst enemies = wizards.filter((_, i) => i !== playerIndex).map(cloneWizard);\n\tconst clonedProjectiles = projectiles.map(cloneProjectile);\n\n\treturn {\n\t\ttick,\n\t\tposition: {...me.position},\n\t\trotation: me.rotation,\n\t\thealth: me.health,\n\t\tmaxHealth: me.maxHealth,\n\t\tstate: me.state,\n\t\tcastingSpell: me.castingSpell,\n\t\tcastProgress: me.castProgress,\n\t\tcastDuration: me.castDuration,\n\t\tchannelingSpell: me.channelingSpell,\n\t\tchannelDuration: me.channelDuration,\n\t\tgcdRemaining: me.gcdRemaining,\n\t\tblinkCooldown: me.blinkCooldown,\n\t\tvelocity: {...me.velocity},\n\t\tlastMissileConfig: me.lastMissileConfig ? {...me.lastMissileConfig} : undefined,\n\t\twarmupMultiplier: me.warmupMultiplier,\n\t\tenemies,\n\t\tprojectiles: clonedProjectiles,\n\t\tmyProjectiles: clonedProjectiles.filter((p) => p.ownerId === me.id),\n\t\t// Combat tracking\n\t\tdamageDealt: me.damageDealt,\n\t\tdamageTaken: me.damageTaken,\n\t\tlastHitTick: me.lastHitTick,\n\t};\n}\n\n/**\n * Create a lightweight read-only view of a projectile.\n * Clones only the mutable sub-objects (position) to prevent\n * bot code from corrupting internal simulation state.\n * Scalar fields are safe since they're copied by value.\n */\nfunction projectileView(p: ProjectileState): ProjectileState\n{\n\treturn {\n\t\tid: p.id,\n\t\ttype: p.type,\n\t\townerId: p.ownerId,\n\t\tposition: {x: p.position.x, y: p.position.y},\n\t\trotation: p.rotation,\n\t\tspeed: p.speed,\n\t\tturnRate: p.turnRate,\n\t\tdamage: p.damage,\n\t\tremainingTicks: p.remainingTicks,\n\t};\n}\n\n/**\n * Extract public WizardState fields from an InternalWizardState.\n * Clones position and velocity ({x,y} spreads) to prevent bot code\n * from mutating internal simulation state. Scalar fields are safe.\n */\nfunction extractWizardView(wizard: InternalWizardState): WizardState\n{\n\treturn {\n\t\tid: wizard.id,\n\t\tposition: {x: wizard.position.x, y: wizard.position.y},\n\t\trotation: wizard.rotation,\n\t\thealth: wizard.health,\n\t\tmaxHealth: wizard.maxHealth,\n\t\tstate: wizard.state,\n\t\tcastingSpell: wizard.castingSpell,\n\t\tcastProgress: wizard.castProgress,\n\t\tcastDuration: wizard.castDuration,\n\t\tchannelingSpell: wizard.channelingSpell,\n\t\tchannelDuration: wizard.channelDuration,\n\t\tgcdRemaining: wizard.gcdRemaining,\n\t\tblinkCooldown: wizard.blinkCooldown,\n\t\tvelocity: {x: wizard.velocity.x, y: wizard.velocity.y},\n\t};\n}\n\n/**\n * Get a lightweight read-only view of game state for AI calls.\n * Clones only the small mutable sub-objects (position, velocity as {x,y})\n * to prevent bot code from corrupting internal simulation state.\n * Scalar fields are copied by value and safe from mutation.\n *\n * This is much faster than getPlayerState() (which deep-clones everything)\n * because it only spreads the tiny {x,y} objects, not full deep clones.\n */\nfunction getPlayerStateView(playerIndex: number, wizards: InternalWizardState[], projectiles: ProjectileState[], tick: number): GameState\n{\n\tconst me = wizards[playerIndex]!;\n\tconst enemies = wizards.filter((_, i) => i !== playerIndex).map(extractWizardView);\n\tconst projViews = projectiles.map(projectileView);\n\n\treturn {\n\t\ttick,\n\t\tposition: {x: me.position.x, y: me.position.y},\n\t\trotation: me.rotation,\n\t\thealth: me.health,\n\t\tmaxHealth: me.maxHealth,\n\t\tstate: me.state,\n\t\tcastingSpell: me.castingSpell,\n\t\tcastProgress: me.castProgress,\n\t\tcastDuration: me.castDuration,\n\t\tchannelingSpell: me.channelingSpell,\n\t\tchannelDuration: me.channelDuration,\n\t\tgcdRemaining: me.gcdRemaining,\n\t\tblinkCooldown: me.blinkCooldown,\n\t\tvelocity: {x: me.velocity.x, y: me.velocity.y},\n\t\tlastMissileConfig: me.lastMissileConfig ? {...me.lastMissileConfig} : undefined,\n\t\twarmupMultiplier: me.warmupMultiplier,\n\t\tenemies,\n\t\tprojectiles: projViews,\n\t\tmyProjectiles: projViews.filter((p) => p.ownerId === me.id),\n\t\t// Combat tracking\n\t\tdamageDealt: me.damageDealt,\n\t\tdamageTaken: me.damageTaken,\n\t\tlastHitTick: me.lastHitTick,\n\t};\n}\n\n/** Winner of a single match: a wizard ID, 'draw' (simultaneous kill), or null (timeout). */\nexport type MatchWinner = 'wizard-1' | 'wizard-2' | 'draw' | null;\n\n/** Winner of a fight (aggregate): a wizard ID or 'draw'. Never null. */\nexport type FightWinner = 'wizard-1' | 'wizard-2' | 'draw';\n\n/**\n * Result of a simulation.\n */\n/** A runtime error captured from a bot or missile AI function. */\nexport interface BotError\n{\n\ttick: number;\n\tentityId: string;\n\tmessage: string;\n}\n\nexport interface SimulateResult\n{\n\t/** 'wizard-1'/'wizard-2' = killed opponent, 'draw' = simultaneous kill, null = timeout */\n\twinner: MatchWinner;\n\tticks: number;\n\tfinalState: GameState;\n\thistory: GameState[];\n\t/** Runtime errors thrown by bot or missile AI functions (empty if no errors). */\n\terrors: BotError[];\n}\n\n/**\n * Result of a fight (best-of-5 at different spawn distances).\n */\nexport interface FightResult\n{\n\twizard1Wins: number;\n\twizard2Wins: number;\n\tdraws: number;\n\t/** Winner of the fight: 'wizard-1', 'wizard-2', or 'draw' (never null) */\n\twinner: FightWinner;\n\t/**\n\t * Individual match results (one per spawn distance, non-swapped only).\n\t * Used for visual playback in the web viewer. Scoring includes both sides.\n\t */\n\tmatches: SimulateResult[];\n}\n\n/** Spawn distances for the 5 matches in a fight (creates butterfly effect variation) */\nexport const FIGHT_SPAWN_DISTANCES = [500, 550, 600, 650, 700];\n\n/**\n * Run a fight: 10 matches (5 spawn distances × 2 sides) between two bots.\n * Each spawn distance is played twice — once with each bot on each side —\n * to ensure results are independent of starting position.\n *\n * The `matches` array contains only the 5 non-swapped matches (for visual playback).\n * The scoring aggregates (wizard1Wins, wizard2Wins, draws) include all 10 matches.\n *\n * This is the standard way to determine who wins a matchup.\n * Used by both the tournament system and the visual UI.\n */\nexport function fight(\n\twizard1AI: WizardFunction,\n\twizard2AI: WizardFunction,\n\toptions: {\n\t\tseed?: number;\n\t\tmaxTicks?: number;\n\t} = {},\n): FightResult\n{\n\tconst matches: SimulateResult[] = [];\n\tlet wizard1Wins = 0;\n\tlet wizard2Wins = 0;\n\tlet draws = 0;\n\n\tfor (const spawnDistance of FIGHT_SPAWN_DISTANCES)\n\t{\n\t\t// Normal side: wizard1 on left, wizard2 on right\n\t\tconst result = simulate(wizard1AI, wizard2AI, {\n\t\t\tseed: options.seed,\n\t\t\tmaxTicks: options.maxTicks,\n\t\t\tspawnDistance,\n\t\t});\n\n\t\tmatches.push(result);\n\n\t\tif (result.winner === 'wizard-1')\n\t\t{\n\t\t\twizard1Wins++;\n\t\t}\n\t\telse if (result.winner === 'wizard-2')\n\t\t{\n\t\t\twizard2Wins++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdraws++;\n\t\t}\n\n\t\t// Swapped side: wizard2 on left, wizard1 on right\n\t\t// skipHistory: swapped matches are only for scoring, not visual playback\n\t\tconst swapped = simulate(wizard2AI, wizard1AI, {\n\t\t\tseed: options.seed,\n\t\t\tmaxTicks: options.maxTicks,\n\t\t\tspawnDistance,\n\t\t\tskipHistory: true,\n\t\t});\n\n\t\t// Don't push swapped match to matches array (it's only for scoring)\n\t\tif (swapped.winner === 'wizard-1')\n\t\t{\n\t\t\t// wizard-1 in swapped match = wizard2 from original perspective\n\t\t\twizard2Wins++;\n\t\t}\n\t\telse if (swapped.winner === 'wizard-2')\n\t\t{\n\t\t\twizard1Wins++;\n\t\t}\n\t\telse\n\t\t{\n\t\t\tdraws++;\n\t\t}\n\t}\n\n\tconst winner: FightWinner = wizard1Wins > wizard2Wins ? 'wizard-1'\n\t\t: wizard2Wins > wizard1Wins ? 'wizard-2'\n\t\t\t: 'draw';\n\n\treturn {wizard1Wins, wizard2Wins, draws, winner, matches};\n}\n\n/**\n * Run a full match simulation.\n *\n * @param options.skipHistory - When true, skips recording per-tick history snapshots.\n * This dramatically improves performance (no deep cloning per tick) and is used\n * by the optimizer and fight() scoring. The returned history array will be empty\n * and finalState will still be populated.\n */\nexport function simulate(\n\twizard1AI: WizardFunction,\n\twizard2AI: WizardFunction,\n\toptions: {\n\t\tmaxTicks?: number;\n\t\tseed?: number;\n\t\tspawnDistance?: number;\n\t\tskipHistory?: boolean;\n\t} = {},\n): SimulateResult\n{\n\t// Validate AI functions (Lesson #1)\n\tif (typeof wizard1AI !== 'function')\n\t{\n\t\tthrow new Error('wizard1AI must be a function');\n\t}\n\tif (typeof wizard2AI !== 'function')\n\t{\n\t\tthrow new Error('wizard2AI must be a function');\n\t}\n\n\t// Sanitize options (Lesson #3)\n\tconst {maxTicks: rawMaxTicks = MATCH_DURATION, seed: rawSeed = Date.now(), spawnDistance: rawSpawnDistance, skipHistory = false} = options;\n\n\t// Sanitize seed - ensure valid 32-bit integer\n\tconst seed = Number.isFinite(rawSeed) ? Math.floor(rawSeed) & 0x7FFFFFFF : Date.now() & 0x7FFFFFFF;\n\n\t// Sanitize maxTicks - ensure positive integer with reasonable limit\n\tconst maxTicks = Number.isFinite(rawMaxTicks) && rawMaxTicks > 0\n\t\t? Math.min(Math.floor(rawMaxTicks), 100000) // Cap at 100k ticks (~16 min)\n\t\t: MATCH_DURATION;\n\n\t// Sanitize spawnDistance - must be positive and fit in arena\n\tconst spawnDistance = Number.isFinite(rawSpawnDistance) && rawSpawnDistance! > 0\n\t\t? Math.min(rawSpawnDistance!, Math.min(ARENA_WIDTH, ARENA_HEIGHT) - 100) // Leave room for wizards\n\t\t: SPAWN_DISTANCE;\n\n\tconst config: GameConfig = Object.freeze({\n\t\tarenaSize: Object.freeze({width: ARENA_WIDTH, height: ARENA_HEIGHT}),\n\t\ttickRate: TICKS_PER_SECOND,\n\t\tmaxTicks,\n\t});\n\n\tresetAllHooks();\n\n\tconst initialState = createInitialState(seed, spawnDistance);\n\tconst history: GameState[] = skipHistory ? [] : [initialState];\n\tlet currentTick = 0;\n\tlet wizards: InternalWizardState[] = [\n\t\t{\n\t\t\tid: 'wizard-1',\n\t\t\tposition: {x: ARENA_WIDTH / 2 - spawnDistance / 2, y: ARENA_HEIGHT / 2},\n\t\t\trotation: 0,\n\t\t\thealth: WIZARD_HEALTH,\n\t\t\tmaxHealth: WIZARD_HEALTH,\n\t\t\tstate: 'idle',\n\t\t\tblinkCooldown: 0,\n\t\t\tvelocity: {x: 0, y: 0},\n\t\t\tdamageDealt: 0,\n\t\t\tdamageTaken: 0,\n\t\t\tlastHitTick: 0,\n\t\t},\n\t\t{\n\t\t\tid: 'wizard-2',\n\t\t\tposition: {x: ARENA_WIDTH / 2 + spawnDistance / 2, y: ARENA_HEIGHT / 2},\n\t\t\trotation: 180,\n\t\t\thealth: WIZARD_HEALTH,\n\t\t\tmaxHealth: WIZARD_HEALTH,\n\t\t\tstate: 'idle',\n\t\t\tblinkCooldown: 0,\n\t\t\tvelocity: {x: 0, y: 0},\n\t\t\tdamageDealt: 0,\n\t\t\tdamageTaken: 0,\n\t\t\tlastHitTick: 0,\n\t\t},\n\t];\n\tlet projectiles: ProjectileState[] = [];\n\tconst missileAIs = new Map<string, MissileAIFunction>();\n\tconst allErrors: BotError[] = [];\n\n\twhile (currentTick < maxTicks)\n\t{\n\t\tconst result = tick(currentTick, wizard1AI, wizard2AI, config, wizards, projectiles, missileAIs, seed);\n\t\tcurrentTick = result.nextTick;\n\t\twizards = result.wizards;\n\t\tprojectiles = result.projectiles;\n\t\tif (result.errors.length > 0) allErrors.push(...result.errors);\n\n\t\tif (!skipHistory)\n\t\t{\n\t\t\t// Deep-clone snapshot for history (needed for replay)\n\t\t\thistory.push(getPlayerState(0, wizards, projectiles, currentTick));\n\t\t}\n\n\t\tif (wizards[0]!.health <= 0 || wizards[1]!.health <= 0)\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\tlet winner: MatchWinner = null;\n\tif (wizards[0]!.health <= 0 && wizards[1]!.health <= 0)\n\t{\n\t\twinner = 'draw';\n\t}\n\telse if (wizards[1]!.health <= 0)\n\t{\n\t\twinner = 'wizard-1';\n\t}\n\telse if (wizards[0]!.health <= 0)\n\t{\n\t\twinner = 'wizard-2';\n\t}\n\n\t// Always create final state snapshot (needed for result)\n\tconst finalState = getPlayerState(0, wizards, projectiles, currentTick);\n\n\treturn {\n\t\twinner,\n\t\tticks: currentTick,\n\t\tfinalState,\n\t\thistory: skipHistory ? [] : history,\n\t\terrors: allErrors,\n\t};\n}\n","/**\n * VIBEMANCER - MANUAL MATCH\n *\n * Tick-by-tick match runner used by manual play mode. Wraps the same\n * `tick()` primitive that `simulate()` uses, so stepping forward\n * produces the same trajectory as a batched `simulate()` with the\n * same seed.\n *\n * Extra capabilities over `simulate()`:\n * - step(N) advances N ticks at a time (default 1) — caller controls pacing\n * - replaceMissileAI(id, ai) hot-swaps a missile's AI mid-flight (used by\n * the missile-hijack feature in manual play)\n * - setInvincible(wizardIndex, on) toggles damage immunity per wizard\n */\n\nimport {\n\tARENA_HEIGHT,\n\tARENA_WIDTH,\n\tMATCH_DURATION,\n\tSPAWN_DISTANCE,\n\tTICKS_PER_SECOND,\n\tWIZARD_HEALTH,\n} from '../rules.js';\nimport type {\n\tGameConfig,\n\tGameState,\n\tMissileAIFunction,\n\tProjectileState,\n\tWizardFunction,\n} from '../types.js';\nimport {clearHooks} from './hooks-runtime.js';\nimport {\n\ttype BotError,\n\ttype InternalWizardState,\n\ttype MatchWinner,\n\ttype SimulateResult,\n\tgetPlayerState,\n\ttick,\n} from './simulation.js';\n\nexport interface ManualMatchOptions\n{\n\tseed?: number;\n\tspawnDistance?: number;\n\tmaxTicks?: number;\n}\n\nexport interface StepResult\n{\n\tgameState: GameState;\n\terrors: BotError[];\n\tdone: boolean;\n}\n\n/**\n * Build the initial pair of internal wizards for a match. Mirrors the\n * inline setup in simulate() so both code paths produce identical state.\n */\nfunction createInitialWizards(spawnDistance: number): InternalWizardState[]\n{\n\treturn [\n\t\t{\n\t\t\tid: 'wizard-1',\n\t\t\tposition: {x: ARENA_WIDTH / 2 - spawnDistance / 2, y: ARENA_HEIGHT / 2},\n\t\t\trotation: 0,\n\t\t\thealth: WIZARD_HEALTH,\n\t\t\tmaxHealth: WIZARD_HEALTH,\n\t\t\tstate: 'idle',\n\t\t\tblinkCooldown: 0,\n\t\t\tvelocity: {x: 0, y: 0},\n\t\t\tdamageDealt: 0,\n\t\t\tdamageTaken: 0,\n\t\t\tlastHitTick: 0,\n\t\t},\n\t\t{\n\t\t\tid: 'wizard-2',\n\t\t\tposition: {x: ARENA_WIDTH / 2 + spawnDistance / 2, y: ARENA_HEIGHT / 2},\n\t\t\trotation: 180,\n\t\t\thealth: WIZARD_HEALTH,\n\t\t\tmaxHealth: WIZARD_HEALTH,\n\t\t\tstate: 'idle',\n\t\t\tblinkCooldown: 0,\n\t\t\tvelocity: {x: 0, y: 0},\n\t\t\tdamageDealt: 0,\n\t\t\tdamageTaken: 0,\n\t\t\tlastHitTick: 0,\n\t\t},\n\t];\n}\n\nexport class ManualMatch\n{\n\tprivate readonly wizard1AI: WizardFunction;\n\tprivate readonly wizard2AI: WizardFunction;\n\tprivate readonly seed: number;\n\tprivate readonly maxTicks: number;\n\tprivate readonly config: GameConfig;\n\n\tprivate wizards: InternalWizardState[];\n\tprivate projectiles: ProjectileState[] = [];\n\tprivate missileAIs: Map<string, MissileAIFunction> = new Map();\n\t/** First-replacement originals for hijacked missiles. Used by restoreMissileAI. */\n\tprivate originalMissileAIs: Map<string, MissileAIFunction> = new Map();\n\tprivate currentTick = 0;\n\tprivate done = false;\n\tprivate allErrors: BotError[] = [];\n\tprivate history: GameState[];\n\n\tconstructor(wizard1AI: WizardFunction, wizard2AI: WizardFunction, options: ManualMatchOptions = {})\n\t{\n\t\tif (typeof wizard1AI !== 'function')\n\t\t{\n\t\t\tthrow new Error('wizard1AI must be a function');\n\t\t}\n\t\tif (typeof wizard2AI !== 'function')\n\t\t{\n\t\t\tthrow new Error('wizard2AI must be a function');\n\t\t}\n\n\t\tthis.wizard1AI = wizard1AI;\n\t\tthis.wizard2AI = wizard2AI;\n\n\t\tconst {\n\t\t\tseed: rawSeed = Date.now(),\n\t\t\tspawnDistance: rawSpawn,\n\t\t\tmaxTicks: rawMaxTicks = MATCH_DURATION,\n\t\t} = options;\n\n\t\tthis.seed = Number.isFinite(rawSeed) ? Math.floor(rawSeed) & 0x7FFFFFFF : Date.now() & 0x7FFFFFFF;\n\t\tthis.maxTicks = Number.isFinite(rawMaxTicks) && rawMaxTicks > 0\n\t\t\t? Math.min(Math.floor(rawMaxTicks), 100000)\n\t\t\t: MATCH_DURATION;\n\t\tconst spawnDistance = Number.isFinite(rawSpawn) && rawSpawn! > 0\n\t\t\t? Math.min(rawSpawn!, Math.min(ARENA_WIDTH, ARENA_HEIGHT) - 100)\n\t\t\t: SPAWN_DISTANCE;\n\n\t\tthis.config = Object.freeze({\n\t\t\tarenaSize: Object.freeze({width: ARENA_WIDTH, height: ARENA_HEIGHT}),\n\t\t\ttickRate: TICKS_PER_SECOND,\n\t\t\tmaxTicks: this.maxTicks,\n\t\t});\n\n\t\t// Hook state must start clean — bot useState/useRef from a previous\n\t\t// match would otherwise leak in.\n\t\tclearHooks('wizard-1');\n\t\tclearHooks('wizard-2');\n\n\t\tthis.wizards = createInitialWizards(spawnDistance);\n\t\tthis.history = [getPlayerState(0, this.wizards, this.projectiles, 0)];\n\t}\n\n\t/**\n\t * Advance the match by `count` ticks (default 1). Stops early if the\n\t * match completes mid-batch.\n\t */\n\tstep(count = 1): StepResult\n\t{\n\t\tconst errorsThisCall: BotError[] = [];\n\t\tfor (let i = 0; i < count; i++)\n\t\t{\n\t\t\tif (this.done) break;\n\t\t\tconst result = tick(\n\t\t\t\tthis.currentTick,\n\t\t\t\tthis.wizard1AI,\n\t\t\t\tthis.wizard2AI,\n\t\t\t\tthis.config,\n\t\t\t\tthis.wizards,\n\t\t\t\tthis.projectiles,\n\t\t\t\tthis.missileAIs,\n\t\t\t\tthis.seed,\n\t\t\t);\n\t\t\tthis.currentTick = result.nextTick;\n\t\t\tthis.wizards = result.wizards;\n\t\t\tthis.projectiles = result.projectiles;\n\t\t\tif (result.errors.length > 0)\n\t\t\t{\n\t\t\t\tthis.allErrors.push(...result.errors);\n\t\t\t\terrorsThisCall.push(...result.errors);\n\t\t\t}\n\n\t\t\tthis.history.push(getPlayerState(0, this.wizards, this.projectiles, this.currentTick));\n\n\t\t\tif (\n\t\t\t\tthis.wizards[0]!.health <= 0\n\t\t\t\t|| this.wizards[1]!.health <= 0\n\t\t\t\t|| this.currentTick >= this.maxTicks\n\t\t\t)\n\t\t\t{\n\t\t\t\tthis.done = true;\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\tgameState: this.getGameState(),\n\t\t\terrors: errorsThisCall,\n\t\t\tdone: this.done,\n\t\t};\n\t}\n\n\t/**\n\t * Hot-swap a missile's AI function. Used by the missile-hijack feature\n\t * in manual play. The first replacement remembers the original AI so\n\t * `restoreMissileAI` can put it back. Subsequent replacements update\n\t * the active AI but leave the remembered original alone.\n\t *\n\t * No-op if the projectile id doesn't exist.\n\t */\n\treplaceMissileAI(projectileId: string, ai: MissileAIFunction): void\n\t{\n\t\tconst existing = this.missileAIs.get(projectileId);\n\t\tif (!existing) return;\n\t\tif (!this.originalMissileAIs.has(projectileId))\n\t\t{\n\t\t\tthis.originalMissileAIs.set(projectileId, existing);\n\t\t}\n\t\tthis.missileAIs.set(projectileId, ai);\n\t}\n\n\t/**\n\t * Restore a previously hijacked missile's original AI function.\n\t * No-op if the projectile id doesn't exist or was never hijacked.\n\t */\n\trestoreMissileAI(projectileId: string): void\n\t{\n\t\tconst original = this.originalMissileAIs.get(projectileId);\n\t\tif (!original) return;\n\t\tif (!this.missileAIs.has(projectileId)) return;\n\t\tthis.missileAIs.set(projectileId, original);\n\t\tthis.originalMissileAIs.delete(projectileId);\n\t}\n\n\t/**\n\t * Set the invincibility flag for a wizard. Invincible wizards take 0\n\t * damage from all sources.\n\t */\n\tsetInvincible(wizardIndex: 0 | 1, on: boolean): void\n\t{\n\t\tconst wizard = this.wizards[wizardIndex];\n\t\tif (!wizard) return;\n\t\twizard.invincible = on;\n\t}\n\n\t/**\n\t * Get the current game state from wizard-1's perspective.\n\t * Returned object is a deep clone — safe to mutate.\n\t */\n\tgetGameState(): GameState\n\t{\n\t\treturn getPlayerState(0, this.wizards, this.projectiles, this.currentTick);\n\t}\n\n\tgetCurrentTick(): number\n\t{\n\t\treturn this.currentTick;\n\t}\n\n\tisComplete(): {done: boolean; winner: MatchWinner}\n\t{\n\t\treturn {done: this.done, winner: this.done ? this.computeWinner() : null};\n\t}\n\n\tgetResult(): SimulateResult\n\t{\n\t\treturn {\n\t\t\twinner: this.computeWinner(),\n\t\t\tticks: this.currentTick,\n\t\t\tfinalState: this.getGameState(),\n\t\t\thistory: this.history,\n\t\t\terrors: this.allErrors,\n\t\t};\n\t}\n\n\tdispose(): void\n\t{\n\t\tclearHooks('wizard-1');\n\t\tclearHooks('wizard-2');\n\t\tthis.history = [];\n\t}\n\n\tprivate computeWinner(): MatchWinner\n\t{\n\t\tconst w1 = this.wizards[0]!;\n\t\tconst w2 = this.wizards[1]!;\n\t\tif (w1.health <= 0 && w2.health <= 0) return 'draw';\n\t\tif (w2.health <= 0) return 'wizard-1';\n\t\tif (w1.health <= 0) return 'wizard-2';\n\t\treturn null;\n\t}\n}\n","/**\n * VIBEMANCER - MISSILE TEMPLATES\n *\n * Reusable missile config + AI factories for manual play and bot examples.\n * Each factory clamps its inputs to rules.ts minimums and returns a pure\n * AI function that's safe to register with the engine.\n */\n\nimport {\n\tMISSILE_MIN_DAMAGE,\n\tMISSILE_MIN_SPEED,\n\tMISSILE_MIN_DURATION,\n} from '../rules.js';\nimport type {MissileAIFunction, MissileConfig} from '../types.js';\n\ninterface BaseParams\n{\n\tdamage: number;\n\tspeed: number;\n\tduration: number;\n}\n\ninterface TurningParams extends BaseParams\n{\n\tturnRate: number;\n}\n\nexport type StraightParams = BaseParams;\n\nexport type HomingParams = TurningParams;\n\nexport type AntiHomingParams = TurningParams;\n\nexport interface SpiralParams extends BaseParams\n{\n\tspiralRadius: number;\n\tspiralFreq: number;\n}\n\nexport interface SeekerParams extends TurningParams\n{\n\tminLockDistance: number;\n}\n\nexport interface MissileTemplate\n{\n\tconfig: MissileConfig;\n\tai: MissileAIFunction;\n}\n\nfunction clampBase(p: BaseParams): {damage: number; speed: number; duration: number}\n{\n\treturn {\n\t\tdamage: Math.max(MISSILE_MIN_DAMAGE, p.damage),\n\t\tspeed: Math.max(MISSILE_MIN_SPEED, p.speed),\n\t\tduration: Math.max(MISSILE_MIN_DURATION, Math.floor(p.duration)),\n\t};\n}\n\n/**\n * Fire-and-forget missile. No steering — flies in a straight line.\n */\nexport function straightMissile(p: StraightParams): MissileTemplate\n{\n\tconst base = clampBase(p);\n\tconst config: MissileConfig = {...base, turnRate: 0};\n\tconst ai: MissileAIFunction = () => ({});\n\treturn {config, ai};\n}\n\n/**\n * Homing missile. Steers toward the enemy each tick.\n */\nexport function homingMissile(p: HomingParams): MissileTemplate\n{\n\tconst base = clampBase(p);\n\tconst config: MissileConfig = {...base, turnRate: p.turnRate};\n\tconst ai: MissileAIFunction = ({worldState}) =>\n\t{\n\t\tconst enemy = worldState.enemies[0];\n\t\tif (!enemy) return {};\n\t\treturn {turnToward: enemy.position};\n\t};\n\treturn {config, ai};\n}\n\n/**\n * Anti-homing missile. Steers AWAY from the enemy — useful as a feint or\n * area-denial pattern.\n */\nexport function antiHomingMissile(p: AntiHomingParams): MissileTemplate\n{\n\tconst base = clampBase(p);\n\tconst config: MissileConfig = {...base, turnRate: p.turnRate};\n\tconst ai: MissileAIFunction = ({missileState, worldState}) =>\n\t{\n\t\tconst enemy = worldState.enemies[0];\n\t\tif (!enemy) return {};\n\t\t// Reflect the enemy position through the missile to get an \"away\" target.\n\t\tconst target = {\n\t\t\tx: missileState.position.x * 2 - enemy.position.x,\n\t\t\ty: missileState.position.y * 2 - enemy.position.y,\n\t\t};\n\t\treturn {turnToward: target};\n\t};\n\treturn {config, ai};\n}\n\n/**\n * Spiral missile. Continuously orbits its current heading while advancing.\n * Uses missileState.remainingTicks as a deterministic phase counter so the\n * AI is fully pure (no closure state).\n */\nexport function spiralMissile(p: SpiralParams): MissileTemplate\n{\n\tconst base = clampBase(p);\n\t// turnRate must be high enough for the spiral to manifest visibly\n\tconst config: MissileConfig = {...base, turnRate: 12};\n\tconst radius = Math.max(1, p.spiralRadius);\n\tconst freq = p.spiralFreq;\n\tconst ai: MissileAIFunction = ({missileState}) =>\n\t{\n\t\tconst phase = -missileState.remainingTicks * freq;\n\t\tconst target = {\n\t\t\tx: missileState.position.x + Math.cos(phase) * radius,\n\t\t\ty: missileState.position.y + Math.sin(phase) * radius,\n\t\t};\n\t\treturn {turnToward: target};\n\t};\n\treturn {config, ai};\n}\n\n/**\n * Seeker missile. Homes toward the enemy, but only when farther away than\n * minLockDistance — closer than that, the missile coasts straight (so it\n * doesn't whirl around a target it's about to hit).\n */\nexport function seekerMissile(p: SeekerParams): MissileTemplate\n{\n\tconst base = clampBase(p);\n\tconst config: MissileConfig = {...base, turnRate: p.turnRate};\n\tconst minDist = Math.max(0, p.minLockDistance);\n\tconst minDistSq = minDist * minDist;\n\tconst ai: MissileAIFunction = ({missileState, worldState}) =>\n\t{\n\t\tconst enemy = worldState.enemies[0];\n\t\tif (!enemy) return {};\n\t\tconst dx = enemy.position.x - missileState.position.x;\n\t\tconst dy = enemy.position.y - missileState.position.y;\n\t\tconst distSq = dx * dx + dy * dy;\n\t\tif (distSq < minDistSq) return {};\n\t\treturn {turnToward: enemy.position};\n\t};\n\treturn {config, ai};\n}\n","import {Position} from '../types.js';\r\n\r\n/**\r\n * Calculate the distance between two points.\r\n */\r\nexport function distanceTo(a: Position, b: Position): number\r\n{\r\n\tconst dx = b.x - a.x;\r\n\tconst dy = b.y - a.y;\r\n\treturn Math.sqrt(dx * dx + dy * dy);\r\n}\r\n\r\n/**\r\n * Check if two points are within a certain range of each other.\r\n */\r\nexport function inRange(a: Position, b: Position, range: number): boolean\r\n{\r\n\tconst dx = b.x - a.x;\r\n\tconst dy = b.y - a.y;\r\n\treturn (dx * dx + dy * dy) <= range * range;\r\n}\r\n","import {Position, Velocity} from '../types.js';\r\nimport {normalizeAngle} from './angles.js';\r\n\r\n/**\r\n * Get the position after moving a certain distance in a direction.\r\n */\r\nexport function moveInDirection(position: Position, angle: number, distance: number): Position\r\n{\r\n\tconst radians = angle * (Math.PI / 180);\r\n\treturn {\r\n\t\tx: position.x + Math.cos(radians) * distance,\r\n\t\ty: position.y + Math.sin(radians) * distance,\r\n\t};\r\n}\r\n\r\n/**\r\n * Predict the position after N ticks given current velocity.\r\n */\r\nexport function predictPosition(position: Position, velocity: Velocity, ticks: number): Position\r\n{\r\n\treturn {\r\n\t\tx: position.x + velocity.x * ticks,\r\n\t\ty: position.y + velocity.y * ticks,\r\n\t};\r\n}\r\n\r\n/**\r\n * Calculate the intercept angle for a target moving at a certain velocity.\r\n * Returns null if no intercept solution exists.\r\n */\r\nexport function interceptAngle(\r\n\tshooterPosition: Position,\r\n\ttargetPosition: Position,\r\n\ttargetVelocity: Velocity,\r\n\tprojectileSpeed: number,\r\n): number | null\r\n{\r\n\tconst dx = targetPosition.x - shooterPosition.x;\r\n\tconst dy = targetPosition.y - shooterPosition.y;\r\n\r\n\tconst vx = targetVelocity.x;\r\n\tconst vy = targetVelocity.y;\r\n\r\n\tconst a = vx * vx + vy * vy - projectileSpeed * projectileSpeed;\r\n\tconst b = 2 * (vx * dx + vy * dy);\r\n\tconst c = dx * dx + dy * dy;\r\n\r\n\tlet t: number | null = null;\r\n\r\n\t// Handle the linear case when a is approximately zero\r\n\t// This happens when |targetVelocity| === projectileSpeed\r\n\tif (Math.abs(a) < 1e-10)\r\n\t{\r\n\t\t// Linear equation: b*t + c = 0 => t = -c/b\r\n\t\tif (Math.abs(b) < 1e-10)\r\n\t\t{\r\n\t\t\t// No solution possible\r\n\t\t\treturn null;\r\n\t\t}\r\n\t\tconst linearT = -c / b;\r\n\t\tif (linearT > 0)\r\n\t\t{\r\n\t\t\tt = linearT;\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\t// Quadratic case\r\n\t\tconst discriminant = b * b - 4 * a * c;\r\n\r\n\t\tif (discriminant < 0)\r\n\t\t{\r\n\t\t\treturn null;\r\n\t\t}\r\n\r\n\t\tconst sqrtD = Math.sqrt(discriminant);\r\n\t\tconst t1 = (-b + sqrtD) / (2 * a);\r\n\t\tconst t2 = (-b - sqrtD) / (2 * a);\r\n\r\n\t\tif (t1 > 0 && t2 > 0)\r\n\t\t{\r\n\t\t\tt = Math.min(t1, t2);\r\n\t\t}\r\n\t\telse if (t1 > 0)\r\n\t\t{\r\n\t\t\tt = t1;\r\n\t\t}\r\n\t\telse if (t2 > 0)\r\n\t\t{\r\n\t\t\tt = t2;\r\n\t\t}\r\n\t}\r\n\r\n\tif (t === null)\r\n\t{\r\n\t\treturn null;\r\n\t}\r\n\r\n\tconst interceptPos = {\r\n\t\tx: targetPosition.x + vx * t,\r\n\t\ty: targetPosition.y + vy * t,\r\n\t};\r\n\r\n\tconst dx2 = interceptPos.x - shooterPosition.x;\r\n\tconst dy2 = interceptPos.y - shooterPosition.y;\r\n\r\n\treturn normalizeAngle(Math.atan2(dy2, dx2) * (180 / Math.PI));\r\n}\r\n","import {Position} from '../types.js';\r\nimport {distanceTo} from './distance.js';\r\n\r\n/**\r\n * Find the nearest entity from a list.\r\n */\r\nexport function findNearest<T extends {position: Position}>(from: Position, entities: T[]): T | null\r\n{\r\n\tif (entities.length === 0)\r\n\t{\r\n\t\treturn null;\r\n\t}\r\n\r\n\tlet nearest = entities[0]!;\r\n\tlet minDistance = distanceTo(from, nearest.position);\r\n\r\n\tfor (let i = 1; i < entities.length; i++)\r\n\t{\r\n\t\tconst entity = entities[i]!;\r\n\t\tconst distance = distanceTo(from, entity.position);\r\n\t\tif (distance < minDistance)\r\n\t\t{\r\n\t\t\tminDistance = distance;\r\n\t\t\tnearest = entity;\r\n\t\t}\r\n\t}\r\n\r\n\treturn nearest;\r\n}\r\n\r\n/**\r\n * Find all entities within a certain range.\r\n */\r\nexport function findInRange<T extends {position: Position}>(from: Position, entities: T[], range: number): T[]\r\n{\r\n\treturn entities.filter((entity) => distanceTo(from, entity.position) <= range);\r\n}\r\n\r\n/**\r\n * Sort entities by distance (closest first).\r\n */\r\nexport function sortByDistance<T extends {position: Position}>(from: Position, entities: T[]): T[]\r\n{\r\n\treturn [...entities].sort((a, b) => distanceTo(from, a.position) - distanceTo(from, b.position));\r\n}\r\n","/**\r\n * VIBEMANCER - SPATIAL UTILITIES\r\n *\r\n * Direction vectors and arena bounds utilities.\r\n */\r\n\r\nimport {Position} from '../types.js';\r\nimport {ARENA_WIDTH, ARENA_HEIGHT} from '../rules.js';\r\n\r\n/**\r\n * Normalize a vector to unit length.\r\n * Returns {x: 0, y: 0} for zero-length vectors.\r\n */\r\nexport function normalize(vector: Position): Position\r\n{\r\n\tconst len = Math.sqrt(vector.x * vector.x + vector.y * vector.y);\r\n\tif (len === 0)\r\n\t{\r\n\t\treturn {x: 0, y: 0};\r\n\t}\r\n\treturn {x: vector.x / len, y: vector.y / len};\r\n}\r\n\r\n/**\r\n * Get normalized direction vector from one position toward another.\r\n * Returns {x: 0, y: 0} if positions are identical.\r\n */\r\nexport function directionTo(from: Position, to: Position): Position\r\n{\r\n\tconst dx = to.x - from.x;\r\n\tconst dy = to.y - from.y;\r\n\treturn normalize({x: dx, y: dy});\r\n}\r\n\r\n/**\r\n * Get normalized direction vector from one position away from another.\r\n * Returns {x: 0, y: 0} if positions are identical.\r\n */\r\nexport function directionAway(from: Position, to: Position): Position\r\n{\r\n\tconst dir = directionTo(from, to);\r\n\treturn {x: -dir.x, y: -dir.y};\r\n}\r\n\r\n/**\r\n * Clamp a position to valid arena bounds.\r\n * Note: For wizard-specific clamping with radius, use clampToArena from physics.ts\r\n */\r\nexport function clampPositionToArena(position: Position): Position\r\n{\r\n\treturn {\r\n\t\tx: Math.max(0, Math.min(ARENA_WIDTH, position.x)),\r\n\t\ty: Math.max(0, Math.min(ARENA_HEIGHT, position.y)),\r\n\t};\r\n}\r\n\r\n/**\r\n * Get the length/magnitude of a vector.\r\n */\r\nexport function magnitude(vector: Position): number\r\n{\r\n\treturn Math.sqrt(vector.x * vector.x + vector.y * vector.y);\r\n}\r\n","/**\r\n * VIBEMANCER - COMBAT UTILITIES\r\n *\r\n * Utilities for combat calculations.\r\n */\r\n\r\nimport {Position, Velocity, MissileConfig} from '../types.js';\r\nimport {\r\n\tcalculateMissileCastTime,\r\n\tcalculateWarmupMultiplier,\r\n\tvalidateMissileConfig,\r\n\tTICKS_PER_SECOND,\r\n\tWIZARD_HEALTH,\r\n\tMISSILE_BASE_CAST,\r\n\tMISSILE_DAMAGE_SCALE,\r\n\tMISSILE_DAMAGE_POWER,\r\n\tMISSILE_HOMING_COEFF,\r\n\tMISSILE_TURN_DURATION_COEFF,\r\n\tMISSILE_SPEED_DURATION_COEFF,\r\n\tMISSILE_SPEED_DURATION_BASELINE,\r\n\tMISSILE_MIN_CAST_TIME,\r\n\tMISSILE_MIN_DURATION,\r\n\tMISSILE_MIN_DAMAGE,\r\n\teffectiveTurnRateCost,\r\n} from '../rules.js';\r\n\r\n/**\r\n * Get cast time in ticks for a missile configuration.\r\n * Applies the same clamps/validation as the engine before calculating,\r\n * so the result matches the actual cast time that will be used in-game.\r\n *\r\n * If lastMissileConfig is provided, includes warmup multiplier.\r\n * Pass undefined for first cast (full warmup) or null for base time only.\r\n */\r\nexport function getMissileCastTime(config: MissileConfig, lastMissileConfig?: MissileConfig | null): number\r\n{\r\n\tconst validated = validateMissileConfig(config);\r\n\treturn Math.ceil(calculateMissileCastTime(validated, lastMissileConfig) * TICKS_PER_SECOND);\r\n}\r\n\r\n/**\r\n * Calculate the position to aim at to hit a moving target.\r\n * Returns the intercept point where a missile would hit the target.\r\n *\r\n * @param targetPos - Current target position\r\n * @param targetVel - Target velocity (units per tick)\r\n * @param missileSpeed - Missile speed (units per tick)\r\n * @param myPos - Shooter position\r\n * @returns The position to aim at\r\n */\r\nexport function getLeadPosition(\r\n\ttargetPos: Position,\r\n\ttargetVel: Velocity,\r\n\tmissileSpeed: number,\r\n\tmyPos: Position,\r\n): Position\r\n{\r\n\t// Vector from shooter to target\r\n\tconst dx = targetPos.x - myPos.x;\r\n\tconst dy = targetPos.y - myPos.y;\r\n\r\n\t// Target velocity\r\n\tconst vx = targetVel.x;\r\n\tconst vy = targetVel.y;\r\n\r\n\t// Quadratic coefficients for intercept time\r\n\t// |target + velocity * t - shooter|^2 = (missileSpeed * t)^2\r\n\t// Expands to: (vx^2 + vy^2 - speed^2) * t^2 + 2*(vx*dx + vy*dy) * t + (dx^2 + dy^2) = 0\r\n\tconst a = vx * vx + vy * vy - missileSpeed * missileSpeed;\r\n\tconst b = 2 * (vx * dx + vy * dy);\r\n\tconst c = dx * dx + dy * dy;\r\n\r\n\tlet t: number | null = null;\r\n\r\n\t// Handle the linear case (|velocity| === missileSpeed)\r\n\tif (Math.abs(a) < 1e-10)\r\n\t{\r\n\t\tif (Math.abs(b) > 1e-10)\r\n\t\t{\r\n\t\t\tconst linearT = -c / b;\r\n\t\t\tif (linearT > 0)\r\n\t\t\t{\r\n\t\t\t\tt = linearT;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\telse\r\n\t{\r\n\t\t// Quadratic case\r\n\t\tconst discriminant = b * b - 4 * a * c;\r\n\r\n\t\tif (discriminant >= 0)\r\n\t\t{\r\n\t\t\tconst sqrtD = Math.sqrt(discriminant);\r\n\t\t\tconst t1 = (-b + sqrtD) / (2 * a);\r\n\t\t\tconst t2 = (-b - sqrtD) / (2 * a);\r\n\r\n\t\t\tif (t1 > 0 && t2 > 0)\r\n\t\t\t{\r\n\t\t\t\tt = Math.min(t1, t2);\r\n\t\t\t}\r\n\t\t\telse if (t1 > 0)\r\n\t\t\t{\r\n\t\t\t\tt = t1;\r\n\t\t\t}\r\n\t\t\telse if (t2 > 0)\r\n\t\t\t{\r\n\t\t\t\tt = t2;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t// If no intercept solution, just aim at current position\r\n\tif (t === null)\r\n\t{\r\n\t\treturn {x: targetPos.x, y: targetPos.y};\r\n\t}\r\n\r\n\t// Calculate intercept position\r\n\treturn {\r\n\t\tx: targetPos.x + vx * t,\r\n\t\ty: targetPos.y + vy * t,\r\n\t};\r\n}\r\n\r\n/**\r\n * Calculate optimal missile configuration based on target behavior.\r\n */\r\nexport function getAdaptiveMissileConfig(\r\n\ttargetVelocity: Position,\r\n\tdistance: number,\r\n): {speed: number; turnRate: number; damage: number; duration: number}\r\n{\r\n\tconst targetSpeed = Math.sqrt(targetVelocity.x ** 2 + targetVelocity.y ** 2);\r\n\r\n\t// Fast moving target = more homing\r\n\t// Slow/stationary target = faster missile, light homing for correction\r\n\tif (targetSpeed > 0.8)\r\n\t{\r\n\t\t// Moving target - use significant homing\r\n\t\tconst turnRate = Math.min(3, 1 + targetSpeed * 1.5);\r\n\t\treturn {\r\n\t\t\tdamage: 10,\r\n\t\t\tspeed: 5,\r\n\t\t\tturnRate,\r\n\t\t\tduration: Math.min(250, 100 + distance * 0.5),\r\n\t\t};\r\n\t}\r\n\telse\r\n\t{\r\n\t\t// Stationary/slow target - faster missile with light homing for accuracy\r\n\t\treturn {\r\n\t\t\tdamage: 10,\r\n\t\t\tspeed: 6,\r\n\t\t\tturnRate: 1, // Light homing to correct any aiming errors\r\n\t\t\tduration: 180,\r\n\t\t};\r\n\t}\r\n}\r\n\r\n/**\r\n * Given a cast-time budget (in ticks) and a target distance, find the best\r\n * missile config that fits. Maximizes damage while ensuring the missile\r\n * can reach the target and finishes casting in time.\r\n *\r\n * Returns null if no useful missile fits in the budget.\r\n *\r\n * How it works: tries several speed/turnRate templates. For each, calculates\r\n * the minimum duration to reach `distance`, then solves the cast-time formula\r\n * for the maximum damage that fits within `budgetTicks`.\r\n *\r\n * If `lastMissileConfig` is provided, accounts for warmup bonus: similar\r\n * missiles cast faster, so more damage can fit in the same budget.\r\n */\r\nexport function fitMissileToBudget(\r\n\tbudgetTicks: number,\r\n\tdistance: number,\r\n\toptions?: {minTurnRate?: number; maxDamage?: number; lastMissileConfig?: MissileConfig},\r\n): MissileConfig | null\r\n{\r\n\tconst budgetSec = budgetTicks / TICKS_PER_SECOND;\r\n\tconst minTurnRate = options?.minTurnRate ?? 0;\r\n\tconst maxDamage = options?.maxDamage ?? WIZARD_HEALTH;\r\n\t// Warmup is active when caller explicitly passes lastMissileConfig in options.\r\n\t// Value can be undefined (first cast = full warmup) or MissileConfig (similarity-based).\r\n\t// When key is absent (old callers), no warmup is applied.\r\n\tconst warmupActive = options !== undefined && 'lastMissileConfig' in options;\r\n\tconst lastMissile = options?.lastMissileConfig;\r\n\r\n\t// Minimum useful cast time is 10 ticks (0.1s)\r\n\tif (budgetSec < MISSILE_MIN_CAST_TIME)\r\n\t{\r\n\t\treturn null;\r\n\t}\r\n\r\n\t// Templates: [speed, turnRate] combos to try, ordered by preference.\r\n\t// Lower turnRate = cheaper cast. Higher speed = shorter duration needed.\r\n\t// Negative turnRate = anti-homing (turns away from target) but cheaper cast time.\r\n\tconst templates: [number, number][] = [\r\n\t\t[7, -1], // Fast anti-homing: cheapest cast, curves away from target\r\n\t\t[7, -0.5],// Fast light anti-homing\r\n\t\t[7, 0], // Fast straight: cheapest cast, only hits stationary\r\n\t\t[5, 0], // Medium straight\r\n\t\t[6, 0.5], // Fast with light tracking\r\n\t\t[5, 1], // Medium with tracking\r\n\t\t[4, 1], // Slow with tracking (long range)\r\n\t\t[4, 1.5], // Slow with strong tracking\r\n\t\t[3, 2], // Very slow with heavy tracking (for aggressive homing)\r\n\t];\r\n\r\n\tlet best: MissileConfig | null = null;\r\n\tlet bestDamage = 0;\r\n\r\n\tfor (const [speed, turnRate] of templates)\r\n\t{\r\n\t\tif (turnRate < minTurnRate) continue;\r\n\t\t// Minimum duration to reach target (with 20% margin for homing curve)\r\n\t\tconst margin = turnRate > 0 ? 1.2 : 1.05;\r\n\t\tconst minDurationTicks = Math.max(\r\n\t\t\tMISSILE_MIN_DURATION,\r\n\t\t\tMath.ceil(distance * margin / speed),\r\n\t\t);\r\n\t\tconst durationSec = minDurationTicks / TICKS_PER_SECOND;\r\n\r\n\t\t// Solve cast formula for damage:\r\n\t\t// budgetSec = BASE + SCALE * d^POWER + delivery costs\r\n\t\t// → d = ((budgetSec - fixedCost) / SCALE)^(1/POWER)\r\n\t\tconst turnCost = effectiveTurnRateCost(turnRate);\r\n\t\tconst fixedCost = MISSILE_BASE_CAST\r\n\t\t\t+ MISSILE_HOMING_COEFF * turnCost\r\n\t\t\t+ MISSILE_TURN_DURATION_COEFF * (turnCost * durationSec)\r\n\t\t\t+ MISSILE_SPEED_DURATION_COEFF * (speed * durationSec - MISSILE_SPEED_DURATION_BASELINE);\r\n\r\n\t\t// Estimate warmup multiplier for this template (depends on speed/turnRate/duration,\r\n\t\t// not damage, so we can estimate before solving for damage)\r\n\t\tlet effectiveBudgetSec = budgetSec;\r\n\t\tif (warmupActive && lastMissile)\r\n\t\t{\r\n\t\t\tconst probe: MissileConfig = {damage: lastMissile.damage, speed, turnRate, duration: minDurationTicks};\r\n\t\t\tconst multiplier = calculateWarmupMultiplier(lastMissile, probe);\r\n\t\t\t// multiplier < 1 = faster (more budget), > 1 = slower (less budget)\r\n\t\t\teffectiveBudgetSec = budgetSec / multiplier;\r\n\t\t}\r\n\r\n\t\tconst remaining = effectiveBudgetSec - fixedCost;\r\n\t\tif (remaining <= 0)\r\n\t\t{\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tconst maxDamageFloat = Math.pow(remaining / MISSILE_DAMAGE_SCALE, 1 / MISSILE_DAMAGE_POWER);\r\n\t\tconst damage = Math.min(maxDamage, Math.floor(maxDamageFloat));\r\n\r\n\t\tif (damage < MISSILE_MIN_DAMAGE)\r\n\t\t{\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\t// Verify actual cast time with real warmup (using final damage)\r\n\t\tconst candidate: MissileConfig = {damage, speed, turnRate, duration: minDurationTicks};\r\n\t\tconst actualCastSec = (warmupActive && lastMissile)\r\n\t\t\t? calculateMissileCastTime(candidate, lastMissile)\r\n\t\t\t: calculateMissileCastTime(candidate);\r\n\t\tconst actualCastTicks = Math.max(1, Math.ceil(actualCastSec * TICKS_PER_SECOND));\r\n\r\n\t\tif (actualCastTicks > budgetTicks)\r\n\t\t{\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\tif (damage > bestDamage)\r\n\t\t{\r\n\t\t\tbestDamage = damage;\r\n\t\t\tbest = candidate;\r\n\t\t}\r\n\t}\r\n\r\n\treturn best;\r\n}\r\n","import {WizardFunction} from '../../types.js';\n\n/**\n * Bot: TargetDummy\n *\n * BEHAVIOR: Does absolutely nothing. No movement, no spells, no AI.\n *\n * NAMING RATIONALE: \"Target Dummy\" is universal MMO player vocabulary for the\n * practice objects found in capital cities. Every WoW/FFXIV player has beaten\n * on a target dummy to test DPS rotations. That's exactly what this bot is —\n * a punching bag for testing missile mechanics and baseline damage output.\n * Nobody calls them \"training dummies\"; the player term is always \"target dummy.\"\n *\n * STANDALONE — no tier progression. It's a test fixture, not a combatant.\n */\nexport const TargetDummy: WizardFunction = (_props) =>\n{\n\treturn {\n\t\tmove: {x: 0, y: 0},\n\t};\n};\n","import {WizardFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\n\n/**\n * Bot: Rookie\n *\n * BEHAVIOR: Stands perfectly still and fires straight (non-homing) missiles at\n * the enemy. No movement, no dodging, no shielding. Knows one spell and uses\n * it on cooldown. The wizard equivalent of an FPS player who stands in the open\n * and holds left-click.\n *\n * NAMING RATIONALE: \"Rookie\" is the universal term for a first-timer who barely\n * knows what they're doing. This bot is a day-one player who learned how to cast\n * missile and nothing else. No movement, no defense, just raw \"I press the button.\"\n * We considered \"Noob\" (more accurate) but Rookie is less abrasive while conveying\n * the same thing — a beginner who doesn't know any better.\n *\n * STANDALONE — no tier progression. Rookies either learn to play a real class\n * or quit. This bot represents the rock-bottom of \"at least it shoots.\"\n */\nexport const Rookie: WizardFunction = ({state}) => \n{\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tif (state.state === 'idle') \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {\n\t\t\t\tspell: 'missile',\n\t\t\t\tconfig: {\n\t\t\t\t\tdamage: 10,\n\t\t\t\t\tspeed: 5,\n\t\t\t\t\tturnRate: 0,\n\t\t\t\t\tduration: 200,\n\t\t\t\t},\n\t\t\t\tmissileAI: () => ({}),\n\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t},\n\t\t};\n\t}\n\n\treturn {\n\t\tmove: {x: 0, y: 0},\n\t};\n};\n","import {WizardFunction} from '../../types.js';\nimport {ARENA_WIDTH, ARENA_HEIGHT} from '../../rules.js';\n\n/**\n * Bot: Critter\n *\n * BEHAVIOR: Picks random valid actions each tick — random movement, random spells,\n * random missile configs, random directions. Occasionally cancels its own casts.\n * Uses engine-provided seeded random for deterministic behavior. Useful for\n * finding edge cases in the engine, but completely useless in combat.\n *\n * NAMING RATIONALE: In WoW, critters are the 1-HP ambient mobs (rabbits, squirrels,\n * prairie dogs) that wander around doing nothing useful and die to literally anything.\n * This bot is the wizard equivalent — it flails around randomly and gets destroyed by\n * anyone with a plan. The word \"Critter\" immediately tells any gamer \"this thing is\n * helpless and exists only to fill space.\"\n *\n * STANDALONE — no tier progression. Critters don't level up. However, a future\n * \"Hogger\" bot could be an elite critter: same chaotic spirit but actually dangerous\n * (like the famous WoW elite that wipes unprepared lowbies).\n */\nexport const Critter: WizardFunction = ({state, random}) => \n{\n\t// Use the engine-provided random function (seeded, isolated, deterministic)\n\t// World-space movement: random x/y in [-100, 100] range\n\tconst move = {\n\t\tx: random() * 200 - 100, // -100 to 100\n\t\ty: random() * 200 - 100, // -100 to 100\n\t};\n\n\tif (state.state === 'idle' && random() > 0.95) \n\t{\n\t\tconst spellRoll = random();\n\t\tif (spellRoll > 0.6) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: {\n\t\t\t\t\t\tdamage: Math.floor(random() * 20) + 5,\n\t\t\t\t\t\tspeed: random() * 5 + 2,\n\t\t\t\t\t\tturnRate: random() * 3,\n\t\t\t\t\t\tduration: Math.floor(random() * 200) + 100,\n\t\t\t\t\t},\n\t\t\t\t\t// Missile AI gets its own random from engine - use it for random targeting\n\t\t\t\t\tmissileAI: ({random: missileRandom}) => ({\n\t\t\t\t\t\tturnToward:\n missileRandom() > 0.5\n \t? {\n \t\tx: missileRandom() * ARENA_WIDTH,\n \t\ty: missileRandom() * ARENA_HEIGHT,\n \t}\n \t: undefined,\n\t\t\t\t\t}),\n\t\t\t\t\tdirection: random() * 360,\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t\telse if (spellRoll > 0.3) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t\telse if (state.blinkCooldown === 0) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'blink',\n\t\t\t\t\ttarget: {x: random() * ARENA_WIDTH, y: random() * ARENA_HEIGHT},\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\tif (\n\t\t(state.state === 'casting' || state.state === 'channeling') &&\n random() > 0.98\n\t) \n\t{\n\t\treturn {move, cancel: true};\n\t}\n\n\t// Override auto-aim with random direction — Critter doesn't know where anything is\n\treturn {move, aimDirection: random() * 360};\n};\n","/**\r\n * VIBEMANCER - THREAT ANALYSIS\r\n *\r\n * Pre-computes threat information for incoming projectiles.\r\n * This handles the \"subconscious\" perception of missile trajectories.\r\n */\r\n\r\nimport {Position, ProjectileState} from '../types.js';\r\nimport {\r\n\tCOLLISION_RADIUS,\r\n\tMISSILE_BASE_RADIUS,\r\n\tMISSILE_RADIUS_PER_DAMAGE,\r\n\tMOVE_SPEED,\r\n\tSHIELD_CAST_TIME,\r\n\tARENA_WIDTH,\r\n\tARENA_HEIGHT,\r\n\tARENA_WATER_BUFFER,\r\n} from '../rules.js';\r\nimport {distanceTo} from '../utils/distance.js';\r\nimport {angleTo, normalizeAngle, angleDiff} from '../utils/angles.js';\r\nimport type {AnalyzedThreat} from './types.js';\r\n\r\n// Maximum distance at which a missile is still tracked as a potential threat\r\n// (even if simulation says it will miss — homing missiles can change course)\r\nconst THREAT_RELEVANCE_DISTANCE = 500;\r\n\r\n/**\r\n * Analyze all threats from enemy projectiles.\r\n *\r\n * @param myPos - Current position of the wizard\r\n * @param projectiles - All projectiles in the game\r\n * @param myProjectiles - Only the bot's own projectiles (used for filtering)\r\n * @param ticksUntilReady - Ticks until wizard can start a new action\r\n * @returns Array of analyzed threats sorted by ticksToImpact (soonest first)\r\n */\r\nexport function analyzeThreats(\r\n\tmyPos: Position,\r\n\tprojectiles: ProjectileState[],\r\n\tmyProjectiles: ProjectileState[],\r\n\tticksUntilReady: number,\r\n): AnalyzedThreat[]\r\n{\r\n\t// Filter to enemy projectiles by excluding our own\r\n\tconst myProjectileIds = new Set(myProjectiles.map((p) => p.id));\r\n\tconst enemyProjectiles = projectiles.filter((p) => !myProjectileIds.has(p.id));\r\n\r\n\t// Analyze each projectile\r\n\tconst threats: AnalyzedThreat[] = [];\r\n\r\n\tfor (const projectile of enemyProjectiles)\r\n\t{\r\n\t\tconst analysis = analyzeOneThreat(myPos, projectile, ticksUntilReady);\r\n\t\t// Only include if it will hit or missile is within reasonable distance\r\n\t\tconst missileDistance = Math.sqrt(\r\n\t\t\t(projectile.position.x - myPos.x) ** 2 +\r\n\t\t\t(projectile.position.y - myPos.y) ** 2,\r\n\t\t);\r\n\t\tif (analysis.willHit || missileDistance < THREAT_RELEVANCE_DISTANCE)\r\n\t\t{\r\n\t\t\tthreats.push(analysis);\r\n\t\t}\r\n\t}\r\n\r\n\t// Sort by ticksToImpact (soonest first)\r\n\tthreats.sort((a, b) => a.ticksToImpact - b.ticksToImpact);\r\n\r\n\treturn threats;\r\n}\r\n\r\n/**\r\n * Analyze a single projectile threat.\r\n */\r\nfunction analyzeOneThreat(\r\n\ttargetPos: Position,\r\n\tprojectile: ProjectileState,\r\n\tticksUntilReady: number,\r\n): AnalyzedThreat\r\n{\r\n\t// Calculate collision radius\r\n\tconst missileRadius = MISSILE_BASE_RADIUS + projectile.damage * MISSILE_RADIUS_PER_DAMAGE;\r\n\tconst collisionDist = COLLISION_RADIUS + missileRadius;\r\n\r\n\t// Dodge simulation uses a larger collision distance to account for swept circle collision.\r\n\t// The real game checks the entire missile path each tick (swept), but the simulation only\r\n\t// checks endpoints. Adding half the missile speed compensates for this gap.\r\n\tconst dodgeCollisionDist = collisionDist + projectile.speed * 0.5;\r\n\r\n\t// Simulate missile trajectory toward stationary target\r\n\tconst {willHit, ticksToImpact} = simulateMissileToTarget(\r\n\t\tprojectile,\r\n\t\ttargetPos,\r\n\t\tcollisionDist,\r\n\t);\r\n\r\n\t// Only simulate dodges for missiles that will actually hit (saves ~75% simulation work)\r\n\tlet canDodgeLeft = true;\r\n\tlet canDodgeRight = true;\r\n\tlet canOutrun = true;\r\n\tlet bestDodgeDirection: Position | null = null;\r\n\r\n\tif (willHit)\r\n\t{\r\n\t\tcanDodgeLeft = simulateDodge(projectile, targetPos, 'left', dodgeCollisionDist);\r\n\t\tcanDodgeRight = simulateDodge(projectile, targetPos, 'right', dodgeCollisionDist);\r\n\t\tcanOutrun = simulateDodge(projectile, targetPos, 'away', dodgeCollisionDist);\r\n\r\n\t\tbestDodgeDirection = calculateBestDodgeDirection(\r\n\t\t\tprojectile,\r\n\t\t\ttargetPos,\r\n\t\t\tcanDodgeLeft,\r\n\t\t\tcanDodgeRight,\r\n\t\t\tcanOutrun,\r\n\t\t);\r\n\t}\r\n\r\n\t// Calculate shield timing\r\n\t// Can block if: ticksUntilReady + shield cast time < ticksToImpact\r\n\tconst canBlockInTime = ticksToImpact > ticksUntilReady + SHIELD_CAST_TIME + 1;\r\n\r\n\t// When to start casting shield (leave 1 tick buffer)\r\n\tconst ticksToStartShield = Math.max(0, ticksToImpact - SHIELD_CAST_TIME - 1);\r\n\r\n\treturn {\r\n\t\tid: projectile.id,\r\n\t\tprojectile,\r\n\t\tticksToImpact,\r\n\t\twillHit,\r\n\t\tcanDodgeLeft,\r\n\t\tcanDodgeRight,\r\n\t\tcanOutrun,\r\n\t\tbestDodgeDirection,\r\n\t\tcanBlockInTime,\r\n\t\tticksToStartShield,\r\n\t};\r\n}\r\n\r\n/**\r\n * Simulate a missile moving toward a stationary target.\r\n * Accounts for homing (turn rate) and expiration.\r\n */\r\nfunction simulateMissileToTarget(\r\n\tprojectile: ProjectileState,\r\n\ttargetPos: Position,\r\n\tcollisionDist: number,\r\n): {willHit: boolean; ticksToImpact: number}\r\n{\r\n\tlet pos = {...projectile.position};\r\n\tlet rotation = projectile.rotation;\r\n\tconst maxTicks = projectile.remainingTicks;\r\n\tconst speed = projectile.speed;\r\n\tconst turnRate = projectile.turnRate;\r\n\r\n\tfor (let tick = 1; tick <= maxTicks; tick++)\r\n\t{\r\n\t\t// Apply homing (turn toward target)\r\n\t\tif (turnRate > 0)\r\n\t\t{\r\n\t\t\tconst desiredAngle = angleTo(pos, targetPos);\r\n\t\t\tconst diff = angleDiff(rotation, desiredAngle);\r\n\t\t\tconst maxTurn = turnRate;\r\n\r\n\t\t\tif (Math.abs(diff) <= maxTurn)\r\n\t\t\t{\r\n\t\t\t\trotation = desiredAngle;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\trotation = normalizeAngle(rotation + Math.sign(diff) * maxTurn);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Move forward\r\n\t\tconst rad = rotation * (Math.PI / 180);\r\n\t\tpos = {\r\n\t\t\tx: pos.x + Math.cos(rad) * speed,\r\n\t\t\ty: pos.y + Math.sin(rad) * speed,\r\n\t\t};\r\n\r\n\t\t// Check if missile is out of bounds (with water buffer)\r\n\t\tif (isOutOfBounds(pos))\r\n\t\t{\r\n\t\t\treturn {willHit: false, ticksToImpact: Infinity};\r\n\t\t}\r\n\r\n\t\t// Check collision\r\n\t\tconst dist = distanceTo(pos, targetPos);\r\n\t\tif (dist <= collisionDist)\r\n\t\t{\r\n\t\t\treturn {willHit: true, ticksToImpact: tick};\r\n\t\t}\r\n\t}\r\n\r\n\t// Missile expired without hitting\r\n\treturn {willHit: false, ticksToImpact: Infinity};\r\n}\r\n\r\n/**\r\n * Simulate a dodge attempt.\r\n * Returns true if the dodge avoids the missile.\r\n */\r\nfunction simulateDodge(\r\n\tprojectile: ProjectileState,\r\n\tstartPos: Position,\r\n\tdirection: 'left' | 'right' | 'away',\r\n\tcollisionDist: number,\r\n): boolean\r\n{\r\n\t// Calculate dodge direction vector\r\n\tconst dodgeDir = getDodgeDirection(projectile, startPos, direction);\r\n\r\n\t// If we can't determine a dodge direction, fail\r\n\tif (dodgeDir.x === 0 && dodgeDir.y === 0)\r\n\t{\r\n\t\treturn false;\r\n\t}\r\n\r\n\tlet targetPos = {...startPos};\r\n\tlet missilePos = {...projectile.position};\r\n\tlet missileRotation = projectile.rotation;\r\n\tconst maxTicks = projectile.remainingTicks;\r\n\tconst speed = projectile.speed;\r\n\tconst turnRate = projectile.turnRate;\r\n\r\n\tfor (let tick = 1; tick <= maxTicks; tick++)\r\n\t{\r\n\t\t// Move target\r\n\t\tconst newX = targetPos.x + dodgeDir.x * MOVE_SPEED;\r\n\t\tconst newY = targetPos.y + dodgeDir.y * MOVE_SPEED;\r\n\r\n\t\t// Clamp to arena bounds\r\n\t\ttargetPos = {\r\n\t\t\tx: Math.max(COLLISION_RADIUS, Math.min(ARENA_WIDTH - COLLISION_RADIUS, newX)),\r\n\t\t\ty: Math.max(COLLISION_RADIUS, Math.min(ARENA_HEIGHT - COLLISION_RADIUS, newY)),\r\n\t\t};\r\n\r\n\t\t// Apply missile homing toward new target position\r\n\t\tif (turnRate > 0)\r\n\t\t{\r\n\t\t\tconst desiredAngle = angleTo(missilePos, targetPos);\r\n\t\t\tconst diff = angleDiff(missileRotation, desiredAngle);\r\n\t\t\tconst maxTurn = turnRate;\r\n\r\n\t\t\tif (Math.abs(diff) <= maxTurn)\r\n\t\t\t{\r\n\t\t\t\tmissileRotation = desiredAngle;\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tmissileRotation = normalizeAngle(missileRotation + Math.sign(diff) * maxTurn);\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// Move missile\r\n\t\tconst rad = missileRotation * (Math.PI / 180);\r\n\t\tmissilePos = {\r\n\t\t\tx: missilePos.x + Math.cos(rad) * speed,\r\n\t\t\ty: missilePos.y + Math.sin(rad) * speed,\r\n\t\t};\r\n\r\n\t\t// Check if missile is out of bounds\r\n\t\tif (isOutOfBounds(missilePos))\r\n\t\t{\r\n\t\t\treturn true; // Dodged - missile left arena\r\n\t\t}\r\n\r\n\t\t// Check collision\r\n\t\tconst dist = distanceTo(missilePos, targetPos);\r\n\t\tif (dist <= collisionDist)\r\n\t\t{\r\n\t\t\treturn false; // Got hit while dodging\r\n\t\t}\r\n\t}\r\n\r\n\t// Missile expired without hitting\r\n\treturn true;\r\n}\r\n\r\n/**\r\n * Get the dodge direction vector based on direction type.\r\n */\r\nfunction getDodgeDirection(\r\n\tprojectile: ProjectileState,\r\n\ttargetPos: Position,\r\n\tdirection: 'left' | 'right' | 'away',\r\n): Position\r\n{\r\n\t// Get missile direction vector\r\n\tconst missileAngleRad = projectile.rotation * (Math.PI / 180);\r\n\tconst missileDir = {\r\n\t\tx: Math.cos(missileAngleRad),\r\n\t\ty: Math.sin(missileAngleRad),\r\n\t};\r\n\r\n\tswitch (direction)\r\n\t{\r\n\t\tcase 'left':\r\n\t\t\t// Perpendicular to missile direction (left = counter-clockwise)\r\n\t\t\treturn {x: missileDir.y, y: -missileDir.x};\r\n\r\n\t\tcase 'right':\r\n\t\t\t// Perpendicular to missile direction (right = clockwise)\r\n\t\t\treturn {x: -missileDir.y, y: missileDir.x};\r\n\r\n\t\tcase 'away':\r\n\t\t{\r\n\t\t\t// Away from missile current position\r\n\t\t\tconst dx = targetPos.x - projectile.position.x;\r\n\t\t\tconst dy = targetPos.y - projectile.position.y;\r\n\t\t\tconst len = Math.sqrt(dx * dx + dy * dy);\r\n\t\t\tif (len === 0) return {x: 0, y: 0};\r\n\t\t\treturn {x: dx / len, y: dy / len};\r\n\t\t}\r\n\r\n\t\tdefault:\r\n\t\t\treturn {x: 0, y: 0};\r\n\t}\r\n}\r\n\r\n/**\r\n * Calculate the best dodge direction based on available options.\r\n */\r\nfunction calculateBestDodgeDirection(\r\n\tprojectile: ProjectileState,\r\n\ttargetPos: Position,\r\n\tcanDodgeLeft: boolean,\r\n\tcanDodgeRight: boolean,\r\n\tcanOutrun: boolean,\r\n): Position | null\r\n{\r\n\t// Prefer sidestep over outrunning (faster result)\r\n\tif (canDodgeLeft && canDodgeRight)\r\n\t{\r\n\t\t// Both sides work - pick the one closer to where we want to be\r\n\t\t// For simplicity, just return left\r\n\t\treturn getDodgeDirection(projectile, targetPos, 'left');\r\n\t}\r\n\telse if (canDodgeLeft)\r\n\t{\r\n\t\treturn getDodgeDirection(projectile, targetPos, 'left');\r\n\t}\r\n\telse if (canDodgeRight)\r\n\t{\r\n\t\treturn getDodgeDirection(projectile, targetPos, 'right');\r\n\t}\r\n\telse if (canOutrun)\r\n\t{\r\n\t\treturn getDodgeDirection(projectile, targetPos, 'away');\r\n\t}\r\n\r\n\t// No dodge available\r\n\treturn null;\r\n}\r\n\r\n/**\r\n * Check if a position is out of bounds (past the water buffer).\r\n */\r\nfunction isOutOfBounds(pos: Position): boolean\r\n{\r\n\treturn (\r\n\t\tpos.x < -ARENA_WATER_BUFFER ||\r\n\t\tpos.x > ARENA_WIDTH + ARENA_WATER_BUFFER ||\r\n\t\tpos.y < -ARENA_WATER_BUFFER ||\r\n\t\tpos.y > ARENA_HEIGHT + ARENA_WATER_BUFFER\r\n\t);\r\n}\r\n","/**\r\n * VIBEMANCER - SHARED BOT UTILITIES\r\n *\r\n * Common helpers used by multiple bots for threat analysis and defense.\r\n */\r\n\r\nimport {GameState, Position, ProjectileState} from '../types.js';\r\nimport {analyzeThreats} from '../hooks/threat-analysis.js';\r\nimport type {AnalyzedThreat} from '../hooks/types.js';\r\nimport {distanceTo} from '../utils/distance.js';\r\nimport {ARENA_SIZE, BLINK_RANGE, GCD_DURATION, SHIELD_CAST_TIME, WIZARD_RADIUS} from '../rules.js';\r\n\r\n// Re-export public utility so bots can import from shared\r\nexport {fitMissileToBudget} from '../utils/combat.js';\r\n\r\n// T2 bots: max cast time when no threats are incoming (~2s)\r\nexport const MAX_CAST_BUDGET = 200;\r\n\r\n// Minimum useful damage — d=1 to d=4 missiles waste cast time for negligible output.\r\n// When budget only allows this, fall back to firing a bigger missile and accept the hit.\r\nexport const MIN_USEFUL_DAMAGE = 5;\r\n\r\n/**\r\n * Compute how many ticks until the wizard can start a new action.\r\n */\r\nexport function computeTicksUntilReady(state: GameState): number\r\n{\r\n\tswitch (state.state)\r\n\t{\r\n\t\tcase 'idle':\r\n\t\t\treturn 0;\r\n\t\tcase 'casting':\r\n\t\t\treturn (state.castDuration ?? 0) - (state.castProgress ?? 0);\r\n\t\tcase 'channeling':\r\n\t\t\treturn 0; // can cancel instantly\r\n\t\tcase 'gcd_locked':\r\n\t\t\treturn state.gcdRemaining ?? 0;\r\n\t\tdefault:\r\n\t\t\treturn 0;\r\n\t}\r\n}\r\n\r\n/**\r\n * Get all threats from enemy projectiles using the simulation-based system.\r\n */\r\nexport function getThreats(state: GameState): AnalyzedThreat[]\r\n{\r\n\tconst ticksUntilReady = computeTicksUntilReady(state);\r\n\treturn analyzeThreats(state.position, state.projectiles, state.myProjectiles, ticksUntilReady);\r\n}\r\n\r\n/**\r\n * Get the most urgent threat that will actually hit us.\r\n * Returns null if no threats will hit.\r\n */\r\nexport function getMostUrgentThreat(threats: AnalyzedThreat[]): AnalyzedThreat | null\r\n{\r\n\t// Threats are already sorted by ticksToImpact (soonest first)\r\n\treturn threats.find((t) => t.willHit) ?? null;\r\n}\r\n\r\n/**\r\n * Determine if a threat should force a shield even when a dodge direction exists.\r\n * High-damage homing missiles are too risky to dodge — the missile tracks the wizard\r\n * and the dodge often fails. Shield instead to guarantee survival.\r\n */\r\nexport function shouldForceShield(threat: AnalyzedThreat): boolean\r\n{\r\n\treturn threat.projectile.damage >= 25 && threat.projectile.turnRate > 0;\r\n}\r\n\r\n/**\r\n * Get a dodge direction for movement, even if no threat will directly hit.\r\n * Homing missiles continuously adjust course, so even missiles the simulation\r\n * says will \"miss\" should be dodged if they're close.\r\n *\r\n * Uses the missile's heading to determine which side it will pass the wizard,\r\n * then dodges perpendicular to the heading, AWAY from the approach side.\r\n * When the missile is heading dead-center at us (ambiguous), uses wall\r\n * avoidance as a stable tiebreaker.\r\n */\r\nexport function getDodgeDirectionForMovement(\r\n\tthreats: AnalyzedThreat[],\r\n\tmyPos: Position,\r\n): Position | null\r\n{\r\n\t// Only dodge missiles the simulation predicts will hit\r\n\tconst target = threats.find((t) => t.willHit) ?? null;\r\n\r\n\tif (!target) return null;\r\n\r\n\t// Missile heading direction (from its rotation)\r\n\tconst rad = target.projectile.rotation * (Math.PI / 180);\r\n\tconst missileDir = {x: Math.cos(rad), y: Math.sin(rad)};\r\n\r\n\t// Vector from missile to wizard\r\n\tconst toWizard = {\r\n\t\tx: myPos.x - target.projectile.position.x,\r\n\t\ty: myPos.y - target.projectile.position.y,\r\n\t};\r\n\tconst len = Math.sqrt(toWizard.x * toWizard.x + toWizard.y * toWizard.y);\r\n\tif (len < 0.01) return null;\r\n\r\n\t// Cross product: which side of the missile's path is the wizard on?\r\n\t// cross > 0 → wizard is clockwise from heading → missile passes CCW → dodge CW (away)\r\n\t// cross < 0 → wizard is counter-clockwise → missile passes CW → dodge CCW (away)\r\n\tconst cross = missileDir.x * toWizard.y - missileDir.y * toWizard.x;\r\n\r\n\t// Perpendicular options to missile heading\r\n\tconst dodgeCW = {x: missileDir.y, y: -missileDir.x};\r\n\tconst dodgeCCW = {x: -missileDir.y, y: missileDir.x};\r\n\r\n\t// Deadzone: missile heading nearly straight at us (~8° cone).\r\n\t// Both sides are equally good, so use a stable tiebreaker.\r\n\tconst DEADZONE = 0.15 * len;\r\n\r\n\tif (cross > DEADZONE)\r\n\t{\r\n\t\treturn dodgeCCW;\r\n\t}\r\n\tif (cross < -DEADZONE)\r\n\t{\r\n\t\treturn dodgeCW;\r\n\t}\r\n\r\n\t// Tiebreaker for dead-center missiles: pick the side further from walls\r\n\tconst futureA = {x: myPos.x + dodgeCCW.x * 100, y: myPos.y + dodgeCCW.y * 100};\r\n\tconst futureB = {x: myPos.x + dodgeCW.x * 100, y: myPos.y + dodgeCW.y * 100};\r\n\tconst scoreA = Math.min(futureA.x, ARENA_SIZE - futureA.x, futureA.y, ARENA_SIZE - futureA.y);\r\n\tconst scoreB = Math.min(futureB.x, ARENA_SIZE - futureB.x, futureB.y, ARENA_SIZE - futureB.y);\r\n\r\n\treturn scoreA >= scoreB ? dodgeCCW : dodgeCW;\r\n}\r\n\r\n/**\r\n * Check if it's safe to start an attack (no imminent undodgeable threats).\r\n *\r\n * @param threats - Analyzed threats\r\n * @param castTicks - How many ticks the attack cast will take\r\n * @param gcdTicks - GCD ticks after cast (default 100)\r\n * @param shieldCastTicks - Shield cast time (default 20)\r\n */\r\nexport function isSafeToAttack(\r\n\tthreats: AnalyzedThreat[],\r\n\tcastTicks: number,\r\n\tgcdTicks: number = GCD_DURATION,\r\n\tshieldCastTicks: number = SHIELD_CAST_TIME,\r\n): boolean\r\n{\r\n\t// No threats = safe\r\n\tconst hittingThreats = threats.filter((t) => t.willHit);\r\n\r\n\tfor (const threat of hittingThreats)\r\n\t{\r\n\t\tif (threat.bestDodgeDirection !== null)\r\n\t\t{\r\n\t\t\t// Homing missiles track during GCD, but the wizard moves at full speed\r\n\t\t\t// during GCD and can dodge normally. Only the cast (half speed) plus a\r\n\t\t\t// transition buffer is truly dangerous for dodgeable homing missiles.\r\n\t\t\t// Non-homing missiles only need dodging during the cast itself.\r\n\t\t\tif (threat.projectile.turnRate > 0)\r\n\t\t\t{\r\n\t\t\t\tif (threat.ticksToImpact < castTicks + HOMING_DODGE_BUFFER)\r\n\t\t\t\t{\r\n\t\t\t\t\treturn false;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\telse if (threat.ticksToImpact < castTicks)\r\n\t\t\t{\r\n\t\t\t\treturn false;\r\n\t\t\t}\r\n\t\t\tcontinue;\r\n\t\t}\r\n\r\n\t\t// Undodgeable threat: we need time to finish cast + GCD + shield cast\r\n\t\tconst timeNeeded = castTicks + gcdTicks + shieldCastTicks + 1;\r\n\t\tif (threat.ticksToImpact < timeNeeded)\r\n\t\t{\r\n\t\t\treturn false;\r\n\t\t}\r\n\t}\r\n\r\n\treturn true;\r\n}\r\n\r\n/**\r\n * Calculate a blink target toward arena center (for emergency escapes).\r\n */\r\nexport function getBlinkToCenter(pos: Position): Position\r\n{\r\n\tconst centerX = ARENA_SIZE / 2;\r\n\tconst centerY = ARENA_SIZE / 2;\r\n\tconst dx = centerX - pos.x;\r\n\tconst dy = centerY - pos.y;\r\n\tconst dist = Math.sqrt(dx * dx + dy * dy);\r\n\r\n\tif (dist === 0) return {x: centerX, y: centerY};\r\n\r\n\treturn {\r\n\t\tx: pos.x + (dx / dist) * BLINK_RANGE,\r\n\t\ty: pos.y + (dy / dist) * BLINK_RANGE,\r\n\t};\r\n}\r\n\r\n// ============================================================================\r\n// BLINK DISTANCE FUNCTIONS\r\n// ============================================================================\r\n\r\n// Post-blink vulnerability: shield cast time before wizard can defend (blink has no GCD)\r\nconst POST_BLINK_VULNERABILITY = SHIELD_CAST_TIME;\r\n\r\n// Approximate hit radius for missile safety (wizard radius + typical missile radius)\r\nconst HIT_RADIUS = WIZARD_RADIUS + 3;\r\n\r\n// Homing missiles need extra buffer beyond cast time because they track during GCD\r\nconst HOMING_DODGE_BUFFER = 20;\r\n\r\n// Minimum wall margin when evaluating blink targets (prevents blinking into walls)\r\nconst BLINK_WALL_MARGIN = 20;\r\n\r\n// Minimum post-blink distance from enemy for escape blinks to be worthwhile\r\nexport const MIN_BLINK_ESCAPE_DISTANCE = 250;\r\n\r\n// Scoring constants\r\nconst ANGLE_CANDIDATES = 24; // every 15°\r\nconst DISTANCE_FRACTIONS = [1.0, 0.7, 0.4];\r\n\r\n\r\n/**\r\n * Find the best blink target to increase distance from an enemy.\r\n * Avoids positions where missiles could arrive during the post-blink GCD window.\r\n * Returns null if minResultDistance can't be achieved.\r\n */\r\nexport function getBlinkToIncreaseDistance(\r\n\tmyPos: Position,\r\n\tenemyPos: Position,\r\n\tprojectiles: ProjectileState[],\r\n\tminResultDistance?: number,\r\n): Position | null\r\n{\r\n\treturn findBestBlinkTarget(myPos, enemyPos, projectiles, 'increase', minResultDistance);\r\n}\r\n\r\n/**\r\n * Find the best blink target to decrease distance to an enemy.\r\n * Avoids positions where missiles could arrive during the post-blink GCD window.\r\n */\r\nexport function getBlinkToDecreaseDistance(\r\n\tmyPos: Position,\r\n\tenemyPos: Position,\r\n\tprojectiles: ProjectileState[],\r\n): Position | null\r\n{\r\n\treturn findBestBlinkTarget(myPos, enemyPos, projectiles, 'decrease');\r\n}\r\n\r\n/**\r\n * Core blink target finder. Evaluates candidates across multiple angles and distances,\r\n * scoring by distance goal + wall safety + missile avoidance.\r\n */\r\nfunction findBestBlinkTarget(\r\n\tmyPos: Position,\r\n\tenemyPos: Position,\r\n\tprojectiles: ProjectileState[],\r\n\tgoal: 'increase' | 'decrease',\r\n\tminResultDistance?: number,\r\n): Position | null\r\n{\r\n\tconst currentDist = distanceTo(myPos, enemyPos);\r\n\r\n\t// Base direction: away from enemy (increase) or toward enemy (decrease)\r\n\tlet baseAngle: number;\r\n\tif (currentDist < 0.01)\r\n\t{\r\n\t\t// Overlapping: fallback to toward center\r\n\t\tbaseAngle = Math.atan2(ARENA_SIZE / 2 - myPos.y, ARENA_SIZE / 2 - myPos.x);\r\n\t}\r\n\telse\r\n\t{\r\n\t\tconst dx = goal === 'increase'\r\n\t\t\t? myPos.x - enemyPos.x\r\n\t\t\t: enemyPos.x - myPos.x;\r\n\t\tconst dy = goal === 'increase'\r\n\t\t\t? myPos.y - enemyPos.y\r\n\t\t\t: enemyPos.y - myPos.y;\r\n\t\tbaseAngle = Math.atan2(dy, dx);\r\n\t}\r\n\r\n\t// For decrease: don't overshoot past enemy\r\n\tconst maxBlinkDist = goal === 'decrease'\r\n\t\t? Math.min(BLINK_RANGE, currentDist)\r\n\t\t: BLINK_RANGE;\r\n\r\n\tlet bestTarget: Position | null = null;\r\n\tlet bestScore = -Infinity;\r\n\r\n\tfor (const fraction of DISTANCE_FRACTIONS)\r\n\t{\r\n\t\tconst blinkDist = maxBlinkDist * fraction;\r\n\t\tif (blinkDist < 10) continue;\r\n\r\n\t\tfor (let i = 0; i < ANGLE_CANDIDATES; i++)\r\n\t\t{\r\n\t\t\tconst angle = baseAngle + (i * 2 * Math.PI) / ANGLE_CANDIDATES;\r\n\r\n\t\t\t// Candidate position (clamped with wall buffer to prevent wall-hugging)\r\n\t\t\tconst cx = Math.max(BLINK_WALL_MARGIN, Math.min(ARENA_SIZE - BLINK_WALL_MARGIN, myPos.x + Math.cos(angle) * blinkDist));\r\n\t\t\tconst cy = Math.max(BLINK_WALL_MARGIN, Math.min(ARENA_SIZE - BLINK_WALL_MARGIN, myPos.y + Math.sin(angle) * blinkDist));\r\n\r\n\t\t\t// Skip if clamping made this a non-blink\r\n\t\t\tif (distanceTo(myPos, {x: cx, y: cy}) < 10) continue;\r\n\r\n\t\t\tconst candidate = {x: cx, y: cy};\r\n\r\n\t\t\t// Score: distance from enemy (maximize for increase, minimize for decrease)\r\n\t\t\tconst enemyDist = distanceTo(candidate, enemyPos);\r\n\t\t\tconst distScore = goal === 'increase' ? enemyDist : -enemyDist;\r\n\r\n\t\t\t// Penalize distance from arena center (prevents corner-trapping)\r\n\t\t\tconst center = ARENA_SIZE / 2;\r\n\t\t\tconst distFromCenter = distanceTo(candidate, {x: center, y: center});\r\n\r\n\t\t\t// Missile danger penalty\r\n\t\t\tconst missilePenalty = getMissileDangerAtPosition(candidate, projectiles);\r\n\r\n\t\t\tconst score = distScore - distFromCenter * 0.5 - missilePenalty;\r\n\r\n\t\t\tif (score > bestScore)\r\n\t\t\t{\r\n\t\t\t\tbestScore = score;\r\n\t\t\t\tbestTarget = candidate;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\t// Check minResultDistance threshold\r\n\tif (minResultDistance !== undefined && bestTarget !== null)\r\n\t{\r\n\t\tif (distanceTo(bestTarget, enemyPos) < minResultDistance)\r\n\t\t{\r\n\t\t\treturn null;\r\n\t\t}\r\n\t}\r\n\r\n\treturn bestTarget;\r\n}\r\n\r\n/**\r\n * Estimate missile danger at a position during the post-blink vulnerability window.\r\n * Returns a penalty score (0 = safe, high = dangerous).\r\n *\r\n * Uses the missile's actual heading, speed, and turn rate to estimate arrival time.\r\n * During GCD the wizard moves at full speed (1 u/tick), so missiles only threaten\r\n * the blink destination if they arrive quickly enough that the wizard can't dodge.\r\n */\r\nfunction getMissileDangerAtPosition(\r\n\tpos: Position,\r\n\tprojectiles: ProjectileState[],\r\n): number\r\n{\r\n\tlet danger = 0;\r\n\r\n\tfor (const proj of projectiles)\r\n\t{\r\n\t\tconst dx = pos.x - proj.position.x;\r\n\t\tconst dy = pos.y - proj.position.y;\r\n\t\tconst dist = Math.sqrt(dx * dx + dy * dy);\r\n\r\n\t\t// Can the missile physically reach this position in time?\r\n\t\tif (dist / proj.speed > POST_BLINK_VULNERABILITY) continue;\r\n\r\n\t\tif (proj.turnRate > 0)\r\n\t\t{\r\n\t\t\t// Homing: estimate arrival using heading + turn rate + distance.\r\n\t\t\t// The missile must turn toward the position AND travel to it.\r\n\t\t\t// Minimum arrival ≈ max(turnTime, travelTime) since they overlap.\r\n\t\t\tconst angleToPos = Math.atan2(dy, dx) * (180 / Math.PI);\r\n\t\t\tlet angleDiff = angleToPos - proj.rotation;\r\n\t\t\twhile (angleDiff > 180) angleDiff -= 360;\r\n\t\t\twhile (angleDiff < -180) angleDiff += 360;\r\n\r\n\t\t\tconst turnTicks = Math.abs(angleDiff) / proj.turnRate;\r\n\t\t\tconst travelTicks = dist / proj.speed;\r\n\t\t\tconst estimatedArrival = Math.max(turnTicks, travelTicks);\r\n\r\n\t\t\t// Only dangerous if it arrives before the wizard can move away.\r\n\t\t\t// At full speed (1 u/tick), the wizard needs ~HIT_RADIUS ticks to\r\n\t\t\t// sidestep out of the collision zone. Add a buffer for reaction.\r\n\t\t\tif (estimatedArrival < HIT_RADIUS * 3)\r\n\t\t\t{\r\n\t\t\t\tdanger += 1000;\r\n\t\t\t}\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// Non-homing: check if straight-line path passes near the position\r\n\t\t\tconst rad = proj.rotation * (Math.PI / 180);\r\n\t\t\tconst mx = Math.cos(rad);\r\n\t\t\tconst my = Math.sin(rad);\r\n\r\n\t\t\t// Project position onto missile path (dot product)\r\n\t\t\tconst dot = dx * mx + dy * my;\r\n\t\t\tif (dot < 0) continue; // missile heading away\r\n\r\n\t\t\t// Closest point on missile path to candidate\r\n\t\t\tconst closestX = proj.position.x + mx * dot;\r\n\t\t\tconst closestY = proj.position.y + my * dot;\r\n\t\t\tconst closestDist = Math.sqrt(\r\n\t\t\t\t(pos.x - closestX) ** 2 + (pos.y - closestY) ** 2,\r\n\t\t\t);\r\n\r\n\t\t\t// Check if path passes close enough to hit, within time window\r\n\t\t\tif (closestDist < HIT_RADIUS * 3 && dot / proj.speed <= POST_BLINK_VULNERABILITY)\r\n\t\t\t{\r\n\t\t\t\tdanger += 1000;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\treturn danger;\r\n}\r\n\r\n\r\n// ============================================================================\r\n// T3 SHARED HELPERS\r\n// ============================================================================\r\n\r\n/**\r\n * Compute how fast the enemy is closing distance (positive = approaching).\r\n * Projects enemy velocity onto the vector from enemy to us.\r\n */\r\nexport function getClosingRate(\r\n\tmyPos: {x: number; y: number},\r\n\tenemyPos: {x: number; y: number},\r\n\tenemyVel: {x: number; y: number},\r\n): number\r\n{\r\n\tconst dx = myPos.x - enemyPos.x;\r\n\tconst dy = myPos.y - enemyPos.y;\r\n\tconst dist = Math.sqrt(dx * dx + dy * dy);\r\n\tif (dist < 0.01) return 0;\r\n\treturn (enemyVel.x * dx + enemyVel.y * dy) / dist;\r\n}\r\n\r\n/**\r\n * Compute how many ticks the enemy remains unable to shield.\r\n * Casting missile → remaining cast + GCD. Casting blink/shield → remaining cast only.\r\n * GCD-locked → remaining GCD. Idle/channeling → 0.\r\n */\r\nexport function getEnemyVulnerabilityTicks(enemy: {\r\n\tstate: string;\r\n\tcastingSpell?: string;\r\n\tcastProgress?: number;\r\n\tcastDuration?: number;\r\n\tgcdRemaining?: number;\r\n}): number\r\n{\r\n\tif (enemy.state === 'casting')\r\n\t{\r\n\t\tconst remainingCast = (enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\r\n\t\tif (enemy.castingSpell === 'missile')\r\n\t\t{\r\n\t\t\treturn remainingCast + GCD_DURATION;\r\n\t\t}\r\n\t\treturn remainingCast;\r\n\t}\r\n\tif (enemy.state === 'gcd_locked')\r\n\t{\r\n\t\treturn enemy.gcdRemaining ?? 0;\r\n\t}\r\n\treturn 0;\r\n}\r\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n} from '../shared.js';\nconst WALL_BUFFER = 60;\nconst WALL_PUSH = 30; // How hard to push away from walls\n\n/**\n * Homing AI that sometimes forgets what it's doing.\n */\nconst ChaoticHoming: MissileAIFunction = ({worldState, random}) => \n{\n\tconst enemy = worldState.enemies[0];\n\t// 80% chance to actually track, 20% chance to just fly straight\n\tif (random() < 0.8) \n\t{\n\t\treturn {turnToward: enemy?.position};\n\t}\n\treturn {};\n};\n\n/**\n * Bot: Hogger\n *\n * BEHAVIOR: The elite critter. Chaotic and unpredictable but genuinely dangerous.\n * Randomly varies missile configs each cast (damage 7-15, speed 3-8, random homing),\n * moves erratically but still somewhat toward/away from the enemy, shields when\n * in real danger, and blinks unpredictably. The randomness makes Hogger hard to\n * predict — you never know if the next missile will be a slow tracker or a fast\n * snipe. Unlike Critter's pure randomness, Hogger has enough combat awareness\n * to actually win fights.\n *\n * NAMING RATIONALE: In WoW, Hogger is the iconic level 11 elite gnoll in Elwynn\n * Forest who infamously kills unprepared lowbies. He's technically a basic mob\n * but hits way harder than expected. This bot is the Critter that learned to\n * fight — still chaotic, still a bit dumb, but capable of ending you if you\n * underestimate it. \"Hogger\" is one of WoW's most recognizable references and\n * perfectly captures \"deceptively dangerous chaos.\"\n *\n * STANDALONE — no tier progression. There's only one Hogger.\n */\nexport const Hogger: WizardFunction = ({state, random}) => \n{\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// Erratic movement: mostly toward/away from enemy with random strafe\n\tconst deltaX = enemy.position.x - state.position.x;\n\tconst deltaY = enemy.position.y - state.position.y;\n\tconst normalizedDistance = distance > 0 ? distance : 1;\n\n\t// Dodge if there's a real threat\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\n\tlet moveX: number;\n\tlet moveY: number;\n\n\tif (dodgeDirection) \n\t{\n\t\t// Actually dodge (Hogger isn't stupid)\n\t\tmoveX = dodgeDirection.x * 100;\n\t\tmoveY = dodgeDirection.y * 100;\n\t}\n\telse \n\t{\n\t\t// Random strafe component\n\t\tconst perpX = -deltaY / normalizedDistance;\n\t\tconst perpY = deltaX / normalizedDistance;\n\t\tconst strafeIntensity = random() * 80 - 40; // -40 to 40\n\n\t\t// Approach/retreat: prefer medium range (250-400), but sometimes just wander\n\t\tlet approachIntensity = 0;\n\t\tif (distance > 400) \n\t\t{\n\t\t\tapproachIntensity = 40 + random() * 30; // approach\n\t\t}\n\t\telse if (distance < 200) \n\t\t{\n\t\t\tapproachIntensity = -(30 + random() * 30); // retreat\n\t\t}\n\t\telse \n\t\t{\n\t\t\tapproachIntensity = random() * 60 - 30; // random walk\n\t\t}\n\n\t\tmoveX =\n\t\t\tperpX * strafeIntensity +\n (deltaX / normalizedDistance) * approachIntensity;\n\t\tmoveY =\n\t\t\tperpY * strafeIntensity +\n (deltaY / normalizedDistance) * approachIntensity;\n\n\t\t// Wall avoidance (Hogger doesn't like corners)\n\t\tif (state.position.x < WALL_BUFFER) moveX += WALL_PUSH;\n\t\tif (state.position.x > ARENA_SIZE - WALL_BUFFER) moveX -= WALL_PUSH;\n\t\tif (state.position.y < WALL_BUFFER) moveY += WALL_PUSH;\n\t\tif (state.position.y > ARENA_SIZE - WALL_BUFFER) moveY -= WALL_PUSH;\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\tconst move = {x: moveX, y: moveY};\n\n\t// === DEFENSE: Shield if in real danger ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > 120) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection\n\t) \n\t{\n\t\tif (urgentThreat.canBlockInTime && urgentThreat.ticksToStartShield <= 3) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t\telse if (!urgentThreat.canBlockInTime && state.blinkCooldown === 0) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === OFFENSE: Random blink (sometimes offensive, sometimes random) ===\n\tif (state.state === 'idle' && state.blinkCooldown === 0 && random() < 0.03) \n\t{\n\t\t// 60% chance blink toward enemy, 40% chance random blink\n\t\tif (random() < 0.6 && distance > 300) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: enemy.position},\n\t\t\t};\n\t\t}\n\t\telse \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'blink',\n\t\t\t\t\ttarget: {\n\t\t\t\t\t\tx: 100 + random() * 600,\n\t\t\t\t\t\ty: 100 + random() * 600,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === OFFENSE: Chaotic missiles ===\n\tif (state.state === 'idle' && distance < 500) \n\t{\n\t\t// Random missile config each time\n\t\tconst roll = random();\n\t\tlet damage: number;\n\t\tlet speed: number;\n\t\tlet turnRate: number;\n\t\tlet duration: number;\n\n\t\tif (roll < 0.3) \n\t\t{\n\t\t\t// Fast snipe\n\t\t\tdamage = 8 + Math.floor(random() * 5); // 8-12\n\t\t\tspeed = 6 + random() * 2; // 6-8\n\t\t\tturnRate = 0;\n\t\t\tduration = 80 + Math.floor(random() * 40); // 80-120\n\t\t}\n\t\telse if (roll < 0.7) \n\t\t{\n\t\t\t// Homing tracker\n\t\t\tdamage = 7 + Math.floor(random() * 5); // 7-11\n\t\t\tspeed = 3 + random() * 2; // 3-5\n\t\t\tturnRate = 1 + random() * 2; // 1-3\n\t\t\tduration = 150 + Math.floor(random() * 100); // 150-250\n\t\t}\n\t\telse \n\t\t{\n\t\t\t// Big slow bomb\n\t\t\tdamage = 12 + Math.floor(random() * 4); // 12-15\n\t\t\tspeed = 2 + random() * 2; // 2-4\n\t\t\tturnRate = 0.5 + random() * 1.5; // 0.5-2\n\t\t\tduration = 200 + Math.floor(random() * 100); // 200-300\n\t\t}\n\n\t\treturn {\n\t\t\tmove,\n\t\t\tstartCast: {\n\t\t\t\tspell: 'missile',\n\t\t\t\tconfig: {damage, speed, turnRate, duration},\n\t\t\t\tmissileAI: ChaoticHoming,\n\t\t\t\tdirection:\n angleTo(state.position, enemy.position) + (random() * 20 - 10), // ±10° aim wobble\n\t\t\t},\n\t\t};\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {fitMissileToBudget} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetBlinkToCenter,\n} from '../shared.js';\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\n/**\n * Bot: Doombringer\n *\n * BEHAVIOR: Fires a single maximum-damage homing missile with infinite budget.\n * No damage cap — goes for the biggest possible hit. Exists as a benchmark to\n * demonstrate why lower-damage + shield play is superior. Has basic shield\n * defense but no sophisticated tactics. One fat cast, one fat hit.\n *\n * STANDALONE — no tier progression. Benchmark/test bot.\n */\nexport const Doombringer: WizardFunction = ({state}) => \n{\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > 150) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Shield undodgeable threats ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection\n\t) \n\t{\n\t\tif (urgentThreat.canBlockInTime && urgentThreat.ticksToStartShield <= 3) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t\telse if (!urgentThreat.canBlockInTime && state.blinkCooldown === 0) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === OFFENSE: Fire max-damage homing missile (no damage cap) ===\n\tif (state.state === 'idle') \n\t{\n\t\tconst config = fitMissileToBudget(Infinity, distance, {minTurnRate: 0.5});\n\t\tif (config) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {move: {x: 0, y: 0}};\n};\n","/**\r\n * VIBEMANCER - PARAMETER RUNTIME\r\n *\r\n * Provides the useParam() hook for bots to declare tunable parameters,\r\n * and the infrastructure for the optimizer to inject/discover parameter values.\r\n *\r\n * Design: Module-level state (JS is single-threaded, no race conditions).\r\n * The optimizer sets param values before running a bot, and clears them after.\r\n * During discovery, all useParam calls are recorded.\r\n *\r\n * ## useParam API\r\n *\r\n * ```typescript\r\n * // Basic: just a value, no optimizer config\r\n * const damage = useParam('damage', 15);\r\n *\r\n * // With range: optimizer searches value ± range (sliding window)\r\n * const distance = useParam('distance', 350, {range: 150, min: 0});\r\n *\r\n * // With fixed min/max: optimizer searches [min, max] (fixed bounds)\r\n * const damage = useParam('damage', 15, {min: 5, max: 25});\r\n *\r\n * // With all: range defines search radius, min/max clamp it\r\n * const fraction = useParam('fraction', 0.25, {range: 0.2, min: 0, max: 1});\r\n *\r\n * // With custom step count: optimizer tests 20 values instead of default 10\r\n * const distance = useParam('distance', 500, {range: 200, min: 0, steps: 20});\r\n * ```\r\n *\r\n * - **Arg 1** `name` — unique parameter name (must be consistent across ticks)\r\n * - **Arg 2** `value` — the actual value used in gameplay. This is what your bot\r\n * uses during matches. The optimizer script automatically updates this value.\r\n * - **Arg 3** `config` (optional) — optimizer search configuration:\r\n * - `range` — search radius: optimizer checks `value ± range`. The auto-optimizer\r\n * rewrites `value` after each run, so the search window slides automatically.\r\n * - `min` / `max` — hard constraints (e.g., distance ≥ 0, fraction ≤ 1).\r\n * When `range` is omitted, these define fixed search bounds (old-style).\r\n * - `steps` — how many evenly-spaced values the optimizer tests per pass (default: 10).\r\n * - `substeps` — steps to use in refinement passes (passes 2+). Set to 0 to freeze\r\n * after pass 1 (ideal for boolean params). Defaults to `steps` if not specified.\r\n * - At least `range` or both `min` + `max` must be provided.\r\n *\r\n * Without optimizer config, useParam simply returns `value` every tick.\r\n * With optimizer config, the offline optimizer script can override the value during search.\r\n *\r\n * ## Rules of Hooks\r\n * useParam follows the same rules as useState/useEffect/etc:\r\n * - Must be called at the top level of your bot function (not inside conditionals)\r\n * - Must be called in the same order every tick\r\n * - Violations are detected and throw errors\r\n */\r\n\r\nimport type {WizardFunction} from '../types.js';\r\nimport {validateHookCall} from './hooks-runtime.js';\r\n\r\n/**\r\n * Declaration of a tunable parameter, as discovered by the optimizer.\r\n */\r\nexport interface ParamDeclaration\r\n{\r\n\tname: string;\r\n\tvalue: number;\r\n\trange?: number;\r\n\tmin?: number;\r\n\tmax?: number;\r\n\tsteps: number;\r\n\t/** Steps to use in refinement passes (passes 2+). 0 = freeze after pass 1. Defaults to `steps`. */\r\n\tsubsteps?: number;\r\n}\r\n\r\n// --- Module-level state ---\r\n\r\n/** Current parameter values injected by the optimizer. null = use defaults. */\r\nlet paramValues: Map<string, number> | null = null;\r\n\r\n/** Params discovered during discovery mode. */\r\nconst discoveredParams = new Map<string, ParamDeclaration>();\r\n\r\n/** Whether we're in discovery mode (collecting param declarations). */\r\nlet discovering = false;\r\n\r\n// --- Public API for bot authors ---\r\n\r\n/**\r\n * Declare a tunable parameter. Returns the current value (optimizer-injected or the provided value).\r\n *\r\n * @param name - Unique parameter name (consistent across ticks)\r\n * @param value - The gameplay value. The auto-optimizer rewrites this in source code.\r\n * @param config - Optional optimizer search configuration\r\n * @returns The optimizer-injected value during optimization, or `value` during normal play\r\n *\r\n * @example\r\n * // Simple: no optimizer config\r\n * const damage = useParam('damage', 15);\r\n *\r\n * // With range: optimizer searches value ± range (sliding window)\r\n * const distance = useParam('distance', 350, {range: 150, min: 0});\r\n *\r\n * // With fixed min/max: optimizer searches [min, max]\r\n * const damage = useParam('damage', 15, {min: 5, max: 25});\r\n */\r\nexport function useParam(name: string, value: number, config?: {\r\n\trange?: number;\r\n\tmin?: number;\r\n\tmax?: number;\r\n\tsteps?: number;\r\n\tsubsteps?: number;\r\n}): number\r\n{\r\n\t// Participate in hook index validation (same rules as useState, useEffect, etc.)\r\n\tvalidateHookCall('useParam');\r\n\r\n\t// Register for discovery (only if optimizer config is provided)\r\n\tif (discovering && config)\r\n\t{\r\n\t\tdiscoveredParams.set(name, {\r\n\t\t\tname,\r\n\t\t\tvalue,\r\n\t\t\trange: config.range,\r\n\t\t\tmin: config.min,\r\n\t\t\tmax: config.max,\r\n\t\t\tsteps: config.steps ?? 10,\r\n\t\t\tsubsteps: config.substeps,\r\n\t\t});\r\n\t}\r\n\r\n\t// Return optimizer value or the provided value\r\n\treturn paramValues?.get(name) ?? value;\r\n}\r\n\r\n// --- Optimizer API ---\r\n\r\n/**\r\n * Inject parameter values for the next bot execution.\r\n * The wrapped bot will read these values via useParam().\r\n */\r\nexport function setParamValues(values: Record<string, number>): void\r\n{\r\n\tparamValues = new Map(Object.entries(values));\r\n}\r\n\r\n/**\r\n * Clear injected parameter values. useParam() will return its provided value.\r\n */\r\nexport function clearParamValues(): void\r\n{\r\n\tparamValues = null;\r\n}\r\n\r\n/**\r\n * Start discovery mode. All subsequent useParam() calls with optimizer config\r\n * will register their declarations.\r\n */\r\nexport function startDiscovery(): void\r\n{\r\n\tdiscovering = true;\r\n\tdiscoveredParams.clear();\r\n}\r\n\r\n/**\r\n * Stop discovery mode and return all discovered parameter declarations.\r\n */\r\nexport function stopDiscovery(): ParamDeclaration[]\r\n{\r\n\tdiscovering = false;\r\n\tconst result = [...discoveredParams.values()];\r\n\tdiscoveredParams.clear();\r\n\treturn result;\r\n}\r\n\r\n/**\r\n * Wrap a bot function to inject specific parameter values.\r\n * The returned function sets params before calling the bot and clears them after.\r\n */\r\nexport function wrapWithParams(bot: WizardFunction, params: Record<string, number>): WizardFunction\r\n{\r\n\treturn (props) =>\r\n\t{\r\n\t\tsetParamValues(params);\r\n\t\ttry\r\n\t\t{\r\n\t\t\treturn bot(props);\r\n\t\t}\r\n\t\tfinally\r\n\t\t{\r\n\t\t\tclearParamValues();\r\n\t\t}\r\n\t};\r\n}\r\n","import {WizardFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {getMissileCastTime} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetBlinkToCenter,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\n/**\n * Bot: Turtle\n *\n * BEHAVIOR: Stationary tank with a simple defense-first loop: shield → missile\n * → wait → shield → repeat. Never moves. Always prioritizes having shield\n * ready over dealing damage.\n *\n * COMBAT LOOP:\n * 1. Incoming missile? → Shield immediately (cancel anything else)\n * 2. Hold shield until missile has actually impacted (don't cancel early!)\n * 3. Cancel shield → GCD → Fire one missile → GCD → idle\n * 4. Wait idle for next threat\n * 5. Goto 1\n *\n * CRITICAL: Does NOT cancel shield until incoming missiles have impacted.\n * Shield must be held through impact to actually block damage.\n *\n * Uses damage 10, speed 5, turnRate 1, duration 180 (~81 tick cast).\n * Emergency blinks toward center when caught in GCD with unavoidable hit.\n *\n * NAMING RATIONALE: \"Turtling\" is the universal gaming term for extreme\n * defensive play. Sits still and blocks everything.\n *\n * PROGRESSION LINE: Turtle → Sentinel → Golem\n * - Turtle (tier 1): Stationary, fixed missiles, always-shield-first\n * - Sentinel (tier 2): Stationary, adaptive missile fitting (fitMissileToBudget)\n * - Golem (tier 3): Future — immovable fortress, perfect timing, punishing hits\n *\n * TIER: 1 (base)\n */\n\n// Homing missile: damage 12, speed 5, turnRate 1, duration 180.\n// Range = 5 × 180 = 900 units.\nconst MISSILE_CONFIG = {damage: 12, speed: 5, turnRate: 1, duration: 180};\n\nexport const Turtle: WizardFunction = ({state}) => \n{\n\tconst shieldBuffer = useParam('shieldBuffer', 3.1, {\n\t\trange: 5,\n\t\tmin: 0,\n\t\tsteps: 5,\n\t});\n\tconst shieldHoldTicks = useParam('shieldHoldTicks', 5, {\n\t\trange: 5,\n\t\tmin: 0,\n\t\tsteps: 5,\n\t});\n\tconst ticksToReshield = SHIELD_CAST_TIME + shieldBuffer;\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) return {move: {x: 0, y: 0}};\n\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\tconst shouldStartShield =\n\t\turgentThreat !== null &&\n urgentThreat.ticksToImpact <= SHIELD_CAST_TIME + shieldBuffer;\n\n\t// === CHANNELING SHIELD ===\n\t// Hold shield until the imminent missile(s) have actually impacted.\n\tif (state.state === 'channeling' && state.channelingSpell === 'shield') \n\t{\n\t\t// Don't cancel while a missile is about to hit\n\t\tconst imminentThreat = threats.find(\n\t\t\t(t) => t.willHit && t.ticksToImpact <= shieldHoldTicks,\n\t\t);\n\t\tif (imminentThreat) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}};\n\t\t}\n\n\t\t// Safe to cancel if no next threat, or next threat is far enough to reshield\n\t\tconst nextThreat = threats.find((t) => t.willHit);\n\t\tif (!nextThreat || nextThreat.ticksToImpact > ticksToReshield) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\t// Next threat too soon — keep channeling\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === CASTING SHIELD: let it finish ===\n\tif (state.state === 'casting' && state.castingSpell === 'shield') \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === NEED TO SHIELD NOW: cancel anything else ===\n\tif (shouldStartShield) \n\t{\n\t\tif (state.state === 'casting') \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\tif (state.state === 'idle') \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, startCast: {spell: 'shield'}};\n\t\t}\n\t}\n\n\t// === EMERGENCY BLINK: missile too close for shield, blink available ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n urgentThreat.ticksToImpact < SHIELD_CAST_TIME + shieldBuffer &&\n urgentThreat.ticksToImpact >= 10 &&\n state.blinkCooldown === 0\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t};\n\t}\n\n\t// === ATTACK: one missile if safe window exists ===\n\t// Dynamic safe window: actual cast time (with warmup) + GCD + shield + buffer\n\tconst castTime = getMissileCastTime(MISSILE_CONFIG, state.lastMissileConfig);\n\tconst safeWindow = castTime + GCD_DURATION + SHIELD_CAST_TIME + shieldBuffer;\n\tif (\n\t\tstate.state === 'idle' &&\n (!urgentThreat || urgentThreat.ticksToImpact > safeWindow)\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {\n\t\t\t\tspell: 'missile',\n\t\t\t\tconfig: MISSILE_CONFIG,\n\t\t\t\tmissileAI: ({worldState}) => ({\n\t\t\t\t\tturnToward: worldState.enemies[0]?.position,\n\t\t\t\t}),\n\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t},\n\t\t};\n\t}\n\n\treturn {move: {x: 0, y: 0}};\n};\n","import {WizardFunction, MissileActions} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {getMissileCastTime} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetBlinkToCenter,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\n/**\n * Bot: Sentinel\n *\n * BEHAVIOR: Stationary tank with last-moment shielding AND two-tier offense.\n * Like Turtle, never moves and shields at the last moment. Unlike Turtle,\n * fires bigger missiles (damage 20) when the safe window is large enough,\n * falling back to Turtle's fast missile (damage 12) when pressed.\n *\n * PROGRESSION LINE: Turtle → Sentinel → Golem\n * - Turtle (tier 1): Stationary, fixed missiles, reactive shield timing\n * - Sentinel (tier 2): Stationary, two-tier offense (big + fast missiles)\n * - Golem (tier 3): Immovable fortress, perfect shield timing\n *\n * TIER: 2 (enhanced Turtle)\n */\nexport const Sentinel: WizardFunction = ({state}) =>\n{\n\tconst shieldHoldTicks = useParam('shieldHoldTicks', 2, {\n\t\trange: 5,\n\t\tmin: 0,\n\t\tmax: 15,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\tconst SHIELD_BUFFER = 3;\n\tconst shouldStartShield =\n\t\turgentThreat !== null &&\n urgentThreat.ticksToImpact <= SHIELD_CAST_TIME + SHIELD_BUFFER;\n\n\tconst TICKS_TO_RESHIELD = SHIELD_CAST_TIME + SHIELD_BUFFER;\n\tconst nextThreat = threats.filter((t) => t.willHit)[1] ?? null;\n\tconst safeToCancel =\n\t\t!nextThreat || nextThreat.ticksToImpact > TICKS_TO_RESHIELD;\n\n\tif (state.state === 'channeling' && state.channelingSpell === 'shield')\n\t{\n\t\tconst imminentThreat = threats.find(\n\t\t\t(t) => t.willHit && t.ticksToImpact <= shieldHoldTicks,\n\t\t);\n\t\tif (imminentThreat) return {move: {x: 0, y: 0}};\n\t\tif (safeToCancel) return {move: {x: 0, y: 0}, cancel: true};\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tif (state.state === 'casting' && state.castingSpell === 'shield')\n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tif (shouldStartShield)\n\t{\n\t\tif (state.state === 'casting') return {move: {x: 0, y: 0}, cancel: true};\n\t\tif (state.state === 'idle')\n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, startCast: {spell: 'shield'}};\n\t\t}\n\t}\n\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n urgentThreat.ticksToImpact < SHIELD_CAST_TIME + SHIELD_BUFFER &&\n urgentThreat.ticksToImpact >= 10 &&\n state.blinkCooldown === 0\n\t)\n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t};\n\t}\n\n\t// Two-tier offense: big missile when safe, fast missile otherwise.\n\t// Same speed/turnRate/duration → no warmup switching penalty.\n\tif (state.state === 'idle' && enemy)\n\t{\n\t\tconst FAST_MISSILE = {damage: 12, speed: 5, turnRate: 1, duration: 180};\n\t\tconst BIG_MISSILE = {damage: 20, speed: 5, turnRate: 1, duration: 180};\n\n\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\tconst bigCast = getMissileCastTime(BIG_MISSILE, state.lastMissileConfig);\n\t\tconst bigWindow = bigCast + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tif (!urgentThreat || nextThreatTime > bigWindow)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: BIG_MISSILE,\n\t\t\t\t\tmissileAI: ({worldState}): MissileActions => ({\n\t\t\t\t\t\tturnToward: worldState.enemies[0]?.position,\n\t\t\t\t\t}),\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\tconst fastCast = getMissileCastTime(FAST_MISSILE, state.lastMissileConfig);\n\t\tconst fastWindow = fastCast + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tif (!urgentThreat || nextThreatTime > fastWindow)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: FAST_MISSILE,\n\t\t\t\t\tmissileAI: ({worldState}): MissileActions => ({\n\t\t\t\t\t\tturnToward: worldState.enemies[0]?.position,\n\t\t\t\t\t}),\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {move: {x: 0, y: 0}};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {\n\tGCD_DURATION,\n\tSHIELD_CAST_TIME,\n\tBLINK_CAST_TIME,\n} from '../../rules.js';\nimport {getMissileCastTime, getLeadPosition} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetBlinkToCenter,\n\tfitMissileToBudget,\n\tisSafeToAttack,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n\tgetEnemyVulnerabilityTicks,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst SHIELD_BUFFER = 3;\nconst SHIELD_HOLD_TICKS = 5;\n\n/**\n * Standard homing missile AI — tracks enemy position.\n */\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\n/**\n * Straight missile AI — no homing, flies in initial direction.\n */\nconst StraightAI: MissileAIFunction = () => ({});\n\n/**\n * Bot: Golem\n *\n * BEHAVIOR: Stationary fortress with perfect shield timing and devastating\n * counterattacks during enemy vulnerability windows. Reads enemy cast/GCD\n * state to time punish missiles that land when the enemy can't shield.\n * Handles multi-missile volleys by holding shield through consecutive impacts.\n * Uses progressive cast-cancel thresholds for optimal damage trading.\n *\n * KEY IMPROVEMENTS OVER SENTINEL:\n * - Counterattack punish: fires during enemy cast/GCD recovery\n * - Multi-threat volley awareness: holds shield through consecutive hits\n * - Progressive cast-cancel: graduated damage thresholds\n * - Perfect shield timing: uses ticksToStartShield precisely\n *\n * PROGRESSION LINE: Turtle → Sentinel → Golem\n * TIER: 3 (elite Defensive line)\n */\nexport const Golem: WizardFunction = ({state}) => \n{\n\t// Positive turn rate ensures homing missiles that hit moving targets.\n\t// Hardcoded — optimizer incorrectly pushes this negative for stationary matchups.\n\tconst minTurnRate = 0.5;\n\tconst preemptiveShieldRange = useParam('preemptiveShieldRange', 150, {\n\t\trange: 85,\n\t\tmin: 0,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 100, {\n\t\trange: 75,\n\t\tmin: 10,\n\t});\n\tconst punishMinVulnerability = useParam('punishMinVulnerability', 310, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tsteps: 5,\n\t});\n\tconst preferBlinkDodge =\n\t\tuseParam('preferBlinkDodge', 1, {min: 0, max: 1, steps: 2}) >= 0.5;\n\tconst cancelHeavyDmg = useParam('cancelHeavyDmg', 14, {\n\t\trange: 15,\n\t\tmin: 5,\n\t\tmax: 40,\n\t\tsteps: 5,\n\t});\n\tconst cancelMediumDmg = useParam('cancelMediumDmg', 12, {\n\t\trange: 10,\n\t\tmin: 3,\n\t\tmax: 25,\n\t\tsteps: 5,\n\t});\n\tconst cancelCastRatio = useParam('cancelCastRatio', 0.8, {\n\t\trange: 0.4,\n\t\tmin: 0.3,\n\t\tmax: 1.0,\n\t\tsteps: 5,\n\t});\n\tconst cancelLightDmg = useParam('cancelLightDmg', 8, {\n\t\trange: 5,\n\t\tmin: 1,\n\t\tmax: 15,\n\t\tsteps: 5,\n\t});\n\tconst cancelEarlyCastRatio = useParam('cancelEarlyCastRatio', 0.7, {\n\t\trange: 0.3,\n\t\tmin: 0.0,\n\t\tmax: 0.7,\n\t\tsteps: 5,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 50, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst preemptiveShieldBuffer = useParam('preemptiveShieldBuffer', 11.3, {\n\t\trange: 5,\n\t\tmin: 0,\n\t\tmax: 15,\n\t\tsteps: 5,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 9.5, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\tconst hittingThreats = threats.filter((t) => t.willHit);\n\n\t// === STEP 1: CHANNELING — hold through volleys, cancel when safe ===\n\tif (state.state === 'channeling' && state.channelingSpell === 'shield') \n\t{\n\t\t// Hold if any missile is about to hit\n\t\tconst imminentThreat = hittingThreats.find(\n\t\t\t(t) => t.ticksToImpact <= SHIELD_HOLD_TICKS,\n\t\t);\n\t\tif (imminentThreat) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}};\n\t\t}\n\n\t\t// Hold if next threat will arrive before we can reshield\n\t\tconst nextHitting = hittingThreats[0];\n\t\tconst ticksToReshield = SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tif (nextHitting && nextHitting.ticksToImpact <= ticksToReshield) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}};\n\t\t}\n\n\t\t// Cancel shield if no threat or threat is far enough\n\t\tif (!nextHitting || nextHitting.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// If casting shield, let it finish\n\tif (state.state === 'casting' && state.castingSpell === 'shield') \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === STEP 2: PROGRESSIVE CAST-CANCEL — evaluate damage tradeoff ===\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat\n\t) \n\t{\n\t\t// Sentinel is stationary — can't dodge. Must always consider canceling.\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tconst defenseTime =\n\t\t\tstate.blinkCooldown === 0\n\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tconst willArriveInWindow =\n\t\t\turgentThreat.ticksToImpact <= remainingCast + GCD_DURATION + defenseTime;\n\n\t\tif (willArriveInWindow) \n\t\t{\n\t\t\tconst incomingDamage = urgentThreat.projectile.damage;\n\t\t\tconst castRatio = (state.castProgress ?? 0) / (state.castDuration ?? 1);\n\n\t\t\t// Always cancel for lethal or heavy damage\n\t\t\tif (incomingDamage >= state.health || incomingDamage >= cancelHeavyDmg) \n\t\t\t{\n\t\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t\t}\n\t\t\t// Significant damage + early/mid cast: cancel\n\t\t\tif (incomingDamage >= cancelMediumDmg && castRatio < cancelCastRatio) \n\t\t\t{\n\t\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t\t}\n\t\t\t// Medium damage + very early cast: cancel\n\t\t\tif (\n\t\t\t\tincomingDamage >= cancelLightDmg &&\n castRatio < cancelEarlyCastRatio\n\t\t\t) \n\t\t\t{\n\t\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === STEP 3: BLINK-DODGE — 100% damage avoidance (optimizer-controlled) ===\n\tif (\n\t\tpreferBlinkDodge &&\n state.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n urgentThreat.ticksToImpact >= BLINK_CAST_TIME &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t};\n\t}\n\n\t// === STEP 3b: SHIELD — fallback when blink unavailable (90% block) ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= SHIELD_BUFFER\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'shield'},\n\t\t};\n\t}\n\n\t// === STEP 4: PREEMPTIVE SHIELD — enemy casting at close range ===\n\tif (\n\t\tstate.state === 'idle' &&\n enemy &&\n enemy.state === 'casting' &&\n enemy.castingSpell === 'missile'\n\t) \n\t{\n\t\tconst distance = distanceTo(state.position, enemy.position);\n\t\tif (distance < preemptiveShieldRange) \n\t\t{\n\t\t\tconst remainingCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTicks = distance / flightTimeDivisor;\n\t\t\tconst ticksUntilHit = remainingCast + estimatedFlightTicks;\n\t\t\tif (\n\t\t\t\tticksUntilHit <\n SHIELD_CAST_TIME + SHIELD_BUFFER + preemptiveShieldBuffer\n\t\t\t) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === STEP 5: OFFENSE — punish mode + standard counterattack ===\n\tif (state.state === 'idle' && enemy) \n\t{\n\t\tconst offenseDefenseTime =\n\t\t\tstate.blinkCooldown === 0\n\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tconst distance = distanceTo(state.position, enemy.position);\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst vulnerabilityTicks = getEnemyVulnerabilityTicks(enemy);\n\n\t\t// --- PUNISH MODE: exploit enemy cast/GCD vulnerability ---\n\t\tif (vulnerabilityTicks >= punishMinVulnerability) \n\t\t{\n\t\t\tconst flightTimeEstimate = distance / flightTimeDivisor;\n\t\t\tconst punishBudget = vulnerabilityTicks - flightTimeEstimate;\n\t\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\t\tconst safeBudget = nextThreatTime - GCD_DURATION - offenseDefenseTime;\n\t\t\tconst effectiveBudget = Math.min(\n\t\t\t\tpunishBudget,\n\t\t\t\tsafeBudget,\n\t\t\t\tMAX_CAST_BUDGET,\n\t\t\t);\n\n\t\t\tif (effectiveBudget > 0) \n\t\t\t{\n\t\t\t\tconst config = fitMissileToBudget(effectiveBudget, distance, {\n\t\t\t\t\tminTurnRate: 0,\n\t\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t\t});\n\n\t\t\t\tif (config && config.damage >= MIN_USEFUL_DAMAGE) \n\t\t\t\t{\n\t\t\t\t\tconst castTime = getMissileCastTime(config, state.lastMissileConfig);\n\t\t\t\t\tconst actualFlight = distance / config.speed;\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tcastTime + actualFlight < vulnerabilityTicks &&\n isSafeToAttack(threats, castTime)\n\t\t\t\t\t) \n\t\t\t\t\t{\n\t\t\t\t\t\tconst aimTarget =\n\t\t\t\t\t\t\tconfig.turnRate > 0\n\t\t\t\t\t\t\t\t? enemy.position\n\t\t\t\t\t\t\t\t: getLeadPosition(\n\t\t\t\t\t\t\t\t\tenemy.position,\n\t\t\t\t\t\t\t\t\tenemy.velocity,\n\t\t\t\t\t\t\t\t\tconfig.speed,\n\t\t\t\t\t\t\t\t\tstate.position,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\t\t\tstartCast: {\n\t\t\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\tmissileAI: config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\t\t\t\tdirection: angleTo(state.position, aimTarget),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// --- STANDARD MODE: adaptive homing in safe windows ---\n\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\tconst safeBudget = nextThreatTime - GCD_DURATION - offenseDefenseTime;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (config && config.damage >= Math.min(MIN_USEFUL_DAMAGE, enemyHP)) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {move: {x: 0, y: 0}};\n};\n","import {WizardFunction} from '../../types.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToDecreaseDistance,\n\tshouldForceShield,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 50;\n\n// Melee stab: 20 damage, speed 8, 3-hit kill.\nconst STAB_CONFIG = {damage: 20, speed: 8, turnRate: 0, duration: 10};\n\n/**\n * Aggressive movement: dodge missiles using threat analysis, close distance, light strafe.\n */\nfunction aggressiveMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\t// When actively dodging, commit fully\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\t// Light strafe + aggressive closing\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\t// Always close distance aggressively (melee wants to be in the enemy's face)\n\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Shadowblade\n *\n * BEHAVIOR: Melee assassin. Blinks to the enemy, then lands devastating point-blank\n * stab attacks (15 damage, 30u range, ~60 tick cast = 2-hit kill). Runs directly\n * at the enemy with minimal strafe, shields undodgeable threats. The entire\n * strategy is: get close, stab, kill. Simple and brutal.\n *\n * PROGRESSION LINE: Shadowblade → Nightblade → Voidblade\n * - Shadowblade (tier 1): Offensive blink, melee stabs, basic shield\n * - Nightblade (tier 2): + missile-aware blinks, adaptive stabs, timed defense\n * - Voidblade (tier 3): Future — perfect assassination timing, inescapable engages\n *\n * TIER: 1 (base)\n */\nexport const Shadowblade: WizardFunction = ({state}) => \n{\n\tconst stabRange = useParam('stabRange', 76, {\n\t\trange: 30,\n\t\tmin: 20,\n\t\tmax: 80,\n\t\tsteps: 5,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 252, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 55, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 61, {\n\t\trange: 20,\n\t\tmin: 50,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 23, {\n\t\trange: 30,\n\t\tmin: 15,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// Cancel shield if no longer needed\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tif (state.state === 'idle') \n\t{\n\t\t// === BLINK: Dodge missile + close gap simultaneously (first priority) ===\n\t\t// Wait until the missile is close (<=40 ticks) so the blink actually dodges it.\n\t\t// Blinking too early means the missile can still re-track. Too late (<10) and the\n\t\t// blink cast won't complete in time.\n\t\tif (\n\t\t\turgentThreat &&\n state.blinkCooldown === 0 &&\n distance > stabRange &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t\t) \n\t\t{\n\t\t\tconst blinkTarget =\n\t\t\t\tgetBlinkToDecreaseDistance(\n\t\t\t\t\tstate.position,\n\t\t\t\t\tenemy.position,\n\t\t\t\t\tstate.projectiles,\n\t\t\t\t) ?? enemy.position;\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t};\n\t\t}\n\n\t\t// Pre-stab shield: already at melee range, survival beats landing another stab.\n\t\t// Gated to stab range so out-of-range threats wait for the blink window.\n\t\tif (\n\t\t\turgentThreat &&\n distance < stabRange &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3\n\t\t)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\n\t\t// Melee stab when close enough (offense over defense at melee range)\n\t\tif (distance < stabRange)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: STAB_CONFIG,\n\t\t\t\t\tmissileAI: () => ({}), // Non-homing, point blank\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// === DEFENSE: Shield undodgeable threats (only when blink can't handle it) ===\n\t\t// Don't shield if blink is available — wait for the blink window instead.\n\t\tif (\n\t\t\turgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3 &&\n (state.blinkCooldown > 0 || urgentThreat.ticksToImpact < 10)\n\t\t)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// Aggressive movement — always close distance\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = aggressiveMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t);\n\treturn {move};\n};\n","import {WizardFunction} from '../../types.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToDecreaseDistance,\n\tshouldForceShield,\n\tfitMissileToBudget,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 50;\nconst SHIELD_BUFFER = 3;\n\n/**\n * Aggressive movement: dodge missiles, close distance, light strafe.\n */\nfunction aggressiveMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\t// When actively dodging, commit fully\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\t// Light strafe + aggressive closing\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\t// Always close distance aggressively\n\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Nightblade\n *\n * BEHAVIOR: Enhanced melee assassin. Same aggressive engagement as Shadowblade —\n * blinks directly to the enemy and stabs for 15 damage (2-hit kill). The tier 2\n * upgrade is PREEMPTIVE DEFENSE: Nightblade watches the enemy's cast bar and\n * shields before a point-blank missile is even launched. At melee range, missiles\n * arrive almost instantly after launch — too fast to react. Nightblade anticipates\n * the threat. Also has emergency blink and proper channeling management.\n *\n * PROGRESSION LINE: Shadowblade → Nightblade → Voidblade\n * - Shadowblade (tier 1): Offensive blink, melee stabs, basic shield (reactive only)\n * - Nightblade (tier 2): + preemptive shield vs enemy casts, emergency blink\n * - Voidblade (tier 3): Future — perfect assassination timing, inescapable engages\n *\n * TIER: 2 (enhanced Shadowblade)\n */\nexport const Nightblade: WizardFunction = ({state}) => \n{\n\tconst stabRange = useParam('stabRange', 263, {range: 38, min: 0, steps: 5});\n\tconst stabRangeVsCasting = stabRange + 20;\n\tconst preemptiveShieldRange = useParam('preemptiveShieldRange', 261, {\n\t\trange: 85,\n\t\tmin: 0,\n\t\tsteps: 7,\n\t});\n\tconst acceptHitFraction = useParam('acceptHitFraction', 0, {\n\t\trange: 0.2,\n\t\tmin: 0,\n\t\tmax: 1,\n\t\tsteps: 5,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', -11.4, {range: 5.25, steps: 5});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 300, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 22, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 92, {\n\t\trange: 20,\n\t\tmin: 50,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 50, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 19, {\n\t\trange: 30,\n\t\tmin: 15,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 12, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst enemyFiringSoonThreshold = useParam('enemyFiringSoonThreshold', 50, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst meleeAbortRange = useParam('meleeAbortRange', 200, {\n\t\trange: 50,\n\t\tmin: 50,\n\t\tmax: 200,\n\t\tsteps: 7,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = aggressiveMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t);\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\t// Keep shield up if enemy is about to fire at close range (preemptive shield scenario)\n\t\tconst enemyFiringSoon =\n\t\t\tdistance < preemptiveShieldRange &&\n enemy.state === 'casting' &&\n enemy.castingSpell === 'missile' &&\n (enemy.castDuration ?? 0) -\n (enemy.castProgress ?? 0) +\n distance / flightTimeDivisor <\n enemyFiringSoonThreshold;\n\t\tif (\n\t\t\t(!urgentThreat && !enemyFiringSoon) ||\n (urgentThreat && urgentThreat.ticksToImpact > shieldCancelThreshold)\n\t\t) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast for LETHAL incoming damage ===\n\t// DPS trade: we accepted the hit when we started casting. Only cancel to survive.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER\n\t\t) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === CANCEL: Abort melee cast if target escaped (non-homing missile won't reach) ===\n\t// Melee missiles are non-homing with short range (~63u). If the enemy blinked away,\n\t// continuing the cast is wasted time while we're vulnerable.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n distance > meleeAbortRange\n\t) \n\t{\n\t\treturn {move, cancel: true};\n\t}\n\n\t// === BLINK: Dodge missile + close gap simultaneously (first priority) ===\n\t// Blink both dodges the incoming missile AND closes distance — better than shielding.\n\t// Only shield when blink is on cooldown.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n distance > stabRange &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\tconst blinkTarget =\n\t\t\tgetBlinkToDecreaseDistance(\n\t\t\t\tstate.position,\n\t\t\t\tenemy.position,\n\t\t\t\tstate.projectiles,\n\t\t\t) ?? enemy.position;\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t};\n\t}\n\n\t// === DEFENSE: Shield lethal/high-damage undodgeable threats — survival over offense ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&\n urgentThreat.projectile.damage >= state.health &&\n urgentThreat.canBlockInTime\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'shield'},\n\t\t};\n\t}\n\n\t// === OFFENSE: Budget-based melee stab when close enough ===\n\t// Tier 2 upgrade: fits the highest-damage stab into the available time window\n\t// before needing to shield. Also stabs from farther when enemy is casting/channeling\n\t// (they move at 0.5 or 0 speed, so the cast delay doesn't let them dodge).\n\tconst effectiveStabRange =\n\t\tenemy.state === 'casting' || enemy.state === 'channeling'\n\t\t\t? stabRangeVsCasting\n\t\t\t: stabRange;\n\t// Skip offense when a lethal missile could reach us during cast+GCD — dodge at full speed instead\n\tconst myProjectileIds = new Set(state.myProjectiles.map((p) => p.id));\n\tconst hasNearbyLethalMissile = state.projectiles.some(\n\t\t(p) =>\n\t\t\t!myProjectileIds.has(p.id) &&\n p.damage >= state.health &&\n distanceTo(state.position, p.position) / p.speed <\n MAX_CAST_BUDGET + GCD_DURATION,\n\t);\n\tif (\n\t\tstate.state === 'idle' &&\n distance < effectiveStabRange &&\n !hasNearbyLethalMissile\n\t) \n\t{\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Account for enemy's active cast — their missile will arrive after cast completes + flight time\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTime = distance / flightTimeDivisor;\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t);\n\t\t}\n\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tlet config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\t// If budget too small for useful stab, fire bigger and accept the hit\n\t\t// Only accept small hits (< 25% HP) — don't trade away large chunks of health\n\t\t// Never accept when enemy is actively casting (their missile WILL arrive during our long cast)\n\t\tconst enemyCasting =\n\t\t\tenemy.state === 'casting' && enemy.castingSpell === 'missile';\n\t\tif (\n\t\t\t(!config || config.damage < minDmg) &&\n !enemyCasting &&\n (!urgentThreat ||\n urgentThreat.projectile.damage < state.health * acceptHitFraction)\n\t\t) \n\t\t{\n\t\t\tconfig = fitMissileToBudget(MAX_CAST_BUDGET, distance, {\n\t\t\t\tminTurnRate,\n\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t});\n\t\t}\n\n\t\tif (config) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: () => ({}),\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats (only when blink can't handle it) ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (\n\t\t\turgentThreat.canBlockInTime &&\n (state.blinkCooldown > 0 || urgentThreat.ticksToImpact < 10)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === DEFENSE: Preemptive shield when enemy is casting a missile at close range ===\n\t// At melee range, missiles arrive almost instantly after launch — too fast to react.\n\t// Shield BEFORE the missile launches. Trigger when shield will finish BEFORE missile hits.\n\tif (\n\t\tstate.state === 'idle' &&\n distance < preemptiveShieldRange &&\n enemy.state === 'casting' &&\n enemy.castingSpell === 'missile'\n\t) \n\t{\n\t\tconst remainingCast = (enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\tconst estimatedFlightTicks = distance / flightTimeDivisor;\n\t\tconst ticksUntilHit = remainingCast + estimatedFlightTicks;\n\t\t// Shield when hit is close enough that we MUST start now, but far enough that\n\t\t// the shield finishes in time. The window: SHIELD_CAST_TIME <= ticksUntilHit < SHIELD_CAST_TIME+15\n\t\tif (\n\t\t\tticksUntilHit >= SHIELD_CAST_TIME &&\n ticksUntilHit < SHIELD_CAST_TIME + 15\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction} from '../../types.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {\n\tARENA_SIZE,\n\tGCD_DURATION,\n\tSHIELD_CAST_TIME,\n\tBLINK_CAST_TIME,\n} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToDecreaseDistance,\n\tshouldForceShield,\n\tfitMissileToBudget,\n\tMIN_USEFUL_DAMAGE,\n\tgetEnemyVulnerabilityTicks,\n} from '../shared.js';\nimport {getMissileCastTime} from '../../utils/combat.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst WALL_BUFFER = 50;\nconst SHIELD_BUFFER = 3;\n\n/**\n * Aggressive movement: dodge missiles, close distance, light strafe.\n */\nfunction aggressiveMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Voidblade\n *\n * BEHAVIOR: Reactive counter-puncher. Shields everything, then fires quick stabs\n * during enemy vulnerability windows (GCD/casting) when they can't shield back.\n * At melee range, the shield blocks ~90% of incoming damage while Voidblade's\n * counter-stabs land at full damage — winning through attrition.\n *\n * CORE LOOP (melee range):\n * 1. Enemy casts missile → Voidblade blink-dodges (100% avoid) or shields (90% block)\n * 2. Enemy enters GCD → Voidblade fires quick stab (lands unblocked)\n * 3. Voidblade enters GCD → enemy recovers → repeat\n *\n * KEY IMPROVEMENTS OVER NIGHTBLADE:\n * - Blink-dodge priority: avoids 100% of damage when blink available, shields as fallback\n * - Reads enemy vulnerability to time counter-stabs perfectly\n * - Cancel-into-defense: aborts own cast if enemy missile incoming\n * - Punish budget: sizes stabs to fit exactly in the vulnerability window\n *\n * PROGRESSION LINE: Shadowblade → Nightblade → Voidblade\n * TIER: 3 (elite Melee line)\n */\nexport const Voidblade: WizardFunction = ({state}) => \n{\n\tconst preemptiveShieldRange = useParam('preemptiveShieldRange', 348, {\n\t\trange: 85,\n\t\tmin: 50,\n\t\tsteps: 7,\n\t});\n\tconst shieldWindow = useParam('shieldWindow', 18, {\n\t\trange: 10,\n\t\tmin: 5,\n\t\tmax: 30,\n\t\tsteps: 5,\n\t});\n\tconst punishMinVulnerability = useParam('punishMinVulnerability', 10, {\n\t\trange: 15,\n\t\tmin: 10,\n\t\tsteps: 5,\n\t});\n\tconst preferBlinkDodge =\n\t\tuseParam('preferBlinkDodge', 0, {min: 0, max: 1, steps: 2}) >= 0.5;\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 231, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 250,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 45, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 92, {\n\t\trange: 20,\n\t\tmin: 50,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 40, {\n\t\trange: 30,\n\t\tmin: 15,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst blinkPastMargin = useParam('blinkPastMargin', 50, {\n\t\trange: 30,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst blinkPastDistance = useParam('blinkPastDistance', 200, {\n\t\trange: 100,\n\t\tmin: 50,\n\t\tmax: 350,\n\t\tsteps: 7,\n\t});\n\tconst engageBlinkRange = useParam('engageBlinkRange', 30, {\n\t\trange: 75,\n\t\tmin: 30,\n\t\tmax: 200,\n\t\tsteps: 7,\n\t});\n\tconst counterStabRange = useParam('counterStabRange', 27, {\n\t\trange: 20,\n\t\tmin: 10,\n\t\tmax: 60,\n\t\tsteps: 7,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 7.5, {\n\t\trange: 4,\n\t\tmin: 3,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst enemyFlightTimeDivisor = useParam('enemyFlightTimeDivisor', 10, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 10,\n\t\tsteps: 5,\n\t});\n\tconst idleProvokeBudget = useParam('idleProvokeBudget', 37, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst meleeAbortRange = useParam('meleeAbortRange', 50, {\n\t\trange: 50,\n\t\tmin: 50,\n\t\tmax: 200,\n\t\tsteps: 7,\n\t});\n\tconst enemyFiringSoonThreshold = useParam('enemyFiringSoonThreshold', 50, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst vulnerabilityTicks = getEnemyVulnerabilityTicks(enemy);\n\n\tconst move = aggressiveMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t);\n\n\t// Precompute blink targets — used by blink-dodge and engage-blink.\n\t// blinkCloseTarget: from getBlinkToDecreaseDistance (dodge + close gap). Null at melee range.\n\t// blinkPastTarget: blink ~200u past enemy (dodge only, loses position). For in-flight missiles only.\n\tlet blinkCloseTarget: {x: number; y: number} | null = null;\n\tlet blinkPastTarget: {x: number; y: number} | null = null;\n\tif (state.blinkCooldown === 0) \n\t{\n\t\tblinkCloseTarget = getBlinkToDecreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t);\n\t\tif (!blinkCloseTarget) \n\t\t{\n\t\t\tconst dx = enemy.position.x - state.position.x;\n\t\t\tconst dy = enemy.position.y - state.position.y;\n\t\t\tconst norm = Math.sqrt(dx * dx + dy * dy) || 1;\n\t\t\tblinkPastTarget = {\n\t\t\t\tx: Math.max(\n\t\t\t\t\tblinkPastMargin,\n\t\t\t\t\tMath.min(\n\t\t\t\t\t\tARENA_SIZE - blinkPastMargin,\n\t\t\t\t\t\tstate.position.x + (dx / norm) * blinkPastDistance,\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t\ty: Math.max(\n\t\t\t\t\tblinkPastMargin,\n\t\t\t\t\tMath.min(\n\t\t\t\t\t\tARENA_SIZE - blinkPastMargin,\n\t\t\t\t\t\tstate.position.y + (dy / norm) * blinkPastDistance,\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 1: CHANNELING — hold shield until missile passes ===\n\tif (state.state === 'channeling') \n\t{\n\t\t// Hold shield while there's an active threat OR enemy is about to fire\n\t\tconst enemyFiringSoon =\n\t\t\tdistance < preemptiveShieldRange &&\n enemy.state === 'casting' &&\n enemy.castingSpell === 'missile' &&\n (enemy.castDuration ?? 0) -\n (enemy.castProgress ?? 0) +\n distance / enemyFlightTimeDivisor <\n enemyFiringSoonThreshold;\n\n\t\tif (\n\t\t\t(!urgentThreat && !enemyFiringSoon) ||\n (urgentThreat && urgentThreat.ticksToImpact > shieldCancelThreshold)\n\t\t) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === STEP 2: CAST-CANCEL INTO DEFENSE ===\n\t// If we're casting a stab and a missile is incoming, cancel so we can\n\t// blink-dodge (if available) or shield. Defense time depends on blink availability.\n\tif (state.state === 'casting' && state.castingSpell === 'missile') \n\t{\n\t\t// Cancel if target escaped melee range\n\t\tif (distance > meleeAbortRange) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\n\t\t// Defense time: blink (10t) is faster than shield (20t + 3t buffer)\n\t\tconst defenseTime =\n\t\t\tstate.blinkCooldown === 0\n\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\n\t\t// Cancel for any undodgeable incoming threat we can't finish casting before\n\t\tif (urgentThreat && !urgentThreat.bestDodgeDirection) \n\t\t{\n\t\t\tconst remainingCast =\n\t\t\t\t(state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\t\tconst willArriveInWindow =\n\t\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + defenseTime;\n\t\t\tif (willArriveInWindow) \n\t\t\t{\n\t\t\t\treturn {move, cancel: true};\n\t\t\t}\n\t\t}\n\n\t\t// Cancel when enemy starts casting a missile at close range —\n\t\t// at melee range their missile will arrive too fast to react after our cast finishes\n\t\tif (\n\t\t\tdistance < preemptiveShieldRange &&\n enemy.state === 'casting' &&\n enemy.castingSpell === 'missile'\n\t\t) \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst remainingOurCast =\n\t\t\t\t(state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\t\tconst estimatedFlightTicks = distance / enemyFlightTimeDivisor;\n\t\t\tconst ticksUntilEnemyHit = remainingEnemyCast + estimatedFlightTicks;\n\t\t\tconst ticksAfterOurCast = remainingOurCast + GCD_DURATION + defenseTime;\n\n\t\t\t// If enemy missile will arrive before we can defend after our cast, cancel now\n\t\t\tif (ticksUntilEnemyHit < ticksAfterOurCast) \n\t\t\t{\n\t\t\t\treturn {move, cancel: true};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === STEP 3: BLINK-DODGE — 100% damage avoidance (preferred over shield) ===\n\tif (state.state === 'idle' && state.blinkCooldown === 0) \n\t{\n\t\t// Blink target for dodging: close-gap target if available, else blink-past for in-flight only\n\t\tconst dodgeBlinkTarget = blinkCloseTarget ?? blinkPastTarget;\n\n\t\t// 3a: Blink-dodge an in-flight missile (missile already fired, can't be cancelled)\n\t\tif (\n\t\t\tpreferBlinkDodge &&\n dodgeBlinkTarget &&\n urgentThreat &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: dodgeBlinkTarget},\n\t\t\t};\n\t\t}\n\n\t\t// 3b: Preemptive blink-dodge — only when we have a close-gap target.\n\t\t// At melee range, preemptive blink loses positioning (200u walk back) and\n\t\t// the enemy can cancel their cast. Shield is better for preemptive defense.\n\t\tif (\n\t\t\tpreferBlinkDodge &&\n blinkCloseTarget &&\n distance < preemptiveShieldRange &&\n enemy.state === 'casting' &&\n enemy.castingSpell === 'missile'\n\t\t) \n\t\t{\n\t\t\tconst remainingCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTicks = distance / enemyFlightTimeDivisor;\n\t\t\tconst ticksUntilHit = remainingCast + estimatedFlightTicks;\n\n\t\t\tif (\n\t\t\t\tticksUntilHit >= BLINK_CAST_TIME &&\n ticksUntilHit < BLINK_CAST_TIME + shieldWindow\n\t\t\t) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'blink', target: blinkCloseTarget},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// 3c: Engage blink — close distance during enemy vulnerability (they can't react)\n\t\tif (\n\t\t\tblinkCloseTarget &&\n distance > engageBlinkRange &&\n vulnerabilityTicks >= punishMinVulnerability &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkCloseTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 4: SHIELD — fallback when blink can't help (90% block) ===\n\n\t// 4a: Shield in-flight missiles\n\t// Shield when: no blink target at all, or missile too close for blink (<10 ticks)\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&\n urgentThreat.canBlockInTime &&\n ((!blinkCloseTarget && !blinkPastTarget) || urgentThreat.ticksToImpact < 10)\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'shield'},\n\t\t};\n\t}\n\n\t// 4b: Preemptive shield — when blink can't close gap (melee range or cooldown)\n\tif (\n\t\tstate.state === 'idle' &&\n !blinkCloseTarget &&\n distance < preemptiveShieldRange &&\n enemy.state === 'casting' &&\n enemy.castingSpell === 'missile'\n\t) \n\t{\n\t\tconst remainingCast = (enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\tconst estimatedFlightTicks = distance / enemyFlightTimeDivisor;\n\t\tconst ticksUntilHit = remainingCast + estimatedFlightTicks;\n\n\t\tif (\n\t\t\tticksUntilHit >= SHIELD_CAST_TIME &&\n ticksUntilHit < SHIELD_CAST_TIME + shieldWindow\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 5: COUNTER-STAB — fire during enemy vulnerability ===\n\t// Only fire when enemy CAN'T shield (in GCD or mid-cast).\n\t// Use small, fast stabs that fit within the vulnerability window.\n\tif (state.state === 'idle' && distance < counterStabRange) \n\t{\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst flightTime = Math.ceil(distance / flightTimeDivisor) + 1;\n\n\t\tif (vulnerabilityTicks >= punishMinVulnerability) \n\t\t{\n\t\t\t// Budget: stab must land while enemy is still vulnerable\n\t\t\t// castTime + flightTime < vulnerabilityTicks\n\t\t\tconst punishBudget = vulnerabilityTicks - flightTime;\n\n\t\t\t// Also need time to shield if another missile is incoming\n\t\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t\t// Account for enemy's active cast — their missile will arrive after cast completes + flight time\n\t\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t\t{\n\t\t\t\tconst remainingEnemyCast =\n\t\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\t\tconst estimatedFlightTime = distance / enemyFlightTimeDivisor;\n\t\t\t\tnextThreatTime = Math.min(\n\t\t\t\t\tnextThreatTime,\n\t\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Defense time: blink (10t) is faster than shield (20t + 3t buffer)\n\t\t\tconst defenseTime =\n\t\t\t\tstate.blinkCooldown === 0\n\t\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\t\tconst safeBudget = nextThreatTime - GCD_DURATION - defenseTime;\n\t\t\tconst effectiveBudget = Math.min(punishBudget, safeBudget);\n\n\t\t\tif (effectiveBudget > 0) \n\t\t\t{\n\t\t\t\tconst config = fitMissileToBudget(effectiveBudget, distance, {\n\t\t\t\t\tminTurnRate: 0,\n\t\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t\t});\n\n\t\t\t\tif (config && config.damage >= MIN_USEFUL_DAMAGE) \n\t\t\t\t{\n\t\t\t\t\tconst castTime = getMissileCastTime(config, state.lastMissileConfig);\n\t\t\t\t\tif (castTime + flightTime <= vulnerabilityTicks) \n\t\t\t\t\t{\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tmove,\n\t\t\t\t\t\t\tstartCast: {\n\t\t\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\tmissileAI: () => ({}),\n\t\t\t\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Enemy is idle or channeling shield at close range — fire a quick stab to provoke.\n\t\t// Idle: they must shield or eat damage.\n\t\t// Channeling: shield blocks ~90% but 10% still lands. Stabs whittle HP so they\n\t\t// can't just hold shield forever. Why: otherwise the bot waits for a\n\t\t// vulnerability window that a channeling player never opens.\n\t\tif ((enemy.state === 'idle' || enemy.state === 'channeling') && !urgentThreat)\n\t\t{\n\t\t\t// Use a small budget to keep stabs fast and stay reactive\n\t\t\tconst quickBudget = idleProvokeBudget;\n\t\t\tconst config = fitMissileToBudget(quickBudget, distance, {\n\t\t\t\tminTurnRate: 0,\n\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t});\n\n\t\t\tif (config) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove,\n\t\t\t\t\tstartCast: {\n\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\tmissileAI: () => ({}),\n\t\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\n\nconst SimpleHoming: MissileAIFunction = ({worldState}) => \n{\n\t// Note: random is available but not used by this simple homing AI\n\tconst enemy = worldState.enemies[0];\n\treturn {\n\t\tturnToward: enemy?.position,\n\t};\n};\n\n/**\n * Bot: Bonemancer\n *\n * BEHAVIOR: Stands still and fires slow, homing missiles constantly. Every missile\n * tracks the enemy with turnRate 2 — they curve relentlessly toward the target.\n * No movement, no shields, just an unending stream of seeking projectiles. The\n * missiles are slow (speed 3) but long-lived (300 ticks) and will chase you across\n * the entire arena.\n *\n * NAMING RATIONALE: Named after Diablo 2's Bone Necromancer (\"Bonemancer\"), whose\n * signature spell Bone Spirit is a slow-moving, auto-tracking projectile that hunts\n * enemies relentlessly. That's exactly what this bot does — it stands in place and\n * spams seeking missiles. The homing behavior is the key identity: these aren't\n * aimed shots, they're heat-seeking spirits that chase you down. Every D2 player\n * knows the Bonemancer — it's one of the most iconic builds.\n *\n * PROGRESSION LINE: Bonemancer → Lich → Archlich\n * - Bonemancer (tier 1): Stationary, spams slow homing missiles\n * - Lich (tier 2): Future — enhanced homing with adaptive missiles and defense\n * - Archlich (tier 3): Future — master of tracking magic, undodgeable death swarm\n * The progression follows the D2 necromancer power fantasy: from bone apprentice\n * to undead overlord, each tier's missiles become harder to escape.\n *\n * TIER: 1 (base)\n */\nexport const Bonemancer: WizardFunction = ({state}) => \n{\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tif (state.state === 'idle') \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {\n\t\t\t\tspell: 'missile',\n\t\t\t\tconfig: {\n\t\t\t\t\tdamage: 12,\n\t\t\t\t\tspeed: 3,\n\t\t\t\t\tturnRate: 2,\n\t\t\t\t\tduration: 300,\n\t\t\t\t},\n\t\t\t\tmissileAI: SimpleHoming,\n\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t},\n\t\t};\n\t}\n\n\treturn {\n\t\tmove: {x: 0, y: 0},\n\t};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n\tfitMissileToBudget,\n\tshouldForceShield,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst WALL_BUFFER = 60;\n\nconst SHIELD_BUFFER = 3;\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\nfunction smartStrafe(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\tif (normalizedDistance === 0) return {x: 0, y: 0};\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDir: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDir = dodgeDirection;\n\t}\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDir =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDir = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tconst intensity = dodgeDirection ? 100 : strafeIntensity;\n\treturn {x: strafeDir.x * intensity, y: strafeDir.y * intensity};\n}\n\n/**\n * Bot: Lich\n *\n * BEHAVIOR: Homing missile specialist with strong-tracking adaptive missiles.\n * Uses fitMissileToBudget with minTurnRate 1.0 — higher than other bots (0.5) —\n * producing missiles with superior tracking at the cost of some damage/speed.\n * Strafing launches missiles from different angles, creating multi-angle pressure.\n *\n * Shields undodgeable/critical threats, emergency blinks. Cancels missile cast\n * only for lethal incoming damage.\n *\n * KEY DIFFERENCES FROM BONEMANCER:\n * - Bonemancer: stationary, no defense, fixed d=7/s=3/t=2/dur=300\n * - Lich: mobile, full defense, adaptive strong-tracking homing missiles\n *\n * PROGRESSION LINE: Bonemancer → Lich → Archlich\n * - Bonemancer (tier 1): Stationary, spams fixed slow homing missiles, no defense\n * - Lich (tier 2): Mobile + defense, adaptive strong-tracking missiles (minTurnRate 1.0)\n * - Archlich (tier 3): Future — converging web patterns, impossible to escape\n *\n * TIER: 2 (enhanced Bonemancer)\n */\nexport const Lich: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 768, {\n\t\trange: 175,\n\t\tmin: 0,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', 0.4, {range: 1.5, steps: 5});\n\tconst shieldHealthThreshold = useParam('shieldHealthThreshold', 99, {\n\t\trange: 23,\n\t\tmin: 1,\n\t\tsteps: 7,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 138, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 100, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 62, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 7, {\n\t\trange: 30,\n\t\tmin: 5,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 27, {\n\t\trange: 30,\n\t\tmin: 15,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 5.8, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst strafe = smartStrafe(\n\t\tstate.position,\n\t\tenemy,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t);\n\n\t// Distance management (retreat if too close, approach if too far)\n\tlet moveX = strafe.x;\n\tlet moveY = strafe.y;\n\n\tif (!dodgeDirection) \n\t{\n\t\tconst deltaX = enemy.position.x - state.position.x;\n\t\tconst deltaY = enemy.position.y - state.position.y;\n\t\tconst normalizedDistance = distance > 0 ? distance : 1;\n\n\t\tif (distance > targetDistance + distanceDeadZone) \n\t\t{\n\t\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t\telse if (distance < targetDistance - distanceDeadZone) \n\t\t{\n\t\t\tmoveX -= (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY -= (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\tconst move = {x: moveX, y: moveY};\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast only for LETHAL incoming damage ===\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER\n\t\t) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK: Dodge missile + reposition (first priority) ===\n\t// Blink dodges the incoming missile — no GCD after, so can immediately resume casting.\n\t// Only shield when blink is on cooldown.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t};\n\t}\n\n\t// === DEFENSE: Shield threats (when blink on cooldown) ===\n\t// Shield undodgeable, high-damage homing, or when health is critical\n\tif (state.state === 'idle' && urgentThreat) \n\t{\n\t\tconst forceShield = shouldForceShield(urgentThreat);\n\t\tconst undodgeable = !urgentThreat.bestDodgeDirection;\n\t\tconst healthLow = state.health < shieldHealthThreshold;\n\t\tconst healthCritical =\n\t\t\tstate.health <= urgentThreat.projectile.damage * 2 + 1;\n\t\tconst doShield =\n\t\t\tforceShield || (undodgeable && healthLow) || healthCritical;\n\n\t\tif (\n\t\t\tdoShield &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= SHIELD_BUFFER\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === OFFENSE: Adaptive strong-tracking homing missiles ===\n\t// minTurnRate 1.0 gives better tracking than other bots (0.5) at the cost of damage/speed.\n\t// Skip offense when a lethal missile could reach us during cast+GCD — dodge at full speed instead\n\tconst myProjectileIds = new Set(state.myProjectiles.map((p) => p.id));\n\tconst hasNearbyLethalMissile = state.projectiles.some(\n\t\t(p) =>\n\t\t\t!myProjectileIds.has(p.id) &&\n p.damage >= state.health &&\n distanceTo(state.position, p.position) / p.speed <\n MAX_CAST_BUDGET + GCD_DURATION,\n\t);\n\tif (state.state === 'idle' && !hasNearbyLethalMissile) \n\t{\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Account for enemy's active cast — their missile will arrive after cast completes + flight time\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTime = distance / flightTimeDivisor;\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t);\n\t\t}\n\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Lich specializes in strong tracking (minTurnRate 1.0 vs typical 0.5)\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (config && config.damage >= minDmg) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Budget too small for useful missile — wait for threat to pass, then fire\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {getMissileCastTime, getLeadPosition} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n\tfitMissileToBudget,\n\tshouldForceShield,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n\tgetEnemyVulnerabilityTicks,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst WALL_BUFFER = 60;\nconst SHIELD_BUFFER = 3;\n\nconst HomingAI: MissileAIFunction = ({worldState}) =>\n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\nconst StraightAI: MissileAIFunction = () => ({});\n\nfunction isNearWall(position: {x: number; y: number}): boolean\n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\nfunction smartStrafe(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n): {x: number; y: number}\n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\tif (normalizedDistance === 0) return {x: 0, y: 0};\n\n\tconst cw = {x: -deltaY / normalizedDistance, y: deltaX / normalizedDistance};\n\tconst ccw = {x: deltaY / normalizedDistance, y: -deltaX / normalizedDistance};\n\n\tlet dir: {x: number; y: number};\n\n\tif (dodgeDirection)\n\t{\n\t\tdir = dodgeDirection;\n\t}\n\telse if (isNearWall(position))\n\t{\n\t\tconst fCw = {x: position.x + cw.x * 100, y: position.y + cw.y * 100};\n\t\tconst fCcw = {x: position.x + ccw.x * 100, y: position.y + ccw.y * 100};\n\t\tconst sCw = Math.min(fCw.x, ARENA_SIZE - fCw.x, fCw.y, ARENA_SIZE - fCw.y);\n\t\tconst sCcw = Math.min(fCcw.x, ARENA_SIZE - fCcw.x, fCcw.y, ARENA_SIZE - fCcw.y);\n\t\tdir = sCw > sCcw ? cw : ccw;\n\t}\n\telse\n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tdir = cycle === 0 ? cw : ccw;\n\t}\n\n\tconst intensity = dodgeDirection ? 100 : strafeIntensity;\n\treturn {x: dir.x * intensity, y: dir.y * intensity};\n}\n\n/**\n * Bot: Archlich\n *\n * BEHAVIOR: Lich's proven core (mobile homing specialist) plus vulnerability\n * exploitation. Defense, movement, and standard offense are identical to Lich.\n * The T3 advantage: when the enemy is locked in GCD/cast, fires fast straight\n * punish missiles that land during the vulnerability window.\n *\n * PROGRESSION LINE: Bonemancer → Lich → Archlich\n * TIER: 3 (elite Homing line)\n */\nexport const Archlich: WizardFunction = ({state}) =>\n{\n\t// Same params as Lich — Archlich is Lich + punish mode (disabled by default, optimizer tunes)\n\tconst targetDistance = useParam('targetDistance', 737, {range: 175, min: 0});\n\tconst minTurnRate = useParam('minTurnRate', 0.4, {range: 1.5, steps: 5});\n\tconst shieldHealthThreshold = useParam('shieldHealthThreshold', 99, {range: 23, min: 1, steps: 7});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 300, {range: 75, min: 80, max: 300});\n\tconst strafeIntensity = useParam('strafeIntensity', 96, {range: 40, min: 20, max: 100, steps: 7});\n\tconst approachSpeed = useParam('approachSpeed', 65, {range: 30, min: 10, max: 70, steps: 7});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 7, {range: 30, min: 5, max: 80, steps: 7});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {range: 75, min: 50, max: 300});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 38, {range: 30, min: 15, max: 80, steps: 7});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 5.8, {range: 4, min: 2, max: 12, steps: 5});\n\tconst punishMinVulnerability = useParam('punishMinVulnerability', 500, {range: 80, min: 30, steps: 7});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) return {move: {x: 0, y: 0}};\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst strafe = smartStrafe(state.position, enemy, dodgeDirection, state.tick, strafeCyclePeriod, strafeIntensity);\n\n\tlet moveX = strafe.x;\n\tlet moveY = strafe.y;\n\tif (!dodgeDirection)\n\t{\n\t\tconst dx = enemy.position.x - state.position.x;\n\t\tconst dy = enemy.position.y - state.position.y;\n\t\tconst nd = distance > 0 ? distance : 1;\n\t\tif (distance > targetDistance + distanceDeadZone)\n\t\t{\n\t\t\tmoveX += (dx / nd) * approachSpeed;\n\t\t\tmoveY += (dy / nd) * approachSpeed;\n\t\t}\n\t\telse if (distance < targetDistance - distanceDeadZone)\n\t\t{\n\t\t\tmoveX -= (dx / nd) * approachSpeed;\n\t\t\tmoveY -= (dy / nd) * approachSpeed;\n\t\t}\n\t}\n\tconst mag = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (mag > 100) \n\t{\n\t\tmoveX = (moveX / mag) * 100; moveY = (moveY / mag) * 100; \n\t}\n\tconst move = {x: moveX, y: moveY};\n\n\t// === DEFENSE: identical to Lich ===\n\tif (state.state === 'channeling')\n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) return {move, cancel: true};\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tif (state.state === 'casting' && state.castingSpell === 'missile' && urgentThreat && urgentThreat.projectile.damage >= state.health)\n\t{\n\t\tconst rem = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (urgentThreat.ticksToImpact <= rem + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER) return {move, cancel: true};\n\t}\n\n\tif (state.state === 'idle' && urgentThreat && state.blinkCooldown === 0 && urgentThreat.ticksToImpact >= 10 && urgentThreat.ticksToImpact <= blinkWindowMax)\n\t{\n\t\treturn {move: {x: 0, y: 0}, startCast: {spell: 'blink', target: getBlinkToCenter(state.position)}};\n\t}\n\n\tif (state.state === 'idle' && urgentThreat)\n\t{\n\t\tconst forceShield = shouldForceShield(urgentThreat);\n\t\tconst undodgeable = !urgentThreat.bestDodgeDirection;\n\t\tconst healthLow = state.health < shieldHealthThreshold;\n\t\tconst healthCritical = state.health <= urgentThreat.projectile.damage * 2 + 1;\n\t\tif ((forceShield || (undodgeable && healthLow) || healthCritical) && urgentThreat.canBlockInTime && urgentThreat.ticksToStartShield <= SHIELD_BUFFER)\n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, startCast: {spell: 'shield'}};\n\t\t}\n\t}\n\n\t// === OFFENSE ===\n\tconst myIds = new Set(state.myProjectiles.map((p) => p.id));\n\tconst hasLethal = state.projectiles.some((p) => !myIds.has(p.id) && p.damage >= state.health && distanceTo(state.position, p.position) / p.speed < MAX_CAST_BUDGET + GCD_DURATION);\n\tif (state.state === 'idle' && !hasLethal)\n\t{\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\n\t\t// T3 PUNISH: fast straight missiles during vulnerability\n\t\tconst vulnTicks = getEnemyVulnerabilityTicks(enemy);\n\t\tif (vulnTicks >= punishMinVulnerability && distance < 300)\n\t\t{\n\t\t\tconst flight = distance / flightTimeDivisor;\n\t\t\tconst pBudget = vulnTicks - flight;\n\t\t\tconst ntt = urgentThreat?.ticksToImpact ?? Infinity;\n\t\t\tconst sBudget = ntt - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\t\tconst eBudget = Math.min(pBudget, sBudget, MAX_CAST_BUDGET);\n\t\t\tif (eBudget > 0)\n\t\t\t{\n\t\t\t\tconst cfg = fitMissileToBudget(eBudget, distance, {minTurnRate: 0, maxDamage: enemyHP, lastMissileConfig: state.lastMissileConfig});\n\t\t\t\tif (cfg && cfg.damage >= MIN_USEFUL_DAMAGE)\n\t\t\t\t{\n\t\t\t\t\tconst ct = getMissileCastTime(cfg, state.lastMissileConfig);\n\t\t\t\t\tif (ct + distance / cfg.speed < vulnTicks)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst aim = cfg.turnRate > 0 ? enemy.position : getLeadPosition(enemy.position, enemy.velocity, cfg.speed, state.position);\n\t\t\t\t\t\treturn {move, startCast: {spell: 'missile', config: cfg, missileAI: cfg.turnRate > 0 ? HomingAI : StraightAI, direction: angleTo(state.position, aim)}};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// STANDARD MODE: identical to Lich\n\t\tlet ntt = urgentThreat?.ticksToImpact ?? Infinity;\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile')\n\t\t{\n\t\t\tconst rem = (enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tntt = Math.min(ntt, rem + distance / flightTimeDivisor);\n\t\t}\n\t\tconst sBudget = ntt - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budget = Math.min(sBudget, MAX_CAST_BUDGET);\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tconst cfg = fitMissileToBudget(budget, distance, {minTurnRate, lastMissileConfig: state.lastMissileConfig});\n\t\tif (cfg && cfg.damage >= minDmg)\n\t\t{\n\t\t\treturn {move, startCast: {spell: 'missile', config: cfg, missileAI: HomingAI, direction: angleTo(state.position, enemy.position)}};\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {getMissileCastTime} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tisSafeToAttack,\n\tgetBlinkToCenter,\n\tshouldForceShield,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst WALL_BUFFER = 60;\n\nconst SHIELD_BUFFER = 3;\n\n// Fixed homing missile: damage 12, speed 5, turnRate 1, duration 180\n// Range: 5 × 180 = 900 units (covers arena)\nconst MISSILE_CONFIG = {damage: 12, speed: 5, turnRate: 1, duration: 180};\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\nfunction smartStrafe(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\tif (normalizedDistance === 0) return {x: 0, y: 0};\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDir: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDir = dodgeDirection;\n\t}\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDir =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDir = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tconst intensity = dodgeDirection ? 100 : strafeIntensity;\n\treturn {x: strafeDir.x * intensity, y: strafeDir.y * intensity};\n}\n\n/**\n * Bot: Flamecaller\n *\n * BEHAVIOR: Long-range homing missile caster with fixed missile config.\n * Maintains 350u distance, strafes to dodge, and fires standard homing\n * missiles (d=10, s=5, t=1, dur=180). Shields undodgeable threats,\n * emergency blinks. A straightforward ranged caster that trades\n * consistency for adaptability.\n *\n * PROGRESSION LINE: Flamecaller → Pyromancer → Infernalist\n * - Flamecaller (tier 1): Fixed homing missiles, basic strafe and defense\n * - Pyromancer (tier 2): + adaptive fitting, cast canceling, smart fallbacks\n * - Infernalist (tier 3): Future — overwhelming adaptive fire\n *\n * TIER: 1 (base)\n */\nexport const Flamecaller: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 753, {\n\t\trange: 150,\n\t\tmin: 0,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 273, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 20, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 40, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 56, {\n\t\trange: 30,\n\t\tmin: 5,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst safeAttackCastEstimate = useParam('safeAttackCastEstimate', 35, {\n\t\trange: 25,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst strafe = smartStrafe(\n\t\tstate.position,\n\t\tenemy,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t);\n\n\t// Distance management (retreat if too close, approach if too far)\n\tlet moveX = strafe.x;\n\tlet moveY = strafe.y;\n\n\tif (!dodgeDirection) \n\t{\n\t\tconst deltaX = enemy.position.x - state.position.x;\n\t\tconst deltaY = enemy.position.y - state.position.y;\n\t\tconst normalizedDistance = distance > 0 ? distance : 1;\n\n\t\tif (distance > targetDistance + distanceDeadZone) \n\t\t{\n\t\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t\telse if (distance < targetDistance - distanceDeadZone) \n\t\t{\n\t\t\tmoveX -= (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY -= (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\tconst move = {x: moveX, y: moveY};\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Let shield cast finish ===\n\tif (state.state === 'casting' && state.castingSpell === 'shield') \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (urgentThreat.canBlockInTime) \n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= SHIELD_BUFFER) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\telse if (state.blinkCooldown === 0) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === OFFENSE: Fire fixed homing missile when safe ===\n\tif (state.state === 'idle') \n\t{\n\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\tconst castTime = getMissileCastTime(\n\t\t\tMISSILE_CONFIG,\n\t\t\tstate.lastMissileConfig,\n\t\t);\n\t\tconst safeWindow =\n\t\t\tcastTime + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER;\n\n\t\tif (\n\t\t\tnextThreatTime > safeWindow &&\n isSafeToAttack(threats, safeAttackCastEstimate)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: MISSILE_CONFIG,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tisSafeToAttack,\n\tgetBlinkToCenter,\n\tfitMissileToBudget,\n\tshouldForceShield,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 60;\n\nconst SHIELD_BUFFER = 3;\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\nfunction smartStrafe(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\tif (normalizedDistance === 0) return {x: 0, y: 0};\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDir: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDir = dodgeDirection;\n\t}\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDir =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDir = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tconst intensity = dodgeDirection ? 100 : strafeIntensity;\n\treturn {x: strafeDir.x * intensity, y: strafeDir.y * intensity};\n}\n\n/**\n * Bot: Pyromancer\n *\n * BEHAVIOR: Adaptive homing missile specialist at long range. Maintains 400u\n * distance, strafes to dodge, and uses fitMissileToBudget with minTurnRate 0.5\n * to fire the highest-damage homing missile that fits in the safe window.\n * Shields undodgeable threats, emergency blinks. A versatile ranged caster\n * that adapts its missiles to the situation.\n *\n * PROGRESSION LINE: Flamecaller → Pyromancer → Infernalist\n * - Flamecaller (tier 1): Fixed homing missiles, basic strafe and defense\n * - Pyromancer (tier 2): + adaptive fitting, cast canceling, smart fallbacks\n * - Infernalist (tier 3): Future — overwhelming adaptive fire\n *\n * TIER: 2 (enhanced Flamecaller)\n */\nexport const Pyromancer: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 643, {\n\t\trange: 225,\n\t\tmin: 0,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', 0.2, {range: 1.5, steps: 5});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 129, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 33, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 69, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 18, {\n\t\trange: 30,\n\t\tmin: 5,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 26, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 5.3, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst safeAttackCastEstimate = useParam('safeAttackCastEstimate', 35, {\n\t\trange: 25,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst strafe = smartStrafe(\n\t\tstate.position,\n\t\tenemy,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t);\n\n\t// Distance management (retreat if too close, approach if too far)\n\tlet moveX = strafe.x;\n\tlet moveY = strafe.y;\n\n\tif (!dodgeDirection) \n\t{\n\t\tconst deltaX = enemy.position.x - state.position.x;\n\t\tconst deltaY = enemy.position.y - state.position.y;\n\t\tconst normalizedDistance = distance > 0 ? distance : 1;\n\n\t\tif (distance > targetDistance + distanceDeadZone) \n\t\t{\n\t\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t\telse if (distance < targetDistance - distanceDeadZone) \n\t\t{\n\t\t\tmoveX -= (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY -= (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\tconst move = {x: moveX, y: moveY};\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast only for LETHAL incoming damage ===\n\t// DPS trade: we accepted the hit when we started casting. Only cancel to survive.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER\n\t\t) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK: Dodge missile + reposition ===\n\t// Blink dodges the incoming missile — no GCD after, so can immediately fire back.\n\t// Wide window (10-80) prevents idle gaps where budget is too small for a missile\n\t// but the threat is too far for the old narrow blink window.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t};\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats (when blink on cooldown) ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (urgentThreat.canBlockInTime) \n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= SHIELD_BUFFER) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === OFFENSE: Adaptive homing missiles ===\n\t// Skip offense when a lethal missile could reach us during cast+GCD — dodge at full speed instead\n\tconst myProjectileIds = new Set(state.myProjectiles.map((p) => p.id));\n\tconst hasNearbyLethalMissile = state.projectiles.some(\n\t\t(p) =>\n\t\t\t!myProjectileIds.has(p.id) &&\n p.damage >= state.health &&\n distanceTo(state.position, p.position) / p.speed <\n MAX_CAST_BUDGET + GCD_DURATION,\n\t);\n\tif (state.state === 'idle' && !hasNearbyLethalMissile) \n\t{\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Account for enemy's active cast — their missile will arrive after cast completes + flight time\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTime = distance / flightTimeDivisor;\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t);\n\t\t}\n\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Pyromancer wants homing missiles — no maxDamage cap so missiles punch through shields\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (\n\t\t\tconfig &&\n config.damage >= minDmg &&\n isSafeToAttack(threats, safeAttackCastEstimate)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Budget too small for useful missile — wait for threat to pass, then fire\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {\n\tARENA_SIZE,\n\tGCD_DURATION,\n\tSHIELD_CAST_TIME,\n\tBLINK_CAST_TIME,\n} from '../../rules.js';\nimport {getMissileCastTime, getLeadPosition} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tisSafeToAttack,\n\tgetBlinkToIncreaseDistance,\n\tfitMissileToBudget,\n\tshouldForceShield,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n\tgetClosingRate,\n\tgetEnemyVulnerabilityTicks,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst WALL_BUFFER = 60;\nconst SHIELD_BUFFER = 3;\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\nconst StraightAI: MissileAIFunction = () => ({});\n\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\nfunction smartStrafe(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst len = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\tif (len === 0) return {x: 0, y: 0};\n\n\tconst cw = {x: -deltaY / len, y: deltaX / len};\n\tconst ccw = {x: deltaY / len, y: -deltaX / len};\n\n\tlet dir: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tdir = dodgeDirection;\n\t}\n\telse if (isNearWall(position)) \n\t{\n\t\tconst fCw = {x: position.x + cw.x * 100, y: position.y + cw.y * 100};\n\t\tconst fCcw = {x: position.x + ccw.x * 100, y: position.y + ccw.y * 100};\n\t\tconst sCw = Math.min(fCw.x, ARENA_SIZE - fCw.x, fCw.y, ARENA_SIZE - fCw.y);\n\t\tconst sCcw = Math.min(\n\t\t\tfCcw.x,\n\t\t\tARENA_SIZE - fCcw.x,\n\t\t\tfCcw.y,\n\t\t\tARENA_SIZE - fCcw.y,\n\t\t);\n\t\tdir = sCw > sCcw ? cw : ccw;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tdir = cycle === 0 ? cw : ccw;\n\t}\n\n\tconst intensity = dodgeDirection ? 100 : strafeIntensity;\n\treturn {x: dir.x * intensity, y: dir.y * intensity};\n}\n\n/**\n * Bot: Infernalist\n *\n * BEHAVIOR: Rapid-fire caster that exploits warmup bonus for accelerating DPS.\n * Fires consistent homing missiles to build warmup, punishes vulnerability windows\n * with warmup-boosted fast casts. Proactive blink kiting when enemy closes.\n *\n * KEY IMPROVEMENTS OVER PYROMANCER:\n * - Warmup exploitation: always passes lastMissileConfig for bonus\n * - Punish mode: straight missiles during enemy vulnerability\n * - Proactive blink kiting: monitors closing rate\n * - Progressive cast-cancel: graduated thresholds\n *\n * PROGRESSION LINE: Flamecaller → Pyromancer → Infernalist\n * TIER: 3 (elite Caster line)\n */\nexport const Infernalist: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 285, {range: 225, min: 0});\n\tconst minTurnRate = useParam('minTurnRate', 0.2, {range: 1.5, steps: 5});\n\tconst dangerDistance = useParam('dangerDistance', 222, {\n\t\trange: 150,\n\t\tmin: 0,\n\t});\n\tconst closingRateThreshold = useParam('closingRateThreshold', 0.94, {\n\t\trange: 0.65,\n\t\tmin: 0,\n\t\tsteps: 5,\n\t});\n\tconst punishMinVulnerability = useParam('punishMinVulnerability', 340, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tsteps: 5,\n\t});\n\tconst preferBlinkDodge =\n\t\tuseParam('preferBlinkDodge', 0, {min: 0, max: 1, steps: 2}) >= 0.5;\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 109, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 250,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 23, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 53, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 34, {\n\t\trange: 30,\n\t\tmin: 5,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst cancelHeavyDmg = useParam('cancelHeavyDmg', 40, {\n\t\trange: 15,\n\t\tmin: 5,\n\t\tmax: 40,\n\t\tsteps: 5,\n\t});\n\tconst cancelMediumDmg = useParam('cancelMediumDmg', 9, {\n\t\trange: 10,\n\t\tmin: 3,\n\t\tmax: 25,\n\t\tsteps: 5,\n\t});\n\tconst cancelCastRatio = useParam('cancelCastRatio', 0.78, {\n\t\trange: 0.4,\n\t\tmin: 0.3,\n\t\tmax: 1.0,\n\t\tsteps: 5,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 100, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 75, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst proactiveBlinkRange = useParam('proactiveBlinkRange', 100, {\n\t\trange: 150,\n\t\tmin: 100,\n\t\tmax: 500,\n\t\tsteps: 7,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 5.4, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst safeAttackCastEstimate = useParam('safeAttackCastEstimate', 35, {\n\t\trange: 25,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst strafe = smartStrafe(\n\t\tstate.position,\n\t\tenemy,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t);\n\n\t// Distance management with wall-aware retreat\n\tlet moveX = strafe.x;\n\tlet moveY = strafe.y;\n\n\tif (!dodgeDirection) \n\t{\n\t\tconst deltaX = enemy.position.x - state.position.x;\n\t\tconst deltaY = enemy.position.y - state.position.y;\n\t\tconst norm = distance > 0 ? distance : 1;\n\n\t\tif (distance > targetDistance + distanceDeadZone) \n\t\t{\n\t\t\tmoveX += (deltaX / norm) * approachSpeed;\n\t\t\tmoveY += (deltaY / norm) * approachSpeed;\n\t\t}\n\t\telse if (distance < targetDistance - distanceDeadZone) \n\t\t{\n\t\t\tlet rx = -(deltaX / norm) * approachSpeed;\n\t\t\tlet ry = -(deltaY / norm) * approachSpeed;\n\t\t\tif (state.position.x < WALL_BUFFER && rx < 0) rx = 0;\n\t\t\tif (state.position.x > ARENA_SIZE - WALL_BUFFER && rx > 0) rx = 0;\n\t\t\tif (state.position.y < WALL_BUFFER && ry < 0) ry = 0;\n\t\t\tif (state.position.y > ARENA_SIZE - WALL_BUFFER && ry > 0) ry = 0;\n\t\t\tmoveX += rx;\n\t\t\tmoveY += ry;\n\t\t}\n\t}\n\n\tconst mag = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (mag > 100) \n\t{\n\t\tmoveX = (moveX / mag) * 100;\n\t\tmoveY = (moveY / mag) * 100;\n\t}\n\n\tconst move = {x: moveX, y: moveY};\n\n\t// === STEP 1: CHANNELING ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === STEP 2: PROGRESSIVE CAST-CANCEL ===\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tconst defenseTime =\n\t\t\tstate.blinkCooldown === 0\n\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tconst willArriveInWindow =\n\t\t\turgentThreat.ticksToImpact <= remainingCast + GCD_DURATION + defenseTime;\n\n\t\tif (willArriveInWindow) \n\t\t{\n\t\t\tconst incomingDamage = urgentThreat.projectile.damage;\n\t\t\tconst castRatio = (state.castProgress ?? 0) / (state.castDuration ?? 1);\n\n\t\t\tif (incomingDamage >= state.health || incomingDamage >= cancelHeavyDmg) \n\t\t\t{\n\t\t\t\treturn {move, cancel: true};\n\t\t\t}\n\t\t\tif (incomingDamage >= cancelMediumDmg && castRatio < cancelCastRatio) \n\t\t\t{\n\t\t\t\treturn {move, cancel: true};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === BLINK-DODGE — optimizer-controlled alternative to shield ===\n\tif (\n\t\tpreferBlinkDodge &&\n state.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&\n urgentThreat.ticksToImpact >= BLINK_CAST_TIME &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (blinkTarget) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 3: SHIELD — undodgeable or high-damage homing threats ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (\n\t\t\turgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= SHIELD_BUFFER\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 5: PROACTIVE BLINK KITING ===\n\tif (\n\t\tstate.state === 'idle' &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst closingRate = getClosingRate(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tenemy.velocity,\n\t\t);\n\t\tif (\n\t\t\t(closingRate > closingRateThreshold && distance < proactiveBlinkRange) ||\n distance < dangerDistance\n\t\t) \n\t\t{\n\t\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\t\tstate.position,\n\t\t\t\tenemy.position,\n\t\t\t\tstate.projectiles,\n\t\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t\t);\n\t\t\tif (blinkTarget) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === STEP 6: OFFENSE ===\n\tif (state.state === 'idle') \n\t{\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst vulnerabilityTicks = getEnemyVulnerabilityTicks(enemy);\n\n\t\t// --- PUNISH MODE: warmup-accelerated missiles during vulnerability ---\n\t\tif (vulnerabilityTicks >= punishMinVulnerability && distance < 500) \n\t\t{\n\t\t\tconst flightTimeEstimate = distance / flightTimeDivisor;\n\t\t\tconst punishBudget = vulnerabilityTicks - flightTimeEstimate;\n\t\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\t\tconst safeBudget =\n\t\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\t\tconst effectiveBudget = Math.min(\n\t\t\t\tpunishBudget,\n\t\t\t\tsafeBudget,\n\t\t\t\tMAX_CAST_BUDGET,\n\t\t\t);\n\n\t\t\tif (effectiveBudget > 0) \n\t\t\t{\n\t\t\t\tconst config = fitMissileToBudget(effectiveBudget, distance, {\n\t\t\t\t\tminTurnRate: 0,\n\t\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t\t});\n\n\t\t\t\tif (config && config.damage >= MIN_USEFUL_DAMAGE) \n\t\t\t\t{\n\t\t\t\t\tconst castTime = getMissileCastTime(config, state.lastMissileConfig);\n\t\t\t\t\tconst actualFlight = distance / config.speed;\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tcastTime + actualFlight < vulnerabilityTicks &&\n isSafeToAttack(threats, castTime)\n\t\t\t\t\t) \n\t\t\t\t\t{\n\t\t\t\t\t\tconst aimTarget =\n\t\t\t\t\t\t\tconfig.turnRate > 0\n\t\t\t\t\t\t\t\t? enemy.position\n\t\t\t\t\t\t\t\t: getLeadPosition(\n\t\t\t\t\t\t\t\t\tenemy.position,\n\t\t\t\t\t\t\t\t\tenemy.velocity,\n\t\t\t\t\t\t\t\t\tconfig.speed,\n\t\t\t\t\t\t\t\t\tstate.position,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tmove,\n\t\t\t\t\t\t\tstartCast: {\n\t\t\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\tmissileAI: config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\t\t\t\tdirection: angleTo(state.position, aimTarget),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// --- STANDARD MODE: warmup-consistent homing missiles ---\n\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tlet config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (\n\t\t\tconfig &&\n config.damage >= minDmg &&\n isSafeToAttack(threats, safeAttackCastEstimate)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Fallback: fire bigger missile, accept incoming hit\n\t\tif (!config || config.damage < minDmg) \n\t\t{\n\t\t\tconfig = fitMissileToBudget(MAX_CAST_BUDGET, distance, {\n\t\t\t\tminTurnRate,\n\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t});\n\t\t\tif (config) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove,\n\t\t\t\t\tstartCast: {\n\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {getMissileCastTime} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tisSafeToAttack,\n\tgetBlinkToCenter,\n\tgetBlinkToIncreaseDistance,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tshouldForceShield,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\nconst WALL_BUFFER = 50;\n\n/**\n * Smart combat movement: dodge using threat analysis, avoid walls, commit to direction.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\tminDistance: number,\n\tmaxDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\t// Priority 1: Use dodge direction (from threat analysis or close missile avoidance)\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\t// Priority 2: Avoid walls\n\telse if (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\t// Priority 3: Alternate strafe direction\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\t// When actively dodging, commit fully — no approach/retreat dilution\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\t// Distance management (only when not dodging)\n\tif (currentDistance > maxDistance) \n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < minDistance) \n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Spellshot\n *\n * BEHAVIOR: Uses interceptAngle to calculate where the enemy will be and fires\n * fast, non-homing missiles (speed 8, turnRate 0) along the predicted path.\n * Strafes at medium range (300-400), shields undodgeable threats, emergency\n * blinks when shield isn't available. The key mechanic is PREDICTION — these\n * missiles don't track, they go exactly where you calculated the enemy would be.\n *\n * NAMING RATIONALE: \"Spellshot\" — a spell that is a single, precisely aimed shot.\n * Like a sniper's \"called shot\" but magical. The defining feature is the intercept\n * calculation: this bot doesn't fire tracking missiles, it calculates the exact\n * angle needed to hit a moving target. \"Shot\" implies precision, singular impact,\n * and skill-based aiming — everything this bot is about.\n *\n * PROGRESSION LINE: Spellshot → Spelltracer → Spellseeker\n * - Spellshot (tier 1): Basic intercept prediction, non-homing missiles\n * - Spelltracer (tier 2): Future — predictive homing (missiles that lead AND track)\n * - Spellseeker (tier 3): Future — perfect prediction, multi-angle attacks\n * The naming progression: shot (single bullet) → tracer (bullet that tracks a path)\n * → seeker (actively hunts). Each tier adds more intelligence to the projectile,\n * evolving from \"I calculate where you'll be\" to \"my missile calculates where you'll be.\"\n *\n * NOTE: A separate future archetype \"Spellslinger\" (volume-of-fire) is reserved\n * for a rapid-fire bot that prioritizes quantity over prediction.\n *\n * TIER: 1 (base)\n */\nexport const Spellshot: WizardFunction = ({state}) => \n{\n\tconst dangerDistance = useParam('dangerDistance', 45, {range: 100, min: 0});\n\tconst targetDistance = useParam('targetDistance', 339, {\n\t\trange: 150,\n\t\tmin: 0,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 22, {\n\t\trange: 50,\n\t\tmin: 0,\n\t});\n\tconst minCombatDistance = targetDistance - distanceDeadZone;\n\tconst maxCombatDistance = targetDistance + distanceDeadZone;\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 230, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 36, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 78, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst missileDamage = useParam('missileDamage', 30, {\n\t\trange: 10,\n\t\tmin: 5,\n\t\tmax: 30,\n\t\tsteps: 5,\n\t});\n\tconst missileSpeed = useParam('missileSpeed', 12, {\n\t\trange: 3,\n\t\tmin: 3,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst missileTurnRate = useParam('missileTurnRate', 0.3, {\n\t\trange: 1.5,\n\t\tsteps: 5,\n\t});\n\tconst missileMinDuration = useParam('missileMinDuration', 55, {\n\t\trange: 30,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst durationRangeFactor = useParam('durationRangeFactor', 0.67, {\n\t\trange: 0.5,\n\t\tmin: 0.5,\n\t\tmax: 2.0,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast if dangerous threat will arrive before we can shield ===\n\t// Only cancel for: high-damage homing (always) or undodgeable when health is at risk\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tconst mustCancel =\n\t\t\tshouldForceShield(urgentThreat) ||\n state.health <= urgentThreat.projectile.damage * 2 + 1;\n\t\tif (mustCancel) \n\t\t{\n\t\t\tconst remainingCast =\n\t\t\t\t(state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\t\tif (\n\t\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + 3\n\t\t\t) \n\t\t\t{\n\t\t\t\tconst dodgeDir = getDodgeDirectionForMovement(threats, state.position);\n\t\t\t\tconst cancelMove = combatMove(\n\t\t\t\t\tstate.position,\n\t\t\t\t\tenemy,\n\t\t\t\t\tdistance,\n\t\t\t\t\tminCombatDistance,\n\t\t\t\t\tmaxCombatDistance,\n\t\t\t\t\tdodgeDir,\n\t\t\t\t\tstate.tick,\n\t\t\t\t\tstrafeCyclePeriod,\n\t\t\t\t\tstrafeIntensity,\n\t\t\t\t\tapproachSpeed,\n\t\t\t\t);\n\t\t\t\treturn {move: cancelMove, cancel: true};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (urgentThreat.canBlockInTime) \n\t\t{\n\t\t\t// Start shield at the right time (small buffer for safety)\n\t\t\tif (urgentThreat.ticksToStartShield <= 3) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\t// Can't dodge, can't shield → blink away (emergency)\n\t\telse if (state.blinkCooldown === 0) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === DISTANCE BLINK: enemy too close, blink to reestablish kiting range ===\n\tif (\n\t\tstate.state === 'idle' &&\n distance < dangerDistance &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (blinkTarget) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// Smart combat movement\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\tminCombatDistance,\n\t\tmaxCombatDistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t);\n\n\t// === OFFENSE: Fast homing missiles ===\n\tif (state.state === 'idle' && distance < 600) \n\t{\n\t\tconst missileConfig = {\n\t\t\tdamage: missileDamage,\n\t\t\tspeed: missileSpeed,\n\t\t\tturnRate: missileTurnRate,\n\t\t\tduration: Math.max(\n\t\t\t\tmissileMinDuration,\n\t\t\t\tMath.ceil((distance * durationRangeFactor) / missileSpeed),\n\t\t\t),\n\t\t};\n\t\tconst castTime = getMissileCastTime(missileConfig, state.lastMissileConfig);\n\t\tif (isSafeToAttack(threats, castTime)) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: missileConfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n\tgetBlinkToIncreaseDistance,\n\tfitMissileToBudget,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tshouldForceShield,\n\tMAX_CAST_BUDGET,\n\tgetEnemyVulnerabilityTicks,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\nconst WALL_BUFFER = 50;\n\nconst SHIELD_BUFFER = 3;\n\n/**\n * Smart combat movement: dodge using threat analysis, avoid walls, commit to direction.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\tminDistance: number,\n\tmaxDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\t// When actively dodging, commit fully\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\t// Distance management\n\tif (currentDistance > maxDistance) \n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < minDistance) \n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Spelltracer\n *\n * BEHAVIOR: Enhanced ranged sniper with adaptive missile fitting and intercept\n * prediction. Uses fitMissileToBudget to find the highest-damage fast missile\n * that fits the safe window, then fires it along the predicted intercept angle.\n * Maintains medium-long range (300-450), shields undodgeable threats with proper\n * timing, emergency blinks, and distance blinks when cornered. The key mechanic\n * is still PREDICTION — but now with adaptive damage optimization.\n *\n * PROGRESSION LINE: Spellshot → Spelltracer → Spellseeker\n * - Spellshot (tier 1): Fixed config intercept prediction, non-homing missiles\n * - Spelltracer (tier 2): + adaptive fitting, timed defense, distance management\n * - Spellseeker (tier 3): Future — perfect prediction, multi-angle attacks\n *\n * TIER: 2 (enhanced Spellshot)\n */\nexport const Spelltracer: WizardFunction = ({state}) => \n{\n\t// Tunable parameters (run optimizer script to find optimal values)\n\tconst dangerDistance = useParam('dangerDistance', 199, {\n\t\trange: 175,\n\t\tmin: 0,\n\t});\n\tconst targetDistance = useParam('targetDistance', 555, {range: 200, min: 0});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 12, {\n\t\trange: 50,\n\t\tmin: 0,\n\t});\n\tconst rangeMin = targetDistance - distanceDeadZone;\n\tconst rangeMax = targetDistance + distanceDeadZone;\n\tconst minTurnRate = useParam('minTurnRate', 0.9, {range: 1.5, steps: 5});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 140, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 48, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 80, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 23, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 12, {\n\t\trange: 4,\n\t\tmin: 3,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst enemyResponseEstimate = useParam('enemyResponseEstimate', 217, {\n\t\trange: 100,\n\t\tmin: 50,\n\t\tmax: 300,\n\t\tsteps: 7,\n\t});\n\tconst threatFallbackThreshold = useParam('threatFallbackThreshold', 250, {\n\t\trange: 100,\n\t\tmin: 100,\n\t\tmax: 400,\n\t\tsteps: 7,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast only for LETHAL incoming damage ===\n\t// DPS trade: we accepted the hit when we started casting. Only cancel to survive.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER\n\t\t) \n\t\t{\n\t\t\tconst dodgeDir = getDodgeDirectionForMovement(threats, state.position);\n\t\t\tconst cancelMove = combatMove(\n\t\t\t\tstate.position,\n\t\t\t\tenemy,\n\t\t\t\tdistance,\n\t\t\t\trangeMin,\n\t\t\t\trangeMax,\n\t\t\t\tdodgeDir,\n\t\t\t\tstate.tick,\n\t\t\t\tstrafeCyclePeriod,\n\t\t\t\tstrafeIntensity,\n\t\t\t\tapproachSpeed,\n\t\t\t);\n\t\t\treturn {move: cancelMove, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK: Dodge undodgeable/forced-shield threats (offensive alternative to shielding) ===\n\t// When we would otherwise shield, blink instead — no GCD after blink, so can fire back immediately.\n\t// Only for threats that can't be dodged by movement (homing/undodgeable).\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {\n\t\t\t\tspell: 'blink',\n\t\t\t\ttarget:\n distance < targetDistance\n \t? (getBlinkToIncreaseDistance(\n \t\tstate.position,\n \t\tenemy.position,\n \t\tstate.projectiles,\n \t) ?? getBlinkToCenter(state.position))\n \t: getBlinkToCenter(state.position),\n\t\t\t},\n\t\t};\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats (when blink on cooldown) ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (urgentThreat.canBlockInTime) \n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= SHIELD_BUFFER) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === DISTANCE BLINK: enemy too close, blink to reestablish range ===\n\tif (\n\t\tstate.state === 'idle' &&\n distance < dangerDistance &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (blinkTarget) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// Smart combat movement\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\trangeMin,\n\t\trangeMax,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t);\n\n\t// === OFFENSE: Adaptive homing missiles ===\n\t// Skip offense when a lethal missile could reach us during cast+GCD — dodge at full speed instead\n\tconst myProjectileIds = new Set(state.myProjectiles.map((p) => p.id));\n\tconst hasNearbyLethalMissile = state.projectiles.some(\n\t\t(p) =>\n\t\t\t!myProjectileIds.has(p.id) &&\n p.damage >= state.health &&\n distanceTo(state.position, p.position) / p.speed <\n MAX_CAST_BUDGET + GCD_DURATION,\n\t);\n\tif (state.state === 'idle' && distance < 600 && !hasNearbyLethalMissile) \n\t{\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Anticipate enemy's next missile based on their current state\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + distance / flightTimeDivisor,\n\t\t\t);\n\t\t}\n\t\t// No imminent threat — estimate enemy's likely response time\n\t\tif (nextThreatTime > threatFallbackThreshold) \n\t\t{\n\t\t\tconst enemyLockTime = getEnemyVulnerabilityTicks(enemy);\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tenemyLockTime + enemyResponseEstimate + distance / flightTimeDivisor,\n\t\t\t);\n\t\t}\n\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Try adaptive fitting for the safe budget\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\t// HIGH-DAMAGE PRIORITY: Sniper missiles need to chip through shields.\n\t\t// Damage 5 through 90% shield = 0.5 chip (useless).\n\t\t// Damage 25 through 90% shield = 2.5 chip (kills in ~24 hits).\n\t\t// Always prefer the adaptive config if damage >= 15; otherwise use a\n\t\t// fixed big-shot fallback that guarantees meaningful shield chip.\n\t\tconst BIG_SHOT = {damage: 25, speed: 10, turnRate: 0.5, duration: 80};\n\n\t\tif (config && config.damage >= 15)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Adaptive fitting produced weak missile or failed — fire big shot instead\n\t\t// This ensures Spelltracer always does meaningful damage, even if it takes\n\t\t// a hit during the longer cast (trade is worthwhile at high damage).\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > 50)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: BIG_SHOT,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Imminent threat — wait for it to pass, then fire\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {\n\tARENA_SIZE,\n\tGCD_DURATION,\n\tSHIELD_CAST_TIME,\n\tBLINK_CAST_TIME,\n} from '../../rules.js';\nimport {getMissileCastTime, getLeadPosition} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n\tgetBlinkToIncreaseDistance,\n\tfitMissileToBudget,\n\tshouldForceShield,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n\tgetClosingRate,\n\tgetEnemyVulnerabilityTicks,\n\tisSafeToAttack,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\nconst StraightAI: MissileAIFunction = () => ({});\n\nconst WALL_BUFFER = 50;\nconst SHIELD_BUFFER = 3;\n\n/**\n * Smart combat movement: dodge, avoid walls, maintain sniper range.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\tminDistance: number,\n\tmaxDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\tif (currentDistance > maxDistance) \n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < minDistance) \n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Spellseeker\n *\n * BEHAVIOR: Elite sniper that uses intercept-aimed straight missiles during vulnerability\n * windows. Combines Spelltracer's adaptive fitting with precise lead-position aiming\n * and vulnerability exploitation. Straight punish missiles at sniper range are nearly\n * unavoidable. Proactive distance control via closing rate detection.\n *\n * KEY IMPROVEMENTS OVER SPELLTRACER:\n * - Intercept-aimed punish: getLeadPosition + straight missiles during vulnerability\n * - Proactive distance blink: monitors closing rate, blinks before danger zone\n * - Progressive cast-cancel: graduated damage thresholds\n * - Warmup exploitation: always passes lastMissileConfig\n *\n * PROGRESSION LINE: Spellshot → Spelltracer → Spellseeker\n * TIER: 3 (elite Sniper line)\n */\nexport const Spellseeker: WizardFunction = ({state}) => \n{\n\tconst dangerDistance = useParam('dangerDistance', 65, {\n\t\trange: 175,\n\t\tmin: 0,\n\t});\n\tconst targetDistance = useParam('targetDistance', 289, {\n\t\trange: 200,\n\t\tmin: 0,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 22, {\n\t\trange: 50,\n\t\tmin: 0,\n\t});\n\tconst rangeMin = targetDistance - distanceDeadZone;\n\tconst rangeMax = targetDistance + distanceDeadZone;\n\tconst minTurnRate = useParam('minTurnRate', 3.5, {range: 1.5, steps: 5});\n\tconst closingRateThreshold = useParam('closingRateThreshold', 1.79, {\n\t\trange: 0.65,\n\t\tmin: 0,\n\t\tsteps: 5,\n\t});\n\tconst punishMinVulnerability = useParam('punishMinVulnerability', 279, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tsteps: 5,\n\t});\n\tconst preferBlinkDodge =\n\t\tuseParam('preferBlinkDodge', 1, {min: 0, max: 1, steps: 2}) >= 0.5;\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 231, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 250,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 87, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 80, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 100, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 88, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst proactiveBlinkRange = useParam('proactiveBlinkRange', 433, {\n\t\trange: 150,\n\t\tmin: 100,\n\t\tmax: 500,\n\t\tsteps: 7,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 12, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst castingFlightTimeDivisor = useParam('castingFlightTimeDivisor', 12, {\n\t\trange: 4,\n\t\tmin: 3,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst enemyResponseEstimate = useParam('enemyResponseEstimate', 168, {\n\t\trange: 100,\n\t\tmin: 50,\n\t\tmax: 300,\n\t\tsteps: 7,\n\t});\n\tconst threatFallbackThreshold = useParam('threatFallbackThreshold', 250, {\n\t\trange: 100,\n\t\tmin: 100,\n\t\tmax: 400,\n\t\tsteps: 7,\n\t});\n\tconst punishAttackRange = useParam('punishAttackRange', 213, {\n\t\trange: 150,\n\t\tmin: 200,\n\t\tmax: 700,\n\t\tsteps: 7,\n\t});\n\tconst safeAttackCastEstimate = useParam('safeAttackCastEstimate', 35, {\n\t\trange: 25,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\trangeMin,\n\t\trangeMax,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t);\n\n\t// === STEP 1: CHANNELING — cancel shield when safe ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === STEP 2: PROGRESSIVE CAST-CANCEL ===\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tconst defenseTime =\n\t\t\tstate.blinkCooldown === 0\n\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tconst willArriveInWindow =\n\t\t\turgentThreat.ticksToImpact <= remainingCast + GCD_DURATION + defenseTime;\n\n\t\tif (willArriveInWindow) \n\t\t{\n\t\t\tconst incomingDamage = urgentThreat.projectile.damage;\n\t\t\tconst castRatio = (state.castProgress ?? 0) / (state.castDuration ?? 1);\n\n\t\t\tif (incomingDamage >= state.health || incomingDamage >= 20) \n\t\t\t{\n\t\t\t\treturn {move, cancel: true};\n\t\t\t}\n\t\t\tif (incomingDamage >= 10 && castRatio < 0.8) \n\t\t\t{\n\t\t\t\treturn {move, cancel: true};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === STEP 3: BLINK-DODGE — 100% damage avoidance (optimizer-controlled) ===\n\tif (\n\t\tpreferBlinkDodge &&\n state.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&\n urgentThreat.ticksToImpact >= BLINK_CAST_TIME &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\tconst midRange = targetDistance;\n\t\tconst target =\n\t\t\tdistance < midRange\n\t\t\t\t? (getBlinkToIncreaseDistance(\n\t\t\t\t\tstate.position,\n\t\t\t\t\tenemy.position,\n\t\t\t\t\tstate.projectiles,\n\t\t\t\t) ?? getBlinkToCenter(state.position))\n\t\t\t\t: getBlinkToCenter(state.position);\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target},\n\t\t};\n\t}\n\n\t// === STEP 4: SHIELD — undodgeable or critical threats ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (\n\t\t\turgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= SHIELD_BUFFER\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 5: PROACTIVE DISTANCE BLINK ===\n\tif (\n\t\tstate.state === 'idle' &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst closingRate = getClosingRate(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tenemy.velocity,\n\t\t);\n\t\tif (\n\t\t\t(closingRate > closingRateThreshold && distance < proactiveBlinkRange) ||\n distance < dangerDistance\n\t\t) \n\t\t{\n\t\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\t\tstate.position,\n\t\t\t\tenemy.position,\n\t\t\t\tstate.projectiles,\n\t\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t\t);\n\t\t\tif (blinkTarget) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === STEP 6: OFFENSE ===\n\tif (state.state === 'idle' && distance < 600) \n\t{\n\t\tconst offenseDefenseTime =\n\t\t\tstate.blinkCooldown === 0\n\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst vulnerabilityTicks = getEnemyVulnerabilityTicks(enemy);\n\n\t\t// --- PUNISH MODE: intercept-aimed straight missiles during vulnerability ---\n\t\tif (\n\t\t\tvulnerabilityTicks >= punishMinVulnerability &&\n distance < punishAttackRange\n\t\t) \n\t\t{\n\t\t\tconst flightTimeEstimate = distance / flightTimeDivisor;\n\t\t\tconst punishBudget = vulnerabilityTicks - flightTimeEstimate;\n\t\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\t\tconst safeBudget = nextThreatTime - GCD_DURATION - offenseDefenseTime;\n\t\t\tconst effectiveBudget = Math.min(\n\t\t\t\tpunishBudget,\n\t\t\t\tsafeBudget,\n\t\t\t\tMAX_CAST_BUDGET,\n\t\t\t);\n\n\t\t\tif (effectiveBudget > 0) \n\t\t\t{\n\t\t\t\tconst config = fitMissileToBudget(effectiveBudget, distance, {\n\t\t\t\t\tminTurnRate: 0,\n\t\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t\t});\n\n\t\t\t\tif (config && config.damage >= MIN_USEFUL_DAMAGE) \n\t\t\t\t{\n\t\t\t\t\tconst castTime = getMissileCastTime(config, state.lastMissileConfig);\n\t\t\t\t\tconst actualFlight = distance / config.speed;\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tcastTime + actualFlight < vulnerabilityTicks &&\n isSafeToAttack(threats, castTime)\n\t\t\t\t\t) \n\t\t\t\t\t{\n\t\t\t\t\t\tconst aimTarget =\n\t\t\t\t\t\t\tconfig.turnRate > 0\n\t\t\t\t\t\t\t\t? enemy.position\n\t\t\t\t\t\t\t\t: getLeadPosition(\n\t\t\t\t\t\t\t\t\tenemy.position,\n\t\t\t\t\t\t\t\t\tenemy.velocity,\n\t\t\t\t\t\t\t\t\tconfig.speed,\n\t\t\t\t\t\t\t\t\tstate.position,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tmove,\n\t\t\t\t\t\t\tstartCast: {\n\t\t\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\tmissileAI: config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\t\t\t\tdirection: angleTo(state.position, aimTarget),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// --- STANDARD MODE: adaptive homing missiles ---\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Anticipate enemy's next missile based on their current state\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + distance / castingFlightTimeDivisor,\n\t\t\t);\n\t\t}\n\t\t// No imminent threat — estimate enemy's likely response time\n\t\tif (nextThreatTime > threatFallbackThreshold) \n\t\t{\n\t\t\tconst enemyLockTime = getEnemyVulnerabilityTicks(enemy);\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tenemyLockTime +\n enemyResponseEstimate +\n distance / castingFlightTimeDivisor,\n\t\t\t);\n\t\t}\n\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\t// HIGH-DAMAGE PRIORITY: Sniper missiles need meaningful shield chip.\n\t\t// Fixed big-shot fallback ensures Spellseeker always does significant damage.\n\t\tconst BIG_SHOT = {damage: 28, speed: 10, turnRate: 0.5, duration: 80};\n\n\t\tif (\n\t\t\tconfig &&\n config.damage >= 15 &&\n isSafeToAttack(threats, safeAttackCastEstimate)\n\t\t)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Adaptive fitting weak or failed — fire big shot for meaningful damage\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > 50)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: BIG_SHOT,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {getMissileCastTime} from '../../utils/combat.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tisSafeToAttack,\n\tgetBlinkToDecreaseDistance,\n\tshouldForceShield,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 60;\n\nconst QUICK_CONFIG = {damage: 15, speed: 5, turnRate: 1, duration: 70};\nconst HEAVY_CONFIG = {damage: 15, speed: 5, turnRate: 1, duration: 150};\n\n// Missile effective range (speed * duration). Use HEAVY when QUICK can't reach.\nconst QUICK_RANGE = QUICK_CONFIG.speed * QUICK_CONFIG.duration; // 350\n\n/**\n * Standard homing missile AI.\n */\nconst StandardHoming: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {\n\t\tturnToward: enemy?.position,\n\t};\n};\n\n/**\n * Check if near wall.\n */\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart combat movement with threat-aware dodging.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\ttargetDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n\tdistanceDeadZone: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\t// Perpendicular directions\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\t// Priority 1: Use dodge direction (from threat analysis or close missile avoidance)\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\t// Priority 2: Avoid walls\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\t// Priority 3: Commit to direction\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\t// When actively dodging, commit fully — no approach/retreat dilution\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\t// Distance management (only when not dodging)\n\tif (currentDistance > targetDistance + distanceDeadZone) \n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < targetDistance - distanceDeadZone) \n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\t// Normalize\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Battlemage\n *\n * BEHAVIOR: Balanced mid-range duelist. Shields undodgeable threats, interrupts\n * enemy casts with quick missiles, saves blink for emergencies OR gap-closing.\n * Switches between quick (10 dmg, fast) and heavy (15 dmg, slow) missile configs\n * based on safety window and range. Will aggressively trade hits when health allows.\n * The unique trait is cast-interruption: fires quick missiles specifically when the\n * enemy is casting, punishing long cast times.\n *\n * PROGRESSION LINE: Battlemage → Warmage → Archmage\n * - Battlemage (tier 1): Quick/heavy fixed configs, cast interruption, basic defense\n * - Warmage (tier 2): Adaptive missile fitting (fitMissileToBudget), smarter attacks\n * - Archmage (tier 3): Future — supreme duelist, perfect tactical mastery\n *\n * TIER: 1 (base)\n */\nexport const Battlemage: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 274, {\n\t\trange: 150,\n\t\tmin: 150,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 219, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 36, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 80, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 31, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 35, {\n\t\trange: 30,\n\t\tmin: 15,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst blinkDistanceOffset = useParam('blinkDistanceOffset', 100, {\n\t\trange: 40,\n\t\tmin: 0,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst cancelMinRemainingCast = useParam('cancelMinRemainingCast', 19, {\n\t\trange: 30,\n\t\tmin: 5,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst interruptRange = useParam('interruptRange', 324, {\n\t\trange: 150,\n\t\tmin: 150,\n\t\tmax: 600,\n\t\tsteps: 7,\n\t});\n\tconst attackRange = useParam('attackRange', 556, {\n\t\trange: 150,\n\t\tmin: 200,\n\t\tmax: 700,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\t// Cancel shield if no imminent threats\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel long missile casts if undodgeable threat approaches ===\n\t// Only cancel when health is too low to trade (aggressive casts accepted the risk).\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\tremainingCast > cancelMinRemainingCast &&\n urgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + 1 &&\n state.health <= urgentThreat.projectile.damage * 2 + 1\n\t\t) \n\t\t{\n\t\t\tconst dodgeDirection = getDodgeDirectionForMovement(\n\t\t\t\tthreats,\n\t\t\t\tstate.position,\n\t\t\t);\n\t\t\tconst move = combatMove(\n\t\t\t\tstate.position,\n\t\t\t\tenemy,\n\t\t\t\tdistance,\n\t\t\t\ttargetDistance,\n\t\t\t\tdodgeDirection,\n\t\t\t\tstate.tick,\n\t\t\t\tstrafeCyclePeriod,\n\t\t\t\tstrafeIntensity,\n\t\t\t\tapproachSpeed,\n\t\t\t\tdistanceDeadZone,\n\t\t\t);\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK: Dodge missile + close gap simultaneously (first priority) ===\n\t// Blink both dodges the incoming missile AND closes distance — better than shielding.\n\t// Only shield when blink is on cooldown.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n distance > targetDistance - blinkDistanceOffset &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\tconst blinkTarget =\n\t\t\tgetBlinkToDecreaseDistance(\n\t\t\t\tstate.position,\n\t\t\t\tenemy.position,\n\t\t\t\tstate.projectiles,\n\t\t\t) ?? enemy.position;\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t};\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats (only when blink can't handle it) ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (\n\t\t\turgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3 &&\n (state.blinkCooldown > 0 || urgentThreat.ticksToImpact < 10)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// Movement: smart strafe + maintain optimal distance\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\ttargetDistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t\tdistanceDeadZone,\n\t);\n\n\t// === OFFENSE ===\n\tif (state.state === 'idle') \n\t{\n\t\t// Interrupt if enemy is casting (quick attack)\n\t\tif (enemy.state === 'casting' && distance < interruptRange) \n\t\t{\n\t\t\tconst castTime = getMissileCastTime(QUICK_CONFIG);\n\t\t\tif (isSafeToAttack(threats, castTime)) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove,\n\t\t\t\t\tstartCast: {\n\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\tconfig: QUICK_CONFIG,\n\t\t\t\t\t\tmissileAI: StandardHoming,\n\t\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\n\t\t// Standard attacks at combat range\n\t\tif (distance < attackRange) \n\t\t{\n\t\t\tconst heavyCastTime = getMissileCastTime(HEAVY_CONFIG);\n\t\t\tif (isSafeToAttack(threats, heavyCastTime)) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove,\n\t\t\t\t\tstartCast: {\n\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\tconfig: HEAVY_CONFIG,\n\t\t\t\t\t\tmissileAI: StandardHoming,\n\t\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Fallback: quick homing missile when heavy isn't safe (only if in range)\n\t\t\tconst quickCastTime = getMissileCastTime(QUICK_CONFIG);\n\t\t\tif (\n\t\t\t\tdistance < QUICK_RANGE * 0.9 &&\n isSafeToAttack(threats, quickCastTime)\n\t\t\t) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove,\n\t\t\t\t\tstartCast: {\n\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\tconfig: QUICK_CONFIG,\n\t\t\t\t\t\tmissileAI: StandardHoming,\n\t\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\n\t\t\t// Not safe to attack — wait for threat to pass, then fire\n\t\t}\n\t}\n\n\t// === FALLBACK DEFENSE: Shield when no attack window exists ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'shield'},\n\t\t};\n\t}\n\n\treturn {move};\n};\n","import {\n\tWizardFunction,\n\tMissileAIFunction,\n\tMissileActions,\n} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tfitMissileToBudget,\n\tgetBlinkToDecreaseDistance,\n\tgetBlinkToIncreaseDistance,\n\tshouldForceShield,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 60;\n\nconst SHIELD_BUFFER = 3;\n\n/**\n * Standard homing missile AI.\n */\nconst StandardHoming: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {\n\t\tturnToward: enemy?.position,\n\t};\n};\n\n/**\n * Check if near wall.\n */\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart combat movement with threat-aware dodging.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\ttargetDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n\tdistanceDeadZone: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\t// Perpendicular directions\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\tif (currentDistance > targetDistance + distanceDeadZone) \n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < targetDistance - distanceDeadZone) \n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Warmage\n *\n * BEHAVIOR: Enhanced Battlemage with adaptive missile fitting. Uses fitMissileToBudget\n * to maximize damage within safe attack windows instead of fixed quick/heavy configs.\n * Same close-range playstyle: shields undodgeable threats, blinks to close distance\n * or escape, aggressive hit-trading when health allows. The adaptive fitting means\n * every attack is optimized for the current situation — no wasted cast time.\n *\n * PROGRESSION LINE: Battlemage → Warmage → Archmage\n * - Battlemage (tier 1): Quick/heavy fixed configs, cast interruption, basic defense\n * - Warmage (tier 2): Adaptive missile fitting, optimized damage windows\n * - Archmage (tier 3): Future — supreme duelist, perfect tactical mastery\n *\n * TIER: 2 (enhanced Battlemage)\n */\nexport const Warmage: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 188, {\n\t\trange: 150,\n\t\tmin: 0,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', 0.4, {range: 1.5, steps: 5});\n\tconst blinkEscapeDistance = useParam('blinkEscapeDistance', 42, {\n\t\trange: 190,\n\t\tmin: 0,\n\t\tsteps: 7,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 299, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 33, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 80, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 33, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 37, {\n\t\trange: 30,\n\t\tmin: 15,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst blinkDistanceOffset = useParam('blinkDistanceOffset', 100, {\n\t\trange: 40,\n\t\tmin: 0,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst attackRange = useParam('attackRange', 300, {\n\t\trange: 150,\n\t\tmin: 200,\n\t\tmax: 700,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 9.5, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast only for LETHAL incoming damage ===\n\t// DPS trade: we accepted the hit when we started casting. Only cancel to survive.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + 1\n\t\t) \n\t\t{\n\t\t\tconst dodgeDirection = getDodgeDirectionForMovement(\n\t\t\t\tthreats,\n\t\t\t\tstate.position,\n\t\t\t);\n\t\t\tconst move = combatMove(\n\t\t\t\tstate.position,\n\t\t\t\tenemy,\n\t\t\t\tdistance,\n\t\t\t\ttargetDistance,\n\t\t\t\tdodgeDirection,\n\t\t\t\tstate.tick,\n\t\t\t\tstrafeCyclePeriod,\n\t\t\t\tstrafeIntensity,\n\t\t\t\tapproachSpeed,\n\t\t\t\tdistanceDeadZone,\n\t\t\t);\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK: Dodge missile + close gap simultaneously (first priority) ===\n\t// Blink both dodges the incoming missile AND closes distance — better than shielding.\n\t// Only shield when blink is on cooldown.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n distance > targetDistance - blinkDistanceOffset &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\tconst blinkTarget =\n\t\t\tgetBlinkToDecreaseDistance(\n\t\t\t\tstate.position,\n\t\t\t\tenemy.position,\n\t\t\t\tstate.projectiles,\n\t\t\t) ?? enemy.position;\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t};\n\t}\n\n\t// === BLINK ESCAPE: Enemy too close, blink away to reestablish dueling range ===\n\t// Warmage is a duelist at 280 range — if enemy closes to melee, blink out.\n\tif (\n\t\tstate.state === 'idle' &&\n distance < blinkEscapeDistance &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst escapeTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (escapeTarget) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: escapeTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats (only when blink can't handle it) ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (\n\t\t\turgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3 &&\n (state.blinkCooldown > 0 || urgentThreat.ticksToImpact < 10)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// Movement\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\ttargetDistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t\tdistanceDeadZone,\n\t);\n\n\t// === OFFENSE: Adaptive missile fitting ===\n\tif (state.state === 'idle' && distance < attackRange) \n\t{\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Account for enemy's active cast — their missile will arrive after cast completes + flight time\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTime = distance / flightTimeDivisor;\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t);\n\t\t}\n\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Warmage moves while casting, so always need homing to compensate\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (config && config.damage >= minDmg) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI:\n config.turnRate > 0 ? StandardHoming : (): MissileActions => ({}),\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Budget too small for useful missile — wait for threat to pass, then fire\n\t}\n\n\t// === FALLBACK DEFENSE: Shield when no attack window exists ===\n\t// If all offense checks failed and there's an undodgeable threat, shield it.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'shield'},\n\t\t};\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {\n\tARENA_SIZE,\n\tGCD_DURATION,\n\tSHIELD_CAST_TIME,\n\tBLINK_CAST_TIME,\n} from '../../rules.js';\nimport {getMissileCastTime, getLeadPosition} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tfitMissileToBudget,\n\tgetBlinkToDecreaseDistance,\n\tgetBlinkToIncreaseDistance,\n\tshouldForceShield,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n\tgetEnemyVulnerabilityTicks,\n\tisSafeToAttack,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst WALL_BUFFER = 60;\nconst SHIELD_BUFFER = 3;\n\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {turnToward: enemy?.position};\n};\n\nconst StraightAI: MissileAIFunction = () => ({});\n\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart combat movement with threat-aware dodging.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\ttargetDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n\tdistanceDeadZone: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\tif (currentDistance > targetDistance + distanceDeadZone) \n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < targetDistance - distanceDeadZone) \n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Archmage\n *\n * BEHAVIOR: Versatile duelist that adapts missile choice based on distance and HP.\n * Close range → straight missiles (no turn cost = more damage). Mid/far range → homing.\n * Uses dual-blink aggressively (gap-close during vulnerability, escape when trade is bad).\n * HP-aware: ahead → aggressive close range; behind → defensive ranged kiting.\n *\n * KEY IMPROVEMENTS OVER WARMAGE:\n * - Range-adaptive missiles: straight close, homing far\n * - Vulnerability-timed blinks: gap-close during enemy cast/GCD\n * - HP-aware aggression: adjusts distance + risk tolerance based on HP differential\n * - Progressive cast-cancel: graduated thresholds\n *\n * PROGRESSION LINE: Battlemage → Warmage → Archmage\n * TIER: 3 (elite Duelist line)\n */\nexport const Archmage: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 214, {\n\t\trange: 150,\n\t\tmin: 0,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', -14.6, {range: 1.5, steps: 5});\n\tconst blinkEscapeDistance = useParam('blinkEscapeDistance', 442, {\n\t\trange: 190,\n\t\tmin: 0,\n\t\tsteps: 7,\n\t});\n\tconst punishMinVulnerability = useParam('punishMinVulnerability', 85, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tsteps: 5,\n\t});\n\tconst aggressiveHPThreshold = useParam('aggressiveHPThreshold', 4, {\n\t\trange: 20,\n\t\tmin: 0,\n\t\tsteps: 5,\n\t});\n\tconst preferBlinkDodge =\n\t\tuseParam('preferBlinkDodge', 1, {min: 0, max: 1, steps: 2}) >= 0.5;\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 248, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 250,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 100, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 57, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 37, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst aggressiveDistanceOffset = useParam('aggressiveDistanceOffset', 99, {\n\t\trange: 50,\n\t\tmin: 0,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst cautiousDistanceOffset = useParam('cautiousDistanceOffset', 42, {\n\t\trange: 50,\n\t\tmin: 0,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst cancelHeavyDmg = useParam('cancelHeavyDmg', 40, {\n\t\trange: 15,\n\t\tmin: 5,\n\t\tmax: 40,\n\t\tsteps: 5,\n\t});\n\tconst cancelMediumDmg = useParam('cancelMediumDmg', 20, {\n\t\trange: 10,\n\t\tmin: 3,\n\t\tmax: 25,\n\t\tsteps: 5,\n\t});\n\tconst cancelCastRatio = useParam('cancelCastRatio', 0.74, {\n\t\trange: 0.4,\n\t\tmin: 0.3,\n\t\tmax: 1.0,\n\t\tsteps: 5,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 100, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 29, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst blinkGapThreshold = useParam('blinkGapThreshold', 80, {\n\t\trange: 75,\n\t\tmin: 20,\n\t\tmax: 200,\n\t\tsteps: 7,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 4.5, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst closeRangeStraightThreshold = useParam(\n\t\t'closeRangeStraightThreshold',\n\t\t50,\n\t\t{range: 100, min: 50, max: 300, steps: 7},\n\t);\n\tconst safeAttackCastEstimate = useParam('safeAttackCastEstimate', 35, {\n\t\trange: 25,\n\t\tmin: 10,\n\t\tmax: 80,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\n\t// HP-aware distance: ahead → fight closer, behind → kite at range\n\tconst hpDiff = state.health - enemy.health;\n\tconst isAggressive = hpDiff >= -aggressiveHPThreshold;\n\tconst effectiveTargetDistance = isAggressive\n\t\t? targetDistance - aggressiveDistanceOffset\n\t\t: targetDistance + cautiousDistanceOffset;\n\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\teffectiveTargetDistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t\tdistanceDeadZone,\n\t);\n\n\t// === STEP 1: CHANNELING ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === STEP 2: PROGRESSIVE CAST-CANCEL ===\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tconst defenseTime =\n\t\t\tstate.blinkCooldown === 0\n\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tconst willArriveInWindow =\n\t\t\turgentThreat.ticksToImpact <= remainingCast + GCD_DURATION + defenseTime;\n\n\t\tif (willArriveInWindow) \n\t\t{\n\t\t\tconst incomingDamage = urgentThreat.projectile.damage;\n\t\t\tconst castRatio = (state.castProgress ?? 0) / (state.castDuration ?? 1);\n\n\t\t\tif (incomingDamage >= state.health || incomingDamage >= cancelHeavyDmg) \n\t\t\t{\n\t\t\t\treturn {move, cancel: true};\n\t\t\t}\n\t\t\tif (incomingDamage >= cancelMediumDmg && castRatio < cancelCastRatio) \n\t\t\t{\n\t\t\t\treturn {move, cancel: true};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === STEP 3: BLINK-DODGE — 100% damage avoidance (optimizer-controlled) ===\n\tif (\n\t\tpreferBlinkDodge &&\n state.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat)) &&\n urgentThreat.ticksToImpact >= BLINK_CAST_TIME &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\t// Aggressive: blink toward enemy to dodge + close gap\n\t\t// Defensive: blink away to dodge + increase distance\n\t\tif (isAggressive && distance > effectiveTargetDistance) \n\t\t{\n\t\t\tconst blinkTarget = getBlinkToDecreaseDistance(\n\t\t\t\tstate.position,\n\t\t\t\tenemy.position,\n\t\t\t\tstate.projectiles,\n\t\t\t);\n\t\t\tif (blinkTarget) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (blinkTarget) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 4: BLINK ESCAPE — enemy too close ===\n\tif (\n\t\tstate.state === 'idle' &&\n distance < blinkEscapeDistance &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst escapeTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (escapeTarget) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: escapeTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 5: SHIELD ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (\n\t\t\turgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= SHIELD_BUFFER &&\n (state.blinkCooldown > 0 || urgentThreat.ticksToImpact < 10)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === STEP 6: VULNERABILITY BLINK — gap-close during enemy cast/GCD ===\n\tif (\n\t\tstate.state === 'idle' &&\n state.blinkCooldown === 0 &&\n isAggressive &&\n distance > effectiveTargetDistance + blinkGapThreshold &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst vulnerabilityTicks = getEnemyVulnerabilityTicks(enemy);\n\t\tif (vulnerabilityTicks >= punishMinVulnerability) \n\t\t{\n\t\t\tconst blinkTarget = getBlinkToDecreaseDistance(\n\t\t\t\tstate.position,\n\t\t\t\tenemy.position,\n\t\t\t\tstate.projectiles,\n\t\t\t);\n\t\t\tif (blinkTarget) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === STEP 7: OFFENSE ===\n\tif (state.state === 'idle' && distance < 500) \n\t{\n\t\tconst offenseDefenseTime =\n\t\t\tstate.blinkCooldown === 0\n\t\t\t\t? BLINK_CAST_TIME\n\t\t\t\t: SHIELD_CAST_TIME + SHIELD_BUFFER;\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst vulnerabilityTicks = getEnemyVulnerabilityTicks(enemy);\n\n\t\t// --- PUNISH MODE: range-adaptive missiles during vulnerability ---\n\t\tif (vulnerabilityTicks >= punishMinVulnerability) \n\t\t{\n\t\t\tconst flightTimeEstimate = distance / flightTimeDivisor;\n\t\t\tconst punishBudget = vulnerabilityTicks - flightTimeEstimate;\n\t\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\t\tconst safeBudget = nextThreatTime - GCD_DURATION - offenseDefenseTime;\n\t\t\tconst effectiveBudget = Math.min(\n\t\t\t\tpunishBudget,\n\t\t\t\tsafeBudget,\n\t\t\t\tMAX_CAST_BUDGET,\n\t\t\t);\n\n\t\t\tif (effectiveBudget > 0) \n\t\t\t{\n\t\t\t\t// Close range → straight for max damage, far → light homing\n\t\t\t\tconst punishTurnRate = distance < closeRangeStraightThreshold ? 0 : 0.2;\n\t\t\t\tconst config = fitMissileToBudget(effectiveBudget, distance, {\n\t\t\t\t\tminTurnRate: punishTurnRate,\n\t\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t\t});\n\n\t\t\t\tif (config && config.damage >= MIN_USEFUL_DAMAGE) \n\t\t\t\t{\n\t\t\t\t\tconst castTime = getMissileCastTime(config, state.lastMissileConfig);\n\t\t\t\t\tconst actualFlight = distance / config.speed;\n\n\t\t\t\t\tif (\n\t\t\t\t\t\tcastTime + actualFlight < vulnerabilityTicks &&\n isSafeToAttack(threats, castTime)\n\t\t\t\t\t) \n\t\t\t\t\t{\n\t\t\t\t\t\tconst aimTarget =\n\t\t\t\t\t\t\tconfig.turnRate > 0\n\t\t\t\t\t\t\t\t? enemy.position\n\t\t\t\t\t\t\t\t: getLeadPosition(\n\t\t\t\t\t\t\t\t\tenemy.position,\n\t\t\t\t\t\t\t\t\tenemy.velocity,\n\t\t\t\t\t\t\t\t\tconfig.speed,\n\t\t\t\t\t\t\t\t\tstate.position,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tmove,\n\t\t\t\t\t\t\tstartCast: {\n\t\t\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\tmissileAI: config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\t\t\t\tdirection: angleTo(state.position, aimTarget),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// --- STANDARD MODE: range-adaptive missiles ---\n\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\tconst safeBudget = nextThreatTime - GCD_DURATION - offenseDefenseTime;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Adapt turn rate to distance: close → straight, far → homing\n\t\tconst effectiveMinTurnRate =\n\t\t\tdistance < closeRangeStraightThreshold ? 0 : minTurnRate;\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tlet config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate: effectiveMinTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (\n\t\t\tconfig &&\n config.damage >= minDmg &&\n isSafeToAttack(threats, safeAttackCastEstimate)\n\t\t) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Fallback: fire bigger missile, accept incoming hit\n\t\tif (!config || config.damage < minDmg) \n\t\t{\n\t\t\tconfig = fitMissileToBudget(MAX_CAST_BUDGET, distance, {\n\t\t\t\tminTurnRate: effectiveMinTurnRate,\n\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t});\n\t\t\tif (config) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove,\n\t\t\t\t\tstartCast: {\n\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\tconfig,\n\t\t\t\t\t\tmissileAI: config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {interceptAngle, moveInDirection} from '../../utils/movement.js';\nimport {getMissileCastTime} from '../../utils/combat.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tisSafeToAttack,\n\tgetBlinkToCenter,\n\tshouldForceShield,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 80;\n\n/**\n * Standard homing missile: higher damage for faster kills. s=5 × dur=150 = 750u range.\n */\nconst STANDARD_CONFIG = {damage: 15, speed: 5, turnRate: 1, duration: 150};\n\n/**\n * Quick attack config for use under pressure (shorter cast time).\n * Same low turn/duration for warmup compatibility.\n */\nconst QUICK_CONFIG = {damage: 10, speed: 5, turnRate: 1, duration: 80};\n\n/**\n * Predictive homing missile AI - leads the target.\n */\nconst StormchaserMissileAI: MissileAIFunction = ({\n\tworldState,\n\tmissileState,\n}) => \n{\n\tconst enemy = worldState.enemies[0];\n\tif (!enemy) return {};\n\n\tconst angle = interceptAngle(\n\t\tmissileState.position,\n\t\tenemy.position,\n\t\tenemy.velocity,\n\t\tmissileState.speed,\n\t);\n\tif (angle !== null) \n\t{\n\t\treturn {turnToward: moveInDirection(missileState.position, angle, 100)};\n\t}\n\treturn {turnToward: enemy.position};\n};\n\n/**\n * Check if position is near a wall.\n */\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart combat movement with threat-aware dodging.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\ttargetDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n\tdistanceDeadZone: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\t// Perpendicular directions for strafing\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\t// Priority 1: Use dodge direction (from threat analysis or close missile avoidance)\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\t// Priority 2: Avoid walls\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\t// Priority 3: Commit to strafe direction (unpredictability)\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\t// When actively dodging, commit fully — no approach/retreat dilution\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\t// Distance management (tighter band than other bots, only when not dodging)\n\tif (currentDistance > targetDistance + distanceDeadZone) \n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < targetDistance - distanceDeadZone) \n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\t// Normalize to max speed\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Stormchaser\n *\n * BEHAVIOR: Fights aggressively while managing defense intelligently. Uses\n * two fixed missile configs (standard homing + quick attack) with predictive\n * homing missile AI (interceptAngle on the missile itself). Blink-dodges\n * incoming threats, shields when blink is on cooldown. Tight distance\n * management (350 units, ±30 band).\n *\n * PROGRESSION LINE: Stormchaser → Stormcaller → Stormforger\n * - Stormchaser (tier 1): Fixed missiles, predictive homing AI, blink-dodge\n * - Stormcaller (tier 2): + fitMissileToBudget, predictive homing, optimized damage\n * - Stormforger (tier 3): Future — supreme berserker, perfect aggression\n *\n * TIER: 1 (base)\n */\nexport const Stormchaser: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 191, {\n\t\trange: 150,\n\t\tmin: 0,\n\t});\n\tconst healthLowThreshold = useParam('healthLowThreshold', 45, {\n\t\trange: 20,\n\t\tmin: 1,\n\t\tsteps: 5,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 130, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 42, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 68, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 26, {\n\t\trange: 25,\n\t\tmin: 10,\n\t\tmax: 60,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 25, {\n\t\trange: 30,\n\t\tmin: 15,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst cancelMinRemainingCast = useParam('cancelMinRemainingCast', 5, {\n\t\trange: 50,\n\t\tmin: 5,\n\t\tmax: 150,\n\t\tsteps: 7,\n\t});\n\tconst attackRange = useParam('attackRange', 676, {\n\t\trange: 200,\n\t\tmin: 300,\n\t\tmax: 800,\n\t});\n\tconst healthDamageRatio = useParam('healthDamageRatio', 5, {\n\t\trange: 2,\n\t\tmin: 0.5,\n\t\tmax: 5,\n\t\tsteps: 7,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// === DEFENSE: Handle channeling state ===\n\tif (state.state === 'channeling') \n\t{\n\t\t// Cancel shield if no imminent danger\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast for threats arriving during vulnerability window ===\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tconst incomingDamage = urgentThreat.projectile.damage;\n\t\tconst arrivesInGCD =\n\t\t\turgentThreat.ticksToImpact > remainingCast &&\n urgentThreat.ticksToImpact <= remainingCast + GCD_DURATION;\n\n\t\t// Cancel for undodgeable threats when trade isn't worth it\n\t\tif (!urgentThreat.bestDodgeDirection) \n\t\t{\n\t\t\tconst canSurviveHit =\n\t\t\t\tstate.health > incomingDamage * healthDamageRatio + 1;\n\t\t\tconst tradeWorthIt =\n\t\t\t\tcanSurviveHit && incomingDamage <= QUICK_CONFIG.damage;\n\t\t\tif (\n\t\t\t\t!tradeWorthIt &&\n remainingCast > cancelMinRemainingCast &&\n urgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + 1\n\t\t\t) \n\t\t\t{\n\t\t\t\tconst dodgeDir = getDodgeDirectionForMovement(threats, state.position);\n\t\t\t\tconst cancelMove = combatMove(\n\t\t\t\t\tstate.position,\n\t\t\t\t\tenemy,\n\t\t\t\t\tdistance,\n\t\t\t\t\ttargetDistance,\n\t\t\t\t\tdodgeDir,\n\t\t\t\t\tstate.tick,\n\t\t\t\t\tstrafeCyclePeriod,\n\t\t\t\t\tstrafeIntensity,\n\t\t\t\t\tapproachSpeed,\n\t\t\t\t\tdistanceDeadZone,\n\t\t\t\t);\n\t\t\t\treturn {move: cancelMove, cancel: true};\n\t\t\t}\n\t\t}\n\n\t\t// Cancel for ANY threat arriving during GCD when health is critical\n\t\t// At low health, surviving is more important than finishing the attack.\n\t\tif (\n\t\t\tarrivesInGCD &&\n state.health <= incomingDamage + 1 &&\n remainingCast > 10\n\t\t) \n\t\t{\n\t\t\tconst dodgeDir = getDodgeDirectionForMovement(threats, state.position);\n\t\t\tconst cancelMove = combatMove(\n\t\t\t\tstate.position,\n\t\t\t\tenemy,\n\t\t\t\tdistance,\n\t\t\t\ttargetDistance,\n\t\t\t\tdodgeDir,\n\t\t\t\tstate.tick,\n\t\t\t\tstrafeCyclePeriod,\n\t\t\t\tstrafeIntensity,\n\t\t\t\tapproachSpeed,\n\t\t\t\tdistanceDeadZone,\n\t\t\t);\n\t\t\treturn {move: cancelMove, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK: Dodge missile + reposition (first priority) ===\n\t// Blink dodges the incoming missile — no GCD after, so can immediately fire back.\n\t// Only shield when blink is on cooldown.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t};\n\t}\n\n\t// === DEFENSE: Shield threats (when blink is on cooldown) ===\n\tif (state.state === 'idle' && urgentThreat) \n\t{\n\t\tconst forceShield = shouldForceShield(urgentThreat);\n\t\tconst undodgeable = !urgentThreat.bestDodgeDirection;\n\t\tconst healthLow = state.health < healthLowThreshold;\n\t\tconst healthCritical =\n\t\t\tstate.health <= urgentThreat.projectile.damage * healthDamageRatio + 1;\n\t\tconst shouldShield =\n\t\t\tforceShield || (undodgeable && healthLow) || healthCritical;\n\n\t\tif (shouldShield && urgentThreat.canBlockInTime) \n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= 3) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === MOVEMENT: Smart combat movement ===\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\ttargetDistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t\tdistanceDeadZone,\n\t);\n\n\t// === OFFENSE: Attack aggressively while moving ===\n\tif (state.state === 'idle' && distance < attackRange) \n\t{\n\t\t// Primary: standard homing missile\n\t\tconst standardCastTime = getMissileCastTime(STANDARD_CONFIG);\n\t\tif (isSafeToAttack(threats, standardCastTime)) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: STANDARD_CONFIG,\n\t\t\t\t\tmissileAI: StormchaserMissileAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Fallback: quick missile under pressure (shorter cast = smaller vulnerability window)\n\t\tconst quickCastTime = getMissileCastTime(QUICK_CONFIG);\n\t\tif (isSafeToAttack(threats, quickCastTime)) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: QUICK_CONFIG,\n\t\t\t\t\tmissileAI: StormchaserMissileAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Not safe to attack — wait for threat to pass, then fire\n\t}\n\n\t// === FALLBACK DEFENSE: Shield when no attack window exists ===\n\t// If all offense checks failed and there's an undodgeable threat, shield it\n\t// rather than sitting idle and taking the hit.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'shield'},\n\t\t};\n\t}\n\n\t// Default: just move (dodging/positioning)\n\treturn {move};\n};\n","import {\n\tWizardFunction,\n\tMissileAIFunction,\n\tMissileActions,\n} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {interceptAngle, moveInDirection} from '../../utils/movement.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n\tfitMissileToBudget,\n\tshouldForceShield,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 80;\n\nconst SHIELD_BUFFER = 3;\n\n/**\n * Predictive homing missile AI - leads the target.\n */\nconst StormcallerMissileAI: MissileAIFunction = ({\n\tworldState,\n\tmissileState,\n}) => \n{\n\tconst enemy = worldState.enemies[0];\n\tif (!enemy) return {};\n\n\tconst angle = interceptAngle(\n\t\tmissileState.position,\n\t\tenemy.position,\n\t\tenemy.velocity,\n\t\tmissileState.speed,\n\t);\n\tif (angle !== null) \n\t{\n\t\treturn {turnToward: moveInDirection(missileState.position, angle, 100)};\n\t}\n\treturn {turnToward: enemy.position};\n};\n\n/**\n * Check if position is near a wall.\n */\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart combat movement with threat-aware dodging.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\ttargetDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n\tdistanceDeadZone: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tif (dodgeDirection) \n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\tif (currentDistance > targetDistance + distanceDeadZone) \n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < targetDistance - distanceDeadZone) \n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Stormcaller\n *\n * BEHAVIOR: Enhanced Stormchaser with adaptive missile fitting (fitMissileToBudget)\n * AND predictive homing missiles. Combines aggressive fighting philosophy with\n * optimized damage output. Uses budget-based missile fitting to maximize damage\n * within safe windows. Falls back to quick missiles under pressure. Same smart\n * trade/shield decisions as Stormchaser but with better resource usage.\n *\n * PROGRESSION LINE: Stormchaser → Stormcaller → Stormforger\n * - Stormchaser (tier 1): Adaptive missiles, smart trading, aggressive defense\n * - Stormcaller (tier 2): + fitMissileToBudget, predictive homing, optimized damage\n * - Stormforger (tier 3): Future — supreme berserker, perfect aggression\n *\n * TIER: 2 (enhanced Stormchaser)\n */\nexport const Stormcaller: WizardFunction = ({state}) => \n{\n\t// Tunable parameters (run optimizer script to find optimal values)\n\tconst targetDistance = useParam('targetDistance', 129, {\n\t\trange: 200,\n\t\tmin: 0,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', 0.5, {range: 1.5, steps: 5});\n\tconst shieldHealthThreshold = useParam('shieldHealthThreshold', 20, {\n\t\trange: 23,\n\t\tmin: 1,\n\t\tsteps: 7,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 281, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 69, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 69, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 18, {\n\t\trange: 25,\n\t\tmin: 10,\n\t\tmax: 60,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 70, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst attackRange = useParam('attackRange', 430, {\n\t\trange: 200,\n\t\tmin: 300,\n\t\tmax: 800,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 10.8, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// === DEFENSE: Handle channeling state ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast only for LETHAL incoming damage ===\n\t// DPS trade: we accepted the hit when we started casting. Only cancel to survive.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + 1\n\t\t) \n\t\t{\n\t\t\tconst dodgeDirection = getDodgeDirectionForMovement(\n\t\t\t\tthreats,\n\t\t\t\tstate.position,\n\t\t\t);\n\t\t\tconst move = combatMove(\n\t\t\t\tstate.position,\n\t\t\t\tenemy,\n\t\t\t\tdistance,\n\t\t\t\ttargetDistance,\n\t\t\t\tdodgeDirection,\n\t\t\t\tstate.tick,\n\t\t\t\tstrafeCyclePeriod,\n\t\t\t\tstrafeIntensity,\n\t\t\t\tapproachSpeed,\n\t\t\t\tdistanceDeadZone,\n\t\t\t);\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK: Dodge missile + reposition (first priority) ===\n\t// Blink dodges the incoming missile — no GCD after, so can immediately fire back.\n\t// Only shield when blink is on cooldown.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t};\n\t}\n\n\t// === DEFENSE: Shield threats (when blink is on cooldown) ===\n\tif (state.state === 'idle' && urgentThreat) \n\t{\n\t\tconst forceShield = shouldForceShield(urgentThreat);\n\t\tconst undodgeable = !urgentThreat.bestDodgeDirection;\n\t\tconst healthLow = state.health < shieldHealthThreshold;\n\t\tconst healthCritical =\n\t\t\tstate.health <= urgentThreat.projectile.damage * 2 + 1;\n\t\tconst doShield =\n\t\t\tforceShield || (undodgeable && healthLow) || healthCritical;\n\n\t\tif (doShield && urgentThreat.canBlockInTime) \n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= 3) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === MOVEMENT: Smart combat movement ===\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\ttargetDistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t\tdistanceDeadZone,\n\t);\n\n\t// === OFFENSE: Adaptive missile fitting with predictive homing ===\n\tif (state.state === 'idle' && distance < attackRange) \n\t{\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Account for enemy's active cast — their missile will arrive after cast completes + flight time\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTime = distance / flightTimeDivisor;\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t);\n\t\t}\n\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Predictive homing compensates for movement during cast\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (config && config.damage >= minDmg) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI:\n config.turnRate > 0\n \t? StormcallerMissileAI\n \t: (): MissileActions => ({}),\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Budget too small for useful missile — wait for threat to pass, then fire\n\t}\n\n\t// === FALLBACK DEFENSE: Shield when no attack window exists ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3\n\t) \n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'shield'},\n\t\t};\n\t}\n\n\treturn {move};\n};\n","import {\n\tWizardFunction,\n\tMissileAIFunction,\n\tMissileActions,\n} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {interceptAngle, moveInDirection} from '../../utils/movement.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {getMissileCastTime, getLeadPosition} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n\tfitMissileToBudget,\n\tshouldForceShield,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n\tgetEnemyVulnerabilityTicks,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\n\nconst WALL_BUFFER = 80;\n\nconst SHIELD_BUFFER = 3;\n\n/**\n * Predictive homing missile AI - leads the target.\n */\nconst HomingAI: MissileAIFunction = ({\n\tworldState,\n\tmissileState,\n}) =>\n{\n\tconst enemy = worldState.enemies[0];\n\tif (!enemy) return {};\n\n\tconst angle = interceptAngle(\n\t\tmissileState.position,\n\t\tenemy.position,\n\t\tenemy.velocity,\n\t\tmissileState.speed,\n\t);\n\tif (angle !== null)\n\t{\n\t\treturn {turnToward: moveInDirection(missileState.position, angle, 100)};\n\t}\n\treturn {turnToward: enemy.position};\n};\n\nconst StraightAI: MissileAIFunction = () => ({});\n\n/**\n * Check if position is near a wall.\n */\nfunction isNearWall(position: {x: number; y: number}): boolean\n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart combat movement with threat-aware dodging.\n */\nfunction combatMove(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tcurrentDistance: number,\n\ttargetDistance: number,\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n\tapproachSpeed: number,\n\tdistanceDeadZone: number,\n): {x: number; y: number}\n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = currentDistance > 0 ? currentDistance : 1;\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDirection: {x: number; y: number};\n\n\tif (dodgeDirection)\n\t{\n\t\tstrafeDirection = dodgeDirection;\n\t}\n\telse if (isNearWall(position))\n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDirection =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse\n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDirection = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tif (dodgeDirection)\n\t{\n\t\treturn {x: strafeDirection.x * 100, y: strafeDirection.y * 100};\n\t}\n\n\tlet moveX = strafeDirection.x * strafeIntensity;\n\tlet moveY = strafeDirection.y * strafeIntensity;\n\n\tif (currentDistance > targetDistance + distanceDeadZone)\n\t{\n\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t}\n\telse if (currentDistance < targetDistance - distanceDeadZone)\n\t{\n\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\tif (position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\tif (position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0) retreatX = 0;\n\t\tif (position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\tif (position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0) retreatY = 0;\n\t\tmoveX += retreatX;\n\t\tmoveY += retreatY;\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100)\n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\treturn {x: moveX, y: moveY};\n}\n\n/**\n * Bot: Stormforger\n *\n * BEHAVIOR: Enhanced Stormcaller with vulnerability exploitation. Takes the exact\n * Stormcaller foundation (adaptive missile fitting + predictive homing) and adds\n * a punish mode that fires fast straight missiles when the enemy is locked in\n * GCD or cast animation. During vulnerability windows, uses getLeadPosition for\n * accurate straight shots that arrive before the enemy can react.\n *\n * PROGRESSION LINE: Stormchaser → Stormcaller → Stormforger\n * - Stormchaser (tier 1): Adaptive missiles, smart trading, aggressive defense\n * - Stormcaller (tier 2): + fitMissileToBudget, predictive homing, optimized damage\n * - Stormforger (tier 3): + vulnerability exploitation, punish missiles during enemy GCD\n *\n * TIER: 3 (elite Berserker line)\n */\nexport const Stormforger: WizardFunction = ({state}) =>\n{\n\t// Tunable parameters (run optimizer script to find optimal values)\n\tconst targetDistance = useParam('targetDistance', 271, {\n\t\trange: 200,\n\t\tmin: 0,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', 0.5, {range: 1.5, steps: 5});\n\tconst shieldHealthThreshold = useParam('shieldHealthThreshold', 20, {\n\t\trange: 23,\n\t\tmin: 1,\n\t\tsteps: 7,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 300, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 72, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 69, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 21, {\n\t\trange: 25,\n\t\tmin: 10,\n\t\tmax: 60,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 81, {\n\t\trange: 40,\n\t\tmin: 20,\n\t\tmax: 120,\n\t\tsteps: 7,\n\t});\n\tconst attackRange = useParam('attackRange', 339, {\n\t\trange: 200,\n\t\tmin: 300,\n\t\tmax: 800,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 11.4, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst punishMinVulnerability = useParam('punishMinVulnerability', 30, {\n\t\trange: 80,\n\t\tmin: 30,\n\t\tsteps: 7,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy)\n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// === DEFENSE: Handle channeling state ===\n\tif (state.state === 'channeling')\n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold)\n\t\t{\n\t\t\treturn {move: {x: 0, y: 0}, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast only for LETHAL incoming damage ===\n\t// DPS trade: we accepted the hit when we started casting. Only cancel to survive.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t)\n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + 1\n\t\t)\n\t\t{\n\t\t\tconst dodgeDirection = getDodgeDirectionForMovement(\n\t\t\t\tthreats,\n\t\t\t\tstate.position,\n\t\t\t);\n\t\t\tconst move = combatMove(\n\t\t\t\tstate.position,\n\t\t\t\tenemy,\n\t\t\t\tdistance,\n\t\t\t\ttargetDistance,\n\t\t\t\tdodgeDirection,\n\t\t\t\tstate.tick,\n\t\t\t\tstrafeCyclePeriod,\n\t\t\t\tstrafeIntensity,\n\t\t\t\tapproachSpeed,\n\t\t\t\tdistanceDeadZone,\n\t\t\t);\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK: Dodge missile + reposition (first priority) ===\n\t// Blink dodges the incoming missile — no GCD after, so can immediately fire back.\n\t// Only shield when blink is on cooldown.\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t)\n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t};\n\t}\n\n\t// === DEFENSE: Shield threats (when blink is on cooldown) ===\n\tif (state.state === 'idle' && urgentThreat)\n\t{\n\t\tconst forceShield = shouldForceShield(urgentThreat);\n\t\tconst undodgeable = !urgentThreat.bestDodgeDirection;\n\t\tconst healthLow = state.health < shieldHealthThreshold;\n\t\tconst healthCritical =\n\t\t\tstate.health <= urgentThreat.projectile.damage * 2 + 1;\n\t\tconst doShield =\n\t\t\tforceShield || (undodgeable && healthLow) || healthCritical;\n\n\t\tif (doShield && urgentThreat.canBlockInTime)\n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= 3)\n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === MOVEMENT: Smart combat movement ===\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst move = combatMove(\n\t\tstate.position,\n\t\tenemy,\n\t\tdistance,\n\t\ttargetDistance,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t\tapproachSpeed,\n\t\tdistanceDeadZone,\n\t);\n\n\t// === OFFENSE: Vulnerability punish + adaptive missile fitting ===\n\tif (state.state === 'idle' && distance < attackRange)\n\t{\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst vulnerabilityTicks = getEnemyVulnerabilityTicks(enemy);\n\n\t\t// --- PUNISH MODE: fast straight missiles during enemy GCD/cast ---\n\t\tif (\n\t\t\tvulnerabilityTicks >= punishMinVulnerability &&\n distance < 500\n\t\t)\n\t\t{\n\t\t\tconst punishBudget = vulnerabilityTicks - distance / flightTimeDivisor;\n\n\t\t\tif (punishBudget > 0)\n\t\t\t{\n\t\t\t\tconst config = fitMissileToBudget(\n\t\t\t\t\tMath.min(punishBudget, MAX_CAST_BUDGET),\n\t\t\t\t\tdistance,\n\t\t\t\t\t{\n\t\t\t\t\t\tminTurnRate: 0,\n\t\t\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t\t\t},\n\t\t\t\t);\n\n\t\t\t\tif (config && config.damage >= MIN_USEFUL_DAMAGE)\n\t\t\t\t{\n\t\t\t\t\tconst castTime = getMissileCastTime(config, state.lastMissileConfig);\n\t\t\t\t\tconst flightTime = distance / config.speed;\n\n\t\t\t\t\tif (castTime + flightTime < vulnerabilityTicks)\n\t\t\t\t\t{\n\t\t\t\t\t\tconst aimTarget =\n\t\t\t\t\t\t\tconfig.turnRate > 0\n\t\t\t\t\t\t\t\t? enemy.position\n\t\t\t\t\t\t\t\t: getLeadPosition(\n\t\t\t\t\t\t\t\t\tenemy.position,\n\t\t\t\t\t\t\t\t\tenemy.velocity,\n\t\t\t\t\t\t\t\t\tconfig.speed,\n\t\t\t\t\t\t\t\t\tstate.position,\n\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tmove,\n\t\t\t\t\t\t\tstartCast: {\n\t\t\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\t\tmissileAI:\n config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\t\t\t\tdirection: angleTo(state.position, aimTarget),\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// --- STANDARD MODE: adaptive missile fitting with predictive homing ---\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Account for enemy's active cast — their missile will arrive after cast completes + flight time\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile')\n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTime = distance / flightTimeDivisor;\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t);\n\t\t}\n\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Predictive homing compensates for movement during cast\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (config && config.damage >= minDmg)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI:\n config.turnRate > 0\n \t? HomingAI\n \t: (): MissileActions => ({}),\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Budget too small for useful missile — wait for threat to pass, then fire\n\t}\n\n\t// === FALLBACK DEFENSE: Shield when no attack window exists ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n !urgentThreat.bestDodgeDirection &&\n urgentThreat.canBlockInTime &&\n urgentThreat.ticksToStartShield <= 3\n\t)\n\t{\n\t\treturn {\n\t\t\tmove: {x: 0, y: 0},\n\t\t\tstartCast: {spell: 'shield'},\n\t\t};\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {getMissileCastTime} from '../../utils/combat.js';\nimport {ARENA_SIZE, GCD_DURATION} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tisSafeToAttack,\n\tgetBlinkToCenter,\n\tgetBlinkToIncreaseDistance,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tshouldForceShield,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 60;\n\n/**\n * Standard homing missile: higher damage, good range. s=5 × dur=150 = 750u range.\n */\nconst STANDARD_CONFIG = {damage: 15, speed: 5, turnRate: 1, duration: 150};\n\n/**\n * Quick homing missile for firing under pressure.\n * Similar speed/turn for warmup compatibility.\n */\nconst QUICK_CONFIG = {damage: 10, speed: 5, turnRate: 1, duration: 80};\n\n/**\n * Standard homing missile AI.\n */\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {\n\t\tturnToward: enemy?.position,\n\t};\n};\n\n/**\n * Check if near wall.\n */\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart strafe with threat-aware dodging and wall avoidance.\n */\nfunction smartStrafe(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\tif (normalizedDistance === 0) return {x: 0, y: 0};\n\n\t// Perpendicular directions\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDir: {x: number; y: number};\n\n\t// Priority 1: Use dodge direction (from threat analysis or close missile avoidance)\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDir = dodgeDirection;\n\t}\n\t// Priority 2: Avoid walls\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDir =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\t// Priority 3: Commit to direction (switch every ~200 ticks)\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDir = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\t// When actively dodging, commit fully — no approach/retreat dilution\n\tconst intensity = dodgeDirection ? 100 : strafeIntensity;\n\treturn {x: strafeDir.x * intensity, y: strafeDir.y * intensity};\n}\n\n/**\n * Bot: Spellspinner\n *\n * BEHAVIOR: Maintains medium range (350 units), strafes constantly to dodge\n * missiles, and fires homing missiles (damage 10, speed 4, turnRate 2). Heavy\n * emphasis on movement — 80% strafe intensity when not dodging, 100% when dodging.\n * Shields only undodgeable threats, emergency blinks when shield isn't available.\n * The constant circular strafing motion traces patterns like thread being spun.\n *\n * NAMING RATIONALE: Like a spider spinning a web of projectiles while circling its\n * prey. The constant strafing movement pattern traces circles — spinning thread\n * around the arena. \"Spell\" + \"spinner\" = a wizard who spins spells around the\n * battlefield. The kiting behavior (maintaining distance while attacking) creates\n * a web-like pattern of missiles and movement that traps opponents.\n *\n * PROGRESSION LINE: Spellspinner → Spellweaver → Spellbinder\n * - Spellspinner (tier 1): Fixed homing missiles, constant strafe, basic defense\n * - Spellweaver (tier 2): + adaptive missile fitting, more sophisticated patterns\n * - Spellbinder (tier 3): Future — inescapable web of magic, perfect distance control\n * The progression: spinner (raw thread) → weaver (creates patterns) → binder\n * (constrains and traps). Each tier's projectile web becomes harder to escape.\n *\n * TIER: 1 (base)\n */\nexport const Spellspinner: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 338, {\n\t\trange: 150,\n\t\tmin: 150,\n\t\tmax: 450,\n\t});\n\tconst dangerDistance = useParam('dangerDistance', 242, {\n\t\trange: 100,\n\t\tmin: 100,\n\t});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 210, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 31, {\n\t\trange: 40,\n\t\tmin: 30,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 25, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 40, {\n\t\trange: 30,\n\t\tmin: 5,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst blinkWindowMax = useParam('blinkWindowMax', 68, {\n\t\trange: 30,\n\t\tmin: 15,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// Smart strafe (dodge direction includes close homing missile avoidance)\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst strafe = smartStrafe(\n\t\tstate.position,\n\t\tenemy,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t);\n\n\t// Distance management (only when not dodging)\n\tlet moveX = strafe.x;\n\tlet moveY = strafe.y;\n\n\tif (!dodgeDirection) \n\t{\n\t\tconst deltaX = enemy.position.x - state.position.x;\n\t\tconst deltaY = enemy.position.y - state.position.y;\n\t\tconst normalizedDistance = distance > 0 ? distance : 1;\n\n\t\tif (distance > targetDistance + distanceDeadZone) \n\t\t{\n\t\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t\telse if (distance < targetDistance - distanceDeadZone) \n\t\t{\n\t\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\t\tif (state.position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\t\tif (state.position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0)\n\t\t\t\tretreatX = 0;\n\t\t\tif (state.position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\t\tif (state.position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0)\n\t\t\t\tretreatY = 0;\n\t\t\tmoveX += retreatX;\n\t\t\tmoveY += retreatY;\n\t\t}\n\t}\n\n\t// Normalize\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\tconst move = {x: moveX, y: moveY};\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\t// Cancel shield if no imminent threats\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}}; // Hold shield if still threatened\n\t}\n\n\t// === DEFENSE: Cancel missile cast if lethal threat arrives during GCD ===\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tconst arrivesInGCD =\n\t\t\turgentThreat.ticksToImpact > remainingCast &&\n urgentThreat.ticksToImpact <= remainingCast + GCD_DURATION;\n\n\t\tif (\n\t\t\tarrivesInGCD &&\n state.health <= urgentThreat.projectile.damage + 1 &&\n remainingCast > 10\n\t\t) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === BLINK-DODGE: For undodgeable threats OR to create attack window ===\n\t// Blink to center (stays in combat range) and dodges the current missile.\n\t// Also blink when persistent threats block all offense (creates safe firing window).\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n state.blinkCooldown === 0 &&\n urgentThreat.ticksToImpact >= 10 &&\n urgentThreat.ticksToImpact <= blinkWindowMax\n\t) \n\t{\n\t\tconst undodgeableOrForced =\n\t\t\t!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat);\n\t\tconst cannotAttack = !isSafeToAttack(\n\t\t\tthreats,\n\t\t\tgetMissileCastTime(QUICK_CONFIG),\n\t\t);\n\n\t\tif (undodgeableOrForced || cannotAttack) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === DEFENSE: Shield undodgeable/high-damage threats (when blink on cooldown) ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (urgentThreat.canBlockInTime) \n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= 3) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\n\t// === DISTANCE BLINK: enemy too close, blink to reestablish kiting range ===\n\tif (\n\t\tstate.state === 'idle' &&\n distance < dangerDistance &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (blinkTarget) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === OFFENSE: Attack when safe ===\n\tif (state.state === 'idle' && distance < 500) \n\t{\n\t\t// Primary: standard homing missile\n\t\tconst standardCastTime = getMissileCastTime(STANDARD_CONFIG);\n\t\tif (isSafeToAttack(threats, standardCastTime)) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: STANDARD_CONFIG,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Fallback: quick missile under pressure\n\t\tconst quickCastTime = getMissileCastTime(QUICK_CONFIG);\n\t\tif (isSafeToAttack(threats, quickCastTime)) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig: QUICK_CONFIG,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n\tgetBlinkToIncreaseDistance,\n\tfitMissileToBudget,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tshouldForceShield,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 60;\n\nconst SHIELD_BUFFER = 3;\n\n/**\n * Standard homing missile AI.\n */\nconst HomingAI: MissileAIFunction = ({worldState}) => \n{\n\tconst enemy = worldState.enemies[0];\n\treturn {\n\t\tturnToward: enemy?.position,\n\t};\n};\n\n/**\n * Check if near wall.\n */\nfunction isNearWall(position: {x: number; y: number}): boolean \n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart strafe with threat-aware dodging and wall avoidance.\n */\nfunction smartStrafe(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n): {x: number; y: number} \n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\tif (normalizedDistance === 0) return {x: 0, y: 0};\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDir: {x: number; y: number};\n\n\tif (dodgeDirection) \n\t{\n\t\tstrafeDir = dodgeDirection;\n\t}\n\telse if (isNearWall(position)) \n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDir =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse \n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDir = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tconst intensity = dodgeDirection ? 100 : strafeIntensity;\n\treturn {x: strafeDir.x * intensity, y: strafeDir.y * intensity};\n}\n\n/**\n * Bot: Spellweaver\n *\n * BEHAVIOR: Enhanced Spellspinner with adaptive missile fitting. Same medium-range\n * kiting playstyle — maintains distance, strafes heavily — but uses fitMissileToBudget\n * to maximize damage within safe attack windows. Always uses homing missiles since\n * kiting means enemies are always moving. More sophisticated than Spellspinner's\n * fixed damage/speed/turnRate configuration.\n *\n * NAMING RATIONALE: A weaver creates intricate patterns from raw thread. Where the\n * Spellspinner produces raw threads of magic (fixed missiles), the Spellweaver\n * combines them into optimized patterns (adaptive fitting). The name suggests\n * craftsmanship and sophistication — the same kiting web, but deliberately woven\n * rather than chaotically spun.\n *\n * PROGRESSION LINE: Spellspinner → Spellweaver → Spellbinder\n * - Spellspinner (tier 1): Fixed homing missiles, constant strafe, basic defense\n * - Spellweaver (tier 2): + adaptive missile fitting, more sophisticated patterns\n * - Spellbinder (tier 3): Future — inescapable web, perfect distance control\n *\n * TIER: 2 (enhanced Spellspinner)\n */\nexport const Spellweaver: WizardFunction = ({state}) => \n{\n\tconst targetDistance = useParam('targetDistance', 322, {\n\t\trange: 150,\n\t\tmin: 0,\n\t});\n\tconst dangerDistance = useParam('dangerDistance', 272, {\n\t\trange: 125,\n\t\tmin: 0,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', 0.2, {range: 1.5, steps: 5});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 296, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 42, {\n\t\trange: 40,\n\t\tmin: 30,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 68, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 12, {\n\t\trange: 30,\n\t\tmin: 5,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 2, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy) \n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// Smart strafe\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst strafe = smartStrafe(\n\t\tstate.position,\n\t\tenemy,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t);\n\n\t// Distance management (only when not dodging)\n\tlet moveX = strafe.x;\n\tlet moveY = strafe.y;\n\n\tif (!dodgeDirection) \n\t{\n\t\tconst deltaX = enemy.position.x - state.position.x;\n\t\tconst deltaY = enemy.position.y - state.position.y;\n\t\tconst normalizedDistance = distance > 0 ? distance : 1;\n\n\t\tif (distance > targetDistance + distanceDeadZone) \n\t\t{\n\t\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t\telse if (distance < targetDistance - distanceDeadZone) \n\t\t{\n\t\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\t\tif (state.position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\t\tif (state.position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0)\n\t\t\t\tretreatX = 0;\n\t\t\tif (state.position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\t\tif (state.position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0)\n\t\t\t\tretreatY = 0;\n\t\t\tmoveX += retreatX;\n\t\t\tmoveY += retreatY;\n\t\t}\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100) \n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\tconst move = {x: moveX, y: moveY};\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling') \n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast only for LETHAL incoming damage ===\n\t// DPS trade: we accepted the hit when we started casting. Only cancel to survive.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t) \n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER\n\t\t) \n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t) \n\t{\n\t\tif (urgentThreat.canBlockInTime) \n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= SHIELD_BUFFER) \n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\telse if (state.blinkCooldown === 0) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === DISTANCE BLINK: enemy too close, blink to reestablish kiting range ===\n\tif (\n\t\tstate.state === 'idle' &&\n distance < dangerDistance &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t) \n\t{\n\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (blinkTarget) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === OFFENSE: Adaptive missile fitting ===\n\t// Skip offense when a lethal missile could reach us during cast+GCD — dodge at full speed instead\n\tconst myProjectileIds = new Set(state.myProjectiles.map((p) => p.id));\n\tconst hasNearbyLethalMissile = state.projectiles.some(\n\t\t(p) =>\n\t\t\t!myProjectileIds.has(p.id) &&\n p.damage >= state.health &&\n distanceTo(state.position, p.position) / p.speed <\n MAX_CAST_BUDGET + GCD_DURATION,\n\t);\n\tif (state.state === 'idle' && distance < 600 && !hasNearbyLethalMissile) \n\t{\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Account for enemy's active cast\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile') \n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTime = distance / flightTimeDivisor;\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t);\n\t\t}\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Spellweaver always uses homing (kiting means enemies are always moving)\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (config && config.damage >= minDmg) \n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Budget too small for useful missile — wait for threat to pass, then fire\n\t}\n\n\treturn {move};\n};\n","import {WizardFunction, MissileAIFunction} from '../../types.js';\nimport {angleTo} from '../../utils/angles.js';\nimport {distanceTo} from '../../utils/distance.js';\nimport {ARENA_SIZE, GCD_DURATION, SHIELD_CAST_TIME} from '../../rules.js';\nimport {getMissileCastTime, getLeadPosition} from '../../utils/combat.js';\nimport {\n\tgetThreats,\n\tgetMostUrgentThreat,\n\tgetDodgeDirectionForMovement,\n\tgetBlinkToCenter,\n\tgetBlinkToIncreaseDistance,\n\tfitMissileToBudget,\n\tMIN_BLINK_ESCAPE_DISTANCE,\n\tshouldForceShield,\n\tMAX_CAST_BUDGET,\n\tMIN_USEFUL_DAMAGE,\n\tgetEnemyVulnerabilityTicks,\n} from '../shared.js';\nimport {useParam} from '../../engine/params-runtime.js';\nconst WALL_BUFFER = 60;\n\nconst SHIELD_BUFFER = 3;\n\n/**\n * Standard homing missile AI.\n */\nconst HomingAI: MissileAIFunction = ({worldState}) =>\n{\n\tconst enemy = worldState.enemies[0];\n\treturn {\n\t\tturnToward: enemy?.position,\n\t};\n};\n\n/**\n * Straight missile AI — no homing, flies in initial direction.\n */\nconst StraightAI: MissileAIFunction = () => ({});\n\n/**\n * Check if near wall.\n */\nfunction isNearWall(position: {x: number; y: number}): boolean\n{\n\treturn (\n\t\tposition.x < WALL_BUFFER ||\n position.x > ARENA_SIZE - WALL_BUFFER ||\n position.y < WALL_BUFFER ||\n position.y > ARENA_SIZE - WALL_BUFFER\n\t);\n}\n\n/**\n * Smart strafe with threat-aware dodging and wall avoidance.\n */\nfunction smartStrafe(\n\tposition: {x: number; y: number},\n\tenemy: {position: {x: number; y: number}},\n\tdodgeDirection: {x: number; y: number} | null,\n\ttick: number,\n\tstrafeCyclePeriod: number,\n\tstrafeIntensity: number,\n): {x: number; y: number}\n{\n\tconst deltaX = enemy.position.x - position.x;\n\tconst deltaY = enemy.position.y - position.y;\n\tconst normalizedDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);\n\tif (normalizedDistance === 0) return {x: 0, y: 0};\n\n\tconst strafeClockwise = {\n\t\tx: -deltaY / normalizedDistance,\n\t\ty: deltaX / normalizedDistance,\n\t};\n\tconst strafeCounterClockwise = {\n\t\tx: deltaY / normalizedDistance,\n\t\ty: -deltaX / normalizedDistance,\n\t};\n\n\tlet strafeDir: {x: number; y: number};\n\n\tif (dodgeDirection)\n\t{\n\t\tstrafeDir = dodgeDirection;\n\t}\n\telse if (isNearWall(position))\n\t{\n\t\tconst futureClockwise = {\n\t\t\tx: position.x + strafeClockwise.x * 100,\n\t\t\ty: position.y + strafeClockwise.y * 100,\n\t\t};\n\t\tconst futureCounterClockwise = {\n\t\t\tx: position.x + strafeCounterClockwise.x * 100,\n\t\t\ty: position.y + strafeCounterClockwise.y * 100,\n\t\t};\n\t\tconst scoreClockwise = Math.min(\n\t\t\tfutureClockwise.x,\n\t\t\tARENA_SIZE - futureClockwise.x,\n\t\t\tfutureClockwise.y,\n\t\t\tARENA_SIZE - futureClockwise.y,\n\t\t);\n\t\tconst scoreCounterClockwise = Math.min(\n\t\t\tfutureCounterClockwise.x,\n\t\t\tARENA_SIZE - futureCounterClockwise.x,\n\t\t\tfutureCounterClockwise.y,\n\t\t\tARENA_SIZE - futureCounterClockwise.y,\n\t\t);\n\t\tstrafeDir =\n\t\t\tscoreClockwise > scoreCounterClockwise\n\t\t\t\t? strafeClockwise\n\t\t\t\t: strafeCounterClockwise;\n\t}\n\telse\n\t{\n\t\tconst cycle = Math.floor(tick / strafeCyclePeriod) % 2;\n\t\tstrafeDir = cycle === 0 ? strafeClockwise : strafeCounterClockwise;\n\t}\n\n\tconst intensity = dodgeDirection ? 100 : strafeIntensity;\n\treturn {x: strafeDir.x * intensity, y: strafeDir.y * intensity};\n}\n\n/**\n * Bot: Spellbinder\n *\n * BEHAVIOR: Enhanced Spellweaver with vulnerability exploitation. Same medium-range\n * kiting playstyle — maintains distance, strafes heavily, uses fitMissileToBudget\n * for adaptive homing missiles. The T3 upgrade adds a punish mode that fires fast\n * straight missiles timed to land while the enemy is locked in a cast or GCD,\n * when they cannot shield. Defense, movement, and standard offense are identical\n * to Spellweaver.\n *\n * NAMING RATIONALE: A binder constrains and locks down opponents. Where the\n * Spellweaver optimizes missile patterns (adaptive fitting), the Spellbinder\n * reads the enemy's state and punishes vulnerability windows — binding them\n * to their commitments with unavoidable damage.\n *\n * PROGRESSION LINE: Spellspinner → Spellweaver → Spellbinder\n * - Spellspinner (tier 1): Fixed homing missiles, constant strafe, basic defense\n * - Spellweaver (tier 2): + adaptive missile fitting, more sophisticated patterns\n * - Spellbinder (tier 3): + vulnerability punish mode with fast straight missiles\n *\n * TIER: 3 (elite Spellspinner line)\n */\nexport const Spellbinder: WizardFunction = ({state}) =>\n{\n\tconst targetDistance = useParam('targetDistance', 134, {\n\t\trange: 150,\n\t\tmin: 0,\n\t});\n\tconst dangerDistance = useParam('dangerDistance', 389, {\n\t\trange: 125,\n\t\tmin: 0,\n\t});\n\tconst minTurnRate = useParam('minTurnRate', 0.2, {range: 1.5, steps: 5});\n\tconst strafeCyclePeriod = useParam('strafeCyclePeriod', 123, {\n\t\trange: 75,\n\t\tmin: 80,\n\t\tmax: 300,\n\t});\n\tconst strafeIntensity = useParam('strafeIntensity', 30, {\n\t\trange: 40,\n\t\tmin: 30,\n\t\tmax: 100,\n\t\tsteps: 7,\n\t});\n\tconst approachSpeed = useParam('approachSpeed', 50, {\n\t\trange: 30,\n\t\tmin: 10,\n\t\tmax: 70,\n\t\tsteps: 7,\n\t});\n\tconst distanceDeadZone = useParam('distanceDeadZone', 7, {\n\t\trange: 30,\n\t\tmin: 5,\n\t\tmax: 80,\n\t\tsteps: 7,\n\t});\n\tconst shieldCancelThreshold = useParam('shieldCancelThreshold', 150, {\n\t\trange: 75,\n\t\tmin: 50,\n\t\tmax: 300,\n\t});\n\tconst flightTimeDivisor = useParam('flightTimeDivisor', 10.8, {\n\t\trange: 4,\n\t\tmin: 2,\n\t\tmax: 12,\n\t\tsteps: 5,\n\t});\n\tconst punishMinVulnerability = useParam('punishMinVulnerability', 30, {\n\t\trange: 80,\n\t\tmin: 30,\n\t\tsteps: 7,\n\t});\n\n\tconst enemy = state.enemies[0];\n\tif (!enemy)\n\t{\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\tconst distance = distanceTo(state.position, enemy.position);\n\n\t// Analyze threats using simulation\n\tconst threats = getThreats(state);\n\tconst urgentThreat = getMostUrgentThreat(threats);\n\n\t// Smart strafe\n\tconst dodgeDirection = getDodgeDirectionForMovement(threats, state.position);\n\tconst strafe = smartStrafe(\n\t\tstate.position,\n\t\tenemy,\n\t\tdodgeDirection,\n\t\tstate.tick,\n\t\tstrafeCyclePeriod,\n\t\tstrafeIntensity,\n\t);\n\n\t// Distance management (only when not dodging)\n\tlet moveX = strafe.x;\n\tlet moveY = strafe.y;\n\n\tif (!dodgeDirection)\n\t{\n\t\tconst deltaX = enemy.position.x - state.position.x;\n\t\tconst deltaY = enemy.position.y - state.position.y;\n\t\tconst normalizedDistance = distance > 0 ? distance : 1;\n\n\t\tif (distance > targetDistance + distanceDeadZone)\n\t\t{\n\t\t\tmoveX += (deltaX / normalizedDistance) * approachSpeed;\n\t\t\tmoveY += (deltaY / normalizedDistance) * approachSpeed;\n\t\t}\n\t\telse if (distance < targetDistance - distanceDeadZone)\n\t\t{\n\t\t\tlet retreatX = -(deltaX / normalizedDistance) * approachSpeed;\n\t\t\tlet retreatY = -(deltaY / normalizedDistance) * approachSpeed;\n\t\t\tif (state.position.x < WALL_BUFFER && retreatX < 0) retreatX = 0;\n\t\t\tif (state.position.x > ARENA_SIZE - WALL_BUFFER && retreatX > 0)\n\t\t\t\tretreatX = 0;\n\t\t\tif (state.position.y < WALL_BUFFER && retreatY < 0) retreatY = 0;\n\t\t\tif (state.position.y > ARENA_SIZE - WALL_BUFFER && retreatY > 0)\n\t\t\t\tretreatY = 0;\n\t\t\tmoveX += retreatX;\n\t\t\tmoveY += retreatY;\n\t\t}\n\t}\n\n\tconst magnitude = Math.sqrt(moveX * moveX + moveY * moveY);\n\tif (magnitude > 100)\n\t{\n\t\tmoveX = (moveX / magnitude) * 100;\n\t\tmoveY = (moveY / magnitude) * 100;\n\t}\n\n\tconst move = {x: moveX, y: moveY};\n\n\t// === DEFENSE: Handle channeling ===\n\tif (state.state === 'channeling')\n\t{\n\t\tif (!urgentThreat || urgentThreat.ticksToImpact > shieldCancelThreshold)\n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t\treturn {move: {x: 0, y: 0}};\n\t}\n\n\t// === DEFENSE: Cancel missile cast only for LETHAL incoming damage ===\n\t// DPS trade: we accepted the hit when we started casting. Only cancel to survive.\n\tif (\n\t\tstate.state === 'casting' &&\n state.castingSpell === 'missile' &&\n urgentThreat &&\n urgentThreat.projectile.damage >= state.health\n\t)\n\t{\n\t\tconst remainingCast = (state.castDuration ?? 0) - (state.castProgress ?? 0);\n\t\tif (\n\t\t\turgentThreat.ticksToImpact <=\n remainingCast + GCD_DURATION + SHIELD_CAST_TIME + SHIELD_BUFFER\n\t\t)\n\t\t{\n\t\t\treturn {move, cancel: true};\n\t\t}\n\t}\n\n\t// === DEFENSE: Shield undodgeable or high-damage homing threats ===\n\tif (\n\t\tstate.state === 'idle' &&\n urgentThreat &&\n (!urgentThreat.bestDodgeDirection || shouldForceShield(urgentThreat))\n\t)\n\t{\n\t\tif (urgentThreat.canBlockInTime)\n\t\t{\n\t\t\tif (urgentThreat.ticksToStartShield <= SHIELD_BUFFER)\n\t\t\t{\n\t\t\t\treturn {\n\t\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\t\tstartCast: {spell: 'shield'},\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t\telse if (state.blinkCooldown === 0)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: getBlinkToCenter(state.position)},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === DISTANCE BLINK: enemy too close, blink to reestablish kiting range ===\n\tif (\n\t\tstate.state === 'idle' &&\n distance < dangerDistance &&\n state.blinkCooldown === 0 &&\n (!urgentThreat || urgentThreat.bestDodgeDirection !== null)\n\t)\n\t{\n\t\tconst blinkTarget = getBlinkToIncreaseDistance(\n\t\t\tstate.position,\n\t\t\tenemy.position,\n\t\t\tstate.projectiles,\n\t\t\tMIN_BLINK_ESCAPE_DISTANCE,\n\t\t);\n\t\tif (blinkTarget)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove: {x: 0, y: 0},\n\t\t\t\tstartCast: {spell: 'blink', target: blinkTarget},\n\t\t\t};\n\t\t}\n\t}\n\n\t// === OFFENSE: Punish mode — fast straight missiles during enemy vulnerability ===\n\tif (\n\t\tstate.state === 'idle' &&\n getEnemyVulnerabilityTicks(enemy) >= punishMinVulnerability &&\n distance < 500\n\t)\n\t{\n\t\tconst vulnerabilityTicks = getEnemyVulnerabilityTicks(enemy);\n\t\tconst flightTimeEstimate = distance / flightTimeDivisor;\n\t\tconst punishBudget = vulnerabilityTicks - flightTimeEstimate;\n\t\tconst nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst effectiveBudget = Math.min(\n\t\t\tpunishBudget,\n\t\t\tsafeBudget,\n\t\t\tMAX_CAST_BUDGET,\n\t\t);\n\n\t\tif (effectiveBudget > 0)\n\t\t{\n\t\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\t\tconst config = fitMissileToBudget(effectiveBudget, distance, {\n\t\t\t\tminTurnRate: 0,\n\t\t\t\tmaxDamage: enemyHP,\n\t\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t\t});\n\n\t\t\tif (config && config.damage >= MIN_USEFUL_DAMAGE)\n\t\t\t{\n\t\t\t\tconst castTime = getMissileCastTime(config, state.lastMissileConfig);\n\t\t\t\tconst actualFlight = distance / config.speed;\n\n\t\t\t\tif (castTime + actualFlight < vulnerabilityTicks)\n\t\t\t\t{\n\t\t\t\t\tconst aimTarget =\n\t\t\t\t\t\tconfig.turnRate > 0\n\t\t\t\t\t\t\t? enemy.position\n\t\t\t\t\t\t\t: getLeadPosition(\n\t\t\t\t\t\t\t\tenemy.position,\n\t\t\t\t\t\t\t\tenemy.velocity,\n\t\t\t\t\t\t\t\tconfig.speed,\n\t\t\t\t\t\t\t\tstate.position,\n\t\t\t\t\t\t\t);\n\t\t\t\t\treturn {\n\t\t\t\t\t\tmove,\n\t\t\t\t\t\tstartCast: {\n\t\t\t\t\t\t\tspell: 'missile',\n\t\t\t\t\t\t\tconfig,\n\t\t\t\t\t\t\tmissileAI: config.turnRate > 0 ? HomingAI : StraightAI,\n\t\t\t\t\t\t\tdirection: angleTo(state.position, aimTarget),\n\t\t\t\t\t\t},\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// === OFFENSE: Adaptive missile fitting (identical to Spellweaver) ===\n\t// Skip offense when a lethal missile could reach us during cast+GCD — dodge at full speed instead\n\tconst myProjectileIds = new Set(state.myProjectiles.map((p) => p.id));\n\tconst hasNearbyLethalMissile = state.projectiles.some(\n\t\t(p) =>\n\t\t\t!myProjectileIds.has(p.id) &&\n p.damage >= state.health &&\n distanceTo(state.position, p.position) / p.speed <\n MAX_CAST_BUDGET + GCD_DURATION,\n\t);\n\tif (state.state === 'idle' && distance < 600 && !hasNearbyLethalMissile)\n\t{\n\t\tlet nextThreatTime = urgentThreat?.ticksToImpact ?? Infinity;\n\n\t\t// Account for enemy's active cast\n\t\tif (enemy.state === 'casting' && enemy.castingSpell === 'missile')\n\t\t{\n\t\t\tconst remainingEnemyCast =\n\t\t\t\t(enemy.castDuration ?? 0) - (enemy.castProgress ?? 0);\n\t\t\tconst estimatedFlightTime = distance / flightTimeDivisor;\n\t\t\tnextThreatTime = Math.min(\n\t\t\t\tnextThreatTime,\n\t\t\t\tremainingEnemyCast + estimatedFlightTime,\n\t\t\t);\n\t\t}\n\t\tconst safeBudget =\n\t\t\tnextThreatTime - GCD_DURATION - SHIELD_CAST_TIME - SHIELD_BUFFER;\n\t\tconst budgetTicks = Math.min(safeBudget, MAX_CAST_BUDGET);\n\n\t\t// Spellbinder always uses homing in standard mode (kiting means enemies are always moving)\n\t\tconst enemyHP = Math.ceil(enemy.health);\n\t\tconst minDmg = Math.min(MIN_USEFUL_DAMAGE, enemyHP);\n\t\tconst config = fitMissileToBudget(budgetTicks, distance, {\n\t\t\tminTurnRate,\n\t\t\tmaxDamage: enemyHP,\n\t\t\tlastMissileConfig: state.lastMissileConfig,\n\t\t});\n\n\t\tif (config && config.damage >= minDmg)\n\t\t{\n\t\t\treturn {\n\t\t\t\tmove,\n\t\t\t\tstartCast: {\n\t\t\t\t\tspell: 'missile',\n\t\t\t\t\tconfig,\n\t\t\t\t\tmissileAI: HomingAI,\n\t\t\t\t\tdirection: angleTo(state.position, enemy.position),\n\t\t\t\t},\n\t\t\t};\n\t\t}\n\n\t\t// Budget too small for useful missile — wait for threat to pass, then fire\n\t}\n\n\treturn {move};\n};\n","/**\r\n * VIBEMANCER - BOT REGISTRY\r\n *\r\n * Single source of truth for all bots, ordered from weakest to strongest.\r\n * Run the tournament test to determine the correct ordering.\r\n *\r\n * To reorder: run `npx vitest run tests/bots/tournament.test.ts`\r\n * and update the list below based on the results.\r\n *\r\n * BOT NAMING SCHEME (3-tier progression):\r\n *\r\n * | Group | Tier 1 (base) | Tier 2 (enhanced) | Tier 3 (elite) |\r\n * |------------|----------------|-------------------|-----------------|\r\n * | Defensive | Turtle | Sentinel | Golem |\r\n * | Duelist | Battlemage | Warmage | Archmage |\r\n * | Homing | Bonemancer | Lich | Archlich |\r\n * | Caster | Flamecaller | Pyromancer | Infernalist |\r\n * | Melee | Shadowblade | Nightblade | Voidblade |\r\n * | Sniper | Spellshot | Spelltracer | Spellseeker |\r\n * | Berserker | Stormchaser | Stormcaller | Stormforger |\r\n * | Kiter | Spellspinner | Spellweaver | Spellbinder |\r\n *\r\n * Standalone: TargetDummy, Critter, Hogger, Rookie, Doombringer\r\n * Reserved: Spellslinger (future volume-of-fire archetype)\r\n *\r\n */\r\n\r\nimport {WizardFunction} from '../types.js';\r\nimport {TargetDummy} from './standalone/TargetDummy.js';\r\nimport {Critter} from './standalone/Critter.js';\r\nimport {Rookie} from './standalone/Rookie.js';\r\nimport {Hogger} from './standalone/Hogger.js';\r\nimport {Doombringer} from './standalone/Doombringer.js';\r\nimport {Turtle} from './defensive/01_Turtle.js';\r\nimport {Sentinel} from './defensive/02_Sentinel.js';\r\nimport {Golem} from './defensive/03_Golem.js';\r\nimport {Battlemage} from './duelist/01_Battlemage.js';\r\nimport {Warmage} from './duelist/02_Warmage.js';\r\nimport {Archmage} from './duelist/03_Archmage.js';\r\nimport {Bonemancer} from './homing/01_Bonemancer.js';\r\nimport {Lich} from './homing/02_Lich.js';\r\nimport {Archlich} from './homing/03_Archlich.js';\r\nimport {Flamecaller} from './caster/01_Flamecaller.js';\r\nimport {Pyromancer} from './caster/02_Pyromancer.js';\r\nimport {Infernalist} from './caster/03_Infernalist.js';\r\nimport {Shadowblade} from './melee/01_Shadowblade.js';\r\nimport {Nightblade} from './melee/02_Nightblade.js';\r\nimport {Voidblade} from './melee/03_Voidblade.js';\r\nimport {Spellshot} from './sniper/01_Spellshot.js';\r\nimport {Spelltracer} from './sniper/02_Spelltracer.js';\r\nimport {Spellseeker} from './sniper/03_Spellseeker.js';\r\nimport {Stormchaser} from './berserker/01_Stormchaser.js';\r\nimport {Stormcaller} from './berserker/02_Stormcaller.js';\r\nimport {Stormforger} from './berserker/03_Stormforger.js';\r\nimport {Spellspinner} from './kiter/01_Spellspinner.js';\r\nimport {Spellweaver} from './kiter/02_Spellweaver.js';\r\nimport {Spellbinder} from './kiter/03_Spellbinder.js';\r\n\r\nexport interface BotEntry\r\n{\r\n\tname: string;\r\n\tai: WizardFunction;\r\n\tdescription: string;\r\n\ttier?: number; // 1, 2, 3 for progression bots; undefined for standalone\r\n\tgroup: string; // progression line name or 'Standalone'\r\n}\r\n\r\nexport interface BotGroup\r\n{\r\n\tlabel: string;\r\n\tbots: BotEntry[];\r\n}\r\n\r\n/**\r\n * All bots organized by progression line.\r\n * Each group contains bots from the same archetype, ordered by tier.\r\n */\r\nexport const BOT_GROUPS: BotGroup[] = [\r\n\t{\r\n\t\tlabel: 'Standalone',\r\n\t\tbots: [\r\n\t\t\t{name: 'TargetDummy', ai: TargetDummy, description: 'Does nothing (for testing)', group: 'Standalone'},\r\n\t\t\t{name: 'Critter', ai: Critter, description: 'Random actions (chaos test)', group: 'Standalone'},\r\n\t\t\t{name: 'Rookie', ai: Rookie, description: 'Basic homing missiles', group: 'Standalone'},\r\n\t\t\t{name: 'Hogger', ai: Hogger, description: 'Chaotic elite — random but dangerous', group: 'Standalone'},\r\n\t\t\t{name: 'Doombringer', ai: Doombringer, description: 'Single max-damage shot benchmark', group: 'Standalone'},\r\n\t\t],\r\n\t},\r\n\t{\r\n\t\tlabel: 'Defensive',\r\n\t\tbots: [\r\n\t\t\t{name: 'Turtle', ai: Turtle, description: 'Stationary tank, last-moment shields', tier: 1, group: 'Defensive'},\r\n\t\t\t{name: 'Sentinel', ai: Sentinel, description: 'Two-tier offense (big + fast missiles) + timed shields', tier: 2, group: 'Defensive'},\r\n\t\t\t{name: 'Golem', ai: Golem, description: 'Stationary fortress, counterattack punish', tier: 3, group: 'Defensive'},\r\n\t\t],\r\n\t},\r\n\t{\r\n\t\tlabel: 'Melee',\r\n\t\tbots: [\r\n\t\t\t{name: 'Shadowblade', ai: Shadowblade, description: 'Blink-in melee assassin', tier: 1, group: 'Melee'},\r\n\t\t\t{name: 'Nightblade', ai: Nightblade, description: '+ preemptive shields, smart stabs', tier: 2, group: 'Melee'},\r\n\t\t\t{name: 'Voidblade', ai: Voidblade, description: '+ vulnerability engage, disengage mode', tier: 3, group: 'Melee'},\r\n\t\t],\r\n\t},\r\n\t{\r\n\t\tlabel: 'Homing',\r\n\t\tbots: [\r\n\t\t\t{name: 'Bonemancer', ai: Bonemancer, description: 'Slow homing missile spam', tier: 1, group: 'Homing'},\r\n\t\t\t{name: 'Lich', ai: Lich, description: 'Slow missile swarm + defense', tier: 2, group: 'Homing'},\r\n\t\t\t{name: 'Archlich', ai: Archlich, description: '+ vulnerability punish missiles during enemy GCD', tier: 3, group: 'Homing'},\r\n\t\t],\r\n\t},\r\n\t{\r\n\t\tlabel: 'Caster',\r\n\t\tbots: [\r\n\t\t\t{name: 'Flamecaller', ai: Flamecaller, description: 'Fixed homing missiles, long range', tier: 1, group: 'Caster'},\r\n\t\t\t{name: 'Pyromancer', ai: Pyromancer, description: '+ adaptive fitting, cast canceling', tier: 2, group: 'Caster'},\r\n\t\t\t{name: 'Infernalist', ai: Infernalist, description: '+ warmup exploitation, punish mode', tier: 3, group: 'Caster'},\r\n\t\t],\r\n\t},\r\n\t{\r\n\t\tlabel: 'Sniper',\r\n\t\tbots: [\r\n\t\t\t{name: 'Spellshot', ai: Spellshot, description: 'Intercept prediction shots', tier: 1, group: 'Sniper'},\r\n\t\t\t{name: 'Spelltracer', ai: Spelltracer, description: '+ adaptive fitting, full defense', tier: 2, group: 'Sniper'},\r\n\t\t\t{name: 'Spellseeker', ai: Spellseeker, description: '+ intercept-aimed punish, proactive kiting', tier: 3, group: 'Sniper'},\r\n\t\t],\r\n\t},\r\n\t{\r\n\t\tlabel: 'Duelist',\r\n\t\tbots: [\r\n\t\t\t{name: 'Battlemage', ai: Battlemage, description: 'Close-range fighter, cast interrupts', tier: 1, group: 'Duelist'},\r\n\t\t\t{name: 'Warmage', ai: Warmage, description: '+ adaptive missile fitting', tier: 2, group: 'Duelist'},\r\n\t\t\t{name: 'Archmage', ai: Archmage, description: '+ range-adaptive, HP-aware aggression', tier: 3, group: 'Duelist'},\r\n\t\t],\r\n\t},\r\n\t{\r\n\t\tlabel: 'Berserker',\r\n\t\tbots: [\r\n\t\t\t{name: 'Stormchaser', ai: Stormchaser, description: 'Aggressive trader, predictive homing', tier: 1, group: 'Berserker'},\r\n\t\t\t{name: 'Stormcaller', ai: Stormcaller, description: '+ budget-fitted homing, smarter defense', tier: 2, group: 'Berserker'},\r\n\t\t\t{name: 'Stormforger', ai: Stormforger, description: '+ vulnerability punish missiles during enemy GCD', tier: 3, group: 'Berserker'},\r\n\t\t],\r\n\t},\r\n\t{\r\n\t\tlabel: 'Kiter',\r\n\t\tbots: [\r\n\t\t\t{name: 'Spellspinner', ai: Spellspinner, description: 'Kiting with homing missiles', tier: 1, group: 'Kiter'},\r\n\t\t\t{name: 'Spellweaver', ai: Spellweaver, description: '+ budget-fitted adaptive missiles', tier: 2, group: 'Kiter'},\r\n\t\t\t{name: 'Spellbinder', ai: Spellbinder, description: '+ vulnerability punish missiles during enemy GCD', tier: 3, group: 'Kiter'},\r\n\t\t],\r\n\t},\r\n];\r\n\r\n// Flat map for lookup by name\r\nconst BOT_MAP = new Map<string, BotEntry>();\r\nfor (const group of BOT_GROUPS)\r\n{\r\n\tfor (const bot of group.bots)\r\n\t{\r\n\t\tBOT_MAP.set(bot.name, bot);\r\n\t}\r\n}\r\n\r\n/**\r\n * All bots ordered from weakest (#1) to strongest (#29).\r\n * Based on round-robin tournament results.\r\n * To update: run `npm run tournament`\r\n */\r\nconst ALL_BOTS_ORDER = [\r\n\t'TargetDummy', // #1 (1 pts, 0W)\r\n\t'Critter', // #2 (1 pts, 0W)\r\n\t'Hogger', // #3 (9 pts, 29W)\r\n\t'Bonemancer', // #4 (9 pts, 32W)\r\n\t'Rookie', // #5 (9 pts, 38W)\r\n\t'Turtle', // #6 (27 pts, 90W)\r\n\t'Spellspinner', // #7 (27 pts, 104W)\r\n\t'Flamecaller', // #8 (28 pts, 103W)\r\n\t'Doombringer', // #9 (30 pts, 108W)\r\n\t'Golem', // #10 (30 pts, 126W)\r\n\t'Sentinel', // #11 (30 pts, 98W)\r\n\t'Shadowblade', // #12 (33 pts, 120W)\r\n\t'Stormchaser', // #13 (36 pts, 116W)\r\n\t'Warmage', // #14 (39 pts, 144W)\r\n\t'Battlemage', // #15 (45 pts, 141W)\r\n\t'Spellweaver', // #16 (45 pts, 164W)\r\n\t'Stormcaller', // #17 (48 pts, 170W)\r\n\t'Pyromancer', // #18 (48 pts, 161W)\r\n\t'Lich', // #19 (51 pts, 165W)\r\n\t'Archlich', // #20 (58 pts, 184W)\r\n\t'Infernalist', // #21 (60 pts, 197W)\r\n\t'Stormforger', // #22 (60 pts, 211W)\r\n\t'Spellshot', // #23 (63 pts, 196W)\r\n\t'Spelltracer', // #24 (63 pts, 186W)\r\n\t'Spellbinder', // #25 (66 pts, 199W)\r\n\t'Archmage', // #26 (72 pts, 229W)\r\n\t'Nightblade', // #27 (72 pts, 230W)\r\n\t'Spellseeker', // #28 (76 pts, 243W)\r\n\t'Voidblade', // #29 (78 pts, 258W)\r\n];\r\n\r\nexport const ALL_BOTS: BotEntry[] = ALL_BOTS_ORDER.map((name) =>\r\n{\r\n\tconst bot = BOT_MAP.get(name);\r\n\tif (!bot) throw new Error(`Bot \"${name}\" not found in BOT_GROUPS`);\r\n\treturn bot;\r\n});\r\n","export * from './standalone/TargetDummy.js';\r\nexport * from './standalone/Rookie.js';\r\nexport * from './standalone/Critter.js';\r\nexport * from './standalone/Hogger.js';\r\nexport * from './standalone/Doombringer.js';\r\nexport * from './defensive/01_Turtle.js';\r\nexport * from './defensive/02_Sentinel.js';\r\nexport * from './defensive/03_Golem.js';\r\nexport * from './melee/01_Shadowblade.js';\r\nexport * from './melee/02_Nightblade.js';\r\nexport * from './melee/03_Voidblade.js';\r\nexport * from './homing/01_Bonemancer.js';\r\nexport * from './homing/02_Lich.js';\r\nexport * from './homing/03_Archlich.js';\r\nexport * from './caster/01_Flamecaller.js';\r\nexport * from './caster/02_Pyromancer.js';\r\nexport * from './caster/03_Infernalist.js';\r\nexport * from './sniper/01_Spellshot.js';\r\nexport * from './sniper/02_Spelltracer.js';\r\nexport * from './sniper/03_Spellseeker.js';\r\nexport * from './duelist/01_Battlemage.js';\r\nexport * from './duelist/02_Warmage.js';\r\nexport * from './duelist/03_Archmage.js';\r\nexport * from './berserker/01_Stormchaser.js';\r\nexport * from './berserker/02_Stormcaller.js';\r\nexport * from './berserker/03_Stormforger.js';\r\nexport * from './kiter/01_Spellspinner.js';\r\nexport * from './kiter/02_Spellweaver.js';\r\nexport * from './kiter/03_Spellbinder.js';\r\nexport * from './registry.js';\r\n","/**\r\n * VIBEMANCER - STATE HOOKS\r\n *\r\n * React-style hooks for reading game state.\r\n * These handle the \"subconscious\" perception that humans do instinctively.\r\n *\r\n * UNITS REFERENCE (100 ticks = 1 second):\r\n * Position: absolute world coordinates, 0-800 on each axis (800×800 arena)\r\n * Velocity: units per tick on each axis (player max speed = 1 u/t)\r\n * Health: hit points (max 60)\r\n * Ticks: game ticks (10ms each, 100/sec). Divide by 100 for seconds.\r\n * Shield: block multiplier 0.0-0.9 (0.9 = blocks 90% damage)\r\n */\r\n\r\nimport {Position, Velocity, ProjectileState} from '../types.js';\r\nimport {getBotContext, useMemo} from '../engine/hooks-runtime.js';\r\nimport {SHIELD_MAX_BLOCK, SHIELD_DECAY_PER_SECOND, SHIELD_MIN_BLOCK, TICKS_PER_SECOND} from '../rules.js';\r\nimport type {EnemyState, AnalyzedThreat} from './types.js';\r\nimport {analyzeThreats} from './threat-analysis.js';\r\n\r\n// ============================================================\r\n// SELF STATE HOOKS\r\n// ============================================================\r\n\r\n/**\r\n * Get your current health (0-60). Wizard dies at 0.\r\n */\r\nexport function useHealth(): number\r\n{\r\n\treturn getBotContext().health;\r\n}\r\n\r\n/**\r\n * Get your current position as {x, y} in world coordinates (0-800).\r\n * Position is clamped to [5, 795] (arena bounds minus wizard radius).\r\n */\r\nexport function usePosition(): Position\r\n{\r\n\tconst ctx = getBotContext();\r\n\treturn {x: ctx.position.x, y: ctx.position.y};\r\n}\r\n\r\n/**\r\n * Get your current velocity as {x, y} in units/tick.\r\n * Max magnitude is 1 u/t when idle, 0.5 u/t when casting, 0 when channeling shield.\r\n */\r\nexport function useVelocity(): Velocity\r\n{\r\n\tconst ctx = getBotContext();\r\n\treturn {x: ctx.velocity.x, y: ctx.velocity.y};\r\n}\r\n\r\n/**\r\n * Get your current status:\r\n * - 'idle': free to act\r\n * - 'casting': casting a spell (missile or blink). Can move at 50% speed.\r\n * - 'channeling': channeling shield. Cannot move. Cancel anytime with cancel().\r\n * - 'gcd_locked': global cooldown after spell. Can move at full speed, but cannot cast.\r\n */\r\nexport function useStatus(): 'idle' | 'casting' | 'channeling' | 'gcd_locked'\r\n{\r\n\treturn getBotContext().state;\r\n}\r\n\r\n/**\r\n * Get ticks until you can start a new spell.\r\n *\r\n * Returns 0 when idle or channeling (shield can be canceled immediately).\r\n * During casting: remaining cast ticks. During GCD: remaining GCD ticks.\r\n *\r\n * Note: 100 ticks = 1 second.\r\n */\r\nexport function useTicksUntilReady(): number\r\n{\r\n\tconst ctx = getBotContext();\r\n\r\n\tswitch (ctx.state)\r\n\t{\r\n\t\tcase 'idle':\r\n\t\t\treturn 0;\r\n\t\tcase 'casting':\r\n\t\t\t// Remaining cast time\r\n\t\t\tif (ctx.castDuration !== undefined && ctx.castProgress !== undefined)\r\n\t\t\t{\r\n\t\t\t\treturn ctx.castDuration - ctx.castProgress;\r\n\t\t\t}\r\n\t\t\treturn 0;\r\n\t\tcase 'channeling':\r\n\t\t\t// Can cancel anytime, so 0\r\n\t\t\treturn 0;\r\n\t\tcase 'gcd_locked':\r\n\t\t\treturn ctx.gcdRemaining ?? 0;\r\n\t\tdefault:\r\n\t\t\treturn 0;\r\n\t}\r\n}\r\n\r\n/**\r\n * Get current shield block multiplier.\r\n *\r\n * Returns 0 if not channeling shield.\r\n * Returns 0.3-0.9 if channeling: starts at 0.9 (blocks 90%), decays by 0.2/sec,\r\n * minimum 0.3 (blocks 30%). The remaining damage gets through:\r\n * actualDamage = incomingDamage × (1 - shieldStrength).\r\n */\r\nexport function useShieldStrength(): number\r\n{\r\n\tconst ctx = getBotContext();\r\n\r\n\tif (ctx.state !== 'channeling' || ctx.channelingSpell !== 'shield')\r\n\t{\r\n\t\treturn 0;\r\n\t}\r\n\r\n\t// Calculate shield strength based on channel duration\r\n\tconst channelDuration = ctx.channelDuration ?? 0;\r\n\tconst decayPerTick = SHIELD_DECAY_PER_SECOND / TICKS_PER_SECOND;\r\n\tconst strength = SHIELD_MAX_BLOCK - (channelDuration * decayPerTick);\r\n\treturn Math.max(SHIELD_MIN_BLOCK, strength);\r\n}\r\n\r\n/**\r\n * Get blink cooldown remaining in ticks. Returns 0 if ready to blink.\r\n *\r\n * Cooldown scales with distance used:\r\n * - 100 units → ~100 ticks (1s)\r\n * - 300 units (max range) → 2000 ticks (20s)\r\n *\r\n * Note: 100 ticks = 1 second.\r\n */\r\nexport function useBlinkCooldown(): number\r\n{\r\n\treturn getBotContext().blinkCooldown;\r\n}\r\n\r\n/**\r\n * Get currently casting spell, or null if not casting.\r\n * Returns 'missile', 'shield', or 'blink'.\r\n */\r\nexport function useCastingSpell(): 'missile' | 'shield' | 'blink' | null\r\n{\r\n\tconst ctx = getBotContext();\r\n\tif (ctx.state !== 'casting')\r\n\t{\r\n\t\treturn null;\r\n\t}\r\n\treturn ctx.castingSpell ?? null;\r\n}\r\n\r\n/**\r\n * Get cast progress as {current, total} in ticks, or null if not casting.\r\n *\r\n * current = ticks elapsed, total = ticks needed. When current >= total, spell fires.\r\n * Note: 100 ticks = 1 second.\r\n */\r\nexport function useCastProgress(): {current: number; total: number} | null\r\n{\r\n\tconst ctx = getBotContext();\r\n\tif (ctx.state !== 'casting')\r\n\t{\r\n\t\treturn null;\r\n\t}\r\n\tif (ctx.castProgress === undefined || ctx.castDuration === undefined)\r\n\t{\r\n\t\treturn null;\r\n\t}\r\n\treturn {current: ctx.castProgress, total: ctx.castDuration};\r\n}\r\n\r\n// ============================================================\r\n// ENEMY STATE HOOK\r\n// ============================================================\r\n\r\n/**\r\n * Get enemy wizard state.\r\n *\r\n * Returns position, velocity, health, status, casting spell, and shield strength.\r\n * Note: you cannot see the enemy's missile configs or exact cooldown timers —\r\n * only their status and what's visible on the field.\r\n */\r\nexport function useEnemy(): EnemyState\r\n{\r\n\tconst ctx = getBotContext();\r\n\tconst enemy = ctx.enemies[0];\r\n\r\n\tif (!enemy)\r\n\t{\r\n\t\t// No enemy - return default state\r\n\t\treturn {\r\n\t\t\tposition: {x: 0, y: 0},\r\n\t\t\tvelocity: {x: 0, y: 0},\r\n\t\t\thealth: 0,\r\n\t\t\tstatus: 'idle',\r\n\t\t\tcastingSpell: null,\r\n\t\t\tshieldStrength: 0,\r\n\t\t};\r\n\t}\r\n\r\n\t// Calculate enemy shield strength\r\n\tlet shieldStrength = 0;\r\n\tif (enemy.state === 'channeling' && enemy.channelingSpell === 'shield')\r\n\t{\r\n\t\tconst channelDuration = enemy.channelDuration ?? 0;\r\n\t\tconst decayPerTick = SHIELD_DECAY_PER_SECOND / TICKS_PER_SECOND;\r\n\t\tshieldStrength = Math.max(SHIELD_MIN_BLOCK, SHIELD_MAX_BLOCK - (channelDuration * decayPerTick));\r\n\t}\r\n\r\n\treturn {\r\n\t\tposition: {x: enemy.position.x, y: enemy.position.y},\r\n\t\tvelocity: {x: enemy.velocity.x, y: enemy.velocity.y},\r\n\t\thealth: enemy.health,\r\n\t\tstatus: enemy.state,\r\n\t\tcastingSpell: enemy.state === 'casting' ? (enemy.castingSpell ?? null) : null,\r\n\t\tshieldStrength,\r\n\t};\r\n}\r\n\r\n// ============================================================\r\n// PROJECTILE HOOKS\r\n// ============================================================\r\n\r\n/**\r\n * Get all your active (in-flight) projectiles.\r\n * Each has position, rotation (degrees), speed (u/t), turnRate, remainingTicks.\r\n */\r\nexport function useMyProjectiles(): ProjectileState[]\r\n{\r\n\treturn getBotContext().myProjectiles;\r\n}\r\n\r\n// ============================================================\r\n// COMBAT TRACKING HOOKS\r\n// ============================================================\r\n\r\n/**\r\n * Get total damage you've dealt this match.\r\n */\r\nexport function useDamageDealt(): number\r\n{\r\n\treturn getBotContext().damageDealt;\r\n}\r\n\r\n/**\r\n * Get total damage you've taken this match.\r\n */\r\nexport function useDamageTaken(): number\r\n{\r\n\treturn getBotContext().damageTaken;\r\n}\r\n\r\n/**\r\n * Get the tick number when you last took damage. Returns 0 if never hit.\r\n * Compare with useTick() to get ticks since last hit.\r\n */\r\nexport function useLastHitTick(): number\r\n{\r\n\treturn getBotContext().lastHitTick;\r\n}\r\n\r\n// ============================================================\r\n// ARENA HOOKS\r\n// ============================================================\r\n\r\n/**\r\n * Get arena dimensions. Default: {width: 800, height: 800}.\r\n * Wizards are clamped to [5, 795] on each axis (radius = 5).\r\n */\r\nexport function useArenaSize(): {width: number; height: number}\r\n{\r\n\tconst ctx = getBotContext();\r\n\treturn {width: ctx.arenaWidth, height: ctx.arenaHeight};\r\n}\r\n\r\n/**\r\n * Get current game tick (starts at 0, increments each tick).\r\n * 100 ticks = 1 second. Match ends at 30,000 ticks (5 minutes).\r\n */\r\nexport function useTick(): number\r\n{\r\n\treturn getBotContext().tick;\r\n}\r\n\r\n// ============================================================\r\n// THREAT ANALYSIS HOOKS\r\n// ============================================================\r\n\r\n/**\r\n * Get analyzed threats from all incoming enemy projectiles.\r\n * Sorted by ticksToImpact (soonest first). Only includes missiles within 500 units\r\n * or that are predicted to hit.\r\n *\r\n * Each threat includes:\r\n * - ticksToImpact: ticks until hit (Infinity if will miss)\r\n * - willHit: true if missile hits your current position\r\n * - canDodgeLeft/Right: whether strafing perpendicular to missile heading works\r\n * - canOutrun: whether moving away from missile escapes it\r\n * - bestDodgeDirection: {x, y} unit vector to dodge optimally, or null if undodgeable\r\n * - canBlockInTime: whether you can raise shield before impact\r\n * - ticksToStartShield: when to START channeling shield to block in time\r\n */\r\nexport function useThreats(): AnalyzedThreat[]\r\n{\r\n\tconst ctx = getBotContext();\r\n\tconst ticksUntilReady = useTicksUntilReady();\r\n\r\n\t// Memoize to avoid recalculating every call\r\n\treturn useMemo(() =>\r\n\t{\r\n\t\treturn analyzeThreats(\r\n\t\t\tctx.position,\r\n\t\t\tctx.projectiles,\r\n\t\t\tctx.myProjectiles,\r\n\t\t\tticksUntilReady,\r\n\t\t);\r\n\t}, [ctx.projectiles, ctx.myProjectiles, ctx.position.x, ctx.position.y, ticksUntilReady]);\r\n}\r\n\r\n/**\r\n * Get the most imminent threat, or null if no threats.\r\n * Shorthand for useThreats()[0].\r\n */\r\nexport function useClosestThreat(): AnalyzedThreat | null\r\n{\r\n\tconst threats = useThreats();\r\n\treturn threats[0] ?? null;\r\n}\r\n\r\n/**\r\n * Get your missiles analyzed from the enemy's perspective.\r\n * Useful to predict when enemy will shield/dodge your attacks.\r\n */\r\nexport function useMyThreatsToEnemy(): AnalyzedThreat[]\r\n{\r\n\tconst ctx = getBotContext();\r\n\tconst enemy = ctx.enemies[0];\r\n\r\n\tif (!enemy)\r\n\t{\r\n\t\treturn [];\r\n\t}\r\n\r\n\t// Memoize to avoid recalculating every call\r\n\treturn useMemo(() =>\r\n\t{\r\n\t\t// Analyze from enemy's perspective\r\n\t\t// Pass empty array for \"enemy's own projectiles\" since we're analyzing our missiles as threats TO them\r\n\t\treturn analyzeThreats(\r\n\t\t\tenemy.position,\r\n\t\t\tctx.myProjectiles, // Our projectiles are their threats\r\n\t\t\t[], // Enemy has no \"own projectiles\" in this context\r\n\t\t\t0, // We don't know enemy's ticksUntilReady\r\n\t\t);\r\n\t}, [ctx.myProjectiles, enemy.position.x, enemy.position.y]);\r\n}\r\n","/**\r\n * VIBEMANCER - ACTION BUILDERS\r\n *\r\n * Fluent API for constructing bot actions with type-safe chaining.\r\n *\r\n * UNITS REFERENCE (100 ticks = 1 second):\r\n * Position: absolute world coordinates, 0-800 on each axis (800×800 arena)\r\n * Movement: direction vector, magnitude auto-normalized to max 100\r\n * Speed: units per tick (player moves at 1 unit/tick = 100 units/sec)\r\n * Duration: ticks (divide by 100 for seconds)\r\n * Angles: degrees (0°=right, 90°=down, 180°=left, 270°=up)\r\n * Damage: raw HP removed on hit (wizard has 60 HP)\r\n * Turn rate: degrees per tick the missile can rotate\r\n */\r\n\r\nimport {WizardActions, MissileConfig, MissileAIFunction} from '../types.js';\r\nimport type {FinalAction, ActionBuilder} from './types.js';\r\n\r\n/**\r\n * Create a FinalAction (no further chaining possible).\r\n */\r\nfunction createFinalAction(action: WizardActions): FinalAction\r\n{\r\n\treturn {\r\n\t\t_toAction: () => action,\r\n\t};\r\n}\r\n\r\n/**\r\n * Create an ActionBuilder (can chain .move()).\r\n */\r\nfunction createActionBuilder(baseAction: Omit<WizardActions, 'move'>): ActionBuilder\r\n{\r\n\tconst action: WizardActions = {\r\n\t\t...baseAction,\r\n\t\tmove: {x: 0, y: 0},\r\n\t};\r\n\r\n\treturn {\r\n\t\t_toAction: () => action,\r\n\t\tmove(x: number, y: number): FinalAction\r\n\t\t{\r\n\t\t\treturn createFinalAction({\r\n\t\t\t\t...action,\r\n\t\t\t\tmove: {x, y},\r\n\t\t\t});\r\n\t\t},\r\n\t};\r\n}\r\n\r\n// ============================================================\r\n// ACTION BUILDERS\r\n// ============================================================\r\n\r\n/**\r\n * Channel a shield that blocks incoming damage.\r\n *\r\n * Starts at 90% block, decays by 20% per second, minimum 30%.\r\n * Takes 20 ticks (0.2s) to activate. Movement is disabled while channeling.\r\n * Cancel anytime with cancel(). Triggers 100-tick (1s) GCD after cancel.\r\n *\r\n * Can chain .move() — movement applies during the 20-tick cast, NOT during channel.\r\n *\r\n * @example\r\n * return shield(); // shield and stay still\r\n * return shield().move(100, 0); // move right while cast starts\r\n */\r\nexport function shield(): ActionBuilder\r\n{\r\n\treturn createActionBuilder({\r\n\t\tstartCast: {spell: 'shield'},\r\n\t});\r\n}\r\n\r\n/**\r\n * Cast a missile spell.\r\n *\r\n * Cast time scales with damage, speed, duration, and turn rate — bigger missiles\r\n * take longer to cast. While casting you move at 50% speed. After firing, 100-tick\r\n * (1s) GCD before next spell.\r\n *\r\n * Repeated similar missiles cast 20% faster (warmup bonus). Switching styles\r\n * incurs a 20% penalty.\r\n *\r\n * Can chain .move() for simultaneous movement while casting.\r\n *\r\n * @param config - Missile stats:\r\n * - damage: HP removed on hit (1-60 typical). Also sets hitbox: radius = 2 + 0.1×damage.\r\n * - speed: units/tick (min 1.5). Player moves at 1 u/t, so 5 = 5× player speed.\r\n * - duration: ticks the missile lives (min 10). Range ≈ speed × duration.\r\n * - turnRate: degrees/tick of homing (0 = straight line, 3 = moderate homing, 5+ = strong).\r\n * Negative = no homing + minor speed cost reduction.\r\n * @param ai - Called every tick to control missile steering. Receives:\r\n * - missileState: the missile's position, rotation (degrees), speed, remainingTicks\r\n * - worldState: full game state (all wizards, projectiles)\r\n * - random(): seeded PRNG [0, 1)\r\n * Return { turnToward: {x, y} } to home toward a position, or {} to fly straight.\r\n * @param direction - Launch angle in degrees (0°=right, 90°=down, 180°=left, 270°=up).\r\n * Tip: use Math.atan2(dy, dx) * (180 / Math.PI) to aim at a target.\r\n *\r\n * @example\r\n * // Straight missile aimed at enemy\r\n * const angle = Math.atan2(dy, dx) * (180 / Math.PI);\r\n * return missile({damage: 15, speed: 6, duration: 200, turnRate: 0}, () => ({}), angle);\r\n *\r\n * // Homing missile that tracks enemy\r\n * return missile(\r\n * {damage: 10, speed: 5, duration: 300, turnRate: 3},\r\n * ({worldState}) => ({turnToward: worldState.enemies[0]?.position}),\r\n * angle,\r\n * );\r\n */\r\nexport function missile(config: MissileConfig, ai: MissileAIFunction, direction: number): ActionBuilder\r\n{\r\n\treturn createActionBuilder({\r\n\t\tstartCast: {\r\n\t\t\tspell: 'missile',\r\n\t\t\tconfig,\r\n\t\t\tmissileAI: ai,\r\n\t\t\tdirection,\r\n\t\t},\r\n\t});\r\n}\r\n\r\n/**\r\n * Teleport to an absolute position on the arena.\r\n *\r\n * Max range: 300 units from current position (clamped by engine if further).\r\n * Cast time: 10 ticks (0.1s). Cooldown scales with distance:\r\n * - 100 units → 100 ticks (1s)\r\n * - 300 units → 2000 ticks (20s)\r\n *\r\n * Cannot chain .move() — blink IS the movement.\r\n *\r\n * @param x - Target X position (0-800, absolute world coordinate)\r\n * @param y - Target Y position (0-800, absolute world coordinate)\r\n *\r\n * @example\r\n * return blink(400, 400); // blink to center\r\n * return blink(enemy.position.x, enemy.position.y); // blink to enemy\r\n */\r\nexport function blink(x: number, y: number): FinalAction\r\n{\r\n\treturn createFinalAction({\r\n\t\tmove: {x: 0, y: 0}, // No manual movement with blink\r\n\t\tstartCast: {\r\n\t\t\tspell: 'blink',\r\n\t\t\ttarget: {x, y},\r\n\t\t},\r\n\t});\r\n}\r\n\r\n/**\r\n * Cancel current cast or channel (e.g. stop shielding to attack).\r\n *\r\n * Canceling a cast/channel triggers 100-tick (1s) GCD.\r\n * Can chain .move() for simultaneous movement.\r\n *\r\n * @example\r\n * return cancel().move(-100, 0); // cancel and dodge left\r\n */\r\nexport function cancel(): ActionBuilder\r\n{\r\n\treturn createActionBuilder({\r\n\t\tcancel: true,\r\n\t});\r\n}\r\n\r\n/**\r\n * Move in a direction without casting any spell.\r\n *\r\n * This is a **direction vector**, not a target position. The engine normalizes\r\n * the magnitude to max 100, then moves at 1 unit/tick (100 units/sec).\r\n * Positive X = right, positive Y = down.\r\n *\r\n * To move toward a target position, subtract your position:\r\n * move(target.x - myPos.x, target.y - myPos.y)\r\n *\r\n * @param x - Horizontal direction (positive = right, negative = left)\r\n * @param y - Vertical direction (positive = down, negative = up)\r\n *\r\n * @example\r\n * return move(100, 0); // move right\r\n * return move(enemy.position.x - myPos.x, enemy.position.y - myPos.y); // move toward enemy\r\n */\r\nexport function move(x: number, y: number): FinalAction\r\n{\r\n\treturn createFinalAction({\r\n\t\tmove: {x, y},\r\n\t});\r\n}\r\n\r\n/**\r\n * Do nothing — no action, no movement.\r\n */\r\nexport function idle(): FinalAction\r\n{\r\n\treturn createFinalAction({\r\n\t\tmove: {x: 0, y: 0},\r\n\t});\r\n}\r\n\r\n/**\r\n * Extract WizardActions from a FinalAction.\r\n * Used by the engine to get the actual action.\r\n */\r\nexport function extractAction(finalAction: FinalAction): WizardActions\r\n{\r\n\treturn finalAction._toAction();\r\n}\r\n","/**\r\n * VIBEMANCER - BOT WRAPPER\r\n *\r\n * Utilities to convert between old-style (WizardFunction) and new-style (BotFunction) bots.\r\n */\r\n\r\nimport {WizardFunction, WizardActions, GameState, GameConfig} from '../types.js';\r\nimport {withBotContext} from '../engine/hooks-runtime.js';\r\nimport {extractAction} from './action-builders.js';\r\nimport type {BotFunction, BotContext} from './types.js';\r\n\r\n/**\r\n * Convert a new-style bot (using hooks) to an old-style bot (WizardFunction).\r\n *\r\n * This allows new bots to work with the existing simulation.\r\n *\r\n * @example\r\n * const NewBot: BotFunction = () => {\r\n * const health = useHealth();\r\n * return health < 10 ? shield() : idle();\r\n * };\r\n *\r\n * // Convert to work with simulate()\r\n * const oldStyleBot = wrapNewBot(NewBot);\r\n * simulate(oldStyleBot, opponent);\r\n */\r\nexport function wrapNewBot(newBot: BotFunction): WizardFunction\r\n{\r\n\treturn ({state, config}: {state: GameState; config: GameConfig; random: () => number}): WizardActions =>\r\n\t{\r\n\t\t// Create the BotContext from GameState\r\n\t\t// Note: entityId is inherited from the simulation's outer runWithHooks call\r\n\t\tconst botContext: BotContext = {\r\n\t\t\tentityId: 'unused', // Not used - entity ID comes from simulation\r\n\t\t\ttick: state.tick,\r\n\t\t\tposition: state.position,\r\n\t\t\tvelocity: state.velocity,\r\n\t\t\thealth: state.health,\r\n\t\t\tmaxHealth: state.maxHealth,\r\n\t\t\tstate: state.state,\r\n\t\t\tcastingSpell: state.castingSpell,\r\n\t\t\tcastProgress: state.castProgress,\r\n\t\t\tcastDuration: state.castDuration,\r\n\t\t\tchannelingSpell: state.channelingSpell,\r\n\t\t\tchannelDuration: state.channelDuration,\r\n\t\t\tgcdRemaining: state.gcdRemaining,\r\n\t\t\tblinkCooldown: state.blinkCooldown,\r\n\t\t\tenemies: state.enemies.map((e) => ({\r\n\t\t\t\tid: e.id,\r\n\t\t\t\tposition: e.position,\r\n\t\t\t\tvelocity: e.velocity,\r\n\t\t\t\thealth: e.health,\r\n\t\t\t\tstate: e.state,\r\n\t\t\t\tcastingSpell: e.castingSpell,\r\n\t\t\t\tchannelingSpell: e.channelingSpell,\r\n\t\t\t\tchannelDuration: e.channelDuration,\r\n\t\t\t})),\r\n\t\t\tprojectiles: state.projectiles,\r\n\t\t\tmyProjectiles: state.myProjectiles,\r\n\t\t\tarenaWidth: config.arenaSize.width,\r\n\t\t\tarenaHeight: config.arenaSize.height,\r\n\t\t\tdamageDealt: state.damageDealt,\r\n\t\t\tdamageTaken: state.damageTaken,\r\n\t\t\tlastHitTick: state.lastHitTick,\r\n\t\t};\r\n\r\n\t\t// Run the new-style bot with context\r\n\t\t// Use withBotContext (not runBotWithContext) to preserve the entity ID\r\n\t\t// set by the simulation's outer runWithHooks call\r\n\t\tconst finalAction = withBotContext(botContext, newBot);\r\n\r\n\t\t// Extract WizardActions from FinalAction\r\n\t\treturn extractAction(finalAction);\r\n\t};\r\n}\r\n\r\n/**\r\n * Type guard to check if a bot is a new-style BotFunction.\r\n * New-style bots have 0 parameters, old-style bots have at least 1.\r\n */\r\nexport function isNewStyleBot(bot: WizardFunction | BotFunction): bot is BotFunction\r\n{\r\n\treturn bot.length === 0;\r\n}\r\n","/**\r\n * VIBEMANCER - OPTIMIZER UTILITIES\r\n *\r\n * Core functions used by the offline parameter optimizer script.\r\n * These handle combo generation, range narrowing, and fight scoring.\r\n *\r\n * The optimizer uses multi-pass coordinate descent:\r\n * - Pass 1: Coarse search across the effective range for each param\r\n * - Pass 2+: Fine search zoomed into the neighborhood of the best result\r\n *\r\n * Parameters can define their search window two ways:\r\n * - `range`: sliding window centered on current value (value ± range)\r\n * - `min`/`max`: fixed bounds (old-style)\r\n *\r\n * Both can be combined: range defines the search radius, min/max clamp it.\r\n */\r\n\r\nimport type {ParamDeclaration} from './params-runtime.js';\r\nimport type {FightResult} from './simulation.js';\r\nimport {WIZARD_HEALTH} from '../rules.js';\r\n\r\n/**\r\n * Compute the effective min/max search window for a parameter.\r\n *\r\n * - If `range` is set: window is `[value - range, value + range]`, clamped by optional min/max\r\n * - If only `min`/`max` are set: window is `[min, max]` directly\r\n * - If neither: returns `[value, value]` (no search)\r\n */\r\nexport function getEffectiveRange(p: ParamDeclaration): {min: number; max: number}\r\n{\r\n\tif (p.range != null)\r\n\t{\r\n\t\treturn {\r\n\t\t\tmin: Math.max(p.value - p.range, p.min ?? -Infinity),\r\n\t\t\tmax: Math.min(p.value + p.range, p.max ?? Infinity),\r\n\t\t};\r\n\t}\r\n\treturn {\r\n\t\tmin: p.min ?? p.value,\r\n\t\tmax: p.max ?? p.value,\r\n\t};\r\n}\r\n\r\n/**\r\n * Generate evenly-spaced candidate values for a single parameter.\r\n *\r\n * Divides [min, max] into `steps` evenly-spaced values. For example,\r\n * min=0, max=100, steps=5 produces [0, 25, 50, 75, 100].\r\n *\r\n * @param min - Lower bound of search range\r\n * @param max - Upper bound of search range\r\n * @param steps - Number of evenly-spaced values to generate (minimum 2)\r\n * @returns Array of candidate values, sorted ascending\r\n */\r\nexport function generateCandidates(min: number, max: number, steps: number): number[]\r\n{\r\n\tif (steps < 2) steps = 2;\r\n\tconst values: number[] = [];\r\n\tfor (let i = 0; i < steps; i++)\r\n\t{\r\n\t\tvalues.push(min + (max - min) * i / (steps - 1));\r\n\t}\r\n\treturn values;\r\n}\r\n\r\n/**\r\n * Generate all combinations (cartesian product) of candidate values for multiple params.\r\n *\r\n * For N params with S1, S2, ... SN steps each, produces S1 × S2 × ... × SN combinations.\r\n * Each combination is a Record<string, number> mapping param name to value.\r\n *\r\n * @param params - Parameter declarations with search ranges and step counts\r\n * @returns Array of all parameter combinations to evaluate\r\n */\r\nexport function generateCombos(params: ParamDeclaration[]): Record<string, number>[]\r\n{\r\n\tif (params.length === 0) return [{}];\r\n\r\n\t// Generate candidates per param\r\n\tconst candidateArrays = params.map((p) =>\r\n\t{\r\n\t\tconst {min, max} = getEffectiveRange(p);\r\n\t\treturn {\r\n\t\t\tname: p.name,\r\n\t\t\tvalues: generateCandidates(min, max, p.steps),\r\n\t\t};\r\n\t});\r\n\r\n\t// Cartesian product\r\n\tlet combos: Record<string, number>[] = [{}];\r\n\tfor (const {name, values} of candidateArrays)\r\n\t{\r\n\t\tconst next: Record<string, number>[] = [];\r\n\t\tfor (const combo of combos)\r\n\t\t{\r\n\t\t\tfor (const value of values)\r\n\t\t\t{\r\n\t\t\t\tnext.push({...combo, [name]: value});\r\n\t\t\t}\r\n\t\t}\r\n\t\tcombos = next;\r\n\t}\r\n\r\n\treturn combos;\r\n}\r\n\r\n/**\r\n * Narrow parameter ranges around the best combo found in the previous pass.\r\n *\r\n * Centers each param on its best value and shrinks the search range to one gap width.\r\n * This provides finer resolution in subsequent passes. Hard min/max constraints are preserved.\r\n *\r\n * @param params - Original parameter declarations\r\n * @param best - Best parameter combination from the previous pass\r\n * @returns New parameter declarations with narrowed ranges for the next pass\r\n */\r\nexport function narrowRange(params: ParamDeclaration[], best: Record<string, number>): ParamDeclaration[]\r\n{\r\n\treturn params.map((p) =>\r\n\t{\r\n\t\tconst bestVal = best[p.name] ?? p.value;\r\n\t\tconst effectiveSubsteps = p.substeps ?? p.steps;\r\n\r\n\t\t// substeps: 0 — freeze this param at its best value (skip in subsequent passes)\r\n\t\tif (effectiveSubsteps === 0)\r\n\t\t{\r\n\t\t\treturn {...p, value: bestVal, min: bestVal, max: bestVal, range: undefined, steps: 2};\r\n\t\t}\r\n\r\n\t\tconst {min: effMin, max: effMax} = getEffectiveRange(p);\r\n\t\tconst gap = (effMax - effMin) / (p.steps - 1);\r\n\t\tconst narrowedSteps = effectiveSubsteps;\r\n\r\n\t\tif (p.range != null)\r\n\t\t{\r\n\t\t\t// Range mode: center on best, shrink range to one gap\r\n\t\t\treturn {...p, value: bestVal, range: gap, steps: narrowedSteps};\r\n\t\t}\r\n\t\t// Fixed min/max mode: narrow bounds around best, clamped to original\r\n\t\treturn {\r\n\t\t\t...p,\r\n\t\t\tmin: Math.max(p.min ?? bestVal, bestVal - gap),\r\n\t\t\tmax: Math.min(p.max ?? bestVal, bestVal + gap),\r\n\t\t\tsteps: narrowedSteps,\r\n\t\t};\r\n\t});\r\n}\r\n\r\n/**\r\n * Score a FightResult from one bot's perspective.\r\n *\r\n * Returns a continuous score that provides gradient information beyond binary win/loss:\r\n * - Win: 3.0 base + up to 0.5 HP bonus (higher remaining HP = better)\r\n * - Draw: 1.0\r\n * - Loss: 0.0 base + up to 0.5 bonus for low enemy HP (closer fights = better)\r\n *\r\n * For a mirrored fight set (bot as wizard-1 AND wizard-2), call this twice\r\n * and sum the scores for a balanced evaluation.\r\n *\r\n * @param result - The fight result to score\r\n * @returns Continuous score in range [0, 3.5] per match\r\n */\r\nexport function scoreFight(result: FightResult): number\r\n{\r\n\tlet score = 0;\r\n\r\n\tfor (const match of result.matches)\r\n\t{\r\n\t\tif (match.winner === 'wizard-1')\r\n\t\t{\r\n\t\t\t// Won: 3.0 base + HP bonus\r\n\t\t\tscore += 3.0 + (match.finalState.health / WIZARD_HEALTH) * 0.5;\r\n\t\t}\r\n\t\telse if (match.winner === 'draw' || match.winner === null)\r\n\t\t{\r\n\t\t\tscore += 1.0;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// Lost: small bonus for low enemy HP\r\n\t\t\tconst enemyHP = match.finalState.enemies[0]?.health ?? WIZARD_HEALTH;\r\n\t\t\tscore += (1 - enemyHP / WIZARD_HEALTH) * 0.5;\r\n\t\t}\r\n\t}\r\n\r\n\treturn score;\r\n}\r\n\r\n/**\r\n * Score a FightResult from wizard-2's perspective.\r\n * Same scoring logic as scoreFight but with roles reversed.\r\n *\r\n * Use this for tournaments where both sides of a pairing need scoring.\r\n */\r\nexport function scoreFightAsWizard2(result: FightResult): number\r\n{\r\n\tlet score = 0;\r\n\r\n\tfor (const match of result.matches)\r\n\t{\r\n\t\tif (match.winner === 'wizard-2')\r\n\t\t{\r\n\t\t\t// Won: 3.0 base + HP bonus (wizard-2's remaining HP)\r\n\t\t\tconst hp = match.finalState.enemies[0]?.health ?? WIZARD_HEALTH;\r\n\t\t\tscore += 3.0 + (hp / WIZARD_HEALTH) * 0.5;\r\n\t\t}\r\n\t\telse if (match.winner === 'draw' || match.winner === null)\r\n\t\t{\r\n\t\t\tscore += 1.0;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// Lost: small bonus for low wizard-1 HP\r\n\t\t\tconst enemyHP = match.finalState.health;\r\n\t\t\tscore += (1 - enemyHP / WIZARD_HEALTH) * 0.5;\r\n\t\t}\r\n\t}\r\n\r\n\treturn score;\r\n}\r\n","/**\r\n * VIBEMANCER — BROWSER SANDBOX\r\n *\r\n * Provides Web Worker-based sandboxing for bot code execution in the browser.\r\n * Same compiled bundles as the isolated-vm sandbox (MatchSandbox), but runs\r\n * in a Web Worker instead of a V8 isolate.\r\n *\r\n * Architecture:\r\n * - Host: creates Worker from Blob URL, communicates via postMessage\r\n * - Worker: loads compiled bundle (sets globalThis.__fight/__simulate),\r\n * dispatches fight/simulate calls, posts results back\r\n *\r\n * Safety:\r\n * - Timeout via setTimeout + worker.terminate() catches infinite loops\r\n * - No memory limit (browser manages worker memory; worst case = tab crash)\r\n * - Prototype freeze prevents cross-bot sabotage (same banner as isolated-vm)\r\n * - No Node.js APIs available in Web Workers\r\n *\r\n * NOTE: This file has ZERO Node.js dependencies. It works in any JS environment.\r\n */\r\n\r\nimport type {GameState, MissileAIFunction, ProjectileState, WizardActions} from '../types.js';\r\nimport type {BotError, FightResult, MatchWinner, SimulateResult} from './simulation.js';\r\nimport type {StepResult} from './manual-match.js';\r\n\r\n// ============================================================\r\n// WORKER PROTOCOL\r\n// ============================================================\r\n\r\n/**\r\n * Minimal Worker interface for dependency injection.\r\n * Matches the browser Worker API subset we need.\r\n * For tests, a Node.js worker_threads adapter can implement this.\r\n */\r\nexport interface WorkerLike\r\n{\r\n\tpostMessage(data: unknown): void;\r\n\tterminate(): void;\r\n\taddEventListener(type: string, listener: (ev: unknown) => void): void;\r\n\tremoveEventListener(type: string, listener: (ev: unknown) => void): void;\r\n}\r\n\r\n/**\r\n * Factory function that creates a WorkerLike from a JavaScript code string.\r\n * Default: creates a browser Web Worker via Blob URL.\r\n * Override in options.createWorker for testing with Node.js worker_threads.\r\n */\r\nexport type WorkerFactory = (code: string) => {worker: WorkerLike; cleanup?: () => void};\r\n\r\n/**\r\n * Options for browser sandbox creation.\r\n */\r\nexport interface BrowserSandboxOptions\r\n{\r\n\t/** Timeout in ms for fight/simulate calls (default: 30000). */\r\n\ttimeoutMs?: number;\r\n\t/** Custom worker factory for dependency injection (testing). */\r\n\tcreateWorker?: WorkerFactory;\r\n}\r\n\r\n// ============================================================\r\n// WORKER BOOTSTRAP\r\n// ============================================================\r\n\r\n/**\r\n * JavaScript bootstrap code appended to the compiled bundle.\r\n * Runs inside the worker after the IIFE sets up the relevant globals\r\n * (e.g., __fight/__simulate for fight bundles, __manualMatchInit/Step/etc\r\n * for manual-play bundles).\r\n *\r\n * The dispatcher is generic: any method name `foo` resolves to\r\n * `globalThis.__foo`. This allows new bundle types to expose new method\r\n * names without changing the bootstrap.\r\n *\r\n * Environment-adaptive: works in both browser Web Workers and\r\n * Node.js worker_threads (for testing).\r\n */\r\nconst WORKER_BOOTSTRAP = `\r\n;(function() {\r\n\tvar _port = null;\r\n\ttry { _port = require('worker_threads').parentPort; } catch(e) {}\r\n\r\n\tfunction send(data) {\r\n\t\tif (_port) _port.postMessage(data);\r\n\t\telse self.postMessage(data);\r\n\t}\r\n\r\n\tfunction listen(fn) {\r\n\t\tif (_port) _port.on('message', fn);\r\n\t\telse self.addEventListener('message', function(e) { fn(e.data); });\r\n\t}\r\n\r\n\tlisten(function(data) {\r\n\t\tif (data && data.method) {\r\n\t\t\ttry {\r\n\t\t\t\tvar fn = globalThis['__' + data.method];\r\n\t\t\t\tif (typeof fn !== 'function') {\r\n\t\t\t\t\tthrow new Error('Unknown sandbox method: ' + data.method);\r\n\t\t\t\t}\r\n\t\t\t\tvar result = fn(data.options);\r\n\t\t\t\tsend({id: data.id, result: result});\r\n\t\t\t} catch(err) {\r\n\t\t\t\tsend({id: data.id, error: (err && err.message) || String(err)});\r\n\t\t\t}\r\n\t\t}\r\n\t});\r\n\r\n\tsend({type: 'ready'});\r\n})();\r\n`;\r\n\r\n/**\r\n * Create the full worker script from a compiled match bundle.\r\n * Appends the message-handling bootstrap to the bundle IIFE.\r\n */\r\nexport function createWorkerScript(bundle: string): string\r\n{\r\n\treturn bundle + '\\n' + WORKER_BOOTSTRAP;\r\n}\r\n\r\n// ============================================================\r\n// BROWSER MATCH SANDBOX\r\n// ============================================================\r\n\r\ninterface PendingCall\r\n{\r\n\tresolve: (value: unknown) => void;\r\n\treject: (error: Error) => void;\r\n\ttimer: ReturnType<typeof setTimeout>;\r\n}\r\n\r\n/**\r\n * Browser-compatible sandboxed match runner using Web Workers.\r\n *\r\n * Same compiled bundles as MatchSandbox (isolated-vm), but runs in a\r\n * Web Worker instead. All fight/simulate calls are async (postMessage-based).\r\n *\r\n * Usage:\r\n * ```ts\r\n * // Bundle is compiled server-side or at build time (Node.js only)\r\n * const bundle = await MatchSandbox.compile(bot1, bot2);\r\n *\r\n * // Run in browser via Web Worker\r\n * const sandbox = await BrowserMatchSandbox.fromBundle(bundle);\r\n * const result = await sandbox.fight({ seed: 42 });\r\n * sandbox.dispose();\r\n * ```\r\n */\r\nexport class BrowserMatchSandbox\r\n{\r\n\tprivate worker: WorkerLike;\r\n\tprivate workerCleanup: (() => void) | undefined;\r\n\tprivate timeout: number;\r\n\tprivate disposed = false;\r\n\tprivate nextId = 0;\r\n\tprivate pending = new Map<number, PendingCall>();\r\n\tprivate messageHandler: ((ev: unknown) => void) | null = null;\r\n\tprivate errorHandler: ((ev: unknown) => void) | null = null;\r\n\r\n\tprivate constructor(\r\n\t\tworker: WorkerLike,\r\n\t\tworkerCleanup: (() => void) | undefined,\r\n\t\ttimeout: number,\r\n\t)\r\n\t{\r\n\t\tthis.worker = worker;\r\n\t\tthis.workerCleanup = workerCleanup;\r\n\t\tthis.timeout = timeout;\r\n\t}\r\n\r\n\t/**\r\n\t * Create a browser sandbox from a pre-compiled bundle string.\r\n\t * The bundle should be the output of MatchSandbox.compile() (or equivalent IIFE\r\n\t * that sets globalThis.__fight and globalThis.__simulate).\r\n\t */\r\n\tstatic async fromBundle(\r\n\t\tbundle: string,\r\n\t\toptions?: BrowserSandboxOptions,\r\n\t): Promise<BrowserMatchSandbox>\r\n\t{\r\n\t\tconst timeout = options?.timeoutMs ?? 30000;\r\n\t\tconst code = createWorkerScript(bundle);\r\n\r\n\t\tlet worker: WorkerLike;\r\n\t\tlet cleanup: (() => void) | undefined;\r\n\r\n\t\tif (options?.createWorker)\r\n\t\t{\r\n\t\t\tconst created = options.createWorker(code);\r\n\t\t\tworker = created.worker;\r\n\t\t\tcleanup = created.cleanup;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t// Browser default: Blob URL Worker\r\n\t\t\t// Uses globalThis to avoid TypeScript error in Node.js builds (no DOM lib)\r\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- runtime browser check\r\n\t\t\tconst BrowserWorker = (globalThis as Record<string, unknown>).Worker as\r\n\t\t\t\t{new (url: string | URL): WorkerLike} | undefined;\r\n\t\t\tif (!BrowserWorker)\r\n\t\t\t{\r\n\t\t\t\tthrow new Error(\r\n\t\t\t\t\t'Web Worker API not available. Pass a createWorker factory for Node.js usage.',\r\n\t\t\t\t);\r\n\t\t\t}\r\n\t\t\tconst blob = new Blob([code], {type: 'application/javascript'});\r\n\t\t\tconst blobUrl = URL.createObjectURL(blob);\r\n\t\t\tcleanup = (): void => URL.revokeObjectURL(blobUrl);\r\n\t\t\tworker = new BrowserWorker(blobUrl);\r\n\t\t}\r\n\r\n\t\tconst sandbox = new BrowserMatchSandbox(worker, cleanup, timeout);\r\n\r\n\t\ttry\r\n\t\t{\r\n\t\t\tawait sandbox.waitForReady(timeout);\r\n\t\t}\r\n\t\tcatch(error)\r\n\t\t{\r\n\t\t\tsandbox.dispose();\r\n\t\t\tthrow error;\r\n\t\t}\r\n\r\n\t\treturn sandbox;\r\n\t}\r\n\r\n\t/**\r\n\t * Wait for the worker to post {type: 'ready'}, then attach permanent handlers.\r\n\t */\r\n\tprivate waitForReady(timeout: number): Promise<void>\r\n\t{\r\n\t\treturn new Promise<void>((resolve, reject) =>\r\n\t\t{\r\n\t\t\tconst timer = setTimeout(() =>\r\n\t\t\t{\r\n\t\t\t\treject(new Error('Worker initialization timed out'));\r\n\t\t\t}, timeout);\r\n\r\n\t\t\tconst readyListener = (ev: unknown): void =>\r\n\t\t\t{\r\n\t\t\t\t// Unwrap MessageEvent (browser) or raw data (Node.js adapter)\r\n\t\t\t\tconst data = this.unwrapEvent(ev);\r\n\t\t\t\tif (data && typeof data === 'object' && 'type' in data && data.type === 'ready')\r\n\t\t\t\t{\r\n\t\t\t\t\tclearTimeout(timer);\r\n\t\t\t\t\tthis.worker.removeEventListener('message', readyListener);\r\n\t\t\t\t\tthis.attachHandlers();\r\n\t\t\t\t\tresolve();\r\n\t\t\t\t}\r\n\t\t\t};\r\n\r\n\t\t\tthis.worker.addEventListener('message', readyListener);\r\n\t\t});\r\n\t}\r\n\r\n\t/**\r\n\t * Extract message data from a browser MessageEvent or raw Node.js data.\r\n\t */\r\n\tprivate unwrapEvent(ev: unknown): Record<string, unknown> | null\r\n\t{\r\n\t\tif (ev == null) return null;\r\n\t\tif (typeof ev !== 'object') return null;\r\n\r\n\t\t// Browser MessageEvent has .data\r\n\t\tconst maybeEvent = ev as {data?: unknown};\r\n\t\tconst raw = maybeEvent.data !== undefined ? maybeEvent.data : ev;\r\n\r\n\t\tif (raw != null && typeof raw === 'object')\r\n\t\t{\r\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- runtime type-checked above\r\n\t\t\treturn raw as Record<string, unknown>;\r\n\t\t}\r\n\t\treturn null;\r\n\t}\r\n\r\n\t/**\r\n\t * Attach permanent message and error handlers for fight/simulate responses.\r\n\t */\r\n\tprivate attachHandlers(): void\r\n\t{\r\n\t\tthis.messageHandler = (ev: unknown): void =>\r\n\t\t{\r\n\t\t\tconst data = this.unwrapEvent(ev);\r\n\t\t\tif (!data || data.id == null) return;\r\n\r\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- id is always a number from our protocol\r\n\t\t\tconst id = data.id as number;\r\n\t\t\tconst handler = this.pending.get(id);\r\n\t\t\tif (!handler) return;\r\n\r\n\t\t\tclearTimeout(handler.timer);\r\n\t\t\tthis.pending.delete(id);\r\n\r\n\t\t\tif (data.error)\r\n\t\t\t{\r\n\t\t\t\thandler.reject(new Error(String(data.error)));\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\thandler.resolve(data.result);\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t\tthis.errorHandler = (ev: unknown): void =>\r\n\t\t{\r\n\t\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- ErrorEvent shape from Worker\r\n\t\t\tconst message = (ev as {message?: string}).message ?? 'Worker error';\r\n\t\t\t// Reject all pending calls\r\n\t\t\tfor (const [, handler] of this.pending)\r\n\t\t\t{\r\n\t\t\t\tclearTimeout(handler.timer);\r\n\t\t\t\thandler.reject(new Error(message));\r\n\t\t\t}\r\n\t\t\tthis.pending.clear();\r\n\t\t};\r\n\r\n\t\tthis.worker.addEventListener('message', this.messageHandler);\r\n\t\tthis.worker.addEventListener('error', this.errorHandler);\r\n\t}\r\n\r\n\t// ============================================================\r\n\t// PUBLIC API\r\n\t// ============================================================\r\n\r\n\t/**\r\n\t * Run a full fight (10 matches: 5 spawn distances x 2 sides).\r\n\t * Returns a Promise because Worker communication is async.\r\n\t */\r\n\tasync fight(options?: {seed?: number; maxTicks?: number}): Promise<FightResult>\r\n\t{\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- worker returns FightResult shape\r\n\t\treturn this.call('fight', options ?? {}) as Promise<FightResult>;\r\n\t}\r\n\r\n\t/**\r\n\t * Run a single simulation. Returns a Promise.\r\n\t *\r\n\t * @param options.params1 - useParam overrides for bot 1 (wizard-1)\r\n\t * @param options.params2 - useParam overrides for bot 2 (wizard-2)\r\n\t */\r\n\tasync simulate(options?: {\r\n\t\tseed?: number;\r\n\t\tmaxTicks?: number;\r\n\t\tspawnDistance?: number;\r\n\t\tskipHistory?: boolean;\r\n\t\tparams1?: Record<string, number>;\r\n\t\tparams2?: Record<string, number>;\r\n\t}): Promise<SimulateResult>\r\n\t{\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- worker returns SimulateResult shape\r\n\t\treturn this.call('simulate', options ?? {}) as Promise<SimulateResult>;\r\n\t}\r\n\r\n\t/**\r\n\t * Dispose the worker and free all resources.\r\n\t * The sandbox cannot be used after disposal.\r\n\t */\r\n\tdispose(): void\r\n\t{\r\n\t\tif (!this.disposed)\r\n\t\t{\r\n\t\t\tthis.disposed = true;\r\n\r\n\t\t\t// Reject all pending calls\r\n\t\t\tfor (const [, handler] of this.pending)\r\n\t\t\t{\r\n\t\t\t\tclearTimeout(handler.timer);\r\n\t\t\t\thandler.reject(new Error('BrowserMatchSandbox has been disposed'));\r\n\t\t\t}\r\n\t\t\tthis.pending.clear();\r\n\r\n\t\t\t// Remove event listeners\r\n\t\t\tif (this.messageHandler)\r\n\t\t\t{\r\n\t\t\t\tthis.worker.removeEventListener('message', this.messageHandler);\r\n\t\t\t}\r\n\t\t\tif (this.errorHandler)\r\n\t\t\t{\r\n\t\t\t\tthis.worker.removeEventListener('error', this.errorHandler);\r\n\t\t\t}\r\n\r\n\t\t\t// Terminate worker\r\n\t\t\tthis.worker.terminate();\r\n\r\n\t\t\t// Clean up Blob URL (or other resources)\r\n\t\t\tthis.workerCleanup?.();\r\n\t\t}\r\n\t}\r\n\r\n\t/**\r\n\t * Whether this sandbox has been disposed.\r\n\t */\r\n\tget isDisposed(): boolean\r\n\t{\r\n\t\treturn this.disposed;\r\n\t}\r\n\r\n\t// ============================================================\r\n\t// INTERNAL\r\n\t// ============================================================\r\n\r\n\t/**\r\n\t * Send a generic method call to the worker. Used by sibling sandboxes\r\n\t * (e.g. BrowserManualMatchSandbox) that need to dispatch to method\r\n\t * names other than fight/simulate. The worker bootstrap looks up\r\n\t * `globalThis['__' + method]` and calls it with `options`.\r\n\t */\r\n\tcallRaw(method: string, options: unknown): Promise<unknown>\r\n\t{\r\n\t\treturn this.call(method, options);\r\n\t}\r\n\r\n\t/**\r\n\t * Send a method call to the worker and wait for the response.\r\n\t * Times out and terminates the worker if no response within timeout.\r\n\t */\r\n\tprivate call(method: string, options: unknown): Promise<unknown>\r\n\t{\r\n\t\tthis.ensureNotDisposed();\r\n\t\tconst id = this.nextId++;\r\n\r\n\t\treturn new Promise((resolve, reject) =>\r\n\t\t{\r\n\t\t\tconst timer = setTimeout(() =>\r\n\t\t\t{\r\n\t\t\t\tthis.pending.delete(id);\r\n\t\t\t\t// Worker is stuck — terminate and clean up all resources\r\n\t\t\t\tthis.worker.terminate();\r\n\t\t\t\tthis.workerCleanup?.();\r\n\t\t\t\tthis.disposed = true;\r\n\t\t\t\treject(new Error(`BrowserMatchSandbox timed out after ${this.timeout}ms`));\r\n\t\t\t}, this.timeout);\r\n\r\n\t\t\tthis.pending.set(id, {resolve, reject, timer});\r\n\t\t\tthis.worker.postMessage({id, method, options});\r\n\t\t});\r\n\t}\r\n\r\n\tprivate ensureNotDisposed(): void\r\n\t{\r\n\t\tif (this.disposed)\r\n\t\t{\r\n\t\t\tthrow new Error('BrowserMatchSandbox has been disposed');\r\n\t\t}\r\n\t}\r\n}\r\n\r\n// ============================================================\r\n// CONVENIENCE FUNCTIONS\r\n// ============================================================\r\n\r\n/**\r\n * One-shot browser-sandboxed fight. Creates worker, runs fight, disposes.\r\n */\r\nexport async function browserSandboxFight(\r\n\tbundle: string,\r\n\toptions?: {seed?: number; maxTicks?: number} & BrowserSandboxOptions,\r\n): Promise<FightResult>\r\n{\r\n\tconst sandbox = await BrowserMatchSandbox.fromBundle(bundle, options);\r\n\ttry\r\n\t{\r\n\t\treturn await sandbox.fight({seed: options?.seed, maxTicks: options?.maxTicks});\r\n\t}\r\n\tfinally\r\n\t{\r\n\t\tsandbox.dispose();\r\n\t}\r\n}\r\n\r\n/**\r\n * One-shot browser-sandboxed simulate. Creates worker, runs simulate, disposes.\r\n */\r\nexport async function browserSandboxSimulate(\r\n\tbundle: string,\r\n\toptions?: {\r\n\t\tseed?: number;\r\n\t\tmaxTicks?: number;\r\n\t\tspawnDistance?: number;\r\n\t\tskipHistory?: boolean;\r\n\t\tparams1?: Record<string, number>;\r\n\t\tparams2?: Record<string, number>;\r\n\t} & BrowserSandboxOptions,\r\n): Promise<SimulateResult>\r\n{\r\n\tconst sandbox = await BrowserMatchSandbox.fromBundle(bundle, options);\r\n\ttry\r\n\t{\r\n\t\treturn await sandbox.simulate({\r\n\t\t\tseed: options?.seed,\r\n\t\t\tmaxTicks: options?.maxTicks,\r\n\t\t\tspawnDistance: options?.spawnDistance,\r\n\t\t\tskipHistory: options?.skipHistory,\r\n\t\t\tparams1: options?.params1,\r\n\t\t\tparams2: options?.params2,\r\n\t\t});\r\n\t}\r\n\tfinally\r\n\t{\r\n\t\tsandbox.dispose();\r\n\t}\r\n}\r\n\r\n// ============================================================\r\n// BROWSER MANUAL-MATCH SANDBOX\r\n// ============================================================\r\n\r\n/**\r\n * Options accepted by `__manualMatchInit` (worker-side). Mirrors\r\n * `ManualMatchOptions` from manual-match.ts, but without the constructor's\r\n * AI parameters since the player AI is a worker-local stub.\r\n */\r\nexport interface ManualMatchInitOptions\r\n{\r\n\tseed?: number;\r\n\tspawnDistance?: number;\r\n\tmaxTicks?: number;\r\n\tinitialHumanActions?: WizardActions;\r\n}\r\n\r\nexport interface ManualMatchStepRequest\r\n{\r\n\thumanActions?: WizardActions;\r\n\thumanMissileTargets?: Record<string, {x: number; y: number}>;\r\n\tcount?: number;\r\n}\r\n\r\n/**\r\n * Long-lived Web Worker sandbox holding a single ManualMatch instance.\r\n *\r\n * Unlike BrowserMatchSandbox (which runs one batched fight/simulate per\r\n * worker), BrowserManualMatchSandbox keeps the worker alive across many\r\n * step calls so the engine state and hook state persist between ticks.\r\n * This is what manual play mode uses: one worker per session, disposed\r\n * when the user leaves the page or starts a new match.\r\n *\r\n * Usage:\r\n * ```ts\r\n * const sandbox = await BrowserManualMatchSandbox.fromBundle(opponentBundle);\r\n * await sandbox.init({seed: 42});\r\n * for (let i = 0; i < 100; i++) {\r\n * await sandbox.step({humanActions: {move: {x: 100, y: 0}}, count: 1});\r\n * }\r\n * await sandbox.dispose();\r\n * ```\r\n */\r\nexport class BrowserManualMatchSandbox\r\n{\r\n\tprivate readonly inner: BrowserMatchSandbox;\r\n\r\n\tprivate constructor(inner: BrowserMatchSandbox)\r\n\t{\r\n\t\tthis.inner = inner;\r\n\t}\r\n\r\n\t/**\r\n\t * Create a manual-match sandbox from a pre-compiled bundle. The bundle\r\n\t * must be the output of `compileManualMatchBundle()` — the regular\r\n\t * `compileMatchBundle()` output won't work since it doesn't expose the\r\n\t * `__manualMatch*` globals.\r\n\t */\r\n\tstatic async fromBundle(\r\n\t\tbundle: string,\r\n\t\toptions?: BrowserSandboxOptions,\r\n\t): Promise<BrowserManualMatchSandbox>\r\n\t{\r\n\t\tconst inner = await BrowserMatchSandbox.fromBundle(bundle, options);\r\n\t\treturn new BrowserManualMatchSandbox(inner);\r\n\t}\r\n\r\n\t/**\r\n\t * Initialize the worker-side ManualMatch instance.\r\n\t * Returns the initial GameState (tick 0).\r\n\t */\r\n\tasync init(options?: ManualMatchInitOptions): Promise<GameState>\r\n\t{\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- worker returns GameState shape\r\n\t\treturn this.inner.callRaw('manualMatchInit', options ?? {}) as Promise<GameState>;\r\n\t}\r\n\r\n\t/**\r\n\t * Advance the match by `request.count` ticks (default 1), updating\r\n\t * the player's human actions and any hijacked missile targets first.\r\n\t */\r\n\tasync step(request: ManualMatchStepRequest = {}): Promise<StepResult>\r\n\t{\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- worker returns StepResult shape\r\n\t\treturn this.inner.callRaw('manualMatchStep', request) as Promise<StepResult>;\r\n\t}\r\n\r\n\t/**\r\n\t * Replace a missile's AI with a worker-local hijack stub that reads\r\n\t * from `latestHumanMissileTargets[projectileId]`. Subsequent step()\r\n\t * calls with `humanMissileTargets` populated for this id steer the\r\n\t * missile.\r\n\t */\r\n\tasync hijackMissile(projectileId: string): Promise<void>\r\n\t{\r\n\t\tawait this.inner.callRaw('manualMatchHijack', {projectileId});\r\n\t}\r\n\r\n\t/**\r\n\t * Restore a hijacked missile's original AI.\r\n\t */\r\n\tasync releaseMissile(projectileId: string): Promise<void>\r\n\t{\r\n\t\tawait this.inner.callRaw('manualMatchRelease', {projectileId});\r\n\t}\r\n\r\n\t/**\r\n\t * Toggle invincibility for a wizard.\r\n\t */\r\n\tasync setInvincible(wizardIndex: 0 | 1, on: boolean): Promise<void>\r\n\t{\r\n\t\tawait this.inner.callRaw('manualMatchSetInvincible', {wizardIndex, on});\r\n\t}\r\n\r\n\t/**\r\n\t * Get the current game state without advancing.\r\n\t */\r\n\tasync getState(): Promise<GameState>\r\n\t{\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- worker returns GameState shape\r\n\t\treturn this.inner.callRaw('manualMatchGetState', {}) as Promise<GameState>;\r\n\t}\r\n\r\n\t/**\r\n\t * Get the full SimulateResult-compatible result object (history + winner).\r\n\t */\r\n\tasync getResult(): Promise<SimulateResult>\r\n\t{\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- worker returns SimulateResult shape\r\n\t\treturn this.inner.callRaw('manualMatchGetResult', {}) as Promise<SimulateResult>;\r\n\t}\r\n\r\n\t/**\r\n\t * Dispose the worker-side ManualMatch instance. Does NOT terminate the\r\n\t * worker — call dispose() for that.\r\n\t */\r\n\tasync resetMatch(): Promise<void>\r\n\t{\r\n\t\tawait this.inner.callRaw('manualMatchDispose', {});\r\n\t}\r\n\r\n\t/**\r\n\t * Terminate the worker and free all resources.\r\n\t */\r\n\tdispose(): void\r\n\t{\r\n\t\tthis.inner.dispose();\r\n\t}\r\n\r\n\tget isDisposed(): boolean\r\n\t{\r\n\t\treturn this.inner.isDisposed;\r\n\t}\r\n}\r\n\r\n/**\r\n * Re-import-friendly types (re-exported here so consumers don't need to\r\n * cherry-pick from internal modules).\r\n */\r\nexport type {\r\n\tBotError,\r\n\tGameState,\r\n\tMatchWinner,\r\n\tMissileAIFunction,\r\n\tProjectileState,\r\n\tStepResult,\r\n\tWizardActions,\r\n};\r\n","/**\r\n * VIBEMANCER - FIGHT STATISTICS\r\n *\r\n * Extracts detailed per-bot statistics from a SimulateResult history.\r\n * Used by testBot().simulate() and vibemancer trace for debugging.\r\n */\r\n\r\nimport type {GameState} from './types.js';\r\nimport type {SimulateResult} from './engine/simulation.js';\r\nimport {TICKS_PER_SECOND, SHIELD_MAX_BLOCK, SHIELD_DECAY_PER_SECOND, SHIELD_MIN_BLOCK} from './rules.js';\r\n\r\n/** Detailed statistics for one bot in a single match. */\r\nexport interface FightStats\r\n{\r\n\t/** Total damage dealt to the opponent. */\r\n\tdamageDealt: number;\r\n\t/** Total damage taken from the opponent. */\r\n\tdamageTaken: number;\r\n\t/** Number of missiles launched. */\r\n\tmissilesLaunched: number;\r\n\t/** Number of missiles that dealt damage (hit the opponent). */\r\n\tmissileHits: number;\r\n\t/** Hit rate (0-1). NaN if no missiles fired. */\r\n\thitRate: number;\r\n\t/** Number of our missiles that hit while enemy was shielding. */\r\n\thitsEnemyShielded: number;\r\n\t/** Number of our missiles that hit while enemy had no shield. */\r\n\thitsEnemyUnshielded: number;\r\n\t/** Total raw damage our missiles would have dealt without shields. */\r\n\trawDamageDealt: number;\r\n\t/** Damage blocked by enemy shields. */\r\n\tdamageBlockedByEnemy: number;\r\n\t/** Percentage of our raw damage blocked by enemy shields (0-1). */\r\n\tenemyBlockRate: number;\r\n\t/** Number of times we were hit by enemy missiles. */\r\n\thitsReceived: number;\r\n\t/** Number of hits received while shield was channeling. */\r\n\thitsShielded: number;\r\n\t/** Number of hits received without shield. */\r\n\thitsUnshielded: number;\r\n\t/** Total raw damage that hit us (before shield reduction). */\r\n\trawDamageReceived: number;\r\n\t/** Total damage blocked by shields. */\r\n\tdamageBlocked: number;\r\n\t/** Percentage of incoming raw damage that was blocked (0-1). */\r\n\tblockRate: number;\r\n\t/** Ticks spent in 'casting' state. */\r\n\tcastingTicks: number;\r\n\t/** Ticks spent channeling shield. */\r\n\tshieldTicks: number;\r\n\t/** Ticks spent in GCD lockout. */\r\n\tgcdTicks: number;\r\n\t/** Ticks spent idle (not casting, channeling, or in GCD). */\r\n\tidleTicks: number;\r\n\t/** Number of times shield was activated. */\r\n\tshieldCount: number;\r\n\t/** Number of times blink was used. */\r\n\tblinkCount: number;\r\n\t/** Total match duration in ticks. */\r\n\ttotalTicks: number;\r\n}\r\n\r\n/**\r\n * Compute the shield block percentage at a given channel duration.\r\n * Same formula as the engine: starts at 90%, decays 20%/sec, min 30%.\r\n */\r\nfunction shieldBlockPct(channelDurationTicks: number): number\r\n{\r\n\tconst channelSec = channelDurationTicks / TICKS_PER_SECOND;\r\n\treturn Math.max(SHIELD_MIN_BLOCK, SHIELD_MAX_BLOCK - SHIELD_DECAY_PER_SECOND * channelSec);\r\n}\r\n\r\n/**\r\n * Extract fight statistics from a SimulateResult.\r\n * Returns stats for wizard-1 (the bot under test).\r\n */\r\nexport function extractStats(result: SimulateResult): FightStats\r\n{\r\n\tconst history = result.history;\r\n\tif (history.length === 0)\r\n\t{\r\n\t\treturn emptyStats(result.ticks);\r\n\t}\r\n\r\n\tlet damageDealt = 0;\r\n\tlet damageTaken = 0;\r\n\tlet missilesLaunched = 0;\r\n\tlet missileHits = 0;\r\n\tlet hitsEnemyShielded = 0;\r\n\tlet hitsEnemyUnshielded = 0;\r\n\tlet rawDamageDealt = 0;\r\n\tlet damageBlockedByEnemy = 0;\r\n\tlet hitsReceived = 0;\r\n\tlet hitsShielded = 0;\r\n\tlet hitsUnshielded = 0;\r\n\tlet rawDamageReceived = 0;\r\n\tlet damageBlocked = 0;\r\n\tlet castingTicks = 0;\r\n\tlet shieldTicks = 0;\r\n\tlet gcdTicks = 0;\r\n\tlet idleTicks = 0;\r\n\tlet shieldCount = 0;\r\n\tlet blinkCount = 0;\r\n\r\n\tlet prevState: GameState | null = null;\r\n\r\n\tfor (const state of history)\r\n\t{\r\n\t\tif (prevState)\r\n\t\t{\r\n\t\t\t// Damage dealt (enemy HP decreased)\r\n\t\t\tconst prevEnemy = prevState.enemies?.[0];\r\n\t\t\tconst curEnemy = state.enemies?.[0];\r\n\t\t\tconst prevEnemyHp = prevEnemy?.health ?? 0;\r\n\t\t\tconst curEnemyHp = curEnemy?.health ?? 0;\r\n\t\t\tif (curEnemyHp < prevEnemyHp)\r\n\t\t\t{\r\n\t\t\t\tconst dmg = prevEnemyHp - curEnemyHp;\r\n\t\t\t\tdamageDealt += dmg;\r\n\t\t\t\tmissileHits++;\r\n\r\n\t\t\t\tif (prevEnemy?.channelingSpell === 'shield')\r\n\t\t\t\t{\r\n\t\t\t\t\t// Our missile hit while enemy was shielding\r\n\t\t\t\t\thitsEnemyShielded++;\r\n\t\t\t\t\tconst enemyBlockPct = shieldBlockPct(prevEnemy.channelDuration ?? 0);\r\n\t\t\t\t\tconst rawDmg = dmg / (1 - enemyBlockPct);\r\n\t\t\t\t\trawDamageDealt += rawDmg;\r\n\t\t\t\t\tdamageBlockedByEnemy += rawDmg - dmg;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t// Hit without shield — raw = actual\r\n\t\t\t\t\thitsEnemyUnshielded++;\r\n\t\t\t\t\trawDamageDealt += dmg;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Damage taken (our HP decreased)\r\n\t\t\tif (state.health < prevState.health)\r\n\t\t\t{\r\n\t\t\t\tconst dmg = prevState.health - state.health;\r\n\t\t\t\tdamageTaken += dmg;\r\n\t\t\t\thitsReceived++;\r\n\r\n\t\t\t\tif (prevState.channelingSpell === 'shield')\r\n\t\t\t\t{\r\n\t\t\t\t\t// Hit while shielding — compute actual block amount\r\n\t\t\t\t\thitsShielded++;\r\n\t\t\t\t\tconst blockPct = shieldBlockPct(prevState.channelDuration ?? 0);\r\n\t\t\t\t\t// damageTaken = rawDamage * (1 - blockPct)\r\n\t\t\t\t\t// rawDamage = damageTaken / (1 - blockPct)\r\n\t\t\t\t\tconst rawDmg = dmg / (1 - blockPct);\r\n\t\t\t\t\tconst blocked = rawDmg - dmg;\r\n\t\t\t\t\trawDamageReceived += rawDmg;\r\n\t\t\t\t\tdamageBlocked += blocked;\r\n\t\t\t\t}\r\n\t\t\t\telse\r\n\t\t\t\t{\r\n\t\t\t\t\t// Hit without shield — raw damage = damage taken\r\n\t\t\t\t\thitsUnshielded++;\r\n\t\t\t\t\trawDamageReceived += dmg;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\r\n\t\t\t// Missile launches (new projectiles appearing)\r\n\t\t\tconst prevMyProj = prevState.myProjectiles?.length ?? 0;\r\n\t\t\tconst curMyProj = state.myProjectiles?.length ?? 0;\r\n\t\t\tif (curMyProj > prevMyProj)\r\n\t\t\t{\r\n\t\t\t\tmissilesLaunched += curMyProj - prevMyProj;\r\n\t\t\t}\r\n\r\n\t\t\t// State tracking\r\n\t\t\tif (state.castingSpell) castingTicks++;\r\n\t\t\telse if (state.channelingSpell === 'shield') shieldTicks++;\r\n\t\t\telse if (state.gcdRemaining && state.gcdRemaining > 0) gcdTicks++;\r\n\t\t\telse idleTicks++;\r\n\r\n\t\t\t// Shield activations\r\n\t\t\tif (state.channelingSpell === 'shield' && prevState.channelingSpell !== 'shield')\r\n\t\t\t{\r\n\t\t\t\tshieldCount++;\r\n\t\t\t}\r\n\r\n\t\t\t// Blink usage\r\n\t\t\tif (prevState.castingSpell === 'blink' && state.castingSpell !== 'blink' && state.gcdRemaining && state.gcdRemaining > 0)\r\n\t\t\t{\r\n\t\t\t\tblinkCount++;\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tprevState = state;\r\n\t}\r\n\r\n\tconst totalTicks = result.ticks;\r\n\r\n\treturn {\r\n\t\tdamageDealt,\r\n\t\tdamageTaken,\r\n\t\tmissilesLaunched,\r\n\t\tmissileHits,\r\n\t\thitRate: missilesLaunched > 0 ? missileHits / missilesLaunched : NaN,\r\n\t\thitsEnemyShielded,\r\n\t\thitsEnemyUnshielded,\r\n\t\trawDamageDealt,\r\n\t\tdamageBlockedByEnemy,\r\n\t\tenemyBlockRate: rawDamageDealt > 0 ? damageBlockedByEnemy / rawDamageDealt : 0,\r\n\t\thitsReceived,\r\n\t\thitsShielded,\r\n\t\thitsUnshielded,\r\n\t\trawDamageReceived,\r\n\t\tdamageBlocked,\r\n\t\tblockRate: rawDamageReceived > 0 ? damageBlocked / rawDamageReceived : 0,\r\n\t\tcastingTicks,\r\n\t\tshieldTicks,\r\n\t\tgcdTicks,\r\n\t\tidleTicks,\r\n\t\tshieldCount,\r\n\t\tblinkCount,\r\n\t\ttotalTicks,\r\n\t};\r\n}\r\n\r\nfunction emptyStats(totalTicks: number): FightStats\r\n{\r\n\treturn {\r\n\t\tdamageDealt: 0, damageTaken: 0,\r\n\t\tmissilesLaunched: 0, missileHits: 0, hitRate: NaN,\r\n\t\thitsEnemyShielded: 0, hitsEnemyUnshielded: 0,\r\n\t\trawDamageDealt: 0, damageBlockedByEnemy: 0, enemyBlockRate: 0,\r\n\t\thitsReceived: 0, hitsShielded: 0, hitsUnshielded: 0,\r\n\t\trawDamageReceived: 0, damageBlocked: 0, blockRate: 0,\r\n\t\tcastingTicks: 0, shieldTicks: 0, gcdTicks: 0, idleTicks: totalTicks,\r\n\t\tshieldCount: 0, blinkCount: 0, totalTicks,\r\n\t};\r\n}\r\n\r\n/** Format stats as a human-readable summary string. */\r\nexport function formatStats(stats: FightStats, botName: string): string\r\n{\r\n\tconst lines: string[] = [];\r\n\tconst pct = (n: number): string => ((n / stats.totalTicks) * 100).toFixed(1);\r\n\tconst sec = (n: number): string => (n / TICKS_PER_SECOND).toFixed(1);\r\n\r\n\tlines.push(` ${botName} stats:`);\r\n\tlines.push(' Offense:');\r\n\tlines.push(` Missiles: ${stats.missilesLaunched} fired, ${stats.missileHits} hit (${isNaN(stats.hitRate) ? 'N/A' : (stats.hitRate * 100).toFixed(0) + '%'})`);\r\n\tlines.push(` Damage: ${stats.damageDealt.toFixed(1)} dealt${stats.damageBlockedByEnemy > 0 ? ` (${stats.rawDamageDealt.toFixed(1)} raw, ${stats.damageBlockedByEnemy.toFixed(1)} blocked by enemy = ${(stats.enemyBlockRate * 100).toFixed(0)}%)` : ''}`);\r\n\tif (stats.missileHits > 0)\r\n\t{\r\n\t\tlines.push(` Hits: ${stats.hitsEnemyUnshielded} clean, ${stats.hitsEnemyShielded} shielded`);\r\n\t}\r\n\tlines.push(' Defense:');\r\n\tlines.push(` Hits taken: ${stats.hitsReceived} — ${stats.hitsUnshielded} unshielded, ${stats.hitsShielded} shielded`);\r\n\tlines.push(` Damage: ${stats.damageTaken.toFixed(1)} taken${stats.damageBlocked > 0 ? ` (${stats.rawDamageReceived.toFixed(1)} raw, ${stats.damageBlocked.toFixed(1)} blocked = ${(stats.blockRate * 100).toFixed(0)}%)` : ''}`);\r\n\tlines.push(` Shield: ${stats.shieldCount} activations, ${sec(stats.shieldTicks)}s channeled`);\r\n\tlines.push(` Blinks: ${stats.blinkCount}`);\r\n\tlines.push(` Time: ${pct(stats.castingTicks)}% casting, ${pct(stats.shieldTicks)}% shielding, ${pct(stats.gcdTicks)}% GCD, ${pct(stats.idleTicks)}% idle`);\r\n\r\n\treturn lines.join('\\n');\r\n}\r\n","/**\r\n * VIBEMANCER - TESTING UTILITIES\r\n *\r\n * Helpers for writing automated tests for your bot.\r\n * Import from '@vibemancer/core' in your test files.\r\n *\r\n * @example\r\n * import {testBot} from '@vibemancer/core';\r\n * import {MyWizard} from '../src/bot';\r\n *\r\n * test('beats TargetDummy', async () => {\r\n * const result = await testBot(MyWizard).fight('TargetDummy');\r\n * expect(result.won).toBe(true);\r\n * });\r\n */\r\n\r\nimport type {BotFunction} from './hooks/types.js';\r\nimport type {WizardFunction} from './types.js';\r\nimport type {FightResult, SimulateResult, MatchWinner, FightWinner} from './engine/simulation.js';\r\nimport {fight, simulate, FIGHT_SPAWN_DISTANCES} from './engine/simulation.js';\r\nimport {isNewStyleBot, wrapNewBot} from './hooks/bot-wrapper.js';\r\nimport {extractStats} from './stats.js';\r\nimport type {FightStats} from './stats.js';\r\n\r\n// Import all built-in bots\r\nimport * as allBots from './bots/index.js';\r\n\r\n/** Result of a testBot().fight() call with convenience accessors. */\r\nexport interface TestFightResult\r\n{\r\n\t/** The raw FightResult from the simulation engine. */\r\n\traw: FightResult;\r\n\t/** Overall winner: 'wizard-1' | 'wizard-2' | 'draw'. */\r\n\twinner: FightWinner;\r\n\t/** True if your bot won the fight. */\r\n\twon: boolean;\r\n\t/** True if your bot lost the fight. */\r\n\tlost: boolean;\r\n\t/** True if the fight was a draw. */\r\n\tdrawn: boolean;\r\n\t/** Number of individual matches your bot won (out of 10). */\r\n\twins: number;\r\n\t/** Number of individual matches your bot lost (out of 10). */\r\n\tlosses: number;\r\n\t/** Number of individual matches that were draws. */\r\n\tdraws: number;\r\n}\r\n\r\n/** Result of a testBot().simulate() call with convenience accessors. */\r\nexport interface TestSimulateResult\r\n{\r\n\t/** The raw SimulateResult from the simulation engine. */\r\n\traw: SimulateResult;\r\n\t/** Match winner. */\r\n\twinner: MatchWinner;\r\n\t/** True if your bot won. */\r\n\twon: boolean;\r\n\t/** True if your bot lost. */\r\n\tlost: boolean;\r\n\t/** True if the match was a draw or timeout. */\r\n\tdrawn: boolean;\r\n\t/** Number of ticks the match lasted. */\r\n\tticks: number;\r\n\t/** Your bot's remaining HP. */\r\n\tmyHealth: number;\r\n\t/** Enemy's remaining HP. */\r\n\tenemyHealth: number;\r\n\t/** Detailed fight statistics (missiles, damage, shield usage, time breakdown). */\r\n\tstats: FightStats;\r\n\t/** Runtime errors thrown by bot or missile AI (empty if none). */\r\n\terrors: Array<{tick: number; entityId: string; message: string}>;\r\n}\r\n\r\n/** Builder returned by testBot(). */\r\nexport interface TestBotBuilder\r\n{\r\n\t/**\r\n\t * Run a full fight (10 matches) against a named built-in bot.\r\n\t * @param opponent - Built-in bot name (e.g., 'Battlemage', 'TargetDummy').\r\n\t */\r\n\tfight(opponent: string, options?: {seed?: number}): TestFightResult;\r\n\r\n\t/**\r\n\t * Run a single match against a named built-in bot.\r\n\t * @param opponent - Built-in bot name.\r\n\t */\r\n\tsimulate(opponent: string, options?: {\r\n\t\tseed?: number;\r\n\t\tspawnDistance?: number;\r\n\t\tmaxTicks?: number;\r\n\t}): TestSimulateResult;\r\n}\r\n\r\n/**\r\n * Resolve a built-in bot name to a WizardFunction.\r\n */\r\nfunction resolveBuiltinBot(name: string): WizardFunction\r\n{\r\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- dynamic bot lookup\r\n\tconst botMap = allBots as unknown as Record<string, WizardFunction | BotFunction>;\r\n\tconst bot = botMap[name];\r\n\tif (!bot)\r\n\t{\r\n\t\tconst available = Object.keys(botMap).filter((k) => typeof botMap[k] === 'function').join(', ');\r\n\t\tthrow new Error(`Unknown opponent \"${name}\". Available bots: ${available}`);\r\n\t}\r\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- branching on length+shape detection\r\n\treturn typeof bot === 'function' && bot.length === 0 && isNewStyleBot(bot as BotFunction)\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- guarded by isNewStyleBot above\r\n\t\t? wrapNewBot(bot as BotFunction)\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- legacy style bot after guard\r\n\t\t: bot as WizardFunction;\r\n}\r\n\r\n/**\r\n * Wrap a bot function for use with the simulation engine.\r\n */\r\nfunction wrapBot(bot: WizardFunction | BotFunction): WizardFunction\r\n{\r\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- runtime shape detection\r\n\tif (isNewStyleBot(bot as BotFunction))\r\n\t{\r\n\t\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- guarded by isNewStyleBot above\r\n\t\treturn wrapNewBot(bot as BotFunction);\r\n\t}\r\n\t// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- legacy style bot after guard\r\n\treturn bot as WizardFunction;\r\n}\r\n\r\n/**\r\n * Create a test builder for your bot.\r\n *\r\n * @param bot - Your bot function (the same function you export from src/bot.ts).\r\n * @returns A builder with .fight() and .simulate() methods.\r\n *\r\n * @example\r\n * ```ts\r\n * import {testBot} from '@vibemancer/core';\r\n * import {MyWizard} from '../src/bot';\r\n *\r\n * test('beats TargetDummy', () => {\r\n * const result = testBot(MyWizard).fight('TargetDummy');\r\n * expect(result.won).toBe(true);\r\n * });\r\n *\r\n * test('survives 10 seconds against Battlemage', () => {\r\n * const result = testBot(MyWizard).simulate('Battlemage', {maxTicks: 1000});\r\n * expect(result.myHealth).toBeGreaterThan(0);\r\n * });\r\n *\r\n * test('kills at close range', () => {\r\n * const result = testBot(MyWizard).simulate('TargetDummy', {spawnDistance: 200});\r\n * expect(result.won).toBe(true);\r\n * expect(result.ticks).toBeLessThan(1000);\r\n * });\r\n * ```\r\n */\r\nexport function testBot(bot: WizardFunction | BotFunction): TestBotBuilder\r\n{\r\n\tconst wrappedBot = wrapBot(bot);\r\n\r\n\treturn {\r\n\t\tfight(opponent: string, options?: {seed?: number}): TestFightResult\r\n\t\t{\r\n\t\t\tconst opponentBot = resolveBuiltinBot(opponent);\r\n\t\t\tconst raw = fight(wrappedBot, opponentBot, {seed: options?.seed});\r\n\r\n\t\t\treturn {\r\n\t\t\t\traw,\r\n\t\t\t\twinner: raw.winner,\r\n\t\t\t\twon: raw.winner === 'wizard-1',\r\n\t\t\t\tlost: raw.winner === 'wizard-2',\r\n\t\t\t\tdrawn: raw.winner === 'draw',\r\n\t\t\t\twins: raw.wizard1Wins,\r\n\t\t\t\tlosses: raw.wizard2Wins,\r\n\t\t\t\tdraws: raw.draws,\r\n\t\t\t};\r\n\t\t},\r\n\r\n\t\tsimulate(opponent: string, options?: {\r\n\t\t\tseed?: number;\r\n\t\t\tspawnDistance?: number;\r\n\t\t\tmaxTicks?: number;\r\n\t\t}): TestSimulateResult\r\n\t\t{\r\n\t\t\tconst opponentBot = resolveBuiltinBot(opponent);\r\n\t\t\tconst raw = simulate(wrappedBot, opponentBot, {\r\n\t\t\t\tseed: options?.seed,\r\n\t\t\t\tspawnDistance: options?.spawnDistance ?? FIGHT_SPAWN_DISTANCES[2], // 600 (middle distance)\r\n\t\t\t\tmaxTicks: options?.maxTicks,\r\n\t\t\t});\r\n\r\n\t\t\treturn {\r\n\t\t\t\traw,\r\n\t\t\t\twinner: raw.winner,\r\n\t\t\t\twon: raw.winner === 'wizard-1',\r\n\t\t\t\tlost: raw.winner === 'wizard-2',\r\n\t\t\t\tdrawn: raw.winner === null || raw.winner === 'draw',\r\n\t\t\t\tticks: raw.ticks,\r\n\t\t\t\tmyHealth: raw.finalState.health,\r\n\t\t\t\tenemyHealth: raw.finalState.enemies?.[0]?.health ?? 0,\r\n\t\t\t\tstats: extractStats(raw),\r\n\t\t\t\terrors: raw.errors,\r\n\t\t\t};\r\n\t\t},\r\n\t};\r\n}\r\n","/**\r\n * VIBEMANCER - FIGHT TRACE\r\n *\r\n * Extracts a structured event log from a SimulateResult history.\r\n * Both bots' actions are tracked: state changes, missile launches with\r\n * full config, hits, damage, dodge proximity, movement patterns.\r\n *\r\n * Used by:\r\n * - vibemancer trace (CLI debug command)\r\n * - scripts/fight-trace.ts (internal diagnostic)\r\n * - User tests that want event-level analysis\r\n */\r\n\r\nimport type {GameState, ProjectileState} from './types.js';\r\nimport type {SimulateResult, BotError} from './engine/simulation.js';\r\nimport {distanceTo} from './utils/distance.js';\r\n\r\n// ─── Types ───────────────────────────────────────────────────────────────────\r\n\r\nexport interface TraceEvent\r\n{\r\n\ttick: number;\r\n\t/** 'W1' = wizard-1, 'W2' = wizard-2 */\r\n\tactor: 'W1' | 'W2';\r\n\ttype: TraceEventType;\r\n\tdetail: string;\r\n}\r\n\r\nexport type TraceEventType =\r\n\t| 'STATE' // State transition (idle→casting, etc.)\r\n\t| 'FIRE' // Missile launched (includes config + range)\r\n\t| 'HIT' // Damage dealt to opponent\r\n\t| 'HURT' // Damage taken from opponent\r\n\t| 'DODGE_START' // Enemy missile enters 150u proximity\r\n\t| 'DODGE_CLOSE' // Enemy missile enters 50u proximity\r\n\t| 'MOVE' // Movement summary (every 100 ticks)\r\n\t| 'ERROR' // Bot or missile AI threw a runtime error\r\n\t| 'WARNING'; // Common mistake detected (move/blink confusion, etc.)\r\n\r\nexport interface TraceSummary\r\n{\r\n\twinner: string;\r\n\tticks: number;\r\n\tw1Name: string;\r\n\tw2Name: string;\r\n\tw1FinalHp: number;\r\n\tw2FinalHp: number;\r\n\tw1: TraceBotSummary;\r\n\tw2: TraceBotSummary;\r\n}\r\n\r\nexport interface TraceBotSummary\r\n{\r\n\tmissilesLaunched: number;\r\n\thits: number;\r\n\tdamageDealt: number;\r\n\tdamageReceived: number;\r\n\tshields: number;\r\n\tblinks: number;\r\n\tdodgeEncounters: number;\r\n\tmovement: {strafe: number; approach: number; retreat: number; still: number};\r\n}\r\n\r\n// ─── Event Extraction ────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Extract a structured event log from simulation history.\r\n * Tracks both bots' state changes, missile launches (with full config + range),\r\n * hits, damage, dodge proximity, movement patterns, and runtime errors.\r\n */\r\nexport function extractTraceEvents(history: GameState[], errors?: BotError[]): TraceEvent[]\r\n{\r\n\tconst events: TraceEvent[] = [];\r\n\r\n\tlet prevW1State = '';\r\n\tlet prevW2State = '';\r\n\tlet prevW1Health = history[0]?.health ?? 60;\r\n\tlet prevW2Health = history[0]?.enemies[0]?.health ?? 60;\r\n\tlet prevW1Spell = '';\r\n\tlet prevW2Spell = '';\r\n\tconst prevProjectileIds = new Set<string>();\r\n\r\n\tfor (let i = 0; i < history.length; i++)\r\n\t{\r\n\t\tconst state = history[i]!;\r\n\t\tconst tick = state.tick;\r\n\t\tconst enemy = state.enemies[0]!;\r\n\t\tconst distance = distanceTo(state.position, enemy.position);\r\n\r\n\t\t// --- W1 state transitions ---\r\n\t\tconst w1State = state.state;\r\n\t\tconst w1Spell = state.castingSpell ?? state.channelingSpell ?? '';\r\n\t\tif (w1State !== prevW1State || w1Spell !== prevW1Spell)\r\n\t\t{\r\n\t\t\tlet detail = `${prevW1State || '?'}→${w1State}`;\r\n\t\t\tif (w1Spell) detail += ` (${w1Spell})`;\r\n\t\t\tif (w1State === 'casting' && state.castDuration) detail += ` [${state.castDuration}t]`;\r\n\t\t\tdetail += ` dist=${distance.toFixed(0)}`;\r\n\t\t\tevents.push({tick, actor: 'W1', type: 'STATE', detail});\r\n\t\t}\r\n\r\n\t\t// --- W2 state transitions ---\r\n\t\tconst w2State = enemy.state;\r\n\t\tconst w2Spell = enemy.castingSpell ?? enemy.channelingSpell ?? '';\r\n\t\tif (w2State !== prevW2State || w2Spell !== prevW2Spell)\r\n\t\t{\r\n\t\t\tlet detail = `${prevW2State || '?'}→${w2State}`;\r\n\t\t\tif (w2Spell) detail += ` (${w2Spell})`;\r\n\t\t\tif (w2State === 'casting' && enemy.castDuration) detail += ` [${enemy.castDuration}t]`;\r\n\t\t\tdetail += ` dist=${distance.toFixed(0)}`;\r\n\t\t\tevents.push({tick, actor: 'W2', type: 'STATE', detail});\r\n\t\t}\r\n\r\n\t\t// --- Damage events ---\r\n\t\tif (enemy.health < prevW2Health)\r\n\t\t{\r\n\t\t\tconst dmg = prevW2Health - enemy.health;\r\n\t\t\tevents.push({\r\n\t\t\t\ttick, actor: 'W1', type: 'HIT',\r\n\t\t\t\tdetail: `dealt ${dmg.toFixed(1)} → enemy HP=${enemy.health.toFixed(1)} dist=${distance.toFixed(0)}`,\r\n\t\t\t});\r\n\t\t}\r\n\t\tif (state.health < prevW1Health)\r\n\t\t{\r\n\t\t\tconst dmg = prevW1Health - state.health;\r\n\t\t\tconst stateTag = state.state === 'channeling' ? ' (SHIELDED)'\r\n\t\t\t\t: state.state === 'casting' ? ` (CASTING ${state.castingSpell})`\r\n\t\t\t\t\t: state.state === 'gcd_locked' ? ' (GCD)'\r\n\t\t\t\t\t\t: ` (${state.state})`;\r\n\t\t\tevents.push({\r\n\t\t\t\ttick, actor: 'W1', type: 'HURT',\r\n\t\t\t\tdetail: `took ${dmg.toFixed(1)} → HP=${state.health.toFixed(1)}${stateTag} dist=${distance.toFixed(0)}`,\r\n\t\t\t});\r\n\t\t}\r\n\t\t// W2 damage taken = W1 damage dealt (already covered above as HIT)\r\n\t\t// W2 damage dealt = W1 damage taken (already covered above as HURT)\r\n\r\n\t\t// --- Missile launches (with full config) ---\r\n\t\tconst currentIds = new Set<string>();\r\n\t\tfor (const proj of state.projectiles)\r\n\t\t{\r\n\t\t\tcurrentIds.add(proj.id);\r\n\t\t\tif (!prevProjectileIds.has(proj.id))\r\n\t\t\t{\r\n\t\t\t\tconst owner: 'W1' | 'W2' = proj.ownerId === 'wizard-1' ? 'W1' : 'W2';\r\n\t\t\t\tconst range = (proj.speed * proj.remainingTicks).toFixed(0);\r\n\t\t\t\tevents.push({\r\n\t\t\t\t\ttick, actor: owner, type: 'FIRE',\r\n\t\t\t\t\tdetail: `dmg=${proj.damage} spd=${proj.speed} turn=${proj.turnRate} life=${proj.remainingTicks} range=${range} dist=${distance.toFixed(0)}`,\r\n\t\t\t\t});\r\n\t\t\t\t// Warn if missile range < distance (will expire before reaching target)\r\n\t\t\t\tif (proj.speed * proj.remainingTicks < distance * 0.8 && proj.turnRate >= 0)\r\n\t\t\t\t{\r\n\t\t\t\t\tevents.push({\r\n\t\t\t\t\t\ttick, actor: owner, type: 'WARNING',\r\n\t\t\t\t\t\tdetail: `missile range (${range}) may be too short for distance (${distance.toFixed(0)}) — consider increasing duration`,\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tprevProjectileIds.clear();\r\n\t\tfor (const id of currentIds) prevProjectileIds.add(id);\r\n\r\n\t\t// --- Dodge proximity events (enemy missiles approaching W1) ---\r\n\t\tif (i > 0)\r\n\t\t{\r\n\t\t\tconst speed = Math.sqrt(state.velocity.x ** 2 + state.velocity.y ** 2);\r\n\t\t\tfor (const proj of state.projectiles)\r\n\t\t\t{\r\n\t\t\t\tif (proj.ownerId === 'wizard-1') continue;\r\n\t\t\t\tconst missileDistance = distanceTo(state.position, proj.position);\r\n\t\t\t\tconst prevProj = history[i - 1]!.projectiles.find((p) => p.id === proj.id);\r\n\t\t\t\tconst prevMissileDist = prevProj ? distanceTo(history[i - 1]!.position, prevProj.position) : Infinity;\r\n\r\n\t\t\t\tif (missileDistance <= 150 && prevMissileDist > 150)\r\n\t\t\t\t{\r\n\t\t\t\t\tevents.push({\r\n\t\t\t\t\t\ttick, actor: 'W1', type: 'DODGE_START',\r\n\t\t\t\t\t\tdetail: formatDodgeDetail(state, proj, missileDistance, speed),\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t\tif (missileDistance <= 50 && prevMissileDist > 50)\r\n\t\t\t\t{\r\n\t\t\t\t\tevents.push({\r\n\t\t\t\t\t\ttick, actor: 'W1', type: 'DODGE_CLOSE',\r\n\t\t\t\t\t\tdetail: formatDodgeDetail(state, proj, missileDistance, speed),\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// --- Movement summary every 100 ticks ---\r\n\t\tif (tick > 0 && tick % 100 === 0)\r\n\t\t{\r\n\t\t\tconst speed = Math.sqrt(state.velocity.x ** 2 + state.velocity.y ** 2);\r\n\t\t\tconst moveType = classifyMovement(state, enemy, speed);\r\n\t\t\tconst incomingMissiles = state.projectiles.filter((p) => p.ownerId !== 'wizard-1').length;\r\n\t\t\tevents.push({\r\n\t\t\t\ttick, actor: 'W1', type: 'MOVE',\r\n\t\t\t\tdetail: `${moveType} spd=${speed.toFixed(2)} dist=${distance.toFixed(0)} missiles=${incomingMissiles} ${state.state} hp=${state.health.toFixed(1)}`,\r\n\t\t\t});\r\n\t\t}\r\n\r\n\t\tprevW1State = w1State;\r\n\t\tprevW2State = w2State;\r\n\t\tprevW1Health = state.health;\r\n\t\tprevW2Health = enemy.health;\r\n\t\tprevW1Spell = w1Spell;\r\n\t\tprevW2Spell = w2Spell;\r\n\t}\r\n\r\n\t// --- Inject runtime errors as ERROR events ---\r\n\tif (errors && errors.length > 0)\r\n\t{\r\n\t\tfor (const err of errors)\r\n\t\t{\r\n\t\t\tconst actor: 'W1' | 'W2' = err.entityId === 'wizard-1' ? 'W1'\r\n\t\t\t\t: err.entityId === 'wizard-2' ? 'W2'\r\n\t\t\t\t\t: err.entityId.startsWith('missile') ? (/* missile errors attributed to owner */ 'W1') : 'W1';\r\n\t\t\tevents.push({\r\n\t\t\t\ttick: err.tick,\r\n\t\t\t\tactor,\r\n\t\t\t\ttype: 'ERROR',\r\n\t\t\t\tdetail: err.message,\r\n\t\t\t});\r\n\t\t}\r\n\t\t// Re-sort by tick so errors appear in chronological order\r\n\t\tevents.sort((a, b) => a.tick - b.tick || (a.type === 'ERROR' ? 1 : 0));\r\n\t}\r\n\r\n\treturn events;\r\n}\r\n\r\n// ─── Summary ─────────────────────────────────────────────────────────────────\r\n\r\n/**\r\n * Generate a summary from trace events and the simulation result.\r\n */\r\nexport function summarizeTrace(\r\n\tevents: TraceEvent[],\r\n\tresult: SimulateResult,\r\n\tw1Name: string,\r\n\tw2Name: string,\r\n): TraceSummary\r\n{\r\n\tconst final = result.history[result.history.length - 1];\r\n\r\n\tfunction botSummary(actor: 'W1' | 'W2'): TraceBotSummary\r\n\t{\r\n\t\tconst myEvents = events.filter((e) => e.actor === actor);\r\n\t\tconst moveEvents = myEvents.filter((e) => e.type === 'MOVE');\r\n\t\treturn {\r\n\t\t\tmissilesLaunched: myEvents.filter((e) => e.type === 'FIRE').length,\r\n\t\t\thits: myEvents.filter((e) => e.type === 'HIT').length,\r\n\t\t\tdamageDealt: myEvents.filter((e) => e.type === 'HIT').reduce((sum, e) =>\r\n\t\t\t{\r\n\t\t\t\tconst m = e.detail.match(/dealt ([\\d.]+)/);\r\n\t\t\t\treturn sum + (m ? parseFloat(m[1]!) : 0);\r\n\t\t\t}, 0),\r\n\t\t\tdamageReceived: myEvents.filter((e) => e.type === 'HURT').reduce((sum, e) =>\r\n\t\t\t{\r\n\t\t\t\tconst m = e.detail.match(/took ([\\d.]+)/);\r\n\t\t\t\treturn sum + (m ? parseFloat(m[1]!) : 0);\r\n\t\t\t}, 0),\r\n\t\t\tshields: myEvents.filter((e) => e.type === 'STATE' && e.detail.includes('shield')).length,\r\n\t\t\tblinks: myEvents.filter((e) => e.type === 'STATE' && e.detail.includes('blink')).length,\r\n\t\t\tdodgeEncounters: myEvents.filter((e) => e.type.startsWith('DODGE')).length,\r\n\t\t\tmovement: {\r\n\t\t\t\tstrafe: moveEvents.filter((e) => e.detail.startsWith('STRAFE')).length,\r\n\t\t\t\tapproach: moveEvents.filter((e) => e.detail.startsWith('APPROACH')).length,\r\n\t\t\t\tretreat: moveEvents.filter((e) => e.detail.startsWith('RETREAT')).length,\r\n\t\t\t\tstill: moveEvents.filter((e) => e.detail.startsWith('STILL')).length,\r\n\t\t\t},\r\n\t\t};\r\n\t}\r\n\r\n\tconst winnerName = result.winner === 'wizard-1' ? w1Name\r\n\t\t: result.winner === 'wizard-2' ? w2Name\r\n\t\t\t: result.winner === 'draw' ? 'draw'\r\n\t\t\t\t: 'timeout';\r\n\r\n\treturn {\r\n\t\twinner: winnerName,\r\n\t\tticks: result.ticks,\r\n\t\tw1Name,\r\n\t\tw2Name,\r\n\t\tw1FinalHp: final?.health ?? 0,\r\n\t\tw2FinalHp: final?.enemies[0]?.health ?? 0,\r\n\t\tw1: botSummary('W1'),\r\n\t\tw2: botSummary('W2'),\r\n\t};\r\n}\r\n\r\n// ─── Formatting ──────────────────────────────────────────────────────────────\r\n\r\n/** Format trace events as a human-readable string. */\r\nexport function formatTraceEvents(events: TraceEvent[]): string\r\n{\r\n\treturn events.map((e) =>\r\n\t{\r\n\t\tconst t = String(e.tick).padStart(5);\r\n\t\tconst a = e.actor.padEnd(2);\r\n\t\tconst ty = e.type.padEnd(12);\r\n\t\treturn ` T${t} ${a} ${ty} ${e.detail}`;\r\n\t}).join('\\n');\r\n}\r\n\r\n/** Format a full trace summary as a human-readable string. */\r\nexport function formatTraceSummary(summary: TraceSummary): string\r\n{\r\n\tconst {w1Name, w2Name, w1, w2} = summary;\r\n\tconst lines = [\r\n\t\t` Winner: ${summary.winner} @ T${summary.ticks}`,\r\n\t\t` Final HP: ${w1Name}=${summary.w1FinalHp.toFixed(1)}, ${w2Name}=${summary.w2FinalHp.toFixed(1)}`,\r\n\t\t'',\r\n\t\t` ${w1Name}: ${w1.missilesLaunched} missiles, ${w1.hits} hits (${w1.missilesLaunched > 0 ? ((w1.hits / w1.missilesLaunched) * 100).toFixed(0) : 'N/A'}%), ${w1.damageDealt.toFixed(1)} dmg dealt, ${w1.damageReceived.toFixed(1)} dmg taken, ${w1.shields} shields, ${w1.blinks} blinks`,\r\n\t\t` ${w2Name}: ${w2.missilesLaunched} missiles, ${w2.hits} hits (${w2.missilesLaunched > 0 ? ((w2.hits / w2.missilesLaunched) * 100).toFixed(0) : 'N/A'}%), ${w2.damageDealt.toFixed(1)} dmg dealt, ${w2.damageReceived.toFixed(1)} dmg taken, ${w2.shields} shields, ${w2.blinks} blinks`,\r\n\t\t'',\r\n\t\t` ${w1Name} movement: ${w1.movement.approach} approach, ${w1.movement.strafe} strafe, ${w1.movement.retreat} retreat, ${w1.movement.still} still | ${w1.dodgeEncounters} dodge encounters`,\r\n\t];\r\n\treturn lines.join('\\n');\r\n}\r\n\r\n/**\r\n * Generate diagnostic tips based on trace analysis.\r\n * Identifies common problems and suggests fixes.\r\n * Returns an array of human-readable tips (empty if no issues found).\r\n */\r\nexport function diagnoseTrace(events: TraceEvent[], summary: TraceSummary): string[]\r\n{\r\n\tconst tips: string[] = [];\r\n\tconst {w1} = summary;\r\n\r\n\t// Check for runtime errors\r\n\tconst errors = events.filter((e) => e.actor === 'W1' && e.type === 'ERROR');\r\n\tif (errors.length > 0)\r\n\t{\r\n\t\tconst firstErr = errors[0]!;\r\n\t\ttips.push(`BOT ERROR: Your bot threw \"${firstErr.detail}\" on tick ${firstErr.tick}. The bot does nothing when it throws. Fix this first.`);\r\n\t\tif (errors.length > 1) tips.push(` (${errors.length} total errors — likely the same bug repeating every tick)`);\r\n\t}\r\n\r\n\t// Check for range warnings\r\n\tconst rangeWarnings = events.filter((e) => e.actor === 'W1' && e.type === 'WARNING' && e.detail.includes('range'));\r\n\tif (rangeWarnings.length > 0)\r\n\t{\r\n\t\ttips.push(`RANGE: ${rangeWarnings.length} of your missiles had range shorter than target distance. Increase missile duration or get closer before firing.`);\r\n\t}\r\n\r\n\t// No missiles fired\r\n\tif (w1.missilesLaunched === 0 && errors.length === 0)\r\n\t{\r\n\t\ttips.push('NO OFFENSE: Your bot fired 0 missiles. Check that you return missile() when useTicksUntilReady() === 0.');\r\n\t}\r\n\r\n\t// Very low hit rate\r\n\tif (w1.missilesLaunched >= 3 && w1.hits === 0)\r\n\t{\r\n\t\ttips.push(`ACCURACY: Fired ${w1.missilesLaunched} missiles but 0 hit. Consider: more homing (higher turnRate), or use getLeadPosition() for straight missiles, or get closer.`);\r\n\t}\r\n\telse if (w1.missilesLaunched >= 5 && w1.hits / w1.missilesLaunched < 0.25)\r\n\t{\r\n\t\ttips.push(`LOW ACCURACY: Only ${((w1.hits / w1.missilesLaunched) * 100).toFixed(0)}% hit rate. Try increasing turnRate for more homing, or use getLeadPosition() for prediction.`);\r\n\t}\r\n\r\n\t// No shields against incoming damage\r\n\tif (w1.damageReceived > 20 && w1.shields === 0)\r\n\t{\r\n\t\ttips.push(`NO DEFENSE: Took ${w1.damageReceived.toFixed(0)} damage with 0 shields. Use useThreats() to detect incoming missiles and shield() when you can't dodge.`);\r\n\t}\r\n\r\n\t// Too much idle time\r\n\tif (summary.ticks > 500)\r\n\t{\r\n\t\tconst idleMovements = w1.movement.still;\r\n\t\tconst totalMovements = idleMovements + w1.movement.approach + w1.movement.strafe + w1.movement.retreat;\r\n\t\tif (totalMovements > 0 && idleMovements / totalMovements > 0.5)\r\n\t\t{\r\n\t\t\ttips.push(`IDLE: Your bot was stationary ${((idleMovements / totalMovements) * 100).toFixed(0)}% of the time. Move to dodge missiles and close distance.`);\r\n\t\t}\r\n\t}\r\n\r\n\treturn tips;\r\n}\r\n\r\n/** Format diagnostic tips as a human-readable string. */\r\nexport function formatDiagnosis(tips: string[]): string\r\n{\r\n\tif (tips.length === 0) return '';\r\n\tconst lines = [' Diagnosis:'];\r\n\tfor (const tip of tips)\r\n\t{\r\n\t\tlines.push(` - ${tip}`);\r\n\t}\r\n\treturn lines.join('\\n');\r\n}\r\n\r\n// ─── Helpers ─────────────────────────────────────────────────────────────────\r\n\r\nfunction formatDodgeDetail(state: GameState, proj: ProjectileState, missileDistance: number, speed: number): string\r\n{\r\n\tconst angle = dodgeAngle(state.velocity, proj.rotation);\r\n\treturn `missile at ${missileDistance.toFixed(0)}u rot=${proj.rotation.toFixed(0)}° dodge_angle=${angle.toFixed(0)}° ` +\r\n\t\t`spd=${speed.toFixed(2)} ${state.state}`;\r\n}\r\n\r\nfunction dodgeAngle(wizardVelocity: {x: number; y: number}, missileRotation: number): number\r\n{\r\n\tconst speed = Math.sqrt(wizardVelocity.x ** 2 + wizardVelocity.y ** 2);\r\n\tif (speed < 0.01) return 0;\r\n\tconst missileRad = missileRotation * (Math.PI / 180);\r\n\tconst dot = Math.cos(missileRad) * (wizardVelocity.x / speed) + Math.sin(missileRad) * (wizardVelocity.y / speed);\r\n\treturn Math.acos(Math.max(-1, Math.min(1, Math.abs(dot)))) * (180 / Math.PI);\r\n}\r\n\r\nfunction classifyMovement(state: GameState, enemy: GameState['enemies'][0], speed: number): string\r\n{\r\n\tif (speed < 0.1) return 'STILL';\r\n\tconst toEnemyAngle = Math.atan2(enemy.position.y - state.position.y, enemy.position.x - state.position.x);\r\n\tconst moveAngle = Math.atan2(state.velocity.y, state.velocity.x);\r\n\tlet angleDiff = ((moveAngle - toEnemyAngle) * 180 / Math.PI) % 360;\r\n\tif (angleDiff > 180) angleDiff -= 360;\r\n\tif (angleDiff < -180) angleDiff += 360;\r\n\tconst absAngle = Math.abs(angleDiff);\r\n\tif (absAngle < 30) return 'APPROACH';\r\n\tif (absAngle > 150) return 'RETREAT';\r\n\tif (absAngle >= 60 && absAngle <= 120) return 'STRAFE';\r\n\tif (absAngle < 60) return 'APPROACH+STRAFE';\r\n\treturn 'RETREAT+STRAFE';\r\n}\r\n"],"mappings":"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","names":["move","magnitude","dist2","tick","oldPos","move","isOutOfBounds","tick","move","tick","angleDiff","magnitude","move","SHIELD_BUFFER","HomingAI","nextThreatTime","safeBudget","config","WALL_BUFFER","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","aggressiveMove","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","aggressiveMove","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","HomingAI","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","HomingAI","StraightAI","isNearWall","smartStrafe","tick","move","ntt","sBudget","cfg","WALL_BUFFER","SHIELD_BUFFER","MISSILE_CONFIG","HomingAI","isNearWall","smartStrafe","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","HomingAI","isNearWall","smartStrafe","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","HomingAI","StraightAI","isNearWall","smartStrafe","tick","move","nextThreatTime","safeBudget","config","HomingAI","WALL_BUFFER","tick","magnitude","move","HomingAI","WALL_BUFFER","SHIELD_BUFFER","combatMove","tick","magnitude","move","HomingAI","StraightAI","WALL_BUFFER","SHIELD_BUFFER","combatMove","tick","magnitude","move","nextThreatTime","safeBudget","config","WALL_BUFFER","isNearWall","combatMove","tick","magnitude","dodgeDirection","move","WALL_BUFFER","SHIELD_BUFFER","StandardHoming","isNearWall","combatMove","tick","magnitude","dodgeDirection","move","WALL_BUFFER","SHIELD_BUFFER","HomingAI","StraightAI","isNearWall","combatMove","tick","magnitude","move","blinkTarget","nextThreatTime","safeBudget","config","WALL_BUFFER","QUICK_CONFIG","isNearWall","combatMove","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","isNearWall","combatMove","tick","magnitude","dodgeDirection","move","WALL_BUFFER","SHIELD_BUFFER","HomingAI","StraightAI","isNearWall","combatMove","tick","magnitude","dodgeDirection","move","config","WALL_BUFFER","STANDARD_CONFIG","QUICK_CONFIG","HomingAI","isNearWall","smartStrafe","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","HomingAI","isNearWall","smartStrafe","tick","magnitude","move","WALL_BUFFER","SHIELD_BUFFER","HomingAI","StraightAI","isNearWall","smartStrafe","tick","magnitude","move","tick","angleDiff"]}