C语言项目—扫雷完整代码超详解(含有递归展开)

🍪设计思路

1.提供菜单对于玩家选择
2.游戏可以多次玩
3.不是雷 显示周围雷的个数
4.排雷周围没有雷,可实现展开

在这里插入图片描述

🔔# 程序结构

test.c 测试代码的逻辑
game.c 扫雷游戏的实现
game.c 函数的声明 符号的定义

🍔具体功能实现

🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍

🏀主函数实现

运用do while循环 case语句
用户选择1进入游戏
选择0 退出游戏
这里需要注意case语句结束后,加入转向语句break

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));//生成随机数种子 用于电脑下棋坐标
	do
	{
		menu();
		printf("请选择:>\n");
		scanf("%d", &input);//选择菜单选项
		switch (input)
		{
		case 1:
			game();//三子棋游戏的实现
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误 请重新输入\n");
			break;
		}
	} while (input);
}

🎄打印游戏菜单

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

🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍

💡game函数实现

💡创建扫雷棋盘

为什么要创建两个棋盘呢?
mine棋盘是用来存储雷信息,我们布置雷使用的,
show棋盘是用来展示周围雷个数,我们给玩家展示使用的

	char mine[ROWS][COLS] = { 0 };//mine数组是用来存放布置好的雷的信息
	char show[ROWS][COLS] = { 0 };//show数组是用来存放排查出的雷的信息

注意!这里ROWS COLS 是在头文件宏定义的
当我们扫雷 排查蓝色位置时,排查周围8个坐标已经越界造成非法访问
解决的办法:上下两行多创建一行,左右两端多创建一列,可以完美解决这个问题

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2

在这里插入图片描述

🍪初始化棋盘

用set传参
用‘ 0 ’来初始化mine数组
用’ * ’来初始化show数组 保持神秘感hhhh

//初始化棋盘
void Init_board(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

🍮打印棋盘

我们虽然创建数组用的11 * 11的数组
但是我们使用的是中间9*9的棋盘
外面一圈是为了防止越界而生成的
这里传参row col 和上面初始化数组中传入的行和列rows cols进行了区分
为了方便排雷,我们给予行和列的坐标

棋盘展示:
在这里插入图片描述

//打印行线
void line_board(int row, int col)
{
	int i = 0;
	for (i = 0; i <= row; i++)
	{
		printf("----");
	}
	printf("\n");
}
//打印棋盘
void Print_board(char board[ROWS][COLS], int row, int col)
{
	printf("    ");
	int n = 0;
	for (n = 1; n <= col; n++)
	{
		printf(" %-3d", n);
	}
	printf("\n");
	line_board(row, col);
	int i = 0;
	for (i = 1; i <= row; i++)
	{
		printf("%-3d|", i);
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf(" %c |", board[i][j]);
		}
		printf("\n");
		line_board(row, col);
	}
}

🍚放置雷

注意:rand函数之前要用srand搭配使用
这里放置雷 传入ROW,COL只在9*9格子里放
不要放入ROWS COLS (刚开始学我犯下的低级错误…始终棋盘里不是十个雷)😭

//放置雷
void set_mine(char mine[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int count = Easy_count;
	while (count)
	{
		x = rand()%row+1;//rand()%10的取值范围是0~8  +1之后取值1~9
		y = rand()%col+1;//
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

🍥获取周围雷的数量

如果想知道周围有多少雷 需要遍历周围八个坐标
字符0-字符0 = 0;
字符1-字符0 = 0;
只要把周围八个坐标的数值加起来减掉8 * ‘ 0 ’ 就得到该坐标周围雷的个数

//如果输入的坐标不是雷 获取输入坐标周围雷的数量
static int get_mine_count(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';
}

🍥排查雷

如果排查的周围没有雷
可以使用递归展开区域

如果把所有雷都找到了 排雷成功
9*9棋盘 -十个雷 只要排查71个位置
如果71个不是雷的位置都被排查 则排雷成功
这里创建了一个win变量

//查找雷
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)-Easy_count)
	{
		printf("请输入排查雷的坐标:>\n");

		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾你被炸死了\n");
				Print_board(mine, ROW, COL);//被炸死后打印埋雷的棋盘
				break;
			}
			if (show[x][y] != '*')
			{
				printf("该坐标已经被排查过了 换一个试试\n");
				continue;	
			}
			else
			{
				Unfold(mine, show,row, col, x, y, &win);
				Print_board(show, ROW, COL);
			}
		}
		else
		{
			printf("坐标输入错误 请重新输入\n");
		}

}

🍥递归展开

递归展开几个重要条件:
该坐标不是雷
该坐标周围不是雷 这时候可以展开一片

每发现一个周围没有雷的点,就将其设置成空格,同时给win++
每发现一个周围有雷的点,就调用get_mine_count函数,将结果返回给这个点,同时win++
注意,由于是递归的方式来排查点,一定不要重复对排过的点进行win++
将win通过指针传址的方式与Find_mine函数建立联系

