如何实现精致扫雷游戏(可扩散可标记)---保姆级教程

2023-10-26

目录

思路:

main函数内容:

创建“双子”数组:

初始化数组:

打印棋盘:

布置雷:

排查雷:

初阶:

大致过程:

如何统计周围雷的个数:

判断是否赢得游戏:

进阶:(可扩散可标记)

1.排雷:

2.标记:

标记要更改的时show数组,呈现给玩家标记信息;

 3.取消标记:

 整体代码呈现:

 所有代码呈现:

game.h

game.c

test.c

总结:


这期内容我们来讲讲怎么用C语言实现扫雷游戏:

思路:

首先,还是和上一篇写的N子棋游戏一样,分为三个文件:

game.h  --  函数的声明、宏的定义、头文件的引用

game.c  --  函数的编写

test.c     --  函数的定义、整体逻辑的组织

创建一个棋盘,布置雷,雷的分布当然不能直接展现在玩家的面前,

因此,我们要创建两个数组,一个数组记录雷的分布信息,另一个数组记录玩家排雷的信息;

当所有没有雷的地方都被排干净之后,玩家胜利!

当玩家排到有雷的地方时,就会被炸死;

main函数内容:

首先打印一个目录供玩家选择是否进行游戏:

void menu()
{
	printf("          1.play         \n");
	printf("          0.exit         \n");
}

这是就需要创建一个变量input来接受玩家的选择,从而给出相应的反应:

当玩家选择1,开始游戏;

当玩家选择0,退出游戏,即退出程序;

每次游戏结束都判断一下玩家是否要再来一局游戏,打印目录供玩家选择;

因此,就形成了一种循环结构: 

do...while循环

先打印一次目录,供玩家选择,若选择1,则进入游戏;若选择0,则退出循环;

而玩家的选择又是一种选择结构:

switch语句

结合二者:

将switch语句嵌入到do...while语句中:

int main()
{
	int input = 0;
	srand((unsigned int)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);       //while语句使用input作为判断条件,看似随意,实则十分巧妙
	return 0;              //input不仅可以当作switch语句的选择条件,当玩家选择0,即退出游戏时
}                          //input为0,条件为假,跳出循环;玩家不选择0,无论是选择1,还是选择 
                           //错误,都为非0,条件为真,继续循环。

不太了解switch语句的可以看看我之前写的文章 

switch语句--超详解

 main函数完成了,剩下的大头就是game函数了:

创建“双子”数组:

上面提到过我们要创建两个二维数组,

一个用来存放布置雷的信息

一个用来存放排查雷的信息

	char mine[ROWS][COLS] = { 0 };  //布置雷信息的数组
	char show[ROWS][COLS] = { 0 };  //玩家排雷的数组

数组中的 ROWS 与 COLS 为宏常量,

为了方便控制棋盘的大小以及游戏的难度,我们就可以定义宏常量来代替数组的大小,当想要增加或降低游戏难度时,就可以只修改宏,便将整个程序所用到的该常量修改。

ps:宏常量的定义要在game.h头文件中定义

#define ROW 9           //行数
#define COL 9           //列数

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

#define MINE_NUM 10     //控制雷的数量,后面会讲解

这时,肯定会有细心的朋友发现,我定义了两类宏:ROW  COL 与 ROWS COLS ,而且后者永远比前者多2。

这就要考虑到我们排雷时统计周围雷个数的问题了:

当我们排查雷时,要看这个格子周围的8个格子有没有雷,但是处于边界的格子周围总是不足8个格子,这时我们就需要将数组扩大一圈,这样边界的格子周围也有8个格子了,便于统计周围雷的个数。

 由此,我们无论是布置雷,还是排查雷 都只使用中间的ROW行、COL列。

初始化数组:

    mine数组初始化为'0'
    show数组初始化为'*'

创建一个初始化数组的函数:

	init_board(mine, ROWS, COLS, '0');
	init_board(show, ROWS, COLS, '*');
void init_board(char board[ROWS][COLS], int rows, int cols, char ch)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = ch;
		}
	}
}

由于我们要初始化两个数组,而且初始化内容也不一样,

所以,我们直接将初始化的字符也传参过去,这样就可以想初始化为什么就初始化为什么了。

