扫雷游戏课程设计报告.docx

上传人:b****8 文档编号:10663021 上传时间:2023-02-22 格式:DOCX 页数:23 大小:342.12KB
下载 相关 举报
扫雷游戏课程设计报告.docx_第1页
第1页 / 共23页
扫雷游戏课程设计报告.docx_第2页
第2页 / 共23页
扫雷游戏课程设计报告.docx_第3页
第3页 / 共23页
扫雷游戏课程设计报告.docx_第4页
第4页 / 共23页
扫雷游戏课程设计报告.docx_第5页
第5页 / 共23页
点击查看更多>>
下载资源
资源描述

扫雷游戏课程设计报告.docx

《扫雷游戏课程设计报告.docx》由会员分享,可在线阅读,更多相关《扫雷游戏课程设计报告.docx(23页珍藏版)》请在冰豆网上搜索。

扫雷游戏课程设计报告.docx

扫雷游戏课程设计报告

(一)需求分析

题目:

32、实现一个N*M的扫雷游戏

设计要求:

能够实现一个N*M的扫雷游戏

a、能够打开一个方格(由于做的是静态显示,故在控制台上方格用‘—’代替),已打开的方格不能关闭

b、能够标记一个方格,标记方格的含义是对该方格有雷的预测(并不表示真的一定有雷)

c、能够给出游戏结果:

输、赢

d、N和M可由玩家自己设置

系统功能需求分析:

一个数字和一个雷(boom)。

你可以打开(open)一个方格,如果你打开的是一个boom,那么就失败;否则就会打开一个数字,该数字是位于[0,8]的一个整数,该数字表示其所有邻居方格所包含的雷数,应用该信息可以帮助你扫雷。

点击到了某区域发现其周围没有雷,那么显而易见应该点开周围的区域,拓展空白区域

(二)概要设计

由于知识储备不足,VC中的MFC应用程序又过于复杂,故退而求其次,不再采用动态显示和界面图形化,采用静态显示来实现扫雷游戏中的主要功能。

用键盘上的‘1’键代替鼠标左击,即打开一个方格查看其属性,已打开的方格不能在关闭;用键盘上的‘2’键代替鼠标右击,即标记一个方格,标记方格的含义是对该方格有雷的预测(并不表示真的一定有雷)

用键盘上的‘↑’‘↓’‘←’‘→’四个键来实现光标在控制台上的自由移动,

相当于用鼠标实现光标在图形界面的移动

游戏区域的高度与宽度和总雷数可由玩家自己设定

应题目要求设计了一个基类:

Base和一个继承类:

Game。

基类Base主要实现一些基本功能:

游戏结束时输出游戏的结果:

输赢;

返回控制台上光标的位置返回按下键时所对应的按键控制符

基类Base:

类名

成员类别

类型

成员名

描述

Base

方法

staticint

Output(constchar*)

在当前位置上输出一串字符

staticint

GotoXY(int,int);

取得控制台上光标的坐标位置并返回

staticint

GetKey();

等待按下键,并返回所对应的按键控制符

继承类Game是本程序的主要内容,也是实现扫雷游戏的关键部分。

主要实现的功能:

初始化图形界面,把游戏区域在控制台上显示出来;利用随机函数进行随机布雷,以保证玩家每次玩游戏时雷的分布位置均不同;得到一个坐标位置周围的雷数,并把数值返回;在一个坐标点上(x,y)点击,在该位置上显示其周围的雷数或拓展空白区域或失败;如果一个坐标点的周围没有雷,则拓展空白区域,并递归拓展;其中saolei()函数是类Game里的关键函数体,用来判断玩家按下了哪个键,并作出相应反应(上下左右四个方向的移动,打开一个方格,标记一个方格),并判断游戏的输与赢

继承类Game:

类名

成员类别

类型

成员名

描述

 

Game

 

属性

int

curX,curY

光标位置

int

poolWidth,poolHeight

游戏区域的高度与宽度

int

pool[GAME_MAX_HEIGHT+1][GAME_MAX_WIDTH+1]

用二维数组来存储雷的相关属性

conststaticint

GMARK_BOOM;

GMARK_EMPTY

GMARK_MARK;

雷的属性:

雷(*),空,标记(#)

方法

int

initpool(int,int,int)

初始布雷

Int

MoveCursor()

移动光标

int

huatu(int)

在控制台上把扫雷区域显示出来

int

tryopen(int,int)

在一个坐标点上(x,y)点击,在该位置上显示其周围的雷数或拓展空白区域或失败

int

shownum(int,int)

得到一个坐标位置周围的雷数,并把数值返回

int

tuozhan(int,int)

如果一个坐标点的周围没有雷,则拓展空白区域,并递归拓展

int

saolei()

扫雷并判断输赢

 

(三)详细设计

核心算法:

 

(1)布雷函数:

初始化时把数组里的值全部置为0,然后利用srand(),rand()随机机制产生随机数,分别对列和行取模,便产生了雷的随机位置。

但是布雷前,先要判断此随机位置是否已经布上了雷。

intGame:

:

initpool(intwidth,intheight,intnum)

{poolWidth=width;

poolHeight=height;if(num<0||num>=width*height||width>GAME_MAX_HEIGHT||width<=0||height<=0||height>GAME_MAX_HEIGHT)return1;

//初始是把游戏区域也即是数组里的值全都置为0

for(inty=0;y<=height+1;y++)

{for(intx=0;x<=width+1;x++)

{pool[y][x]=0;}

}

//利用伪随机函数进行随机布雷,以保证每次点击游戏时雷的分布位置不同

srand(time(NULL));

while(num!

=0){

intx=rand()%width+1;

inty=rand()%height+1;

if(pool[y][x]==0){

pool[y][x]=GMARK_BOOM;

num--;//num为设置的总雷数}

}

//初始化光标位置

curX=1;curY=1;

MoveCursor();

return0;

}

(2)打开方格,查看方格的属性

//在该位置上显示其周围的雷数或拓展空白区域或失败

intGame:

:

tryopen(intx,inty){

intm=0;

if(pool[y][x]&GMARK_BOOM)m=-1;

else{

intcount=shownum(x,y);

if(count==0)tuozhan(x,y);//拓展空白区域

elsepool[y][x]=count;}

returnm;}

(3)获得周围雷的数目

扫描其周围的所有相邻方格,记录雷数,并把值返回;扫描前要选择合理的起始点

intGame:

:

shownum(intx,inty){

intcount=0;

for(intY=-1;Y<=1;Y++)

for(intX=-1;X<=1;X++){

if(pool[y+Y][x+X]&GMARK_BOOM)

count++;}

returncount;}

(4)展拓空白区域

展拓原因:

当玩家点击的方块周围无雷时,此方块会被绘为空白,此时没有必要让玩家将其周围一一点开,应直接打开展拓条件:

周围雷数为零

函数inttuozhan(intx,inty)

参数:

x,y所点击的方块位置

算法:

递归

递归结束条件:

某一个方块不需要拓展就是因为其周围的雷数不是零

intGame:

:

tuozhan(intx,inty){

if((x>0&&x<=poolWidth)&&(y>0&&y<=poolHeight)&&(pool[y][x]==0)){intcount=shownum(x,y);

if(count==0){

pool[y][x]=GMARK_EMPTY;

for(intY=-1;Y<=1;Y++)

for(intX=-1;X<=1;X++)

{tuozhan(x+X,y+Y);}

}

elsepool[y][x]=count;}

return0;}

(5)在控制台上把扫雷区域显示出来

在还未按键时,调用huatu()函数把游戏界面在控制台上显示出来,即全部显示为方格。

以后每按键一次。

刷新一次界面

intGame:

:

huatu(intn=0)

{for(inty=1;y<=poolHeight;y++)

{Base:

:

GotoXY(1,y);

for(intx=1;x<=poolWidth;x++)

{if(pool[y][x]==0)

putchar('.');

elseif(pool[y][x]==GMARK_EMPTY)

putchar('');

elseif(pool[y][x]>0&&pool[y][x]<=8)

putchar('0'+pool[y][x]);

elseif(n==0&&(pool[y][x]&GMARK_MARK))

putchar('#');

elseif(pool[y][x]&GMARK_BOOM)

{if(n!

=0)putchar('*');

elseputchar('.');}

}

}

return0;}

(6)判断是否胜利

判断游戏是否胜利的条件在saolei()函数体内,在主函数里调用saolei()进行循环,直到戏赢了或输了才退出循环,并在控制台上输出结果;游戏赢了返回1,按下ESC键退出游戏返回-1,记为输,扫到雷也是返回-1

判断是否赢了游戏:

按下键‘1’后,对整个游戏区域进行检测,如果检测不到值为0的方格,即所有的雷已被扫出,则返回1,游戏胜利;按下ESC键退出游戏和扫到雷均为输,返回-1

if(Key==KEY_1)//判断是否赢了游戏

{inty=1;

for(;y<=poolHeight;++y)

{intx=1;

for(;x<=poolWidth;++x)

{if(pool[y][x]==0)break;

}

if(x<=poolWidth)break;}

if(!

(y<=poolHeight))

{m=1;

}

}

(四)调试分析和测试

(1)由玩家设置游戏区域的高度与宽度,并设置总雷数

按下回车键后界面如下:

按下键‘1’开始扫雷,扫雷过程部分界面如下:

…………

游戏输时显示的结果:

(2)直接按下ESC键时输掉游戏:

然后按下回车键后,直接按下ESC键,结果如下:

(3)为了使快速赢得游戏,减少雷的数目:

游戏运行过程部分界面如下:

按下键‘2’对怀疑有雷的区域进行标记

游戏胜利时显示的界面:

(五)源程序

附录源程序:

#include

#include

#include//包含伪随机数发生函数

#include//包含返回一个字符在控制台屏幕上坐标的文件

#include//包含getch()库函数:

从控制台读取一个字符,但不显示在屏幕上

//#define定义的一些宏变量

//键盘上上、下、左、右四个键和一些按键对应的ASCII编码

#defineKEY_UP0xE048

#defineKEY_DOWN0xE050

#defineKEY_LEFT0xE04B

#defineKEY_RIGHT0xE04D

#defineKEY_ESC0x001B

#defineKEY_1'1'

#defineKEY_2'2'

//设置游戏区域的最大长度与高度

#defineGAME_MAX_WIDTH100

#defineGAME_MAX_HEIGHT100

//游戏初始时在控制台上输出的游戏提示

#defineGAMETITLE"ArrowKey:

MoveCursorKEY1:

OpenKey2:

Mark"

//当游戏结束时输出的字符

#defineGAMEWIN"Congratulations!

YouWin!

Thankyouforplaying!

\n"

#defineGAMEOVER"Sorry,youlose!

thankyouforplaying!

\n"

#defineGAMEEND"PressESCtoexit\n"

//基类

//Baseclass

classBase{

public:

staticintOutput(constchar*);

staticintGotoXY(int,int);

staticintGetKey();

};

intBase:

:

GetKey()//等待按下键,并返回所对应的按键控制符

{intnkey=getch(),nk=0;

if(nkey>=128||nkey==0)nk=getch();//等待你按下任意键后,把该键字符所对应的ASCII符付给nk后,在执行下面语句

returnnk>0?

nkey*256+nk:

nkey;

}

intBase:

:

GotoXY(intx,inty)//取得控制台上光标的坐标位置并返回

{

COORDcd;

cd.X=x;cd.Y=y;

returnSetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE),cd);

}

intBase:

:

Output(constchar*pstr)//在当前位置上输出一串字符

{

for(;*pstr;pstr++)putchar(*pstr);//函数向标准输出设备输出一个变量的字符形式

return0;

}

//继承

classGame:

publicBase

{

private:

intcurX,curY;

intpoolWidth,poolHeight;//雷区间的总高度与宽度,相当与height

intpool[GAME_MAX_HEIGHT+2][GAME_MAX_WIDTH+2];

public:

Game():

curX(0),curY(0){poolWidth=poolHeight=0;}

intinitpool(int,int,int);//初始化游戏区域,设置游戏区域的高度与宽度,并设置总雷数

intMoveCursor(){returnBase:

:

GotoXY(curX,curY);}

inthuatu(int);//在控制台上把扫雷区域显示出来

intshownum(int,int);//得到一个坐标位置周围的雷数,并把数值返回

inttryopen(int,int);//在一个坐标点上(x,y)点击,在该位置上显示其周围的雷数或拓展空白区域或失败

intsaolei();

private:

inttuozhan(int,int);//如果一个坐标点的周围没有雷,则拓展空白区域,并递归拓展

private:

conststaticintGMARK_BOOM;

conststaticintGMARK_EMPTY;

conststaticintGMARK_MARK;

};

constintGame:

:

GMARK_BOOM=0x10;//*所对应的ASCII编码,用*表示地雷

constintGame:

:

GMARK_EMPTY=0x100;//空白所对应的ASCII编码

constintGame:

:

GMARK_MARK=0x200;//#所对应的ASCII编码,用#表示作标记

