扫雷 | C语言 | 简单易懂 | 扫雷相关知识点总结

news2024/11/19 3:25:41

扫雷思路

相信大家都有玩过扫雷吧!其实在我们学习完C语言中函数数组之后,我们就有能力制作一个简单的扫雷小游戏了。

先考虑扫雷游戏的思路:

扫雷游戏我们需要利用二维数组将其进行初始化以及赋值“雷”,就以9*9个雷盘来分析,我们要制作一个9*9的雷盘,其中放有10个雷,然后显示“加密”之后的雷盘,接着用户输入想要排查雷的坐标,根据这个坐标:

如果这个坐标上是雷,那么游戏失败结束游戏;

如果这个坐标不是雷,那么就显示这个坐标附近的八个坐标有几个雷。

因为在这个坐标不是雷的情况下,我们要向用户显示,周围雷的个数,如果在同一个雷盘的情况下,我们就不得不将这个周围雷的个数重新赋值给那么坐标上,这样做的话会严重影响这个程序的复杂度,并且很容易出错!

所以建议大家在写扫雷的程序中,利用两个二维数组,使其中一个是存放雷,而另一个是为玩家展示并且显示相对应的坐标周围有多少个雷。

在排查雷的过程中,我们要注意的是,排查的是输入坐标的周围八个坐标内一共有几个雷。但是,如果排查的雷是四边的边缘坐标时,访问周围8个坐标,则会产生越界访问。所以为了避免这种情况的发生,我们需要将其行列都应该多设置2个,这样就可以解决越界访问的问题了。

我们来看一下程序: 

我们可以得知以下思路

  • 程序打印菜单
  • 玩家选择玩游戏or退出游戏
  • 雷盘初始化
  • 布置雷
  • 排查雷

我们接下来就根据上面这几个功能进行编写代码。

代码梳理

主函数

我们可以看出在游戏刚刚开始时,也就是程序刚刚开始运行时,他会有一个菜单来显示,询问玩家要进行哪一步程序,那么我们来理一下思路:

如果用户选择“1”,那么用户就开始游戏

如果用户选择“0”,那么就是退出程序

如果用户选择的不是“1”or“0”,那么就是非法输入

而在这一思路中,有三种情况并且这个选择得一直做到输入正确的数字之后才开始游戏,所以我们可以利用do...while循环语句以及switch分支语句来进行编写程序:

int main()
{
	int input = 0;
	menu();
	do
	{
		scanf("%d", &input);
		switch(input)
		{
		case 1:
			//game();
			break;
		case 0:
			printf("退出游戏!\n");
			break;
		default:
			printf("非法输入!请重新输入!\n");
			break;
		}

	} while (input);
	return 0;
}

do...while语句的使用方法:

do
    循环语句;
while(表达式);

switch语句的使用方法:

switch(整型表达式)
{
    语句项;
}

菜单函数

 这个菜单我们可以通过printf函数简单的输出,当然我们为了主函数更加简单,我们可以利用函数(假设这个函数名字是menu),使其主函数更加的便于理解。而我们需要注意的是menu函数只是利用printf函数打印菜单,在这段程序中函数menu并没有返回任何数,也没有使用任何数。其中代码是:

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

在雷盘以及显示盘的初始化过程中,两个数组初始化其实代码都是一样的,所以在这里,我们将放置雷的信息的数组命名为MineBoard,将给玩家展示的数组命名为ShowBoard,而这个初始化的函数命名为InitBoard。

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

这里参数传入了一个set的作用就是,使其初始化的代码一个就可以完成。

InitBoard(MineBoard,ROWS,COLS,'0');
  //雷盘刚刚开始的时候:初始化都为零;最后将雷排为1,将周围八个坐标的值相加,就是雷的个数
InitBoard(ShowBoard,ROWS,COLS,'*');

布置雷

时间戳的使用

#include <time.h>

srand((unsigned)time(NULL));//强制类型转化

int x = 0;
int y = 0;

x = rand() % x索取的范围;
y = rand() % y索取的范围;

让随机布置10个雷,可以利用时间函数时间戳进行强制类型转换。并且一直随机布置雷,直到10个雷都成功布置,所以我们需要一个while(count)循环,并且定义一个常量EASY_COUNT的值为10。

#define EASY_COUNT 10

而在这里我们要注意的有:

  • 数组访问的是11*11的格子,但是在布置雷的过程中,只能是在9*9的范围之内。所以x与y的取值必须是在1~9之间,所以在随机取模之后的结果值再加1。
  • 在布置雷的过程中,必须确保成功布置10个雷,所以要进行判断一下是否这个坐标之前没有被布置过,要确保不能重复布置同一个坐标。
void SetMine(char MineBoard[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (MineBoard[x][y] == '0')
		{
			MineBoard[x][y] = '1';
			count--;
		}
	}
}

排查雷

在排查雷的过程中,有这么几种情况:

  • 输入的坐标是雷,被“炸死”,游戏输了,游戏结束;
  • 输入的坐标不是雷,显示周围八个坐标一共有几个雷;
  • 输入的坐标重复排查,提醒玩家重新输入;
  • 输入的坐标越界,提醒玩家重新输入;
  • 将所有的雷都排查出来,没有被“炸死”,游戏胜利,游戏结束。

在这几种情况之中,可以写出以下代码:

FindMine(char MineBoard[ROWS][COLS], char ShowBoard[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 (ShowBoard[x][y] != '*')
			{
				printf("该坐标已被查询!请重新输入坐标!");
			}
			if (MineBoard[x][y] == '1')
			{
				printf("很遗憾!你被炸死了!\n");
				Show(MineBoard, ROW, COL);
				break;
			}
			else
			{
				int ret = GetMine(MineBoard, x, y);
				ShowBoard[x][y] = ret+'0';
				Show(ShowBoard, ROW, COL);
				win++;
			}		
		}
		else
		{
			printf("非法输入!请重新输入!\n");
		}	
	}
	if (win == ROW * COL - EASY_COUNT)
	{
		printf("恭喜您!排查成功!\n");
		Show(MineBoard, ROW, COL);
	}

}

其实这里最难也是最易懂的就是,在排查的坐标不是雷并且显示周围坐标雷的个数的情况。我们在初始化雷盘的时候,我们将不是雷都设置为字符‘0’,把是雷都设置为字符‘1’,而这里排查雷就会有一个简单的小方法,将周围八个坐标的值都加起来,利用ASCII码值,其中字符‘0’的ASCII码值是0,将刚刚加起来的数减掉8个字符‘0’,就可以得到周围雷的个数了。

int GetMine(char MineBoard[ROWS][COLS], int x, int y)
{
	return MineBoard[x - 1][y - 1]
		+ MineBoard[x - 1][y]
		+ MineBoard[x - 1][y + 1]
		+ MineBoard[x][y - 1]
		+ MineBoard[x][y + 1]
		+ MineBoard[x + 1][y - 1]
		+ MineBoard[x + 1][y]
		+ MineBoard[x + 1][y + 1]
		- 8*'0';
}

完整代码

上面已经游戏所有的思路和代码了,为了方面大家检查复习,我将完整的代码在这附上,以便大家熟悉:

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

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

void InitBoard(char Board[ROWS][COLS], int rows, int cols, char set);
void Show(char Board[ROWS][COLS], int row, int col);
void SetMine(char MineBoard[ROWS][COLS], int row, int col);
FindMine(char MineBoard[ROWS][COLS], char ShowBoard[ROWS][COLS], int row, int col);

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

void Show(char Board[ROWS][COLS], int row, int col)
{
	int i = 0;
	int j = 0;
	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 MineBoard[ROWS][COLS], int row, int col)
{
	int count = EASY_COUNT;
	while (count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (MineBoard[x][y] == '0')
		{
			MineBoard[x][y] = '1';
			count--;
		}
	}
}


int GetMine(char MineBoard[ROWS][COLS], int x, int y)
{
	return MineBoard[x - 1][y - 1]
		+ MineBoard[x - 1][y]
		+ MineBoard[x - 1][y + 1]
		+ MineBoard[x][y - 1]
		+ MineBoard[x][y + 1]
		+ MineBoard[x + 1][y - 1]
		+ MineBoard[x + 1][y]
		+ MineBoard[x + 1][y + 1]
		- 8*'0';
}

