C语言实现扫雷小游戏

目录

思路

实现

1.游戏菜单

1.1 打印游戏菜单

1.2 玩家自主选择

2.定义并且初始化二维数组

2.1 定义两个二维数组

2.2 初始化二维数组

3.打印游戏主界面

4. 在雷阵中布置雷(随机生成)

5.排查雷

6. 结合以上所有步骤,完成扫雷游戏

进阶

 1.发散式      

 思路

2.标记地雷与取消标记

全面改进后


思路

1.设置一个游戏菜单,让玩家可以自主选择开始游戏或者退出游戏。

2. 定义并初始化两个二维数组,一个用于存放雷阵,一个用于打印出游戏界面(玩家看的)。

3. 游戏主界面的打印。

4. 在雷阵中布置雷(随机生成)。

5. 排查雷(游戏主体)。

6. 结合以上所有步骤,完成扫雷游戏

实现

首先我们打开我们的Visual Studio,创建一个项目

为了代码的简洁性和方便我们调试代码,我们在这里创建了一个头文件,两个源文件

接下来我们开始编写程序一步步实现我们的扫雷游戏。

1.游戏菜单

1.1 打印游戏菜单

        首先我们可以使用printf函数打印出一个相对精美的游戏菜单

代码如下:

//test.c

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

void test()
{
	menu();//打印游戏菜单
}

int main()
{
	test();
	return 0;
}

1.2 玩家自主选择

        我们要实现玩家自主选择玩还是不玩的功能,可以引入一个input变量来接受玩家键入的数据,然后通过switch语句来根据玩家键入的数据作出对应的操作。

        同时,如果玩家玩一次不过瘾想继续玩,我们就需要利用循环结构来完成这一操作,我们可以发现,菜单一开始肯定是要打印出来的,所以是先做一次再判断,我们用do...while语句来实现这个功能。

        代码如下:

#define _CRT_SECURE_NO_WARNINGS 1

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

void game()
{
	printf("扫雷\n");
}

void test()
{
	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);//如果玩家键入0,游戏结束,跳出循环
}

int main()
{
	test();
	return 0;
}

2.定义并且初始化二维数组

        我们要实现扫雷游戏的功能,首先需要一个二维数组来存放地雷,其次,玩家肯定不能直接看到地雷的位置,这时候我们就需要另一个二维数组用于给玩家看。

2.1 定义两个二维数组

        为了后续我们能够升级扫雷难度(扩张行和列),我们可以在game.h文件中利用#define定义的常量标识符来充当行和列,届时我们只需要修改ROWCOL的值就可以更改扫雷的行列数。注:

        假设我们要实现9*9的扫雷,如果我们定义的是9*9的二维数组,我们在扫最边上的位置时,就会造成数组越界,所以为了避免这种情况的发生,我们需要定义一个11*11(ROW+2*COL+2)的二维数组。

代码如下:

//test.c

#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 };//定义展示数组
}

void test()
{
	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);//如果玩家键入0,游戏结束,跳出循环
}

int main()
{
	test();
	return 0;
}

//game.h

#define _CRT_SECURE_NO_WARNINGS 1

//因为test.c文件中包含了game.h,所以可以直接在game.h文件中包含头文件
#include <stdio.h>

//定义行列数
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2

//game.c

#include "game.h"//包含头文件

void init_board(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;//给数组中每个元素赋值
		}
	}
}


2.2 初始化二维数组

        初始化二维数组之前,我们先得知道我们需要把它们初始化成什么,为了后续方便,我们把地雷数组的雷赋值为'1',其他位置赋值为'0'。(这个后文会讲到),而展示数组,我们可以把他全部赋值为 '*',因为'*'代表未知,当然你要想设置成其他字符也可以,这里我们还是把它初始化为'*'。

        接下来我们设置一个函数来初始化两个二维数组

代码如下:

//test.c

void game()
{
	//定义地雷数组
	char mine[ROWS][COLS] = { 0 };
	//定义展示数组
	char show[ROWS][COLS] = { 0 };
	//初始化地雷数组
	init_board(mine, ROWS, COLS, '0');
	//初始化展示数组
	init_board(show, ROWS, COLS, '*');
}

//game.h


//因为test.c文件中包含了game.h,所以可以直接在game.h文件中包含头文件
#include <stdio.h>

//定义行列数
#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2

//初始化二维数组
void init_board(char board[ROWS][COLS], int rows, int cols, char set);

//game.c

#include "game.h"//包含头文件

void init_board(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;//给数组中每个元素赋值
		}
	}
}

3.打印游戏主界面

我们先看看游戏界面

        当然这是博主自己的游戏界面,如果觉得不好看你也可以自己设计一个,不过打印数组一定不能省略。

        我们需要在游戏开始后打印一个展示界面,以便后续玩家来选取坐标。

代码如下:

//test.c

void game()
{
	//定义地雷数组
	char mine[ROWS][COLS] = { 0 };
	//定义展示数组
	char show[ROWS][COLS] = { 0 };
	//初始化地雷数组
	init_board(mine, ROWS, COLS, '*');
	//初始化展示数组
	init_board(show, ROWS, COLS, '*');
	//打印展示界面
	print_board(show, ROW, COL);
}

//game.h

//打印游戏主界面
void print_board(char show[ROWS][COLS], int rows, int cols);

//game.c

void print_board(char board[ROWS][COLS], int row, int col)
{
	int i = 0,j = 0;
	printf("0|");//美观
	for (j = 1; j <= row; j++)
	{
		printf("%d ", j);//打印列提示数字
	}
	printf("|\n");//换行
	printf("-|");//美观
	for (j = 0; j < col; j++)
	{
		printf("--");//美观
	}
	printf("|\n");
	for (i = 1; i <= row; i++)//由于二维数组行列数为11,所以行下标从1开始打印
	{
		//打印行
		printf("%d|", i);//打印行提示数字
		for (j = 1; j <= row; j++)//由于二维数组行列数为11,所以行下标从1开始打印
		{
			printf("%c ", board[i][j]);
		}
		printf("|\n");//打印完一行后换行
	}
	printf("-|");//美观
	for (j = 0; j < col; j++)
	{
		printf("--");//美观
	}
	printf("|\n");
}

4. 在雷阵中布置雷(随机生成)

        既然是扫雷游戏,我们肯定就要布置地雷,上文中已经讲过,我们将地雷数组的雷赋值为'1',其他位置赋值为'0',现在已经把地雷数组全部初始化为'0',那么接下来我们就需要安放地雷,也就是布置'1'进地雷数组。

        当然,如果我们需要随机生成雷,我们就要用到rand()函数、srand()函数和time()函数,而这三个库函数分别要引入<stdlib.h>头文件和<time.h>头文件,这里就不做过多讲解,可以自行查找。

代码如下:

//test.c

void game()
{
	//随机数起点
	srand((unsigned int)time(NULL));
	//定义地雷数组
	char mine[ROWS][COLS] = { 0 };
	//定义展示数组
	char show[ROWS][COLS] = { 0 };
	//初始化地雷数组
	init_board(mine, ROWS, COLS, '0');
	//初始化展示数组
	init_board(show, ROWS, COLS, '*');
	//打印展示界面
	print_board(show, ROW, COL);
	//布置雷
	set_mine(mine, ROW, COL);
	print_board(mine, ROW, COL);//调试时检测雷是否被正确安置
}

//game.h

//定义雷数
#define NUMMINE 10

//布置雷
void set_mine(char mine[ROWS][COLS], int row, int col);

//game.c
void set_mine(char mine[ROWS][COLS], int row, int col)
{
	int count = 0;//循环变量
	while(count<NUMMINE)
	{
		int x = rand() % row + 1;//每次生成随机行下标
		int y = rand() % col + 1;//每次生成随机列下标
		//判断随机坐标是否已经有雷
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count++;//不可放在if语句外
		}
	}
}

5.排查雷

        前期工作已经准备完毕,接下来就轮到排查雷了,我们知道扫雷游戏是检测玩家选中的坐标,如果该坐标有雷,游戏结束;如果该坐标没雷,检查周围八个坐标探测雷的个数并在该坐标显示出来。

        那么我们怎么通过代码来实现这一操作呢?

代码如下:

//test.c

void game()
{
	//随机数起点
	srand((unsigned int)time(NULL));
	//定义地雷数组
	char mine[ROWS][COLS] = { 0 };
	//定义展示数组
	char show[ROWS][COLS] = { 0 };
	//初始化地雷数组
	init_board(mine, ROWS, COLS, '0');
	//初始化展示数组
	init_board(show, ROWS, COLS, '*');
	//打印展示界面
	print_board(show, ROW, COL);
	//布置雷
	set_mine(mine, ROW, COL);
	//print_board(mine, ROW, COL);//调试时检测雷是否被正确安置
	//排查雷
	search_mine(mine, show, ROW, COL);
}

//game.h

//排查雷
void search_mine(char mine[ROWS][COLS ],char show[ROWS][COLS], int row, int col);

//game.c

//探查周围雷的个数
int get_mine_num(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1]
		+ mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] +
		mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0';//计算周围雷的个数
}

//查找该位置
void search(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* pcount)
{
	int ret = get_mine_num(mine, x, y) + '0';//获取周围雷的个数
	if (ret == '0')
	{
		show[x][y] = ' ';
	}
	else
	{
		show[x][y] = ret;
	}
	*pcount = *pcount + 1;//每成功探查一个坐标,count+1
}

void search_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0, y = 0;
	int count = 0;//判断是否胜利
	while (1)
	{
		printf("请输入你要排查的坐标:>");
		scanf("%d %d", &x, &y);
		printf("\n");
		//判断坐标是否合法
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			//判断该坐标是否被排查过
			if (show[x][y] == '*')
			{
				//判断该坐标是否是雷
				if (mine[x][y] == '1')
				{
					printf("很遗憾,你被炸死了\n");
					print_board(mine, ROW, COL);//打印雷阵,显示雷的位置
					break;
				}
				else if (mine[x][y] == '0')
				{
					search(mine, show, row, col, x, y, &count);//查找该位置
					print_board(show, ROW, COL);//每探查过一次打印一次展示界面
					//判断是否已经探查出所有的雷
					if (count == (row * col - NUMMINE))
					{
						printf("恭喜你成功探查出所有的雷!!!\n");
						printf("\n");
						print_board(mine, ROW, COL);//打印雷阵,显示雷的位置
						break;
					}
				}
			}
			else
			{
				printf("该坐标已经被排查过,请重新输入\n");
				printf("\n");
			}
		}
		else
		{
			printf("输入错误,请重新输入\n");
			printf("\n");
		}
	}
}


6. 结合以上所有步骤,完成扫雷游戏

        我们把扫雷的实现细化为5个步骤,现在让我们把他们整合起来

最终我们得到了扫雷的全部代码:

分文件展示

game.h:

//因为test.c文件中包含了game.h,所以可以直接在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

//初始化二维数组
void init_board(char board[ROWS][COLS], int rows, int cols, char set);

//打印游戏主界面
void print_board(char show[ROWS][COLS], int rows, int cols);

//定义雷数
#define NUMMINE 10

//布置雷
void set_mine(char mine[ROWS][COLS], int row, int col);

//排查雷
void search_mine(char mine[ROWS][COLS ],char show[ROWS][COLS], int row, int col);

test.c:

#include "game.h" //包含头文件

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

