Malav Patel

Shortest Path in a Maze

You are given a grid of size m by n, a 2d integer array walls, and a 2-tuple escape_point. A maze runner begins at position (0, 0). The runner can move up, down, left, right in the grid. Their goal is to reach the escape point. However, there are walls in the maze (their 2d locations specified by walls) that are impassable. Find the minimum number of steps needed to get to the escape point. If there is no path, return -1.

Solution

The idea is to use Breadth-First-Search. It works because BFS will sweep all the closest neighbors starting at the start point. If they happen to be the escape, the program terminates. Otherwise, we look at the neighbors’ neighbors and so on.

Pseudocode

def bfs_maze(m, n, walls, start_point, escape_point):
    q = create_empty_queue()
    visited = create_empty_set()

    q.enqueue((start_point, 0)) # add (start_point, level in bfs tree)
    visited.add(start_point)

    directions = ((1, 0), (-1, 0), (0, 1), (0, -1))

    while q not empty:
        pos, level = q.dequeue()
        
        if pos == escape_point:
            return level
        
        for dir in directions:
            neighbor = pos + direction

            if (neighbor not a wall) and (neighbor not visited) and (neighbor in bounds):
                q.enqueue( (neighbor, level + 1) )
                visited.add(neighbor)
    
    return -1 # return -1 if no valid path found

Python


def bfs_maze(rows, cols, walls, escape_point):
    q = queue.Queue() # create empty queue to store to-be-processed nodes
    visited = set() # create empty set to store already-touched nodes

    start = ((0, 0), 0) # maze runner begins at position (0, 0) and at the root of the bfs tree (level 0)
    q.put(start) # place the first node in the queue
    visited.add(start[0]) # mark the node as touched

    directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] # directions to move along are right left up down

    while not q.empty():
        pos, level = q.get()

        if pos == escape_point:
            return level

        for dir in directions:
            neighbor = (pos[0] + dir[0], pos[1]+ dir[1])

            if (neighbor not in walls) and (neighbor not in visited) and valid_point(neighbor, rows, cols):
                q.put((neighbor, level + 1))
                visited.add(neighbor)
        

    return -1

def valid_point(pos, m, n):
    i, j = pos

    if 0 <= i < m and 0 <= j < n:
        return True
    else:
        return False

Example

Consider the following maze where solid squares are walls and the * is the escape point:

 .  .  .  .  . 
 ■  ■  .  ■  ■ 
 .  .  .  .  . 
 .  .  .  .  . 
 .  .  .  .  * 

One of shortest paths has 8 steps (there are several with 8 steps) and looks like the following:

 +  +  +  .  . 
 ■  ■  +  ■  ■ 
 .  .  +  +  + 
 .  .  .  .  + 
 .  .  .  .  * 

The output of our program is as expected:

rows = 5
cols = 5
walls = [(1, 0), (1, 1), (1, 3), (1, 4)]
escape_point = (4, 4)
bfs_maze(rows, cols, walls, escape_point)

8 # output

The order of traversal is shown below:

 1   2   3   4   6
██  ██   5  ██  ██
13   9   7   8  11
18  14  10  12  16
21  19  15  17  20