FindMine(char MineBoard[ROWS][COLS], char ShowBoard[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 (ShowBoard[x][y] != '*')
			{
				printf("该坐标已被查询!请重新输入坐标!");
			}
			if (MineBoard[x][y] == '1')
			{
				printf("很遗憾!你被炸死了!\n");
				Show(MineBoard, ROW, COL);
				break;
			}
			else
			{
				int ret = GetMine(MineBoard, x, y);

				ShowBoard[x][y] = ret+'0';
				Show(ShowBoard, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("非法输入!请重新输入!\n");
		}
	}

	if (win == ROW * COL - EASY_COUNT)
	{
		printf("恭喜您!排查成功!\n");
		Show(MineBoard, ROW, COL);
	}
}

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

void play()
{
	char MineBoard[ROWS][COLS] = { 0 };

	char ShowBoard[ROWS][COLS] = { 0 };

	InitBoard(MineBoard,ROWS,COLS,'0');
	    
	InitBoard(ShowBoard,ROWS,COLS,'*');

	Show(ShowBoard, ROW, COL);

	SetMine(MineBoard, ROW, COL);
	
	FindMine(MineBoard, ShowBoard, ROW,COL);
}

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

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

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

相关文章

集合-Map系列

系列文章目录 1.集合-Collection-CSDN博客​​​​​​ 2.集合-List集合-CSDN博客 3.集合-ArrayList源码分析(面试)_喜欢吃animal milk的博客-CSDN博客 4.数据结构-哈希表_喜欢吃animal milk的博客-CSDN博客 5.集合-set系列集合-CSDN博客 6.集合-Map系列-CSDN博客 文章目…

指针详解第三部分

1. 字符指针变量 一种指针类型为字符指针 char*&#xff08;下面有两个代码表示了两种不同的用法&#xff09; int main() {char ch w;char* pc &ch;*pc w;return 0; }int main() {const char* pstr "hello bit.";//这⾥是把⼀个字符串放到pstr指针变量⾥了…

什么是JWT?深入理解JWT从原理到应用

&#x1f389;&#x1f389;欢迎来到我的CSDN主页&#xff01;&#x1f389;&#x1f389; &#x1f3c5;我是Java方文山&#xff0c;一个在CSDN分享笔记的博主。&#x1f4da;&#x1f4da; &#x1f31f;推荐给大家我的专栏《ELement》。&#x1f3af;&#x1f3af; &#x1…

使用 Python 和 wxPython 进行批量文件扩展名替换

引言&#xff1a; 在日常的文件管理中&#xff0c;有时我们需要将一大批文件的扩展名进行替换。手动一个个重命名文件是一项繁琐的任务&#xff0c;但是使用 Python 编程语言和 wxPython 模块可以轻松地实现这一功能。本文将介绍如何使用 Python 和 wxPython 创建一个简单的图形…

Python学习笔记之分支结构与循环结构

Python学习笔记之分支结构与循环结构 一、分支结构 使用关键字if、elif、else 练习1&#xff1a;使用分支结构实现分段函数求值 """分段函数求值""" x float(input("x "))if x > 1:y 3 * x - 5 elif x < -1:y 5 * x 3…

开启创意思维,畅享Mindomo Desktop for Mac思维导图之旅

在数字化时代&#xff0c;我们需要一个强大而直观的工具来整理和展现我们的思维。Mindomo Desktop for Mac&#xff08;Mindomo&#xff09;作为一款免费的思维导图软件&#xff0c;将为您提供卓越的创意思维体验。 Mindomo拥有直观的界面和丰富的功能&#xff0c;让您能够方便…

手搭手Mybatis-Plus多数据源异构数据迁移案例

环境介绍 技术栈 springbootmybatis-plusdruidbaomidoumysqloracledm 软件 版本 mysql 8 IDEA IntelliJ IDEA 2022.2.1 JDK 1.8 Spring Boot 2.7.13 mybatis 2.3.1 pom.xml所需依赖 <dependencies><dependency><groupId>org.springframework.…

【Golang】包

包 Go语言是使用包来组织源代码的&#xff0c;包&#xff08;package&#xff09;是多个 Go 源码的集合&#xff0c;是一种高级的代码复用方案 Go语言中为我们提供了很多内置包&#xff0c;如 fmt、os、io 等。 任何源代码文件必须属于某个包&#xff0c;同时源码文件的第一…

【2】c++设计模式——>UML表示类之间的继承关系

继承也叫作泛化&#xff08;Generalization&#xff09;&#xff0c;用于描述父子类之间的关系&#xff0c;父类又称为基类或者超类&#xff0c;子类又称作派生类。在UML中&#xff0c;继承关系用带空心三角形的实线来表示。 关于继承关系一共有两种&#xff1a;普通继承关系和…

【共享模型-----管程】

文章目录 1. 为什么会出现线程安全问题2. synchronized 解决方案2.1 线程八锁 3. 变量的线程安全分析3.1 局部变量线程安全分析3.2 常见线程安全类 1. 为什么会出现线程安全问题 一段代码块内如果存在对共享资源的多线程读写操作&#xff0c;称这段代码块为临界区 共享资源&a…

一张图看懂 8 种网络协议

什么是网络协议&#xff1f; 网络协议就是计算机之间沟通的语言&#xff0c;为了有效地交流&#xff0c;计算机之间需要一种共同的规则或协议&#xff0c;就像我们和老外沟通之前&#xff0c;要先商量好用哪种语言&#xff0c;要么大家都说中文&#xff0c;要么大家都说英语&a…

优思学院|六西格玛将烹饪和美味提升至极致

最近&#xff0c;我们曾提到一个美国男子如何利用六西格玛来控制糖尿病。这表明六西格玛逐渐被认为是一个不仅可以在工作场所之外使用&#xff0c;尤其不仅限于制造业的系统。 六西格玛的核心理念是改进过程的质量&#xff0c;从而改善最终结果。如果你做了晚餐或尝试了一道新…

ESP32设备驱动-OLED显示BME280传感器数据

OLED显示BME280传感器数据 文章目录 OLED显示BME280传感器数据1、BME280介绍2、硬件准备3、软件准备4、代码实现在本文中,我们将介绍如何使用OLED显示BME280传感器的数据。 1、BME280介绍 BME280 传感器用于测量有关环境温度、大气压力和相对湿度的读数。 它主要用于以低功耗…

2023/10/4 -- ARM

今日任务&#xff1a;QT实现TCP服务器客户端搭建的代码&#xff0c;现象 ser&#xff1a; #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);server new QTcpSe…

JMeter性能分析实战一:日常登录接口

负载测试 日常需求&#xff1a;负载测试&#xff01; 对于桥的负载测试&#xff1a;我给你20t的一排车辆&#xff0c;看你能不能撑得住20t&#xff01; 对于系统的负载测试&#xff1a; 逐步增加负载&#xff0c;便于问题的发现和定位&#xff0c;不要操之过急。逐步增加负载…

由Long类型引发的生产事故

事情原由 今天测试忽然在群里发了一个看似非常简单的线上问题&#xff0c;具体是&#xff1a;在后台通过订单编号(orderId)修改订单信息时&#xff0c;修改不成功 &#xff0c;修改前后的订单数据完全没有发生变化。第一眼看到这个问题的时候&#xff0c;我心想后台实现逻辑并不…

CSS 语法

CSS 实例 CSS 规则由两个主要的部分构成&#xff1a;选择器&#xff0c;以及一条或多条声明: 选择器通常是您需要改变样式的 HTML 元素。 每条声明由一个属性和一个值组成。 属性&#xff08;property&#xff09;是您希望设置的样式属性&#xff08;style attribute&#x…

监狱劳动工具管理系统|智工具DW-S308的功能

监狱劳动工具管理系统(智工具DW-S308)是依托互3D技术、云计算、大数据、RFID技术、数据库技术、AI、视频分析技术对工具进行统一管理、分析的信息化、智能化、规范化的系统。 目前监狱的劳动工具管理很多还停留在固定工位&#xff0c;人盯人、人管人等落后的管理模式&#xff…

图谱滤波(Graph Spectral Processing)-1

时域和空域信号的滤波是图像处理的基本技术之一&#xff0c;迄今已得到广泛的研究。Graph Spectral Processing(GSP)可以处理不规则结构的信号&#xff0c;这些信号在数学上用图形表示。利用谱图理论研究了图信号滤波的理论和方法。在图像处理中&#xff0c;图是表示像素形成的…

华为云云耀云服务器L实例评测|RabbitMQ的Docker版本安装 + 延迟插件安装 QQ邮箱和阿里云短信验证码的主题模式发送

前言 最近华为云云耀云服务器L实例上新&#xff0c;也搞了一台来玩&#xff0c;期间遇到各种问题&#xff0c;在解决问题的过程中学到不少和运维相关的知识。 本篇博客介绍RabbitMQ的Docker版本安装和配置&#xff0c;延迟插件的安装&#xff1b;结合QQ邮箱和阿里云短信验证码…