欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 文化 > C语言实现扫雷游戏(有展开一片和标记雷的功能)

C语言实现扫雷游戏(有展开一片和标记雷的功能)

2025/3/16 6:56:15 来源:https://blog.csdn.net/2402_89035880/article/details/145379024  浏览:    关键词:C语言实现扫雷游戏(有展开一片和标记雷的功能)

实现准备

分2个.c源文件和1个.h头文件去写代码

  1. test.c 对扫雷游戏进行测试
  2. game.c 扫雷游戏功能的实现
  3. game.h 扫雷游戏功能的声明

扫雷游戏

1.test.c对扫雷游戏进行测试

首先我们要先把玩游戏的框架写出来,然后一步一步去完成其功能
跟着下面的代码的节奏走一步一步来,我会标记步骤
需要理解的地方,我都会解释,不理解的可以私信我

//scanf和printf需要用到该头文件
#include <stdio.h>//3
//菜单
void menu()
{printf("***********************\n");printf("***     1. play     ***\n");printf("***     0. exit     ***\n");printf("***********************\n");
}//4
//扫雷游戏的实现
//这个扫雷游戏的具体功能下面再说
void game()
{printf("扫雷游戏\n");//这里只是为了测试逻辑
}//2
void test()
{int input = 0;//用do while循环是让其上来就打印菜单以供我们选择do{menu();//菜单printf("请选择:");scanf("%d", &input);switch (input){case 1:game();//扫雷游戏的实现break;case 0:printf("退出游戏\n");break;default:printf("选择错误\n");break;}} while (input);//while()中放input是因为游戏退出的条件就是input为0
}//1
int main()
{test();//测试扫雷游戏return 0;
}

这样我们就搭建了一个扫雷游戏测试框架
下面我们对代码进行测试
在这里插入图片描述
接下来就是对扫雷游戏的实现

2.扫雷游戏的实现

接下来是test.c和game.c和game.c配合着实现扫雷游戏

在test.c的game()函数中

void game()
{char mine[11][11];//雷的信息char show[11][11];//展示界面
}

我们用两个字符数组:一个表示雷的信息,另一个是展示给玩家看的界面
实现9×9的扫雷棋盘,至于为什么它们的行和列都是11排查雷的时候会解释

因为11这个数字我们在这里会经常用到,而且为了使棋盘大小可以简易
变换,这里我们用到了#define

在game.h中

#pragma once#include <stdio.h>#define ROW 9
#define COL 9#define ROWS ROW+2
#define COLS COL+2

这里为什么#define了ROW、ROWS和COL、COLS后面会说

那么在test.c的game()函数中

#include "game.h"void game()
{char mine[ROWS][COLS];//雷的信息char show[ROWS][COLS];//展示界面
}

在test.c中用了game.h的东西,要引用头文件game.h

为什么用" “而不用< >
自己写的用” " 编译器提供的用< >

为了方便直接把这里所有要用到的库函数的头文件全部放在game.h中
而在test.c中只用include “game.h” 就行了

接下来是初始化棋盘

初始化棋盘InitBoard

test.c的game()中

void game()
{char mine[ROWS][COLS];//雷的信息char show[ROWS][COLS];//展示界面//初始化棋盘InitBoard(mine, ROWS, COLS, '0');//'0'InitBoard(show, ROWS, COLS, '*');//'*'
}

把字符数组mine全部初始化为字符零’0’
把字符数组mine全部初始化为字符星号’*’

game.h中

//初始化棋盘
void InitBoard(char board[ROWS][COLS], int r, int c, char set);

对该函数进行声明,set是要初始化的字符是什么

game.c中

#include "game.h"void InitBoard(char board[ROWS][COLS], int r, int c, char set)
{int i = 0;for (i = 0; i < r; i++){int j = 0;for (j = 0; j < c; j++){board[i][j] = set;}}
}

运用for循环把字符数组初始化

接下来是打印棋盘

打印棋盘DisplayBoard

test.c的game()中

void game()
{char mine[ROWS][COLS];//雷的信息char show[ROWS][COLS];//展示界面//初始化棋盘InitBoard(mine, ROWS, COLS, '0');//'0'InitBoard(show, ROWS, COLS, '*');//'*'//打印棋盘DisplayBoard(show, ROW, COL);DisplayBoard(mine, ROW, COL);
}

