C语言 #具有展开功能的排雷游戏

文章目录

前言

一、整个排雷游戏的思维梳理

二、整体代码分布布局

三、游戏主体逻辑实现--test.c

四、整个游戏头文件的引用以及函数的声明-- game.h

五、游戏功能的具体实现 -- game.c

六、老六版本 

总结


前言

路漫漫其修远兮,吾将上下而求索。


一、整个排雷游戏的思维梳理

当玩家点开游戏,就会出现菜单供玩家进行选择,是玩游戏还是退出游戏(当然,你还可以整点有意思的选项,让正经的排雷游戏变得不正经);

当玩家输入自己的选择后,要么退出游戏,要么进入排雷游戏;当玩家进入排雷游戏时,就要先给玩家展示一下棋盘,让玩家输入想要排查的坐标,计算机再判断玩家输入的这个坐标是不是'雷',如果是雷,游戏结束;当然菜单会再出现一次,玩家可自行判断要不要再来一局游戏;如果不是雷就要向玩家展示此坐标周围雷的个数,并且玩家会一直输入坐标进行排雷直到玩家踩到雷或者玩家排雷成功;

感觉不难,让我们一起来实现吧~

二、整体代码分布布局

明确分工,提高效率~

test.c   //整个游戏的主题逻辑

game.c  //游戏功能的具体实现

game.h   //包含一些头文件以及函数进行函数声明

三、游戏主体逻辑实现--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, '*');//电脑随机放雷SetMine(mine, ROW, COL);//向玩家展示所要排雷的棋盘DisplayBoard(show, ROW, COL);//玩家排雷FindMine(mine, show, ROW, COL);
}
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");}} while (input);return 0;
}

注意:

1、 为什么创建了两个数组,并且都是字符数组

由于在 show 展示的棋盘上我们想要用 * 来初始化此棋盘,显然show 棋盘中底层用来存放数据的二维数组就应该是字符数组;如果要将雷的信息放在show 中,那么显示给玩家看也会一览无余雷的位置,所以这里应该创建两个相同的“棋盘”,一是用来专门存放电脑随机分配的雷的信息以及排雷,另外一个就是展示给玩家看的,实现与玩家的互动;那么这两个棋盘在类型与大小上保持一致就会更好操作,当然你也可以不让这两个数组一样,只不过是在处理上会更加麻烦了而已 

2、数组的创建为什么用ROWS 与 COLS创建呢,而展示却是用ROW 与 COL

在头文件game.h 中,ROW ,COL 是我们定义的标识符常量即为9,而ROWS,COLS 在ROW ,COL 的基础上大了2;

