本篇是基于扫雷游戏拓展的功能讲解,如果有不知道扫雷游戏怎么写的,可以在我的另一篇博客学习,下面我先展示一下拓展三个功能,并逐一功能进行讲解。
1.零雷拓展
零雷拓展是什么功能?想必大家对这个词可能很陌生,其实这个词是小编自己取得,其实它的含义很简单,就是如果排查位置不是雷,周围也没有雷,可以展开周围的⼀片的功能,那么该怎么实现这个功能呢?
首先,我们要想,这个功能需要加在哪里?那么我们先找到需要展示棋盘的地方。
这是我们上次打印雷个数的地方,其基本思想是Getmine函数负责统计输入坐标的九宫格内有几个’1‘字符的,并把返回的值赋值给count,count+’0‘表示count的字符,比如:1+’0‘=’1‘。最后修改show该坐标的值。
修改方案:
我们可以对count的值做判断,如果count=0,那么则表示,输入的这个坐标周围的九个格子中有0个雷,需要将show数组中该坐标周围的九宫格的字符数全部转为’0‘(原为’*‘),那么这个很好解决,这里有两个方法:
- 一个是一一列举出该坐标周围九宫格的坐标后赋值’0‘
- 用for循坏赋值
小编用的是第二种方法,代码如下:
int count = Getmine(mine, x, y);
if (count == 0)
{
int i = 0;
int j = 0;
for (i = -1; i <= 1; i++)
{
for (j = -1; j <= 1; j++)
{
show[x + i][y + j] = '0';
}
}
Displayboard(show, row, col);
}
else
{
show[x][y] = count + '0';
Displayboard(show, row, col);
}
那么运行结果如何?
哇哦,达到了想要的效果,那是不是真的完成了呢?小编很遗憾的告诉大家还没结束。
那么为什么呢?还请看下图
大家还记的扫雷是怎么判断结束的吗,小编帮大家回忆一下:
我们设了一个局部变量ret,用ret来记录我们扫雷的次数,当我们下的次数等于除去雷的空白个数时,就会判断游戏结束。
那么设想一下如果我们采用了上面的方法来实现零雷拓展的会出现什么情况,答案是没办法通过ret的值判断游戏结束,那么有的同学就会问了,那我ret+9不就行了,那这位同学就没有考虑到两次或多次零雷拓展重叠的情况了,如果重叠,那么你算的已经有18次,但实际上却没有那么多次。总结原因,之前游戏结束判断方式不适合在零雷拓展的情况。
那么这就是我们要解决的第二个问题,如何在零雷拓展的情况下判断游戏结束
思路:
既然算空白数不行,那我们算雷的数目行不行?答案是肯定的,大概思路是每走完一步,我们都检查一下show数组的'*'的数量,如果'*'的数量等于雷的数目,那么就证明了游戏结束。
代码如下:
int Showmine(char show[ROWS][COLS], int row, int col)
{
int ret = 0;
int i = 0;
int j = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if (show[i][j] == '*')
{
ret++;
}
}
}
return ret;
}
int ret = Showmine(show,row,col);
if (ret == SetCount)
{
printf("恭喜你,胜利了!!!\n");
Displayboard(show, row, col);
break;
}
那么功能结果如何?
功能完成!由于小编太懒了,没有逐一功能演示,还请见谅。后续将持续更新计时,保存最快记录的功能。
2.计时功能
计时功能可能是这这三个功能里最简单的了,因为实现这个功能这个只需要一个时间函数就能完成了,那就是clock()时间函数。那么小编先来介绍一下这函数是如何使用的。
上面是clock()函数的定义,可能有点晦涩难懂,我们结合代码去理解。
int main()
{
clock_t start, end;//先定义两个clock()类型也就是long类型的局部变量
double duration;//负责接受时间差的变量
start = clock();//记录进程开始
Sleep(1000);
end = clock();//记录进程结束
duration = (double)(end - start) / CLOCKS_PER_SEC;//clock()计算得到的单位是毫秒,
//CLOCKS_PER_SEC其实就是1000
//毫秒除以1000也就是秒单位
printf("%f\n", duration);
return 0;
}
运行结果:
好了,在了解了时间函数clock()之后,我们就开始完成计时功能吧。
2.1计时整体游戏
还是一样,我们要先找到计时的功能的位置,也就是游戏的开始和结束的位置。
也就是上图的位置,因为排查雷即是玩家游戏开始的地方,也是判断游戏结束的地方。
那么接下来就是插入时间函数了,具体操作如下:
void game()
{
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);
clock_t start_time = clock();
Findmine(mine, show, ROW, COL);
clock_t end_time = clock();
double elapsed_time = (double)((end_time - start_time) / CLOCKS_PER_SEC);
printf("计时结束!\n");
printf("经过的时间为 %.2f 秒\n", elapsed_time);
}
运行结果:
2.2 分步计时显示
思路如下:
具体操作如下:
![](https://i-blog.csdnimg.cn/blog_migrate/38800e843bbd681ceaf8932f61e7ae80.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ebb1cf6cae2f1ac106aaf643562d1475.png)
运行结果:
3.存储最快扫雷记录
这个功能是三个功能中最复杂的,但是不用怕,小编一步一步教你如何完成这个功能。
首先,我们来想一想要实现这个功能要考虑哪些小功能呢。小编总结了以下几点
- 判断游戏结束方式,是雷炸死,还是通关
- 写记录
- 读记录
- 是否刷新记录,需要一个记录比较的功能
由以上四点结合组成了储存最快记录的功能,我们一一解决。
3.1判断游戏结束方式,是雷炸死,还是通关、
修改方案:
改变Findmine函数的返回类型,把void改成int,雷炸炸死,返回0。通关,返回1;
具体操作如下;
![](https://i-blog.csdnimg.cn/blog_migrate/822986862ef9cb9f40e7ee38292f5412.png)
![](https://i-blog.csdnimg.cn/blog_migrate/17d4e7d5d7a5de9b8958cc1a2eeaa7fa.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3f9a7dc1d4641c0a447153734d85ca0c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c9a4fb3cf17a6a94de7b009ff1bac2b6.png)
3.2写记录(保存记录)
根据上面的思维导图,我们知道写记录应该放在下图的位置。
这里的保存记录就会涉及到文件操作,不懂的同学也没有关系,我会简单说一下相关函数的作用,我们先把这个保存记录的函数写成SaveRecard(&elapsed_time);参数为通关时间的取地址,为了大家便于理解,这里小编先把这个函数给大家看一下,然后逐步给大家解释。
void SaveRecard(double* recard)
{
FILE* pf = fopen("recard.dat", "w");//1
if (pf == NULL)//2
{
perror("SaveRecard");
return;
}
char arr1[100];//3
sprintf(arr1, "The fastest recard is %lf",*recard);//4
fwrite(arr1,sizeof(arr1),1,pf);//5
fclose(pf);//6
pf = NULL;//7
}
总共7步:
- 以写的操作打开了一个文件名为recard.dat的文件,并把该文件的地址给了pf指针。注意:不管这个文件有没有,都会刷新文件内容,从文件建立初始地址开始写的操作,如果没有这个文件,会自己创建这个文件。
- 判断pf是否为空指针,如果是,则打印错误并且返回;
- 创建一个字符数组,用来存放下一步要写入的字符串;
- 将The fastest recard is (最快记录的时间),以字符串的形式放到arr1的数组中;
- 将arr1的字符串内容以二进制的方式写入文件名为recard.dat的文件中;
- 关闭文件;
- pf置成空指针;
好了,写记录的功能就完成了,那运行效果如何?
3.3 读记录并且打印
读记录并且打印的位置放在着排雷之前即可,下图是小编放的位置;
还是和上面一下,我们先看这个函数;
void ReadRecard(void)
{
FILE* pf = fopen("recard.dat", "r");//1
if (pf == NULL)//2
{
perror("ReadRecard");
return;
}
char arr[100] = { 0 };//3
fread(arr, sizeof(arr), 1, pf);//4
printf("%s\n\n", arr);//5
fclose(pf);//6
pf = NULL;//7
}
其实整体思路和Saverecard函数差不多;
- 以读的操作打开了一个文件名为recard.dat的文件,并把该文件的地址给了pf指针。
- 判断pf是否为空指针,如果是,则打印错误并且返回;
- 创建一个字符数组,用来存放下一步要读出的字符串;
- 以二进制的方式从文件读出字符串,也就是The fastest recard is (最快记录的时间),以字符串的形式放到arr的数组中;
- 将arr数组的内容以字符串方式打印出来;
- 关闭文件;
- pf置成空指针;
那么效果如何?
嗯嗯,不错,能坚持到这的都是狠人!接下来只差一步就成了
3.4 比较记录是否被刷新
看到这的朋友一定知道这个函数放在哪了吧,你肯定知道的吧?
好的,接下来我们来想一想,如何比较呢?学了上面的读写操作是不是更好理解了呢,大概思路是什么?无非是从文件中读出记录的数值,然后跟新纪录进行比较,如果是新纪录更快就保存记录。具体代码如下:
int CmpRecard(double* recard)
{
FILE* pf = fopen("recard.dat", "r");
if (pf == NULL)
{
perror("ReadRecard");
return 0;
}
char arr[100] = { 0 };
fseek(pf, 22, SEEK_CUR);
fread(arr, sizeof(arr), 1, pf);
fclose(pf);
pf = NULL;
int ret = atoi(arr);
if (ret > *recard)
{
return 1;
}
else if (ret == 0)
{
return 0;
}
else
{
return -1;
}
}
哇哦,可能有的小伙伴就会说这么复杂吗?能看懂吗?小编分模块功能给大家讲解。
![](https://i-blog.csdnimg.cn/blog_migrate/52a12c879efc26c5754d6bd812df8dff.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c551c226e1def770e023431f841b0d5f.png)
好了,比较记录的函数就分析到这里,接下来就是最后一步,判断是否刷新保存记录就完成了
终于完成了,看到这的朋友都是牛人,最后展现一下功能
4.完整代码
game.h
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROWS 7
#define COLS 7
#define ROW ROWS-2
#define COL COLS-2
#define SetCount 24
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);
int Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
int Showmine(char show[ROWS][COLS], int row, int col);
void SaveRecard(double* recard);
void ReadRecard(void);
int CmpRecard(double* recard);
game.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void Initboard(char board[ROWS][COLS], int rows, int cols, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void Displayboard(char board[ROWS][COLS], int row, int col)
{
printf("-------------扫雷--------------\n");
int i = 0;
int 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");
}
printf("-------------扫雷--------------\n\n");
}
void Setmine(char mine[ROWS][COLS], int row, int col)
{
int count = SetCount;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
int Getmine(char mine[ROWS][COLS], int x, int y)
{
int ret = 0;
int i = 0;
int j = 0;
for (i = -1; i <= 1; i++)
{
for (j = -1; j <= 1; j++)
{
if (mine[x + i][y + j] == '1')
{
ret++;
}
}
}
return ret;
}
int Showmine(char show[ROWS][COLS], int row, int col)
{
int ret = 0;
int i = 0;
int j = 0;
for (i = 1; i <= row; i++)
{
for (j = 1; j <= col; j++)
{
if (show[i][j] == '*')
{
ret++;
}
}
}
return ret;
}
void SaveRecard(double* recard)
{
FILE* pf = fopen("recard.dat", "w");
if (pf == NULL)
{
perror("SaveRecard");
return;
}
char arr1[100];
sprintf(arr1, "The fastest recard is %lf", *recard);
fwrite(arr1, sizeof(arr1), 1, pf);
fclose(pf);
pf = NULL;
}
int Findmine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
while (1)
{
extern clock_t start;
clock_t end = clock();
double elapsed= (double)((end-start) / CLOCKS_PER_SEC);
printf("计时:%.2f\n", elapsed);
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);
return 0;
}
else
{
int count = Getmine(mine, x, y);
if (count == 0)
{
int i = 0;
int j = 0;
for (i = -1; i <= 1; i++)
{
for (j = -1; j <= 1; j++)
{
show[x + i][y + j] = '0';
}
}
Displayboard(show, row, col);
}
else
{
show[x][y] = count + '0';
Displayboard(show, row, col);
}
}
int ret = Showmine(show, row, col);
if (ret == SetCount)
{
printf("恭喜你,胜利了!!!\n");
Displayboard(show, row, col);
return 1;
}
}
else
printf("输入错误坐标,");
}
}
void ReadRecard(void)
{
FILE* pf = fopen("recard.dat", "r");
if (pf == NULL)
{
perror("ReadRecard");
return;
}
char arr[100] = { 0 };
fread(arr, sizeof(arr), 1, pf);
printf("%s\n\n", arr);
fclose(pf);
pf = NULL;
}
int CmpRecard(double* recard)
{
FILE* pf = fopen("recard.dat", "r");
if (pf == NULL)
{
perror("ReadRecard");
return 0;
}
char arr[100] = { 0 };
fseek(pf, 22, SEEK_CUR);
fread(arr, sizeof(arr), 1, pf);
fclose(pf);
pf = NULL;
int ret = atoi(arr);
if (ret > *recard)
{
return 1;
}
else if (ret == 0)
{
return 0;
}
else
{
return -1;
}
}
text.c
#include<stdio.h>
#include "game.h"
#include<time.h>
clock_t start;
void menu()
{
printf("-----------------------\n");
printf("------1.开始游戏-------\n");
printf("------0.退出游戏-------\n");
printf("-----------------------\n");
}
void game()
{
start=clock();
ReadRecard();
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);
clock_t start_time = clock();
int tmp=Findmine(mine, show, ROW, COL);
clock_t end_time = clock();
double elapsed_time = (double)((end_time - start_time) / CLOCKS_PER_SEC);
printf("计时结束!\n");
printf("经过的时间为 %.2f 秒\n", elapsed_time);
//判断是否通关
if (tmp == 1)
{
//是否刷新记录
int ret = CmpRecard(&elapsed_time);
//是,则保存记录
if (ret>=0)
{
printf("恭喜破纪录\n");
SaveRecard(&elapsed_time);
}
}
}
int main()
{
srand((unsigned int)time(NULL));
int input = 1;
do
{
menu();
printf("是否开始游戏>");
scanf("%d", &input);
switch (input)
{
case 1:game(); break;
case 0:printf("退出游戏"); break;
default:printf("输入错误,请重新输入"); break;
}
} while (input);
return 0;
}