扫雷?拿来吧你(递归展开+坐标标记)

文章目录

    • 前言
    • 一、游戏规则
    • 二、效果演示
      • 1、初级版
      • 2、升级版
    • 三、游戏设计思路
    • 四、游戏交互界面
      • 1、创建游戏菜单
      • 2、创建游戏逻辑主体
    • 五、扫雷游戏实现(初级版)
      • 1、游戏框架搭建
      • 2、游戏内函数实现
        • (1)创建雷盘
        • (2)初始化雷盘
        • (3)打印扫雷棋盘
        • (4)布置雷
        • (5)扫雷并判断输赢
    • 六、游戏缺陷
    • 七、扫雷升级版
      • 1、展开功能
      • 2、标记功能
      • 3、搭建游戏菜单
      • 4、排雷函数封装
    • 七、完整代码
      • 1、游戏交互主体-test.c
      • 2、声明部分-game.h
      • 3、函数定义-game.c
    • 总结

前言

如果聊起有什么让大家印象深刻的童年游戏,那么《 扫雷 》的地位应该是不可撼动的。这个搭载在 Windows 系统菜单里的小游戏在当时可谓大红大紫,算的上是我们实打实的童年回忆了。

  • 或许我们在一遍又一遍玩扫雷的同时,都会回忆起小学夏天的那节电脑课上,穿着鞋套,在硕大的白色电脑上,瞒着老师偷偷玩一把扫雷的简单快乐吧~

  • 如今我们用经典的C语言复刻扫雷,还能否勾起你对童年的回忆呢?

扫雷?拿来吧你(递归展开+坐标标记)_第1张图片

一、游戏规则

扫雷游戏规则:玩家需要尽快找出雷区中的所有不是地雷的方块,而不许踩到地雷。

众所周知。点开其中一个小方格之后,数字是几,就说明它周围的八个方位就有几个雷

比如下图中就表示红框内存在2个雷

扫雷?拿来吧你(递归展开+坐标标记)_第2张图片

二、效果演示

1、初级版

扫雷?拿来吧你(递归展开+坐标标记)_第3张图片

2、升级版

扫雷?拿来吧你(递归展开+坐标标记)_第4张图片

三、游戏设计思路

在介绍过三子棋的实现后,要实现扫雷是不是就会感觉就简单一些了呢?其实二者有很多相似的地方,扫雷游戏的实现主要是对下面两个模块的实现:

一、游戏交互界面

  1. 创建游戏菜单
  2. 创建游戏逻辑主体

二、扫雷游戏实现

  1. 创建扫雷棋盘
  2. 初始化扫雷棋盘
  3. 布置雷
  4. 打印扫雷棋盘
  5. 用户排查雷
  6. 判断输赢

这里准备了一张图,便大家更直观的理清游戏实现原理与游戏实现的逻辑 :

扫雷?拿来吧你(递归展开+坐标标记)_第5张图片

四、游戏交互界面

1、创建游戏菜单

代码展示:

void menu()
{
	printf("* * * * * * * * * * * * * * * * * *\n");
	printf("* * * * * * 1.扫雷游戏  * * * * * *\n");
	printf("* * * * * * 0.退出游戏  * * * * * *\n");
	printf("* * * * * * * * * * * * * * * * * *\n");
}

2、创建游戏逻辑主体

代码展示:

int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d",&input);
		switch (input)
		{case 1:
			game();//扫雷游戏
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}

	} while (input);
	return 0;
}

1、srand((unsigned int)time(NULL)) 表示随机数生成函数通过时间戳生成随机数,为下面的布置雷做准备。

2、do-while() 因为游戏至少可以进入一次,至少让玩家输入一次玩或不玩,所以用do-while()循环–循环代码至少执行一次。

观察上面这段代码,其中我们使用了一个game()函数来实现扫雷游戏,但是这个函数还未定义,下面将实现对game()函数的定义。

五、扫雷游戏实现(初级版)

1、游戏框架搭建

代码展示

