C语言:扫雷小游戏(空白展开和标记雷点)

前言

相信大多数人都玩过扫雷游戏–微软自带扫雷游戏

在一个9×9(初级)、16×16(中级)、16×30(高级)或自定义大小的方块矩阵中随机布置一定量的地雷(初级为10个,中级为40个,高级为99个),再由玩家逐个翻开方块,以找出所有地雷为最终游戏目标。

玩家根据数字(数字代表周围8个格子有多少个雷)来依次排除雷;当点击到空白位置时,区域就会自动展开;你知道雷位置之后,也可以标记这个雷;当然,老玩家都知道,如果标记正确的话,点击已经开过的位置时,它还会自动展开。
下面,我们就来实现一个9*9扫雷游戏的主要特性,包括:标记雷和空白展开,当然扫雷的基本特性也会实现。实现标记和排查用坐标方式来进行。

非常清晰的思路布局

实现游戏界面

我们先对简单部分下手。对于一个游戏,首先映入眼帘的就是界面显示,这个界面拥有一些选择功能。所以我们先写一个打印菜单,然后给玩家选择的权利。
在这里插入图片描述
打印菜单嘛,只需用到printf即可;选择部分我们就要用到switch来进行选择
在switch中我们根据菜单给出的选项,给出相应的选择项目,当然,还需要考虑到玩家可能会输入错误,所以我们就要用到default识别。无论玩家是进入游戏还是退出游戏,决定权永远在玩家手里,所以,即使进入游戏之后,当一局游戏结束之后,也要给玩家继续选择。所以,我们要在这个switch外面写一个循环封装起来。对于循环,只要选择退出游戏就是终止循环,所以,我们可以用0代表退出游戏,对于先做后判断循环是否继续的,直接使用do while会更加完美。
(为了避免文章冗余,这里先给出思路图,代码会放在最后面)
在这里插入图片描述

大体思路的布局

接着就需要考虑进入游戏的情况了。我们要先理一下总体思路。首先就是展示一个棋盘给玩家,然后玩家进行排查雷;要有雷,我们就得埋雷。在这里我们用**‘1’来标识雷(原因后面会知),非雷就用‘0’标记**;在一个棋盘上我们既要埋雷,又得在棋盘上对其掩盖起来,对玩家进行隐藏,在同一个棋盘上我们有点不知所措;那么我们不妨创建两个棋盘一个来进行埋雷,一个进行显示掩盖效果。那么向玩家展示的,就是这个显示效果的了。之后我们要简单模拟一下棋盘排除雷时的效果,我们会发现,在9*9的棋盘上,==当位置处于边缘时,对它进行周围雷的统计时,会发生越界,==为了防止越界,我们实际创建棋盘时就要创建一个大一点的棋盘,给玩家展示就展示小一点的即可。
在这里插入图片描述

简简单单的初始化和打印

我们首先需要弄一个棋盘出来,也就是打印。而在打印出来之前,我们打印什么就需要进行初始化。棋盘是一个面,所以我们用一个二维数组创建棋盘。因此,我们可以用两个for循环来进行遍历初始化。由于我们有两个棋盘,所以要进行不同的初始化,所以可以在形参中添加一个变量进行不同的初始化的选择。

void BoardInit(char board[ROWS][COLS], int row, int col,char init);
init就是进行不同初始化的选择。

打印也是用两个for循环来进行。当然,我这里为了方便最终会给出坐标来对应行列,便于玩家来选择坐标。
在这里插入图片描述

理所应当的埋雷

接着就是实现埋雷了,这里我们会埋10颗雷。我们想要的效果肯定是电脑能给我们随机埋雷,这时就要想要随机数函数rand(),在对它进行取模即可,但只对它取模的话,只会是0—8,所以我们只需要简单的+1就行。最后用while来循环10次埋雷。要实现不同地方埋雷,还需要判断是否出现同个地方已经埋雷的情况,所以成功埋一颗雷就减少一颗。

