实现目标:
在之前,已经完成了扫雷的简化版本,在此基础上,这篇博客来进行展开一片的实现,如下图所示:
如果还不知道如何编写扫雷的小伙伴可以点击以下链接进行查看:
扫雷实现的初级具体代码https://blog.csdn.net/weixin_72883322/article/details/127793890?spm=1001.2014.3001.5501
逻辑推理:
那么,这个展开一片的逻辑是什么呢?
大致可以这么想,当你显示一个格子时,如果这个格子不是雷,并且其周围8个也没有雷,那么就将这9块格子全部展示出来,然后,再分别判断被展开的8个格子周围的8个是否有雷,如果没有雷,就继续展开其周围的8个格子,讲到这里,应该就很容易判断出想要做到展开一片的功能,就需要用到递归。
先统计周围8个格子的雷数,如果为0展开全部,然后再判断周围8个的8个......以此类推,这里需要注意的是,若一个格子已经展开,就不需要再继续展开了,否则就会出现死递归的现象!!!
我们设计一个open_board()的函数来实现递归操作,根据上面的推理,我们知道判断是否需要展开需要判断这个格子是否已经被展开,并且还要知道雷数,因此两个棋盘都需要作为参数传给函数,并且,由于在设计二维数组时为了便于操作,我们将数组的行和列数都加了2(详情请看扫雷实现简化版的博客),因此我们还应该传入我们想让函数看到的行数和列数,这样才能避免发生错误
具体代码:
void open_board(char show[][COLS],char mine[][COLS],int x,int y,int * z,int row,int col)
{
int count = 0;
count = mine_num(mine, x, y);
if (count == 0 && show[x][y] == '*'&&x>=1&&x<=row&&y>=1&&y<=col)
{
show[x][y] = ' ';
//print_board(show, ROW, COL);
(*z)++;
open_board(show, mine, x - 1, y - 1,z,row,col);
open_board(show, mine, x - 1, y,z, row, col);
open_board(show, mine, x - 1, y+1,z, row, col);
open_board(show, mine, x , y - 1,z, row, col);
open_board(show, mine, x , y + 1,z, row, col);
open_board(show, mine, x + 1, y - 1,z, row, col);
open_board(show, mine, x + 1, y ,z, row, col);
open_board(show, mine, x + 1, y + 1,z, row, col);
}
else if (count != 0 && show[x][y] == '*' && x >= 1 && x <= row && y >= 1 && y <= col)
{
show[x][y] = count + '0';
(*z)++;
}
}
在排雷函数中的使用:
open_board(show,mine, i, j,&win,ROW,COL);
为什么要将win的地址传给函数呢?
我们知道扫雷的游戏获胜规则是总格子数-排除的格子数==雷数,为了让游戏正常结束,递归中每次展开一个格子,就要让win++,这样才能正常统计已经排除的数。
game.c的新代码:
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void init_board(char board[][COLS], int rows, int cols, char c)
{//c为初始化内容的指定,用此技巧可以一个函数完成两个初始化功能
int i = 0;
for (i = 0; i < rows; i++)
{
int j = 0;
for (j = 0; j < cols; j++)
board[i][j] = c;
}
}
void print_board(char board[][COLS], int row, int col)
{
int j = 0;
int i = 0;
printf("-----------扫雷----------\n");
for (i = 0; i <= row; i++)
{
if (i == 0)
printf(" ");
else
printf("%d ", i);
}
//这是为了让游戏可玩度更高,打印出了一个标识行
printf("\n");
for (i = 0; i <= row; i++)
{
printf("--");
if (i == 0)
printf("|");
}
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 set_mine(char board[][COLS], int row, int col)
{
int count = 0;
while (count < EASY_PATTERN)
{
int i = rand() % row + 1;
int j = rand() % col + 1;
if (board[i][j] != '1')
{
count++;
board[i][j] = '1';
}
}
}
int mine_num(char board[][COLS], int x, int y)
{
return board[x - 1][y - 1] + board[x - 1][y] + board[x - 1][y + 1]
+ board[x][y - 1] + board[x][y + 1] + board[x + 1][y - 1]
+ board[x + 1][y] + board[x + 1][y + 1]-8*'0';
}
void open_board(char show[][COLS],char mine[][COLS],int x,int y,int * z,int row,int col)
{
int count = 0;
count = mine_num(mine, x, y);
if (count == 0 && show[x][y] == '*'&&x>=1&&x<=row&&y>=1&&y<=col)
{
show[x][y] = ' ';
//print_board(show, ROW, COL);
(*z)++;
open_board(show, mine, x - 1, y - 1,z,row,col);
open_board(show, mine, x - 1, y,z, row, col);
open_board(show, mine, x - 1, y+1,z, row, col);
open_board(show, mine, x , y - 1,z, row, col);
open_board(show, mine, x , y + 1,z, row, col);
open_board(show, mine, x + 1, y - 1,z, row, col);
open_board(show, mine, x + 1, y ,z, row, col);
open_board(show, mine, x + 1, y + 1,z, row, col);
}
else if (count != 0 && show[x][y] == '*' && x >= 1 && x <= row && y >= 1 && y <= col)
{
show[x][y] = count + '0';
(*z)++;
}
}
void mine_clear(char mine[][COLS], char show[][COLS], int row, int col)
{
int i = 0;
int j = 0;
int count = 0;
int win = 0;
while(win<ROW*COL-EASY_PATTERN)
{
print_board(show, ROW, COL);
//print_board(mine, ROW, COL);
printf("请输入要排查的坐标:>");
scanf("%d %d", &i, &j);
if (i >= 1 && i <= row && j >= 1 && j <= col)
{
if (show[i][j] == '*')
{
open_board(show,mine, i, j,&win,ROW,COL);
if (mine[i][j] == '1')
{
printf("你被炸死了,真惨!\n");
Sleep(500);
break;
}
}
else
{
printf("该坐标已经被排查过,请重新输入!\n");
Sleep(500);
}
}
else
{
printf("输入错误,请重新输入!\n");
Sleep(500);
}
system("cls");
}
if (win == ROW * COL - EASY_PATTERN)
{
printf("你真是个排雷大师呢!\n");
print_board(mine, ROW, COL);
}
else
{
printf("真遗憾,下次努力把。\n");
print_board(mine, ROW, COL);
}
}
注意,该博客的扫雷实现运用了模块化编写,想要完成编译还需要上一篇博客的text.c和game.h文件,这两个文件并没有进行修改,这里不再给出,请参考这篇博客:
扫雷实现的初级具体代码https://blog.csdn.net/weixin_72883322/article/details/127793890?spm=1001.2014.3001.5501
现在还剩下标记雷和显示剩余雷的功能还没实现啦!!虽然学习任务比较多,但是我还是会努力哒!!