前言
本篇文章使用C语言实现简单小游戏---扫雷。(文章最后有完整代码链接)
想必大多数人都玩过或者了解过扫雷的游戏规则,但是在这里,我们在一起重温一下扫雷的游戏规则,也更好的让我们了解程序的实现目的。
扫雷:扫雷就是要把所有非地雷的格子揭开即胜利;踩到地雷格子就算失败。游戏主区域由很多个方格组成。使用鼠标左键随机点击一个方格,方格即被打开并显示出方格中的数字;方格中数字则表示其周围的8个方格隐藏了几颗雷。
在了解游戏规则后,我们就用C语言来实现这个简单小游戏。
这是我们解决资源管理器内所创建的文件,下来我们就进入代码内部。
一,游戏使用到的头文件和游戏声明
1.头文件的包含
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
2.符号的声明
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10
3.函数的声明
初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
打印棋盘
void DisplayBoard(char board[ROWS][COLS],int row, int col);
布置雷
void SetMine(char mine[ROWS][COLS], int row, int col);
排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row,int col);
二,游戏测试
1. 主函数
和三子棋一样,主函数仍然简单,函数内部调用test()测试函数。
int main()
{
?? ?test();
?? ?return 0;
}
2.test()函数
void test()
{
int input = 0;
srand((unsigned int )time(NULL));
do
{
menu();// 游戏菜单
printf("请选择>");
scanf("%d", &input);
switch (input)
{
case 1:
// 扫雷游戏
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
}
解析test()函数内部:
A.?srand((unsigned int )time(NULL))??
利用时间戳,形成随机数,主要目的是实现游戏中地雷的随机埋放。
B.menu() 游戏菜单
void menu()// 游戏菜单
{
printf("********************\n");
printf("**** 1. play *****\n");
printf("**** 0. exit *****\n");
printf("********************\n");
}
创建菜单,实现效果演示:
?C:switch case 选择语句:
使用此语句实现玩家的自主选择,当输入1时进行扫雷游戏,当输入0时,退出游戏。显示效果如下:
? ? ?
3.game()函数
game()函数是主要游戏实现函数,以下是game函数主要是实现逻辑图和实现顺序
?这里先让大家看一下完整代码,实现顺序如上所示:
下面主要进入我们的游戏实现逻辑当中......划重点
三,游戏实现
1.创建棋盘
// 创建数组
// 创建雷的数组(mine) 显示的数组(show) 两个数组一样规模 一样类型
char mine[ROWS][COLS] = { 0 }; // 存放布置好的雷的信息
char show[ROWS][COLS] = { 0 }; // 存放排查出的雷的信息
在这里我们需要创建2个相同大小,相同类型的二维数组。
问:我们为什么要创建2个相同的数组呢? 答:是因为我们在玩扫雷的时候我们首先看到的是一个未知得棋盘,不知道哪里埋放着雷,如果我们触碰到雷结束游戏后,我们需要给玩家呈现这局游戏所有点位的情况,这样以便于玩家清楚所有雷都在那里,为什么死,因此我们需要两个棋盘才能完成这项任务。
在这里我们创建了11X11大小的棋盘,但是只显示9X9大小的棋盘也就是81个格子,打算埋放10颗雷,玩家可以在头文件自行更改雷的个数。
问:我们显示9X9大小的棋盘,为什么创建11X11大小的棋盘?
答:这是因为扫雷游戏规则中,我们在排雷的过程中,会显示周围8个格子的雷的个数,如果创建一个9X9的格子,那当我们在边角的时候,我们周围的格子不够8个,但是我们还要访问周围8个格子,这时候我们必然会造成数组越界问题,具体情况如下图所示,这时候我们如果创建11X11的棋盘,对11X11的棋盘都初始化为字符' 0 ', 我们只显示内部的9X9的格子,我们依然不会影响游戏,并且也解决了数组越界的问题。
如图所示,我们假设要查找坐标为(9,9)格子周围8个格子的雷的个数,如果我们还只是9X9的格子,我们图中的红色阴影区域就处于数组的范围之外,因为我们需要创建11X11的棋盘,就可以轻松化解这个问题。
2.初始化棋盘
// 初始化mine数组为全'0'
InitBoard(mine,ROWS,COLS,'0');//初始化--->棋盘函数
// 初始化show数组为全'*'
InitBoard(show,ROWS,COLS,'*');//初始化--->棋盘函数
和三子棋一样,我们依然自定义函数InitBoard(),具体代码如下:
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0, j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
在这里我们对第一块mine棋盘全部初始化为字符 ' 0 '?(注意这里是字符0,不是数字0),我们对第二块show棋盘全部初始化为字符' * ' ,最终玩家首先会看到一幅全是字符' * '的棋盘,这样也符合游戏规则。
3.打印棋盘
自定义函数DisplayBoard(),具体代码如下:
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0, j = 0;
//列号的打印
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");
}
}
这样通过调用DisplayBoard()函数我们可以检查一下,我们刚才所创建和初始化的棋盘是否符合我们的要求,我们打印棋盘演示如下:
?我们发现,打印出来的棋盘也完全符合我们的要求,我们也只需要最终将show棋盘显示给玩家即可。
4.布置雷
在这里我们自定义函数SetMine()函数,具体代码如下:
// 布置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = EASY_COUNT;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
我们对此代码进行分析:
A:如何实现随机布雷?
答:我们创建rand函数,利用时间戳生成随机数,因为我们所创建的棋盘大小是9X9大小,我们我们只需要给生成的随机数模上row(col)即可得到0~row-1 ,因此我们再 +1?即可得到一个在0~row(col)的随机数,从而实现随机布雷。
我们设定count个雷,如果我们埋下一颗雷,我们就count-1,同时我们将初始化的字符' 0 ' 变成字符' 1 ' , 只有我们识别到目标格子是字符 ' 0 ' 时才会埋雷,这也解决了在同一位置重复埋雷的问题。?
假设我们随机埋下10颗雷,演示一下棋盘:
?我们发现,我们随机埋下了10颗雷,并且也改成了字符' 1 '
5.排雷
自定义函数FindMine(),具体代码如下所示:
// 排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0, y = 0;
int win = 0;
while (win<ROW*COL- EASY_COUNT) {
printf("请输入要排查的坐标:>");
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾你被炸死了\n");
DisplayBoard(mine, row, col);
break;
}
else
{
//计算x,y坐标周围有几个雷
int n = get_mine_count(mine,x,y);
show[x][y] = n+'0'; // 数字+'0'可以转换成对应的ASCII
DisplayBoard(show, row, col);
win++;
}
}
else
{
printf("输入坐标非法,无法排雷,请重新输入\n");
}
}
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功\n");
DisplayBoard(mine, row,col);
}
}
其中调用了get_mine_count函数,其代码如下:
static int get_mine_count(char mine[ROWS][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';
}
接下来对这两段代码进行分析:
当我们输入需要排查的坐标的时候,我们所输入的坐标也必须合法,必须在1~row(col)之间的数字,如果在此之外,我们将会提醒玩家“输入坐标非法,无法排雷,请重新输入”字样。
当我们输入正确的坐标时候,我们需要对这个坐标下所对应的字符进行判断,如果是字符' 1 ' ,说明踩中雷,说明游戏结束,这时候我们将完整棋盘打印出来,玩家也可以了解本局游戏的情况。
如果是字符 ' 0 ' ,说明玩家没有踩中雷,根据游戏规,我们需要显示这个格子周围8个格子中存在雷的个数,这时候我们调用了get_mine_count函数,我们先看一下统计周围雷的个数的代码:
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';
我们发现,此代码将周围8个格子的字符全部加了起来,我们知道如果是字符 ' 0 ' 和字符 ' 1 '所对应的ASCII码值相差1,我们可以通过ASCII码值来进行判断,我们将周围8个字符相加,然后再减去8个字符' 0 ' 的和,这样,我们就可以得到周围8个格子中有多少个字符 ' 1 ' 的格子,也就是雷的个数。
为了方便大家理解,我们假定有10个雷,我们将Mine棋盘和show棋盘都显示,我们根据棋盘制定输入,看是否可以得到我们想要的结果:
如果我们排除万难,最终将81个格子排完,我们将会获得胜利,具体判断代码如下:
if (win == row * col - EASY_COUNT)
{
printf("恭喜你,排雷成功\n");
DisplayBoard(mine, row,col);
}
我们这里假定有80个雷,我们显示一下:
?至此,我们扫雷小游戏也写完了,完整代码我也放在我的Gitee仓库,链接如下:
C语言: C语言代码学习-练习 - Gitee.com
其中这3个对应资源管理器3个文件
总结
本节内容主要用C语言实现了小游戏---> 扫雷,大家可以拷贝到编译器里面玩一玩,如果大家觉得还不错有收获的话,点赞收藏走一波呗~?
由于我的个人技术水平有限,各位大佬发现错误及时指出哦~
这里是 用C语言实现《三子棋?》 小游戏的链接,大家有兴趣也可以看看哦:
[ C语言 ] 用C语言实现小游戏 ---- 三子棋 代码 + 解析_小白又菜的博客-CSDN博客
|