ai-code-battle/bots/kamikaze/strategy.js
jedarden 4dd91decad feat(bot): implement Kamikaze bot (JavaScript) — aggressive self-sacrifice archetype
Max-aggression strategy with unconditional attack: every unit charges the
nearest enemy, never retreats, and presses engagements within attack range.
Fixed self-collision bug by reserving all starting positions and freeing
them as bots move away. Falls back to enemy core rushing when no enemies
are visible, with exploratory spreading when no targets exist.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 17:11:28 -04:00

165 lines
4.5 KiB
JavaScript

const { distance2, manhattan, moveDir, posKey } = require("./grid");
const DIRECTIONS = ["N", "E", "S", "W"];
function computeMoves(state) {
const { rows, cols, attack_radius2 } = state.config;
const myId = state.you.id;
const myBots = [];
const enemyBots = [];
for (const bot of state.bots) {
if (bot.owner === myId) myBots.push(bot);
else enemyBots.push(bot);
}
if (myBots.length === 0) return [];
const walls = new Set(state.walls.map((w) => posKey(w.row, w.col)));
const enemyCores = state.cores.filter(
(c) => c.owner !== myId && c.active
);
const energySet = new Set(
(state.energy || []).map((e) => posKey(e.row, e.col))
);
// Reserve all starting positions so no two bots end up on the same tile.
// When a bot moves away, its old cell is freed for others.
const committed = new Set(
myBots.map((b) => posKey(b.position.row, b.position.col))
);
const moves = [];
// Bots closest to enemies decide first — they get priority on attack positions
myBots.sort((a, b) => {
const da = nearestEnemyDist2(a.position, enemyBots, rows, cols);
const db = nearestEnemyDist2(b.position, enemyBots, rows, cols);
return da - db;
});
for (const bot of myBots) {
const br = bot.position.row;
const bc = bot.position.col;
const target = findNearestEnemy(br, bc, enemyBots, rows, cols);
let bestDir = null;
let bestScore = -Infinity;
for (const dir of DIRECTIONS) {
const [nr, nc] = moveDir(br, bc, dir, rows, cols);
const nk = posKey(nr, nc);
if (walls.has(nk)) continue;
if (committed.has(nk)) continue;
let score = 0;
if (target) {
const distToTarget = distance2(
nr, nc, target.position.row, target.position.col, rows, cols
);
// Close distance to nearest enemy as fast as possible
score -= distToTarget * 100;
// Heavy bonus for staying in attack range — press the engagement
if (distToTarget <= attack_radius2) {
score += 200;
}
// Also prefer closing distance to other enemies (don't tunnel-vision)
let totalEnemyDist = 0;
for (const e of enemyBots) {
totalEnemyDist += distance2(
nr, nc, e.position.row, e.position.col, rows, cols
);
}
score -= totalEnemyDist;
// Grab energy only when it's directly along the attack path
if (energySet.has(nk)) {
score += 5;
}
} else {
// No enemies visible — rush enemy core to raze it
if (enemyCores.length > 0) {
const coreDist = nearestCoreDist(
nr, nc, enemyCores, rows, cols
);
score -= coreDist * 100;
} else {
// No targets at all — spread outward to explore
// Prefer moving away from other friendly bots
let friendProximity = 0;
for (const other of myBots) {
if (other === bot) continue;
friendProximity += distance2(
nr, nc, other.position.row, other.position.col, rows, cols
);
}
score += friendProximity * 0.5;
}
// Collect energy while roaming (we need it to keep spawning)
if (energySet.has(nk)) {
score += 10;
}
}
if (score > bestScore) {
bestScore = score;
bestDir = dir;
}
}
if (bestDir) {
const [nr, nc] = moveDir(br, bc, bestDir, rows, cols);
committed.delete(posKey(br, bc));
committed.add(posKey(nr, nc));
moves.push({
position: { row: br, col: bc },
direction: bestDir,
});
}
// If no direction is viable the bot holds; its starting cell stays in committed
}
return moves;
}
function findNearestEnemy(r, c, enemyBots, rows, cols) {
let best = null;
let bestDist = Infinity;
for (const e of enemyBots) {
const d = distance2(r, c, e.position.row, e.position.col, rows, cols);
if (d < bestDist) {
bestDist = d;
best = e;
}
}
return best;
}
function nearestEnemyDist2(pos, enemyBots, rows, cols) {
let minD = Infinity;
for (const e of enemyBots) {
const d = distance2(
pos.row, pos.col, e.position.row, e.position.col, rows, cols
);
if (d < minD) minD = d;
}
return minD;
}
function nearestCoreDist(r, c, cores, rows, cols) {
let minD = Infinity;
for (const core of cores) {
const d = manhattan(
r, c, core.position.row, core.position.col, rows, cols
);
if (d < minD) minD = d;
}
return minD;
}
module.exports = { computeMoves };