Too far down the rabbit hole on A*. Need a second pair of eyes
Question:
My A-Star code is posted below, pretty much the same as everyone elses but I’ve gone too far down the rabbit hole and can’t step back and see what’s causing my problem.
I have a 2D array and as standard it’s dimensions are matrix[y][x] the coordinates that i use for my start and finish are in the x,y order. "Shouldn’t be difficult to manage!" i hear you cry and i totally agree with you.
My issue is that if I have a matrix that is, let’s say 20,15 i.e 15 rows of 20, if my destination is located in a position of x> 15, the algorithm gets stuck in an infinite loop because it can’t find the destination. I know that this is because it’s gotten the x and y dimensions of the matrix muddled up somewhere but I’m blown if I can see where it is.
Anyone who has time and gives it a look would be forever in my good graces
class Node():
#constructor, takes in a parent node and the nodes position as arguments
def __init__(self, parent=None, position=None):
self.parent = parent
self.position = position
#Fields for the calculations of each node's cost
self.g = 0
self.h = 0
self.f = 0
def __eq__(self, other):
return self.position == other.position
def __hash__(self):
return hash(self.position)
def astar(matrix, start, end):
#returns the path as a list of tuples
TIE_BREAKER = 0.02
#create start and end nodes
start_node = Node(None, start)
#start_node.g = start_node.h = start_node.f = 0
end_node = Node(None, end)
#end_node.g = end_node.h = end_node.f = 0
#create open and closed lists
open_list = []
closed_list = set()
#add the start node to the open_list
open_list.append(start_node)
#start looping until we find the end node
while len(open_list) > 0:
#get the current node
current_node = open_list[0]
current_index = 0
#check if there is an item in the open_list with a lower f-cost and if so, change our current node and index to that
for i, item in enumerate(open_list):
if item.f < current_node.f:
current_node = item
current_index = i
#pop the current_node off the open list and add it to the closed list
open_list.pop(current_index)
closed_list.add(current_node)
#what to do if we've found the end
if current_node == end_node:
#create an empty list for our path
path = []
current = current_node
#loop through until there is nothing left in the chain
while current is not None:
#add the current node to the path list
path.append(current.position)
#change the current node to its parent
current = current.parent
#return the path, reversed
return path[::-1]
#generate children of the current node#
children = []
#for each position in the 4 possible directions
for new_position in [(0, -1), (0, 1), (-1, 0), (1, 0)]:
#get the child nodes position
node_position = (current_node.position[0] + new_position[0], current_node.position[1] + new_position[1])
#check that the node's position is within the bounds of the matrix
if node_position[0] >= len(matrix) or node_position[0] < 0 or node_position[1] >= len(matrix[0]) or node_position[1] < 0:
#skip this iteration if it is out of bounds
continue
#check that the node is walkable
if matrix[node_position[0]][node_position[1]] != 0:
continue
#Create a new node
new_node = Node(current_node, node_position)
#add it to the list of children
children.append(new_node)
#loop through the children
for child_node in children:
#see if the child_node is on the closed list and if so, skip it
if child_node in closed_list:
continue
child_node.g = current_node.g + 1
child_node.h = float((abs((child_node.position[0] - end_node.position[0])) + abs((child_node.position[1] - end_node.position[1])))) * (1.0 + TIE_BREAKER)
child_node.f = child_node.g + child_node.h
#check if child node is already on the open_list
for open_node in open_list:
if child_node == open_node and child_node.g > open_node.g:
continue
#if it's passed both the above checks, add it to the open list
open_list.append(child_node)
I’ve tried swapping the indices in the node_position conditional check and it just causes an index out of range error which makes sense but doesn’t
Answers:
When you post a question about code, please provide the code to reproduce your problem. I’ve now added this to reproduce it:
height = 15
width = 20
matrix = [ [0 for x in range(width)] for y in range(height)]
astar(matrix, (1,1), (15,5))
Your first problem is caused by line:
if node_position[0] >= len(matrix) or node_position[0] < 0 or node_position[1] >= len(matrix[0]) or node_position[1] < 0:
len(matrix)
is the height, and len(matrix[0])
is the width of the matrix, so change this line to:
if node_position[0] >= len(matrix[0]) or node_position[0] < 0 or node_position[1] >= len(matrix) or node_position[1] < 0:
Your second problem then becomes line:
if matrix[node_position[0]][node_position[1]] != 0:
node_position[0]
is the ‘x’ coordinate and should be last, node_position[1]
is the ‘y’ coordinate and should be first, so change this line to:
if matrix[node_position[1]][node_position[0]] != 0:
My A-Star code is posted below, pretty much the same as everyone elses but I’ve gone too far down the rabbit hole and can’t step back and see what’s causing my problem.
I have a 2D array and as standard it’s dimensions are matrix[y][x] the coordinates that i use for my start and finish are in the x,y order. "Shouldn’t be difficult to manage!" i hear you cry and i totally agree with you.
My issue is that if I have a matrix that is, let’s say 20,15 i.e 15 rows of 20, if my destination is located in a position of x> 15, the algorithm gets stuck in an infinite loop because it can’t find the destination. I know that this is because it’s gotten the x and y dimensions of the matrix muddled up somewhere but I’m blown if I can see where it is.
Anyone who has time and gives it a look would be forever in my good graces
class Node():
#constructor, takes in a parent node and the nodes position as arguments
def __init__(self, parent=None, position=None):
self.parent = parent
self.position = position
#Fields for the calculations of each node's cost
self.g = 0
self.h = 0
self.f = 0
def __eq__(self, other):
return self.position == other.position
def __hash__(self):
return hash(self.position)
def astar(matrix, start, end):
#returns the path as a list of tuples
TIE_BREAKER = 0.02
#create start and end nodes
start_node = Node(None, start)
#start_node.g = start_node.h = start_node.f = 0
end_node = Node(None, end)
#end_node.g = end_node.h = end_node.f = 0
#create open and closed lists
open_list = []
closed_list = set()
#add the start node to the open_list
open_list.append(start_node)
#start looping until we find the end node
while len(open_list) > 0:
#get the current node
current_node = open_list[0]
current_index = 0
#check if there is an item in the open_list with a lower f-cost and if so, change our current node and index to that
for i, item in enumerate(open_list):
if item.f < current_node.f:
current_node = item
current_index = i
#pop the current_node off the open list and add it to the closed list
open_list.pop(current_index)
closed_list.add(current_node)
#what to do if we've found the end
if current_node == end_node:
#create an empty list for our path
path = []
current = current_node
#loop through until there is nothing left in the chain
while current is not None:
#add the current node to the path list
path.append(current.position)
#change the current node to its parent
current = current.parent
#return the path, reversed
return path[::-1]
#generate children of the current node#
children = []
#for each position in the 4 possible directions
for new_position in [(0, -1), (0, 1), (-1, 0), (1, 0)]:
#get the child nodes position
node_position = (current_node.position[0] + new_position[0], current_node.position[1] + new_position[1])
#check that the node's position is within the bounds of the matrix
if node_position[0] >= len(matrix) or node_position[0] < 0 or node_position[1] >= len(matrix[0]) or node_position[1] < 0:
#skip this iteration if it is out of bounds
continue
#check that the node is walkable
if matrix[node_position[0]][node_position[1]] != 0:
continue
#Create a new node
new_node = Node(current_node, node_position)
#add it to the list of children
children.append(new_node)
#loop through the children
for child_node in children:
#see if the child_node is on the closed list and if so, skip it
if child_node in closed_list:
continue
child_node.g = current_node.g + 1
child_node.h = float((abs((child_node.position[0] - end_node.position[0])) + abs((child_node.position[1] - end_node.position[1])))) * (1.0 + TIE_BREAKER)
child_node.f = child_node.g + child_node.h
#check if child node is already on the open_list
for open_node in open_list:
if child_node == open_node and child_node.g > open_node.g:
continue
#if it's passed both the above checks, add it to the open list
open_list.append(child_node)
I’ve tried swapping the indices in the node_position conditional check and it just causes an index out of range error which makes sense but doesn’t
When you post a question about code, please provide the code to reproduce your problem. I’ve now added this to reproduce it:
height = 15
width = 20
matrix = [ [0 for x in range(width)] for y in range(height)]
astar(matrix, (1,1), (15,5))
Your first problem is caused by line:
if node_position[0] >= len(matrix) or node_position[0] < 0 or node_position[1] >= len(matrix[0]) or node_position[1] < 0:
len(matrix)
is the height, and len(matrix[0])
is the width of the matrix, so change this line to:
if node_position[0] >= len(matrix[0]) or node_position[0] < 0 or node_position[1] >= len(matrix) or node_position[1] < 0:
Your second problem then becomes line:
if matrix[node_position[0]][node_position[1]] != 0:
node_position[0]
is the ‘x’ coordinate and should be last, node_position[1]
is the ‘y’ coordinate and should be first, so change this line to:
if matrix[node_position[1]][node_position[0]] != 0: