扫雷游戏的模拟实现

目录

扫雷游戏分析和设计(含递归展开)

1.1 扫雷游戏的功能说明

1.2 游戏的分析和设计

        1.2.1 数据结构的分析

        1.2.2 文件结构设计

扫雷游戏的代码实现

2.1 代码分析

2.1.1 main()

2.1.2 game()

2.2 代码实现

        2.2.1 teat.c

        2.2.2 game.c

        2.2.3 game.h

扫雷游戏的扩展

 3.1 递归展开



扫雷游戏分析和设计(含递归展开)

1.1 扫雷游戏的功能说明

        • 使⽤控制台实现经典的扫雷游戏
        • 游戏可以通过菜单实现继续玩或者退出游戏
        • 扫雷的棋盘是9*9的格⼦
        • 默认随机布置10个雷
        • 可以排查雷
                ◦ 如果位置不是雷,就显⽰周围有⼏个雷
                ◦ 如果位置是雷,就炸死游戏结束
                ◦ 把除10个雷之外的所有雷都找出来,排雷成功,游戏结束

        

1.2 游戏的分析和设计

        1.2.1 数据结构的分析
在进行扫雷的过程中需要保存棋盘的信息和雷的信息,所以用一个 9*9 的数组进行保存。

若设置雷就存放1,其他存放0。

假设我们访问(2,5)这个坐标时,需要访问周围8个坐标中有雷的个数,统计个数为1。
假设我们访问(8,6)这个坐标时,访问周围8个坐标时会出现越界访问的情况,所以为了防止这种情况的发生,我们在设计棋盘时可以扩大一圈,将棋盘设计为 11*11 ,这样就避免了越界访问的问题。
继续分析,在棋盘上布置了雷,雷的信息(1)和非雷的信息(0),假设排查某一个位置后,这个坐标不是雷,但这个坐标周围有雷,那么需要将排查出雷的信息储存并打印出来,作为排雷的重要信息。那么雷的个数信息存放在哪里?如果存放在布置雷的数组中,这样雷的信息和雷的个数信息就可能或产⽣混淆和打印上的困难。
所以我们使用两个数组来存放信息。一个专门存放棋盘(数组mine),一个专门存放雷(数组show),这样就不会相互干扰了。把雷布置到 mine数组里,在mine数组中排查雷,排查出的数据存放在show数组里,并且打印show数组的信息给后期排查参考。
同时为了保证数组类型的一致性,可用于同一套函数进行处理,mine开始的初始化用 ‘0’ ,雷用 ‘1’ 表示。如下图:
对应的数组分别是:
1. char mine[11][11] = {0};//用来存放布置好雷的信息
2. char show[11][11] = {0};//用来存放排查出雷的信息
        1.2.2 文件结构设计
1. test.c//游戏的测试逻辑
2. game.c//游戏函数的实现
3. game.h//游戏需要的声明

扫雷游戏的代码实现

2.1 代码分析

2.1.1 main()

我们希望这个游戏可以一直玩下去,这需要通过循环来实现,通过分析我们知道,这个循环至少需要执行一次,所以用do while循环来实现;要选择玩与不玩,可以通过switch来实现--输入1开始游戏,输入0退出游戏,输入其他值的时候提示错误。函数menu() 用来打印菜单。

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

	return 0;
}
2.1.2 game()

我们通过game()函数来实现游戏的具体逻辑。先创建上文提到的两个数组通过上文分析数组应该是11*11的二维数组,为了方便后期修改代码,这里可以用 #define 来定义常量。

#define ROW 9 //棋盘实际大小
#define COL 9

#define ROWS ROW+2 //数组实际大小
#define COLS COL+2
void game()
{
	char mine[ROWS][COLS] = { 0 };//存放布置好的雷
	char show[ROWS][COLS] = { 0 };//存放排查出雷的信息

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

	//打印
	DisplayBoard(show, ROW, COL);
	
	//布置雷
	SetMine(mine, ROW, COL);
	//DisplayBoard(mine, ROW, COL);
	
	//排雷
	FindMine(mine, show, ROW , COL);
	
}

InitBoard函数--初始化

 我们可以通过for循环的嵌套给数组进行初始化,按照每一行每一列依次进行赋值。

// 初始化
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0, j = 0;
	for (i = 0; i < ROWS; i++)
	{
		for (j = 0; j < COLS; j++)
		{
			board[i][j] = set;
		}
	}
}

 DisplayBoard函数--打印

打印棋盘上的数跟数组初始化类似。当然我们也可以在增加一些信息上去,可以通过for循环将坐标行跟列对应的数字也打印出来。如图:

//打印
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 1, 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");
	}
}

 SetMine函数--布置雷

我们需要将雷随机的分布在棋盘上,这就需要通过 rand() srand() time() 函数来实现随机数的生成。只要包含调用这些函数的头文件就可以使用了。调用这些函数需要的头文件全都放在文件 game.h 中,所以只要包含了 #include"game.h" 就可以生成随机数了。

//布置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
	//初始化10个雷
	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--;
		}

	}
}

FindMine函数--排雷 