打印棋盘:

上面我们已经说过了,布置雷和排查雷我们只需用到中间的ROW行 与 COL列的数组,

所以打印棋盘时,也只需打印中间这些部分

为了方便玩家排雷输入坐标的准确性,我们也可以在棋盘的外围打印出行数与列数。

另一方面,玩家输入的坐标可以直接作为二维数组的下标,因为真正的棋盘行列的下标都是从1开始的。

  

	display_board(mine, ROW, COL);   //注意:这里传参传ROW与COL
	display_board(show, ROW, COL);
void display_board(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	//打印列数
	for (i = 0; i <= col; i++)
	{
		printf("%d ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)       //第一行已经打印了一个0,所以这里i从1开始。
	{
		//打印行数
		printf("%d ", i);
		for (j = 1; j <= col; j++)
		{
			printf("%c ", board[i][j]);   //i与j都从1开始,正好符合玩家坐标的输入
		}
		printf("\n");
	}
}

布置雷:

要在mine数组中存放雷的信息,mine数组原本全是'0',布置雷的地方就被改成'1'

其次,我们想到的就是雷分布的随机性,

这里我们还是使用rand-srand函数:

定义两个变量n、m,用来存放布置的雷的坐标。

int n = rand() % ROW + 1;

int m = rand() % COL + 1;

(rand的取值范围为 0 ~ 32767 ,% ROW 后 ,即rand() % ROW 的取值范围为 0~ROW-1

再加上1 之后,n的取值范围就为1~ROW,符合棋盘的大小;

同理,m的取值范围也为1~COL)

 

srand要在主函数起始位置定义,同时通过time库函数,运用时间戳来确保随机性,

因为时间是 无时无刻都在变化的。

srand函数要包含头文件<stdlib.h>

time函数要包含头文件<time.h>

#include <stdlib.h>
#include <time.h>

 还要定义一个变量k存储雷的数量:

int k = MINE_NUM;

用循环的结构将MINE_NUM个雷全部布置下来,

每布置一个雷,k就减1

当然,还有一个非常重要的细节,布置过雷的地方不能再布置雷了,

因此,我们需要先判断一下该位置有没有雷,没有雷时,我们再布置雷。

void set_mine(char board[ROWS][COLS], int row, int col)
{
	int k = MINE_NUM;
	int n = 0;
	int m = 0;
	while (k)                  //循环布置雷,当要布置的雷的数量为0,即全部的雷已经布置完成
	{                          //k=0时,跳出循环。
		n = rand() % ROW + 1;
		m = rand() % COL + 1;
		if (board[n][m] == '0')//判断该位置是否有雷,为'0'则没有雷
		{
			board[n][m] = '1'; //布置雷
			k--;               //雷的数量减1
		}
	}
}

排查雷:

初阶:

大致过程:

输入一个坐标,判断该位置有没有雷,

有雷,玩家被炸死,游戏结束

没有类,则显示该坐标的周围8个格子一共有几个雷

排查雷是一个循环的过程,要一直进行下去,除非玩家被炸死,或者玩家赢得游戏才会结束。

每次排完雷之后判断一下玩家是否赢得游戏,当棋盘上只剩下雷的时候,玩家赢得游戏。

如何统计周围雷的个数:

这就要联系到我们初始化mine二维数组时,将它初始化为'0'了。

布置雷时,我们将'0'改为'1',

统计周围雷的个数时,我们只需将周围的数加起来就好

都是我们发现,这里的'0'与'1'都是字符啊,怎么能加起来呢?

这里就有一个知识点:

字符相加时,加的都是ASCII值,字符'0' 的ASCII值为48,字符'1'的ASCII值为49

'0' - '0' = 0(字符'0'减字符'0'等于数字0),'1' - '0' = 1(字符'1'减字符'0'等于数字1);

因此,我们只需将周围8个字符加起来,再减去8个'0',就可以得到周围雷的个数了。


讲到这里,肯定有人会问,那直接创建int 类型数组,放数字在里面不就好了,

但是,我们不仅要统计周围雷的个数,还要将这个数放到show数组里,呈现给玩家

还是统一两个数组的类型比较好

(若是你实在要用int 类型也是可以的)


得到周围雷的个数后,此时的类型为整形(减去8个'0'后),我们要再加上一个'0'后,变为字符类型,放到show数组里

(直接加起来减去7个'0'也是可以的)

判断是否赢得游戏:

在循环外定义一个int类型变量win,每当排除一个坐标后win就加一

循环部分的最后判断win的大小

win=ROW*COL-MINE_NUM时,就代表棋盘上只剩下雷,此时就赢游戏了。

break跳出循环。

函数定义: 

find_mine(mine, show, ROW, COL);

函数内容: 

void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int n = 0;
	int m = 0;
	int win = 0;
	while (1)
	{
		printf("请输入您要排查的坐标:>");
		scanf("%d%d", &n, &m);           //输入要排查的坐标
		if (n >= 1 && n <= row && m >= 1 && m <= col)  //判断坐标是否合法,即是否再棋盘上
		{
			if (mine[n][m] == '1')     //判断该坐标是否为雷
			{
				printf("很遗憾,您被炸死了\n");  //为雷则被炸死,游戏结束
				display_board(mine, ROW, COL);  //打印一下雷的分布,让玩家瞑目
				break;                          //被炸死,游戏结束,跳出循环
			}
			else
			{
				int num = get_mine_num(mine, n, m);  //统计周围雷个数的函数
				show[n][m] = num + '0';              //加'0',变为字符
				display_board(show, ROW, COL);       //打印棋盘,让玩家获得排查雷的信息
				win++;
				if (win == row * col - MINE_NUM)     //判断是否获胜
				{
					printf("恭喜你,赢得游戏!\n");
					break;       //赢得游戏,游戏结束,跳出循环
				}
			}
		}
		else           //坐标非法,进入下一次循环,重新输入排查坐标
			printf("该坐标非法,");
	}
}
//统计周围雷个数的函数
int get_mine_num(char mine[ROWS][COLS], int n, int m)
{
	return (mine[n - 1][m]
		+ mine[n - 1][m - 1]
		+ mine[n][m - 1]
		+ mine[n + 1][m - 1]
		+ mine[n + 1][m]
		+ mine[n + 1][m + 1]
		+ mine[n][m + 1]
		+ mine[n - 1][m + 1]) - 8 * '0';  //将周围8个格子的字符加起来,减去8个'0'
}

进阶:(可扩散可标记)

  

进阶版本与初阶版本相比,无非就是多了扩散与标记功能,其他功能都是一样的:

因为多了标记功能,所以在玩家操作的时候就不止排雷一种操作了,要让玩家选择操作

有了标记功能,当然也要有取消标记的功能,这两个功能是共存的。

所有的操作都要在循环中进行,除非被雷炸死,或者赢得游戏游戏结束,才能退出循环,结束游戏。

在循环刚开始我们就要打印一个目录,让玩家选择操作:

void menu_word()
{
	printf("          1.排雷         \n");
	printf("          2.标记         \n");
	printf("          3.取消标记         \n");
}

 涉及到选择,当然就离不开switch选择结构:

1.排雷:

其实排雷的过程和初阶版本的差不多,只不过排雷的函数变了,其余功能都一样:

        case 1:
		{
			int n = 0;
			int m = 0;
			printf("请输入您要排查的坐标:>");
			scanf("%d%d", &n, &m);
			if (n<1 || n > ROW || m < 1 || m > COL) //判断坐标的合法性
			{
				printf("输入坐标有误\n");   //不合法,就提示玩家坐标有误
				continue;                  //continue进入下一次循环
			}
			if (show[n][m] != '*' && show[n][m] != '#')//判断该坐标是否已经被排查过了
			{                                          //#为标记字符
				printf("该坐标已被占用\n");    //被排查过了,就提示玩家坐标已被占用
				continue;                     //continue进入下一次循环
			}
			else if (mine[n][m] == '1')   //判断该坐标是否有雷
			{
				system("cls");      //清理界面,使界面显得整洁
				printf("您被炸死了\n");  //有雷,则玩家被炸死
				display_board(mine, ROW, COL);  //打印雷的分布,让玩家死的瞑目
				flag = '0';
				break;            //break跳出switch语句
			}           //(注意:这里跳出的是switch语句,而不是循环,因为switch语句
			else        //对break语句有反应,只是跳出switch语句,而switch语句对continue语
			{           //句没有反应,所以continue时就是直接进入下一次循环)
				find_mine(mine, show, ROW, COL, n, m, pw);//排查雷的函数
				system("cls");    //清理界面,使界面显得整洁
				display_board(show, ROW, COL);  //打印棋盘,让玩家获得排雷的信息
				if (win == ROW * COL - MINE_NUM) //判断是否赢得游戏
				{
					system("cls");  //清理界面,使界面显得整洁
					printf("恭喜你,赢得游戏!\n");
					display_board(mine, ROW, COL);  //打印雷的分布
					flag = '0';
					break;
				}
			}
			break;
		}

  

 上面的代码段里说过了,在switch语句中的break只能跳出switch语句,

因此,我们在循环外定义了一个字符变量flag,当要退出循环时,在break之前,将flag赋值为'0'

在循环的最后部分,判断flag是否为'0',若是,则再来一个break跳出循环;若不是,则继续循环。

排雷的函数:

其实扩散就是利用函数递归来实现的

当该坐标周围没有雷时,就将周围8个坐标也点开,

直到点开的那个坐标周围有雷时,就不再扩散了,递归终止。

find_mine(mine, show, ROW, COL, n, m, pw);
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int n, int m, int* pw)
{ //n,m为玩家要排查的坐标;pw为win变量的地址,传递至可以通过形参来改变实参
	if (n >= 1 && n <= row && m >= 1 && m <= col)  //判断坐标是否合法
	{
		if (show[n][m] == '*' || show[n][m] == '#')//判断坐标是否被占用(#为标记字符)
		{
			int num = get_mine_num(mine, n, m);    //计算该坐标周围的雷数
			char ch = num + '0';
			show[n][m] = ch;                       //放到show数组里
			(*pw)++;                               //win++ (每排查一个,就加一)
			if (ch == '0')        //判断该坐标周围是否有雷,
			{                    //没有雷,则进入递归;有雷,则跳过,不递归
				find_mine(mine, show, ROW, COL, n - 1, m, pw);  //周围8个格子统统来一遍
				find_mine(mine, show, ROW, COL, n - 1, m - 1, pw);
				find_mine(mine, show, ROW, COL, n, m - 1, pw);
				find_mine(mine, show, ROW, COL, n + 1, m - 1, pw);
				find_mine(mine, show, ROW, COL, n + 1, m, pw);
				find_mine(mine, show, ROW, COL, n + 1, m + 1, pw);
				find_mine(mine, show, ROW, COL, n, m + 1, pw);
				find_mine(mine, show, ROW, COL, n - 1, m + 1, pw);
			}
		}
	}
}
//计算周围雷数量的函数
int get_mine_num(char mine[ROWS][COLS], int n, int m)
{
	return (mine[n - 1][m]
		+ mine[n - 1][m - 1]
		+ mine[n][m - 1]
		+ mine[n + 1][m - 1]
		+ mine[n + 1][m]
		+ mine[n + 1][m + 1]
		+ mine[n][m + 1]
		+ mine[n - 1][m + 1]) - 8 * '0';
}

