Search code examples
pythonalgorithmpygamegridpath-finding

Visualize Pathfinding Algorithm


I was doing a pathfinding visualizer in pygame and I pretty much finished but there's still one thing that I do not like about the algorithm part of it and it's the fact that when you press the visualize algorithm button it shows you the shortest path in yellow and all of the nodes the algorithm has visited ever in light blue but it shows you instantaneously and I want it to color the nodes accordingly step by step to actually reach the effect of visualizing (like in here https://clementmihailescu.github.io/Pathfinding-Visualizer/#), I tried to write some code in the function that seemed like it would have worked as intended but it didn't, here is the code:

# Breadth First Search Algorithm
def bfs(graph, start, goal):
    explored = []

    # Queue for traversing the
    # graph in the BFS
    queue = [[start]]

    # If the desired node is
    # reached
    if start == goal:
        return

    # Loop to traverse the graph
    # with the help of the queue

    while queue:
        path = queue.pop(0)
        node = path[-1]
        y, x = node
        # Codition to check if the
        # current node is not visited

        if node not in explored and nodes_rows[x][y].color is not BLACK:

            nodes_rows[x][y].color = LIGHT_BLUE
            neighbours = graph[node]

            # Loop to iterate over the
            # neighbours of the node
            for neighbour in neighbours:
                new_path = list(path)
                new_path.append(neighbour)
                queue.append(new_path)

                # Condition to check if the
                # neighbour node is the goal
                if neighbour == goal:
                    new_path.remove(start)
                    new_path.remove(goal)
                    return new_path

            explored.append(node)

    return None

The nodes_rows[x][y].color == color_name is the code that is responsible for coloring nodes on the grid which is represented by a dictionary(I provided it so it's gonna be easier for you to understand how coloring works in general in my program). The problem with that implementation is when I do add the coloring part at if statement to color all the neighbors it does it instantly on the grid without showing a kind of an animation that shows the coloring process node by node, my question is can I do it so it colors them each iteration rather than all at once by adding something to this code and not writing a new one and if I do need to write a new one that what is the instructions how can I do so?

Here is what I mean by coloring all at once like it does for now:

https://cdn.discordapp.com/attachments/772816508015083552/832303260911272046/PowerPoint_-_1_2021-04-15_20-13-35_Trim.mp4

Edit:

try:
    while True:
        if not ticks or pygame.time.get_ticks() - ticks >= 500:
           ticks = pygame.time.get_ticks()
           nodes = next(algorithm)
           if nodes_rows[nodes[-1][1]][nodes[-1][0]].color != BLUE:
              nodes_rows[nodes[-1][1]][nodes[-1][0]].color = LIGHT_BLUE
              pygame.display.update()
except StopIteration:
    pass

Tried doing it with yield and if I print it it does yield a list every half a second with a new explored node at the end like intended but it updates it all at once after waiting total amount of ticks I tried playing with the indent of display.update() but didn't work either I don't even know what to do at this point

Thanks to everyone contributing to help <3


Solution

  • Per the comments above, here is a simple example of a generator to help you grasp the idea.

    def counter(x):
        for i in range(x):
            yield i
    c = counter(3)
    
    In: next(c)
    Out: 0
    In: next(c)
    Out: 1
    In: next(c)
    Out: 2
    

    What's happening is that every time you call next, the function will continue to run until it reaches the next yield, at which point it will return the yielded value.


    It will also remember where it left off, so the next time you call next, the function will continue where it left off.


    In your application, this could be used to yield the list of explored locations, then draw those locations in whatever color you like, call next(bfs) to step forward and yield the next list of explored locations, and so on until of course you find the solution and run out of items to yield.


    One more example, perhaps a little more closely related to what you are trying to do:

    def make_path():
        path = []
        
        i = 0
        while True:
            path.append(i)
            i += 1
            yield path
            
    c = make_path()
    
    for _ in range(6):
        print(next(c))
    
    Out: [0]
         [0, 1]
         [0, 1, 2]
         [0, 1, 2, 3]
         [0, 1, 2, 3, 4]
         [0, 1, 2, 3, 4, 5]