【C语言】扫雷游戏(可展开)——超细教学

🚩纸上得来终觉浅, 绝知此事要躬行。
🌟主页:June-Frost
🚀专栏:C语言

🔥该篇将运用数组来实现 扫雷游戏。

目录:

  • 🌟思路框架
    • 测试
    • 游戏
  • 🌟测试部分函数实现
  • 🌟 游戏部分函数实现
  • 🌟完整的代码
  • ❤️ 结语

🌟思路框架

测试

通过迭代保证每次玩完游戏后可以再来一局或者退出。


游戏

说明:

1 .  实现扫雷,首先需要布置雷,通过一个二维数组就可以很容易的将雷的信息存储下来。只需要将二维数组的元素赋予两个不同的值,例如,’1’ ——雷,’0‘ ——无雷,或者 ‘#’ ——雷,’ * ’ ——无雷,只不过这两种在后续排查雷中操作不同(通过一个循环前者可以加起来计算出周围有几个雷,后者可以通过比较来判定有多少雷)。


 如上图,对于 排查雷(周围有多少雷),如果只有一个数组,那么在周围如果只有一个雷的情况下(’1‘——表示雷,’0‘——表示无雷),该位置的数组元素就会被赋值为1,在最后展示棋盘的时候,这个1就会造成歧义,就会不知道这个’1‘究竟是雷,还是周围雷的个数。
所以,通过建立另一个数组,来记录周围雷的数量。
如图:

3.

但是我们可以将其逻辑统一:

  1.  为了简便操作,我们将两个数组规定为同类型同大小(两个都扩充一圈)。
  • 优点1:可以方便数组操作,下标一样,不需要重新计算对应数组元素的下标。
  • 优点2:同类型意味着可以使用同一个函数,不需要一个功能写两次。

每个部分都是单独的函数体,下面将会单独对每个函数体进行实现。


🌟测试部分函数实现

  • 选择是否玩游戏

 与三子棋一样,该板块的循环部分将会使用do while 循环来实现(保证一开始可以选择,运行完游戏部分后还可以继续选择)。

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"int main()
{int input = 0;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;
}

🌟 游戏部分函数实现

 首先我们根据游戏部分的思路框架,将整个游戏的逻辑写在game()函数中。
 前提:

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

定义两种,可以在后续分别用于不同的函数。

  • 游戏逻辑
void game()
{char mine[ROWS][COLS] = { 0 };//存放雷char show[ROWS][COLS] = { 0 };//存放排查出的雷的信息//初始化InitBoard(mine, ROWS, COLS, '0');//都先初始化为无雷InitBoard(show, ROWS, COLS, '*');//布置雷SetMine(mine, ROW, COL);//打印第二个棋盘DisplayBoard(show, ROW, COL);//排查雷FineMine(mine, show, ROW, COL);
}
  • 初始化

 由于想使用一个函数就可以将两个棋盘初始化,所以在参数上需要增加一个变量用于接收初始化的值。

void InitBoard(char board[ROWS][COLS], int row, int col, char set)
{int i = 0;for (i = 0; i < row; i++){int j = 0;for (j = 0; j < col; j++){board[i][j] = set;}}
}
  • 布置雷

 前提:#define Set_Mine 10;//需要布置雷的数量
 布置雷是随机的,所以我们采用伪随机数,将下标在合法范围内随机,如果该坐标下是无雷的,就赋值‘1’,定为有雷。

