#创作灵感#
经过一个下午的调试和假设,终于完成了这个简易版的扫雷
接下来我来为大家讲解代码的逻辑和实现是如何完成的
首先我们知道扫雷这个游戏
他的界面是这样的,首先,我们点击一个位置,它会显示周边的雷的信息,
它显示的信息是周边一圈有几个雷
首先我们就要知道,我们需要一个棋盘来表示我们扫雷的棋盘
首先我们要创建一个游戏菜单 ,打印是否进行游戏,如果选择1,则进行游戏,如果选择0,则退出游戏,如果选择的不是1,不是0,那我们就打印输入错误
首先我们想到的就是需要一个输入,需要一个循环,那么我们选则用什么循环更加合适呢?
首先我们想到的就是使用do....while循环,因为do.....while循环是可以直接做事情的
那么我们定义一个变量
main()
{
int input = 0;
do
{
caidan();
printf("请选择:");
scanf("%d", &input);
}while(input);
}
首先我们定义一个变量input,然后写一个循环来表示我们对游戏的选择,我们首先创建一个caidan函数(英文不会写),来打印我们游戏的菜单,从中做出选择,我们用scanf来输入我们的input,从而对input取值,对于caidan函数我们不需要对其进行传参,所以参数可以不写。
对于caidan函数我们内部是这样定义的:
void caidan()
{
printf("*************************************\n");
printf("********** 1. Play ***********\n");
printf("********** 0. Exit ***********\n");
printf("*************************************\n");
}
因为我们的caidan函数不需要返回值,所以我们在类型写viod即可
打印一个菜单样式即可
然后我们使用switch选择语句来选择是否就行游戏
如果是1,则开始游戏
如果是0,则退出游戏
如果是其他数字,则打印输入错误,请重新输入
然后我们的代码实现是这样婶的:
int main()
{
int input = 0;
do
{
caidan();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("扫雷游戏\n");
//游戏实现
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
然后我们选择1,就可以进行游戏内部的实现了。
那么我们把这个游戏的内部封装为一个函数,即game()函数
然后我们的代码就到了这样婶的:
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
caidan();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("扫雷游戏\n");
//游戏实现
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
然后呢,我们就要对game()函数的代码内部进行实现。
首先我们要定义一个头文件,我们命名为game.h
我们在game.h中定义一个ROW,来表示行,定义一个COL,来表示列
game.h内部的代码是这样婶的:
#define ROW 9
//扫雷棋盘的行
#define COL 9
//扫雷棋盘的列
因为我们要看周围8个坐标,所以我们要使棋盘为11,否则就会越界
所以我们要定义两个值为11的变量,以防止我们的访问不会越界
基本定义的代码是这样的:
#define ROWS ROW+2
//外部棋盘的行
#define COLS COL+2
//外部棋盘的列
然后我们就可以定义我们game函数的内部内容:
创建两个字符数组:
void game()
{
//创建两个个棋盘
char mine[ROWS][COLS] = { 0 };
//游戏内部棋盘
char show[ROWS][COLS] = { 0 };
//展示棋盘
}
因为我们的game()函数不需要进行返回,所以类型写成viod即可
在game函数内部,我们需要创建两个字符数组。
mine数组呢,需要我们存放雷的信息
show数组呢,则是展示给玩家观看的
然后我们将mine棋盘中全部初始化为0
将show棋盘全部初始化为*号
为了避免我们使用重复的函数来进行实现,所以我们选择使用一个函数来分别传不同的参数,来实现初始化的功能。
void game()
{
//创建两个个棋盘
char mine[ROWS][COLS] = { 0 };
//游戏内部棋盘
char show[ROWS][COLS] = { 0 };
//游戏展示棋盘
//内部棋盘初始化
Init_bool_come(mine, ROWS, COLS, '0');
//展示棋盘初始化
Init_bool_come(show, ROWS, COLS, '*');
}
我们设置一个Init_bool_come的函数来实现我们需要的数组初始化
第一个参数我们传需要初始化的字符数组,第二个参数我们传行,第三个参数我们传列
第四个参数我们传要初始化的字符,展示给玩家的我们展示‘*’号,内部我们放置雷的地方展示0
那么我们现在的初始化函数是未定义的
对于这个初始化函数,我们只需要定义一次即可,因为他们的功能是相同的。
因此,我们在game.h中来定义这个函数的声明
在game.c中来完成函数的内容
首先我们要在game.h中定义函数声明,那么声明我们怎么定义呢?
可以看到我们需要传入一个字符数组,两个整形,和一个字符型
因此我们可以进行如下声明:
void Init_bool_come(char arr[ROWS][COLS], int rows, int cols, char set);
因为我们的函数只做初始化作用,所以不需要什么返回值,其中,我们用char arr[ROWS][COLS],来表示我们的数组,用rows来表示列,用cols来表示行
以及用我们的char set来接受我们的字符
随后我们就可以在game.c中进行实现我们的函数,将我们的棋盘全部初始化为对应得字符
其中函数内容如下:
void Init_bool_come(char arr[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++)
{
arr[i][j] = set;
}
}
}
其中,我们想象成一个二维数组,将二维数组进行初始化,创建一个i ,一个 j ,然后令
arr[i][j] = set 令每一个元素变成我们定义的字符
以上即可完成我们对于棋盘的初始化问题,
然后我们可以打印棋盘看一眼初始化是否成功,也就是二维数组的打印
其中代码是这样的:
int i = 0;
int j = 0;
for (i = 0; i < 11; i++)
{
for (j = 0; j < 11; j++)
{
printf(" %c", arr[i][j]);
}
printf("\n");
}
我们先定义一个循环变量 i 再定义一个循环变量 j 然后因为我们的数组是在11*11中初始化的,所以我们将循环条件设置为 i<11 和 j<11 然后我们用%c进行打印,因为我们的数据内容都是字符型。
正确初始化后应该是这样的或是这样的:
然后我们就可以在这里布置我们的雷的信息
我们定义一个Dis_boom()函数;来实现我们的布置雷的信息,
因为雷的分布是随机的,所以我们也可以用我们猜数字中随机值来定义我们的雷
首先我们在9*9的方格中进行游戏,所以布置雷的信息只需要在9*9的方格中定义即可
所以我们对函数的传参即可为:
Dis_boom(mine, ROW, COL);
将内部棋盘传给我们的函数,用来布置雷,将我们事先定义的行:ROW(9),和列:COL(9)传参给我们布置雷的函数,以获得在9*9方格中布置我们的雷的信息
其中,我们还是要在我们的game.h头文件中来声明我们的函数
其中在game.h中声明情况如下:
void Dis_boom(char mine[ROWS][COLS], int row, int col);
因为我们的布置雷的信息不需要返回什么,只需要布置即可,所以我们将函数的返回值设为void
将函数char mine[ROWS][COLS]将这个棋盘传参给我们的布置雷的函数,我们用于接收,将行和列一并接收,分别为 int row 和 int col
然后我们在game.c中来完成函数的实现
在完成函数的实现前,我们需要定义一共有几个雷,假如我们玩简单难度的扫雷,我们一共有10颗雷,所以我们在game.h这个头文件中定义一个炸弹的信息,即10个雷
//定义10个雷
#define BOOM 10
然后我们就可以完成函数的实现
具体实现代码如下:
void Dis_boom(char mine[ROWS][COLS], int row, int col)
{
int put = BOOM;
//设置一个循环来布置我们的10个雷
while (put)
{
//定义横纵坐标1~9随机
int x = rand() % row + 1;
int y = rand() % col + 1;
//因为我们定义的row和col为9,对9取模只能得到0~8的数字,+1即可得到1~9
if (mine[x][y] == '0')
//如果mine的位置为 0 进入
{
mine[x][y] = '1';
//改mine中的为1
put--;
//我们雷的个数减一
}
}
}
我们设置一个int put 来存储我们雷的个数
然后我们设置一个循环来布置雷,因为雷如果不为0,则表示还有雷没有进行布置,所以我们写一个while循环,将参数设置为put,因为当我们的put不为0的时候,表示雷还没有全部布置完,循环也就不会停止。
在我们的while内部,我们定义两个变量,一个x,一个y,他们所表示的是下mine数组中的x,y坐标。
以达到我们设置雷的效果
其中x和y的值是需要改变的,所以我们运用我们猜数字游戏中设置随机数的库函数rand,来设置随机坐标,因为我们的雷是在9*9的方格中定义,所以我们只需要让x和y在1~9之间进行随机即可,
所以我们可以写成 rand() % row +1 即对9取模再加1,因为对9取模会得到0~8的随机数,+1后则可得到1~9的随机数
然后我们的rand函数需要一个 srand来辅助 ,所以我们就可以在main函数中定义一下srand
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
caidan();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("扫雷游戏\n");
//游戏实现
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
其中srand则是我们rand的辅助工具,因为它需要一个无符号整形,所以我们强制类型转换成unsigned int 类型,又因为需要一个种子,所以我们需要一个time,也就是时间戳,来表示种子,
time函数需要一个 time.h的头文件,所以我们还需要包含一个time.h,又因为srand和rand需要stdlib.h的头文件,所以我们还要包含一个stdlib.h,才能完成我们对随机数的定义
然后我们再看这个代码:
void Dis_boom(char mine[ROWS][COLS], int row, int col)
{
int put = BOOM;
//设置一个循环来布置我们的10个雷
while (put)
{
//定义横纵坐标1~9随机
int x = rand() % row + 1;
int y = rand() % col + 1;
//因为我们定义的row和col为9,对9取模只能得到0~8的数字,+1即可得到1~9
if (mine[x][y] == '0')
//如果mine的位置为 0 进入
{
mine[x][y] = '1';
//改mine中的为1
put--;
//我们雷的个数减一
}
}
}
如果我们的mine数组中,坐标x,y中的内容为字符0,
则改mine数组中坐标x,y中的内容为字符1 ,字符1表示该位置是雷
然后我们雷的个数减一,则表示布置完一个雷了
然后我们一直循环往复,我们的10个雷就布置完成了。
当我们布置雷后,我们就可以进行排雷操作了,
当然,我们可以打印一下看看布置是否成功
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf(" %c", arr[i][j]);
}
printf("\n");
}
依旧还是二维数组的打印,我们利用二维数组的打印,即可看到我们是否布置成功
然后我们封装一个函数,来打印我们游玩的棋盘
print_bool(show, ROW, COL);
我们将展示棋盘show传给我们的函数,因为我们要在9*9中游玩,所以我们只需要传ROW,和COL即可。
然后我们在game.h中声明我们的函数
void print_bool(char arr[ROWS][COLS], int row, int col);
将棋盘,和ROW,COL接收过来
然后我们打印棋盘的具体实现代码如下:
void print_bool(char arr[ROWS][COLS], int row, int col)
{
int i = 0;
//先打印列号
for (i = 0; i <= row; i++)
{
printf("%d ",i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
int j = 0;
printf("%d",i);
for (j = 1; j <= col; j++)
{
printf(" %c", arr[i][j]);
}
printf("\n");
}
}
我们创建一个循环变量i 先将列号打印出来, 以方便我们观察坐标
然后打印\0,然后我们创建一个循环,将行号也打印出来,以方便我们观察
然后我们以%c来打印我们的数组内容
打印完一行以后换行即可
-----------------------------------------------------
然后我们就可以进行排雷的操作了
Find_Boom(mine, show, ROW, COL);
首先我们设置一个Find_Boom(mine, show, ROW, COL); 将mine,show这两个数组依次传给我们排雷的函数,并且将棋盘设置为9*9,也就是将ROW,和COL也传给我们的函数
然后还是,我们在game.h中声明一下这个函数
void Find_Boom(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
然后在game.c中实现这个排雷函数
void Find_Boom(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
}
我们设置一个x和y来表示要排雷的坐标,用win来表示我们的胜利
那么我们可以想到,当我们的雷没有排完的时候,我们就要一直排雷,
所以我们可以设置一个while循环,来表示需要一直排雷
那么while结束的条件是什么呢?
当我们的win小于我们的row * col 也就是9*9方格所有坐标,减去雷的个数,我们就要循环排雷
void Find_Boom(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
//如果win小于我们的row*col减去雷的个数,就循环排雷
while(win < row * col - BOOM)
{
printf("请输入要排查的坐标:");
scanf("%d %d",&x,&y);
}
}
我们输入x,y来表示我们的排雷坐标
因为我们要在9*9的方格中排雷,所以,如果我们的x大于等于1,并且小于等于我们的行(row)
并且y大于等于1,小于等于我们的列(col)则进行排雷操作,否则打印输入坐标有误
void Find_Boom(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
//如果win小于我们的row*col减去雷的个数,就循环排雷
while(win < row * col - BOOM)
{
printf("请输入要排查的坐标:");
scanf("%d %d",&x,&y);
//如果x大于1,并且小于row 且 y大于1,小于col
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
}
else
{
printf("输入的坐标错误,请重新输入\n");
}
}
}
那么如果输入的坐标内容为字符’1‘,则显示被炸死了,游戏结束,并且打印一下棋盘中雷的位置给我们玩家看一眼,那么代码的实现如下:
void Find_Boom(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
//如果win小于我们的row*col减去雷的个数,就循环排雷
while(win < row * col - BOOM)
{
printf("请输入要排查的坐标:");
scanf("%d %d",&x,&y);
//如果x大于1,并且小于row 且 y大于1,小于col
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//如果输入的mine坐标中等于1,就被炸死,游戏结束
if (mine[x][y] == '1')
{
printf("你被炸死了。\n");
//被炸死后打印雷的信息
print_bool(mine, ROW, COL);
//跳出循环,游戏结束
break;
}
}
else
{
printf("输入的坐标错误,请重新输入\n");
}
}
}
如果我们没有被炸死,则打印周围有几个雷,那么怎么计算周围有几个雷呢?我们就要计算周围8个坐标,将周围8个坐标加在一起减去0的个数,即mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y] + mine[x][y + 1] + mine[x + 1][y + 1] + mine[x + 1][y - 1] + mine[x - 1][y + 1] - 8 * '0'
我们封装成一个ShowBoom函数来表示,然后展示给玩家看,然后使win++
void Find_Boom(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
//如果win小于我们的row*col减去雷的个数,就循环排雷
while(win < row * col - BOOM)
{
printf("请输入要排查的坐标:");
scanf("%d %d",&x,&y);
//如果x大于1,并且小于row 且 y大于1,小于col
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//如果输入的mine坐标中等于1,就被炸死,游戏结束
if (mine[x][y] == '1')
{
printf("你被炸死了。\n");
//被炸死后打印雷的信息
print_bool(mine, ROW, COL);
//跳出循环,游戏结束
break;
}
//如果没被炸死,则打印周围有几个雷
else
{
int count = ShowBoom(mine, x, y);
show[x][y] = count + '0';
//打印出来给玩家看一眼
print_bool(show, row, col);
//已经排完的+1
win++;
}
}
else
{
printf("输入的坐标错误,请重新输入\n");
}
}
}
那么ShowBoom函数我们需要将内部棋盘传参给它,然后将坐标也传给它
然后ShowBoom函数的内部实现如下:
int ShowBoom(char mine[ROWS][COLS], int x, int y)
{
//排查周围8个坐标有没有雷
return(mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y] + mine[x][y + 1]
+ mine[x + 1][y + 1] + mine[x + 1][y - 1] + mine[x - 1][y + 1] - 8 * '0');
}
我们将排查周围8个坐标有几个雷,然后显示输出到屏幕上,如果全部不是雷的坐标都被找到,则排雷成功。
------------------------------------------------------------------------------------------------------------------------
以上则是我们扫雷的逻辑以及代码实现,最终代码为:
//test.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include"game.h"
#include<stdlib.h>
#include<time.h>
void caidan()
{
printf("*************************************\n");
printf("********** 1. Play ***********\n");
printf("********** 0. Exit ***********\n");
printf("*************************************\n");
}
void game()
{
//创建两个个棋盘
char mine[ROWS][COLS] = { 0 };
//游戏内部棋盘
char show[ROWS][COLS] = { 0 };
//游戏展示棋盘
//内部棋盘初始化
Init_bool_come(mine, ROWS, COLS, '0');
//展示棋盘初始化
Init_bool_come(show, ROWS, COLS, '*');
//打印棋盘查看一下
//print_bool(mine, ROW, COL);
//布置雷,我们需要在内部棋盘布置,只需要在9*9的格子中布置即可
Dis_boom(mine, ROW, COL);
print_bool(show, ROW, COL);
//排雷的函数定义,我们需要将mine 和 show 都传进去
Find_Boom(mine, show, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
caidan();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("扫雷游戏\n");
//游戏实现
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
return 0;
}
//game.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#define ROW 9
//扫雷棋盘的行
#define COL 9
//扫雷棋盘的列
#define ROWS ROW+2
//外部棋盘的行
#define COLS COL+2
//外部棋盘的列
//定义棋盘初始化
void Init_bool_come(char arr[ROWS][COLS], int rows, int cols, char set);
//传参,棋盘,行,列,和要初始化的字符
//打印棋盘的函数定义
void print_bool(char arr[ROWS][COLS], int row, int col);
//布置雷的函数定义
void Dis_boom(char mine[ROWS][COLS], int row, int col);
//定义10个雷
#define BOOM 10
//定义找雷函数
void Find_Boom(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//game.c
#define _CRT_SECURE_NO_WARNINGS
#include"game.h"
//棋盘初始化的实现逻辑
void Init_bool_come(char arr[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++)
{
arr[i][j] = set;
}
}
}
//打印棋盘实现
void print_bool(char arr[ROWS][COLS], int row, int col)
{
int i = 0;
//先打印列号
for (i = 0; i <= row; i++)
{
printf("%d ",i);
}
printf("\n");
for (i = 1; i <= row; i++)
{
int j = 0;
printf("%d",i);
for (j = 1; j <= col; j++)
{
printf(" %c", arr[i][j]);
}
printf("\n");
}
}
void Dis_boom(char mine[ROWS][COLS], int row, int col)
{
int put = BOOM;
//设置一个循环来布置我们的10个雷
while (put)
{
//定义横纵坐标1~9随机
int x = rand() % row + 1;
int y = rand() % col + 1;
//因为我们定义的row和col为9,对9取模只能得到0~8的数字,+1即可得到1~9
if (mine[x][y] == '0')
//如果mine的位置为 0 进入
{
mine[x][y] = '1';
//改mine中的为1
put--;
//我们雷的个数减一
}
}
}
int ShowBoom(char mine[ROWS][COLS], int x, int y)
{
//排查周围8个坐标有没有雷
return(mine[x - 1][y] + mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y] + mine[x][y + 1]
+ mine[x + 1][y + 1] + mine[x + 1][y - 1] + mine[x - 1][y + 1] - 8 * '0');
}
void Find_Boom(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int win = 0;
//如果win小于我们的row*col减去雷的个数,就循环排雷
while(win < row * col - BOOM)
{
printf("请输入要排查的坐标:");
scanf("%d %d",&x,&y);
//如果x大于1,并且小于row 且 y大于1,小于col
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
//如果输入的mine坐标中等于1,就被炸死,游戏结束
if (mine[x][y] == '1')
{
printf("你被炸死了。\n");
//被炸死后打印雷的信息
print_bool(mine, ROW, COL);
//跳出循环,游戏结束
break;
}
//如果没被炸死,则打印周围有几个雷
else
{
int count = ShowBoom(mine, x, y);
show[x][y] = count + '0';
//打印出来给玩家看一眼
print_bool(show, row, col);
//已经排完的+1
win++;
}
}
else
{
printf("输入的坐标错误,请重新输入\n");
}
}
}