综合实验报告——迷宫(maze)

综合实验报告——迷宫(maze)

项目简介:

  • 代码量小,注释详细
  • 具有图形化页面,可以自定义色彩
  • 实现了搜索过程的可视化
  • 可以随机生成一定有通路的迷宫,并且可以进行相应的操作,比如移动等
  • 添加了一些额外的操作,比如重新生成迷宫,寻找最短距离的通路

    ……

代码讲解:

注意:这里导进了第三方库easyx图形库,所以必须得先导进去才有效果

  步骤:请下载最新版 EasyX 安装程序,直接运行,并跟随提示安装即可。


  在生成迷宫过程中采用了bfs(广度优先),生成通路时也是采用了bfs,利用bfs的特性就一定可以找到最短距离的通路。

   生成: 利用比较简单的bfs生成简易迷宫,迷宫里面分成了许多的单元格,每个单元格里面记录了五个数据,分别是当前单元格的状态,上下左右单元格的状态(是否能走向这四个方向),利用随机法,每一次只打通一面,且打通的面肯定是联通的,这也保证了一定有路。在生成迷宫中比较精华的是设置了带访问点,且随机去访问这些待访问点且只打通一面,从而产生了迷宫效果。

  **生成通路:**同样也是利用bfs进行搜索,上述已经说过了一定有解,所利用bfs就一定可以生成一条最短的通路。

效果展示:

图片展示

码源:

由于代码量不多就直接发出来了!

#include<easyx.h>
#include<vector>//可以访问的边
#include<stdlib.h>
#include<time.h>
#include<conio.h>
#include<graphics.h>
#include <stdio.h>

#define MAP_ROW 20	/*25*/
#define MAP_COL 25	/*45*/

#define OUT_ROW MAP_ROW - 1
#define OUT_COL MAP_COL - 1

struct Node
{
	int flag;			//表示关键结点是否访问过 0:未访问 1:已访问 2:待访问
									// 3:人物 4:目的地
	bool left, right, top, buttom;	//表示这个节点周围的四堵墙 0:不可通过的墙 1:可通过的空地
};
//搜索中所用来保存节点的
struct node{
	int x ; int y; int pre;
}query[MAP_ROW*MAP_COL*4];

int map_v[MAP_ROW][MAP_COL] = { 0 };//0-没访问过,1-已经访问过,2-最短路

int index_Row, index_Col;			//人所在行,列

void init(Node map[][MAP_COL]);

int bfs(int x, int y);

void drawMap(Node map[][MAP_COL]);

void play(Node map[][MAP_COL]);

void borderThrough(Node map[][MAP_COL], const COORD node);

