武汉理工大学数据结构综合实验——连连看游戏综合实践


实验目的

(1)线性结构实验目的
调研连连看游戏,了解连连看游戏的功能和规则等
掌握集成开发工具VS2019的使用和C++的基础编程
了解MFC框架
了解线性结构,掌握数组的遍历、消子和胜负判断等算法
了解企业软件开发过程,应用迭代开发思路进行项目开发,养成良好的编程习惯和培养软件工程化思维

(2)非线性结构实验目的
调研连连看游戏,了解连连看游戏的功能和规则等 掌握集成开发工具VS2019的使用和C++的基础编程
了解MFC框架
掌握图的数据结构和应用,图的常用算法,能够利用图的算法实现游戏连通判断和胜负判断
了解企业软件开发过程,应用迭代开发思路进行项目开发,养成良好的编程习惯和培养软件工程化思维

主要仪器设备及耗材

1.安装了Windows 10操作系统的PC机1台
2.PC机系统上安装了Microsoft Visual Studio 2019开发环境


以下是本篇文章正文内容,下面案例可供参考

一、实验要求

(1)线性结构实验要求
连连看游戏是对一堆图案中的相同图案进行配对的简单游戏,在一定的规则之内对相同的图案进行消除处理,在规定时间内消除所有图案后玩家就获胜。
主界面,进行各项操作的入口;
开始游戏,系统根据设置的主题风格生成一个图片布局,以供玩家点击消除
消子,对玩家选中的两种图片进行判断,判断是否符合消除规则;
判断胜负,当游戏完成后,需要判断游戏胜负;
提示,显示界面上能够消除的一对图片;
重排,根据随机数,重新排列游戏地图上的图片;
计时,设定一定时间来辅助游戏是否结束;
游戏模式,有基本模式、休闲模式和关卡模式三种,可以根据是否定时等规则进行设置,增强趣味性。

(2)非线性结构实验要求
与线性结构中的功能完全相同,不同点在于游戏数据存储结构和算法。

二、分析与设计(非线性结构)

依据上述的实验目的与要求,可导出实现的连连看游戏的流程为:

① 主界面设计(主界面布局,背景图片绘制)
② 开始游戏(添加游戏对话框,绘制游戏界面背景,游戏界面布局,加载游戏元素图片,绘制游戏地图)
③ 消子判断(添加鼠标事件,选择图片,消除相同元素图片,一条直线消子,两条直线消子,三条直线消子)
④ 判断胜负(判断胜负,控制开始游戏按钮状态)
⑤ 扩展功能(提示,重排)

1.数据结构的设计

代码如下(示例):

//保存游戏地图中的点
typedef struct tagVertex
{
    
    
	int row;     //行
	int col;     //列
	int info;    //信息类
}Vertex;

图存储结构
AdjMatrix m_AdjMatrix;	//边数组  
	Vertices m_Vertices;//顶点数组
	int m_nVexnum;		//顶点数
	int m_nArcnum;		//边数量

2.核心算法设计

一条直线消子

代码如下(示例):

//连通判断
bool CGameLogic::IsLink(int anMap[][4], Vertex v1, Vertex v2) {
    
    

	//一条直线消子
	int nRow1 = v1.row;
	int nCol1 = v1.col;
	int nRow2 = v2.row;
	int nCol2 = v2.col;

	//把第一个点保存到数组
	AddVertex(v1);

	//判断能否横向连通
	if (nRow1 == nRow2)
	{
    
    
		if (LinkInRow(anMap, v1, v2))
		{
    
    
			AddVertex(v2);       //把第二个点保存到数组
			return true;
		}
	}

	//判断能否纵向连通
	if (nCol1 == nCol2)
	{
    
    
		if (LinkInCol(anMap, v1, v2) == true)
		{
    
    
			AddVertex(v2);       //把第二个点保存到数组
			return true;
		}
	}

	//两条直线消子
	if (OneCornerLink(anMap, v1, v2) == true)
	{
    
    
		AddVertex(v2);
		return true;
	}


	//三条直线消子
	if (TwoCornerLink(anMap, v1, v2) == true)
	{
    
    
		AddVertex(v2);
		return true;
	}

	DeleteVertex();

	return false;
}