void game()
{
	//随机数起点
	srand((unsigned int)time(NULL));
	//定义地雷数组
	char mine[ROWS][COLS] = { 0 };
	//定义展示数组
	char show[ROWS][COLS] = { 0 };
	//初始化地雷数组
	init_board(mine, ROWS, COLS, '0');
	//初始化展示数组
	init_board(show, ROWS, COLS, '*');
	//打印展示界面
	print_board(show, ROW, COL);
	//布置雷
	set_mine(mine, ROW, COL);
	//print_board(mine, ROW, COL);//调试时检测雷是否被正确安置
	//排查雷
	search_mine(mine, show, ROW, COL);
}

void test()
{
	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);//如果玩家键入0,游戏结束,跳出循环
}

int main()
{
	test();
	return 0;
}

game.c:

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"//包含头文件

void init_board(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;//给数组中每个元素赋值
		}
	}
}

void print_board(char board[ROWS][COLS], int row, int col)
{
	printf("-------------扫雷-------------\n");
	int i = 0,j = 0;
	printf("0|");//美观
	for (j = 1; j <= row; j++)
	{
		printf("%d ", j);//打印列提示数字
	}
	printf("|\n");//换行
	printf("-|");//美观
	for (j = 0; j < col; j++)
	{
		printf("--");//美观
	}
	printf("|\n");
	for (i = 1; i <= row; i++)//由于二维数组行列数为11,所以行下标从1开始打印
	{
		//打印行
		printf("%d|", i);//打印行提示数字
		for (j = 1; j <= row; j++)//由于二维数组行列数为11,所以行下标从1开始打印
		{
			printf("%c ", board[i][j]);
		}
		printf("|\n");//打印完一行后换行
	}
	printf("-|");//美观
	for (j = 0; j < col; j++)
	{
		printf("--");//美观
	}
	printf("|\n");
	printf("-------------扫雷-------------\n\n");
}

void set_mine(char mine[ROWS][COLS], int row, int col)
{
	int count = 0;//循环变量
	while(count<NUMMINE)
	{
		int x = rand() % row + 1;//每次生成随机行下标
		int y = rand() % col + 1;//每次生成随机列下标
		//判断随机坐标是否已经有雷
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count++;//不可放在if语句外
		}
	}
}

//探查周围雷的个数
int get_mine_num(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1]
		+ mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] +
		mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0';//计算周围雷的个数
}

//查找该位置
void search(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* pcount)
{
	int ret = get_mine_num(mine, x, y) + '0';//获取周围雷的个数
	if (ret == '0')
	{
		show[x][y] = ' ';
	}
	else
	{
		show[x][y] = ret;
	}
	*pcount = *pcount + 1;//每成功探查一个坐标,count+1
}

void search_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0, y = 0;
	int count = 0;//判断是否胜利
	while (1)
	{
		printf("请输入你要排查的坐标:>");
		scanf("%d %d", &x, &y);
		printf("\n");
		//判断坐标是否合法
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			//判断该坐标是否被排查过
			if (show[x][y] == '*')
			{
				//判断该坐标是否是雷
				if (mine[x][y] == '1')
				{
					printf("很遗憾,你被炸死了\n");
					print_board(mine, ROW, COL);//打印雷阵,显示雷的位置
					break;
				}
				else if (mine[x][y] == '0')
				{
					search(mine, show, row, col, x, y, &count);//查找该位置
					print_board(show, ROW, COL);//每探查过一次打印一次展示界面
					//判断是否已经探查出所有的雷
					if (count == (row * col - NUMMINE))
					{
						printf("恭喜你成功探查出所有的雷!!!\n");
						printf("\n");
						print_board(mine, ROW, COL);//打印雷阵,显示雷的位置
						break;
					}
				}
			}
			else
			{
				printf("该坐标已经被排查过,请重新输入\n");
				printf("\n");
			}
		}
		else
		{
			printf("输入错误,请重新输入\n");
			printf("\n");
		}
	}
}

进阶

        以上的扫雷游戏只是基础版本,我们还需要不断的完善,进阶。

