~扫雷游戏来啦~(C语言)~~万字大文

~扫雷游戏来啦~(C语言)~~万字大文_第1张图片

 先赞后看,不足指正!

这将对我有很大的帮助!

项目专栏:游戏~

阿哇旭的主页:Awas-Home page


目录

引言 

一、编译环境

二、游戏要求

三、游戏分析设计

四、多文件操作

五、游戏选择菜单

六、游戏功能实现

1.预定义信息

2.初始化棋盘

3.打印棋盘

4.布置雷

5.排雷和标记雷

6.胜利判断

七、游戏逻辑搭建

八、完整代码

九、结语 


引言 

        相信大家小时候都玩过有趣的扫雷小游戏吧!在这里,我们可以利用自己学习过的知识来实现这个游戏项目。

        可能用到的知识:

  1. 随机数的生成:捣蛋小游戏
  2. 数组的使用:小小数组,给贝蒂坐下~(转载快去点赞!

一、编译环境

        集成开发环境(IDE):Visual Studio 2022,若要使用scanf函数,请添加如下注释:

#define _CRT_SECURE_NO_WARNINGS
//忽略scanf函数的安全警告

        那么,话不多说,我们一起来看看吧! 


二、游戏要求

1. 玩家通过游戏菜单选择开始游戏(继续玩)或者退出游戏

2. 扫雷的棋盘是默认为 9*9 的格子

3. 默认随机布置10个雷

4. 可以排查雷

(1)如果排查方块不是雷,就会继续排查周围的方块,直到排到有雷的方块(显示周围有几个雷)或到达边界为止;

(2)如果排查方块是雷,就炸死,游戏结束 ;

(3)玩家可以对认为是雷的方块进行标记处理;

(4)把除10个雷方块之外的所有非雷方块都找出来,排雷成功,游戏结束;


三、游戏分析设计

        1. 我们需要在9*9的棋盘上进行操作,通过创建一个9*9的数组来存放信息,布置雷时,有雷存放“1”,无雷存放“0”;

        2. 在排查雷过程中,如果排查方块无雷,就在该方块处显示周围八个方块中雷的数量;

~扫雷游戏来啦~(C语言)~~万字大文_第2张图片

        为了防止数组越界的情况,在设计的时候,我们可以给棋盘(数组)扩大一圈,雷还是布置在中间的9*9的坐标上,周围一圈不去布置雷就行,这样就解决了越界的问题。所以我们将存放数据的数组创建成11*11 是比较合适。

~扫雷游戏来啦~(C语言)~~万字大文_第3张图片

         3. 在游戏中,我们不能把布置信息展示给玩家。因此,我们要创建两个数组,一个数组(mine)用于存放布置雷的信息,另一个数组(show)用于向玩家展示;

        4. 若排查方块没有雷,就统计显示周围八个方块中雷的数量;

        5. 布置雷棋盘全部初始化为 '0' 并随机布雷,展示给玩家的棋盘全部初始化为 '*',为未排查状态。

~扫雷游戏来啦~(C语言)~~万字大文_第4张图片


~扫雷游戏来啦~(C语言)~~万字大文_第5张图片


四、多文件操作

        使用多个文件来组织和管理项目。将代码分散到多个文件中有助于提高游戏代码的可读性、可维护性和可扩展性。具体步骤:

        1. 创建头文件 game.h ,用于存放数据类型以及功能函数的声明;

        附:(其他源文件引用即可,#include "game.h")

        2. 创建源文件 test.c ,用于文件中游戏的测试逻辑;

        3. 创建源文件 game.c ,用于文件中游戏功能函数的实现。


五、游戏选择菜单

(1)功能

1. 玩家通过输入选择 1. 开始游戏,0. 退出游戏;

2. 若输入错误,则会提醒玩家重新选择。

(2)代码实现 

        我们可以利用简单的 do-whileswitch 结构来实现功能。

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

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL)); // 使用当前时间作为随机数种子
	do
	{
		menu(); // 菜单
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			system("cls"); // 清空屏幕指令,头文件
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,重新选择!\n");
			break;
		}

	} while (input); // 选择0,退出游戏

	return 0;
}

