扫雷小游戏

目录

一.主体部分

1.菜单

2.主函数

二. game 函数的实现

1.初始化棋盘

2.布置雷

3.打印棋盘

4.扫雷过程

扫雷主体部分

统计一个位置周围有几个雷

向周围扩

三.完整代码

1. test.c 文件(主函数文件)

2. game.c 文件(函数文件)

3. game.h 文件(头文件)

四.总结


目录

一.主体部分

1.菜单

2.主函数

二. game 函数的实现

1.初始化棋盘

2.布置雷

3.打印棋盘

4.扫雷过程

扫雷主体部分

统计一个位置周围有几个雷

向周围扩


一.主体部分

1.菜单

通常在开始游戏之前都会有一个菜单供玩家选择,代码如下

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

2.主函数

为了能够多次游玩这个游戏,主函数中用一个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\n");
			break;
		default:
			printf("选择无效,请重新输入\n\n");
			break;
		}

	} while (input);


	return 0;
}

二. game 函数的实现

1.初始化棋盘

为了代码书写的方便,选择创建两个二维数组,一个用来储存雷的位置(char mine[][]),其中将雷的位置置成 '1',非雷的位置置成 '0',另一个用于展示给玩家(char show[][]),最开始全部置成 '*',表示所有位置都是未知的  ,其中玩家所有的排雷操作都会在 show 上实现, mine 只是作为一个储存功能。

假设我们需要一个 9 * 9 的棋盘,若我们创建的数组就是9*9的话,那我们在统计每一个位置周围八格雷的数量是就需要判断周围是否越界,就造成了很多的代码浪费,所以选择创建一个(9+2)*(9+2)的数组。行 ,列的下标都为0~10,但我们只使用其中的1~9作为有效棋盘,并且在 mine 棋盘中将外围一整圈都置成'0',这样我们在统计周围雷的数量时就不需要判断是否越界。

#define ROW 9  //棋盘有效行数
#define COL 9  //棋盘有效列数
#define ROWS ROW+2  //棋盘实际行数
#define COLS COL+2  //棋盘实际列数
#define EASY_COUNT 10  //雷的数量

棋盘初始化函数如下

void InitBoard(char board[ROWS][COLS], int row, int col, char c)//初始化棋盘
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			board[i][j] = c;
		}
	}
}

2.布置雷

棋盘初始化之后我们就需要在 mine 棋盘上随机生成雷,这里需要用到一个rand函数生成随机数并与前面提到的srand函数搭配使用,具体用法这里就不作详细介绍 ,代码如下

void SetMine(char board[ROWS][COLS])//放置雷
{
	int num = 0;//雷的数量

	while (num < EASY_COUNT)
	{
		int x = rand() % 9 + 1;//确保1<=x<=9
		int y = rand() % 9 + 1;//确保1<=y<=9

		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			num++;
		}
	}
}

3.打印棋盘

通过对二维数组的遍历进行打印

void DisplayBoard(char board[ROWS][COLS], int row, int col)//显示棋盘
{
	printf("\n<<     扫雷     >>\n");
	for (int i = 0; i < 10; 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");
	}
}

4.扫雷过程

扫雷主体部分

实现扫雷的逻辑就是每排查一个点,首先判断该点是不是雷,若是,那么就被炸死了,游戏结束;若不是,那么就需要统计该点周围有几个雷并展示给玩家,若该点周围没有雷,那么就需要以该点为中心向周围扩散,将所有周围没有雷的点扩散开,总的逻辑代码如下

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int* win)//扫雷
{
	int x = 0, y = 0;

	while (1)//用循环实现多次排雷
	{
		printf("请输入想要排查的坐标(行 列):>");
		scanf("%d %d", &x, &y);

		if (mine[x][y] == '1')//踩到雷
		{
			Sleep(500);
			printf("很遗憾,你被炸死了\n\n");
			DisplayBoard(mine, ROW, COL);//打印出雷的位置让玩家死的瞑目
			break;
		}
		else if(show[x][y] == '*')//该点不是雷并且没被排查过
		{
			int sum = GetMineCount(mine, x, y);//统计周围雷的数量

			if (!sum)//若周围没有雷
			{
				Spread(mine, show, row, col, x, y, win);//向周围扩散
			}
			else
			{
				show[x][y] = sum + '0';
				(*win)++;//统计已经排查过的点的个数
			}

			Sleep(500);//延迟0.5s
			system("cls");//清屏以保持可视化整洁
			DisplayBoard(show, ROW, COL);//打印棋盘
		}
		else//该点已经被排查过
		{
			Sleep(500);
			printf("该位置已被排查过,请重新选择\n");
		}

		if (*win == row * col - EASY_COUNT)//如果所有不是雷的点都被排查出来了,获得胜利
		{
			Sleep(500);
			printf("恭喜你,扫雷成功\n\n");
			return;
		}
	}
}

统计一个位置周围有几个雷

在游玩扫雷的过程中每排查一个点,首先要做的是判断该点是不是雷,这个只需要判断 mine 棋盘中的该点是否等于 '1'即可,然后就是要统计该点周围八格有几个雷并显示,这里就受到了前面将  雷点置成 '1'的便利,我们只需要将这九格加起来再减去 9*'0' 就得到了该点周围雷的数量,代码如下

int GetMineCount(char board[ROWS][COLS], int x, int y)//统计一个点周围雷的数量
{
	int sum = 0;
	for (int i = -1; i <= 1; i++)
	{
		sum += (board[x + i][y - 1] + board[x + i][y] + board[x + i][y + 1]);
	}

	return sum - 9 * '0';
}

向周围扩

这里需要使用到递归的思想,那么递归的条件是 1.该点不是雷、2.该点周围没有雷、3.该点没有被排查过。符合以上三个条件就可以向周围递归扩散,将每一个被扩散的点都在 show 棋盘中置成空格

