Improve bot (try)

This commit is contained in:
Mike Müller 2026-03-08 21:11:12 +01:00
parent 86ae86febc
commit cfeda33d33
2 changed files with 199 additions and 49 deletions

1
.gitignore vendored
View File

@ -130,3 +130,4 @@ dist
.yarn/install-state.gz
.pnp.*
highscores.json

View File

@ -1452,23 +1452,123 @@ function adjacentCrate(x, y) {
return false;
}
function enemyInBlastPotential(player) {
function countAdjacentCrates(x, y) {
let count = 0;
for (const dir of DIRECTIONS) {
const nx = x + dir.dx;
const ny = y + dir.dy;
if (inBounds(nx, ny) && state.map[ny][nx].type === "crate") {
count += 1;
}
}
return count;
}
function threatenedEnemiesFromCell(player, x, y) {
let count = 0;
for (const other of state.players) {
if (!other.alive || other.id === player.id) {
continue;
}
if (player.x !== other.x && player.y !== other.y) {
if (x !== other.x && y !== other.y) {
continue;
}
const distance = Math.abs(player.x - other.x) + Math.abs(player.y - other.y);
const distance = Math.abs(x - other.x) + Math.abs(y - other.y);
if (distance > player.flameRange) {
continue;
}
if (!hasHardBlockBetween(player.x, player.y, other.x, other.y)) {
return true;
if (!hasHardBlockBetween(x, y, other.x, other.y)) {
count += 1;
}
}
return false;
return count;
}
function nearestEnemyDistance(x, y, playerId) {
let best = Infinity;
for (const other of state.players) {
if (!other.alive || other.id === playerId) {
continue;
}
const distance = Math.abs(x - other.x) + Math.abs(y - other.y);
if (distance < best) {
best = distance;
}
}
return best;
}
function botFindDirection(player, evaluator, maxDepth = 8) {
const visited = new Set([tileKey(player.x, player.y)]);
const queue = [];
for (const dir of DIRECTIONS) {
const nx = player.x + dir.dx;
const ny = player.y + dir.dy;
if (!canEnterTile(nx, ny, player)) {
continue;
}
visited.add(tileKey(nx, ny));
queue.push({ x: nx, y: ny, depth: 1, firstDir: dir.key });
}
let best = null;
while (queue.length > 0) {
const node = queue.shift();
const score = evaluator(node.x, node.y, node.depth);
if (Number.isFinite(score)) {
if (!best || score > best.score || (score === best.score && node.depth < best.depth)) {
best = { score, depth: node.depth, dir: node.firstDir };
}
}
if (node.depth >= maxDepth) {
continue;
}
for (const dir of DIRECTIONS) {
const nx = node.x + dir.dx;
const ny = node.y + dir.dy;
const key = tileKey(nx, ny);
if (visited.has(key)) {
continue;
}
if (!canEnterTile(nx, ny, player)) {
continue;
}
visited.add(key);
queue.push({ x: nx, y: ny, depth: node.depth + 1, firstDir: node.firstDir });
}
}
return best ? best.dir : null;
}
function pickEscapeDirection(player) {
return botFindDirection(
player,
(x, y, depth) => {
const danger = estimateDangerAt(x, y);
let score = -danger * 3 - depth * 0.55;
if (danger < 0.6) {
score += 3.5;
}
if (!state.fireLookup.has(tileKey(x, y))) {
score += 0.6;
}
const tile = state.map[y][x];
if (tile && tile.powerup) {
score += 0.8;
}
return score;
},
9,
);
}
function enemyInBlastPotential(player) {
return threatenedEnemiesFromCell(player, player.x, player.y) > 0;
}
function updateBot(player, dt) {
@ -1481,63 +1581,112 @@ function updateBot(player, dt) {
if (player.ai.thinkTimer > 0) {
return;
}
player.ai.thinkTimer = 0.13 + Math.random() * 0.2;
player.ai.thinkTimer = 0.08 + Math.random() * 0.12;
const currentDanger = estimateDangerAt(player.x, player.y);
const liveEnemies = state.players.filter((p) => p.alive && p.id !== player.id);
const targetEnemy = liveEnemies.sort(
const currentKey = tileKey(player.x, player.y);
const targetEnemy = state.players
.filter((p) => p.alive && p.id !== player.id)
.sort(
(a, b) =>
Math.abs(a.x - player.x) + Math.abs(a.y - player.y) -
(Math.abs(b.x - player.x) + Math.abs(b.y - player.y)),
)[0];
if (state.fireLookup.has(currentKey) || currentDanger >= 1.2) {
player.ai.desiredDir = pickEscapeDirection(player);
return;
}
const cratePressure = countAdjacentCrates(player.x, player.y);
const enemyPressure = threatenedEnemiesFromCell(player, player.x, player.y);
if (
player.bombsPlaced < player.bombCapacity &&
player.ai.bombCooldown <= 0 &&
currentDanger < 0.9 &&
(adjacentCrate(player.x, player.y) || enemyInBlastPotential(player)) &&
hasEscapeRouteAfterBomb(player)
currentDanger < 0.8 &&
hasEscapeRouteAfterBomb(player) &&
(enemyPressure > 0 || cratePressure >= 2 || (cratePressure >= 1 && Math.random() < 0.45))
) {
if (dropBomb(player)) {
player.ai.bombCooldown = 1.4 + Math.random() * 0.8;
player.ai.bombCooldown = enemyPressure > 0 ? 0.8 + Math.random() * 0.45 : 1.1 + Math.random() * 0.55;
}
}
let desiredDir = botFindDirection(
player,
(x, y, depth) => {
const tile = state.map[y][x];
if (!tile || !tile.powerup) {
return Number.NEGATIVE_INFINITY;
}
return 8 - depth - estimateDangerAt(x, y) * 1.8;
},
7,
);
if (!desiredDir && targetEnemy) {
desiredDir = botFindDirection(
player,
(x, y, depth) => {
const danger = estimateDangerAt(x, y);
if (danger > 4.2) {
return Number.NEGATIVE_INFINITY;
}
const dist = Math.abs(targetEnemy.x - x) + Math.abs(targetEnemy.y - y);
let score = 6 - dist * 1.1 - depth * 0.4 - danger * 1.3;
if (countAdjacentCrates(x, y) > 0) {
score += 0.35;
}
return score;
},
8,
);
}
if (!desiredDir) {
desiredDir = botFindDirection(
player,
(x, y, depth) => {
const crates = countAdjacentCrates(x, y);
if (crates <= 0) {
return Number.NEGATIVE_INFINITY;
}
return crates * 2.2 - depth * 0.55 - estimateDangerAt(x, y) * 1.4;
},
8,
);
}
if (!desiredDir) {
const options = [];
const dirs = shuffle([...DIRECTIONS]);
for (const dir of dirs) {
for (const dir of shuffle([...DIRECTIONS])) {
const nx = player.x + dir.dx;
const ny = player.y + dir.dy;
if (!canEnterTile(nx, ny, player)) {
continue;
}
const danger = estimateDangerAt(nx, ny);
let score = Math.random() * 0.35 - danger * 1.65;
const nearestEnemy = nearestEnemyDistance(nx, ny, player.id);
let score = Math.random() * 0.18 - danger * 1.8 - nearestEnemy * 0.16;
if (danger < currentDanger) {
score += 2.5;
}
const tile = state.map[ny][nx];
if (tile.powerup) {
score += 1.6;
}
if (adjacentCrate(nx, ny)) {
score += 0.3;
score += 0.25;
}
if (targetEnemy) {
const nowDist = Math.abs(targetEnemy.x - player.x) + Math.abs(targetEnemy.y - player.y);
const newDist = Math.abs(targetEnemy.x - nx) + Math.abs(targetEnemy.y - ny);
score += (nowDist - newDist) * 0.45;
const tile = state.map[ny][nx];
if (tile.powerup) {
score += 1.4;
}
options.push({ dir: dir.key, score });
}
options.sort((a, b) => b.score - a.score);
player.ai.desiredDir = options.length > 0 ? options[0].dir : null;
desiredDir = options.length > 0 ? options[0].dir : null;
}
player.ai.desiredDir = desiredDir;
}
function registerMultiplayerKill(killerPlayerId, victimPlayerId) {