扫雷游戏实验报告1文档格式.docx
《扫雷游戏实验报告1文档格式.docx》由会员分享,可在线阅读,更多相关《扫雷游戏实验报告1文档格式.docx(14页珍藏版)》请在冰豆网上搜索。
♦描述精化后每个模块的处理过程;
♦确定主要的数据及其数据结构;
♦确定输入输出数据的内外部形式;
♦界面的设计
以下是详细设计:
(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("
Pressanykeytokey;
*若输入的是有效的功能键,就返回*
}
第五步,对语句“对其他key值进行判断处理”进一步求精。
switch(key){
caseENTER:
caseSPACE:
gameRes=openMine(pi,pj);
break;
*打开方块*
caseUP:
moveUp();
*光标上移一格*
caseDOWN:
moveDown();
*光标下移一格*
caseLEFT:
moveLeft();
*光标左移一格*
caseRIGHT:
moveRight();
*光标右移一格*
caseLOWERF:
caseUPPERF:
flagBlock(pi,pj);
*标记地雷*
caseLOWERA:
caseUPPERA:
gameRes=autoOpen(pi,pj);
*挖开地雷*
caseLOWERQ:
caseUPPERQ:
questBlock(pi,pj);
*标记疑问*
default:
这里的两个变量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。
第六步,对“判断游戏是否胜利”功能模块进一步求精。
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"
miner.c"
intgameRes;
*记录游戏结束的结果状态:
0表示退出游戏;
-1游戏失败;
1为胜利。
*
initGraph();
*初始化图形显示方式*
do{
newGame();
*初始化新的游戏*
gameRes=0;
do{*主循环——处理按键信息*
intkey=getKey();
if(key==ESC)break;
switch(key){*对其他有效操作的处理*
caseENTER:
caseSPACE:
gameRes=openMine(pi,pj);
caseUP:
moveUp();
caseDOWN:
moveDown();
caseLEFT:
moveLeft();
caseRIGHT:
moveRight();
caseLOWERF:
caseUPPERF:
flagBlock(pi,pj);
caseLOWERA:
caseUPPERA:
gameRes=autoOpen(pi,pj);
caseLOWERQ:
questBlock(pi,pj);
}
if(checkWin())gameRes=1;
}while(!
}while(!
confirm(gameRes));
return0;
*------文件main.c结束------*
*文件key.c*
#include<
bios.key;
*------文件key.c结束------*
*文件miner.c*
graphics."
Pressanykeyto0;
*初始化游戏*
voidnewGame(){
cleardevice();
setGrade();
generateMine();
pi=pj=0;
drawTable();
*判断游戏是否胜利*
intcheckWin(){
inti,j;
for(i=0;
i<
ROW;
i++){
for(j=0;
j<
COL;
j++){
if(table[i][j]==0&
&
flag[i][j]!
=OPEN)return0;
return1;
*判断游戏是否重新开始*
intconfirm(intres){
charbuffer[100];
intkk;
if(res==1){
strcpy(buffer,"
Youwinthegame,playagain(YN)?
elseif(res==-1){
Youlosethegame,playagain(YN)?
else{
Exitgame,playagain(YN)?
setcolor(WHITE);
outtextxy(0,0,buffer);
kk=bioskey(0);
kk=(kk&
0xff);
if(kk=='
Y'
||kk=='
y'
)return0;
*光标上移一格*
voidmoveUp(){
if(pi>
0){
pi--;
drawBlock(pi,pj);
drawBlock(pi+1,pj);
*光标下移一格*
voidmoveDown(){
if(pi<
ROW-1){
pi++;
drawBlock(pi-1,pj);
*光标左移一格*
voidmoveLeft(){
if(pj>
pj--;
drawBlock(pi,pj+1);
*光标右移一格*
voidmoveRight(){
if(pj<
COL-1){
pj++;
drawBlock(pi,pj-1);
*标记有雷*
voidflagBlock(inti,intj){
if(flag[i][j]==FLAGED){
flag[i][j]=UNFLAG;
elseif(flag[i][j]==UNFLAG){
flag[i][j]=FLAGED;
drawBlock(i,j);
*标记不确定*
voidquestBlock(inti,intj){
if(flag[i][j]==QUESTION){
if(flag[i][j]==UNFLAG)flag[i][j]=QUESTION;
*挖雷*
intopenMine(inti,intj){
intii,jj,k;
if(flag[i][j]==OPEN)return0;
if(table[i][j]){*meetamine*
for(ii=0;
ii<
ii++){
for(jj=0;
jj<
jj++){
if(table[ii][jj]&
flag[ii][jj]==UNFLAG){
flag[ii][jj]=EXPLOD;
drawBlock(ii,jj);
return-1;
*游戏失败*
else{
flag[i][j]=OPEN;
if(num[i][j]==0){
for(k=0;
k<
8;
k++){
intni=i+di[k];
intnj=j+dj[k];
if(ni>
=0&
ni<
ROW&
nj>
nj<
COL)
openMine(ni,nj);
*自动挖开*
intautoOpen(inti,intj){
intk,c=0;
intni,nj;
intret=0;
if(!
(flag[i][j]==OPEN))return0;
ni=i+di[k];
nj=j+dj[k];
if(flag[ni][nj]==FLAGED)c++;
if(c==num[i][j]){
if(flag[ni][nj]==UNFLAG){
if(openMine(ni,nj)==-1)ret=-1;
returnret;
*-----文件miner.c结束------*