当前位置:网站首页>该不会还有人不懂用C语言写扫雷游戏吧

该不会还有人不懂用C语言写扫雷游戏吧

2022-07-06 09:11:00 East-sunrise

目录

一.前言

二.扫雷游戏展示及介绍

三.扫雷游戏编程实现

1.初始菜单及大致的程序结构

 2.game( )函数的实现

1.思考与准备

2.初始化扫雷地图

3.打印雷盘

4.布置雷

3.扫雷功能函数实现(game( )函数的核心)

1.扫雷功能代码的基本框架

2.“统计雷”功能的实现 

3.“展开一片”功能的实现 

4.“标记雷”功能的实现

四.最终代码展示

1.test.c源文件代码

 2.game.h头文件代码

 3.game.c源文件代码

五.总结


 

一.前言

扫雷游戏,是一款规则简单,容易上手的一款益智小游戏。该不会有人没玩过吧?上一篇博客我分享了通过C语言实现三子棋。那么扫雷游戏是否也能由我们自己通过C语言来实现呢?不会吧不会吧,该不会还有人不懂用C语言写扫雷游戏吧??不会的!因为,接下来,我将手把手教你实现!

二.扫雷游戏展示及介绍

在这我们先将我们的最终成果呈现出来,了解清楚我们要实现什么,才有思路去执行️

  • 当我们运行程序后会有一个选择菜单供玩家选择开始游戏or退出
  • 选择开始游戏后便会呈现出一个雷盘供玩家游戏

 

  • 而当玩家输入坐标开始扫雷后,若没踩中雷并且此坐标的周围8个位置中有雷的话,玩家排查的坐标就会将周围雷数的信息呈现出来 

  • 如果玩家所选择的坐标周围都没有雷的话,那会自动展开一片,展开到周围有雷的位置就停止。 

  • 而当玩家确定哪个位置是雷时,便可开启“标记”功能,将雷标记出来。
  • 如果最后成功将所有雷的位置都标记出来,则游戏胜利。如果在扫雷的过程中踩到雷,则游戏失败 

了解完我们自己将实现的扫雷,是否和真正的扫雷游戏大致相同呢!?

那么接下来就开启我们的编程之旅吧!! 

三.扫雷游戏编程实现

1.初始菜单及大致的程序结构

  •  每个游戏都会有他的初始菜单,让玩家进行选择。这里我们就简单的给玩家提供游戏or退出两种选择️
  • 而我们想要使得玩家在每次结束一局游戏后,可以再继续选择继续or退出,那么我们就应该构建一个循环结构体。而又因为初始菜单肯定是在游戏开始前就应该呈现出来的,也就意味着还没进行循环判断时就已经执行了———>那么我们就会想到应该使用do...while循环结构️

代码展示:

void menu()
{
	printf("************************\n");
	printf("******   1.play   ******\n");
	printf("******   0.exit   ******\n");
	printf("************************\n");
}
int main()
{
	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( )函数的实现

1.思考与准备

game函数封装了游戏的所有功能,执行game函数也就是开始游戏。

而我们作为游戏开发者需要实现什么功能呢?

        1.呈现雷盘

        2.布置雷

        3.“展开一片”功能

        4.排查雷及标记雷

        5.输赢判断

️因为我们需要封装的函数很多,所以为了代码的可读性较强,我们可以创建一个以.c为后缀名的test.c源文件用于存放main函数;创建一个game.c源文件用于存放对各种功能函数的实现;再创建一个以.h为后缀名的game.h用于存放各个函数的声明

2.初始化扫雷地图

  • 从上面的游戏截图我们可以看到, 这是一个9x9的扫雷地图。根据我们学过的知识很容易想到,我们应该使用一个二维数组去实现它。
  • 在初始化数组的时候,我们可以自己规定元素️。在这我是规定未排查时是一个 ‘*’ 字符,而用与布置雷的那个数组我是准备:以字符‘ 1 ’代表雷,而非雷的位置就用字符‘ 0 ’
  • 而我们布置雷所使用的二维数组肯定不能呈现出来,那不就等于开挂了嘛?又因为当玩家在排查雷,标记雷的时候会对数组的元素进行改变,那么我们可以创建两个数组。一个用于布置雷,存放雷的位置信息;另一个呈现出来供玩家进行操作
  • 而当我们要排查雷的时候是要统计周围一圈有多少个雷,如果要统计的位置处与雷盘的边缘,在统计周围一圈时会出现数组越界访问的问题!️
    所以我们在统计的时候还要对坐标的合法性进行判断,那太麻烦了。我们为了防止越界,不妨在数组的四周都多一行,那么在排查的时候就不会越界了
    这样的话两个数组都要变为11行11列

 代码展示:

void game()
{
	char mine[ROWS][COLS] = { 0 };//定义一个布置雷数组
	char show[ROWS][COLS] = { 0 };//定义一个供玩家操作的数组
	InitBoard(mine, ROWS, COLS, '0');//初始化数组
	InitBoard(show, ROWS, COLS, '*');//初始化数组
}

//遍历数组进行初始化
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;
		}
	}
}