2.标记:

  

标记要更改的时show数组,呈现给玩家标记信息;

将要标记的地方改为'#';

要注意的是:已经被排查过的地方不能被标记;

		case 2:
			sign_mine(show, ROW, COL);
			break;
void sign_mine(char show[ROWS][COLS], int row, int col)
{
	int n = 0;
	int m = 0;
	printf("请输入要标记的坐标:>");
	scanf("%d%d", &n, &m);
	if (show[n][m] == '*') //只有当该坐标上的字符为'*'时,才可以被标记
	{
		show[n][m] = '#';  //将'*'改为'#',即为标记
		system("cls");     //清空界面,使界面整洁
		display_board(show, ROW, COL); //打印棋盘,呈现给玩家信息
	}
	else     //若不能被标记,则提醒玩家,让玩家重新选择操作
	{
		printf("该坐标不可标记\n");
	}
}

 3.取消标记:

就是将标记的地方重新改为'*';

注意:只有'#'可以被更改。

		case 3:
			c_sign(show, ROW, COL);
			break;
void c_sign(char show[ROWS][COLS], int row, int col)
{
	int n = 0;
	int m = 0;
	printf("请输入要取消标记的坐标:>");
	scanf("%d%d", &n, &m);
	if (show[n][m] == '#')      //判断是否可以取消标记
	{
		show[n][m] = '*';      //更改为'*'
		system("cls");         //清理界面,使界面整洁
		display_board(show, ROW, COL); //打印棋盘,给玩家信息
	}
	else     //若该坐标上的字符不为'#',则不可取消标记,
	{        //提醒玩家该位置未被标记,让玩家重新选择操作
		printf("该坐标未被标记\n");
	}
}

 整体代码呈现:

	int win = 0;          //win变量,用于判断是否赢得游戏
	int* pw = &win;       //取地址,用于传参,通过形参来改变实参
	char flag = '?';      //由于判断游戏的状态
	while (1)             //进入循环,开始操作,一直循环,除非break跳出循环
	{
		menu_word();      //打印目录,让玩家选择操作内容
		int input = 0;
		printf("请选择您的操作:>");
		scanf("%d", &input);
		switch (input)    //根据玩家的选择,执行程序
		{
		case 1:           //排雷
		{
			int n = 0;
			int m = 0;
			printf("请输入您要排查的坐标:>");
			scanf("%d%d", &n, &m);  //输入排雷的坐标
			if (n<1 || n > ROW || m < 1 || m > COL)  //判断坐标是否合法
			{
				printf("输入坐标有误\n");
				continue;                    //若坐标非法,则continue进入下一次循环
			}
			if (show[n][m] != '*' && show[n][m] != '#')  //判断坐标是否被占用
			{
				printf("该坐标已被占用\n");
				continue;                   //若坐标被占用,则continue进入下一次循环
			}
			else if (mine[n][m] == '1')     //判断该坐标是否有雷
			{
				system("cls");           
				printf("您被炸死了\n");     //若有雷,则被炸死,游戏结束
				display_board(mine, ROW, COL); //打印雷的分布,让玩家死的瞑目
				flag = '0';     //将flag赋值为'0',用于后面的判断,表示游戏结束
				break;          //break跳出switch语句
			}
			else               //若该坐标不为雷,则进行排雷
			{
				find_mine(mine, show, ROW, COL, n, m, pw);  //排雷的函数
				system("cls");
				display_board(show, ROW, COL);   //打印棋盘,提供玩家排雷信息
				if (win == ROW * COL - MINE_NUM)  //判断是否赢得游戏
				{
					system("cls");
					printf("恭喜你,赢得游戏!\n");
					display_board(mine, ROW, COL); //打印雷的分布
					flag = '0'; //将flag赋值为'0',用于后面的判断,表示游戏结束
					break;      //break跳出switch语句
				}
			}
			break;
		}
		case 2:
			sign_mine(show, ROW, COL);  //进行标记的函数
			break;
		case 3:
			c_sign(show, ROW, COL);     //取消标记的函数
			break;
		default:
			printf("选择错误\n");       //若玩家选择错误,则提醒玩家,让玩家重新选择
			break;
		}
		if (flag == '0')  //判断flag的值,若为'0',则代表游戏结束,break跳出循环
		{                 //否则,进行下一次循环
			break;
		}
	}

 所有代码呈现:

game.h

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW 9
#define COL 9

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

#define MINE_NUM 10

//初始化棋盘的声明
void init_board(char board[ROWS][COLS], int rows, int cols, char ch);

//打印棋盘的声明
void display_board(char board[ROWS][COLS], int row, int col);

//布置雷的声明
void set_mine(char board[ROWS][COLS], int row, int col);

//排查雷的声明
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

//标记雷的声明
void sign_mine(char show[ROWS][COLS], int row, int col);

//取消标记的声明
void c_sign(char show[ROWS][COLS], int row, int col);

game.c

#include "game.h"

//初始化棋盘的函数
void init_board(char board[ROWS][COLS], int rows, int cols, char ch)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = ch;
		}
	}
}

//打印棋盘的函数
void display_board(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	//打印列数
	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");
	}
}

//布置雷的函数
void set_mine(char board[ROWS][COLS], int row, int col)
{
	int k = MINE_NUM;
	int n = 0;
	int m = 0;
	while (k)
	{
		n = rand() % ROW + 1;
		m = rand() % COL + 1;
		if (board[n][m] == '0')
		{
			board[n][m] = '1';
			k--;
		}
	}
}

//排查雷的函数
//void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
//{
//	int n = 0;
//	int m = 0;
//	int win = 0;
//	while (1)
//	{
//		printf("请输入您要排查的坐标:>");
//		scanf("%d%d", &n, &m);
//		if (n >= 1 && n <= row && m >= 1 && m <= col)
//		{
//			if (mine[n][m] == '1')
//			{
//				printf("很遗憾,您被炸死了\n");
//				display_board(mine, ROW, COL);
//				break;
//			}
//			else
//			{
//				int num = get_mine_num(mine, n, m);
//				show[n][m] = num + '0';
//				display_board(show, ROW, COL);
//				win++;
//				if (win == row * col - MINE_NUM)
//				{
//					printf("恭喜你,赢得游戏!\n");
//					break;
//				}
//			}
//		}
//		else
//			printf("该坐标非法,");
//	}
//}

