计算机软件实习3—基于WIN32的迷宫设计

课前预习

课程要求

  1. 迷宫游戏是非常经典的游戏,在该题中要求随机生成一个迷宫,并求解迷宫;
  2. 要求查找并理解迷宫生成的算法,并尝试用两种不同的算法来生成随机的迷宫。
  3. 要求游戏支持玩家走迷宫,和系统走迷宫路径两种模式。玩家走迷宫,通过键盘方向键控制,并在行走路径上留下痕迹;系统走迷宫路径要求基于 A*算法实现,输出走迷宫的最优路径并显示。 设计交互友好的游戏图形界面。

题目难点

  1. 要随机生成迷宫,并且要给出解决方案。
  2. 支持玩家走迷宫和系统走迷宫两种方案。
  3. 设计友好的交互界面。

迷宫实现所需的算法原理

在这里,我们选择了Prime算法来作为实现随机生成迷宫的算法,下面我们先了解一下prime算法。
Prime算法的核心步骤:
在带权连通图中V是包含所有顶点的集合, U已经在最小生成树中的节点,从图中任意某一顶点v开始,此时集合U={v}。
重复执行下述操作:
在所有u∈U,w∈V-U的边(u,w)∈E中找到一条权值最小的边,将(u,w)这条边加入到已找到边的集合,并且将点w加入到集合U中,当U=V时,就找到了这颗最小生成树。
下面我们通过图示法来演示一下工作流程:
在这里插入图片描述
首先,确定起始顶点。在这里以顶点A作为起始点。根据查找法则,与点A相邻的点有点B和点H,比较AB与AH,AB的权值小,因此我们选择点B,如下图。并将点B加入到U中。
在这里插入图片描述
继续下一步,此时集合U中有{A,B}两个点,再分别以这两点为起始点,根据查找法则(选取权值较小的边),找到边BC(当有多条边权值相等时,可选任意一条),如下图。并将点C加入到U中。
在这里插入图片描述
继续,此时集合U中有{A,B,C}三个点,根据查找法则,我们找到了符合要求的边CI,如下图。并将点I加入到U中。
在这里插入图片描述
继续,此时集合U中有{A,B,C,I}四个点,根据查找法则,找到符合要求的边CF(当C有多条边时,选择权值最小的那个,因此这里选择CF),如下图。并将点F加入到集合U中。
在这里插入图片描述
继续,依照查找法则我们找到边FG,如下图。并将点G加入到U中。
在这里插入图片描述
继续,依照查找法则我们找到边GH,如下图。并将点H加入到U中。
在这里插入图片描述
继续,依照查找法则我们找到边CD,如下图。并将点D加入到U中。
在这里插入图片描述
继续,依照查找法则我们找到边DE,如下图。并将点E加入到U中。
在这里插入图片描述
此时,满足U = V,即找到了这颗最小生成树。

基于WIN32的迷宫制作

功能实现

本次实验只完成了随机生成迷宫和人工走迷宫,因此一共分为两大块:
第一块是随机生成迷宫:

struct block{
	int row,column,direction;
	block(int _row,int _column,int _direction){
		row = _row;
		column = _column;
		direction = _direction;
	}
};

struct point {
	int x;
	int y;
}start,end;

这是定义一个向量,以及初始化挖墙的矿工。

vector<block> myblock;
int x_num=1,y_num=1;//矿工位置
int G[100][100];


这是一个初始化函数,游戏结束后重新开始,就需初始化。

void init() {
	//将地图全部置为墙
	memset(G,WALL,sizeof(G));
	//定义起始点
	G[0][0] = NOTHING;
	start.x = start.y = 0;


	m_play.len=1;
	m_play.m_pos[0].x=1;
	m_play.m_pos[0].y=1;
	generatedestination();

}

这一部分是矿工找墙。

void FindBlock() {
	//找出与当前位置相邻的墙
	if(x_num+1<=m && G[x_num+1][y_num] == WALL) {//down
		myblock.push_back(block(x_num+1,y_num,down));
	}
	if(y_num+1<=n && G[x_num][y_num+1] == WALL) {//right
		myblock.push_back(block(x_num,y_num+1,rt));
	}
	if(x_num-1>=1 && G[x_num-1][y_num] == WALL) {//up
		myblock.push_back(block(x_num-1,y_num,up));
	}
	if(y_num-1>=1 && G[x_num][y_num-1] == WALL) {//left
		myblock.push_back(block(x_num,y_num-1,lt));
	}
}

这一部分是随机生成迷宫。