六、游戏功能实现

1.预定义信息

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

#define ROWS ROW+2 // 扩展行
#define COLS COL+2 // 扩展列
 
#define BOMB_COUNT 10 // 布置雷个数


#include
#include
#include
#include

        记得在其他源文件中引用(#include "game.h")。 

2.初始化棋盘

(1)功能

1. mine数组全部初始化为 '0'。

2. show数组全部初始化为 '*'。

(2)代码实现

        我们可以 for 的双循环结构,来实现 mineshow 棋盘的初始化。

// 初始化棋盘
void InitBoard(char arr[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++)
		{
			arr[i][j] = set;
		}
	}
}

3.打印棋盘

(1)功能

打印向玩家展示的游戏棋盘,美化棋盘,使其更加整洁。

(2)代码实现 

        利用 for 的循环结构打印棋盘。

// 打印棋盘
void DisplayBoard(char Board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("  ----——----------扫雷游戏---------------\n"); // 标头
	// 打印列号
	for (i = 0; i <= col; i++)
	{
		if (i == 0)
		{
			printf("    "); // 四空格
		}
		else
		{
			printf(" %d  ", i); //三空格一数字
		}
	}
	printf("\n"); // 换行

	for (i = 0; i <= row; i++)
	{
		if (i == 0)
		{
			printf("   |"); // 三空格一竖线
		}
		else
		{
			printf("---|"); // 三短横一竖线
		}
	}
	printf("\n");

	for (i = 1; i <= row; i++)
	{
		printf(" %d |", i); // 打印行号
		for (j = 1; j <= col; j++)
		{
			printf(" %c |", Board[i][j]); // 棋盘字符
		}
		printf("\n");
		for (j = 0; j <= col; j++)
		{
			if (j == 0)
			{
				printf("   |");
			}
			else
			{
				printf("---|");
			}
		}
		printf("\n");
	}
	printf("  ---------------------------------------\n"); // 结束线
}

(3)效果展示

~扫雷游戏来啦~(C语言)~~万字大文_第6张图片

4.布置雷

(1)功能

在mine数组中随机布置雷,

(2)代码实现

         利用随机数来确定布雷坐标,以达到在mine数组棋盘中随机布雷的目的。

// 布置雷
void SetMine(char arr[ROWS][COLS], int row, int col)
{
	int count = BOMB_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (arr[x][y] == '0')
		{
			arr[x][y] = '1';
				count--;
		}
	}
}

(3)效果展示 

~扫雷游戏来啦~(C语言)~~万字大文_第7张图片

5.排雷和标记雷

(1)功能

1. 玩家排雷或标记雷,若排查位置是雷,则递归展开,直至排查到雷;

2 .对认为是雷的位置进行标记处理。

(2)代码实现

// 统计排查坐标周围雷的数量-(法1)
static int GetBombCount(char mine[ROWS][COLS], int x, int y)
{
	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');
}

// 统计排查坐标周围雷的数量-(法2)
//static int GetBombCount(char mine[ROWS][COLS], int x, int y)
//{
//	int i = 0;
//	int count = 0;
//	for (i = x - 1; i <= x + 1; i++)
//	{
//		int j = 0;
//		for (j = y - 1; y <= y + 1; j++)
//		{
//			if (mine[i][j] == '1')
//			{
//				count += (mine[i][j] - '0');
//			}
//		}
//	}
//}