️这里应该注意的是,我们没有直接用数字去定义行和列,而是用变量。如果我们直接用数字去定义,在后期想要改变游戏难度,扩大雷盘(增加行数和列数),那么岂不是要把整个程序所有的行和列都改了?那样的程序耦合度太高,拓展力太差,并不是一个好代码。

所以我们可以在game.h头文件中对行数和列数进行宏定义,然后在两个源文件中分别使用#include"game.h"语句包含这个头文件即可。

3.打印雷盘

打印雷盘这个操作在游戏的过程中需要执行很多次,所以我准备封装一个函数:DisplayBoard(show, ROW, COL);用于打印雷盘(打印数组)

🥲🥲有一说一,博主在写这个函数的时候花了很多时间...其实就是对于打印二维数组的这个知识掌握得还不够牢靠。

我们在打印的时候,先想好每一行每一列由什么元素构成,而在打印的时候是需要通过使用循环,而循环,顾名思义就是一件事情我们重复地去做。所以我们应该把我们要打印的内容去归类 

总结来说就是:尝试去将一些重复的元素抓出来,然后根据需要(需要几行?需要几列?)去进行循环的打印。而对于一些循环次数较为特殊的,我们就对其循环加上判断条件(或单独处理)。

代码展示:

DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("--------------  扫雷游戏  ---------------\n");
	for (i = 0; i <= row; i++)
	{
		if (i == 0)
		{
			for (j = 0; j <= col; j++)
			{
				printf("| %d ", j);
			}
			printf("|\n");
			for (j = 0; j <= col; j++)
			{
				printf("|---");
			}
			printf("|\n");
		}
		else
		{
			for (j = 0; j <= col; j++)
			{
				if (j == 0)
				{
					printf("| %d ", i);
				}
				else
				{
					printf("|");
					printf(" %c ", board[i][j]);
				}
			}
			printf("|\n");
			if (i < col)
			{
				for (j = 0; j <= col; j++)
				{
					printf("|---");
				}
				printf("|\n");
			}
		}
	}
	printf("--------------  扫雷游戏  ---------------\n");
}

4.布置雷

我们准备在雷盘中随机布置雷,所以就需要用到生成随机数的操作(相信各位大佬对于这个操作已经轻车熟路了吧)

而雷的个数我们同样也可以像行和列一样进行宏定义

代码展示:

SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		//判断
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
	}
}
  •  count就代表雷数,我们可以把这个变量放在while循环的判断条件中,巧妙地结合了 0为假,非0为真的道理️只要布置了一个雷count就-1,当count还没减到0就意味着雷还未布置完成,则循环就继续执行
  • 另外,代码写到这里的时候兄弟们不要忘记要在main函数中生成随机数 srand((unsigned)time(NULL)); 还有包含头文件#include<stdlib.h>、#include<time.h>

3.扫雷功能函数实现(game( )函数的核心)

如果说,game( )函数是这个扫雷游戏代码的核心的话,那么接下来的扫雷功能函数则是game( )函数的核心!!重点来了!!敲黑板!!

同样的我们把扫雷功能封装为一个函数FindMine(mine, show, ROW, COL); 

  • 其实当扫雷函数执行时,才是玩家操作的开始。我们上面写的只不过是为玩家扫雷而做的一些准备而已
  • 那么玩家可以有什么操作呢?也就是扫雷函数我们需要实现什么功能呢?
  • 当玩家还未确定雷的位置时,玩家是否只是在进行排查,而在排查后根据游戏所反馈的一些雷的信息再去进一步确定雷的位置?
  • 而当确定雷的位置后就要进行标记了。而玩家在进行每一步操作是否也需要进行输赢的判断呢?(玩家是否踩到雷了)
  • 所以接下来,让我们来啃下这块硬骨头吧!️