intGame:

:

initpool(intwidth,intheight,intnum)

{poolWidth=width;

poolHeight=height;if(num<0||num>=width*height||width>GAME_MAX_HEIGHT||width<=0||height<=0||height>GAME_MAX_HEIGHT)return1;

//初始是把游戏区域也即是数组里的值全都置为0

for(inty=0;y<=height+1;y++)

{

for(intx=0;x<=width+1;x++)

{

pool[y][x]=0;

}

}

//利用伪随机函数进行随机布雷,以保证每次点击游戏时雷的分布位置不同

srand(time(NULL));

while(num!

=0){

intx=rand()%width+1;

inty=rand()%height+1;

if(pool[y][x]==0){

pool[y][x]=GMARK_BOOM;

num--;//num为设置的总雷数}

}

//初始化光标位置

curX=1;curY=1;

MoveCursor();

return0;

}

//在控制台上把扫雷区域显示出来

intGame:

:

huatu(intn=0)

{

for(inty=1;y<=poolHeight;y++)

{

Base:

:

GotoXY(1,y);

for(intx=1;x<=poolWidth;x++)

{

if(pool[y][x]==0)

putchar('.');

elseif(pool[y][x]==GMARK_EMPTY)

putchar('');

elseif(pool[y][x]>0&&pool[y][x]<=8)

putchar('0'+pool[y][x]);

elseif(n==0&&(pool[y][x]&GMARK_MARK))

putchar('#');

elseif(pool[y][x]&GMARK_BOOM)

{

if(n!

=0)putchar('*');

else

putchar('.');

}

}

}

return0;

}

//得到一个坐标位置周围的雷数,并把数值返回

intGame:

:

shownum(intx,inty){

intcount=0;

for(intY=-1;Y<=1;Y++)

for(intX=-1;X<=1;X++){

if(pool[y+Y][x+X]&GMARK_BOOM)

count++;}

returncount;

}

//在一个坐标点上(x,y)点击,在该位置上显示其周围的雷数或拓展空白区域或失败

intGame:

:

tryopen(intx,inty){

intm=0;

if(pool[y][x]&GMARK_BOOM)m=-1;

else{

intcount=shownum(x,y);

if(count==0)tuozhan(x,y);//拓展空白区域

elsepool[y][x]=count;

}

returnm;

}

//如果一个坐标点的周围没有雷,则拓展空白区域,并递归拓展

intGame:

:

tuozhan(intx,inty){

if((x>0&&x<=poolWidth)&&(y>0&&y<=poolHeight)&&(pool[y][x]==0)){

intcount=shownum(x,y);

if(count==0){

pool[y][x]=GMARK_EMPTY;

for(intY=-1;Y<=1;Y++)

for(intX=-1;X<=1;X++)

{tuozhan(x+X,y+Y);}

}

elsepool[y][x]=count;

}

return0;

}

intGame:

:

saolei()//游戏赢了返回1,按下ESC键退出游戏返回-1,记为输,扫到雷也是返回-1

{

intKey=Base:

:

GetKey();

intm=0,nArrow=0;

switch(Key)

{

caseKEY_UP:

{

if(curY>1)curY--;

nArrow=1;

}break;

caseKEY_DOWN:

{if(curY

nArrow=1;

}break;

caseKEY_LEFT:

{if(curX>1)curX--;

nArrow=1;

}break;

caseKEY_RIGHT:

{

if(curX

nArrow=1;

}break;

caseKEY_1:

{

m=tryopen(curX,curY);

}break;

caseKEY_2:

{

if((pool[curY][curX]

&~(GMARK_MARK|GMARK_BOOM))==0)

{

pool[curY][curX]^=GMARK_MARK;

}

}break;

caseKEY_ESC:

{

m=-1;

}break;

}

if(Key==KEY_1)//判断是否赢了游戏

{

inty=1;

for(;y<=poolHeight;++y)

{

intx=1;

for(;x<=poolWidth;++x)

{

if(pool[y][x]==0)break;

}

if(x<=poolWidth)break;

}

if(!

(y<=poolHeight))

{

m=1;

}

}

if(nArrow==0)

{

huatu();////如果按下了key_1,key_2键,则显示扫雷结果

}

MoveCursor();

returnm;

}

voidmain(){

intx,y,b;//定义扫雷区域的高与宽,并设置游戏中得总雷数

cout<<"宽度:

";

cin>>x;

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 简历

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1