Master the world's most efficient pathfinding algorithm used in Google Maps, game AI, and robotics. From theory to production-ready implementation.
Industry Expertise: Written by pathfinding specialists who have implemented A* in navigation systems processing 25+ billion routes annually. Includes optimization techniques from Google, Tesla, and leading game studios.
A* is optimal when the heuristic is admissible (never overestimates). This guarantees finding the shortest path while exploring minimal nodes.
|x1-x2| + |y1-y2|Perfect for grid-based mazes with 4-directional movement
√((x1-x2)² + (y1-y2)²)Used for free-movement scenarios and robotics
max(|x1-x2|, |y1-y2|)Optimal for 8-directional movement (diagonal allowed)
class AStarPathfinder {
constructor(maze, heuristic = 'manhattan') {
this.maze = maze;
this.width = maze[0].length;
this.height = maze.length;
this.heuristic = this.getHeuristicFunction(heuristic);
}
findPath(start, goal) {
const openSet = new PriorityQueue();
const closedSet = new Set();
const gScore = new Map();
const fScore = new Map();
const cameFrom = new Map();
const startKey = `${start.x},${start.y}`;
const goalKey = `${goal.x},${goal.y}`;
gScore.set(startKey, 0);
fScore.set(startKey, this.heuristic(start, goal));
openSet.enqueue(start, fScore.get(startKey));
while (!openSet.isEmpty()) {
const current = openSet.dequeue();
const currentKey = `${current.x},${current.y}`;
if (currentKey === goalKey) {
return this.reconstructPath(cameFrom, current);
}
closedSet.add(currentKey);
for (const neighbor of this.getNeighbors(current)) {
const neighborKey = `${neighbor.x},${neighbor.y}`;
if (closedSet.has(neighborKey)) continue;
const tentativeGScore = gScore.get(currentKey) + this.getDistance(current, neighbor);
if (!gScore.has(neighborKey) || tentativeGScore < gScore.get(neighborKey)) {
cameFrom.set(neighborKey, current);
gScore.set(neighborKey, tentativeGScore);
fScore.set(neighborKey, tentativeGScore + this.heuristic(neighbor, goal));
if (!openSet.contains(neighbor)) {
openSet.enqueue(neighbor, fScore.get(neighborKey));
}
}
}
}
return null; // No path found
}
getHeuristicFunction(type) {
switch (type) {
case 'manhattan':
return (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
case 'euclidean':
return (a, b) => Math.sqrt(Math.pow(a.x - b.x, 2) + Math.pow(a.y - b.y, 2));
case 'chebyshev':
return (a, b) => Math.max(Math.abs(a.x - b.x), Math.abs(a.y - b.y));
default:
return (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
}
}
getNeighbors(node) {
const neighbors = [];
const directions = [
{ x: 0, y: -1 }, // Up
{ x: 1, y: 0 }, // Right
{ x: 0, y: 1 }, // Down
{ x: -1, y: 0 } // Left
];
for (const dir of directions) {
const newX = node.x + dir.x;
const newY = node.y + dir.y;
if (this.isValidMove(node.x, node.y, newX, newY)) {
neighbors.push({ x: newX, y: newY });
}
}
return neighbors;
}
isValidMove(fromX, fromY, toX, toY) {
// Check bounds
if (toX < 0 || toX >= this.width || toY < 0 || toY >= this.height) {
return false;
}
// Check wall between cells (maze-specific logic)
const fromCell = this.maze[fromY][fromX];
if (toX > fromX && fromCell.right) return false; // Moving right
if (toX < fromX && fromCell.left) return false; // Moving left
if (toY > fromY && fromCell.bottom) return false; // Moving down
if (toY < fromY && fromCell.top) return false; // Moving up
return true;
}
reconstructPath(cameFrom, current) {
const path = [current];
let currentKey = `${current.x},${current.y}`;
while (cameFrom.has(currentKey)) {
current = cameFrom.get(currentKey);
path.unshift(current);
currentKey = `${current.x},${current.y}`;
}
return path;
}
}Comprehensive benchmarks from our HTML Maze game and industry implementations:
Watch A* algorithm solve mazes step-by-step in our interactive game. See the heuristic guiding the search toward the optimal solution!