A* Search Algorithm in Python

Last Updated : 22 Sep, 2026

The A* search algorithm is a pathfinding algorithm used to find the shortest path between a starting point and a destination. It combines the actual cost of reaching a cell with a heuristic estimate of the remaining distance.

Examples: Consider a grid where 1 represents an unblocked cell and 0 represents a blocked cell.

Input:
Start: (8, 0)
Destination: (0, 0)
Output:
(8, 0) → (7, 0) → (6, 0) → (5, 0) → (4, 1) → (3, 2) → (2, 1) → (1, 0) → (0, 0)

Explanation:
A* searches through the available cells and selects the path with the lowest estimated total cost from the source to the destination.

How A* Search Algorithm Works?

A* calculates three values for each cell:

  • g: Cost of reaching the current cell from the source.
  • h: Estimated cost from the current cell to the destination.
  • f: Total estimated cost, calculated as f = g + h.

The algorithm selects the cell with the lowest f value and continues searching until the destination is reached.

Approach

The algorithm uses a priority queue to process the cell with the lowest f value first.

  • Create a priority queue and add the source cell.
  • Calculate the g, h, and f values for each reachable cell.
  • Select the cell with the lowest f value.
  • Check its neighboring cells.
  • Update a neighbor when a lower-cost path is found.
  • Store the parent of each cell to reconstruct the final path.
  • Stop when the destination is reached.

Step-by-Step Implementation

Step 1: Import Required Modules and Define the Cell Class

The Cell class stores the f, g, h values and parent position for each grid cell.

Python
import heapq
class Cell:
    def __init__(self):
        self.parent_i = 0
        self.parent_j = 0
        self.f = float('inf')
        self.g = float('inf')
        self.h = 0

Explanation:

  • heapq is used to create the priority queue.
  • Cell stores the f, g, and h values for each cell.
  • parent_i and parent_j store the position of the previous cell.

Step 2: Define Grid Helper Functions

These functions check valid cells, blocked cells, destination cells, and calculate the heuristic value.

Python
ROW = 9
COL = 10

def is_valid(row, col):
    return 0 <= row < ROW and 0 <= col < COL

def is_unblocked(grid, row, col):
    return grid[row][col] == 1

def is_destination(row, col, dest):
    return row == dest[0] and col == dest[1]

def calculate_h_value(row, col, dest):
    return ((row - dest[0]) ** 2 + (col - dest[1]) ** 2) ** 0.5

Explanation:

  • is_valid() checks whether a cell is inside the grid.
  • is_unblocked() checks whether the cell can be visited.
  • is_destination() checks whether the current cell is the destination.
  • calculate_h_value() calculates the estimated distance to the destination.

Step 3: Reconstruct the Path

The trace_path() function follows the parent cells from the destination to the source and reverses the path.

Python
def trace_path(cell_details, dest):
    path = []
    row, col = dest

    while not (
        cell_details[row][col].parent_i == row
        and cell_details[row][col].parent_j == col
    ):
        path.append((row, col))
        row = cell_details[row][col].parent_i
        col = cell_details[row][col].parent_j

    path.append((row, col))
    path.reverse()

    print("The Path is:")
    for cell in path:
        print("->", cell, end=" ")
    print()

Explanation:

  • The function starts from the destination cell.
  • It follows the parent cells until it reaches the source.
  • reverse() changes the path from source-to-destination order.

The a_star_search() function validates the source and destination and initializes the visited cells and priority queue.

Python
def a_star_search(grid, src, dest):

    if not is_valid(src[0], src[1]) or not is_valid(dest[0], dest[1]):
        print("Source or destination is invalid")
        return

    if not is_unblocked(grid, src[0], src[1]) or not is_unblocked(grid, dest[0], dest[1]):
        print("Source or destination is blocked")
        return

    if is_destination(src[0], src[1], dest):
        print("We are already at the destination")
        return

    closed_list = [[False for _ in range(COL)] for _ in range(ROW)]
    cell_details = [[Cell() for _ in range(COL)] for _ in range(ROW)]

    i, j = src

    cell_details[i][j].f = 0
    cell_details[i][j].g = 0
    cell_details[i][j].h = 0
    cell_details[i][j].parent_i = i
    cell_details[i][j].parent_j = j

    open_list = []
    heapq.heappush(open_list, (0.0, i, j))

