【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.mzph.cn/news/50836.shtml

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

相关文章

【0824作业】C++ 拷贝赋值函数、匿名对象、友元、常成员函数和常对象、运算符重载

一、思维导图 二、作业&#xff1a;实现关系运算符的重载 关系运算符重载 概念&#xff1a; 种类&#xff1a;>、>、< 、< 、 、!表达式&#xff1a;L#R (L表示左操作数&#xff0c;R表示有操作数&#xff0c;#表示运算符)左操作数&#xff1a;既可以是左值也可以…

tcl学习之路(五)(Vivado时序约束)

1.主时钟约束 主时钟通常是FPGA器件外部的板机时钟或FPGA的高速收发器输出数据的同步恢复时钟信号等。下面这句语法大家一定不会陌生。该语句用于对主时钟的名称、周期、占空比以及对应物理引脚进行约束。 create_clock -name <clock_name> -periood <period> -wa…

学习JAVA打卡第三十八天

String 类的常用方法 ⑴public int length&#xff08;&#xff09; String 类中的length&#xff08;&#xff09;方法获取了一个String对象的字符序列的长度&#xff0c;例如&#xff1a; String china “1945年抗战胜利”&#xff1b; int n1,n2&#xff1b; n1china.leng…

python并发编程

一、程序提速的方法 二、python对并发编程的支持 多线程&#xff1a;threading&#xff0c;利用CPU和IO可以同时执行的原理&#xff0c;让CPU不会干巴巴等待IO完成&#xff1b;多进程&#xff1a;multiprocess&#xff0c;利用多核CPU的能力&#xff0c;真正的并行执行任务&am…

数据结构入门 — 链表详解_单链表

前言 数据结构入门 — 单链表详解* 博客主页链接&#xff1a;https://blog.csdn.net/m0_74014525 关注博主&#xff0c;后期持续更新系列文章 文章末尾有源码 *****感谢观看&#xff0c;希望对你有所帮助***** 系列文章 第一篇&#xff1a;数据结构入门 — 链表详解_单链表 第…

pycharm远程连接docker容器

pycharm远程连接docker容器 1.根据镜像创建容器2.进入容器3.修改容器的root密码4. 容器安装openssh-server和openssh-client5.修改SSH配置文件6.重启ssh服务7. 退出测试8.配置pycharm并连接docker容器9. 选择docker环境 1.根据镜像创建容器 sudo docker run -itd --nameconn_t…

Spark Standalone环境搭建及测试

&#x1f947;&#x1f947;【大数据学习记录篇】-持续更新中~&#x1f947;&#x1f947; 篇一&#xff1a;Linux系统下配置java环境 篇二&#xff1a;hadoop伪分布式搭建&#xff08;超详细&#xff09; 篇三&#xff1a;hadoop完全分布式集群搭建&#xff08;超详细&#xf…

【Unity3D赛车游戏】【三】如何将汽车进入驱动模式——四驱,二驱转换

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

Linux服务——http协议及nginx服务

目录 一、HTTP协议 1、跨网络的主机间通讯方式 套接字相关的系统调用 2、HTTP协议访问网站的过程 3、http协议状态码分类 常见的http协议状态码 4、MIME 5、URL组成 6、HTTP协议版本 7、系统处理http请求的工作模式 8、apache与nginx的区别 二、I/O模型 I/O模型相关…

建议收藏|软考机构推荐看这一篇就够了

需要最近因为软考改革成机考&#xff0c;大家都在问还有没有必要找机构学&#xff1f;本来已经进入自学阶段的考生&#xff0c;也纷纷开始慌张机考改革会不会影响考试难度&#xff1f;今天胖圆给大家总结一下软考要不要报机构&#xff1f;市面上的软考培训机构如何选择&#xf…

使用Xshell7控制多台服务同时安装ZK最新版集群服务

一: 环境准备: 主机名称 主机IP 节点 (集群内通讯端口|选举leader|cline端提供服务)端口 docker0 192.168.1.100 node-0 2888 | 3888 | 2181 docker1 192.168.1.101 node-1 2888 | 388…