int get_mine_num(char mine[ROWS][COLS], int n, int m)
{
	return (mine[n - 1][m]
		+ mine[n - 1][m - 1]
		+ mine[n][m - 1]
		+ mine[n + 1][m - 1]
		+ mine[n + 1][m]
		+ mine[n + 1][m + 1]
		+ mine[n][m + 1]
		+ mine[n - 1][m + 1]) - 8 * '0';
}





//排查雷的函数 (可以扩散排查)!!!
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int n, int m, int* pw)
{
	if (n >= 1 && n <= row && m >= 1 && m <= col)
	{
		if (show[n][m] == '*' || show[n][m] == '#')
		{
			int num = get_mine_num(mine, n, m);
			char ch = num + '0';
			show[n][m] = ch;
			(*pw)++;
			if (ch == '0')
			{
				find_mine(mine, show, ROW, COL, n - 1, m, pw);
				find_mine(mine, show, ROW, COL, n - 1, m - 1, pw);
				find_mine(mine, show, ROW, COL, n, m - 1, pw);
				find_mine(mine, show, ROW, COL, n + 1, m - 1, pw);
				find_mine(mine, show, ROW, COL, n + 1, m, pw);
				find_mine(mine, show, ROW, COL, n + 1, m + 1, pw);
				find_mine(mine, show, ROW, COL, n, m + 1, pw);
				find_mine(mine, show, ROW, COL, n - 1, m + 1, pw);
			}
		}
	}
}

