C语言低配版扫雷游戏

news/2024/11/30 9:39:06/

文章目录

    • 一.分析
    • 二.游戏代码结构
    • 2.1主程序
    • 2.1.1main()函数
    • 2.1.2menu()函数
    • 2.1.3game()函数
    • 2.2game.h
    • 2.3game.c
    • 2.3.1初始化地图Init_Map()
    • 2.3.2打印地图Print_map()
    • 2.3.3布置雷Set_Beng()
    • 2.3.4判断是否为雷BengBeng()
    • 2.3.5计算坐标周围雷的个数Count_Beng
    • 三.结尾

一.分析

扫雷我想大部分应该都玩过吧,游戏规则我在这里就不做解释了。

首先我们得需要两个二维数组来存放我们所需的内容,

第一个二维数组里存放的内容是让玩家看的见的内容:
在这里插入图片描述
像这样,我们把这些灰色的方格用 * 代替,为了让玩家知道要在这里输入坐标,然后输入坐标后,要返回一个信息:这个坐标周围有几个雷。我们第一个数组目前就存放这些东西。

第二个数组存放的信息就是雷的位置了,通过rand函数来随机存放我们需要的雷的个数

如果我们希望格子是7 * 7的,那我们必须让地图的大小变成9 * 9的。

这是因为,我们需要有一个检查玩家输入的这个坐标周围的信息,如果地图范围只有7 * 7的话,那是不是边缘的信息在检查的时候就越界了?这样的话我们直接往外扩大一圈,然后初始化成0是不是就解决了。
而且我们两个地图的大小必须相同,这样可以起到一个映射的效果,因为玩家在第一个地图输入的坐标,该位置也可以反映到第二个地图的位置上,然后通过检查第二个地图里该坐标周围的雷的个数,然后在显示出来。
在这里插入图片描述
只用红框里面这部分,外面不用。

放置雷的方法:

可以将第二个数组里的内容全部初始化成0,然后随机将某些位置的0变成1,这样是为了在判断一个坐标周围位置的雷的时候,可以把他们的坐标(每个坐标要减去‘0’)加起来返回。

二.游戏代码结构

2.1主程序

#include "game.h"void menu()
{printf("************************\n");printf("******   1.play   ******\n");printf("******   0.exit   ******\n");printf("************************\n");
}void game()
{char uvis[ROWS][COLS];//让玩家看不到的地图char vis[ROWS][COLS];//能让玩家看到的地图//初始化地图Init_Map(uvis, vis);//打印可看见的地图Print_map(vis);//打印不可看见的地图//Print_map(uvis);//布置雷Set_Beng(uvis);Print_map(uvis);while (1){//输入坐标并判断是否为雷int flag = 0;flag = BengBeng(vis, uvis);if (flag == -1){printf("很遗憾,你被炸死了\n");break;}else if (flag == 1){system("cls");Print_map(vis);Print_map(uvis);}elsebreak;}
}int main()
{int input = 0;srand((unsigned int)time(NULL));do{menu();printf("请输入->");scanf("%d", &input);switch (input){case 1:system("cls");game();break;case 0:break;default:printf("输入错误,请重新输入:\n");break;}} while (input);return 0;
}

为了更好的维护写的代码,我把和实现游戏有关的代码全部写到了game.c这个文件里。现在我们先分析这个文件里的代码。

2.1.1main()函数

