The A* algorithm is a path-finding algorithm similar to Dijkstra's algorithm, and works by visiting nodes (using a heuristic to decide which node to visit next), and comparing this node to the nodes already visited which are in the closed list.
In my implementation, the # of nodes visited / second decreases dramatically as the closed-list increases in size. While initially, the algorithm visits around 3,000 nodes/second, this decreases to less than 50 nodes/second as the closed-list grows > 10,000 nodes. The only thing that becomes computationally more expensive is the comparison of the new node to the open- and closed-lists, and storing the new node in the closed list.
Therefore, I think that I can dramatically increase performance by storing the closed-list in a more efficient way!
Here are some excerpts from my implementation. First, the Node class, which is used for the definition of all Nodes:
class Node:
"""
A node class for A* Pathfinding
"""
def __init__(self, parent=None, position=None):
self.parent = parent
self.position = position
self.g = 0 # g = actual cost of reaching this node
self.h = 0 # h = heuristic, used for determining which node to visit next
self.f = 0 # f = g + h
def __eq__(self, other):
return self.position == other.position
# defining less than for purposes of heap queue
def __lt__(self, other):
return self.f < other.f
# defining greater than for purposes of heap queue
def __gt__(self, other):
return self.f > other.f
I use the heap queue for storing the open-list as I thought that this could improve speed. However, it only did so marginally (±5%).
Below is my A* implementation, condensed so that only the relevant operations are included:
def find_a_star_path(self, current_pos, target_pos):
# Initialize start- and end-nodes with zero cost
start_node = self.Node(None, current_pos)
start_node.g = start_node.h = start_node.f = 0.0
end_node = self.Node(None, target_pos)
end_node.g = end_node.h = end_node.f = 0.0
# Initialize open- and closed list
open_list = []
closed_list = []
# Heapify the open_list and Add the start node
heapq.heapify(open_list)
heapq.heappush(open_list, start_node)
# As long as there are "open" nodes, we continue A*.
while len(open_list) > 0:
# Find node with the lowest cost F, this is visited next
current_node = heapq.heappop(open_list)
closed_list.append(current_node)
if current_node == end_node:
# if current_node = end_node, the process is finished.
# Some code that finds all possible next nodes from the next node
# This next node is called child
# child.g, child.h and child.f are calculated
# Now check if the new node is better than another node with the same position but a different parent.
filtered_open_nodes = (open_node for open_node in open_list if child == open_node)
open_node = next(filtered_open_nodes, None)
while open_node:
if child.f > open_node.f:
add_to_open = False
break
else:
# The new node is better than the other path to this node, so remove it.
open_list.remove(open_node)
open_node = next(filtered_open_nodes, None)
if add_to_open == True:
heapq.heappush(open_list, child)