top of page

Learn, Explore & Get Support from Freelance Experts

Welcome to Colabcodes, where innovation drives technology forward. Explore the latest trends, practical programming tutorials, and in-depth insights across software development, AI, ML, NLP and more. Connect with our experienced freelancers and mentors for personalised guidance and support tailored to your needs.

Coding expert help blog - colabcodes

Exploring the A* Search Algorithm with Python

  • Writer: Samul Black
    Samul Black
  • Aug 19, 2024
  • 7 min read

Updated: Aug 7

The A* search algorithm is one of the most widely used algorithms for pathfinding and graph traversal. It combines the strengths of Dijkstra’s algorithm and Greedy Best-First Search, making it highly effective for finding the shortest path from a start node to a goal node in a weighted graph. In this blog, we’ll dive into the details of the A* algorithm, understand its components, and walk through its implementation in Python.


A* Search Algorithm with Python - colabcodes

A* Search Algorithm: Heuristic Pathfinding Explained

Finding the most efficient path between two points is a common problem in computer science, especially in fields like artificial intelligence, game development, robotics, and logistics. Among the many algorithms designed to solve this challenge, the A* (A-Star) Search Algorithm stands out for its balance of accuracy and efficiency.

A* is an informed search algorithm, meaning it uses additional information (a heuristic) to decide which paths to explore. This results in faster solutions compared to uninformed algorithms like BFS or DFS, especially in large search spaces.

A* determines the best path by evaluating a cost function for each possible step. This function combines the actual cost of reaching a node and the estimated cost to the goal.


f(n) = g(n)+h(n)


The algorithm always explores the node with the lowest f(n) value next, prioritizing paths that seem most promising. This strategy helps avoid unnecessary detours, making A* both optimal and efficient—provided the heuristic function is well-designed.

Before diving deeper, it's important to understand the three key components in the A* cost function:


  • g(n): The cost of the path from the start node to the current node n. This is the actual distance (or time, or cost) accumulated so far.

  • h(n): The heuristic estimate of the cost from node n to the goal. This must be estimated intelligently using domain knowledge.

  • f(n): The total estimated cost of the path through n, which is the sum of g(n) and h(n).


Key Concepts in A* Search

To understand A* at a deeper level, it's useful to explore the key concepts that make the algorithm effective. These include how it uses heuristics, how it manages the nodes it needs to evaluate, and what data structures are involved.


1. Heuristic Function (h)

The heuristic function is central to A*’s ability to make intelligent decisions during the search process. It provides an estimate of how far the current node is from the goal. The better this estimate, the fewer nodes A* will need to explore.

A good heuristic should be:


  • Admissible: It never overestimates the actual cost to reach the goal.

  • Consistent (Monotonic): The estimated cost from the current node should not exceed the cost of moving to a neighbor plus the estimated cost from that neighbor to the goal.


Examples of Heuristics:


  • Manhattan Distance: Common in grid-based systems where movement is limited to horizontal and vertical directions.

  • Euclidean Distance: Used when diagonal movement is allowed.

  • Hamming Distance: Used in puzzles like the 8-puzzle to count mismatched tiles.


2. Priority Queue (Open List)

To manage which node to explore next, A* uses a priority queue, typically implemented with a min-heap. This allows it to efficiently retrieve the node with the lowest f(n) value at each step.

The open list is a dynamic collection of nodes that have been discovered but not yet evaluated. The algorithm continuously pulls nodes from this list based on priority, ensuring the most promising paths are explored first.


3. Open and Closed Lists

A* maintains two main collections during execution:


  • Open List: Nodes that are discovered and queued for evaluation.

  • Closed List: Nodes that have already been visited and evaluated. Once a node is in the closed list, it will not be processed again.


This dual-list approach prevents redundant processing and ensures optimal performance by avoiding revisiting already explored nodes.


A* Algorithm Steps

Let’s break down the core steps of the A* algorithm. These form the basic operational structure that guides the search from start to goal.


1. Initialization

The algorithm starts by placing the start node into the open list. At this point:


  • The cost to reach the start node g(n) is zero.

  • The heuristic h(n) is calculated using the heuristic function.

  • The total cost f(n) is also calculated.


The closed list is initialized as empty. It will hold nodes that have already been evaluated.


2. Main Loop

The algorithm then enters a loop that continues until the goal is reached or the open list is empty.


Step-by-step actions:


  • Remove the node with the lowest f(n) from the open list.

  • If this node is the goal, reconstruct the path and return it.

  • Otherwise, generate all valid neighbors of the current node.

  • For each neighbor:

    • Calculate g(n) as the cost from start to the neighbor.

    • Calculate h(n) using the heuristic.

    • Compute f(n) = g(n) + h(n).

    • If the neighbor is already in the closed list with a lower f(n), skip it.

    • If it’s new or has a better score, update it and add it to the open list.


3. Termination

The algorithm stops when:


  • The goal node is reached, and the path can be reconstructed from the stored parent pointers.

  • The open list becomes empty, meaning there is no path to the goal. This can happen in cases where obstacles block all possible routes.


Applications of A* Algorithm

A* is one of the most versatile search algorithms used in real-world applications. Its ability to find optimal paths with minimal computation makes it ideal for scenarios where efficiency and accuracy are critical.


1. GPS and Navigation Systems

