【扫雷游戏】简单模拟实现讲解

前言


  • 🚄 输入是学习的本质,输出是学习的手段。
  • 🔖 分享每一次的学习,期待你我都有收获。
  • 🎇 欢迎🔎关注👍点赞⭐️收藏✉评论,共同进步!
  • 🌊 “要足够优秀,才能接住上天给的惊喜和机会”
  • 💬 博主水平有限,如若有误,请多指正,万分感谢!

主要讲一下思想和一些注意点。
扫雷是什么,相信大家没玩过也听过,我就不过多赘述了。
这是一个扫雷界面。
![在这里插入图片描述](https://img-blog.csdnimg.cn/7f7344259c7940fb91aebb0b272ddc12.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Luj5bKaXw==,size_13,color_FFFFFF,t_70,g_se,x_要写一个扫雷游戏需要分为以下步骤

要实现扫雷,需要分为以下步骤:

  • 创造一个这样的棋盘
  • 布置雷
  • 扫雷
☁️1. 创造棋盘,假设我们需要的是9*9的棋盘。

当我们选中一个位置时,如果这个位置不是雷,那么游戏就应该给我们一个反馈,告诉我们周围一圈8个位置有几个雷。
就像这样:在这里插入图片描述

这就表明周围这一圈里有3个雷。如果恰好我们选的位置就是雷,那么我们就被炸死了,游戏就直接结束。


这里就面临一个问题了,如果我选的是中间的位置,那周围有8个位置可以反馈,如果我选的是边缘呢?
在这里插入图片描述
显然这已经超出数组范围,越界了。因此,我们不妨再创造这样一个棋盘时,就将范围扩大些。

比如我们要的是一个9*9的棋盘,那么我们就将范围扩大到11 * 11(上下、左右各增加一行一列。绿色为扩大后的范围),只要不在扩大的那部分中埋雷,那么显示出来的数字也只会是9 * 9 这个区间中雷的个数,如图所示:
(假设 * 表示雷)
在这里插入图片描述

  • 创建数组——大小确定
//用define更方便调整游戏棋盘大小

//实际游戏范围
#define ROW 9  
#define COL 9  


//扩大的范围
#define ROWS 11
#define COLS 11
☁️2.如何设置雷,如何设置反馈?

有些老铁觉得可以在一个棋盘里
用1和0分别表示雷和非雷
就像这样:(1表示雷 ,0表示非雷)
在这里插入图片描述
但是这样做会和反馈周围雷数的1产生二义性,因此不合适。


又有些老铁认为可以用*表示雷,#表示非雷,但是这样不够方便,没有用0和1表示来得方便。为什么不够方便一会儿就知道了,当然如果非要这样实现也是可以的。只是判断的时候会比较复杂一点。

在这里插入图片描述


既然我们既要用1和0表示雷和非雷,又不能与反馈的数字产生二义性,那么我们不妨再开一个等大的棋盘,这样,一个棋盘用来布置雷,但是不展示给玩家,另一个棋盘则作为反馈信息的棋盘,展示给玩家。


一开始我们先把布置雷的棋盘初始化,全置为0(非雷),以后再随机埋雷。
将反馈展示给玩家的棋盘全置为 * ,就是一个没有动过的棋盘

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

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

效果如下(对比):

在这里插入图片描述

在这里插入图片描述
然后是布置雷:

#define Count 10   //布置10个雷

//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = Count;
	while (count)
	{
		int x = rand() % 9 + 1;
		int y = rand() % 9 + 1;
		if (mine[x][y] == '0') //如果这个位置不是雷
		{
			mine[x][y] = '1';
			count--;
		}
		//如果这个位置已经是雷,就不会再重复布置。
	}

}

布置完雷后,我们可以看一下效果:
在这里插入图片描述

  • 布置雷盘和反馈盘

☁️3. 扫雷,何时结束,如何反馈。
int Show(char mine[ROW][COL], int x, int y)
{
	return mine[x - 1][y - 1] +
		   mine[x - 1][y] +
		   mine[x - 1][y + 1] +
		   mine[x + 1][y - 1] +
		   mine[x + 1][y] +
		   mine[x + 1][y + 1] +
		   mine[x][y - 1] +
		   mine[x][y + 1];
}

