扫雷小游戏是我们经常在电脑上玩的一个小游戏,非常的考验人的耐心和智力,这次我们用C语言将其实现一下,因为我还没有掌握关于C语言插入图片或者用鼠标点击的阶段,因此这次只是实现一个比较基础的版本,后面会继续的完善。
概述
在电脑上的扫雷小游戏是分难度等级的,难度越高,棋盘越大,地雷的数量也就越多,因此我也按照这样的思路,设置了不同的难度和不同的地雷数,我将三个等级分别用不同的头文件和.c文件来写的,可能有些笨拙,但是我会随着知识的不断掌握,不断的去完善。
主函数
//菜单函数
void menu()
{
printf("**********************************\n");
printf("********** 1. play *********\n");
printf("********** 0. exit *********\n");
printf("**********************************\n");
}
int main()
{
srand((unsigned int)time(NULL));
int input = 0;
do
{
menu();
printf("请输入选项:>\n");
scanf("%d", &input);
getchar();
switch (input)
{
case 1:
{
char n = 0;
printf("请选择难度:>\n");
printf("l代表低级,m代表中级,h代表高级\n");
scanf("%c", &n);
switch (n)
{
case 'l':
low_game();
break;
case 'm':
middle_game();
break;
case 'h':
high_game();
break;
}
//game();
break;
}
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新选择\n");
break;
}
} while (input);
return 0;
}
主函数包括了显示菜单和选择游戏难度,菜单函数比较简单,就是将选项打印出来。选项的选择和难度的选择是通过switch语句来实现的,我用了两个嵌套的switch语句来进行游戏选项和难度的选项。
这里不同的游戏难度对应不同的函数,三个难度的游戏函数整体上是一样的,我将其封装了三个不同的文件方便修改。
游戏函数
这里我只展示一下初级的游戏难度,其他的只是稍作改动,我会将源码放在GitHub中方便大家阅读。
整个玩游戏的思路是这样的:我们需要两个棋盘,一个用来放置地雷,另一个是将每一次玩家输入坐标之后的棋盘信息实时的显示出来。
首先我们需要将两个棋盘进行初始化,放置地雷的棋盘初始化为字符’0’,这样初始化的原因在后面详细解释,这有关排雷函数的方便运算;显示实时信息的棋盘初始化为字符‘ * ’,这样子可以使棋盘更像是扫雷小游戏的棋盘。
代码实现如下:
void low_InitBoard(char board[LOW_ROWS][LOW_COLS], int row, int col, char set)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
board[i][j] = set;//将数组定义初始化为想要的字符set
}
}
}
因为初始化的字符不同,为了能够整合在一个函数中,所以将要初始化的字符作为参数传给初始化函数。
打印函数
我们在初始化完成之后,要将展示实时信息的棋盘给玩家展示,因此需要一个打印函数。
代码实现:
void low_DisplayBoard(char board[LOW_ROWS][LOW_COLS], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i <= row; i++)
{
if (0 == i)
{
printf(" ");
continue;
}
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");
}
}
为了方便玩家的坐标输入,我们在棋盘的周围对行和列进行了标识,增加了游戏的可玩性。
布雷函数
布置地雷我们用随机数来进行地雷坐标的选择,因为是随机数,所以每次玩游戏地雷的位置都是不同的,也就保证了游戏的可玩性。地雷用字符’1’来表示,具体原因同样在后面解释。
代码实现:
void low_Put_Mine(char board[LOW_ROWS][LOW_COLS], int row, int col)
{
int count = EASY_COUNT;
int x = 0;
int y = 0;
while (count > 0)
{
x = rand() % row + 1;
y = rand() % col + 1;
if (board[x][y] == '0')//只有防雷成功,count减一
{
board[x][y] = '1';
count--;
}
}
}
这个里面有一点要注意,就是count变量是用来存放要布置的地雷数量的,每一次布置雷之后,count要减一,这里面需要一个判断,就是说当布置地雷的坐标是空的,才能够布置地雷成功,如果没有这个判断,布置雷的数量可能会有问题。
排雷函数
整个游戏的核心就是这个排雷函数,要根据玩家输入的坐标来判断该坐标周围的雷数并计算,然后返回给玩家,方便玩家根据这个信息来进行下一步的选择,因此,排雷是非常关键的,这个函数如果出错的话,就会给玩家错误信息,使游戏出现重大失误。
排雷的方法是这样的:就是计算输入坐标周围的8个地方的雷数,但是如何计算能够更加的方便呢?
这就是前面将地雷位置和非地雷位置设置为字符‘1’和字符‘0’的原因。我们在计算坐标周围的雷数时,可以将周围的8个坐标的所有字符加起来,然后减去8个字符‘0’,因为计算的时ASCII码值,这样子计算下来就可以计算出周围地雷的数量,非常的方便。如果设置为‘ * ’和‘ # ’,计算的话就要将8个位置都判断一次,就非常的没有效率。
代码实现:
void low_Sweep(char mine[LOW_ROWS][LOW_COLS], char show[LOW_ROWS][LOW_COLS], int x, int y)
{
int count = 0;
count = mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] +
mine[x - 1][y] + mine[x + 1][y] + mine[x - 1][y + 1] +
mine[x][y + 1] + mine[x + 1][y + 1] - 8 * '0';
if (0 == count)
{
int i = 0;
int j = 0;
show[x][y] = ' ';
for (i = -1; i <= 1; i++)
{
for (j = -1; j <= 1; j++)
{
//用两组for循环来进行递归,进入递归的条件是在数组能够到达的范围内,并且用show数组中*来减少已经遍历过的元素
if ((x + i) >= 1 && (x + i) <= LOW_ROW && (y + j) >= 1 && (y + j) <= LOW_COL && show[x + i][y + j] == '*')
{
low_Sweep(mine, show, x + i, y + j);
}
}
}
}
else
{
show[x][y] = count + '0';
}
}
//对输入坐标周围的雷数进行计算
void low_Calu_Mine(char mine[LOW_ROWS][LOW_COLS], char show[LOW_ROWS][LOW_COLS], int row, int col)
{
int x = 0;
int y = 0;
int count = row*col - EASY_COUNT;
printf("请输入选择的坐标:\n");
while (count)
{
scanf("%d%d", &x, &y);
printf("\n");
if (x >= 1 && x <= row&&y >= 1 && y <= col)
{
if (mine[x][y] == '1')
{
printf("很遗憾,此处有雷,光荣牺牲\n");
low_DisplayBoard(mine, LOW_ROW, LOW_COL);
break;
}
else
{
low_Sweep(mine, show, x, y);
low_DisplayBoard(show, LOW_ROW, LOW_COL);
count = low_get_mine(show, LOW_ROW, LOW_COL) - EASY_COUNT;
}
}
else
{
printf("输入坐标错误,请重新输入\n");
}
}
if (0 == count)
{
printf("排雷成功\n");
low_DisplayBoard(mine, LOW_ROW, LOW_COL);
}
}
再从函数内部详细的讲解一下:
1.在排雷函数的开始要让用户对坐标进行输入,输入这一块就要考虑几个问题。
首先是坐标的合法性,坐标的输入要在棋盘的范围之内,如果输入超出了边界,就要重新输入;并且扫雷游戏只需要玩家不断的输入坐标,然后电脑计算地雷数,输入是不间断的,直到玩家排雷成功或者踩到地雷才结束,因此输入这一块要用循环来完成。
2.排雷并不只是标识出输入的坐标周围的雷数,按照电脑上扫雷小游戏的玩法,要能够扩展出一片,如下图:
也就是说,如果输入的坐标周围没有地雷,就需要去遍历周围的8个位置,如果其中某一个位置的周围也没有地雷,那么就继续向外遍历,直到遇到一个坐标周围出现地雷,这部分需要用递归来完成。通俗点解释:就是如果计算出坐标周围地雷数是0,就去向外扩展。
在这部分编写的时候有一些复杂,主要问题就是:在向外扩展的时候,如果是边界的坐标,向外不断扩展就会导致数组越界,因此递归时需要有判断条件,就是遍历的数组坐标要限制在合理的范围内。
3.排雷的结果判定
踩到地雷很容易判断,就是输入坐标的所在是字符‘1’;
但是判断排雷成功就需要去遍历整个数组,每输入一次遍历一次数组,计算没有输入的坐标个数,直到没有输入的坐标个数变为0.就是说能够输入的坐标已经没有了,并且还没有踩到地雷,这就是排雷成功了。
运行结果
菜单界面
初级
中级
高级
踩到地雷
排雷成功
总结
这就是扫雷小游戏的基本思路,但是我认为还有一些地方可以改善:
1.现在只是最基本的程序,使用输入坐标来完成的,我觉得可以修改为用鼠标点击进行选择,这样子可以提高玩家的游戏体验。
2.我觉得如果可以上升到图形化界面,可以将字符‘0’和字符‘1’定义为其他的图案,不需要修改程序。
Github链接:https://github.com/YJYCXD/dream/tree/master/Hanoi