1、第 0 页 共 19 页中南林业科技大学数据结构课程设计报告必做题: 连连看游戏 选做题: 姓 名: 学 号: 专业班级:软件工程 1 班 学 院: 计算机与信息工程学院 指导老师: 签名: 2017 年 1 月 10 日程序实现 ,功能 (40 分)代码编写格式,注释 (20 分)文档内容 (20 分)文档格式 (10 分) 文档图表 (10 分)抄袭,总成绩 0 分总分第 1 页 共 19 页目录一、连连看问题 31.1 需求分析 41.1.1 程序的功能 .41.1.2.输入输出的要求 .41.2 系统设计 41.2.1 程序流程图 41.2.2 程序组成 51.3 调试测试 51.3.
2、1 测试用例 51.3.2 测试结果 61.3.3 存在问题 61.3.4 改进设想 71.4 程序界面 71.5 核心源程序清单 .8第 2 页 共 19 页1、连连看问题问题描述该游戏对一堆图案中相同的图案进行配对,点击开始按钮后,要求在一定的时间内完成对所有的图片配对,玩家每次选择两个图形,如果两图形相同,且这两个图形之间存在少于 2 个拐角的连通路径,则可以消除这两个图形。成功消除一对图片则计分板会增加 10 分,对应的时间增加 3 秒。要求各类类型的图片个数为偶数个,途中若有玩家找不到可以匹配的图片时可以点击提示按钮,由系统提示出一对可以消除的图片,通过玩家的点击可以消除。当所有的图
3、片消除时,系统提示消息为“恭喜你,通关了” ,如果没有在规定时间完成所有图片的配对,则系统提示为“游戏结束” 。基本要求(1)生成游戏初始局面;(2) 每次用户选择两个图形,如果图形能满足一定条件(如果两个图形一样,且这个两 个图形直接存在少于 2 个弯的拐角) ,则两个图形都能消掉。给定具有相同图形的任意两个格子,我们需要寻找这两个格子之间在转弯少的情况下,经过格子数目少的路径。如果 这个优路径的转弯数目少于 2,则这个两个格子可以消去; (3) 判断游戏是否结束。如果所有图形全部消去,游戏结束;(4) 判断死锁,当游戏玩家不可能消去任意两个图像的时候,游戏进入“死锁”状态。设计思想判断两个
4、图形可以消除的条件是:两个图形必须相同,它们之间存在着 0 个拐角,或 1 个拐角,亦或是 2 个拐角可以互相连通消除,否则,消除不了。广度优先搜索的具体实现:如果两个图形可以通过 0 个拐角连通,则从第一个选中的图片一次向右,向下,向左,向上搜索可以和当前选中图片消除的另一图片,当搜索到与之相同的图片时,则与之消去;图片布局刷新;如果两个图片之间存在 1 个拐角可以消去,则广度搜索从当前图片出发,向左,右上下一次寻找一个路径节点(没有图片的点),使得寻找到的路径节点可以与选中的图片一线连通,并且可以与第二次选中的图片一线连通,则可以判断为可消除图片;图片布局重新刷新;如果连个图片之间存在两个
5、拐角可以连通,则分为两种情况:一种是两个图片在矩形区域的最外沿,则通过判断是否存在一条线使得在两待消除的图片周围一个单位,若存在则消去。二是两图片在矩形区域内部,则在两待消除的图片水平方向和垂直方向上寻找另外两个中间点能使两中间点之间连通,并且其中一个中间点能和待消图片 1 一线连通,另一中间点能和待消图片 2 一线连通,如若找到这样的点,则判断两图片能消去,找不到则不能消去。1.1 需求分析1.1.1 程序的功能(1) 初始化游戏界面该部分主要由执行窗口创建函数及游戏地图加载函数来实现。通过数据的初始化及游戏地图资源的加载为用户呈现一个游戏初始的界面。第 3 页 共 19 页(2) 图 片
6、的 选 择该 功 能 主 要 由 鼠 标 来 完 成 。 在 OnLButtonDown()事 件 函 数 中 通 过 鼠 标 的点 击 事 件 选 取 所 要 消 除 的 两 个 相 同 图 片 。(3) 图 形 的 判 断 与 消 除对 于 第 (2)步 所 选 的 两 个 图 片 , 对 其 连 通 性 进 行 判 断 : 如 果 两 图 片 直 线连 通 , 则 相 互 消 去 ; 如 果 两 图 片 连 接 为 一 个 拐 点 且 相 通 , 则 相 互 消 去 ; 如 果两 图 片 连 接 为 两 个 拐 点 且 相 通 , 则 相 互 消 去 ; 否 则 , 不 能 消 去 。(
7、4) 判 断 游 戏 是 否 死 锁 或 结 束如 果 所 有 的 图 片 全 部 消 去 , 则 提 示 “游 戏 结 束 ! ”的 信 息 。 当 游 戏 玩家 不 可 能 在 消 去 任 意 两 个 图 片 时 , 游 戏 进 入 死 锁 状 态 。 此 时 提 示 相 关 信 息 。( 5) 游 戏 的 提 示当 玩 家 找 不 到 两 个 可 以 消 去 的 函 数 时 , 可 以 通 过 点 击 提 示 按 钮 获 取 系 统帮 助 , 继 续 游 戏 。1.1.2 输入输出的要求无具体要求,当玩家第一次点击图片时,对应图片响应点击消息,图片变成红色底片,若第二次点击的图片能和第一
8、次的图片连通,则同时消失,否则选中的图片为红色的底。1.2 系统设计1.2.1 程序流程图1.2.2 程序组成a.主要组成类:图 1LLKLLKDLG 类 LLKBUTTON 类OnInitDialog OnLButtonDownOnPaint FindLineOnClickedStart FindOneConnerInitMap FindSideShowMap FindTwoConnerOnClickedTipsOnClickedSwapIsWinOnClickedClose第 4 页 共 19 页b.构成结构图图 21.3 调试测试1.3.1 测试用例图 3连连看游戏界面显示模块显示游戏界
9、面显示提示信息鼠标控制模块选择图片控制游戏开始与结束图像处理模块图片识别图片判断图片消除第 5 页 共 19 页1.3.2 测试结果图 4图 51.3.3 存在问题(1)在设计时没有要求去设计提示次数,所以在设计时没有设计点击提示次数的限制条件,玩家可以一直点击提示。(2)还有分数是固定的,玩家完成所有图片的连接消除,所得的分数是一致的。第 6 页 共 19 页(3)为了提高算法的执行效率,鼠标点击图片的次序不同,可能导致出现本可以消除的一对图片不能消去,但解决方案很简单,就是颠倒鼠标点击的次序。(4)待消除的两图片在矩形区域内部并且有两个拐角时,为了提高执行效率,附加了一些判断条件,导致代码
10、量增多。1.3.4 改进设想(1)根据实际玩家需求,对系统提示次数加以限制,比如最多可以提示 5 次之类的具体方案,或者没使用一次提示,则所得分为 5 分而不是 10 分。(2)找一个良好的分数与时间的函数,使得玩家用时少的时候,所得分数相对要高一些。1.4 程序界面图 61.5 核心程序清单(1)LLKDLG.cpp 中/在重画函数中添加时间信息,关卡信息 和 路径线条,通过窗口重绘,来画路径和改变时间void CLLKDlg:OnPaint()CPaintDC dc3(this);第 7 页 共 19 页CWindowDC dc(this);CPen lPen(PS_SOLID, 2, R
11、GB(222,211,140);/画笔的样式,宽度,颜色/设置字体颜色dc.SelectObject(/选择画笔CFont font; /字体类CString str;str.Format(_T(“剩余时间: %3d 秒“),m_time);/m_time 中存储剩余时间信息font.CreatePointFont(100,_T(“宋体“); /请求的的大小,取其 1/10 为其字体大小,这个函数提供了一种简单的方法来创建指定字体类型和字体大小/设置字体dc.SelectObject(dc.SetTextColor(RGB(222,211,140);dc.SetBkColor(TRANSPAR
12、ENT);dc.TextOut(10,40,str); /显示时间/显示分数font.DeleteObject();font.CreatePointFont(100,_T(“宋体“);dc.SelectObject(dc.SetBkColor(RGB(201,186,131);/*m_score= m_time-30;*/str.Format(_T(“分数:%d“),m_score);dc.SetTextColor(RGB(255,255,255);dc.TextOut(610,35,str);CWindowDC dc2(this);CPen pen(PS_SOLID, 3, RGB(161,
13、23,21);dc2.SelectObject(pen);/画出路径if (LLKButton:ms_firstBtn != NULL pt1.x = LLKButton:ms_firstBtn-m_location.y * 50 + 45;pt1.y = LLKButton:ms_firstBtn-m_location.x * 50 + 70;pt2.x = LLKButton:ms_secondBtn-m_location.y * 50 + 45;pt2.y = LLKButton:ms_secondBtn-m_location.x * 50 + 70;pt3.x = LLKButton:
14、ms_ptCross1.y * 50 + 45;pt3.y = LLKButton:ms_ptCross1.x * 50 + 70;pt4.x = LLKButton:ms_ptCross2.y * 50 + 45;pt4.y = LLKButton:ms_ptCross2.x * 50 + 70;/无拐点if (LLKButton:ms_ptCross1.x = -1)第 8 页 共 19 页dc2.MoveTo(pt1);dc2.LineTo(pt2);/一个拐点else if (LLKButton:ms_ptCross2.x = -1)dc2.MoveTo(pt1);dc2.LineTo
15、(pt3);dc2.MoveTo(pt3);dc2.LineTo(pt2);/两个拐点elsedc2.MoveTo(pt1);dc2.LineTo(pt3);dc2.MoveTo(pt3);dc2.LineTo(pt4);dc2.MoveTo(pt4);dc2.LineTo(pt2); /初始化地图void CLLKDlg:InitMap(int mapMAXY)int i,j;int x,y;int type;/随机数种子srand(unsigned int)time(NULL);/srand 函数是随机数发生器的初始化函数。原型:void srand(unsigned int seed);
16、为了防止随机数每次重复,常常使用系统时间来初始化,即使用 time 函数来获得系统时间,它的返回值为从 00:00:00 GMT, January 1, 1970 到现在所持续的秒数,然后将 time_t 型数据转化为(unsigned)型再传给 srand 函数,/map 值表示图片类型,0 表示没有图片for(i=0;iCreate(str, WS_CHILD|BS_BITMAP|WS_VISIBLE,CRect(70+(i%(MAXY-2)*50, 70+(i/(MAXY-2)*50,120 +(i%(MAXY -2)*50, 120 +(i/(MAXY -2)*50), this,ID
17、C_BLOCK+i);if(btn-m_ID)/如果为 0 则不显示/尽量用绝对路径str.Format(_T(“res%d.bmp“), btn-m_ID);HBITMAP m_fkBmp = (HBITMAP):LoadImage /VC 中显示 bmp要用到 CBitmap 类(AfxGetInstanceHandle(),str, IMAGE_BITMAP, 0, 0,LR_CREATEDIBSECTION|LR_LOADFROMFILE);/加载图片if(m_fkBmp = NULL)if (MessageBox (_T(“ 缺 少 图 片 资 源! “), _T(“ 错 误“),M
18、B_ICONERROR|MB_OK) = IDOK)CDialog:OnCancel();return;btn-SetBitmap(m_fkBmp);btn-ShowWindow(SW_SHOW);elsebtn-ShowWindow(SW_HIDE);/是否通关了BOOL CLLKDlg:IsWin(void)/时间结束,没有过关if (m_time = 0)KillTimer(1);第 11 页 共 19 页MessageBox(_T(“Game Over !“),_T(“时间结束“);m_time = 30;/清除桌面的按钮for(int i=0; ip2.y)?p1.y:p2.y;mi
19、n = (p1.ymapp1.xi != 0)return FALSE;return TRUE;/在同一列if( (p1.y) = (p2.y) )第 12 页 共 19 页max = (p1.xp2.x)?p1.x:p2.x;min = (p1.xmapip1.y !=0)return FALSE;return TRUE;return FALSE;/有一个拐点的路径BOOL LLKButton:FindOneConner(CPoint p1, CPoint p2)int maxx, maxy, minx, miny;maxx = (p1.xp2.x)?p1.x:p2.x;maxy = (p1
20、.yp2.y)?p1.y:p2.y;minx = (p1.xmapminxmaxy = 0)ms_ptCross1.x = minx;ms_ptCross1.y = maxy;if (FindLine ( p1, ms_ptCross1) if(parent-mapminxminy = 0)ms_ptCross1.x = minx;ms_ptCross1.y = miny;if (FindLine (p1, ms_ptCross1) if(parent-mapmaxxminy = 0)ms_ptCross1.x = maxx;ms_ptCross1.y = miny;if (FindLine
21、(p1, ms_ptCross1) if(parent-mapmaxxmaxy = 0)ms_ptCross1.x = maxx;ms_ptCross1.y = maxy;if (FindLine (p1, ms_ptCross1) return FALSE;BOOL LLKButton:FindSide(CPoint p1, CPoint p2)int max, min;int i;BOOL line=TRUE;BOOL col=TRUE;if( (p1.x) = (p2.x) )max = (p1.yp2.y)?p1.y:p2.y;min = (p1.ymapp1.x-1i !=0)lin
22、e=FALSE;break;if(line)ms_ptCross1.x = p1.x-1;ms_ptCross1.y = p1.y;ms_ptCross2.x = p1.x-1;ms_ptCross2.y = p2.y;return TRUE;elseline=TRUE;for(i=min; imapp1.x+1i !=0) /下侧line=FALSE;break;if(line)ms_ptCross1.x = p1.x+1;ms_ptCross1.y = p1.y;ms_ptCross2.x = p1.x+1;ms_ptCross2.y = p2.y;return TRUE;elseline
23、=FALSE;if( (p1.y) = (p2.y) )max = (p1.xp2.x)?p1.x:p2.x;min = (p1.xmapip1.y-1 !=0) /左侧col=FALSE;break;if(col)ms_ptCross1.x = p1.x;ms_ptCross1.y = p1.y-1;ms_ptCross2.x = p2.x;ms_ptCross2.y = p2.y-1;return TRUE;elsecol=TRUE;for(i=min; imapip1.y+1 !=0) /右侧col=FALSE;第 15 页 共 19 页break;if(col)ms_ptCross1.
24、x = p1.x;ms_ptCross1.y = p1.y+1;ms_ptCross2.x = p2.x;ms_ptCross2.y = p2.y+1;return TRUE;elsecol=FALSE;if(line | col)return TRUE;elsereturn FALSE;/两个拐点BOOL LLKButton:FindTwoConner(CPoint p1, CPoint p2)/两个拐点必定有一个坐标相同,另一个坐标分别跟两个结点相同int i;CPoint tempPoint1 = 0;CPoint tempPoint2 = 0;/在两个目标结点的水平线上寻找拐点if(p
25、1.y=p2.y)for(i=p1.y-1; i=0; i-)/向左侧找tempPoint1.x = p1.x;tempPoint1.y = i;ms_ptCross1 = tempPoint1;if(parent-maptempPoint1.xtempPoint1.y=0)if(FindLine(tempPoint1, p1)tempPoint2.x = p2.x;tempPoint2.y = tempPoint1.y;if(parent-maptempPoint2.xtempPoint2.y=0)第 16 页 共 19 页if(FindLine(tempPoint1,tempPoint2)
26、ms_ptCross2 = tempPoint2;if(FindLine(tempPoint2, p2)return TRUE;if(p1.ymaptempPoint1.xtempPoint1.y=0)if(FindLine(tempPoint1, p1)tempPoint2.x = p2.x;tempPoint2.y = tempPoint1.y;if(parent-maptempPoint2.xtempPoint2.y=0)if(FindLine(tempPoint1,tempPoint2)ms_ptCross2 = tempPoint2;if(FindLine(tempPoint2, p
27、2)return TRUE;/在两个目标结点的垂直线上寻找拐点if(p1.x=p2.x)for(i=p1.x-1; i=0; i-)/上侧寻找tempPoint1.x = i;tempPoint1.y = p1.y;第 17 页 共 19 页ms_ptCross1 = tempPoint1;if(parent-maptempPoint1.xtempPoint1.y=0) )if(FindLine(tempPoint1, p1)tempPoint2.x = tempPoint1.x;tempPoint2.y = p2.y;if (parent -map tempPoint2.xtempPoint
28、2.y=0)if(FindLine(tempPoint1,tempPoint2)ms_ptCross2 = tempPoint2;if(FindLine(tempPoint2, p2)return TRUE;if(p1.xmaptempPoint1.xtempPoint1.y=0) )if(FindLine(tempPoint1, p1)tempPoint2.x = tempPoint1.x;tempPoint2.y = p2.y;if (parent -map tempPoint2.xtempPoint2.y=0)if(FindLine(tempPoint1,tempPoint2)ms_ptCross2 = tempPoint2;if(FindLine(tempPoint2, p2)return TRUE;第 18 页 共 19 页return FALSE;