创建的棋盘比实际的棋盘的横列均多2 的原因是,在排雷的时候,如果玩家输入的坐标不是雷的话,就要计算该坐标周围雷的个数;如果是计算边缘坐标周围的雷的个数,周围的坐标数都不同,所以为了统一处理,保证实际棋盘中的每个做坐标周围均有8个坐标;(当然你也可以不这样做,头铁的话只有麻烦自己了

3、既然为字符数组是如何求得非雷坐标周围雷的个数的?

例如如果非雷用‘  ’ 来表示,雷用 ‘*’ 来表示,利用循环产生包括此非雷坐标的9个坐标,一个个判断产生的 坐标再判断是不是雷'*' 即可,是雷的话便利用计数器 count++ 即可;本文写的代码中,非雷为 ‘0’ ,雷为 ‘1’ ,由于字符本质上就是ASCII码值,所以也可以利用那么可以用加法与减法的原则来计算周围坐标雷的个数(当然,我用的是循环+计数器);

4、电脑随机放置雷的操作的原理

在此之前,我们得了解一下rand srand time;

rand 与 srand 的头文件 <stdlib.h>

time 的头文件 <time.h>

int rand(void); 库函数rand 没有参数,但是有返回值,其返回值类型为int;

库函数可以生成随机数,但是它是基于算法生成得,故而并不是真正的随机数,是伪随机数;rand 生成伪随机数的范围为 0-32767;

而使用rand 一般是要与srand 一起使用的;srand产生随机变化的数seed(种子), 而rand 是基于srand 产生的数即seed(种子)进行产生伪随机数算法的运算;当使用rand 的程序中没有srand时,rand 便会默认种子为1;

void srand( unsigned int seed) ;库函数的参数类型为  unsigned int,如若想让rand 基于 seed产生随机的值,那么这个seed 就得变化。而什么是变化的呢?聪明的你可能会说是时间!没错,真棒,就是时间;我们可以利用时间戳,即利用库函数 time ;

注:时间戳:当前时间与计算机时间 1970年1月1日午夜之间的差值(单位是秒); 

time_t time (time_t * timer);  

库函数 time 的返回类型为 time_t ,其参数为 time_t* 类型的指针;

即如果正常使用库函数time ,得有指针参数,time 会返回一位 time_t 类型的返回值;如果不想用time 的参数,那么就用空指针NULL;

只要每一次程序运行是 seed 都不同,那么便可以保证 rand 在每一次程序执行时产生的随机值不会有相同;所以将 seed 产生放在main 函数中即可;

5、玩家排雷--展开操作的原理

说“展开”,可能你还不知道是什么意思;你玩排雷游戏的时候,是不是有时会展开一片呢?展开这一片的功能就是此处探讨的展开功能;如下图所示:

展开的格子,是非雷且周围没有雷;--> 这里便包含了两个条件

当周围8个坐标有雷的时候,就不会展开为空格,取而代之的是标明此坐标周围雷的个数;

图解:

分析:当玩家输入一个坐标的时候,首先要判断此坐标是否在排雷对象棋盘的合理范围之内;在合理范围之内便进行上图的操作;会用到三个功能,一是计算一个坐标周围雷的个数,二是展开空格的功能,三是判断玩家输赢;

四、整个游戏头文件的引用以及函数的声明-- game.h

代码如下:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>#define ROW 9
#define COL 9#define ROWS ROW+2
#define COLS COL+2#define MINE_COUNT 10
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set);//电脑随机放雷
void SetMine(char mine[ROWS][COLS], int row, int col);//计算周围八个坐标雷的个数
int ArroundingMine(char mine[ROWS][COLS], int x, int y);//展开功能
void ExtendBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);//玩家排雷是否成功的判断条件
int IsWin(char show[ROWS][COLS], int row, int col);//向玩家展示所要排雷的棋盘
void DisplayBoard(char show[ROWS][COLS], int row, int col);//玩家排雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

分析:

在头文件中实现头文件、函数的声明,以及定义标识符常量,只要源文件声明包含了此头文件,相当于此源文件也声明了一次;

注:1、#define 定义的标识符常量 --> 在全局使用时更易于修改,非常方便;

2、函数一定要做到先声明再使用!

五、游戏功能的具体实现 -- game.c

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

就是二维数组的初始化,两重循环嘎嘎整起来就可以了!

2、电脑随机放雷

//电脑随机放雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{int x = 0;int y = 0;int count = MINE_COUNT;while (count){x = rand() % row + 1;y = rand() % col + 1;//埋雷的范围是1-9if (mine[x][y] == '0'){mine[x][y] = '1';count--;}}
}

雷坐标的产生并不是每一次都可以产生有效的坐标,且雷坐标的个数受雷个数的影响(此处的 MINE_COUNT 就是在头文件中定义的标识符常量--雷的个数--为10),所以此处应该用循环,并且是用while 循环;

看上面的代码,相信聪明的你一定可以理解!

3、向玩家展示所要排雷的棋盘

//向玩家展示所要排雷的棋盘
void DisplayBoard(char show[ROWS][COLS], int row, int col)
{//展示的show 棋盘上的1-9//还要添上坐标int i = 0;int j = 0;for (j = 0; j <= col; j++){printf("%d ", j);}printf("\n");for (i = 1; i <= row; i++){printf("%d ", i);for (j = 1; j <= col; j++){printf("%c ", show[i][j]);}//打印完一行就换行printf("\n");}
}

这个函数就有点讲究了,不仅向玩家展示了棋盘,还加上了索引;记得换行记得换行记得换行!!!重要的事情说三遍!

二维数组在内存中是一块连续的空间,如果想打印出规规整整的棋盘样式,就得在每打印完一行之后,写个printf("\n"); 以实现手动换行;

其次就是加上索引:在每一行前加上行数,以表示此行是第几行;显然就会多一列,那么用一行的数字对应此列为第几列时,就应该从0开始;

注意你的目标,你打印的时 show 数组中1-9 行,1-9 列的数据;

4、计算周围八个坐标雷的个数

