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
|
.yarn/install-state.gz
|
||||||
.pnp.*
|
.pnp.*
|
||||||
|
|
||||||
|
highscores.json
|
||||||
|
|||||||
247
src/game.js
247
src/game.js
@ -1452,23 +1452,123 @@ function adjacentCrate(x, y) {
|
|||||||
return false;
|
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) {
|
for (const other of state.players) {
|
||||||
if (!other.alive || other.id === player.id) {
|
if (!other.alive || other.id === player.id) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (player.x !== other.x && player.y !== other.y) {
|
if (x !== other.x && y !== other.y) {
|
||||||
continue;
|
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) {
|
if (distance > player.flameRange) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!hasHardBlockBetween(player.x, player.y, other.x, other.y)) {
|
if (!hasHardBlockBetween(x, y, other.x, other.y)) {
|
||||||
return true;
|
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) {
|
function updateBot(player, dt) {
|
||||||
@ -1481,63 +1581,112 @@ function updateBot(player, dt) {
|
|||||||
if (player.ai.thinkTimer > 0) {
|
if (player.ai.thinkTimer > 0) {
|
||||||
return;
|
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 currentDanger = estimateDangerAt(player.x, player.y);
|
||||||
const liveEnemies = state.players.filter((p) => p.alive && p.id !== player.id);
|
const currentKey = tileKey(player.x, player.y);
|
||||||
const targetEnemy = liveEnemies.sort(
|
const targetEnemy = state.players
|
||||||
(a, b) =>
|
.filter((p) => p.alive && p.id !== player.id)
|
||||||
Math.abs(a.x - player.x) + Math.abs(a.y - player.y) -
|
.sort(
|
||||||
(Math.abs(b.x - player.x) + Math.abs(b.y - player.y)),
|
(a, b) =>
|
||||||
)[0];
|
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 (
|
if (
|
||||||
player.bombsPlaced < player.bombCapacity &&
|
player.bombsPlaced < player.bombCapacity &&
|
||||||
player.ai.bombCooldown <= 0 &&
|
player.ai.bombCooldown <= 0 &&
|
||||||
currentDanger < 0.9 &&
|
currentDanger < 0.8 &&
|
||||||
(adjacentCrate(player.x, player.y) || enemyInBlastPotential(player)) &&
|
hasEscapeRouteAfterBomb(player) &&
|
||||||
hasEscapeRouteAfterBomb(player)
|
(enemyPressure > 0 || cratePressure >= 2 || (cratePressure >= 1 && Math.random() < 0.45))
|
||||||
) {
|
) {
|
||||||
if (dropBomb(player)) {
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const options = [];
|
let desiredDir = botFindDirection(
|
||||||
const dirs = shuffle([...DIRECTIONS]);
|
player,
|
||||||
for (const dir of dirs) {
|
(x, y, depth) => {
|
||||||
const nx = player.x + dir.dx;
|
const tile = state.map[y][x];
|
||||||
const ny = player.y + dir.dy;
|
if (!tile || !tile.powerup) {
|
||||||
if (!canEnterTile(nx, ny, player)) {
|
return Number.NEGATIVE_INFINITY;
|
||||||
continue;
|
}
|
||||||
}
|
return 8 - depth - estimateDangerAt(x, y) * 1.8;
|
||||||
const danger = estimateDangerAt(nx, ny);
|
},
|
||||||
let score = Math.random() * 0.35 - danger * 1.65;
|
7,
|
||||||
|
);
|
||||||
|
|
||||||
if (danger < currentDanger) {
|
if (!desiredDir && targetEnemy) {
|
||||||
score += 2.5;
|
desiredDir = botFindDirection(
|
||||||
}
|
player,
|
||||||
|
(x, y, depth) => {
|
||||||
const tile = state.map[ny][nx];
|
const danger = estimateDangerAt(x, y);
|
||||||
if (tile.powerup) {
|
if (danger > 4.2) {
|
||||||
score += 1.6;
|
return Number.NEGATIVE_INFINITY;
|
||||||
}
|
}
|
||||||
|
const dist = Math.abs(targetEnemy.x - x) + Math.abs(targetEnemy.y - y);
|
||||||
if (adjacentCrate(nx, ny)) {
|
let score = 6 - dist * 1.1 - depth * 0.4 - danger * 1.3;
|
||||||
score += 0.3;
|
if (countAdjacentCrates(x, y) > 0) {
|
||||||
}
|
score += 0.35;
|
||||||
|
}
|
||||||
if (targetEnemy) {
|
return score;
|
||||||
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);
|
8,
|
||||||
score += (nowDist - newDist) * 0.45;
|
);
|
||||||
}
|
|
||||||
|
|
||||||
options.push({ dir: dir.key, score });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
options.sort((a, b) => b.score - a.score);
|
if (!desiredDir) {
|
||||||
player.ai.desiredDir = options.length > 0 ? options[0].dir : null;
|
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 = [];
|
||||||
|
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);
|
||||||
|
const nearestEnemy = nearestEnemyDistance(nx, ny, player.id);
|
||||||
|
let score = Math.random() * 0.18 - danger * 1.8 - nearestEnemy * 0.16;
|
||||||
|
if (danger < currentDanger) {
|
||||||
|
score += 1.6;
|
||||||
|
}
|
||||||
|
if (adjacentCrate(nx, ny)) {
|
||||||
|
score += 0.25;
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
desiredDir = options.length > 0 ? options[0].dir : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.ai.desiredDir = desiredDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerMultiplayerKill(killerPlayerId, victimPlayerId) {
|
function registerMultiplayerKill(killerPlayerId, victimPlayerId) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user