首页 >> 大全

【编程之路(007)扫雷游戏】(C语言实现)

2023-06-23 大全 41 作者:考证青年

目录

引言

菜单的打印

初始化棋盘

棋盘的打印

布置雷区

确定周围雷数量值

标记

取消标记

递归展开空白安全区

统计被正确标记的雷数

扫雷的具体过程

源码

引言

扫雷这个游戏想必大家都不陌生。如果你知道扫雷游戏的规则,那么你可以跳过这一段,直接到main函数的整体逻辑这里开始。如果你还不知道扫雷怎么玩,我也给你复制好了游戏规则。

胜利条件:

在一个9*9方块矩阵中随机布置一定量的地雷10个。由玩家逐个翻开方块,以找出所有地雷为最终游戏目标。如果玩家翻开的方块有地雷,则游戏结束。

游戏主区域由很多个方格组成。使用鼠标左键随机点击一个方格,方格即被打开并显示出方格中的数字;方格中数字则表示其周围的8个方格隐藏了几颗雷。

在判断出不是雷的方块上按下左键(我们的是输入坐标),可以打开该方块。如果方块上出现数字,则该数字表示其周围3×3区域中的地雷数,一般为8个格子,对于边块为5个格子,对于角块为3个格子。所以扫雷中最大的数字为8;如果方块上为空(相当于0),则可以递归地打开与空相邻的方块;如果不幸触雷,则游戏结束。

main函数的整体逻辑

int main()
{srand((unsigned int)time(NULL));//暂时不管int choice = 0;do{printf("            游戏介绍:            \n");printf("输入 1 为开始游戏,输入 0 为退出游戏\n");printf("游戏最终的胜利即为把全部的雷标记出来\n");menu();printf("请选择\n");scanf("%d", &choice);switch (choice){case 1:{game();break;}case 0:{printf("游戏退出\n");break;}default:{printf("选择错误,重新选择\n");break;}}} while (choice);return 0;
}

这里的逻辑与上一篇三子棋【编程之路(006)三子棋游戏】(C语言实现)的博客-CSDN博客一样,也很简单,如果不太明白,可以点击链接进去看看。接下来我们便实现游戏各个函数的功能。

菜单的打印

void menu()
{printf("************************\n");printf("*****    1.play    *****\n");printf("*****    0.exit    *****\n");printf("************************\n");
}

初始化棋盘

void InitBoard(char board[ROWS][COLS], int rows, int cols, char ch)
{int i = 0, j = 0;for (i = 0; i < rows; i++){for (j = 0; j < cols; j++){board[i][j] = ch;}}
}

这里要注意的是我们扫雷游戏其实是有两个棋盘(二维数组实现)的。一个是玩家玩的棋盘界面;另一个是我们布置雷区的棋盘。有了整体的把控,我们对于游戏的实现便有了大致的方向。

由于扫雷要探索周围一圈的区域,也就是八个格子,对于中间的格子还好,我们可以很好的表示,但是对于边缘和四角的的格子,我们探索周围就会出现越界的情况(如下图)。解决方法是直接定义比棋盘规模多两行的行列。如9*9的规模我们便定义成(9+2)*(9+2)的规模。先说明ROWS和COLS是9+2的规模,ROW和COL是9的规模。那么为什么还要多次一举定义出ROW和COL呢,那是因为虽然我们的规模大,但还是有很多操作是需要用到9*9的。接下来其他功能的实现便可以很直观的看到。

棋盘的打印

void Display(char board[ROWS][COLS], int row, int col)
{printf("-------------------\n");int i = 0, j = 0;for (j = 0; j <= col; j++)//这个for循环是打印棋盘的列坐标{printf("%d ", j);}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("-------------------\n");
}

这里所打印出来的棋盘是展示棋盘,而不是雷区棋盘。我们在函数传参的时候把'*'传个展示棋盘,让其保持待探索的感觉。效果如下

布置雷区

void SetMine(char board[ROWS][COLS], int row, int col)
{int x = 0, y = 0;int count = AMOUNT;//AMOUNT是我们定义的雷的数量while (count){x = rand() % row + 1;//(0~8)+1==1~9y = rand() % col + 1;//(0~8)+1==1~9if (board[x][y] == '0'){board[x][y] = '1';//雷我们用字符'1'来表示,因为形参的类型是charcount--;}}
}

rand()函数和main()函数中的srand()函数随机生成了个雷。

对于rand函数和srand函数不懂的可以参考这一篇文章

rand()和srand()函数的用法的博客-CSDN博客和rand函数怎么用

确定周围雷数量值

int AmountMine(char board[ROWS][COLS], int x, int y)
{return (board[x - 1][y] +board[x - 1][y - 1] +board[x][y - 1] +board[x + 1][y - 1] +board[x + 1][y] +board[x + 1][y + 1] +board[x][y + 1] +board[x - 1][y + 1] - 8 * '0');//减去8*'0':因为我们的雷区中都是字符型的'0'//非雷'1'//雷。//所以如果有n个雷,就会产生n*'1'-n*'0'的整数。('1'-'0'=1)
}

