各位少年,大家好,我是博主那一脸阳光
,我们学会了数组,exturn声明外部文件,static修饰静态变量,那么很显然,我们需要用到我们学习这些,实现一个扫雷游戏。
扫雷游戏介绍以及玩法
在地图上布置十个雷
排查雷:如果位置是雷,就炸死了,游戏结束,不是雷,就统计 :就统计周围雷的个数,并显示。
这个1表示周围格子有雷,如果把所有非雷的位置找到,游戏结束,如果碰到雷游戏结束。
如何实现9*9扫雷
数据的分析和设计
数据结构的分析
扫雷过程中,布置雷和排查除的雷信息都需要存储,所以我们需要一定的数据结构来存储这些信息,因为我们需要在99的棋盘上布置的信息和排查雷,我们首先想到的就是创建一个99的数组来存放信息。
那如果这个位置布置雷,我们就存放1,没有布置雷就存放0.
那问题出来了,如果说我们要访问第0行第0列,我们知道一个方块等排查旁边一个元素,那么很显然,这里出现了越界。所以说要创建11杠11的模块来实现。
多文件的使用
我们在写一性大型游戏的时候,往往需要N多行代码,比如说这个比较小众的扫雷,就需要两百块C语言代码,我们才能达到最总目的
我们主要分为这三个模块性,来帮我们实现扫雷这个游戏的代码,我们会使用到自定义头文件,使用多文件会让我们代码更加方面简介,易修改。
我们创建了三个文件,然后我们来编写程序即可,test c我们放代码的文本,自定义函数在game。c中使用,然后放到test。c
game。h放头文件以及define定义,总之game点h是来测试游戏的。
#include<stdio.h>
void menu()
{
printf("*******************\n");
printf("****** 1.play ********\n");
printf("****** 0.exit *********\n");
printf("*******************\n");
}
int main()
{
int input = 0;
do
{
menu();
priintf("请输入:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("扫雷\n");
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择!\n");
break;
}
} while (input);
return 0;
}
do while:如果我们打开这个游戏,肯定要玩一次,先玩一次,玩完以后再判断do-while玩不玩下次的问题(1)代表false玩,0代表true假不玩,我们这里不需要多判断用switch语句是最好的。
好,我们把扫雷换成自定义函数game(),来实现这个游戏
#include<stdio.h>
void menu()
{
printf("*******************\n");
printf("****** 1.play ********\n");
printf("****** 0.exit *********\n");
printf("*******************\n");
}
int main()
{
int input = 0;
do
{
menu();
priintf("请输入:");
scanf("%d", &input);
switch (input)
{
case 1:
void game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,重新选择!\n");
break;
}
} while (input);
return 0;
}
假设我们排查(2,5)这个坐标时,我们访问周围的⼀圈8个⻩⾊位置,统计周围雷的个数是1
假设我们排查(8,6)这个坐标时,我们访问周围的⼀圈8个⻩⾊位置,统计周围雷的个数时,最下⾯的三
个坐标就会越界,为了防⽌越界,我们在设计的时候,给数组扩⼤⼀圈,雷还是布置在中间的99的坐
标上,周围⼀圈不去布置雷就⾏,这样就解决了越界的问题。所以我们将存放数据的数组创建成1111
是⽐较合适。
排雷的假设 周围加上⼀圈的棋盘比较合适
我们多谢几行的方面性比写代码限制效率高,存放数据的类型太多,容易产生歧义,在统计一个坐标周围的雷的个数的时候,可能会越界。
雷的隐秘,没初始化。
我们在棋盘上布置了雷,棋盘上雷的信息(1)和⾮雷的信息(0),假设我们排查了某
⼀个位置后,这个坐标处不是雷,这个坐标的周围有1个雷,那我们需要将排查出的雷的数量信息记录
存储,并打印出来,作为排雷的重要参考信息的。那这个雷的个数信息存放在哪⾥呢?如果存放在布
置雷的数组中,这样雷的信息和雷的个数信息就可能或产⽣混淆和打印上的困难。
这⾥我们肯定有办法解决,⽐如:雷和⾮雷的信息不要使⽤数字,使⽤某些字符就⾏,这样就避免冲
突了,但是这样做棋盘上有雷和⾮雷的信息,还有排查出的雷的个数信息,就⽐较混杂,不够⽅便。
这⾥我们采⽤另外⼀种⽅案,我们专⻔给⼀个棋盘(对应⼀个数组mine)存放布置好的雷的信息,再
给另外⼀个棋盘(对应另外⼀个数组show)存放排查出的雷的信息。这样就互不⼲扰了,把雷布置到
mine数组,在mine数组中排查雷,排查出的数据存放在show数组,并且打印show数组的信息给后期
排查参考。
同时为了保持神秘,show数组开始时初始化为字符 ‘*’,为了保持两个数组的类型⼀致,可以使⽤同⼀
套函数处理,mine数组最开始也初始化为字符’0’,布置雷改成’1’。如下
char mine[11] [11]//雷用1表示非雷0表示
```c
char show[11] [11]//雷用1表示非雷0表示
```对应的数组应该是:
char mine[11][11] = {0};//⽤来存放布置好的雷的信息
char show[11][11] = {0};//⽤来存放排查出的雷的个数信息
大家想象,如果想玩大点的扫雷游戏,不要九乘九了,要20*20是不是要更改的很麻烦
所以说这里来头文件定义两个宏
game.H
#define ROW 9
#define COL 9//后面会使用
#define ROWS ROW+2
#define COLS 11
void game()
{
char mine[ROWS][COLS]
char show[ROWS][COLS]
}
这样方便了不少,我们修改上面的宏改变整个数组的大小,改变扫雷游戏的大小。
初始化棋盘
我们写一个自定义函数 来把这个扫雷的棋盘初始化,写出来,我们自定义一个InitBoard函数,给他传三个参数
InitBoard(mine,ROWS,COLS);
你要初始化布置雷的数组你也要初始化,隐藏数组show
InitBoard(show,ROWS,COLS);
game.h这里需要放一个函数的声明,如果以后后期改游戏配置会在game头文件中更改
void InitBoard(char arr[ROWS][COLS], int rows, int cols);
然后我们在game .c里实现
#include"game.h"
void InitBoard(char arr[ROWS][COLS], int rows, int cols)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
arr[i][j] = 0;
}
}
}
那问题出来了,我们得初始化o,那么*就是遮住棋盘的符号怎么打印呢?再写一个,是可以的,但是有点挫,那我们如何用一个game点c的模板,完成两个数组的初始化呢?那我们再传入一个参数**或者0传进去
test。c
void game()
{
char mine[ROWS][COLS];
char show[ROWS][COLS];
InitBoard(mine, ROWS, COLS,'0');
InitBoard(show, ROWS, COLS,'*');
}
game .c
#include"game.h"
void InitBoard(char arr[ROWS][COLS], int rows, int cols,char set)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
arr[i][j] = set;
}
}
}
.game.h
```c
void InitBoard(char arr[ROWS][COLS], int rows, int cols,char set);
我们实现了扫雷棋盘的打印,通过循环的方式把扫雷的基本模型实现了,但是我们得打印出来呀?判断输赢呀?
由于篇幅的问题,我们只能下篇分析介绍