我们只是要打印9×9的棋盘所以传参用ROW和COL

game.h中

//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int r, int c);

game.c中

void DisplayBoard(char board[ROWS][COLS], int r, int c)
{printf("------扫雷游戏-------\n");int i = 0;for (i = 0; i <= c; i++){printf("%d ", i);//列}printf("\n");for (i = 1; i <= r; i++){printf("%d ", i);//行int j = 0;for (j = 1; j <= c; j++){printf("%c ", board[i][j]);}printf("\n");}
}

我们用图来表示如何实现
我们要知道的是打印11×11中的9×9
因为我们要实现9×9的棋盘
在这里插入图片描述
test.c的game()中

    //打印棋盘DisplayBoard(show, ROW, COL);//DisplayBoard(mine, ROW, COL);

这个mine数组的打印只是让我们看看是否真的初始化了
实际游戏中是不会打印它的

布置雷SetMine

test.c的game()中

void game()
{char mine[ROWS][COLS];//雷的信息char show[ROWS][COLS];//展示界面//初始化棋盘InitBoard(mine, ROWS, COLS, '0');//'0'InitBoard(show, ROWS, COLS, '*');//'*'//打印棋盘DisplayBoard(show, ROW, COL);//布置雷  //布置雷也是在9×9的棋盘中布置,所以传ROW和COLSetMine(mine, ROW, COL);DisplayBoard(mine, ROW, COL);//这里是布置完雷打印出来看看//是否真的布置雷了,真正玩游戏的时候是不可能把雷的位置打印出来的
}

game.h中

//布置雷
void SetMine(char mine[ROWS][COLS], int r, int c);

因为我们只会在mine数组中布置,所以这里的形参数组也直接用mine了
之前是因为两个数组都要用,所以用的board数组,board意思就是棋盘

game.c中

void SetMine(char mine[ROWS][COLS], int r, int c)
{int x = 0;int y = 0;int count = 0;while (count < EASYCOUNT){x = rand() % r + 1;y = rand() % c + 1;if (mine[x][y] == '0'){mine[x][y] = '1';count++;}}
}

x和y是布置的雷的坐标
count起到计数的作用
EASYCOUNT是布置的雷的个数

在game.h中#define了

#define EASYCOUNT 10

简单版本是10个雷

rand()函数是生成随机数的函数
因为要让电脑随机布置雷
而rand()要配合srand()和time()使用
这三个函数我的猜数字游戏里讲到了这里就不讲怎么用了

	x = rand() % r + 1;y = rand() % c + 1;

因为要生成的坐标的范围是(1,1) -> (9,9)
这里 r 和 c 都是9,rand() % 9生成0 ~ 8的随机数 +1 就是1 ~ 9

在test.c的test()中

void test()
{srand((unsigned int)time(NULL));//生成一个随机数的生成起点int input = 0;下面是之前的代码,不展示了,占地方

在game.h中

#pragma once
#include <stdio.h>
#include <stdlib.h>//rand和srand要用的头文件
#include <time.h>//time要用的头文件#define ROW 9
#define COL 9#define ROWS ROW+2
#define COLS COL+2#define EASYCOUNT 10
下面的代码也不展示了

game.c的SetMine中

if (mine[x][y] == '0'){mine[x][y] = '1';count++;}

这段代码的意思的把mine数组中的 ‘0’ 改成 ‘1’
而 ‘0’ 表示不是雷,‘1’ 表示雷,直到布置10个雷。

下面图片展示
在这里插入图片描述
在这里插入图片描述

排查雷FindMine

在test.c的game()中

void game()
{char mine[ROWS][COLS];//雷的信息char show[ROWS][COLS];//展示界面//初始化棋盘InitBoard(mine, ROWS, COLS, '0');//'0'InitBoard(show, ROWS, COLS, '*');//'*'//打印棋盘DisplayBoard(show, ROW, COL);//布置雷SetMine(mine, ROW, COL);DisplayBoard(mine, ROW, COL);//排查雷FindMine(mine, show, ROW, COL);
}

排查雷两个数组都要用到
因为展示给玩家的是show数组
玩家在排查雷的时候,该函数要对照mine数组看看该坐标是不是雷

game.h中

//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int r, int c);

game.c中

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int r, int c)
{int x = 0;//玩家输入的横坐标int y = 0;//玩家输入的纵坐标int win = 0;//获胜计数while (win < r * c - EASYCOUNT)//这里是有10个雷,那么获胜的条件就是排查出71个不是雷的坐标//win == 71 时,出循环{printf("请输入要排查的坐标:");scanf("%d%d", &x, &y);//输入的坐标必须合法//输入的坐标的合法性if (x >= 1 && x <= r && y >= 1 && y <= c)//合法条件{//排查过的坐标不能再次排查//判断该坐标是否已经被排查过if (show[x][y] == '*' || show[x][y] == '!')//我们是在show数组中排查的//'*'表示还没有排查过//'!'是标记雷,万一标记的地方不是雷但是我们标记了//也可以排查{//判断该坐标是否为雷if (mine[x][y] == '1')//是雷//用mine数组来看(x,y)坐标是不是雷{printf("很遗憾,你被炸死了\n");DisplayBoard(mine, r, c);//游戏结束,展示哪里是雷break;//游戏结束,跳出循环}else//不是雷{//我们知道网页版的扫雷游戏,该坐标周围有雷的话,会显示雷的个数//这也就是mine和show为什么是11×11的原因//因为9×9的话,角落的坐标找周围雷的个数会越界访问//得到周围8个坐标的雷的个数int count = GetMineCount(mine, x, y);show[x][y] = count + '0';//把数字转为字符放在show数组中展示给玩家看if (count == '0'){//展开一片win = UnfoldBoard(mine, show, x, y, win, r, c);}else{win++;}//得到雷得个数后进行展示DisplayBoard(show, r, c);//标记雷的位置MarkMine(show, r, c);}}else//排查过{printf("该坐标已经被排查过\n");}}else//不合法{printf("输入的坐标不合法\n");}}if (win == r * c - EASYCOUNT)//获胜条件{printf("恭喜你,排雷成功\n");}
}

得到周围雷得个数GetMineCount

game.c中

static int GetMineCount(char mine[ROWS][COLS], int x, int y)
{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');
}

周围有8个坐标
已知:
‘0’ - ‘0’ -> 0
‘1’ - ‘0’ -> 1

‘8’ - ‘0’ -> 8
所以把周围8个坐标的字符全部加起来再减去8个字符零就是周围雷的个数

展开一片UnfoldBoard

game.c中

static int UnfoldBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int win, int r, int c,)
{//递归条件:递归的坐标必须合法if(x >= 1 && x <= r && y >= 1 && y <= c){if(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') == 0)//达到展开条件才递归{win++;//胜利计数show[x][y] = ' ';//将要递归的坐标改为空格int i = -1;int j = -1;for (i = -1; i <= 1; i++){for (j = -1; j <= 1; j++){if(show[x+i][y+i] == '*'){win = UnfoldBoard(mine, show, x + i, y + i, win, r, c);//递归 返回win 胜利计数}}}}else{win++;//胜利计数int count = GetMineCount(mine, x, y);show[x][y] = count + '0';}}return win;//返回胜利计数
}