void game()
{
	//创建雷盘:
	char mine[ROWS][COLS] = { 0 };//存放布置好的雷的信息
	char show[ROWS][COLS] = { 0 };//存放排查出的雷的信息

	//初始化雷盘:
	InitBoard(mine, ROWS, COLS,'0');
	InitBoard(show, ROWS, COLS,'*');

	//布置雷:
	SetMine(mine, ROW, COL);

	//向玩家打印扫雷棋盘:
	DisPlayBoard(show, ROW, COL);

	//排查雷并判断输赢
	FindMine(mine,show,ROW,COL);
}

2、游戏内函数实现

(1)创建雷盘

代码展示:

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2

char mine[ROWS][COLS] = { 0 };//存放布置好的雷的信息
char show[ROWS][COLS] = { 0 };//存放排查出的雷的信息

问题1:这里为什么创建两个棋盘?

假设这里只创建一个棋盘,那么这一个棋盘上就要放置3种信息。分别为雷、非雷、排查出周围雷的信息。虽然这种方法也没太大的问题,但是一个棋盘上放置3种信息会给之后打印棋盘时造成麻烦,不易打印。

所以我们创建两个二维数组,一个数组存放布置好雷的信息;另一个数组存放排查出的雷的信息。这样创建不仅方便之后的打印,也可以将问题分解从而让问题层次更加分明,解题思路更加清晰。

问题2:为什么使用宏常量?

其实这个问题在前面的三子棋游戏中也有介绍。在这里使用宏常量主要是为了方便程序的修改,增加了程序的可塑性。

就像我这里通过宏定义将行列通设定为9,也就是9*9的雷盘。假如后面我想要玩12*12的雷盘,我只需要将宏定义中的9改为12即可,这样就省去了在程序中大量修改的精力,使代码可塑性更高。

问题3:为什么二位数组的行列选择ROWS、COLS而不是ROW、COL

通过扫雷游戏规则我们已知,如果一个位置不是雷,我们要排查它周围8个坐标是不是雷,对于9*9的棋盘,当在排查边界时可能会出现越界的情况。因此我们创建11*11的棋盘这样就很好的解决了这个问题。

那么怎么让用户看到9*9的棋盘呀?其实用户看到的只是程序员想让你看到的,后面我们只需要打印11*11中的9*9就ok了。
扫雷?拿来吧你(递归展开+坐标标记)_第6张图片

(2)初始化雷盘

代码展示:

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;
		}
	}
}

这里就是简单使用两个for循环遍历整个棋盘从而实现棋盘初始化,这里就不在过多赘述。

补充:这里是将mine[ROWS][COLS]中的内容初始化为‘0’show[ROWS][COLS]中内容初始化为‘*’

效果展示:
扫雷?拿来吧你(递归展开+坐标标记)_第7张图片

(3)打印扫雷棋盘

代码展示:

//打印棋盘
DisPlayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 1;
	int j = 1;
	printf("------扫雷游戏------\n");
	for (i = 1; i <= row; i++)
	{

		for (j = 1; j <= col; j++)
		{
			printf("%c ",board[i][j]);
		}
		printf("\n");
	}
}

效果展示:
扫雷?拿来吧你(递归展开+坐标标记)_第8张图片

简单观察不难发现,上述棋盘使用户查找坐标时变得困难,不易于用户输入排雷坐标 ,因此我们可以通过下面简单优化,为棋盘标号:

优化后代码:

//打印棋盘
DisPlayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 1;
	int j = 1;
	printf("------扫雷游戏------\n");
	//打印列号
	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");
	}
}

优化后效果展示:

扫雷?拿来吧你(递归展开+坐标标记)_第9张图片

这样以来雷盘坐标是不是就变得更加直观清楚了 !

(4)布置雷

代码展示:

#define EASY_COUNT 10
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	//布置10个雷
	int count = EASY_COUNT;
	while (count)
	{
		//生产随机的下标
		int x = rand()%row+1;
		int y = rand()%col+1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

1、这里使用宏定义布置雷的个数,便于修改,增加代码的可塑性。

2、rand()函数与srand((unsigned int)time(NULL))函数配合生成随机数,rand()%row+1rand()%col+1保证了生成了横纵坐标在合理范围之内。

补充:这里规定雷为'1'

(5)扫雷并判断输赢

1.排雷原理

在扫雷的时候我们还需要定义一个函数,用来返回排查坐标周围地雷的个数。

下面用一张图来解析排查原理:

假设排查坐标为(x,y),我们可以如右图,依次返回其周围8个坐标下对应的值,由于我们上面规定,雷为‘1’,非雷为‘0’,则字符相加减对应ASCLL码值相加减。

例如:(x,y)周围有1个雷,则7*'0'+'1'-8*'0'即表示‘1’的ASCLL码值减‘0’的ASCLL码值,返回整数1,即周围有1个雷

方案一:逐个遍历
代码展示:

//返回排查坐标周围雷的数量
int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y] +
		mine[x - 1][y - 1] +
		mine[x][y - 1] +
		mine[x + 1][y - 1] +
		mine[x + 1][y] +
		mine[x + 1][y + 1] +
		mine[x][y + 1] +
		mine[x - 1][y + 1] - 8 * '0';
}

方案二:循环遍历
代码展示:

int get_mine_count2(char mine[ROWS][COLS], int x, int y)
{
	int i = 0;
	int count = 0;
	for (i = -1; i <= 1; i++)
	{
		int j = 0;
		for (j = -1; j <= 1; j++)
		{
			if (mine[x + i][y + j] == '1')
				count++;
		}
	}
	return count;
}

2.扫雷流程+判断输赢

设计思路:

1.输入排查的坐标

2.判断坐标合法性
(1)合法 -程序执行下一步
(2)不合法 -重新输入

3.检查坐标处是不是雷
(1)是雷 - 很遗憾炸死了 - 游戏结束
(2)不是雷 - 统计坐标周围有几个雷 - 存储排查雷的信息到show数组,游戏继续

程序执行过程如下图:

扫雷?拿来吧你(递归展开+坐标标记)_第10张图片

代码展示:

注意:row*col- EASY_COUNT表示所有非雷数量,所有非雷坐标全部排查则游戏正常结束,玩家胜利。

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{   //思路:
	//1. 输入排查的坐标
    //2、判断坐标合法性
	//3. 检查坐标处是不是雷
	   // (1) 是雷   - 很遗憾炸死了 - 游戏结束
	   // (2) 不是雷  - 统计坐标周围有几个雷 - 存储排查雷的信息到show数组,游戏继续

	int x = 0;
	int y = 0;
	int win = 0;

	while (win<row*col- EASY_COUNT)
	{
		printf("请输入要排查的坐标:>");
		scanf("%d%d", &x, &y);//x--(1,9)  y--(1,9)

		//判断坐标的合法性
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				DisplayBoard(mine, row, col);
				break;
			}
			else
			{
				//不是雷情况下,统计x,y坐标周围有几个雷
				int count = get_mine_count(mine, x, y);
				show[x][y] = count+'0';
				//显示排查出的信息
				DisplayBoard(show, row, col);
				win++;
			}
		}
		else
		{
			printf("坐标不合法,请重新输入\n");
		}
	}

	if (win == row * col - EASY_COUNT)
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, row, col);//向玩家展示一下布雷棋盘
	}
}

到这里初级扫雷游戏就搭建完成了,我们来试玩一把:

扫雷?拿来吧你(递归展开+坐标标记)_第11张图片

六、游戏缺陷

1.上述代码每次排雷只展开一个坐标,排雷效率较低.

2.没有标记功能,缺少趣味性.

七、扫雷升级版

显然,初级扫雷虽然能实现扫雷游戏的基本功能,但是缺少展开功能和标记功能,扫雷仿佛没了灵魂,那么我们应该如何画龙点睛,让扫雷如虎添翼,更上一层呢?下面我们就针对游戏缺陷进一步改进!

1、展开功能

如果没有思路,我们可以先观察一下游戏中的展开过程:

扫雷?拿来吧你(递归展开+坐标标记)_第12张图片

聪明的你们一定会发现:当排雷坐标周围处地雷数量为0时,棋盘会向周围展开,并且将周围雷的数量显示到屏幕上,直到坐标周围地雷数不为0​​。​

显然,这是一个递归问题,当排雷坐标周围地雷数为0,棋盘会继续遍历周围个坐标,直到坐标周围出现雷,递归停止。