对于玩家排查的一个(x,y)的格子,我们应该返回周围一圈有多少雷,如下图

标记雷

void FlagMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col, int x, int y)
{if (x >= 1 && x <= row && y >= 1 && y <= col){show[x][y] = '!';}
}

我们把标记的雷在展示数组上用'!'标记。

取消标记

void FlagCancel(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col, int x, int y)
{if (show[x][y] == '!'){show[x][y] = '*';}
}

我们想把原来标记的取消,直接在改成'*'即可。

递归展开空白安全区

void Spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{int around_x = 0;int around_y = 0;int count = 0;//坐标合法if (x >= 1 && x <= 9 && y >= 1 && y <= 9){//遍历周围坐标for (around_x = -1; around_x <= 1; around_x++){for (around_y = -1; around_y <= 1; around_y++){//如果这个坐标不是雷if (mine[x + around_x][y + around_y] == '0'){//统计周围雷的个数count = AmountMine(mine, x + around_x, y + around_y);if (count == 0){if (show[x + around_x][y + around_y] == '*'){show[x + around_x][y + around_y] = ' ';Spread(mine, show, x + around_x, y + around_y);}}//如果是雷就直接在展示棋盘上显示周围有几个雷的数字else{show[x + around_x][y + around_y] = count + '0';}}}}}
}

统计被正确标记的雷数

int ClearMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{int i = 0, j = 0;int end = 0;for (i = 1; i <= row; i++){for (j = 1; j <= col; j++){if (mine[i][j] == '1' && show[i][j] == '!')//在雷区棋盘上是雷并且在展示棋盘上是'!'才是被标记的正确的雷end++;}}return end;
}

如果玩家把所有的雷标记出来,那么就直接胜利了。

扫雷的具体过程

void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{int n = 0, x = 0, y = 0;int win = 0;//统计被标记的个数(可能被标记的都是雷,也有可能标错)int flag = 0;//falg为0代表未取消标记,为1代表被取消标记//这是我为了方便展示棋盘而设置的int fail = 0;//fail为0代表没有被炸死,为1代表被炸死
again:while (win < AMOUNT){printf("功能 1:排查雷 2:标记雷 3:取消标记\n");printf("请选择功能和排查的坐标(功能 横 纵)\n");scanf("%d %d %d", &n, &x, &y);switch (n){case 1:break;case 2:{FlagMine(show, mine, row, col, x, y);win++;break; }case 3:{FlagCancel(show, mine, row, col, x, y); flag = 1;win--;break;}}if (x >= 1 && x <= row && y >= 1 && y <= col){if (mine[x][y] == '1' && show[x][y] == '*' && flag == 0){printf("你被炸死了,下面是雷区分布图 1为雷  0非雷\n");Display(mine, ROW, COL);fail = 1;break;}else if (show[x][y] == '!')Display(show, ROW, COL);else if (flag == 1)Display(show, ROW, COL);else {int count = AmountMine(mine, x, y);if (count == 0){show[x][y] = ' ';Spread(mine, show, x, y);Display(show, ROW, COL);}else{show[x][y] = count + '0';Display(show, ROW, COL);}}}else{printf("输入错误,请重新输入");}}if (AMOUNT == ClearMine(mine,show,row,col))//如果雷的数量都被标记出来,就成功。{printf("恭喜你,扫雷成功\n");}else if(fail == 1)//如果触发到fail为1,则说明爆炸过了,就失败了。{printf("请选择是否再来一把\n");}else{printf("标记的雷有误,请仔细检查,重新标记\n");//到这里就是标记了至少一个安全区域,需要重新排雷goto again;//goto语句直接跳到重新排雷的地方}
}

源码

该部分代码为game.h文件中的


#pragma once#include
#include
#include#define ROW 9
#define COL 9#define ROWS ROW+2
#define COLS COL+2#define AMOUNT 10//菜单
void menu();//棋盘初始化
void InitBoard(char board[ROWS][COLS], int rows, int cols, char ch);//展示棋盘
void Display(char board[ROWS][COLS], int row, int col);//布置雷
void SetMine(char board[ROWS][COLS], int row, int col);//选择坐标区域雷的数量
int AmountMine(char board[ROWS][COLS], int row, int col);//扫雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);//标记雷
void FlagMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col, int x, int y);//取消标记
void FlagCancel(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col, int x, int y);//排查选择坐标区域周围是否安全
void Spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y);//返回被标记的雷的个数
int ClearMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

该部分代码为game.c文件中的

