Learn how you can become a Python programmer in just 12 weeks.

    We respect your privacy. Unsubscribe at anytime.

    Solve a Maze in Python with a Stack

    What will we cover in this tutorial?

    Given an input of a maze, how can we solve it?

    That is – given an input like this the following.

    #############
    S #     #   #
    # #  ##   # #
    # #  ## ### #
    #       #   #
    ######### # #
    #         # E
    #############
    

    How can we find a path through the maze starting from S and ending at E.

    #############
    S.#.....#...#
    #.#. ##...#.#
    #.#. ## ###.#
    #...    #  .#
    ######### #.#
    #         #.E
    #############
    

    We will create a program that can do that in Python.

    Step 1: Reading and representing the Maze

    An good representation of the maze makes it easier to understand the code. Hence, the first part is to figure out how to represent the maze.

    To clarify, imagine that this maze.

    v
    

    Was represented by one long string, like this.

    maze = "#############S #     #   ## #  ##   # ## #  ## ### ##       #   ########## # ##         # E#############"
    

    While this is possible, if you also know the dimensions (8 rows and 13 columns), it makes things difficult to navigate in. Say, you are somewhere in the maze, and you want to see what is right above you. How do you do that?

    Yes, you can do it, but it is complex.

    Now image you have the following representation.

    maze = [['#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#'],
            ['S', ' ', '#', ' ', ' ', ' ', ' ', ' ', '#', ' ', ' ', ' ', '#'],
            ['#', ' ', '#', ' ', ' ', '#', '#', ' ', ' ', ' ', '#', ' ', '#'],
            ['#', ' ', '#', ' ', ' ', '#', '#', ' ', '#', '#', '#', ' ', '#'],
            ['#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', ' ', ' ', ' ', '#'],
            ['#', '#', '#', '#', '#', '#', '#', '#', '#', ' ', '#', ' ', '#'],
            ['#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', ' ', 'E'],
            ['#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#']]
    

    This will make it easier to navigate in the maze.

    Then maze[1][0] represents row 1 (2nd row) and column 0 (1st column), which is S.

    If we assume that the maze is represented in a file, then we can read that file and convert it with the following code.

    def load_maze(file_name):
        f = open(file_name)
        maze = f.read()
        f.close()
        return maze
    def convert_maze(maze):
        converted_maze = []
        lines = maze.splitlines()
        for line in lines:
            converted_maze.append(list(line))
        return converted_maze
    
    maze = load_maze("maze.txt")
    maze = convert_maze(maze)
    print(maze)
    

    Step 2: Creating a print function of the maze

    As you probably noticed, the print statement of the maze (print(maze)) did not do a very good job.

    [['#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#'], ['S', ' ', '#', ' ', ' ', ' ', ' ', ' ', '#', ' ', ' ', ' ', '#'], ['#', ' ', '#', ' ', ' ', '#', '#', ' ', ' ', ' ', '#', ' ', '#'], ['#', ' ', '#', ' ', ' ', '#', '#', ' ', '#', '#', '#', ' ', '#'], ['#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', ' ', ' ', ' ', '#'], ['#', '#', '#', '#', '#', '#', '#', '#', '#', ' ', '#', ' ', '#'], ['#', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '#', ' ', 'E'], ['#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#', '#']]
    

    In order to be able to follow what happens in the code later on, it would be nice with a better representation of the code.

    def print_maze(maze):
        for row in maze:
            for item in row:
                print(item, end='')
            print()
    
    print_maze(maze)
    

    This will print the maze in a more human readable way.

    #############
    S #     #   #
    # #  ##   # #
    # #  ## ### #
    #       #   #
    ######### # #
    #         # E
    #############
    

    Step 3: Finding the starting point of the maze

    First thing first. We need to find where we enter the maze. This is where the S is located.

    We could have some assumptions here, say, that the starting point is always located on the outside borders of the maze. To make it more general, we will assume that the starting point, S, can be located anywhere in the maze.

    This makes the algorithm to find it easy, but of course not effective in worst case, as we need to check all locations in the maze.

    def find_start(maze):
        for row in range(len(maze)):
            for col in range(len(maze[0])):
                if maze[row][col] == 'S':
                    return row, col
    
    start = find_start(maze)
    print(start)
    

    Which will go through all rows and columns one by one and check if it is the starting point. When found, it will return it immediately.

    Notice, that we do assume that we have a maze with at least one row, also that the starting point exists. If not, it will not return anything.

    Step 4: Find if there is a path in the maze

    This is where a stack comes in handy. If you are new to what a stack is, then check out this tutorial.

    For the purpose here, we are not looking for performance, we just need the concept of a stack.

    To see if there is a way through the maze, you need to check all paths. As some leads to the same points, we need to keep track of where we have been. If we reach a dead end, where we have been, we need to backtrack to the last point with options, and check them out.

    Wow. That was a lot of talking. How can we understand that a bit better.

    Let’s make a simple maze and see it.

    ###
    S E
    ###
    

    When you are at start, S, you have (in theory) 4 possible ways to go. Up, Down, Right, Left. In the above example you can only go Right. Let’s do that, and mark the position we just left.

    ###
    X@E
    ###
    

    Now we have marked where we have been with X and where we are with @. Then we can check all possible ways. Up, Down, Right, Left. Again we can only go Right, as Left we have already been there.

    Luckily, going Right is the exit E and we are done.

    Now that was a simple maze. Now how does a stack come into the picture? Well, we used it secretly and in a very simple case.

    So let’s try a bit more complex case.

    #####
    S # E
    #   #
    # # #
    #####
    

    Now let’s also have a stack with the positions we can visit. We will use the coordinate system as the representation of the list of lists we use to represent the maze. That means that the upper left corner is (0, 0) and the lower right corner is (5, 5) in this case.

    The starting point is (1 , 0).

    Now, let’s have a stack in the picture and put the staring point on that stack.

    #####
    S # E
    #   #
    # # #
    #####      stack -> (1, 0)
    

    When we enter we look for points on the stack. Yes, we have one point (1, 0). Then we pop it off and check all possible positions. Up, Down, Right, Left. Only one is possible, Left, which is (1, 1). Updating where we been, then it looks like this.

    #####
    X # E
    #   #
    # # #
    #####      stack -> (1, 1)
    

    Notice, that the stack changed and we marked the first point with X.

    We do the same. Pop of the stack and get (1, 1) and check Up, Down, Right, Left. Only down is possible, which leaves us in (2, 1). Now the picture looks like this.

    #####
    XX# E
    #   #
    # # #
    #####      stack -> (2, 1)
    

    Then we do the same. We pop of the stack and get (2, 1) and check Up, Down, Right, Left. Now we can go both Down and Right, which is (3, 1) and (2, 2), respectively. Hence, we push both of them on the stack.

    #####
    XX# E
    #X  #
    # # #               (3, 1)
    #####      stack -> (2, 2)
    

    Depending on the order we put thins on stack, it will look like that.

    Now we pop of the top element (3, 1) and check Up, Down, Right, Left. But there are no way to go from there, it is a dead end. Then we mark that spot and do not push anything on the stack.

    #####
    XX# E
    #X  #
    #X# #
    #####      stack -> (2, 2)
    

    Now we pop (2, 2) of the stack, as this is the first possibility we have to go another way in the maze. Then check Up, Down, Right, Left. We can only go Right. After marking pushing we have.

    #####
    XX# E
    #XX #
    #X# #
    #####      stack -> (3, 2)
    

    Now we can both go up and down and add that to the stack.

    #####
    XX# E
    #XXX#
    #X# #               (3, 2)
    #####      stack -> (3, 4)
    

    Now we pop (3, 2) of the stack. Then we check all and see we can only go Right.

    #####
    XX#XE
    #XXX#
    #X# #               (4, 2)
    #####      stack -> (3, 4)
    

    Then we pop (4, 2) of the stack and see it is the exit, E. That is just what we needed.

    The stack keeps track on all the possible choices we have along the way. If we reach a dead end (also, if we have visited all around us and marked it by X), we can look at the last possible choice we skipped, which is on the top of the stack.

    That actually means, that we do not need to move backwards along the road we went, we can go directly to the point on the stack. We know we can reach that point, as we have already visited a neighboring point, which is connected. Also, as we mark everything along the way, we do not go in endless loops. We only visit every point at most once.

    Now, if there was no way to the exit, E, this will eventually terminate when there is no points on the stack.

    Step 5: Implementing the algorithm

    The above can be made into a simple algorithm to determine whether there is a way through the maze.

    The pseudo code for the algorithm could be something like this.

    stack.push(start)
    while stack is not empty
        location = stack.pop()
        if location is marked continue to next location in loop
        if location is marked as exit - return True (we found the exit)
        for loc as Up, Down, Left, Right:
            if loc is valid and not marked add to stack
    return False (if all locations are tried (stack empty) there is not way to the exit)
    

    Now let’s turn that into code.

    # This is only a helper function to see if we have a valid positino.
    def is_valid_position(maze, pos_r, pos_c):
        if pos_r < 0 or pos_c < 0:
            return False
        if pos_r >= len(maze) or pos_c >= len(maze[0]):
            return False
        if maze[pos_r][pos_c] in ' E':
            return True
        return False
    
    def solve_maze(maze, start):
        # We use a Python list as a stack - then we have push operations as append, and pop as pop.
        stack = []
        # Add the entry point (as a tuple)
        stack.append(start)
        # Go through the stack as long as there are elements
        while len(stack) > 0:
            pos_r, pos_c = stack.pop()
            if maze[pos_r][pos_c] == 'E':
                print("GOAL")
                return True
            if maze[pos_r][pos_c] == 'X':
                # Already visited
                continue
            # Mark position as visited
            maze[pos_r][pos_c] = 'X'
            # Check for all possible positions and add if possible
            if is_valid_position(maze, pos_r - 1, pos_c):
                stack.append((pos_r - 1, pos_c))
            if is_valid_position(maze, pos_r + 1, pos_c):
                stack.append((pos_r + 1, pos_c))
            if is_valid_position(maze, pos_r, pos_c - 1):
                stack.append((pos_r, pos_c - 1))
            if is_valid_position(maze, pos_r, pos_c + 1):
                stack.append((pos_r, pos_c + 1))
        # We didn't find a path, hence we do not need to return the path
        return False
    

    The full code

    Here we present the full code. It prints out the state through the processing. It expects a file called maze.txt with the maze in it. See below for a possible file content.

    def load_maze(file_name):
        f = open(file_name)
        maze = f.read()
        f.close()
        return maze
    
    def convert_maze(maze):
        converted_maze = []
        lines = maze.splitlines()
        for line in lines:
            converted_maze.append(list(line))
        return converted_maze
    
    def print_maze(maze):
        for row in maze:
            for item in row:
                print(item, end='')
            print()
    
    def find_start(maze):
        for row in range(len(maze)):
            for col in range(len(maze[0])):
                if maze[row][col] == 'S':
                    return row, col
    
    from time import sleep
    
    # This is only a helper function to see if we have a valid positino.
    def is_valid_position(maze, pos_r, pos_c):
        if pos_r < 0 or pos_c < 0:
            return False
        if pos_r >= len(maze) or pos_c >= len(maze[0]):
            return False
        if maze[pos_r][pos_c] in ' E':
            return True
        return False
    
    def solve_maze(maze, start):
        # We use a Python list as a stack - then we have push operations as append, and pop as pop.
        stack = []
        # Add the entry point (as a tuple)
        stack.append(start)
        # Go through the stack as long as there are elements
        while len(stack) > 0:
            pos_r, pos_c = stack.pop()
            print("Current position", pos_r, pos_c)
            if maze[pos_r][pos_c] == 'E':
                print("GOAL")
                return True
            if maze[pos_r][pos_c] == 'X':
                # Already visited
                continue
            # Mark position as visited
            maze[pos_r][pos_c] = 'X'
            # Check for all possible positions and add if possible
            if is_valid_position(maze, pos_r - 1, pos_c):
                stack.append((pos_r - 1, pos_c))
            if is_valid_position(maze, pos_r + 1, pos_c):
                stack.append((pos_r + 1, pos_c))
            if is_valid_position(maze, pos_r, pos_c - 1):
                stack.append((pos_r, pos_c - 1))
            if is_valid_position(maze, pos_r, pos_c + 1):
                stack.append((pos_r, pos_c + 1))
    
            # To follow the maze
            print('Stack:' , stack)
            print_maze(maze)
    
        # We didn't find a path, hence we do not need to return the path
        return False
    
    maze = load_maze("maze.txt")
    maze = convert_maze(maze)
    print_maze(maze)
    start = find_start(maze)
    print(start)
    print(solve_maze(maze, start))
    

    Then for some possible content of file maze.txt needed by the program above. To make the above code work, save the content below in a file called maze.txt in the same folder from where you run the code.

    #############
    S #     #   #
    # #  ##   # #
    # #  ## ### #
    #       #   #
    ######### # #
    #         # E
    #############
    

    Further work

    The above code only answers if there is a path from S to E in the maze. It would also be interesting to print a route out. Further improvements could be finding the shortest path.

    Python for Finance: Unlock Financial Freedom and Build Your Dream Life

    Discover the key to financial freedom and secure your dream life with Python for Finance!

    Say goodbye to financial anxiety and embrace a future filled with confidence and success. If you’re tired of struggling to pay bills and longing for a life of leisure, it’s time to take action.

    Imagine breaking free from that dead-end job and opening doors to endless opportunities. With Python for Finance, you can acquire the invaluable skill of financial analysis that will revolutionize your life.

    Make informed investment decisions, unlock the secrets of business financial performance, and maximize your money like never before. Gain the knowledge sought after by companies worldwide and become an indispensable asset in today’s competitive market.

    Don’t let your dreams slip away. Master Python for Finance and pave your way to a profitable and fulfilling career. Start building the future you deserve today!

    Python for Finance a 21 hours course that teaches investing with Python.

    Learn pandas, NumPy, Matplotlib for Financial Analysis & learn how to Automate Value Investing.

    “Excellent course for anyone trying to learn coding and investing.” – Lorenzo B.

    6 thoughts on “Solve a Maze in Python with a Stack”

      • You should have a stack keeping track of the choices you make along the way – of course pop them off when you backtrack. Then at the end, you have the path in reverse.

        Hope it helps,
        Rune

        Reply

    Leave a Comment