简体   繁体   English

C中的迷宫求解算法

[英]Maze solving algorithm in C

basically, I'm trying to implement an algorithm in C that can solve a maze using the right-hand or left-hand rule. 基本上,我正在尝试在C中实现一个可以使用右手或左手规则解决迷宫的算法。 I get a triangular maze like this in a file: 我在文件中得到了这样的三角形迷宫:

迷宫

I have already implemented functions to parse the file and load the maze into a dynamic 2D array. 我已经实现了解析文件的函数并将迷宫加载到动态2D数组中。 From the values in the array, I can tell whether each field has or doesn't have a right border, left border, and vertical border (top or bottom, depending on the position of the field). 从数组中的值,我可以判断每个字段是否具有右边框,左边框和垂直边框(顶部或底部,具体取决于字段的位置)。 I'm also told the coordinates of the starting point (ie 6,1 in this particular example) and the initial border to follow (ie bottom) and, of course, whether to use the right hand or the left hand rule to find the exit. 我还告诉起点的坐标(即这个特定例子中的6,1)和要遵循的初始边界(即底部),当然,还要使用右手或左手规则来查找出口。

I'm supposed to list the coordinates of the fields that lead to the exit. 我应该列出通往出口的字段的坐标。 I've basically implemented all the necessary functions to parse and check the maze validity, but I don't exactly understand how to put a right hand and left hand rule in an algorithm. 我基本上已经实现了解析和检查迷宫有效性的所有必要功能,但我并不完全理解如何在算法中放置右手和左手规则。 I hope this is enough information to understand what I'm trying to do. 我希望这是足够的信息,以了解我正在尝试做什么。 I don't necessarily need specific code, I only need to understand how to actually go about this. 我不一定需要特定的代码,我只需要了解如何实际执行此操作。

Thanks. 谢谢。

Let's formulate the right-hand rule in terms of the triangular maze cells. 让我们根据三角形迷宫细胞制定右手规则。 Suppose we have entered a cell by crossing its bottom edge, as indicated by a gray arrow in the diagram below. 假设我们通过穿过其底边进入了一个单元格,如下图中的灰色箭头所示。

右手统治

The right-hand rule tells us to keep our right hand on a wall. 右手规则告诉我们将右手放在墙上。 When we enter a cell, where is the wall? 当我们进入一个牢房时,墙在哪里?

In the first case above, there is no wall to the immediate right. 在上面的第一个案例中,右边没有墙。 There must be a wall somewhere to the right of us, so we want to keep turning right until we hit it. 在我们右边的某个地方必须有一堵墙,所以我们要继续向右转,直到我们击中它。 Turning right in the triangular maze means turning 60 degrees clockwise and stepping over the triangle's edge into the adjacent cell. 在三角形迷宫中向右转动意味着顺时针旋转60度并跨过三角形边缘进入相邻的单元格。

In the second case, there is a wall on the immediate right when we enter the cell. 在第二种情况下,当我们进入牢房时,右边有一堵墙。 We want to keep this wall to our right, so we turn left and step to the adjacent cell in that direction. 我们希望将这个墙保持在我们的右边,所以我们向左转并沿着那个方向步进到相邻的单元。

In the third case, there are walls on both sides. 在第三种情况下,两侧都有墙壁。 We must turn around and leave the cell. 我们必须转身离开牢房。 We're returning to the previous cell by moving in the opposite direction, so the wall to the right will be a different edge of the triangle this time. 我们通过向相反方向移动返回到前一个单元格,因此这次右边的墙将成为三角形的不同边缘。

To follow the left-hand rule, we use similar reasoning on a mirror image of the illustration above. 为了遵循左手规则,我们在上图中的镜像上使用类似的推理。