void SetMine(char board[ROWS][COLS], int row, int col)
{int count = Set_Mine;while (count){int x = rand() % row + 1;int y = rand() % col + 1;if (x >= 1 && x <= row && y >= 1 && y <= col){if (board[x][y] == '0'){board[x][y] = '1';count--;//布置成功,雷数减少1}}}
}
  • 打印棋盘

 为了方便玩家定位坐标,在打印棋盘的时候,可以将行和列打印出来。并且展示出来的,是中间的区域(不展示扩容区域)

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{printf("--------扫雷游戏-------\n");int i = 0;for (i = 0; i <= col; i++)//打印列{printf("%d ", i);}printf("\n");for (i = 1; i <= row; i++){printf("%d ", i);//打印行int j = 0;for (j = 1; j <= col; j++){printf("%c ", board[i][j]);}printf("\n");}
}
  • 排查雷

计数:

int GetMineCount(char board[ROWS][COLS], int x, int y)
{int i = x - 1;int j = y - 1;int ret = 0;for (i = x - 1; i <= x + 1; i++){for (j = y - 1; j <= y + 1; j++){ret += board[i][j];}}return ret - 9 * '0';
}

排雷:

void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col)
{int win = 0;//如果要赢 需要的有检查的次数,例如:9*9棋盘,有10个雷,检查出71个地区不是雷,就意味着获胜。while (win<ROW*COL-Set_Mine){int x = 0;int y = 0;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{Unfold(mine,show, ROW, COL, x, y,&win);//递归展开DisplayBoard(show, ROW, COL);//展示棋盘}}else{printf("输入非法,需要重新输入\n");}}if (win == ROW * COL - Set_Mine){printf("游戏获胜\n");DisplayBoard(mine, ROW, COL);}
}

展开:

如何实现上面的展开效果呢?
当我们点击一个坐标后,看是否满足3个条件:
1.这个坐标不是雷(如果按照博主的排雷逻辑,这个条件不需要判断,因为坐标下不是雷的时候才会调用展开函数)
2.周围没有雷
3.没有被递归过(防止死递归)
 这时被点击的地方就可以被赋值为空,并对周围的8个区块看是否符合这样的条件,8个区块也按照这个逻辑,如果满足三个条件,被赋空并且遍历周围,如果不满足就显示周围有多少雷,或者不做任何反应,按照这样的逻辑一直延续下去。这种情况我们可以使用递归去解决。

void Unfold(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col,int x,int y, int* count_w)
{if (x >= 1 && x <= row && y >= 1 && y <= col)//判断合法{if (!GetMineCount(mine, x, y) && show[x][y] != ' ')//周围没有雷并且该位置没有被递归过{show[x][y] = ' ';int i = 0;int j =0;int ret = 0;//直接遍历9个,至于中间的那个坐标,最后一个else if 会处理for (i = x - 1; i <= x + 1; i++){for (j = y - 1; j <= y + 1; j++){Unfold(mine, show, row,col,i, j, count_w);(* count_w)++;}}}else if (GetMineCount(mine, x, y))//周围有雷{int count = GetMineCount(mine, x, y);show[x][y] = count + '0';(* count_w)++;}else if (show[x][y] == ' ')//被递归过{;//不需要操作}}//不合法的函数不需要任何处理
}

这里需要判断合法性,因为扩容出去的那一圈的元素不可以递归,而且也不可以计算 (不然按照GetMineCount的逻辑会越界访问,而且就会可以计算出,也不会打印这个区域,所以可以直接不用管,递归这些地方的时候,函数会什么都不处理)。


🌟完整的代码

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 Set_Mine 10//需要布置雷的数量
//游戏逻辑
void game();
//初始化
void InitBoard(char board[ROWS][COLS], int row, int col, char set);
//埋雷
void SetMine(char board[ROWS][COLS], int row, int col);
//展示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col); 
//排雷
void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

game .c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//菜单
void menu()
{printf("***********************\n");printf("*****   1. play   *****\n");printf("*****   0. exit   *****\n");printf("***********************\n");
}//初始化
void InitBoard(char board[ROWS][COLS], int row, int col, char set)
{int i = 0;for (i = 0; i < row; i++){int j = 0;for (j = 0; j < col; j++){board[i][j] = set;}}
}
//布置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{int count = Set_Mine;while (count){int x = rand() % row + 1;int y = rand() % col + 1;if (x >= 1 && x <= row && y >= 1 && y <= col){if (board[x][y] == '0'){board[x][y] = '1';count--;//布置成功,雷数减少1}}}
}
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{printf("--------扫雷游戏-------\n");int i = 0;for (i = 0; i <= col; i++)//打印列{printf("%d ", i);}printf("\n");for (i = 1; i <= row; i++){printf("%d ", i);//打印行int j = 0;for (j = 1; j <= col; j++){printf("%c ", board[i][j]);}printf("\n");}
}
//计数
int GetMineCount(char board[ROWS][COLS], int x, int y)
{int i = x - 1;int j = y - 1;int ret = 0;for (i = x - 1; i <= x + 1; i++){for (j = y - 1; j <= y + 1; j++){ret += board[i][j];}}return ret - 9 * '0';
}//展开
void Unfold(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col,int x,int y, int* count_w)
{if (x >= 1 && x <= row && y >= 1 && y <= col){if (!GetMineCount(mine, x, y) && show[x][y] != ' ')//周围没有雷并且该位置没有被递归过{show[x][y] = ' ';int i = 0;int j =0;int ret = 0;for (i = x - 1; i <= x + 1; i++){for (j = y - 1; j <= y + 1; j++){Unfold(mine, show, row,col,i, j, count_w);(* count_w)++;}}}else if (GetMineCount(mine, x, y))//周围有雷{int count = GetMineCount(mine, x, y);show[x][y] = count + '0';(* count_w)++;}else if (show[x][y] == ' ')//被递归过{;}}
}
//排雷
void FineMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row,int col)
{int win = 0;while (win<ROW*COL-Set_Mine){int x = 0;int y = 0;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{Unfold(mine,show, ROW, COL, x, y,&win);//递归展开DisplayBoard(show, ROW, COL);//展示棋盘}}else{printf("输入非法,需要重新输入\n");}}if (win == ROW * COL - Set_Mine){printf("游戏获胜\n");DisplayBoard(mine, ROW, COL);}
}//游戏逻辑
void game()
{char mine[ROWS][COLS] = { 0 };//存放雷char show[ROWS][COLS] = { 0 };//存放排查出的雷的信息//初始化InitBoard(mine, ROWS, COLS, '0');//都先初始化为无雷InitBoard(show, ROWS, COLS, '*');//布置雷SetMine(mine, ROW, COL);//打印第二个棋盘DisplayBoard(show, ROW, COL);DisplayBoard(mine, ROW, COL);//排查雷FineMine(mine, show, ROW, COL);
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"int main()
{int input = 0;srand((unsigned)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.rhkb.cn/news/103607.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

23种设计模式攻关

&#x1f44d;一、创建者模式 &#x1f516;1.1、单例模式 单例模式&#xff08;Singleton Pattern&#xff09;&#xff0c;用于确保一个类只有一个实例&#xff0c;并提供全局访问点。 在某些情况下&#xff0c;我们需要确保一个类只能有一个实例&#xff0c;比如数据库连接…

Qt+Pyhton实现麒麟V10系统下word文档读写功能

目录 前言1.C调用python1.1 安装Python开发环境1.2 修改Qt工程配置1.3 初始化Python环境1.4 C 调用Python 函数1.5 常用的Python接口 2.python虚拟环境2.1Python虚拟环境简介2.2 virtualenv 安装及使用2.3 在C程序中配置virtualenv 虚拟环境 3.python-docx库的应用4.总结 前言 …

WPF中手写地图控件(3)——动态加载地图图片

瓦片增加一个Loading动画 可以查看我的另一个博客WPF中自定义Loading图 从中心扩散 进行从里到外的扩散&#xff0c;方向是上左下右。如下图所示 于是我们可以定义一个拥有坐标X跟Y的集合&#xff0c;他允许这个集合&#xff0c;内部使用枚举器的MoveNext自动排序&#xf…

文件容灾备份方案,软件容灾备份方案

信息是企业的核心资产。然而&#xff0c;信息数据丢失的风险接踵而至。事故系统异常、病毒攻击、硬件损坏和自然灾害都可能导致重要数据的丢失。这就是为什么文档灾难恢复备份计划如此重要。本文将详细介绍文档灾难恢复备份计划的必要性&#xff0c;以及如何实施有效的备份方案…

使用Nodejs搭建简单的HTTP服务器 - 内网穿透公网远程访问

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址&#x1f340;小结&#x1f340; &#x1f389;博客主页&#xff1a;小智_x0___0x_ &#x1f389;欢迎关注&#xff1a;&…

LLM架构自注意力机制Transformers architecture Attention is all you need

使用Transformers架构构建大型语言模型显著提高了自然语言任务的性能&#xff0c;超过了之前的RNNs&#xff0c;并导致了再生能力的爆炸。 Transformers架构的力量在于其学习句子中所有单词的相关性和上下文的能力。不仅仅是您在这里看到的&#xff0c;与它的邻居每个词相邻&…

韦东山-电子量产工具项目:UI系统

代码结构 所有代码都已通过测试跑通&#xff0c;其中代码结构如下&#xff1a; 一、include文件夹 1.1 common.h #ifndef _COMMON_H #define _COMMON_Htypedef struct Region {int iLeftUpX; //区域左上方的坐标int iLeftUpY; //区域左下方的坐标int iWidth; //区域宽度…

如何在 Opera 中启用DNS over HTTPS

DNS over HTTPS&#xff08;基于HTTPS的DNS&#xff09;是一种更安全的浏览方式&#xff0c;但大多数 Web 浏览器默认情况下不启用它。了解如何在 Opera 浏览器中启用该功能。 您可能不知道这一点&#xff0c;但您的网络浏览器并不像您希望的那样私密或安全。您会看到&#xff…

搭建AI智能问答的这些前期工作可不要忘了

在搭建AI智能问答之前&#xff0c;我们需要做好相应的前期准备工作&#xff0c;不能盲目地去搭建模型。这样很容易导致我们模型后续的不完整性。所以looklook今天就基于搭建AI智能问答最基础的思路&#xff0c;带领大家一起去理顺一下我们需要做什么前期工作才能保证AI智能问答…

Web 开发 Django 管理工具

上次为大家介绍了 Django 的模型&#xff0c;通过模型就可以操作数据库&#xff0c;从而就可以改变页面的展示内容&#xff0c;那问题来了&#xff0c;我们只能通过手动编辑模型文件来配置模型吗&#xff1f;当然不是&#xff0c;Django 为我们提供了强大的工具&#xff0c;可以…

0101prox-shardingsphere-中间件

1 启动ShardingSphere-Proxy 1.1 获取 目前 ShardingSphere-Proxy 提供了 3 种获取方式&#xff1a; 二进制发布包DockerHelm 这里我们使用Docker安装。 1.2 使用Docker安装 step1&#xff1a;启动Docker容器 docker run -d \ -v /Users/gaogzhen/data/docker/shardings…

安装Node(脚手架)

目录 一&#xff0c;安装node&#xff08;脚手架&#xff09;1.1&#xff0c; 配置vue.config.js1.2&#xff0c; vue-cli3x的目录介绍1.3&#xff0c; package.json 最后 一&#xff0c;安装node&#xff08;脚手架&#xff09; 从官网直接下载安装即可&#xff0c;自带npm包管…

OpenCV基础知识(5)— 几何变换

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。OpenCV中的几何变换是指改变图像的几何结构&#xff0c;例如大小、角度和形状等&#xff0c;让图像呈现出缩放、翻转、旋转和透视效果。这些几何变换操作都涉及复杂、精密的计算。OpenCV将这些计算过程都封装成了非常灵活的…

自动化编排工具Terraform介绍(一)

Terraform是什么&#xff1f;: Terraform 是 HashiCorp 公司旗下的 Provision Infrastructure 产品, 是 AWS APN Technology Partner 与 AWS DevOps Competency Partner。Terraform 是一个 IT 基础架构自动化编排工具&#xff0c;它的口号是“Write, Plan, and Create …

优化时间流:区间调度问题的探索与解决

在浩如烟海的信息时代&#xff0c;时间的有效管理成为了一门不可或缺的艺术。无论是生活中的琐事&#xff0c;还是工作中的任务&#xff0c;时间都在无声地流逝&#xff0c;挑战着我们的智慧。正如时间在日常生活中具有的宝贵价值一样&#xff0c;在计算机科学领域&#xff0c;…

3:Ubuntu上配置QT交叉编译环境并编译QT程序到Jetson Orin Nano(ARM)

1.Ubuntu Qt 配置交叉编译环境 1.1 ubuntu 20.04安装Qt sudo apt-get install qtcreator 1.2 配置QT GCC配置同上 最后配置Kits 上面设置完成之后 &#xff0c;设置Kits 中的Device(这是为了能够直接把项目部署到arm设备上) 点击NEXT之后会出现连接被拒绝&#xff0c;不用担…

USB Type-C端口集成式ESD静电保护方案 安全低成本

Type-C端口是根据USB3.x和USB4协议传输数据的&#xff0c;很容易受到电气过载&#xff08;EOS&#xff09;和静电放电&#xff08;ESD&#xff09;事件的影响。由于Type-C支持随意热插拔功能&#xff0c;其内部高集成度的芯片&#xff0c;更容易受到人体静电放电的伤害和损坏。…

Golang使用消息队列(RabbitMQ)

最近在使用Golang做了一个网盘项目&#xff08;类似百度网盘&#xff09;&#xff0c;这个网盘项目有一个功能描述如下&#xff1a;用户会删除一个文件到垃圾回收站&#xff0c;回收站的文件有一个时间期限&#xff0c;比如24h&#xff0c;24h后数据库中记录和oss中文件会被删除…

使用端口映射实现Spring Boot服务端接口的公网远程调试:详细配置与步骤解析

文章目录 前言1. 本地环境搭建1.1 环境参数1.2 搭建springboot服务项目 2. 内网穿透2.1 安装配置cpolar内网穿透2.1.1 windows系统2.1.2 linux系统 2.2 创建隧道映射本地端口2.3 测试公网地址 3. 固定公网地址3.1 保留一个二级子域名3.2 配置二级子域名3.2 测试使用固定公网地址…

使用 Feature Flags 与可观测工具实现数据库灰度迁移

场景描述 很多企业会遇到数据库升级、或数据库迁移的情况&#xff0c;尤其是在自建数据库服务向云数据库服务、自建机房向云机房、旧数据库向新数据库迁移等场景。 然而&#xff0c;我们需要在整个移植过程中保证其稳定性、避免数据遗失、服务宕机等情况&#xff0c;最常见的移…