武汉理工大学数据结构与算法综合实验连连看.docx
《武汉理工大学数据结构与算法综合实验连连看.docx》由会员分享,可在线阅读,更多相关《武汉理工大学数据结构与算法综合实验连连看.docx(25页珍藏版)》请在冰豆网上搜索。
武汉理工大学数据结构与算法综合实验连连看
学生学号
Xxx
实验课成绩
学生实验报告书
实验课程名称
数据结构与算法综合实验
开课学院
计算机科学与技术学院
指导教师姓名
xx
学生姓名
学生专业班级
xxxx
2015
--
2016
学年
第
2
学期
实验课程名称:
实验项目名称
连连看游戏综合实践
报告成绩
实验者
xxx
专业班级
xxxxx
组别
同组者
完成日期
年月日
第一部分:
实验分析与设计(可加页)
一、实验目的和要求
1.目的
●调研连连看游戏,了解连连看游戏的功能和规则等。
●掌握集成开发工具。
●掌握C++的基础编程。
●了解MFC框架,包括MFCDialog应用程序和GDI编程。
●了解线性结构,重点掌握数组和栈操作,掌握数组的遍历、消子和胜负判断等算法。
●了解企业软件开发过程,了解系统需求分析和设计,应用迭代开发思路进行项目开发。
●养成良好的编程习惯和培养软件工程化思维,综合应用“C++编程、MFCDialog、算法、线性结构”等知识,开发“连连看游戏”桌面应用程序,达到掌握和应用线性核心知识的目的。
2.要求
待开发的连连看游戏称为“欢乐连连看”,使用二维数组来保存游戏地图的数据,实现连连看的核心功能。
欢乐连连看的功能有:
主界面、开始游戏、消子、判断胜负、提示、重排、计时、游戏模式。
●主界面
游戏主界面就是进行各项操作的入口。
●开始游戏
玩家选择开始游戏模式,进入游戏后,选择开始游戏,系统根据设置的主题风格生成图片布局,以供玩家点击消除。
游戏地图大小为640*400,是一个16行乘10列的矩形,分成160个小正方形,存放160张图片,每张图片大小为40*40。
●消子
对玩家选中的两张图片进行判断,判断是否符合消除规则。
只有符合以下规则的图片对才能被消除:
v一条直线连通
v两条直线连通
v三条直线连通
如果可以消除,从游戏地图中提示连接路线,然后消除这两张图片,并计算相应的积分。
如果不能消除,则保持原来的游戏地图。
●判断胜负
当游戏完成后,需要判断游戏胜负。
不同模式下判断胜负的规则不同。
v基本模式时,如果在五分钟内将游戏地图的所有图片都消除,则提示玩家胜利。
v休闲模式时,如果游戏地图中所有图片都被消除,则提示玩家获胜。
●提示
可以提示界面上能够消除的一对图片。
●计时
设定一定时间来辅助游戏是否结束。
●重排
根据随机数,重新排列地图上的图片。
●游戏模式
游戏模式有:
基本模式、休闲模式和关卡模式三种,可以根据是否定时等规则进行设置。
二、分析与设计
1.数据结构的设计
1)顶点存储
添加global.h文件,定义结构体tagVertex,用于保存游戏地图中一个点的行号、列号、值信息。
typedefstructtagVertex
{
introw;//行
intcol;//列
intinfo;//信息类
}Vertex;
2)游戏地图存储结构
使用二位数组来保存连连看游戏地图,在给没一种图片一个编号,并将这些编号保存在二位数组中。
用户在屏幕上选择两张图片,对应为数组中的两组坐标。
分别实现三个消子判断算法:
一条直线连通、两条直线连通、三条直线连通,并使用者三个算法进项消子判断。
若符合消子规则,就在屏幕上消除一对图片,并把数组对应元素清空。
1 游戏地图中图片种类和重复次数与游戏的级别汉难度有关。
图片种类越多,重复次数越小,游戏难度越大,反之则越容易。
2 只有两张相同的图片才能消除。
为保证游戏中的图片能够完全消掉,每种图片出现的次数一定是偶数,即2的倍数。
3 地图的大小与图片元素种类之间的关系
地图的行数*地图的列数=图片种类数*每种图片重复的次数。
4 地图数据的存储
a.用int类型的动态二位数组(int**m_pGameMap)存储地图中元素图片的编号。
b.获得某行某列对应的元素编号。
2.核心算法设计
●随机开局算法
1)计算游戏中元素个数:
行数*列数。
2)计算每种花色重复数:
行数*列数/花色数。
1 判断(行数*列数%花色数)是否为0。
如果不为0,则进行异常处理。
2 判断每一种花色的重复数能否被2整除,如果不能被二整除,则进行异常处理。
3)按从左到右,从上到下,将花色数填入游戏地图。
实现代码如下:
intnRepeatNum=nRows*nCols/nPicNums;
intcount=0;
for(inti=0;i{for(intj=0;j{m_Map[count/nCols][count%nCols]=i;count++;}}4)由于生成的地图是规则的,因此,需要将地图中的花色打乱。实现思路是:随机选择两个元素,将其值对调,重复若干次。●消子判断的流程1)获得选中的两张图片的行号和列号。2)判断选中的图片是否同色,不同色,则不能相消。判断选中的图片是否为同一个图片,如果为不为同一个图片,则不能相消。3)判断连通性,如以下三种情况均不满足,则结束。1 首先判断能否一条直线连通。2 如果不能一条直线连通,则判断能否两条直线连通。3 如果不能两条直线连通,则判断能否三条直线连通。4)获得连通路径,绘制连线。5)消除图片●一条直线消子算法1 判断两个顶点,行是否相同,若相同,则判断两个顶点在X方向是否连通。在CGameLogic类定义RowLink()函数事项X方向的连通判断。依次判断在X方向两个顶点间每一个顶点,是否都为空,全为空表示可以连通,否则不能连通。实现代码如下:boolCGameLogic::RowLink(intm_Map[10][15],Vertexv1,Vertexv2){//将两个点的列进行调整,使nCol1的值小于nCol2的值introw=v1.row;intcol1=v1.col;intcol2=v2.col;if(col1>col2){inttemp=col1;col1=col2;col2=temp;}//判断两个顶点间是否有不为空的图片for(inti=col1+1;i<=col2;i++){if(i==col2){returntrue;}if(m_Map[row][i]!=BLANK){break;}}returnfalse;2 判断两个顶点,列是否相同,若相同,则判断两个顶点在Y方向是否连通。在CGameLogic类定义ColLink()函数事项Y方向的连通判断。依次判断在Y方向两个顶点间每一个顶点,是否都为空,全为空表示可以连通,否则不能连通。实现代码如下:boolCGameLogic::ColLink(intm_Map[10][15],Vertexv1,Vertexv2){introw1=v1.row;introw2=v2.row;intcol=v1.col;if(row1>row2){inttemp=row1;row1=row2;row2=temp;}for(inti=row1+1;i<=row2;i++){if(i==row2){returntrue;}if(m_Map[i][col]!=BLANK){break;}}returnfalse;}●两条直线消子算法若一条直线无法连通,则判断两条直线的情况。在CGameLogic类中定义OneCornerLink()函数判断两点是否能两条直线连通。先判断两个顶点的X和Y方向的直线相交的两个顶点,是否为空。若能构成两条指向连通,那么相交的顶点必须为空才行。若顶点有一个为空,则判断该顶点与两个顶点,横向与纵向一条直线是否连通,若都连通,则表示两条直线消子成功,否则不能相消。实现代码如下:boolCGameLogic::OneCornerLink(intm_Map[10][15],Vertexv1,Vertexv2){introw1=v1.row;intcol1=v1.col;introw2=v2.row;intcol2=v2.col;//判断相交的顶点是否为空if(m_Map[row1][col2]==BLANK){//判断两个同行的顶点是否一条直线连通if(LineY(m_Map,row1,row2,col2)&&LineX(m_Map,row1,col1,col2)){VertexV={row1,col2,BLANK};AddVertex(V);returntrue;}}if(m_Map[row2][col1]==BLANK){//判断两个同列顶点是否一条直线连通if(LineY(m_Map,row1,row2,col1)&&LineX(m_Map,row2,col1,col2)){VertexV={row2,col1,BLANK};AddVertex(V);returntrue;}}returnfalse;}●三条直线消子算法若两条直线无法连通,则判断三条直线的情况。在CGameLogic类中定义TwoCornerLink()函数判断两点能否三条直线连通。三条直线消子时,假设选择的两张图片的位置为(nRow1,nCol1)和(nRow2,nCol2),则先寻找与Y轴平行的连通线段。如果Y轴没有找到可以连通的三条直线,则寻找以X轴平行的连通线段。1)搜索关键路径假设玩家选择的两个顶点为V0(row0,col0),V3(row3,col3),步骤如下:第一步:从地图的第一行开始扫描,当前扫描到nRow行。第二步:设置拐点:V1(nRow,col0),V2(nRow,col3)。第三步:判断V1和V2是否水平方向向上连通,如果连通,则V1到V2的连线即为关键路径。如果不连通则接着扫描下一行,重复第二~四步。2)判断三条直线连通采用枚举法判断三条直线连通,假设玩家选择两个顶点为V0和V3,判断三条直线连通的具体实现的具体步骤如下:1 找到其中一条关键路径V1,V2。2 判断V1和V0是否连通。3 判断V2和V3是否连通。4 如果同时满足V1和V0连通,V2和V3连通,则V0和V3满足三条直线连通。否则,在此关键路径下V0和V3不连通,找到下一条关键路径,重复2~4,直到判断出V0和V3是否连通。3)保存连通路径使用栈来保存连通路径中的关键点:起始点V0,拐点V1,拐点V2和终点V3。保存连通路径的步骤如下:1 保存其实点V0。2 判断是否存在能够满足三条直线消子的关键路径V1、V2。3 如果存在,保存顶点V1、V2、V3,如果不存在,在删除起始点V0。实现代码如下:boolCGameLogic::TwoCornerLink(intm_Map[10][16],Vertexv1,Vertexv2){introw1=v1.row;intcol1=v1.col;introw2=v2.row;intcol2=v2.col;for(intcol=0;col<16;col++){if(m_Map[row1][col]==BLANK&&m_Map[row2][col]==BLANK){if(LineY(m_Map,row1,row2,col)){if(LineX(m_Map,row1,col1,col)&&LineX(m_Map,row2,col2,col)){VertexV1={row1,col,BLANK};VertexV2={row2,col,BLANK};AddVertex(V1);AddVertex(V2);returntrue;}}}}for(introw=0;row<10;row++){if(m_Map[row][col1]==BLANK&&m_Map[row][col2]==BLANK){if(LineX(m_Map,row,col1,col2)){if(LineY(m_Map,row,row1,col1)&&LineY(m_Map,row,row2,col2)){VertexV1={row,col1,BLANK};VertexV2={row,col2,BLANK};AddVertex(V1);AddVertex(V2);returntrue;}}}}returnfalse;}4)胜负判断算法当所有元素被消掉,进行胜负判断,遍历地图中所有元素的值,当所有元素都为空时,表示获胜,游戏结束,否则继续游戏。实现代码如下:if(m_GameProgress.GetPos()<=0&&!cgc.IsBlank(cgc.m_Map)){KillTimer(PLAY_TIMER_ID);intresult=MessageBox(_T("很遗憾,时间到了!,是否重新开始游戏?"),_T("提示"));if(result=IDOK){GetDlgItem(IDC_BUTTON_START)->EnableWindow(TRUE);}else{exit(0);}IsPlaying=false;}elseif(m_GameProgress.GetPos()>0&&cgc.IsBlank(cgc.m_Map)){KillTimer(PLAY_TIMER_ID);intresult;result=MessageBox(_T("获胜!是否重新开始游戏?"),_T("提示"));if(result=IDOK){GetDlgItem(IDC_BUTTON_START)->EnableWindow(TRUE);}else{exit(0);}}5)重排当进行游戏的过程中会出现无法再进行消子的情况,点击重排按钮就可以将剩下子进行随机重排以便客户能够正常进行消子操作。首先在CGameLogic类中定义一个DisOrderMap()函数来对剩下的元素进行重排,实现代码如下:voidCGameLogic::DisOrderMap(intm_Map[10][16]){intnRows=10;intnCols=16;srand((int)time(NULL));intnVertexNum=nRows*nCols;for(inti=0;i{//随机获得两个坐标intnIndex1=rand()%nVertexNum;intnIndex2=rand()%nVertexNum;intnTemp=m_Map[nIndex1/nCols][nIndex1%nCols];m_Map[nIndex1/nCols][nIndex1%nCols]=m_Map[nIndex2/nCols][nIndex2%nCols];m_Map[nIndex2/nCols][nIndex2%nCols]=nTemp;}}然后在CGameControl类中定义一个DisOrder()函数来调用DisOrderMap()函数,最后再CGameDlg类的OnBnClickedButtonReset()函数中调用DisOrder()函数,实现代码如下:voidCGameDlg::OnBnClickedButtonReset(){//TODO:在此添加控件通知处理程序代码m_dcMem.BitBlt(0,0,800,600,&m_dcBG,0,0,SRCCOPY);InvalidateRect(FALSE);m_gameControl.DisOrder();UpDateMap();}6)帮助在原有的基础上重新插入一个对话框,重新定义一个CHelpDialog类,在这个类中将写有相关游戏说明的图片加载进界面中去,加上滚动条。实现代码如下:BOOLCHelpDialog::OnInitDialog(){CDialogEx::OnInitDialog();ASSERT((IDM_ABOUTBOX&0xFFF0)==IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX<0xF000);HICONm_hIcon;m_hIcon=AfxGetApp()->LoadIcon(IDR_MAINFRAME);//IDR_ICON为图标资源名SetIcon(m_hIcon,TRUE);//SetbigiconSetIcon(m_hIcon,FALSE);//Setsmallicon//加载图片资源HANDLEbmp=::LoadImage(NULL,_T("theme\\picture\\Help1.bmp"),IMAGE_BITMAP,0,0,LR_LOADFROMFILE);//获得当前对话框的视频内存CClientDCdc(this);//创建与视频内存兼容的内存DCm_dcHelp.CreateCompatibleDC(&dc);//将位图资源选入DCm_dcHelp.SelectObject(bmp);//初始化内存DCm_dcMen.CreateCompatibleDC(&dc);CBitmapbmpMem;bmpMem.CreateCompatibleBitmap(&dc,500,400);m_dcMen.SelectObject(&bmpMem);//绘制背景到内存中m_dcMen.BitBlt(0,0,800,600,&m_dcHelp,0,0,SRCCOPY);//绘制帮助信息显示区域this->GetDlgItem(IDC_BUTTON_HELP)->GetWindowRect(&m_rtHelp);this->ScreenToClient(&m_rtHelp);//绘制帮助信息UpdateHelp(0);//表示滚动条的初始位置为0//设置滚动条范围CBitmapbmpHelp;bmpHelp.Attach(bmp);BITMAPbmpInfo;bmpHelp.GetBitmap(&bmpInfo);//设置滚动条范围((CScrollBar*)this->GetDlgItem(IDC_SCROLLBAR))->SetScrollRange(0,bmpInfo,bmpHelp);returnTRUE;//returnTRUEunlessyousetthefocustoacontrol//异常:OCX属性页应返回FALSE//TODO:在此添加额外的初始化}滚动条设置:voidCHelpDialog::OnVScroll(UINTnSBCode,UINTnPos,CScrollBar*pScrollBar){UINTPos;intnMaxPos,nMinPos;//TODO:在此添加消息处理程序代码和/或调用默认值switch(nSBCode){caseSB_LINEUP:Pos-=-1;break;caseSB_LINEDOWN:Pos+=1;break;caseSB_PAGEUP:Pos-=10;break;caseSB_PAGEDOWN:Pos+=10;break;caseSB_TOP:Pos=nMinPos;caseSB_BOTTOM:Pos=nMaxPos;caseSB_THUMBPOSITION://点击在滑块上Pos=nPos;default:break;}pScrollBar->SetScrollPos(Pos,TRUE);//设置滚动条当前点的值UpdateHelp(Pos);CDialogEx::OnVScroll(nSBCode,nPos,pScrollBar);}7)暂停游戏点击暂停游戏按钮,按钮的文字由”暂停游戏“变成”继续游戏“字样,并且用户不能进行游戏,同时进度条停止计时。实现代码如下:voidCGameDlg::OnBnClickedBtnGameStop(){CStringbtnPauseName;CStringgoGame("继续游戏");CStringpauGame("暂停游戏");GetDlgItemText(IDC_BTN_GAME_STOP,btnPauseName);if(btnPauseName==pauGame){//计时器停止计时KillTimer(PLAY_TIMER_ID);SetDlgItemText(IDC_BTN_GAME_STOP,goGame);m_bPlaying=FALSE;m_dcMem.BitBlt(m_rtGameRect.left+40,m_rtGameRect.top+40,m_rtGameRect.Width(),m_rtGameRect.Height(),&m_stopGame,m_rtGameRect.left,m_rtGameRect.top,SRCCOPY);InvalidateRect(&m_rtGameRect,FALSE);}else{//计时器开始计时SetTimer(PLAY_TIMER_ID,1000,NULL);SetDl
for(intj=0;j{m_Map[count/nCols][count%nCols]=i;count++;}}4)由于生成的地图是规则的,因此,需要将地图中的花色打乱。实现思路是:随机选择两个元素,将其值对调,重复若干次。●消子判断的流程1)获得选中的两张图片的行号和列号。2)判断选中的图片是否同色,不同色,则不能相消。判断选中的图片是否为同一个图片,如果为不为同一个图片,则不能相消。3)判断连通性,如以下三种情况均不满足,则结束。1 首先判断能否一条直线连通。2 如果不能一条直线连通,则判断能否两条直线连通。3 如果不能两条直线连通,则判断能否三条直线连通。4)获得连通路径,绘制连线。5)消除图片●一条直线消子算法1 判断两个顶点,行是否相同,若相同,则判断两个顶点在X方向是否连通。在CGameLogic类定义RowLink()函数事项X方向的连通判断。依次判断在X方向两个顶点间每一个顶点,是否都为空,全为空表示可以连通,否则不能连通。实现代码如下:boolCGameLogic::RowLink(intm_Map[10][15],Vertexv1,Vertexv2){//将两个点的列进行调整,使nCol1的值小于nCol2的值introw=v1.row;intcol1=v1.col;intcol2=v2.col;if(col1>col2){inttemp=col1;col1=col2;col2=temp;}//判断两个顶点间是否有不为空的图片for(inti=col1+1;i<=col2;i++){if(i==col2){returntrue;}if(m_Map[row][i]!=BLANK){break;}}returnfalse;2 判断两个顶点,列是否相同,若相同,则判断两个顶点在Y方向是否连通。在CGameLogic类定义ColLink()函数事项Y方向的连通判断。依次判断在Y方向两个顶点间每一个顶点,是否都为空,全为空表示可以连通,否则不能连通。实现代码如下:boolCGameLogic::ColLink(intm_Map[10][15],Vertexv1,Vertexv2){introw1=v1.row;introw2=v2.row;intcol=v1.col;if(row1>row2){inttemp=row1;row1=row2;row2=temp;}for(inti=row1+1;i<=row2;i++){if(i==row2){returntrue;}if(m_Map[i][col]!=BLANK){break;}}returnfalse;}●两条直线消子算法若一条直线无法连通,则判断两条直线的情况。在CGameLogic类中定义OneCornerLink()函数判断两点是否能两条直线连通。先判断两个顶点的X和Y方向的直线相交的两个顶点,是否为空。若能构成两条指向连通,那么相交的顶点必须为空才行。若顶点有一个为空,则判断该顶点与两个顶点,横向与纵向一条直线是否连通,若都连通,则表示两条直线消子成功,否则不能相消。实现代码如下:boolCGameLogic::OneCornerLink(intm_Map[10][15],Vertexv1,Vertexv2){introw1=v1.row;intcol1=v1.col;introw2=v2.row;intcol2=v2.col;//判断相交的顶点是否为空if(m_Map[row1][col2]==BLANK){//判断两个同行的顶点是否一条直线连通if(LineY(m_Map,row1,row2,col2)&&LineX(m_Map,row1,col1,col2)){VertexV={row1,col2,BLANK};AddVertex(V);returntrue;}}if(m_Map[row2][col1]==BLANK){//判断两个同列顶点是否一条直线连通if(LineY(m_Map,row1,row2,col1)&&LineX(m_Map,row2,col1,col2)){VertexV={row2,col1,BLANK};AddVertex(V);returntrue;}}returnfalse;}●三条直线消子算法若两条直线无法连通,则判断三条直线的情况。在CGameLogic类中定义TwoCornerLink()函数判断两点能否三条直线连通。三条直线消子时,假设选择的两张图片的位置为(nRow1,nCol1)和(nRow2,nCol2),则先寻找与Y轴平行的连通线段。如果Y轴没有找到可以连通的三条直线,则寻找以X轴平行的连通线段。1)搜索关键路径假设玩家选择的两个顶点为V0(row0,col0),V3(row3,col3),步骤如下:第一步:从地图的第一行开始扫描,当前扫描到nRow行。第二步:设置拐点:V1(nRow,col0),V2(nRow,col3)。第三步:判断V1和V2是否水平方向向上连通,如果连通,则V1到V2的连线即为关键路径。如果不连通则接着扫描下一行,重复第二~四步。2)判断三条直线连通采用枚举法判断三条直线连通,假设玩家选择两个顶点为V0和V3,判断三条直线连通的具体实现的具体步骤如下:1 找到其中一条关键路径V1,V2。2 判断V1和V0是否连通。3 判断V2和V3是否连通。4 如果同时满足V1和V0连通,V2和V3连通,则V0和V3满足三条直线连通。否则,在此关键路径下V0和V3不连通,找到下一条关键路径,重复2~4,直到判断出V0和V3是否连通。3)保存连通路径使用栈来保存连通路径中的关键点:起始点V0,拐点V1,拐点V2和终点V3。保存连通路径的步骤如下:1 保存其实点V0。2 判断是否存在能够满足三条直线消子的关键路径V1、V2。3 如果存在,保存顶点V1、V2、V3,如果不存在,在删除起始点V0。实现代码如下:boolCGameLogic::TwoCornerLink(intm_Map[10][16],Vertexv1,Vertexv2){introw1=v1.row;intcol1=v1.col;introw2=v2.row;intcol2=v2.col;for(intcol=0;col<16;col++){if(m_Map[row1][col]==BLANK&&m_Map[row2][col]==BLANK){if(LineY(m_Map,row1,row2,col)){if(LineX(m_Map,row1,col1,col)&&LineX(m_Map,row2,col2,col)){VertexV1={row1,col,BLANK};VertexV2={row2,col,BLANK};AddVertex(V1);AddVertex(V2);returntrue;}}}}for(introw=0;row<10;row++){if(m_Map[row][col1]==BLANK&&m_Map[row][col2]==BLANK){if(LineX(m_Map,row,col1,col2)){if(LineY(m_Map,row,row1,col1)&&LineY(m_Map,row,row2,col2)){VertexV1={row,col1,BLANK};VertexV2={row,col2,BLANK};AddVertex(V1);AddVertex(V2);returntrue;}}}}returnfalse;}4)胜负判断算法当所有元素被消掉,进行胜负判断,遍历地图中所有元素的值,当所有元素都为空时,表示获胜,游戏结束,否则继续游戏。实现代码如下:if(m_GameProgress.GetPos()<=0&&!cgc.IsBlank(cgc.m_Map)){KillTimer(PLAY_TIMER_ID);intresult=MessageBox(_T("很遗憾,时间到了!,是否重新开始游戏?"),_T("提示"));if(result=IDOK){GetDlgItem(IDC_BUTTON_START)->EnableWindow(TRUE);}else{exit(0);}IsPlaying=false;}elseif(m_GameProgress.GetPos()>0&&cgc.IsBlank(cgc.m_Map)){KillTimer(PLAY_TIMER_ID);intresult;result=MessageBox(_T("获胜!是否重新开始游戏?"),_T("提示"));if(result=IDOK){GetDlgItem(IDC_BUTTON_START)->EnableWindow(TRUE);}else{exit(0);}}5)重排当进行游戏的过程中会出现无法再进行消子的情况,点击重排按钮就可以将剩下子进行随机重排以便客户能够正常进行消子操作。首先在CGameLogic类中定义一个DisOrderMap()函数来对剩下的元素进行重排,实现代码如下:voidCGameLogic::DisOrderMap(intm_Map[10][16]){intnRows=10;intnCols=16;srand((int)time(NULL));intnVertexNum=nRows*nCols;for(inti=0;i{//随机获得两个坐标intnIndex1=rand()%nVertexNum;intnIndex2=rand()%nVertexNum;intnTemp=m_Map[nIndex1/nCols][nIndex1%nCols];m_Map[nIndex1/nCols][nIndex1%nCols]=m_Map[nIndex2/nCols][nIndex2%nCols];m_Map[nIndex2/nCols][nIndex2%nCols]=nTemp;}}然后在CGameControl类中定义一个DisOrder()函数来调用DisOrderMap()函数,最后再CGameDlg类的OnBnClickedButtonReset()函数中调用DisOrder()函数,实现代码如下:voidCGameDlg::OnBnClickedButtonReset(){//TODO:在此添加控件通知处理程序代码m_dcMem.BitBlt(0,0,800,600,&m_dcBG,0,0,SRCCOPY);InvalidateRect(FALSE);m_gameControl.DisOrder();UpDateMap();}6)帮助在原有的基础上重新插入一个对话框,重新定义一个CHelpDialog类,在这个类中将写有相关游戏说明的图片加载进界面中去,加上滚动条。实现代码如下:BOOLCHelpDialog::OnInitDialog(){CDialogEx::OnInitDialog();ASSERT((IDM_ABOUTBOX&0xFFF0)==IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX<0xF000);HICONm_hIcon;m_hIcon=AfxGetApp()->LoadIcon(IDR_MAINFRAME);//IDR_ICON为图标资源名SetIcon(m_hIcon,TRUE);//SetbigiconSetIcon(m_hIcon,FALSE);//Setsmallicon//加载图片资源HANDLEbmp=::LoadImage(NULL,_T("theme\\picture\\Help1.bmp"),IMAGE_BITMAP,0,0,LR_LOADFROMFILE);//获得当前对话框的视频内存CClientDCdc(this);//创建与视频内存兼容的内存DCm_dcHelp.CreateCompatibleDC(&dc);//将位图资源选入DCm_dcHelp.SelectObject(bmp);//初始化内存DCm_dcMen.CreateCompatibleDC(&dc);CBitmapbmpMem;bmpMem.CreateCompatibleBitmap(&dc,500,400);m_dcMen.SelectObject(&bmpMem);//绘制背景到内存中m_dcMen.BitBlt(0,0,800,600,&m_dcHelp,0,0,SRCCOPY);//绘制帮助信息显示区域this->GetDlgItem(IDC_BUTTON_HELP)->GetWindowRect(&m_rtHelp);this->ScreenToClient(&m_rtHelp);//绘制帮助信息UpdateHelp(0);//表示滚动条的初始位置为0//设置滚动条范围CBitmapbmpHelp;bmpHelp.Attach(bmp);BITMAPbmpInfo;bmpHelp.GetBitmap(&bmpInfo);//设置滚动条范围((CScrollBar*)this->GetDlgItem(IDC_SCROLLBAR))->SetScrollRange(0,bmpInfo,bmpHelp);returnTRUE;//returnTRUEunlessyousetthefocustoacontrol//异常:OCX属性页应返回FALSE//TODO:在此添加额外的初始化}滚动条设置:voidCHelpDialog::OnVScroll(UINTnSBCode,UINTnPos,CScrollBar*pScrollBar){UINTPos;intnMaxPos,nMinPos;//TODO:在此添加消息处理程序代码和/或调用默认值switch(nSBCode){caseSB_LINEUP:Pos-=-1;break;caseSB_LINEDOWN:Pos+=1;break;caseSB_PAGEUP:Pos-=10;break;caseSB_PAGEDOWN:Pos+=10;break;caseSB_TOP:Pos=nMinPos;caseSB_BOTTOM:Pos=nMaxPos;caseSB_THUMBPOSITION://点击在滑块上Pos=nPos;default:break;}pScrollBar->SetScrollPos(Pos,TRUE);//设置滚动条当前点的值UpdateHelp(Pos);CDialogEx::OnVScroll(nSBCode,nPos,pScrollBar);}7)暂停游戏点击暂停游戏按钮,按钮的文字由”暂停游戏“变成”继续游戏“字样,并且用户不能进行游戏,同时进度条停止计时。实现代码如下:voidCGameDlg::OnBnClickedBtnGameStop(){CStringbtnPauseName;CStringgoGame("继续游戏");CStringpauGame("暂停游戏");GetDlgItemText(IDC_BTN_GAME_STOP,btnPauseName);if(btnPauseName==pauGame){//计时器停止计时KillTimer(PLAY_TIMER_ID);SetDlgItemText(IDC_BTN_GAME_STOP,goGame);m_bPlaying=FALSE;m_dcMem.BitBlt(m_rtGameRect.left+40,m_rtGameRect.top+40,m_rtGameRect.Width(),m_rtGameRect.Height(),&m_stopGame,m_rtGameRect.left,m_rtGameRect.top,SRCCOPY);InvalidateRect(&m_rtGameRect,FALSE);}else{//计时器开始计时SetTimer(PLAY_TIMER_ID,1000,NULL);SetDl
m_Map[count/nCols][count%nCols]=i;
count++;
}
4)由于生成的地图是规则的,因此,需要将地图中的花色打乱。
实现思路是:
随机选择两个元素,将其值对调,重复若干次。
●消子判断的流程
1)获得选中的两张图片的行号和列号。
2)判断选中的图片是否同色,不同色,则不能相消。
判断选中的图片是否为同一个图片,如果为不为同一个图片,则不能相消。
3)判断连通性,如以下三种情况均不满足,则结束。
1 首先判断能否一条直线连通。
2 如果不能一条直线连通,则判断能否两条直线连通。
3 如果不能两条直线连通,则判断能否三条直线连通。
4)获得连通路径,绘制连线。
5)消除图片
●一条直线消子算法
1 判断两个顶点,行是否相同,若相同,则判断两个顶点在X方向是否连通。
在CGameLogic类定义RowLink()函数事项X方向的连通判断。
依次判断在X方向两个顶点间每一个顶点,是否都为空,全为空表示可以连通,否则不能连通。
boolCGameLogic:
:
RowLink(intm_Map[10][15],Vertexv1,Vertexv2)
//将两个点的列进行调整,使nCol1的值小于nCol2的值
introw=v1.row;
intcol1=v1.col;
intcol2=v2.col;
if(col1>col2)
inttemp=col1;
col1=col2;
col2=temp;
//判断两个顶点间是否有不为空的图片
for(inti=col1+1;i<=col2;i++)
if(i==col2)
returntrue;
if(m_Map[row][i]!
=BLANK)
break;
returnfalse;
2 判断两个顶点,列是否相同,若相同,则判断两个顶点在Y方向是否连通。
在CGameLogic类定义ColLink()函数事项Y方向的连通判断。
依次判断在Y方向两个顶点间每一个顶点,是否都为空,全为空表示可以连通,否则不能连通。
ColLink(intm_Map[10][15],Vertexv1,Vertexv2)
introw1=v1.row;
introw2=v2.row;
intcol=v1.col;
if(row1>row2)
inttemp=row1;
row1=row2;
row2=temp;
for(inti=row1+1;i<=row2;i++)
if(i==row2)
if(m_Map[i][col]!
●两条直线消子算法
若一条直线无法连通,则判断两条直线的情况。
在CGameLogic类中定义OneCornerLink()函数判断两点是否能两条直线连通。
先判断两个顶点的X和Y方向的直线相交的两个顶点,是否为空。
若能构成两条指向连通,那么相交的顶点必须为空才行。
若顶点有一个为空,则判断该顶点与两个顶点,横向与纵向一条直线是否连通,若都连通,则表示两条直线消子成功,否则不能相消。
OneCornerLink(intm_Map[10][15],Vertexv1,Vertexv2)
//判断相交的顶点是否为空
if(m_Map[row1][col2]==BLANK)
//判断两个同行的顶点是否一条直线连通
if(LineY(m_Map,row1,row2,col2)&&LineX(m_Map,row1,col1,col2))
VertexV={row1,col2,BLANK};
AddVertex(V);
if(m_Map[row2][col1]==BLANK)
//判断两个同列顶点是否一条直线连通
if(LineY(m_Map,row1,row2,col1)&&LineX(m_Map,row2,col1,col2))
VertexV={row2,col1,BLANK};
returnfalse;}
●三条直线消子算法
若两条直线无法连通,则判断三条直线的情况。
在CGameLogic类中定义TwoCornerLink()函数判断两点能否三条直线连通。
三条直线消子时,假设选择的两张图片的位置为(nRow1,nCol1)和(nRow2,nCol2),则先寻找与Y轴平行的连通线段。
如果Y轴没有找到可以连通的三条直线,则寻找以X轴平行的连通线段。
1)搜索关键路径
假设玩家选择的两个顶点为V0(row0,col0),V3(row3,col3),步骤如下:
第一步:
从地图的第一行开始扫描,当前扫描到nRow行。
第二步:
设置拐点:
V1(nRow,col0),V2(nRow,col3)。
第三步:
判断V1和V2是否水平方向向上连通,如果连通,则V1到V2的连线即为关键路径。
如果不连通则接着扫描下一行,重复第二~四步。
2)判断三条直线连通
采用枚举法判断三条直线连通,假设玩家选择两个顶点为V0和V3,判断三条直线连通的具体实现的具体步骤如下:
1 找到其中一条关键路径V1,V2。
2 判断V1和V0是否连通。
3 判断V2和V3是否连通。
4 如果同时满足V1和V0连通,V2和V3连通,则V0和V3满足三条直线连通。
否则,在此关键路径下V0和V3不连通,找到下一条关键路径,重复2~4,直到判断出V0和V3是否连通。
3)保存连通路径
使用栈来保存连通路径中的关键点:
起始点V0,拐点V1,拐点V2和终点V3。
保存连通路径的步骤如下:
1 保存其实点V0。
2 判断是否存在能够满足三条直线消子的关键路径V1、V2。
3 如果存在,保存顶点V1、V2、V3,如果不存在,在删除起始点V0。
TwoCornerLink(intm_Map[10][16],Vertexv1,Vertexv2)
for(intcol=0;col<16;col++)
if(m_Map[row1][col]==BLANK&&m_Map[row2][col]==BLANK)
if(LineY(m_Map,row1,row2,col))
if(LineX(m_Map,row1,col1,col)&&LineX(m_Map,row2,col2,col))
VertexV1={row1,col,BLANK};
VertexV2={row2,col,BLANK};
AddVertex(V1);
AddVertex(V2);
for(introw=0;row<10;row++)
if(m_Map[row][col1]==BLANK&&m_Map[row][col2]==BLANK)
if(LineX(m_Map,row,col1,col2))
if(LineY(m_Map,row,row1,col1)&&LineY(m_Map,row,row2,col2))
VertexV1={row,col1,BLANK};
VertexV2={row,col2,BLANK};
4)胜负判断算法
当所有元素被消掉,进行胜负判断,遍历地图中所有元素的值,当所有元素都为空时,表示获胜,游戏结束,否则继续游戏。
if(m_GameProgress.GetPos()<=0&&!
cgc.IsBlank(cgc.m_Map)){
KillTimer(PLAY_TIMER_ID);
intresult=MessageBox(_T("很遗憾,时间到了!
,是否重新开始游戏?
"),_T("提示"));
if(result=IDOK){
GetDlgItem(IDC_BUTTON_START)->EnableWindow(TRUE);
else{
exit(0);
IsPlaying=false;
elseif(m_GameProgress.GetPos()>0&&cgc.IsBlank(cgc.m_Map))
intresult;
result=MessageBox(_T("获胜!
是否重新开始游戏?
5)重排
当进行游戏的过程中会出现无法再进行消子的情况,点击重排按钮就可以将剩下子进行随机重排以便客户能够正常进行消子操作。
首先在CGameLogic类中定义一个DisOrderMap()函数来对剩下的元素进行重排,实现代码如下:
voidCGameLogic:
DisOrderMap(intm_Map[10][16])
intnRows=10;
intnCols=16;
srand((int)time(NULL));
intnVertexNum=nRows*nCols;
for(inti=0;i{//随机获得两个坐标intnIndex1=rand()%nVertexNum;intnIndex2=rand()%nVertexNum;intnTemp=m_Map[nIndex1/nCols][nIndex1%nCols];m_Map[nIndex1/nCols][nIndex1%nCols]=m_Map[nIndex2/nCols][nIndex2%nCols];m_Map[nIndex2/nCols][nIndex2%nCols]=nTemp;}}然后在CGameControl类中定义一个DisOrder()函数来调用DisOrderMap()函数,最后再CGameDlg类的OnBnClickedButtonReset()函数中调用DisOrder()函数,实现代码如下:voidCGameDlg::OnBnClickedButtonReset(){//TODO:在此添加控件通知处理程序代码m_dcMem.BitBlt(0,0,800,600,&m_dcBG,0,0,SRCCOPY);InvalidateRect(FALSE);m_gameControl.DisOrder();UpDateMap();}6)帮助在原有的基础上重新插入一个对话框,重新定义一个CHelpDialog类,在这个类中将写有相关游戏说明的图片加载进界面中去,加上滚动条。实现代码如下:BOOLCHelpDialog::OnInitDialog(){CDialogEx::OnInitDialog();ASSERT((IDM_ABOUTBOX&0xFFF0)==IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX<0xF000);HICONm_hIcon;m_hIcon=AfxGetApp()->LoadIcon(IDR_MAINFRAME);//IDR_ICON为图标资源名SetIcon(m_hIcon,TRUE);//SetbigiconSetIcon(m_hIcon,FALSE);//Setsmallicon//加载图片资源HANDLEbmp=::LoadImage(NULL,_T("theme\\picture\\Help1.bmp"),IMAGE_BITMAP,0,0,LR_LOADFROMFILE);//获得当前对话框的视频内存CClientDCdc(this);//创建与视频内存兼容的内存DCm_dcHelp.CreateCompatibleDC(&dc);//将位图资源选入DCm_dcHelp.SelectObject(bmp);//初始化内存DCm_dcMen.CreateCompatibleDC(&dc);CBitmapbmpMem;bmpMem.CreateCompatibleBitmap(&dc,500,400);m_dcMen.SelectObject(&bmpMem);//绘制背景到内存中m_dcMen.BitBlt(0,0,800,600,&m_dcHelp,0,0,SRCCOPY);//绘制帮助信息显示区域this->GetDlgItem(IDC_BUTTON_HELP)->GetWindowRect(&m_rtHelp);this->ScreenToClient(&m_rtHelp);//绘制帮助信息UpdateHelp(0);//表示滚动条的初始位置为0//设置滚动条范围CBitmapbmpHelp;bmpHelp.Attach(bmp);BITMAPbmpInfo;bmpHelp.GetBitmap(&bmpInfo);//设置滚动条范围((CScrollBar*)this->GetDlgItem(IDC_SCROLLBAR))->SetScrollRange(0,bmpInfo,bmpHelp);returnTRUE;//returnTRUEunlessyousetthefocustoacontrol//异常:OCX属性页应返回FALSE//TODO:在此添加额外的初始化}滚动条设置:voidCHelpDialog::OnVScroll(UINTnSBCode,UINTnPos,CScrollBar*pScrollBar){UINTPos;intnMaxPos,nMinPos;//TODO:在此添加消息处理程序代码和/或调用默认值switch(nSBCode){caseSB_LINEUP:Pos-=-1;break;caseSB_LINEDOWN:Pos+=1;break;caseSB_PAGEUP:Pos-=10;break;caseSB_PAGEDOWN:Pos+=10;break;caseSB_TOP:Pos=nMinPos;caseSB_BOTTOM:Pos=nMaxPos;caseSB_THUMBPOSITION://点击在滑块上Pos=nPos;default:break;}pScrollBar->SetScrollPos(Pos,TRUE);//设置滚动条当前点的值UpdateHelp(Pos);CDialogEx::OnVScroll(nSBCode,nPos,pScrollBar);}7)暂停游戏点击暂停游戏按钮,按钮的文字由”暂停游戏“变成”继续游戏“字样,并且用户不能进行游戏,同时进度条停止计时。实现代码如下:voidCGameDlg::OnBnClickedBtnGameStop(){CStringbtnPauseName;CStringgoGame("继续游戏");CStringpauGame("暂停游戏");GetDlgItemText(IDC_BTN_GAME_STOP,btnPauseName);if(btnPauseName==pauGame){//计时器停止计时KillTimer(PLAY_TIMER_ID);SetDlgItemText(IDC_BTN_GAME_STOP,goGame);m_bPlaying=FALSE;m_dcMem.BitBlt(m_rtGameRect.left+40,m_rtGameRect.top+40,m_rtGameRect.Width(),m_rtGameRect.Height(),&m_stopGame,m_rtGameRect.left,m_rtGameRect.top,SRCCOPY);InvalidateRect(&m_rtGameRect,FALSE);}else{//计时器开始计时SetTimer(PLAY_TIMER_ID,1000,NULL);SetDl
//随机获得两个坐标
intnIndex1=rand()%nVertexNum;
intnIndex2=rand()%nVertexNum;
intnTemp=m_Map[nIndex1/nCols][nIndex1%nCols];
m_Map[nIndex1/nCols][nIndex1%nCols]=m_Map[nIndex2/nCols][nIndex2%nCols];
m_Map[nIndex2/nCols][nIndex2%nCols]=nTemp;
然后在CGameControl类中定义一个DisOrder()函数来调用DisOrderMap()函数,
最后再CGameDlg类的OnBnClickedButtonReset()函数中调用DisOrder()函数,实现代码如下:
voidCGameDlg:
OnBnClickedButtonReset()
//TODO:
在此添加控件通知处理程序代码
m_dcMem.BitBlt(0,0,800,600,&m_dcBG,0,0,SRCCOPY);
InvalidateRect(FALSE);
m_gameControl.DisOrder();
UpDateMap();
6)帮助
在原有的基础上重新插入一个对话框,重新定义一个CHelpDialog类,在这个类中将写有相关游戏说明的图片加载进界面中去,加上滚动条。
BOOLCHelpDialog:
OnInitDialog()
CDialogEx:
OnInitDialog();
ASSERT((IDM_ABOUTBOX&0xFFF0)==IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX<0xF000);
HICONm_hIcon;
m_hIcon=AfxGetApp()->LoadIcon(IDR_MAINFRAME);//IDR_ICON为图标资源名
SetIcon(m_hIcon,TRUE);//Setbigicon
SetIcon(m_hIcon,FALSE);//Setsmallicon
//加载图片资源
HANDLEbmp=:
LoadImage(NULL,_T("theme\\picture\\Help1.bmp"),IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
//获得当前对话框的视频内存
CClientDCdc(this);
//创建与视频内存兼容的内存DC
m_dcHelp.CreateCompatibleDC(&dc);
//将位图资源选入DC
m_dcHelp.SelectObject(bmp);
//初始化内存DC
m_dcMen.CreateCompatibleDC(&dc);
CBitmapbmpMem;
bmpMem.CreateCompatibleBitmap(&dc,500,400);
m_dcMen.SelectObject(&bmpMem);
//绘制背景到内存中
m_dcMen.BitBlt(0,0,800,600,&m_dcHelp,0,0,SRCCOPY);
//绘制帮助信息显示区域
this->GetDlgItem(IDC_BUTTON_HELP)->GetWindowRect(&m_rtHelp);
this->ScreenToClient(&m_rtHelp);
//绘制帮助信息
UpdateHelp(0);//表示滚动条的初始位置为0
//设置滚动条范围
CBitmapbmpHelp;
bmpHelp.Attach(bmp);
BITMAPbmpInfo;
bmpHelp.GetBitmap(&bmpInfo);
((CScrollBar*)this->GetDlgItem(IDC_SCROLLBAR))->SetScrollRange(0,bmpInfo,bmpHelp);
returnTRUE;//returnTRUEunlessyousetthefocustoacontrol
//异常:
OCX属性页应返回FALSE
在此添加额外的初始化
滚动条设置:
voidCHelpDialog:
OnVScroll(UINTnSBCode,UINTnPos,CScrollBar*pScrollBar)
UINTPos;
intnMaxPos,nMinPos;
在此添加消息处理程序代码和/或调用默认值
switch(nSBCode)
caseSB_LINEUP:
Pos-=-1;
caseSB_LINEDOWN:
Pos+=1;
caseSB_PAGEUP:
Pos-=10;
caseSB_PAGEDOWN:
Pos+=10;
caseSB_TOP:
Pos=nMinPos;
caseSB_BOTTOM:
Pos=nMaxPos;
caseSB_THUMBPOSITION:
//点击在滑块上
Pos=nPos;
default:
pScrollBar->SetScrollPos(Pos,TRUE);//设置滚动条当前点的值
UpdateHelp(Pos);
OnVScroll(nSBCode,nPos,pScrollBar);
7)暂停游戏
点击暂停游戏按钮,按钮的文字由”暂停游戏“变成”继续游戏“字样,并且用户不能进行游戏,同时进度条停止计时。
OnBnClickedBtnGameStop()
CStringbtnPauseName;
CStringgoGame("继续游戏");
CStringpauGame("暂停游戏");
GetDlgItemText(IDC_BTN_GAME_STOP,btnPauseName);
if(btnPauseName==pauGame)
//计时器停止计时
SetDlgItemText(IDC_BTN_GAME_STOP,goGame);
m_bPlaying=FALSE;
m_dcMem.BitBlt(m_rtGameRect.left+40,m_rtGameRect.top+40,m_rtGameRect.Width(),m_rtGameRect.Height(),&m_stopGame,m_rtGameRect.left,m_rtGameRect.top,SRCCOPY);
InvalidateRect(&m_rtGameRect,FALSE);
else
//计时器开始计时
SetTimer(PLAY_TIMER_ID,1000,NULL);
SetDl
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1