因为我们这里胜利的条件是win == 71,所以展开一片的时候必须记录win
否则win就到不了71了
展开一片的递归这里没法细讲,如果不理解可以私信我

标记雷的位置MarkMine

game.c中

static void MarkMine(char show[ROWS][COLS], int r, int c)
{printf("是否标记雷的位置: 1.标记  0.不标记\n");int input = 0;scanf("%d". &input);while(input){printf("请输入要标记的坐标:");int x = 0;int y = 0;scanf("%d %d", &x, &y);show[x][y] = '!';//'!'表示要标记的雷的位置DisplayBoard(show, r, c);//标记后展示printf("继续标记请输入:“1”,否则输入“0”\n");scanf("%d", &input);}
}

到了这里整个扫雷游戏就结束了
这里我们把雷的数量设置为2来测验一下
在这里插入图片描述

扫雷游戏完整代码

test.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 game()
{char mine[ROWS][COLS];//雷的信息char show[ROWS][COLS];//展示界面//初始化棋盘InitBoard(mine, ROWS, COLS, '0');//'0'InitBoard(show, ROWS, COLS, '*');//'*'//打印棋盘DisplayBoard(show, ROW, COL);//布置雷SetMine(mine, ROW, COL);//DisplayBoard(mine, ROW, COL);//排查雷FindMine(mine, show, ROW, COL);
}void test()
{srand((unsigned int)time(NULL));int input = 0;do{menu();printf("请选择:");scanf("%d", &input);switch (input){case 1:game();break;case 0:printf("退出游戏\n");break;default:printf("选择错误\n");break;}} while (input);
}int main()
{test();return 0;
}

game.h的代码

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <time.h>#define ROW 9
#define COL 9#define ROWS ROW+2
#define COLS COL+2#define EASYCOUNT 10//初始化棋盘
void InitBoard(char board[ROWS][COLS], int r, int c, char set);//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int r, int c);//布置雷
void SetMine(char mine[ROWS][COLS], int r, int c);//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int r, int c);

game.c的代码

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"void InitBoard(char board[ROWS][COLS], int r, int c, char set)
{int i = 0;for (i = 0; i < r; i++){int j = 0;for (j = 0; j < c; j++){board[i][j] = set;}}
}void DisplayBoard(char board[ROWS][COLS], int r, int c)
{printf("------扫雷游戏-------\n");int i = 0;for (i = 0; i <= c; i++){printf("%d ", i);//列}printf("\n");for (i = 1; i <= r; i++){printf("%d ", i);//行int j = 0;for (j = 1; j <= c; j++){printf("%c ", board[i][j]);}printf("\n");}
}void SetMine(char mine[ROWS][COLS], int r, int c)
{int x = 0;int y = 0;int count = 0;while (count < EASYCOUNT){x = rand() % r + 1;y = rand() % c + 1;if (mine[x][y] == '0'){mine[x][y] = '1';count++;}}
}static int GetMineCount(char mine[ROWS][COLS], int x, int y)
{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');
}static void MarkMine(char show[ROWS][COLS], int r, int c)
{printf("是否标记雷的位置: 1.标记  0.不标记\n");int input = 0;scanf("%d", &input);while (input){printf("请输入要标记的坐标:");int x = 0;int y = 0;scanf("%d %d", &x, &y);show[x][y] = '!';//标记'!'DisplayBoard(show, r, c);printf("继续标记请输入:“1”,否则输入“0”\n");scanf("%d", &input);}}static int UnfoldBoard(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int win, int r, int c)
{//递归x,y范围合法if (x >= 1 && x <= r && y >= 1 && y <= c){if (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') == 0){show[x][y] = ' ';win++;int i = -1;int j = -1;for (i = -1; i <= 1; i++){for (j = -1; j <= 1; j++){if (show[x + i][y + j] == '*'){win = UnfoldBoard(mine, show, x + i, y + j, win, r, c);}}}}else{win++;int count = GetMineCount(mine, x, y);show[x][y] = count + '0';}}return win;
}void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int r, int c)
{int x = 0;int y = 0;int win = 0;while (win < r * c - EASYCOUNT){printf("请输入要排查的坐标:");scanf("%d%d", &x, &y);//输入的坐标的合法性if (x >= 1 && x <= r && y >= 1 && y <= c){//判断该坐标是否已经被排查过if (show[x][y] == '*' || show[x][y] == '!'){//判断该坐标是否为雷if (mine[x][y] == '1'){printf("很遗憾,你被炸死了\n");DisplayBoard(mine, r, c);break;}else{//得到周围8个坐标的雷的个数int count = GetMineCount(mine, x, y);show[x][y] = count + '0';//展开一片if (count == 0){win = UnfoldBoard(mine, show, x, y, win, r, c);}else{win++;}DisplayBoard(show, r, c);//标记雷的位置MarkMine(show, r, c);}}else{printf("该坐标已经被排查过\n");}}else{printf("输入的坐标不合法\n");}}if (win == r * c - EASYCOUNT){printf("恭喜你,排雷成功\n");}
}

觉得不错的话给个三连哦!
请添加图片描述

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词