void CreateMaze(){
	//生成迷宫
	srand((unsigned)time(NULL));//随机数种子
	FindBlock();
	//第一步压入两堵墙(起点右边和起点下面)进入循环
	while(myblock.size()) {
		int BlockSize = myblock.size();
		//随机选择一堵墙(生成0 ~ BlockSize-1之间的随机数,同时也是vector里墙的下标)
		int randnum = rand() % BlockSize;
		block SelectBlock = myblock[randnum];
		x_num = SelectBlock.row;//矿工来到我们"选择的墙"这里
		y_num = SelectBlock.column;
		//根据当前选择的墙的方向进行后续操作
		//此时,起始点 选择的墙 目标块 三块区域在同一直线上
		//我们让矿工从"选择的墙"继续前进到"目标块"
		//矿工有穿墙能力 :)
		switch(SelectBlock.direction) {
			case down: {
				x_num++;
				break;
			}
			case rt: {
				y_num++;
				break;
			}
			case lt: {
				y_num--;
				break;
			}
			case up: {
				x_num--;
				break;
			}
		}
		//目标块如果是墙
		if(G[x_num][y_num]==WALL) {
			//打通墙和目标块
			G[SelectBlock.row][SelectBlock.column] = G[x_num][y_num] = NOTHING;
			//再次找出与矿工当前位置相邻的墙
			FindBlock();
		}
		else{//如果不是呢?说明我们的矿工挖到了一个空旷的通路上面 休息一下就好了
			//relax
		}
		//删除这堵墙(把用不了的墙删了,对于那些已经施工过了不必再施工了,同时也是确保我们能跳出循环)
		myblock.erase(myblock.begin()+randnum);
	}
}

这一部分是用来在界面上生成迷宫以及展现用户自己走迷宫的路线。

void MyPaint(HDC hdc){//生成迷宫 
	//绘制背景
	HBRUSH hbr=CreateSolidBrush(RGB(0,0,0));//创建画刷颜色为黑色,用来绘制背景
	SelectObject(hdc,hbr);//通过for循环,初始化网格的背景色为黑色
	for(int y=0;y<SIZE;y++)
	{
		for(int x=0;x<SIZE;x++)
		{
			Rectangle(hdc,x*WIDTH,y*HEIGHT,(x+1)*WIDTH,(y+1)*HEIGHT);
		}
	}
	
	CreateMaze();
	//绘制路
	HBRUSH hbrwhite=CreateSolidBrush(RGB(255,255,255));//创建画刷颜色为白色,用来绘制路
	SelectObject(hdc,hbrwhite);//通过for循环,将选定坐标的对应的矩形涂满颜色
	for(int i=1;i<SIZE;i++){
        for(int j=1;j<SIZE;j++){
			if(G[i][j] == NOTHING)Rectangle(hdc,i*WIDTH,j*HEIGHT,(i+1)*WIDTH,(j+1)*HEIGHT);
		}
    }

    //绘制走的路
	HBRUSH hbrred=CreateSolidBrush(RGB(255,0,0));//创建画刷颜色为红色,用来绘制走的路
	SelectObject(hdc,hbrred);//通过for循环,将选定坐标的对应的矩形涂满颜色    
	Rectangle(hdc,1*WIDTH,1*HEIGHT,(1+1)*WIDTH,(1+1)*HEIGHT);
	for(int z=0;z<m_play.len;z++)
	{
		Rectangle(hdc,m_play.m_pos[z].x*WIDTH,m_play.m_pos[z].y*HEIGHT,(m_play.m_pos[z].x+1)*WIDTH,(m_play.m_pos[z].y+1)*HEIGHT);
	}

	//绘制终点
    HBRUSH hbrblue=CreateSolidBrush(RGB(0,0,255));//创建画刷颜色为蓝色,用来绘制终点
	SelectObject(hdc,hbrblue);
	Rectangle(hdc,m_destination.x*WIDTH,m_destination.y*HEIGHT,(m_destination.x+1)*WIDTH,(m_destination.y+1)*HEIGHT);
}
//定义一个程序开始的函数
void OnStart(HWND hWnd){
	init();
}

第二部分是用键盘走迷宫:
这个结构体用来表示用户自己走迷宫。

struct play{
	POINT m_pos[MAXSIZE];//定义结点的X,Y坐标表示用户要走的路,其中m_pos[0]表示第一个位置
	int m_direction;//定义变量表示运行的方向
	int len;//长度
}m_play;

下面是键盘控制部分。

     		switch(wParam)
			{				
			case VK_UP:
				m_play.m_pos[0].y=m_play.m_pos[0].y-1;//向上运动
				m_play.len++;					
				break;
			case VK_RIGHT:
				m_play.m_pos[0].x=m_play.m_pos[0].x+1;//向左运动
				m_play.len++;
				break;
			case VK_DOWN:
				m_play.m_pos[0].y=m_play.m_pos[0].y+1;//向下运动
				m_play.len++;
				break;
			case VK_LEFT:
			    m_play.m_pos[0].x=m_play.m_pos[0].x-1;//向右运动
			    m_play.len++;
				break;
			}
			for(z=m_play.len-1;z>=1;z--){//更新迷宫的其他部分
			    m_play.m_pos[z]=m_play.m_pos[z-1];
			}
			//到达目的地,游戏结束
			if(m_play.m_pos[0].x==m_destination.x&&m_play.m_pos[0].y==m_destination.y){//当到达终点
				if(IDYES==MessageBox(hWnd,"Game Over,你想重新开始吗?","提示",MB_YESNO)){//游戏结束后出现交互界面,判断是重新开始游戏还是结束游戏
					OnStart(hWnd);
				}
				else{
					PostQuitMessage(0);
				}
			}				
		    hdc=GetDC(hWnd);//提取迷宫移动的方向的句柄
			MyPaint(hdc);//用画刷展示	
			break;

成果展示

最终的视频演示如下:
在这里插入图片描述
prime算法内容讲解转载博客

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值