C语言开发一篇文章带你还原童年,扫雷游戏完整源码分析
想必屏幕前的你,肯定玩过windows系统自带的那个游戏,扫雷
回想当年,我根本没看懂这个游戏是怎么玩的
比起扫雷,三维弹球对我更有吸引力
跑题了
本篇博客就让我们一起来试试,如何通过C语言代码,制作出一个"扫雷游戏se" 1.游戏程序主函数
在编写这类游戏代码时,我们要用到的主函数基本是一致的
扫雷游戏的主函数和猜数字游戏的主函数相差很小 void menu()//简易目录 { printf("*************************** "); printf("**** 1. play 0. exit***** "); printf("*************************** "); } int main() { int input = 0; do { menu(); printf("请选择:>"); scanf("%d", &input); switch (input) { case 1: game();//实现游戏的函数 break; case 0: printf("退出游戏 "); break; default: printf("输入错误 "); break; } } while (input); return 0; }2.游戏实现原理
想写好一串代码,首先我们要知道扫雷游戏需要通过什么方式来实现
我们需要一个9x9的棋盘,用于生成我们的雷以及玩家的游玩
在c语言中当然无法直接产生这样的画面
但我们可以同符号*或者#来代替网格,用1和0来表示有无雷
如果我们只生成一个棋盘,那1和0会直接显示出来,达不到隐藏的效果
所以我们需要用二维数组生成两个棋盘,一个用于存放雷,一个用于玩家的游玩 char mine[ROWS][COLS];//雷区布置 char show[ROWS][COLS];//玩家看到的界面
扫雷游戏我们使用头文件+源文件的形式撰写代码
这样写代码的优点在于后续我们可以直接通过更改.h文件中的数组,从而更改我们的格子大小
如: 改成12x12的游玩界面,改变雷区布雷个数等等
所以我们需要在game.h中定义这些符号 #include #include #include #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2
同时我们要在主函数的最上面引用这个自己写的头文件
只要把库函数头文件放入game.h文件,在其他源文件中只需引用game.h
不需要再次引用、之类 #include "game.h"棋盘大小为什么需要11x11?
你可能注意到了,在生成数组的时候,我使用了ROWS,其值为ROW+2
我们最终展示的只是9x9的游戏界面,但生成的棋盘其实是11x11的
这是因为我们需要在mine数组中实现扫描雷区的操作
玩过扫雷游戏的你肯定知道:在你点击一个格子的时候,如果这个格子不是雷
它会显示一个数字,告诉你它周围的8个格子中有几颗雷
如图所示:
在C语言中,我们可以用函数统计周围8个格子中雷’1’的个数
但是如果你来到边缘,那就出现问题了
如果我们想统计边缘的格子周边有几颗雷,就会遇到这种溢出数组的情况
此时代码会报错
为了避免这个问题,我们可以在原来9x9的基础上在周围加一圈空白的格子
也就是代码所示的ROW(行)COL(列)都要+2的情况 #define ROW 9 #define COL 9 #define ROWS ROW+2 #define COLS COL+2游戏过程
这里简单梳理一下我们的游戏过程
(1)玩家选择开始游戏
(2)生成两个棋盘,一个放置雷扫描雷,一个向玩家展示游戏界面
(3)玩家输入坐标,选择排雷位置
(4)有雷–>玩家被炸死,游戏结束;无雷–>显示周边有几颗雷,游戏继续
(5)所有雷被排出,游戏胜利
3.游戏代码实现
接下来就进入我们的游戏代码部分 3-1.初始化和打印 //初始化扫雷 InitBoard(mine, ROWS, COLS, "0"); InitBoard(show, ROWS, COLS, "*"); //打印扫雷 DisplayBoard(mine, ROW, COL); DisplayBoard(show, ROW, COL);
我们需要初始化两个棋盘,其中雷区初始化为0(0代表无雷),展示区初始化为’*’,用✳代替界面
同时我们打印这两个棋盘,查看初始化效果
因为这是我们的自定义函数,所以需要在.h文件中定义函数,在另外一个.c文件中包含函数的实现
//初始化棋盘 void InitBoard(char board[ROWS][COLS], int rows, int cols, char set); //打印 void DisplayBoard(char board[ROWS][COLS], int row, int col);
初始化函数和打印函数比较简单,使用for语句达成我们的需求 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; } } } void DisplayBoard(char board[ROWS][COLS], int row, int col) { int i = 0; int j = 0; printf("------扫雷游戏------ "); //打印列号 for (i = 0; i <= col; i++) { printf("%d ", i); } printf(" "); for (i = 1; i <= row; i++)//只打印中心的99方格 { printf("%d ", i);//打印行号 for (j = 1; j <= col; j++)//只打印中心的99方格 { printf("%c ", board[i][j]); } printf(" "); } printf("-------------------- "); }
需要注意的是我们的最后打印棋盘的时候是从i=1开始的,这样就能避开添加的空白边缘区域,只打印中心的99方格
同时我们添加了列号和行号,这样能让玩家清除的知道自己应该输入什么坐标
3-2.布置雷区 //布置雷 SetMine(mine, ROW, COL);
同样的,我们需要在game.h中定义这个函数 //布置地雷 void SetMine(char mine[ROWS][COLS], int row, int col);
在game.c中写入自定义函数的实现 //放置雷 void SetMine(char mine[ROWS][COLS], int row, int col) { int count = EASY_COUNT; while (count) { int x = rand() % row + 1; int y = rand() % col + 1; if (mine[x][y] =="0") { mine[x][y] = "1"; count--; } } }
这里面出现了一个前面没有提到的变量,EASY_COUNT
本来这个位置只是个10
但如果我们想更改布雷个数,那每次都需要更改这里的10,后面的代码中也需要更改,非常麻烦
所以我们改为使用一个自定义变量,在game.h中定义这个变量的值 #define EASY_COUNT 10
这个值就代表我们布置雷的个数了 3-3.玩家排查雷//在主函数中引用这个函数 FindMine(mine,show, ROW, COL);//需要把mine数组中排查的雷放入show //在game.h中定义这个函数 void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int row, int col);
因为我们需要把mine数组中排查出的雷的个数放入show数组中打印出来
所以这里我们需要把两个数组都传送过去
1.输入排查的坐标
2.检查坐标处是不是雷
·是雷 -boom!炸死 -游戏结束
·不是雷 -统计坐标周围有几个雷-存储排雷的信息到show数组,游戏继续 void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) { int x = 0; int y = 0; while (1) { printf("请输入排雷坐标:> "); scanf("%d%d", &x, &y); //判断坐标是否正确 if (x >= 1 && x <= row && y >= 1 && y <= col) { if (mine[x][y] == "1") { printf("很遗憾,你被炸死了 "); DisplayBoard(mine, ROW, COL); break; } else { //不是雷的情况下,统计坐标周围有几个雷 int count = get_mine_count(mine, x, y); show[x][y] = count + "0"; } } else { printf("坐标错误,请重新输入 "); } } }
注意,这里面我们需要添加一个代码来判断坐标合法性
我们的棋盘是9x9,玩家要是输入一个(99,99)的坐标,那肯定不在数组中的,是无效的
我们需要提醒玩家他输错了 ‘0’的作用show[x][y] = count + "0";
你可能会对这行代码感到疑惑
为什么要在count后面+上一个‘0’?
这里就和我们ascii码表有关了
因为我们初始化数组和布置雷的时候,我们给数组传入的都是1和0这两个符号,并不是数字!
但是在show数组中我们需要给玩家显示一个数字的字符
这里面我们提供的是1的字符,并不是1它本身
而我们在计算周边雷的个数的时候,传回来的是一个具体的数字
观察表格,你会发现数字和对应的字符中间,都差了48
而48恰好是字符’0’对应的ASCII码值
所以我们需要用count加上字符’0’,以此在界面中向玩家展示周边8格有几颗雷 3-4. 系统扫描雷
如何把玩家选择的格子周边的雷扫描出来呢?
设玩家的选择的坐标为x和y
我们只需要把这些坐标全部在二维数组中键入,就能逐个扫描出雷的个数 //统计雷的个数 static int get_mine_count(char mine[ROWS][COLS], int x, int y) { return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1] + mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * "0"; }
这里因为我们扫描出来的也是‘1’的字符,系统中是字符1的ascii码值49
所以我们需要减去8个字符‘0’,这样就能得到雷的个数的数字
(然后在之前的那个函数中接受,count+‘0’,在show数组中显示)
这个函数是在玩家排查雷的函数之前的
因为在主函数中我们不需要使用这个自定义函数,所以不需要在game.h中定义
我们想让它只在game.c中生效,所以用static修饰它
static的作用
1.修饰局部变量
2.修饰全局变量
3.修饰函数
上面的代码其实还少了一个东西
我们需要判断玩家什么时候胜利——雷区的0(无雷方块)全部被玩家找出,玩家就胜利了 int win = 0; while (win< row * col - EASY_COUNT)
这里我们需要更改的是whlie函数
其中 row * col - EASY_COUNT 指方格总数减去雷的个数,得到的是无雷方块的个数
玩家每成功排除一个无雷方块,win就会加一个数字 else { //不是雷的情况下,统计坐标周围有几个雷 int count = get_mine_count(mine, x, y); show[x][y] = count + "0"; //显示排查出来的信息 DisplayBoard(show, ROW, COL); win++; }
当win达到无雷方块个数的时候,whlie循环就会停止
随后我们判断玩家是否胜利,如果胜利,就打印棋盘,让玩家知道雷的位置 if (win == row * col - EASY_COUNT) { printf("恭喜你,游戏胜利! "); DisplayBoard(mine, ROW, COL); }
这里必须要判断,因为你失败了也是会跳出循环的!
到此,我们的扫雷代码就是完成了 4.查看结果
这里我作弊,将雷的个数设置为80并打印出布置雷之后的棋盘
输入最后一个雷的位置,系统提示我们游戏胜利
输入雷的坐标后,也会提示你被炸死了
到这里我们可以确认代码是编写成功了!
对啦对啦! 另外的话 为了帮助大家,轻松,高效学习C语言/C++,我给大家分享我收集的资源,从最零基础开始的教程到C语言项目案例, 帮助大家在学习C语言的道路上披荆斩棘!可以来我粉丝群领取哦~
编程学习书籍分享:
编程学习视频分享:
整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程) 最重要的是你可以在群里面交流提问编程问题哦!
对于C/C++感兴趣可以关注小编在后台私信我:【编程交流】一起来学习哦!可以领取一些C/C++的项目学习视频资料哦!已经设置好了关键词自动回复,自动领取就好了!
13!国乒爆大冷,世界冠军首轮输球,林高远绝境发力横扫名将北京时间2023年2月18日,德班世乒赛国乒队内选拔赛继续进行,男单小组赛第一轮,世界冠军林高远迎战赵子豪。在这场5局3胜制的比赛中,林高远发挥出色,以30取得开门红。女单赛场爆出
春天就是白衬衫的季节呀!白衬衫这样穿最好看!春天,该把白衬衫掏出来啦!它可是叠穿的一把好手呀,提亮增加层次感,有它真好单穿就更不必说了,简简单单的就能把人衬得又美又帅!金丝眼镜白衬衫黑裤子尖头鞋小手表,谁这样穿不又酷又性感?
要知道,想送你回家的人,东南西北都顺路很多时候我都好像没什么特别的话想要跟你说但就是希望你能在我身边我的愿望是现在当个快乐的女孩中年时当个快乐的阿姨老年时当个快乐的老太婆总之拥有快乐的一生东西的消失有理有据关系的消失无
知乎者也林祥鹭他乡说故乡今夕为何夕,他乡说故乡。题记工作四年后,在一个熟悉的毕业季里,我离职了,于是决心从一座城市去往另一座城市,散步。当飞机掠过高空,一种不接地气的心跳又回来了。福建东南沿海的平潭岛在视
泉州两日游Day120230203游安平桥安海关帝庙古刺桐港真武庙蟳埔村西街钟楼。安平桥是中古时代世界最长的梁式石桥,也是中国现存最长的海港大石桥。位于泉州市晋江安海镇和南安水头镇之间的海湾上,享有
被誉为天下第一山的泰山,到底有多美?看完你就知道了头条热榜爬完泰山全都老实了泰山,是我国五岳之首,自古以来就是我国文化的象征之一。它高耸入云,巍峨壮观,被誉为天下第一山。而对于很多人来说,攀登泰山已成为一项不可或缺的旅行体验。今天
雪里禅韵清净寺清净寺是位于包头市稀土高新区境内的一处汉传佛教净土宗寺院,殿宇楼阁鳞次栉比高大巍峨。雕梁画栋,斗拱飞檐,红墙和黄色的琉璃瓦以及汉白玉雕栏,古朴静雅色彩对比鲜明,极具视觉冲击力。一场
春意正浓梅景来寒气尽,春意来。良辰梅景,阳光正好,来,一起开启春天里的梅好之旅吧!天鹅湖公园天鹅湖原名荷叶地,因湖面呈天鹅形状得名。多彩的梅花分布在天鹅湖西北岸边,暗香浮动,美不胜收。匡河公园选
济南大观园,承载了我多少回忆济南大观园是一个记载了几代济南人美好记忆的地方。济南大观园位于济南市市中区,建于1932年,距今已有91年的历史,古色古香优雅别致。济南大观园按照南京夫子庙上海城隍庙古建筑风格进行
这几天亲历的加拿大一件小事,引发我们全家深深的思考!事情不大,但是折射出一个大的观念孩子今年春假也就是三月初要去中美洲的哥斯达黎加旅游这个项目是学校委托第三方也就是一家专做团队旅游的公司搞的开始操作是从去年3月份总费用是每一个人31
大咖论道丨刘焕兰发展康养旅游汝城大有可为国际生态生命安全科学院院士中华文化促进会康养文旅委员会主任刘焕兰。红网时刻新闻2月18日讯(记者戴科)当养生邂逅旅游,形成了康养旅游的新模式。2月18日,湖南乡村旅游赋能乡村振兴研
老照片1945年,苏联军队在东北头条创作挑战赛从九一八事变算起,到1945年,日本人在东北地区统治了整整14年。这14年,东北人民深受蹂躏,承受了无尽的屈辱。1945年2月,美英苏联三国在雅尔塔签署了雅尔塔协议,
中国古代出土的十大稀世宝剑,埋藏千年,剑出惊天下剑,在中国古代文化中占据了重要且独特的地位。剑,至尊至贵,乃短兵之祖。因其携之轻便,佩之神采,用之迅捷,故历朝王公帝侯,文士侠客,商贾庶民,莫不以持之为荣。剑与艺,自古常纵横沙场,
谁是中国历史上最大的贪官都说清朝的和珅是中国历史上最大的贪官。其实和珅只能算得上是清朝最大的贪官。那么,中国历史上最大的贪官该是谁呢?这顶帽子,应戴在明代的严嵩父子头上。也许以纯金银田产而言,严嵩父子比不
红色故事绘今日推荐中国核事业的领航人朱光亚11月13日推荐1985年11月13日,我国第一颗原子弹爆轰试验场纪念碑落成,所以今日特别推荐连环画中国核事业的领航人朱光亚。1985年11月13日,我国第一颗原子弹爆轰试验场地纪
专访经济学家邢予青解码全球价值链,中国企业应该怎么做?十余年间,价值链以及更加学术化的增加值贸易概念,从国际贸易研究圈内的小众讨论,随时代涌动,突破了学术圈层,成为了几乎所有人都关心的话题。不过,宏大叙事之中,价值链究竟从微观层面如何
马兰拉面老板,出资2924亿,收购世界化工巨头,他为何这么牛?有心栽花花不开,无心插柳柳成荫。有些人蝇营狗苟,却终究一事无成,有些人偶有为之,却成就繁花硕果。其中固然有机遇与运气的缘故,同时也与个人的能力与品质不可分开,就像马兰拉面的创始人,
李连杰带女儿打太极,女儿一身黑欧范十足,颜值不如妈但气质高级黑色可以说是经典中的经典,无论你多么讨厌这种颜色,衣柜中总少不了一两件黑色的单品,特别是到了秋冬季走在大街上,几乎每隔两步就能看到一个穿黑色衣服的路人。作为功夫巨星李连杰的女儿,J
唐长安旅行手册舌尖上的唐朝,可太顶了看过N部穿越网剧你是否也想来一次地地道道的穿越众所周知在政治经济文化外交等方面都有着辉煌成就的唐朝是中国古代最强盛的朝代之一作为一个资深吃货最关心的当然是唐朝人民的伙食问题了今天我
中国最美五大建筑,你去过哪个?头条创作挑战赛都说,建筑是凝固的艺术。那中国建筑,大约是一首凝固的诗。它穿过时间,静静伫立着,越是古老,越像一首遥远的诗。传说在古希腊神话中,音乐之神俄耳甫斯有一把七弦琴,他的琴声
带娃踩过的坑那些带娃踩过的坑对于很多第一次当爸爸妈妈的父母们来说,带娃始终是一件孰能生巧的事情,很多时候是要经历过一些错误之后,才会意识到怎样带娃才是正确的操作。但是,除了自己和孩子一起摸爬滚
来自一个新手宝爸的劝告我家的育儿经你是否还在为突如其来的惊喜而高兴的不知所措。那我作为一个新手宝爸我觉得我还是很有发言权的,希望可以通过自己的分享来缓解新手宝爸宝妈的焦虑。俗话说十月怀胎,一朝分娩,看起