使用if语句判断所输入的坐标是否雷,如果是雷就跳出循环,如果不是就判断周围有几个雷(用GetMineCount函数判断)。当然,为了程序的安全在这之前需要对坐标进行判断,只有坐标合法才能使用。

//排雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0, y = 0;
	int win = 0;
	while (win < (row * col) - EASY_COUNT)
	{
		printf("请输入坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if ('1' == mine[x][y])
			{
				printf("很遗憾 失败了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				int count = GetMineCount(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);
	}
}

 GetMineCount函数--数雷

数雷的两种写法,一种是通过循环嵌套的方式算出雷的个数,另一种是直接将有几个雷的坐标返回。

//数雷
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	//直接返回周围8个坐标有几个雷
	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 GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	int i = 0, j = 0;
	int count = 0;
	for (i = -1; i <= 1; i++)
	{
		for (j = -1; j <= 1; j++)
		{
			count += (mine[x + i][y + j] - '0');
		}
	}
	return count;
}

2.2 代码实现

        2.2.1 teat.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 };//存放排查出雷的信息

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

	//打印
	DisplayBoard(show, ROW, COL);
	
	//布置雷
	SetMine(mine, ROW, COL);
	//DisplayBoard(mine, ROW, COL);
	
	//排雷
	FindMine(mine, show, ROW , COL);
	
}

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

	return 0;
}
        2.2.2 game.c
#include"game.h"

// 初始化
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0, j = 0;
	for (i = 0; i < ROWS; i++)
	{
		for (j = 0; j < COLS; j++)
		{
			board[i][j] = set;
		}
	}
}

//打印
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 1, 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");
	}
}

//布置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
	//初始化10个雷
	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--;
		}

	}
}

//数雷
//int GetMineCount(char mine[ROWS][COLS], int x, int y)
//{
//	//直接返回周围8个坐标有几个雷
//	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 GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	int i = 0, j = 0;
	int count = 0;
	for (i = -1; i <= 1; i++)
	{
		for (j = -1; j <= 1; j++)
		{
			count += (mine[x + i][y + j] - '0');
		}
	}
	return count;
}

//排雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0, y = 0;
	int win = 0;
	while (win < (row * col) - EASY_COUNT)
	{
		printf("请输入坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if ('1' == mine[x][y])
			{
				printf("很遗憾 失败了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				int count = GetMineCount(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);
	}
}
        2.2.3 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 EASY_COUNT 10

//初始化
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);

//打印
void DisplayBoard(char board[ROWS][COLS], int row, int col);

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

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

//数雷
int GetMineCount(char mine[ROWS][COLS], int x, int y);


扫雷游戏的扩展

 3.1 递归展开

如果排查位置不是雷,周围也没有雷,可以展开周围的⼀⽚。这里我们用递归的方式进行实现。判断是否进行展开的条件是这个坐标不是雷,且周围也没有雷的时候才会展开,如果周围有雷的话就计算周围雷的个数。

void ChainBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
	int i = 0, j = 0;
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		if (show[x][y] == '*')
		{
			if (GetMineCount(mine, x, y) == 0)
			{
				show[x][y] = ' ';
				for (i = x - 1; i <= x + 1; i++)
				{
					for (j = y - 1; j <= y + 1; j++)
					{
						ChainBoard(mine, show, row, col, i, j);
					}
				}
			}
			else
			{
				show[x][y] = GetMineCount(mine, x, y) + '0';
			}
		}
	}
}

新的 game.c

#include"game.h"

// 初始化
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0, j = 0;
	for (i = 0; i < ROWS; i++)
	{
		for (j = 0; j < COLS; j++)
		{
			board[i][j] = set;
		}
	}
}

//打印
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int i = 1, 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");
	}
}

//布置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
	//初始化10个雷
	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--;
		}

	}
}

//数雷
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	//直接返回周围8个坐标有几个雷
	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 GetMineCount(char mine[ROWS][COLS], int x, int y)
//{
//	int i = 0, j = 0;
//	int count = 0;
//	for (i = -1; i <= 1; i++)
//	{
//		for (j = -1; j <= 1; j++)
//		{
//			count += (mine[x + i][y + j] - '0');
//		}
//	}
//	return count;
//}

//递归排雷
void ChainBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{
	int i = 0, j = 0;
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		if (show[x][y] == '*')
		{
			if (GetMineCount(mine, x, y) == 0)
			{
				show[x][y] = ' ';
				for (i = x - 1; i <= x + 1; i++)
				{
					for (j = y - 1; j <= y + 1; j++)
					{
						ChainBoard(mine, show, row, col, i, j);
					}
				}
			}
			else
			{
				show[x][y] = GetMineCount(mine, x, y) + '0';
			}
		}
	}
}

//排雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0, y = 0;
	int win = 0;
	while (win < (row * col) - EASY_COUNT)
	{
		printf("请输入坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if ('1' == mine[x][y])
			{
				printf("很遗憾 失败了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				ChainBoard(mine, show, ROW, COL, x, y);
				int count = GetMineCount(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);
	}
}

  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值