//扫雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS])
{
	int x, y;
	int endcount = ROW * COL - Count; //非雷的个数,用于标记
	int count = Count;//雷的个数

	//程序结束有两种情况:
	//1.被炸死
	//2.非雷点全走过了,只剩下雷点,排雷成功
	do
	{
		if (endcount == 0) //非雷点为0
		{
			printf("已无雷区\n");
			break;
		}
		printf("请输入坐标:\n");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
		{
			{
				if (mine[x][y] == '1')
				{
					printf("你被炸死了\n");
					//展开整个图
					PrintBoard(mine, ROW, COL);
					break;
				}
				//否则提示该坐标周围有多少雷
				else
				{
					int showcount = Show(mine, x, y);

					show[x][y] = showcount + '0';//数字转字符
					PrintBoard(show, ROW, COL);
				}
			}
		}

		else
		{
			printf("坐标错误,请重新输入");
			continue;
		}
	} while (1);
}

解释一下Show函数:
在ASCII表中,我们可以观察到,
‘0’-‘0’=0 ( 字符0-字符0等于 数字0 )
而其他任意数字字符减去字符0可以得到该数,比如
‘5’-‘0’= 5
在这里插入图片描述
因此 Show函数将周围八个字符(不是0就是1)
全加起来,再减去8个‘0’。就能得到一个数字,
该数字就表明周围有几个1。


这也就解释了为什么说用1和0表示雷和非雷方便。


最后再解释一下这行代码:

show[x][y] = showcount + '0';//数字转字符

因为show数组保存的是字符,无法完全接收这个返回来的整型数字。

字符数 - ‘0’ = 数字 ——> 字符数 = 数字 + ‘0’


完整代码:

game.h

#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>

//实际游戏范围
#define ROW 9  
#define COL 9  


//扩大的范围
#define ROWS 11
#define COLS 11

#define Count 10; //雷的个数


//初始化棋盘
void InitBoard(char board[ROWS][COLS], int row, int col, char set);

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

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

//扫雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS]);

game.c

#define _CRT_SECURE_NO_WARNINGS
#include"game.h"


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



//打印棋盘
void PrintBoard(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 mine[ROWS][COLS], int row, int col)
{
	int count = Count;
	while (count)
	{
		int x = rand() % 9 + 1;
		int y = rand() % 9 + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}

}

int Show(char mine[ROWS][COLS], int x, int y)
{
	return	mine[x - 1][y - 1] +
			mine[x - 1][y] +
			mine[x - 1][y + 1] +
			mine[x + 1][y - 1] +
			mine[x + 1][y] +
			mine[x + 1][y + 1] +
			mine[x][y - 1] +
			mine[x][y + 1] - 8 * '0';
		
}



//扫雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS])
{
	int x, y;
	int endcount = ROW * COL - Count; //非雷的个数,用于标记
	int count = Count;//雷的个数

	//程序结束有两种情况:
	//1.被炸死
	//2.非雷点全走过了,只剩下雷点,排雷成功
	do
	{
		if (endcount == 0) //非雷点为0
		{
			printf("已无雷区\n");
			break;
		}
		printf("请输入坐标:\n");
		scanf("%d%d", &x, &y);
		if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
		{
			{
				if (mine[x][y] == '1')
				{
					printf("你被炸死了\n");
					//展开整个图
					PrintBoard(mine, ROW, COL);
					break;
				}
				//否则提示该坐标周围有多少雷
				else
				{
					endcount--; //没找到一个非雷点,非雷数减一,当非雷数为0 ,说明排雷成功,游戏结束
					int showcount = Show(mine, x, y);

					show[x][y] = showcount + '0';
					PrintBoard(show, ROW, COL);
				}
			}
		}

		else
		{
			printf("坐标错误,请重新输入");
			continue;
		}
	} while (1);
}



test.c

#define _CRT_SECURE_NO_WARNINGS

#include"game.h"
#include<time.h>


void game()
{
	srand((unsigned int)time(NULL));

	char mine[ROWS][COLS]; //雷盘
	char show[ROWS][COLS];//反馈盘

	//初始化棋盘
	InitBoard(mine, ROWS, COLS, '0');  //0非雷 
	InitBoard(show, ROWS, COLS, '*');
	//布置雷
	SetMine(mine, ROW, COL);
	PrintBoard(show, ROW, COL);
	//扫雷
	int x, y;
	FindMine(mine, show);


}




int main()
{
	game();
}

在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

阿波呲der

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

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

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

打赏作者

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

抵扣说明:

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

余额充值