扫雷游戏详解加代码

目录

❀扫雷游戏的基本介绍:

❀游戏网站:​​​​

❀扫雷的代码实现

⭐实现思路:

⭐那么如何实现雷和排查后显示出来的数字不冲突而且打印数字比较方便呢?

⭐怎么保证雷的随机性呢?

❀代码具体实践:

1.定义:

2.初始化

3.对雷的位置进行布置:

4.排查雷

5.打印出棋盘

❀全部代码:

test.c

game.c

game.h

❀结语:


全文共7500多字,请认真浏览哦(ง •_•)ง

❀扫雷游戏的基本介绍:

扫雷游戏详解加代码_第1张图片

这是一个使用控制台实现的经典游戏,它的棋盘通常为9*9的格子(当然也有其他样式的,不过这个比较经典)

它的玩法是点击一个坐标下的点,当这个点为雷的时候——>Game Over

如果这个点不是雷,那么会告诉你这个点周围(这个小格子围一圈的八个格子)有几个雷,把除10个雷以外的非雷找出来,排雷成功——>You Win

❀游戏网站:​​​​

扫雷游戏网页版 - Minesweeper

免费玩在线小游戏 | 来自Microsoft Start的游戏 (msn.cn)

❀扫雷的代码实现

⭐实现思路:

扫雷的过程中需要布置雷,而且要把这些雷的信息存储好,这里通过数据结构来实现

我们要先在9*9的棋盘上布置信息,那么意味着我们需要创建这样一块空间,首先想到的就是利用数组实现,如果这个地方放置雷,那么就存放1,没有布置就放0

但是当扫雷排查边缘的坐标时,我们通常会访问周围的一圈格子,但是数组的大小不够,会导致越界访问的

如图:

扫雷游戏详解加代码_第2张图片

于是在设计棋盘时,我们还需要扩大这个数组大小,扩大一圈,那么实际上我们需要创建一个11*11大小的数组

那现在又有一个问题:当排查坐标时,如果周围有一个雷,那么我们就需要在这个坐标上显示雷的个数为1,但是这与雷的位置填的是1冲突了,因此不能使用这种方式。

⭐那么如何实现雷和排查后显示出来的数字不冲突而且打印数字比较方便呢?

1.不使用0/1表示雷和非雷

2.使用两个棋盘,一个棋盘表示布置好的雷,另一个棋盘表示存放排查出的雷的信息,并且为了保证玩家看不到信息,展示出来的棋盘不能显示任何信息,因此用*代替下面的信息

这里使用第二个方案。

但是,现在又有一个疑惑了?

⭐怎么保证雷的随机性呢?

使用函数rand()和sand()函数来生成随即坐标

❀代码具体实践:

1.定义:

定义两个棋盘,这里使用二维数组

扫雷游戏详解加代码_第3张图片

这里定义row和col的定义,为下面的排雷做好准备

2.初始化

把一个棋盘(这里用数组实现)初始化为0

另一个棋盘,全部设置为*,增加神秘感

这里代码使用一个函数来实现初始化:

//数组初始化
void BoardInit(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;   //set表示要初识化的字符
		}
	}
}

3.对雷的位置进行布置:

注意:

用于随机生成坐标的rand函数的种子srand函数只需要在main函数中声明一次即可。

我们在布置雷的时候要检查该位置是否已经存在雷,避免重复布置。

//埋雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = MINE_COUNT;
	while (count)
	{
		int x = rand() % row + 1;      //随机生成雷的坐标
		int y = rand() % col + 1;
		if (board[x][y] == '0')        //检查该位置是否已经有雷
		{
			board[x][y] = '1';
			count--;
		}
	}
}

4.排查雷

这个就需要与玩家用户进行互动了,用户需要输入要排查的坐标

1.如果是雷,游戏结束

2.如果不是,要显示旁边一圈的雷的数量

3.当排查的这个坐标旁边没有雷时,往往会展开旁边的一片这里会使用递归来实现这个功能

4.如果发现最后实现了只剩雷,游戏胜利

这里分别实现上面的几个情况:

1.这里需要标记雷的位置,以便于判断是否是雷,这里使用!来在这个棋盘里代表雷