扫雷?拿来吧你(递归展开+坐标标记)_第13张图片

代码展示:

static void Digit_boom(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y, int row, int col, int* win)
{
	if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标合法性
	{
		int ret = Around_num2(mine, x, y);//接受坐标周围雷的数量

		if (ret == 0)//递归条件--周围雷数为0
		{
			(*win)++;//每排查一个坐标,排查次数加1,为判断输赢做准备
			show[x][y] = '0';//显示周围雷数
			int i = 0;
			int j = 0;
			//用两个循环遍历周围8个坐标
			for (i = -1; i <= 1; i++)
			{
				for (j = -1; j <= 1; j++)
				{
					if (show[x + i][y + j] == '*')//递归的坐标必须是未排查过的坐标,
                                                  //防止死递归
					{
						Digit_boom(show, mine, x + i, y + j, row, col,win);
					}
				}
			}
		}
		else
		{    //条件不满足退出递归
			
			(*win)++;//排查坐标,次数加1
			show[x][y] = ret + '0';//显示周围雷数
		}
	}

}

1、递归参数win这里为传址调用,作用为计数器,统计排雷次数,方便之后判断输赢。

2、注意此递归有两个条件:
条件一:if (ret == 0)为大前提,只有周围坐标为0才可能进入递归
条件二:if(show[x+i][y+j]=='*') ,为小前提,进入递归的坐标必须是未排查过的坐标,否则可能重复排查坐标,出现死递归

效果展示:

扫雷?拿来吧你(递归展开+坐标标记)_第14张图片

2、标记功能

标记功能,作用是把玩家确定的雷坐标标记出来,或者是把不确定的雷坐标标记出来。它的作用只是做记号,相比于递归展开,标记功能显然更容易实现。

由于是做标记,我们需要再引入一个标志符号,为了不与雷盘符号*冲突,易于玩家区分,这里使用#作为标记符号。

代码展示:(标记雷)

//标记雷
static void flag(char show[ROWS][COLS], int row, int col)
{

	while (1)
	{
		int x = 0;
		int y = 0;
		printf("请输入标记坐标(输入:0 0退出):>");
		scanf("%d %d",&x,&y);
		if (x >= 1 && x <= row && y >= 0 && y <= col)
		{
			if (show[x][y] != '#')
			{
				show[x][y] = '#';
				break;
			}
			else
			{
					printf("该坐标已经标记,请重新选择\n");
			}

		}
		else
		{
			printf("坐标越界,请重新输入\n");
		}
		if (x == y && x == 0)
			break;
	}
}

代码展示:(取消标记)

static void cancel_flag(char show[ROWS][COLS], int row, int col)
{
	while (1)
	{
		int x = 0;
		int y = 0;
		printf("请输入取消标记坐标(输入:0 0退出):>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 0 && y <= col)
		{
			if (show[x][y] == '#')//比较用双等号!!!
			{
				show[x][y] = '*';
				break;
			}
			else
			{
				printf("该坐标还未标记,请重新选择\n");
			}
		}
		else
		{
			printf("坐标越界,请重新输入\n");
		}
		if (x == y && x == 0)
			break;
	}
}

效果展示 :
扫雷?拿来吧你(递归展开+坐标标记)_第15张图片

3、搭建游戏菜单

当我们添加了这些功能后,为了能够有更好的游戏体验我们可以为这些功能添加一个菜单,如下图:

扫雷?拿来吧你(递归展开+坐标标记)_第16张图片

代码展示:

static void menu2()
{
	printf("┌-----------------------------┐\n"); 
	printf("├**********1.排雷*************┤\n");
	printf("├**********2.标记*************┤\n");
	printf("├**********3.取消标记*********┤\n");
	printf("└-----------------------------┘\n");

}

4、排雷函数封装

在实现了上述功能后,我们可以重新封装排雷函数FindMine()

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int choice = -1;
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < col * row - Easy_Count)
	{
		menu2();
		printf("请选择:>");
		scanf("%d", &choice);
        //标记
		if (2 == choice)
		{
			flag(show, ROW, COL);
			DisplayBoard(show, ROW, COL);

		}
        //取消标记
		else if (3 == choice)
		{
			cancel_flag(show, ROW, COL);
			DisplayBoard(show, ROW, COL);
		}
        //排雷
		else if(1==choice)
		{
			printf("请输入排查坐标:>");
			scanf("%d%d", &x, &y);
			if (x >= 0 && x <= row && y >= 0 && y <= col)
			{
				if (mine[x][y] == '1')
				{
					DisplayBoard(mine, ROW, COL);
					printf("很遗憾,你被炸死了!\n");
					break;	
				}
				else if(show[x][y]=='*')
				{
					Digit_boom(show, mine, x, y, row, col, &win);
					DisplayBoard(show, row, col);
				}
				else
				{
					printf("该坐标已排查,请重新选择\n");
				}

			}
			else
			{
				printf("输入坐标错误,请重新输入\n");
			}
		}
		else
		{
			printf("选择错误,请重新选择\n");
		}
	}
	if (win == col * row - Easy_Count)//非雷坐标全部排查完毕
	{
		DisplayBoard(mine, ROW, COL);
		printf("恭喜你,你赢了咯!\n");
	}
	

}