Explanation:

  • The function first checks whether the source and destination are valid and unblocked.
  • closed_list keeps track of visited cells.
  • cell_details stores information about every cell.
  • The source cell is added to the priority queue with an initial cost of 0.

Step 5: Explore Neighboring Cells

The algorithm removes the cell with the lowest f value and checks its valid neighboring cells.

Python
    directions = [
        (0, 1), (0, -1), (1, 0), (-1, 0),
        (1, 1), (1, -1), (-1, 1), (-1, -1)
    ]

    while open_list:
        _, i, j = heapq.heappop(open_list)

        if closed_list[i][j]:
            continue

        closed_list[i][j] = True

        for di, dj in directions:
            new_i = i + di
            new_j = j + dj

            if not is_valid(new_i, new_j):
                continue

            if not is_unblocked(grid, new_i, new_j):
                continue

            if closed_list[new_i][new_j]:
                continue

Explanation:

  • The algorithm removes the cell with the lowest f value from the priority queue.
  • It marks the selected cell as visited.
  • The eight possible neighboring directions are checked.
  • Invalid, blocked, or already visited cells are skipped.

Step 6: Calculate Costs and Update the Queue

For each valid neighbor, the algorithm calculates g, h, and f values and adds better paths to the priority queue.

Python
            if is_destination(new_i, new_j, dest):
                cell_details[new_i][new_j].parent_i = i
                cell_details[new_i][new_j].parent_j = j

                print("The destination cell is found")
                trace_path(cell_details, dest)
                return

            g_new = cell_details[i][j].g + 1.0
            h_new = calculate_h_value(new_i, new_j, dest)
            f_new = g_new + h_new

            if cell_details[new_i][new_j].f > f_new:
                cell_details[new_i][new_j].f = f_new
                cell_details[new_i][new_j].g = g_new
                cell_details[new_i][new_j].h = h_new

                cell_details[new_i][new_j].parent_i = i
                cell_details[new_i][new_j].parent_j = j

                heapq.heappush(
                    open_list,
                    (f_new, new_i, new_j)
                )

    print("Failed to find the destination cell")

Explanation:

  • If a neighboring cell is the destination, its parent is stored and the path is reconstructed.
  • g_new represents the cost from the source to the current cell.
  • h_new represents the estimated cost to the destination.
  • f_new = g_new + h_new gives the total estimated cost.
  • If a better path is found, the cell information and priority queue are updated.

The grid, source, and destination are defined and passed to the A* search function.

Python
def main():
    grid = [
        [1, 0, 1, 1, 1, 1, 0, 1, 1, 1],
        [1, 1, 1, 0, 1, 1, 1, 0, 1, 1],
        [1, 1, 1, 0, 1, 1, 0, 1, 0, 1],
        [0, 0, 1, 0, 1, 0, 0, 0, 0, 1],
        [1, 1, 1, 0, 1, 1, 1, 0, 1, 0],
        [1, 0, 1, 1, 1, 1, 0, 1, 0, 0],
        [1, 0, 0, 0, 0, 1, 0, 0, 0, 1],
        [1, 0, 1, 1, 1, 1, 0, 1, 1, 1],
        [1, 1, 1, 0, 0, 0, 1, 0, 0, 1]
    ]

    src = [8, 0]
    dest = [0, 0]

    a_star_search(grid, src, dest)


if __name__ == "__main__":
    main()

Output:

The destination cell is found
The Path is
-> (8, 0) -> (7, 0) -> (6, 0) -> (5, 0) -> (4, 1) -> (3, 2) -> (2, 1) -> (1, 0) -> (0, 0)

Explanation:

  • The grid uses 1 for unblocked cells and 0 for blocked cells.
  • src defines the starting cell and dest defines the destination cell.
  • a_star_search() runs the A* algorithm using the given grid and positions.
Comment