int judg(Node map[][MAP_COL])//判断是否到达了终点
{
	if (map[OUT_ROW][OUT_COL].flag == 3)
		return 1;
	else
		return 0;
}
int main()
{
	srand((unsigned)time(NULL));
	initgraph(MAP_COL * 45, MAP_ROW * 30);      //设置了整张地图的规格
	Node map[MAP_ROW][MAP_COL] = { 0 };			//每个墙 都没打通的  每个结点 都是没有访问过
	int map_v[MAP_ROW][MAP_COL] = { 0 };

	init(map);							/*初始化地图*/
	drawMap(map);
	while (1)
	{
		play(map);
		drawMap(map);
		if (judg(map))
		{
			MessageBox(GetHWnd(), L"恭喜你赢了", L"Vectory", MB_OK);
			memset(map, 0, sizeof(Node) * MAP_ROW * MAP_COL);
			init(map);
			drawMap(map);
		}
	}
	getchar();
	closegraph();
	return 0;
}
int bfs(int x, int y,const Node map[][MAP_COL])//**存在bug
{
	int first = 1, second = 1;//模拟队列
	map_v[x][y] = 1;
	query[second].pre = -1;
	query[second].x = x;
	query[second].y = y;
	second++;
	while (second > first) {
		int xx = query[first].x, yy = query[first].y;//行,列
		map_v[xx][yy] = 1;
		if (map[xx][yy].top == 1) 
		{//上面可走
			if (xx >= 1 && xx <= MAP_ROW - 1 && yy >= 0 && yy <= MAP_COL-1&&map_v[xx-1][yy]==0) {
				query[second].pre = first;
				query[second].x = xx - 1;
				query[second++].y = yy;
			}
		}
		if (map[xx][yy].buttom == 1)
		{//下面可以走
			if (xx >=0 && xx <= MAP_ROW - 2 && yy >= 0 && yy <= MAP_COL - 1&&map_v[xx + 1][yy] == 0) {
				query[second].pre = first;
				query[second].x = xx+1;
				query[second++].y = yy;
				if (xx == MAP_ROW - 2 && yy == MAP_COL - 1) { return second-1; }//走到了终点,并且返回头指针
			}
		}
		if (map[xx][yy].left == 1)
		{//左面可以走
			if (xx >= 0 && xx <= MAP_ROW - 1 && yy >= 1 && yy <= MAP_COL - 1 && map_v[xx][yy-1] == 0) {
				query[second].pre = first;
				query[second].x = xx;
				query[second++].y = yy-1;
			}
		}
		if (map[xx][yy].right == 1)
		{//右面可以走
			if (xx >= 0 && xx <= MAP_ROW - 1 && yy >= 0 && yy <= MAP_COL - 2 && map_v[xx][yy+1] == 0) {
				query[second].pre = first;
				query[second].x = xx;
				query[second++].y = yy+1;
				if (xx == MAP_ROW - 1 && yy == MAP_COL - 2) { return second-1; }//走到了终点,并且返回头指针
			}
		}
		first++;
	}
	return 1;//一般情况下是不会出现这个结果的
}
void ans(int start) //传入first头指针
{
	int x = query[start].x, y = query[start].y;
	map_v[x][y] = 2;
	if (query[start].pre != -1) {
		ans(query[start].pre);
	}
	return;
}
void play(Node map[][MAP_COL])
{
	/*******玩家操作*******/
	char dir;
	/******找到小人******/
	for (int Row = 0; Row < MAP_ROW; Row++)
	{
		for (int Col = 0; Col < MAP_COL; Col++)
		{
			if (map[Row][Col].flag == 3)
			{
				index_Row = Row;
				index_Col = Col;
			}
		}
	}
	dir = _getch();//从键盘获取值
	switch (dir)
	{
	case 'q':					//寻找通路的方法
		setfillcolor(RGB(255,165,0));//实现按钮闪变
		solidrectangle(25 * 40, 100, 25 * 45, 150);
		settextstyle(18, 0, _T("楷体"));
		settextcolor(RGB(255,106,106));
		outtextxy(25 * 40 + 8, 115, _T("寻找通路"));
		ans(bfs(0, 0,map));
		drawMap(map);
		break;
	case 'r':					// 重新生成迷宫
		setfillcolor(RGB(255, 165, 0));
		solidrectangle(25 * 40, 200, 25 * 45, 250);
		settextstyle(18, 0, _T("楷体"));
		settextcolor(RGB(255, 106, 106));
		outtextxy(25 * 40 + 8, 225, _T("重新生成"));
		Sleep(100);
		memset(map, 0, sizeof(Node) * MAP_ROW * MAP_COL);
		init(map);			/*初始化地图*/
		drawMap(map);
		break;
	
	case 'w':
	case 72:
		//判断人物所在点上 top 是否可以通行
		if (map[index_Row][index_Col].top == 1 && index_Row >= 1)
		{
			map[index_Row][index_Col].flag = 1;
			map[index_Row - 1][index_Col].flag = 3;
		}
		break;
	case 's':
	case 80:
		//判断人物所在点 buttom 是否可以通行
		if (map[index_Row][index_Col].buttom == 1 && index_Row < MAP_ROW - 1)
		{
			map[index_Row][index_Col].flag = 1;
			map[index_Row + 1][index_Col].flag = 3;
		}
		break;
	case 'a':
	case 75:
		//判断人物所在点 left 是否可以通行
		if (map[index_Row][index_Col].left == 1 && index_Col >= 1)
		{
			map[index_Row][index_Col].flag = 1;
			map[index_Row][index_Col - 1].flag = 3;
		}
		break;
	case 'd':
	case 77:
		//判断人物所在点 right 是否可以通行
		if (map[index_Row][index_Col].right == 1)
		{
			map[index_Row][index_Col].flag = 1;
			map[index_Row][index_Col + 1].flag = 3;
		}
		break;
	}
}

