扫雷(包含排雷函数递归的讲解)

今天晚上总算是把扫雷给写完了

深夜写代码,我也是要羽化成仙了。。。。

后面的一点一大片空白的递归思路我也会讲

总之先看吧

    分三个模块

    test.c      用来存放主函数        并调用自定义函数实现功能

    game.h      用来存放头文件    预处理语句define定义        还有函数声明

   (俗称  include大杂烩)

    game.c        用来实现主函数要调用的自定义函数

那么开始

    test.c

#include"game.h"


void game()
{
	/*游戏函数中实现的函数分4个
	1 构建 mine(布雷棋盘)   show(排雷棋盘)
	2 打印棋盘
	3  布置雷
	4  判断输赢,以及布雷棋盘对附近雷的数目显示*/

	//定义棋盘数组
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	//构建棋盘
	initpoard(mine, ROWS, COLS, '0');
	initpoard(show, ROWS, COLS, '*');

	//打印棋盘
	print_porad(mine, ROWS, COLS);
	printf("\n");
	print_porad(show, ROWS, COLS);
	printf("\n");

	//布雷
	layout_mine(mine, ROWS, COLS);

	//打印棋盘
	print_porad(mine, ROWS, COLS);
	printf("\n");
	print_porad(show, ROWS, COLS);
	printf("\n");

	//进行扫雷,判断输赢
	mine_clear(mine, show, ROW, COL);






}



int main()
{
	srand((unsigned int)time(NULL));
	int nut = 0;
	do
	{
		printf("******************************************\n");
		printf("*******1、开始游戏******0、退出游戏*******\n");
		printf("******************************************\n");

		scanf_s("%d", &nut);
		switch (nut)
		{
		case 1:
			printf("开始游戏\n");
			game();//进入游戏函数
			break;
		case 0:
			break;
		default:
			printf("输入错误,请重新输入\n");
		}






	} while (nut);







	return 0;

}

game.h

#include<stdio.h>
#include <stdlib.h>
#include<time.h>
#define  ROW 15
#define  COL 15
#define  ROWS  ROW+2
#define  COLS  COL+2
#define   DIFFICULTY     15
void initpoard(char arr[ROWS][COLS], int rows, int cols, char set);
void print_porad(char arr[ROWS][COLS], int rows, int cols);
void layout_mine(char mine[ROWS][COLS], int rows, int cols);
void mine_clear(char mine[ROWS][COLS], char show[ROWS][COLS], int rows, int cols);

game.c

#include"扫雷2game.h"

void initpoard(char arr[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++)
		{
			arr[i][j] = set;
		}
	}
}



//打印棋盘
void print_porad(char arr[ROWS][COLS], int rows, int cols)
{
	int i = 0; int j = 0;

	for (i = 0; i <=ROW; i++)
	{
		printf("%-2d ", i);
	}
	printf("\n");



	for (i = 1; i <= ROW; i++)
	{
		printf("%-2d ", i);
		for (j = 1; j <= COL; j++)
		{
			printf("%-2c ", arr[i][j]);
		}
		printf("\n");

	}
}




//布雷
void layout_mine(char mine[ROWS][COLS], int rows, int cols)
{
	int i = 0;
	int j = 0;
	int nut = DIFFICULTY;


	while (nut)
	{
		i = rand() % ROW + 1;
		j = rand() % COL + 1;



		if (mine[i][j] == '0')
		{
			mine[i][j] = '1';
			nut--;
		}

	}
}



void explore(char mine[ROWS][COLS], char show[ROWS][COLS], int i, int j)
	{
	if (show[i][j] == ' ' || ROW < i || i < 1 || COL < j || j < 1)
	{
		goto end;
	}
		char s = 0;
		s = mine[i - 1][j - 1] +
			mine[i - 1][j] +
			mine[i - 1][j + 1] +
			mine[i][j - 1] +
			mine[i][j + 1] +
			mine[i + 1][j - 1] +
			mine[i + 1][j] +
			mine[i + 1][j + 1] - 8 * '0';

		show[i][j] = s + '0';

		int p = i - 1;
		int g = j - 1;
		int maxi = i + 1;
		int maxj = j + 1;
		if (s + '0' == '0')
		{
			show[i][j] = ' ';

			for (p = i - 1; p <= maxi; p++)
			{
				for (g = j - 1; g <= maxj; g++)
				{
					explore(mine, show, p, g);
				}
			}
		}
		//进行函数的递归   对周围没有雷的空地蔓延
	end:
		;	
	}



