扫雷游戏实验报告1.docx
- 文档编号:3448951
- 上传时间:2022-11-23
- 格式:DOCX
- 页数:23
- 大小:24.96KB
扫雷游戏实验报告1.docx
《扫雷游戏实验报告1.docx》由会员分享,可在线阅读,更多相关《扫雷游戏实验报告1.docx(23页珍藏版)》请在冰豆网上搜索。
扫雷游戏实验报告1
简易版扫雷游戏实验报告
一、问题描述
模仿windows扫雷游戏,开发一个简易版扫雷游戏系统。
二、功能分析
总体游戏过程分析:
在屏幕中央位置显示一个雷区,雷区范围内预先埋设了一定数量的随机分布的地雷;玩家通过上下左右键控制光标在雷区的各个小方块之间移动并做标记;若能正确标记出雷区中的所有地雷,则游戏胜利;否则踩雷,游戏失败。
简易版扫雷游戏要具备以下功能:
1)开局:
首先选择游戏等级,然后生成不同等级的雷区界面。
游戏等级分为三级:
各等级方块数为——初级:
8×8、中级:
16×16、高级:
24×24;各级地雷数=总方块数/6;雷区每个方块下面或埋藏有1个地雷,或者没有地雷;
2)挖雷:
将光标移到某个方块,并按空格或回车键,可挖开它;若所揭方块下有雷,则踩雷,此时所有含地雷的块都标记,该局游戏失败;如果方块上出现数字,它代表在它周围的8个方块中共有多少颗地雷;
3)标记地雷:
在光标所在的某个方块上按F或f键,则标记此块下埋着地雷(实际上可能是误标),显示为F。
每标记一个地雷,地雷数减少1;
4)标记疑问:
在光标所在的某个方块上按Q或q键,则在某方块上面标一个问号(?
),意味着没有把握判定它是否有雷。
标记为?
的块可在恰当的时候再按标记地雷或挖开;
5)自动挖开:
如果某个数字方块周围的地雷全都标记完,则在该方块上按A或a键,将其剩下的方块挖开;
6)输入:
通过键盘输入相关指示信息;
7)输出:
提示游戏是否成功,是否继续。
三、程序设计
扫雷游戏系统的顶层层次图如下:
从以下几方面“自顶向下”进一步逐步求精:
♦继续分析已有功能,直到精化出所有子功能,确定模块间接口;
♦描述精化后每个模块的处理过程;
♦确定主要的数据及其数据结构;
♦确定输入输出数据的内外部形式;
♦界面的设计
以下是详细设计:
(1)确定游戏的界面。
游戏屏幕中央显示由小方块组成的雷区,小方块的背景显示为浅灰,表示该方块没有被挖开或标记;小方块的背景显示为深灰,表示它已经被挖开或标记为“F”、“?
”或“*”;当选中一个方块要操作时,它的边框线为红色。
在屏幕的左上方显示游戏是否成功等信息,这也是游戏结果的表现形式。
如图2所示。
(2)决定游戏的输入方式。
采用键盘,通过光标的移动来选择操作的小方块,根据所敲击的键值来选择游戏的功能。
下面是对键盘功能键的定义:
♦上,下,左,右键用来移动光标的位置;
♦回车或者空格键用来挖开光标当前指向的一个方块;
♦F,f标记当前光标指向的方块有地雷;
♦Q,q在光标指向方块打一个问号,表示可能有地雷;
♦A,a自动挖开光标周围的方块;
♦ESC退出游戏。
(3)确定主要的数据,这里主要是有关雷区的数据。
♦雷区界面数据
intMAXCOL=640;/*屏幕最大宽度*/
intMAXROW=480;/*屏幕最大高度*/
#define_ROW24/*雷区最多方块行数*/
#define_COL24/*雷区最多方块列数*/
intROW=8;/*当前游戏雷区行数*/
intCOL=8;/*当前游戏雷区列数*/
#defineSIZEX20/*雷区方块的水平大小(像素数)*/
#defineSIZEY20/*雷区方块的垂直大小(像素数)*/
#defineSTARTX(MAXCOL-COL*SIZEX)/2/*水平起始位置*/
#defineSTARTY(MAXROW-ROW*SIZEY)/2/*垂直起始位置*/
♦雷区内部数据
inttotalMine;/*整个雷区所含的地雷总数*/
inttable[_ROW][_COL];/*数组table的每个元素值记录了雷区对应方块是否有雷:
1表示有雷,0表示没有雷*/
intnum[_ROW][_COL];
/*数组num的每个元素值记录了雷区对应方块周围有多少个地雷*/
intflag[_ROW][_COL];
/*数组flag的每个元素值记录了雷区对应方块当前的状态*/
在整个系统中table[_ROW][_COL]、num[_ROW][_COL]、flag[_ROW][_COL]是最核心的数据,是雷区的内部表示,游戏的挖雷、标记雷、标记疑问、自动挖开等界面操作,在内部实际上是对这些数据的操作。
♦方块(i,j)的状态取值:
#defineUNFLAG0/*表示该方块还没有被打开或者标记*/
#defineFLAGED1/*标记该方块有地雷*/
#defineQUESTION20/*表示该方块可能有地雷*/
#defineEXPLOD30/*踩到地雷爆炸了*/
#defineOPEN40/*一个没有地雷的方块被打开*/
♦光标当前位置数据
intpi,pj;/*记录光标的当前位置,初始时光标在(0,0)上*/
intdi[8]={1,0,-1};/*x方向偏移量*/
intdj[8]={,-1,-1};/*y方向偏移量*/
♦游戏状态数据
intgameRes;
/*记录游戏结束的结果状态,值为0表示按esc键退出游戏;-1表示游戏失败;gameRes=1表示游戏胜利。
实际为输出数据的内部形式*/
♦功能键的键值
/*为避免书写错误和明确含义,对系统的按键值用符号常量来定义*/
/*上,下,左,右键*/
#defineUP0x4800
#defineDOWN0x5000
#defineLEFT0x4b00
#defineRIGHT0x4d00
/*回车、空格键*/
#defineENTER0x1c0d
#defineSPACE0x3920
/*F,f*/
#defineUPPERF0x2146
#defineLOWERF0x2166
/*Q,q*/
#defineUPPERQ0x1051
#defineLOWERQ0x1071
/*A,a*/
#defineUPPERA0x1e41
#defineLOWERA0x1e61
/*ESC*/
#defineESC0x011b
(4)功能模块进一步求精
在C系统中屏幕的输出默认的方式是文本方式,所以需要首先将显示方式设置为图形方式。
简易版扫雷程序主控模块的流程如图3所示,下面从主控模块入手,继续利用逐步求精的方法来设计系统。
第一步,写出主程序的基本框架,确定各子模块名字和参数。
intmain()
{
initGraph();/*图形显示方式初始化*/
do{
newGame();/*初始化新游戏,即开局*/
intgameRes=0;
do{
intkey=getKey();/*读入操作信息*/
if(key==ESC)
{gameRes=0;break;}
switch(key){
对其他key值进行判断处理;
}
if(checkWin())/*判断游戏是否胜利*/
gameRes=1;
}while(!
gameRes);
}while(!
confirm(gameRes));
return0;
}
主控模块的各子模块说明:
ØvoidinitGraph()函数:
用于将显示器显示方式设置为图形方式。
ØvoidnewGame()函数:
开局—初始化新游戏界面及数据。
ØintgetKey()函数:
得到从键盘读入的操作信息。
ØintcheckWin()函数:
用于判断游戏是否获胜。
若返回值为1表示胜利,返回值为0表示游戏还没结束。
Øintconfirm(int)函数:
用于判断游戏是否重新开始。
参数的值来自gameRes,gameRes=0,表示按esc键退出游戏;gameRes=-1,表示踩到地雷,游戏失败;gameRes=1,表示游戏胜利。
Confirm的返回值为0表示重新开始游戏;为1表示关闭游戏,退出程序。
第二步,对“图形初始化”功能模块进一步求精。
voidinitGraph()
{
intgdriver=DETECT,gmode,errorcode;/*自动检测最高图形模式*/
initgraph(&gdriver,&gmode,"");/*初始化图形模式*/
errorcode=graphresult();/*读取初始化结果*/
if(errorcode!
=grOk){/*如果有错误发生*/
printf("Graphicserror:
%s\n",grapherrormsg(errorcode));
printf("Pressanykeytohalt:
");
getch();
exit
(1);/*返回错误码*/
}
MAXCOL=getmaxx();/*获取当前屏幕最大宽度*/
MAXROW=getmaxy();/*获取当前屏幕最大高度*/
}
第三步,对“初始化新游戏”功能模块进一步求精。
voidnewGame()
{
cleardevice();/*清除屏幕*/
setGrade();/*设置游戏等级*/
generateMine();/*随机生成雷区地雷分布*/
pi=pj=0;/*设置初始光标位置*/
drawTable();/*实现在屏幕上画出雷区*/
}
SetGrade()的继续细化:
voidsetGrade()
功能:
设置游戏等级。
参数:
无
返回值:
无
算法描述:
1.在屏幕左上角提示:
Pleasechoicegrade(JforJunior,MfoeMedium,SforSenior):
2.等待读取一个键值:
若该键是’J’或’j’,则表示初级:
COL=8;ROW=8;
若该键是’M’或’m’,则表示中级:
COL=16;ROW=16;
若该键是’S’或’s’,则表示初级:
COL=24;ROW=24;
generateMine()的继续细化:
voidgenerateMine()
功能:
随机生成地雷的分布。
参数:
无
返回值:
无
算法描述:
1.计算将要生成的地雷的总数,存放在变量totalMine中。
2.将table数组和num数组清零,table和num都是全局的二维数组,table记录
坐标(x,y)中是否有地雷,0表示没有地雷,1表示有。
num数组表示和(x,y)相邻的所有小方块中有多少颗地雷。
3.随机生成totalMine个地雷的坐标,动态更新table和num数组的状态,
drawTable()的继续细化:
voiddrawTable()
{
inti,j;
for(i=0;i for(j=0;j drawBlock(i,j);/*画小方块*/ } drawBlock的细化: drawBlock(i,j)函数根据flag[i][j]的值画出在区域中坐标为(i,j)的方块,参数i,j是在整个雷区中当前光标的所在的行数和列数。 函数的实现过程如下: a)设置四个变量x,y,xx,yy,(x,y)和(xx,yy)用来表示方块在图形屏幕中左上角和右下角的坐标。 b)设置一个color变量,用于表示方块的颜色。 如果flag[i][j]==UNFLAG,则color值为LIGHTGRAY,表示方块没有被挖开或标记;如果不成立,则color的值为DARKGRAY,表示方块已经被挖开或标记为“F”、“? ”或“*”。 c)调用graphics.h库函数rectangle(x,y,xx,yy)画一格矩形,setfillstyle(SOLID_FILL,color)设置填充方式,floodfill(x+1,y+1,WHITE)填充该方块。 为了让方块看起来有立体感,将矩形上方和左方的边线设为白色,下方和右方的边线设为黑色。 d)光标所在的方块,我们将其边线设为红色。 e)根据flag[i][j]的不同值,在方块上作不同的标记: UNFLAG(表示该方块还没有被打开或者标记); FLAGED(标记该方块有地雷),方块上标记“F”; QUESTION(标记该方块可能有地雷),方块上标记“? ”; EXPLOD(表示踩到地雷爆炸了),方块上标记“*”; OPEN(表示一个没有地雷的方块被打开),方块上标上数字,该数字为周围的地雷数,即num[i][j]的值,或者没有数字,表示该方块周围没有地雷。 第四步,对“读入操作信息”功能模块进一步求精。 intgetKey(void) { intkey; while (1){ key=bioskey(0);/*调用bioskey(0)等待从键盘上读入一个按键*/ switch(key){ caseENTER: caseUP: caseDOWN: caseLEFT: caseRIGHT: caseESC: caseSPACE: caseLOWERF: caseUPPERF: caseLOWERA: caseUPPERA: caseLOWERQ: caseUPPERQ: returnkey;/*若输入的是有效的功能键,就返回*/ } } } 第五步,对语句“对其他key值进行判断处理”进一步求精。 switch(key){ caseENTER: caseSPACE: gameRes=openMine(pi,pj);break;/*打开方块*/ caseUP: moveUp();break;/*光标上移一格*/ caseDOWN: moveDown();break;/*光标下移一格*/ caseLEFT: moveLeft();break;/*光标左移一格*/ caseRIGHT: moveRight();break;/*光标右移一格*/ caseLOWERF: caseUPPERF: flagBlock(pi,pj);break;/*标记地雷*/ caseLOWERA: caseUPPERA: gameRes=autoOpen(pi,pj);break;/*挖开地雷*/ caseLOWERQ: caseUPPERQ: questBlock(pi,pj);break;/*标记疑问*/ default: break; } 这里的两个变量pi,pj表示为光标在雷区中的位置。 当key值为ENTER或SPACE时,执行gameRes=openMine(pi,pj)操作,函数openMine(i,j)用于挖开光标指向的方块(i,j)。 参数: i表示坐标的行的值,j表示坐标的列的值。 返回: 整型。 挖开了一个雷返回-1,否则返回0。 算法描述细化如下: a)判断方块是否已经被挖开。 若是返回0值,若不是,接(b)。 b)判断是否有地雷。 若有,整个雷区没有挖开的且有地雷的方块的flag值置为EXPLOD,并调用drawBlock()函数重新画这些方块,然后返回-1值,表示踩到地雷。 若没有地雷,接(c)。 c)将flag值置为OPEN,调用drawBlock()函数重新画该方块。 判断该方块的num值,若值为0,递归调用openMine函数挖该方块周围的八个方块,num值不为0时,返回0值,表示成功挖开该方块。 当key值为UP、DOWN、LEFT或RIGHT时,分别执行moveUp()、moveDown()、moveLeft()或moveRight()操作。 函数moveUp(),将当前光标(pi,pj)上移一格操作。 其实现过程为: 判断边界条件pi>0,然后调用drawBlock()函数重新画光标原来所指的方块和现在所指的方块。 其余函数的实现类似,要注意边界条件的判断。 函数moveDown(),当前光标(pi,pj)下移一格操作;函数moveLeft(),当前光标(pi,pj)左移一格操作;函数moveRight(),当前光标(pi,pj)右移一格操作。 当key值为LOWERF或UPPERF时,执行flagBlock(pi,pj)操作。 函数flagBlock(i,j)标记光标所指的方块(i,j)有地雷。 函数实现过程如下: 如果方块的flag值为FLAGED,则将flag值设为UNFLAG;如果方块的flag值为UNFLAG,则将flag值设为FLAGED。 接着调用drawBlock()函数重新画这个方块。 当key值为LOWERA或UPPERA时,执行gameRes=autoOpen(pi,pj)操作。 函数autoOpen(i,j)表示如果某个已经打开了的小方块显示的数字等于它周围已经被标记为雷的小方块的数目,则表明周围没有被挖开的小方块都已经不是地雷的,自动挖开这些小方块。 参数: i表示坐标的行的值,j表示坐标的列的值。 返回: 整型。 自动挖开的过程中由于玩家的标记有错误导致挖开了一个雷使得游戏失败,返回-1,否则返回0。 函数实现过程如下: a)判断当前的方块是否被挖开。 若没有,自动挖开无效,返回0;若是,接(b)。 b)计算当前方块周围被标记为FLAGED的方块数目。 若该数目等于当前方块的num[i][j]值,对(i,j)小方块的八个邻居中所有没有被挖开的小方块调用openMine函数。 如果自动挖开过程中踩到地雷,返回-1;否则,返回0值。 当key值为LOWERQ或UPPERQ时,执行questBlock(pi,pj)操作。 函数questBlock(i,j)在光标所指向的方块(i,j)上打一个问号,表示可能有地雷。 函数实现过程: 如果方块的flag值为QUESTION,则将flag值设为UNFLAG;如果方块的flag值为UNFLAG,则将flag值设为QUESTION。 接着调用drawBlock()函数重新画这个方块。 第六步,对“判断游戏是否胜利”功能模块进一步求精。 intcheckWin() 功能: 判断当前玩家是否已经取胜(即所有没有雷的坐标都已经被挖开了)。 参数: 无 返回值: 整型值,1表示玩家取得了胜利,0表示没有。 算法描述: 对每个没有地雷的坐标,判断该坐标是否已经被挖开,如果遇到有一个没有挖开,返回0;相反,如果所有没有地雷的坐标都已经被挖开返回1。 第七步,对“判断游戏是否重新开始”功能模块进一步求精。 intconfirm(intres) 功能: 判断当前玩家是否还要重新开始新的游戏。 参数: 游戏结束的状态: 0-ESC退出,-1-失败,1-胜利 返回值: 整型值,1表示继续,0表示结束游戏。 算法描述: 首先在屏幕左上角位置显示当前的游戏结束状态,提示是否继续游戏; 然后等待玩家从键盘输入键值: 若输入’Y’或’y’,则返回1——继续游戏;若输入’N’或’n’,则返回0——结束游戏。 四、编码 扫雷游戏的源程序分为三个文件: main.c、miner.c和key.c,其中main.c的内容为主控程序,key.c中包含获取输入信息的函数以及功能键的键值,miner.c中将初始化部分、界面绘制的部分、信息处理部分以及相关的数据集中在一起。 /*文件main.c*/ #include"key.c" #include"miner.c" intmain() { intgameRes;/*记录游戏结束的结果状态: 0表示退出游戏;-1游戏失败;1为胜利。 */ initGraph();/*初始化图形显示方式*/ do{ newGame();/*初始化新的游戏*/ gameRes=0; do{/*主循环——处理按键信息*/ intkey=getKey();/*读入操作信息*/ if(key==ESC)break; switch(key){/*对其他有效操作的处理*/ caseENTER: caseSPACE: gameRes=openMine(pi,pj);break; caseUP: moveUp();break; caseDOWN: moveDown();break; caseLEFT: moveLeft();break; caseRIGHT: moveRight();break; caseLOWERF: caseUPPERF: flagBlock(pi,pj);break; caseLOWERA: caseUPPERA: gameRes=autoOpen(pi,pj);break; caseLOWERQ: caseUPPERQ: questBlock(pi,pj);break; } if(checkWin())gameRes=1; }while(! gameRes); }while(! confirm(gameRes)); return0; } /*------文件main.c结束------*/ /*文件key.c*/ #include /*定义有效的键值*/ #defineENTER0x1c0d #defineUP0x4800 #defineDOWN0x5000 #defineLEFT0x4b00 #defineRIGHT0x4d00 #defineESC0x011b #defineSPACE0x3920 #defineLOWERF0x2166 #defineUPPERF0x2146 #defineLOWERA0x1e61 #defineUPPERA0x1e41 #defineLOWERQ0x1071 #defineUPPERQ0x1051 /*获取按键信息,返回有效的操作值*/ intgetKey(void){ while (1){ intkey=bioskey(0); switch(key){ caseENTER: caseUP: caseDOWN: caseLEFT: caseRIGHT: caseESC: caseSPACE: caseLOWERF: caseUPPERF: caseLOWERA: caseUPPERA: caseLOWERQ: caseUPPERQ: returnkey; } } } /*------文件key.c结束------*/ /*文件miner.c*/ #include #include #include #include intMAXCOL=640;/*屏幕最大宽度*/ intMAXROW=480;/*屏幕最
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 扫雷 游戏 实验 报告