C语言中如何实现扫雷小游戏
这篇文章给大家分享的是有关C语言中如何实现扫雷小游戏的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。
游戏简介
扫雷,是一款益智类小游戏。 游戏目标是找出所有没有地雷的方格,完成游戏;要是按了有地雷的方格,游戏失败;玩家可标记雷的位置。游戏以完成时间来评高低。有不同的游戏难度可选择。
实现的功能介绍
1.计时 2.初始化雷盘 3.打印雷盘 4.随机设置雷的分布,可选择游戏难易程度 5.统计坐标位置周围的雷数 6.第一次排雷不会被炸死 7.扩展式排雷,展开周围的非雷区 8.给所选坐标位置做标记,或取消标记
该程序分为三个文件:
1.game.h :包含头文件的引用、函数的声明和宏定义 2.game.c :包含游戏各功能函数的具体实现 3.pro.c :各功能函数的调用(程序的流程) PS:文章末尾附完整代码 及 游戏效果图
因为排雷时要计算每个位置周围八个位置的雷数,所以在创建数组时要多一圈,即行列都要加2。给用户显示的数组不需要加。
游戏功能代码详解
1.计时
运用clock函数,该函数需要的头文件为 “time.h” 函数原型:clock_t clock(void); 功能:程序从启动到函数调用占用CPU的时间 这个函数返回从“开启这个程序进程”到“程序中调用clock()函数”时之间的CPU时钟计时单元(clock tick)数,在MSDN中称之为挂钟时间;若挂钟时间不可取,则返回-1。其中clock_t是用来保存时间的数据类型。
voidset_time()//计时 { printf("用时:%u秒\n",clock()/CLOCKS_PER_SEC); }
2.初始化雷盘 这里我用到的是memset函数,需要的头文件为“memory.h”或“string.h” 函数原型:void *memset(void *s, int ch, size_t n); 功能:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。在一段内存块中填充某个给定的值。
voidinit_board(charboard[ROWS][COLS],introw,intcol,charc)//初始化雷盘 { memset(board,c,row*col*sizeof(board[0][0])); }
3.打印雷盘 运用两个循环体实现雷盘数组的赋值、行号、列号的打印。正式游戏时可以加上system(“CLS”); 清屏语句,每次调用时都清屏一次,使游戏画面更简洁清晰。 我们把计时函数放在里面,每次打印雷盘时就可以显示所用的时间。
voiddisp_board(charboard[ROWS][COLS],introw,intcol)//打印雷盘 { inti=0; intj=0; //system("CLS");//清屏 for(i=0;i<=row;i++) { printf("%2d",i);//打印行号 } printf("\n"); for(i=1;i<=row;i++) { printf("%2d",i);//打印列号 for(j=1;j<=col;j++) { printf("%2c",board[i][j]); } printf("\n"); } printf("\n"); set_time();//打印所用的时间 }
4.随机设置雷的分布,可选择游戏难易程度 放置雷必须是随机的,这里用到了rand函数,它和srand函数配合使用产生随机数。srand(time(NULL))放在主函数中调用一次,通过系统时间提供的种子值,使rand函数生成不同的伪随机数序列。
voidset_mine(charboard[ROWS][COLS],introw,intcol,intcount)//置雷 { intx=0; inty=0; while(count) { x=rand()%row+1;//随机位置范围1~row y=rand()%col+1;//随机位置范围1~col if(board[x][y]=='0')//判断是否已有雷 { board[x][y]='1';//有雷的位置赋为1 count--; } } }
5.统计坐标位置周围的雷数 及 未扫的位置的个数 当扫到一个没有雷的位置时,会显示这个位置周围一圈八个位置的含雷的总数,所以我们要写一个“数雷”函数来数。
intcount_mine(charmine[ROWS][COLS],intx,inty)//数雷 { returnmine[x-1][y]+mine[x-1][y+1]+mine[x][y+1]+mine[x+1][y+1]+mine[x+1][y]+ mine[x+1][y-1]+mine[x][y-1]+mine[x-1][y-1]-8*'0';//数周围一圈八个位置的雷数 } intcount_print(charprint[ROWS][COLS],introw,intcol)//数未扫位置 { intcount=0; inti=0; for(i=1;i<=row;i++) { intj=0; for(j=1;j<=col;j++) { if(print[i][j]=='@'||print[i][j]=='*') { count++; } } } returncount; }
6.第一次排雷不会被炸死 为了增加游戏的可玩性,加入“第一次排雷不被炸死”这个函数。当第一次排就遇到雷时,我们把雷偷偷挪走,随机放在一个原本无雷的位置。
voidsafe_mine(charmine[ROWS][COLS],charprint[ROWS][COLS],intx,inty,introw,intcol)//第一次排雷不炸死 { charch=0; intret=1; intnumber=0; if(mine[x][y]=='1')//第一次踩到雷后补救 { mine[x][y]='0'; charch=count_mine(mine,x,y); print[x][y]=ch+'0';//数字对应的ASCII值和数字字符对应的ASCII值相差48,即'0'的ASCII值 extend_board(mine,print,x,y); while(ret)//在其余有空的地方设置一个雷 { intx=rand()%row+1;//产生1到row的随机数,在数组下标为1到10的范围内布雷 inty=rand()%col+1;//产生1到col的随机数,在数组下标为1到10的范围内布雷 if(mine[x][y]=='0')//找不是雷的地方布雷 { mine[x][y]='1'; disp_board(print,row,col); //disp_board(mine,row,col); ret--; break; } } } }
7.扩展式排雷,展开周围的非雷区 当游戏中排到一个周围一圈都无雷的位置时,运用递归,实现扩展展开周围的一片无雷区。
voidextend_board(charmine[ROWS][COLS],charprint[ROWS][COLS],intx,inty)//运用递归扩展周围 { intn=0; n=count_mine(mine,x,y); if(n==0)//当该位置周围雷数为0时扩展 { print[x][y]='';//扩展的位置变为“空格”打印出来 if(mine[x-1][y]=='0'&&print[x-1][y]=='@') { extend_board(mine,print,x-1,y);//递归 } if(mine[x+1][y]=='0'&&print[x+1][y]=='@') { extend_board(mine,print,x+1,y); } if(mine[x][y+1]=='0'&&print[x][y+1]=='@') { extend_board(mine,print,x,y+1); } if(mine[x-1][y+1]=='0'&&print[x-1][y+1]=='@') { extend_board(mine,print,x-1,y+1); } if(mine[x+1][y+1]=='0'&&print[x+1][y+1]=='@') { extend_board(mine,print,x+1,y+1); } if(mine[x][y-1]=='0'&&print[x][y-1]=='@') { extend_board(mine,print,x,y-1); } if(mine[x+1][y-1]=='0'&&print[x+1][y-1]=='@') { extend_board(mine,print,x+1,y-1); } if(mine[x-1][y-1]=='0'&&print[x-1][y-1]=='@') { extend_board(mine,print,x-1,y-1); } } else print[x][y]=n+'0'; } intfind_mine(charmine[ROWS][COLS],charprint[ROWS][COLS],introw,intcol,intcount)//排雷 { intx=0; inty=0; intnumber=0; intret=0; while(1) { printf("输入坐标扫雷\n"); scanf("%d%d",&x,&y);//玩家输入扫雷的坐标位置 if((x>=1&&x<=row)&&(y>=1&&y<=col))//判断输入坐标是否有误,输入错误重新输入 { if(mine[x][y]=='0')//没踩到雷 { number++;//记录扫雷的次数 charch=count_mine(mine,x,y);//数雷数 print[x][y]=ch+'0';//数字对应的ASCII值和数字字符对应的ASCII值相差48,即'0'的ASCII值 extend_board(mine,print,x,y); disp_board(mine,row,col); disp_board(print,row,col); if(count_print(print,row,col)==count)//剩余未扫位置=雷数时胜利 { return0; } to_sign(print);//判断是否标记 disp_board(print,row,col); } elseif(mine[x][y]=='1')//踩到雷 { if(ret==0&&number==0) { ret++; safe_mine(mine,print,x,y,row,col); } else return1; } } else { printf("输入错误!请重新输入\n"); } } }
8.给所选坐标位置做标记,或取消标记 扫雷游戏还有一个功能:可以给你认为是雷的位置标记,或者取消标记。 我通过三个函数来实现,一个判断用户是否需要标记;一个实现标记功能,将@标记成* ;一个实现取消标记功能,将* 改回@。
voidto_sign(charboard[ROWS][COLS])//判断是否标记 { intchose_b=0; intx=0; inty=0; printf("是否需要标记/取消标记:>\n(1.标记;2.取消标记;3.跳过该步骤):>"); scanf("%d",&chose_b); do{ switch(chose_b) { case1: { printf("请输入需要标记的位置坐标:>\n"); scanf("%d%d",&x,&y); sign(board,x,y); break; } case2: { printf("请输入取消标记的位置坐标:>\n"); scanf("%d%d",&x,&y); unsign(board,x,y); break; } case3: { printf("跳过此步骤。\n"); chose_b=0; break; } default: {printf("输入错误!\n"); chose_b=0; break; } } chose_b=0; }while(chose_b); } voidsign(charboard[ROWS][COLS],intx,inty)//用‘*'标记雷 { if(board[x][y]=='@') { board[x][y]='*'; } } voidunsign(charboard[ROWS][COLS],intx,inty)//取消标记 { if(board[x][y]=='*') { board[x][y]='@'; } }
附:完整代码
game.h
#ifndef_GAME_H_ #define_GAME_H_ //用到的头文件 #include#include #include #include #include //定义打印的雷盘行、列 #define_ROW9 #define_COL9 #defineROW16 #defineCOL16 //定义数组的行、列 #define_ROWS_ROW+2 #define_COLS_COL+2 #defineROWSROW+2 #defineCOLSCOL+2 //定义难、易程度雷数 #defineEASY_COUNT10 #defineHARD_COUNT40 //定义游戏中的函数 voidinit_board(charboard[ROWS][COLS],introw,intcol,charc);//初始化 voiddisp_board(charboard[ROWS][COLS],introw,intcol);//打印 voidset_mine(charboard[ROWS][COLS],introw,intcol,intcount);//置雷 voidsafe_mine(charmine[ROWS][COLS],charprint[ROWS][COLS],intx,inty,introw,intcol);//第一次排雷不炸死 intfind_mine(charmine[ROWS][COLS],charprint[ROWS][COLS],introw,intcol,intcount);//排雷 intcount_mine(charmine[ROWS][COLS],intx,inty);//数雷 voidextend_board(charmine[ROWS][COLS],charprint[ROWS][COLS],intx,inty);//扩展 voidto_sign(charboard[ROWS][COLS]);//判断是否标记 voidsign(charboard[ROWS][COLS],intx,inty);//标记 voidunsign(charboard[ROWS][COLS],intx,inty);//取消标记 intcount_print(charprint[ROWS][COLS],introw,intcol);//数未扫位置 #endif//_GAME_H_
game.c
#define_CRT_SECURE_NO_WARNINGS #include"game.h" voidset_time()//计时 { printf("用时:%u秒\n",clock()/CLOCKS_PER_SEC); } voidinit_board(charboard[ROWS][COLS],introw,intcol,charc)//初始化雷盘 { memset(board,c,row*col*sizeof(board[0][0])); } voiddisp_board(charboard[ROWS][COLS],introw,intcol)//打印雷盘 { inti=0; intj=0; system("CLS");//清屏 for(i=0;i<=row;i++)//加行号 { printf("%2d",i); } printf("\n"); for(i=1;i<=row;i++)//加列号 { printf("%2d",i); for(j=1;j<=col;j++) { printf("%2c",board[i][j]); } printf("\n"); } printf("\n"); set_time();//打印所用的时间 } voidset_mine(charboard[ROWS][COLS],introw,intcol,intcount)//置雷 { intx=0; inty=0; while(count) { x=rand()%row+1;//随机位置范围1~row y=rand()%col+1;//随机位置范围1~col if(board[x][y]=='0')//判断是否已有雷 { board[x][y]='1'; count--; } } } voidsafe_mine(charmine[ROWS][COLS],charprint[ROWS][COLS],intx,inty,introw,intcol)//第一次排雷不炸死 { charch=0; intret=1; intnumber=0; if(mine[x][y]=='1')//第一次踩到雷后补救 { mine[x][y]='0'; charch=count_mine(mine,x,y); print[x][y]=ch+'0';//数字对应的ASCII值和数字字符对应的ASCII值相差48,即'0'的ASCII值 extend_board(mine,print,x,y); while(ret)//在其余有空的地方设置一个雷 { intx=rand()%row+1;//产生1到row的随机数,在数组下标为1到10的范围内布雷 inty=rand()%col+1;//产生1到col的随机数,在数组下标为1到10的范围内布雷 if(mine[x][y]=='0')//找不是雷的地方布雷 { mine[x][y]='1'; disp_board(print,row,col); //disp_board(mine,row,col); ret--; break; } } } } intcount_mine(charmine[ROWS][COLS],intx,inty)//数雷 { returnmine[x-1][y]+mine[x-1][y+1]+mine[x][y+1]+mine[x+1][y+1]+mine[x+1][y]+ mine[x+1][y-1]+mine[x][y-1]+mine[x-1][y-1]-8*'0';//数周围一圈八个位置的雷数 } intcount_print(charprint[ROWS][COLS],introw,intcol)//数未扫位置 { intcount=0; inti=0; for(i=1;i<=row;i++) { intj=0; for(j=1;j<=col;j++) { if(print[i][j]=='@'||print[i][j]=='*') { count++; } } } returncount; } voidextend_board(charmine[ROWS][COLS],charprint[ROWS][COLS],intx,inty)//运用递归扩展周围 { intn=0; n=count_mine(mine,x,y); if(n==0)//当该位置周围雷数为0时扩展 { print[x][y]='';//扩展的位置变为“空格”打印出来 if(mine[x-1][y]=='0'&&print[x-1][y]=='@') { extend_board(mine,print,x-1,y);//递归 } if(mine[x+1][y]=='0'&&print[x+1][y]=='@') { extend_board(mine,print,x+1,y); } if(mine[x][y+1]=='0'&&print[x][y+1]=='@') { extend_board(mine,print,x,y+1); } if(mine[x-1][y+1]=='0'&&print[x-1][y+1]=='@') { extend_board(mine,print,x-1,y+1); } if(mine[x+1][y+1]=='0'&&print[x+1][y+1]=='@') { extend_board(mine,print,x+1,y+1); } if(mine[x][y-1]=='0'&&print[x][y-1]=='@') { extend_board(mine,print,x,y-1); } if(mine[x+1][y-1]=='0'&&print[x+1][y-1]=='@') { extend_board(mine,print,x+1,y-1); } if(mine[x-1][y-1]=='0'&&print[x-1][y-1]=='@') { extend_board(mine,print,x-1,y-1); } } else print[x][y]=n+'0'; } intfind_mine(charmine[ROWS][COLS],charprint[ROWS][COLS],introw,intcol,intcount)//排雷 { intx=0; inty=0; intnumber=0; intret=0; while(1) { printf("输入坐标扫雷\n"); scanf("%d%d",&x,&y);//玩家输入扫雷的坐标位置 if((x>=1&&x<=row)&&(y>=1&&y<=col))//判断输入坐标是否有误,输入错误重新输入 { if(mine[x][y]=='0')//没踩到雷 { number++;//记录扫雷的次数 charch=count_mine(mine,x,y);//数雷数 print[x][y]=ch+'0';//数字对应的ASCII值和数字字符对应的ASCII值相差48,即'0'的ASCII值 extend_board(mine,print,x,y); //disp_board(mine,row,col); disp_board(print,row,col); if(count_print(print,row,col)==count)//剩余未扫位置=雷数时胜利 { return0; } to_sign(print);//判断是否标记 disp_board(print,row,col); } elseif(mine[x][y]=='1')//踩到雷 { if(ret==0&&number==0) { ret++; safe_mine(mine,print,x,y,row,col); } else return1; } } else { printf("输入错误!请重新输入\n"); } } } voidsign(charboard[ROWS][COLS],intx,inty)//用‘*'标记雷 { if(board[x][y]=='@') { board[x][y]='*'; } } voidunsign(charboard[ROWS][COLS],intx,inty)//取消标记 { if(board[x][y]=='*') { board[x][y]='@'; } } voidto_sign(charboard[ROWS][COLS])//判断是否标记 { intchose_b=0; intx=0; inty=0; printf("是否需要标记/取消标记:>\n(1.标记;2.取消标记;3.跳过该步骤):>"); scanf("%d",&chose_b); do{ switch(chose_b) { case1: { printf("请输入需要标记的位置坐标:>\n"); scanf("%d%d",&x,&y); sign(board,x,y); break; } case2: { printf("请输入取消标记的位置坐标:>\n"); scanf("%d%d",&x,&y); unsign(board,x,y); break; } case3: { printf("跳过此步骤。\n"); chose_b=0; break; } default: {printf("输入错误!\n"); chose_b=0; break; } } chose_b=0; }while(chose_b); }
pro.c
#define_CRT_SECURE_NO_WARNINGS #include"game.h" voidmenu() { printf("+---------------------------------+\n"); printf("+Welcometo扫雷世界!+\n"); printf("+ο(=>ω<=)ρ⌒☆+\n"); printf("+1、play+\n"); printf("+0、exit+\n"); printf("+---------------------------------+\n"); } voidgame() { charmine[ROWS][COLS]={0}; charprint[ROWS][COLS]={0}; intchose_m=0; intret=0; printf("请选择模式(1、简单2、困难):>");//选择游戏难易程度,产生不同大小的棋盘和雷数 scanf("%d",&chose_m); switch(chose_m) { case1: { init_board(mine,ROWS,COLS,'0');//初始化雷盘 init_board(print,ROWS,COLS,'@'); set_mine(mine,_ROW,_COL,EASY_COUNT);//布雷 //disp_board(mine,_ROW,_COL);//打印雷盘 disp_board(print,_ROW,_COL); intret=find_mine(mine,print,_ROW,_COL,EASY_COUNT);//扫雷,踩到雷返回1,没有踩到雷返回0 while(1)//循环扫雷 { if(ret==0)//若返回0则胜利 { disp_board(print,_ROW,_COL); printf("WOW~YOUWIN!\n\n"); break; } if(ret)//若返回1则失败 { disp_board(mine,_ROW,_COL);//打印雷盘 printf("GAMEOVER!\n"); break; } disp_board(print,_ROW,_COL);//打印玩家棋盘 } break; } case2: { init_board(mine,ROWS,COLS,'0');//初始化雷盘 init_board(print,ROWS,COLS,'@'); set_mine(mine,ROW,COL,HARD_COUNT);//布雷 //disp_board(mine,ROW,COL);//打印雷盘 disp_board(print,ROW,COL); while(1)//循环扫雷 { intret=find_mine(mine,print,ROW,COL,HARD_COUNT);//扫雷,踩到雷返回1,没有踩到雷返回0 if(ret==0)//若返回0胜利 { disp_board(print,ROW,COL); printf("WOW~YOUWIN!\n\n"); break; } if(ret)//若返回1失败 { disp_board(mine,ROW,COL);//打印雷盘 printf("GAMEOVER!\n"); break; } disp_board(print,ROW,COL);//打印玩家棋盘 } break; } default: { printf("输入错误!\n"); break; } } } voidtext() { srand((unsignedint)time(NULL));//产生随机值发生器 intchose=0;//选择是否开始游戏 do { menu();//菜单 printf("请选择:>"); scanf("%d",&chose); switch(chose) { case1: game();//开始游戏 break; case0: printf("退出游戏\n"); break; default: printf("输入错误,没有该选项\n"); break; } }while(chose); } intmain() { text(); system("pause"); return0; }
游戏效果图
①开始选择菜单、难易模式选择②两种难度扫雷 ↓9×9雷盘 10颗雷↓16×16雷盘 40颗雷③演示标记④GAME OVER 玩家失败演示⑤WIN 玩家成功演示
感谢各位的阅读!关于“C语言中如何实现扫雷小游戏”这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,让大家可以学到更多知识,如果觉得文章不错,可以把它分享出去让更多的人看到吧!
推荐阅读
windows安装touble c
近期有些网友想要了解windows?安装touble的相关情况,小编通过整理给您分享一下。为什么现在还需要TurboC?在当今V...
C/C++如何获取CAN信号
C/C++如何获取CAN信号本篇内容主要讲解“C/C++如何获取C...
C语言怎么通过二分查找实现猜数字游戏
C语言怎么通过二分查找实现猜数字游戏本文小编为大家详细介绍“C语言...
C语言数据结构中的线性表怎么使用
C语言数据结构中的线性表怎么使用这篇文章主要介绍“C语言数据结构中...
C语言的数据结构怎么理解
C语言的数据结构怎么理解这篇文章主要介绍了C语言的数据结构怎么理解...
C语言与C++中内存管理的方法
C语言与C++中内存管理的方法这篇文章主要介绍了C语言与C++中内...
C语言链式队列与循环队列怎么实现
C语言链式队列与循环队列怎么实现这篇文章主要介绍了C语言链式队列与...
C语言冒泡排序怎么实现
C语言冒泡排序怎么实现这篇文章主要介绍了C语言冒泡排序怎么实现的相...
C语言如何实现斐波那契数列
C语言如何实现斐波那契数列这篇文章主要介绍了C语言如何实现斐波那契...
C语言如何实现无符号数和有符号数间的运算
C语言如何实现无符号数和有符号数间的运算本篇内容主要讲解“C语言如...