【课程记录·本】WUT数据结构与算法综合实验 | 基于C++&MFC的欢乐连连看游戏的设计与实现(附下载链接)

本文基于文章“ https://blog.csdn.net/cxh_1231/article/details/89577820 ”二次修改发布,原文已失效,不再维护
 
我的微信公众号知行校园汇,点击查看,欢迎关注

本科资料汇总https://blog.csdn.net/cxh_1231/article/details/112465790
 
我的其他平台(点击蓝字可访问):

CSDN  |  哔哩哔哩  |  GitHub  |  Gitee  |  开源中国  |  微信小程序  |  知行达摩院

1、项目演示与源码、课程报告下载

1.1 项目演示:

【WUT数据结构与算法综合实验】欢乐连连看(C++ & MFC)指导视频

  视频无法加载?视频不够清晰?点击此处观看原视频  

1.2 源码、课程报告下载

下载链接1:

  点击此处前往公众号文章(网盘下载,包含3次实验的全部资料)  
在这里插入图片描述
下载链接2(推荐)

  点击此处前往面包多平台下载 欢乐连连看 全部资料  
 
  点击此处前往面包多平台下载 本课程 全部资料(推荐)  

2、项目指导参考视频

  点击此处前往视频列表  

3、实验教材

实验教材

4、实验任务、目的与要求

任务列表:
  • 创建工程
  • 绘制欢迎语
  • 主界面设计
  • 游戏界面设计
  • 绘制游戏地图
  • 同色消子
  • 程序结构调整
  • 消子判断
  • 数据结构设计
  • 开始游戏
  • 消子
  • 判断胜负
  • 提示
  • 重排
  • 计时
  • 帮助
  • ……
实验目的以及要求:
1.目的
  1. 调研连连看游戏,了解连连看游戏的功能和规则等。
  2. 掌握集成开发工具(Visual C++ 6.0 或 Microsoft Visual Studio 2010)。
  3. 掌握 C++的基础编程。
  4. 了解 MFC 框架,包括 MFC Dialog 应用程序和 GDI 编程。
  5. 了解线性结构,重点掌握数组和栈操作,掌握数组的遍历、消子胜负判断等算法。
  6. 进一步掌握图的数据结构和应用,重点了解邻接矩阵的存储结构。
  7. 掌握图的常用算法,比如,深度优先遍历、图的联通判断等,能够利用图的算法实现游戏联通判断和胜负判断。
  8. 了解企业软件开发过程,了解系统需求分析和设计,应用迭代开发思路进行项目开发。
  9. 养成良好的编程习惯和培养软件工程化思维,综合应用“C++编程、MFC Dialog、算法、线性结构或者图”等知识,开发“连连看游戏”桌面应用程序,达到掌握和应用线性结构以及非线性结构核心知识的目的。
2.要求
  1. 连连看游戏是对一堆图案中的相同图案进行配对消除的简单游戏,在一定的规则之内对相同的图案进行消除处理,在规定的时间内消除所有的图案后玩家获胜。
  2. 利用MFC Dialog应用程序设计游戏主界面,通过鼠标点击事件实现游戏界面加载;
  3. 通过图的相关知识,完成“开始游戏”操作,进一步实现“消子”“判断胜负”“提示”“重排”“计时”等功能。
  4. 游戏规则是一条直线消子、两条直线消子、三条直线消子。
    在这里插入图片描述

5、实现说明

在Microsoft Visual Studio 2017集成开发环境中新建一个MFC对话框(Dialog)工程,解决方案:Lianliankan,项目名称:欢乐连连看,工程名称:LLK,主对话框:CLLKDlg,然后修改主界面的对话框属性值,如窗口标题、窗体按钮、对话框图标等。

CLLKDlg类为主界面对话框类,在该类中添加游戏界面“基本模式”按钮BN_CLICKED消息响应函数CLLKDlg::OnBnClickedBtnBasic()。创建并显示游戏界面对话框,并控制主界面的隐藏和显示。

为项目添加CGameDlg对话框类,该类为游戏界面,与用户进行信息交换,为其添加相关数据成员和成员函数,相关数据成员和成员函数实现的功能如下表:

数据成员描述
CDC m_dcMem位图内存DC,游戏界面的背景图片保存到该DC中
CDC m_dcElement元素内存DC
CDC m_dcMask掩码内存DC
CGameControl m_GameC游戏控制类对象
成员函数描述
void InitBackground();加载游戏背景图片到位图DC中,设置游戏界面对话框的大小
virtual BOOL OnInitDialog();对话框初始化函数,并调用InitBackground()
afx_msg void OnPaint();对话框WM_PAINT消息响应函数,将游戏背景图片绘制到对话框界面中
void InitElement();初始化游戏元素和掩码
void UpdateMap();更新游戏地图
void OnClickedButtonStart();“开始游戏”按钮BN_CLICKEN消息响应函数,开始游戏,生成游戏地图,并显示游戏地图
void UpdateWindow();更新界面,从生成的游戏地图图结构中取出顶点,根据顶点从元素图片中取出相对应的元素,显示在游戏地图中
void DrawTipFrame(int, int);绘制游戏提示框
void DrawTipLine(Vertex,int)绘制提示线
void OnClickedButtonStop();“暂停游戏”按钮BN_CLICKEN消息响应函数
void OnClickedButtonTips();“提示”按钮BN_CLICKEN消息响应函数
void OnClickedButtonRestart();“重排”按钮BN_CLICKEN消息响应函数
void OnBnClickedButtonHelp();“帮助”按钮BN_CLICKEN消息响应函数
void OnBnClickedButtonSet();“设置”按钮BN_CLICKEN消息响应函数

为工程项目添加CGameControl类,该类为游戏的控制类,实现整个项目的控制,其相关数据成员和成员函数的实现描述如下表:

数据成员描述
CGraph m_graph;保存生成的游戏地图的图结构
Vertex m_ptSelFirst;记录第一次选中的点信息
Vertex m_ptSelSec;记录第二次选中的点信息
成员函数描述
void StartGame(void);开始游戏,调用CGameLogic类的成员函数,生成游戏地图的图结构
int GetElement(int nRow, int nCol);从游戏地图的图结构中取出顶点,用于界面第nRow行、nCol列显示元素的数据
void SetFirstPoint(int nRow, int nCol);设置第一个点
void SetSecPoint(int nRow, int nCol);设置第二个点
bool Link(Vertex avPath[MAX_VERTEX_NUM], int &nVexnum);判断是否连通
bool IsWin(int nTime);判断是否在规定时间内消子完成而获胜
void Reset(void);实现图的重排

为工程添加游戏业务逻辑CGameLogic类,实现逻辑上的相关操作,其相关数据类型和成员函数的实现描述如下表:

数据成员描述
Vertex m_avPath[MAX_VERTEX_NUM];保存连接路径的起始点、拐点、终点
int m_anPath[MAX_VERTEX_NUM];保存在进行连接判断时所经过的顶点
int m_nCorner;保存路径中的拐点数
int m_nVexNum;表示顶点数
成员函数描述
void InitMap(CGraph &graph);根据随机生成的数据,初始化游戏地图
void UpdateArc(CGraph &graph, int nRow, int nCol);判断游戏地图中的nRow行、nCol列的顶点与它上下左右的顶点是否有边,如果有边,则更新图结构
bool IsLink(CGraph &graph, Vertex v1, Vertex v2);判断v1、v2两个点是否连通
void Clear(CGraph &graph, Vertex v1, Vertex v2);将v1、v2两个点进行消子
bool SearchPath(CGraph &graph, int nV0, int nV1);使用深度优先搜索算法判断两个点是否连通
bool IsExsit(int nVi);判断顶点是否已在路径中存在
bool IsCornor(void);判断拐点的有效性
void PushVertex(int v)/ PopVertex();添加/取出一个路径顶点
int GetVexPath(Vertex avPath[MAX_NUM]);得到路径,返回的是顶点数
bool IsBlank(CGraph &graph);判断图中顶点是不是全是空
bool SearchValidPath(CGraph &graph);寻找可以进行消除的图片,用于提示
void ResetGraph(CGraph& graph);实现图结构的重排

为工程添加程序的数据结构类CGraph类,从图的结构上实现消子等相关功能,其其相关数据类型和成员函数的实现描述如下表:

数据成员描述
Vertices m_Vertices;一维数组,保存连连看游戏地图中的顶点
AdjMatrix m_AdjMatrix;二维数组,保存连连看游戏中的顶点的边
int m_nVexnum;顶点个数
int m_nArcnum;边数量
成员函数描述
void InitGraph();初始化图结构
int AddVertex(int nInfo);添加顶点,并获得顶点个数
void AddArc(int nV1Index,int nV2Index);添加生成边
int GetVertex(int nIndex);获取顶点索引号
bool GetArc(int nV1Index, int nV2Index);获得两个顶点的边信息
void UpdateVertex(int nIndex,int info);更新顶点信息,将图顶点数组中索引号为nIndex的顶点的值更新为info
int GetVexnum();获取图中顶点的个数
void ClearGraph(void);清理图结构
void ChangeVerex(int nIndex1, int nIndex2);调换两个点的位置

为项目添加全局头文件global.h,用来存放整个项目需要的数据,相关数据设计如下表:

#define BLANK-1
#define MAX_ROW10//初始行数
#define MAX_COL12//初始列数
#define MAX_VERTEX_NUM120//顶点数
#define MAX_PIC_NUM10//图片花色
#define REPEAT_NUM12//每种花色图片个数
#define MAP_TOP50//游戏地图左上角纵坐标
#define MAP_LETF50//游戏地图左上角横坐标
#define PIC_HEIGHT40//游戏地图高度
#define PIC_WIDTH40//游戏地图宽度
#define PLAY_TIMER_ID1//计时器的编号
#define GAME_LOSE-1//失败
#define GAME_SUCCESS0//获胜
#define GAME_PLAY1//游戏正在进行

6、核心代码

初始化游戏地图算法:随机生成打乱地图代码:

//初始化游戏地图
void CGameLogic::InitMap(CGraph &graph){
	//随机生成地图
	int anTemp[MAX_VERTEX_NUM];
	//多少花色
	for (int i = 0; i < MAX_PIC_NUM; i++)	{
		//重复数
		for (int j = 0; j < REPEAT_NUM; j++)
		{
			anTemp[i * REPEAT_NUM + j] = i;
		}
	}
	srand((int)time(NULL)); //设置种子
	//随机任意交换两个数字
	for (int i = 0; i < 300; i++)	{
		//随机得到两个坐标
		int nIndex1 = rand() % MAX_VERTEX_NUM;
		int nIndex2 = rand() % MAX_VERTEX_NUM;
		//交换两个数值
		int nTemp = anTemp[nIndex1];
		anTemp[nIndex1] = anTemp[nIndex2];
		anTemp[nIndex2] = nTemp;
	}
	//初始化顶点
	for (int i = 0; i < MAX_VERTEX_NUM; i++)	{
		graph.AddVertex(anTemp[i]);
	}
	//更新弧信息
	for (int i = 0; i < MAX_ROW; i++)	{
		for (int j = 0; j < MAX_COL; j++)		{
			UpdateArc(graph, i, j);
		}
	}
}

连通判断算法:使用深度优先搜索算法实现

//连接判断函数
bool CGameLogic::IsLink(CGraph &graph, Vertex v1, Vertex v2){
	//获得顶点索引号
	int nV1Index = v1.row * MAX_COL + v1.col;
	int nV2Index = v2.row * MAX_COL + v2.col;
	PushVertex(nV1Index);  //压入第一个点
	//搜寻两点之间的连通路径
	if (SearchPath(graph, nV1Index, nV2Index) == true)	{
		return true;
	}
	PopVertex();
	if (v1.row == v2.row) {
		if (v1.row == 0 || v1.row == MAX_ROW - 1) {
			return true;
		}
	}
	if (v1.col == v2.col) {
		if (v1.col == 0 || v1.col == MAX_COL - 1) {
			return true;
		}
	}
	return false;
}
//使用深度优先搜索法搜寻一条有效连通路径
bool CGameLogic::SearchPath(CGraph &graph, int nV0, int nV1){
	int nVexnum = graph.GetVexnum();//得到顶点数
	//遍历图中nV0行,从0列到nVexnum列,值为true的点
	for (int nVi = 0; nVi < nVexnum; nVi++)	{
		if (graph.GetArc(nV0, nVi) && !IsExsit(nVi)){
			//压入当前顶点,假设为路径的一个有效顶点
			PushVertex(nVi);
			//当拐点数大于2 时,直接放弃该顶点
			if (m_nCorner > 2){
				PopVertex();       //取出压入的顶点,与PushWertex(nVi)对应
				continue;
			}
			//当前顶点不是nVi时,继续搜寻下一个相邻且连通的顶点
			if (nVi != nV1)			{
				//当中间顶点不为空时,表示该条路径不通
				if (graph.GetVertex(nVi) != BLANK)				{
					PopVertex();    //取出压入的顶点,与PushWertex(nVi)对应
					continue;
				}
				//如果nVi是一个已消除的点,则判断(nVi,nV1)是否连通
				if (SearchPath(graph, nVi, nV1)){
					return true;
				}
			}
			else{
				return true;
			}
			PopVertex();     //取出压入的顶点,与PushWertex(nVi)对应
		}
	}
	return false;
}