// 递归排雷
void SpreadBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	if (x<1 || x>ROW || y<1 || y>COL)
	{
		return;
	}
	if (show[x][y] != '*')
	{
		return;
	}
	int count = GetBombCount(mine, x, y);
	if (count == 0) // 无雷继续扩展
	{
		show[x][y] = ' '; // 扩展为' '
		SpreadBoard(mine, show, x, y + 1);
		SpreadBoard(mine, show, x, y - 1);
		SpreadBoard(mine, show, x + 1, y - 1);
		SpreadBoard(mine, show, x + 1, y + 1);
		SpreadBoard(mine, show, x + 1, y);
		SpreadBoard(mine, show, x - 1, y + 1);
		SpreadBoard(mine, show, x - 1, y - 1);
		SpreadBoard(mine, show, x - 1, y);
	}
	else
	{
		show[x][y] = '0' + count; // 有雷输出个数
	}

}

// 排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("请输入要排查雷的坐标:");
	scanf("%d %d", &x, &y);
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		if (show[x][y] == '*')
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了!\n");
				strcpy(gameStatus, "DEFEAT");
			}
			else
			{
				SpreadBoard(mine, show, x, y); // 递归排雷扩展
				sum--;
				if (sum == 0)
				{
					strcpy(gameStatus, "VICTORY"); // 用于将一个字符串的内容复制到另一个字符串中
				}
			}
		}
		else
		{
			printf("该坐标已被排查\n");
		}
	}
	else
	{
		printf("坐标无效,重新输入\n");
	}
}

// 标记雷
void MarkMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int input = 0;
	printf("请输入要标记雷位置的坐标:>");
	scanf("%d %d", &x, &y);
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		if (mine[x][y] == '1')
		{
			show[x][y] = '!';
			sum--;
			if (sum == 0)
			{
				strcpy(gameStatus, "VICTORY"); // 用于将一个字符串的内容复制到另一个字符串中
			}
		}
		else
		{
			printf("该坐标已被排查或标记\n");
		}
	}
	else
	{
		printf("坐标无效,重新输入\n");
	}
}

6.胜利判断

(1)功能

设置一个全局变量 sum = 布雷数量、游戏状态变量 gameStatus,排雷后变量 sum 减一,标记雷后变量sum 减一,直到 sum 为零,然后 strcmp(gameStatus, "VICTORY") ,再进行判断。

(2)代码实现

1.strcmp是C语言中的一个字符串比较函数,用于比较两个字符串是否相等。

函数原型如下:

int strcmp(const char *str1, const char *str2);

2.strcpy是C语言中的一个字符串复制函数,用于将一个字符串的内容复制到另一个字符串中。

函数原型如下:

char *strcpy(char *dest, const char *src);

 

if (strcmp(gameStatus, "VICTORY") == 0)
{
	DisplayBoard(mine, ROW, COL);
	DisplayBoard(show, ROW, COL);
	printf("恭喜你,扫雷成功!");
	break;
}
else if (strcmp(gameStatus, "DEFEAT") == 0)
{
	DisplayBoard(mine, ROW, COL);
	DisplayBoard(show, ROW, COL);
	printf("很遗憾,扫雷失败!");
	break;
}

七、游戏逻辑搭建

(1)运行顺序

1. 初始化棋盘

2. 随机布置雷

3. 玩家选择标记雷或排查雷

4. 判断是否被雷炸死

5. 判断是否扫雷成功

6. 若步骤4或5均未发生,就继续执行步骤 1、2、3,循环往复。

(2)逻辑框架

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

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

// 功能选择
void choose()
{
	printf(" ****************************************\n");
	printf(" ************** 1. 排查雷 ***************\n");
	printf(" ************** 2. 标记雷 ***************\n");
	printf(" ****************************************\n");
}

// 游戏
void game()
{
	// 完成游戏
	// 存放布置雷信息
	char mine[ROWS][COLS] = { 0 }; // 全部初始化为'0'
	// 存放排查雷信息
	char show[ROWS][COLS] = { 0 }; // 全部初始化为'*'

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

	// 布置雷
	// 在9x9的棋盘上随机布置若干个雷
	SetMine(mine, ROW, COL);
	//DisplayBoard(mine, ROW, COL); // 打印布置雷信息的棋盘
	//DisplayBoard(show, ROW, COL); // 打印游戏棋盘

	// 1.排查雷
	//FindMine(mine, show, ROW, COL);

	// 2.标记雷
	//MarkMine(mine,show, ROW, COL);
}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL)); // 使用当前时间作为随机数种子
	do
	{
		menu(); // 菜单
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			system("cls"); // 清空屏幕指令,头文件
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,重新选择!\n");
			break;
		}

	} while (input); // 选择0,退出游戏

	return 0;
}

