C语言扫雷(递归展开)(详细注释)(完整代码)

目录

一、功能实现

1.雷盘初始化与打印

雷盘定义

初始化雷盘

打印雷盘

随机布置雷

2.玩家排查雷

获取坐标周围雷数

递归展开

胜负判断

显示雷位置

二、游戏试玩

三、游戏完整代码

game.h 

test.c

game.c


      扫雷大家应该都知道,翻开一个格子,显示的数字就是周围 8 格所含的雷数。例如,红色框框里的1周围8格就只有一个雷。

      我们定义两个数组来实现,show数组存放玩家看到的棋盘,mine数组存放隐藏的雷盘

这两个数组搭配使用,就能计算某个位置周围的雷数,并且修改show数组来显示这个位置的雷数。

但是如果计算边缘格子周围的雷数时,数组会越界

我们只要在周围留一圈就能解决这个问题

如下图所示,假设我们要玩 9x9 的大小,我们的数组大小就定义为 11x11

一、功能实现

1.雷盘初始化与打印

  • 雷盘定义

  • 初始化雷盘

初始化show,mine两个数组,show 存放 '*' , mine存放 '0'

void init_board(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;
		}
	}
}
  • 打印雷盘

//打印show雷盘

void display_board(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;

	//这里打印上面一行数字
	printf("|");
	for (j = 0; j <= col; j++)
	{
		printf(" %-2d |", j);
	}
	printf("\n");


	for (i = 1; i <= row; i++)
	{
		printf("|");
		for (j = 0; j <= col; j++)
		{
			printf("----|");//打印两行之间的分割线
		}
		printf("\n");

		printf("|");
		printf(" %-2d |", i);//打印左边一列数字

		for (j = 1; j <= col; j++)
		{
			printf(" %2c |",board[i][j]);//打印show数组
	
		}
		printf("\n");
	}
}

 效果图:

  • 随机布置雷

随机布置雷,将mine中的 '0' 改为 '1'

//随机布置雷,在mine数组里随机设置COUNT个雷

void set_mine(char mine[ROWS][COLS], int row, int col)
{
	int count = COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;

		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';  //地雷设置为 '1'
			count--;
		}
	}
}

2.玩家排查雷

  • 获取坐标周围雷数

计算该坐标在mine中 周围 '1'的个数

//获取一个格子周围的雷数

int get_mine_count(char mine[ROWS][COLS], int x, int y)  
{
	//mine 中存放的是字符'0' 和 '1'
		return (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] +
			mine[x + 1][y] +
			mine[x + 1][y + 1] - 8 * '0');
}
  • 递归展开

我们玩扫雷时,翻开一个格子会展开一片,如上图所示,翻开黑格子,展开紫色区域

我们可以用递归来实现

当这个格子周围没雷时,显示空白,然后继续递归它周围的八个格子

有雷时,显示雷数,停止递归。

//递归展开

void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* win)
{

	if (x >= 1 && x <= ROW && y >= 1 && y <= COL) //限制在棋盘内展开,防止越界
	{
		int count = get_mine_count(mine, x, y);//获取雷数

		if (count == 0) //四周没雷,进入递归展开
		{
			show[x][y] = ' ';//四周没雷的改为 空格  ' '
			

			int i = 0;
			//向四周共8个位置递归
			for (i = x - 1; i <= x + 1; i++)
			{
				int j = 0;
				for (j = y - 1; j <= y + 1; j++)
				{

					//只对 '*' 进行展开,防止死循环
					if (show[i][j] == '*')
					{
						expand(mine, show, i, j, win);
					}

				}
			}
		}
		else   //四周有雷显示雷数
		{
			show[x][y] = count + '0';
		}
	
		//记录展开的数量
		(*win)++;
	}
}
  • 胜负判断

这里定义了一个 win 来表示翻开的格子数,当翻开的格子数量 = 行 x 列 - 雷数 ===> 排雷成功。

//玩家排查雷

void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;//初始化 翻开的格子数量

	while (win < row * col - COUNT)//当翻开的格子数量 = 行 x 列 - 雷数 ===> 排雷成功
	{
		printf("请输入要排查的坐标:>");
		scanf("%d %d", &x, &y);

		//检查坐标是否越界
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{

			//检查坐标是否排查过了
			if (show[x][y] == '*')
			{

				if (mine[x][y] == '1')
				{
					system("cls");
					show_mine(mine, show, row, col);
					printf("-----------很遗憾,你被炸死了-----------\n");
					break;
				}
				else
				{
					//展开
					expand(mine, show, x, y, &win);

					system("cls");//清屏
					display_board(show, row, col);
					printf("--------------还需翻开%d格--------------\n", row * col - COUNT - win);
				}
			}
			else
			{
				printf("该坐标已排查,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}

    if (win == row* col - COUNT)
	{
		system("cls");
	    show_mine(mine, show, row, col);//展示地雷位置
	    printf("------------恭喜你,排雷成功-----------\n");
	}	
}
  • 显示雷位置

//显示地雷位置,排雷成功或被炸死后 向玩家展示地雷位置

void show_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int i = 0;
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		for (j = 1; j <= COL; j++)
		{
			if (mine[i][j] == '1')
			{
				show[i][j] = '@';    //将地雷改成 '@'
			}
		}
	}
	display_board(show, row, col);   //打印
}

二、游戏试玩

游戏设置    10 行 ,10 列  ,15个雷

 排雷成功

 排雷失败

三、游戏完整代码

game.h 

头文件   常量定义,函数声明

#pragma once

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


//玩家看到的大小
#define ROW 10
#define COL 10


//实际数组大小,防止越界
#define ROWS ROW+2
#define COLS COL+2


#define COUNT 10//雷数

//初始化棋盘
void init_board(char board[ROWS][COLS], int rows, int cols, char set);

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

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

//获取坐标周围地雷数
int get_mine_count(char mine[ROWS][COLS], int x, int y);

//显示地雷位置并打印
void show_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

//递归展开
void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* win);

