Improve bot (try)
This commit is contained in:
parent
86ae86febc
commit
cfeda33d33
1
.gitignore
vendored
1
.gitignore
vendored
@ -130,3 +130,4 @@ dist
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
|
||||
highscores.json
|
||||
|
||||
211
src/game.js
211
src/game.js
@ -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) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user