int main()
{int input = 0;srand((unsigned int)time(NULL));do{menu();//菜单printf("请输入->");scanf("%d", &input);switch (input){case 1:system("cls");game();break;case 0:break;default:printf("输入错误,请重新输入:\n");break;}} while (input);return 0;
}

main函数最外面的框架是do…while循环。循环里面是一个switch的选择语句。
我们通过玩家输入0,1来操作,如果输入1,则开始游戏,如果输入0就是退出游戏,如果输入其它就提示输入错误,重新输入。

srand((unsigned int)time(NULL));这行代码是为了和后面的rand函数相关联,到时候在解释,现在可以先跳过不用看。

system(“cls”);这是一个清屏的代码,主要是为了清理掉上次玩的游戏显示的界面,让画面变得更简洁。

2.1.2menu()函数

void menu()
{printf("************************\n");printf("******   1.play   ******\n");printf("******   0.exit   ******\n");printf("************************\n");
}

就是一个菜单,打印出来是这个效果:
在这里插入图片描述

2.1.3game()函数

void game()
{//ROWS,COLS我会在game.h头文件处解释char uvis[ROWS][COLS];//让玩家看不到的地图char vis[ROWS][COLS];//能让玩家看到的地图//初始化地图Init_Map(uvis, vis);//打印可看见的地图Print_map(vis);//打印不可看见的地图//Print_map(uvis);//布置雷Set_Beng(uvis);Print_map(uvis);while (1){//输入坐标并判断是否为雷int flag = 0;flag = BengBeng(vis, uvis);if (flag == -1){printf("很遗憾,你被炸死了\n");break;}else if (flag == 1){system("cls");Print_map(vis);Print_map(uvis);}elsebreak;}
}

游戏实现的步骤都在这里面,但是每个函数如何实现的放在了game.c文件里面。

在这里插入图片描述

2.2game.h

在这里我先把.h头文件里的内容给大家展示一下,以防一会在介绍.c文件里函数时,有些东西大家不知道。

//这是一会需要用到的库函数
#include <stdlib.h>
#include <stdio.h>
#include <time.h>//对一些参数做的宏定义//ROW,COL分别是行和列,是你希望地图的大小
#define ROW    9
#define COL    9//这两个是实际需要的行和列,刚在说过了,需要大一圈
#define ROWS   ROW + 2
#define COLS   COL + 2//这是你希望设置的炸弹的个数
#define BENG   10//下面就是一会需要用到的函数//初始化地图
void Init_Map(char uvis[ROW][COLS], char vis[ROWS][COLS]);//打印地图
void Print_map(char map[ROWS][COLS]);//布置雷
void Set_Beng(char uvis[ROWS][COLS]);//判断是否为雷
int BengBeng(char vis[ROWS][COLS], char uvis[ROWS][COLS]);

2.3game.c

#include "game.h"//初始化地图
void Init_Map(char uvis[ROW][COLS], char vis[ROWS][COLS])
{//玩家看到的地图数组内容全部初始化成*//看不到的数组里初始化为空格int i = 0;int j = 0;for (i = 0; i < ROWS; i++){for (j = 0; j < COLS; j++){vis[i][j] = '*';uvis[i][j] = '0';}}
}//打印地图
void Print_map(char map[ROWS][COLS])
{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 <= ROW; j++){printf("%c ", map[i][j]);//map对应的那两个数组大小都是11*11,这里只是把一个数组中间9*9的拿出来//用来存放玩家输入的信息,所以存放的信息都是从1开始。}printf("\n");}
}//布置雷
void Set_Beng(char uvis[ROWS][COLS])
{int amount = BENG;while (amount){char x = rand() % ROW + 1;char y = rand() % COL + 1;if (uvis[x][y] == '0'){uvis[x][y] = '1';amount--;}}
}int Count_Beng(int x, int y, char uvis[ROWS][COLS])
{//因为外边两圈全初始化成0了,行列数只是打印出来//并没有改变数组里的内容return uvis[x + 1][y] +uvis[x - 1][y] +uvis[x][y + 1] +uvis[x][y - 1] +uvis[x - 1][y - 1] +uvis[x - 1][y + 1] +uvis[x + 1][y + 1] +uvis[x + 1][y - 1] - 8 * '0';}//判断是否为雷
int BengBeng(char vis[ROWS][COLS], char uvis[ROWS][COLS])
{while(1){//玩家输入坐标printf("请输入坐标->");int x = 0;int y = 0;scanf("%d %d", &x, &y);int count = 0;int count1 = 0;if (x >= 1 && x <= ROW && y >= 1 && y <= COL){if(uvis[x][y] != '1'){vis[x][y] = Count_Beng(x, y, uvis) + '0';system("cls");Print_map(vis);Print_map(uvis);count1++;}else{return -1;}}else{printf("输入不合法\n");}if (count1 == ROW * COL - BENG){printf("很遗憾,你没被炸死\n");return 0;}return 1;}
}

2.3.1初始化地图Init_Map()

void Init_Map(char uvis[ROW][COLS], char vis[ROWS][COLS])
{//玩家看到的地图数组内容全部初始化成*//看不到的数组里初始化为空格int i = 0;int j = 0;for (i = 0; i < ROWS; i++){for (j = 0; j < COLS; j++){vis[i][j] = '*';uvis[i][j] = '0';}}
}

用两个for循环把二维数组都遍历一遍。

2.3.2打印地图Print_map()

先给你们看下地图的样子:
在这里插入图片描述

void Print_map(char map[ROWS][COLS])
{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 <= ROW; j++){printf("%c ", map[i][j]);//map对应的那两个数组大小都是11*11,这里只是把一个数组中间9*9的拿出来//用来存放玩家输入的信息,所以存放的信息都是从1开始。}printf("\n");}
}

先要在第一行把列号打印出来,也就是i=0的位置上。
后面每一行在打印之前也就是每一行的j=0的位置上把每一行的行号打印出来。
剩下的位置就是数组里的元素的位置,但是记住我们是从坐标[1,1]开始打印的。就是把11 * 11其中9 * 9位置上的信息打印出来。玩家在输入坐标的时候也是从[1,1]-[ROW,COL]。
行,列坐标是直接打印出来的,不会影响我们外面那一圈的内容。

2.3.3布置雷Set_Beng()

void Set_Beng(char uvis[ROWS][COLS])
{int amount = BENG;while (amount){char x = rand() % ROW + 1;char y = rand() % COL + 1;if (uvis[x][y] == '0'){uvis[x][y] = '1';amount--;}}
}

这里的rand函数是和刚在main函数里的srand函数一起用的。
srand()括号里面的内容里可以随便写一个值,可以把它当成种子,如果没有就默认为1,rand()每次用的时候都会检查是否调用过srand()函数,如果有就会产生一个随机值。但是这个种子如果不变的话,rand函数产生的随机值就固定了,换句话说,他就第一次随机,以后的值和第一次的一样。
这样的话我们就必须让srand函数里面的种子也是个随机值/不同值。这样就麻烦了,这不是套娃吗?所以为了满足我们的需求,我们通过time()函数来当作种子。
time():获取当前日历时间作为time_t类型的值.
我们直接把时间当成种子带进去,时间可不是固定的每分每秒都在运行。这样就会让我们每秒产生的随机值都不一样

因为time函数的返回值是time_t类型的,而srand需要的类型是unsigned int类型的,所以我们在使用时要强制类型转换。
time函数的参数填NULL空指针就行。

但是这个随机值的取值返回太大,所有我们必须把它限制在我们想要的范围来作为炸弹的坐标,我们发现炸弹的坐标应该在1 ~ ROW,和1 ~ COL之间。而一个任意数%x,这个取值范围是0 ~ x-1.所以为了让炸弹的坐标在我们希望的范围中就给%之后的值+1.

再布置炸弹的前提下是必须这个位置上的内容是‘0’,因为我们初始化的时候就全设为’0’,这样防止重复布置雷。我们将布置成功后那个坐标的内容置成‘1’。然后没布置成功一个amount–。布置的个数是我们需要的数量就停下来。

2.3.4判断是否为雷BengBeng()

int BengBeng(char vis[ROWS][COLS], char uvis[ROWS][COLS])
{while(1){//玩家输入坐标printf("请输入坐标->");int x = 0;int y = 0;scanf("%d %d", &x, &y);int count = 0;int count1 = 0;if (x >= 1 && x <= ROW && y >= 1 && y <= COL){if(uvis[x][y] != '1'){vis[x][y] = Count_Beng(x, y, uvis) + '0';system("cls");Print_map(vis);//Print_map(uvis);count1++;}else{return -1;}}else{printf("输入不合法\n");}if (count1 == ROW * COL - BENG){printf("很遗憾,你没被炸死\n");return 0;}return 1;}
}

玩家每走一步,我们都判断一下结果。

首先判断的是玩家输入的坐标是否合法,只有合法了,才能真正的判断这个位置是否为雷,或者这个位置周围有多少雷。

如果这一个位置的坐标不是雷,我们就通过函数Count_Beng来计算这个位置周围有多少雷,然后记录并打印下来,让玩家知道。
如果这一个位置的坐标就是雷的话,之间返回-1

玩家每成功下好一步,变量count1就++,这样的话在最后判断count1是否等于ROW * COL - BENG。
ROW * COL - BENG是棋盘的所有坐标之和-炸弹的个数,也就是地图上玩家所有不是炸弹的坐标的个数,如果满了就返回0说明,游戏结束,玩家赢了。

如果既没有返回0,也没有返回-1,说明游戏还要继续,还没结束,这样我们就返回1,以供game()函数里判断。

用函数Count_Beng计算出来的结果要+‘0’。因为我们返回的是一个整型,而数组里存放的是一个字符,所以+‘0’,让数字变成字符,这样才能打印出来让玩家看到。

2.3.5计算坐标周围雷的个数Count_Beng

int Count_Beng(int x, int y, char uvis[ROWS][COLS])
{//因为外边两圈全初始化成0了,行列数只是打印出来//并没有改变数组里的内容return uvis[x + 1][y] +uvis[x - 1][y] +uvis[x][y + 1] +uvis[x][y - 1] +uvis[x - 1][y - 1] +uvis[x - 1][y + 1] +uvis[x + 1][y + 1] +uvis[x + 1][y - 1] - 8 * '0';		   
}

我们把该坐标周围8个位置的坐标- 8 * ‘0’加起来之间返回即可,因为我们当初布置雷的时候把雷的位置里的内容记成了’1’,如果减去’0’也就是数字1,周围有几个雷,就有几个1,全加起来就是雷的个数。
在这里插入图片描述

三.结尾

以上就是扫雷的全部内容了。源码在上面已经列出来了,需要的可以自取。但是为了看到自己布置雷的位置,所以把玩家不该看到的地图也打印出来了,你们如果不想要注释掉就行。


http://www.ppmy.cn/news/537148.html

相关文章

Thunderbolt雷电接口

Intel发明雷电/雷雳Thunderbolt技术&#xff08;Amd的笔记本没有&#xff09;&#xff0c;融合PCI Express和DisplayPort两种通信协议&#xff0c;还兼容usb充电协议。 既可以传输PCIE总线高额数据带宽传输&#xff08;外接硬盘甚至显卡&#xff09;&#xff0c;又能传输视频信…

cesium效果篇—雷电效果

cesium的雷电效果 前言成品图思路代码总结 前言 刚开始学cesium的时候&#xff0c;发现网上很多关于雾雨雪的后处理效果&#xff0c;当时就在想&#xff0c;怎么没有更多一点的雷电、云层之类的效果呢&#xff0c;前两年在cesium一个群的群主发了一个基于sdk的网站&#xff0c…

4K/8K的雷电(thunderbolt)解决方案

1.背景 随着RED等公司推出便携式6k和8k数字电影摄像机&#xff0c;现场制作会生成数量惊人的数据。所有这些数据都需要在现场进行编目和存档&#xff0c;然后才能传输到演播室进行编辑和整理。针对这项工作&#xff0c;传统的单一移动计算机解决方案提供的性能有限&#xff0c…

雷电网络

虽然在区块链技术蓬勃发展的今天&#xff0c;比特币日益显得臃肿和老旧&#xff0c;但比特币社区仍然为区块链技术贡献着重要的思想。基于闪电网络的思路&#xff0c;以太坊社区也提出了自己的链下微支付通道解决方案——雷电网络&#xff08;RaidenNetwork&#xff09;。 Rai…

用计算机里可以加50度电,一台电脑一天用多少度电 节电节能的建议和措施

我们每天用电脑&#xff0c;你的电脑到底每天消耗你多少度电呢。很多人都想过&#xff0c;但是却没有计算过。我们每天用电脑&#xff0c;你的电脑到底每天消耗你多少度电呢。很多人都想过&#xff0c;但是却没有计算过。今天就为大家介绍一台电脑一天用多少度电以及一些节电节…

台式计算机消耗的电,计算机一天要消耗多少电量

一个是CRT显示器&#xff0c;它是传统的CRT显示器. 通常&#xff0c;17平面屏幕约为75-80W. 另一半的功率基本上消耗在主机的硬盘&#xff0c;主板&#xff0c;CPU&#xff0c;风扇和扬声器上. 以下是jy135的编辑每天收集的计算机用电量&#xff0c;欢迎阅读. 台式机一天要消耗…

微服务的文件配置

1 基于本地文件配置的痛点 ①修改本地配置文件 需要重启服务 ②viper能监听本地配置文件变动 修改内存中变量的值 貌似可以满足需求 痛点如果实例过多 手动改极有可能出错 很多服务都依赖一个配置 运维可以写脚本批量修改 出问题运维不想背锅 ③ 多语言开发的实例 使用…

无显示器玩转树莓派桌面版

title: 无显示器玩转树莓派桌面版 zhaoolee在Github开启了长篇连载《树莓派不吃灰》https://github.com/zhaoolee/pi 目前已经更新到18篇&#xff0c;主要是给树莓派刷Ubuntu当做家庭服务器用。 恰好手头还有一块闲置的树莓派4B &#xff0c;我打算深度玩一下树莓派桌面版&…