迷宫游戏算法

来源:互联网 发布:mac 终端连接linux 编辑:程序博客网 时间:2024/05/22 13:07

      前两天在慕课网学习c++时,老师留下了一道作业题,关于迷宫算法的:自己设置一个迷宫,里面有起点和终点,然后程序要帮助闯关者找到逃生的路径【起点——出口】。当时老师给出的提示是使用“左手扶墙”或“右手扶墙”策略,大意就是,比如“左手扶墙”,从起点开始走时,就始终保持着左手扶墙的状态,这样一直走,最终你会找到迷宫的出口。嗯,这种方法确实挺实用的,但恐怕不适用于一些特殊的路形,比如“日”字型迷宫,如果我们把起点放在“日”字中间那一横的某一点上,那么无论闯关者使用左手扶墙还是右手扶墙策略,最终他都将陷入一个死循环中。。。

       考虑到上面的这种情况,我觉得有必要新设计一个程序算法。而这种算法,我暂且称之为“灌水法”,顾名思义,就是在起点处一直灌水,最后水一定会从出口流出去。而活水所走的路径,就是程序所要寻找的路径。

      程序的核心算法很简单,首先设计一个迷宫类,类里面开辟两个数组,一个是地图数组,一个是路径数组,除此之外,还需要两个变量,一个代表初始位置,一个代表出口位置。在地图数组中,每个元素用1和0来表示有路和无路;而在路径数组中,每个节点元素只能在这四个数值中选择一个:1、-1、16和-16,这是因为我用的是16*16的一维矩阵,而上面的四哥元素值可以分别对应矩阵中的上下左右。

      程序运行后,首先调用maze类的初始化函数来设置迷宫,至于如何初始化迷宫程序员可以随意设置。重点在下面的两个函数,首先说put_water,它每次先寻找一个单向路径,如果找到,就记录下这个单向路径的走法,直到遇到一个岔路口为止,在此之后它会递归调用自身,依次进入每个岔路口并摸清它们的情况,因为函数调用栈开销的特性,这样当程序从某个死路回溯时,就会重新刷新原先记录的record数组。。。接下来是显示路径函数showpath,这个就要简单多了,无非是先把被破坏的迷宫复位,然后按照record数组记录的走法显示出逃生路径。其中,“森”字表示障碍物,“起”表示起点,“终”字表示出口位置,“人”字代表逃生所走的路径,本来老师要求的是动态显示出逃生过程,但为了本文的简洁,我就不写这些冗余的代码了,以下源代码


此为类声明:

class maze{public:maze(int m[], int r[], int , int );int put_water(int, int);void showpath(int *);protected:int map[256], record[256], Start, Exit;};

此为类定义:

#include <iostream>#include "maze.h"using namespace std;maze::maze(int m[], int r[], int s, int e){for (int i = 0; i < 256; i++){map[i] = m[i];if (i % 16 == 0)  printf("\n");if (m[i])  printf("  ");else printf("森");}for (int i = 0; i < 256; i++)  record[i] = r[i];Start = s;Exit = e;}int maze::put_water(int p, int count)// p表示当前位置,count配合record记录以寻找的路径{int new_p;while (map[p - 1] + map[p - 16] + map[p + 1] + map[p + 16] == 1) //如果当前位置不是岔路口,则一直记录直至到岔路口或出口为止{if (p == Exit)        {            record[count] = 0;            return 1;        }map[p] = 0; //配合这个while循环,就这样一直把水灌满这个单向路new_p = (p - 1)*map[p - 1] + (p - 16)*map[p - 16] + (p + 1)*map[p + 1] + (p + 16)*map[p + 16]; //寻找单向路的下一个节点record[count] = new_p - p; //记录下来这个单向路的走法count++;p = new_p;}map[p] = 0;if (map[p - 1] + map[p - 16] + map[p + 1] + map[p + 16] > 1) //这里表示这个路口属于岔路口,至少有两个选择,最多有三个选择{if (1 == map[p - 1]) //先往上寻找,表达式为真表示上面有路{record[count] = -1;if (1 == put_water(p - 1, count + 1)) //既然上面有路,就递归调用自身,看看上面的路情况如何return 1;}if (1 == map[p - 16]) //再往左寻找,表达式为真表示左方有路{record[count] = -16;if (1 == put_water(p - 16, count + 1)) //同上return 1;}if (1 == map[p + 1]) //再往右寻找,表达式为真表示右方有路{record[count] = 1;if (1 == put_water(p + 1, count + 1))return 1;}if (1 == map[p + 16]) //最后往下寻找,表达式为真表示下面有路{record[count] = 16;if (1 == put_water(p + 16, count + 1))return 1;}}return 0;}void maze::showpath(int m[]){printf("\n\n我们逃跑的路径是:\n");for (int i = 0; i < 256; i++) {map[i] = m[i];}map[10] = -9;for (int i = 0; record[i]; i++){Start += record[i];map[Start]++;}for (int i = 0; i < 256; i++){if (i % 16 == 0)  printf("\n");switch (map[i]){case 0:printf("森");break;case 1:printf("  ");break;case 2:printf("人");break;case 9:printf("起");break;case -9:printf("终");}}}

最后这个是主函数:

#include <iostream>#include "maze.h"int MAPARR[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0,0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0,0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0,0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0,0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0,0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,0, 1, 1, 1, 0, 9, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0,0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0,0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0,0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0,0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, RECORD[256];int main(){maze m(MAPARR,RECORD,149,26);if (0 == m.put_water(149, 0)){printf("迷宫设计有误!");return 0;}else  m.showpath(MAPARR);return 0;}


运行效果如图:









5 0