//计算周围八个坐标雷的个数
int ArroundingMine(char mine[ROWS][COLS], int x, int y)
{int i = 0;int j = 0;int count = 0;for(i = -1; i <= 1; i++){for (j = -1; j <= 1; j++){if (mine[x + i][y + j] == '1')count++;}}return count;
}

原理前面提过,如果不太理解这里的代码可以往前翻翻,不过相信聪明的你一定是理解的;

5、展开功能

//展开功能
void ExtendBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{//产生该坐标周围八个坐标int i = 0;int j = 0;int count = 0;for (i = x-1; i <= x+1; i++){for (j= y-1; j <= y+1; j++){//避免重查if (show[i][j] == '*'){//不是雷if (mine[i][j] == '0'){count = ArroundingMine(mine, i, j);if (count == 0){show[i][j] = ' ';ExtendBoard(mine, show, i, j);}else{show[i][j] = count + '0';}}}}}
}

基本上就是这张图的意思:

注:此处的show[i][j] = count + '0'; 

因为数组 show 是char 类型的数组,所以其元素是char 类型,然而count = ArroundingMine(mine, i, j); 计算周围坐标雷的个数的函数的ArroundingMine  的返回类型是 int 类型,所以用来接收其返回值的 conut 是 int 类型,想让数字转换成字符,利用ascii码值即可,即加上 '0' ;

6、玩家排雷是否成功的判断条件

//玩家排雷是否成功的判断条件
int IsWin(char show[ROWS][COLS], int row, int col)
{int i = 0;int j = 0;int count = 0;for (i = 1; i <= row; i++){for (j = 1; j <= col; j++){if (show[i][j] == '*')count++;}}return count;
}

7、玩家排雷

    //玩家输入想要排查的坐标
    // 输入的坐标合法
    //避免重查
    //在mine 棋盘上判断该坐标是不是雷;是雷,游戏结束;不是雷,游戏继续;
    //在此坐标不是雷的基础上,计算该坐标周围雷的个数;如果个数为0,先排除重查的坐标,再计算此坐标周围八个坐标的周围八个坐标的雷的个数
    //递归; 如果个数不为0,就在show 棋盘对应的坐标上展示此坐标周围雷的个数
    //循环的结束:当玩家踩到雷或者排雷成功
    //排雷成功的标准:在show 棋盘上剩下的*个数与雷的个数相同

//玩家排雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{int x = 0;int y = 0;int win = 0;int count = 0;win = IsWin(show, ROW, COL);while (win > MINE_COUNT){printf("玩家请输入想要排查的坐标:>");scanf("%d %d", &x, &y);if (x >= 1 && x <= row && y >= 1 && y <= col){if (show[x][y] == '*'){if (mine[x][y] == '1'){printf("哎呦,踩到雷了,游戏结束\n");DisplayBoard(mine, ROW, COL);break;}else{count = ArroundingMine(mine, x, y);if (count == 0){ExtendBoard(mine, show, x, y);}else{show[x][y] = count + '0';}win = IsWin(show, ROW, COL);DisplayBoard(show, ROW, COL);}}else{printf("此坐标已经排查过,请重新输入\n");}}else{printf("输入的坐标不再棋盘的范围内,请重新输入\n");}}if (win == MINE_COUNT){printf("恭喜玩家排雷成功\n");DisplayBoard(mine, ROW, COL);DisplayBoard(show, ROW, COL);}
}

六、老六版本 

1、老娘费尽心思做的小游戏,发给你,你必须给我玩!不玩就让你电脑关机;

2、可以不让你的电脑关机,但是你得喊我 “爸爸” (叉除、忽略均无效反抗,只要你不输入 “爸爸”,都给你关机)

效果如下:

test.c 代码如下:

#include"game.h"void menu()
{printf("****************************\n");printf("*****1、 play **************\n");printf("*****0、 exit **************\n");printf("*****2、 不玩 **************\n");printf("****************************\n");
}
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);//玩家排雷FindMine(mine, show, ROW, COL);
}void punish()
{char input[20] = { 0 };printf("老娘幸幸苦苦做的游戏,你居然不玩\n");Sleep(1000);printf("好好好,必须惩罚你\n");system("shutdown -s -t 60");
again:printf("叫我爸爸,免你电脑不死\n");scanf("%s", &input);if (strcmp(input, "爸爸") == 0){printf("乖,儿子;爸爸这就饶你电脑不死\n");system("shutdown -a");}else{printf("哦呦,有能耐嗦,倒计时了哦~再不喊电脑就关机了喔\n");goto again;}
}
int main()
{int input = 0;do{menu();printf("玩家请输入选择:>");scanf("%d", &input);switch (input){case 1:game();break;case 0:printf("退出游戏成功\n");break;case 2:punish();break;default:printf("输入错误,请重新输入\n");break;}} while (input);return 0;
}

