[c语言]扫雷小游戏全网最详讲解!

简介

  本文将对扫雷游戏进行全面的分析,从每一个模块,每一个功能一 一详述,从而让扫雷游戏变得简单易懂,更容易上手!!!

正文

  本游戏即将实现扫雷的基本功能:

    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 !!!


猜你喜欢

转载自blog.csdn.net/Code_beeps/article/details/83242477
今日推荐