C-Sharp | Java | Python | Swift | GO | WPF | Ruby | Scala | F# | JavaScript | SQL | PHP | Angular | HTML
Get_maze_lists: We introduce get_maze_lists to parse in a string into a list of lists. With int arrays we can process the maze easier.
Display: This method displays the maze_lists in a character format. It is used to show our agent's progress.
Valid_pos: When we try to move to a new square, we want to ensure the square is on the map—here valid_pos will tell us.
Modify_path: This takes the current data and moves the agent in one possible direction. It returns a value that indicates what happened.
Python program that navigates maze
# The maze string, use "x" for a wall, 1 for start.
maze = """
   xxx1.
 x x   .
 x2x  x.
 xxx   .
 x   x .
   x xx""";
def get_maze_lists(maze):
    # Split on period.
    lines_raw = maze.split(".")
    # Remove newlines.
    lines_stripped = list(map(lambda line: line.strip("\n"), lines_raw))
    # Result 2D array.
    result = []
    for line in lines_stripped:
        # Create row.
        row = []
        for char in line:
            if char == "x":
                row.append(-1)
            elif char == "1":
                row.append(1)
            elif char == "2":
                row.append(-3)
            else:
                row.append(0)
        # Add row to result.
        result.append(row)
    return result
def display(maze_lists):
    # Display current maze progress.
    for row in maze_lists:
        line = ""
        for column in row:
            if column == -1:
                line += "x"
            elif column == 1:
                line += "1"
            elif column == -3:
                line += "2"
            elif column == 0:
                line += " "
            else:
                line += "."
        print(line)
def valid_pos(maze_lists, row, new_row, new_column):
    # Determines if coordinates are within range.
    if new_row < 0: return False
    if new_column < 0: return False
    if new_row >= len(maze_lists): return False
    if new_column >= len(maze_lists[row]): return False
    return True
def modify_path(maze_lists):
    # All possible moves.
    moves = [[-1, 0],
             [0, -1], [0, 1],
             [1, 0]]
    # Loop over rows and columns.
    for row in range(len(maze_lists)):
        for column in range(len(maze_lists[row])):
            value = maze_lists[row][column]
            if value == -1:
                # Do nothing on wall.
                pass
            elif value == -3:
                # Do nothing on end point.
                pass
            elif value >= 1:
                # For a reached square, add a step number.
                for move in moves:
                    new_row = row + move[0]
                    new_column = column + move[1]
                    # Ensure in range.
                    if valid_pos(maze_lists, row, new_row, new_column):
                        test_value = maze_lists[new_row][new_column]
                        # Set move integer in new square.
                        if test_value == 0:
                            maze_lists[new_row][new_column] = value + 1
                            # A move was made.
                            return 0
                        elif test_value == -3:
                            # We are done if we can move to the end point.
                            return 1
    # Nothing can be done.
    return -1
# Initialize maze lists from string.
data = get_maze_lists(maze)
# Display initial map.
display(data)
# Walk through maze.
count = 0
while True:
    value = input()
    result = modify_path(data)
    if result == 1:
        print("DONE:", count, "moves")
        break
    elif result == -1:
        print("FAIL:", count, "moves")
        break
    else:
        display(data)
    count += 1
Output
   xxx1
 x x
 x2x  x
 xxx
 x   x
   x xx
   xxx1
 x x  .
 x2x  x
 xxx
 x   x
   x xx
   xxx1
 x x ..
 x2x  x
 xxx
 x   x
   x xx
   xxx1
 x x...
 x2x  x
 xxx
 x   x
   x xx
   xxx1
 x x...
 x2x. x
 xxx
 x   x
   x xx
   xxx1
 x x...
 x2x..x
 xxx
 x   x
   x xx
   xxx1
 x x...
 x2x..x
 xxx.
 x   x
   x xx
   xxx1
 x x...
 x2x..x
 xxx..
 x   x
   x xx
   xxx1
 x x...
 x2x..x
 xxx..
 x  .x
   x xx
   xxx1
 x x...
 x2x..x
 xxx...
 x  .x
   x xx
   xxx1
 x x...
 x2x..x
 xxx...
 x  .x.
   x xx
   xxx1
 x x...
 x2x..x
 xxx...
 x ..x.
   x xx
   xxx1
 x x...
 x2x..x
 xxx...
 x...x.
   x xx
   xxx1
 x x...
 x2x..x
 xxx...
 x...x.
  .x xx
   xxx1
 x x...
 x2x..x
 xxx...
 x...x.
  .x.xx
   xxx1
 x x...
 x2x..x
 xxx...
 x...x.
 ..x.xx
   xxx1
 x x...
 x2x..x
 xxx...
 x...x.
