30ea2f42c2
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
82 lines
2.1 KiB
TypeScript
82 lines
2.1 KiB
TypeScript
import type { Position } from '@dflike/shared';
|
|
import type { GameMap } from './GameMap.js';
|
|
|
|
interface Node {
|
|
x: number;
|
|
y: number;
|
|
g: number;
|
|
h: number;
|
|
f: number;
|
|
parent: Node | null;
|
|
}
|
|
|
|
function heuristic(a: Position, b: Position): number {
|
|
return Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
}
|
|
|
|
const NEIGHBORS = [
|
|
{ dx: 0, dy: -1 },
|
|
{ dx: 0, dy: 1 },
|
|
{ dx: -1, dy: 0 },
|
|
{ dx: 1, dy: 0 },
|
|
];
|
|
|
|
export function findPath(map: GameMap, start: Position, goal: Position): Position[] | null {
|
|
if (start.x === goal.x && start.y === goal.y) return [];
|
|
|
|
const open: Node[] = [];
|
|
const closed = new Set<string>();
|
|
const key = (x: number, y: number) => `${x},${y}`;
|
|
|
|
const startNode: Node = {
|
|
x: start.x, y: start.y,
|
|
g: 0, h: heuristic(start, goal), f: heuristic(start, goal),
|
|
parent: null,
|
|
};
|
|
open.push(startNode);
|
|
|
|
while (open.length > 0) {
|
|
// Find lowest f
|
|
let bestIdx = 0;
|
|
for (let i = 1; i < open.length; i++) {
|
|
if (open[i].f < open[bestIdx].f) bestIdx = i;
|
|
}
|
|
const current = open.splice(bestIdx, 1)[0];
|
|
|
|
if (current.x === goal.x && current.y === goal.y) {
|
|
// Reconstruct path (excluding start)
|
|
const path: Position[] = [];
|
|
let node: Node | null = current;
|
|
while (node && !(node.x === start.x && node.y === start.y)) {
|
|
path.push({ x: node.x, y: node.y });
|
|
node = node.parent;
|
|
}
|
|
return path.reverse();
|
|
}
|
|
|
|
closed.add(key(current.x, current.y));
|
|
|
|
for (const { dx, dy } of NEIGHBORS) {
|
|
const nx = current.x + dx;
|
|
const ny = current.y + dy;
|
|
if (!map.isWalkable(nx, ny) || closed.has(key(nx, ny))) continue;
|
|
|
|
const g = current.g + 1;
|
|
const h = heuristic({ x: nx, y: ny }, goal);
|
|
const existing = open.find(n => n.x === nx && n.y === ny);
|
|
|
|
if (existing) {
|
|
if (g < existing.g) {
|
|
existing.g = g;
|
|
existing.f = g + h;
|
|
existing.parent = current;
|
|
}
|
|
} else {
|
|
open.push({ x: nx, y: ny, g, h, f: g + h, parent: current });
|
|
}
|
|
}
|
|
}
|
|
|
|
return null; // No path found
|
|
}
|