·功能介绍
我们实现一个扫雷,那么必须具有其基本的功能,如下图:
那么相比较一般的用C实现的扫雷,我们需要添加如下的三个功能。
防止第一次被炸死;标记雷;连续展开。
·代码实现以及解释
一、主函数的实现以及扫雷的构思框架(test.c)
int main()
{
int input=0;
do
{
menu();
printf("请选择\n");
scanf("%d",&input);
switch(input)
{
case 1:
printf("请开始游戏\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("请重新选择\n");
break;
}
}
while(input);
return 0;
}
·首先对于主函数我们需要一个菜单界面以便于让我们进行选择,上面我们使用了switch语句让玩家进行选择。
·就像大家看到的,里面还包括了一个game();以及一个menu();前者是我们进行扫雷所需要的函数,后者是我们的一个菜单界面,效果如下图:
void menu()
{
printf("*****************************************\n");
printf("***************1.开始游戏****************\n");
printf("************ 0.退出游戏 *************\n");
printf("*****************************************\n");
}
·这个menu函数相信大家都比较熟悉,再次就不多作说明了。
void game()
{
char mine[ROW][COL];
char show[ROW][COL];
init_board(mine, show);
display_board(show);
set_board(mine);
show_board(mine);
my_step(mine,show);
}
·这是我们的game函数,下面我们来说明一下我们扫雷的游戏函数需要什么?
·mine、show 首先我们定义了两个9x9大小棋盘分别为mine以及show,其中一个为真实的棋盘用于系统的判定,我们把其中的参数设置为1和0,其中的1为雷。另外一个棋盘为展示棋盘为我们用户在扫雷的时候看到的棋盘,展开的地方我们用数字表示,隐藏的部分我们用字符'*'来表示。
注意:我们用二维数组来实现棋盘,row和rol分别表示行和列。
·init_board(); 接着,我们使用了一个初始化函数即init_board();,这个函数的主要功能就是在游戏开始前将我们的两个棋盘分别初始化。就像上面我们提到的一个初始化为1和0的棋盘,一个初始为全部是*的棋盘。
·display_board(); 我们实现了棋盘的初始化自然要让我们看到棋盘的样子才能进行扫雷,因此我们使用了这个函数。注意:展示棋盘仅仅展示我们的show这个棋盘。
·show_board(); 这个是我们在实现程序时为了看到真实棋盘的样子所写的一个函数,用于展示真实的棋盘给用户看到。
·my_step(); 这个函数就是玩家行动的意思,也就是扫雷已经开始啦!
二、扫雷功能的实现(game.c)
·在开始之前我们需要说明的是,由于判定以及展开的时候需要对周围八个点进行处理,在处理靠边的点的时候容易发生越界,因此我们将真实棋盘设定为11x11,但是我们所看到的仅仅是一个9x9的棋盘,其余的点我们全部设置为0,这样就避免了错误。
void init_board(char mine[ROW][COL], char show[ROW][COL])
{
int i = 0;
int j = 0;
for (i = 1; i < ROW - 1; i++)
{
for (j = 1; j < COL - 1; j++)
{
mine[i][j] = '0';
}
}
for (i = 1; i < ROW - 1; i++)
{
for (j = 1; j < COL - 1; j++)
{
show [i][j] = '*';
}
}
}
·这边让我们的函数分别初始化,真实的函数全部初始为0,放雷放在我们后面的函数进行。
void display_board(char show[ROW][COL])
{
int i, j;
for(i=0; i<ROW-1; i++)
{
printf("%d ",i);
}
for(i=1; i<ROW-1; i++)//仅仅展示9x9的棋盘
{
printf("\n");
printf("%d ",i);
for(j=1; j<COL-1; j++)
{
printf("%c ",show[i][j]);
}
}
printf("\n");
}
·在这个函数中除了展示我们的show数组之外,我们也给周围的行和列表明的数字以便于扫雷的时候输入坐标。注意:我们仅仅只展示9x9的棋盘。实现后的效果如下图:
void set_board(char mine[ROW][COL])
{
int i,j;
int count=10;
while(count)
{
i=rand()%8+1;
j=rand()%8+1;
if(mine[i][j]=='0')
{
mine[i][j]='1';
count--;
}
}
}
·我们这边使用了一个产生随机值的语句rand();,当然我们需要引用相关的头文件:
#include<time.h>
#include<stdlib.h>
这边需要注意的是我们的坐标设定的是1~9,所以rand()%9产生0~9就是不对的。当然你可能会考虑到如果重复位置设雷怎么办?因此我们使用了一个if语句进行判定。
void show_board(char mine[ROW][COL])
{
int i, j;
for(i=0; i<ROW-1; i++)
{
printf("%d ",i);
}
for(i=1; i<ROW-1; i++)
{
printf("\n");
printf("%d ",i);
for(j=1; j<COL-1; j++)
{
printf("%c ",mine[i][j]);
}
}
printf("\n");
}
·这是展示真实棋盘的函数,同上面的display_board();是一样的。
int get_mine(char mine[ROW][COL] ,int i,int j)
{
int count=0;
if(mine[i-1][j]=='1')
{
count++;
}
if(mine[i-1][j-1]=='1')
{
count++;
}
if(mine[i-1][j+1]=='1')
{
count++;
}
if(mine[i][j+1]=='1')
{
count++;
}
if(mine[i][j-1]=='1')
{
count++;
}
if(mine[i+1][j-1]=='1')
{
count++;
}
if(mine[i+1][j]=='1')
{
count++;
}
if(mine[i+1][j+1]=='1')
{
count++;
}
return count;
}
·这边我们写了一个新的函数叫做get_mine函数
·这个函数的主要作用是获取你输入坐标(扫雷位置)周围八个点有雷的个数,返回一个int值。通过这个函数,我们就可以实现扫雷周围显示雷个数的功能了。
void safe_board(char mine[ROW][COL],int i,int j)
{
int count=1;
while(count)
{
if(mine[i][j]='1')
{
int m=rand()%8+1;
int n=rand()%8+1;
mine[i][j]='0';
mine[m][n]='1';
count--;
}
else
count--;
}
}
·这边是一个安全检查的函数,这个函数的作用就是当你第一输入位置扫到雷的时候我们可以将这个雷移到其他的地方。这边我们定义了一个count,为了防止移走的雷在随机设置的时候设置到另外一个雷上。
void menu_board()
{
printf("**** 1.扫雷 0.标雷与撤销 ***\n");
}
·这是我们在扫雷之前的一个菜单选项,可以让我们扫雷或者是进行标记。
void open_board(char mine[ROW][COL],char show[ROW][COL],int i,int j)
{
if(mine[i][j]=='0'&&i>=0&&j>=0&&show[i][j]=='*')
{
show[i][j]=get_mine(mine,i,j)+'0';
}
if(mine[i][j-1]=='0'&&i>=0&&j-1>=0&&show[i][j-1]=='*')
{
show[i][j-1]=get_mine(mine,i,j-1)+'0';
if(get_mine(mine,i,j-1)==0)
{
open_board(mine,show,i,j-1);
}
}
if(mine[i][j+1]=='0'&&i>=0&&j+1>=0&&show[i][j+1]=='*')
{
show[i][j+1]=get_mine(mine,i,j+1)+'0';
if(get_mine(mine,i,j+1)==0)
{
open_board(mine,show,i,j+1);
}
}
if(mine[i-1][j]=='0'&&i-1>=0&&j>=0&&show[i-1][j]=='*')
{
show[i-1][j]=get_mine(mine,i-1,j)+'0';
if(get_mine(mine,i-1,j)==0)
{
open_board(mine,show,i-1,j);
}
}
if(mine[i-1][j-1]=='0'&&i-1>=0&&j-1>=0&&show[i-1][j-1]=='*')
{
show[i-1][j-1]=get_mine(mine,i-1,j-1)+'0';
if(get_mine(mine,i-1,j-1)==0)
{
open_board(mine,show,i-1,j-1);
}
}
if(mine[i-1][j+1]=='0'&&i-1>=0&&j+1>=0&&show[i-1][j+1]=='*')
{
show[i-1][j+1]=get_mine(mine,i-1,j+1)+'0';
if(get_mine(mine,i-1,j+1)==0)
{
open_board(mine,show,i-1,j+1);
}
}
if(mine[i+1][j+1]=='0'&&i+1>=0&&j+1>=0&&show[i+1][j+1]=='*')
{
show[i+1][j+1]=get_mine(mine,i+1,j+1)+'0';
if(get_mine(mine,i+1,j+1)==0)
{
open_board(mine,show,i+1,j+1);
}
}
if(mine[i+1][j]=='0'&&i+1>=0&&j>=0&&show[i+1][j]=='*')
{
show[i+1][j]=get_mine(mine,i+1,j)+'0';
if(get_mine(mine,i+1,j)==0)
{
open_board(mine,show,i+1,j);
}
}
if(mine[i+1][j-1]=='0'&&i+1>=0&&j-1>=0&&show[i+1][j-1]=='*')
{
show[i+1][j-1]=get_mine(mine,i+1,j-1)+'0';
if(get_mine(mine,i+1,j-1)==0)
{
open_board(mine,show,i+1,j-1);
}
}
}
如上面代码,展开函数是我们的重点。
·这个函数包括了两个部分,一个是展示我们周围的雷数,另外一个是展开周围,继续展示雷数。
·展开就是上图的功能,当你输入一个坐标的时候,判定周围有没有雷,有雷的显示一个数字,没有雷的话显示0。当继续遇到0的时候将0直接展开,数字的话依然显示数字。最后循环,当周围全部为大于0的数字时停止展开。
·if中的条件:接着让我们先来看if中的条件吧。首先i>=0&&j>=0判断坐标的位置,防止无限展开形成越界。第二,(mine[i][j]=='0',判定这个位置是否有雷。第三,show[i][j]=='*',判定这个位置是否已经展开。
·数字的显示:调用我们刚才的get_mine();函数,通过+'0'来将int转成字符数字便于显示。
·递归:在很多的相关代码中仅仅显示周围八个点的值,而我们需要实现连续展开,条件中如果为0,改变所传的参数,调用open_board();继续展开,知道周围全部的数字不为0时。
void my_step(char mine[ROW][COL], char show[ROW][COL])
{
int i = 0;
int j = 0;
int count=71;
int input=0;
while (count)
{
do
{
menu_board();
printf("请选择\n");
scanf("%d",&input);
switch(input)
{
case 1:
printf("请输入坐标\n");
scanf("%d%d", &i,&j);
if(count==71)
{
safe_board(mine,i,j);
}
if (mine[i][j] == '1')
{
printf("踩到雷了:\n");
show_board(mine);
return;
}
else
{
open_board(mine,show,i,j);
display_board(show);
count--;
}
break;
case 0:
printf("输入坐标\n");
scanf("%d%d", &i,&j);
if(show[i][j]=='*')
{
show[i][j]='!';
}
else
show[i][j]='*';
display_board(show);
break;
default:
printf("请重新选择\n");
break;
}
break;
}while(input);
}
printf("扫雷成功\n");
}
·这是我们game中的最重要的函数。
·我们定义了一个变量为count,count即为安全的位置,一共为71个,每一次扫雷完成都会--,当count为0的时候,我们跳出do while,显示扫雷成功。
·我们通过switch来实现开始选择标雷还是扫雷,如果输入为0,就是简单的将show中的*改为!,以此来标识是否为雷。
·那么safe_check();函数是如何调用的呢。我们只需要知道第一个踩的是否为雷就行了,这边我们直接判断count的值来实现,如果为count==71那么意思就是我们猜到雷了,而一旦转移那么我们就相当于扫出了一个安全区域,那么count--就小于71这个数值,这样就巧妙的将safe_check();只实现了一次,以防出现多次调用了。
三、头文件的引用(game.h)
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 11
#define COL 11
void init_board(char mine[ROW][COL], char show[ROW][COL]);//初始化
void display_board(char show[ROW][COL]);//展示棋盘
void set_board(char mine[ROW][COL]);//放雷
void show_board(char mine[ROW][COL]);//展示棋盘
void my_step(char mine[ROW][COL], char show[ROW][COL]);//玩家行动
int get_mine(char mine[ROW][COL]);//探知雷数
void open_board(char mine[ROW][COL],char show[ROW][COL],int i,int j);//展开
void safe_board(char mine[ROW][COL],int i,int j);//第一步安全检查
void menu_board();//第二个菜单
·头文件中,我们需要定义所有的头文件以及引用的各种函数,这边添加了相关的注释方便大家观看。
·其中的define定义了行和列的大小,我们可以直接通过改变ROW和COL的值来改变难度,当然你要是愿意的话,写一个选择难度的函数也是可以的。
·代码展示
最后,直接贴出我们的三个文件的代码,分别为test.c、game.c以及game.h
game.h
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define ROW 11
#define COL 11
void init_board(char mine[ROW][COL], char show[ROW][COL]);//初始化
void display_board(char show[ROW][COL]);//展示棋盘
void set_board(char mine[ROW][COL]);//放雷
void show_board(char mine[ROW][COL]);//展示棋盘
void my_step(char mine[ROW][COL], char show[ROW][COL]);//玩家行动
int get_mine(char mine[ROW][COL]);//探知雷数
void open_board(char mine[ROW][COL],char show[ROW][COL],int i,int j);//展开
void safe_board(char mine[ROW][COL],int i,int j);//第一步安全检查
void menu_board();//第二个菜单
test.c
#include"game.h"
void menu()
{
printf("*****************************************\n");
printf("***************1.开始游戏****************\n");
printf("************ 0.退出游戏 *************\n");
printf("*****************************************\n");
}
void game()
{
char mine[ROW][COL];
char show[ROW][COL];
init_board(mine, show);
display_board(show);
set_board(mine);
show_board(mine);
my_step(mine,show);
}
int main()
{
int input=0;
do
{
menu();
printf("请选择\n");
scanf("%d",&input);
switch(input)
{
case 1:
printf("请开始游戏\n");
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("请重新选择\n");
break;
}
}
while(input);
return 0;
}
game.c
#include"game.h"
void init_board(char mine[ROW][COL], char show[ROW][COL])
{
int i = 0;
int j = 0;
for (i = 1; i < ROW - 1; i++)
{
for (j = 1; j < COL - 1; j++)
{
mine[i][j] = '0';
}
}
for (i = 1; i < ROW - 1; i++)
{
for (j = 1; j < COL - 1; j++)
{
show [i][j] = '*';
}
}
}
void display_board(char show[ROW][COL])
{
int i, j;
for(i=0; i<ROW-1; i++)
{
printf("%d ",i);
}
for(i=1; i<ROW-1; i++)
{
printf("\n");
printf("%d ",i);
for(j=1; j<COL-1; j++)
{
printf("%c ",show[i][j]);
}
}
printf("\n");
}
void set_board(char mine[ROW][COL])
{
int i,j;
int count=10;
while(count)
{
i=rand()%8+1;
j=rand()%8+1;
if(mine[i][j]=='0')
{
mine[i][j]='1';
count--;
}
}
}
void show_board(char mine[ROW][COL])
{
int i, j;
for(i=0; i<ROW-1; i++)
{
printf("%d ",i);
}
for(i=1; i<ROW-1; i++)
{
printf("\n");
printf("%d ",i);
for(j=1; j<COL-1; j++)
{
printf("%c ",mine[i][j]);
}
}
printf("\n");
}
void my_step(char mine[ROW][COL], char show[ROW][COL])
{
int i = 0;
int j = 0;
int count=71;
int input=0;
while (count)
{
do
{
menu_board();
printf("请选择\n");
scanf("%d",&input);
switch(input)
{
case 1:
printf("请输入坐标\n");
scanf("%d%d", &i,&j);
if(count==71)
{
safe_board(mine,i,j);
}
if (mine[i][j] == '1')
{
printf("踩到雷了:\n");
show_board(mine);
return;
}
else
{
open_board(mine,show,i,j);
display_board(show);
count--;
}
break;
case 0:
printf("输入坐标\n");
scanf("%d%d", &i,&j);
if(show[i][j]=='*')
{
show[i][j]='!';
}
else
show[i][j]='*';
display_board(show);
break;
default:
printf("请重新选择\n");
break;
}
break;
}while(input);
}
printf("扫雷成功\n");
}
int get_mine(char mine[ROW][COL] ,int i,int j)
{
int count=0;
if(mine[i-1][j]=='1')
{
count++;
}
if(mine[i-1][j-1]=='1')
{
count++;
}
if(mine[i-1][j+1]=='1')
{
count++;
}
if(mine[i][j+1]=='1')
{
count++;
}
if(mine[i][j-1]=='1')
{
count++;
}
if(mine[i+1][j-1]=='1')
{
count++;
}
if(mine[i+1][j]=='1')
{
count++;
}
if(mine[i+1][j+1]=='1')
{
count++;
}
return count;
}
void open_board(char mine[ROW][COL],char show[ROW][COL],int i,int j)
{
if(mine[i][j]=='0'&&i>=0&&j>=0&&show[i][j]=='*')
{
show[i][j]=get_mine(mine,i,j)+'0';
}
if(mine[i][j-1]=='0'&&i>=0&&j-1>=0&&show[i][j-1]=='*')
{
show[i][j-1]=get_mine(mine,i,j-1)+'0';
if(get_mine(mine,i,j-1)==0)
{
open_board(mine,show,i,j-1);
}
}
if(mine[i][j+1]=='0'&&i>=0&&j+1>=0&&show[i][j+1]=='*')
{
show[i][j+1]=get_mine(mine,i,j+1)+'0';
if(get_mine(mine,i,j+1)==0)
{
open_board(mine,show,i,j+1);
}
}
if(mine[i-1][j]=='0'&&i-1>=0&&j>=0&&show[i-1][j]=='*')
{
show[i-1][j]=get_mine(mine,i-1,j)+'0';
if(get_mine(mine,i-1,j)==0)
{
open_board(mine,show,i-1,j);
}
}
if(mine[i-1][j-1]=='0'&&i-1>=0&&j-1>=0&&show[i-1][j-1]=='*')
{
show[i-1][j-1]=get_mine(mine,i-1,j-1)+'0';
if(get_mine(mine,i-1,j-1)==0)
{
open_board(mine,show,i-1,j-1);
}
}
if(mine[i-1][j+1]=='0'&&i-1>=0&&j+1>=0&&show[i-1][j+1]=='*')
{
show[i-1][j+1]=get_mine(mine,i-1,j+1)+'0';
if(get_mine(mine,i-1,j+1)==0)
{
open_board(mine,show,i-1,j+1);
}
}
if(mine[i+1][j+1]=='0'&&i+1>=0&&j+1>=0&&show[i+1][j+1]=='*')
{
show[i+1][j+1]=get_mine(mine,i+1,j+1)+'0';
if(get_mine(mine,i+1,j+1)==0)
{
open_board(mine,show,i+1,j+1);
}
}
if(mine[i+1][j]=='0'&&i+1>=0&&j>=0&&show[i+1][j]=='*')
{
show[i+1][j]=get_mine(mine,i+1,j)+'0';
if(get_mine(mine,i+1,j)==0)
{
open_board(mine,show,i+1,j);
}
}
if(mine[i+1][j-1]=='0'&&i+1>=0&&j-1>=0&&show[i+1][j-1]=='*')
{
show[i+1][j-1]=get_mine(mine,i+1,j-1)+'0';
if(get_mine(mine,i+1,j-1)==0)
{
open_board(mine,show,i+1,j-1);
}
}
}
void safe_board(char mine[ROW][COL],int i,int j)
{
int count=1;
while(count)
{
if(mine[i][j]='1')
{
int m=rand()%8+1;
int n=rand()%8+1;
mine[i][j]='0';
mine[m][n]='1';
count--;
}
else
count--;
}
}
void menu_board()
{
printf("**** 1.扫雷 0.标雷与撤销 ***\n");
}
最后,记得引用头文件 #include"game.h"。