在game.h 文件多引用两头文件即可:

注:在C语言中,有一个system 函数--> 执行系统命令; 其头文件是 <windows.h>

关机指令: shutdown -s -t 60

其中,shutdown 意为关机; -s -->  设置关机   -t --> 设置倒计时关机    60 --> 代表60秒

没查没搜,我是这样记的: -s --> set 设置  -t --> time 时间

取消关机指令 : shutdown -a 

-a -->  abandon 放弃 

当然,利用好 system("shutdown -s -t 60") 和 system("shutdown -a"),你可以更加老六


总结

完,赶快动手试一试吧!

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

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

相关文章

【OSCP系列】OSCP靶机-BTRsys-2.1(原创)

OSCP系列靶机—BTRsys-2.1 原文转载已经过授权 原文链接&#xff1a;Lusen的小窝 - 学无止尽&#xff0c;不进则退 (lusensec.github.io) 一、主机发现 二、端口扫描 1、快速扫描 2、全端口扫描 3、服务系统探测 4、漏洞探测 80端口扫到了一些目录&#xff0c;有wordpress框…

Paimon数据湖详解(第49天)

系列文章目录 一. Paimon数据湖增删改查 二. 查询优化 三. 系统表 四. Lookup Joins 文章目录 系列文章目录前言Paimon数据湖的使用1、创建Table1.1 创建catalog管理的表1.2 分区表1.3 Create Table As&#xff08;了解&#xff09;1.4 Create Table Like1.5 表属性1.6 创建外…

无心剑中译莎士比亚《爱如星辰引迷舟》

莎士比亚十四行诗第116首 Sonnet 116 爱如星辰引迷舟 Let me not to the marriage of true minds Admit impediments. Love is not love Which alters when it alteration finds, Or bends with the remover to remove: O, no! it is an ever-fixed mark That looks on tempe…

C++(week14): C++提高:(一)面向对象设计:设计原则、设计模式

文章目录 一、面向对象设计的概念4.统一建模语言&#xff1a;UML语言StartUML 二、类与类之间的关系0.总结(1)类与类的五种关系(2)区别(3)面向对象 vs 基于对象 1.继承 (泛化耦合)2.组合 (Composition)3.聚合 (Aggregation)4.关联(1)双向关联(2)单向关联 5.依赖 (Dependency) 三…

简单几步,把浏览器书签转换成导航网页

废话不多说直奔主题上干货 Step 1 下载浏览器书签 1&#xff0c;电脑浏览器点击下载Pintree Pintree 是一个开源项目&#xff0c;旨在将浏览器书签导出成导航网站。通过简单的几步操作&#xff0c;就可以将你的书签转换成一个美观且易用的导航页面。 2. 安装 Pintree B…

【保姆级讲解下QT6.3】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

【人工智能 | 机器学习 | 理论篇】线性模型

文章目录 1. 基本形式2. 线性回归3. 对数几率回归4. 线性判别分析5. 多分类学习6. 类别不平衡问题 1. 基本形式 设有 d 个属性描述的示例 x ( x 1 , x 2 , x 3 , . . . , x d ) x ({x_1, x_2, x_3, ..., x_d}) x(x1​,x2​,x3​,...,xd​) 线性模型&#xff08;linear mode…

每天一个设计模式之命令模式(第二天)

交互模式中的命令模式&#xff0c;在开始记录之前&#xff0c;要讲些自己的感受&#xff0c;真真切切的感受到了悟性的瓶颈&#xff01;一共十页书&#xff0c;需要自己细细琢磨品味&#xff0c;至少三四遍才大概了解了他们间的逻辑&#xff0c;我需要调整下自己的学习思路&…

快速写一个Makefile

本文主要展示Makefile的基本要素和示例&#xff0c;让读者可以快速写出一个实用的Makefile。 简要说明 Makefile&#xff0c;GNU make命令工具。 书写格式 <target> : <prerequisites> [tab] <commands> <target> 文件名或某操作的名字&#xff0…

