摘要:作者时间网站地址摘要语言实现我们小时候玩过的扫雷游戏,最近看到了一些扫雷游戏的简单实现,但是总有功能上的缺失,玩起来不那么的原汁原味,因此我增加了一些新功能确保玩家首次排雷一定不会炸死。
作者:Nico
时间: 2021-11-10
网站地址:[]:https://github.com/sxfinn
C语言实现我们小时候玩过的扫雷游戏,最近看到了一些扫雷游戏的简单实现,但是总有功能上的缺失,玩起来不那么的“原汁原味”,因此我增加了一些新功能:
- 确保玩家首次排雷一定不会炸死。
- 加入了计时器记录结束时间。
- 扩展式排雷,展开周围的非雷区。
比较难的一点是扩展式排雷,使用用递归函数处理相对方便。
探索排雷位的周围八个区域。
总归情况就分三类,可探索的区域为8个,5个,3个。但这样分类实在麻烦,所以我们可以选择在创建雷盘的时候,将二维数组的维度扩大一些,使其不用考虑多种情况,而只用考虑探索周围八个雷区。
我们可以给外侧再加一层,即给二维数组行列分别加二,并且把外层全部设置为非雷区域,就可以解决这一问题。
展开周围的非雷区
递归过程:如果(x,y)位置周围八区的雷数为0,则从八个区域展开,展开的位置的 x坐标是从x-1到x+1,而 y 的位置是从y-1到y+1的范围中,因此嵌套两重循环。
进入条件:只有之前没有展开过,且坐标在雷盘内的位置才进入递归。
终止条件:如果探索的周围八个位置有雷,则停止,并让该位置显示雷的数量。
探索八区代码实现:
提醒:’*‘是未排雷的区域,’ ‘是代表已经展开过的区域。
//计算排查位置处周围雷的个数int calculate(char mine[ROWS][COLS],int x,int y){ //因为雷的位置放的是字符‘1’ // 加起来之后应该分别减去‘0’,才得到雷的个数 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";}
//展开周围都没有雷的雷盘(扩展式排雷)void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)//扩展函数{ //利用calculate函数判断周围是否有雷 if (calculate(mine, x, y) == 0) {//判断周围雷的个数,若为0,则需要展开 show[x][y] = " ";//展开的位置都置为空格 int i = 0; int j = 0; //该位置可以拓展才检查周围8个位置是否能拓展 for (i = x - 1; i <= x + 1; i++) { for (j = y - 1; j <= y + 1; j++) { if (show[i][j] == "*" && i > 0 && i <= ROW && j > 0 && j <= COL) { //如果该位置未被扫过且在棋盘范围内则继续递归调用expand函数 //再依次进入判断周围8个位置是能被展开还是不能 expand(mine, show, i, j); } } } } else { show[x][y] = calculate(mine, x, y)+"0"; //不需要展开则显示附近雷的个数 }}
这两个函数结合起来使用便可以达到扩展展开的效果。
使用效果:
可以看到在选择(5,6)后一片非雷区被展开了,并且边缘部分的雷个数被打印在了相应位置。
由于实在找不到什么好看的符号代替’ ‘,看着可能会有点难受?,欢迎评论区给出建议!
我将代码分为了test.c
、game.h
、game.c
三个部分。
test.c
是游戏实现的主体框架。
game.h
是所用到的头文件以及自定义函数声明。
game.c
是游戏的具体实现模块。
除了上面的递归有些难度外,其他的都比较易懂,不再多带带阐述,下面的源码中我给出了每一步的注释,解释的很清楚,相信各位一边看代码一边想会有更多的收获。
#include"game.h"int main(){ srand((unsigned)time(NULL)); int input = 0; do { menu();//菜单 printf("请输入->(1 / 0)/n"); scanf("%d", &input); switch (input) { case 1: game();//选择1就进入game break; case 0://选择0就退出 printf("退出游戏!/n"); break; default: printf("输入错误,请重新输入:/n"); break; } } while (input); return 0;}
#pragma once#include #include //时间戳函数头文件#include //rand、srand函数头文件#include #define ROW 9//雷的区域的行数#define COL 9//雷的区域的列数#define ROWS ROW+2//数组的一维大小#define COLS COL+2//数组的二维大小#define NUM 50//雷的个数//进入游戏void game();//打印菜单void menu();//计时器void set_time();//初始化数组void init_array(char array[ROWS][COLS], int rows, int cols, char symbol);//布置雷void lay_mines(char mine[ROWS][COLS], int row, int col);//打印void show_interface(char show[ROWS][COLS], int row, int col);//排查雷void mine_detection(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);//计算雷的个数int calculate(char mine[ROWS][COLS], int x, int y);//保证第一次安全排雷int one_safe(char mine[ROWS][COLS], int x, int y);//扩展式排雷以及记录周围的雷数量void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);
#include"game.h"//游戏流程void game(){ system("cls");//清屏 //创建两个数组 char mine[ROWS][COLS] = { 0 };//布置雷的数组 char show[ROWS][COLS] = { 0 };//游戏界面的显示雷个数的数组 //初始化两个数组 init_array(mine, ROWS, COLS, "0");//初始化为‘0’ init_array(show, ROWS, COLS, "*");//初始化为‘*’ //布置雷 lay_mines(mine, ROW, COL); //打印出show数组 show_interface(show, ROW, COL); //排查雷 mine_detection(mine, show, ROW, COL); set_time();}//菜单函数void menu(){ printf("***************************/n"); printf("* ****** MENU ******* */n"); printf("* ******1.play******* */n"); printf("* ******0.exit******* */n"); printf("* ******************* */n"); printf("***************************/n");}//计时函数,void set_time(){ //打印出从程序运行到目前所用的时间 printf("本次用时:%u s/n", clock() / CLOCKS_PER_SEC);}//初始化数组void init_array(char array[ROWS][COLS], int rows, int cols, char symbol){ int i = 0; int j = 0; for (i = 0; i < rows; i++) { for (j = 0; j < cols; j++) { array[i][j] = symbol; } }}//布置地雷void lay_mines(char mine[ROWS][COLS],int row,int col){ int count = NUM;//NUM为雷的个数 while (count)//循环条件,每次count-1 { int x = rand() % row + 1; int y = rand() % col + 1; if (mine[x][y] == "0") { mine[x][y] = "1"; count--; } }}//展示游戏界面void show_interface(char show[ROWS][COLS], int row, int col){ int i = 0; int j = 0; for (i = 0; i <= col; i++)//打印出列号 { printf("%d ", i); } printf("/n"); for (i = 0; i <= col; i++) { printf("—"); } printf("/n"); for (i = 1; i <= row; i++) { printf("%d|", i);//打印出行号 for (j = 1; j <= col; j++) { printf("%c ", show[i][j]); } printf("/n"); } for (i = 0; i <= col; i++) { printf("—"); } printf("/n");}//排查雷void mine_detection(char mine[ROWS][COLS],char show[ROWS][COLS], int row, int col){ int cnt = 0;//cnt为已排查出的雷的个数 int x = 0; int y = 0; while (cnt < ROW * COL - NUM)//循环条件是还有雷没有排查 { int ret = 0; //输入要排查雷的坐标 printf("请输入要排查的坐标:(X,Y)/n"); scanf("%d %d", &x, &y); if (cnt == 0)//第一次排雷 { if (x >= 1 && x <= row && y >= 1 && y <= col) { //首次输入的坐标如果是雷,则将雷移动到另一个位置 one_safe(mine, x, y);//保证第一次总不是雷 //因为无论如何都不是雷,直接跳转到记录雷和扩展的函数expand goto next; } else { printf("坐标非法,请重新输入:/n"); continue; } } //确保坐标在设定的范围中 if (x >= 1 && x <= row && y >= 1 && y <= col) { if (mine[x][y] == "0") { next://goto跳转到这里 expand(mine, show, x, y); //打印出排查过一次后的界面 show_interface(show, ROW, COL); cnt++;//排雷成功次数加一 } else { //如果排查的位置放的是‘1’则表示该位置为炸弹 printf("很遗憾,您被炸死了!/n"); //游戏结束,打印出所有雷的位置 show_interface(mine, ROW, COL); break; } } else { printf("坐标非法,请重新输入:/n"); } } if (cnt == ROW * COL - NUM)//可排雷个数为0时 { printf("恭喜您,排雷成功!/n"); //游戏结束 show_interface(mine, ROW, COL); }}//计算排查位置处周围雷的个数int calculate(char mine[ROWS][COLS],int x,int y){ //因为雷的位置放的是字符‘1’ // 加起来之后应该分别减去‘0’,才得到雷的个数 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";}//保证第一次不会踩到雷int one_safe(char mine[ROWS][COLS], int x, int y){ int count = 1; //判断第一次是否是雷 if (mine[x][y] == "1") { mine[x][y] = "0"; //将雷随机放入另一个没有雷的位置 while (count) { x = rand() % ROW + 1; y = rand() % COL + 1; if (mine[x][y] == "0") { mine[x][y] = "1"; count--; } } return 1;//是雷则返回1 } else return 0;//不是雷则返回0 //其实这里可以返回值也可以不返回,因为在我最开始写代码时,最开始的思路是: //非雷 是雷这两种情况分别进入不同的分支,不过后来又换了一种思路}//展开周围都没有雷的雷盘(扩展式排雷)void expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)//扩展函数,判断是否需要递归式展开。{ //利用calculate函数判断周围是否有雷 if (calculate(mine, x, y) == 0) {//判断周围雷的个数,若为0,则需要展开 show[x][y] = " ";//展开的位置都置为空格 int i = 0; int j = 0; //该位置可以拓展才检查周围8个位置是否能拓展 for (i = x - 1; i <= x + 1; i++) { for (j = y - 1; j <= y + 1; j++) { if (show[i][j] == "*" && i > 0 && i <= ROW && j > 0 && j <= COL) { //如果该位置未被扫过且在棋盘范围内则继续递归调用expand函数 //再依次进入判断周围8个位置是能被展开还是不能 expand(mine, show, i, j); } } } }
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/123190.html
摘要:展示雷盘和初始化雷盘不一样,展示雷盘只需要用即可,并不需要将都展示出来,只是为了我们更好的计算扫雷的位置周围的雷的数量。 目录 1、需求分析 2、程序架构 3、代码实现(分函数呈现) (1)主函数代码实现 分析: 异常处理: (2)游戏主函数实现 分析: (3)初始化函数的实现 分析: (4...
摘要:设计实现扫雷游戏大致思路创建文件想法实现设计一个函数,实现建议菜单循环和分支选择游戏选项创造一个扫雷版面版面的大小最后是要可控的如何存放雷和版面的信息呢考虑排查雷时候的思路,我们要判断该位置周围个格子里面是否有雷初始化 ...
摘要:新人小白的第一篇博客,有什么不好之处望多提意见。这个扫雷小游戏主要是基于二维数组,循环与基本的函数知识等。请输入坐标提示玩家输入坐标。换行是为了看着好看,要不然打印出来的数组会变形的。用来接收判断输赢的函数的返回值。 新人小白的第一篇博客,有什么不好之处望多提意见。 ...
摘要:上一期咱们用语言实现了三子棋的小游戏语言实现三子棋今天我们再来写个扫雷的游戏,说起扫雷,相信大家都不陌生,可能许多朋友还是玩扫雷的高手。 上一期咱们用C语言实现了三子棋的小游戏 C语言实现三子棋 今天我们再来写个扫雷的游戏,说起扫雷,相信大家都不陌生,可能许多朋友还是...
摘要:条消息语言入门三子棋语言实现详细版的博客博客条消息语言入门三子棋语言实现详细版的博客博客我们将雷盘初始化为统一的符号。 目录 1.原理简介 2.分布目标及代码实现 3.总结 1.原理简介 首先我们需要一个空的雷盘,在其中随机埋入十枚雷,当我们排这颗雷时,若此位置为雷,则游戏失败,若不...
阅读 2206·2021-11-17 09:33
阅读 2753·2021-11-12 10:36
阅读 3365·2021-09-27 13:47
阅读 854·2021-09-22 15:10
阅读 3451·2021-09-09 11:51
阅读 1347·2021-08-25 09:38
阅读 2732·2019-08-30 15:55
阅读 2572·2019-08-30 15:53