2

I'm trying to look into the A* Algorithm but I'm kind of having a hard time understanding a specific part. So the A* Algorithm Python Code with the example is this:

class Node():
    """A node class for A* Pathfinding"""

    def __init__(self, parent=None, position=None):
        self.parent = parent
        self.position = position

        self.g = 0
        self.h = 0
        self.f = 0

    def __eq__(self, other):
        return self.position == other.position


def astar(maze, start, end):
    """Returns a list of tuples as a path from the given start to the given end in the given maze"""

    # Create start and end node
    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

    # Initialize both open and closed list
    open_list = []
    closed_list = []

    # Add the start node
    open_list.append(start_node)

    # Loop until you find the end
    while len(open_list) > 0:

        # Get the current node
        current_node = open_list[0]
        current_index = 0
        for index, item in enumerate(open_list):
            if item.f < current_node.f:
                current_node = item
                current_index = index

        # Pop current off open list, add to closed list
        open_list.pop(current_index)
        closed_list.append(current_node)

        # Found the goal
        if current_node == end_node:
            path = []
            current = current_node
            while current is not None:
                path.append(current.position)
                current = current.parent
            return path[::-1] # Return reversed path

        # Generate children
        children = []
        for new_position in [(0, -1), (0, 1), (-1, 0), (1, 0), (-1, -1), (-1, 1), (1, -1), (1, 1)]: # Adjacent squares

            # Get node position
            node_position = (current_node.position[0] + new_position[0], current_node.position[1] + new_position[1])

            # Make sure within range
            if node_position[0] > (len(maze) - 1) or node_position[0] < 0 or node_position[1] > (len(maze[len(maze)-1]) -1) or node_position[1] < 0:
                continue

            # Make sure walkable terrain
            if maze[node_position[0]][node_position[1]] != 0:
                continue

            # Create new node
            new_node = Node(current_node, node_position)

            # Append
            children.append(new_node)

        # Loop through children
        for child in children:

            # Child is on the closed list
            for closed_child in closed_list:
                if child == closed_child:
                    continue

            # Create the f, g, and h values
            child.g = current_node.g + 1
            child.h = ((child.position[0] - end_node.position[0]) ** 2) + ((child.position[1] - end_node.position[1]) ** 2)
            child.f = child.g + child.h

            # Child is already in the open list
            for open_node in open_list:
                if child == open_node and child.g > open_node.g:
                    continue

            # Add the child to the open list
            open_list.append(child)


def main():

    maze = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 1, 0, 0, 0, 0, 0]]

    start = (4, 3)
    end = (4, 5)

    path = astar(maze, start, end)
    print(path)


if __name__ == '__main__':
    main()

In the

for index, item in enumerate(open_list):
    if item.f < current_node.f:
        current_node = item
        current_index = index

I don't get how the current_node can be defined as the item in the maze I've given above. In the example I've given above, the start = (4,3) and end = (4,5), giving the only possible shortest distance would be as something like the following:

maze = [[0, 0, 0, 0, *, 0, 0, 0, 0, 0],
        [0, 0, 0, *, 1, *, 0, 0, 0, 0],
        [0, 0, 0, *, 1, *, 0, 0, 0, 0],
        [0, 0, 0, *, 1, *, 0, 0, 0, 0],
        [0, 0, 0, s, 1, e, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
        [0, 0, 0, 0, 1, 0, 0, 0, 0, 0]]

with the s being the start_node and e being the end_node.

However, in the code of the A* Algorithm, the current_node becomes the item only if the item.f is smaller than the current_node.f. In the example I've given here, I can't see that the first * would have an f value smaller than the f value of the start_node - I mean, in the code, we already have described the start_node.f = 0 haven't we? And we defined the first current_node as the start_node... so no item in the open_list would have an item.f value smaller than zero..

How is this possible?? Or am I missing something here??

1 Answer 1

1

I think the clue is that you have to take into account the two lines above this for loop as well:

# Get the current node
current_node = open_list[0]
current_index = 0
for index, item in enumerate(open_list):
  if item.f < current_node.f:
    current_node = item
    current_index = index

What happens:

  • In the first iteration of your while loop:
    • There is only one item in the open_list, being the start_node where indeed f=0
    • So after the above code block, this start node becomes the current_node
    • Right after the above loop the start_node is removed from the open_list: open_list.pop(current_index)
    • The open_list is then populated by the valid neighbouring locations (by walking its children)
  • In the second iteration of your while loop:
    • The above code block looks for the item in the open_list with the lowest f value
    • because of the first line current_node = open_list[0], you will be sure that the new current_node is always one from the open_list.
    • as the start_node has been removed from the open_list, it will for sure be replaced here
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.