保姆级别的扫雷游戏

简介: 保姆级别的扫雷游戏

一.扫雷游戏的分析和设计

1.扫雷游戏的功能说明

  • 使用控制台实现经典的扫雷游戏
  • 游戏可以通过菜单实现继续玩或者退出游戏
  • 扫雷的棋盘是9*9的格子
  • 默认随机布置10个雷
  • 可以排查雷
  1. 如果位置不是雷,就显示周围有几个雷
  2. 如果位置是雷,就炸死,游戏结束
  3. 把除10个雷之外的所有非雷都找出来,排雷成功,游戏结束

2.数据结构的分析

       扫雷过程中,布置的雷和排查出的雷的信息都需要储存,所以我们需要一定的数据结构来存储这些信息。

2.1.文件结构的设计

text.c//写游戏的测试逻辑
game.c//写游戏中函数的实现等
game.h//写游戏需要的数据结构类型和函数声明等

2.2 为什么需要创建11*11的数组?

      完成一个9*9的扫雷游戏,首先想到的是创建一个9*9的数组。但这里,还需要创建11*11的数组。理由如下:排查雷时,若排查的位置不是雷,就显示周围一圈(8个位置)有几个雷。而当该位置位于坐标的边界时,则会出现越界的情况。为了防止越界,在设计时,给数组扩大一圈,雷还是布置在9*9的坐标上,周围一圈不去布置雷,这样就解决了越界问题。

2.3 为什么要两个棋盘来存放信息?

       在棋盘上布置雷,符号‘1’为雷,符合‘0’为非雷。当我们排查雷时,若某位置不是雷,会返回该位置周围有几个雷,若有一个雷,则返回数字1。此时,若返回的数字1与符号‘1’(雷)在同一个棋盘上,玩家则无法区分它是数字1,还是符号‘1’。因此,需要两个棋盘来存放信息。

因此,需要在game.h文件中,定义行,列和10个雷,来让这3个文件都使用。只有其他两个文件都写上#include"game.h",在game.h文件中声明函数和数据结构类型都通用。

#include<stdio.h>其余两个文件就不用加这个头文件了
#define ROW 9//行
#define COL 9//列
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10//10个雷

二.扫雷游戏的代码实现

      设计一个扫雷游戏需要初始化棋盘,打印棋盘,布置雷,排查雷。

      首先,建立text.c文件,通过菜单实现继续玩或者退出游戏,使用do while循环实现先选择后判断

(如果不太了解函数传参,可以看博主的这篇文章https://blog.csdn.net/wait___wait/article/details/135047014?spm=1001.2014.3001.5502ss

(switch语句有些遗忘的,也可以看看博主这篇文章https://blog.csdn.net/wait___wait/article/details/134805824?spm=1001.2014.3001.5501

main函数

#include"game.h"//自己定义的头文件用“”
void menu()
{
  printf("*******************\n");
  printf("******1.paly*******\n");
  printf("******0.exit*******\n");
  printf("*******************\n");
}
int main()
{
  int input = 0;
  do
  {
    menu();
    printf("请选择:>\n");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
    {
      game();
      break;
    }
    case 0:
    {
      printf("退出游戏\n");
      break;
    }
    default:
    {
      printf("输入不符合规定,请重新选择\n");
      break;
    }
    }
 
  }while(input);
  return 0;
}

上述代码中涉及game()函数,因而在text.c(游戏的测试逻辑)这个文件中要进行定义。

根据对该游戏的分析和设计,知,game()中要创建两个数组来存放布置好的雷,和排查出雷的信息。

char mine[ROWS][COLS];//存放布置好的雷
char show[ROWS][COLS];//存放排查出雷的信息

初始化棋盘

text.c

数组是11*11,所需棋盘是9*9

//初始化棋盘 ,11*11数组
InitBoard(show, ROWS, COLS, '*');//show数组最开始全是‘*’
InitBoard(mine, ROWS, COLS, '0');//mine数组最开始全是‘0’

game.c

//初始化棋盘
void InitBoard(char board[ROWS][COLS],  int rows, int cols, char set)
{
  for (int i = 0; i < rows; i++)
  {
    for (int j = 1; j < cols; j++)//j若初始化为0,后面排查雷时,输入的坐标在棋盘上输出的坐标会不一样
 
    {
      board[i][j] = set;
    }
  }
}

game.h

void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);//初始化棋盘

运行示例图

打印棋盘

text.c

show数组是11*11,但传过去的参数ROW,COL定义为9,因此打印的棋盘是9*9

//打印棋盘
PlayBoard(show, ROW, COL);
//PlayBoard(mine, ROW, COL);//检查‘0’是否设置好

game.h

void PlayBoard(char board[ROWS][COLS], int row, int col);//打印棋盘

game.c

