赞
踩
Hello,大家好,我是余同学。这两个月真是太忙了,无暇给大家更新文章…
暑假不是写了个扫雷小游戏吗(Link)?考虑到很多同学对代码没有透彻的理解,那么,这篇文章,我们来详细分析一下代码.
我们分为三个部分来讲:生成雷区
,生成雷区数字
和刷新与判断
首先,我们的雷区不能是定义好的矩阵,肯定得用随机数生成。
用随机数的话,就出现了一个问题:
srand(time(NULL));
的情况下)这个特性是什么样的呢?
我们来看下示例:
#include <bits/stdc++.h>
using namespace std;
int main ()
{
//srand(time(NULL));
int random=rand()%10;
cout<<random;
return 0;
}
运行结果:
第一次:
第二次:
可以看到, 虽然是"随机数
",但是,程序每次输出的数却是一样的,这是怎么回事?
程序每次生成的数来源于一个随机数种子,如果我们不改变它的话,那么程序就会一直使用这个种子,从而导致每次生成的随机数都一样
因此,我们要使用随机数种子
其具体原理即是利用每次运行的时间互不相同,生成的随机数也不同
srand(time(NULL));
可以使用rand()%x
生成随机数,也可以使用宏定义,即使用define
#define random(x) rand()%(x)
那么,我们看看效果吧
第一次输出:
第二次输出:
也就是说,如果我们要生成一个从a
到b
区间的随机数,可以用以下指令:
int number=a+rand()%b; //随机数生成区间为:a ~ a+b-1
int number=rand()%b; //随机数生成区间为:1 ~ b-1
随机数搞定了,接下来,就是考虑生成扫雷矩阵的事情了
现在,我们面临的最大问题就是:在不使用字符串的情况下,如何用0~9这十个数字实现数字与雷的分别
其实,实现很简单,我使用的方法是:用1~8
作为数字,0
作为附近9格无雷标志(其实还是数字),9
作为雷
那么,我们的矩阵是 10×10
的,只要通过循环生成 100
个随机数就行了
看代码吧:
#include<bits/stdc++.h> /* #include <iostream> #include <cstdlib> #include <windows.h> #include <ctime> #include <iomanip> */ #define random(x) 1+rand()%(x) using namespace std; int ui[12][12],b[12][12]; //多开一些没有坏处,原因后面会讲 int main(){ srand(time(NULL)); //random seed, srand(time(0)); //system("color 1B"); system("title MineSweeper"); int cnt=10; //地雷个数 while(cnt){ int x=random(10);//a+rand()%b = [a, a+b-1] int y=random(10); if(!ui[x][y]){//if(ui[x][y]==0){ ui[x][y]=9; cnt--; } } return 0; }
忘记说了,我设了两个二维数组,其中,ui
数组的功能是:存储整个雷区;
数组b
的功能则是:存储每个方格的状态
(详见Part.3
)
至此,我们的第一部分结束。
如果有的同学疑惑上面的代码没有输出时,那是因为我们只写了生成雷区的代码,并没有写生成雷区旁数字的代码,详细请看Part.2 生成雷区数字
要完成这一部分,我们首先得了解地雷旁数字的生成规律:
每个数字代表周围8
格内的地雷数量
还是上图片:
这样,我们的逻辑就清晰了,只要用双重循环遍历二维数组b
的每一项,判断这个点是否为地雷,如果是,就跳过这个点,继续向下遍历;否之,计算出该点周围8
个方格的总雷数(我们暂且定义为sum
),sum
就是这个点的数值
来把这一部分的代码实现吧
for(int i=1;i<=10;i++){ for(int j=1;j<=10;j++){ int sum=0; //统计周围地雷数量 if(ui[i][j]!=9){ //该点不为雷 /* 下面的这些坐标具体位置详见上图 */ if(ui[i-1][j-1]==9) sum++;//sum=!(a[i-1][j-1]-9)+!(a[i-1][j]-9)... if(ui[i-1][j]==9) sum++; if(ui[i-1][j+1]==9) sum++; if(ui[i][j-1]==9) sum++; if(ui[i][j+1]==9) sum++; if(ui[i+1][j-1]==9) sum++; if(ui[i+1][j]==9) sum++; if(ui[i+1][j+1]==9) sum++; ui[i][j]=sum; } } }
现在,我们就知道了为什么前面的两个二维数组都要开大一点了:
因为二维数组的索引是从0
开始的,而我们使用1
作为起始索引(具体见上方代码两个for
循环中),就是为了避免在边缘上的方块无法获取到周围地雷数,从而导致程序出bug的原因
然后,代码中的8个 if
语句都是统计周围地雷数的,如果你想简单亿点的话,把if
语句替换成这一行:
sum=!(ui[i-1][j-1]-9)+!(ui[i-1][j]-9)+!(ui[i-1][j+1]-9)+
!(ui[i][j-1]-9)+!(ui[i][j+1]-9)+!(ui[i+1][j-1]-9)+!(ui[i+1][j]-9)+!(ui[i+1][j+1]-9);
这里我分做两行写,是怕有的同学看不清,大家在实际替换中删掉换行符就可以了
这一部分完整代码:
#include<bits/stdc++.h> #include "windows.h" /* #include <iostream> #include <cstdlib> #include <windows.h> #include <ctime> #include <iomanip> */ #define random(x) 1+rand()%(x) using namespace std; int ui[12][12],b[12][12]; int main(){ srand(time(NULL)); //random seed //system("color 1B"); system("title MineSweeper"); int cnt=10;//cout<<"Booms:";cin>>cnt; while(cnt){ int x=random(10);//a+rand()%b = [a, a+b-1] int y=random(10); if(!ui[x][y]){//if(ui[x][y]==0){ ui[x][y]=9; cnt--; } } for(int i=1;i<=10;i++){ for(int j=1;j<=10;j++){ int sum=0; if(ui[i][j]!=9){ if(ui[i-1][j-1]==9) sum++;//sum=!(a[i-1][j-1]-9)+!(a[i-1][j]-9)... if(ui[i-1][j]==9) sum++; if(ui[i-1][j+1]==9) sum++; if(ui[i][j-1]==9) sum++; if(ui[i][j+1]==9) sum++; if(ui[i+1][j-1]==9) sum++; if(ui[i+1][j]==9) sum++; if(ui[i+1][j+1]==9) sum++; /*sum=!(ui[i-1][j-1]-9)+!(ui[i-1][j]-9)+!(ui[i-1][j+1]-9)+ !(ui[i][j-1]-9)+!(ui[i][j+1]-9)+!(ui[i+1][j-1]-9)+ !(ui[i+1][j]-9)+!(ui[i+1][j+1]-9);*/ ui[i][j]=sum; } } } /*for(int i=1;i<=10;i++){ for(int j=1;j<=10;j++){ cout<<ui[i][j]<<' '; //output the numbers } cout<<endl; }*/ return 0; }
大家把最后一段的注释符删掉,就可以看到我们的矩阵了
但是,我们到现在才做完整个项目的一半,我们还没有写判断操作符的代码,这就要留到Part 3
来讲了
我们先来讲刷新屏幕
为什不先写判断输入的代码呢?
因为我们得让程序先把扫雷的矩阵输出出来
我们看看这张图片
这是原版的扫雷界面,通过观察每个方格,可以发现,每个方格都会出现三种状态:已翻开(包括空格和数字,用1表示)、未翻开(包括数字和地雷)和旗子(用户标雷处)
那么,我们可以用"#
"表示未翻开的区域,用"P
"表示旗子(
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。