C语言实现扫雷

news2024/11/24 18:40:46

总有一天你要一个人在暗夜中,向那座桥走过去

目录

一、文件及其对应代码

1.test.c

2.game.c

3.game.h

二、数组创建解析

1.创建两个数组的原因

2.预设数组较大的原因

三、计算周围雷的个数

四、向外扩展并延伸判断


扫雷游戏,相信大家都玩过,今天练习用C语言实现扫雷。先介绍一下规则,因为是C语言实现,所以每次要输入想排查的雷的坐标,若该坐标为雷则失败;若该坐标周边八个坐标中有雷,则显示雷个数,若无雷则继续向周边延伸排查,一直到某坐标周边8个坐标有雷为止

先看代码,再看思路及我觉得其中比较难实现的部分

本文中用到的一个工程中包含多个文件的方法,它们之间的关系及对应思路,在这个文章中有介绍过,大家可以去看http://t.csdn.cn/qnOxE

一、文件及其对应代码

1.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(mine, ROW, COL);
	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();
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("输入错误,请重新选择:\n");
			break;
		}
	} while (input);
	return 0;
}

2.game.c

#include"game.h"
//初始化棋盘,初始11*11的
InitBoard(char Board[ROWS][COLS], int rows, int cols,char ret)
{
	int i = 0;
	for (i = 0; i < rows; i++)
	{
		int j = 0;
		for (j = 0; j < cols; j++)
		{
			Board[i][j] = ret;
		}
	}
}

//打印棋盘
DisplayBoard(char Board[ROWS][COLS], int row, int col)
{
	int i = 0;
	printf("----------扫雷游戏---------\n");
	for (i = 0; i <= col; i++)
	{
		printf("%d  ", i);
	}
	printf("\n");
	for (i = 1; i <= row; i++)
	{
		int j = 0;
		printf("%d  ",i);
		for (j = 1; j <=col; j++)
		{
			printf("%c  ", Board[i][j]);
		}
		printf("\n");
	}
}


//布置雷
SetMine(char mine[ROWS][COLS], int row, int col)
{
	int count1 = 1;
	while (count1 <= count)
	{
		int x = rand() % row + 1;
		int y = rand() % col + 1;
		if (mine[x][y] == '0')
		{
			mine[x][y] = '1';
			count1++;
		}
	}
}

//排查雷
void menu2()
{
	printf("1.排查雷\n");
	printf("2.标记雷\n");
}

FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
	int blank = row * col - count;
	while (blank > 0)
	{
	 
		printf("请输入你要排查的坐标:");
		int x = 0;
		int y = 0;
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (mine[x][y] == '1')
			{
				printf("抱歉,你被炸死了,游戏结束\n");
				printf("雷的具体信息为:\n");
				DisplayBoard(mine, ROW, COL);
				break;
			}
			else
			{
				Change(mine, show, ROW, COL, x, y);
				 
				DisplayBoard(show, ROW, COL);
				blank--;
			}
		}
		else
			printf("输入错误,请重新输入:\n");

	}
}
//判断周围有几个雷
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
	return (mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0');
}

//对周围延伸继续向外判断
void Change(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col,int x, int y)
{
	int count_ = GetMineCount(mine, x, y);
	if (count_ == 0)
	{
		//遍历周围8个坐标
		show[x][y] = ' ';
		int i = 0;
		for (i = x - 1; i <= x + 1; i++)
		{
			int j = 0;
			for (j = y - 1; j <= y + 1; j++)
			{
				if(show[i][j]=='*')
					Change(mine, show, ROW, COL, i,j);
			}
		}
	}
	else
		show[x][y] = count_ + '0';
}

3.game.h

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define count 10
InitBoard(char Board[ROWS][COLS], int rows, int cols,char ret);
DisplayBoard(char Board[ROWS][COLS], int row, int col);
SetMine(char mine[ROWS][COLS], int row, int col);
FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
int GetMineCount(char mine[ROWS][COLS], int row, int col);
//int GetMineCount2(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y,int* blank);
void Change(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y);

二、数组创建解析

1.创建两个数组的原因