1.扫雷功能代码的基本框架

在准备实现扫雷功能时,因为扫雷这个过程会执行许多功能,也有一些我们需要注意的问题。刚开始可能会感觉有点不知从何下手。

那么我们可以模拟自己正要进行扫雷,要去执行什么(=我们需要实现的功能),然后遇到我们要实现的功能时可以先定义成一个函数,将代码的基本框架构建后再去实现。

        首先我们看到了一个每个坐标都未排查的雷盘,那么我们就输入一个坐标(我们可能会按错坐标?——进行坐标合法性判断),当我们选择排查的那个坐标周围一圈甚至更大的范围都没有雷的时候,雷盘自动将非雷的位置都变为空格,自动展开到有雷的信息的位置停止——“展开一片”的功能spread( )函数。当我们排查的坐标不是雷但其周围一圈有雷,此位置便会变成一个数字表示周围一圈的雷数量——统计雷的功能get_mine_count( )函数。当我们通过推断雷的信息,确认了某个坐标即是雷,所以我们想标记他——标记雷的功能SignMin( )函数而当我们标记的位置都是雷并且雷盘中的雷都被我们标记完了的话则游戏胜利——输赢判断的功能

当我们对扫雷的游戏过程有了大致的思路后,便能一步一步地创建出基本的代码框架,而当遇到上面需要实现的功能时,可以先定义成函数再去实现。

 代码展示:

FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{

	int count = COUNT_M;
	int x = 0;
	int y = 0;
	while (count)
	{
		printf("直接输入坐标可进行排查\n");
		printf("输入0 0则开启标记雷功能(再次输入则关闭)\n");
		printf("请输入坐标:-> ");
		scanf("%d %d", &x, &y);
		if (x > 0 && x <= ROW && y > 0 && y <= COL)
		{
			if (mine[x][y] == '1')
			{
				printf("你被炸死了!\n");
				break;
			}
			else if (show[x][y] == ' ')
			{
				printf("此位置已经被排查过,请重新选择\n");
			}
			else if (show[x][y] == '!')
			{
				printf("此位置是您标记的雷,请重新选择\n");
			}
			else
			{

				if (get_mine_count( ) != 0)
				{
					int count = get_mine_count( );
					show[x][y] = count + '0';
					DisplayBoard(show, ROW, COL);
				}
				else
				{
					show[x][y] = ' ';
					spread( );
					DisplayBoard(show, ROW, COL);
				}
			}
		}
		//开启标记功能
		else if (x == 0 && y == 0)
		{
			SignMin( );
			if (count == 0)
				break;
			DisplayBoard(show, ROW, COL);
			printf("剩余雷数:%d\n", count);
		}
		else
		{
			printf("输入的坐标非法,请重新输入\n");
		}
	}
	printf("剩余雷数:%d\n", count);
	if (count == 0)
		printf("扫雷成功,游戏胜利!!\n");
	return 1;
}

2.“统计雷”功能的实现 

我们知道,扫雷游戏的过程中,当玩家选择排查一个坐标的时候,游戏会对其坐标周围一圈8个位置进行排查,然后统计其周围一圈雷的数量并将其数量反映出来。所以我们需要设计一个统计雷的功能

由于由于数组中存放的是字符,想要统计坐标周围一圈的雷数,如果一个一个进行if判断那太麻烦了
所以我们可以想到,因为如果不是雷就是‘0’,是雷的话就是‘1’,那么我们可以把周围八个坐标加起来,再减去8*‘0’

注:字符‘0’的ASCII码值是48,字符‘1’的ASCII值是49

最后我们将一个3x3的数组坐标画出来,就能够对数组进行操作了

代码展示:

int get_mine_count(char board[ROWS][COLS], int x, int y)
{
	return(board[x - 1][y] + board[x - 1][y - 1] + 
		board[x][y - 1] + board[x + 1][y + 1] + 
		board[x + 1][y] + board[x + 1][y - 1] + 
		board[x - 1][y + 1] + board[x][y + 1] - 8 * '0');
}

 另解:

当我们清楚了3x3数组中各个坐标的数值,既然可以一个个加起来,那么是否也可以利用循环呢?

假设我们选择排查的坐标是x , y,当对包括此坐标及其周围一圈一共9个坐标遍历的时候,实际上就是对数组(横坐标)下标x-1,x,x+1,和(纵坐标)下标y-1,y,y+1俩俩组合一共9个坐标进行遍历,那么我们就可以利用循环解决这个问题️

代码展示:

int get_mine_count(char board[ROWS][COLS], int x, int y)
{
    int sum = 0;
	for (int i = -1; i <= 1; i++)
	{
		for (int j = -1;j <= 1; j++)
		{
			sum += (board[i][j] - '0');
			return sum;
		}
	}
}

3.“展开一片”功能的实现 

扫雷游戏为了避免浪费玩家时间,明知道那一片区域没有雷,却还是要一个一个去点,所以是有一个“展开一片”的功能。而我们要如何去实现它呢?那就应该想一想有什么实现的前提条件。 

️BUG之——死递归 

我们会想到:就你选择一个坐标嘛,然后这个位置不是雷(条件1),那这个位置自动变为空格后,它周围一圈也没雷(条件2),而又如果周围一圈的那些坐标周围一圈也没雷,那就都变为空格,到最后就像连环爆炸一样,展开一片......——此时就会想到“递归函数”这把利器了️

但是!

️当你要去实现的时候你会发现最后会陷入死递归(类似死循环,没有终止) 原因如图

我们直接打印出布置雷的数组,模拟上面我们说的那个递归函数思路

    能够执行展开函数的条件就是:1.此位置不是雷 2.其周围一圈也没雷

那么我们假设一开始选择排查红圈位置,而红圈位置不是雷,所以对红圈周围一圈进行判断。红圈周围一圈也不是雷的话,就让其周围那些坐标也递归调用展开函数。假设绿圈位置也调用了展开函数,满足条件1和条件2后让绿圈位置周围一圈的坐标也递归调用,此时就会重新回到红圈位置————陷入死递归

所以综上所述,实现展开一片的递归函数需要3个条件:1.此位置不是雷 2.其周围一圈没有雷 3.该坐标还未被排查过。而递归函数一定需要一个终止条件,这个条件即是:展开到周围一圈有雷的坐标便停止递归调用

代码展示:

spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	
	for (int i = x - 1; i <= x + 1; i++)
	{
		for (int j = y - 1; j <= y + 1; j++)
		{
			if ((i > 0) && (i <= ROW) && (j > 0) && (j <= COL))
			{
				if ((mine[i][j] == '0') && (show[i][j] != ' ') && (get_mine_count(mine, i, j) == 0))
				{
					show[i][j] = ' ';
					spread(mine, show, i, j);
				}
				if (get_mine_count(mine, i, j) != 0)
				{
					int count = get_mine_count(mine, i, j);
					show[i][j] = count + '0';
				}
			}
		}
	}
}

4.“标记雷”功能的实现

当玩家经过排查确认了雷的坐标后,就要对其进行标记

所以我们会想到:是要等玩家排查后想要标记,才开启这个标记雷的功能。那么我们就需要设计得这个功能是由玩家决定什么时候开启

其实并不难实现。就像我们的游戏初始菜单,供玩家选择开始游戏or退出游戏,那么我们可以在这里规定当玩家输入什么之后便开启标记功能,再次输入则关闭标记功能

其次,当我们在标记雷的时候,因为这关乎到游戏是否胜利,所以我们可以设计每次玩家标记过后,我们将剩余雷数呈现出来。

代码展示:

void SignMin(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col,int* win)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("请输入要标记的坐标(输入0 0 则退出标记功能):-> ");
		scanf("%d %d", &x, &y);
		if ((x > 0 && x <= row) && (y > 0 && y <= col))
		{
			if (show[x][y] == '*')//表明没排查过也没标记过
			{
				show[x][y] = '!';//规定标记的记号为‘!’
				if (mine[x][y] == '1')//表示标记正确(标记到雷了)
				{
					(* win)--;//不能是*win--啊
				}
				if (*win == 0)
					return 1;
				DisplayBoard(show, ROW, COL);
				printf("剩余雷数:%d\n", *win);
			}
			else
			{
				if (show[x][y] == '!')//提供玩家取消标记的选择
				{
					show[x][y] = '*';
					if (mine[x][y] == '1')
					{
						(*win)++;
					}
					DisplayBoard(show, ROW, COL);
					printf("剩余雷数:%d\n", *win);
				}
					
				else
					printf("该坐标已被扫过,无需标记\n");
				continue;
			}
		}
		else
		{
			if (x == 0 && y == 0)//退出标记功能
			{
				return 1;
			}
			printf("坐标越界,请重新标记\n");
			continue;
		}
	}
}

这里由2个需要注意的地方:

我们想要剩余雷数经过标记雷功能函数的执行后会发生变化,而雷的数量是全局变量,倘若我们直接以函数参数传入(值传递),在标记雷功能函数中对其进行修改后,当函数结束后,其实际的数值并不会改变。所以我们这里就需要运用指针,达到函数参数为地址传递的效果即可解决问题️

另外一个需要注意的地方是,在函数中对指针进行增减时,博主一开始是写:*win++,但是当执行的时候会出现bug。其实是因为指针运算符 * 的优先级较低,如果按照一开始的写法,是先地址-1再进行解引用。所以我们可以加上括号:(*win)++ 即可解决问题️

四.最终代码展示

当我们将预期的功能函数一个个实现后,我们的编程之旅也即将结束了以下是最终代码展示,如果在哪个环节有可以优化的地方也欢迎大家建议,我们一起交流,一起前进!

1.test.c源文件代码

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
	printf("************************\n");
	printf("******  1. play   ******\n");
	printf("******  0. exit   ******\n");
	printf("************************\n");
}
void game()
{
	char mine[ROWS][COLS] = { 0 };//存放布置好的雷的信息
	char show[ROWS][COLS] = { 0 };//存放排查出的雷的信息
	//初始化数组的内容为指定的内容
	//mine数组在没有布置雷的时候都是字符0
	InitBoard(mine, ROWS, COLS,'0');
	//show数组在没有排查雷的时候都是*表示神秘
	InitBoard(show, ROWS, COLS,'*');
	//设置雷
	SetMine(mine, ROW, COL);
	DisplayBoard(show, ROW, COL);//打印只需要打印9x9即可
	//排查雷
	FindMine(mine, show, ROW, COL);
}

int main()
{
	int input = 0;
	srand((unsigned)time(NULL));
	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头文件代码

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9
#define COL 9

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

#define COUNT_M 10

//初始化雷盘
InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//设置雷
SetMine(char board[ROWS][COLS],int row,int col);
//打印
DisplayBoard(char board[ROWS][COLS],int row,int col);
//扫雷
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"

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

//布置雷
SetMine(char board[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int count = COUNT_M;//宏定义雷的个数
	while (count)
	{
		x = rand() % row + 1;//x范围是0 — row
		y = rand() % col + 1;
		if (board[x][y] != '1')
		{
			board[x][y] = '1';
			count--;
		}
	}
}

//打印雷盘
DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("--------------  扫雷游戏  ---------------\n");
	for (i = 0; i <= row; i++)
	{
		if (i == 0)
		{
			for (j = 0; j <= col; j++)
			{
				printf("| %d ", j);
			}
			printf("|\n");
			for (j = 0; j <= col; j++)
			{
				printf("|---");
			}
			printf("|\n");
		}
		else
		{
			for (j = 0; j <= col; j++)
			{
				if (j == 0)
				{
					printf("| %d ", i);
				}
				else
				{
					printf("|");
					printf(" %c ", board[i][j]);
				}
			}
			printf("|\n");
			if (i < col)
			{
				for (j = 0; j <= col; j++)
				{
					printf("|---");
				}
				printf("|\n");
			}
		}
	}
	printf("--------------  扫雷游戏  ---------------\n");
}

//统计雷
int get_mine_count(char board[ROWS][COLS],int x,int y)
{
	int count = (board[x - 1][y - 1] + board[x - 1][y]
		+ board[x - 1][y + 1] + board[x][y - 1]
		+ board[x][y + 1] + board[x + 1][y - 1]
		+ board[x + 1][y] + board[x + 1][y + 1]) - 8 * '0';
	return count;
}

//展开一片
spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	
	for (int i = x - 1; i <= x + 1; i++)
	{
		for (int j = y - 1; j <= y + 1; j++)
		{
			if ((i > 0) && (i <= ROW) && (j > 0) && (j <= COL))
			{
				if ((mine[i][j] == '0') && (show[i][j] != ' ') && (get_mine_count(mine, i, j) == 0))
				{
					show[i][j] = ' ';
					spread(mine, show, i, j);
				}
				if (get_mine_count(mine, i, j) != 0)
				{
					int count = get_mine_count(mine, i, j);
					show[i][j] = count + '0';
				}
			}
		}
	}
}