在这里插入图片描述

//递归展开一片区域
void Unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y,int* win)
{
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		//排除已经排查过的坐标 避免重复
		if (show[x][y] == ' ' || show[x][y] != '*')
			return;
		else if (get_mine_count(mine, x, y) != 0)//如果周围有雷
		{
			//需要字符0 但是返回的是数字0,所以需要+' 0 '转换一下
			show[x][y] = get_mine_count(mine, x, y) + '0';
			(*win)++;
			return;
		}
		else//如果周围没雷
		{
			show[x][y] = ' ';//把坐标设置为空格
			(*win)++;//找到一个雷 返回给win 离成功又进一步!
			Unfold(mine, show, row, col, x - 1, y, win);
			Unfold(mine, show, row, col, x + 1, y, win);
			Unfold(mine, show, col, col, x, y - 1, win);
			Unfold(mine, show, row, col, x, y + 1, win);

		}
	}
	return;
}

🍥判断输赢

		//判断输赢
		if (win == (row * col) - Easy_count)
		{
			printf("恭喜你取得胜利!\n");
		}

🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍🎍

🏠完整代码

🏠test.c

#define  _CRT_SECURE_NO_WARNINGS 1


#include "game.h"
//测试扫雷逻辑

void menu()
{
	printf("********************************\n");
	printf("*********** 1 .play ************\n");
	printf("*********** 0 .exit ************\n");
	printf("********************************\n");
}
//扫雷游戏的实现
void game()
{
	//创建存放棋盘变量
	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };
	//初始化棋盘
	Init_board(mine,ROWS,COLS,'0');
	Init_board(show,ROWS,COLS,'*');
	//打印棋盘
	Print_board(show, ROW, COL);
	//存放雷
	set_mine(mine, ROW, COL);
	//排查雷
	Find_mine(mine,show, ROW, COL);

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

🏠game.h

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

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2

#define Easy_count 10

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

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

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

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

//展开一片
void Unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col,int x,int y,int* win);

🏠game.c

#define  _CRT_SECURE_NO_WARNINGS 1

#include "game.h"
//初始化棋盘
void Init_board(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			board[i][j] = set;
		}
	}
}

//打印行线
void line_board(int row, int col)
{
	int i = 0;
	for (i = 0; i <= row; i++)
	{
		printf("----");
	}
	printf("\n");
}
//打印棋盘
void Print_board(char board[ROWS][COLS], int row, int col)
{
	printf("    ");
	int n = 0;
	for (n = 1; n <= col; n++)
	{
		printf(" %-3d", n);
	}
	printf("\n");
	line_board(row, col);
	int i = 0;
	for (i = 1; i <= row; i++)
	{
		printf("%-3d|", i);
		int j = 0;
		for (j = 1; j <= col; j++)
		{
			printf(" %c |", board[i][j]);
		}
		printf("\n");
		line_board(row, col);
	}
}

//放置雷
void set_mine(char mine[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int count = Easy_count;
	while (count)
	{
		x = rand()%row+1;
		y = rand()%col+1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

//获取输入坐标周围雷的数量
static int get_mine_count(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 Unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y, int* win)
{
	if (x >= 1 && x <= row && y >= 1 && y <= col)
	{
		if (show[x][y] == ' ' || show[x][y] != '*')
			return;
		else if (get_mine_count(mine, x, y) != 0)
		{
			show[x][y] = get_mine_count(mine, x, y) + '0';
			(*win)++;
			return;
		}
		else
		{
			show[x][y] = ' ';
			(*win)++;
			Unfold(mine, show, row, col, x - 1, y, win);
			Unfold(mine, show, row, col, x + 1, y, win);
			Unfold(mine, show, col, col, x, y - 1, win);
			Unfold(mine, show, row, col, x, y + 1, win);

		}
	}
	return;
}

//查找雷
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)-Easy_count)
	{
		printf("请输入排查雷的坐标:>\n");

		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("很遗憾你被炸死了\n");
				Print_board(mine, ROW, COL);//被炸死后打印埋雷的棋盘
				break;
			}
			if (show[x][y] != '*')
			{
				printf("该坐标已经被排查过了 换一个试试\n");
				continue;	
			}
			else
			{
				Unfold(mine, show, row, col, x, y, &win);
				Print_board(show, ROW, COL);
			}
		}
		else
		{
			printf("坐标输入错误 请重新输入\n");
		}
		if (win == (row * col) - Easy_count)
		{
			printf("恭喜你取得胜利!\n");
		}
	}
}

这里的扫雷不包含 计时和标记的功能 此后会完善
个人水平不足 如果代码中有错误,可以多多在评论区指出,一定会及时修改!
谢谢大家看到这里 觉得有收获的话可以三连一下 一起加油!

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值