//行号相同时,判断横向是否连通
bool CGameLogic::LinkInRow(int anMap[][4], Vertex v1, Vertex v2) {
    
    
	int nCol1 = v1.col;
	int nCol2 = v2.col;
	int nRow = v1.row;
	//保证nCol1的值小于nCol2
	if (nCol1 > nCol2) {
    
    
		//数据交换
		int nTemp = nCol1;
		nCol1 = nCol2;
		nCol2 = nTemp;
	}

	//直通
	for (int i = nCol1 + 1; i <= nCol2; i++)
	{
    
    
		if (i == nCol2)return true;
		if (anMap[nRow][i] != BLANK)break;
	}//判断这条直线上是否都为空白区域,如果全为空白区域,则横向连通
	return false;
}

//列号相同时,判断纵向是否连通
bool CGameLogic::LinkInCol(int anMap[][4], Vertex v1, Vertex v2) {
    
    
	int nRow1 = v1.row;
	int nRow2 = v2.row;
	int nCol = v1.col;
	//保证nRow1的值大于nRow2
	if (nRow1 > nRow2)
	{
    
    
		//数据交换
		int nTemp = nRow1;
		nRow1 = nRow2;
		nRow2 = nTemp;
	}
	//直通
	for (int i = nRow1 + 1; i <= nRow2; i++)
	{
    
    
		if (i == nRow2)return true;
		if (anMap[i][nCol] != BLANK)break;
	}//判断这条直线上是否都为空白区域,如果全为空白区域,则纵向连通
	return false;
}

两条直线消子

代码如下(示例):