Next, we need a numerical representation of the grid cells. 接下来,我们需要网格单元格的数字表示。 There are various ways to number the cells and edges. 有多种方法可以对单元格和边缘进行编号。 One possibility is shown in the diagram below. 一种可能性如下图所示。 The edges of every triangle are numbered 0, 1, 2. At left are the two orientations of a triangle, showing the edge numbering for each. 每个三角形的边缘编号为0,1,2。左边是三角形的两个方向,显示每个三角形的边编号。 In the center are the eight possible wall configurations for each triangle orientation. 在中心是每个三角形方向的八种可能的墙壁配置。 At right are the decimal and binary representations of each wall configuration, using the edge number to index binary digits from right to left, ie , from least significant digit to most significant digit. 右边是每个墙配置的十进制和二进制表示,使用边数来从右到左索引二进制数字, 从最低有效数字到最高有效数字。

边缘编号和墙壁配置

With this numbering scheme, the maze shown in the question can be represented with the following text. 使用此编号方案,问题中显示的迷宫可以用以下文本表示。 The first line contains the number of rows and columns in the grid. 第一行包含网格中的行数和列数。 The second line describes our starting point: row number, column number, crossed edge. 第二行描述了我们的起点:行号,列号,交叉边。 The remaining lines contain the cell descriptions. 其余行包含单元格描述。

6 7
6 1 2
4 2 1 1 5 0 3
4 1 2 0 2 0 1
4 0 1 0 1 3 4
4 2 7 4 0 1 1
6 4 1 1 6 4 2
2 2 6 0 2 2 6

The final piece of the implementation is a transition matrix that lets us look up the next state in a maze traversal given the current state and the edge we're crossing. 实现的最后一部分是一个转换矩阵,它允许我们在迷宫遍历中查找下一个状态,给定当前状态和我们正在越过的边缘。

Before describing the transition matrix, let me be quite clear about how we're numbering the grid. 在描述转换矩阵之前,让我非常清楚我们如何对网格进行编号。 Externally, we'll number rows and columns starting from one, as shown in the diagram. 在外部,我们将从1开始对行和列进行编号,如图所示。 That's how we'll read the starting location and it's how we'll display the solution to the user. 这就是我们如何阅读起始位置,以及我们如何向用户显示解决方案。 Within the program, however, it is convenient to number the rows and columns starting from zero, because that's how the C language indexes arrays. 但是,在程序中,从零开始对行和列进行编号很方便,因为这就是C语言索引数组的方式。 For instance, we'll say the top left cell is in row 0 and column 0 . 比如,我们会说的左上角单元格是行0和列0 This means that if we have a two-dimensional integer array called maze , we refer to the upper left cell as maze[0][0] . 这意味着如果我们有一个叫做maze的二维整数数组,我们将左上角的单元格称为maze[0][0]

Given the row index r and column index c of a triangular grid cell, using zero-based numbering, let's figure out the triangle orientation from the parity of r and c . 给定三角形网格单元的行索引r和列索引c ,使用从零开始的编号,让我们从rc的奇偶校验中找出三角形方向。 If they have the same parity, meaning they are both odd or both even, the triangle points downward. 如果它们具有相同的奇偶校验,意味着它们都是奇数或两者都是偶数,则三角形指向下方。 Otherwise, it points upward. 否则,它指向上方。 An easy way to implement this is by calculating (r+c)%2 , which is 0 if the parities are the same and 1 if the parities differ. 实现这一点的一种简单方法是计算(r+c)%2 ,如果奇偶校验相同则为0如果奇偶校验不同则为1

Next, we have to take into account the edge that we're crossing. 接下来,我们必须考虑到我们正在越过的边缘。 If we know the orientation of the triangle we're leaving and the number of the edge that we're crossing, we can work out: 如果我们知道我们要离开的三角形的方向以及我们正在越过的边缘的数量,我们可以解决:

  • The row number of the triangle that we're entering. 我们输入的三角形的行号。

  • The column number of the triangle that we're entering. 我们输入的三角形的列号。

  • The number of the edge that we crossed, in the context of the newly entered triangle. 在新输入的三角形的上下文中,我们交叉的边数。

All of this information is represented in the following three-dimensional array. 所有这些信息都在以下三维数组中表示。

    moves[2][3][3] = {
        { {-1, 0, 1}, {0, 1, 2}, {0, -1, 0} },
        { {0, 1, 2}, {1, 0, 0}, {0, -1, 1} } };