最终效果展示:

为了让扫雷界面更简洁,在每次打印雷盘前增加了清屏操作:system("cls");

扫雷?拿来吧你(递归展开+坐标标记)_第17张图片

七、完整代码

1、游戏交互主体-test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//游戏菜单
menu()
{
	printf("* * * * * * * * * * * * * * * * * *\n");
	printf("* * * * * * 1.扫雷游戏  * * * * * *\n");
	printf("* * * * * * 0.退出游戏  * * * * * *\n");
	printf("* * * * * * * * * * * * * * * * * *\n");
}
//扫雷游戏
void game()
{
	//创建扫雷棋盘
	char mine[ROWS][COLS];
	char show[ROWS][COLS];
	//初始化扫雷棋盘
	Init(mine, ROWS, COLS, '0');
	Init(show, ROWS, COLS, '*');
	//布置雷
	SetMine(mine, ROW, COL);
	//打印扫雷棋盘
	DisplayBoard(show, ROW, COL);
	//开始扫雷并判断输赢
	FindMine(mine, show, ROW, COL);

}
//交互界面
int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do {
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input);
	return 0;
}

2、声明部分-game.h

#pragma once
//包含头文件
#include 
#include 
#include
#include
#include
//宏定义
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define Easy_Count 10
//初始化雷盘
void Init(char board[ROWS][COLS],int rows,int cols,char x);
//打印雷盘
void DisplayBoard(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);

3、函数定义-game.c

 #define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//初始化雷盘