//标记雷的函数
void sign_mine(char show[ROWS][COLS], int row, int col)
{
	int n = 0;
	int m = 0;
	printf("请输入要标记的坐标:>");
	scanf("%d%d", &n, &m);
	if (show[n][m] == '*')
	{
		show[n][m] = '#';
		system("cls");
		display_board(show, ROW, COL);
	}
	else
	{
		printf("该坐标不可标记\n");
	}
}

//取消标记的函数
void c_sign(char show[ROWS][COLS], int row, int col)
{
	int n = 0;
	int m = 0;
	printf("请输入要取消标记的坐标:>");
	scanf("%d%d", &n, &m);
	if (show[n][m] == '#')
	{
		show[n][m] = '*';
		system("cls");
		display_board(show, ROW, COL);
	}
	else
	{
		printf("该坐标未被标记\n");
	}
}

test.c

#include "game.h"

void menu()
{
	printf("          1.play         \n");
	printf("          0.exit         \n");
}

void menu_word()
{
	printf("          1.排雷         \n");
	printf("          2.标记         \n");
	printf("          3.取消标记         \n");
}

void game()
{
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	//初始化棋盘
	//mine数组初始化为'0'
	//show数组初始化为'*'
	init_board(mine, ROWS, COLS, '0');
	init_board(show, ROWS, COLS, '*');

	//打印棋盘
	//display_board(mine, ROW, COL);
	display_board(show, ROW, COL);

	//布置雷
	set_mine(mine, ROW, COL);
	//display_board(mine, ROW, COL);

	//排查雷
	//find_mine(mine, show, ROW, COL);
	int win = 0;
	int* pw = &win;
	char flag = '?';
	while (1)
	{
		menu_word();
		int input = 0;
		printf("请选择您的操作:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
		{
			int n = 0;
			int m = 0;
			printf("请输入您要排查的坐标:>");
			scanf("%d%d", &n, &m);
			if (n<1 || n > ROW || m < 1 || m > COL)
			{
				printf("输入坐标有误\n");
				continue;
			}
			if (show[n][m] != '*' && show[n][m] != '#')
			{
				printf("该坐标已被占用\n");
				continue;
			}
			else if (mine[n][m] == '1')
			{
				system("cls");
				printf("您被炸死了\n");
				display_board(mine, ROW, COL);
				flag = '0';
				break;
			}
			else
			{
				find_mine(mine, show, ROW, COL, n, m, pw);
				system("cls");
				display_board(show, ROW, COL);
				if (win == ROW * COL - MINE_NUM)
				{
					system("cls");
					printf("恭喜你,赢得游戏!\n");
					display_board(mine, ROW, COL);
					flag = '0';
					break;
				}
			}
			break;
		}
		case 2:
			sign_mine(show, ROW, COL);
			break;
		case 3:
			c_sign(show, ROW, COL);
			break;
		default:
			printf("选择错误\n");
			break;
		}
		if (flag == '0')
		{
			break;
		}
	}
}

