【C语言递归应用】扫雷游戏

      话不多说,先上图:

   

   

 首先,要用到的头文件如下:

# ifndef __SWEEPMINE_H__
# define __SWEEPMINE_H__


# include <stdio.h>
# include <stdlib.h>
# include <time.h>

# define ROW 14  //打印的扫雷界面的行数(可改)
# define COL 15  //打印的扫雷界面的列数(可改)
# define MINECOUNT 20 //想布雷的个数(可改)
# define ROWS (ROW+2)   //为方便打印用户界面,后面定义数组大小时需要用到
# define COLS (COL+2)


//初始化雷阵
void InitMine(char mine[ROWS][COLS], char show[ROWS][COLS]);

//打印扫雷界面
void Display(char show[ROWS][COLS]);

//放置地雷
void SetMine(char mine[ROWS][COLS]);

//扫出坐标周围雷的个数并显示出来
int MineNum(int row, int col);

//利用递归进行展开
void Recursion(int row, int col);

//保证第一次不踩雷
void FirstSafe(char mine[ROWS][COLS], char show[ROWS][COLS]);

//判断未展开的坐标数
int Residue();

# endif // __SWEEPMINE_H__

下面介绍一下游戏的源码,首先是主函数,这部分没有什么可以讲的,相信读者可以根据注释看懂,代码如下:

# pragma warning(disable: 4996)

# include "SweepMine.h"

void Game(); //函数声明

void GameMenu()//打印游戏目录的函数
{
	printf("=================================\n");
	printf("|0.退出游戏|     |1.开始游戏|\n");
	printf("=================================\n");
}

int main()
{
	int i = 0;
	int j = 0;
	int input = 1;

	while (input)
	{
		printf("请选择(0 或者 1):\n");
		GameMenu();
		scanf("%d", &input);

		switch (input)
		{
		case 0:
		{
			printf("退出游戏!\n");
			break;
		}	
		case 1:
		{
			Game();
			break;
		}
		default:
		{
			printf("选择错误,请重新选择:\n");
		}		
		}		
	}
	printf("下次再见~\n");

	return 0;
}

      下面是游戏真正需要用到的函数的部分,首先介绍一下写扫雷游戏代码的过程:

(1)需要有一个打印游戏界面的函数;

(2)需要有一个初始化雷阵的函数,以便玩家结束一场游戏后开始下一场游戏;

(3)当初始化雷阵之后,需要将预先设置的雷数随机的分布在雷阵中;

(4)为增强玩家的游戏体验,写一个函数,使得玩家第一次选择不会踩到地雷;

(5)需要写一个函数统计玩家所指定的坐标周围的雷的个数;

(6)通过一个递归函数实现将雷阵展开,即不是被数字或者雷环绕的坐标都自动展开。在这里,提醒读者一个地方,如果读者是和我的一样通过原来的坐标的基础上从八个方向依次判断递归,需要特别注意:通过传递坐标实现递归调用自身的过程中,需要将原来的坐标排除在外,否则会造成栈溢出,原因是两个坐标间的相互调用递归函数

(7)需要统计一下剩余未展开的坐标数,以此来与所布的雷数对比;

(8)要有一个判断输赢的条件,如果剩余的坐标数与雷数相等,说明玩家排雷成功,否则继续排雷。

以下是代码部分,其中有部分代码是为了简单的界面美化,仅供参考:

#pragma warning(disable: 4996)

# include "game.h"

//将雷阵数组和界面显示数组定义为全局变量可以减少函数调用时参数传递,但编程时需要注意全局变量的特点
char mine[ROWS][COLS] = { 0 }; 
char show[ROWS][COLS] = { 0 };

void Game()
{
	int count = 0;
	int x = 0;
	int y = 0;
	int i = 0;

	printf("    ");
	for (i = 0; i < (COL / 2 - 2); ++i)
		printf("   ");
	printf("扫雷游戏,开始!\n");

	InitMine(mine, show);
	SetMine(mine);
	Display(show);
	//Display(mine); 
	FirstSafe(mine, show);

	while (Residue(show) != MINECOUNT)//当未展开的坐标数等于雷的个数时,跳出循环
	{
		printf("请输入坐标:");
		scanf("%d %d", &x, &y);


		if ((x >= 1 && x <= ROW) && (y >= 1 && y <= COL))// 判断坐标是否合法
		{
			int ret = 0;

			if (mine[x][y] == '1')
			{
				printf("你踩到雷了!\n");
				return 0;
			}
			else
			{
				Recursion(x, y);
				Display(show);

				printf("    ");
				for (i = 0; i < (COL / 2 - 2); ++i)
					printf("***");
				printf("未展开坐标数:%d", Residue());
				for (i = 0; i < (COL / 2 - 1); ++i)
					printf("***");
				printf("\n");
					
			}
		}
		else
		{
			printf("输入坐标有误,重新输入:");
		}
	}

	printf("恭喜你,扫雷成功!\n");
	Display(mine);
}

//初始化雷阵
void InitMine(char mine[ROWS][COLS], char show[ROWS][COLS])
{
	int i = 0;
	int j = 0;

	for (i = 0; i < ROW + 2; ++i)
	{
		for (j = 0; j < COL + 2; ++j)
		{
			show[i][j] = '*';
			mine[i][j] = '0';
		}
	}
}