一个mine数组和一个show数组,其中第一个数组用于布置雷,第二个数组是具体展现出来的效果,包括刚开始的时候未排查雷的游戏界面,以及玩家输入要排查的雷的坐标后展现出的排查点坐标周围的雷的个数和继续递归延展出的雷的个数

如下,这个面板就是应该要呈现在玩家面前的show数组的棋盘, 我们在C语言中用   ‘*’   来表示下面的网格,再标上行号和列号方便玩家输入坐标(行号和列号需要打印上去,不能占用数组空间)。假如是9*9的棋盘,其中有10个雷,那么玩家就可以通过输入的坐标在这个棋盘上面进行排雷。将这个数组元素全部初始化为字符‘*’,当玩家输入某个坐标时,先判断这个坐标对应在mine数组中是不是雷,不是的话,再对它周围的8个元素进行排查,计算出周围有几个雷,将它对应的坐标元素更改为字符几,如果周围没有雷的话,先将对应坐标元素改为空格,然后通过函数递归对向外扩展,直到坐标周围有雷为止,周围有雷就可以显示周围雷的个数,也就初步到达了我们扫雷游戏的效果

 而我们布置雷的数组,则是先初始化为字符‘0’,当电脑随机生成雷的坐标后,将置雷的数组对应坐标元素就可以改为字符‘1’即可

注意:show数组只是展现给玩家看的,我们设计的排雷步骤其实都是在mine数组内进行的,然后将结果展示在show数组上然后打印出来

2.预设数组较大的原因

如图所示,可以看到,明明是9*9的扫雷棋盘,但是却在创建数组的时候创建了11*11的数组。这是因为在我们判断一个坐标周围有没有雷的时候,是遍历它周围的8个数组元素,这就导致了一个问题,边界位置点的周围雷数量怎么计算。如果9*9的棋盘,就创建9*9的数组的话,那么数组外的元素都是随机值,这会导致对边界位置点的周围雷数判断出现错误。将数组创建为11*11,并且初始化时全部初始化为字符‘0’,这就相当于自动规定了数组边界点元素周围的棋盘外的雷数量为0,可以很好的解决这个问题,当然,在打印棋盘的时候,我们只需要打印数组的1-9行和1-9列即可

三、计算周围雷的个数

这个其实比较简单,但初学者很难把握,我们创建mine(布置雷)数组的时候,将所有字符元素全部初始化为字符‘0’,布置雷时,将有雷位置的元素值赋为字符‘1’,那么在判断周围有几个雷的时候,就可以将周围八个元素的值相加,然后再减去8*‘0’,就可以得到一个整数,这就是某网格坐标周围的雷数,再将这个整数加字符‘0’,就能将这个整数变为它对应的字符,再将这个字符赋值给show数组对应的坐标即可。最后打印出show数组即可

四、向外扩展并延伸判断

这个其实是游戏改进最难的地方,依照我们上面说的,如果周围没有雷的话,将那个点的元素赋值为‘0’即可,但这并不是我们想要的,试想,9*9的棋盘,除去10个雷,都还有71个位置需要排雷,更不用说更大的棋盘了,如果一个一个排的话,那么时间成本将会非常高,游戏趣味性也会变低。

根据扫雷游戏的原理,对于周围无雷的位置,自动继续向周边延伸排查。为了简易的实现这个功能,就需要用到函数递归的原理,当计算周围雷个数的函数的返回值为0时,将show数组对应坐标元素值赋为‘ ’(空格),然后继续对这个无雷位置的周围8个坐标进行排查,如果这些坐标周围8个点中有周围雷的个数依旧为0的话,就继续对这个数为0的点周围的8个坐标进行排查,如此一直往复,就可以将周围雷个数为0的点全部找出,大大节省了时间成本,思考过程还是比较抽象的,但代码不是很难,详细实现过程大家可以参考代码了解其实现过程。

如图,当排查一个位置后,将会一直向外排查并延伸,并且自动标出雷个数

注意:对一个点周围8个元素进行遍历时,要依次判断即将遍历的坐标对应的show数组元素值是不是‘*’,如果不是‘*’,证明这个位置已经判断过了,如果再对这个坐标进行递归,就会造成死递归,程序也会崩溃

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

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