那么我们需要添加一些什么功能呢?

        让我们打开网上的扫雷游戏

 

 1.发散式      

 思路

        我们可以看到,在我们排查了一个坐标以后,如果这个坐标周围都没有雷,那么他的周围呈发散式散开,直到遇到周围有雷的坐标,如果我们需要实现这个功能,需要怎么做呢?

        让我们想一想,如果我们排查了一个周围都没有雷的坐标,是不是就要排查这个坐标周围的八个坐标,然后这八个坐标如果其中还有周围都没有雷的坐标,我们还需要再排查这些坐标周围的没有被排查过的坐标,这很明显可以通过递归来实现,通过之前的学习我们知道,递归要有两个必要条件:

  • 存在限制条件,当满足这个限制条件的时候,递归便不再继续。
  • 每次递归调用之后越来越接近这个限制条件。

        那我们来想一想这个递归函数的限制条件

限制条件

1. 该坐标在游戏界面之中

2. 该坐标不是雷

3. 该坐标周围没有雷

4. 该坐标没有被排查过

        以上的2和3可以合并为1个,1和4可以合并成一个所以我们只需要用两个判断语句就可以完成

代码

代码如下: 

//查找该位置
void search(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* pcount)
{
	//递归限制条件1 4
	if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '*')
	{
		int ret = get_mine_num(mine, x, y) + '0';//获取周围雷的个数
		if (ret == '0')
		{
			show[x][y] = ' ';
		}
		else
		{
			show[x][y] = ret;
		}
		*pcount = *pcount + 1;//每成功探查一个坐标,count+1
		//递归限制条件2 3
		if (show[x][y] == ' ')
		{
			search(mine, show, row, col, x - 1, y - 1, pcount);
			search(mine, show, row, col, x - 1, y, pcount);
			search(mine, show, row, col, x - 1, y + 1, pcount);
			search(mine, show, row, col, x, y - 1, pcount);
			search(mine, show, row, col, x, y + 1, pcount);
			search(mine, show, row, col, x + 1, y - 1, pcount);
			search(mine, show, row, col, x + 1, y, pcount);
			search(mine, show, row, col, x + 1, y + 1, pcount);
		}
	}
}

通过以上的代码我们就成功完成了发散式的操作。

2.标记地雷与取消标记

        如图所示,圈中的坐标很明显一定是雷,但我们如果下次不小心查到这个坐标,游戏就会失败,所以我们需要把这种一定是雷的坐标标记起来,让我们在下次不小心查到这个坐标的时候提示一下重新查,而且当我们标记错坐标时,我们还得取消标记,所以我们需要添加标记地雷和取消地雷这一操作,同时提醒剩余的雷数。

        这里就不作过多介绍。直接看更改过的代码

全面改进后

game.h:

#define _CRT_SECURE_NO_WARNINGS 1

//因为test.c文件中包含了game.h,所以可以直接在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

//初始化二维数组
void init_board(char board[ROWS][COLS], int rows, int cols, char set);

//打印游戏主界面
void print_board(char show[ROWS][COLS], int rows, int cols);

//定义雷数
#define NUMMINE 10

//布置雷
void set_mine(char mine[ROWS][COLS], int row, int col);

//排查雷
void search_mine(char mine[ROWS][COLS ],char show[ROWS][COLS], int row, int col);

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()
{
	//随机数起点
	srand((unsigned int)time(NULL));
	//定义地雷数组
	char mine[ROWS][COLS] = { 0 };
	//定义展示数组
	char show[ROWS][COLS] = { 0 };
	//初始化地雷数组
	init_board(mine, ROWS, COLS, '0');
	//初始化展示数组
	init_board(show, ROWS, COLS, '*');
	//打印展示界面
	print_board(show, ROW, COL);
	//布置雷
	set_mine(mine, ROW, COL);
	print_board(mine, ROW, COL);//调试时检测雷是否被正确安置
	//排查雷
	search_mine(mine, show, ROW, COL);
}

