计算机图形学作品设计报告
题目:
基于MFC和VC++6.0的连连看游戏系统设计
小组成员: (第三组)朱丽菊
李雪瑶
翁科宇
二〇##年六月
1 需求分析
游戏作为一种产业已经渐渐地为中国大众接受,它所带来的经济效益和社会效益更是人们之前所没有估计到的,当游戏作为一种很重要的娱乐手段被大众接受时,游戏产业就已经注定会带来巨大的经济效益。
连连看游戏是一款经典的小游戏,操作简单,适合所有的玩家。连连看游戏软件在windows环境下用VC++6.0工具,基于MFC框架设计开发,可以实现初始化界面,用户使用鼠标点击两张相同图案的小方块,链接路径若避开其他图片,且不超过两次转弯则消除次对小方块,并有初级、中级、高级的游戏难度选择和时间限制,丰富了游戏的娱乐性。此连连看游戏程序的目的是为了满足人们休闲的需要,在紧张工作之余休闲类的小游戏能够给人带来最大程度的放松,它界面美观,操作简单,具有一定的趣味性,是一款老少皆宜的休闲作品。
关于连连看的功能描述如下:运行游戏并自动进行初始化工作,将游戏区域上方 作为信息显示区域,并且通过菜单功能进行操控,菜单功能包括初级、中级和高级可供选择,放弃和退出游戏功能,提示和重列功能以及积分榜功能。游戏区域中心区域作为小方块的地图,由多种人物图案组成分布在不同位置,界面设有关卡、提示次数、 重列次数、进度条以及积分的显示。玩家可以通过选取相同的两个物件来对他们进行 消除操作,直到将游戏区域中的所有方块对都被消除后为胜利,并且给予破记录后的提示功能。 对于资源的需求,图片资源必不可少,游戏中的小方块是一组图片资源,选用两 组图案和排列完全相同但是背景色不同的两组图片作为小方块的初始状态和选定状态。为了使游戏更具娱乐性,背景音乐及音效的添加也是必不可少的,所以此游戏程序也加入了的音乐文件资源。
2 总体设计
2.1运行环境:
运行环境首先需要操作系统的支持,开发本程序使用Microsoft Windows操作系统,为使开发连连看游戏程序能稳定运行,在硬件上至少需要内存不低于64MB,40G 硬盘空间,对显卡要求不高,都能满足显卡需求,还有音响用于播放音乐。 保证了以上的配置,就能保证使用Microsoft Visual C++ 6.0 在Microsoft Windows系统中开发连连看游戏程序环境的可行性。
2.2功能模块设计:
本程序共分为三个模块,即游戏模块、选项模块和帮助模块。
在游戏模块中下分为初级子模块、中级子模块、高级子模块、放弃子模块和退出子模块。对于初级子模块、中级子模块和高级子模块通过时间限制的长短加以区分,放弃子模块用于退出当前的游戏界面,退出子模块用于退出游戏程序。
选项模块中下分为提示子模块、重列子模块、暂停子模块、音乐子模块、音效子 模块以及积分榜子模块。提示子模块和重列子模块作为游戏的特殊功能,增加游戏的 趣味性。暂停子模块可以暂停游戏。音乐子模块和音效子模块可以相互切换,用以清晰的显示音乐和音效的效果。积分榜子模块用于记录游戏的积分信息,并且对于破记录给予提示。
帮助模块中下分为关于连连看子模块和帮助子模块。关于连连看子模块主要介绍一下连连看的游戏方法和作者信息。帮助子模块用于介绍本游戏的一些特殊功能的使用方法。
2.3算法设计:
2.3.1游戏地图数据的设计
对于整个游戏区域,可以把它看作一个是由信息显示区域和图案方块游戏区域组成的地图。信息显示区域可以通过使用铅笔工具修改属性直接在需要的位置上画出即可实现。图案方块游戏区域由图案小方块零散地分布在地图的不同位置区域,并且每 一个图案小方块都有与其对应的完全一样的另外一个小方块。图案方块游戏区域被抽象成一个有坐标位置属性的平面,平面上零散的地分布着若干个小方块,并且这些小 方块的人物图案起码是成对出现的,可以把游戏区域地图用一个数组 llk_map[i][j]来表示,llk_map[i][j]是把地图设计成一个动态分配的int 整型一维数组,对地图中的行列数的表达,用一个转换法即可。
在游戏开始前,需要对游戏的地图进行初始化。设计一个初始化的类,信息的显示直接调用绘图类即可。对于方块地图游戏区域的初始,设计初级、中级、高级均为10×9 大小的区域,物的种类共计15个图案,首先通过srand ()设定随机数发生,然后通过rand()函数进行图片种类数量的选择。至此,游戏地图的设计完毕。
2.3.2 图案方块连接判断的设计
对于选中的两个方块的销毁,它们必须符合以下三个条件:
⑵ 选中的两个图案相同。
⑵ 选中的两个方块之间在没有障碍物的情况下,可以用若干垂直的直线线段连 起来。
⑶ 这些将它们连接起来的直线线段的折点不超过两个。
同种物件的连接方式大致可以分成以下3 种:
⑴ 直连方式。在直连方式中,必须要求所选定的两个方块在同一水平直线上,并且两个方块之间没有任何其它的图案方块。在3 种连接方式中,直连方式是最简单的。
⑵ 有一个折点的垂直线段连接。所选定的两个方块如果通过折点的方式连接,那么对于折点来说,每个折点必定有且至少有一个坐标是和其一个目标点是相同的,即折点必定在两个目标点所在的 X 方向或Y 方向的直线上。此外,对于一个折点连 接的情况,折点应该为第一个选中方块的横向现或纵向线与第二个选中方块的纵向线 或横向线相交而得出。还需说明的是,是这些所有可能连接的交线经过的每一个图案 方块区域,它们都不允许被障碍物所阻挡。
⑶ 有两个折点的垂直线段连接。这种方式的两个折点所连成的直线与两物件的直接连线可以构成平行线,因此可以根据这个规律,将这条水平线在游戏区域允许的条件上下移动,然后通过判断整条带垂直折点的曲线之间有无障碍物方式来确定是否可以联通。
经过上面较为详细的分析后,对选定的方块是否可以作抵消操作可以这样去设计,首先,对简单的直连情况进行判断,看其是否符合条件,假如不能,再加深一个 级别的复杂度,对一个折点的情况进行判断,如果不符合条件,再对两个折点的情况进行判断。
2.3.3 游戏胜利判断算法设计
与前面的两个选定方块能否抵消的判断功能相比,它的实现显得相当简单,只需对图案方块游戏区域所有方块进行判断就可以了,只要检测到地图中还有一个图案方块还没有被抵消,并且游戏的级别不是高级,则证明游戏还没有结束,完成判断。如果时间耗尽,或者在没有生命点数时出现死局,则终止游戏。如果通过初级、中级后,进入高级,并且地图中的所有小方块都被消除,则证明游戏已经胜利,如果打破记录,会弹出提示。
2.3.4 游戏道具算法设计
在此游戏中的道具有重排和提示两种,在游戏进行的过程中,如果玩家暂时找不到可解的小方块,就可以使用道具功能。重排功能是在原小方块位置不变的情况下,重新排列图案的顺序,以找到可解的小方块。提示功能用于提示玩家一组可解的小方块,便于玩家可以继续进行游戏。
对于重排功能的实现,首先将所有小方块的位置编号暂时保存到内存中,然后随机地分布在有小方块的位置上,通过随机函数实现此功能。
对于提示功能,首先编写一个检查连接的类,判断选中的两个小方块是否可以进行消除操作,这个类可以被提示功能函数调用,也可以在检查是否有解时被调用。在提示功能中,首先调用检查连接的类查找到一个可以连接的小方块,并且标记,然后 更改其为选中状态,即改变颜色以达到醒目,用来提示玩家。
3 详细设计
3.1功能模块设计与实现
此连连看游戏程序根据模块的性质分为以下几大类:基本功能模块,主要通过游戏的菜单显示出来,分为游戏模块、选项模块以及帮助模块。游戏绘图模块,包括小方块图案的载入以及游戏地图背景的绘制。游戏交互模块,主要是快捷键和鼠标交互功能的实现。游戏算法模块,包括游戏的连接判断算法以及游戏胜利判断的算法。
3.1.1游戏模块的设计与实现
游戏模块中下分为初级子模块、中级子模块、高级子模块、放弃子模块和退出子模块。设置为10×9的地图,即横向为10个小方块, 纵向为9个小方块。由于地图的大小为10行9列共90个小方块, 每个种类的人物设定出现 6 次为偶数, 这样就可以实现成对的出现了,在游戏中可以完成配对。这样一来,共 90 个小方块且每个人物出现 6 次, 也就是说,共有 15 个人物。初级、 中级和高级不可同时处于可选状态,即初级正在游戏的时候,打开菜单,初级的选项是不可选的,而中级和高级应为可选状态,通过 pCmdUI->Enable()将初级、 中级和高级设置成可以切换的菜单, 然后分别在初级、中级和高级各自的初始化中设置其可选或者是不可选的状态即可实现。游戏模块中的放弃子模块设定好放弃的状态即可,游戏模块中的退出子模块调用程序退出语句 PostQuitMessage(0)即可,比较容易实现。
3.1.2选项模块设计与实现
选项模块中分为提示子模块、 重列子模块、 暂停子模块、 音乐子模块、 音效子模块以及积分榜子模块。对于提示子模块和重列子模块,将这两种算法各封存在一个类当中,作为道具可以随时进行调用,并且有次数记录,当记录次数到达零以下,立即给出提示。对于提示模块,采用遍历的方法进行查找,首先遍历同一种图块中出现在不同坐标位置的图案小方块, 以此模拟第一次点击, 然后遍历同一种图块的剩余的不同坐标图案小方块的出现,以此模拟第二次点击,判断前后 2 次模拟点击选中的不同位置的同种图块能否连通,能连通则高亮显示这2个图块,提示完成;否则,继续检测连通情况直至找到为止。对于重列模块,首先通过遍历的方法,依次读取各个位置的图片,增加到临时的图片列表中,然后再随机的循环依次在原来有图片的地方添加图片,从图片列表中删除用过的图片,不删除就会有可能重复了,最后进行刷新即可。流程图如图3.1和3.2所示。
图3.1 重列功能判断流程图
图3.2提示功能判断流程图
对于暂停子模块,首先停止计数器运行,即停止进度条的运行,并且弹出对话框,实现暂停功能,点击确定后可以继续进行游戏。音乐和音效功能子模块设置为缺省关系,即音效激活状态下音乐功能不可用,反之亦然,通过 pCmdUI->SetCheck()语句实现缺省功能,音乐音效的播放通过 PlaySound功能即可实现。PlaySound 函数的声明为 BOOL PlaySound(LPCSTR pszSound, HMODULE hmod,DWORD fdwSound);参数 pszSound 是指定了要播放声音的字符串,该参数可以是 WAVE 文件的名字,或是 WAV 资源的名字,或是内存中声音数据的指针,或是在系统注册表 WIN.INI 中定义的系统事件声音。如果该参数为NULL则停止正在播放的声音。参数 hmod 是应用程序的实例句柄, 当播放 WAV 资源时要用到该参数,否则它必须为 NULL。参数 fdwSound 是标志的组合,如下表所示。 若成功则函数返回 TRUE, 否则返回 FALSE。
对于积分榜子模块,首先在游戏结束后弹出对话框提示输入姓名,游戏的关卡数、积分自动进行统计,然后保存到文档中。对于破记录的提示问题,在一个游戏结束时,会弹出 CAddDlg 看得到的分数,这个对话框弹出来时,从数据文件读取出来,然后排序,比较一下当前的分数,当前分数大于那个链表中最大值,就提示破记录了,但是在调出积分榜时,首先要进行一下排序,然后在表格中显示出来。
3.1.3 帮助模块的设计与实现
帮助模块的设计比较简单,其下属子菜单功能相同,只是弹出对话框进行提示,通过 AfxMessageBox 实现,将必要的说明和提示信息写入即可实现。
3.1.4 游戏地图数据的设计与实现
整个游戏地图分为信息显示区域和游戏区域。信息显示区域部分分布在整个地图的上方,左侧为关卡、 重列和提示信息,通过使用铅笔工具直接在相应的位置上直接描绘出来。中间为进度条, 通过 move to 和 link to 绘制一个边框,然后通过计数器进行控制,每隔固定时间内在进度条当前时间位置进行加 1 操作,即在固定时间内绘制固定的进度。在信息显示区域的右侧,绘制积分的显示,同样通过使用铅笔工具进行绘制即可,比较容易实现。
对于游戏区域,被抽象成一个有坐标位置属性平面,平面上零散地分布着若干小方块,在绘制地图的类中添加地图数据成员变量,用于记录动态分配出来的一维数组地图空间的首地址,对于地图中的某个小方块的类型,可以用一个整型的 ID 来进行识别。地图的数据结构设计完毕。在游戏开始前,先要进行初始化,为了保证小方块能成对地出现,不应该仅仅对人物图案做简单地随机抽取,然后将随机选取出来的小方块放到地图中去就了事,而是需要成对的选取人物小方块,就是说地图中的小方块必须是偶数个才行。把地图设置成动态分配的方式,目的是让其空间可以根据行列数的需求动态地获取,而对于实际大小不同的地图空间即可。
对于图案的布局,依旧是采用常用的机制,先用 srand()函数对时间函数布下随机种子,然后调用 rand()函数对具体的图案方块的种类进行随机的获取。
对于图片资源的,可以先在内存创建两个内存位图,并对它们的图像进行载入。在需要使用的时候,则可以从这些内存位图中直接去拷贝,并绘制到游戏区域内存位图中去。首先,对方块的两种状态所对应的图片分别创建相应的设备环境,并且为它们分配关联的内存位图变量。而对于游戏区域的绘制,则通过设备环境中的图形物件群的调用进行直接绘制。
3.1.5 图案方块的判断连接的设计与实现
设计思路是:假设目标点 p1 , p2 ,如果有两个折点分别为 z1 , z2 那么,所要进行的是:
⑵ 如果验证 p1, p2 直线连线, 则连接成立。
⑵ 对于一个折点和两个折点的情况下。搜索以 p1, p2 的 x,y 方向四条直线(可能某两条直线会重合)上的有限点,每次取两点作为 z1, z2 ,验证 p1 到 z1/z1 到 z2/z2到 p2 是否都能直线相连,是则连接成立,这是两个折点的情况。对于一个折点, 即z1=z2,对判断没影响,每次取一点作为 z1 或 z2 ,验证 p1 到 z1/z1 或 z2/z2 到 p2 是否都能直线相连,是则连接成立。
图3.3 连接判断流程图
根据流程图的实现中,先是对直连方式中的X方向直连, 以及Y方向直连这两种方式作出判断, 如果尚未取得结果,再调用一个折点的情况进行判断,或者调用两个折点的情况进行判断。把直连方式分成直通、左通和右通三种情况,简单介绍一下。直通就是在选定的两个方块的直连线中,没有被任何方块所阻碍。左通就是选定的两个方块的连线之间有其它方块阻碍,但是通过它的左侧可以将它们无障碍的连通,从而不构成垂直折点的效果,而两个折点了连通方式中的其中一个类似的情况是,线的连通起码要偏移一个方块的距离来形成连通。右通形式与左通形式相同。对于如何判断选定两个图案是否可以连通抵消,是通过一个接口函数CheckConnection来实现的。
首先,通过方块的不同状态确定所选位置是否有图案,通过一个变量 PicIndex进行记录,如果有图案则记录位置,如果没有则不进行操作,然后选择第二个位置,如果此前所选位置有图案的话, 那么首先判断第二次所选的小方块是不是第一次所选的小方块, 通过坐标进行记录和判断, 如果是就恢复原始状态,如果不是就进行判断了。先对人物图案是否相同进行判断,通过 llk_map[ytempindex][xtempindex].PicIndex =llk_map[yindex][xindex].PicIndex 语句实现,如果不同则恢复原始状态,如果相同就进行路径的判断了,在这里就需要用到前面所说的 CheckConnection 函数进行判断了,判断通过就进行消除,对于图片的消除,先更改图片状态,然后进行绘图,并且加上音效的播放代码,就可以实现预期的效果了。
3.1.6 游戏结束判断功能的设计与实现
在游戏程序中,游戏结束的判断往往是非常重要的,它表示着进行一次游戏的最终结果。对于本游戏程序对游戏结束的判断,自然是先检查游戏区域中的小方块是否都消除,由于某个位置是否由小方块采用不同的数值进行记录,所以只需判断各个小方块位置的状态就可以了。其次,由于本游戏程序的设计是按照连贯性进行设计的,倘若在初级或者是中级进行游戏时,当地图中的小方块都被消除时,应自动进行下一级,这里可以通过 if 和 else 进行判断,如果是在高级中将游戏区域中的小方块都消除完,那么游戏将结束,并给予游戏结束的提示。还有一种情况就是当游戏没有进行完的情况,当时间都消耗尽或者已无生命点数时出现死局时,即游戏地图中没有一组可以消除的方块,则游戏结束,都应给予提示游戏结束的原因,在计数器中添加相应的数值进行跟踪,当计数器也就是进度条到达底线时,在游戏结束函数中采用触发模式对游戏进行终止,对无生命点数的判断也采用相同的原理来实现。
3.1.7 鼠标交互功能的设计与实现
鼠标选取两个图案方快后,程序将自动判断所选的两个方块是否能进行抵消操作。下面按照预先设计出的鼠标事件处理流程, 对整个功能模块的具体协调与实现过程进行简单地描述。
(1)首先,利用鼠标的当前坐标位置 point 对每个小单元方块的宽度和高度分别取模,获取当前鼠标落点所在游戏区域的具体行列数;
(2)判断出该行列数是否符合条件。保证运算出来的行数和列数在预定义区域最大行数和列数内,并且点击的区域状态不是空白方块区域。
(3)对于判断此次鼠标事件的选取是否与第一个方块的选取一样,只需通过用于记录第一个被选中的方块的行列数的成员变量是否有效即可。
(4)对于本次选中的方块为第一个选中的情况,先对选中的方块添加一个红色的矩形外边框,用以提示用户当前的第一个方块选中所在的位置。
(5)对该选定方块作一些判断,以便更高效的处理。判断选中的方块与前一方块是否为同一方块,并且不与上一次选定的方块为同一方块,然后才跳到下一步对两个选定的方块是否可以抵消的流程中去。
(6)调用前面已经实现的大功能函数CheckConnection来判断所选两个图案是否可以相互抵消。
(7)如果可以抵消,对选中的两个方块在内部核心地图对应的数据状态作适当的修改,将它们的状态记作已经被销毁的空方块状态。
(8)完成第二个图案的选取工作与相关的功能操作后,需要对前面已经选取的第一个方块位置的记录做清理工作,以便下一个新方块组的选择。
(9)最后,判断此次的鼠标操作是否已经胜利结束,如果是则给予用户提示,然后重新开始新的一关。
图3.4 鼠标交互流程图
4 源程序
(源程序给出主要模块的头文件和源文件)
//画地图子模块
void CLXYView::OnDraw(CDC* pDC)
{
CLXYDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
// 渲染客户去
CRect rcBlock;
rcBlock.SetRect(0,0,660,640);
pDC->FillSolidRect(&rcBlock,RGB(100,100,200));
CDC dcMemory1,dcMemory2;
dcMemory1.CreateCompatibleDC(pDC);
dcMemory1.SelectObject(&bitmap1);
dcMemory2.CreateCompatibleDC(pDC);
dcMemory2.SelectObject(&bitmap2);
//输出图形
int i,j;
for(i=1;i<=ROW_NUMBER;i++)
{
for(j=1; j<=LINE_NUMBER;j++)
{
if(llk_map[i][j].PicIndex>0)
{
if(llk_map[i][j].state==0)
pDC->BitBlt(tilex(j),tiley(i),
PIC_WIDTH,PIC_HEIGHT,&dcMemory1,PIC_WIDTH*(llk_map[i][j].PicIndex-1),0 ,SRCCOPY);
else if(llk_map[i][j].state==1)
pDC->BitBlt(tilex(j),tiley(i),
PIC_WIDTH,PIC_HEIGHT,&dcMemory2,PIC_WIDTH*(llk_map[i][j].PicIndex-1),0 ,SRCCOPY);
}
}
}
if(bingame)
{
DrawRectangleHollow();
DrawInfo(120,20,"剩余时间");
DrawInfo(20,20,"级别:");
DrawInfo(20,40,"关卡:");
DrawInfo(20,60,"重列:");
DrawInfo(20,80,"提示:");
DrawInfo(540,20,"分数:");
DrawInfo2(60,20,g_userlevel);
DrawInfo2(60,40,g_usergate);
DrawInfo2(60,60,m_sortnumber);
DrawInfo2(60,80,m_tishinumber);
DrawInfo2(560,40,g_userscore);
}
}
//判断连接子模块
//
bool CLXYView::CheckConnection(int x1, int y1, int x2, int y2)
{
int i,temp;
//分为八种
if((x1==x2))//同一行
{
if(IsLineConnection(x1,y1,x2,y2)) return true;
else
{
for(i=1;i<=x1;i++)
{
if(IsLinePicEmpty(x1,y1,(x1-i),y1))
if(IsNotLineConnection((x1-i),y1,x2,y2))
return true;
}
temp=VIRTUAL_ROW-x1;
for(i=1;i<=temp;i++)
{
if(IsLinePicEmpty(x1,y1,(x1+i),y1))
if(IsNotLineConnection((x1+i),y1,x2,y2))
return true;
}
}
}
if((y1==y2))//同一列
{
if(IsLineConnection(x1,y1,x2,y2)) return true;
else
{
for(i=1;i<=y1;i++)
{
if(IsLinePicEmpty(x1,y1,x1,(y1-i)))
if(IsNotLineConnection(x1,(y1-i),x2,y2))
return true;
}
temp=VIRTUAL_LINE-y1;
for(i=1;i<=temp;i++)
{
if(IsLinePicEmpty(x1,y1,x1,(y1+i)))
if(IsNotLineConnection(x1,(y1+i),x2,y2))
return true;
}
}
}
if((x2>x1)&&(y2>y1)) //(x2,y2)在(x1,y1)右下方
{
if(IsNotLineConnection(x1,y1,x2,y2))//先判断只有一个折点 * 代表两个点
// *----------|
return true; // | |
// |__________*
//先往下判断
temp=VIRTUAL_ROW-x1;
for(i=1;i<=temp;i++)
{
if(IsLinePicEmpty(x1,y1,(x1+i),y1))
{
if(IsNotLineConnection((x1+i),y1,x2,y2))
return true;
}
}
//往右判断
temp=VIRTUAL_LINE-y1;
for(i=1;i<=temp;i++)
{
if(IsLinePicEmpty(x1,y1,x1,(y1+i)))
{
if(IsNotLineConnection(x1,(y1+i),x2,y2))
return true;
}
}
//往上判断
for(i=1;i<=x1;i++)
{
if(IsLinePicEmpty(x1,y1,(x1-i),y1))
{
if(IsNotLineConnection((x1-i),y1,x2,y2))
return true;
}
}
//往左判断
for(i=1;i<=y1;i++)
{
if(IsLinePicEmpty(x1,y1,x1,(y1-1)))
{
if(IsNotLineConnection(x1,(y1-1),x2,y2))
return true;
}
}
}
if((x2>x1)&&(y2<y1)) //(x2,y2)在(x1,y1)左下方
{
if(IsNotLineConnection(x1,y1,x2,y2))//先判断只有一个折点 * 代表两个点
// |----------*
return true; // | |
// *__________|
//先往下判断
temp=VIRTUAL_ROW-x1;
for(i=1;i<=temp;i++)
{
if(IsLinePicEmpty(x1,y1,(x1+i),y1))
{
if(IsNotLineConnection((x1+i),y1,x2,y2))
return true;
}
}
//往左判断
temp=y1;
for(i=1;i<=temp;i++)
{
if(IsLinePicEmpty(x1,y1,x1,(y1-i)))
{
if(IsNotLineConnection(x1,(y1-i),x2,y2))
return true;
}
}
//往上判断
temp=x1;
for(i=1;i<=temp;i++)
{
if(IsLinePicEmpty(x1,y1,(x1-i),y1))
{
if(IsNotLineConnection((x1-i),y1,x2,y2))
return true;
}
}
//往右判断
temp=VIRTUAL_LINE-y1;
for(i=1;i<=temp;i++)
{
if(IsLinePicEmpty(x1,y1,x1,(y1+i)))
{
if(IsNotLineConnection(x1,(y1+i),x2,y2))
return true;
}
}
}
if((x2<x1)&&(y2<y1)) //(x2,y2)在(x1,y1) 左上方
{
if(IsNotLineConnection(x1,y1,x2,y2))//先判断只有一个折点 * 代表两个点
// *----------|
return true; // | |
// |__________*
//先往上判断
temp=x1;
for(i=1;i<=temp;i++)
{
if(IsLinePicEmpty(x1,y1,(x1-i),y1))
{
if(IsNotLineConnection((x1-i),y1,x2,y2))
return true;
}
}
//往左判断
temp=y1;
for(i=1;i<=temp;i++)
{
if(IsLinePicEmpty(x1,y1,x1,(y1-i)))
{
if(IsNotLineConnection(x1,(y1-i),x2,y2))
return true;
}
}
//先往下判断
temp=VIRTUAL_ROW-x1;
for(i=1;i<=temp;i++)
{
if(IsLinePicEmpty(x1,y1,(x1+i),y1))
{
if(IsNotLineConnection((x1+i),y1,x2,y2))
return true;
}
}
//往右判断
temp=VIRTUAL_LINE-y1;
for(i=1;i<=temp;i++)
{
if(IsLinePicEmpty(x1,y1,x1,(y1+i)))
{
if(IsNotLineConnection(x1,(y1+i),x2,y2))
return true;
}
}
}
if((x2<x1)&&(y2>y1)) //(x2,y2)在(x1,y1)右上方
{
if(IsNotLineConnection(x1,y1,x2,y2))//先判断只有一个折点 * 代表两个点
// |----------*
return true; // | |
// *__________|
//先往上判断
temp=x1;
for(i=1;i<=temp;i++)
{
if(IsLinePicEmpty(x1,y1,(x1-i),y1))
{
if(IsNotLineConnection((x1-i),y1,x2,y2))
return true;
}
}
//往右判断
temp=VIRTUAL_LINE-y1;
for(i=1;i<=temp;i++)
{
if(IsLinePicEmpty(x1,y1,x1,(y1+i)))
{
if(IsNotLineConnection(x1,(y1+i),x2,y2))
return true;
}
}
//先往下判断
temp=VIRTUAL_ROW-x1;
for(i=1;i<=temp;i++)
{
if(IsLinePicEmpty(x1,y1,(x1+i),y1))
{
if(IsNotLineConnection((x1+i),y1,x2,y2))
return true;
}
}
//往左判断
temp=y1;
for(i=1;i<=temp;i++)
{
if(IsLinePicEmpty(x1,y1,x1,(y1-i)))
{
if(IsNotLineConnection(x1,(y1-i),x2,y2))
return true;
}
}
}
return false;
}
bool CLXYView::IsLineConnection(int x1, int y1, int x2, int y2) //判断直线两点是不是可连通的
{
int i,temp;
if(x1==x2) //同一行
{
temp=abs(y2-y1);
if(y2>y1)
{
for(i=1;i<temp;i++)
if(llk_map[x1][y1+i].PicIndex!=0)
return false;
}
else
{
for(i=1;i<temp;i++)
if(llk_map[x1][y1-i].PicIndex!=0)
return false;
}
}
if(y1==y2) //同一列
{
temp=abs(x2-x1);
if(x2>x1)
{
for(i=1;i<temp;i++)
if(llk_map[x1+i][y1].PicIndex!=0)
return false;
}
else
{
for(i=1;i<temp;i++)
if(llk_map[x1-i][y1].PicIndex!=0)
return false;
}
}
return true;
}
// 判断直线(射线)除起始点 图片是否为空
// 0 2 0 0 0 0 0
bool CLXYView::IsLinePicEmpty(int x1, int y1, int x2, int y2)
{
int i,temp;
if(x1==x2) //同一行
{
temp=abs(y2-y1);
if(y2>y1)
{
for(i=1;i<=temp;i++)
if(llk_map[x1][y1+i].PicIndex!=0)
return false;
}
else
{
for(i=1;i<=temp;i++)
if(llk_map[x1][y1-i].PicIndex!=0)
return false;
}
}
if(y1==y2) //同一列
{
temp=abs(x2-x1);
if(x2>x1)
{
for(i=1;i<=temp;i++)
if(llk_map[x1+i][y1].PicIndex!=0)
return false;
}
else
{
for(i=1;i<=temp;i++)
if(llk_map[x1-i][y1].PicIndex!=0)
return false;
}
}
return true;
}
// 情况二:经过一个折点相连(+号代表折点)
// 0 0 0 0 0 0
// 0 2 0 0 0 + * ------ +
// 0 + 0 0 0 2 + ------ *
// (两条路都可连通)
bool CLXYView::IsNotLineConnection(int x1, int y1, int x2, int y2)
{
if(IsLinePicEmpty(x1,y1,x1,y2))
{
if(IsLineConnection(x1,y2,x2,y2))
return true;
}
if(IsLinePicEmpty(x1,y1,x2,y1))
{
if(IsLineConnection(x2,y1,x2,y2))
return true;
}
return false;
}
//是否无解
bool CLXYView::IsNoConnection()
{
int i;
LLK_PATH *pic=NULL;
LLK_PATH *Nextpic = NULL;
for(i=1;i<=15;i++)
{
pic=pic_place[i];
while(pic!=NULL)//pic->next
{
Nextpic=pic;
while(Nextpic->next!=NULL)
{
Nextpic=Nextpic->next;
if(CheckConnection(pic->x,pic->y,Nextpic->x,Nextpic->y))
return false;
}
pic=pic->next;
}
}
return true;
}
//初级子模块
void CLXYView::OnMenuFirst()
{
// TODO: Add your command handler code here
int i,j;
int temprand;
CString str;
CRect rect;
//产生随机序列 (但是做得随机性不好)
srand((unsigned)time(NULL));
for(i=1;i<=ROW_NUMBER;i++)
for(j=1;j<=LINE_NUMBER;j++)
{
while(1)
{
temprand=rand()%15; // 产生0-14的随机数
if(pic_number[temprand]<6)
{
pic_number[temprand]++;
llk_map[i][j].state=0;
llk_map[i][j].event=0;
llk_map[i][j].PicIndex=temprand+1; // 1-15
break;
}
}
}
//列出每幅图片所有的位置
ListPicPlace();
//初始化事件
InitEvent();
g_userscore=0;
g_userlevel=1;
g_usergate=1;
//程序控制
bingame=true;
bfirst=true;
DrawRectangleHollow();
m_nUpper=0;
DrawProgress(m_nUpper); // 总共400个时间
m_nSpeed=2;
SetTimer(1,1600,0);
DrawInfo(120,20,"剩余时间");
DrawInfo(20,20,"级别:");
DrawInfo(20,40,"关卡:");
DrawInfo(20,60,"重列:");
DrawInfo(20,80,"提示:");
DrawInfo(540,20,"分数:");
DrawInfo2(60,20,g_userlevel);
DrawInfo2(60,40,g_usergate);
DrawInfo2(60,60,m_sortnumber);
DrawInfo2(60,80,m_tishinumber);
DrawInfo2(560,40,g_userscore);
DrawMap();
}
//中级子模块
void CLXYView::OnMenuSecond()
{
// TODO: Add your command handler code here
int i,j;
int temprand;
CString str;
CRect rect;
//产生随机序列 (但是做得随机性不好)
srand((unsigned)time(NULL));
for(i=1;i<=ROW_NUMBER;i++)
for(j=1;j<=LINE_NUMBER;j++)
{
while(1)
{
temprand=rand()%15; // 产生0-14的随机数
if(pic_number[temprand]<6)
{
pic_number[temprand]++;
llk_map[i][j].state=0;
llk_map[i][j].event=0;
llk_map[i][j].PicIndex=temprand+1; // 1-15
break;
}
}
}
//列出每幅图片所有的位置
ListPicPlace();
//初始化事件
InitEvent();
g_userscore=0;
g_userlevel=2;
g_usergate=1;
//程序控制
bingame=true;
bsecond=true;
DrawRectangleHollow();
m_nUpper=0;
DrawProgress(m_nUpper); // 总共400个时间
m_nSpeed=2;
SetTimer(1,1200,0);
DrawInfo(120,20,"剩余时间");
DrawInfo(20,20,"级别:");
DrawInfo(20,40,"关卡:");
DrawInfo(20,60,"重列:");
DrawInfo(20,80,"提示:");
DrawInfo(540,20,"分数:");
DrawInfo2(60,20,g_userlevel);
DrawInfo2(60,40,g_usergate);
DrawInfo2(60,60,m_sortnumber);
DrawInfo2(60,80,m_tishinumber);
DrawInfo2(560,40,g_userscore);
DrawMap();
}
//高级子模块
void CLXYView::OnMenuThree()
{
// TODO: Add your command handler code here
int i,j;
int temprand;
CString str;
CRect rect;
//产生随机序列 (但是做得随机性不好)
srand((unsigned)time(NULL));
for(i=1;i<=ROW_NUMBER;i++)
for(j=1;j<=LINE_NUMBER;j++)
{
while(1)
{
temprand=rand()%15; // 产生0-14的随机数
if(pic_number[temprand]<6)
{
pic_number[temprand]++;
llk_map[i][j].state=0;
llk_map[i][j].event=0;
llk_map[i][j].PicIndex=temprand+1; // 1-15
break;
}
}
}
//列出每幅图片所有的位置
ListPicPlace();
//初始化事件
InitEvent();
g_userscore=0;
g_userlevel=3;
g_usergate=1;
//程序控制
bingame=true;
bthree=true;
DrawRectangleHollow();
m_nUpper=0;
DrawProgress(m_nUpper); // 总共400个时间
m_nSpeed=2;
SetTimer(1,800,0);
DrawInfo(120,20,"剩余时间");
DrawInfo(20,20,"级别:");
DrawInfo(20,40,"关卡:");
DrawInfo(20,60,"重列:");
DrawInfo(20,80,"提示:");
DrawInfo(540,20,"分数:");
DrawInfo2(60,20,g_userlevel);
DrawInfo2(60,40,g_usergate);
DrawInfo2(60,60,m_sortnumber);
DrawInfo2(60,80,m_tishinumber);
DrawInfo2(560,40,g_userscore);
DrawMap();
}
//放弃子模块
void CLXYView::OnMenuBack()
{
// TODO: Add your command handler code here
KillTimer(1);
bingame=false;
bfirst=false;
bsecond=false;
bthree=false;
bSecondClicked=false;
bstop=false;
m_dotNumber=0;
m_tishinumber=10;
m_sortnumber=5;
//图片坐标
xindex=-1;
yindex=-1;
InitMap();
DrawBlank();
}
//退出子模块
void CLXYView::OnMenuExit()
{
// TODO: Add your command handler code here
PostQuitMessage(0);
}
//提示子模块
void CLXYView::OnMenuInfo()
{
// TODO: Add your command handler code here
if(bingame)
{
if(m_tishinumber>0)
{
m_tishinumber--;
DrawInfo(60,80,m_tishinumber);
BackPicPlace();
}
else
{
AfxMessageBox("提示已经用完");
}
}
}
//重列子模块
void CLXYView::OnMenuSort()
{
// TODO: Add your command handler code here
if(bingame)
{
if(m_sortnumber>0)
{
m_sortnumber--;
DrawInfo(60,60,m_sortnumber);
SortPicPlace();
}
else
{
AfxMessageBox("重列次数已经用完");
}
}
}
//暂停子模块
void CLXYView::OnMenuStop()
{
// TODO: Add your command handler code here
if(bstop)
SetTimer(1,1000,0);
else KillTimer(1);
bstop=!bstop;
}
//积分子模块
void CLXYView::OnMenuScore()
{
// TODO: Add your command handler code here
CRecordDlg recorddlg;
recorddlg.DoModal();
}
//时间到子模块
void CLXYView::OnTimer(UINT nIDEvent)
{
// TODO: Add your message handler code here and/or call default
if(m_nUpper<400)
{ m_nUpper=m_nUpper+m_nSpeed;
DrawProgress(m_nUpper);
}
else
{
KillTimer(1);
MessageBox(_T("你的时间用完了,SORRY!"),_T("连连看"),MB_YESNO);
//调用排行榜
OnMenuBack();
}
CView::OnTimer(nIDEvent);
}
5 程序运行效果图
(程序运行效果图给出程序的动态效果图)
[参考文献]
[1] 严蔚敏,吴伟民.数据结构(C语言版)[M]. 北京:清华大学出版社,1997.4
[2] 沈晴霓,聂青,苏京霞.现代程序设计—C++与数据结构面向对象的方法与实现[M].
北京:北京理工大学出版社,2002.8
[3] Thomas Connolly Carolyn Begg. Database Systems [M].北京:电子工业出版社,2004.7
[4] Roger Bate, Sandy Shrum, CMM Integration Framework[J], CMU/SEI Spotlight 1998.9
[5] J P Kuilboer,N Ashrafi, Software Process and Produt Improvement[J]. An Empirical Assessment,2000.4
[6] 张美金 著. 基于ASP技术的远程教育系统体系结构的研究. http://172.50.0.88:86 /~cddbn/Y517807/pdf/index.htm,20##-05-01.
[7]王伟国,刘永萍,王生年,徐晓鹏.B/S模式网上考试系统分析与设计[J].石河子大学学报(自然科学版),2003,6(2):145-147