//进行扫雷,判断输赢
	void mine_clear(char mine[ROWS][COLS], char show[ROWS][COLS], int rows, int cols)
		 {
		

		int i = 0; int j = 0; int cont = 0;
		while (ROW * COL - DIFFICULTY > cont)
		{
			scanf_s("%d%d", &i, &j);

			if (1 <= i && i <= ROW && 1 <= j && j <= COL)
			{
				if (mine[i][j] == '1')
				{
					printf("很遗憾,你被炸死了\n");
					break;
				}
				else
				{
					explore(mine, show, i, j);//探查当前位置周围还有没有雷,有几个
					cont++;
				}

				//打印棋盘
				print_porad(mine, ROWS, COLS);
				printf("\n");
				print_porad(show, ROWS, COLS);
				printf("\n");

			}
			else
				printf("输入错误,请重新输入\n");
		}

		if (ROW * COL - DIFFICULTY == cont)
			printf("你赢了\n");

	}

这是完整的代码

前面的就不详细讲了,因为现在已经很晚了。。。

来讲排雷函数吧

void explore(char mine[ROWS][COLS], char show[ROWS][COLS], int i, int j)
	{
	if (show[i][j] == ' ' || ROW < i || i < 1 || COL < j || j < 1)
	{
		goto end;
	}
		char s = 0;
		s = mine[i - 1][j - 1] +
			mine[i - 1][j] +
			mine[i - 1][j + 1] +
			mine[i][j - 1] +
			mine[i][j + 1] +
			mine[i + 1][j - 1] +
			mine[i + 1][j] +
			mine[i + 1][j + 1] - 8 * '0';

		show[i][j] = s + '0';

		int p = i - 1;
		int g = j - 1;
		int maxi = i + 1;
		int maxj = j + 1;
		if (s + '0' == '0')
		{
			show[i][j] = ' ';

			for (p = i - 1; p <= maxi; p++)
			{
				for (g = j - 1; g <= maxj; g++)
				{
					explore(mine, show, p, g);
				}
			}
		}
		//进行函数的递归   对周围没有雷的空地蔓延
	end:
		;	
	}
[i - 1][j - 1]   [i - 1][j + 1]    [i - 1][j] 

[i][j - 1]       [i][j]            [i][j + 1]           

[i + 1][j - 1]   [i + 1][j]        [i + 1][j + 1]			
			
			
			
			
		

当mine布置雷数组[ i ][ j ]  所在的位置    数值为‘0’的时候

说明周围的八个 位置肯定是没用雷的   也就是没用‘1’

我们的目的:由递归完成对周围的扫荡       如果扫荡的坐标周围的      八个位置中有雷,则停止继续递归,留下当前位置对检测到的地雷数量

讲的有点抽象

我们画图来看看

 

 

 

 ———>(   )标示进入explore函数传参     

     [i-1][j-1]在传参之后    代替之前的  i    j     成为传参后分支函数中的  i   与    j  再次进行一次如上所示的传参    也就是递归

    检查周围是否有雷(字符‘1’)

 运用算式

 

 s 是char类型       mine数组也是char类型           对char类型的变量而言     进行操作符的运算时,char类型变量都会以ascii码值进行运算   和储存,打印则是以ascii码值对应的字符而打印

所以如果周围没有雷的话,此时的show【i】【j】就会等于‘0’字符零;有的话就会显示相对应个数的字符数字

当然如果递归中     再这个算式里算得不是字符‘0’    递归就会停止(不再继续传参下去了)

函数就会返回上一层,进去另外的分支函数递归操作

递归的判断条件1             不是‘0’不给进

 但是又有一个问题

 当[i-1][j-1]这层再次进行递归时,原本的i  和j  所在的位置又要进行一次传参(图案如上所示)

这样的话不就重复了吗

那么就将已经传参过的mine[i][j]赋值为‘    ’(空格符)

当有一次重复传参到用过的  i   与   j

判断   mine[ i ][ j ]=‘    ’   直接跳出函数结束,不传参了,不就行了

 用goto语句到到函数末尾(当然在这里也不能让它越界了,所以要加上防止越界条件)

再运用||操作符,只要满足其中一个条件,直接pass

像不像生活中那些筛人的场景

只要有任何一方面不达标直接pass

不说了。。。都是泪

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值