提示功能算法:

//提示按钮功能实现
bool CGameControl::Help(Vertex avPath[MAX_VERTEX_NUM], int & nVexnum)
{
	CGameLogic logic;

	//判断是否为空
	if (logic.IsBlank(m_graph) == true)
	{
		return false;
	}
	//查找一个有效的提示路径
	if (logic.SearchValidPath(m_graph))
	{
		//返回路径顶点
		nVexnum = logic.GetVexPath(avPath);
	 
		return true;
	}
	return false;

}
//提示算法
bool CGameLogic::SearchValidPath(CGraph& graph){
	//得到顶点数
	int nVexnum = graph.GetVexnum();
	for (int i = 0; i < nVexnum; i++){
		//得到第一个非空顶点
		if (graph.GetVertex(i) == BLANK){
			continue;
		}
		//遍历得到第二个同色顶点
		for (int j = 0; j < nVexnum; j++){
			if (i != j)
			{
				//如果第i个点和第j个点同色
				if (graph.GetVertex(i) == graph.GetVertex(j)){
					//压入第一个点
					PushVertex(i);
					if (SearchPath(graph, i, j) == true){
						return true;
					}
					//取出压入的顶点时,与PushVertex(i);对应
					PopVertex();
				}

			}
		}
	}
	return false;
}

重排功能算法:根据随机数交换两张图片位置

//实现图结构的重排
void CGameLogic::ResetGraph(CGraph& graph){
	//随机交换顶点数组中两个顶点的值
	for (int i = 0; i < 200; i++){
		//随机得到两个坐标
		int nIndex1 = rand() % MAX_VERTEX_NUM;
		int nIndex2 = rand() % MAX_VERTEX_NUM;
		//交换两个数值
		graph.ChangeVerex(nIndex1, nIndex2);
	}
	//更新弧信息
	for (int i = 0; i < MAX_ROW; i++){
		for (int j = 0; j < MAX_COL; j++){
			UpdateArc(graph, i, j);
		}
	}
}

7、运行结果截图

游戏主界面:

在这里插入图片描述

游戏界面:

在这里插入图片描述

暂停界面:

在这里插入图片描述

连线界面:

在这里插入图片描述

上图点击重排后界面:

在这里插入图片描述

两个拐角连线界面:

在这里插入图片描述

完成游戏获胜界面:

在这里插入图片描述

点击“帮助”按钮界面:

在这里插入图片描述

点击“设置”按钮界面:

在这里插入图片描述

8、写在最后

能看到这篇博客的同学,应该是WUTer吧^_^

既然你看到了这篇博客,就请听学长啰嗦几句。

关于 数据结构与算法综合实验 的这三次实验来说,虽然总体难度较大,但是老师都给了指导视频,按部就班把视频看完,并根据视频,一步一步的来做,大部分都能够完成最终的实验。这不是重点,重点是这次实验要有所收获。其实代码到处都能找到,找你们的学长学姐,他们都有。找不到学长学姐,这不是有学长在网上给你们分享嘛(^_^)。不可否认,我当时做实验的时候,也是参考学长的代码(虽然他的全是BUG),然后结合视频来做的。重要的是理解这些代码在干什么,各个算法,代码之间的逻辑性,以及代码之间的调用关系,这些在以后的项目中都会用到(感兴趣可以去搜索一下MVC模型,这次实验基本是这种模式)。

总之,所有的一切要亲自动手试试,正如:纸上得来终觉浅,绝知此事要躬行。只有动手试试之后,你才会发现,理论和现实之间有很大的差距!动手之后,你也会觉得:不过如此。

— The END —

  • 15
    点赞
  • 100
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾年之璐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值