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

Asked By: Matt B

||

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:
Answered By: bterwijn
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.