📝 What You’ll Learn
- What the A* pathfinding algorithm is
- How it combines Dijkstra’s and Greedy Best-First Search
- How to implement it in JavaScript
- Real-world analogy and use cases
- Heuristic functions and performance best practices
🔍 What Is the A* Algorithm?
A* (pronounced A-star) is a graph traversal and pathfinding algorithm that finds the shortest path from a start node to a goal node.
It combines the cost from the start (known as g
) and a heuristic estimate of the cost to the goal (known as h
) to form a total score f = g + h
.
Unlike Dijkstra’s algorithm, which blindly expands all nearby paths, A* uses heuristics to prioritize nodes that look promising.
🧭 When Should You Use A*?
- When you need the shortest path on a map or grid
- When performance matters and you're not okay with brute-force search
- In real-time games, robotics, routing systems, etc.
📦 Core Concepts
Term | Meaning |
---|---|
g |
Cost from start to current node |
h |
Heuristic estimate to goal |
f = g + h |
Total estimated cost of path through node |
Open Set | Nodes to evaluate (priority queue or array) |
Closed Set | Already evaluated nodes |
💡 Heuristic: Manhattan Distance
For grid-based movement (no diagonals), the common heuristic is Manhattan distance:
function heuristic(a, b) {
return Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
}
This ensures admissibility — meaning it never overestimates — which guarantees optimality.
🧱 JavaScript Implementation (Grid Example)
function aStar(start, end, grid) {
const openSet = [start];
const closedSet = new Set();
start.g = 0;
start.f = heuristic(start, end);
while (openSet.length > 0) {
openSet.sort((a, b) => a.f - b.f);
const current = openSet.shift();
if (current === end) {
return reconstructPath(current);
}
closedSet.add(current);
for (const neighbor of getNeighbors(current, grid)) {
if (neighbor.wall || closedSet.has(neighbor)) continue;
const tentativeG = current.g + 1;
if (!openSet.includes(neighbor) || tentativeG < neighbor.g) {
neighbor.g = tentativeG;
neighbor.f = neighbor.g + heuristic(neighbor, end);
neighbor.previous = current;
if (!openSet.includes(neighbor)) {
openSet.push(neighbor);
}
}
}
}
return []; // no path found
}
function reconstructPath(node) {
const path = [];
while (node) {
path.unshift(node);
node = node.previous;
}
return path;
}
🚧 Real-World Analogy
Imagine you're navigating a city using a map.
g
is how far you've walked.h
is a straight-line guess to your destination.- A* balances both, helping you choose the route that looks fastest and actually is.
⚠️ Common Pitfalls
- ❌ Using an inadmissible heuristic (overestimates cost) — breaks optimality
- ❌ Forgetting to update a node’s
g
when a better path is found - ❌ Not using a priority queue — sorting openSet each loop is inefficient
✅ Best Practices
- Use a min-heap or binary heap for the openSet for large grids
- Always ensure the heuristic is admissible and consistent
- Visualize openSet/closedSet/path when debugging
- For weighted grids, adjust
g
accordingly
🎯 Use Cases
- Game AI (NPC pathfinding)
- Robot motion planning
- Map-based navigation (e.g. GPS)
- Puzzle solving (e.g. 8-puzzle)
📘 Recap
- A* finds the shortest path using
f = g + h
- It combines Dijkstra’s algorithm with a heuristic
- It's efficient and optimal (with the right heuristic)
- Ideal for grid navigation, games, and intelligent agents