今天给大家带来的是详细版的扫雷,运用到的知识大概有函数,数组,递归等。
好啦现在来开始扫雷的详细讲解把。
一.主函数
void game()
{
;
}
void menu()
{
printf("***********************************\n");
printf("***** 1.play *****\n");
printf("***** 0.exit *****\n");
printf("***********************************\n");
}
int main()
{
int input = 0;
do
{
menu();
printf("请选择开始或退出游戏\n");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("退出成功\n");
break;
default:
break;
}
} while (input);
return 0;
}
首先我们现在main函数中创建出整体的框架,这里我们的游戏菜单界面大家可以自行发挥,首先看一下我这个代码的展示结果吧!
这是我们多次输出的结果。细心的朋友可以发现我们的game函数里面只有一个“;”,这个就是我们下一步执行的任务了。
二. game函数的书写版书及顺序(实现代码的过程)
game函数就是我们执行的主要框架了,这边我会将一些程序步骤分为函数来写,这样会使函数边的清晰明了。在这个标题中不会将所有的函数展开我会先将大致的函数步骤用文字表现出来。
void game()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//棋盘的初始化
//棋盘的打印
//设置雷
//排查雷
}
这就是我们game函数内主要的内容等会我会给大家将这几个分为几个详细的板块来给大家详细的讲一下。
还有就是ROWS,COLS不是一个变量而是#define定义的常量,目的是将来容易修改
#define ROWS 11 #define ROW 9
#define COLS 11 #define COL 9
三.棋盘的初始化
按照打印顺序我们首先来完成棋盘的初始化,我们给它起一个名字InitBoard,话不多说我们直接来写代码
void game()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//棋盘的初始化
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//棋盘的打印
//设置雷
//排查雷
}
void InitBoard(char arr[ROWS][COLS], int rows, int cols,char ret)
{
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
arr[i][j] = ret;
}
}
}
这边就是我们game函数的变化和初始化棋盘的代码了,是不是很简单。这里讲一下为什么要将‘0’‘*’传过去,因为如果不进行传参的化就需要写两个这样的函数了,这样就显的有点麻烦,不过如果你不嫌麻烦完全可以哈。
四.打印棋盘
接下来来到我们的打印棋盘环节,当然这个函数也不是特别的难,相信各位应该都会的,还是像初始化那先先给他起一个名字PrintBoard,话不多说继续上代码
void game()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//棋盘的初始化
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//棋盘的打印
PrintBoard(show, ROW, COL);
//设置雷
//排查雷
}
void PrintBoard(char arr[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int m = 0;
for (m = 0; m <= row; m++)
{
printf("%d ", m);
}
printf("\n");
for (i = 1; i <= row; i++)
{
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
上面就是我们的代码还有运行结果了,这边需要注意的时候打印时要从第1行开始,因为当组合排查一个9宫格内有多少雷是,如果恰巧只进入了9行,那么当判断边缘字符的时候会越界,所以我们直接在格子外面多加一行,也就是如果显示为9则让二维数组的长宽高都为11即可。
五.存放雷也就是藏雷
这边我们需要我们之前提到的一个知识rand()和srand的使用不知道的可以看博主的第一篇文章,这边我们也给他起一个名字SetMine,好了我们继续话不多说上代码
void game()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//棋盘的初始化
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//棋盘的打印
/*PrintBoard(mine, ROW, COL);*/
//设置雷
SetMine(mine, ROW, COL);
PrintBoard(mine, ROW, COL);
//排查雷
}
void SetMine(char arr[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int flag = count;
while (flag)
{
x = rand() % row + 1;//产生1到9的随机值,不用10的原因是
y = rand() % col + 1;//10产生的是0到9的随机值
if (arr[x][y] == '0' && x >= 1 && x <= row && y >= 1 && y <= col)
{
arr[x][y] = '1';
flag--;
}
}
}
上面就是我们的代码和运行图片了,这里没有什么太多需要注意的,需要注意的一点我已经在代码上标明了。
六.排查雷
来打我们的主要函数了这里面会涉及到雷的排查,雷个数的输入,展开棋盘以及标记雷我下面给大家分几个小点来讲,依旧是代码配合文字的形式。
1.排查雷
这里我们给他起一个名字为FindMine,下面展示我们的代码
void game()
{
char mine[ROWS][COLS] = { 0 };
char show[ROWS][COLS] = { 0 };
//棋盘的初始化
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
//棋盘的打印
/*PrintBoard(mine, ROW, COL);*/
//设置雷
SetMine(mine, ROW, COL);
PrintBoard(show, ROW, COL);
//排查雷
FindMine(mine, show, ROW, COL);
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int ch = 0;
while (row*col - count - win)
{
printf("请输入要排查的坐标\n");
scanf("%d%d", &x, &y);
if (show[x][y] == '*')
{
Spread(show, mine, x, y);
}
else
{
printf("该坐标已被占用,请重新输入\n");
}
if (mine[x][y] == '1')
{
printf("不好意思你被炸死了\n");
PrintBoard(mine, ROW, COL);
break;
}
PrintBoard(show, ROW, COL);
PlantFlag(show, ROW, COL);///标注那个坐标是雷
}
if (win == row*col - count)
{
printf("恭喜你你已经排完了所有的雷\n");
win = 0;
}
}
现在开始我们的扫雷游戏就有了基本的雏行了只要把我们的Spread函数,MineCount函数以及PlanFlat函数实现就可以实现我们整个扫雷的功能。那我们话不多说直接开展下一个环节吧。
2.展开棋盘
这边展开棋盘我们会用到递归的方式然后依循环的方式实现展开的各个细节,算了还是话不多说直接上代码吧
void Spread(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)
{
if (x >= 1 && x <= ROW && y <= COL && y >= 1)
{
win++;
int i = 0;
int j = 0;
int ret = MineCount(mine, x, y);
if (ret == 0)
{
show[x][y] = ' ';
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= x + 1; j++)
{
if (show[i][j] == '*')
{
Spread(show, mine, i, j);
}
}
}
}
else
{
show[x][y] = ret + '0';
}
}
}
这时我们的game函数基本上以及完成了,不会做出改变也就不给大家看了,这边跟初级扫雷不一样的是将win设置为全局边量使函数在不断递归和循环中++,仔细看我们上一个小块中排查雷的代码中有win=0这样的代码,这时为了在下一把时,win可以重置。
3.计算9宫格内雷的数量
这边我们会用到一个计算的技巧1+‘0’=‘1’ 2+‘0’=‘2’由此我们可以得到1=‘1’-‘0’,这个函数中我们会用到这个东西,如果知道这个东西代码还是挺简单的,还是话不多说直接上代码好吧:
int MineCount(char mine[ROWS][COLS], int x, int y)
{
return mine[x][y - 1] + mine[x][y + 1]
+ mine[x - 1][y] + mine[x - 1][y + 1] + mine[x - 1][y - 1]
+ mine[x + 1][y] + mine[x + 1][y + 1] + mine[x + 1][y - 1] - 8 * '0';
}
正如大家所见非常简短的一个代码很简单对吧!哈哈那我们继续下一个吧也是我们扫雷的最后一步
4.标记雷以及取消标记
这边我们只需要运用一些循环啊还有数组啊什么的直接写就ok了,我们把怀疑有雷的标记为“!”
那我们话再次不多说,再次上代码
void PlantFlag(char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int ret = 0;
char ch = 0;
int m = 0;
int n = 0;
char a = 0;
while (1)
{
printf("是否需要标记雷的位置Y/N:");
while ((ch = getchar()) != '\n');
scanf("%c", &m);
printf("\n");
if (m == 'Y')
{
printf("请输入排除雷的位置:");
scanf("%d%d", &x, &y);
printf("\n");
if (show[x][y] == '*' && x >= 1 && x <= row && y >= 1 && y <= col)
{
show[x][y] = '!';
}
else
{
printf("输入错误\n");
}
}
else if (m == 'N')
{
printf("取消成功\n");
break;
}
else
{
printf("输入失败请重新输入\n");
}
PrintBoard(show, ROW, COL);
printf("是否要取消标记Y/N\n");
while ((ch = getchar()) != '\n');
int a = getchar();
if (a == 'Y')
{
scanf("%d%d", &m, &n);
if (show[m][n] == '!')
{
show[m][n] == '*';
printf("取消成功\n");
PrintBoard(show, ROW, COL);
}
else
{
printf("此处没有标记\n");
}
}
else
{
printf("退出成功\n");
}
}
}
看着好长不过这个的思路真的很简单这边我把标记和取消标记写到一个函数中了,大家可以分别写,然后用选择语句调用就可以了,当然了哈我这里写的有点锉,相信大家可以写出更好的哈哈,写好了喊我去看看哦!!
好了这就是我们扫雷所有的代码了,有朋友们可能会好奇我的库函数呢还有我的#define呢?哈哈哈我其实分页面写的分别有3个头文件,我下面给大家展示出来
七.全部代码
1.game.h
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define count 10
#define COLS 11
#define ROWS 11
#define COL 9
#define ROW 9
//初始化棋盘
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char ret);
//打印棋盘
void PrintBoard(char arr[ROWS][COLS], int sow, int col);
//设置雷
void SetBoard(char arr[ROWS][COLS], int row, int col);
//查找雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//查看雷的数量
int MineCount(char mine[ROWS][COLS], int x, int y);
//标记雷
void PlantFlag(char show[ROWS][COLS], int row, int col);
//展开函数
void Spread(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y);
2.game.c
#include"game.h"
int win = 0;
//初始化棋盘
void InitBoard(char arr[COLS][ROWS], int rows, int cols, char ret)
{
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
{
arr[i][j] = ret;
}
}
}
//打印棋盘
void PrintBoard(char arr[COLS][ROWS], int sow, int col)
{
int i = 0;
int m = 0;
for (m = 0; m <= sow; m++)
{
printf("%d ", m);
}
printf("\n");
for (i = 1; i <= sow; i++)//必须要是小于等于,因为是从0开始的话一样会越界
{
int j = 0;
printf("%d ", i);
for (j = 1; j <= col; j++)
{
printf("%c ", arr[i][j]);
}
printf("\n");
}
}
void SetBoard(char arr[ROWS][COLS], int row, int col)
{
int ret = count;
while (ret)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (arr[x][y] == '0')
{
arr[x][y] = '1';
ret--;
}
}
}
int MineCount(char mine[ROWS][COLS], int x, int y)
{
return mine[x][y - 1] + mine[x][y + 1]
+ mine[x - 1][y] + mine[x - 1][y + 1] + mine[x - 1][y - 1]
+ mine[x + 1][y] + mine[x + 1][y + 1] + mine[x + 1][y - 1] - 8 * '0';
}
void PlantFlag(char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int ret = 0;
char ch = 0;
int m = 0;
int n = 0;
char a = 0;
while (1)
{
printf("是否需要标记雷的位置Y/N:");
while ((ch = getchar()) != '\n');
scanf("%c", &m);
printf("\n");
if (m == 'Y')
{
printf("请输入排除雷的位置:");
scanf("%d%d", &x, &y);
printf("\n");
if (show[x][y] == '*' && x >= 1 && x <= row && y >= 1 && y <= col)
{
show[x][y] = '!';
}
else
{
printf("输入错误\n");
}
}
else if (m == 'N')
{
printf("取消成功\n");
break;
}
else
{
printf("输入失败请重新输入\n");
}
PrintBoard(show, ROW, COL);
printf("是否要取消标记Y/N\n");
while ((ch = getchar()) != '\n');
int a = getchar();
if (a == 'Y')
{
scanf("%d%d", &m, &n);
if (show[m][n] == '!')
{
show[m][n] == '*';
printf("取消成功\n");
PrintBoard(show, ROW, COL);
}
else
{
printf("此处没有标记\n");
}
}
else
{
printf("退出成功\n");
}
}
}
void Spread(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)
{
if (x >= 1 && x <= ROW && y <= COL && y >= 1)
{
win++;
int i = 0;
int j = 0;
int ret = MineCount(mine, x, y);
if (ret == 0)
{
show[x][y] = ' ';
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= x + 1; j++)
{
if (show[i][j] == '*')
{
Spread(show, mine, i, j);
}
}
}
}
else
{
show[x][y] = ret + '0';
}
}
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int ch = 0;
while (row*col - count - win)
{
printf("请输入要排查的坐标\n");
scanf("%d%d", &x, &y);
if (show[x][y] == '*')
{
Spread(show, mine, x, y);
}
else
{
printf("该坐标已被占用,请重新输入\n");
}
if (mine[x][y] == '1')
{
printf("不好意思你被炸死了\n");
PrintBoard(mine, ROW, COL);
break;
}
PrintBoard(show, ROW, COL);
PlantFlag(show, ROW, COL);///标注那个坐标是雷
}
if (win == row*col - count)
{
printf("恭喜你你已经排完了所有的雷\n");
win = 0;
}
}
3.mine.c
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include"gime.h"
void menu()
{
printf("************************************\n");
printf("**** 1.plat 0.exit ****\n");
printf("************************************\n");
}
void game()
{
char show[ROWS][COLS] = { 0 };
char mine[ROWS][COLS] = { 0 };
//初始化棋盘
//1.扫雷在边缘显示雷的数量的时候会越界则把行和列变为11则不会
//2.需要打印2个棋盘一个是存放雷的一个是展示雷的
InitBoard(show, ROWS, COLS, '*');
InitBoard(mine, ROWS, COLS, '0');
//存放雷的步骤
SetBoard(mine, ROW, COL);
/*PrintBoard(mine, ROW, COL);*/
PrintBoard(show, ROW, COL);
FindMine(mine, show, ROW, COL);//寻找雷
/*PlantFlag(show, ROW, COL);*/
}
int main()
{
srand((unsigned)time(NULL));
int input = 0;
do
{
menu();
printf("请输入1/0\n");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("提出成功\n");
break;
default:
printf("输入错误请从新输入\n");
break;
}
} while (input);
return 0;
}