您的位置:首页 > 编程语言

原创!字符版扫雷 控制台程序源代码,欢迎拍砖...

2008-11-22 18:51 639 查看
VC6 + XP/Vista 调试成功

这是我自己写的一个字符版扫雷的控制台程序(有些功能还比较欠缺)...

编程不久,第一次写这么多的代码,有些注释以及变量命名不合规范,还望指出交流..

也有朋友指出这个代码更像是C风格的而不是C++的,对于C++风格我确实了解不多,希望大家能来信指出

我的邮件地址是liliflashfly(at)gmail.com,你的意见将是我前进的动力,谢谢!

相关工程(VC6版)可以在CSDN的资源http://download.csdn.net/source/799581上下载....

思路请见



说明文档:



初始化





得到边界n,m值,

建立数组mine_count,全部初始化为0(作为后台数据界面,-1表示地雷,非负数表示周围的地雷数)

建立字符数组mine_state,全部初始化为'#',表示还未探索

挖出的地雷数dig_num=0,游戏结束mine_end=false,游戏胜利mine_win=false

得到地雷值mine_num,建立循环,利用随机函数,产生mine_num个坐标(越界和地雷覆盖(bool IsMine)判断),

并且每个坐标周围的坐标地雷值(mine_count)自增一次(通过MineCountPP(y,x)函数)。

并且这些地雷坐标保存到结构体(x,y)数组Mine_Location[mine_count]中.





界面

0 1 2 3 4 5 6 7 8 9 10

1 # # # # # # # # # #

2 # # # 1 # # # # # #

3 # # # 2 # # # # # #

4 # # # # # # # # # #

5 # # 1 # # # # # # #

6 # # 1 P # # # # # #

7 # # # # # # # # # #

8 # # # # # # # # # #

9 # # # # # # # # # #

10# # # # # # # # # #

你还有8个地雷没有挖出。。。



开始游戏



输入一次坐标,返回一次全局状态(mine_state)

状态:

未探索:            '#'

已探索:    mine_state[x][y]!='#'

  周围有地雷:相应数字,如 '6'

  插旗(挖雷成功):     笑脸图案,char值为2

  空的:          ' '

  地雷:          'O'

判断错误:        'X'

爆炸点:         '_'



输入方式

s y x (step到某个坐标(x,y),即确定那里没有地雷)

d y x (dig某个坐标(x,y),即确定那里有地雷,插旗)

输入后先执行x--,y--,再判断是否越界和mine_state是否为'#',否则提示出错。



坐标输入后判断





1、s输入,是地雷(mine_count==-1)

此坐标mine_state为'_',

遍历Mine_Location[],使剩下的未探索的地雷click=true,mine_state为'*'(通过fail()函数)



  d输入,不是地雷(mine_count!=-1)

此坐标mine_state为'X',

遍历Mine_Location[],使剩下的未探索的地雷click=true,mine_state为'*'(通过fail()函数)



然后游戏结束(mine_end=true),输出mine_state。



2、s输入,不是地雷(mine_count!=-1)

  mine_count>0

mine_state[y][x]=mine_count+'0';

mine_count==0

mine_state[y][x]=' ';

回溯第归直到mine_count>0 或者 到了边界

3、d输入,是地雷(mine_count==-1)

mine_state[y][x]='P';

dig_num++;

if(dig_num==mine_num)

输出,胜利,结束(mine_end=true)

//类Mine的头文件 minectrl.h

#ifndef _MINE_CTRL_

#define _MINE_CTRL_

#include "iostream"

#include "iomanip"

#include "string"

#include "ctime"

#include "cstdlib"

using namespace std;

struct Mine_coordinate{

int x,y;

};

class Mine

{

friend void Run(Mine *oMine);

public:

Mine(void){};

Mine(int x,int y,int MineNum); //带参构造函数,随机生成扫雷坐标,形成后台数据界面

virtual ~Mine(void);

private:

//功能函数

//每局游戏的初始化函数

void MineCountPP(int y,int x); //对地雷坐标周围一圈中没有越界且不是地雷的坐标进行自增,因为增加了一个地雷

void GetXY(int *x,int *y); //获得两个不越界的随机值

void Help(void); //帮助坐标

//每局游戏进行时的函数

void Screen_Out(void); //游戏界面函数,显示游戏状况

void Operate(void); //主要操作函数,用于处理游戏时玩家的输入

void Deal_blank(int x,int y); //回溯函数,解决到达一个周围无地雷的坐标的情况

bool Mine::IsInputOk(char &ch,int &x,int &y); //判断坐标输入是否合法

//每局游戏的结束状态判断函数

void Fail(void); //游戏失败时将未发现的地雷标注出来

bool GoOn(void); //判断是否继续游戏

bool Win(void); //判断是否胜利

//数据

char **mine_state; //每个坐标的状态,游戏的实时显示界面

int **mine_count; //每个坐标周围的地雷数,游戏的后台数据界面

int dig_num,mine_num; //挖出地雷数,地雷的数量

int X,Y;//保存边界

bool mine_end,mine_win; //判断游戏是否结束,判断是否胜利

Mine_coordinate *Mine_Location; //用于建立一个记录地雷位置的数组

};

