前言(瞎逼逼)
先弟学c未半而中道实战,今扫雷三分,素来装(zhao)逼(da),此程(序)危只因之秋也。
然……然则……算了,总之一句话,我也来凑个热闹,分享一些我的见解,或者说以我的视角,再说一遍这老掉牙,啊不,这经典的实战项目。
面向编程的Ctrlcv工程师们,以及有需求的兄弟们可以移步gitee啦(源代码)
前期准备
一、游戏规则
俗话说得好,知己知彼,方能百战百die(bushi)。
如图所示,在我们进入游戏后,映入眼帘的便是那个一个个小白块,所组成的9*9的盘面,而再此之中又随机买了十颗地雷,当我们啊,一不小心的“踩”到了,boom!哦吼,死咯(小手一摊)。
显然,通关条件便是绕开盘面上所有的雷,然后踩完所有的空白格,最后获得胜利。
二、分门别类
分门别类,何为分门别类。你想想在我们日常生活当中,不同属性的房间承担着不同的功能,在厨房做饭,在卧室睡觉,在厕所解决三急,我们在大多数情况下,便会去相应的房间做相应的事,这样就不会出现吃喝拉撒睡都在卧室……当然,不是不行,只是“房子”还小的时候,还能凑合,房子慢慢变大的话,我们要干的事多了,难免不会出乱。
这时候我们就应该对给类函数进行包装,变成一个个独立的.c文件,也就相当于个个功能属性不同的房间,分门别类。而且万丈高楼平地起,盖大楼从来不是一个人的事,分门别类后,我们要干什么,怎么干便愈发清晰明了。
那么这时候聪明的孩子就会问啦,这是时候我们应该怎么办呢~
诶!好吧,其实也没啥,只不过像平常一样,多造个.c文件,把要用的函数都塞进去。而且众所周知,有些东西在不是自己的,要用别人的东西的时候,我们是不是得说一声呀,因此我们还得整个.h文件。
游戏搭建
一、搭建思路
我的思路很简单,跟一般人也没什么区别,无外乎游戏开始,创建盘面,初始化盘面,布置雷,然后开始扫雷。
最后把雷扫了,要么把雷给踩了,而不变的便是游戏结束。
但倘若如此我也不必如此单领出来,主要是觉得这样的思路存在着些许问题,为什么是布置完雷,玩家才开始排查,这样会不会出现开局既踩雷的情况呢,我觉得并非不可能,而且真这么做了,玩家也遇到了,体验也不会好的吧。
因此我做出了以下的些许调整,既在布置雷之前,就排一次雷,而且先不告诉玩家,在完成布置后,进行检测(地雷),返回一个数值,最后再告诉玩家,保证玩家永远不会第一步便踩雷。
二、基础框架
这个嘛,很基础,想必大街也都会,而且大家多多少少都会有点自己的想法,所以我也就不过多讲述了,大家看一看就好了。

三、框架主体
这是我的代码,实现思路及过程,不过话说回来各位走过路过的大佬们,我想问你们件事,就是假如玩家在输入坐标时,输入了中文,对没错,中文,程序为什么没有像我设计的那样返回,而是无限的输出数字0。
1、布置、初始化盘面
(按照惯例我先把代码贴出来啦)



OK,兄弟们,来到最为关键的地方了,搭建游戏的核心,而按照我们一开始的思路,我们在开始后面,紧跟着的便是布置盘面了。
诚然扫雷的盘面是一个9*9的盘面,我们固然可以直接整个arr[9][9]的二维数组,可是这样纯度,啊不,效率,啧,也不行……嗯,就是后续可操作空间变小了,按我那位不愿透露姓名的无中生同学的话来说,这么做无异于自断双腿,然后靠着双手爬过了终点,虽然是爬过了100米的赛道,但他没法跑的更远、更快。
换而言之,后续要加入改难度的功能时,这无数的arr[9][9],就宛若💩山,不对,💩界珠穆朗玛峰! 所以我们将通过define的定义宏,将标识符同步替换成相应数字,到时候要做修改时,直接从这里该就好了。就比如这样。
然后就可以这样amazing啊,如此这般后续调整只需要简简单单调整一下row的数字,全局的数据因此而改变。
实现

啊,这时眼力尖的兄弟就发现了:不是,哥们~你咋造两个数组啊~怎么还是11*11啊~
☝️ 🤓诶,有眼光 ,首先第一点,为啥是双数组,而非单数组。
你想想,地雷一般埋在哪里,地里是不是,我们一般走在哪里,地上是不是。假如说地雷丢在地上,怕是也没谁会踩上去的是吧,那是不是得做点伪装。

那么第二个问题就来了,为啥是11*11,而非9*9
如图所示,假设红色即为我们点击的位置,橙色既为周围八个,1为地雷。而从图中我们可以轻易的看出位于左边的方框,有一个地雷,那么右边的呢。当然你可能会说这不一个地雷都没有吗,但按照左侧方框检测方式,检测周围八格时,你懂的,没有被定义、初始化的数,一般而言都是随机数,到时候玩家点击右侧时,就可能会看到,他点击方块旁边,看到有成千上万课地雷,保准一玩一个不吱声。
当然你也可以说,制作不同的方法来检测嘛,起初我也是这么想的,麻烦不说,我的老师还说这是重复造轮子,是不可取的,是没有必要的。
那么现在我们将在再回来看这图,现在在上下左右都加了一行一列,并把他们全都初始化了0,那么我们再回过头看我们刚刚点击的地方,是不是清晰明了了。
啊……至于为啥要初始化字符0,说实话我忘了,反正老师说要这么做。
2.踩雷(仅一次),查看雷


3. 布置雷、计算雷
布置

1.简简单单,利用随机函数给xy一各个值,然后在对应arr[x][y]上由0变为1,即为埋地雷,刚才也说个要绕开嘛,所以凡是随机到了与@相同坐标时,重随一遍。
2.而且别忘了时间函数,忘了的话这就不是随机了。
3.先前也说了我们在数组上下左右都加了一行,如果这里的xy不加一,就有可能出现雷埋墙里这种可能。
计算
这时候我们的雷区已经充满了0和1(地雷),你知道这说明什么,说明我的数组都是通(tong)讯(xing)录(lian),而且0超多,就说明我们可以进行计算啦。
计算公式也很简单,就是简简单单将玩家所检测位置周围八个以此加上去,而详情在下图。
4.排查雷
源代码
#include "and.h"
void start()
{
printf("****************************\n");
printf("****************************\n");
printf("********* 扫雷 *********\n");
printf("*********1.开始游戏*********\n");
printf("*********2.游戏设置*********\n");
printf("*********3.结束游戏*********\n");
printf("****************************\n");
printf("****************************\n");
}
void set_up()
{
printf("****************************\n");
}
//需求清单
//1.难度选择
//2.时间挑战
//还在做……
void game()
{
printf("————游戏开始————\n");
char mine[rows][lists] = { 0 };
char land[rows][lists] = { 0 };
init(mine, rows, lists, '0');//1.初始化雷区
init(land, rows, lists, '#');//2.初始化游戏区
look_over(land, row, list);//显示游玩区域
int click1 = 0;
int click2 = 0;
do//防止程序出错的保险
{
printf("请选择你要排查的位置:");//输入排查部分
scanf("%d%d", &click1, &click2);
} while (click1 < 0 || click1 > row || click2 < 0 || click2 > list);
land[click1][click2] = '@';//保证不会出现开局踩雷,或是凭空踩掉雷的保险
place(mine, land, row, list);//布置雷,并避开@,确保不会凭空消失一个雷
int a = count(mine, click1, click2);//计算@旁边有多少颗雷
land[click1][click2] = a + '0'; //并顶掉@
look_over(land, row, list);//游戏初步开始
check(mine, land, row, list);//排查雷
look_over(mine, row, list);//游戏结算,给玩家看雷的位置
}
int main()
{
srand((unsigned int)time(NULL));//时间函数
int i = 0;
do
{
start();
printf("请选择:");
scanf("%d", &i);
if (i == 1)//可以使用switch函数
{
game();
printf("是否还有在来一局?\n");
printf("1.是 2.否 >");
scanf("%d", &i);
if (i == 2)
{
break;
}
}
else if (i == 2)
{
set_up();
i = 0;
}
else if (i == 3)
{
break;
}
else
{
printf("选择错误\n");
continue;
}
} while (i);
printf("青山不改,绿水长流,拜拜了您内");
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#define row 9
#define list row//列
#define rows row+2
#define lists list+2
#define landmine 10//地雷的数量
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void init(char arr[rows][lists],int x, int y,char set);
//初始化
void look_over(char arr[rows][lists],int x,int y);
//查看
void place(char arr1[rows][lists], char arr2[rows][lists], int x, int y);
//布置地雷
void check(char arr1[rows][lists], char arr2[rows][lists], int x, int y);
//排查雷
int count(char arr[rows][lists], int x, int y);
//计算雷的个数
#define _CRT_SECURE_NO_WARNINGS
#include "and.h"
void init(char arr[rows][lists], int x, int y,char set)
{
for (int i = 0; i < x; i++)
{
for (int j = 0; j < y; j++)
{
arr[i][j] = set;
}
}
}
void look_over(char arr[rows][lists], int x, int y)
{
for (int i = 0; i <= x ; i++)
{
if (i < 10)
printf(" ");//对齐数组
printf("%d", i);
printf(" ");
}
printf("\n");
for (int i = 1; i <= x; i++)
{
if (i < 10)
printf(" ");
printf("%d ", i);
for (int j = 1; j < y+1; j++)
{
printf(" %c ", arr[i][j]);
}
printf("\n");
}
}
void place(char arr1[rows][lists], char arr2[rows][lists], int x, int y)
{
int add = landmine;
int i = 0;
while (i < add)
{
x = rand() % row + 1;
y = rand() % list + 1;
if ((arr1[x][y] == '0') && (arr2[x][y] != '@'))
{
arr1[x][y] = '1';
i++;
}
}
}
int count(char arr[rows][lists], int x, int y)
{
return arr[x - 1][y] +//计算雷的个数
arr[x - 1][y - 1] +//因为初始化时,输入的值均为字符零一
arr[x][y - 1] +//而简单的相加又只适用于整形值,字符难以使用,而恰巧ascii码的
arr[x + 1][y - 1] +//‘0’-‘0’=0,‘1’-‘0’=1
arr[x + 1][y] +//既0的ascii码等于48,1等于49,相减,则恰巧等于 1
arr[x + 1][y + 1] +//而减去8个字符0,约等于将字符零一,化为整形零一。
arr[x][y + 1] +
arr[x - 1][y + 1] - 8 * '0';
}
void check(char arr1[rows][lists], char arr2[rows][lists], int x, int y)
{
int q = (row * list) - (landmine + 1);
int w = 0;
while(w<q)
{
//look_over(arr1, row, list);
printf("请选择你要排查的位置:");
int choice1 = 0;
int choice2 = 0;
scanf("%d%d", &choice1, &choice2);
if (choice1 < 0 || choice1 > row|| choice2 < 0 || choice2 > list)
{
printf("很抱歉,不存在\n");
look_over(arr2, row, list);
continue;
}
if (arr1[choice1][choice2] == '1')
{
printf("——嘣!!!是地雷!游戏结束——\n");
break;
}
else if (arr2[choice1][choice2] == '#')
{
int a = count(arr1, choice1, choice2);
arr2[choice1][choice2] = a+'0';
//如果直接那cont得来的值等效,则会出现空白部分,因为本身count的值,为整形,而arr2是字符。
//其本质就是电脑将count的值,转化为ascii码,而对应的ascii码,是指令操作符,既空白。
//所以加上一个字符0,既加上48的ascii码,使其显示真正的得数。
look_over(arr2, row, list);
w++;
}
else
{
continue;
}
if(w == q)
printf("——恭喜你,游戏通关——\n");
}
}