C语言之扫雷游戏(初级版)
1. 扫雷游戏介绍扫雷是一款众所周知的经典电脑小游戏,每次游戏开始于一个9*9的网格,系统通过算法随机分配10颗地雷,玩家需要做的,是排查出这10颗雷,既知道这10颗雷所在的位置.今天就让我学习如何通过C语言来实现这款小游戏2. 扫雷游戏的实现方案1.游戏的方格我们通过二维数组来实现,第一个数组mine我们来存放雷的位置,第二个数组show我们来展示扫雷游戏的图形页面.2.创建数组时为了防止越界,我们统一创建成11*11的二维数组3.以上创建的数组均为字符数组4.该项目我们通过工程化的思维去解决,创建三个文件来实现分工main.c : 用于测试游戏运行game.c : 用于存放游戏的实现代码game.h :用于声明函数与调用头文件3.游戏实现逻辑3.1 游戏菜单界面打印对于游戏初始化菜单我们可以通过printf函数来实现功能,具体代码如下voidmenu(){printf(*****************\n);printf(*****扫雷游戏****\n);printf(***1.开始游戏****\n);printf(***0.退出游戏****\n);printf(*****************\n);printf(请输入:);}页面展示大家也可以根据自己喜好来设置菜单3.2 游戏棋盘的设置3.2.1 先定义棋盘的大小我们要创建一个9 * 9的棋盘,但是为了防止数组越界,我们应创建11*11的棋盘来保证无越界风险,为了方便,我们通过宏定义的方式来定义棋盘边界,下次若想改变棋盘大小,只需在game.h中改变宏定义即可#defineROW9#defineCOL9#defineROWS(ROW2)#defineCOLS(COL2)3.2.2 棋盘的布置我们可以创建两个二维字符数组数组mine来标记地雷的位置,0表示安全,1表示地雷数组show来展示棋盘界面,未查找的坐标,我们用字符*表示,已查找的坐标我们直接显示它周围地雷个数//雷盘charmine[ROWS][COLS];//展示盘charshow[ROWS][COLS];3.2.3 棋盘的初始化我们可以创建一个专门的函数用于棋盘的初始化以下为初始化棋盘函数代码展示//初始化棋盘//初始化要彻底!!!!voidinit_keyboard(charkeyboard[ROWS][COLS],introws,intcols,charset){for(inti0;irows;i){for(intj0;jcols;j){keyboard[i][j]set;}}}3.2.4 棋盘的打印我们打印棋盘是要考虑玩家的观感体验,所以要给棋盘打上标号,以下为打印棋盘函数代码展示//打印棋盘voidprint_keyboard(charkeyboard[ROWS][COLS],introw,intcol){for(inti0;icol;i)//此处我们打印列标{printf(%d ,i);}printf(\n);for(inti1;irow;i){printf(%d ,i);//行标打印for(intj1;jcol;j){printf(%c ,keyboard[i][j]);}printf(\n);}printf(\n);}只需要打印show数组对于show数组,打印效果如下3.2.5 布置雷对于雷的布置,我们要用到rand函数,所以要用到头文件time.h和stdlib.h,同时我们还需要初始化随机数种子,因为一整局下来地雷位置是不变的,所以我们只需要在main.c文件中初始化随机数种子即可.以下为布置雷函数的代码展示//布置雷voidset_mine(charkeyboard[ROWS][COLS],introw,intcol){intcountEASY_COUNT;while(count){intxrand()%row1;intyrand()%col1;if(keyboard[x][y]0){keyboard[x][y]1;count--;}}}布置完地雷后棋盘打印如下在实际游戏中,布置完地雷后不需要打印mine数组,不要让玩家一开始就看见你的地雷位置哦~.当然,我们做测试的时候可以打印mine数组来检查代码是否正确3.2.6 排雷接下来就是我们最关键的排雷函数部分我们先规定总雷数,然后计算出安全格个数,每次成功排查出一个安全的格子,我们的安全格就减一,用while循环的方式,当安全格为0时,游戏胜利.inttotal_mineEASY_COUNT;intsafe_placerow*col-total_mine;我们让玩家输入坐标,选中要排查的格子,定义坐标此处通过对scanf返回值的校验,当玩家输入非数组字符时,就会触发拦截,提醒玩家输入正确字符.然后使用getchar()函数,对缓存区进行物理清零,防止程序死循环intx0,y0;printf(请输入需要查找的坐标\n);// 功能: 拦截非法字符输入(如字母、符号)防止程序死循环挂起if(scanf(%d%d,x,y)!2){printf(请输入正确的字符\n);//缓冲区物理清空while(getchar()!\n);continue;}当输入合法坐标后,我们就要对该坐标进行排查,首先确保输入字符在棋盘范围内,利用if...else...结构,将不合法坐标排查在外若检查的坐标为地雷,则触发淘汰提示词,然后打印mine数组,告知玩家字符位置,并结束游戏若玩家检查的不是地雷,则在检查位置显示其周围地雷个数,我们这可以通过对周围一圈中mine数组元素求和的方式,来统计检查位置周围地雷个数这里的字符相加实际上是ASCII相加,所以要减去字符’0’的ASCII码值,得到的才是周围真正的总雷数//add_mines函数intadd(charmine[ROWS][COLS],intx,inty){/*这里的字符相加实际上是ASCII相加,所以要减去字符0的ASCII码值, 得到的才是周围真正的总雷数*/return(mine[x1][y-1]mine[x1][y]mine[x1][y1]mine[x][y-1]mine[x][y1]mine[x-1][y-1]mine[x-1][y]mine[x-1][y1])-8*0;}如果玩家输入之前以排查过的坐标我们要记得提醒玩家,不要在一个地方重复排查哦排雷函数代码实现如下voidfind_mine(charmine[ROWS][COLS],charshow[ROWS][COLS],introw,intcol){inttotal_mineEASY_COUNT;intsafe_placerow*col-total_mine;while(safe_place){intx0,y0;printf(请输入需要查找的坐标\n);// 功能: 拦截非法字符输入(如字母、符号)防止程序死循环挂起if(scanf(%d%d,x,y)!2){printf(请输入正确的字符\n);//缓冲区物理清空while(getchar()!\n);continue;}if(x1xrowy1ycol){//踩雷了if(mine[x][y]1){printf(你被炸死了\n);print_keyboard(mine,row,col);break;}else//没踩雷{if(show[x][y]!*)//防止同一个位置重复排查{printf(该坐标已排查,请重新输入\n);}else{intcount_minesadd(mine,x,y);/*因为count_mines本质还是一个整型数字,要转化成字符,需将其变成其本身数字对应的ASCII码值*/show[x][y]count_mines0;print_keyboard(show,row,col);safe_place--;}}if(safe_place0){printf(你赢了!!!\n);}}else{printf(坐标无效,请重新输入\n);}}}其中show[x][y] count_mines 0;这个部分,因为count_mines本质还是一个整型数字,要转化成字符,需将其变成其本身数字对应的ASCII码值这样我们整体的主体代码就基本完成了4. 游戏代码game.h#pragmaonce#includestdio.h#includestdlib.h#includetime.h#defineEASY_COUNT10#defineROW9#defineCOL9#defineROWS(ROW2)#defineCOLS(COL2)//初始化棋盘voidinit_keyboard(charkeyboard[ROWS][COLS],introws,intcols,charset);//打印棋盘voidprint_keyboard(charkeyboard[ROWS][COLS],introw,intcol);///随机布置雷voidset_mine(charkeyboard[ROWS][COLS],introw,intcol);//排雷voidfind_mine(charmine[ROWS][COLS],charshow[ROWS][COLS],introw,intcol);game.c#define_CRT_SECURE_NO_WARNINGS#includegame.h//初始化棋盘//初始化要彻底!!!!voidinit_keyboard(charkeyboard[ROWS][COLS],introws,intcols,charset){for(inti0;irows;i){for(intj0;jcols;j){keyboard[i][j]set;}}}//打印棋盘voidprint_keyboard(charkeyboard[ROWS][COLS],introw,intcol){for(inti0;icol;i){printf(%d ,i);}printf(\n);for(inti1;irow;i){printf(%d ,i);for(intj1;jcol;j){printf(%c ,keyboard[i][j]);}printf(\n);}printf(\n);}//布置雷voidset_mine(charkeyboard[ROWS][COLS],introw,intcol){intcountEASY_COUNT;while(count){intxrand()%row1;intyrand()%col1;if(keyboard[x][y]0){keyboard[x][y]1;count--;}}}//add_mines函数intadd(charmine[ROWS][COLS],intx,inty){//这里的字符相加实际上是ASCII相加,所以要减去字符0的ASCII码值,得到的才是周围真正的总雷数return(mine[x1][y-1]mine[x1][y]mine[x1][y1]mine[x][y-1]mine[x][y1]mine[x-1][y-1]mine[x-1][y]mine[x-1][y1])-8*0;}//排雷voidfind_mine(charmine[ROWS][COLS],charshow[ROWS][COLS],introw,intcol){inttotal_mineEASY_COUNT;intsafe_placerow*col-total_mine;while(safe_place){intx0,y0;printf(请输入需要查找的坐标\n);// 功能: 拦截非法字符输入(如字母、符号)防止程序死循环挂起if(scanf(%d%d,x,y)!2){printf(请输入正确的字符\n);//缓冲区物理清空while(getchar()!\n);continue;}if(x1xrowy1ycol){//踩雷了if(mine[x][y]1){printf(你被炸死了\n);print_keyboard(mine,row,col);break;}else//没踩雷{if(show[x][y]!*)//防止同一个位置重复排查{printf(该坐标已排查,请重新输入\n);}else{intcount_minesadd(mine,x,y);/*因为count_mines本质还是一个整型数字,要转化成字符,需将其变成其本身数字对应的ASCII码值*/show[x][y]count_mines0;print_keyboard(show,row,col);safe_place--;}}if(safe_place0){printf(你赢了!!!\n);}}else{printf(坐标无效,请重新输入\n);}}}main.c#define_CRT_SECURE_NO_WARNINGS#includegame.hvoidmenu(){printf(*****************\n);printf(*****扫雷游戏****\n);printf(***1.开始游戏****\n);printf(***0.退出游戏****\n);printf(*****************\n);printf(请输入:);}voidgame(){printf(\n);//雷盘charmine[ROWS][COLS];//展示盘charshow[ROWS][COLS];//初始化棋盘init_keyboard(mine,ROWS,COLS,0);init_keyboard(show,ROWS,COLS,*);//打印棋盘print_keyboard(show,ROW,COL);/*print_keyboard(mine, ROW, COL);*///布置雷set_mine(mine,ROW,COL);/*print_keyboard(mine, ROW, COL);*///排雷find_mine(mine,show,ROW,COL);}intmain(){//初始化随机种子srand((unsignedint)time(NULL));intinput0;do{menu();if(scanf(%d,input)!1){printf(请输入正确的字符\n);while(getchar()!\n);continue;}switch(input){case1:{game();break;}case0:{printf(已退出游戏\n);return0;//退出游戏}default:{printf(输入错误,请重新输入\n);break;}}}while(1);return0;}5.碎碎念讲讲我遇到的一些问题吧我一开始棋盘初始化时没有将mine数组初始化彻底,只初始化了9*9的mine数组,这样会导致周围一圈全是随机数,所以大家初始化一定要彻底啊! ! !另外在scanf输入读取的时候,忽略了返回值,没有考虑字母或者其他非法字符的输入,导致了程序的死循环,scnaf读取时,还是要考虑其返回值啊,毕竟vs2022的提醒不是毫无道理的我在一开始调用函数的时候,是直接将声明的函数复制过去用的,忘记删除返回类型,导致错误﹏例如void print_keyboard(char keyboard[ROWS][COLS], int row, int col);还有就是在统计查找点周围地雷总数的时候,没有考虑到int类型数字到char类型数字的转换,如果要将一个int类型数字打印成其对应的char类型数字,需将其变成其本身数字对应的ASCII码值6. 总结这款扫雷游戏用到的知识并不复杂,大家可以都尝试做一下.有实力的小伙伴也可以去尝试做一个我们平常玩的那个扫雷游戏时点开,若排查点周围无雷,自动向四周延展排查的效果,或者优化一下UI界面之类.以上就是本篇博客的所有内容啦,如果你喜欢,不妨点个免费的赞,谢谢⭐