void Init(char board[ROWS][COLS], int rows, int cols, char x)
{
	int i = 0;
	for (i=0;i<rows;i++)
	{
		int j = 0;
		for (j=0;j<cols;j++)
		{
			board[i][j] = x;
		}
	}
}
//打印雷盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	system("cls");
	printf("-----扫雷游戏-----\n");
	int i = 0;
	int j = 0;
	for (j = 0; j <= col; j++)//打印列号
	{
		printf("%d ",j);
	}
	printf("\n");

	for (i = 1; i <= row; i++)
	{
		printf("%d ",i);//打印行
		for (j = 1; j <=col; j++)
		{
			printf("%c ",board[i][j]);
		}
		printf("\n");
	}
}
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = Easy_Count;
	while (count)
	{
		int i = rand() % row + 1;
		int j = rand() % col + 1;
		if (mine[i][j] == '0')
		{
			mine[i][j] = '1';
			count--;
		}

	}
}
//显示周围雷数
//方案一
static int Around_num1(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y] + mine[x - 1][y - 1] +
		mine[x - 1][y + 1] + mine[x][y - 1] +
		mine[x][y + 1] + mine[x + 1][y] + 
		mine[x + 1][y - 1] + mine[x + 1][y + 1]-8*'0';
}
//方案二
static int Around_num2(char mine[ROWS][COLS], int x, int y)
{
	int i = 0;
	int count = 0;
	for (i = -1; i <= 1; i++)
	{
		int j = 0;
		for (j = -1; j <= 1; j++)
		{
			if (mine[x + i][y + j] == '1')
				count++;
		}
	}
	return count;
}
//标记雷
static void flag(char show[ROWS][COLS], int row, int col)
{

	while (1)
	{
		int x = 0;
		int y = 0;
		printf("请输入标记坐标(输入:0 0退出):>");
		scanf("%d %d",&x,&y);
		if (x >= 1 && x <= row && y >= 0 && y <= col)
		{
			if (show[x][y] != '#')
			{
				show[x][y] = '#';
				break;
			}
			else
			{
					printf("该坐标已经标记,请重新选择\n");
			}

		}
		else
		{
			printf("坐标越界,请重新输入\n");
		}
		if (x == y && x == 0)
			break;
	}
}
//取消标记
static void cancel_flag(char show[ROWS][COLS], int row, int col)
{
	while (1)
	{
		int x = 0;
		int y = 0;
		printf("请输入取消标记坐标(输入:0 0退出):>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 0 && y <= col)
		{
			if (show[x][y] == '#')//比较用双等号!!!
			{
				show[x][y] = '*';
				break;
			}
			else
			{
				printf("该坐标还未标记,请重新选择\n");
			}
		}
		else
		{
			printf("坐标越界,请重新输入\n");
		}
		if (x == y && x == 0)
			break;
	}
}
//如果排雷坐标周围雷数为零,递归展开
static void Digit_boom(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y, int row, int col, int* win)
{
	if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标合法性
	{
		int ret = Around_num2(mine, x, y);//接受坐标周围雷的数量

		if (ret == 0)//递归条件--周围雷数为0
		{
			(*win)++;//每排查一个坐标,排查次数加1,为判断输赢做准备
			show[x][y] = '0';
			int i = 0;
			int j = 0;
			//用两个循环遍历周围8个坐标
			for (i = -1; i <= 1; i++)
			{
				for (j = -1; j <= 1; j++)
				{
					if (show[x + i][y + j] == '*')//递归的坐标必须是未排查过的坐标,防止死递归
					{
						Digit_boom(show, mine, x + i, y + j, row, col,win);
					}
				}
			}
		}
		else
		{
			//条件不满足退出递归
			(*win)++;//排查坐标,次数加1
			show[x][y] = ret + '0';
		}
	}

}
//扫雷菜单
static void menu2()
{
	printf("┌-----------------------------┐\n"); 
	printf("├**********1.排雷*************┤\n");
	printf("├**********2.标记*************┤\n");
	printf("├**********3.取消标记*********┤\n");
	printf("└-----------------------------┘\n");

}
//扫雷并判断输赢

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int choice = -1;
	int x = 0;
	int y = 0;
	int win = 0;
	while (win < col * row - Easy_Count)
	{
		menu2();
		printf("请选择:>");
		scanf("%d", &choice);
		if (2 == choice)
		{
			flag(show, ROW, COL);
			DisplayBoard(show, ROW, COL);

		}
		else if (3 == choice)
		{
			cancel_flag(show, ROW, COL);
			DisplayBoard(show, ROW, COL);
		}
		else if(1==choice)
		{
			printf("请输入排查坐标:>");
			scanf("%d%d", &x, &y);
			if (x >= 0 && x <= row && y >= 0 && y <= col)
			{
				
				if (mine[x][y] == '1')
				{
					
						
					DisplayBoard(mine, ROW, COL);
					printf("很遗憾,你被炸死了!\n");
					break;
					
				}
				else if(show[x][y]=='*')
				{
					Digit_boom(show, mine, x, y, row, col, &win);
					DisplayBoard(show, row, col);
				}
				else
				{
					printf("该坐标已排查,请重新选择\n");
				}

			}
			else
			{
				printf("输入坐标错误,请重新输入\n");
			}
		}
		else
		{
			printf("选择错误,请重新选择\n");
		}
	}
	if (win == col * row - Easy_Count)
	{
		DisplayBoard(mine, ROW, COL);
		printf("恭喜你,你赢了咯!\n");
	}
}

总结

由于作者水平有限,如笔下有误,敬请留言。

如果本文对您有所帮助,请给博主点赞关注哦,笔者会持续更新干货教程,期待与君共勉!

你可能感兴趣的:(『C语言』初阶+进阶,c++,开发语言,c语言)