今天我们要做的就是用C语言写出扫雷游戏的代码。对不起,今天是一个没有骚话的很正经的分享。还请继续看下去。不想看讲解的,完整代码在文末。OK,进入正文!
在写代码之前,我们应该要想想设计步骤。同上次写三子棋一样,我们第一步要做的就是打印游戏开始菜单,为了程序整体看上去显得比较整齐,我们把游戏菜单也用一个函数实现。代码如下。
void menu()
{
printf("****************************\n");
printf("******** 1.玩游戏 *******\n");
printf("******** 0.退出 *******\n");
printf("****************************\n");
}
当玩家选择玩游戏后,也就是正式要设计游戏部分了,我们还是要先初始化两个二维数组,初始化两个的目的是分清设计者雷盘和玩家雷盘,我们程序员需要能看到哪些地方有雷哪些地方没有雷,而我们展示给玩家的是一个隐藏了雷的雷盘。所以需要初始化两个雷盘。
这里的ROWS和COLS是我们雷盘的行数和列数,进行初始化时我没有用for循环(比较麻烦,但便于理解,可自行尝试)而是用了memset库函数,这个函数的具体用法如果大家不懂可以自行查阅,这里不做陈述。初始化了雷盘后就要给雷盘里赋值了,我们将玩家雷盘全初始化为’*'星号,将设计者雷盘初始化为’0’字符0。这里为什么将设计者雷盘初始化为字符0而不是其他符号,我们在这里先卖个关子,在后面会解释。
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char set) //初始化雷盘
{
memset(&arr[0][0], set, rows*cols*sizeof(arr[0][0]));
}
雷盘初始化之后为了看看我们的雷盘到底有没有初始化好,我们还需要一个打印函数,以便我们将雷盘打印出来检测。打印出来的本应该只是一个雷盘,但为了输入坐标方便,我们将雷盘加上行号和列号。代码如下:
void DisplayBoard(char arr[ROWS][COLS], int row, int col) //打印雷盘
{
int i = 0;
int j = 0;
for(i=0; i<=col; i++)
{
printf("%d ",i);
}
printf("\n");
for(i=1; i<=row; i++)
{
printf("%d ",i);
for(j=1; j<=col; j++)
{
printf("%c ",arr[i][j]);
}
printf("\n");
}
}
将雷盘初始化好打印后也没问题,我们就应该向雷盘中布雷了。布雷必定是一个随机的过程,所以我们需要用到随机数,为了让系统产生的随机数在我们雷盘的行和列内,需要对随机数加以限制。并且在布雷前应该先检查这个坐标有没有雷,如果已经有雷了,我们自然要重新选择地方布雷。每布一个雷,总雷数就要减一。
void SetMine(char arr[ROWS][COLS], int row, int col) //布置雷
{
int count = Easy_Count;
int x = 0;
int y = 0;
while(count)
{
x = rand()%row+1;
y = rand()%col+1;
if(arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
完成了上述操作后,我们就应该扫雷了。在扫雷之前我们需要弄清楚几个问题。首先我们为了玩家的游戏体验,是不是让他第一次不被雷炸死,也就是如果玩家第一次踩到雷了,我们需要将雷移到别的地方,保证第一次安全不被炸死。其次如果玩家踩到了非雷的地方,我们是不是应该将这个坐标周围8个位置的雷数告诉玩家,显示出来。再有是不是还需要展开功能呢,如果这个坐标周围8个坐标也都没有雷,我们是不是可以将它们展开,直到遇到雷呢。这样其实就跟我们平时在电脑上玩的扫雷差不多了。所以我们的扫雷函数中应该有实现这几个功能的函数。
首先是我们获取周围雷数的函数。还记得我们开始给设计者雷盘布置时,没有雷的地方都是字符0,有雷的地方是字符1。现在我们需要获取一个坐标周围的雷数时,只需要将它们加起来就是了。而不用循环便利。
int GetMineCount(char mine[ROWS][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*48;
}
下面是第一次保证安全的函数,这个很简单,如果它是雷的话,我们给它重新赋值,让它不是雷就可以了。
void safe(char mine[ROWS][COLS], int x, int y) //第一次安全
{
mine[x][y] = '0';
x = rand()%9+1;
y = rand()%9+1;
mine[x][y] = '1';
}
我们还需要一个判断函数,不然什么时候扫雷结束啊。总不能一直扫下去吧。当玩家没被雷炸死时,这个坐标就会显示出数字。那如果剩下的没扫的地方的和是总雷数,那就是玩家把雷扫完了,也就是游戏胜利了。
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++;
}
}
}
return count;
}
接下来是展开函数,也就是遍历该坐标周围每一点,查它们的周围有没有雷,这个比较简单。
void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y) //展开雷阵
{
int ret = 0;
ret = GetMineCount(mine, x, y);
if (ret == 0)
{
show[x][y] = ' ';
if (x - 1>0 && y>0 && show[x - 1][y] == '*')
OpenMine(mine, show, row, col, x - 1, y);
if (x - 1>0 && y + 1 <= col && show[x - 1][y + 1] == '*')
OpenMine(mine, show, row, col, x - 1, y + 1);
if (x>0 && y + 1 <= col && show[x][y + 1] == '*')
OpenMine(mine, show, row, col, x, y + 1);
if (x + 1 <= row && y + 1 <= col && show[x + 1][y + 1] == '*')
OpenMine(mine, show, row, col, x + 1, y + 1);
if (x + 1 <= row && y>0 && show[x + 1][y] == '*')
OpenMine(mine, show, row, col, x + 1, y);
if (x + 1 <= row && y - 1>0 && show[x + 1][y - 1] == '*')
OpenMine(mine, show, row, col, x + 1, y - 1);
if (x>0 && y - 1>0 && show[x][y - 1] == '*')
OpenMine(mine, show, row, col, x, y - 1);
if (x - 1>0 && y - 1>0 && show[x - 1][y - 1] == '*')
OpenMine(mine, show, row, col, x - 1, y - 1);
}
else
{
show[x][y] = GetMineCount(mine, x, y) + '0';
}
}
完成了上述准备工作后我们就可以正式开始扫雷了。
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) //排雷
{
int x = 0;
int y = 0;
int times = 0;
int win = 0;
int count = 0;
while(Iswin(show, row, col) != Easy_Count)
{
printf("输入你要排查的坐标:");
scanf("%d%d",&x, &y);
times++;
if(x>=1 && x<=row && y>=1 && y<=col) //判断坐标合法
{
if(mine[x][y] == '1' ) //为雷分为第一次为雷和其他次为雷
{
if(1 == times) //第一次为雷抢救一下
{
safe(mine, x, y);
count = GetMineCount(mine, x, y);
show[x][y] = count+48;
OpenMine(mine, show, ROW, COL, x, y);
DisplayBoard(show, row ,col);
win++;
}
else //不是第一次不抢救
{
printf("很遗憾,你被炸死了\n");
DisplayBoard(mine, row, col);
break;
}
}
if(mine[x][y] == '0') //输入坐标不是雷
{
count = GetMineCount(mine, x, y); //获取周围雷数
show[x][y] = count+48; //显示雷数
OpenMine(mine, show, ROW, COL, x, y);
DisplayBoard(show, row ,col);
win++;
}
}
else
{
printf("输入坐标无效,重新输入\n");
}
}
if(Iswin(show, row, col) == Easy_Count) //获胜条件
{
printf("恭喜你,排雷成功\n");
printf("共计扫雷次数为%d\n",times);
DisplayBoard(mine, row, col);
}
}
至此我们的函数的游戏部分就完成了,我们需要把它串起来。
完整代码如下,大家自行就能看懂。
我们将所有代码分成这三个部分,game.h里放我们所有自己编写的函数的头文件。game.c里放我们自己编写的函数。teat.c里面放我们的主函数,以及将游戏串起来的game函数。
game.h内的代码
#ifndef __GAME_H__
#define __GAME_H__
#include
#include
#include
#include
#define ROW 9
#define COL 9
#define ROWS 11
#define COLS 11
#define Easy_Count 10
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char set); //初始化雷阵
void DisplayBoard(char arr[ROWS][COLS],int row, int col); //打印雷盘
void SetMine(char arr[ROWS][COLS], int row, int col); //布置雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col); //扫雷
int GetMineCount(char mine[ROWS][COLS], int row, int col); //获取周围雷数
void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y); //展开雷阵
void is_win(char mine[ROWS][COLS], int x, int y);
#endif //__GAME_H__
game.c内的代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char set) //初始化雷盘
{
memset(&arr[0][0], set, rows*cols*sizeof(arr[0][0]));
}
void DisplayBoard(char arr[ROWS][COLS], int row, int col) //打印雷盘
{
int i = 0;
int j = 0;
for(i=0; i<=col; i++)
{
printf("%d ",i);
}
printf("\n");
for(i=1; i<=row; i++)
{
printf("%d ",i);
for(j=1; j<=col; j++)
{
printf("%c ",arr[i][j]);
}
printf("\n");
}
}
void SetMine(char arr[ROWS][COLS], int row, int col) //布置雷
{
int count = Easy_Count;
int x = 0;
int y = 0;
while(count)
{
x = rand()%row+1;
y = rand()%col+1;
if(arr[x][y] == '0')
{
arr[x][y] = '1';
count--;
}
}
}
int GetMineCount(char mine[ROWS][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*48;
}
void safe(char mine[ROWS][COLS], int x, int y) //第一次安全
{
mine[x][y] = '0';
x = rand()%9+1;
y = rand()%9+1;
mine[x][y] = '1';
}
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++;
}
}
}
return count;
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) //排雷
{
int x = 0;
int y = 0;
int times = 0;
int win = 0;
int count = 0;
while(Iswin(show, row, col) != Easy_Count)
{
printf("输入你要排查的坐标:");
scanf("%d%d",&x, &y);
times++;
if(x>=1 && x<=row && y>=1 && y<=col) //判断坐标合法
{
if(mine[x][y] == '1' ) //为雷分为第一次为雷和其他次为雷
{
if(1 == times) //第一次为雷抢救一下
{
safe(mine, x, y);
count = GetMineCount(mine, x, y);
show[x][y] = count+48;
OpenMine(mine, show, ROW, COL, x, y);
DisplayBoard(show, row ,col);
win++;
}
else //不是第一次不抢救
{
printf("很遗憾,你被炸死了\n");
DisplayBoard(mine, row, col);
break;
}
}
if(mine[x][y] == '0') //输入坐标不是雷
{
count = GetMineCount(mine, x, y); //获取周围雷数
show[x][y] = count+48; //显示雷数
OpenMine(mine, show, ROW, COL, x, y);
DisplayBoard(show, row ,col);
win++;
}
}
else
{
printf("输入坐标无效,重新输入\n");
}
}
if(Iswin(show, row, col) == Easy_Count) //获胜条件
{
printf("恭喜你,排雷成功\n");
printf("共计扫雷次数为%d\n",times);
DisplayBoard(mine, row, col);
}
}
void OpenMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y) //展开雷阵
{
int ret = 0;
ret = GetMineCount(mine, x, y);
if (ret == 0)
{
show[x][y] = ' ';
if (x - 1>0 && y>0 && show[x - 1][y] == '*')
OpenMine(mine, show, row, col, x - 1, y);
if (x - 1>0 && y + 1 <= col && show[x - 1][y + 1] == '*')
OpenMine(mine, show, row, col, x - 1, y + 1);
if (x>0 && y + 1 <= col && show[x][y + 1] == '*')
OpenMine(mine, show, row, col, x, y + 1);
if (x + 1 <= row && y + 1 <= col && show[x + 1][y + 1] == '*')
OpenMine(mine, show, row, col, x + 1, y + 1);
if (x + 1 <= row && y>0 && show[x + 1][y] == '*')
OpenMine(mine, show, row, col, x + 1, y);
if (x + 1 <= row && y - 1>0 && show[x + 1][y - 1] == '*')
OpenMine(mine, show, row, col, x + 1, y - 1);
if (x>0 && y - 1>0 && show[x][y - 1] == '*')
OpenMine(mine, show, row, col, x, y - 1);
if (x - 1>0 && y - 1>0 && show[x - 1][y - 1] == '*')
OpenMine(mine, show, row, col, x - 1, y - 1);
}
else
{
show[x][y] = GetMineCount(mine, x, y) + '0';
}
}
test.c内的代码
#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
printf("****************************\n");
printf("******** 1.玩游戏 *******\n");
printf("******** 0.退出 *******\n");
printf("****************************\n");
}
void game()
{
char mine[ROWS][COLS] = {0};
char show[ROWS][COLS] = {0};
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');
DisplayBoard(show, ROW, COL);
SetMine(mine,ROW,COL);
//DisplayBoard(mine, ROW, COL);
FindMine(mine, show, ROW, COL);
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请选择:");
scanf("%d",&input);
switch(input)
{
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("重新输入\n");
break;
}
}while(input);
return 0;
}