C语言:扫雷(递归+清屏)详细讲解

news2024/11/18 3:33:11

目录

一.前言

二.功能功能实现

 1.游戏菜单/雷盘定义

雷盘定义: 

2.定义布局(数组)/初始化雷盘

数组:

 初始化雷盘:

3.打印棋盘

4.布置雷(利用随机数)

5.排查雷(判断周围雷)

1.判断周围雷数:

2.递归排查(调用在排查雷当中)

3.清屏

 4.判断输赢:

三.成品

1.game.h(函数声明)

2.game.c(游戏的实现)

3.test.c(游戏测试)


一.前言

 如图,本文将对扫雷小游戏进行模拟实现

以9x9为例(在本文中用标识符常量的方式进行定义,便于更改)

二.功能功能实现

 1.游戏菜单/雷盘定义

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

雷盘定义: 

//玩家看到的大小
#define ROW 9
#define COL 9
//实际大小-> 防止实际计算时越界访问
#define ROWS ROW+2
#define COLS COL+2
//雷数
#define EAZY_COUNT 10;

2.定义布局(数组)/初始化雷盘

数组:

	char mine[ROWS][COLS] = { 0 };
	char show[ROWS][COLS] = { 0 };

 初始化雷盘:

//test.c:
//初始化数组
	InitBoard(mine, ROWS, COLS, '0');//棋盘初始化
	InitBoard(show, ROWS, COLS, '*');//玩家视角,对分布进行隐藏
//访问两次该自定义函数
//game.h:
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i, j;
	for (i = 0;i < rows;i++)
	{
		for( j = 0;j < cols;j++)
		{
			board[i][j] = set;
		}
	}
}
// char set 是为了方便两次对初始化不同内容 
//当然也可以使用memset函数进行初始化,如下:
//                 *(board)[COLS]
void InitBoard(char board[][COLS], int rows, int cols, char set)
{
    int i, j;
	for (i = 0;i < rows;i++)
	{
		for( j = 0;j < cols;j++)
		{
			memset(board[i],'0',sizeof(arr[i]));
            //printf("%c "board[i][j]); 遍历数组查看是否完全初始化
		}
    //printf("\n");
	}
}

3.打印棋盘

//game.h
void DisplayBoard(char board[ROWS][COLS],int row,int col);
//game.c
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int j, i;
	printf("------扫雷-------\n");
	for (i = 0;i <= col;i++)
	{
		printf("%d ",i);
	}
	printf("\n");
	for (i = 1;i <= row;i++)
	{
		printf("%d ", i);
		for (j = 1;j <= col;j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("------扫雷-------\n");
}

4.布置雷(利用随机数)

//game.h
void SetMine(char mine[ROWS][COLS], int row, int col);

 利用随机数生成雷

time头文件:<time.h>   srand头文件:<stdlib.h>

test.c:

srand((unsigned int)time(NULL)); 

//game.c
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = EAZY_COUNT;
	while (count)
	{
        //利用随机数定位xy轴坐标
		int x = rand() % row + 1;
		int y = rand() % col + 1;
        //进行赋值,1代表为雷
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}

5.排查雷(判断周围雷)

1.判断周围雷数:

//game.c
//判断周围一圈是否有雷
int GetmyCount(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';
}

2.递归排查(调用在排查雷当中)

递归用于清除排雷处附近无雷区

//game.h
void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* win);

//game.c
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 = GetmyCount(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)++;
	}

3.清屏

清屏是为了下一步下棋,打印区域过于冗余

 4.判断输赢:

 结合清屏,并调用递归排查

//game.h
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

//game.c
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0, y = 0,z=0;
	int win = 0;;// 用于判断是否所有的雷已经排尽
	int s = EAZY_COUNT;//雷数
	while (win < (row * col) - s)
	{
		system("cls");
		if (z == 0) 
		{
			printf("游戏开始\n");
		}
		if (z > 0)
		{
			printf("\n");
		}
		DisplayBoard(show, ROW, COL);
		printf("请输入要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y>=1 && y <= col)
		{
			if (mine[x][y] == '1')
			{

				system("cls");
				printf("很遗憾,被炸死了!!1\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				expand(mine, show, x, y, &win);

				//统计周围雷数
				int c = GetmyCount(mine, x, y);
				show[x][y]=c + '0';// 字符0ascll为48 1为49 加字符即可打印出周围集合的相关的字符
				//每排查一次打印一次棋盘
				DisplayBoard(mine, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法请重新输入\n");
		}
		z++;
	}
	if (win == (row * col) - s)
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);

	}
}

三.成品

1.game.h(函数声明)

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

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EAZY_COUNT 10;//雷的数量

//初始化
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//打印
void DisplayBoard(char board[ROWS][COLS],int row,int col);
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col);
//排查雷
void FindMine(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);

2.game.c(游戏的实现)

#include "game.h"

void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
	int i, j;
	for (i = 0;i < rows;i++)
	{
		for( j = 0;j < cols;j++)
		{
			board[i][j] = set;
		}
	}
}
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
	int j, i;
	printf("------扫雷-------\n");
	for (i = 0;i <= col;i++)
	{
		printf("%d ",i);
	}
	printf("\n");
	for (i = 1;i <= row;i++)
	{
		printf("%d ", i);
		for (j = 1;j <= col;j++)
		{
			printf("%c ", board[i][j]);
		}
		printf("\n");
	}
	printf("------扫雷-------\n");
}
void SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count = EAZY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count--;
		}
	}
}
int GetmyCount(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 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 = GetmyCount(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 FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0, y = 0,z=0;
	int win = 0;
	int s = EAZY_COUNT;
	while (win < (row * col) - s)
	{
		system("cls");
		if (z == 0) 
		{
			printf("游戏开始\n");
		}
		if (z > 0)
		{
			printf("\n");
		}
		DisplayBoard(show, ROW, COL);
		printf("请输入要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y>=1 && y <= col)
		{
			if (mine[x][y] == '1')
			{

				system("cls");
				printf("很遗憾,被炸死了!!1\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				expand(mine, show, x, y, &win);

				//统计周围雷数
				int c = GetmyCount(mine, x, y);
				show[x][y]=c + '0';// 字符0ascll为48 1为49 加字符即可打印出周围集合的相关的字符
				//每排查一次打印一次棋盘
				DisplayBoard(mine, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法请重新输入\n");
		}
		z++;
	}
	if (win == (row * col) - s)
	{
		printf("恭喜你,排雷成功\n");
		DisplayBoard(mine, ROW, COL);

	}
}

3.test.c(游戏测试)

#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 };
	//初始化数组
	InitBoard(mine, ROWS, COLS, '0');
	InitBoard(show, ROWS, COLS, '*');
	//打印棋盘
	DisplayBoard(show, ROW, COL);
	//布置雷
	SetMine(mine, ROW, COL);	 
	//DisplayBoard(mine, ROW, COL);
	//排查雷
	FindMine(mine, show,ROW,COL);
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择\n");
			break;
		}
	} while (input);

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/814044.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

卡特兰数 公式及其应用

卡特兰数可用于两种场景&#xff08;编程&#xff09; n个元素入栈&#xff0c;共有几种出栈方法n个不同的元素可以组成多少种不同形态的二叉树 卡特兰数的公式是 比如说&#xff1a; 5个元素入栈 那么一共有 种出栈方法 再比如说&#xff08;干咳一声&#xff09; 有4个不…

【雕爷学编程】MicroPython动手做(20)——掌控板之三轴加速度3

知识点&#xff1a;什么是掌控板&#xff1f; 掌控板是一块普及STEAM创客教育、人工智能教育、机器人编程教育的开源智能硬件。它集成ESP-32高性能双核芯片&#xff0c;支持WiFi和蓝牙双模通信&#xff0c;可作为物联网节点&#xff0c;实现物联网应用。同时掌控板上集成了OLED…

读发布!设计与部署稳定的分布式系统(第2版)笔记27_安全性下

1. 安全配置出现失误 1.1. 攻击者已经通过使用开箱默认的admin登录名和密码&#xff0c;进入了不少应用程序、网络设备和数据库 1.2. 出现配置的遗漏 1.2.1. 服务器默认启用不需要的特性 1.2.1.1. 我们忘记&#xff08;或不知道&#xff09;禁用它们&#xff0c;从而开放了…

C++ 多线程编程导论(下)

文章目录 参考资料线程安全&#xff08;续&#xff09;门闩与屏障——latch 对象与 barrier 对象门闩&#xff08;latch&#xff09;屏障&#xff08;barrier&#xff09; 一次性调用——once_flag 对象与 call_once 函数 异步任务未来与承诺——future 对象与 promise 对象fut…

Ubuntu网络设置之固定IP详解

尊敬的家人们&#xff0c;欢迎观看我的文章&#xff01;今天&#xff0c;我们将为您介绍Ubuntu22.04操作系统中固定IP的设置方法&#xff0c;帮助您更好地管理网络连接并提高网络稳定性。 什么是固定IP&#xff1f; 在网络中&#xff0c;IP地址是设备在网络上的唯一标识。通常…

用html+javascript打造公文一键排版系统9:主送机关排版

一、主送机关的规定 公文一般在标题和正文之间还有主送机关&#xff0c;相关规定为&#xff1a; 主送机关 编排于标题下空一行位置&#xff0c;居左顶格&#xff0c;回行时仍顶格&#xff0c;最后一个机关名称后标全角冒号。如主送机关名称过多导致公文首页不能显示正文时&…

哨兵模式原理

哨兵模式原理 一、定义二、作用三、故障转移机制主节点的选举: 哨兵的模式一、哨兵对主从复制集群进行监控二、哨兵与哨兵之间互相进行监控三、监控的目的 故障切换的原理?cluster模式cluster模式同步两种方式 一、定义 哨兵(sentinel):是一个分布式系统&#xff0c;用于对主…

pytest 入门

1,安装pytest 打开终端或命令提示符窗口,在终端中运行以下命令来安装pytest: pip install pytestpip install -i https://pypi.tuna.tsinghua.edu.cn/simple pytest 确保您的系统上已经安装了Python。您可以在终端中运行以下命令来检查Python的安装情况: pytest --version…

MATLAB | 如何绘制这样的描边散点图?

part.-1 前前言 最近略忙可能更新的内容会比较简单&#xff0c;见谅哇&#xff0c;今日更新内容&#xff1a; part.0 前言 看到gzhBYtools科研笔记(推荐大家可以去瞅瞅&#xff0c;有很多有意思的图形的R语言复现&#xff01;&#xff01;)做了这样一张图&#xff1a; 感觉很…

RK3588平台开发系列讲解(LCD篇)FrameBuffer 操作步骤

文章目录 一、FrameBuffer 介绍二、屏幕参数信息的获取三、刷新 FrameBuffer四、FrameBuffer 例程沉淀、分享、成长,让自己和他人都能有所收获!😄 📢在应用程序中,操作/dev/fbX 的一般步骤进行介绍。 打开 FrameBuffer 设备;获取 FrameBuffer 设备的固定信息和可变信息;…

生成对抗网络DCGAN学习实践

在AI内容生成领域&#xff0c;有三种常见的AI模型技术&#xff1a;GAN、VAE、Diffusion。其中&#xff0c;Diffusion是较新的技术&#xff0c;相关资料较为稀缺。VAE通常更多用于压缩任务&#xff0c;而GAN由于其问世较早&#xff0c;相关的开源项目和科普文章也更加全面&#…

809协议服务端程序解码程序

809协议服务端程序解码程序 目录概述需求&#xff1a; 设计思路实现思路分析1.服务端2.code: 拓展实现性能参数测试&#xff1a;1.功能测试 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip…

【LeetCode】探索杨辉三角模型

一、题目描述 力扣原题 首先我们要来了解一下题目本身在说些什么&#xff0c;通过下方的动图我们可以更加清楚地看到杨辉三角是怎样一步步生成的。给到的示例中我们通过输入杨辉三角的行数&#xff0c;然后通过计算得到这个杨辉三角的每一行是什么具体的数值 二、模型选择 首先…

大数据技术之ClickHouse---入门篇---介绍

星光下的赶路人star的个人主页 一棵树长到它想长到的高度之后&#xff0c;它才知道怎样的空气适合它 文章目录 1、Clickhouse入门1.1 什么是Clickhouse1.1.1 Clickhouse的特点1.1.1.1 列示储存1.1.1.2 DBMS的功能1.1.1.3 多样化引擎1.1.1.4 高吞吐写入能力1.1.1.5 数据分区与线…

JAVA SE -- 第十三天

&#xff08;全部来自“韩顺平教育”&#xff09; 集合 一、集合框架体系 集合主要是两组&#xff08;单列集合、双列集合&#xff09; Collection接口有两个重要的子接口List 、Set&#xff0c;它们的实现子类都是单列集合 Map接口的实现子类是双列集合&#xff0c;存放的…

进阶C语言——再识结构体

1 结构的基础知识 结构是一些值的集合&#xff0c;这些值称为成员变量。结构的每个成员可以是不同类型的变量。 2 结构的声明 struct tag {member-list; }variable-list;如果下面来描述一个学生的话&#xff0c;我们会想到学生的姓名&#xff0c;成绩&#xff0c;性别等&#…

【序列化工具JdkSerialize和Protostuff】

序列化工具对比 JdkSerialize&#xff1a;java内置的序列化能将实现了Serilazable接口的对象进行序列化和反序列化&#xff0c; ObjectOutputStream的writeObject()方法可序列化对象生成字节数组 Protostuff&#xff1a;google开源的protostuff采用更为紧凑的二进制数组&#…

1-linux下mysql8.0.33安装

在互联网企业的日常工作/运维中&#xff0c;我们会经常用到mysql数据库&#xff0c;而linux下mysql的安装方式有三种&#xff1a; 1.mysql rpm安装 2.mysql二进制安装 3.mysql源码安装 今天就为大家讲讲linux下mysql8.0.33版本rpm方式的安装。 1.前提 1.1.系统版本 Cent…

目标识别数据集互相转换——xml、txt、json数据格式互转

VOC数据格式与YOLO数据格式互转 1.VOC数据格式 VOC&#xff08;Visual Object Classes&#xff09;是一个常用的计算机视觉数据集&#xff0c;它主要用于对象检测、分类和分割任务。VOC的标注格式&#xff0c;也被许多其他的数据集采用&#xff0c;因此理解这个数据格式是很重…

Pytest+Allure+Excel接口自动化测试框架实战

1. Allure 简介 简介 Allure 框架是一个灵活的、轻量级的、支持多语言的测试报告工具&#xff0c;它不仅以 Web 的方式展示了简介的测试结果&#xff0c;而且允许参与开发过程的每个人可以从日常执行的测试中&#xff0c;最大限度地提取有用信息。 Allure 是由 Java 语言开发…