//标记雷
void SignMin(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col,int* win)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("请输入要标记的坐标(输入0 0 则退出标记功能):-> ");
		scanf("%d %d", &x, &y);
		if ((x > 0 && x <= row) && (y > 0 && y <= col))
		{
			if (show[x][y] == '*')
			{
				show[x][y] = '!';
				if (mine[x][y] == '1')
				{
					(* win)--;
				}
				if (*win == 0)
					return 1;
				DisplayBoard(show, ROW, COL);
				printf("剩余雷数:%d\n", *win);
			}
			else
			{
				if (show[x][y] == '!')
				{
					show[x][y] = '*';
					if (mine[x][y] == '1')
					{
						(*win)++;
					}
					DisplayBoard(show, ROW, COL);
					printf("剩余雷数:%d\n", *win);
				}
					
				else
					printf("该坐标已被扫过,无需标记\n");
				continue;
			}
		}
		else
		{
			if (x == 0 && y == 0)//退出标记功能
			{
				return 1;
			}
			printf("坐标越界,请重新标记\n");
			continue;
		}
	}
}

//排查雷
FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{

	int win = COUNT_M;
	int* p = &win;
	int x = 0;
	int y = 0;
	while (win)
	{
		printf("直接输入坐标可进行排查\n");
		printf("输入0 0则开启标记雷功能(再次输入则关闭)\n");
		printf("请输入坐标:-> ");
		scanf("%d %d", &x, &y);
		if (x > 0 && x <= ROW && y > 0 && y <= COL)
		{
			if (mine[x][y] == '1')
			{
				printf("你被炸死了!\n");
				break;
			}
			else if (show[x][y] == ' ')
			{
				printf("此位置已经被排查过,请重新选择\n");
			}
			else if (show[x][y] == '!')
			{
				printf("此位置是您标记的雷,请重新选择\n");
			}
			else
			{
				
				if (get_mine_count(mine, x, y) != 0)
				{
					int count = get_mine_count(mine, x, y);
					show[x][y] = count + '0';
					DisplayBoard(show, ROW, COL);
				}
				else
				{
					show[x][y] = ' ';
					spread(mine, show, x, y);
					DisplayBoard(show, ROW, COL);
				}
			}
		}
		//开启标记功能
		else if (x == 0 && y == 0)
		{
			SignMin(mine,show,row, col, p);
			if (win == 0)
				break;
			DisplayBoard(show, ROW, COL);
			printf("剩余雷数:%d\n", win);
		}
		else
		{
			printf("输入的坐标非法,请重新输入\n");
		}
	}
	printf("剩余雷数:%d\n", win);
	if(win == 0)
		printf("扫雷成功,游戏胜利!!\n");
	return 1;
}

五.总结

这次扫雷游戏编程,比我们之前的那次三子棋游戏编程的难度要更上一层。要求我们熟练掌握数组、函数等知识外,还运用了递归、指针的知识,并且整个代码的逻辑也更为复杂️所以我觉得十分值得我们去钻研和实践。在写的过程中我们必不可少会遇到bug,但是我们要以一种好的心态去对待bug,不经历磨练曾能变强,在我们一次次寻找推理bug的原因时,在我们一次次优化代码解决bug时,就是我们变强的过程最后也感谢大家的观看,如果文中有什么错误的地方or可以改进的地方,也欢迎大家指点建议,我们一起交流,一起前进!

码字不易,️点赞关注走一波!respct!

原网站

版权声明
本文为[East-sunrise]所创,转载请带上原文链接,感谢
https://blog.csdn.net/m0_63710200/article/details/124928375