经典游戏扫雷,在学习C语言基础后,今天试着用C语言去实现这个经典小游戏。
扫雷想必大家都不陌生,但是,是不是还有很多人不知道怎么扫雷呢?是不是一点就踩雷被呢?别看了,说的就是你!那么,下面就给大家介绍一下这个游戏的玩法。
注意:(改图截自扫雷游戏网页版)
图例:
左上角的数字 10–表示雷的个数(此时为8由于我已经手动标了两处旗,表明我已经排除两个雷了)
右上角的数字目前为 002–表示的是排查出雷的个数(图中的两个小旗)
游戏规则:
我们看序号 1 ,以我们画圈的哪个数字 1为中心画一个九宫格,此时九宫格内只有一个方块,而我们这个中心数字又
是 1,表明以这个数字为中心的九宫格内的八个方块里有一个雷,并且,此时这个九宫格内只有一个方块还没有点开,
其他地方都点开了并且显示了数字说明不是雷,因此这一个方块里一定是雷,我们插上小旗表明已经排除此处是雷。
我们再来看序号 2,同样以我们画圈的那个数字 1 为中心画一个九宫格,但由于九宫格最底下那一行已经超出了游戏
范围,因此在边界线上的数字为中心画九宫格只需要考虑九宫格边界线以内的情况就可以。同理,此时中心数字是 1
表明现在这个九宫格内的八个方块有一个一定是雷,由于只有一个方块没有排除,其他均已排除并显示数字表明不是
雷,只有这最有一个方块没排除,因此一定是雷。
最后,如果中心数字是 2 哪以中心数字 2 为九宫格内的八个方块内一定有两个雷,其他数字同理。
现在,应该没有人不会了吧。
由上面玩游戏的过程可以体会到,我们需要两个二维素组来分别存放雷和
展示雷
我们看到,一开始点开这个游戏界面的时候,只有小方块,因此我们也需
要将它进行一个初始化
存放好后的信息都已经放在了数组里,玩家进行游戏时,需要根据我们展示的信息来
完成扫雷,因此我们需要将这个数组打印出来给玩家观察
在进行扫雷之前,需要规定多大的棋盘里一共需要埋下多少颗雷来让玩家进行扫雷
当上述准备过程都已经完成后,那接下来就是扫雷的过程啦,根据我们的玩法规则,结
合展示的信息完成扫雷
扫雷展开过程 效果展示
(该视频录制扫雷网页版)
下面我们一起来观察一下这个展开效果。视频中可以观察到,前几次我点开的每一个方
块都只有这一个方块没了并且显示了数字,根据我们的规则,以这个数字为中心的九宫
格是有该数字个雷的,因此他的周围没有展开
我的最后一步,点开了右下角的一个方块,一下子周围很多方块都消失了,这就是展开
的效果,根据规则我们可以知道,展开是需要一定前提的:
1.这个坐标本身不是雷
2.这个坐标九宫格内的其他坐标不能是雷
到最后,如果玩家找到了所有的雷,那么意味着排雷成功,若是中途触雷,则直接判定失败
采取分模块写法:
1.test.c的主源文件中写主体步骤
2.game.h 中申明扫雷游戏模块相关的函数申明,自定义等
3.game.c 中实现函数定义
test.c 中代码如下:
void game()//游戏函数实现
{
char mine[ROWS][COLS] = { 0 };//存放雷的数组
char show_mine[ROWS][COLS] = { 0 };//存放展示扫雷的素组
}
void menu() //菜单函数实现
{
printf("****************\n");
printf("**** 1.play ****\n");
printf("**** 0.exit ****\n");
printf("****************\n");
}
int main()
{
int input = 0;
srand((unsigned)time(NULL));
do
{
menu(); //菜单
printf("请选择: \n");
scanf("%d", &input);
switch (input)
{
case 1:
game();
break;
case 0:
printf("即将退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);//玩家输入的值来进行循环的判断,可达到循环玩游戏的效果
return 0;
}
game.h 中代码如下:
#define ROW 9 //操作的行
#define COL 9 //操作的列
#define COLS COL+2 //防止数组越界
#define ROWS ROW+2 //防止素组越界
由于我们看到的扫雷的棋盘是一个9*9的棋盘,因此我们操作的棋盘应该就是这个大小,但是当判断内容时,我们是以一个数字为中心的九宫格,那么,在边界上的数字(如上图所示)的九宫格就会超过需要的棋盘大小,为了防止越界,我们将棋盘的行列均扩大,上下左右均需要扩大,因此行列扩大两行。
test.c 中代码如下:
void game()
{
char mine[ROWS][COLS] = { 0 };//存放雷的数组
char show_mine[ROWS][COLS] = { 0 };//展示扫雷的素组--给玩家观察的数组
Initboard(mine, ROWS, COLS, '0'); //初始化为字符‘0’
Initboard(show_mine, ROWS, COLS, '*');//初始化为字符‘*’
}
game.h 中代码如下:
//初始化数组中的内容
void Initboard(char board[ROWS][COLS], int rows, int cols ,char sz);
game.c 中代码如下:
game.c 中实现函数时所涉及的头文件我们放在game.h中,可以防止重复引用
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"//用了game.h 中 ROWS COLS 等自定义的变量须应用头文件
void Initboard(char board[ROWS][COLS], int rows, int cols,char sz)
{
//**传入 sz 这个字符参数 方便我们一个函数可以解决 雷的棋盘和展示棋盘的初始化**
int i = 0;
int j = 0;
for (i = 0; i < rows; i++)
{
for (j = 0; j < cols; j++)
{
board[i][j] = sz;
}
}
}
test.c 中代码如下:
void game()
{
char mine[ROWS][COLS] = { 0 };//存放雷的数组
char show_mine[ROWS][COLS] = { 0 };//展示扫雷的数组--给玩家观察的数组
Initboard(mine, ROWS, COLS, '0'); //初始化为字符‘0’
Initboard(show_mine, ROWS, COLS, '*');//初始化为字符‘*’
print_board(show_mine, ROW, COL);//展示扫雷的数组
//注意的是,我们操作的是展示的9*9的棋盘,因此我们传入的棋盘行列大小也是9*9,即ROW,COL
}
game.h 中代码如下:
#include
//打印初始化后的内容
void print_board(char board[ROWS][COLS], int row, int col);
我们传入的是show_mine 这个数组,因此这里的【ROWS】【COLS】也应当是和
show_mine创建的时候保持一直,但我们展示的却是9*9的棋盘,因此传入的行列是
row和col
game.c 中代码如下:
void print_board(char board[ROWS][COLS], int row, int col)
{
printf("--------扫雷-------\n");//上分割线
int i = 0;
int j = 0;
//打印横坐标
for (i = 0; i <= row; i++)
{
if (i == 0)
printf(" ");
else
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("--------扫雷-------\n");下分割线
}
test.c 中代码如下:
void game()
{
char mine[ROWS][COLS] = { 0 };//存放雷的数组
char show_mine[ROWS][COLS] = { 0 };//展示扫雷的数组--给玩家观察的数组
Initboard(mine, ROWS, COLS, '0'); //初始化为字符‘0’
Initboard(show_mine, ROWS, COLS, '*');//初始化为字符‘*’
print_board(show_mine, ROW, COL);//展示扫雷的数组
Set_mine(mine, ROW, COL);
//埋雷的过程,只操作我们存放雷的数组
//print_board(mine, ROW, COL);//可以观察设置的雷是否正确
}
game.h 中代码如下:
#define junior_mine 10 //雷的数目
#include //srand,rand 的头文件
#include //time 的头文件
//设置雷
void Set_mine(char board[ROWS][COLS], int row, int col);
同上面展示雷的数组一样,埋雷的这个数组操作的是创建的 mine这个数组
【ROWS】【COLS】需要保持一直,由于扫雷的方块是9*9大小,所以我们传
入的棋盘大小任然是row 和 col
game.c 中代码如下:
void Set_mine(char board[ROWS][COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = junior_mine;
while (count) //雷的数量为0时跳出循环说明雷已经布置完了
{
//随机产生雷的坐标在 1~9的范围内
x = rand() % row + 1;
y = rand() % col + 1;
if (board[x][y] == '0') //说明该坐标处没有雷并且可以设置雷
{
board[x][y] = '1'; //埋雷成功 用 字符 1表示
count--;
}
}
}
test.c 中代码如下:
void game()
{
char mine[ROWS][COLS] = { 0 };//存放雷的数组
char show_mine[ROWS][COLS] = { 0 };//展示扫雷的数组--给玩家观察的数组
Initboard(mine, ROWS, COLS, '0'); //初始化为字符‘0’
Initboard(show_mine, ROWS, COLS, '*');//初始化为字符‘*’
print_board(show_mine, ROW, COL);//展示扫雷的数组
Set_mine(mine, ROW, COL);
Find_mine(mine, show_mine, ROW, COL);
}
game.h 中代码如下:
//排雷
void Find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
game.c 中代码如下:
//统计该坐标周围雷的个数
int sum_mine(char board[ROWS][COLS], int x, int y)
{
int sum = 0;
int i = 0;
int j = 0;
for (i = x - 1; i <= x + 1; i++)
{
//产生以该坐标为中心的九宫格内的八个坐标
for (j = y - 1; j <= y + 1; j++)
{
sum+=board[i][j] - '0'; //注意数字和字符之间的转换
}
}
return sum;
}
void Find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
printf("请输入坐标进行排查:\n");
int x = 0;
int y = 0;
//IsWin函数用于判断是否胜利 下在一步具体实现
while (IsWin(show,row,col)==0)
{
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= 1 && y <= col)//判断坐标合法性
{
if (mine[x][y] == '1')
{
printf("踩雷!\n");
print_board(mine, ROW, COL);//踩雷后给玩家展示踩雷在哪里
//和雷的全部坐标
break;
}
else //没有踩雷
{
int count = sum_mine(mine, x, y);//先统计该做标的周围的雷
show[x][y] = count + '0';//注意int类型和字符类型转换
Open_Not_mine(mine, show, x, y);//展开该坐标周围没有雷的方块
print_board(show, ROW, COL);//展示完以及雷的个数统计后,展示给玩家观察
}
}
else
printf("输入错误,请重新输入\n");
}
由于展开周围坐标没有雷的方块这个函数不属于游戏模块test.c 中的game()里的函数,因此不需要写在game.h中声明
game.c 中代码如下:
void Open_Not_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
int i = 0;
int j = 0;
int count = 0;
//判断坐标是否合法
if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
{
for (i = -1; i<= 1; i++)
{ //该坐标为中心的八个坐标的循环写法
for (j = -1; j<= 1; j++)
{
//如果这个坐标不是雷的情况下
if (mine[x + i][y + j] == '0')
{
// 注意是 [x+i]和[y+i] 是改坐标周围其他坐标
//统计这个坐标周围的雷
count = sum_mine(mine, x + i, y + j);
if (count == 0) //坐标周围没有雷,说明可以展开
{
if (show[x + i][y + j] == '*')
{
show[x + i][y + j] = ' ';
Open_Not_mine(mine, show, x + i, y + j);
//回去递归调用周围坐标判断
}
}
else //周围有雷 则这个坐标轴为中心的九宫格将不展开
{
show[i + x][j + y] = count + '0';
//统计这个坐标周围有几个雷并显示在改坐标处
}
}
}
}
}
}
同展开函数 Open_Not_mine 一样, 判断输赢这个函数也不需要在game.h中进行声明
game.c中代码如下:
int IsWin(char show[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
int count = 0;
for (i = 1; i <= row;i++)
{
//判断坐标范围
for (j = 1; j <= col; j++)
{
if (show[i][j] == '*')
{
count++; //统计show数组在9*9中棋盘上一共有多少个不是雷的个数
}
}
} //雷的个数
return count == junior_mine;//相等返回1 不想动返回0
//相等时说明 没有雷的方块都已经找到了,其余的位置都为雷--胜利
//不相等的时候表明,不是雷的方块还没有全部找出来--未胜利
}