前言
对于C语言的初学者,写个扫雷是有一定难度的。对于我而言也是有一定难度的,不过我应该能写出来。
准备工作
1.准备好要求用到的C语言知识,至少要掌握二维数组的知识。
2.准备好电脑,并搭建C语言开发环境。
编写代码开始
首先,引用好必要的头文件:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
然后,写主函数:
int main()//煮函数
{
srand(time(NULL));
return 0;
}
接着,我们再创建两个源文件:<你给文件的名字>.h和<你给文件的名字>.cpp。在解决方案管理器右击源文件即可找到创建方式。如果你的解决方案管理器不见了,就点这里:
接下来我们到新创建的.cpp文件里面写代码,我创建的文件名为game.cpp,考虑到这种东西需要一点界面提示,我们可以创建一个函数,用于打印游戏菜单,比如我的界面:
void Game_menu()//游戏菜单打印函数
{
printf("\n");
printf("------游戏菜单------\n");
printf("--------------------\n");
printf("--------1.玩--------\n");
printf("--------0.滚--------\n");
printf("--------------------\n");
printf("请选择你的选项:>");
}
光靠这个东西肯定还不够,我们再创建一个函数来调用这个函数,这样就可以让这个东西有点用了。下面给出我的调用方案:
int Game_opt()//游戏选项函数
{
int opt = 0;
do {
Game_menu();
scanf("%d", &opt);
switch (opt)
{
default:
{
printf("找茬是吧你!\n");
break;
}
case 0:
{
printf("给爷爬!\n");
break;
}
case 1:
{
printf("祝你游戏不愉快\n");
break;
}
}
} while (opt != 0);
return 0;
}
这样就通过后面这个函数来调用前面的函数了。此处涉及switch语句的知识,如果你还没学switch语句,你应该不会看这篇文章。这里注意了,如果你的被调用函数放在调用该函数的函数之后,你需要在这个函数前面声明被调用函数。~不然你的编译器会报错。接下来我们让主函数调用这个函数:
extern int Game_opt();//声明外部函数
int main()//煮函数
{
srand(time(NULL));
Game_opt();
return 0;
}
这个主函数从一开始就设置了随机值初始值,这对后面的随机生成很有用处。
接下来我们展示一下运行结果
运行结果符合预期。接下来我们可以用Game_opt()来调用游戏主函数,记位 int Game main(),接下来要思考了,界面做完了,要简单做一下扫雷还得创建一大堆变量吧,而且这些变量要调用非常头疼,这时候,我们就要用到二维数组了。
//1.创建棋盘
char minetable[Ys][Xs] = { '0' };//布雷数组
char blindtable[Ys][Xs] = { '0' };//玩家界面数组
扫雷是一个二维游戏,所以我们创建二维数组能很好对付。上面的代码表示我创建了两个大小一样的二维数组,往{}里面放‘0’是为了初始化数组,然而,这还不够。我们要创建一个函数来达到我们想要的初始化效果。对了,我们之前创建的头文件可以排上用场了。我在头文件里面定义了一些数据,方便日后修改:
X被我用作是列数,而Y被我用作行数,这个很重要,之前我把我的定义搞反了,导致函数有一部分的数组操作失败。
讲完这些,我们来讲如何用循环初始化我们创建的数组:
void initialization(char sz[Ys][Xs], char a)//数组初始化函数——把二维数组的每一行都填满 a 接受的参数。
{
for (int i = 0; i < Ys; i++)
{
for (int j = 0; j < Xs; j++)
{
sz[i][j] = a;
}
}
}
如何用这个函数自定义初始化我们创建的数组呢?用这个就行:
initialization(minetable, '0');
initialization(blindtable, WENHAO);
我们把布雷数组里面塞满'0',把玩家数组里面塞满我们定义的内容WENHAO,我们就可以得到这种东西
如果你想直接在终端上打印数组的内容,这里提供一个之后要使用的函数:
void show(char sz[Ys][Xs])//数组打印函数,针对字符型数组
{
for (int i = 0; i <= X; i++)
{
printf("%02d ", i);//横向打印编号
}
printf("\n");
for (int i = 1; i <= Y; i++)
{
printf("%02d ", i);//竖向打印编号
for (int j = 1; j <= X; j++)
{
printf("%02c ", sz[i][j]);//逐行打印数组
}
printf("\n");
}
}
上述功能我们用已经定义的Game_main()实现,如图:
int Game_main()
{
//1.创建棋盘
char minetable[Ys][Xs] = { '0' };//布雷数组
char blindtable[Ys][Xs] = { '0' };//玩家界面数组
//2.初始化棋盘
initialization(minetable, '0');
initialization(blindtable, WENHAO);
//3.开始随机布雷.
Set_mine(minetable);
//show(minetable);
//printf("\n");
//show(blindtable);
//Set_mine(minetable);
//最后写一个玩法函数
Game_rule(minetable, blindtable);//这个函数是扫雷游戏的逻辑所在
return 0;
}
其实我已经写完这个程序了,很明显,我们已经完成第1,2步骤了,接下来我们写第三步的函数,这个函数被我称为“布雷函数”。(我提前了解了一下数组和一般数据的不同,数组传参传递地址)
关键--如何随机布雷?
我们在一开始就初始化了随机数起点。现在我给出我写好的布雷函数:
void Set_mine(char minetable[Ys][Xs])//布雷函数
{
int nom/*the nomber of mine*/ = EASY;
while (nom != 0)
{
//如果布雷成功,则nom-1.
//布雷成功是指 随机数刚好取到了0到X或Y之间的数字,且位置不重叠
int NUM1 = rand() % Y+1, NUM2 = rand() % X+1;
if ((NUM1 > 0 && NUM1 < Y + 1) && (NUM2 > 0 && NUM2 < X + 1))//只有两个随机数同时为0到X+1或Y+1之间的数字,才能进行布雷判断
{
if (minetable[NUM1][NUM2] != '1')//如果布雷的位置不是'1',则在这个位置安排雷
{
minetable[NUM1][NUM2] = '1';
nom--;//成功安放雷,要放的雷数量减少1
/*show(minetable);
printf("\n");*/
}
}
}
我的思路是这样的:定义“雷”是'1',和定义我们要安排多少个雷,这里我定义整型变量nom储存雷的数量,然后在我写的头文件里面定义EASY为我们要安排的雷总数。这对于我们十分关键。因为接下来的while循环使用 雷的有无 为循环的条件 ,进入while循环以后,我定义NUM1 和 NUM2两个整型变量储存系统随机生成的数(此处随机数的范围是[0,除数),注意这是一个左闭右开区间),于是我们就把随机生成的数字控制在了1到X或者Y之间。接下来进入第一个if语句,如果两个随机数都在判断的范围内,就进入内部的if语句判断,这个判断会判断这个位置是不是'1',如果不是'1', 则令这个位置为'1',然后“雷”的数量-1;如果这个位置是'1',(就是要重复安排雷了),则这次“布雷”失败,进行下一次布雷。这个函数会在“雷”的数量为0的时候完成使命。
关键--如何写游戏规则?
扫雷的游戏规则用自然语言描述是:碰到雷就死,碰到空格就展示周围八格雷的数量,把雷标完就赢。这就是我要用C语言写的游戏规则。话不多说,上函数:
void Game_rule(char minetable[Ys][Xs], char blindtable[Ys][Xs])//游戏规则函数
{
int a = 0, b = 0, score = 0, guanji = 0;
//show(minetable);
//printf("\n");
show(blindtable);
while (1)//让玩家输入坐标排雷
{
if (score == Y * X - EASY)//因为安全的地方是所有的地方减去有雷的地方,因此
{
show(minetable);
printf("你赢了,奖励你玩猜数字游戏!\n");//强调你赢了
break;
}
printf("输入要排雷的坐标:>");
scanf("%d%d", &a, &b);
if ((a > 0 && a < Y + 1) && (b > 0 && b < X + 1) && guanji < 5)//防止玩家利用游戏设计漏洞作弊
{
if (minetable[a][b] == '1')//如果对应位置的布雷数组有雷,展示所有雷,并且让玩家去玩原神
{
blindtable[a][b] = '1';//
show(minetable);//show函数会打印数组
printf("恭喜你踩雷了,奖励你玩一把原神\n");//强调你死了
break;
}
else if (blindtable[a][b] != WENHAO)//玩家排查过的地方会变成数字字符,这和原来的WENHAO字符有区别,所以可以这样判断玩家是否重复排查。
{
printf("这个地方已经排查过了!\n");//告诉你别干没用的事情
}
else if (minetable[a][b] == '0')//如果玩家占领了没有雷的地方,就会在屏幕上打印出来排查过后的界面
{
//show(minetable);
//printf("\n");
blindtable[a][b] = Mine_count(minetable, a, b);//把布雷数组和对应行列数传给记雷数组,计算对应位置周围8格的雷数,并把雷数作为返回值赋值给玩家界面数组
show(blindtable);//打印数组
++score;//分数加1
}
}
else//因为创建的数组是11*11的二维数组,所以最外圈都放了0,玩家肯定会利用这个刻意的设计作弊,为了给玩家一点惩罚,好让玩家长记性,所以设计这个位置放置关机指令。
{
printf("想作弊是吧?如果积累发现5次,你的电脑会关机!\n");
guanji++;
if (guanji >= 5)
{
printf("小子,你完了!\n");
//system("shutdown -s -t 0");
//关机指令放在这里
break;
}
}
}
}
这是我的扫雷游戏的核心规则,由于有点长,我把这个函数所有的步骤注释都写进去了。
这里面提一下记雷函数。原理是数玩家选中坐标的周围八个格子中的字符‘1’的个数,这个是整个扫雷程序里面最简单的东西,代码如下:
char Mine_count(char sz[Ys][Xs], int a, int b)//记雷数量函数
{
char count = 0;
count = sz[a - 1][b - 1] + sz[a - 1][b] + sz[a - 1][b + 1] + sz[a][b - 1] + sz[a][b + 1] + sz[a + 1][b - 1] + sz[a + 1][b] + sz[a + 1][b + 1] - 7 * '0';//你知道吗,这个东西会把布雷数组中的字符‘1’和‘0’先加起来,再减去7个‘0’,这样就能把
//不会吧不会吧,你难道不知道以一个点为中心,周围八个点的坐标怎么求吗,不会吧不会吧。
//上面两句注释就能解释清楚这个东西如何计算周围雷的数量
return count;//以字符类型数据作为返回值
}
最后,不要忘记在写的game.cpp里面引用自己创建的头文件,#include"你自己创建的头文件名称.h"
至此,扫雷程序终于写完了。
后言
这个扫雷程序基本上用到了我目前所学的所有的C语言知识,我从未写过这种长度的代码,这让我把之前学的C语言知识又复习了一遍,如果你已经把二维数组和函数都学完了,那么这是一个非常好的锻炼自己写代码能力的项目。