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…

常见OVS网桥及其链接接口详解

目录 引言OVS简介常见OVS网桥 QBR&#xff08;qbr&#xff09;PLY网桥br-intbr-tunbr-routerbrcps常见网桥链接接口 QVOQVIQVMPatch网桥和接口的工作原理应用场景 虚拟化环境数据中心网络云计算平台 1. 引言 开放虚拟交换机&#xff08;Open vSwitch&#xff0c;简称OVS&…

iOS卡顿优化

概述 怎么优化下面场景离屏渲染的情况 图层阴影(Shadow):设置图层的 shadowOpacity、shadowOffset、shadowRadius 等属性。 图层圆角(Corner Radius):设置图层的 cornerRadius 属性,并同时启用了 masksToBounds。 图层蒙版(Mask):设置图层的 mask 属性或使用 maskT…

【保姆级讲解下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;我需要调整下自己的学习思路&…

使用 Vue 2.x + Element UI 搭建后台管理系统详解

引言 Vue.js 是一个非常流行的前端框架&#xff0c;而 Element UI 是基于 Vue 2.x 的一套完整的 UI 组件库&#xff0c;非常适合用来构建企业级的后台管理系统。本文将详细介绍如何使用 Vue 2.x 和 Element UI 来搭建一个后台管理系统&#xff0c;包括项目初始化、路由配置、状…

高级java每日一道面试题-2024年7月29日-并发篇-什么时候用乐观锁,什么时候用悲观锁?

面试官: 什么时候用乐观锁,什么时候用悲观锁? 我回答: 乐观锁和悲观锁是两种常用的并发控制策略&#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.…

百日筑基第三十四天-JAVA中的强/软/弱/虚引用

百日筑基第三十四天-JAVA中的强/软/弱/虚引用 Java对象的引用被划分为4种级别&#xff0c;分别为强引用、软引用、弱引用以及虚引用。帮助程序更加灵活地控制对象的生命周期和JVM进行垃圾回收。 强引用 强引用是最普遍的引用&#xff0c;一般把一个对象赋给一个引用变量&…

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…