void test()
{
	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);//如果玩家键入0,游戏结束,跳出循环
}

int main()
{
	test();
	return 0;
}

game.c:

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"//包含头文件

void init_board(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;//给数组中每个元素赋值
		}
	}
}

void print_board(char board[ROWS][COLS], int row, int col)
{
	printf("-------------扫雷-------------\n");
	int i = 0,j = 0;
	printf("0|");//美观
	for (j = 1; j <= row; j++)
	{
		printf("%d ", j);//打印列提示数字
	}
	printf("|\n");//换行
	printf("-|");//美观
	for (j = 0; j < col; j++)
	{
		printf("--");//美观
	}
	printf("|\n");
	for (i = 1; i <= row; i++)//由于二维数组行列数为11,所以行下标从1开始打印
	{
		//打印行
		printf("%d|", i);//打印行提示数字
		for (j = 1; j <= row; j++)//由于二维数组行列数为11,所以行下标从1开始打印
		{
			printf("%c ", board[i][j]);
		}
		printf("|\n");//打印完一行后换行
	}
	printf("-|");//美观
	for (j = 0; j < col; j++)
	{
		printf("--");//美观
	}
	printf("|\n");
	printf("-------------扫雷-------------\n\n");
}

void set_mine(char mine[ROWS][COLS], int row, int col)
{
	int count = 0;//循环变量
	while(count<NUMMINE)
	{
		int x = rand() % row + 1;//每次生成随机行下标
		int y = rand() % col + 1;//每次生成随机列下标
		//判断随机坐标是否已经有雷
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count++;//不可放在if语句外
		}
	}
}

//探查周围雷的个数
int get_mine_num(char mine[ROWS][COLS], int x, int y)
{
	return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1]
		+ mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] +
		mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0';//计算周围雷的个数
}

//查找该位置
void search(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* pcount)
{
	//递归限制条件1 4
	if (x >= 1 && x <= row && y >= 1 && y <= col && show[x][y] == '*')
	{
		int ret = get_mine_num(mine, x, y) + '0';//获取周围雷的个数
		if (ret == '0')
		{
			show[x][y] = ' ';
		}
		else
		{
			show[x][y] = ret;
		}
		*pcount = *pcount + 1;//每成功探查一个坐标,count+1
		//递归限制条件2 3
		if (show[x][y] == ' ')
		{
			search(mine, show, row, col, x - 1, y - 1, pcount);
			search(mine, show, row, col, x - 1, y, pcount);
			search(mine, show, row, col, x - 1, y + 1, pcount);
			search(mine, show, row, col, x, y - 1, pcount);
			search(mine, show, row, col, x, y + 1, pcount);
			search(mine, show, row, col, x + 1, y - 1, pcount);
			search(mine, show, row, col, x + 1, y, pcount);
			search(mine, show, row, col, x + 1, y + 1, pcount);
		}
	}
}

//打印游戏菜单
void game_menu()
{
	printf("|---------------------------|\n");
	printf("|--------1.继续扫雷---------|\n");
	printf("|--------2.标记地雷---------|\n");
	printf("|--------3.取消标记---------|\n");
	printf("|-------0.返回主菜单--------|\n");
	printf("|---------------------------|\n");
}


//标记地雷
void mark_mine(char show[ROWS][COLS], int row, int col, int* pleave_mine_num)
{
	int x = 0, y = 0;
	//判断是否还有标记位置
	if (*pleave_mine_num > 0)
	{
		while (1)
		{
			printf("请输入你要标记的坐标:>");
			scanf("%d %d", &x, &y);
			//判断坐标合法性
			if (x >= 1 && x <= row && y >= 1 && y <= col)
			{
				//判断坐标是否已被排查
				if (show[x][y] == '*')
				{
					show[x][y] = '#';
					print_board(show, ROW, COL);
					*pleave_mine_num = *pleave_mine_num - 1;
					printf("剩余雷的个数:%d\n", *pleave_mine_num);
					break;
				}
				else
					printf("该坐标已被排查,请重新输入\n");
			}
			else
			{
				printf("坐标输入有误,请重新输入\n");
			}
		}
	}
	else
	{
		printf("标记地雷数已达上限,请取消标记或继续扫雷!\n");
	}
}

