💾 Archived View for gem.sdf.org › s.kaplan › cheatsheets › data-structures-and-algorithms › a_star.m… captured on 2024-06-16 at 12:43:58.
⬅️ Previous capture (2023-09-08)
-=-=-=-=-=-=-
# A* Algorithm Implementation Cheatsheet ## Overview A* algorithm is an informed search algorithm that finds the shortest path between two given nodes on a graph. It uses a heuristic function to estimate the cost of reaching the goal from the current node. A* algorithm is guaranteed to find the shortest path if the heuristic is admissible and consistent. ## Implementation 1. Define the heuristic function that estimates the cost of reaching the goal from the current node. 2. Define the data structures to be used: `open_list`, `closed_list`, `g_score`, `h_score`, and `f_score`. 3. Add the starting node to the `open_list` with an initial `g_score` of 0 and `h_score` calculated using the heuristic function. 4. While the `open_list` is not empty, select the node with the lowest `f_score` and remove it from `open_list`. 5. If the selected node is the goal, return the path. 6. Add the selected node to the `closed_list`. 7. For each neighboring node of the selected node, calculate its tentative `g_score` and `h_score` using the heuristic function. 8. If the neighboring node is already in the `closed_list`, skip it. 9. If the neighboring node is not in the `open_list`, add it to the `open_list`. 10. If the neighboring node is already in the `open_list` and the tentative `g_score` is greater than or equal to its current `g_score`, skip it. Otherwise, update the `g_score`, `h_score`, and `f_score` of the neighboring node and add it to the `open_list`. 11. Repeat steps 4-10 until the `open_list` is empty. ## Python Example
import heapq
def heuristic(a, b):
return abs(b[0] - a[0]) + abs(b[1] - a[1])
def astar(array, start, goal):
neighbors = [(0,1),(0,-1),(1,0),(-1,0),(1,1),(1,-1),(-1,1),(-1,-1)]
close_set = set()
came_from = {}
gscore = {start:0}
fscore = {start:heuristic(start, goal)}
oheap = []
heapq.heappush(oheap, (fscore[start], start))
while oheap:
current = heapq.heappop(oheap)[1]
if current == goal:
data = []
while current in came_from:
data.append(current)
current = came_from[current]
return data
close_set.add(current)
for i, j in neighbors:
neighbor = current[0] + i, current[1] + j
tentative_g_score = gscore[current] + heuristic(current, neighbor)
if 0 <= neighbor[0] < array.shape[0]:
if 0 <= neighbor[1] < array.shape[1]:
if array[neighbor[0]][neighbor[1]] == 1:
continue
else:
continue
else:
continue
if neighbor in close_set and tentative_g_score >= gscore.get(neighbor, 0):
continue
if tentative_g_score < gscore.get(neighbor, 0) or neighbor not in [i[1]for i in oheap]:
came_from[neighbor] = current
gscore[neighbor] = tentative_g_score
fscore[neighbor] = tentative_g_score + heuristic(neighbor, goal)
heapq.heappush(oheap, (fscore[neighbor], neighbor))
return None
## C++ Example
using namespace std;
typedef pair<int, int> pii;
const int INF = 1e9;
int heuristic(pii a, pii b) {
return abs(b.first - a.first) + abs(b.second - a.second);
}
vector<pii> astar(vector<vector<int>>& grid, pii start, pii goal) {
int n = grid.size(), m = grid[0].size();
vector<pii> dirs = {% raw %}{{0,1}, {0,-1}, {1,0}, {-1,0}, {1,1}, {1,-1}, {-1,1}, {-1,-1}}{% endraw %};
priority_queue<pii, vector<pii>, greater<pii>> pq;
unordered_map<int, unordered_map<int, int>> gscore;
unordered_map<int, unordered_map<int, int>> fscore;
unordered_map<int, unordered_map<int, pii>> came_from;
set<pii> closed_set;
gscore[start.first][start.second] = 0;
fscore[start.first][start.second] = heuristic(start, goal);
pq.push({fscore[start.first][start.second], (start.first << 16) | start.second});
while (!pq.empty()) {
auto curr = pq.top(); pq.pop();
int x = curr.second >> 16, y = curr.second & 0xFFFF;
if (make_pair(x, y) == goal) {
vector<pii> path;
while (x != start.first || y != start.second) {
path.push_back({x, y});
int tmp_x = x, tmp_y = y;
x = came_from[tmp_x][tmp_y].first;
y = came_from[tmp_x][tmp_y].second;
}
path.push_back({x, y});
reverse(path.begin(), path.end());
return path;
}
closed_set.insert({x, y});
for (auto dir : dirs) {
int nx = x + dir.first, ny = y + dir.second;
if (nx < 0 || nx >= n || ny < 0 || ny >= m || grid[nx][ny] == 1) continue;
if (closed_set.count({nx, ny})) continue;
int tentative_gscore = gscore[x][y] + heuristic({x, y}, {nx, ny});
if (!gscore.count(nx) || !gscore[nx].count(ny) || tentative_gscore < gscore[nx][ny]) {
came_from[nx][ny] = {x, y};
gscore[nx][ny] = tentative_gscore;
fscore[nx][ny] = tentative_gscore + heuristic({nx, ny}, goal);
pq.push({fscore[nx][ny], (nx << 16) | ny});
}
}
}
return {};
}
int main() {
vector<vector<int>> grid = {
{0, 0, 0, 0, 0},
{0, 1, 1, 1, 0},
{0, 1, 0, 0, 0},
{0, 1, 0, 1, 0},
{0, 0, 0, 1, 0}
};
pii start = {0, 0}, goal = {4, 4};
auto path = astar(grid, start, goal);
for (auto p : path) {
cout << "(" << p.first << ", " << p.second << ") ";
}
cout << endl;
return 0;
}
## Resources - [A* Search Algorithm](https://en.wikipedia.org/wiki/A*_search_algorithm): Wikipedia page on A* algorithm - [Introduction to A* Pathfinding](https://www.redblobgames.com/pathfinding/a-star/introduction.html): Red Blob Games tutorial on A* algorithm - [A* Search Algorithm](https://www.geeksforgeeks.org/a-search-algorithm/): GeeksforGeeks tutorial on A* algorithm - [Pathfinding with A*](https://theory.stanford.edu/~amitp/GameProgramming/): Amit’s A* Pages with detailed explanations and interactive examples - [A* Pathfinding for Beginners](http://www.ai-junkie.com/astar/astar1.html): AI Junkie tutorial on A* algorithm with code samples - [A* Pathfinding Visualization Tool](https://qiao.github.io/PathFinding.js/visual/): Pathfinding.js Visualizer for A* algorithm - [A* Pathfinding for Beginners - Introduction to Pathfinding](https://www.youtube.com/watch?v=-L-WgKMFuhE): YouTube video tutorial on A* algorithm by Sebastian Lague