C++扫雷

总起

随便看到了这篇文章,想起来半年前我无聊写的扫雷;虽然我后来不无聊就没有管代码了,但也是可以playing的。


正文

代码

# include <bits/stdc++.h>
using namespace std;
int minesweeper ();
//``````
int data (int n){
	switch (n){
		case 1: return minesweeper();
//		``````
		default: cout<<"目录未找到该program"<<endl; return 0;
	}
}
int main (){
	cout<<"欢迎来到益智小游戏!"<<endl;
	int sum=0;
	while (true){
		cout<<"Your 积分:"<<sum<<endl;
		cout<<"Some 益智游戏"<<endl;
		string project[]={"扫雷"};
		for (int i=1; i<=1; ++i){
			cout<<i<<" "<<project[i-1]<<endl;
		}
		cout<<"join(序号)?:";
		int want;
		cin>>want;
		sum+=data(want);
		cout<<endl;
	}
}
int minesweeper (){
	cout<<"扫雷"<<endl;
	int cx, cy;
	cout<<"What 全局大小?(先行后列, 空格隔开):";
	cin>>cx>>cy;
	cx+=2;
	cy+=2;
	int arr[cx][cy]={0};
	int m;
	cout<<"How many 地雷?:";
	cin>>m; 
	srand(time(0));
	int i=0;
	while (i<m){
		int x=rand()%(cx-2-1+1)+1, y=rand()%(cy-2-1+1)+1;
		if (arr[x][y]==0){
			arr[x][y]=-1;
			++i;
		}
	}
	for (int i=1; i<cx-1; ++i){
		for (int j=1; j<cy-1; ++j){
			if (arr[i][j]!=-1){
				for (int x=i-1; x<=i+1; ++x){
					for (int y=j-1; y<=j+1; ++y){
						if (arr[x][y]==-1){
							++arr[i][j];
						}
					}
				}
			}
		}
	}
	bool flag[cx][cy]={0};
	while (true){
		cout<<"  ";
		for (int n=1; n<cy-1; ++n){
			cout<<setw(2)<<n<<" ";
		}
		cout<<endl;
		for (int i=1; i<cx-1; ++i){
			cout<<setw(2)<<i;
			for (int j=1; j<cy-1; ++j){
				if (flag[i][j]){
					cout<<setw(2)<<arr[i][j]<<" ";
				} else {
					cout<<setw(2)<<"*"<<" ";
				}
			}
			cout<<endl;
		}
		int a, b;
		cout<<"Here 排雷?(先行后列, 空格隔开):";
		cin>>a>>b;
		if (a>cx-2||b>cy-2){
			cout<<"超出范围了"<<endl;
			continue;
		}
		flag[a][b]=1;
		if (arr[a][b]==-1){
			cout<<"Game over!"<<endl;
			for (int i=1; i<cx-1; ++i){
				for (int j=1; j<cy-1; ++j){
					cout<<setw(4);
					if (arr[i][j]==-1) cout<<"雷";
					else cout<<"地";
				}
				cout<<endl;
			}
			cout<<endl<<"游戏结束"<<endl;
			return 0;
		}
		int sum=0;
		for (int i=1; i<cx-1; ++i){
			for (int j=1; j<cy-1; ++j){
				if (flag[i][j]) ++sum;
			}
		}
		if (sum==m){
			cout<<"Game passes!";
			for (int i=1; i<cx-1; ++i){
				for (int j=1; j<cy-1; ++j){
					cout<<setw(4);
					if (arr[i][j]==-1) cout<<"雷";
					else cout<<"地";
				}
				cout<<endl;
			}
			cout<<endl<<"游戏结束"<<endl;
			return cx*cy/16+m*4;
		}
	}
}
//``````

代码要点(大纲)

(只看minesweeper()函数)

