Hello,大家好ฅ˙Ⱉ˙ฅ!今天小狮子我又为大家带来了一篇关于我最近的C语言阶段性学习成果,即和上一篇博客中的傻瓜三子棋并称C语言两大守门将(我自己取的,嘻嘻)的扫雷实现的文章。
目录
一、数组的创建与初始化
二、打印扫雷盘及展示扫雷盘状态
三、设置炸弹坐标
四、最后的玩家交互与判断胜负
在扫雷游戏实现之前,我们要了解扫雷的基本逻辑:
1.扫雷开始时,玩家输入可以选择要排查的位置
2.如果当玩家找到一个未埋雷区,它会显示周围一圈的总共雷数
3.如果玩家成功找出隐藏的炸弹并排除所有未埋雷区则游戏胜利
4.而如果玩家在排雷时遇到炸弹游戏就会失败
了解完大致思路和逻辑之后,我来介绍我写的简单版扫雷的代码实现:
首先我准备了三个文件,分别是头文件game.h、源文件game.c和text.c文件。game.h用于引用各种头文件和定义常量,text.c文件用于实现游戏的初始化,game.c用于存放整个游戏环节中的各种具体实现的函数们。
这是我的text.h的具体代码,大家在练习时可以参考一下❛‿˂̵✧:
#include "game.h"
void menu()
{
printf("*** *扫雷* ***\n");
printf("*** 1.play ***\n");
printf("*** 0.exit ***\n");
printf("***************************\n\n");
}
void play()
{
printf("\n扫雷游戏说明:玩家输入排查坐标\n");
printf("如果玩家在排雷时踩到炸弹游戏就会失败\n");
printf("如果玩家成功找出隐藏的炸弹并排除所有未埋雷区则游戏胜利\n");
printf("且如果当玩家找到一个未埋雷区,它会显示周围一圈的总共雷数\n");
printf("管你听没听懂,玩就完了!\n\n");
}
void game()
{
//1、建立两个扫雷盘 一个用于处理数据 一个用于展示
char hid[Hs][Ls];
char show[Hs][Ls];
//2、初始化两个扫雷盘
chushi(hid, Hs, Ls, '0');
chushi(show, Hs, Ls, '*');
//3、设计可以打印出扫雷盘的函数
display(show, H, L);
//下面这个调用可以帮我们观察内盘 以便于我们后续的调试
/*display(hid, H, L);*/
//4、将炸弹放置进扫雷盘,我们只需要对内盘进行此操作
mine_set(hid, H, L);
display(hid, H, L);
//5、最后的扫雷玩家与电脑交互
sweep(hid, show, H, L);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
play();
do
{
menu();
printf("请选择是否开始游戏:>");
scanf("%d", &input);
switch(input)
{
case 1:
printf("开始游戏!\n\n");
game();
break;
case 0:
printf("退出游戏。\n");
break;
default:
printf("输入错误,请重新输入!\n");
break;
}
} while (input);
return 0;
}
这里我为玩家介绍了大致的游戏规则,放置了菜单和每次游戏的初始化,保证玩家能够想玩多少次就玩多少次,并且将game.c中设计的函数整合起来放在game()函数中。
接下来是头文件game.h的创建和使用,当我们自己创建头文件时,我们可以把函数的设计文件和主函数所在文件分开来,并利用函数的声明将这两者结合起来,利于更加有条理地完成整个程序的设计。除此之外,我们还可以将引用的C语言自带头文件和自定义常量放进我们的头文件中,这样我们就可以完成各文件的整合运用。
下面是我的game.h,我会在下面的设计中用到它(●'◡'●):
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#define H 9
#define L 9
#define Hs H+2
#define Ls L+2
#define M 10
void chushi(char board[Hs][Ls], int h, int l, char chu);
void display(char board[Hs][Ls], int h, int l);
void mine_set(char board[Hs][Ls], int h, int l);
void sweep(char hid[Hs][Ls], char show[Hs][Ls], int h, int l);
最后也是最重要的——我用于存放设计的各个游戏功能函数的源文件game.c,我将会详细介绍其中的各个函数。
玩家在扫雷时,我们需要将隐藏起来的雷区和展示给玩家的界面分开,那么我们在进行扫雷时就需要两个场景,一个用于存放坐标的埋雷状态并用于处理和运算,另一个用于展示给玩家看并给出游戏信息引导玩家进行游戏。因此我设置了两个11*11(防止访问数组元素时越界)的二维数组 hid 和 show 组成两个平面。并将它们初始化分别放入字符 ’0‘ 和 ’*‘。
这是我的初始化函数实现:
void chushi(char board[Hs][Ls], int h, int l, char chu)
{
int i = 0;
int j = 0;
for (i = 0; i < h; i++)
{
for (j = 0; j < l; j++)
{
board[i][j] = chu;
}
}
}
这里我们就一 一选中了两个数组中的元素并指定它们分别为 ’0‘ 和 ’*‘。
在玩家进行游戏时,他需要知道扫雷盘的实时状态以及自己扫雷的记录,那我们就需要设计一个打印的函数。后面可以知道,这个函数还有益于我们调试程序内容,即我们可以通过打印内盘的手段作弊得知炸弹的排布。
这是我的打印函数,经过之前的练习,我们已经可以很好的利用for循环和打印函数来完成这一动作:
void display(char board[Hs][Ls], int h, int l)
{
int i = 0;
int j = 0;
printf("--------------扫雷游戏--------------\n");
printf("温馨提示:下面自带的数字是坐标哦!\n");
printf("请以'*'、'#'和' '分别代表扫雷和标记以及取消标记。\n");
printf("\n");
for (i = 0; i < h + 1; i++)
{
printf(" %-3d", i);
for (j = 1; j < l + 1; j++)
{
if (i == 0)
{
printf("%-3d", j);
}
else
{
printf("%-3c", board[i][j]);
}
}
printf("\n\n");
}
printf("\n");
printf("--------------扫雷游戏--------------\n");
printf("\n");
在设置炸弹坐标时,我们很容易想到它的本质是为内盘元素进行随机坐标赋值,这时候我们就要用到srand()和rand()函数进行随机值的选定,并设置一个while循环以应放的地雷数为限制条件。
下面是我的代码实现:
void mine_set(char board[Hs][Ls], int h, int l)
{
int i = 0;
int j = 0;
int count = M;
while (count)
{
i = rand() % h + 1;
j = rand() % l + 1;
if (board[i][j] == '0')
{
board[i][j] = '1';
count--;
}
}
}
我们用经典的scanf()函数来实现玩家的输入与人机交互,并用多层if()分支结构并建立两个函数 sum() 和 rec() 完成数据处理和判断。其中对于字符的运算和扫雷扩散的递归是本次文章的重难点,要求我们灵活运用我们对于ASCII码值以及字符与整形之间的关系的理解。
void sweep(char hid[Hs][Ls],char show[Hs][Ls], int h, int l)
{
int i = 0;
int j = 0;
char bra = 0;//用于决定玩家的操作性质
int count = M;
int win = 0;
while (win < h * l - M)//保证玩家能够获胜
{
printf("请输入排查坐标:>\n");//提醒玩家按操作步骤进行
printf("行: ");
scanf("%d", &i);
printf("列: ");
scanf("%d", &j);
printf("请选择排雷或设置标记:");
getchar();
scanf("%c", &bra);
if (bra == '#')
{
show[i][j] = '#';
display(show, h, l);
continue;
}
else if (bra == '*')
{
if (show[i][j] != '*')
{
continue;
}
if (i >= 1 && i <= h && j >= 1 && j <= l)
{
if (hid[i][j] == '1')
{
printf("\a恭喜你!你踩到炸弹了!(喜)\n\n");
printf("下面是这次的炸弹排布,‘1’表示炸弹。\n");//失败后展示我们的内盘
display(hid, h, l);
break;
}
else
{
//计算周围一圈的炸弹数并在本坐标值为0时展开周围坐标,为玩家提供有效信息
rec(hid, show, i, j, &win);
display(show, h, l);
}
}
else
{
printf("输入的坐标错误,请重新输入!\n");
}
}
else if (bra == ' ')
{
show[i][j] = '*';
display(show, h, l);
continue;
}
else
{
printf("你干嘛~\n");
}
//判断胜利
if (win == h * l - M)
{
printf("玩家胜利!谢谢您的游玩!\n爱你哦!");
rew();//对胜利玩家的赞赏,可以自行设计
}
}
}
上面的代码中有函数 rec() 用于完成show数组元素的赋值和递归,这个函数中,我们需要再次判断坐标的合法性(因为递归时每一次也要继续放入一个坐标,为防止越界访问和死递归,我们需要用if语句来判断坐标的合法性),并不断为新的show数组元素赋值,然后累加变量win,判断获胜。
这是具体的 rec() 代码实现:
void rec(char hid[Hs][Ls], char show[Hs][Ls], int i, int j,int* win)
{
if (i >= 1 && i <= H && j >= 1 && j <= L && hid[i][j] != '1' && show[i][j] == '*')
{
show[i][j] = sum(hid, i, j);
*win += 1;
if (sum(hid, i, j) == '0')
{
rec(hid, show, i - 1, j - 1, win);
rec(hid, show, i - 1, j, win);
rec(hid, show, i - 1, j + 1, win);
rec(hid, show, i, j - 1, win);
rec(hid, show, i, j + 1, win);
rec(hid, show, i + 1, j - 1, win);
rec(hid, show, i + 1, j, win);
rec(hid, show, i + 1, j + 1, win);
}
else
return;
}
}
可以看到我在对所选坐标不是炸弹时的展示数字(即所选坐标周围一圈的炸弹数量)进行计算时又自定义了一个函数sum(),这个函数帮我们计算坐标周围的炸弹数量,只需要在game.c这个文件中使用,因此不需要在头文件中声明。
这是我的sum()函数实现:
1、直接计数
char sum(char hid[Hs][Ls], int x, int y)
{
int i = 0;
int j = 0;
int count = 0;
for (i = -1; i <= 1; i++)
{
for (j = -1; j <= 1; j++)
{
if(hid[x+i][y+j] == '1')
count++;
}
}
return count + '0';
}
2、间接计数
char sum(char hid[Hs][Ls], int x, int y)
{
int i = 0;
int j = 0;
int sum = 0;
for (i = -1; i <= 1; i++)
{
for (j = -1; j <= 1; j++)
{
sum += hid[x + i][y + j];
}
}
sum -= 8 * '0';
return sum;
}
到此为止我们就将整个扫雷的基本逻辑完成并放于game.c中,现在我们利用函数声明将它们都放在game()函数中:
void game()
{
//1、建立两个扫雷盘 一个用于处理数据 一个用于展示
char hid[Hs][Ls];
char show[Hs][Ls];
//2、初始化两个扫雷盘
chushi(hid, Hs, Ls, '0');
chushi(show, Hs, Ls, '*');
//3、设计可以打印出扫雷盘的函数
display(show, H, L);
//下面这个调用可以帮我们观察内盘 以便于我们后续的调试
/*display(hid, H, L);*/
//4、将炸弹放置进扫雷盘,我们只需要对内盘进行此操作
mine_set(hid, H, L);
/*display(hid, H, L);*/
//5、最后的扫雷玩家与电脑交互
sweep(hid, show, H, L);
}
扫雷的大概运行机制和玩法我们都已经完成实现,想必你也能够发现这样的扫雷还可以有很多的优化,接下来的代码优化小狮子在这里就交给你们了,希望大家在学习C语言的时候能够多多动手多多思考,这样才能天天进步哟❤️~
So much for this article!(放洋屁)非常感谢大家能够看到这里!
在这次的文章中我们一起学习了基础C语言的 简单扫雷实现 ,希望我们在今后的学习中能一如此刻般有同道中人相伴,每天都有每天的热情去进行学习,言尽于此,与大家共勉ฅ( ̳• · • ̳ฅ)!
最后,希望大家能像上次一样为我的文章打分,谢谢大家!◝(⑅•ᴗ•⑅)◜..°♡