How to find the fastest route in a maze (in C) [duplicate]

人盡茶涼 提交于 2019-11-28 13:05:58

Because there is no solution of Dijkstra's algorithm in the question linked by @Jackson, here is a modified algorithm adapted for this maze problem. I take the maze value 0 as no-go, and 1 as the distance for the algorithm.

It looks as though the neigh() function should be recursive, but it is not, it is only there so that 4 neighbours can be examined in the same way. Note that not every path is followed: for a large maze that could be O(no!). Also that the search is not directed to the end point: the end point is encountered, and these features of the algorithm give it its beauty.

#include <stdio.h>
#include <stdio.h>
#include <limits.h>

#define MSIZE  7                            // matrix/maze dims

enum ntype { virgin, listed, visited };     // for status field

typedef struct {
    int x;                                  // grid position of node
    int y;                                  // grid position of node
    int value;                              // 0 or 1 as defined in maze[][]
    int dist;                               // distance from start node
    int status;                             // enum as above
} node_t;

int maze[MSIZE][MSIZE] =                     // maze definition
  { { 1, 1, 1, 1, 1, 1, 1 },
    { 0, 1, 0, 1, 0, 0, 1 },
    { 0, 1, 0, 1, 1, 1, 1 },
    { 0, 1, 0, 0, 0, 1, 1 },
    { 0, 1, 1, 1, 0, 1, 1 },
    { 0, 0, 1, 0, 1, 1, 1 },
    { 1, 1, 1, 1, 0, 1, 1 } };

node_t node [MSIZE][MSIZE];                 // working array
node_t *list[MSIZE*MSIZE];                  // array of current node pointers
int listlen;                                // num elements in list[]

void neigh(node_t *cptr, int dx, int dy)
// examine one neighbour of node cptr, offset by dx,dy
{
    node_t *nptr;                           // pointer to neighbour
    int dist;                               // accumulated distance from start
    int x = cptr->x + dx;                   // work out neighbour coords
    int y = cptr->y + dy;                   // work out neighbour coords
    if (x < 0 || x >= MSIZE || y < 0 || y >= MSIZE)
        return;                             // failed edge test    
    nptr = &node[y][x];                     // point to neighbour
    if (nptr->value == 0)                   // no-go node
        return;
    if (nptr->status == visited)            // do no re-visit
        return;

    dist = cptr->dist + nptr->value;        // accumulate distance from start
    if (dist < nptr->dist)                  // if it's less than what was known...
        nptr->dist = dist;                  // ...update with the new distance
    if (nptr->status == virgin) {           // if it's never been seen...
        list[listlen++] = nptr;             // ... neighbour to list
        nptr->status = listed;              // and set its status
    }
}

int main(void)
{
    int i, j, smallest, smallind;
    node_t *cptr, *eptr;

    // init the struct array
    for (j=0; j<MSIZE; j++) {
        for (i=0; i<MSIZE; i++) {
            cptr = &node[j][i];             // pointer to the array element
            cptr->value = maze[j][i];       // the maze definition
            cptr->x = i;                    // self's position
            cptr->y = j;                    // self's position
            cptr->dist = INT_MAX;           // distance from start (unknown)
            cptr->status = virgin;          // never examined
        }
    }

    eptr = &node[MSIZE-1][MSIZE-1];         // pointer to end node

    cptr = &node[0][0];                     // pointer to start node
    cptr->dist  = 0;                        // distance of start node from itself!

    // main loop
    while (cptr != eptr) {                  // until we reach the target node
        cptr->status = visited;             // we've been here now
        neigh(cptr,  0, -1);                // examine node above
        neigh(cptr, -1, 0);                 // examine node on left
        neigh(cptr,  1, 0);                 // examine node on right
        neigh(cptr,  0, 1);                 // examine node below

        // find smallest distance of nodes in list[] (won't include virgins)
        smallest = INT_MAX;
        smallind = -1;                      // set invalid marker index
        for (i=0; i<listlen; i++) {
            if (smallest > list[i]->dist) { // compare distance with smallest
                smallest = list[i]->dist;   // remembers the smallest
                smallind = i;               // remembers the list index of smallest
            }
        }
        // take smallest for next time and remove from list
        if(smallind < 0) {                  // -1 was the "marker"
            printf("No route found\n");
            return 1;
        }
        cptr = list[smallind];              // smallest becomes current node
        if (listlen)                        // replace in list with last element...
           list[smallind] = list[--listlen];// ... and reduce list length
    }                                       // now examine this node

    printf("Distance = %d\n", eptr->dist);  // show the distance of the end node
    return 0;
}

Program output:

Distance = 12

EDIT The algorithm itself is described in the link, if it breaks there are sure to be other explanations available. I have added more comments to the code.

I use 3 arrays, maze[][] is a simple maze definition like yours. Then node[][] is an array of struct which hold runtime data. I could have initiliased this with the maze definition directly, but because the program this came from had hard-coded test data of one matrix size, and a bigger matrix with data read from file, I left it as it was.

The third array list[] is an array of pointers to the node[][] elements. This is why node[][] elements contain the x,y coordinates within the struct itself: so that I can know the position from the pointer alone. I could have worked with a 1-D maze array, when I could have stored a single index in list[], if so this complication would not have been necessary. But I used a 2-D array, just because it felt "nicer".

I am comfortable working with pointers. In the first nested loop pair in main() headed // init the struct array the first thing I do is to make a pointer, for use in setting the struct fields, because I find the repeated use of array indexing clumsy. So I write for example cptr->x = i; which has the same intent as node[j][i].x = i;.

Fundamentally, this is a search problem. There are lots of well written posts on how to solve it, so I'll talk about a few of the finer points that your will need to consider.

Since you specify want the shortest path, and not just a "pretty good" path, you will need to check every possible route. Depth-first search would be my suggestion.

Most obviously, this is search without reuse. The shortest path will not contain any loops or any case where the cursor will return to a place where it has already been. This keeps the search area reasonable.

If runtime is not an issue, you can write a function to try every possible route, and returning the minimum.

int path_helper(int maze[][N],int height, int width, int row, int col){
  if(row==height-1 && col == width-1) return 0;
  if(maze[row][col]==0) return height*width;
  if(col<0 || row<0 || row>=height || col>=width) return height*width;
  maze[row][col]=0;
  int maze2[7][7]={{0}};
  for(int i = 0; i < height; i++){
    for(int j = 0; j < width; j++)
      maze2[j][i]=maze[j][i];
  }
  return min(min(path_helper(maze2,height,width,row+1,col),
  path_helper(maze2,height,width,row-1,col)),      min(path_helper(maze2,height,width,row,col+1),path_helper(maze2,height,width,row,col-1)))+1;
}

it's ugly but it should do the trick

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!