COMP 142 Project 5: Recursive Rat Maze

The Math and CS department at Rhodes has discovered a bunch of rats lurking on the 4th floor of Ohlendorf Hall. We decided to put them to work solving mazes since that's what they do best. You will write a program to run a rat through a maze, searching for a piece of cheese.

Program overview

You will write a program to open a text file containing a description of a maze, use a recursive algorithm to have the rat find its way through the maze, and print out a solution to the maze as well as the number of steps it took to find the solution.

Maze text file

Each text file describing a maze is organized into lines: each line contains a single string, and all the strings will have the same number of characters.

Here is an example file:

#R#
#..
C.#
This file describes a maze laid out in a 3-by-3 grid, though a maze may have any number of rows and any number of columns. The hash marks (#) specify obstacles in the maze that the rat must go around; there are also implicit walls surrounding the boundary of the maze (so the rat cannot leave the boundaries of the maze). The starting position of the rat is given by the letter R, and the cheese that the rat is seeking is given by the letter C. Periods specify open squares in the grid, where the rat can move to as it searches for the cheese.

I suggest you read the lines of a maze file into a vector of strings, i.e., a vector<string>. If you do this, you can treat this vector as a 2-d grid of characters. In other words, say you read the lines of the file into a vector<string> called maze. You can then use maze[row][col] to access any square of the grid. For instance, in the maze above, maze[0][1] = 'R' and maze[2][0] = 'C'. Note that we use single quotes because R and C are individual characters.

Solving the maze

You will write a recursive algorithm to find the correct path through the maze. In the explanation that follows, we call this algorithm solve_maze, though you can name your function whatever you want.

Here is pseudocode for the outline of solve_maze:

boolean solve_maze(row, col):
  // row and col represent the current position of the rat as it moves 
  // through the maze, searching for the cheese.
  // The return value (boolean) specifies whether or not the maze can be solved
  // from the current (row, col) position.
  
  Print the current position of the rat.
  // This printing step is not needed in the algorithm, but I want you to include it
  // so I can check that your recursion is correct.
  
  Is our current (row, col) where the cheese is?
    If yes, return true.
    
  // If no, keep going below.
  
  Drop a breadcrumb at our current (row, col) position.
  
  Can the rat move NORTH from its current position? 
    If yes, try to solve the maze from one position north.
    If the maze can be solved from one position north, return true.
  
  Can the rat move SOUTH from its current position? 
    If yes, try to solve the maze from one position south.
    If the maze can be solved from one position south, return true.
  
  Can the rat move EAST from its current position? 
    If yes, try to solve the maze from one position east.
    If the maze can be solved from one position east, return true.
  
  Can the rat move WEST from its current position? 
    If yes, try to solve the maze from one position west.
    If the maze can be solved from one position west, return true.
    
  // If we get here, it means the maze cannot be solved from our current position,
  // since trying all four possible directions failed.  We must be in a dead end.
  
  Pick up the breadcrumb from our current position because breadcrumbs only mark
  the solution, and we didn't find one from here.
  
  Return false (indicating we're stuck)
We can explain the recursive formulation as follows. The base cases are when we either (a) find the cheese and return true, or (b) get stuck in a dead end and return false. There are four recursive cases, though we may not have to call all four in every possible situation. Instead, we call them opportunistically. First, we see if we can move one spot north and solve the maze from there. If we can, we're done, so we return true. If we can't solve the maze from one spot north, we'll try south, then east, then west. If none of the directions yielded a solution, then we conclude that we've reached a dead end, so we return false.

What are breadcrumbs?

The "breadcrumbs" are used for marking what squares in the grid we've already visited, for two reasons. First, they will prevent us from going in circles, and second, they will allow us to show the completed path from rat to cheese after it is discovered. Therefore, the lines of code in the algorithm above that ask if the rat can move in a certain direction should not only take into account the placement of walls and obstacles, but also the breadcrumbs.

A breadcrumb is placed in a square at the beginning of the function. It is only removed if we reach a dead end, therefore, when the algorithm finds the cheese, we can be guaranteed that there will be a single trail of breadcrumbs from start to finish. In your code, you will represent breadcrumbs by a lowercase letter 'o'. When the code asks you to drop a breadcrumb, you will literally alter a character in maze[row][col] from a period to a lowercase 'o'. If you ever pick up the breadcrumb, you will change the 'o' back to a period.

Note that the 'R' symbol never moves in the maze; it is used just to signify where the rat begins. All the "moving" in the maze is accomplished with the row and col arguments that are passed (recursively) to solve_maze. In other words, the current location of the rat is the row/col of the current call to solve_maze. Because of the recursion, the rat will automatically backtrack if it finds itself in a dead end. A nice explanation of this is here.

Note: This algorithm is guaranteed to find a solution to the maze if it exists. For some mazes, it will not necessarily find the shortest path if there are multiple paths the rat could take.

Additional things the program must do

After your program solves the maze, it must print out

Suggested functions

I suggest you write the following functions:

Coding style

The standard coding guidelines with respect to functions, commenting, honor code, etc., should be followed.

Helpful hints

To guarantee that your program works exactly the same as mine, you should make sure your rat always checks the directions in the order north, south, east, west.

Challenge problem:

It turns out we don't need an extra function to count the number of breadcrumbs at the end of the program. We can change the recursive formulation of solve_maze to give us this information as the maze is being solved. Change your recursive formulation so that the return value of solve_maze is an int representing the length of the solution path from the current (row, col) to the cheese (instead of a boolean representing whether the maze is solvable from the current (row, col)). Describe your new recursive formulation in a comment at the top of your code.

Sample input and output (four samples)

Your output should match this, especially in terms of the sequence of rat locations, the total calls to solve_maze, the number of breadcrumbs dropped, and the solution picture.

Your program only has to solve one maze each time you run it; the output below is four separate trials.

Enter a maze filename: maze1.txt
The rat is at 0, 1.
The rat is at 1, 1.
The rat is at 2, 1.
The rat is at 2, 0.
Solution found!
solve_maze was called 4 times.
There are 3 breadcrumbs.
Solution: 
#o#
#o.
Co#

=============================================================

Enter a maze filename: maze2.txt
The rat is at 1, 0.
The rat is at 0, 0.
The rat is at 2, 0.
The rat is at 1, 1.
The rat is at 1, 2.
The rat is at 0, 2.
The rat is at 2, 2.
The rat is at 2, 3.
Solution found!
solve_maze was called 8 times.
There are 4 breadcrumbs.
Solution: 
.#.#
ooo#
.#oC

=============================================================

Enter a maze filename: maze3.txt
The rat is at 3, 9.
The rat is at 4, 9.
The rat is at 4, 8.
The rat is at 4, 7.
The rat is at 4, 6.
The rat is at 3, 6.
The rat is at 2, 6.
The rat is at 1, 6.
The rat is at 0, 6.
The rat is at 0, 7.
The rat is at 0, 8.
The rat is at 1, 8.
The rat is at 1, 9.
The rat is at 1, 5.
The rat is at 1, 4.
The rat is at 0, 4.
The rat is at 0, 3.
The rat is at 0, 2.
The rat is at 1, 2.
The rat is at 2, 2.
The rat is at 3, 2.
The rat is at 4, 2.
The rat is at 4, 3.
The rat is at 4, 4.
Solution found!
solve_maze was called 24 times.
There are 18 breadcrumbs.
Solution: 
##ooo#...##
..o#ooo#..#
##o###o####
#.o##.o##o#
##ooC#oooo#

=============================================================

Enter a maze filename: maze4.txt
The rat is at 0, 1.
The rat is at 1, 1.
The rat is at 2, 1.
The rat is at 3, 1.
The rat is at 4, 1.
The rat is at 5, 1.
The rat is at 6, 1.
The rat is at 7, 1.
The rat is at 8, 1.
The rat is at 9, 1.
The rat is at 10, 1.
The rat is at 11, 1.
The rat is at 5, 2.
The rat is at 5, 3.
The rat is at 5, 4.
The rat is at 5, 5.
The rat is at 3, 2.
The rat is at 3, 3.
The rat is at 2, 3.
The rat is at 1, 3.
The rat is at 1, 4.
The rat is at 1, 5.
The rat is at 2, 5.
The rat is at 3, 5.
The rat is at 3, 6.
The rat is at 3, 7.
The rat is at 4, 7.
The rat is at 5, 7.
The rat is at 5, 8.
The rat is at 5, 9.
The rat is at 5, 10.
The rat is at 5, 11.
The rat is at 6, 11.
The rat is at 7, 11.
The rat is at 8, 11.
The rat is at 9, 11.
The rat is at 7, 10.
The rat is at 7, 9.
The rat is at 8, 9.
The rat is at 9, 9.
The rat is at 9, 8.
The rat is at 9, 7.
The rat is at 8, 7.
The rat is at 7, 7.
The rat is at 7, 6.
The rat is at 7, 5.
The rat is at 7, 4.
The rat is at 7, 3.
The rat is at 8, 3.
The rat is at 9, 3.
The rat is at 10, 3.
The rat is at 11, 3.
The rat is at 12, 3.
The rat is at 13, 3.
The rat is at 13, 2.
The rat is at 13, 1.
The rat is at 14, 1.
The rat is at 15, 1.
The rat is at 15, 2.
The rat is at 15, 3.
The rat is at 15, 4.
The rat is at 15, 5.
The rat is at 14, 5.
The rat is at 13, 5.
The rat is at 12, 5.
The rat is at 11, 5.
The rat is at 11, 6.
The rat is at 11, 7.
The rat is at 11, 8.
The rat is at 11, 9.
The rat is at 11, 10.
The rat is at 11, 11.
The rat is at 11, 12.
The rat is at 11, 13.
The rat is at 12, 13.
The rat is at 13, 13.
The rat is at 13, 14.
The rat is at 13, 15.
The rat is at 13, 16.
The rat is at 13, 17.
The rat is at 13, 18.
The rat is at 13, 19.
The rat is at 13, 20.
The rat is at 13, 21.
The rat is at 12, 21.
The rat is at 11, 21.
The rat is at 10, 21.
The rat is at 9, 21.
The rat is at 8, 21.
The rat is at 7, 21.
The rat is at 6, 21.
The rat is at 5, 21.
The rat is at 9, 20.
The rat is at 9, 19.
The rat is at 10, 19.
The rat is at 11, 19.
The rat is at 11, 18.
The rat is at 11, 17.
The rat is at 11, 16.
The rat is at 11, 15.
The rat is at 10, 15.
The rat is at 9, 15.
The rat is at 9, 14.
The rat is at 9, 13.
The rat is at 8, 13.
The rat is at 7, 13.
The rat is at 7, 14.
The rat is at 7, 15.
The rat is at 6, 15.
The rat is at 5, 15.
The rat is at 4, 15.
The rat is at 3, 15.
The rat is at 3, 16.
The rat is at 3, 17.
The rat is at 4, 17.
The rat is at 5, 17.
The rat is at 6, 17.
The rat is at 7, 17.
The rat is at 7, 18.
The rat is at 7, 19.
The rat is at 6, 19.
The rat is at 5, 19.
The rat is at 4, 19.
The rat is at 3, 19.
The rat is at 3, 20.
The rat is at 3, 21.
The rat is at 2, 21.
The rat is at 1, 21.
The rat is at 1, 20.
The rat is at 1, 19.
The rat is at 1, 18.
The rat is at 1, 17.
The rat is at 1, 16.
The rat is at 1, 15.
The rat is at 1, 14.
The rat is at 1, 13.
The rat is at 2, 13.
The rat is at 3, 13.
The rat is at 4, 13.
The rat is at 5, 13.
Solution found!
solve_maze was called 140 times.
There are 121 breadcrumbs.

Solution: 
#o#####################
#o#ooo....#.#ooooooooo#
#o#o#o###.#.#o#######o#
#ooo#ooo#...#o#ooo#ooo#
#.#####o#####o#o#o#o###
#.....#ooooo#C#o#o#o#.#
#.#########o###o#o#o#.#
#.#ooooo#ooo#ooo#ooo#.#
#.#o###o#o#.#o#######.#
#.#o#..ooo#.#ooo#..ooo#
#.#o###########o###o#o#
#.#o#ooooooooo#ooooo#o#
###o#o#######o#######o#
#ooo#o#.....#ooooooooo#
#o###o#####.###########
#ooooo................#
#######################