uniapp开发精选短视频视频小程序实战笔记20240725,实现顶部轮播图和热门短剧

创建项目 创建项目,叫video_app。 在pages.json里面修改一下标题: 新建search搜索页面和me我的页面。 此时界面预览效果如下: 引入静态资源 主要是static里面的内容,全部复制过来。 配置底部导航栏 pages.json,放到顶层,和全部样式同级: "tabBar&quo…

详细分析 Sql Server查询卡顿的排查方向

目录 前言1. 问题所示2. 原理分析2.1 缺乏索引2.2 表碎片2.3 查询计划缓存2.4 锁和阻塞 3. 总结 前言 本篇为理论知识的分析以及对症下药&#xff0c;前阵子发生过Bug&#xff0c;后通过迁移服务器以及数据库最终才解决问题&#xff0c;但是细想当时可能是因为碎片或者缓存的概…

WEBKIT 通过JavaScript 调用本地,硬件未来之窗OS硬件APP

以酒店为例我们需要调用shen份证读取&#xff0c;采集人脸&#xff0c;门锁写房卡&#xff0c;如何通过浏览器调用 1.通过本地http服务 2.通过webkit模式 这里说政务单位模式的集成 由于篇幅问题&#xff0c;怎么集成webkit就不说了 一、webkkit加载交互本地代码 browser.…

23、Python之面向对象:实例属性、类属性,傻傻分不清楚

引言 在上一篇文章中&#xff0c;我们初步介绍了Python面向对象中类定义的语法&#xff0c;顺带介绍了关于面向对象的系统工程中&#xff0c;所涉及的OOA与OOD。 其实&#xff0c;简单来说&#xff0c;类的定义其实就是面向对象的“封装”特性的体现。我们将分析、设计得到的…

BLE自适应跳频算法详解

前言 &#xff08;1&#xff09;自适应跳频算法是相当的简单&#xff0c;小学生都能够看懂&#xff0c;而且网上已经有相当多的关于自适应跳频算法的介绍。既然如此&#xff0c;为什么我还要写这样一篇博客呢&#xff1f; &#xff08;2&#xff09;原因很简单&#xff0c;我发…

内网横向——利用WMI进行内网横向

文章目录 一、WMI介绍二、常规利用方法三、常见利用工具3.1 wmiexec3.2 Invoke-WmiCommand 四、WMI事件订阅的利用4.1 手动实现4.2 Sharp-WMIEvent 网络拓扑&#xff1a; 攻击机kali IP&#xff1a;192.168.111.0 跳板机win7 IP&#xff1a;192.168.111.128&#xff0c;192.168…

业务记录:处理动态表头的CSV/EXCEL文件

业务描述&#xff1a;传入一个动态表头的CSV文件&#xff0c;解析CSV&#xff0c;并保存入库。 CSV文件的表头是不确定的&#xff0c;即顺序和字段个数不确定&#xff0c;以及表头是中文字段。 例如&#xff1a; 为了顺利解析CSV文件&#xff0c;前端需要传入对应的字段名和顺…

axure制作切换栏--动态面板的应用

先看下效果&#xff1a;点击上面的切换栏 切换到西游记栏目&#xff1a; 切换到水浒传栏目&#xff1a; 上述两个图片比对可以发现&#xff0c;在点击切换栏的时候&#xff0c;里面的内容以及切换栏的下面蓝色横线也会发生对应的变化。这里涉及到两个地方的变化&#xff0c;就…

Golang 知识结构图

总结Go的入门知识结构&#xff0c;如下图所示&#xff1a;

图形/视图结构的三个坐标系

图形/视图结构的三个坐标系分别为视图结构系物理结构系&#xff0c;场景坐标系&#xff0c;图形项坐标系。 本文记录实践三个坐标系及视图与场景坐标转换&#xff0c;通过事件槽来显示出来的过程。 自定义1个View视图组件&#xff0c;其中扩展了鼠标点击、鼠标移动的事件&…

Golang | Leetcode Golang题解之第297题二叉树的序列化与反序列化

题目&#xff1a; 题解&#xff1a; type Codec struct{}func Constructor() (_ Codec) {return }func (c Codec) serialize(root *TreeNode) string {if root nil {return "X"}left : "(" c.serialize(root.Left) ")"right : "("…