...x.xx
   xxx1
 x x...
 x2x..x
 xxx...
.x...x.
...x.xx
   xxx1
 x x...
 x2x..x
.xxx...
.x...x.
...x.xx
   xxx1
 x x...
.x2x..x
.xxx...
.x...x.
...x.xx
   xxx1
.x x...
.x2x..x
.xxx...
.x...x.
...x.xx
.  xxx1
.x x...
.x2x..x
.xxx...
.x...x.
...x.xx
.. xxx1
.x x...
.x2x..x
.xxx...
.x...x.
...x.xx
...xxx1
.x x...
.x2x..x
.xxx...
.x...x.
...x.xx
...xxx1
.x.x...
.x2x..x
.xxx...
.x...x.
...x.xx
DONE: 24 moves
Important: With this algorithm, a "dead-end" is counted as the total number of moves. So the algorithm counts its total steps.
And: If we could figure out some way to estimate our progress, the agent could reach its goal with fewer steps.
Make_move: This recursive method copies the maze and then plays a move. It records the lowest count of moves when the end point is reached.
20 moves: This is the optimal solution. I verified it by counting characters (more than once).
Python program that finds optimal path
maze = """
   xxx1.
 x x   .
 x2x  x.
 xxx   .
 x   x .
   x xx""";
def get_maze_lists(maze):
    lines_raw = maze.split(".")
    lines_stripped = list(map(lambda line: line.strip("\n"), lines_raw))
    result = []
    for line in lines_stripped:
        row = []
        for char in line:
            if char == "x":
                row.append(-1)
            elif char == "1":
                row.append(1)
            elif char == "2":
                row.append(-3)
            else:
                row.append(0)
        result.append(row)
    return result
def valid_pos(maze_lists, row, new_row, new_column):
    if new_row < 0: return False
    if new_column < 0: return False
    if new_row >= len(maze_lists): return False
    if new_column >= len(maze_lists[row]): return False
    return True
def make_move(maze_lists_temp, row, column, move_count, lowest):
    # This method copies the lists, and uses recursion to find paths.
    moves = [[-1, 0],
             [0, -1], [0, 1],
             [1, 0]]
    # Create new list and copy each exist in grow into it.
    maze_lists = []
    for row_temp in maze_lists_temp:
        maze_lists.append(row_temp[:])       
    value = maze_lists[row][column]
    if value >= 1:
        for move in moves:
            new_row = row + move[0]
            new_column = column + move[1]
            if valid_pos(maze_lists, row, new_row, new_column):
                test_value = maze_lists[new_row][new_column]
                if test_value == 0:
                    maze_lists[new_row][new_column] = value + 1
                    # Make new move.
                    make_move(maze_lists, new_row, new_column, move_count + 1, lowest)
                elif test_value == -3:
                    # See if lowest move.
                    if move_count + 1 < lowest[0]:
                        lowest[0] = move_count + 1
    return -1
# Initialize.
data = get_maze_lists(maze)
# Find start square.
for i in range(len(data)):
    row = data[i]
    for x in range(len(row)):
        # See if start square.
        if row[x] == 1:
            lowest = [1000]
            make_move(data, i, x, 0, lowest)
            # Print optimal move count.
            print("Optimal moves:", lowest[0])
Output
Optimal moves: 20