目录:
一、test.c文件的实现
1.1 游戏的大体思路
1.2 代码
二、game.c文件的实现
2.1 初始化棋盘
2.2 打印棋盘
2.3 布置雷
2.4 排查雷
2.4.1 排查雷的思路
2.4.2 格子大规模式展开(递归)
三、总结
前言:
本期主要讲解的是网络上的一个小游戏——扫雷,游戏的代码我分为了三个文件去写,1.test.c是用实验游戏的大体思路,2.game.c是用来实现扫雷游戏中具体的各个功能,3.game.h是用来包含头文件和一些宏定义。
《扫雷》是一款大众类的益智小游戏,于1992年发行。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。如图:
![](https://i-blog.csdnimg.cn/blog_migrate/de67fca206b3f440970d9b85e53abde6.png)
一、test文件的实现
1.1
首先我们要去设置一个菜单去提示玩家如何开始游戏,并且为了玩完一局之后能让玩家选择是否继续游戏,我们可以采用do while结构
1.2
上代码
#include"game.h"
void menu()
{
printf("*************************\n");
printf("********* 0.exit ********\n");
printf("********* 1.play ********\n");
printf("*************************\n");
}
void play()
{
char mine[ROWS][COLS] = { 0 }; //存放雷的信息
char show[ROWS][COLS] = { 0 }; //显示棋盘
//初始化棋盘
InitBoard(mine,ROWS,COLS,'0');
InitBoard(show,ROWS,COLS,'*');
//打印棋盘
//Displayboard(mine, ROW, COL);
Displayboard(show, ROW, COL);
//布置雷
Setmine(mine, ROW, COL);
//Displayboard(mine, ROW, COL); //为了展示你在那些地方放置了雷
//排查雷
Sreachmine(mine,show, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
printf("请输入你的选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
play();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
}
} while (input);
return 0;
}
二、game.c文件的实现
2.1
首先为了实现扫雷游戏,我们采用的是二维数组的结构进行存储的。我们可以设置两个棋盘,一个存储布置雷的信息(mine),起初先将mine全部初始化为字符'0',我们可以用字符'0'表示安全,用字符'1'来表示有雷,另一个全部初始化为" * "来供玩家猜想(show)。大题上的思路就是在mine数组上排查雷,在对应位置的show数组上进行显示,话不多说上代码:
void InitBoard(char board[ROWS][COLS], int row, int col,char ret)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ret;
}
}
}
2.2
初始化完之后我们可以设计打印棋盘的函数,来进行观看我们初始化完的结果,并且每次游戏也是需要通过观看棋局来进行下一步操作的。
void Displayboard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("**********扫雷游戏**********\n");
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
2.3
在布置雷方面我们可以通过rand()函数产生的随机数来决定雷放到哪里,这样每一局游戏雷的位置都不会相同,具体rand()函数的原理我的上一篇文章三子棋中有讲到,有不懂的可以去 三子棋这里面进行观看,我们将产生的随机数进行%ROW (此处ROW为9) 运算,就会得到随机的0~8的数字,在加上1就可以得到1~9的数字了,刚好是我们9*9棋盘的范围之内。
void Setmine(char board[ROWS][COLS], int row, int col)
{
int count = 0;
while (count < Difficulty) // 布置十个雷
{
int x = rand() % ROW + 1; // 生成 1~9 的随机数
int y = rand() % COL + 1;
if (board[x][y] == '0') // 判断是否已经布置过雷
{
board[x][y] = '1';
count++;
}
}
}
2.4排查雷
2.4.1
排查雷的时候只需要依次遍历你所选择格子周围的八个格子,统计出它们中字符'1'的个数,这就是这个格子周围雷的数量,最终我们再将这个数字加上我们的字符'0',将这个整型数字转化为字符数字,再存储到我们你所选择的格子上就可以了,也就是show数组上。但是我们却会发现:
![](https://i-blog.csdnimg.cn/blog_migrate/0d2ec93a0b10d04160284c7b1e2a0d48.png)
如果是这个格子,要在它周围进行检查的时候就会检查到棋盘外面,会发生越界访问进而报错,那么这该如何解决呢?其实很简单,我们只需要在定义数组的时候多定义两行两列就可以了,如图:
![](https://i-blog.csdnimg.cn/blog_migrate/c0d30688f2f35cc96d96c9986432b4ab.png)
小的方框是我们扫雷使用的,大的方框就是用来保护我们不会产生数组越界的,也就是说我们在起初定义时要多定义两行两列,但是我们只是用中间的部分,最外围的空间用来保护我们在排查雷时不会发生数组越界的情况。这样在小方框最外围的格子在排查雷的时候可以安安稳稳的进行。
2.4.2
那么最难的部分来了,我们该如何实现大规模展开呢?
这里我们采用递归的方法来解决,大家请看,如果我们选的格子(x,y)不是雷,那么就依次对它周围八个格子进行同样的查雷操作,因此我们需要写出一个查雷函数(Findmine),在对我们选择的格子进行查完之后,再依次对它周围的格子进行查雷。那么问题又来了,大家看图:
![](https://i-blog.csdnimg.cn/blog_migrate/fdd826fbadefd6936ef5c8103a1ee024.png)
假如我们第一次选择的是(x,y),那么在轮到(x+1,y+1)的时候,对它周围再次循环调用,又会调用到(x,y),如此反复,就形成了死循环,程序就会跑死,但是我们在mine数组(布置雷的棋盘)上只能做这么多了,但是大家不要忘了,我们还有一个show数组(我们猜想用的棋盘),如果我们在递归的时候加上某些限定条件是不是就解决了呢?(在此之前我们要知道的是,在查完一个格子时我们就会将这个格子的字符'*'置为空白。)那我们就在每次递归前都判断一下在对应show棋盘上的格子是不是空白的,如果是空白的说明已经排查过雷了,那么就不再进行递归了,如此我们是不是就完美的解决了这个死循环的问题呢。同时还有一个条件,那就是如果这个格子放置的是雷,那么我们也不进行递归,不然如果是雷还递归的话那岂不是直接就遇见雷了?话不多说直接上代码:
int win = 0; //用全局变量来记录已经找到了多少个没有雷的格子
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y) 递归函数
{
int i = 0;
int j = 0;
int count = 0; // 统计周围有几个雷
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (mine[i][j] == '1')
{
count++;
}
}
}
if (count == 0)
{
show[x][y] = ' ';
win++; //找到一个没有雷的win就++
}
else
{
show[x][y] = count + '0';
win++; //这个也是本身并不是雷,所有win也要++
}
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (mine[i][j] != '1' && show[i][j] == '*' && i > 0 && i <= ROW && j > 0 && j <= COL) //条件:首先不是雷 && 展示的棋盘上要为‘*’,说明没有被排查过,同时i和j不能越界
{
Findmine(mine, show, i, j);
}
}
}
}
void Sreachmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) //排查雷
{
int x = 0;
int y = 0;
while (win<ROW*COL-Difficulty)
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
if (x > 0 && x <= ROW && y > 0 && y <= COL)
{
if (mine[x][y] == '0')
{
Findmine(mine, show, x, y);
Displayboard(show, ROW, COL);
}
else
{
printf("扫到雷了,游戏结束\n");
Displayboard(show, ROW, COL);
}
}
else
{
printf("输入错误,请重新输入\n");
}
}
printf("恭喜你,游戏胜利!!\n");
}
三、总结
好耶!!又完成了一篇文章,希望能对大家有所帮助,如果觉得不错👍,可以给博主一个免费的一键三连,点赞、收藏+关注,谢谢大家啦!!
如果有什么疑问或者发现了文章中的问题,可以在评论区留言喔。最后再为大家奉上全部代码👍,我会按照test.c、game.h、game.c的顺序进行上传。
test.c文件
#include"game.h"
void menu()
{
printf("*************************\n");
printf("********* 0.exit ********\n");
printf("********* 1.play ********\n");
printf("*************************\n");
}
void play()
{
char mine[ROWS][COLS] = { 0 }; //存放雷的信息
char show[ROWS][COLS] = { 0 }; //显示棋盘
//初始化棋盘
InitBoard(mine,ROWS,COLS,'0');
InitBoard(show,ROWS,COLS,'*');
//打印棋盘
//Displayboard(mine, ROW, COL);
Displayboard(show, ROW, COL);
//布置雷
Setmine(mine, ROW, COL);
Displayboard(mine, ROW, COL);
//排查雷
Sreachmine(mine,show, ROW, COL);
}
int main()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
printf("请输入你的选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
play();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
}
} while (input);
return 0;
}
game.h文件
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define Difficulty 10
//初始化棋盘
void InitBoard(char board[ROWS][COLS],int row,int col,char ret);
//打印棋盘
void Displayboard(char board[ROWS][COLS], int row, int col);
//布置雷
void Setmine(char baord[ROWS][COLS], int row, int col);
//排查雷
void Sreachmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
game.c文件
#include"game.h"
void InitBoard(char board[ROWS][COLS], int row, int col,char ret)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = ret;
}
}
}
void Displayboard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("**********扫雷游戏**********\n");
for (i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
void Setmine(char board[ROWS][COLS], int row, int col)
{
int count = 0;
while (count < Difficulty) // 布置十个雷
{
int x = rand() % ROW + 1; // 生成 1~9 的随机数
int y = rand() % COL + 1;
if (board[x][y] == '0') // 判断是否已经布置过雷
{
board[x][y] = '1';
count++;
}
}
}
int win = 0;
void Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
int count = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (mine[i][j] == '1')
{
count++;
}
}
}
if (count == 0)
{
show[x][y] = ' ';
win++;
}
else
{
show[x][y] = count + '0';
win++;
}
if (show[x][y] == ' ')
{
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
if (mine[i][j] != '1' && show[i][j] == '*' && i > 0 && i <= ROW && j > 0 && j <= COL)
{
Findmine(mine, show, i, j);
}
}
}
}
}
void Sreachmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (win<ROW*COL-Difficulty)
{
printf("请输入坐标:>");
scanf("%d %d", &x, &y);
if (x > 0 && x <= ROW && y > 0 && y <= COL)
{
if (mine[x][y] == '0')
{
Findmine(mine, show, x, y);
Displayboard(show, ROW, COL);
}
else
{
printf("扫到雷了,游戏结束\n");
Displayboard(show, ROW, COL);
}
}
else
{
printf("输入错误,请重新输入\n");
}
}
printf("恭喜你,游戏胜利!!\n");
}