void Spread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y,int* win)//扩散
{
	int sum = GetMineCount(mine, x, y);//先统计该位置周围雷的数量

	if (!sum && show[x][y] == '*')//若该位置周围没有雷并且该位置没有被排查过
	{
		show[x][y] = ' ';
		(*win)++;//统计已经排查过的点的个数

		for (int i = -1; i <= 1; i++)
		{
			for (int j = -1; j <= 1; j++)
			{
				int nextx = x + i, nexty = y + j;

				if (nextx >= 1 && nextx <= row//若周围位置都有效
					&& nexty >= 1 && nexty <= col)
				{
					Spread(mine, show, row, col, nextx, nexty, win);//对周围扩散
				}

			}
		}
	}
	else if (sum && show[x][y] == '*')//若该位置周围有雷且没被排查过
	{
		show[x][y] = sum + '0';
		(*win)++;//统计已经排查过的点的个数
	}
}

三.完整代码

为了代码的可视化整洁,这里将代码分装成了三个文件

1. test.c 文件(主函数文件)

#include"game.h"

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

void game()
{
	int win = 0;//统计排掉了几个位置

	char mine[ROWS][COLS] = { 0 };//实际棋盘
	char show[ROWS][COLS] = { 0 };//展示棋盘

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

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

	SetMine(mine);//布置雷
	
	//DisplayBoard(mine, ROW, COL);//打印棋盘
	DisplayBoard(show, ROW, COL);//打印棋盘

	FindMine(mine, show, ROW, COL, &win);//扫雷
}

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\n");
			break;
		default:
			printf("选择无效,请重新输入\n\n");
			break;
		}

	} while (input);


	return 0;
}

2. game.c 文件(函数文件)

#include"game.h"

void InitBoard(char board[ROWS][COLS], int row, int col, char c)//初始化棋盘
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			board[i][j] = c;
		}
	}
}

void DisplayBoard(char board[ROWS][COLS], int row, int col)//显示棋盘
{
	printf("\n<<     扫雷     >>\n");
	for (int i = 0; i < 10; 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 num = 0;//雷的数量

	while (num < EASY_COUNT)
	{
		int x = rand() % 9 + 1;
		int y = rand() % 9 + 1;

		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			num++;
		}
	}
}

int GetMineCount(char board[ROWS][COLS], int x, int y)//统计一个点周围雷的数量
{
	int sum = 0;
	for (int i = -1; i <= 1; i++)
	{
		sum += (board[x + i][y - 1] + board[x + i][y] + board[x + i][y + 1]);
	}

	return sum - 9 * '0';
}

void Spread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y,int* win)//扩散
{
	int sum = GetMineCount(mine, x, y);//先统计该位置周围雷的数量

	if (!sum && show[x][y] == '*')//若该位置周围没有雷并且该位置没有被排查过
	{
		show[x][y] = ' ';
		(*win)++;//统计已经排查过的点的个数

		for (int i = -1; i <= 1; i++)
		{
			for (int j = -1; j <= 1; j++)
			{
				int nextx = x + i, nexty = y + j;

				if (nextx >= 1 && nextx <= row//若周围位置都有效
					&& nexty >= 1 && nexty <= col)
				{
					Spread(mine, show, row, col, nextx, nexty, win);//对周围扩散
				}

			}
		}
	}
	else if (sum && show[x][y] == '*')//若该位置周围有雷且没被排查过
	{
		show[x][y] = sum + '0';
		(*win)++;//统计已经排查过的点的个数
	}
}

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int* win)//扫雷
{
	int x = 0, y = 0;

	while (1)//用循环实现多次排雷
	{
		printf("请输入想要排查的坐标(行 列):>");
		scanf("%d %d", &x, &y);

		if (mine[x][y] == '1')//踩到雷
		{
			Sleep(500);
			printf("很遗憾,你被炸死了\n\n");
			DisplayBoard(mine, ROW, COL);//打印出雷的位置让玩家死的瞑目
			break;
		}
		else if(show[x][y] == '*')//该点不是雷并且没被排查过
		{
			int sum = GetMineCount(mine, x, y);//统计周围雷的数量

			if (!sum)//若周围没有雷
			{
				Spread(mine, show, row, col, x, y, win);//向周围扩散
			}
			else
			{
				show[x][y] = sum + '0';
				(*win)++;
			}

			Sleep(500);//延迟0.5s
			system("cls");//清屏以保持可视化整洁
			DisplayBoard(show, ROW, COL);//打印棋盘
		}
		else//该点已经被排查过
		{
			Sleep(500);
			printf("该位置已被排查过,请重新选择\n");
		}

		if (*win == row * col - EASY_COUNT)//如果所有不是雷的点都被排查出来了,获得胜利
		{
			Sleep(500);
			printf("恭喜你,扫雷成功\n\n");
			return;
		}
	}
}


3. game.h 文件(头文件)

#pragma once

#include
#include
#include
#include

#define ROW 9  //棋盘有效行数
#define COL 9  //棋盘有效列数
#define ROWS ROW+2  //棋盘实际行数
#define COLS COL+2  //棋盘实际列数
#define EASY_COUNT 10  //雷的数量

void InitBoard(char board[ROWS][COLS], int row, int col, char c);
void DisplayBoard(char board[ROWS][COLS], int row, int col);
void SetMine(char board[ROWS][COLS]);
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int* win);
int GetMineCount(char board[ROWS][COLS], int x, int y);
void Spread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* win);

四.总结

以上就是扫雷实现的全部内容了,主要是函数的设计和递归思想以及为二维数组的应用,望大家多多支持。

你可能感兴趣的:(c语言,算法)