八、完整代码

(1)game.h

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

#define ROWS ROW+2 // 扩展行
#define COLS COL+2 // 扩展列

#define BOMB_COUNT 10 // 布置雷个数


#include
#include
#include
#include

// 变量声明

extern int sum; // 剩余雷的数量
extern char gameStatus[20]; // 标记游戏状态变量

// 函数声明

// 初始化棋盘函数
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char set);

// 打印棋盘函数
void DisplayBoard(char Board[ROWS][COLS], int row, int col);

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

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

// 标记雷函数
void MarkMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

(2)test.c

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

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

// 功能选择
void choose()
{
	printf(" ****************************************\n");
	printf(" ************** 1. 排查雷 ***************\n");
	printf(" ************** 2. 标记雷 ***************\n");
	printf(" ****************************************\n");
}

// 游戏
void game()
{
	// 完成游戏
	// 存放布置雷信息
	char mine[ROWS][COLS] = { 0 }; // 全部初始化为'0'
	// 存放排查雷信息
	char show[ROWS][COLS] = { 0 }; // 全部初始化为'*'

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

	// 布置雷
	// 在9x9的棋盘上随机布置若干个雷
	SetMine(mine, ROW, COL);
	//DisplayBoard(mine, ROW, COL); // 打印布置雷信息的棋盘
	//DisplayBoard(show, ROW, COL); // 打印游戏棋盘

	// 1.排查雷
	//FindMine(mine, show, ROW, COL);

	// 2.标记雷
	//MarkMine(mine,show, ROW, COL);

	while (1)
	{
		if (strcmp(gameStatus, "FIGHTING")!= 0) // 用于比较两个字符串是否相等
		{
			break;
		}
		int input = 0;
		DisplayBoard(show, ROW, COL);
		choose();
		printf("请选择功能(1/2):>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			FindMine(mine, show, ROW, COL); // 排查雷
			Sleep(1000);
			system("cls");
			break;
		case 2:
			MarkMine(mine, show, ROW, COL); // 标记雷
			Sleep(1000);
			system("cls");
			break;
		default:
			printf("输入错误,重新输入!\n");
			Sleep(1000);
			system("cls");
			break;
		}
		if (strcmp(gameStatus, "VICTORY") == 0) // 用于比较两个字符串是否相等
		{
			DisplayBoard(mine, ROW, COL);
			DisplayBoard(show, ROW, COL);
			printf("恭喜你,扫雷成功!");
			break;
		}
		else if (strcmp(gameStatus, "DEFEAT") == 0) // 用于比较两个字符串是否相等
		{
			DisplayBoard(mine, ROW, COL);
			DisplayBoard(show, ROW, COL);
			printf("很遗憾,扫雷失败!");
			break;
		}
	}
}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL)); // 使用当前时间作为随机数种子
	do
	{
		menu(); // 菜单
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			system("cls"); // 清空屏幕指令,头文件
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,重新选择!\n");
			break;
		}

	} while (input); // 选择0,退出游戏

	return 0;
}

(3)game.h

#include"game.h"
int sum = BOMB_COUNT; // 剩余雷的数量
char gameStatus[20] = "FIGHTING"; // 标记游戏状态变量

// 初始化棋盘
void InitBoard(char arr[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++)
		{
			arr[i][j] = set;
		}
	}
}

