扫雷-不止于扫雷

博客详细介绍了扫雷游戏的算法实现,包括初始化棋盘、统计相邻雷数和扫雷游戏的核心逻辑。通过扩展棋盘边界简化边界条件判断,并利用ASCII码差值快速计算雷的数量。还探讨了扫雷游戏的初始化和埋雷过程,提供了相应的代码示例。

最近乐学来了个花活,“统计一个格子附近雷的个数”

题目搬一下

每个数字表示了该方格周围到底有几个地雷,当然,一个方格周围最多的时候只会有八个。

输入

输入中将包括一系列的地图,每个地图的第一行有两个整数 n 和 m(0 <n,m <= 100),它们表示了地图的行数和列数。下面的 n 行每行都有 m 个字符,其中 "." 表示安全而 "*" 表示地雷。如果地图的 n 和 m 都为 0,则表示输入结束。

输出

针对每一个地图,首先输出一行:

Field #x:

其中 x 是当前地图的编号(从 1 开始)。下面的 n 行则将地图中的 "." 以数字表示,该数字表示该方格周围有多少颗地雷。

        我之前写过一个扫雷的小游戏,于是几乎回想以前的方法就直接给他扬了, 题目是《扫雷-不止于扫雷》,这个标题也是本文的雷。(废话)

        主要思路:

        要判断一个格子周围是否有雷,如果要讨论边界——四条边加四个角,八个特殊情况,八个if叠加else,复制粘贴也是大工程。我们为何不初始化这个雷区多两行两列,也就是在现有雷区的基础上,边框上围多一圈“安全区”呢?

        这样就不需要讨论了,以4x4的初始棋盘为例,见下草图,周围都是点,排到也是安全的,不影响最终计数。

初始化的方法可以选择最笨重的,暴力就完事了:

        由于最大棋盘是100x100,然后具体大小要根据输入的x和y微调。

    char grid[102][102];
    // 初始化棋盘比原始大一圈 
		for(i=0;i<x+2;i++)   // 初始化第一列为点
		{
			grid[i][0]='.';
			grid[i][y+1]='.';  // 初始化最后一列的下一列为点
			grid[i][y+2]='\0'; // 再下一列为字符串结束标志,严谨就完事了
		}
		for(j=0;j<y+2;j++)
		{
			grid[0][j]='.';   // 初始化第一行为点
			grid[x+1][j]='.'; // 初始化最后一行的下一行为点
		}
		for(i=1;i<x+1;i++)
		{
			char s[100];
			scanf("%s",&s);   // 依次输入棋盘的内容,然后顺序读入
			int t=0;
			for(t=0;t<y;t++)
			{
				grid[i][t+1]=s[t];
			}
		}

然后就到排查雷:

        这道题自始自终都只有两个字符:'.' 和 '*';

        可不可以用一下ascii码呢?少用点判断?——答案是肯定的

        这两个字符的ascii码就差了4,把这个点周围的八个ascii加起来,然后用8个理想的安全字符  '.' 减去这个和,然后除以4,得到的刚好就是 * 的个数。

static int get_mine_count(char mine[102][102],int x,int y)
{
	return (8*'.'-(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]))/4;
}

这道题最后涉及到的顺序打印棋盘,这里就只给出思路:

        (1)能不能创建一个一维整型数组,存储每一次输入的行数?

        (2)能不能复制一个棋盘,存储最终的总的所有情况,假如第一次输入4x4,第二次输入3x3,就像这样(下图),最终打印的时候,按照(1)中存储的行数依次打印棋盘呢?


如果你只追求做完这题,那本文结束了,但是按照本文的题目捏,下面还有”亿“些内容


扫雷教程千千万,扫雷方法一二三,如何根据自己的喜好写出自己想要的扫雷游戏而不是照搬呢?

        刚才这道题已经解决了扫雷当中”扫描“的步骤,再加一步“诊断是否被炸”就算是游戏的主体了。当然,扫雷离不开“初始化”—>“判断、排雷”(已解决)这一循环周期

“初始化”:

        如何设置棋盘的大小服务于后面的游戏主体?

        显然,我们可以设置两个棋盘,一个用来埋雷,一个用来给玩家看,让他们排雷。这两个棋盘大小显然不一样,埋雷的棋盘要比排雷的小一圈

        代码仅供参考,内容大可自定义

#define ROWS 10
#define COLS 10

// 初始化棋盘,行列字符,三者自定义
void InitBoard(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;  // 自定义字符
		}
	}
}

        至于“埋雷”:可以选择用函数生成随机数,并用这个随机数对行列数取模,以次限制,埋雷的范围,当然需要对已有的雷进行表示,防止重复埋雷,导致降低难度。

#define ROWS 10
#define COLS 10
#define Easy_count 10
#define Mid_count 50
#define Hard_count 100

void SetMine(char mine[ROWS][COLS],int row,int col)
{
	int count=Easy_count; // 最终布置雷的个数
	while(count)
	{
		int x=rand()%row+1;    // 控制随机生成的雷的下标
		int y=rand()%col+1;
		if(mine[x][y]=='0')    // 避免重复设置雷 
		{
			mine[x][y]='1';
			count--; 
		}
	} 
}

        浅浅讲了扫雷的主体思路,更详细的跟好的可以搜其他大佬的。

        至此,整个扫雷主体搭建就算完成了,至于输入坐标判断这活,佬们都应该会的,不会就是没好好上课!(不是我讲的不好bushi)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值