//取消标记
void quit_mark(char show[ROWS][COLS], int row, int col, int *pleave_mine_num)
{
	//判断是否存在标记
	if (*pleave_mine_num == NUMMINE)
	{
		printf("你并没有任何标记,请标记地雷或继续扫雷\n");
	}
	else
	{
		int x = 0, y = 0;
		while (1)
		{
			printf("请输入你想要取消标记的雷的坐标:>");
			scanf("%d %d", &x, &y);
			if (x >= 1 && x <= row && y >= 1 && y <= col)
			{
				//判断该坐标是否已被标记
				if (show[x][y] == '#')
				{
					show[x][y] = '*';
					print_board(show, ROW, COL);
					*pleave_mine_num = *pleave_mine_num + 1;
					printf("剩余雷的个数:%d\n", *pleave_mine_num);
					break;
				}
			}
			else
			{
				printf("坐标输入有误,请重新输入\n");
			}
		}
	}
}

//选择
void choice(char show[ROWS][COLS], int row, int col, int* pleave_mine_num, int *pflag)
{
	int input = 0;
	do
	{
		game_menu();//打印选择菜单
		printf("请输入你的选择:>");
		scanf("%d", &input);
		printf("\n");
		print_board(show, row, col);
		*pflag = input;
		switch (input)
		{
		case 1:
			break;
		case 2:
			mark_mine(show, row, col, pleave_mine_num);//标记地雷
			break;
		case 3:
			quit_mark(show, row, col, pleave_mine_num);//取消标记
			break;
		case 0:
			break;
		default:
			printf("输入错误,请重新输入\n");
			break;
		}
	} while (input == 2 || input == 3);
}

void search_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0, y = 0;
	int count = 0;//判断是否胜利的变量
	int leave_mine_num = NUMMINE;//表示剩余雷数的变量
	int flag = 0;//判断是否退出游戏的变量
	while (1)
	{
		printf("请输入你要排查的坐标:>");
		scanf("%d %d", &x, &y);
		printf("\n");
		//判断坐标是否合法
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			//判断该坐标是否被标记
			if (show[x][y] == '#')
			{
				printf("该坐标已被标记,不能被排查\n");
			}
			//判断该坐标是否被排查过
			else if (show[x][y] == '*')
			{
				//判断该坐标是否是雷
				if (mine[x][y] == '1')
				{
					printf("很遗憾,你被炸死了\n");
					print_board(mine, ROW, COL);//打印雷阵,显示雷的位置
					break;
				}
				else if (mine[x][y] == '0')
				{
					search(mine, show, row, col, x, y, &count);//查找该位置
					print_board(show, ROW, COL);//每探查过一次打印一次展示界面
					printf("剩余雷的个数:%d\n\n", leave_mine_num);//提醒剩余雷的个数
					//判断游戏是否结束
					if (count != (row * col - NUMMINE))
					{
						choice(show, ROW, COL, &leave_mine_num, &flag);
					}
					//判断玩家是否选择退出游戏
					if (flag == 0)
					{
						printf("返回主菜单\n\n");
						break;
					}
					//判断是否已经探查出所有的雷
					if (count == (row * col - NUMMINE))
					{
						printf("恭喜你成功探查出所有的雷!!!\n");
						printf("\n");
						print_board(mine, ROW, COL);//打印雷阵,显示雷的位置
						break;
					}
				}
			}
			else
			{
				printf("该坐标已经被排查过,请重新输入\n");
				printf("\n");
			}
		}
		else
		{
			printf("输入错误,请重新输入\n");
			printf("\n");
		}
	}
}

好了,以上就是扫雷游戏的C语言实现,让我们下篇文章再见!

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值