布雷(31st行~61st行)

  1. 输入规格,然后要用一个二维数组来表示雷的位置信息:

    int cx, cy;
    cin>>cx>>cy;
    cx+=2;
    cy+=2;
    int arr[cx][cy]={0};
    
    1. cx是长,cy是宽;0cx预留部分作用部分1~cx-1cy同理(至于数据为什么要加一,到标提示数时一起解释)。
    2. arr[cx][cy]={0}是将整个数组初始化数据为0

    ={0}只会把0这个值赋给arr[0][0],但是在数组初始化时,没有对应到的位置的值会默认初始化为0,这样也就做到了将整个数组的各个值都初始化为0

    注意:cpp4.9以前版本不支持在定义数组中写变量!


  1. 输入雷的数量,并随机布雷(-1为雷):
    int m;
    cout<<"How many 地雷?:";
    cin>>m; 
    srand(time(0));
    int i=0;
    while (i<m){
    	int x=rand()%(cx-2-1+1)+1, y=rand()%(cy-2-1+1)+1;
    	if (arr[x][y]==0){
    		arr[x][y]=-1;
    		++i;
    	}
    }
    

    srand(time(0));rand()一组随机数函数(就像固定搭配一样)。

    1. 重复执行以下代码直到计数器(埋下的雷数)i等于设置数量m
    2. 首先int x=rand()%(cx-2-1+1)+1, y=rand()%(cy-2-1+1)+1;随机(在作用部分)找一个点(rand()%(cx-2-1+1)+1rand()%(cy-2-1+1)+1控制随机数大小),
    3. 然后if (arr[x][y]==0)判断这个点有没有被放过雷。
    4. 如果这个点的值为0(初始值(没有被埋过雷(也可以写成arr[x][y]!=-1))),那么就在这个点埋下雷arr[x][y]=-1;,再把计数器加一

  1. 遍历数组,将所有点全部标上提示数:

    for (int i=1; i<cx-1; ++i){
    	for (int j=1; j<cy-1; ++j){
    		if (arr[i][j]!=-1){
    			for (int x=i-1; x<=i+1; ++x){
    				for (int y=j-1; y<=j+1; ++y){
    					if (arr[x][y]==-1){
    						++arr[i][j];
    					}
    				}
    			}
    		}
    	}
    }
    

    for循环有点多,分层解释:

    1. 外层for循环
    for (int i=1; i<cx-1; ++i){
    	for (int j=1; j<cy-1; ++j){
    		......
    	}
    }
    

    这两层是遍历数组,遍历作用部分(作用部分就是我们看到的整个“棋”盘)。


    1. 内层for循环
    		if (arr[i][j]!=-1){
    			for (int x=i-1; x<=i+1; ++x){
    				for (int y=j-1; y<=j+1; ++y){
    					if (arr[x][y]==-1){
    						++arr[i][j];
    					}
    				}
    			}
    		}
    
    1. 判断当前遍历到的点是否为雷if (arr[i][j]!=-1)
    2. 如果不为雷,那么就给这个点标上提示数:
      两个for循环遍历以该点为中心的九宫格,
    3. 判断(九宫格的)遍历是否遇到雷if (arr[x][y]==-1),如果遇到雷:
      将提示数加一++arr[i][j];

    OK,现在应该不难理解为什么最初定义二维数组时要在作用部分的外面一圈多加一个预留部分了:当外层for循环遍历到数组边缘时,内层for循环再一遍历,就数组出界了。为了避免这种情况,所以就要提前预留一部分空间出来。