// 判断(nRow1,nCol1)到(nRow2,nCol2)能否连通(两条直线消子)
bool CGameLogic::OneCornerLink(int anMap[][4], Vertex v1, Vertex v2)
{
    
    
	//直角能够消子,那么顶点一定在与两个点的行和列相交的点,只有这两个点为空,才有可能实现两条直线消子
	int nRow1 = v1.row;
	int nCol1 = v1.col;
	int nRow2 = v2.row;
	int nCol2 = v2.col;
	//确保nRow1<nRow2
	if (nRow1 > nRow2)
	{
    
    
		int nTemp = nRow1;
		nRow1 = nRow2;
		nRow2 = nTemp;

		nTemp = nCol1;
		nCol1 = nCol2;
		nCol2 = nTemp;
	}

	if (nCol1 > nCol2)
	{
    
    
		//判断(nRow1 + 1, nCol1)到(nRow2,nCol2 + 1)能否连通
		if (LineY(anMap, nRow1 + 1, nRow2, nCol1) && LineX(anMap, nRow2, nCol1, nCol2 + 1))
		{
    
    
			Vertex v = {
    
     nRow2,nCol1,BLANK };
			AddVertex(v);
			return true;
		}
		//判断(nRow2 - 1, nCol2)到(nRow1,nCol1 - 1)能否连通
		if (LineY(anMap, nRow2 - 1, nRow1, nCol2) && LineX(anMap, nRow1, nCol2, nCol1 - 1))
		{
    
    
			Vertex v = {
    
     nRow1,nCol2,BLANK };
			AddVertex(v);
			return true;
		}
	}
	else
	{
    
    
		//判断(nRow1 + 1, nCol1)到(nRow2,nCol2 - 1)能否连通
		if (LineY(anMap, nRow1 + 1, nRow2, nCol1) && LineX(anMap, nRow2, nCol1, nCol2 - 1))
		{
    
    
			Vertex v = {
    
     nRow2,nCol1,BLANK };
			AddVertex(v);
			return true;
		}
		//判断(nRow2 - 1, nCol2)到(nRow1,nCol1 + 1)能否连通
		if (LineY(anMap, nRow2 - 1, nRow1, nCol2) && LineX(anMap, nRow1, nCol2, nCol1 + 1))
		{
    
    
			Vertex v = {
    
     nRow1,nCol2,BLANK };
			AddVertex(v);
			return true;
		}
	}

	return false;

三条直线消子

代码如下(示例):

//判断(nRow1,nCol1)到(nRow2,nCol2)能否连通(三条直线消子)
bool CGameLogic::TwoCornerLink(int anMap[][4], Vertex v1, Vertex v2)
{
    
    
	int nRow1 = v1.row;
	int nCol1 = v1.col;
	int nRow2 = v2.row;
	int nCol2 = v2.col;
	bool IsTemp = false;

	//确保nRow1<nRow2
	if (nRow1 > nRow2)
	{
    
    
		//数据交换
		int nTemp = nRow1;
		nRow1 = nRow2;
		nRow2 = nTemp;

		nTemp = nCol1;
		nCol1 = nCol2;
		nCol2 = nTemp;

		IsTemp = true;        //表明两个点交换
	}
	if (nCol1 > nCol2)
	{
    
    
		//找到一条与Y轴平行的连通直线段
		for (int nCol = 0; nCol < 4; nCol++)
		{
    
    
			if (anMap[nRow1][nCol] == BLANK && anMap[nRow2][nCol] == BLANK)
			{
    
    
				if (LineY(anMap, nRow1, nRow2, nCol)) //判断该条Y轴直线是否连通
				{
    
    
					//连通的直线在选中的两个点的左边
					if (nCol2 > nCol && LineX(anMap, nRow1, nCol - 1, nCol) && LineX(anMap, nRow2, nCol, nCol2 - 1))
					{
    
    
						//保存节点
						Vertex Vx1;
						Vertex Vx2;
						Vx1.row = nRow1;
						Vx1.col = nCol;
						Vx2.row = nRow2;
						Vx2.col = nCol;

						if (IsTemp == false)
						{
    
    
							AddVertex(Vx1);
							AddVertex(Vx2);
							return true;
						}
						else
						{
    
    
							AddVertex(Vx2);
							AddVertex(Vx1);
							return true;
						}
					}
					//连通的直线在选中的两个点的中间
					if (nCol1 > nCol && nCol > nCol2 && LineX(anMap, nRow1, nCol, nCol1 - 1) && LineX(anMap, nRow2, nCol2 + 1, nCol))
					{
    
    
						//保存节点
						Vertex Vx1;
						Vertex Vx2;
						Vx1.row = nRow1;
						Vx1.col = nCol;
						Vx2.row = nRow2;
						Vx2.col = nCol;

						if (IsTemp == false)
						{
    
    
							AddVertex(Vx1);
							AddVertex(Vx2);
							return true;
						}
						else
						{
    
    
							AddVertex(Vx2);
							AddVertex(Vx1);
							return true;
						}
					}
					//连通的直线在选中的两个点的右边
					if (nCol > nCol1 && LineX(anMap, nRow1, nCol1 + 1, nCol) && LineX(anMap, nRow2, nCol, nCol2 + 1))
					{
    
    
						//保存节点
						Vertex Vx1;
						Vertex Vx2;
						Vx1.row = nRow1;
						Vx1.col = nCol;
						Vx2.row = nRow2;
						Vx2.col = nCol;

						if (IsTemp == false)
						{
    
    
							AddVertex(Vx1);
							AddVertex(Vx2);
							return true;
						}
						else
						{
    
    
							AddVertex(Vx2);
							AddVertex(Vx1);
							return true;
						}
					}
				}
			}
		}

		//找到一条与X轴平行的连通直线段
		for (int nRow = 0; nRow < 4; nRow++)
		{
    
    
			if (anMap[nRow][nCol1] == BLANK && anMap[nRow][nCol2] == BLANK)
			{
    
    
				if (LineX(anMap, nRow, nCol1, nCol2)) //判断该条Y轴直线是否连通
				{
    
    
					//连通的直线在选中的两个点的上面
					if (nRow1 > nRow && LineY(anMap, nRow1 - 1, nRow, nCol1) && LineY(anMap, nRow, nRow2 - 1, nCol2))
					{
    
    
						//保存节点
						Vertex Vx1;
						Vertex Vx2;
						Vx1.row = nRow;
						Vx1.col = nCol1;
						Vx2.row = nRow;
						Vx2.col = nCol2;

						if (IsTemp == false)
						{
    
    
							AddVertex(Vx1);
							AddVertex(Vx2);
							return true;
						}
						else
						{
    
    
							AddVertex(Vx2);
							AddVertex(Vx1);
							return true;
						}
					}
					//连通的直线在选中的两个点的中间
					if (nRow2 > nRow && nRow > nRow1 && LineY(anMap, nRow1 + 1, nRow, nCol1) && LineY(anMap, nRow, nRow2 - 1, nCol2))
					{
    
    
						//保存节点
						Vertex Vx1;
						Vertex Vx2;
						Vx1.row = nRow;
						Vx1.col = nCol1;
						Vx2.row = nRow;
						Vx2.col = nCol2;

						if (IsTemp == false)
						{
    
    
							AddVertex(Vx1);
							AddVertex(Vx2);
							return true;
						}
						else
						{
    
    
							AddVertex(Vx2);
							AddVertex(Vx1);
							return true;
						}
					}
					//连通的直线在选中的两个点的下面
					if (nRow > nRow2 && LineY(anMap, nRow1 + 1, nRow, nCol1) && LineY(anMap, nRow, nRow2 + 1, nCol2))
					{
    
    
						//保存节点
						Vertex Vx1;
						Vertex Vx2;
						Vx1.row = nRow;
						Vx1.col = nCol1;
						Vx2.row = nRow;
						Vx2.col = nCol2;

						if (IsTemp == false)
						{
    
    
							AddVertex(Vx1);
							AddVertex(Vx2);
							return true;
						}
						else
						{
    
    
							AddVertex(Vx2);
							AddVertex(Vx1);
							return true;
						}
					}
				}
			}
		}
	}
	else
	{
    
    
		//找到一条与Y轴平行的连通直线段
		for (int nCol = 0; nCol < 4; nCol++)
		{
    
    
			if (anMap[nRow1][nCol] == BLANK && anMap[nRow2][nCol] == BLANK)
			{
    
    
				if (LineY(anMap, nRow1, nRow2, nCol)) //判断该条Y轴直线是否连通
				{
    
    
					//连通的直线在选中的两个左边
					if (nCol1 > nCol && LineX(anMap, nRow1, nCol1 - 1, nCol) && LineX(anMap, nRow2, nCol, nCol2 - 1))
					{
    
    
						//保存节点
						Vertex Vx1;
						Vertex Vx2;
						Vx1.row = nRow1;
						Vx1.col = nCol;
						Vx2.row = nRow2;
						Vx2.col = nCol;

						if (IsTemp == false)
						{
    
    
							AddVertex(Vx1);
							AddVertex(Vx2);
							return true;
						}
						else
						{
    
    
							AddVertex(Vx2);
							AddVertex(Vx1);
							return true;
						}
					}
					//连通的直线在选中的两个点之间
					if (nCol2 > nCol && nCol > nCol1 && LineX(anMap, nRow1, nCol1 + 1, nCol) && LineX(anMap, nRow2, nCol, nCol2 - 1))
					{
    
    
						//保存节点
						Vertex Vx1;
						Vertex Vx2;
						Vx1.row = nRow1;
						Vx1.col = nCol;
						Vx2.row = nRow2;
						Vx2.col = nCol;

						if (IsTemp == false)
						{
    
    
							AddVertex(Vx1);
							AddVertex(Vx2);
							return true;
						}
						else
						{
    
    
							AddVertex(Vx2);
							AddVertex(Vx1);
							return true;
						}
					}
					//连通的直线在选中的两个点右边
					if (nCol > nCol2 && LineX(anMap, nRow1, nCol1 + 1, nCol) && LineX(anMap, nRow2, nCol, nCol2 + 1))
					{
    
    
						//保存节点
						Vertex Vx1;
						Vertex Vx2;
						Vx1.row = nRow1;
						Vx1.col = nCol;
						Vx2.row = nRow2;
						Vx2.col = nCol;

						if (IsTemp == false)
						{
    
    
							AddVertex(Vx1);
							AddVertex(Vx2);
							return true;
						}
						else
						{
    
    
							AddVertex(Vx2);
							AddVertex(Vx1);
							return true;
						}
					}
				}
			}
		}

		//找到一条与X轴平行的连通直线段
		for (int nRow = 0; nRow < 4; nRow++)
		{
    
    
			if (anMap[nRow][nCol1] == BLANK && anMap[nRow][nCol2] == BLANK)
			{
    
    
				if (LineX(anMap, nRow, nCol1, nCol2)) //判断该条Y轴直线是否连通
				{
    
    
					//连通直线在两个点上面
					if (nRow1 > nRow && LineY(anMap, nRow1 - 1, nRow, nCol1) && LineY(anMap, nRow, nRow2 - 1, nCol2))
					{
    
    
						//保存节点
						Vertex Vx1;
						Vertex Vx2;
						Vx1.row = nRow;
						Vx1.col = nCol1;
						Vx2.row = nRow;
						Vx2.col = nCol2;

						if (IsTemp == false)
						{
    
    
							AddVertex(Vx1);
							AddVertex(Vx2);
							return true;
						}
						else
						{
    
    
							AddVertex(Vx2);
							AddVertex(Vx1);
							return true;
						}
					}
					//连通直线在两个点之间
					if (nRow2 > nRow && nRow > nRow1 && LineY(anMap, nRow1 + 1, nRow, nCol1) && LineY(anMap, nRow, nRow2 - 1, nCol2))
					{
    
    
						//保存节点
						Vertex Vx1;
						Vertex Vx2;
						Vx1.row = nRow;
						Vx1.col = nCol1;
						Vx2.row = nRow;
						Vx2.col = nCol2;

						if (IsTemp == false)
						{
    
    
							AddVertex(Vx1);
							AddVertex(Vx2);
							return true;
						}
						else
						{
    
    
							AddVertex(Vx2);
							AddVertex(Vx1);
							return true;
						}
					}
					//连通直线在两个点下面
					if (nRow > nRow2 && LineY(anMap, nRow1 + 1, nRow, nCol1) && LineY(anMap, nRow, nRow2 + 1, nCol2))
					{
    
    
						//保存节点
						Vertex Vx1;
						Vertex Vx2;
						Vx1.row = nRow;
						Vx1.col = nCol1;
						Vx2.row = nRow;
						Vx2.col = nCol2;

						if (IsTemp == false)
						{
    
    
							AddVertex(Vx1);
							AddVertex(Vx2);
							return true;
						}
						else
						{
    
    
							AddVertex(Vx2);
							AddVertex(Vx1);
							return true;
						}
					}
				}
			}
		}
	}
	//找到一条与X轴平行的连通直线段
	return false;
}

初始化游戏地图

代码如下(示例):

// 初始化游戏地图
void CGameLogic::InitMap(CGraph& graph)
{
    
    
	//游戏地图数组初始化
	srand((int)time(NULL));//设置种子
	
	//生成16种花色的地图
	int anTemp[MRow][MCol];
	int a = 0;//初始的第一张图片序号
	for (int j = 0; j < MCol; j++) {
    
    
		for (int i = 0; i < MRow; i++) {
    
    
			anTemp[i][j] = j;
		}
	}
	//打乱地图花色顺序
	for (int i = 0; i < MRow ; i++) {
    
    
		for (int j = 0; j < MCol; j++) {
    
    
			int i1 = rand() % MRow;
			int j1= rand() % MCol;
			//交换两个点的花色
			int t = anTemp[i][j];
			anTemp[i][j] = anTemp[i1][j1];
			anTemp[i1][j1] = t;
		}
	}
	
	//初始化顶点
	for (int i = 0; i < MRow; i++) {
    
    
		for (int j = 0; j < MCol; j++) {
    
    
			graph.AddVertex(anTemp[i][j]); 
		}
	}

	//初始化边
	for (int i = 0; i < MRow; i++) {
    
    
		for (int j = 0; j < MCol; j++) {
    
    
			UpdateArc(graph, i, j);
		}
	}

}

搜寻路径

代码如下(示例):

//根据深度优先搜索算法判断是否连通
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();          
				continue;
			}
			//当中间顶点不是nVi时,继续搜寻下一个相邻且相连通的顶点
			if (nVi != nV1)
			{
    
    
				//当中间顶点不为空时,表示该条路径不通
				if (graph.GetVertex(nVi) != BLANK)
				{
    
    	
					//取出压入的顶点
					PopVertex();      
					continue;
				}
				//如果nVi是一个已消除的点,则判断(nVi,nV1)是否连通
				if (SearchPath(graph, nVi, nV1))
				{
    
    
					return true;
				}
			}
			else//表示已经找到一条连通路径,则返回true
			{
    
    
				return true;
			}

			PopVertex();     //取出压入的顶点,与PushWertex()对应
		}
	}
	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)
			{
    
    
				if (graph.GetVertex(i) == graph.GetVertex(j))//第i个点和第j个点同色
				{
    
    
					//压入第一个点
					PushVertex(i);
					if (SearchPath(graph, i, j) == true)//能够连通
					{
    
    
						return true;
					}
					//取出压入的顶点时,与PushVertex(i)对应
					PopVertex();
				}

			}
		}
	}
	return false;
}

//随机任选地图中两个顶点,将元素进行交换,这样进行100次
void CGameLogic::ResetGraph(CGraph& graph)
{
    
    
	//设置种子
	srand((int)time(NULL));
	//随机交换两个顶点的值
	for (int i = 0; i < 100; i++)
	{
    
    
		//随机得到两个坐标
		int n1 = rand() % MAX_VERTEX_NUM;
		int n2 = rand() % MAX_VERTEX_NUM;

		//交换两个数值
		graph.Change(n1, n2);
	}

	//更新弧信息
	for (int i = 0; i < MRow; i++)
	{
    
    
		for (int j = 0; j < MCol; j++)
		{
    
    
			UpdateArc(graph, i, j);
		}
	}

}

3.测试用例设计

选用网上找来的图片作为游戏背景图片。
用图片素材来随机生成不同花色的游戏地图。

4.测试效果

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


总结

详情可参考以下链接
链接:冲冲冲~
提取码:8gc7
复制这段内容后打开百度网盘手机App,操作更方便哦

猜你喜欢

转载自blog.csdn.net/mo_zhe/article/details/112773125
今日推荐