c++c++11recursion2d-vector

Backtracking logic error with maze solving program


I've written a simple recursive maze solving problem that uses recursion to find the least number of moves to solve. However, at dead ends, the program has trouble backing up the traced path.

To try to solve the problem, I started to write inverse functions of the move functions. They could be used to reverse the path, but would still need some way to identify which one to use.

Maze test file:

SWWWW
OOOOE
OWOWW
OOOWW

Code body:

//Read in maze
ifstream mazeFile;
mazeFile.open("MazeSample.txt");

vector<string> mazeRead{ istream_iterator<string>(mazeFile), istream_iterator<string>() };
maze = mazeRead;


    //Move checks
vector<string> maze;
int numMoves;
int leastMoves = 1000;

int row;
int column;

bool canMoveUp(int row, int column) {
    try {
        if (maze.at(row - 1).at(column) != ('O')) {
            cout << "(Can't move up)" << endl;
            if (maze.at(row - 1).at(column) == 'E') {
                return true;
            }
            return false;
        }
    }
    catch (const out_of_range& error) {
        cout << "(Can't move up)" << endl;
        return false;
    }
    return true;
}

bool canMoveDown(int row, int column) {
    try {
        if (maze.at(row + 1).at(column) != ('O')) {
            cout << "(Can't move down)" << endl;
            if (maze.at(row + 1).at(column) == 'E') {
                return true;
            }
            return false;
        }
    }
    catch (const out_of_range& error) {
        cout << "(Can't move down)" << endl;
        return false;
    }
    return true;
}

bool canMoveLeft(int row, int column) {
    try {
        if (maze.at(row).at(column - 1) != ('O')) {
            cout << "(Can't move left)" << endl;
            if (maze.at(row).at(column - 1) == 'E') {
                return true;
            }
            return false;
        }
    }
    catch (const out_of_range& error) {
        cout << "(Can't move left)" << endl;
        return false;
    }
    return true;
}

bool canMoveRight(int row, int column) {
    try {
        if (maze.at(row).at(column + 1) != ('O')) {
            cout << "(Can't move right)" << endl;
            if (maze.at(row).at(column + 1) == 'E') {
                return true;
            }
            return false;
        }
    }
    catch (const out_of_range& error) {
        cout << "(Can't move right)" << endl;
    }
    return true;
}


    //Maze solve function
void solve(int row, int column) {
    numMoves = numMoves + 1; //count moves

    //Base case (solution found; current position is 'E')
    if (maze[row][column] == 'E') {
        if (numMoves < leastMoves) {
            leastMoves = numMoves;
        }
    }

    if (maze[row][column] != 'E') {
        maze[row][column] = 't'; //mark path
    }

    // move up and see if move leads to solution (recursively)
    if (canMoveUp(row, column)) {
        cout << "(Move up)" << endl;
        row = row - 1;
        column = column;
        solve(row, column);
    }

    // if move chosen above doesn't lead to solution, move down & check
    if (canMoveDown(row, column)) {
        cout << "(Move down)" << endl;
        row = row + 1;
        column = column;
        solve(row, column);
    }

    // if move chosen above doesn't lead to solution, move left & check
    if (canMoveLeft(row, column)) {
        cout << "(Move left)" << endl;
        row = row;
        column = column - 1;
        solve(row, column);
    }

    // if move chosen above doesn't lead to solution, move right & check
    if (canMoveRight(row, column)) {
        cout << "(Move right)" << endl;
        row = row;
        column = column + 1;
        solve(row, column);
    }

    // if no above solution works, then unmark cell
    //backtrack (keeps going until all solutions reached)
    maze[row][column] = 'O';
    cout << "Mark as 'O'";
    numMoves = numMoves - 1;


    //TODO: PROBLEM: ROW/COLUMN NOT RESET AFTER STUCK; KEEPS SAME VALUE
            //Questionable code
    if (!canMoveUp(row, column)) {
        //Inverse of canMove?
        row = row + 1;
        column = column;
    }


    //Display vector contents
    cout << endl;
    for (int row = 0; row < maze.size(); row++) {
        cout << endl;
        for (int column = 0; column < maze[row].size(); column++) {
            cout << maze[row][column];
        }
    }
    cout << endl;
}

When hitting a dead end, I expected the maze to back up to the last junction with movement options. Instead, the cursor goes back and forth, failing to solve. This is likely due to the implementation of the move functions; if able to move, it sets the row/column variables to the new space.

The error path appears as follows, toggling itself between 't' and 'O' at row 1, column 1:

SWWWW
tttOE
tWtWW
tttWW

SWWWW
tOtOE
tWtWW
tttWW

Upon not being able to move in any of the four directions, I'd expect the code to undo the previous movement(s) until reaching the last junction.


Solution

  • Without looking too deep into your algorithm

    if (canMoveUp(row, column)) {
        cout << "(Move up)" << endl;
        row = row - 1;
        column = column;
        solve(row, column);
    }
    

    looks fishy. You are changing the variables row and column that you are using in subsequent blocks. If canMoveUp is true, the next check will be canMoveDown('originalrow' - 1, column) which will fail (because it adds 1 to the row again and checks the field you just marked with t.

    Shouldn't it be

    if (canMoveUp(row, column)) {
        cout << "(Move up)" << endl;
        solve(row - 1, column);
    }