(Exercise 8.16 Maze Traversal) This exercise takes me quite a long time to finish. It is about travelling from an entrance of a maze to find the exit. The book introduces a famous algorithm to travelse maze - the wall follower, which is also known as the left-hand rule or the right-hand rule.
Imagine you are in the maze. What you need to do is just keeping your right hand(okay, left hand, if you prefer) on the wall on your right(or left). Then, you keep walking with your right hand on the wall. Eventually, you will arrive at either the exit (or the entrance) of the maze. This algorithm cannot gurantee a shortest path, but it work well when the entrance and exit areat the edge of the maze. (Why do we have this condition? It is because a loop may be formed if the entrance or exit is not at the edge, when the barriers are not connected to outermost wall!)
In my implementation, I use some special techniques to improve the readibility. For example, I use a struct State to record the current state of the maze walker, which include his x-coordinate, y-coordinate and the direction he is facing. To facilitate the use of array subsrcipts, the y-coordinate is reversed so walking to the south is increasing y but not decreasing it. Also, I use an enum Direction to represent the four directions: north, east, south and west. Using enum is quite tricky as you can see how I control the walker to turn left or right by manipulating the variable of type Direction. Functions are also used extensively to avoid repetition of codes.
Okay, time to show my code. Hope you enjoy reading it. And I also encourage you to test it in your computer. You may modify the maze or the position of the entrance and exit to see what happen! If you find any bugs or have any doubts, please feel free to contact me!
// 8.16 Maze Traversal
#include <iostream>
using namespace std;
const int mazeSize = 12;
enum Direction { north, east, south, west }; // for readability
struct State
{
int x;
int y;
Direction front;
};
void printMaze( char maze[][mazeSize+1] );
void look( Direction front, int &x, int &y ); // look at the front and store the square's index in x and y
void advance( State &myState, int nextX, int nextY, char ¤tSquare, char &nextSquare );
bool mazeTraverse( char maze[][mazeSize+1], State& myState, State& startState ); // false when no exit
int main()
{
char maze[][mazeSize+1] // +1 as there is a '\0' at the end of each string
= {
{"############"},
{"#...#......#"},
{"..#.#.####.#"},
{"###.#....#.#"},
{"#....###.#.."},
{"####.#.#.#.#"},
{"#..#.#.#.#.#"},
{"##.#.#.#.#.#"},
{"#........#.#"},
{"######.###.#"},
{"#......#...#"},
{"############"}
};
State start = { 11, 4, west };
State current = start;
maze[start.y][start.x] = 'X';
printMaze( maze );
bool hasExit = mazeTraverse( maze, current, start );
if( hasExit ) cout << "The exit of the maze has been found!" << endl;
else cout << "Oops! You return to the entrance again!" << endl;
return 0;
}
bool mazeTraverse( char maze[][mazeSize+1], State& myState, State& startState )
{
Direction right = (Direction)((myState.front+1) % 4);
int frontX = myState.x;
int frontY = myState.y;
int rightX = myState.x;
int rightY = myState.y;
look( right, rightX, rightY ); // find the index of right square
// see if there is a wall on the right
if( maze[rightY][rightX] == '#' ) // try to follow the wall
{
look( myState.front, frontX, frontY );
// check whether the exit or entry is reached
if( frontX < 0 || frontX >= mazeSize || frontY < 0 || frontY >= mazeSize )
{
if( myState.x == startState.x && myState.y == startState.y ) return false;
else return true;
}
if( maze[frontY][frontX] == '#' ) // wall at the front
{
myState.front = (Direction)((myState.front+4-1) % 4); // turn left
}
else // no wall at the front
{
advance( myState, frontX, frontY, maze[myState.y][myState.x], maze[frontY][frontX] );
printMaze( maze );
}
}
else // no wall on right, turn right and advance
{
myState.front = right;
look( myState.front, frontX, frontY );
advance( myState, frontX, frontY, maze[myState.y][myState.x], maze[frontY][frontX] );
printMaze( maze );
}
return mazeTraverse( maze, myState, startState ); // next decision
}
void printMaze( char maze[][mazeSize+1] )
{
static bool firstPrint = true;
static bool promptToContinue = false;
for( int i=0; i<mazeSize; i++ )
{
for( int j=0; j<mazeSize; j++ )
{
cout << maze[i][j];
}
cout << endl;
}
cout << endl;
if( firstPrint )
{
char answer;
cout << "We are going to solve the maze!" << endl;
cout << "Do you want to watch it step by step?" << endl;
cout << "Enter Y if you want, or N otherwise: ";
cin >> answer;
if( answer == 'Y' || answer == 'y' )
promptToContinue = true;
cout << endl;
firstPrint = false;
}
else if( promptToContinue )
{
char anything[10];
cout << "Input any character to continue: ";
cin >> anything;
}
}
void look( Direction front, int &x, int &y )
{
int pastX = x;
int pastY = y;
switch( front )
{
case north:
y--;
break;
case east:
x++;
break;
case west:
x--;
break;
case south:
y++;
break;
default:
cerr << "Error - Invalid direction" << endl;
};
}
void advance( State &myState, int nextX, int nextY, char ¤tSquare, char &nextSquare )
{
if( nextSquare == 'X' )
currentSquare = '.'; // clear the current square from the path
myState.x = nextX;
myState.y = nextY;
nextSquare = 'X'; // mark as current path
}