我是用11x11的数组作为整个地图大小,游戏界面显示用9x9的地图大小,为什么要这样,目的是统计边界的雷数目时,防止出现越界情况。
如何统计周围雷的个数?
这不得不提到布雷的方式,这里用字符‘1’表示该处有雷,用字符’0’表示该处没雷。这样布雷的方式,在统计时大有好处,因为字符是可以ASCLL的十进制方式运算的,并且
'1'-'0' = 1;
'0'-'0' = 0;
这样就可以把坐标处及周围的八个坐标全加起来再减去9 x ‘0’方法计算周围0的数目。注意这里算出来的值是十进制值,而一开始用的show数组是字符类型的,转换成字符数字还要加上’0’。
下面是函数实现
//统计(x,y)坐标周围雷的个数
int GetMine(char mine[ROWS][COLS], int x, int y)
{
//根据ASCLL码表
//'1'-'0'=1; '0'-'0'=0
// return mine[x - 1][y] +
//mine[x - 1][y - 1] +
//mine[x - 1][y + 1]+
//mine[x][y - 1] +
//mine[x][y + 1] +
//mine[x + 1][y - 1] +
//mine[x + 1][y + 1] +
//mine[x + 1][y] - 8 * '0';
int i = 0;
int j = 0;
int sum = 0;
int len = 0;
for (i = x - 1; i <= x + 1; i++)
{
for (j = y - 1; j <= y + 1; j++)
{
sum += mine[i][j];
len++;
}
}
return sum - len * '0';
}
函数里并没有用 9* ‘0’,而是在循环里统计len,这样做是为了便于以后扩展程序。
当统计到坐标处周围雷的个数为0时,依次检查周围八个位置的周围雷个数,如果统计到任一位置的周围雷个数为0,则继续扩展其周围,如此反复直到把附近位置排查完。
有了大致思路,还需要补充一些细节:
1.检查过的位置,拓展时不用重复检查
2.检查的坐标不能是雷
3.检查周围坐标的周围坐标是一种递归思路
什么是递归?
程序调用自身的编程技巧称为递归( recursion)。 递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。 递归的主要思考方式在于:把大事化小
递归的两个必要条件
在这里的限制条件就是,找出所有周围雷个数为0的位置,找完后递归结束。
为了避免重复检查,每找到一个位置,都把原来的符号替换成空白,我原来初始化显示棋盘的符号为’ * ',则进行替换。下次递归时先检查其位置的符号是否为 ’ * '。
show[x][y] = ' ' ;
函数实现
void SetBlank(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)
{
int count = GetMine(mine, x, y);
if (count == 0)
{
show[x][y] = ' ';
if (x - 1 >= 0 && x <= ROW && y >= 0 && y <= COL && show[x - 1][y] == '*')
SetBlank(show, mine, x - 1, y);
if (x + 1 >= 0 && x + 1 <= ROW&&y >= 0 && y <= COL && show[x + 1][y] == '*')
SetBlank(show, mine, x + 1, y);
if (x >= 0 && x <= ROW && y - 1 >= 0 && y - 1 <= COL && show[x][y - 1] == '*')
SetBlank(show, mine, x, y - 1);
if (x >= 0 && x <= ROW && y + 1 >= 0 && y + 1 <= COL && show[x][y + 1] == '*')
SetBlank(show, mine, x, y + 1);
}
else
{
show[x][y] = count + '0';//存放的数字字符
}
}
代码解读:
x,y是玩家输入的坐标,先用坐标计算其周围雷的个数,若为0,把该位置符号’ * ‘替换成空格’ ‘,保证在9x9的地图内,然后检查该位置符号是否’ * ',如果是,那么x坐标不变,y坐标-1递归一次、y坐标+1递归一次;y坐标不变,x坐标-1递归一次、x坐标+1递归一次;这样如果count=0,就会检查该位置上下左右的坐标,然后上下左右的坐标又会各自检查它们上下左右的坐标,并一直更新周围位置的count数,如此反复,直到不再出现count=0的情况为止。
这里给出一个思路,统计最终显示在棋盘中的’ * '的个数,如果和布雷数一致,那么说明排雷成功,退出找雷。
int CountMine(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++;
}
}
return count;
}
最后给出完整版找雷函数
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
//9*9-10=71不是雷的个数
while (1)
{
printf("请输入要排查的坐标(两个坐标用空格隔开):->");
int x = 0;
int y = 0;
scanf("%d%d", &x, &y);
//1.坐标合法性
if (x >= 1 && x <= row && y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,你被炸死了!\n");
DisplayBoard(mine, row, col);
break;
}
else
{
//2.该坐标处是不是雷?不是雷,统计周围雷的个数
//3.扩展空白
SetBlank(show,mine,x,y);
int count = CountMine(show,row,col);
if (count == EASY_COUNT)
{
printf("恭喜你,排雷成功!\n");
DisplayBoard(show, row, col);
break;
}
DisplayBoard(show, row, col);
}
}
else
{
printf("坐标非法,请重新输入!\n");
}
}
}
最终显示的地图美观程度也很重要,我给出我的显示函数供大家参考
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
int i = 0;
int j = 0;
printf("-------------扫雷游戏---------------\n");
printf("\n");
//打印行号
for (i = 0; i <= row; i++)
{
if (i == 0)
{
printf(" ", i);
}
else
{
printf(" %d ", i);
}
}
printf("\n");
//打印第一行分割符
for (i = 0; i <= row; i++)
{
if (i == 0)
{
printf("---");
}
else if (i == row )
{
printf("|---|-");
}
else
{
printf("|---");
}
}
printf("\n");
//打印棋盘
for (i = 1; i <= row; i++)
{
for (j = 0; j <= col; j++)
{
if (j == 0)
{
printf(" %d ", i);//放列号
}
else if (j == col)
{
printf("| %c |", board[i][j]);
}
else
{
printf("| %c ", board[i][j]);
}
}
printf("\n");
//分割符
for (j = 0; j <=col; j++)
{
if (j == 0)
{
printf("---");
}
else if (j == col)
{
printf("|---|-");
}
else
{
printf("|---");
}
}
printf("\n");
}
printf("\n");
printf("-------------扫雷游戏---------------\n");
}
本文结束,完整扫雷解析,下次再出,大家觉得写的不错,别忘了给个三连鼓励一下作者,欢迎相互关注,相互交流~