#endif



//类Mine的实现文件 minectrl.cpp

#include "minectrl.h"

Mine::Mine(int y,int x,int MineNum):X(x),Y(y),mine_num(MineNum){

int i(0);

mine_state=new char*[Y];

mine_count=new int* [Y];

srand((unsigned)time(NULL)); //随机的初始,现在的时间作为随机种子

//生成并初始化两个二维数组

for(;i<Y;i++){

mine_state[i]=new char[X];

mine_count[i]=new int[X];

memset(mine_state[i],'#',X*sizeof(char)); //初始化:游戏界面,因为字符型只有一字节,所以能用memset()

memset(mine_count[i], 0 ,X*sizeof(int) ); //初始化:后台数据界面,-1 表示地雷,自然数 表示四周一圈的格子里地雷的数量

}

dig_num=0; //初始化:已挖了的地雷数量。

mine_end=mine_win=false; //初始化:游戏结束与胜利均设为否

Mine_Location=new Mine_coordinate[mine_num]; //建立一个记录地雷位置的数组。



//开始随机生成地雷坐标,

for(i=0;i<mine_num;i++){

int x(0),y(0);

GetXY(&x,&y); //获得两个不越界的随机值

while(mine_count[y][x]==-1) //如果坐标已经有地雷了,则重新找

GetXY(&x,&y);

mine_count[y][x]=-1; //标注此地有地雷

MineCountPP(x,y); //使其周围数字加1

Mine_Location[i].y=y; //坐标记录到地雷位置数组里面

Mine_Location[i].x=x;

}



}

void Mine::GetXY(int *x,int *y){

*x=(rand()%X); //生成[0,X-1]的随机数

*y=(rand()%Y); //生成[0,Y-1]的随机数

}

//对地雷坐标周围一圈中没有越界且不是地雷的坐标进行自增,因为增加了一个地雷

void Mine::MineCountPP(int x,int y){

int i,j;

for(i=-1;i<2;i++)

for(j=-1;j<2;j++)

if(y+i>-1 && y+i<Y && x+j>-1 && x+j<X) //自增的时候进行边界之类的判断

if(mine_count[y+i][x+j]!=-1) //确保记录的地方不是另一个地雷

mine_count[y+i][x+j]++;

}

bool Mine::IsInputOk(char &ch,int &x,int &y){

bool ok=true;

if(!(cin>>ch>>x>>y) || (ch!='s'&&ch!='d') ){ //输入不合规范 坐标输成了字母||命令是除s、d以外的字符

ok=false;

cin.clear(); //清除输入流的错误状态,避免死循环

}

while(cin.get()!='/n');

system("cls");

cout<<ch<<" "<<x<<" "<<y<<endl;

x--;y--;

return ok;

}

void Mine::Operate(void){

int x,y; //坐标

char s; //命令字符(s or d)

if( IsInputOk(s,x,y) && //关于命令的输入 兼 输入是否合法的判断

-1<x && x<X && -1<y && y<Y && //关于边界的判断

mine_state[y][x]=='#'){ //关于位置是否探索过的判断



if(s=='s'){//执行“踩”操作,即认为此处无雷

if(mine_count[y][x]==-1){ //踩到雷了

mine_state[y][x]='_'; //将爆炸点标记出来

Fail(); //将未发现的雷标记出来.

mine_end=true; //游戏结束

}else{ //没有踩到雷

if(mine_count[y][x]>0) //此坐标周围有雷

mine_state[y][x]='0'+mine_count[y][x];

else{ //此坐标周围没有地雷,需要向八个方向扩展直至找到边界或者有地雷的地方

Deal_blank(x,y); //回溯第归

}

}

}

else{ //s=='d',执行“挖”操作。即认为此处有雷



if(mine_count[y][x]!=-1){ //此处无雷

mine_state[y][x]='X';

Fail(); //将未发现的雷标记出来.

mine_end=true; //游戏结束

}else{ //挖到雷了

mine_state[y][x]=2; //显示一个笑脸字符,即表示成功

dig_num++; //已挖雷数量增加1

if(dig_num==mine_num){ //雷挖完了

mine_end=true; //结束

mine_win=true; //胜利

}

}

}

}else{ //非法输入提示

cout<<"错误输入,重试!"<<endl;

}

Screen_Out(); //显示此时扫雷情况

}

