// Brawler: aggressive pressure. Closes in while firing, and steps out of shots and areas it can see. // Up close it lines up a slam (a wall behind the enemy), and the stun sets up a shot that can't miss. import { chooseAttack, endTile, evadeDanger, cheb, inSafeZone, pickupStep, searchGoal, shove, stepToward, watchEnemy, type Ctx, type Observation, type Policy, type Turn } from "../lib/bot.ts"; /** Movement phase. */ function movement(obs: Observation, ctx: Ctx): Turn { const escape = evadeDanger(obs); if (escape) return escape; const pos = obs.self.pos; const cd = obs.self.cooldowns; const enemy = obs.enemies[0]; if (cd.move !== 0) return {}; // The zone comes first, before the enemy or a pickup: walk back toward the centre. if (!inSafeZone(obs, pos)) { const d = stepToward(ctx.map, pos, obs.zone.center, 0, enemy ? [enemy.pos] : []); if (d) return { move: d }; } // Pickups are free to take: detour for a nearby one, farther when nobody is in sight. const grab = pickupStep(obs, ctx, enemy ? 3 : 8); if (grab) return { move: grab }; if (!enemy) { const d = stepToward(ctx.map, pos, searchGoal(obs, ctx), 1); return d ? { move: d } : {}; } // Combo setup: with the repulsor ready, stand on the side of the enemy that puts a wall behind them. const dist = cheb(pos, enemy.pos); if (cd.repulsor === 0 && dist <= 2) { const push = ctx.hello.rules.attacks.repulsor.push; if (dist === 1 && shove(ctx.map, pos, enemy.pos, push).slam) return {}; const spot = obs.moves.find((m) => cheb(m.tile, enemy.pos) === 1 && shove(ctx.map, m.tile, enemy.pos, push).slam); if (spot) return { move: spot.dir }; } // Close the distance: always when far or when the guns are cooling, sometimes when near. if (dist > 2 && (dist > 4 || cd.ranged !== 0 || ctx.rand() < 0.3)) { const d = stepToward(ctx.map, pos, enemy.pos, 2, [enemy.pos]); if (d) return { move: d }; } return {}; } /** A line to shout when it slams the enemy into a wall (talking is free, so it never costs a move). */ function taunt(obs: Observation): string | undefined { const slam = obs.events.some((e) => e.kind === "wall_slam" && e.attacker === obs.self.id); return slam && obs.self.cooldowns.shout === 0 ? "Say hello to the wall!" : undefined; } export const brawler: Policy = { version: "brawler-18", decide(obs, ctx): Turn { watchEnemy(obs, ctx); const shout = taunt(obs); if (obs.self.state !== "idle") return shout ? { shout } : {}; const turn = movement(obs, ctx); const attack = chooseAttack(obs, ctx, endTile(obs, turn)); if (attack) turn.attack = attack; if (shout) turn.shout = shout; return turn; }, };