C语言:扫雷小游戏
文接上一篇博文C语言:三子棋小游戏。本篇博文是使用C语言来实现扫雷小游戏的。这里不对扫雷的规则进行赘述。玩家通过键盘输入坐标来探雷。博主在实现扫雷之前从未看过扫雷实现的相关视频,所以这里实现的扫雷完全是博主的原生思路,具有逻辑性。下面详细介绍一下如何实现扫雷。
(建议在阅读过上一篇博文再阅读本文,因为再本文中对重复的问题不会再次解读。)
步骤一:制定框架
框架是什么?如何制定框架?这些问题在三子棋的实现中就已经解答了,这里也不多讲。这里的框架与三子棋的框架完全相同。
#include"detect.h"
int main()
{srand((unsigned)time(NULL));//这里设置了随机种子,为了之后随机生成雷int n = 0;do{menu();printf("your choice:\n");scanf("%d", &n);switch (n){case 1:system("cls");game();break;case 0:system("cls");printf("exit game\n");break;default:printf("input error,again:\n");Sleep(1000);system("cls");}} while (n);return 0;
}
menu是什么在三子棋实现中也提过,这里使用了Sleep函数(程序暂停运行多少毫秒)和system("cls")(清屏)是为了更好的游戏体验。这里的detect.h代码如下:
#pragma once
#define ROW 10
#define COL 10
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
void menu();
void game();
步骤二:实现game函数
跟三子棋的game函数一样,我们需要事先想好整个game的流程。
分析过程:既然有雷,我们需要将雷的位置记录下来,那我们就需要一个字符二维数组,雷用大写字符'O'表示,雷的位置就初始化为'O',其他位置为空格。另外,开局时我们一个地方都没探测,我们难道要将带雷的数组直接打印上去吗?显然这样雷就直接被打印出来了。我们可以想到用第二个字符二维数组,里面初始时全是空格字符,这个数组是用来打印的。我们在这个数组中进行探测。
结束条件:如果探的是雷,那么直接宣告失败,结束本次游戏;如果这一次避开了所有的雷,那么直接宣告成功,结束本次游戏;如果这一次没探到雷而且没有结束,那么显示此处旁边雷的个数
那我们怎么知道探出的是不是雷?其实很简单,在数组一中此处如果是雷,说明这次探到雷了
下面是game的代码
void game()
{char map1[ROW][COL] = { 0 };char map2[ROW][COL] = { 0 };mapInit(map1, ROW, COL);//真正存储炸弹mapInit(map2, ROW, COL);//用来打印BoomInit(map1, ROW, COL);system("cls");showMap(map2, ROW, COL);while (1){int ret=playerMove(map1, map2, ROW, COL,ROW*COL/8);if (ret==1)//炸了{system("cls");showMap(map1, ROW, COL);printf("game over\n");system("pause");break;}else if (ret== 2)//排除了所有炸弹{system("cls");showMap(map1, ROW, COL);//展示所有炸弹位置printf("detect successfully\n");Sleep(2000);break;}system("cls");showMap(map2, ROW, COL);}
}
当然只是代码还是很模糊的,下面依然需要对game中的各个部分进行讲解。
map1:即数组一,用来存放炸弹的
map2:即数组二,用来打印的
ROW,COL:宏定义,这个宏定义在"detect.h"中,之前已经给出
mapInit:用来初始化两个数组,将两个数组的每一个元素变成空格字符
BoomInit:用来将map1的随机位置放上炸弹
showMap:将map2打印出来(连带格子的线条,之后会详细实现)
playerMove:返回值为int类型,玩家在map2中的一个位置进行探测,如果在map1中相应位置是炸弹就返回1,避开所有炸弹就返回2,其他情况返回0;
步骤三:实现game中的函数
void mapInit(char map[ROW][COL], int row, int col)
{for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){map[i][j] = ' ';}}
}//这个函数很简单,不作讲解
void BoomInit(char map[ROW][COL], int row, int col)
{int boomNum = row * col / 6;//6分之一是炸弹//设置炸弹进mapint curNum = 0;int x = 0;int y = 0;while (curNum != boomNum){x = rand() % row;y = rand() % col;if (map[x][y] == ' ')//如果是空格才能正常放入,如果已经是炸弹就不放,重新生成一个坐标{map[x][y] = 'O';curNum++;}}
}void showMap(char map[ROW][COL], int row, int col)
{for (int i = 0; i < row; i++){for (int j = 0; j < col; j++){printf(" %c ", map[i][j]);if (j < col - 1)printf("|");}printf("\n");if (i < row - 1){for (int x = 0; x < col; x++){printf("---");if (x < col - 1)printf("|");}printf("\n");}//这一段其实是将画格子线和棋子一并画出,//需要自己动手操作一下才能明白这段代码每一句是在做什么//不动手再怎么讲都不会弄明白}
}
//warning函数是为了完成playerMove函数而写的,请先看playerMove函数
char warning(char map[ROW][COL], int row, int col, int x, int y)
{int countBoom = 0;//最大为8,加上'0'变成字符for (int i = x - 1; i <= x + 1;i++){for (int j = y - 1; j <= y + 1; j++){if (i >= 0 && i <= row && j >= 0 && j <= col)//越界了就不判断是不是雷{if (map[i][j] == 'O')countBoom++;}}}return countBoom +'0';
}
int playerMove(char map1[ROW][COL],char map2[ROW][COL], int row, int col,int boomNum)
{int x = 0;int y = 0;static count = 0;//静态局部变量,记录用户一共开了几个格子while (1){printf("input x:\n");scanf("%d", &x);printf("input y:\n");scanf("%d", &y);x--;y--;//用户输入的1就是第一个位置,数组下标就是0if (x < 0 || x >= row || y < 0 || y >= col || map2[x][y] != ' '){printf("wrong place,again\n");//非法坐标,重新输入}else{break;//合法,退出循环}}//如果探到雷了,map2的该位置就改成'O',没探到雷就显示旁边有几个雷//这里的warning函数就是用来给出map1的相应位置旁边有几个雷,返回值是char类型map2[x][y] = map1[x][y]=='O'?'O':warning(map1,ROW,COL,x,y);if (map2[x][y] == 'O')return 1;//探到雷了,返回1else//判断是否排除完{count++;if (count == col * row - boomNum){count = 0;//归零,下一次进行游戏count还是从0开始计数,否则count还是之前的值return 2;//避开了所有的雷,返回2}}return 0;//正常进行下一次探测,返回0
}
到这里为止,所有的工作就完成了,我们来看看效果
测试:

配合清屏和睡眠函数效果还是不错的,只是和原版的扫雷少了一个功能。我们知道,原版的扫雷在探到一个格子旁边没有一个炸弹时,也就这里实现的扫雷显示'0'时,会自动将旁边的格子显示出来,只是这个功能实现起来有点困难。当时想过用递归解决这个问题,就是说如果map2中探到一个0雷格,将旁边八个格子打开,如果这八个格子还有0雷格,就会进行递归,继续将旁边的格子打开,只是这样会有一个问题,0雷格挨在一起的话会陷入死递归。个人感觉这个问题使用递归是最好解决的,之后想到了解决方案会更新博文。
相关文章:

C语言:扫雷小游戏
文接上一篇博文C语言:三子棋小游戏。本篇博文是使用C语言来实现扫雷小游戏的。这里不对扫雷的规则进行赘述。玩家通过键盘输入坐标来探雷。博主在实现扫雷之前从未看过扫雷实现的相关视频,所以这里实现的扫雷完全是博主的原生思路,具有逻辑性…...

VScode SSH无法免密登录
配置方法 引用高赞贴:点击 debug方法 连不上需要找到问题原因,看ssh的 log Linux服务器:2222是我们指定的端口,可以是1234等 sudo /usr/sbin/sshd -d -p 2222windows这边:端口号要一致 ssh -vvv ubuntusername192…...

Spring Cloud--从零开始搭建微服务基础环境【四】
😀前言 本篇博文是关于Spring Cloud–从零开始搭建微服务基础环境【四】,希望你能够喜欢 🏠个人主页:晨犀主页 🧑个人简介:大家好,我是晨犀,希望我的文章可以帮助到大家,…...

FreeRTOS操作系统中,断言输出 Error:..\..\FreeRTOS\portable\RVDS\ARM_CM4F\port.c,766 原因
分析:Error:..\..\FreeRTOS\portable\RVDS\ARM_CM4F\port.c,766 出现这个原因表示,你现在系统某个中断的优先级高于FreeRTOS可管理的优先级范围,一旦你这个中断触发,断言的信息即你串口就会输出这个条语句(前提你串口…...
【Linux】进程间通信与同步
IPC进程间通信 无名管道pipe(血缘关系的进程)有名管道fifo (无血缘关系的进程)共享内存信号(开销小)消息队列信号量套接字 进程间同步 文件锁信号量...

SpringBoot 统一功能处理
目录 一、用户登录权限验证 1.1 SpringAOP可以进行处理吗? 1.2 创建自定义拦截器 1.3 将自定义拦截器配置到系统配置项中 1.4 拦截器的实现原理 1.4.1 实现原理源码分析 1.5 统一访问前缀添加 二、统一异常处理 2.1 为什么需要使用统一异常处理?…...
解决:sh: vite: command not found
文章目录 问题描述原因分析解决方案 问题描述 第一次pull项目,运行npm run dev时报错:sh: vite: command not found 原因分析 查看了package.json,发现是有vite的。 没有安装依赖导致的; 解决方案 执行npm i重新安装依赖&#…...

el-select下拉多选框 el-select 设置默认值不可删除功能
Element3.0vue3.0 el-select下拉多选框 el-select 设置默认值不可删除功能 Element-UI是一款广泛使用的Vue.js组件库,其中El-Select下拉多选框组件在实际项目开发中经常被使用。然而,在Element 3.0版本中,El-Select下拉多选框默认值可被删除&…...

Jetsonnano B01 笔记1:基础理解—网络配置—远程连接
今日开始学习 Jetsonnano B01,这是一台小电脑,可以用来: 运行现代 AI 负载,并行运行多个神经网络,以及同时处理来自多个高清传感器的数据,可广泛应用与图像分类、对象检测、图像分割、语音处 理等领域。它…...
Ubuntu系统信息查看指南:了解你的操作系统
要查看Ubuntu系统的信息,你可以使用一些命令行工具来获取系统的各种信息。以下是一些常用的命令: 1. lsb_release -a:列出Ubuntu版本号、发行代号和描述信息。 2. uname -a:显示Linux内核的版本信息。 3. lscpu:提供…...

【STM32】学习笔记-SPI通信
SPI通信 SPI通信(Serial Peripheral Interface)是一种同步的串行通信协议,用于在微控制器、传感器、存储器、数字信号处理器等之间进行通信。SPI通信协议需要使用4个线路进行通信:时钟线(SCLK)、主输入/主输出线(MISO)、主输出/主…...

解决vue项目首行报红( ESLint 配置)和新建的vue文件首行报红问题
目录 前情提要: 修改ESLint 配置 新建的vue文件首行还是报红 报红原因: 解决方法: 前情提要: 在网上查到的方法可能是在package.json文件或者.eslintrc.js文件中添加 requireConfigFile: false 如果此方法对你的错误不起作用…...

Linux 调试技术 Kprobe
目录 用途:一、技术背景1.1 kprobes的特点与使用限制1.2 kprobe原理 二、 基于kprobe探测模块的探测方式2.1、struct kprobe结构体2.2 kprobe API函数2.3 示例代码参考资料: 用途: 判断内核函数是否被调用,获取调用上下文、入参以…...

一文了解评估 K8s 原生存储产品需要关注的关键能力
近些年,越来越多的企业使用 Kubernetes(K8s)支持生产环境关键业务。这些业务往往对存储性能和稳定性具有更高的要求,传统存储方案难以充分满足,因此不少用户开始关注更契合 K8s 环境的 K8s 原生存储方案。 不过&#…...

linux免密登录报错 Bad owner or permissions on /etc/ssh/ssh_config.d/05-redhat.conf
问题:权限不对的 解决: 1.检查文件的所有者和权限。 确保文件的所有者是正确的。 运行以下命令来确定文件的所有者和权限: ls -l /etc/ssh/ssh_config.d/05-redhat.conf 通常情况下,SSH配置文件应该属于root用户。如果所有者不是…...
Kafka常用参数
文章目录 概要broker端参数producer端参数consumer端参数 概要 kafka broker、consumer、和producer都有很多可配置的参数。本文主要总结日常开发中常用到的参数。其中producer端可以在org.apache.kafka.clients.producer.ProducerConfig 中找到配置项,consumer端可…...

NFT Insider#105:The Sandbox即将参加韩国区块链周,YGG的声誉和进步(RAP)将引领玩家晋升到下一层级
引言:NFT Insider由NFT收藏组织WHALE Members(https://twitter.com/WHALEMembers)、BeepCrypto(https://twitter.com/beep_crypto)联合出品,浓缩每周NFT新闻,为大家带来关于NFT最全面、最新鲜、最有价值的讯息。每期周…...
TCP socket error (The proxy type is invalid for this operation).
“TCP socket error (The proxy type is invalid for this operation)” 错误通常是由于使用了无效的代理类型导致的。在使用QModbusTcpClient连接Modbus TCP设备时,如果您没有配置代理服务器,或者配置的代理类型不正确,就会出现这个错误。 …...
根据需求生成一个Vue模块的类图示例
以下是一个Vue模块的类图示例: ------------------------ | VueModule | ------------------------ | -name: string | | -data: object | | -methods: object | | -computed: object | | -watchers: object | ---…...
C# 类class、继承、多态性、运算符重载,相关练习题
34.函数重载 /*函数重载您可以在同一个范围内对相同的函数名有多个定义。函数的定义必须彼此不同,可以是参数列表中的参数类型不同,也可以是参数个数不同。不能重载只有返回类型不同的函数声明。下面的实例演示了几个相同的函数 Add(),用于对…...

第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具
作者:来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗?了解下一期 Elasticsearch Engineer 培训的时间吧! Elasticsearch 拥有众多新功能,助你为自己…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
Java + Spring Boot + Mybatis 实现批量插入
在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法:使用 MyBatis 的 <foreach> 标签和批处理模式(ExecutorType.BATCH)。 方法一:使用 XML 的 <foreach> 标签ÿ…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...