简介
本文将对扫雷游戏进行全面的分析,从每一个模块,每一个功能一 一详述,从而让扫雷游戏变得简单易懂,更容易上手!!!
正文
本游戏即将实现扫雷的基本功能:
1、基本排雷(每一次排查都将显示出周围八个坐标雷数总和)
2、实现第一次排雷安全(即第一次碰到雷也不会炸死,给运气不好的用户带来游戏体验)
3、实现坐标周围没有雷时展开,直到展开到周围有雷。
那我们对扫雷这个游戏开始分析一波:
首先,扫雷需要一个棋盘来放置雷以及供用户在上面操作,那么我们就需要初始化一个棋盘,那么一个棋盘够吗?显然是不够的,一个棋盘供用户使用的话,那么程序猿在哪操作呢?所以我们定义两个数组,一个用显示出来给用户使用,另一个给程序猿布置雷使用。
那么接下来就应该是初始化了,如果我们要一个9X9的棋盘,那我们是不是给数组的行和列赋值9就可以了?显然是不可行的,为什么呢?请看下面讲解:
如果我们只使用9X9的数组初始化棋盘,那么当我们排查边界上有没有雷的时候,循环遍历周围八个就会产生数组越界,所以我们要多设置两行两列来防止数组越界。当然打印棋盘的时候我们只需要打印出来中间的9X9就可以了。
如下图所示:
我们先在一开始在我们的game()函数里定义两个数组:设计者棋盘放雷,玩家棋盘放排查之后的信息
char play_board[ROWS][COLS] = {0};//玩家棋盘
char real_board[ROWS][COLS] = {0};//设计者棋盘(不显示)
然后开始我们的初始化函数,给玩家棋盘初始化全*号,给设计者棋盘初始化为全字符0。
void Init_board(char board[ROWS][COLS],int rows,int cols,char set)//初始化棋盘
{
int i = 0;
int j = 0;
for(i=0; i<rows; i++)
{
for(j=0; j<cols; j++)
{
board[i][j] = set;//使用set是为了后面方便初始化两个棋盘,不用写两个初始化函数
}
}
}
然后就要打印棋盘了:可以在棋盘周围加上行和列,以便用户使用时确认坐标。
void Display_board(char board[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 ",board[i][j]);
}
printf("\n");
}
}
棋盘处理好之后,接下来就是要给棋盘中布置雷了。注意布置雷要用字符1,非雷用字符0,因为用字符1和0来表示在后面游戏跑起来打印的时候可以使用同一个函数就行。如果用数字来表示雷或者非雷,那么打印的时候*号和数字0,1就要区分打印,就得多写一个函数。
void Set_mine(char real_board[ROWS][COLS],int row,int col)//布置雷
{
int x = 0;
int y = 0;
int count = minenum;
while(count)
{
x = rand()%row+1;//产生随机坐标布置雷
y = rand()%col+1;
if(real_board[x][y] == '0')
{
real_board[x][y] = '1';
count--;
}
}
}
接下来我们在排查雷的过程中会统计当前坐标周围的雷数目,所以我们写一个函数来表示:
int Count_mine(char real_board[ROWS][COLS],int x,int y)//统计雷数
{
return real_board[x-1][y-1] + real_board[x-1][y] + real_board[x-1][y+1]
+ real_board[x][y-1] + real_board[x][y+1]
+ real_board[x+1][y-1] + real_board[x+1][y] + real_board[x+1][y+1]
-8*'0';
}
第一次如果踩到雷还是安全的函数(如果当前坐标是雷,就随机产生一个不是雷的坐标将当前坐标替换):
void Fsafe(char real_board[ROWS][COLS],int x,int y)//第一次排雷绝对安全
{
int i = 0;
int j = 0;
if(real_board[x][y] == '1')
{
i = rand()%9+1;
j = rand()%9+1;
if(real_board[i][j] == '0')
{
real_board[i][j] = '1';
real_board[x][y] = '0';
}
}
}
然后是展开函数(如果点开这处坐标周围没雷,那么就排查周围的坐标有没有雷,没雷展开,直到有雷为止),在这使用递归能简单一些:
void Unfold(char play_board[ROWS][COLS],char real_board[ROWS][COLS],int row,int
col,int x,int y)//没雷展开
{
int i = 0;
int j = 0;
if(Count_mine(real_board,x,y) == 0)
{
play_board[x][y] = '0';
for(i=-1; i<=1; i++)
{
for(j=-1; j<=1; j++)
{
if(x+i>=1 && x+i<=row && y+j>=1 && y+j<=col
&& (i != 0||j !=0))
{
if(play_board[x+i][y+j] != '0')
{
Unfold(play_board,real_board,row,col,x+i,y+j);
}
}
}
}
}
else
{
play_board[x][y] = Count_mine(real_board,x,y) + '0';
}
}
判断雷是否排查完
int Iswin(char board[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(board[i][j] == '*')//计算剩余雷的个数
{
count++;
}
}
}
return count;
}
最终是我们的排查函数,也就是最重要的一个部分了
void Find_mine(char play_board[ROWS][COLS],char real_board[ROWS][COLS],int
row,int col)//排雷
{
int x = 0;
int y = 0;
int ret = 0;
int count = 0;//排查次数
while(Iswin(play_board,row,col) != minenum)
{
printf("请输入你要排查的坐标:\n");
scanf("%d%d",&x,&y);
if(x<1 || x>row || y<1 || y>col)
{
printf("输入坐标超出范围,请重新输入:\n");
}
else
{
if(real_board[x][y] == '1')
{
if(count == 0 )
{
Fsafe(real_board,x,y);
Unfold(play_board,real_board,row,col,x,y);
Display_board(play_board,row,col);
count++;
}
else
{
printf("你被炸死了,游戏结束\n");
Display_board(real_board,row,col);
count++;
printf("一共排查了%d次\n",count);
break;
}
}
else
{
Unfold(play_board,real_board,row,col,x,y);
Display_board(play_board,row,col);
count++;
}
}
}
if(Iswin(play_board,row,col) == minenum)
{
printf("恭喜你,游戏胜利!!!\n");
printf("一共排查了%d次\n",count);
Display_board(real_board,row,col);
}
}
最后我们用一个game()函数来将上面的代码整合起来
void game()
{
char play_board[ROWS][COLS] = {0};//玩家棋盘
char real_board[ROWS][COLS] = {0};//设计者棋盘(不显示)
Init_board(play_board,ROWS,COLS,'*');
Init_board(real_board,ROWS,COLS,'0');
Display_board(play_board,ROW,COL);
Set_mine(real_board,ROW,COL);
//Display_board(real_board,ROW,COL);
Find_mine(play_board,real_board,ROW,COL);
}
将以上函数放在我们的game.c源文件中。
再创建一个test.c源文件来存放主函数以及我们的菜单函数:
下面是test.c中的代码:
#include "game.h"
void menu()
{
printf("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
printf("$$$$$$$$$$$$$$$$ 扫雷游戏 $$$$$$$$$$$$$$$$\n");
printf("$$$$$$$$$$$$$$$$ 1. 开始 $$$$$$$$$$$$$$$$\n");
printf("$$$$$$$$$$$$$$$$ 0. 退出 $$$$$$$$$$$$$$$$\n");
printf("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$\n");
}
int main()
{
int input = 0;
srand((unsigned int)time(NULL));
do
{
menu();
printf("请输入你的选择:\n");
scanf("%d",&input);
switch(input)
{
case 1:
game();
break;
case 0:
printf("游戏已退出\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
}while(input);
}
我们还需要创建一个头文件game.h来存放我们所有函数的声明以及各种定义。
以下是game.h里面的代码
#ifndef __GAME_H__
#define __GAME_H__
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//实际的行列(打印出来的棋盘)
#define ROW 9
#define COL 9
//设计时的行列(比实际的多两行以防越界,多出来的两行、列不打印)
#define ROWS ROW+2
#define COLS COL+2
//设置的雷数目
#define minenum 10
void game();
void Init_board(char board[ROWS][COLS],int rows,int cols,char set);//初始化两个棋盘
void Display_board(char board[ROWS][COLS],int row,int col);//展示棋盘
void Set_mine(char real_board[ROWS][COLS],int row,int col);//布置雷
int Count_mine(char real_board[ROWS][COLS],int x,int y);//统计当前坐标周围的雷数
void Find_mine(char play_board[ROWS][COLS],char real_board[ROWS][COLS],int row,int col);//排雷
void Fsafe(char board[ROWS][COLS],int x,int y);//第一次排雷绝对安全
void Unfold(char play_board[ROWS][COLS],char real_board[ROWS][COLS],int row,int col,int x,int y);//没雷展开
int Iswin(char board[ROWS][COLS],int row,int col);//判断是否排完雷赢了
#endif
这样整个游戏就写完了,接下来运行出来看看结果
那么到这里整个扫雷游戏就跑起来了,整个游戏最重要的就是要把整个游戏运行的逻辑搞清楚,然后一些细节问题注意到,比如考虑排查边界坐标时会不会越界、布置雷的时候用的时数字还是字符,考虑并解决到这些问题后,那么写起来就会容易许多
下面是game.c中的完整代码:
#include "game.h"
void Init_board(char board[ROWS][COLS],int rows,int cols,char set)//初始化棋盘
{
int i = 0;
int j = 0;
for(i=0; i<rows; i++)
{
for(j=0; j<cols; j++)
{
board[i][j] = set;
}
}
}
void Display_board(char board[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 ",board[i][j]);
}
printf("\n");
}
}
void Set_mine(char real_board[ROWS][COLS],int row,int col)//布置雷
{
int x = 0;
int y = 0;
int count = minenum;
while(count)
{
x = rand()%row+1;
y = rand()%col+1;
if(real_board[x][y] == '0')
{
real_board[x][y] = '1';
count--;
}
}
}
int Count_mine(char real_board[ROWS][COLS],int x,int y)//统计雷数
{
return real_board[x-1][y-1] + real_board[x-1][y] + real_board[x-1][y+1]
+ real_board[x][y-1] + real_board[x][y+1]
+ real_board[x+1][y-1] + real_board[x+1][y] + real_board[x+1][y+1]
-8*'0';
}
void Fsafe(char real_board[ROWS][COLS],int x,int y)//第一次排雷绝对安全
{
int i = 0;
int j = 0;
if(real_board[x][y] == '1')
{
i = rand()%9+1;
j = rand()%9+1;
if(real_board[i][j] == '0')
{
real_board[i][j] = '1';
real_board[x][y] = '0';
}
}
}
int Iswin(char board[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(board[i][j] == '*')//计算剩余雷的个数
{
count++;
}
}
}
return count;
}
void Unfold(char play_board[ROWS][COLS],char real_board[ROWS][COLS],int row,int col,int x,int y)//没雷展开
{
int i = 0;
int j = 0;
if(Count_mine(real_board,x,y) == 0)
{
play_board[x][y] = '0';
for(i=-1; i<=1; i++)
{
for(j=-1; j<=1; j++)
{
if(x+i>=1 && x+i<=row && y+j>=1 && y+j<=col && (i != 0||j !=0))
{
if(play_board[x+i][y+j] != '0')
{
Unfold(play_board,real_board,row,col,x+i,y+j);
}
}
}
}
}
else
{
play_board[x][y] = Count_mine(real_board,x,y) + '0';
}
}
void Find_mine(char play_board[ROWS][COLS],char real_board[ROWS][COLS],int row,int col)//排雷
{
int x = 0;
int y = 0;
int ret = 0;
int count = 0;//排查次数
while(Iswin(play_board,row,col) != minenum)
{
printf("请输入你要排查的坐标:\n");
scanf("%d%d",&x,&y);
if(x<1 || x>row || y<1 || y>col)
{
printf("输入坐标超出范围,请重新输入:\n");
}
else
{
if(real_board[x][y] == '1')
{
if(count == 0 )
{
Fsafe(real_board,x,y);
Unfold(play_board,real_board,row,col,x,y);
Display_board(play_board,row,col);
count++;
}
else
{
printf("你被炸死了,游戏结束\n");
Display_board(real_board,row,col);
count++;
printf("一共排查了%d次\n",count);
break;
}
}
else
{
Unfold(play_board,real_board,row,col,x,y);
Display_board(play_board,row,col);
count++;
}
}
}
if(Iswin(play_board,row,col) == minenum)
{
printf("恭喜你,游戏胜利!!!\n");
printf("一共排查了%d次\n",count);
Display_board(real_board,row,col);
}
}
void game()
{
char play_board[ROWS][COLS] = {0};//玩家棋盘
char real_board[ROWS][COLS] = {0};//设计者棋盘(不显示)
Init_board(play_board,ROWS,COLS,'*');
Init_board(real_board,ROWS,COLS,'0');
Display_board(play_board,ROW,COL);
Set_mine(real_board,ROW,COL);
//Display_board(real_board,ROW,COL);
Find_mine(play_board,real_board,ROW,COL);
}
PS:在我们的源文件中一定要记得调用我们的头文件game.h !!!