//输入坐标后打印扫雷界面
void Display(char show[ROWS][COLS])
{
	int i = 0;
	int j = 0;


	printf("     ");
	//打印列标号
	for (i = 1; i < COL + 1; ++i)
	{
		if (i < 10)   //为使扫雷标号对齐,小于10,多打印一个空格
		{	
			printf(" %d ", i);
		}
		else
		{
			printf(" %d", i);
		}
	}
	printf("\n");

	printf("    "); //美化游戏界面
	for (i = 1; i < COL + 1; ++i)
	{
		printf("===");
	}
	printf("==\n");

	//打印行标号 和 扫雷的界面
	for (i = 1; i < ROW + 1; ++i) 
	{
		if (i < 10)
			printf(" %d |", i);
		else
			printf(" %d |", i);
		for (j = 1; j < COL + 1; ++j)
		{
			printf(" %c ", show[i][j]);
			if (j == COL)
			{
				printf("|");
			}
		}
		printf("\n");
	}

	printf("    ");//美化游戏界面
	for (i = 1; i < COL + 1; ++i)
	{
		printf("===");
	}
	printf("==\n");
}

//放置地雷
void SetMine(char mine[ROWS][COLS])
{
	int count = MINECOUNT;
	int x = 0;
	int y = 0;
	srand((unsigned)time(NULL));

	while (count)
	{
		x = rand() % ROW + 1; //与行数取余,使得雷能分布在任意一行
		y = rand() % COL + 1; //与列数取余,使得雷能分布在任意一列

		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;		//每次布雷成功后,需要布置的雷数减一
		}
	}
}

//利用递归进行展开
void Recursion(int row, int col)
{
	int ret = 0;

	ret = MineNum(/*mine,*/ row, col);

	if (ret == 0 && show[row][col] == '*')
	{
		show[row][col] = ' ';
		if ((row - 1) > 0 && col > 0 && (show[row - 1][col] == '*')) //上方
			Recursion(row - 1, col);
		if ((row - 1) > 0 && (col + 1) <= COL && (show[row - 1][col + 1] == '*'))//右上方
			Recursion(row - 1, col + 1);
		if ((row) > 0 && (col + 1) <= COL && (show[row][col + 1] == '*'))//右方
			Recursion(row, col + 1);
		if ((row + 1) <= ROW && (col + 1) <= COL && (show[row + 1][col + 1] == '*'))//右下方
			Recursion(row + 1, col + 1);
		if ((row + 1) <= ROW && col > 0 && (show[row + 1][col] == '*'))//下方
			Recursion( row + 1, col);
		if ((row + 1) <= ROW && (col - 1) > 0 && (show[row + 1][col - 1] == '*'))//左下方
			Recursion(row + 1, col - 1);
		if ((row) > 0 && (col - 1) > 0 && (show[row][col - 1] == '*'))//左方
			Recursion(row, col - 1);
		if ((row - 1) > 0 && (col - 1) > 0 && (show[row - 1][col - 1] == '*'))//左上方
			Recursion(row - 1, col - 1);
	}
	else
		show[row][col] = ret + '0';
}


//扫出坐标周围雷的个数
int MineNum(int row, int col)
{
	return mine[row - 1][col] + mine[row - 1][col + 1]  + mine[row][col + 1]  + mine[row + 1][col + 1] 
		+ mine[row + 1][col]  + mine[row + 1][col - 1]  + mine[row][col - 1]  + mine[row - 1][col - 1] - 8*'0';
}

void FirstSafe(char mine[ROWS][COLS], char show[ROWS][COLS])
{
	int x = 0;
	int y = 0;
	int i = 0;

	printf("请输入坐标:");
	scanf("%d %d", &x, &y);
	
	if ((x >= 1 && x <= ROW) && (y >= 1 && y <= COL))// 判断坐标是否合法
	{

		int ret = 0;
		if (mine[x][y] == '1') //排除第一次踩雷
		{
			mine[x][y] = '0';

			while (1) //重新寻找坐标补一颗雷
			{
				int a = 0;
				int b = 0;

				a = rand() % ROW;
				b = rand() % COL;
				if (mine[a][b] == '0')
				{
					mine[a][b] = '1';
				}
				break;
			}
			Recursion(x, y); //布完雷之后,再统计初始坐标周围的雷数(必须放在布雷后,否则如果雷布在初始坐标周围的8个位置,
								   //会出现统计结果出错)
			Display(show);

			printf("    ");//美化游戏界面
			for (i = 0; i < (COL / 2 - 2); ++i)
				printf("***");
			printf("未展开坐标数:%d", Residue());
			for (i = 0; i < (COL / 2 - 1); ++i)
				printf("***");
			printf("\n");

			return ;
		}
		Recursion(x, y);
		Display(show);

		printf("    ");//美化游戏界面
		for (i = 0; i < (COL / 2 - 2); ++i)
			printf("***");
		printf("未展开坐标数:%d", Residue());
		for (i = 0; i < (COL / 2 - 1); ++i)
			printf("***");
		printf("\n");

	}
}

//判断未展开的坐标数
int Residue()
{
	int count = 0;
	int i = 0;
	int j = 0;

	for (i = 1; i <= ROW; ++i)
	{
		for (j = 1; j <= COL; ++j)
		{
			if (show[i][j] == '*')
				count++;
		}
	}
	return count;
}

      如果读者有什么不清楚的地方,或者本文有错误的地方,欢迎您的评论留言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值