总起
随便看到了这篇文章,想起来半年前我无聊写的扫雷;虽然我后来不无聊就没有管代码了,但也是可以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
行)
-
输入规格,然后要用一个
二维数组
来表示雷的位置信息:int cx, cy; cin>>cx>>cy; cx+=2; cy+=2; int arr[cx][cy]={0};
cx
是长,cy
是宽;0
和cx
为预留部分,作用部分为1~cx-1
,cy
同理(至于数据为什么要加一,到标提示数时一起解释)。arr[cx][cy]={0}
是将整个数组初始化数据为0
。
={0}
只会把0
这个值赋给arr[0][0]
,但是在数组初始化时,没有对应到的位置的值会默认初始化为0
,这样也就做到了将整个数组的各个值都初始化为0
。注意:cpp4.9以前版本不支持在定义数组中写变量!
- 输入雷的数量,并随机布雷(设
-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()
是一组随机数函数(就像固定搭配一样)。- 重复执行以下代码直到计数器(埋下的雷数)
i
等于设置数量m
。 - 首先
int x=rand()%(cx-2-1+1)+1, y=rand()%(cy-2-1+1)+1;
随机(在作用部分)找一个点(rand()%(cx-2-1+1)+1
和rand()%(cy-2-1+1)+1
控制随机数大小), - 然后
if (arr[x][y]==0)
判断这个点有没有被放过雷。 - 如果这个点的值为
0
(初始值(没有被埋过雷(也可以写成arr[x][y]!=-1
))),那么就在这个点埋下雷arr[x][y]=-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循环有点多,分层解释:
- 外层
for循环
:
for (int i=1; i<cx-1; ++i){ for (int j=1; j<cy-1; ++j){ ...... } }
这两层是遍历数组,遍历作用部分(作用部分就是我们看到的整个“棋”盘)。
- 内层
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]; } } } }
- 判断当前遍历到的点是否为雷
if (arr[i][j]!=-1)
, - 如果不为雷,那么就给这个点标上提示数:
两个for循环
遍历以该点为中心的九宫格, - 判断(九宫格的)遍历是否遇到雷
if (arr[x][y]==-1)
,如果遇到雷:
将提示数加一++arr[i][j];
。
OK,现在应该不难理解为什么最初定义二维数组时要在作用部分的外面一圈多加一个预留部分了:当外层
for循环
遍历到数组边缘时,内层for循环
再一遍历,就数组出界了。为了避免这种情况,所以就要提前预留一部分空间出来。 - 外层
显示&游戏(62nd
行)
-
定义覆盖层:
此时我们已经布置好了“雷”盘,下一步就是要显示了。
我们可以想,扫雷肯定不能直接显示出来雷的位置啊,不然这“雷”还要“扫”干什么。而也不能全部不显示,不然就真的“踏雪无痕”了,扫过的地方都不知道。bool flag[cx][cy]={0};
为了方便两个数组一一对应,所以就定义相同大小的。顺便将所有数据都初始化为
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; }
cout<<" ";
纯占位置;for (int n=1; n<cy-1; ++n){
遍历列,作用:表头(标数字)cout<<setw(2)<<n<<" ";
。setw(2)
:cout
中的函数,作用:固定宽度输出。- 先遍历行
for (int i=1; i<cx-1; ++i){
,然后在每一行的最开头输出表头cout<<setw(2)<<i;
,后面就是内容了:遍历每一列for (int j=1; j<cy-1; ++j){
, - 先判断被遍历到的这个点有没有被点过
if (flag[i][j]){
(未点开,flag二维数组
中对应的位置的值就是0
;如果已点开,那么该值就被更新为1
(这是后面代码的事)),如果已点开那么就输出这个点的实际值(提示数或雷)cout<<setw(2)<<arr[i][j]<<" ";
,否则(未点开)} else {
,就输出覆盖符号——*
(当然,可以自由改)。 - 最后,一行输出完了,记得换行
cout<<endl;
!
-
输入并验证数据:
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;
- 如果输入的任意一个数据在对应的数据范围(作用部分)之外
if ((a>cx-2||a<=1)||(b>cy-2||b<=1)){
,那么就输出提示语cout<<"超出范围了"<<endl;
,并直接结束本次循环、启动新一轮循环continue;
。 - 不然(数据正常)就将对应的点更新为
1
(已点开状态)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; }
- 如果点开的地方的值为
-1
(雷)if (arr[a][b]==-1){
,那么就输出提示语“Game over!”cout<<"Game over!"<<endl;
,并遍历输出“雷”盘: - 遍历数组作用部分:每一行
for (int i=1; i<cx-1; ++i){
,遍历每一列for (int j=1; j<cy-1; ++j){
;设置每个空位的宽度, - 如果遍历到的位置为雷
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]); 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; }
- 先定义一个变量
sum
用于累加判断未点开的点的个数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){
,那么就说明未点开的部分全都是雷,也就是通关了。就输出提示语“Game passes!”cout<<"Game passes!";
,然后同上遍历输出“雷”盘并再次输出提示语“游戏结束”cout<<endl<<"游戏结束"<<endl;
。 - 最后返回值(积分)
return cx*cy/16+m*4;
(,怎样计算积分随意)。
- 先定义一个变量
花絮
说实话,我原本只是想把源代码发出来就拍拍手了事的。但谁知道InsCode
不支持C++
,于是我只好以文章的形式发布;但我又想,这样会不会太敷衍了,最终在我争做2.5min后,还是决定浅浅的写一下代码解析。我自认为我讲的还是很详细的,有不懂的地方欢迎提出!谢谢大家。
(源代码当然允许CtrlCV走,也可以在我加注释打``````标记的地方添加其他小游戏,I think这也是一种代码交流吧)