fix(pathfinding): use real cost in A* instead of greedy heuristic

updateNeighborNode overwrote gScore with the heuristic, so A* behaved like greedy best-first search and found longer paths than Dijkstra.
This commit is contained in:
lobo committed 2026-10-14 15:40:20 +02:00
1 parent 331d653ae6
commit ce7db0f376
2 files changed
+24 -1

No files matched your search

@@ -117,6 +117,30 @@ describe('PathfindingService', () => {
expect(nodesInShortestPathOrder[0]).toBe(start);
expect(nodesInShortestPathOrder[nodesInShortestPathOrder.length - 1]).toBe(end);
});
it('finds a path as short as Dijkstra on random grids', () => {
let seed = 12345;
const random = (): number => (seed = (seed * 1664525 + 1013904223) % 4294967296) / 4294967296;
for (let run = 0; run < 20; run++) {
const build = (): Node[][] => {
seed = 1000 + run;
const grid = createGrid(20, 20);
for (const node of grid.flat()) {
node.isWall = random() < 0.3;
}
grid[0][0].isWall = false;
grid[19][19].isWall = false;
return grid;
};
const astarGrid = build();
const dijkstraGrid = build();
const astar = service.aStar(astarGrid, astarGrid[0][0], astarGrid[19][19]);
const dijkstra = service.dijkstra(dijkstraGrid, dijkstraGrid[0][0], dijkstraGrid[19][19]);
expect(astar.nodesInShortestPathOrder.length).toBe(dijkstra.nodesInShortestPathOrder.length);
}
});
});
describe('findPath', () => {
@@ -155,7 +155,6 @@ export class PathfindingService {
private updateNeighborNode(neighbor: Node, currentNode: Node, tentativeGScore: number, endNode: Node, openSet: Node[]) {
neighbor.linkedNode = currentNode;
neighbor.nodeData = tentativeGScore;
neighbor['nodeData'] = this.calculateHeuristic(neighbor, endNode);
neighbor['hScore'] = this.calculateHeuristic(neighbor, endNode);
neighbor['fScore'] = neighbor.nodeData + neighbor['hScore'];