失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > C语言实现扫雷完整代码讲解

C语言实现扫雷完整代码讲解

时间:2021-12-12 06:06:42

相关推荐

C语言实现扫雷完整代码讲解

扫雷是一款初学者能用C语言实现的益智小游戏,只需要用二维数组操作雷区即可。

算法详讲 :

游戏思路如下:点击雷区中的任一格子,如果该格子有雷,则游戏结束;如果该格子周围9个格子都不是雷,那么直接展开其附近的九个格子,如此持续下去(说明待会要用到递归),假如该格子附近有雷,则不展开其周围的格子,但要展现出该格子周围的雷数。

根据游戏思路可知,我们需要两个二维数组,一个mine数组,用来记录雷的分布信息,另外一个是show数组,用以为玩家展示排查后雷区的信息。程序所要实现的几个主要功能是:1.初始化数组。2.打印数组。3.随机设置雷。4.排查雷。5.计算某个坐标周围雷的个数。6.玩家选择一个坐标后,展开周围坐标直至周围有雷的坐标。6.根据玩家需要,标记雷的位置。

注意:由于我们在排查边界的雷的情况时,程序会出现越界问题,因此mine、show数组的实际大小要比原来多两行与两列。

常量展示:

#define _CRT_SECURE_NO_WARNINGS #include<stdio.h>#include <stdlib.h> #include<time.h>//雷区的大小#define Row 9#define Col 9//数组实际大小#define Rows Row+2#define Cols Col+2//方便改变雷的个数#define Easydegree 10//统计扫雷的个数int win;

主要功能函数展示:

//初始化函数void Initboard(char board[Rows][Cols],int rows,int cols,char ch);//打印void Showboard(char board[Rows][Cols], int row, int col);//放雷void Setmine(char board[Rows][Cols], int row, int col);//排雷void Cleanmine(char mine[Rows][Cols], char show[Rows][Cols], int row,int col);

test.c部分:

#include"game.h"//扫雷游戏void menu(){printf("*************************\n");printf("****** 1.play ******\n");printf("****** 0.out******\n");printf("*************************\n");return;}void game(){srand((unsigned int)time(NULL));//printf("扫雷游戏\n");//操作雷//‘0’表示没雷,‘1’表示有雷char mine[Rows][Cols] = { 0 };//展现图上的信息//没有扫前图上是'*'char show[Rows][Cols] = { 0 };//初始化mine与showInitboard(mine, Rows, Cols,'0');Initboard(show, Rows, Cols, '*');//打印//Showboard(mine, Row, Col);//Showboard(show, Row, Col);//放雷Setmine(mine, Row, Col);Showboard(mine, Row, Col);Showboard(show, Row, Col);//排雷Cleanmine(mine, show, Row, Col);return;}void test(){int input;do{menu();scanf("%d", &input);switch (input){case 1:printf("扫雷游戏\n");game();break;case 0:printf("退出游戏\n");break;default:printf("输入错误,请重新输入\n");break;}} while (input);}int main(){test();return 0;}

初始化数组:

mine数组的初始元素为'0',表示没有雷;shou数组的初始元素为'*',表示雷区未知;

//初始化棋盘void Initboard(char board[Rows][Cols], int rows, int cols, char ch){int i, j;for (int i = 0; i < rows; i++){for (int j = 0; j < cols; j++){board[i][j] = ch;}}return;}

Tips:由于两个数组的初始化内容不同,函数的功能如需适用于两者,则需多一个初始化内容的参数(函数中的char ch用来接收初始化内容),如果元素单一就传单一变量,否则可传结构体指针.

打印棋盘:

//打印棋盘void Showboard(char board[Rows][Cols], int row, int col){printf("-----------扫雷----------\n");int i = 0;int j = 0;for (i = 0; i <= col; i++)printf("%d", i);printf("\n");for (i = 1; i <= row; i++){printf("%d", i);for (j = 1; j <= col; j++){printf("%c", board[i][j]);}printf("\n");}printf("win=%d\n", win);printf("-----------扫雷----------\n");return;}

Tip:为了使提高玩家体验,减少查找排雷位置的坐标的时间,我们可以额外打印一个坐标系。

设置雷:

void Setmine(char board[Rows][Cols], int row, int col){//输入雷的个数int num = Easydegree;//判断是否应该放雷while (num){int x = rand() % row + 1;int y = rand() % col + 1;if (board[x][y] == '0'){board[x][y] = '1';num--;}}return;}

Tip1:设置常量Easydegree代表雷的个数,减少后续函数参数的数量,因为后续函数判断排雷是否成功,可以通过这个常量Easydegree;

Tip2:用rand函数生成随机数,使用前需要引用srand函数,而且该函数只需要放在test()函数中即可,无需每次进入循环都要生成;

srand((unsigned int)time(NULL));

排查雷:

void Cleanmine(char mine[Rows][Cols], char show[Rows][Cols], int row, int col){//游戏规则:若未知空格里全是雷,则游戏胜利//排雷的过过程中有三种情况:排对了,排错了,位置错了int ret = 0;while (win < (row*col - Easydegree)&&ret==0){int i, j;printf("please enter the location:>\n");scanf("%d%d", &i, &j);if (i < 1 || i > row || j <1 || j > col || show[i][j] != '*'){if (i < 1 || i > row || j <1 || j > col)printf("坐标越界\n");elseprintf("排查重复\n");continue;}else if (mine[i][j] == '1'){printf("你被炸飞辣!游戏失败!\n");break;}else if (mine[i][j] == '0'){//修改查地图//直接在函数内部修改//而且这是一个递归函数get_clean(mine,show, row, col, i, j);Showboard(show, Row, Col);//标记炸弹ret=Mark(mine,show, Row, Col);Showboard(show, Row, Col);}}if (win == (row*col - Easydegree)){printf("游戏胜利!\n");}return;}

Tip:有了全局变量win统计已排雷个数,随时随地都可以知道我们是否已经胜利,但是要注意,全局变量尽量不要滥用,少用、精用、慎用。

同时,该函数内部调用了get_clean( )与Mark( )函数,前者统计被排查格子周围的雷的个数,并修改show数组,是一个递归函数,后者是标记函数,标记你认为是雷的位置,但是如果你标记错了,那就直接游戏失败,因为若该格子无雷也能被你标记了,那是不是就可以作弊辣(*/ω\*)(*/ω\*)(*/ω\*)。。。

统计修改函数get_clean( ):

void get_clean(char mine[Rows][Cols], char show[Rows][Cols], int row, int col, int i, int j){//递归的直接条件//1.越界即归//2.排查过的地方即归//3.win=(row*col - Easydegree)即归//边界无需扫描if (win == (row*col - Easydegree))return;if (i == 0 || j == 0 || i == row +1 || j == col + 1)return;//避免重复扫描,当shou[i][j]=='0'没必要再扫描if (show[i][j] != '*')return;//能够进来说明排雷成功一次,win++;win++;//统计(i,j)位置周围的雷的个数int num = mine[i][j - 1] + mine[i][j + 1] + mine[i - 1][j - 1] + mine[i - 1][j] +mine[i - 1][j + 1] + mine[i + 1][j - 1] + mine[i + 1][j] +mine[i + 1][j + 1] - 8 * '0';//如果周围没有雷,该位置改为0,继续搜索//如果有雷那么,该位置打印雷数,直接返回if (num == 0){show[i][j] = '0';get_clean(mine, show, row, col, i, j-1);get_clean(mine, show, row, col, i, j+1);get_clean(mine, show, row, col, i-1, j-1);get_clean(mine, show, row, col, i-1, j);get_clean(mine, show, row, col, i-1, j+1);get_clean(mine, show, row, col, i+1, j-1);get_clean(mine, show, row, col, i+1, j);get_clean(mine, show, row, col, i+1, j+1);return ;}else{show[i][j] = '0' + num;return;}}

warming:在书写递归函数时一定要注意终结条件,上述递归函数的终结条件有多个:1.被排查位置是否越界?2.被排查位置是否已被排除过?3.游戏是否已经结束。

标记函数Mark( ):

int Mark(char mine[Rows][Cols], char show[Rows][Cols], int row, int col){int num;printf("How many mine do you want to mark?>:\n");scanf("%d", &num);//如果标记错误直接失败while (1){if (num == 0){return 0;}int i, j;printf("please enter the location>:\n");scanf("%d%d", &i, &j);if (mine[i][j] == '1'){//在show数组上,打印排查情况,永远不影响mine数组,保留原数据show[i][j] = 'B';num--;}else{printf("标记错误,游戏失败\n");return 1;}}return;}

tip:通过函数的返回值,可知玩家在标记过程中是否出错,进而在回到Cleanmine()函数时判断是否要结束游戏

代码实现效果:

原始界面:

标记雷:

失败效果:

程序设计的几处亮点:

1.用预定义的方式解决全局变量带来的同步问题

define Row 9

2.设计多玩的模式:

do-while+switch+case

3.函数功能单一且普用

Initboard:由于两个数组的初始化内容不同,

而函数的功能如需适用于两者,则需多一个初始化内容的参数

如果元素单一就传单一变量,否者可传结构体指针

4.扩大边界

为解决排查边缘方格时出现的数组越界访问问题,可以扩大

数组的宽度与长度

5.全局变量win方便同步排雷数据

注意:全局变量尽量少用

本游戏的最大亮点:

6.在show数组上打印排查情况,show与mine互不干扰

结语:

关于扫雷

游戏的更多功能与效果,还请诸位独立去设计代码,多谢阅读。

完结撒花!!!

💐💐💐💐💐💐💐💐💐💐💐💐💐💐💐💐💐💐💐💐💐💐💐💐💐💐

如果觉得《C语言实现扫雷完整代码讲解》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。