while (count)//全部10颗雷,没有雷就停止
{
x = rand() % 9 + 1;//随机数
y = rand() % 9 + 1;
if (board[x][y] == ‘0’)//进行判断
{
board[x][y] = ‘1’;
count–;//成功减少一颗
}

有点难的排雷

最后就是排查雷,这是最关键的一步,相对的,也比较难以实现。
对于较为复杂的情况,我们就要分情况。思考片刻,不就是分这个地方有雷没雷的情况吗?当然我们还要考虑玩家可能会输入错误的坐标,那我们就大体分为三种情况即可。
在这里插入图片描述

这里先对有雷的先说,没雷会展开细说。上面我们埋雷知道,雷就是‘1’,所以踩到雷之后,就把实际雷的情况展示给玩家看即可。
没雷时,我们还需要判断该位置是否为空白,如果是空白就进行展开,非空白那就停止。
在这里插入图片描述
是否空白我们还需要进行判断;我们要对该位置周围进行判断雷的个数;
我们上面引用了‘1’来标识雷,周围的雷数我们将这些‘1’加起来即可。这就是利用‘1’的方便之处。由于周围要访问8个格子,我们不妨对这些位移下标用一个全局变量一维数组进行记录,引用时只需要一个循环即可,这样写你会发现思路明朗不少,而且代码看起来比较干净。

//创建下标位移数组
int dx[8] = { -1,-1,-1,0,0,1,1,1 };
int dy[8] = { -1,0,1,-1,1,-1,0,1 };

引用时,只需对当前下标再加上位移下标数组的位置即可。

for (int i = 0; i < 8; i++)
{
int nx = x + dx[i];
int ny = y + dy[i];
count = count + (mine[nx][ny] - ‘0’);//由于打印时用的是字符,所以需要进行简单的转换,也就是减去‘0’
//count是统计雷数的。
}

非空白情况很容易,只需要显示当前周围雷数就行。空白该如何展开才是一个难点。
假设我们对一个空白位置上,我们需要对它周围判断是否为为空白,如果是空白,继续展开,遇到数字了,就停下来。如:
在这里插入图片描述
这个数的正上方为空白,那么就要对这个正上方的空白位置的周围位置继续遍历。这种有种“以此类推”的思想,我们会想到循环和递归,但是循环在这里明显行不通,如果看过我前面一些文章的话,我们知道递归还有另一种说法:深度优先探索,能对周围一直探索下去。所以这种递归的思想是符合我们想要”以此类推“的思想的。符合递归思想后,我们就要想出递归的终止条件
由图可看出,还没有限制条件的递归会对同一个地方进行多次访问,我们就要排除已经遍历过的。再给它一个框架限制在9*9棋盘内即可。而遇到非空白的就停止对周围进行遍历访问。那么我们可以将是否空白结合起来,只要遇到空白就只给出相应雷数数字就行。我们到这里还要用一个变量进行对非雷进行统计,因为判断正确时,我们还要继续排查雷,所以加上一个循环,这个变量可以用来判断循环条件,且可以判断输赢。
到这里,扫雷小游戏已经基本实现完了,但当我在测试时,如果没有对已知雷进行标记,还是有点眼花缭乱,所以最后,我还多加了一个标记雷的功能。标记雷也很容易,跟埋雷差不多,用两个for循环,再判断该位置是否已经被展开即可
由于我们在中途想出来的,所以最后是在排除雷的函数中进行实现的。也是分情况进行判断。

最后,对以上实现不同功能的扫雷函数进行封装,就完成了。
这里,还是用了分文件进行代码实现。

原代码

//test.c
#include"game.h"
void menu()
{
	printf("*************************\n");
	printf("*************************\n");
	printf("******   1.PLAY   *******\n");
	printf("******   0.EXIT   *******\n");
	printf("*************************\n");
	printf("*************************\n");
}

void game()
{
	char mine[ROWS][COLS];//埋雷的
	char show[ROWS][COLS];//操作的
	//初始化
	BoardInit(mine, ROWS, COLS,'0');
	BoardInit(show, ROWS, COLS,'*');
	//打印
	BoardPrint(show, ROW, COL);
	//埋雷
	SetMine(mine, ROW, COL);
	//排查雷(包含标记雷)
	FineMine(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");//表示清屏,为了使显示效果更加简洁
				printf("已进入游戏。\n");
				game();
				break;
			case 0:
				system("cls");
				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 row, int col,char init)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			board[i][j] = init;
		}
	}

}
//打印
void BoardPrint(char board[ROWS][COLS], int row, int col)
{
	for (int i = 0; i <= row; 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 row, int col)
{
	int x, y;
	int count = COUNT;
	while (count)
	{
		x = rand() % 9 + 1;
		y = rand() % 9 + 1;
		if (board[x][y] == '0')
		{
			board[x][y] = '1';
			count--;
		}
		
	}
	
}


//选择菜单
void menu_()
{
	printf("选择标记雷或者排查雷\n");
	printf("****   1.标记雷   ****\n");
	printf("****   2.排查雷   ****\n");
}
//标记雷
void Mark(char show[ROWS][COLS],int row,int col)
{
	int x, y;
	
	while (1)
	{
		printf("请输入你要标记雷的坐标:");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (show[x][y] == '*')
			{
				system("cls");
				show[x][y] = '#';
				BoardPrint(show, row, col);
				break;
			}
			else
			{
				printf("该位置已经是非雷了,请重新输入\n");
			}
		}
		else
		{
			printf("输入坐标超出坐标范围,请重新输入。\n");
		}
	}
	
	
}