显示&游戏(62nd行)

  1. 定义覆盖层:
    此时我们已经布置好了“雷”盘,下一步就是要显示了。
    我们可以想,扫雷肯定不能直接显示出来雷的位置啊,不然这“雷”还要“扫”干什么。而也不能全部不显示,不然就真的“踏雪无痕”了,扫过的地方都不知道。

    bool flag[cx][cy]={0};
    

    为了方便两个数组一一对应,所以就定义相同大小的。顺便将所有数据都初始化为0


  1. 进入游戏主循环:
    while (true){
    

  1. 输出:

    cout<<"  ";
    for (int n=1; n<cy-1; ++n){
    	cout<<setw(2)<<n<<" ";
    }
    cout<<endl;
    for (int i=1; i<cx-1; ++i){
    	cout<<setw(2)<<i;
    	for (int j=1; j<cy-1; ++j){
    		if (flag[i][j]){
    			cout<<setw(2)<<arr[i][j]<<" ";
    		} else {
    			cout<<setw(2)<<"*"<<" ";
    		}
    	}
    	cout<<endl;
    }
    
    1. cout<<" ";纯占位置;for (int n=1; n<cy-1; ++n){遍历,作用:表头(标数字)cout<<setw(2)<<n<<" ";

      setw(2)cout中的函数,作用:固定宽度输出。

    2. 先遍历for (int i=1; i<cx-1; ++i){,然后在每一行的最开头输出表头cout<<setw(2)<<i;,后面就是内容了:遍历每一列for (int j=1; j<cy-1; ++j){
    3. 先判断被遍历到的这个点有没有被点过if (flag[i][j]){未点开flag二维数组中对应的位置的值就是0;如果已点开,那么该值就被更新为1(这是后面代码的事)),如果已点开那么就输出这个点的实际值(提示数或雷)cout<<setw(2)<<arr[i][j]<<" ";,否则(未点开} else {,就输出覆盖符号——*(当然,可以自由改)。
    4. 最后,一行输出完了,记得换行cout<<endl;
  2. 输入并验证数据:

    int a, b;
    cout<<"Here 排雷?(先行后列, 空格隔开):";
    cin>>a>>b;
    if ((a>cx-2||a<=1)||(b>cy-2||b<=1)){
        cout<<"超出范围了"<<endl;
        continue;
    }
    flag[a][b]=1;
    
    1. 如果输入的任意一个数据在对应的数据范围(作用部分)之外if ((a>cx-2||a<=1)||(b>cy-2||b<=1)){,那么就输出提示语cout<<"超出范围了"<<endl;,并直接结束本次循环、启动新一轮循环continue;
    2. 不然(数据正常)就将对应的点更新为1已点开状态)flag[a][b]=1;
  3. 讨论情况:遇雷:

    if (arr[a][b]==-1){
    	cout<<"Game over!"<<endl;
    	for (int i=1; i<cx-1; ++i){
    		for (int j=1; j<cy-1; ++j){
    			cout<<setw(4);
    			if (arr[i][j]==-1) cout<<"雷";
    			else cout<<"地";
    		}
    		cout<<endl;
    	}
    	cout<<endl<<"游戏结束"<<endl;
    	return 0;
    }
    
    1. 如果点开的地方的值为-1(雷)if (arr[a][b]==-1){,那么就输出提示语“Game over!”cout<<"Game over!"<<endl;,并遍历输出“雷”盘:
    2. 遍历数组作用部分每一行for (int i=1; i<cx-1; ++i){,遍历每一列for (int j=1; j<cy-1; ++j){;设置每个空位的宽度,
    3. 如果遍历到的位置为雷if (arr[i][j]==-1),就输出“雷”cout<<"雷";;否则(遍历到的位置不为雷)else,就输出“地”cout<<"地";
    4. 每行结束了记得换行cout<<endl;
    5. 最后再输出提示语“游戏结束”cout<<endl<<"游戏结束"<<endl;
    6. return 0;这条语句这里不是结束程序哈,这里不是主函数。这里的意思是:返回值(积分)(到主函数中)。
  4. 讨论情况:通关:

    int sum=0;
    for (int i=1; i<cx-1; ++i){
    	for (int j=1; j<cy-1; ++j){
    		if (flag[i][j]); else ++sum;
    	}
    }
    if (sum==m){
    	cout<<"Game passes!";
    	for (int i=1; i<cx-1; ++i){
    		for (int j=1; j<cy-1; ++j){
    			cout<<setw(4);
    			if (arr[i][j]==-1) cout<<"雷";
    			else cout<<"地";
    		}
    		cout<<endl;
    	}
    	cout<<endl<<"游戏结束"<<endl;
    	return cx*cy/16+m*4;
    }
    
    1. 先定义一个变量sum用于累加判断未点开的点的个数int sum=0;。遍历数组作用部分并将未点开的点的数量累加:
    2. 遍历每一行for (int i=1; i<cx-1; ++i){,遍历每一列for (int j=1; j<cy-1; ++j){。如果被遍历的点已被点开if (flag[i][j]),那么什么也不做;否则(未点开else,就让计数器加一++sum
    3. 如果未点开的数量等于雷的数量if (sum==m){,那么就说明未点开的部分全都是,也就是通关了。就输出提示语“Game passes!”cout<<"Game passes!";,然后同上遍历输出“雷”盘并再次输出提示语“游戏结束”cout<<endl<<"游戏结束"<<endl;
    4. 最后返回值(积分)return cx*cy/16+m*4;(,怎样计算积分随意)。

花絮

说实话,我原本只是想把源代码发出来就拍拍手了事的。但谁知道InsCode不支持C++,于是我只好以文章的形式发布;但我又想,这样会不会太敷衍了,最终在我争做2.5min后,还是决定浅浅的写一下代码解析。我自认为我讲的还是很详细的,有不懂的地方欢迎提出!谢谢大家。
(源代码当然允许CtrlCV走,也可以在我加注释打``````标记的地方添加其他小游戏,I think这也是一种代码交流吧)


  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值