//游戏以失败结束的时候 将未发现的地雷全部用'O'显示出来

void Mine::Fail(void)

{

for(int i(0);i<mine_num;i++){//遍历地雷位置数组,看哪些还是'#'(未探索),则显示为'O'

if(mine_state[Mine_Location[i].y][Mine_Location[i].x]=='#')

mine_state[Mine_Location[i].y][Mine_Location[i].x]='O';

}

}

bool Mine::GoOn(void)

{

return !mine_end;

}

bool Mine::Win(void)

{

return mine_win;

}

//游戏界面函数,显示游戏状况

void Mine::Screen_Out(){

int i,j;

cout<<" 0|";

for(i=1;i<=X;i++)

cout<<setw(2)<<i%10; //因为同1行的各#有一空格,两位数的话坐标显示得很拥挤,所以 横坐标 取个位数

//printf("%2d",i%10);

cout<<endl;

for(i=0;i<2*X+3;i++)

cout<<'-';

puts("");

for(i=0;i<Y;i++){

cout<<setw(2)<<i+1<<"|";

//printf("%2d|",i+1);

for(j=0;j<X;j++)

cout<<" "<<mine_state[i][j];

cout<<endl;

}

if(!mine_end){ //如果游戏未结束

cout<<"剩余地雷:"<<mine_num-dig_num<<"个"<<endl<<endl;

cout<<"如果认为(x,y)坐标没有地雷请输入/"s x y/""<<endl;

cout<<"如果认为(x,y)坐标 有地雷请输入/"d x y/""<<endl;

}

/*for(i=0;i<Y;i++)

{

for(j=0;j<X;j++)

printf("%3d",mine_count[i][j]);

puts("");

}后台数据界面*/

}

void Mine::Deal_blank(int x,int y){

if(-1<x && x<X && -1<y && y<Y && mine_state[y][x]=='#'){//先判断是否越界,再判断是否仍是未探索区域

if(mine_count[y][x]==0){//继续回溯

mine_state[y][x]=' ';

int xShift[]={-1,-1,-1,0,0,1,1,1};

int yShift[]={-1,0,1,-1,1,-1,0,1};

for(int i=0;i<8;i++){

Deal_blank(x+xShift[i],y+yShift[i]);

}

}else{

mine_state[y][x]='0'+mine_count[y][x];

}

}

}

void Mine::Help(void){

cout<<"此游戏是扫雷的翻版(还欠缺一些功能,不过可以玩了...)"<<endl;

cout<<"#表示还没有被探索"<<endl;

cout<<(char)2<<"表示此地雷被挖除"<<endl;

cout<<"O表示此地雷没有被发现"<<endl;

cout<<"_表示踩到地雷了,失败"<<endl;

cout<<"X表示此处没有地雷,失败"<<endl;

cout<<"操作方法:"<<endl;

cout<<"如果认为(x,y)坐标没有地雷请输入/"s x y/",如s 3 5"<<endl;

cout<<"如果认为(x,y)坐标 有地雷请输入/"d x y/",如d 6 3"<<endl;

cout<<"回车键继续..."<<endl;

while(cin.get()!='/n'); //使屏幕等待,并消去可能出现的多余字符

}

Mine::~Mine(void){

int i(0);

for(;i<Y;i++)

{

delete [] mine_state[i];

delete [] mine_count[i];

}

delete [] mine_state;

delete [] mine_count;

delete [] Mine_Location;

}

void Run(Mine *oMine){

int x=9,y=9,MineNum=10; //值可以修改,做个自定义,但鉴于是字符版的,界面有限,最好不用改了

char ask;

bool ContinueMine=true; //用于判断是否再玩一局

while(ContinueMine){

system("cls"); //清屏函数

oMine=new Mine(y,x,MineNum);//初始类

oMine->Help(); //显示操作说明

system("cls"); //清屏函数

oMine->Screen_Out(); //显示此时扫雷情况,这里是初始界面

while(oMine->GoOn()){ //GoOn()函数判断是否继续

oMine->Operate();

}

if(oMine->Win()) //Win()函数判断是否胜利

cout<<endl<<"You Win!"<<endl;

else

cout<<endl<<"You lose..."<<endl;

delete oMine;

cout<<"再来?(Y/N)";

ask=cin.get();

if(ask!='y' && ask!='Y') ContinueMine=false;

if(ask=='/n') cin.putback(ask);

while(cin.get()!='/n') ; //消除多余的字符及回车

}

cout<<"ByeBye..."<<endl;

}

//主文件 main.cpp

#include "minectrl.h"

int main(){

Mine *oMine=NULL;

Run(oMine);

return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: