Easyx图形库小游戏---迷宫

在这里插入图片描述

来源:微信公众号「编程学习基地」

迷宫生成

​ ① 十字分割 递归版本

​ ② BFS(即广度算法)

十字分割方法生成

​ 要求初始时迷宫内全是通路,然后随机十字建墙,然后随机在三面墙上打洞,使四个子空间连通。

​ 要求:十字点横纵坐标均要求为偶数(即地图行列为奇数),打洞点要求为奇数。

DFS 方法生成:

​ 像一只地鼠打洞一般,迷宫要求初始时全是阻碍(墙),然后随机方向打洞(挖墙)。

​ 要求,待挖墙的通路(打洞方向)只能与访问过的节点处打穿。

实战演练

十字分割

非常简单的一个方法,不过游戏效果不是很好。下面介绍下算法过程:

首先全部围起来,然后做一个十字墙

在这里插入图片描述

打通十字墙任意三堵墙

在这里插入图片描述

递归生成十字墙,然后打通任意三堵墙

在这里插入图片描述

然后就生成了最简单的迷宫(其实没啥卵用的迷宫,就当是温习递归)

DFS 方法

其实就是一种挖墙算法,嗯,我是这样认为的。详细讲解一下这个算法。

先看一下定义地图的Node结构,

#define MAP_ROW 20	
#define MAP_COL 25
Node map[MAP_ROW][MAP_COL] = {
    
     0 };	//每个墙 都没打通的  每个结点 都是没有访问过
struct Node
{
    
    
	int flag;						//表示关键结点是否访问过 0:未访问 1:已访问 2:待访问
									// 3:人物 4:目的地
	bool left, right, top, buttom;	//表示这个节点周围的四堵墙 0:不可通过的墙 1:可通过的空地
};

然后就是生成迷宫地图,也就是初始化函数init(),先选中左上角作为迷宫的入口,也是人物所在地

map[0][0].flag = 1;			//这个节点已经访问过

定义辅助数组储存待访问节点

COORD waitForVisit[MAP_COL * MAP_ROW];	//存放待访问的结点
int len = 0;							//map里面的坐标的个数

同时已访问节点周围是待访问节点,将其添加到辅助数组里面去,

waitForVisit[len++] = {
    
     1, 0 };		//下方的结点 可以访问
map[1][0].flag = 2;					//待访问
waitForVisit[len++] = {
    
     0, 1 };		//右边的结点 可以访问
map[0][1].flag = 2;					//待访问

效果如下图:

在这里插入图片描述

在绘制地图时设定白色为未访问节点粉红色为访问过的节点,蓝色为待访问节点

if (map[i][j].flag == 0)				//没有访问过这个节点
{
    
    
	setfillcolor(WHITE);
}
else if (map[i][j].flag == 1)			//访问过
{
    
    
	setfillcolor(RGB(255, 193, 193));  
}
else if(map[i][j].flag == 2)			//待访问
{
    
    
	setfillcolor(BLUE);
}

然后就是逐个访问待访问节点,务必要求每个节点都访问到,也就是广度优先搜索

len为待访问节点的数量,从中随机选取一个节点进行访问,并将访问后该节点周围的未访问节点初始化为待访问节点

while (len > 0)
{
    
    
	//随机选取其中的一个结点  进行访问
	m = rand() % len;	//从可以访问的结点中随机取一个
	/*打通这个节点  把这个节点相邻的结点放到map里面*/
	borderThrough(map, waitForVisit[m]);	//borderThrough作用:打通waitForVisit[m]节点的任意一面(上下左右任意一面)
	map[waitForVisit[m].X][waitForVisit[m].Y].flag = 1;		/*标记已访问*/
}

打通节点函数borderThrough()的作用就是将第二个传入参数所在节点的任意一面(上下左右任意一面)打通,打通的要求是该面相邻的节点是已经访问过了的节点

void borderThrough(Node map[][MAP_COL], const COORD node)///函数原型

打通条件:

(node.X + 1 < MAP_ROW) && map[node.X + 1][node.Y].flag == 1	//向下打通
(node.Y - 1 >= 0) && map[node.X][node.Y - 1].flag == 1		//向左打通

打通之后该节点设置为已访问节点(flag==1),然后周围的四个节点(未访问节点,如果有的话)设置成待访问节点。

m为随机选取的待访问节点下标

/*周围的四个节点(如果有) 全部放到map里面*/
if (waitForVisit[m].X - 1 >= 0 && map[waitForVisit[m].X - 1][waitForVisit[m].Y].flag == 0)
{
    
    
	//如果上方的结点没有访问过   设置为待访问 并且把这个位置放到map里面
	map[waitForVisit[m].X - 1][waitForVisit[m].Y].flag = 2;	/*标记待访问*/
	waitForVisit[len++] = {
    
     waitForVisit[m].X - 1, waitForVisit[m].Y };/*添加进待访问节点辅助数组里面*/
}
if (waitForVisit[m].X + 1 < MAP_ROW && map[waitForVisit[m].X + 1][waitForVisit[m].Y].flag == 0)
{
    
    
	//下
	map[waitForVisit[m].X + 1][waitForVisit[m].Y].flag = 2; /*标记待访问*/
	waitForVisit[len++] = {
    
     waitForVisit[m].X + 1, waitForVisit[m].Y };/*添加进待访问节点辅助数组里面*/
}

已经被标记为访问的节点将其从待访问节点数组里面删除

//map[m]已经访问过  从map里面删掉就可以
if (m == len - 1)
	len--;
else
{
    
    
	waitForVisit[m] = waitForVisit[len - 1];
	len--;
}

整个过程慢动作回放如下:

在这里插入图片描述

人物的移动已经讲解过很多次了,可通过上一篇/下一篇查看往期[推箱子]等教程。

更多详细操作后台发送关键字【迷宫算法】获取源码

猜你喜欢

转载自blog.csdn.net/qq_44519484/article/details/109280047