int main()
{
	int input = 0;
	srand((unsigned int)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;
}

总结:

其实每个语句都不难,但是整体组合在一起就显得比较复杂,我们只需要一点点分析,用逻辑将他们联合起来就成功了!

最后,希望这篇文章可以帮助到大家,喜欢的话记得三连哦~

关注博主,后续会持续推迟优质内容~

感谢大家的支持~ 

                                            

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何实现精致扫雷游戏(可扩散可标记)---保姆级教程 的相关文章

随机推荐

  • 用java实现拷贝目录以及目录下文件

    用java实现拷贝目录以及目录下文件 创建一个File对象 也可以说是确定一个文件对象 File f1 new File D file 就相当于获取了这个文件对象 不管这个对象是否真实存在 对文件操作 所以方法里调用的都是File对象 如果
  • 线性代数 --- 置换矩阵 (Permutation matrix)

    置换矩阵就是重新排列后的单位矩阵 对一个矩阵进行行交换 需要通过置换矩阵 permutation matrix 来完成 在对一个Ax b的方程组进行高斯消元的过程中 我们常常会遇到一种情况 也就是消元消不下去的情况 下面 我列出了两个不同的
  • win10下cmder加wsl的多终端配置使用

    win10下cmder加wsl的多终端配置使用 版本说明 版本 作者 日期 备注 0 1 ZY 2020 1 21 初稿 文章目录 win10下cmder加wsl的多终端配置使用 版本说明 一 初衷 二 cmder和wsl 1 cmder
  • Matlab 自相关检测 :自相关函数xcorr

    原文 http blog chinaunix net uid 26275986 id 4342906 html 最近因为工作的关系需要使用matlab作为数据统计的工具 其中一个关键是使用其自相关函数获得数据的估计 自己只在本科时候马马虎虎
  • 易优cms 模板制作教程

    易优cms静态页面编写需要导航条 新闻列表等页面内容信息 在制作模板的时候就用标签来代替 模板标签就是后台设置的数据 所以模板标签你必须得熟悉 你可以看看模板标签大全有哪些标签可以使用 也务必要完全理解本文内容 否则你将不知道怎么使用模板标
  • 免费截图标注贴图工具:Snipaste

    每一次看课 都很羡慕老师能把截图贴在屏幕上 然后能在贴图上写写画画 还不影响你执行其他的电脑操作 超级赞 最最重要的是 贴图功能很适合程序员进行代码比对 效果图比对等 官网下载 Snipaste 解压缩到同名文件夹即可 双击exe文件运行
  • C++ - if 、else、else_if 语句

    C 中的 if else if else 是非常基础的东西 这里需要知道一个东西 大括号 一定要是成对出现 格式为 if 判断语句 如果判断语句成立 运行 如果判断语句不成立 跳过 if 语句 else 如果判断语句不成立 运行 如果判断语
  • 渗透测试-SQL注入之宽字节注入

    SQL注入之宽字节注入 文章目录 SQL注入之宽字节注入 前言 一 什么是宽字节注入 二 宽字节注入获取数据库信息 1 宽字节注入方法 2 宽字节注入防御方法 总结 前言 一 什么是宽字节注入 宽字节是相对于ascII这样单字节而言的 像
  • 信息管理毕业设计 SSM的小区车位出租管理系统(源码+论文)

    文章目录 1 前言 2 实现效果 3 设计方案 4 最后 1 前言 这两年开始毕业设计和毕业答辩的要求和难度不断提升 传统的java web缺少创新和亮点 往往达不到毕业答辩的要求 这两年不断有学弟学妹告诉学长自己做的java web管理系
  • python中pandas库查看数据分布的基本用法

    1 最大值 最小值 平均值 如何在Python中查看pandas dataframe对象列的最大值 最小值 平均值 标准差 中值等 PandasDataFrame 中位数 Python统计大文本行数
  • 设置VLC播放器进行RTSP推流视频

    设置VLC播放器进行RTSP推流视频 一 推流与拉流概念 二 设置推流端 三 设置拉流端 播放端 VLC官网 https www videolan org 一 推流与拉流概念 首先 做几个名词解释 推流 指的是把采集阶段封包好的内容传输到服
  • Easyexcel 3.1.1版本动态表头样式

    需求 动态生成的表头 key value的形式 List
  • Qt中的表格控件QTableWidget是一个非常常用的UI组件

    Qt中的表格控件QTableWidget是一个非常常用的UI组件 它可以用于展示表格数据 并且还有一些便捷的API以及信号槽机制 非常适合初学者入手 本篇文章将会对QTableWidget的使用进行详细介绍 一 如何创建QTableWidg
  • QT 在Window下驱动HID设备

    QT使用HID设备 QT 在Window下驱动HID设备 准备源码 1 下载HID源码 2 解压 拷贝解压目录下的 3 拷贝解压目录下的 4 打开QT建立新工程 把刚准备的window目录的三个文件 5 在QT 配置 pro文件中的SOUR
  • (四)调整PID控制器参数的指南

    一 控制系统设计快速入门和环境 首先确定一下控制任务 快速 精准地控制 必要的稳定性 时域 上升时间 超调等 频域 带宽 阻尼比 然后明白控制系统特点 类积分器 开环稳定性 高度非线性 非最小相位 1 选择一个控制框架 比如说PID 当然也
  • Windows Server2012常见版本

    目录 常见版本 授权方式 常用功能模块介绍 Windows Server 2012这是Windows 8的服务器版本 并且是Windows Server 2008 R2的继任者 该操作系统已经在2012年8月1日完成编译RTM版 并且在20
  • IntelliJ IDEA:新建文件无法识别类型

    参考 https blog csdn net iningwei article details 106115169 总结 File gt Settings gt Editor gt File Types 右侧 Text 然后下面的窗口往下滑
  • C将两个有序的数组合并成一个有序数组

    编写程序 分别输入两个按从小到大排序的数组 a 和 b 将这两个有序数组合并 使合并后的数组仍是从小到大有序的 合并两个有序数组的方法 有两个有序数组a和b 其中数组a的末尾有足够的空间容纳数组b 将数组b容纳到数组a中 创建一个新数组c
  • MyBatis 多表联合查询及优化

    关于优化 对于优化嘛 我这里简单的提几点 大家可以考虑一下 首先 就是对表的设计 在设计表初期 不仅仅要考虑到数据库的规范性 还好考虑到所谓的业务 以及对性能的影响 比如 如果从规范性角度考虑的话 可能就会分多个表 但是如果从性能角度来考虑
  • 如何实现精致扫雷游戏(可扩散可标记)---保姆级教程

    目录 思路 main函数内容 创建 双子 数组 初始化数组 打印棋盘 布置雷 排查雷 初阶 大致过程 如何统计周围雷的个数 判断是否赢得游戏 进阶 可扩散可标记 1 排雷 2 标记 标记要更改的时show数组 呈现给玩家标记信息 3 取消标