相关文章

【c++】图解类和对象(上)

类和对象&#xff08;上&#xff09; 文章目录 类和对象&#xff08;上&#xff09;一、面向过程和面向对象初步认识二、类的引入三、类的定义四、类的访问限定符及封装1.访问限定符2.封装 五、类的作用域六、类的实例化七、类对象模型八、this指针总结 一、面向过程和面向对象…

Mysql中select语句的执行流程?

Mysql中select语句的执行流程&#xff1f; 答&#xff1a; SELECT 语句的执行过程为&#xff1a;连接、查询缓存、a词法分析&#xff0c;语法分析&#xff0c;语义分析&#xff0c;构造执行树&#xff0c;生成执行计划、执行器执行计划&#xff0c;下面开始梳理一次完整的查询…

【MySQL】视图,事务、隔离级别

视图--虚表&#xff0c;不在数据库中存放数据&#xff0c;数据源于基本表。 为什么要使用视图 简化复杂的sql操作&#xff0c;在编写查询后&#xff0c;可以方便的重用它而不必知道它的查询细节。重复使用该sql语句。使用表的组成部分而不是整个表。保护数据&#xff0c;可以给…

vscode编译的时候:未定义标识符 thread

vscode编译的时候&#xff1a;未定义标识符 thread thread’ was not declared in this scope" 未定义标识符 thread 原因 MinGW GCC当前仍缺少标准C 11线程类的实现。 对于跨平台线程实现&#xff0c;GCC标准库依赖于gthreads / pthreads库。如果该库不可用&#xf…

手搓GPT系列之 - 通过理解LSTM的反向传播过程,理解LSTM解决梯度消失的原理 - 逐条解释LSTM创始论文全部推导公式,配超多图帮助理解(上篇)

1. 前言 说起RNN和LSTM&#xff0c;就绕不过Sepp Hochreiter 1997年的开山大作 Long Short-term Memory。奈何这篇文章写的实在是太劝退&#xff0c;整篇论文就2张图&#xff0c;网上很多介绍LSTM的文章都对这个模型反向传播的部分避重就轻&#xff0c;更少见&#xff08;反正…

2023/5/14学习总结

这道题我们可以看到数据范围很小 &#xff0c;所以可以使用暴力枚举&#xff0c;将所有可以组成长方形的长宽全遍历一遍&#xff0c;同时要满足这个长方形里没有障碍物的条件&#xff0c;取得周长最大值 #include<bits/stdc.h> using namespace std; typedef long long …

JavaSE基础(六)—— 面向对象、封装、对象内存图、成员变量和局部变量区别

目录 一、面向对象对象介绍 1. 面向对象的重点学习什么 二、设计对象并使用 1. 设计类&#xff0c;创建对象并使用 1.1 如何得到对象 1.2 如何使用对象 2. 定义类的几个补充注意事项 2.1 对象的成员变量的默认值规则 三、对象内存图 1. 多个对象内存图 2. 两个变量指…

Springboot +Flowable,流程表单应用之静态表单

一.简介 整体上来说&#xff0c;我们可以将Flowable 的表单分为三种不同的类型&#xff1a; 动态表单 这种表单定义方式我们可以配置表单中每一个字段的可读性、可写性、是否必填等信息&#xff0c;不过不能定义完整的表单页面。外置表单 外置表单我们只需要定义一下表单的 k…

生命周期、数据共享、ref引用、购物车案例

生命周期&数据共享 1.组件的生命周期2.组件之间的数据共享3.ref 引用4.购物车案例 1.组件的生命周期 生命周期 & 生命周期函数 生命周期&#xff08;Life Cycle&#xff09;是指一个组件从创建 -> 运行 -> 销毁的整个阶段&#xff0c;强调的是一个时间段。 生命…

chatGPT提问,BGP内容

ChatGPT提问&#xff1a;提问框架 背景角色任务要求 动态路由&#xff1a;内部网关协议&#xff1a;如RIP ISIS OSPF 在同一个公司内部运行的路由协议 外部网关协议&#xff1a;如 BGP 在不同公司之间运行的路由协议 AS&#xff1a;自治系统 每个自治系统都有唯一的…