A* is widely used in mapping and navigation software. Applications like Google Maps or vehicle GPS systems use A* to compute the shortest route between locations, factoring in real-world constraints like road networks and traffic conditions.


2. Video Games and AI Pathfinding

In games, A* helps non-player characters (NPCs) move realistically through virtual worlds. Game developers use it to ensure that enemies or allies can navigate around obstacles and reach targets efficiently.


3. Robotics and Motion Planning

Autonomous robots and drones use A* to plan paths through physical spaces filled with dynamic obstacles. It helps them avoid collisions while finding the shortest route to a destination.


4. Puzzle Solving and Search Problems

A* is often applied to classic search problems like the 8-puzzle, Sudoku solvers, or other combinatorial optimization problems where the state space is large but structured.


Its consistent performance and wide range of applications—from GPS routing to AI pathfinding and robotics—make it a must-know algorithm for developers, computer scientists, and engineers alike.


Python Implementation for A* Search Algorithm

Let’s implement the A* search algorithm in Python. For simplicity, we’ll use a grid-based example where each cell represents a node and neighbors are adjacent cells.

import heapq

class Node:
    def __init__(self, position, parent=None):
        self.position = position  # (row, col)
        self.parent = parent
        self.g = 0  # Cost from start to current node
        self.h = 0  # Heuristic cost to goal
        self.f = 0  # Total cost (g + h)

    def __lt__(self, other):
        return self.f < other.f

def heuristic(a, b):
    # Manhattan distance
    return abs(a[0] - b[0]) + abs(a[1] - b[1])

def astar(grid, start, goal):
    open_list = []
    closed_set = set()

    start_node = Node(start)
    goal_node = Node(goal)

    heapq.heappush(open_list, start_node)

    while open_list:
        current_node = heapq.heappop(open_list)
        current_pos = current_node.position

        if current_pos == goal:
            return reconstruct_path(current_node)

        closed_set.add(current_pos)

        for neighbor in get_neighbors(current_pos, grid):
            if neighbor in closed_set:
                continue

            neighbor_node = Node(neighbor, current_node)
            neighbor_node.g = current_node.g + 1
            neighbor_node.h = heuristic(neighbor, goal)
            neighbor_node.f = neighbor_node.g + neighbor_node.h

            # Check if node is already in open list with lower cost
            if any(open_node.position == neighbor and open_node.f <= neighbor_node.f for open_node in open_list):
                continue

            heapq.heappush(open_list, neighbor_node)

    return None  # No path found

def get_neighbors(position, grid):
    neighbors = []
    row, col = position
    rows, cols = len(grid), len(grid[0])
    directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]  # Up, Down, Left, Right

    for dr, dc in directions:
        r, c = row + dr, col + dc
        if 0 <= r < rows and 0 <= c < cols and grid[r][c] == 0:
            neighbors.append((r, c))
    return neighbors

def reconstruct_path(node):
    path = []
    while node:
        path.append(node.position)
        node = node.parent
    return path[::-1]  # Return reversed path

# Sample Grid
# 0 = free space, 1 = obstacle
grid = [
    [0, 0, 0, 0, 0],
    [0, 1, 1, 1, 0],
    [0, 0, 0, 1, 0],
    [1, 1, 0, 0, 0],
    [0, 0, 0, 1, 0]
]

start = (0, 0)
goal = (4, 4)

path = astar(grid, start, goal)
print("Shortest Path:" if path else "No path found.")
print(path)

Output:

Shortest Path:
[(0, 0), (1, 0), (2, 0), (2, 1), (2, 2), (3, 2), (3, 3), (3, 4), (4, 4)]

This Python implementation of the A* search algorithm solves a pathfinding problem on a 2D grid, where 0 represents free space and 1 represents obstacles. The Node class is used to represent each position on the grid, storing the cost from the start (g), the heuristic estimate to the goal (h), and their sum (f). The algorithm begins by adding the start node to a priority queue (the open list), which always selects the node with the lowest f value for exploration. For each selected node, it checks whether it has reached the goal; if not, it generates valid neighbors using the get_neighbors function, calculates their costs, and adds them to the open list if they provide a better path. The heuristic used is the Manhattan distance, which is suitable for grid-based movement without diagonals. Once the goal is reached, the reconstruct_pathfunction traces the path back to the start using parent references. This approach ensures that the shortest path is found efficiently, avoiding unnecessary exploration of irrelevant nodes.


Conclusion

The A* search algorithm is one of the most powerful and efficient pathfinding techniques available, widely used in fields such as game development, robotics, GPS navigation, and AI planning. By combining the actual cost from the start node with an informed heuristic estimate to the goal, A* makes smart decisions about which paths to explore—resulting in both optimal solutions and significant performance improvements over uninformed methods.

Throughout this guide, we explored the core principles that make A* effective: the use of a consistent and admissible heuristic, the importance of managing open and closed lists efficiently, and the role of the cost function f(n)=g(n)+h(n). We also walked through a complete Python implementation that shows how A* can be applied in a real-world grid environment, complete with obstacles and intelligent pathfinding.

Whether you're building intelligent NPCs in games, navigating autonomous robots through dynamic environments, or solving puzzle and planning problems, A* is a versatile and essential algorithm to have in your toolkit. By understanding its theory and practicing its implementation, you're better equipped to tackle a wide range of optimization and search challenges with confidence.

Get in touch for customized mentorship, research and freelance solutions tailored to your needs.

bottom of page