实现准备
分2个.c源文件和1个.h头文件去写代码
- test.c 对扫雷游戏进行测试
- game.c 扫雷游戏功能的实现
- 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");}
}
觉得不错的话给个三连哦!