//打印棋盘
void PlayBoard(char board[ROWS][COLS], int row, int  col)
{
  printf("------扫雷游戏-------\n");
  for (int i = 0; i <= col; i++)//打印列号0-9
  {
    printf("%d|", i);
  }
  printf("\n");
  for (int i = 1; i <= row; i++)
  {
    printf("%d|", i);//打印行号1-9
    
      for (int j = 1; j <= col; j++)
      {
        printf("%c ", board[i][j]);
      }
      printf("\n");
  }
}

运行示例图


布置雷

text.c

由于布置雷时需要产生随机数,就要用到rand函数。又因为调用rand函数之前要先调用srand函数,所以在该文件的main函数中使用srand函数,

srand((unsigned int)time(NULL));
//布置雷
SetMine(mine, ROW, COL);
//PlayBoard(mine, ROW, COL);//检查是否成功设置雷

game.h

#include<stdlib.h>//srand,rand的头文件
#include<time.h>//time的头文件
void SetMine(char board[ROWS][COLS], int row, int col);//布置雷

game.c

//布置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
  int x = 0, y = 0;
  int count = EASY_COUNT;
  while (count)
  {
    x=rand() % row + 1;//生成1-9的随机数
    y=rand() % row + 1;//生成1-9的随机数
    if (board[x][y] == '0')//判断该坐标是否是雷,防止重设置,导致雷的个数少于10
    {
      board[x][y] = '1';
      count--;//每设置一个雷,count-1。直到10个雷设置完成,count=0,跳出while循环
    }
  }
}

运行实例,显示雷的位置需要把text.c文件中的PlayBoard取消注销

排雷

text.c

FindMine(show, mine, ROW, COL);

game.h

void FindMine(char show[ROWS][COLS], char[ROWS][COLS], int row, int col);//排雷

game.c

//排雷
//如果位置不是雷,就显示周围有几个雷
//如果位置是雷,就炸死,游戏结束
//把十个雷之外的所有雷都找出来,排雷成功,游戏结束
void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
  int x = 0, y = 0; 
  int win = 0;//排查次数
  while (win < row*col-EASY_COUNT)//确保win=71之后,不再继续,因为之后排查的一定是炸弹
  {    
        printf("请输入你要排查的坐标\n");
    scanf("%d %d", &x, &y);
    if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
    {
      if (mine[x][y] == '1')
      {
        printf("很遗憾,你被炸死了\n");
        PlayBoard(mine, row, col);
        break;
      }
      else
      {
        int count = GetMineCount(mine, x, y);//统计雷坐标周围雷的个数
        show[x][y] = count + '0';
        PlayBoard(show, row, col);
                win ++;
      }
    }
    else
    {
      printf("坐标非法,请重新输入\n");
    }
    
  }
  if (win == col * row - EASY_COUNT)
  {
    printf("恭喜你,排雷成功\n");
    PlayBoard(mine, row, col);
  }
  printf("是否继续游戏?\n");
}

计算周围雷的个数

//计算周围雷的个数
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
  int i = 0;
  int j = 0;
  int count = 0;
  for (i = x - 1; i <= x + 1; i++)
  {
    for (j = y - 1; j <= y + 1; j++)
    {
      count += (mine[x][y] - '0');//mine[x][y]-'0'得到的是整数
    }
  }
  return count;
}

mine数组里面的元素是字符型,‘0’也是字符,两个字符相减,得到的是整型数字。

完整代码

text.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
  printf("*******************\n");
  printf("******1.paly*******\n");
  printf("******0.exit*******\n");
  printf("*******************\n");
}
void game()
{
  char show[ROWS][COLS] = { 0 };//全部设为‘*’
  char mine[ROWS][COLS] = { 0 };//全部设为‘0’
  //初始化棋盘
  InitBoard(show, ROWS, COLS, '*');
  InitBoard(mine, ROWS, COLS, '0');
  //打印棋盘
  PlayBoard(show, ROW, COL);
  //PlayBoard(mine, ROW, COL);
  //布置雷‘1'为雷,‘0’非雷
  SetMine(mine, ROW, COL);
  //PlayBoard(mine, ROW, COL);//检查是否成功设置雷
  //排雷
  FindMine(show, mine, ROW, COL);
}
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);
  return 0;
}

game.h

#include<stdio.h>
#include<stdlib.h>//srand,rand的头文件
#include<time.h>//time的头文件
 
 
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10//10个雷
 
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);//初始化棋盘
void PlayBoard(char board[ROWS][COLS], int row, int col);//打印棋盘
void SetMine(char board[ROWS][COLS], int row, int col);//布置雷
void FindMine(char show[ROWS][COLS], char[ROWS][COLS], int row, int col);//排雷

game.c

#include"game.h"
 
