【C语言-扫雷游戏全功能详解】

news2024/11/17 4:58:39

目录

理解扫雷原理

梳理扫雷过程

 9*9 棋盘

初始化棋盘

显示棋盘

​编辑

布置雷

排查雷

统计x,y坐标周围有几个雷

使用递归函数来实现周围没地雷时展开多个

判断成功排除后剩下的方格数是否等于地雷数

排查函数

梳理编写代码思路

头文件game.h

库函数所需要头文件

宏定义

函数声明

源文件game.c

源代码 test.c

菜单

游戏

main函数

完整代码

game.h

game.c

test.c

运行结果


理解扫雷原理

 9*9 棋盘

上面布置10个雷

排查雷

1.如果这个位置不是雷,就计算这个位置的周围8个坐标有几个雷,并显示雷的个数

2.如果这个位置是雷,就炸死了,游戏结束了

3.如果把不是雷的位置都找出来了,那游戏也结束

梳理扫雷过程

 9*9 棋盘

初始化棋盘

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;
		}
	}
}

通过初始化函数

初始化mine这个棋盘来安置随机的地雷的位置(先放入‘0’//后面随机放入‘1’标记地雷的位置)

初始化show这个棋盘来显示扫雷游戏的过程图(先放入‘*’//后面根据坐标逐渐揭开以及周围地雷情况的数字)

  char mine[11][11]                                                                           char show[11][11] 

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

显示棋盘

void DisplayBoard(char board[ROWS][COLS], int row, int col) {
	int i = 0;
	int j = 0;
	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");
}

 如图所示 通过打印行数和列数方便定位

                 通过----------扫雷---------来划分每个图

布置雷

要随机布置10个雷

利用rand 和 srand 函数来生成随机数去随机生成地雷的坐标

注:

rand函数是C/C++中的随机数生成函数,用于生成一个范围在0到RAND_MAX之间的随机整数。它的原型为:

int rand();

srand函数用于设置rand函数的随机数种子,以便每次生成的随机数序列不同。它的原型为:

void srand(unsigned int seed);

在使用rand函数之前,通常会先使用srand函数设置一个种子,可以使用时间戳等随机值作为种子来保证每次运行程序时生成不同的随机数序列。

rand函数生成的随机数并不是真正的随机数,而是伪随机数,它是根据某种算法生成的,每次程序运行时生成的随机数序列是相同的。因此,为了获取更好的随机性,需要使用srand函数设置不同的种子值。

void SetMine(char mine[ROWS][COLS], int row, int col) {
	int count = EASY_COUNT;
	while (count) {
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0') {
			mine[x][y] = '1';
			count--;
		}
	}
}

srand一般在main函数里写 

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

 

排查雷

统计x,y坐标周围有几个雷

int GetMineCount(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');

}

 

如图所示 判断周围8个位置的地雷情况

使用递归函数来实现周围没地雷时展开多个

void spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	int count = GetMineCount(mine, x, y);//周围地雷的个数(此时是字符的ASCII码值)

	if (count == 0)
	{
		show[x][y] = ' ';
		int i = 0, j = 0;

		for (i = -1; i <= 1; i++)
		{
			for (j = -1; j <= 1; j++)
			{
				//连续排除时限制范围在棋盘范围内
				if ((x + i) > 0 && (y + i) > 0 && (x + i < ROWS) && (y + j < COLS) && show[x + i][y + j] == '*')
				{
					spread(mine, show, x + i, y + j);//递归实现周围如果都没地雷连续排除
				}
			}
		}
	}
	else
	{
		show[x][y] = count + '0';//将字符ASCII码值转换为数字从而显示
	}
}

如果周围的地雷数是0 直接将这个位置改为空格 ——逐渐化简图像

递归实现周围如果都没地雷连续排除

判断成功排除后剩下的方格数是否等于地雷数

int IsWin(char show[ROWS][COLS], int row, int col)
{
	int num = 0;
	//排除一个地雷时便进行累加
	for (int i = 1; i <= row; i++)
	{
		for (int j = 1; j <= col; j++)
		{
			if (show[i][j] == '*')
				num++;
		}
	}
	return num;
}

当最后就剩下10个‘ * ’的时候就成功了 

