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;
- }