简体   繁体   中英

Solve a maze backtracking

I am trying to solve a maze using backtracking in C. To solve the maze the following rules:

  • You begin from the position of S and need to go towards E
  • You can only go on '.' path
  • Convert all the '.' into '#' including S and E

The input consists of amxn matrix: Input example:

11 11
+-+-+-+-+-+
S.|...|...|
+.+.+.+-+.+
|.|.|.....|
+.+-+-+-+.+
|...|.|...|
+.+.+.+.+-+
|.|...|.|.|
+.+-+.+.+.+
|...|.....E
+-+-+-+-+-+

Expected solution :

+-+-+-+-+-+
##|...|...|
+#+.+.+-+.+
|#|.|.....|
+#+-+-+-+.+
|###|.|...|
+.+#+.+.+-+
|.|###|.|.|
+.+-+#+.+.+
|...|######
+-+-+-+-+-+

I am trying really hard to solve it but for some reason my program doesn't go back once I reached a point in the maze from where I can't go further.It just go in all the directions where it sees a '.'

My idea was to start from the position of S and using at each recursion step the old position that we were. I will go in all the direction from the position where I am standing if the position that I am looking at is a '.' and if that point was not my old position.

I also think that I have a problem when i reach a point where it was a crossroad when I backtrack. For example:

+-+-+-+-+-+
##|...|...|
+#+.+.+-+.+
|#|.|.....|
+#+-+-+-+.+
|0##|.|...|
+.+#+.+.+-+
|.|###|.|.|
+.+-+#+.+.+
|..1|######
+-+-+-+-+-+

Imagine that I am in the position 0. And I backtracked from 1 changing back the # into '.'.How can I make a statement saying that you have 2 # possibilities to go back , yet you should stop?

My code :

#include <stdio.h>
#include <stdlib.h>

void *safeMalloc(int n) {
    void *p = malloc(n);
    if (p == NULL) {
        printf("Error: malloc(%d) failed. Out of memory?\n", n);
        exit(EXIT_FAILURE);
    }
    return p;
}


char ** readMatrix(int m,int n,int* startI,int* startJ,int* endI,int* endJ){
    char **arr = safeMalloc(m*sizeof(char *));
    int row;
    for (row=0; row < m; row++) {
        arr[row] = safeMalloc(n*sizeof(char));
    }
    int i,j;
    for(i=0;i<m;i++){
        for(j=0;j<m;j++){
            scanf(" %c",&arr[i][j]);
            if(arr[i][j]=='S'){
                *startI=i;
                *startJ=j;
            }
            if(arr[i][j]=='E'){
                *endI=i;
                *endJ=j;
            }
        }
        getchar();
    }

    return arr;
}

void printNumber(char **arr,int m,int n){
    int i,j;
    for(i=0;i<m;i++){
        for(j=0;j<n;j++){
            printf("%c", arr[i][j]);
        }
        printf("\n");
    }
}

void findPath(char** arr,int m,int n,int startI,int startJ,int endI,int endJ,int oldI,int oldJ){
    int i=startI,j=startJ;
    int stepsPossible=4;
            //going up
           if(i-1>=0){
                if((arr[i-1][j]=='.') && ((i-1!=oldI) || (j!=oldJ))){
                    arr[i][j]='#';
                    oldI=i;
                    oldJ=j;
                    findPath(arr,m,n,i-1,j,endI,endJ,oldI,oldJ);
                }else{
                    stepsPossible--;
                }
           }
           //going right

           if(j+1<n){
                if((arr[i][j+1]=='.') && ((i!= oldI) || (j+1!=oldJ))){
                    arr[i][j]='#';
                    oldI=i;
                    oldJ=j;
                    findPath(arr,m,n,i,j+1,endI,endJ,oldI,oldJ);
                }else{
                    stepsPossible--;
                }
           }
           //going left
           if(j-1>=0){
                if((arr[i][j-1]=='.') && ((i!= oldI) || (j-1!=oldJ))){
                    arr[i][j]='#';
                    oldI=i;
                    oldJ=j;
                    findPath(arr,m,n,i,j-1,endI,endJ,oldI,oldJ);
                }else{
                    stepsPossible--;
                }
           }

           //going down
            if(i+1<m){
                if((arr[i+1][j]=='.') && ((i+1!= oldI) || (j!=oldJ))){
                    arr[i][j]='#';
                    oldI=i;
                    oldJ=j;
                    findPath(arr,m,n,i+1,j,endI,endJ,oldI,oldJ);
                }else{
                    stepsPossible--;
                }
           }
        //if the next block is E then we can stop.
           if((arr[i-1][j]=='E') || (arr[i][j+1]=='E') || (arr[i][j-1]=='E') || (arr[i+1][j]=='E')){
                if(arr[i-1][j]=='E'){
                    arr[i-1][j]='#';
                }

                if(arr[i][j+1]=='E'){
                    arr[i][j+1]='#';
                }

                if(arr[i][j-1]=='E'){
                    arr[i][j-1]='#';
                }

                if(arr[i+1][j]=='E'){
                    arr[i+1][j]='#';
                }
                return;
            }


            if(stepsPossible==0){
                if(arr[i-1][j]=='#'){
                    arr[i][j]='.';
                    oldI=i;
                    oldJ=j;
                    findPath(arr,m,n,i-1,j,endI,endJ,oldI,oldJ);
                }else{
                    return;
                }

                if(arr[i][j+1]=='#' ){
                    arr[i][j]='.';
                    oldI=i;
                    oldJ=j;
                    findPath(arr,m,n,i,j+1,endI,endJ,oldI,oldJ);
                }else{
                    return;
                }

                if(arr[i][j-1]=='#' ){
                    arr[i][j]='.';
                    oldI=i;
                    oldJ=j;
                    findPath(arr,m,n,i,j-1,endI,endJ,oldI,oldJ);
                }else{
                    return;
                }

                if(arr[i+1][j]=='#' ){
                    arr[i][j]='.';
                    oldI=i;
                    oldJ=j;
                    findPath(arr,m,n,i+1,j,endI,endJ,oldI,oldJ);
                }else{
                    return;
                }
            }
}


int main()
{
    int m,n;
    scanf("%d %d",&m,&n);
    int startI,startJ,endI,endJ;
    char** arr;
    arr=readMatrix(m,n,&startI,&startJ,&endI,&endJ);
    findPath(arr,m,n,startI,startJ,endI,endJ,startI,startJ);
    printNumber(arr,m,n);
    return 0;
}

I would suggest to use BFS because

  • BFS will find the shortest solution.
  • DFS handles certain mazes very very poorly.

Here is short description of BFS for your case:

  1. Find 'S' in your maze and add it into queue
  2. While queue is not empty check get element from queue. Replace element with "#". If element is E, you are done. Check neighbours(up, down, left right) of the element, if they are ".", then add into queue.
  3. If queue is empty and E not found, then there is no direct path from S to E

Make proper use of the return value, as you can utilize it to simplify your logic. Choose different return values on findPath for the error case (backtracking necessary) and the success case (end reached).

Now you can put setting the # unconditionally in the start of that function, and resetting back to . unconditionally in the end for the backtracking case.

There is no need to count possible directions either, only checking if some call returned success.

Also no need to write the boundary checks over and over again, if you just check them at the start of the function you can just pass invalid coordinates without any issues.

bool findPath(char** arr, size_t sx, size_t sy, int x, int y) {
    if (x < 0 || x >= sx || y < 0 || y >= sy) return false;
    if (arr[x][y] == 'E') {
        are[x][y] = '#';
        return true;
    }
    if (arr[x][y] != '.') return false;
    arr[x][y] = '#';
    bool success = findPath(arr, sx, sy, x-1, y) ||
        findPath(arr, sx, sy, x+1, y) ||
        findPath(arr, sx, sy, x, y-1) ||
        findPath(arr, sx, sy, x, y+1);
    if (!success) arr[x][y] = '.';
    return success;
}

Implementations for backtracking algorithms usually all follow the same pattern:

  1. Try to either trivially reject or accept the current solution.
  2. Modify the current solution.
  3. Try variations.
  4. Clean up the modifications if not successful.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM