前言:对于基础版扫雷,你需要掌握的知识有:循环与分支、函数基础、二维数组以及随机数函数(不懂可以看看我这篇文章《随机数函数 和 猜数字游戏》,需要了解rand,srand,time这三个函数);对于进阶版扫雷,你还得了解函数递归调用的思想。
注意:如果想不看解析只看代码,可以直接阅读 “省略注释的全部参考代码(基础版)” 和 “最终参考代码(全)”的内容。
目录
4. 统计地雷函数CountMine 与 排查地雷函数FindMine
2.2.3 递归扩展函数CheckAndExtend 【重点】
1. 扫雷——基础版
1.1 题目要求(基础版)
1. 游戏可以通过菜单实现继续玩或者退出游戏
2. 扫雷的棋盘是5*5的格⼦
3. 随机布置5个雷
4. 通过坐标排查雷:
◦ 如果该坐标不是雷,就显⽰周围有几个雷
◦ 如果该坐标是雷就炸死,游戏失败
◦ 把除10个雷之外的所有雷都找出来,排雷成功
1.2 前期分析与设计
①. 扫雷的过程中,布置的雷和排查出的雷的信息都需要存储,又因为我们需要在5*5的棋盘上布置雷的信息和排查雷,我们⾸先想到的就是创建⼀个5*5的数组来存放信息。
②. 为了区分坐标处有没有雷,我们把没有雷的地方设置成0,有雷的地方设置成1。
类似这样:(白色块数字是数组下标)
首次分析结果:(1)使用一个5*5的二维数组。(2)有雷设为1,无雷设为0。
1.2.1 问题与解答(问答递进式分析)
问题一:排查坐标时,如果该坐标无雷,那我们要怎样显示该坐标周围雷的个数?
答:我们可以遍历统计周围8个坐标,然后把该坐标下的数组元素的 '0' 改成 雷的个数。
问题二:如果我们要统计贴边位置的周围雷的个数( 如下图(1,5)位置 ),我们还能用遍历周围8个坐标的方法吗?
答:如果不做出处理就直接遍历坐标会导致数组越界访问,但我们仍然使用这种方法。处理方法就是:把原来的数组扩展多2行和2列,变成7*7的数组,而我们让玩家看到和操作的只有中间5*5的空间,这样即使是四角处都能正常遍历周围8格。
这样做还有个好处:当玩家输入(1,1)时,就是访问二维数组下标为[1][1]的元素,即输入的坐标与数组下标 一 一对应。
问题三:情景假设:某坐标周围有1个雷,统计完后,该坐标对应的数组元素从0改成了1;可是1又表示此处有雷,这就造成了意义混乱,我们应该如何区分混淆的信息呢?
答:解决方法是我们使用两个二维数组。我们专门给⼀个棋盘(对应⼀个数组mine)存放布置好的雷的信息,再给另外⼀个棋盘(对应另外⼀个数组show)存放排查出的雷的信息,这样就互不干扰了。
两个二维数组的具体作用:
mine棋盘中:0是安全格,1是地雷格。
show棋盘中:“ * ”是未揭开的区域(可能有有雷);“数字”是已知区域,数字的大小反映着周围地雷的个数。
问答分析总结:(1)数组大小不是5*5,而是7*7。(2)我们需要创建两个数组,1个叫mine,1个叫show。(具体作用看上方)
1.2.2 头文件 —— 工程函数目录
我们要创建3个文件:game.h存放宏定义和函数声明;game.c存放游戏函数定义;test.c 写游戏的测试逻辑。
game.h装有所有函数的声明,我们要做的就是实现这些函数,所以我称之为工程目录。如下:
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h> //函数rand、srand需要用到
#include <time.h> //函数time需要用到
#define mine_num 5 //放置5个地雷
#define ROW 5 //可视棋盘大小
#define COL 5
#define ROWS ROW+2 //真实数组大小
#define COLS COL+2
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘
void PrintBoard(char board[ROWS][COLS], int row, int col);
//放置地雷
void SetMine(char mine[ROWS][COLS], int row, int col);
//排查地雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//计算周围8格有多少地雷
int CountMine(char mine[ROWS][COLS], int x, int y);
//游戏前菜单
void menu();
一些说明:
1. 参数表中的“board”:说明该函数适用于mine和show两个数组。
2. 含“rows 和 cols” :说明传参的值是数组真实边长对应的宏定义,即ROWS和COLS。
3. 含“row 和 col”:说明传参的值是可视棋盘边长对应的宏定义,即ROW与COL。
下面我们来实现头文件中的函数。
1.3 代码实现讲解
1. 棋盘初始化函数:InitBoard
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = set; //注意数值与字符
}
}
}
该函数的具体作用是:用set赋值给二维数组的每一个元素。
注意:把mine数组初始化为0时,不是把数值0赋值给元素,而是把 '0' 的数值赋给元素。正确的使用格式是“ InitBoard ( mine, ROWS, COLS, '0' ); ”。
2. 棋盘打印函数:PrintBoard
void PrintBoard(char board[ROWS][COLS], int row, int col)
{
printf("--------扫雷-------\n");
for (int i = 0; i <= col; i++) //细节1
{
printf("%d ", i);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i); //细节2
for (int j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
这个函数是用来打印可视棋盘的,第一个printf的内容是用来装饰的。
细节分析——2个:
细节1 —— 打印横坐标:
- 变量 i 从0开始打印,可以把真实二维数组中的board[0][0]剔除出可操作区域。
- 因为横坐标的坐标数是与数组的列数对应的,所以i到col结束,而不是到row。(如果想弄成 长方形的可视棋盘,必须弄明白这里的对应关系)
- 可视棋盘的第一行用来打印横坐标,把 board[0][0] ~ board[0][7] 都剔除出可操作区域。
细节2 —— 打印纵坐标:
- 变量i会产生行号,而行数对应的是纵坐标,所以我们在每行打印可视区域之前,都打印1个纵坐标数。
- 可视棋盘的第一列用来打印纵坐标,把 board[1][0] ~ board[7][0] 都剔除出可操作区域。
让我们把初始化后的mine和show打印出来看看:
1. InitBoard(mine, ROWS, COLS, '0');
2. InitBoard(show, ROWS, COLS, '*');
3. PrintBoard(mine, ROW, COL);
4. PrintBoard(show, ROW, COL);
3. 设置地雷函数:SetBoard
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = mine_num;
while (count)
{
//⽣成随机的坐标,布置雷
int x = rand() % row + 1; //要点Ⅰ
int y = rand() % col + 1;
if (board[x][y] == '0') //要点Ⅱ
{
board[x][y] = '1';
count--;
}
}
}
count为还要放置的地雷数,每放置一颗就减减,当count为0时说明放置完成。
(不会rand的要看看这个《随机数函数 和 猜数字游戏》)
要点分析——2个:
要点Ⅰ—— 随机坐标的范围控制:
- 我们要求地雷要放置在5*5的格子里,所以下标要控制在1~5(不能是0~5)。此时row和col的值是5,而 rand() % 5 的结果是 0~4,我们只需要再加上1,结果就变成了1~5。
要点Ⅱ—— 防止重复放雷:
为了防止重复放雷,我们放置地雷之前要检查该格是否已经有雷。没有雷就放置,剩余雷数count要减减;有雷就不能放置,剩余雷数count不能变。
我们看一下放置地雷后的mine棋盘:
1. SetMine(mine, ROW, COL);
2. PrintBoard(mine, ROW, COL);
4. 统计地雷函数CountMine 与 排查地雷函数FindMine
先说统计地雷函数CountMine :
int CountMine(char mine[ROWS][COLS], int x, int y)
{
int cnt = 0;
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
cnt += mine[i][j] - '0'; //要点Ⅰ
}
}
return cnt;
}
用cnt统计,然后返回周围的地雷个数。
要点Ⅰ:
- 由于创建的二维数组的元素类型是char,mine棋盘中的信息1和0都是acsii码的值,不能像这样统计:“ cnt += mine[i][j] ”。这样增加的是'0'和'1'的值,远大于数值0和1。
- 而'0' - '0'就等于数值0,'1' - '0'就等于数值1,所以我们用mine[i][j] - '0'来统计。
再看看FindMine函数怎么实现:
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int surplus = ROW * COL - mine_num; //剩余的非雷区域
int x = 0, y = 0;
printf("请按坐标输入你想排除的位置:");
while (surplus) //要点Ⅰ-↓↓
{
scanf("%d %d", &x, &y);
if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
{//mine棋盘(x,y)的位置为1时,说明踩到地雷游戏结束
if (mine[x][y] == '1')
{
break; //细节❶-↓↓
}
else
{//1.如果没炸,show棋盘要显示(x,y)周围的地雷数
int cnt = CountMine(mine, x, y);
show[x][y] = cnt + '0'; //要点Ⅱ
//2.把统计完的个数更新到show棋盘上
PrintBoard(show, ROW, COL);
//3.这里不是雷,已经排查了,剩余非雷的区域要-1
surplus--;
//4.提示玩家还有多少的地方要排查
if(surplus != 0) //细节❷
{
printf("还有%d个坐标没排除,请继续输入:", surplus);
}
}
}
else //要点Ⅲ
{
printf("输入错误,请重新输入:");
}
}
if (surplus == 0) //要点Ⅰ-↑↑
{
printf("\n游戏胜利!!!\n");
}
else //细节❶-↑↑
{
printf("\n游戏失败,你被炸死了!!答案揭晓,数字1的位置是雷:\n");
PrintBoard(mine, ROW, COL);
}
}
分析——3个要点,2个细节:(↓↓与↑↑表示内容相通,合在一起讲解)
要点Ⅰ:surplus停止循环
- 排除雷的形式是把所有不是雷的格子都揭开,变量surplus表示未揭开的非雷格子数。当surplus为0时,说明剩下的5个格子都是雷,已经排除成功了,要结束排查循环。
- surplus等于0是正常的循环结束,说明游戏胜利了,记得告诉玩家。
细节❶:break停止循环
- 玩家输入x和y,如果mine[x][y]的为'1'时,说明踩到地雷游戏结束。break是异常的循环结束,记得告诉玩家游戏失败。
要点Ⅱ:注意数值与字符的区别
- show的元素类型是char,如果"show[x][y] = cnt",那相当于装了int型的数值,值为cnt。正确的是“cnt + '0' ”,这样才是对应char型的数值,值为 'cnt' 。
细节❷:终端装饰优化
- 如果没有这个if条件限制,当surplus为0时,终端先打印 “还有0个坐标没排除,请继续输入:”,再打印“ 游戏胜利!!!”。这样在玩家的角度看会有点奇怪:明明胜利了,都不用再排除了,为什么还要我继续输入坐标呢?
要点Ⅲ:防止越界输入
玩家的可操作区域只有数组下标的1~row, 1~col。所以要先对输入的x和y进行范围检查,再对board[x][y]进行访问;如果超出范围要提示玩家重新输入。
5. 菜单函数menu 与 主函数main()
menu很简单:
void menu()
{
printf("———————————— 扫雷游戏:选择菜单 ————————————\n");
printf("************** 1. 开始游戏 ****************\n");
printf("************** 0. 离开游戏 ****************\n");
printf("———————————(9X9大小,共%d个雷)———————————————\n", mine_num);
printf("请选择操作指令:");
}
(这里用个占位符,方便以后提示放置了雷的个数)
主函数:
int main()
{
int choice = 0;
srand((unsigned int)time(NULL));
do
{
menu();
scanf("%d", &choice);
switch (choice)
{
case 1:
game();
break;
case 0:
printf("---》游戏结束《---\n");
break;
default:
printf("输入无效指令,请重新输入\n");
}
} while (choice != 0);
return 0;
}
这里用switch分支语句,1是进入游戏,2是正常的退出游戏;如果输入了其他数字,要提醒用户重新输入。
1.3 省略注释的全部参考代码(基础版)
game.h
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <time.h>
#define mine_num 5
#define ROW 5
#define COL 5
#define ROWS ROW+2
#define COLS COL+2
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘
void PrintBoard(char board[ROWS][COLS], int row, int col);
//放置地雷
void SetMine(char mine[ROWS][COLS], int row, int col);
//排查地雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//计算周围8格有多少地雷
int CountMine(char mine[ROWS][COLS], int x, int y);
//游戏前菜单
void menu();
game.c
#include"game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void PrintBoard(char board[ROWS][COLS], int row, int col)
{
printf("--------扫雷-------\n");
for (int i = 0; i <= col; i++)
{
printf("%d ", i);
}
printf("\n");
for (int i = 1; i <= row; i++)
{
printf("%d ", i);
for (int j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\n");
}
}
void SetMine(char board[ROWS][COLS], int row, int col)
{
int count = mine_num;
while (count)
{
int x = rand() % row + 1;
int y = rand() % col + 1;
if (board[x][y] == '0')
{
board[x][y] = '1';
count--;
}
}
}
int CountMine(char mine[ROWS][COLS], int x, int y)
{
int cnt = 0;
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
cnt += mine[i][j] - '0';
}
}
return cnt;
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int surplus = ROW * COL - mine_num;
int x = 0, y = 0;
printf("请按坐标输入你想排除的位置:");
while (surplus)
{
scanf("%d %d", &x, &y);
if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
{
if (mine[x][y] == '1')
{
break;
}
else
{
int cnt = CountMine(mine, x, y);
show[x][y] = cnt + '0';
PrintBoard(show, ROW, COL);
surplus--;
if(surplus != 0)
{
printf("还有%d个坐标没排除,请继续输入:", surplus);
}
}
}
else
{
printf("输入错误,请重新输入:");
}
}
if (surplus == 0)
{
printf("\n游戏胜利!!!\n");
}
else
{
printf("\n游戏失败,你被炸死了!!答案揭晓,数字1的位置是雷:\n");
PrintBoard(mine, ROW, COL);
}
}
void menu()
{
printf("———————————— 扫雷游戏:选择菜单 ————————————\n");
printf("************** 1. 开始游戏 ****************\n");
printf("************** 0. 离开游戏 ****************\n");
printf("———————————(9X9大小,共%d个雷)———————————————\n", mine_num);
printf("请选择操作指令:");
}
test.c
#include"project.h"
void game()
{
char mine[ROWS][COLS];
char show[ROWS][COLS];
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
SetMine(mine, ROW, COL);
PrintBoard(show, ROW, COL);
FindMine(mine, show, ROW, COL);
}
int main()
{
int choice = 0;
srand((unsigned int)time(NULL));
do
{
menu();
scanf("%d", &choice);
switch (choice)
{
case 1:
game();
break;
case 0:
printf("---》游戏结束《---\n");
break;
default:
printf("输入无效指令,请重新输入\n");
}
} while (choice != 0);
return 0;
}
2. 扫雷——进阶版
2.1题目更改(进阶版)
更改1:扫雷的棋盘是9*9的格⼦
更改2:默认随机布置10个雷
新增要求:排除雷时,可以自动扩展排除的范围;扩展停止的条件:排查到某个坐标,该坐标下没有雷,但它的周围有地雷,则不再扩展。
2.2 代码优化更新
(注意:没有更新的部分我会把它注释掉)
2.2.1 修改部分宏定义
#define mine_num 10 //放置10个地雷
#define ROW 9
#define COL 9
//#define ROWS ROW+2
//#define COLS COL+2
因为题目规则中,棋盘大小和放置地雷数都变了,所以mine_num要改成10,ROW和COL要改成9。其他不用修改。
2.2.2 PrintBoard棋盘修饰优化
我们原本的棋盘样子太素了,我给出一个装饰好看的版本:
void PrintBoard(char board[ROWS][COLS], int row, int col)
{
//printf("———————————— ↓↓扫雷↓↓ ————————————\n");
printf("\t ╭"); 第二行打印纵坐标
for (int i = 1; i <= col; i++)
printf("%d-", i);
printf("\b╮\n");
for (int i = 1; i <= row; i++) 打印横坐标和棋盘
{
printf("\t");
printf("%d|", i);
//for (int j = 1; j <= col; j++)
//{
//printf("%c ", board[i][j]);
//}
printf("\b|\n");
}
printf("———————————— ↑↑游戏↑↑ ————————————\n\n"); 最后一行增加边界
}
优化后棋盘的样子:
2.2.3 递归扩展函数CheckAndExtend 【重点】
功能实现的推理分析:
1. 新增要求分析:题目要求可以自动扩展排除的范围,而且还有停止扩展的条件,我们可以想到采用递归的思想。(题目做多了就能有这种直觉)
2. 停止条件分析:条件是“该坐标的周围8格有雷就停止”,这前提是我们知道该格子周围有没有雷,所以该扩展函数的前面部分会运用到CountMine函数统计周围地雷数。
3. 思路延伸①:既然扩展函数内部也把周围地雷数统计,那也可以顺便把该坐标对应的show数组元素的值改成周围地雷数。
4. 思路延伸②:既然对该坐标统计了,那剩余非地雷格子数surplus就得减一。因为是surplus是在排除函数FindMine中创建的,所以在扩展函数需要用指针来操作。
5. 写上递归出口:用cnt接收CountMine函数的返回值,当cnt 不等于0时,需要return出去。
6. 递归出口分析:我们可以采用遍历的方式,对周围的8个格子依次递归进去。(迭代嵌套递归)
7. 思路延伸③ —— 防止重复递归:
每次递归会更换方圈的中心点,中心点一换,新的方圈会与旧的方圈有重合(如下图所示),要防止重合的地方再次统计。但是只看mine棋盘的话只知道这里是无雷的,不知道有没有访问过,这时候就得看这里的show棋盘有没有被改过了,如果show棋盘这里已经不是“*”了,那么说明这里访问过,就不再递归统计。(这里还要对坐标进行判断,防止越界访问)
根据上述的推理,我们可以写出下面这个函数:
void CheckAndExtend(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* sur)
{
int cnt = CountMine(mine, x, y);
show[x][y] = cnt + '0';
(*sur)--;
if(show[x][y]=='0')
show[x][y] = ' ';
if (cnt != 0) //迭代出口
return;
else
{
for (int i = x - 1; i <= x + 1; i++) //迭代嵌套递归
{
for (int j = y - 1; j <= y + 1; j++)
{
if(i >= 1 && i <= ROW && j >= 1 && j <= COL) //防止越界
{
if (show[i][j] == '*') //防止重复迭代
CheckAndExtend(mine, show, i, j, sur); //迭代入口
}
}
}
}
}
为了玩家的界面好看,我把show棋盘上的0换成了空格。
2.2.4 FindMine函数的优化
既然扩展函数CheckAndExtend里面又能统计地雷数,又能改变show棋盘,还能让surplus减减。所以要把旧FineMine函数的重复部分删去,如下:
//void FindMine_new(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
//{
// int surplus = ROW * COL - mine_num; //剩余的非雷区域
// int x = 0, y = 0;
// printf("请按坐标输入你想排除的位置:");
//
// while(surplus) //当非雷区域剩余0个时,说明已经赢了
// {
// scanf("%d %d", &x, &y);
// if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
// {
// if (mine[x][y] == '1')
// {
// break;
// }
else //扩展棋盘后才能打印
{
CheckAndExtend(mine, show, x, y, &surplus); 重复的部分被替换成扩展函数
PrintBoard(show, ROW, COL);
if(surplus==0)
{
break;
}
printf("还有%d个坐标没排除,请继续输入:", surplus);
}
// }
// else
// {
// printf("输入错误,请重新输入:");
// }
// }
// if (surplus == 0)
// {
// printf("\n游戏胜利!!!\n");
// }
// else
// {
// printf("\n游戏失败,你被炸死了!!");
// printf("答案揭晓,数字1的位置是雷:\n");
for (int i = 1; i <= row; i++)
{
for (int j = 1; j <= col; j++)
{
if (mine[i][j] == '0')
mine[i][j] = ' ';
}
}
PrintBoard(mine, ROW, COL);
// }
// printf("再接再厉\n\n");
//}
该函数下面的修改是为了让用户界面更好看。
2.3 最终参考代码(全)
game.h
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 9 //可操作的棋盘大小是9X9
#define COL 9
#define ROWS ROW+2 //真实大小是11X11
#define COLS COL+2
#define mine_num 10 //放置地雷数是10
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘
void PrintBoard(char board[ROWS][COLS], int row, int col);
//放置地雷
void SetMine(char mine[ROWS][COLS], int row, int col);
//排查地雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//计算周围8格有多少地雷
int CountMine(char mine[ROWS][COLS], int x, int y);
//菜单函数
void menu();
//递归扩展函数
void CheckAndExtend(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* sur);
game.c
void menu()
{
printf("———————————— 扫雷游戏:选择菜单 ————————————\n");
printf("************** 1. 开始游戏 ****************\n");
printf("************** 0. 离开游戏 ****************\n");
printf("———————————(9X9大小,共%d个雷)———————————————\n", mine_num);
printf("请选择操作指令:");
}
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
board[i][j] = set;
}
}
}
void PrintBoard(char board[ROWS][COLS], int row, int col)
{
printf("———————————— ↓↓扫雷↓↓ ————————————\n");
printf("\t ╭");
for (int i = 1; i <= col; i++)
printf("%d-", i);
printf("\b╮\n");
for (int i = 1; i <= row; i++)
{
printf("\t");
printf("%d|", i);
for (int j = 1; j <= col; j++)
{
printf("%c ", board[i][j]);
}
printf("\b|\n");
}
printf("———————————— ↑↑游戏↑↑ ————————————\n\n");
}
void SetMine(char mine[ROWS][COLS], int row, int col)
{
int count = mine_num;
while(count)
{
//布雷的坐标要随机
int x = rand() % row + 1;
int y = rand() % col + 1;
//防止同一个坐标重复布雷
if (mine[x][y] == '0')
{
mine[x][y] = '1';
count--;
}
}
}
int CountMine(char mine[ROWS][COLS], int x, int y)
{
int cnt = 0;
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
cnt += mine[i][j] - '0';
}
}
return cnt;
}
void CheckAndExtend(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* sur)
{
int cnt = CountMine(mine, x, y);
show[x][y] = cnt + '0';
(*sur)--;
if (show[x][y] == '0')
show[x][y] = ' ';
if (cnt != 0)
return;
else
{
//show[x][y] = ' ';
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
if (i >= 1 && i <= ROW && j >= 1 && j <= COL)
{
if (show[i][j] == '*')
CheckAndExtend(mine, show, i, j, sur);
}
}
}
}
}
void FindMine_new(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
int surplus = ROW * COL - mine_num;
int x = 0, y = 0;
printf("请按坐标输入你想排除的位置:");
while(surplus)
{
scanf("%d %d", &x, &y);
if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
{
if (mine[x][y] == '1')
{
break;
}
else
{
CheckAndExtend(mine, show, x, y, &surplus);
PrintBoard(show, ROW, COL);
if(surplus==0)
{
break;
}
printf("还有%d个坐标没排除,请继续输入:", surplus);
}
}
else
{
printf("输入错误,请重新输入:");
}
}
if (surplus == 0)
{
printf("\n游戏胜利!!!\n");
}
else
{
printf("\n游戏失败,你被炸死了!!");
printf("答案揭晓,数字1的位置是雷:\n");
for (int i = 1; i <= row; i++)
{
for (int j = 1; j <= col; j++)
{
if (mine[i][j] == '0')
mine[i][j] = ' ';
}
}
PrintBoard(mine, ROW, COL);
}
printf("再接再厉\n\n");
}
test.c
void game()
{
char mine[ROWS][COLS];
char show[ROWS][COLS];
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
SetMine(mine, ROW, COL);
PrintBoard(show, ROW, COL);
FindMine_new(mine, show, ROW, COL);
}
int main()
{
int choice = 0;
srand((unsigned int)time(NULL));
do
{
menu();
scanf("%d", &choice);
switch (choice)
{
case 1:
game();
break;
case 0:
printf("---》游戏结束《---\n\a");
break;
default:
printf("输入无效指令,请重新输入\n");
}
} while (choice != 0);
return 0;
本期分享就到这里,感谢您的支持Thanks♪(・ω・)ノ