1.关于扫雷游戏的介绍
首先如果我们要实现扫雷游戏的话,要明白扫雷游戏是如何运行的!
这里是一个网页版的扫雷游戏的链接,可以先玩一下,这样可以对我们实现扫雷游戏的代码有非常大的帮助!
扫雷游戏网页版 - Minesweeper
2.关于扫雷游戏原理的介绍
根据上面三张图我们可以了解到,对于扫雷游戏我们每次可以进行的操作是,对相关点进行标记或者对相关点进行清扫。
b.而对该点进行清扫的话,又会有两中情况发生,其一是如果该点是雷,哇,金色传说!恭喜你,被雷炸死了!其二,就是如果该点不是雷,那么将返回该点周围一圈八个点的雷数,如果该点周围没有雷,那么将对大量无雷点进行地毯式清扫。
c.那么我们要清扫的点如果是非雷点我们要怎样去实现返回该点附近的雷数呢!
在这里我所用的方法是用两个二维数组去实现的,一个是数组是用来布雷的,而另一个是用来返回非雷点附近的雷数的
3.扫雷游戏相关代码的实现
a.扫雷游戏的菜单界面
做为一个游戏程序,我们在游戏开始前当然要询问玩家是否要开始进行游戏!
#pragma once
#include
#include
#include
#define ROWS 11
#define COLS 11
#define row 9
#define col 9
#define quantity 10
void menu(); //游戏菜单
void mine_clearance_game(); //游戏程序
void Initmine(char [][COLS], int, int, char); //对雷区初始化
void Displaymine(char[][COLS], int, int); //打印雷区
void Thunder_release(char[][COLS], int, int); //放雷
int mine_clearance(char[][COLS], char [][COLS], int, int); //扫雷
int thunder_number(char[][COLS], int, int); //计算该点附近的雷数
int judge(char [][COLS], int, int); //返回已扫点的个数
void flag(char [][COLS], int, int); //对雷区做标记
void menu1();
void mine_clearance1(char[][COLS], char[][COLS], int, int); //扫雷并平铺
void Tile(char[][COLS], char[][COLS], int, int);
上面的代码段是扫雷游戏的头文件
#include"mine_clearance.h"
int main(void)
{
int value = 0;
srand((unsigned)time(NULL));
do
{
printf("是否要玩扫雷玩家请做出你的选择:\n");
menu();
scanf("%d",&value);
switch (value)
{
case 1:mine_clearance_game();
break;
case 0:printf("程序退出!\n");
break;
default:printf("您输入错误请重新输入:\n");
break;
}
} while (value);
return 0;
}
void menu()
{
printf("***********************************\n"
"************ 1.play ************\n"
"************ 0.exit ************\n"
"***********************************\n"
"***********************************\n");
}
在主函数中我们首先声明了一个名为value的int类型的变量用来介绍玩家做出的选择是1还是0,然后,接着就是调用srand()库函数,初始化随机数的生成器,然后是,do{}while循环让玩家先进菜单界面,通过switch多重选择结构来实现对play还是exit的选择,当然玩家只可以在1或0之间选择如果玩家输入其他的数值,将重新输入!
当玩家选择0就会退出程序!
b.扫雷游戏的进行
void mine_clearance_game()
{
char mine[ROWS][COLS] = { 0 };
char mine1[ROWS][COLS] = { 0 };
Initmine(mine, row, col, '0');
Initmine(mine1, row, col, '*');
//Displaymine(mine, row, col);
system("cls");
Displaymine(mine1, row, col);
Thunder_release(mine, row, col);
Displaymine(mine, row, col);
int input = 0;
int flagf = 1;
while (flagf)
{
system("cls");
Displaymine(mine1, row, col);
//Displaymine(mine, row, col);
printf("玩家请你做出选择:\n");
menu1();
scanf("%d",&input);
if (input)
{
system("cls");
flagf=mine_clearance(mine, mine1, row, col);
}
else
{
system("cls");
flag(mine1, row, col);
}
}
//mine_clearance1(mine, mine1, row, col);
}
当玩家选择1后,程序的执行将进入mine_clearance_game()函数中,首先创建雷阵mine和雷阵mine1两个都为char类型的二维数组,然后调用Initmine()函数实现对两个雷阵的初始化,放雷阵均初始话为字符0,反数阵均初始化为字符*
void Initmine(char mine[][COLS], int a, int b, char get)
{
int i = 0;
int j = 0;
for (i = 0; i < ROWS; i++)
{
for (j = 0; j < COLS; j++)
{
mine[i][j] = get;
}
}
}
Displaymine()函数是对数组的打印
void Displaymine(char mine[][COLS], int a, int b)
{
printf("- - - - 扫雷 - - - -\n");
int i = 0;
int j = 0;
for (i = 0; i <= a; i++)
{
printf("%d ",i);
}
printf("\n");
for (i = 1; i <= a; i++)
{
printf("%d ", i);
for (j = 1; j <= b; j++)
{
printf("%c ",mine[i][j]);
}
printf("\n");
}
}
Thunder_release()函数是在雷阵中放置雷
void Thunder_release(char mine[][COLS], int a, int b)
{
int x = 0;
int y = 0;
int n = quantity;
while (n > 0)
{
x = rand() % a+1;
y = rand() % b+1;
if (mine[x][y] == '0')
{
mine[x][y] = '1';
n--;
}
}
}
rand()函数是生成一个随机数,然后我们对这个随机数进行求模运算在加1就会得到1到a的数值quantity是一个字符常量值为要放置的雷数;
然后接着就是while循环,在循环中让玩家做出选择要标记点还是要清扫点
void menu1()
{
printf("***********************************\n"
"************ 1.mine_clearance **\n"
"************ 0.flag ************\n"
"***********************************\n"
"***********************************\n");
}
选择1是进行清扫点
int mine_clearance(char mine[][COLS],char mine1[][COLS], int a, int b)
{
int x = 0;
int y = 0;
int count = 0;
int number = 0;
int input = 0;
while (number
int thunder_number(char mine[][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';
}
int judge(char mine[][COLS], int a, int b)
{
int i = 0;
int j = 0;
int sum = 0;
for (i = 1; i <=a; i++)
{
for (j = 1; j <=b; j++)
{
if (mine[i][j] != '*')
{
sum++;
}
}
}
return sum;
}
注意在扫雷过程中要注意该点是不是标记点,且已经清扫过的点就不需要在清扫了,这里返回的是int类型的值,对flags的值进行改变,当return 1说明玩家可以继续进行扫雷游戏,return 0说明玩家不是被炸死了就是玩家赢得了扫雷游戏的胜利!
选择0是进行标记
void flag(char mine1[][COLS], int a, int b)
{
int x = 0;
int y = 0;
static int number = 0;
int input = 0;
while (number<= quantity)
{
Displaymine(mine1, row, col);
printf("玩家请你做出标记:\n");
scanf("%d %d", &x, &y);
if (mine1[x][y] == '*')
{
mine1[x][y] = ' ';
number++;
Displaymine(mine1, row, col);
}
else
{
system("cls");
printf("该点已被清扫不可在被标记,请重新标记:\n");
continue;
}
system("cls");
Displaymine(mine1, row, col);
printf("玩家你是否还要做标记:\n");
menu();
scanf("%d",&input);
if (!input)
{
break;
}
}
}
此时我们的扫雷游戏简单版就完成了!下面是运行实例图
c.递归实现返回数并平铺
void mine_clearance1(char mine[][COLS], char mine1[][COLS], int a, int b)
{
int x = 0;
int y = 0;
int n = 0;
while (n < 2)
{
printf("玩家请你输入你想清扫的点的坐标:\n");
scanf("%d %d", &x, &y);
if (mine[x][y] == '1')
{
printf("该点有雷,你被炸死了!");
}
else
{
Tile(mine, mine1, x, y);
Displaymine(mine1, row, col);
}
n++;
}
}
void Tile(char mine[][COLS], char mine1[][COLS], int x, int y)
{
int count = 0;
count = thunder_number(mine, x, y)+'0';
mine1[x][y] = '#';
int i = 0, j = 0;
if (count == '0')
{
for (i = -1; i <= 1; i++)
{
for (j = -1; j <= 1; j++)
{
if (x+i>=1&&x+i<=9&&y+j>=1&&y+j<=9&&mine1[x + i][y + j] != '#') //防止在递归的过程中数组下标越界
{
Tile(mine, mine1, x + i, y + j);
}
/*else
{
break;
}*/
}
}
}
mine1[x][y] = count;
}
用递归来解决问题时,是将问题由大变小细化,然后一步一步先解决小的问题在解决大的问题,在用递归处理问题时主要是寻找问题的大模块和小模块之间的粘连性,找到了粘连性也就找到了用递归处理问题的关键!
而对于本问题,当该点不是雷且该点周围的八个点的雷数为0,那么就进行平铺清扫!
当然对于要清扫的点最关键的还是返回该点周围八个点的雷数,那么对于平铺清扫这个问题的用递归处理的时,该问题的粘连性是什么呢?
就像往一个平面倒水一样,水要覆盖地面,前提是该点上无物体,就相当于我们要有清扫一个点,这个点的周围没有雷那么文件要继续对周围的点进行清扫,直到有雷点,
这里要注意原清扫点不可以再被清扫,否则就无法出递归了,还有在递归的时候要注意不能发生数组下标的越界!
在这里有一个问题就是如果雷数过少,代码就会出错误,因为递归每次都是开辟新的存储空间,如果雷数过少就会发生栈溢出,就像这样,如果雷数为0,程序进行就是如图
当雷数调为20