#define _CRT_SECURE_NO_WARNINGS 1#include"game.h"void menu()
{printf("************************\n");printf("*****    1.play    *****\n");printf("*****    0.exit    *****\n");printf("************************\n");
}void InitBoard(char board[ROWS][COLS], int rows, int cols, char ch)
{int i = 0, j = 0;for (i = 0; i < rows; i++){for (j = 0; j < cols; j++){board[i][j] = ch;}}
}void Display(char board[ROWS][COLS], int row, int col)
{printf("-------------------\n");int i = 0, j = 0;for (j = 0; j <= col; j++)//这个for循环是打印棋盘的列坐标{printf("%d ", j);}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("-------------------\n");
}void SetMine(char board[ROWS][COLS], int row, int col)
{int x = 0, y = 0;int count = AMOUNT;while (count){x = rand() % row + 1;//(0~8)+1==1~9y = rand() % col + 1;//(0~8)+1==1~9if (board[x][y] == '0'){board[x][y] = '1';count--;}}
}int AmountMine(char board[ROWS][COLS], int x, int y)
{return (board[x - 1][y] +board[x - 1][y - 1] +board[x][y - 1] +board[x + 1][y - 1] +board[x + 1][y] +board[x + 1][y + 1] +board[x][y + 1] +board[x - 1][y + 1] - 8 * '0');
}void FlagMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col, int x, int y)
{if (x >= 1 && x <= row && y >= 1 && y <= col){show[x][y] = '!';}
}void FlagCancel(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col, int x, int y)
{if (show[x][y] == '!'){show[x][y] = '*';}
}void Spread(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{int around_x = 0;int around_y = 0;int count = 0;//坐标合法if (x >= 1 && x <= 9 && y >= 1 && y <= 9){//遍历周围坐标for (around_x = -1; around_x <= 1; around_x++){for (around_y = -1; around_y <= 1; around_y++){//如果这个坐标不是雷if (mine[x + around_x][y + around_y] == '0'){//统计周围雷的个数count = AmountMine(mine, x + around_x, y + around_y);if (count == 0){if (show[x + around_x][y + around_y] == '*'){show[x + around_x][y + around_y] = ' ';Spread(mine, show, x + around_x, y + around_y);}}else{show[x + around_x][y + around_y] = count + '0';}}}}}
}int ClearMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{int i = 0, j = 0;int end = 0;for (i = 1; i <= row; i++){for (j = 1; j <= col; j++){if (mine[i][j] == '1' && show[i][j] == '!')end++;}}return end;
}void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{int n = 0, x = 0, y = 0;int win = 0;int flag = 0;int fail = 0;
again:while (win < AMOUNT){printf("功能 1:排查雷 2:标记雷 3:取消标记\n");printf("请选择功能和排查的坐标(功能 横 纵)\n");scanf("%d %d %d", &n, &x, &y);switch (n){case 1:break;case 2:{FlagMine(show, mine, row, col, x, y);win++;break; }case 3:{FlagCancel(show, mine, row, col, x, y); flag = 1;win--;break;}}if (x >= 1 && x <= row && y >= 1 && y <= col){if (mine[x][y] == '1' && show[x][y] == '*' && flag == 0){printf("你被炸死了,下面是雷区分布图 1为雷  0非雷\n");Display(mine, ROW, COL);fail = 1;break;}else if (show[x][y] == '!')Display(show, ROW, COL);else if (flag == 1)Display(show, ROW, COL);else {int count = AmountMine(mine, x, y);if (count == 0){show[x][y] = ' ';Spread(mine, show, x, y);Display(show, ROW, COL);}else{show[x][y] = count + '0';Display(show, ROW, COL);}}}else{printf("输入错误,请重新输入");}}if (AMOUNT == ClearMine(mine,show,row,col)){printf("恭喜你,扫雷成功\n");}else if(fail == 1){printf("请选择是否再来一把\n");}else{printf("标记的雷有误,请仔细检查,重新标记\n");goto again;}
}

该部分代码为test.c文件中的

#define _CRT_SECURE_NO_WARNINGS 1#include"game.h"void game()
{//存放雷的棋盘  雷为'1',非雷为'0'  这里是字符1,0char mine[ROWS][COLS] = { 0 };//展示排查后的棋盘  未排查的区域为'*',排查后显示周围雷的数字(也是字符)char show[ROWS][COLS] = { 0 };InitBoard(mine, ROWS, COLS, '0');InitBoard(show, ROWS, COLS, '*');SetMine(mine, ROW, COL);Display(show, ROW, COL);//Display(mine, ROW, COL);FindMine(show, mine, ROW, COL);
}int main()
{srand((unsigned int)time(NULL));int choice = 0;do{printf("            游戏介绍:            \n");printf("输入 1 为开始游戏,输入 0 为退出游戏\n");printf("游戏最终的胜利即为把全部的雷标记出来\n");menu();printf("请选择\n");scanf("%d", &choice);switch (choice){case 1:{game();break;}case 0:{printf("游戏退出\n");break;}default:{printf("选择错误,重新选择\n");break;}}} while (choice);return 0;
}

希望这个扫雷游戏可以帮到你。有什么错误和建议也希望大家可以在评论区发出来。

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了