共享数据-vue3

vuex方案 安装vuex4.x 两个重要变动&#xff1a; 去掉了构造函数Vuex&#xff0c;而使用createStore创建仓库 为了配合composition api&#xff0c;新增useStore函数获得仓库对象&#xff1b;获取路由对象使用useRouter global state 由于vue3的响应式系统本身可以脱离…

【Python】强化学习:原理与Python实战

搞懂大模型的智能基因&#xff0c;RLHF系统设计关键问答 RLHF&#xff08;Reinforcement Learning with Human Feedback&#xff0c;人类反馈强化学习&#xff09;虽是热门概念&#xff0c;并非包治百病的万用仙丹。本问答探讨RLHF的适用范围、优缺点和可能遇到的问题&#xff…

【腾讯云 TDSQL-C Serverless 产品测评】“橡皮筋“一样的数据库『MySQL高压篇』

【腾讯云 TDSQL-C Serverless 产品测评】"橡皮筋"一样的数据库 活动介绍服务一览何为TDSQL &#xff1f;Serverless 似曾相识&#xff1f; 降本增效&#xff0c;不再口号&#xff1f;动手环节 --- "压力"山大实验前瞻稍作简介资源扩缩范围&#xff08;CCU&…

19篇ICCV 2023自动驾驶精选论文解析 | 涵盖3D目标检测、语义分割、点云等方向

ICCV 2023榜单上月已出&#xff0c;今年共收录了2160篇论文&#xff0c;这次是精选了今年ICCV 2023 会议中自动驾驶相关的最新论文来和大家分享&#xff0c;涵盖了3D目标检测、BEV感知、目标检测、语义分割、点云等方向&#xff0c;共19篇。 论文原文以及开源代码文末领取&…

8_分类算法-k近邻算法(KNN)

文章目录 1 KNN算法1.1 KNN算法原理1.2 KNN过程1.3 KNN三要素1.4 KNN分类预测规则1.5 KNN回归预测规则1.6 KNN算法实现方式&#xff08;重点&#xff09;1.7 k近邻算法优缺点 2 KD-Tree2.1 KD Tree构建方式2.2 KD Tree查找最近邻2.3 KNN参数说明 1 KNN算法 定义&#xff1a;如…

开悟Optimization guide for intermediate tracks

目录 认识模型 参考方案&#xff08;按模块拆解&#xff09; 认识模型 模型控制1名英雄进行镜像1 v 1对战 Actor集群资源为64核CPU 问题特点&#xff1a;单一公平对抗场景&#xff08;同英雄镜像对赛&#xff09;&#xff0c;单位时间样本产能低&#xff0c;累计训练资源相…

基于OpenCV实战(基础知识一)

目录 简介 1.计算机眼中的图像 2.图片的读取、显示与保存 3.视频的读取与显示 简介 OpenCV是一个流行的开源计算机视觉库&#xff0c;由英特尔公司发起发展。它提供了超过2500个优化算法和许多工具包&#xff0c;可用于灰度、彩色、深度、基于特征和运动跟踪等的图像处理和…

JMeter性能测试(中)

断言 让程序自动判断预期结果和实际结果是否一致。 响应断言 例子2&#xff1a;可以在同一个HTTP请求下包含多个响应断言 例子1&#xff1a; 步骤; 添加线程组添加HTTP请求添加响应断言 测试字段&#xff1a;要检查的项&#xff08;实际结果&#xff09;模式匹配规则&#…

iFluor 594 Styramide是一种荧光染料,常用于生物分子标记和成像

试剂 | 基础知识概述&#xff08;部分&#xff09;: 中文名称&#xff1a;Alexa Fluor 594酪Styramide 分子量&#xff1a;1341.71 胺的优异替代品 100 Slides 英文名称&#xff1a;iFluor 594 Ex (nm)&#xff1a;588 Em (nm)&#xff1a;604 规格标准&#xff1a;1g&am…