//标记雷的位置
void MarkMine(char board[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("请输入你想要标记位置的坐标->");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)    //判断该坐标是否合法
		{
			if (board[x][y] == '*')//判断该坐标是否被排查
			{
				board[x][y] = '!';
				break;
			}
			else
			{
				printf("该位置不能被标记,请重新输入!\n");
			}
		}
		else
		{
			printf("输入错误,请重新输入!\n");
		}
	}
}

2.获取坐标周围雷的个数 

//获取坐标周围雷的个数
int GetMineCount(char board[ROWS][COLS], int x, int y)
{
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			if (board[i][j] == '1')
			{
				count++;
			}
		}
	}
	return count;
}

3.递归爆炸式展开一片

//递归爆炸式展开一片
void ExplosionSpread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* pw)
{
	if (x >= 1 && x <= row && y >= 1 && y <= col)  //判断坐标是否为排查范围内
	{
		int num = GetMineCount(mine, x, y);   //获取坐标周围雷的个数
		if (num == 0)
		{
			(*pw)++;
			show[x][y] = ' ';   //如果该坐标周围没有雷,就把该坐标置成空格,并向周围八个坐标展开
			int i = 0;
			int j = 0;
			for (i = x - 1; i <= x + 1; i++)
			{
				for (j = y - 1; j <= y + 1; j++)
				{
					if (show[i][j] == '*')    //限制递归条件,防止已经排查过的坐标再次递归,从而造成死递归
						ExplosionSpread(mine, show, row, col, i, j, pw);
				}
			}
		}
		else
		{
			(*pw)++;
			show[x][y] = num + '0';
		}
	}
}

4.实现判断游戏是否结束:

//实现排雷,判断游戏结束与否
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;  //用来标记是否取得胜利
	int* pw = &win;
	char ch = 0;   //用来接受是否需要标记雷
	while (win < row * col - MINE_COUNT)
	{
		printf("请输入你想要排查的坐标->");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)   //判断坐标合法性
		{
			if (mine[x][y] == '1')
			{
				system("cls");
				printf("很遗憾,你被炸死了!\n");
				BoardPrint(mine, row, col);   //被炸死了就打印mine数组,让用户知道自己怎么死的
				break;
			}
			else
			{
				if (show[x][y] != '*')   //判断是否重复排查
				{
					printf("该坐标已被排查,请重新输入!\n");
					continue;  //直接进入下一次循环
				}
				else
				{
					ExplosionSpread(mine, show, row, col, x, y, pw);  //爆炸展开一片
					system("cls");  //清空屏幕
					BoardPrint(show, row, col);  //打印棋盘
					printf("需要标记雷的位置请输入y/Y,否则请按任意键->");
					while ((ch = getchar()) != '\n');  //清理缓冲区
					scanf("%c", &ch);
					if (ch == 'Y' || ch == 'y')
					{
						MarkMine(show, row, col);   //标记雷
						system("cls");
						BoardPrint(show, row, col);
					}
					else
					{
						continue;
					}
				}
			}
		}
		else
		{
			printf("输入错误,请重新输入!\n");
		}
	}
	if (win == row * col - MINE_COUNT)
	{
		system("cls");
		printf("恭喜你,排雷成功!\n");
		BoardPrint(show, row, col);
		return;
	}
}

5.打印出棋盘

//打印雷盘
void BoardPrint(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("------扫雷游戏------\n");
	for (i = 0; i <= row; 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");
	}
	printf("------扫雷游戏------\n");
}

❀全部代码:

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

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

void game()
{
	//定义用于存放雷和显示雷的数组
	char mine[ROWS][COLS];
	char show[ROWS][COLS];
	//数组初始化
	BoardInit(mine, ROWS, COLS, '0');
	BoardInit(show, ROWS, COLS, '*');
	//埋雷
	SetMine(mine, ROW, COL);
	system("cls");   //清除菜单
	//打印出雷盘
	//BoardPrint(mine, ROW, COL);//用于自己调试观察,在发布时注意要把它注释掉(这里注释了)
	BoardPrint(show, ROW, COL);
	//排雷
	FindMine(mine, show, ROW, COL);
}

int main()
{
	srand((unsigned int)time(NULL));//设置随机数的种子
	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);
	return 0;
}

game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

//数组初始化
void BoardInit(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < rows; i++)
	{
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;   //set表示要初识化的字符
		}
	}
}

