扫雷基础与进阶(附有详细解析)

devtools/2024/10/18 20:58:49/

前言:对于基础版扫雷,你需要掌握的知识有:循环与分支、函数基础、二维数组以及随机数函数(不懂可以看看我这篇文章《随机数函数 和 猜数字游戏》,需要了解rand,srand,time这三个函数);对于进阶版扫雷,你还得了解函数递归调用的思想。

注意:如果想不看解析只看代码,可以直接阅读 “省略注释的全部参考代码(基础版)” 和 “最终参考代码(全)”的内容。

目录

1. 扫雷——基础版

1.1 题目要求(基础版)

1.2 前期分析与设计

1.2.1 问题与解答(问答递进式分析)

1.2.2 头文件 —— 工程函数目录 

1.3 代码实现讲解

1. 棋盘初始化函数:InitBoard

2. 棋盘打印函数:PrintBoard

3. 设置地雷函数:SetBoard

4. 统计地雷函数CountMine 与 排查地雷函数FindMine

5. 菜单函数menu 与 主函数main()

1.3 省略注释的全部参考代码(基础版)

2. 扫雷——进阶版

2.1题目更改(进阶版)

2.2 代码优化更新

2.2.1 修改部分宏定义

2.2.2 PrintBoard棋盘修饰优化

2.2.3 递归扩展函数CheckAndExtend 【重点】

2.2.4 FindMine函数的优化

2.3 最终参考代码(全)


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);				    //细节2for (int j = 1; j <= col; j++){printf("%c ", board[i][j]);}printf("\n");}
}

这个函数是用来打印可视棋盘的,第一个printf的内容是用来装饰的。

细节分析——2个:

 细节1 —— 打印横坐标:

  1. 变量 i 从0开始打印,可以把真实二维数组中的board[0][0]剔除出可操作区域。
  2.  因为横坐标的坐标数是与数组的列数对应的,所以i到col结束,而不是到row。(如果想弄成   长方形的可视棋盘,必须弄明白这里的对应关系)
  3. 可视棋盘的第一行用来打印横坐标,把 board[0][0] ~ board[0][7] 都剔除出可操作区域。

 细节2 —— 打印纵坐标:

  1. 变量i会产生行号,而行数对应的是纵坐标,所以我们在每行打印可视区域之前,都打印1个纵坐标数。
  2. 可视棋盘的第一列用来打印纵坐标,把 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.这里不是雷,已经排查了,剩余非雷的区域要-1surplus--;//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♪(・ω・)ノ


http://www.ppmy.cn/devtools/97047.html

相关文章

VueX 使用

1.简介 就是用来多组件共享数据的实现用的 2.使用VueX 因为使用的是vue2 所以下的是vuex3 若是vue3 必须下的是 vue4 npm i vuex3 3.搭建环境 1.创建 src/store/index.js //该文件用于创建一个Vuex中最为核心的store//引入VueX import Vuex from vuex import Vue from vu…

[ABC367C] Enumerate Sequences 题解

[ABC367C] Enumerate Sequences 搜索。 考虑使用 DFS 深搜&#xff0c;对于第 i i i 个数&#xff0c;从 1 1 1 到 r i r_i ri​ 枚举&#xff0c;将 a i a_i ai​ 设为当前枚举的数&#xff0c;并进行下一层递归。 对所有的数填完后&#xff0c;判断当前和是否为 k k …

MyCAT读写分离及实现---MySQL5.7的glibc

中间件代理方式的读写分离&#xff0c;在业务代码中&#xff0c;数据库的操作不直接连接数据库&#xff0c;而是先请求到中间件服务器&#xff08;代理&#xff09;&#xff0c;由代理服务器判断是读操作去从数据服务器&#xff0c;写操作去主数据服务器。 中间件代理服务器&am…

SpringBoot MySQL BinLog 监听数据变化(多库多表)

开始 1&#xff1a;引入mysql-binlog-connector-java.jar <!-- binlog --><dependency><groupId>com.zendesk</groupId><artifactId>mysql-binlog-connector-java</artifactId><version>0.27.1</version></dependency>…

使用DOM破坏启动xss

目录 实验环境&#xff1a; 分析&#xff1a; 找破坏点&#xff1a; 查看源码找函数&#xff1a; 找到了三个方法&#xff0c;loadComments、escapeHTM 、displayComments loadComments escapeHTM displayComments&#xff1a; GOGOGO 实验环境&#xff1a; Lab: Exp…

前端面试——js作用域

说一说JS的作用域吧 作用域的分类 作用域分为&#xff1a;全局作用域&#xff0c;函数作用域&#xff0c;块级作用域 作用域的特性 全局作用域&#xff1a; 能够让变量和函数在全局位置访问&#xff0c;其挂载在浏览器的window对象下面 其中var定义的变量和function函数存…

品牌出海新策略:携手TikTok达人,合作孵化IP实现双赢

在当今数字化时代&#xff0c;TikTok达人的IP孵化作为一种创新的合作模式&#xff0c;正逐渐成为品牌出海的新兴策略。通过与有潜力的TikTok达人合作&#xff0c;共同孵化新的IP&#xff0c;品牌不仅能够突破传统营销的局限&#xff0c;还能实现与达人共同成长的双赢局面。本文…

P1587 [NOI2016] 循环之美

[题目通道]([NOI2016] 循环之美 - 洛谷) #include<map> #include<cmath> #include<cstdio> #include<algorithm> #define fp(i,a,b) for(int ia,Ib;i<I;i) #define file(s) freopen(s".in","r",stdin),freopen(s".out&qu…