//初始化棋盘
void InitBoard(char board[ROWS][COLS],  int rows, int cols, char set)
{
  for (int i = 0; i < rows; i++)
  {
    for (int j = 0; j < cols; j++)
    {
      board[i][j] = set;
    }
  }
}
//打印棋盘
void PlayBoard(char board[ROWS][COLS], int row, int  col)
{
  printf("------扫雷游戏-------\n");
  for (int i = 0; i <= col; i++)//打印列号0-10
  {
    printf("%d|", i);
  }
  printf("\n");
  for (int i = 1; i <= row; i++)
  {
    printf("%d|", i);//打印行号1-9
    
      for (int j = 1; j <= col; j++)//j若初始化为0,棋盘就是9*10,而非9*9
      {
        printf("%c ", board[i][j]);
      }
      printf("\n");
  }
}
//布置雷
void SetMine(char board[ROWS][COLS], int row, int col)
{
  int x = 0, y = 0;
  int count = EASY_COUNT;
  while (count)
  {
    x=rand() % row + 1;//生成1-9的随机数
    y=rand() % row + 1;//生成1-9的随机数
    if (board[x][y] == '0')//判断该坐标是否是雷,防止重设置,导致雷的个数少于10
    {
      board[x][y] = '1';
      count--;//每设置一个雷,count-1。直到10个雷设置完成,count=0,跳出while循环
    }
  }
}
//排雷
//如果位置不是雷,就显示周围有几个雷
//如果位置是雷,就炸死,游戏结束
//把十个雷之外的所有雷都找出来,排雷成功,游戏结束
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
  int i = 0;
  int j = 0;
  int count = 0;
  for (i = x - 1; i <= x + 1; i++)
  {
    for (j = y - 1; j <= y + 1; j++)
    {
      count += (mine[x][y] - '0');
    }
  }
  return count;
}
void FindMine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
{
  int x = 0, y = 0; 
  int win = 0;
  while (win < row*col-EASY_COUNT)
  {
    printf("请输入你要排查的坐标\n");
    scanf("%d %d", &x, &y);
    if (x >= 1 && x <= 9 && y >= 1 && y <= 9)
    {
      if (mine[x][y] == '1')
      {
        printf("很遗憾,你被炸死了\n");
        PlayBoard(mine, row, col);
        break;
      }
      else
      {
        int count = GetMineCount(mine, x, y);//统计雷的坐标
        show[x][y] = count + '0';
        PlayBoard(show, row, col);
        win++;
      }
    }
    else
    {
      printf("坐标非法,请重新输入\n");
    }
    
  }
  if (win == col * row - EASY_COUNT)
  {
    printf("恭喜你,排雷成功\n");
    PlayBoard(mine, row, col);
  }
  printf("是否继续游戏?\n");
}

运行示例

小tip

1

这个是因为打印棋盘时,game.c文件中,第三个for循环里,j初始化为0,导致输入(4,5)坐标,得到(4,6)坐标,所以,要注意自己变量赋值。(并且这个是9*10棋盘,不知道当时怎么没看出来,哈哈)

2

show,mine数组是11*11,但传过去的参数ROW,COL定义为9,因此打印的棋盘是9*9

3

建议写完一个模块,运行一个,好纠错。

欢迎斧正!!!

(听说公主,王子们都有一个好习惯,那就是,


目录
相关文章
|
7月前
|
存储
扫雷游戏讲解(第一版本)
扫雷游戏讲解(第一版本)
60 0
|
7月前
扫雷游戏(优化版)
扫雷游戏(优化版)
62 0
扫雷游戏(优化版)
|
7月前
俄罗斯方块游戏开发实战教程(3):玩家如何控制形状
俄罗斯方块游戏开发实战教程(3):玩家如何控制形状
84 1
|
7月前
|
存储
扫雷游戏的实现以及具体分析(保姆级教学)
扫雷游戏的分析和设计、扫雷游戏的文字描述、开始前的准备---多文件的创建、开始实操、扫雷游戏的扩展
|
7月前
俄罗斯方块游戏开发实战教程(7):消除判断和处理
俄罗斯方块游戏开发实战教程(7):消除判断和处理
88 0
|
7月前
|
算法
泡泡龙游戏开发实战教程(6):实现连锁消除
泡泡龙游戏开发实战教程(6):实现连锁消除
94 0
|
机器学习/深度学习
【扫雷】初级版保姆级教学
【扫雷】初级版保姆级教学
|
存储 C语言
智能三子棋——保姆级教学。
智能三子棋——保姆级教学。
智能三子棋——保姆级教学。
|
小程序 C语言
超详细三子棋(保姆级教学)
三子棋是一款古老的民间传统游戏,又被称为黑白棋、圈圈叉叉棋、井字棋、一条龙、九宫棋等,想必大家都有玩过吧。没玩过的话也可以试着玩一玩,这样对写三子棋这个小游戏的化是会有一个很好的思路的。那么本篇博客就来介绍如何用C语言来实现三子棋小游戏的具体步骤。(编译器用的是VS2019)
150 0