The first index is for the triangle orientation, with 0 for a downward triangle and 1 for an upward triangle. 第一个索引用于三角形方向, 0表示向下三角形, 1表示向上三角形。 The second index is the number of the edge that we're crossing, using the edge numbering of the triangle that we're leaving. 第二个索引是我们正在越过的边缘的数量,使用我们要离开的三角形的边编号。 The third index is used to look up the three pieces of information listed above. 第三个索引用于查找上面列出的三条信息。

  • 0: The change in the row number. 0:行号的变化。

  • 1: The change in the column number. 1:列号的变化。

  • 2: The edge number we just crossed, using the edge numbering of the new triangle. 2:我们刚刚越过的边数,使用新三角形的边编号。

For instance, let's look at the first move in the sample maze. 例如,让我们看看样本迷宫中的第一步。 We start in row 5 , column 0 . 我们从第55 0列开始。 The sum parity is 1 , meaning an upward triangle. 总和奇偶校验为1 ,表示向上三角形。 We're crossing edge 0 . 我们正越过边缘0 Thus, we look at maze[1][0] . 因此,我们看maze[1][0] The resulting information is {0, 1, 2} , telling us that in the new cell: 结果信息是{0, 1, 2} ,告诉我们在新单元格中:

  • The row index changes by 0 . 行索引更改为0

  • The column index changes by 1 . 列索引更改为1

  • We just crossed edge 2 . 我们刚刚越过边缘2

Now we can apply the algorithm in a loop that ends once we have left the bounds of the maze. 现在我们可以在一个循环中应用算法,一旦我们离开迷宫的边界就结束了。

Below is an ANSI C implementation of the concepts I've discussed. 下面是我讨论过的概念的ANSI C实现。 You will have to adapt it to whatever file format you're using to represent the maze. 您将不得不将其调整为您用来表示迷宫的任何文件格式。 Please remember that in the description of the starting location, my format specifies the edge that was crossed to enter the maze. 请记住,在起始位置的描述中,我的格式指定了进入迷宫的交叉边缘。

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

int **maze, row_num, col_num,
    moves[2][3][3] = {   /* moves[orientation][edge] == {dr, dc, crossed} */
        { {-1, 0, 1}, {0, 1, 2}, {0, -1, 0} },
        { {0, 1, 2}, {1, 0, 0}, {0, -1, 1} } };

void go(int r, int c, int crossed, int turn) {
  int edge, *move;
  while (1) {
    if (r < 0 || r >= row_num || c < 0 || c >= col_num) {
      printf("out\n\n"); /* We've left the maze. */
      return;
    }                    /* Increment the indices for external display. */
    printf("%d %d\n", r+1, c+1);
    edge = crossed;      /* We'll consider the crossed edge last. */
    while (1) {          /* Turn and look for an open edge. */
      edge = (edge+turn+3)%3;
      if ((maze[r][c] & (1 << edge)) == 0) {
        break;
      } 
    } 
    move = moves[(r+c)%2][edge];
    r += move[0];        /* After looking up the move, update the */
    c += move[1];        /*  cell position and the edge number. */
    crossed = move[2];
  }
}

int main() {
  int r_start, c_start, crossed_start, r, c;
  scanf("%d %d", &row_num, &col_num);
  scanf("%d %d %d", &r_start, &c_start, &crossed_start);
  --r_start;             /* We decrement the cell indices because */
  --c_start;             /*  they are zero-based internally. */
  maze = (int **) malloc(row_num*sizeof(int *));
  for (r = 0; r < row_num; ++r) {
    maze[r] = (int *) malloc(col_num*sizeof(int));
    for (c = 0; c < col_num; ++c) {
      scanf("%d", &maze[r][c]);
    }
  }
  printf("\nRight-hand rule:\n");
  go(r_start, c_start, crossed_start, -1);
  printf("Left-hand rule:\n");
  go(r_start, c_start, crossed_start, 1);
  return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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