// 打印棋盘
void DisplayBoard(char Board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("  ----——----------扫雷游戏---------------\n"); // 标头
	// 打印列号
	for (i = 0; i <= col; i++)
	{
		if (i == 0)
		{
			printf("    "); // 四空格
		}
		else
		{
			printf(" %d  ", i); //三空格一数字
		}
	}
	printf("\n"); // 换行

	for (i = 0; i <= row; i++)
	{
		if (i == 0)
		{
			printf("   |"); // 三空格一竖线
		}
		else
		{
			printf("---|"); // 三短横一竖线
		}
	}
	printf("\n");

	for (i = 1; i <= row; i++)
	{
		printf(" %d |", i); // 打印行号
		for (j = 1; j <= col; j++)
		{
			printf(" %c |", Board[i][j]); // 棋盘字符
		}
		printf("\n");
		for (j = 0; j <= col; j++)
		{
			if (j == 0)
			{
				printf("   |");
			}
			else
			{
				printf("---|");
			}
		}
		printf("\n");
	}
	printf("  ---------------------------------------\n"); // 结束线
}

// 布置雷
void SetMine(char arr[ROWS][COLS], int row, int col)
{
	int count = BOMB_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (arr[x][y] == '0')
		{
			arr[x][y] = '1';
			count--;
		}
	}
}

// 统计排查坐标周围雷的数量-(法1)
static int GetBombCount(char mine[ROWS][COLS], int x, int y)
{
	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');
}

// 统计排查坐标周围雷的数量-(法2)
//static int GetBombCount(char mine[ROWS][COLS], int x, int y)
//{
//	int i = 0;
//	int count = 0;
//	for (i = x - 1; i <= x + 1; i++)
//	{
//		int j = 0;
//		for (j = y - 1; y <= y + 1; j++)
//		{
//			if (mine[i][j] == '1')
//			{
//				count += (mine[i][j] - '0');
//			}
//		}
//	}
//}

// 递归排雷
void SpreadBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	if (x<1 || x>ROW || y<1 || y>COL)
	{
		return;
	}
	if (show[x][y] != '*')
	{
		return;
	}
	int count = GetBombCount(mine, x, y);
	if (count == 0) // 无雷继续扩展
	{
		show[x][y] = ' '; // 扩展为' '
		SpreadBoard(mine, show, x, y + 1);
		SpreadBoard(mine, show, x, y - 1);
		SpreadBoard(mine, show, x + 1, y - 1);
		SpreadBoard(mine, show, x + 1, y + 1);
		SpreadBoard(mine, show, x + 1, y);
		SpreadBoard(mine, show, x - 1, y + 1);
		SpreadBoard(mine, show, x - 1, y - 1);
		SpreadBoard(mine, show, x - 1, y);
	}
	else
	{
		show[x][y] = '0' + count; // 有雷输出个数
	}

}

// 排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("请输入要排查雷的坐标:");
	scanf("%d %d", &x, &y);
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		if (show[x][y] == '*')
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了!\n");
				strcpy(gameStatus, "DEFEAT");
			}
			else
			{
				SpreadBoard(mine, show, x, y); // 递归排雷扩展
				sum--;
				if (sum == 0)
				{
					strcpy(gameStatus, "VICTORY");/* 用于将一个字符串的内容复制到另一 
                                                   个字符串中*/
				}
			}
		}
		else
		{
			printf("该坐标已被排查\n");
		}
	}
	else
	{
		printf("坐标无效,重新输入\n");
	}
}

// 标记雷
void MarkMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int input = 0;
	printf("请输入要标记雷位置的坐标:>");
	scanf("%d %d", &x, &y);
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		if (mine[x][y] == '1')
		{
			show[x][y] = '!';
			sum--;
			if (sum == 0)
			{
				strcpy(gameStatus, "VICTORY"); /* 用于将一个字符串的内容复制到另一个        
                                                字符串中*/
			}
		}
		else
		{
			printf("该坐标已被排查或标记\n");
		}
	}
	else
	{
		printf("坐标无效,重新输入\n");
	}
}

九、结语 

        希望这篇文章对大家有所帮助,如果你有任何问题和建议,欢迎在评论区留言,这将对我有很大的帮助。

        完结!咻咻~~

你可能感兴趣的:(游戏,游戏,数据结构)