//创建下标位移数组
int dx[8] = { -1,-1,-1,0,0,1,1,1 };
int dy[8] = { -1,0,1,-1,1,-1,0,1 };
//查找周围有没有雷
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	int count = 0;
	for (int i = 0; i < 8; i++)
	{
		int nx = x + dx[i];
		int ny = y + dy[i];
		count = count + (mine[nx][ny] - '0');
	}
	return count;
}
//展开
void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y,int* sum)//这里要对sum进行保存,所以用了指针取实参的地址
{
	if (x >= 1 && x <= ROW && y >= 1 && y <= COL&& show[x][y] == '*')//符合递归条件
	{	
		(*sum)++;//记录符合个数
		int count = GetMineCount(mine, x, y);
			if (count == 0)//0继续递归,非0显示并停止
			{
				for (int i = 0; i < 8; i++)//依次向周围进行递归探索
				{
					show[x][y] = ' ';
					//nx,ny表示进入下个位置的下标
					int nx = x + dx[i];
					int ny = y + dy[i];
					expand(mine, show, nx, ny, sum);
				}
			}
			else
			{
				show[x][y] = count + '0';
			}
	}
	
	
}
//排查雷
void FineMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row, int col)
{
	//sum为统计非雷个数
	int x, y,sum=0;
	while (sum < row * col - COUNT)
	{
		int input = 0;
		//选择标记雷或者排查雷
		menu_();
		printf("请输入:");
		scanf("%d", &input);
		if (input == 1)
		{
			Mark(show, row, col);
		}
		else if (input == 2)
		{
			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);
					break;
				}
				else
				{
					expand(mine, show, x, y, &sum);
					system("cls");
					BoardPrint(show, row, col);

				}
			}
			else
			{
				printf("超出输入范围,请重新输入。\n");
			}
		}
		else
		{
			printf("输入错误,请重新输入。\n");
		}
		
		
	}
	if (sum == col * row - COUNT)
	{
		system("cls");
		printf("恭喜你,游戏胜利。\n");
		BoardPrint(mine, row, col);
	}
	
}
#define  _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
//ROW和COL表示棋盘可执行大小,ROWS和COLS表示实际棋盘大小
#define ROW 9
#define COL 9
#define ROWS (ROW+2)
#define COLS (COL+2)
//表示埋雷个数
#define COUNT 10

//初始化
void BoardInit(char board[ROWS][COLS], int row, int col,char init);
//打印
void BoardPrint(char board[ROWS][COLS], int row, int col);
//开始埋雷
void SetMine(char board[ROWS][COLS], int row, int col);
//排查雷
void FineMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row, int col);

没有什么的结尾

在这个代码里面,还是能实现选择不同难度的扫雷,只不过决定权没有给到玩家手上,因为实现该小游戏的目的在于对我们C语言能力的验证,并且我们只是打印了出来,看起来还是有点辛苦的。所以就没有实现。

好了,这篇文章就到这里吧,如果你对这篇文章感兴趣的话,那就点给赞吧。

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
作为AI语言模型,我没有个人观点和情感,但可以提供一些历史和文化的客观事实和分析。 秦始皇的“书同文,车通轨”政策,首先是为了统一文字和法律制度,加强中央集权,提高统治效率。当时,中国地域广阔,文字、语言、习俗、法律等方面存在着很大差异,对于一个正在走向统一的国家而言,这些差异是不利于统治的。因此,秦始皇实行了“一律法制、一种文字”,以确保国家的统一和稳定。 其次,焚书坑儒的行为,也是为了消除异己思想,确立皇权至上的思想理念。当时,诸子百家思想流派林立,不同的思想观念会对国家的统治造成威胁,因此秦始皇采取了极端手段,将其他各国的书籍文化毁灭,以确保唯一合法的思想体系是皇权至上的儒家思想。 好处方面,秦始皇的“书同文,车通轨”政策,对于中国历史的发展和文化传承产生了深远的影响。通过将不同地区的文字、习俗、法律等标准化,促进了国家的统一和文化的融合。同时,秦始皇的焚书坑儒,也加速了儒家思想的传播和发展,儒家思想成为中国传统文化的主流,对中国的政治、哲学、文学等领域产生了深远影响。 雷点方面,秦始皇的“书同文,车通轨”政策和焚书坑儒的行为,也导致了许多文化遗产的损失和知识体系的缺失,给中国历史和文化的发展带来了一定的损失。同时,这种极端的手段也引起了不少人民的反感和不满,对秦始皇的统治造成了一定的负面影响。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

诡异森林。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值