前言
昨天小编回顾童年,玩起了扫雷游戏,因为游戏都是一条条代码编出来的,小编心血来潮也去试了试,经过几个小时的琢磨也是编出来个入版,今天用这篇文章给大家分享一下。
要掌握的内容
1.函数的条用
2.循环的熟练使用
3.在一个工程里面创建头文件和源文件配合使用
逻辑分析
大家都知道扫雷游戏是在n*n的格子里面随机放置几个雷,然后玩家每点击一个不是雷的格子,就会显示这个方格周围的8个方格里面有多少个雷,再根据其它方格现实的信息,将可能是雷的方格暂且排除,点击没有雷的方格获取更多的信息,最后玩家通过游戏所给的信息将这些雷全部排出来即可游戏通关。
知道了游戏基本逻辑,我们就可以编写代码了,在编写代码时我提前思考了几个问题,以便在编程时遇到问题没有思路。接下来将逐一介绍这几个问题。
注:以下编程内容皆是在vs编译器里面进行,游戏的生成也是在vs运行的面板里,不是生成一个专门的游戏界面。
1.确定方格的数量以及通过什么方式来表示扫雷的棋盘
在基础版里的扫雷游戏都是99的,雷的数量是10个,所以我在编程时也选用了这组数据。
那么用什么方式来表示这个棋盘呢,我想到了二维数组,定义一个数组的大小为99,但出于一些原因呢,我选择定义了一个1111大小的数组,但是雷的位置控制在了中间的99位置范围内。原因我会在后面的内容里解释清楚。
2.怎么在这个9*9的棋盘里面随机布置10个雷
srand((unsigned int)time(NULL));//使种子随时间的变化而变化
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = 0;
for (count = easy; count > 0; )
{
int i = rand() % 9 + 1;//随机生成横坐标
int j = rand() % 9 + 1;//随机生成列坐标
if (mine[i][j] != '1')//如果该位置上没有雷,我们就给他放一个雷
{
mine[i][j] = '1';
count--;
}
}
}
rand函数:根据种子随机生成一个数字,不过是伪随机数。(因篇幅太长不做过多介绍,可以自己了解一下)
time函数:返回一个值,即格林尼治时间1970年1月1日00:00:00到当前时刻的时长,时长单位是秒。它是时刻变化的。
以上是随机布置10个雷的代码段,我用了rand()%9+1来随机生成1——9的数字,rand函数的种子默认是1,如果我们不改变它的种子,他每次生成的数字都是一样的值,所以rand生成的是一个伪随机数,我们每次运行这个程序,它生成的每次的数值都是一样的,所以我们要让他的种子时刻发生变化,我能想到的就只有时间了,所以我调用了time函数,通过srand和time的联合使用使rand的种子时刻发生变化。
到这里我们随机生成坐标的目的就达成了,我们只用在这些坐标的位置上放雷就好了。
3.怎么检测没有放雷的位置周围有几个雷
这里假设坐标为x,y 的坐标没有雷,那么把周围有雷的格子数加起来显示在这个格子就好了。在这里要注意的一个问题是:在99的期盼边缘的方格怎么统计周围的8个方格内雷的数量。
如图:在99的期盼里面边缘的方格周围只有5或者3个,没办法统计数量,即使要统计数量也要分好多种情况,太麻烦了。
所以我在前面说我创建的时1111的棋盘大小,这样就保证每个99每个方格周围都有8个方格了。
注:这里小编为什么要-8 * '0’呢,因为我的两个数组都是字符型。字符型的0换成整形时是48,那把周围的所有字符加起来剪8个字符0就是雷的数量。然后在存入第二个数组时加上一个字符0就是雷的数量对应的字符了。
int count(char mine[ROW][COLS], int x, int y)
{
return mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] +
mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0';
}
上面就是收集周围八个方格里面的雷的数量的代码了。
到这里我在编程前思考的几个问题就结束了,接下来看一下编程的步骤吧。
对一些参数的定义
在编码时我将棋盘的规模大小与放置雷的数量在一个头文件内进行了定义
//原始棋盘大小
#define ROW 9
#define COL 9
//加上扩展期盼后的大小
#define ROWS ROW+2
#define COLS COL+2
//游戏模式
#define easy 10//简单模式:10个雷
#define difficult 30//困难模式
输出游戏开始界面(游戏的逻辑构成)
1.运行代码,选择时开始还是退出;
2.输入1,开始游戏;
输入0,退出游戏;
不小心输入其他的数字就需要重新输入。
void begain()//游戏开始界面
{
printf("*****扫雷 游戏*****\n");
printf("*****开始请按1*****\n");
printf("*****退出请按0*****\n");
}
void game()//游戏运行阶段
{
char mine[ROWS][COLS] = { 0 };//存贮雷的位置
char show[ROWS][COLS] = { 0 };//储存给玩家显示的九宫格内雷的数量
//给两个数组进行初始化操作
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//打印两个数组
//prepare(mine, ROW, COL);
//prepare(show, ROW, COL);
//给第一个数组埋雷
SetMine(mine,ROW,COL);
//prepare(mine, ROW, COL);
//开始游戏
star(mine, show, ROW, COL);
}
int main()//游戏逻辑构成
{
int input = 0;
srand((unsigned int)time(NULL));//为time设置可以随机变换的种子
do
{
begain();
scanf("%d", &input);
switch (input)
{
case 0:
printf("退出游戏\n");
break;
case 1:
printf("游戏开始\n");
game();
break;
default:
printf("输入错误,请重新输入\n");
}
} while (input);
return 0;
}
给两个数组进行初始化操作
对整个棋盘进行初始化。先全部放置字符0
void InitBoard(char board[ROWS][COLS], int row, int col, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)//按行循环
{
for (j = 0; j < col; j++)//按列循环初始化
{
board[i][j] = set;
}
}
}
打印数组的信息
void prepare(char pre[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i <= row; i++)//打印纵坐标
{
printf("%d ",i);
}
printf("\n");
for (i = 1; i <= row; i++)//按行循环
{
printf("%d ", i);//每行开始打印时先打印横坐标
for (j = 1; j <= col; j++)
{
printf("%c ", pre[i][j]);
}
printf("\n");
}
}
开始在9*9的棋盘里面埋雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = 0;
for (count = easy; count > 0; )
{
int i = rand() % 9 + 1;
int j = rand() % 9 + 1;
if (mine[i][j] != '1')
{
mine[i][j] = '1';
count--;
}
}
}
将每个雷控制在横坐标和纵坐标都小于9并且大于0的坐标之内,这样就可以将雷的位置全部放在9*9的棋盘上而不放置到扩展区域中。
开始游戏
游戏运行过程的代码段如下,在一些地方加入了注释便于理解。
int count(char mine[ROW][COLS], int x, int y)//统计数量
{
return mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] +
mine[x][y + 1] + mine[x - 1][y + 1] - 8 * '0';
}
void star(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
// prepare(mine, row, col);
while (win < row * col - easy)//循环进行扫雷,row * col - easy是安全位置的数量
{
printf("请输入坐标值:例(4 5)\n");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= row)//输入的坐标要在9*9的范围里面
{
if (mine[x][y] == '1')
{
printf("你踩到雷了,game over\n");
prepare(mine, row, col);
break;
}
else
{
win++;
printf("恭喜你没踩到雷,还需排查%d个位置\n", row * col - easy - win);//row * col - easy - win是剩余的安全位置的数量
int c = count(mine, x, y);
show[x][y] = c + '0';
prepare(show, row, col);
}
}
else
{
printf("输入错误,请重新输入\n");
}
if (win == row*col-easy)
{
printf("恭喜你,游戏胜利\n");
prepare(mine, row, col);
}
}
}
好了,入门版的扫雷游戏就编好了,有兴趣的可以去试试吧。
------------------------------------------分隔符
扫雷游戏扩展:
1.怎么直接排除周围没有雷的格子;点击这个格子时直接展开一片。
2.怎么保证第一次的坐标一定不是雷。
3.怎么标记一个位置是雷。
4.怎么显示本次游戏一共用了多长时间。
下面这个是我的gitee链接,我将扫雷代码存到这里面了,可以去看看:
https://gitee.com/xiao-li-learning-c-language/projects
这些都是小编的个人理解,有错请指正,一起进步。