void init(Node map[][MAP_COL])
{
	setfillcolor(WHITE);		//填充颜色 白色
	setlinecolor(BLACK);		//线条颜色  红色
	setlinestyle(PS_SOLID, 2);	//线条风格
	//对查询表进行重置
	for (int i = 0; i < MAP_ROW * MAP_COL * 4; i++) {
		query[i].pre = 0;
		query[i].x = 0;
		query[i].y = 0;
	}
	//先选中一个结点  最左上角  arr[0][0]
	map[0][0].flag = 1;			//这个节点已经访问过

	for (int i = 0; i < MAP_ROW; i++) {
		for (int j = 0; j < MAP_COL; j++) {
			map_v[i][j] = { 0 };//对查询访问数组进行初始化
		}
	}

	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;					//待访问
	int m;

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

		/*周围的四个节点(如果有) 全部放到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 };
		}
		if (waitForVisit[m].Y - 1 >= 0 && map[waitForVisit[m].X][waitForVisit[m].Y - 1].flag == 0)
		{
			//左
			map[waitForVisit[m].X][waitForVisit[m].Y - 1].flag = 2;
			waitForVisit[len++] = { waitForVisit[m].X, waitForVisit[m].Y - 1 };
		}
		if (waitForVisit[m].Y + 1 < MAP_COL && map[waitForVisit[m].X][waitForVisit[m].Y + 1].flag == 0)
		{
			//右
			map[waitForVisit[m].X][waitForVisit[m].Y + 1].flag = 2;
			waitForVisit[len++] = { waitForVisit[m].X, waitForVisit[m].Y + 1 };
		}
		//map[m]已经访问过  从map里面删掉就可以
		if (m == len - 1)//没有可以添加的待访问点
			len--;
		else
		{ //对已经访问过的节点进行更新
			waitForVisit[m] = waitForVisit[len - 1];
			len--;
		}
		drawMap(map);
	}

	map[0][0].flag = 3;							//初始化人物
	map[MAP_ROW - 1][MAP_COL - 1].flag = 4;		//初始目的地
	
}

void drawMap(Node map[][MAP_COL])
{
	BeginBatchDraw();
	cleardevice();									//清屏操作
	for (int i = 0; i < MAP_ROW; ++i)
	{
		for (int j = 0; j < MAP_COL; ++j)
		{
			if (map[i][j].flag == 0)				//没有访问过这个节点
			{
				setfillcolor(WHITE);
				if (map_v[i][j] == 2) {
					setfillcolor(RGB(255, 48, 48));
				}
			}
			else if (map[i][j].flag == 1)			//访问过
			{
				setfillcolor(RGB(255, 193, 193));
				if (map_v[i][j] == 2) {
					setfillcolor(RGB(255, 48, 48));
				}
			}
			else if (map[i][j].flag == 2)			//待访问
			{
				setfillcolor(BLUE);
				if (map_v[i][j] == 2) {
					setfillcolor(RGB(255, 48, 48));
				}
			}
			else if (map[i][j].flag == 3)			//人物                          
			{
				setfillcolor(RED);
			}
			else if (map[i][j].flag == 4)			//目的地
			{
				setfillcolor(GREEN);
			}
			solidrectangle(j * 40, i * 30, j * 40 + 39, i * 30 +29);	//绘制
			if (map[i][j].top == 0)				//没有打通
			{
				line(j * 40, i * 30, j * 40 + 39, i * 30);
			}
			if (map[i][j].buttom == 0)
			{
				line(j * 40, i * 30 + 29, j * 40 + 39, i * 30 + 29);
			}
			if (map[i][j].left == 0)
			{
				line(j * 40, i * 30, j * 40, i * 30 + 29);
			}
			if (map[i][j].right == 0)
			{
				line(j * 40+39, i * 30, j * 40 + 39, i * 30 + 29);
			}
		}
	}
	setfillcolor(RGB(240,255,255));
	solidrectangle(25 * 40, 0, 25 * 45, 20 * 30-1);
	setbkmode(TRANSPARENT);//设置当前设备图案填充和文字输出时的背景模式
	rectangle(25 * 40, 100, 25 * 45, 150);
	rectangle(25 * 40, 200, 25 * 45, 250);
	RECT r = {25 * 40, 300, 25 * 45, 500};
	setfillcolor(RGB(238,238,209));
	solidrectangle(25 * 40, 100, 25 * 45, 150);
	solidrectangle(25 * 40, 200, 25 * 45, 250);
	settextstyle(18, 0, _T("楷体"));
	settextcolor(RGB(0,0,0));
	outtextxy(25 * 40 + 8, 115, _T("寻找通路(q)"));
	outtextxy(25 * 40 + 8, 225, _T("重新生成(r)"));
	drawtext(_T("* * * * *\n(要在英文输入状态下)\n按压方向箭头进行移动\n\n按压r,q可以进行相应操作"), &r, DT_WORDBREAK);

	EndBatchDraw();//结束绘画,并呈现出来
}

void borderThrough(Node map[][MAP_COL], const COORD node)
{
	int flag = 0;
	int dir;
	do
	{
		//判断周围的结点 有哪些是可以打通的   可以打通就打通
		dir = rand() % 4;
		switch (dir)
		{
		case 0:
			if ((node.X + 1 < MAP_ROW) && map[node.X + 1][node.Y].flag == 1)//下
			{
				map[node.X + 1][node.Y].top = 1;		//打通两堵墙
				map[node.X][node.Y].buttom = 1;
				flag = 1;
				break;
			}
		case 1:
			if ((node.Y - 1 >= 0) && map[node.X][node.Y - 1].flag == 1)//左
			{
				map[node.X][node.Y - 1].right = 1;		//打通两堵墙
				map[node.X][node.Y].left = 1;
				flag = 1;
				break;
			}
		case 2:
			if (node.Y + 1 < MAP_COL && map[node.X][node.Y + 1].flag == 1)//右
			{
				map[node.X][node.Y + 1].left = 1;		//打通两堵墙
				map[node.X][node.Y].right = 1;
				flag = 1;
				break;
			}
		case 3:
			if ((node.X - 1 >= 0) && map[node.X - 1][node.Y].flag == 1)//上
			{
				map[node.X - 1][node.Y].buttom = 1;
				map[node.X][node.Y].top = 1;
				flag = 1;
				break;
			}
		}
	} while (!flag);
}

欢迎大家访问我的个人博客

本文章到此结束!感谢阅读!

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值