//排查雷
void find_mine(char mine[ROWS][COLS],char show[ROWS][COLS],int row, int col);

test.c

游戏测试文件

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"


//菜单
void menu()
{
	printf("======================\n");
	printf("||       扫雷       ||\n");
	printf("||    1-开始游戏    ||\n");
	printf("||    0-退出游戏    ||\n");
	printf("======================\n");
}


//游戏流程
void game()
{
	char mine[ROWS][COLS] = { 0 };//存放布置的雷(隐藏的)
	char show[ROWS][COLS] = { 0 };//存放排查的雷(游戏看到的)
	

	//初始化棋盘
	//mine 全为'0'
	//show 全为'*'
	init_board(mine, ROWS, COLS, '0');
	init_board(show, ROWS, COLS, '*');

	//随机布置雷
	set_mine(mine,ROW,COL);

	//打印棋盘
	//display_board(mine, ROW, COL);
	display_board(show, ROW, COL);
	printf("--------------需要翻开%d格--------------\n", ROW * COL - COUNT);

	//排查雷(游戏开始)
	find_mine(mine, show, ROW, COL);
}




int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));

	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			system("cls");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新输入\n");
			break;
		}
	} while (input);

	return 0;
}

game.c

函数定义

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"


//将两个数组初始化,show全为'*' ,mine全为'0'

void init_board(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;
		}
	}
}


//打印show棋盘

void display_board(char board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;

	//这里打印上面一行数字
	printf("|");
	for (j = 0; j <= col; j++)
	{
		printf(" %-2d |", j);
	}
	printf("\n");


	for (i = 1; i <= row; i++)
	{
		printf("|");
		for (j = 0; j <= col; j++)
		{
			printf("----|");//打印两行之间的分割线
		}
		printf("\n");

		printf("|");
		printf(" %-2d |", i);//打印左边一列数字

		for (j = 1; j <= col; j++)
		{
			printf(" %2c |",board[i][j]);//打印show数组
	
		}
		printf("\n");
	}
}


//随机设置雷,在mine数组里随机设置COUNT个雷

void set_mine(char mine[ROWS][COLS], int row, int col)
{
	int count = COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;

		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';  //地雷设置为 '1'
			count--;
		}
	}
}


//获取一个格子周围的雷数

int get_mine_count(char mine[ROWS][COLS], int x, int y)  
{
	//mine 中存放的是字符'0' 和 '1'
		return (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] +
			mine[x + 1][y] +
			mine[x + 1][y + 1] - 8 * '0');
}


//递归展开

void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* win)
{

	if (x >= 1 && x <= ROW && y >= 1 && y <= COL) //限制在棋盘内展开,防止越界
	{
		int count = get_mine_count(mine, x, y);//获取雷数

		if (count == 0) //四周没雷,进入递归展开
		{
			show[x][y] = ' ';//四周没雷的改为 空格  ' '
			

			int i = 0;
			//向四周共8个位置递归调用
			for (i = x - 1; i <= x + 1; i++)
			{
				int j = 0;
				for (j = y - 1; j <= y + 1; j++)
				{

					//只对 '*' 进行展开,防止死循环
					if (show[i][j] == '*')
					{
						expand(mine, show, i, j, win);
					}

				}
			}
		}
		else   //四周有雷显示雷数
		{
			show[x][y] = count + '0';
		}
		
		//记录展开的数量
		(*win)++;
	}
}


//显示地雷位置,排雷成功或被炸死后 向玩家展示地雷位置

void show_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int i = 0;
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		for (j = 1; j <= COL; j++)
		{
			if (mine[i][j] == '1')
			{
				show[i][j] = '@';    //将地雷改成 '@'
			}
		}
	}
	display_board(show, row, col);   //打印
}


//玩家排查雷

void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;//初始化 翻开的格子数量

	while (win < row * col - COUNT)//当翻开的格子数量 = 行 x 列 - 雷数 ===> 排雷成功
	{
		printf("请输入要排查的坐标:>");
		scanf("%d %d", &x, &y);

		//检查坐标是否越界
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{

			//检查坐标是否排查过了
			if (show[x][y] == '*')
			{

				if (mine[x][y] == '1')
				{
					system("cls");
					show_mine(mine, show, row, col);
					printf("-----------很遗憾,你被炸死了-----------\n");
					break;
				}
				else
				{
					//展开
					expand(mine, show, x, y, &win);

					system("cls");//清屏
					display_board(show, row, col);
					printf("--------------还需翻开%d格--------------\n", row * col - COUNT - win);
				}
			}
			else
			{
				printf("该坐标已排查,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
    
    if (win == row* col - COUNT)
	{
		system("cls");
	    show_mine(mine, show, row, col);//展示地雷位置
	    printf("------------恭喜你,排雷成功-----------\n");
	}
}

  • 53
    点赞
  • 142
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值