扫雷的解析

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录



提示:以下是本篇文章正文内容,下面案例可供参考

一、扫雷的规则

扫雷就是随机布置雷的所在,然后输入坐标进行查找位置,逐个翻开方块,以最后将所有的雷都找到为结束;

注:如果翻开一格如周围方块内无雷则开始进行爆炸式展开;

二、扫雷的逐步解析

1.初始化棋盘

在所有游戏中,我们都会看见一个菜单,扫雷也不例外。

在C语言中我们可已使用函数来打印菜单;

代码如下(示例):

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

}

就像上述代码一样,我们用menu来打印我们的菜单然后在menu函数的调用完毕后使用循环来进行对menu函数的运用;

代码如下(示例): 

int main() {
    int a = 0;
    menu();//扫雷菜单的构建
    do {
        scanf("%d", &a);
        switch (a) {
        case 1:game(); break;//开始游戏
        case 0:printf("退出游戏"); break;
        default:printf("输入错误"); break;
        }
    } while (a > 1);
}

跟上述代码所述这里用do while循环比其他循环要好一些,其他的也可以. 

其余的我们可以用game函数,同时我们也可以开多个文件,来减少代码的繁杂,同时我们可以开一个头文件将我们需要的头文件包括进去,可以减轻代码量.

#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<time.h>
#include<stdlib.h>

#define COL 9//列
#define ROW 9//行
#define COLS COL+2
#define ROWS ROW+2

只要在其他的文件中我们加上一个include"文件名.c"文件就可以了,同时也可以往头文件加入一些宏定义也可以在其他文件里使用,就像上所述代码一样.

2.扫雷游戏的主体

在扫雷中我们首先初始化数组,各位要两个数组不然一个数组是不够用用的,因为我们要让其中一个作为遮挡雷的,一个显示雷,这样想一下,其实不够的一个数组

代码如下(示例):

char borad[ROWS][COLS];//初始化棋盘
char mine[ROWS][COLS];//表示雷的布局
init_borad(mine,ROW, COL, '0');
init_borad(borad,ROW, COL,'*');

就像上述代码一样我们用一个来初始化设置棋盘最后用*来隐藏雷.

其次这样以后我们初始话我们的棋盘开始布置雷之前.

代码如下:

void init_borad(char arr[ROWS][COLS], int r, int c,char set)
{
	for (int a = 0; a < ROWS; a++) {
		for (int j = 0; j < COLS; j++) {
			arr[a][j] = set;
		}
	}
}//初始化棋盘布局

(1)打印棋盘

这样各位我们的棋盘就已经初始化成了'0'或者'*',各位也可以设置循环打印看一下是否正常初始

代码如下 :

void printf_borad(char arr[ROWS][COLS], int r, int c)
{
	for (int a = 0; a <= r; a++) 
	{
		printf("%2d", a);
	}
	printf("\n");

	for (int a = 1; a <= r; a++) 
	{
		printf("%2d", a);
		for (int d = 1; d <= c; d++)
		{
			printf("%2c", arr[a][d]);
		}

		printf("\n");
	}
	
}//用于打印棋盘

 %2d其实就是空两格的意思,而我们多加的循环是为了让扫雷代码出来后坐标的输入更加方便.

游戏规则规定,若查找的位置没有地雷,显示周围8格中地雷的个数,对于棋盘边缘的格子而言,它的周围不足8个格子,这样会导致写代码很麻烦,而且很容易出错(越界)。因此,我们可以考虑在棋盘周围再加一圈格子,即11*11,但无需在最外圈布置地雷,这样就完美解决了越界问题。

就如通显示如下:

就像这样一样可以让我们更加直观的看到我们要处理的坐标的位置.

(2)接下来就是扫雷的使用程序的设置也就是随机设置雷

我们可以使用rand的函数但但是用rand函数的话是伪随机其实实际内容还是固定在那几个,但加上srand函数就可以实现真正的随机(注释:srand((unsigned int)time(NULL))这个随机函数他是跟据运行程序时间来设置);

而在srand函数中的time函数会返回当前的日历时间,其实返回的是1970年1月1日0时0分0秒到现在程序运行时间之间的差值,单位是秒。time函数返回的这个时间差也被叫做:时间戳。

    time函数的时候需要包含头文件:time.h

代码如下:

void set_borad(char borad[ROWS][COLS], int r, int c) 
{
	srand((unsigned int)time(NULL));
	int count = e;
	while (count) {
		int x = rand() % r + 1;
		int y = rand() % c + 1;
		if (borad[x][y] == '0') {
			borad[x][y] = '1';
			count--;
		}
	}
}//用于随机布置雷

而在其中的rand%r和rand%c其实就是r和c就是上面宏定义中的rol和col就是两个9,这个rand%其实就是显示0-8我们+1就是为了让他成为1-9这样这样我们在布局中就正好和我们的棋盘数相对应了。

上述代码实际上就是为了让雷随机布局在我们的mine的多维数组之中,数组之间是相互连结的并非想变量一样是另外创建的,所以这里的数会实际反映到数组中去。

然后我们来判断周围是否存在雷然后我们根据周围的雷来给我们输入的值表示

代码如下:

int pro_mine(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';

}//用于计算周围是否有雷;

原理其实就像是

 

图画的不好各位见谅。 

下来我们来搜索雷

void Find_mine(char borad[ROWS][COLS], char mine[ROWS][COLS], int r, int c, int rs, int cs) {
	int x, y;
	int d = 0;
	int count = e;
	int win = 0;//胜利条件
	while (win < r * c - count)
	{
		puts("请输入坐标");
		scanf("%d", &x);
		scanf("%d", &y);
		if (x >= 1 && x <= r && y >= 1 && y <= c)
		{
			if (borad[x][y] != ' ') {
				
				int d = pro_mine(mine, x, y);
				mine[x][y] = d + '0';
				printf_borad(borad, r, c);
			}
			win++;
		}

		else {
			printf("坐标错误,请重新输入");
			break;
		}
		if (win == r * c + count) {
			printf("恭喜你,排除了所有的雷");
		}
	}

在上述的函数中我们输入x,y坐标然后社值的while循环其实r*c就是81然后减去我们的雷数

1.这样我们就没有找完会继续下去保证玩家能在雷未找完的情况下,稳定进行游戏,并正常退出游戏。

2.我们通过玩家输入xy坐标并判断其坐标的合理性。

最后扫雷的进阶模式

1。在扫雷中我们常常能看到这样的爆炸展开其中就采取了递归的方式

现在我们用代码来表现这种爆炸方式的展开,各位注意越界和死循环啊

我们用上述的pro_mine函数来对周围进行遍历然后确定周围的雷

代码如下:

void f_mine(char mine[ROWS][COLS], char borad[ROWS][COLS], int r, int c, int x, int y, int *win) {
	
	int p = *win;
	if (x >= 1 && x <= r && y >= 1 && y <= c)
	{
		int count = pro_mine(mine, x, y);
		if (count == 0) {
			borad[x][y] = ' ';
			int i = 0;
			for (i = x - 1; i <= x + 1; i++) {
				int j = 0;
				for (j = y - 1; j <= y + 1; j++)
				{
					if (borad[i][j] == '*') {
						f_mine(mine, borad, r, c, i, j,&p);
						p++;
					}
				}
			}
		}
		else {
			borad[x][y] = count + '0';
		}
	}
}

用win指针是为了确定展开数是多少来防止在Find_mine函数中的win的数不增加从而导致爆展与原先的一个个展开方式相冲突。 第一个if语句是为了确定我们是真的处于正确的坐标之中。

让他第第二个则是因为pro函数最后算下来就是0;如果是零那就证明这附近没有地雷的,我们就进行爆展然后在for循环中的x-1和y-1还有x+1,y+1的for循环是为了在周围的八个数中循环,然后我们设置递归函数。

下面的else就是为了确定我们周围的坐标之间有多的雷然后‘0’递增。

然后正常的扫雷程序还有标志功能这个其实比较简单我就不说了各位直接上吧

void mark_cor(char borad[ROWS][COLS],char mine[ROWS][COLS],int r,int c,int *win)
{
	int x = 0;
	int y = 0;
	while (1) {
		puts("请输入要标记的坐标");
		scanf("%d%d", &x,&y);
		if (x > 0 && x <= r && y > 0 && y <= c) {
			if (borad[x][y] == '*') {
				borad[x][y] = '!';
				if (borad[x][y] == '*' && mine[x][y] == '1') {
					win++;
				}
				break;
			}
			else 
			{
				printf("抱歉该位置已经被标记过了\n");
			}
		}
		else
		{
			printf("输入位置错误\n");
		}
	}
}

总结

提示:这里对文章进行总结:
以上我是我对扫雷的理解和重构,可能不太清晰。实际上可能不太好,各位见谅。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值