动态组件、插槽、自定义指令、Eslint和prettierrc配置、axios全局挂载

动态组件、插槽、自定义指令、Eslint和prettierrc配置、axios全局挂载 动态组件插槽体验插槽的基础用法作用域插槽 自定义指令Eslint和prettierrc配置prettierrc axios全局挂载 动态组件 动态组件指的是动态切换组件的显示与隐藏。 如何实现动态组件渲染 vue 提供了一个内置的…

Visual Studio 2022 CMake+MinGW+GDB 调试目标程序

前段时间笔者在使用MinGW编译了QtCreator后&#xff0c;想要进行调试。最开始使用VSCode进行调试&#xff0c;可是可以调试&#xff0c;但是发现调试过程中反应比较慢&#xff0c;毕竟QtCreator整个源代码工程还是非常大的&#xff0c;VSCode是由JS语言编写&#xff0c;执行效率…

Golang每日一练(leetDay0065) 位1的个数、词频统计

目录 191. 位1的个数 Nnumber of 1-bits &#x1f31f; 192. 统计词频 Word Frequency &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C每日一练 专栏 Java每日一练 专栏 191. 位1的个数 Nnum…

Java面试知识点(全)-JVM面试知识点一

[Java面试知识点(全) 导航&#xff1a; https://nanxiang.blog.csdn.net/article/details/130640392 注&#xff1a;随时更新 SQL优化 r m y s q l q u e r y ( " S E L E C T u s e r n a m e F R O M u s e r W H E R E s i g n u p d a t e > ′ r mysql_query(…

RK3568平台开发系列讲解(网络篇)图解linux ping

🚀返回专栏总目录 文章目录 一、SOCK_RAW套接字实现的ping二、ping命令发送端内核实现三、ping命令接收端内核实现沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 ping 命令采用 ICMP 协议,是一个用户空间程序,它打开一个 SOCK_RAW 套接字或者ICMP套接字发送ICMP_…

Chrome启动参数常用参数

Chrome常用参数请参考下表。 序号 参数 说明 1 --allow-outdated-plugins 不停用过期的插件。 2 --allow-running-insecure-content 默认情况下&#xff0c;https 页面不允许从 http 链接引用 javascript/css/plug-ins。添加这一参数会放行这些内容。 3 …

Python爬虫入门教程,BeautifulSoup基本使用及实践

Python爬虫入门教程&#xff0c;BeautifulSoup基本使用及实践 爬虫&#xff0c;是学习Python的一个有用的分支&#xff0c;互联网时代&#xff0c;信息浩瀚如海&#xff0c;如果能够便捷的获取有用的信息&#xff0c;我们便有可能领先一步&#xff0c;而爬虫正是这样的一个工具…

大数据之PySpark的RDD创建和分区

文章目录 前言一、RDD创建二、RDD分区数总结 前言 #博学谷IT学习技术支持# 上篇文章对PySpark的RDD做了简单的介绍&#xff0c;以及总结了RDD的特性&#xff0c;该篇文章主要介绍RDD的创建方式&#xff0c;PySpark的RDD创建方式主要有两种&#xff0c;一种是在程序中直接创建&…

11 dubbo源码学习_dubbo协议通信

dubbo在通信上也支持非常多的网络协议,而dubbo协议属于dubbo框架自研,整体协议也比较有代表性,采用定长协议头+变长协议休的形式; 1. dubbo协议格式 Magic - Magic High & Magic Low (16 bits)标识协议版本号,Dubbo 协议:0xdabbReq/Res (1 bit)标识是请求或响应。请…

「OceanBase 4.1 体验」|OCP Express

文章目录 一、简介二、特性介绍2.1 数据库管理2.2 数据库可观测性 一、简介 OCP Express 是一个基于 Web 的 OceanBase 4.x 轻量化管理工具&#xff0c;作为 OceanBase 数据库的工具组件&#xff0c;它集成在 OceanBase 数据库集群中&#xff0c;支持数据库集群关键性能指标查看…