//埋雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
	int count = MINE_COUNT;
	while (count)
	{
		int x = rand() % row + 1;      //随机生成雷的坐标
		int y = rand() % col + 1;
		if (board[x][y] == '0')        //检查该位置是否已经有雷
		{
			board[x][y] = '1';
			count--;
		}
	}
}

//打印雷盘
void BoardPrint(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	printf("------扫雷游戏------\n");
	for (i = 0; i <= row; 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");
	}
	printf("------扫雷游戏------\n");
}

//标记雷的位置
void MarkMine(char board[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	while (1)
	{
		printf("请输入你想要标记位置的坐标->");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)    //判断该坐标是否合法
		{
			if (board[x][y] == '*')        //判断该坐标是否被排查
			{
				board[x][y] = '!';
				break;
			}
			else
			{
				printf("该位置不能被标记,请重新输入!\n");
			}
		}
		else
		{
			printf("输入错误,请重新输入!\n");
		}
	}
}

//获取坐标周围雷的个数
int GetMineCount(char board[ROWS][COLS], int x, int y)
{
	int i = 0;
	int j = 0;
	int count = 0;
	for (i = x - 1; i <= x + 1; i++)
	{
		for (j = y - 1; j <= y + 1; j++)
		{
			if (board[i][j] == '1')
			{
				count++;
			}
		}
	}
	return count;
}

//递归爆炸式展开一片,直到遇到雷
void ExplosionSpread(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* pw)
{
	if (x >= 1 && x <= row && y >= 1 && y <= col)  //判断坐标是否为排查范围内
	{
		int num = GetMineCount(mine, x, y);   //获取坐标周围雷的个数
		if (num == 0)
		{
			(*pw)++;
			show[x][y] = ' ';   //如果该坐标周围没有雷,就把该坐标置成空格,并向周围八个坐标展开
			int i = 0;
			int j = 0;
			for (i = x - 1; i <= x + 1; i++)
			{
				for (j = y - 1; j <= y + 1; j++)
				{
					if (show[i][j] == '*')    //限制递归条件,防止已经排查过的坐标再次递归,从而造成死递归
						ExplosionSpread(mine, show, row, col, i, j, pw);
				}
			}
		}
		else
		{
			(*pw)++;
			show[x][y] = num + '0';
		}
	}
}

//实现排雷,判断游戏结束与否
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;  //用来标记是否取得胜利
	int* pw = &win;
	char ch = 0;   //用来接受是否需要标记雷
	while (win < row * col - MINE_COUNT)
	{
		printf("请输入你想要排查的坐标->");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)   //判断坐标合法性
		{
			if (mine[x][y] == '1')
			{
				system("cls");
				printf("很遗憾,你被炸死了!\n");
				BoardPrint(mine, row, col);   //被炸死了就打印mine数组,让用户知道自己怎么死的
				break;
			}
			else
			{
				if (show[x][y] != '*')   //判断是否重复排查
				{
					printf("该坐标已被排查,请重新输入!\n");
					continue;  //直接进入下一次循环
				}
				else
				{
					ExplosionSpread(mine, show, row, col, x, y, pw);  //爆炸展开一片
					system("cls");  //清空屏幕
					BoardPrint(show, row, col);  //打印棋盘
					printf("需要标记雷的位置请输入y/Y,否则请按任意键->");
					while ((ch = getchar()) != '\n');  //清理缓冲区
					scanf("%c", &ch);
					if (ch == 'Y' || ch == 'y')
					{
						MarkMine(show, row, col);   //标记雷
						system("cls");
						BoardPrint(show, row, col);
					}
					else
					{
						continue;
					}
				}
			}
		}
		else
		{
			printf("输入错误,请重新输入!\n");
		}
	}
	if (win == row * col - MINE_COUNT)
	{
		system("cls");
		printf("恭喜你,排雷成功!\n");
		BoardPrint(show, row, col);
		return;
	}
}

game.h

#pragma once

#include
#include
#include
#include

#define ROW 9
#define COL 9
#define ROWS ROW + 2
#define COLS COL + 2
#define MINE_COUNT 10

//数组初始化
void BoardInit(char board[ROWS][COLS], int rows, int cols, char set);
//埋雷
void SetMine(char board[ROWS][COLS], int row, int col);
//排雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//打印雷盘
void BoardPrint(char board[ROWS][COLS], int row, int col);

❀结语:

感谢观看,欢迎点赞收藏O(∩_∩)O

你可能感兴趣的:(游戏,c语言)