排查函数

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (1) {
		printf("请输入要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col) {
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				spread(mine, show, x, y);//计算周围地雷数量,连续排除无雷的方格
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法,重新输入\n");
		}
		int ret = IsWin(show, row, col);
		if (ret == EASY_COUNT)//当累加的地雷数量等于布置地雷的数量则说明地雷全部排除
		{
			printf("恭喜你,通关成功!\n");
			DisplayBoard(mine, ROW, COL);
			break;
		}
	}
}

 

梳理编写代码思路

头文件game.h

库函数所需要头文件

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

stdio.h

rand(头文件:stdlib.h

time(头文件:time.h

需要rand(头文件:stdlib.h)和time(头文件:time.h),结合使用可以使电脑随机布置雷

宏定义

扫雷标准是9行9列,但是由于考虑到靠边的坐标计算周围的地雷数时,还需要不包括越界的地方

所以将两个数组都扩大一圈,就不会有这方面的困扰了,即变为了11行11列的二维数组

#pragma once

#define COL 9
#define ROW 9

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

#define EASY_COUNT 10

使用宏定义的方式分别定义ROW9,COL9,ROWS11,COLS11,以及设置的地雷数EASY_COUNT10

注:需要在定义之前加#pragma once

#pragma once是C++中的预处理指令,用于确保头文件只被编译一次。当多个源文件包含同一个头文件时,如果没有#pragma once指令,会导致头文件被重复包含,从而引发编译错误。使用#pragma once可以避免这种情况的发生,提高编译效率。

函数声明

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

//显示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int colt);

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

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

//统计x,y坐标周围有几个雷
int GetMineCount(char mine[ROWS][COLS], int x, int y);

//判断成功排除后剩下的方格数是否等于地雷数
int IsWin(char show[ROWS][COLS], int row, int col);

//使用递归函数来实现周围没地雷时展开多个
void spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);

源文件game.c

包含所有有关的游戏的函数

具体代码见下

源代码 test.c

菜单

void menu()
	{
		printf("*****************************\n");
		printf("********** 1.PLAY **********\n");
		printf("********** 2.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, '*');
		//显示show棋盘
		DisplayBoard(mine, ROW, COL);
		//布置雷
		//显示mine棋盘
		SetMine(mine, ROW, COL);
		DisplayBoard(show, ROW, COL);

		//排查雷
		FindMine(mine, show, ROW, COL);
	}

 相当于将实现游戏的代码单独列出来

main函数

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

写循环来实现在游戏结束后,继续打印选择的菜单,由玩家决定是否继续进行游戏 

完整代码

game.h

#define _CRT_SECURE_NO_WARNINGS 1

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

#define COL 9
#define ROW 9

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

#define EASY_COUNT 10

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

//显示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int colt);

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

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

//统计x,y坐标周围有几个雷
int GetMineCount(char mine[ROWS][COLS], int x, int y);

//判断成功排除后剩下的方格数是否等于地雷数
int IsWin(char show[ROWS][COLS], int row, int col);

//使用递归函数来实现周围没地雷时展开多个
void spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);

game.c

#include "game.h"

#define _CRT_SECURE_NO_WARNINGS 1

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

}
int IsWin(char show[ROWS][COLS], int row, int col)
{
	int num = 0;
	//排除一个地雷时便进行累加
	for (int i = 1; i <= row; i++)
	{
		for (int j = 1; j <= col; j++)
		{
			if (show[i][j] == '*')
				num++;
		}
	}
	return num;
}

void spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
	int count = GetMineCount(mine, x, y);//周围地雷的个数(此时是字符的ASCII码值)

	if (count == 0)
	{
		show[x][y] = ' ';
		int i = 0, j = 0;

		for (i = -1; i <= 1; i++)
		{
			for (j = -1; j <= 1; j++)
			{
				//连续排除时限制范围在棋盘范围内
				if ((x + i) > 0 && (y + i) > 0 && (x + i < ROWS) && (y + j < COLS) && show[x + i][y + j] == '*')
				{
					spread(mine, show, x + i, y + j);//递归实现周围如果都没地雷连续排除
				}
			}
		}
	}
	else
	{
		show[x][y] = count + '0';//将字符ASCII码值转换为数字从而显示
	}
}

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int x = 0;
	int y = 0;
	int win = 0;
	while (1) {
		printf("请输入要排查的坐标:>");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col) {
			if (mine[x][y] == '1')
			{
				printf("很遗憾,你被炸死了\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				spread(mine, show, x, y);//计算周围地雷数量,连续排除无雷的方格
				DisplayBoard(show, ROW, COL);
				win++;
			}
		}
		else
		{
			printf("坐标非法,重新输入\n");
		}
		int ret = IsWin(show, row, col);
		if (ret == EASY_COUNT)//当累加的地雷数量等于布置地雷的数量则说明地雷全部排除
		{
			printf("恭喜你,通关成功!\n");
			DisplayBoard(mine, ROW, COL);
			break;
		}
	}
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

	void menu()
	{
		printf("*****************************\n");
		printf("********** 1.PLAY **********\n");
		printf("********** 2.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, '*');
		//显示show棋盘
		DisplayBoard(mine, ROW, COL);
		//布置雷
		//显示mine棋盘
		SetMine(mine, ROW, COL);
		DisplayBoard(show, ROW, COL);

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

运行结果

部分运行截图

 

 

 

 

 

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

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

相关文章

STM32 cubemx配置USART DMA传输

文章目录 前言一、DMA概念二、STM32 DMA数据手册分析3.DMA模式介绍4.cubemx配置总结 前言 本篇文章来讲解DMA的概念&#xff0c;并使用DMA来进行串口的数据收发。 一、DMA概念 DMA&#xff08;Direct Memory Access&#xff0c;直接内存访问&#xff09;是一种计算机系统的技…

vue 设置数组

手写获取数据 <el-form-item label"缴纳方"><el-select v-model"form.invoiceCategoryName" placeholder"请选择缴纳方"><el-optionv-for"item in kplmList":key"item.value":label"item.label":v…

求解方程x^2=a的根,不使用库函数直接求解(不动点迭代法)

首先可以将方程两边同时加上x&#xff0c;&#xff0c;这时候两边同时再除以1x&#xff0c;就得到了&#xff0c;变形为。&#xff08;变性后的迭代式不唯一&#xff0c;这里随便选取一个&#xff09; 当x是准确值的时候&#xff0c;两边应该是相等的&#xff0c;如果x是近似值…

数据库连接池的使用

十、数据库连接池的使用 1、c3p0数据库连接池 硬编码&#xff1a; c3p0的帮助文档 c3p0配置文件&#xff1a; 2、dbcp数据库连接池 配置文件&#xff1a; 3、druid 将上面三个做成工具类&#xff1a;

单网卡实现 双IP 双网段(内外网)同时运行

前提是内外网是同一网线连接&#xff08;双网线双网卡的具体可以自己搜索下。理论上都设置静态IP后把外网跃点设置小&#xff0c;内网跃点设置大&#xff0c;关闭自动跃点设置同一个接口跃点数&#xff0c;在通过命令提示符添加内网网址走内网网关就可以了&#xff09;。 需要使…

Mac 定时重启 TouchBar 脚本(缓解闪烁问题)

背景 Mac 笔记本 TouchBar 是真的脆啊&#xff0c;合盖使用一段时间就废了&#xff0c;右侧一直闪烁简直亮瞎眼 &#x1f602; 经过观察&#xff0c;总结出闪烁规律如下&#xff1a; 工作状态&#xff1a;不断操作电脑时&#xff0c;触控栏处于工作状态&#xff0c;几乎不闪…

Git分布式版本控制工具和GitHub(一)--简介

一.Git概述 1.Git简介 【1】什么是Git? Git就是代码版本管理工具。 【2】为什么要使用Git &#xff08;1&#xff09;版本控制 写代码就是不断写BUG的过程&#xff08;当然我们是不会这么说的&#xff09;&#xff0c;很多时候你写了100行代码之后&#xff0c;突然醒悟&…

APP自动化测试-Python+Appium+Pytest+Allure框架实战封装(详细)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 pytest只是单独的…

Spring——更快捷的存储 / 获取Bean对象

文章目录 前言一、存储 Bean 对象类注解为什么有五个类注解使用类注解存储对象配置扫描路径(重中之重)添加注解存储 Bean 对象 方法注解配置扫描路径(重中之重)使用方法注解存储对象 二、获取 Bean 对象Autowired属性注入Setter注入构造方法注入 Resource 总结 前言 本人是一个…

day17-二叉树的迭代遍历

二叉树的迭代遍历 二叉树的递归遍历是比较简单的 前序中序后序 前序遍历 前序遍历代码如下&#xff1a; class Solution { public:vector<int> preorderTraversal(TreeNode* root) {vector<int> res;preorder(root,res);return res;}void preorder(TreeNode* …

进度条 Vue2 +PC端 ,点击页面加载时页面顶部的那个蓝条

提示&#xff1a; <div class"global-loading"> </div> css:less <style lang"less"> .global-loading {background-color: red;height: 10px;position: fixed;z-index: 999;width: 0;animation: grow 2s infinite forwards;}keyframe…

ROS从入门到精通6-8:costmap代价地图插件编写案例(prohibition_layer)

目录 0 专栏介绍1 为什么需要代价地图插件?2 自定义代价地图插件3 仿真测试0 专栏介绍 本专栏旨在通过对ROS的系统学习,掌握ROS底层基本分布式原理,并具有机器人建模和应用ROS进行实际项目的开发和调试的工程能力。 🚀详情:《ROS从入门到精通》 1 为什么需要代价地图插…

PHP: 开发入门macOS系统下的安装和配置

安装Homebrew 安装 ~~友情提示&#xff1a;这个命令对网络有要求&#xff0c;可能需要翻墙或者用你的手机热点试试&#xff0c;或者把DNS换成&#xff08;114.114.114.114 和 8.8.8.8&#xff09; /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebr…

tensorRT模型性能测试

目录 前言1. 模型训练1.1 模型1.2 数据集1.3 xml2yolo1.4 yolo2json1.5 json2yolo1.6 训练 2. TRT模型转换2.1 YOLOv5 ONNX导出2.2 YOLOv6 ONNX导出2.3 YOLOv5 engine生成2.4 YOLOv6 engine生成 3. TRT模型测试3.1 YOLOv5 engine mAP测试3.2 YOLOv5 engine 速度测试3.3 YOLOv6 …

代码随香录算法训练营day16 | 104. 二叉树的最大深度,559. N 叉树的最大深度,111. 二叉树的最小深度,222. 完全二叉树的节点个数

目录 104. 二叉树的最大深度 559. N 叉树的最大深度 111. 二叉树的最小深度 222. 完全二叉树的节点个数 104. 二叉树的最大深度 学了回溯之后再来做一下 思路&#xff1a; 二叉树节点的深度&#xff1a;指从根节点到该节点的最长简单路径边的条数或者节点数&#xff08;取…

chatgpt 接口使用(一)

使用api实现功能 参考链接&#xff1a;https://platform.openai.com/examples 安装库&#xff1a; pip3 install openai 例如&#xff1a; import os import openaiopenai.api_key os.getenv("OPENAI_API_KEY") response openai.ChatCompletion.create(model&q…

Flutter:使用url_launcher打开外部浏览器、拨打电话、发送短信、打开第三方app、打开应用商店下载应用

前言 Flutter中的url_launcher是一个用于打开URL的插件。它允许在Flutter应用程序中打开网址、发送电子邮件、拨打电话等操作。使用url_launcher插件&#xff0c;可以轻松地在应用程序中集成各种URL操作。 官方地址 https://pub-web.flutter-io.cn/packages/url_launcher 安…

Oracle 时间多少秒以后 oracle interval 多少分钟之前 Oracle日期1小时后 Java时间多少秒以后 Java日期多少天之前

Oracle 时间多少秒以后 oracle interval 多少分钟之前 Oracle日期1小时后 Java时间多少秒以后 Java日期多少天之前 一、概述 在项目开发中&#xff0c;遇到一个类似于 超时关闭的订单&#xff08;超过1分钟后关闭订单&#xff09; 的需求&#xff0c;在数据的时间写入时&#x…

【Python机器学习】实验06 KNN最近邻算法

文章目录 KNN算法前言 距离度量(1) 欧式距离(2) 曼哈顿距离(Manhattan distance)(3) 切比雪夫距离(Chebyshev distance)(4) 闵可夫斯基距离(Minkowski distance)(5) 汉明距离(Hamming distance)(6) 余弦相似度(Cosine Similarity) KNN算法介绍1 数据的准备2 划分训练数据和测试…

C++11 线程库

1 thread类的简单介绍 在C11之前&#xff0c;涉及到多线程问题&#xff0c;都是和平台相关的&#xff0c;比如windows和linux下各有自己的接口&#xff0c;这使得代码的可移植性比较差。C11中最重要的特性就是对线程进行支持了&#xff0c;使得C在并行编程时不需要依赖第三方库…