【C语言】 扫雷游戏(保姆级的实现过程)

简介: 扫雷游戏,想必大家都有玩过吧。没完过的话也可以试着玩一玩,这样对写扫雷游戏这个小游戏的化是会有一个很好的思路的。那么本片博客就来介绍如何实现扫雷游戏的具体步骤。扫雷游戏链接👉 扫雷游戏网页版 - Min

Ⅰ 前言

扫雷游戏,想必大家都有玩过吧。没完过的话也可以试着玩一玩,这样对写扫雷游戏这个小游戏的化是会有一个很好的思路的。那么本片博客就来介绍如何实现扫雷游戏的具体步骤。

扫雷游戏链接👉 扫雷游戏网页版 - Minesweeper

Ⅱ 模块化编程  

再说实现三子棋逻辑思路前,我们来说说什么是 模块化编程 吧🤔

传统方式编程:所有的函数均放在main.c里,若使用的模块比较多,则一个文件内会有很多的代码,不利于代码的组织和管理,而且很影响编程者的思路。

模块化编程:把各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数的声明,其它.c文件想使用其中的代码时,只需要#include "XXX.h"文件即可。使用模块化编程可极大的提高代码的可阅读性、可维护性、可移植性等。

传统方式编程:所有的函数均放在main.c里,若使用的模块比较多,则一个文件内会有很多的代码,不利于代码的组织和管理,而且很影响编程者的思路。

模块化编程:把各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数的声明,其它.c文件想使用其中的代码时,只需要#include "XXX.h"文件即可。使用模块化编程可极大的提高代码的可阅读性、可维护性、可移植性等!

总的来说就是:当你代码比较多的时候,就可以采用模块化编程来完成这个程序😜

Ⅲ 游戏思路与逻辑

创建菜单界面函数选择退出游戏或者是进入游戏。

存放布置好雷的新星以及存放排查出雷的信息。

首先,进行雷的初始化棋盘。

然后,再打印出雷的初始化棋盘。注意:一定是要先进行 初始化 然后再 打印棋盘。

接着,就可以布置雷的信息了。

最后,输入排查雷的坐标。

检查出的坐标是不是雷,布置雷存放的是字符(1) 没有放置的是字符(0)

输入坐标的时候一共有④种情况:《很遗憾,你被炸死了!》、《宁已经在这里输入过坐标了,请重新输入!》、《宁输入的坐标范围错误!重新输入》、《恭喜你,排雷成功!太优秀了!》

然后,再回到步骤①,是否选择 进入游戏 以及 退出游戏。

Ⅳ 实现游戏步骤/过程

① 创建颜色函数

🖊创建颜色函数color()

前景色颜色的对应值↓

0=黑色                8=灰色  
1=蓝色                9=淡蓝色        十六进制                                  
2=绿色                10=淡绿色        A          
3=湖蓝色              11=淡浅绿色      B 
4=红色                12=淡红色        C  
5=紫色                13=淡紫色        D          
6=黄色                14=淡黄色        E         
7=白色                15=亮白色        F

color()创建颜色函数如下↓

void color(short x) 
{
  if (x >= 0 && x <= 15)
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x);   
  else
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
}

使用颜色函数的好处实际上无非就是让程序运行看上去更加美观、鲜明,实际上没什么实际作用,这点是我们要知道的。

这里的STD_OUTPUT_HANDLE需要引头文件#include<Windows.h>,才可以进行使用。

② 菜单界面(menu)

菜单界面函数实际上就像是我们的一个界面,就好比是游戏的界面目录,餐馆当中的菜单。一样的道理。这个是库函数就有的我们只需要直接引用下即可。示例代码如下↓

void menu()
{
  color(0);       //Black 黑色
  system("cls");  //清屏.
  color(10);
  printf("|-----------|扫雷游戏|-----------|\n");
  printf("|********************************|\n");
  printf("|★★★★★★★★★★★★★★★★|\n");
  printf("|★★   1.开始  0.退出       ★★|\n");
  printf("|★★★★★★★★★★★★★★★★|\n");
  printf("|0 = 不是雷 ---------- 1 = 它是雷|\n");
  printf("|--------------------------------|\n");
}

注→在这里用到了一个system("cls"); 达到了一个清屏的效果,只有加了这个,你才可以让cmd中的界面全部为黑色。因为我们还在这个清屏指令上+color(0); 这个代表的是,黑色。


③ 实现多行多列扫雷

1. #define ROW 9
2. #define COL 9

使用 #define 宏定义在这里的好处:

方便程序的修改,不用对整个程序进行修改,只需对宏定义上进行修改。

提高程序的运行效率,更加方便模块化。

在三子棋基础上,只需改变宏定义的值,就可以实现多子棋的效果。

在程序当中,是9行9列的,如果想修改成10行10列的只需要把#define改成10即可!

10行10列的扫雷棋盘如下 👇

image.png

但是十行十列就是会出现这个的情况,当然这个问题也是非常的好解决的。大家可以看下怎么去解决这个问题。☆⌒(*^-゜)v THX!!

④ 实现多个雷

#define Thunder 10

这里 #define 在上面提到过,就不提了。

修改雷的个数也只需要把这上面的数字(10)修改变的数字修改,当然修改请理性修改,你不可能 9*9 的棋盘,给很多雷吧,那还怎么玩哈哈哈😤。

比如修改成 40 个雷,看看效果如下所示 👇image.png

⑤ 棋盘初始化

打印棋盘,本质上是打印数组的内容。如下所示👇

void Initialization(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;
    }
  }
}

char set 是实参传递到形参的字符。

实参数组名 可以进行省略,但是 不能进行省略。


⑥ 棋盘的打印

打印棋盘,本质上是打印数组的内容,这里数组的内容是字符'0'。如下所示👇

void Print_board(char board[ROWS][COLS], int row, int col)
{
  int i = 0; 
  int j = 0;
  color(7);
  printf("════════════════════\n");
  for (i = 0; i <= row; i++)
  {
    if (i == 0)
    {
      printf("%d|", i);
    }
    else
    {
      printf("%d|", i);
    }
  }
  printf("\n/|══════════════════");
  printf("\n");
  for (i = 1; i <= row; i++)
  {
    printf("%d|", i);
    for (j = 1; j <= col; j++)
    {
      printf("%c|", board[i][j]);
    }
    printf("\n");
  }
  color(6);
  printf("\n-----扫雷游戏------\n");
}

打印棋盘的效果图,如下所示👇  

image.png

⑦ 布置雷的信息

打印完棋盘之后,就开始布置雷。

void Lay_thunder(char Findout[ROWS][COLS], int row, int col)
{
  //布置雷
  int count = Thunder;
  while (count)
  {
    int x = rand() % row + 1;
    int y = rand() % col + 1;
    if (Findout[x][y] = '0')
    {
      Findout[x][y] = '1';
      count--;
    }
  }
}

这里还用到了一个知识点【随机数】

在实际开发中,我们往往需要一定范围内的随机数,过大或者过小都不符合要求,那么,如何产生一定范围的随机数呢?我们可以利用取模的方法:

int a = rand() % 10;    //产生0~9的随机数,注意10会被整除

如果要规定上下限:

int a = rand() % 51 + 13;    //产生13~63的随机数

分析:取模即取余,rand()%51+13 我们可以看成两部分:rand()%51 是产生 0~50 的随机数,后面+13保证 a 最小只能是 13,最大就是 50+13=63

使用 <time.h> 头文件中的 time() 函数即可得到当前的时间(精确到秒),就像下面这样:

srand((unsigned)time(NULL));

🍁注意:这个在程序当中是只执行一次即可!

⑧ 玩家输入雷实现步骤

这里的玩家输入坐标,在玩家输入下棋的时候,定义了个静态局部变量,在执行代码的时候。玩游戏的时候会提醒一次, 输入第一个坐标记得空一格!每次进入游戏只有一次,这里主要就是用到了 静态局部变量 就可以保证上一次的值不会被销毁。

检查坐标处是不是雷,布置存放的是字符'1',没有放置雷存放的是字符'0'。

判断坐标输入合法性几种情况:

很遗憾,你被炸死了!

宁已经在这里输入过坐标了,请重新输入!

宁输入的坐标范围错误!重新输入。

恭喜你,排雷成功!太优秀了!

void Check(char Layouts[ROWS][COLS], char Findout[ROWS][COLS], int row, int col)
{
  //1.输入排查雷的坐标
  //2.检查坐标处是不是雷,布置雷存放的是字符'1',没有放置雷存放的是字符'0'。
  int x, y;
  int win = 0;
  while (win<row*col - Thunder)
  {
    static int j = 1;//延长生明周期,
    while (j)
    {
      color(8);
      printf("--------------------------\n");
      printf("[输入第一个坐标记得空一格!]\n");
      printf("--------------------------\n");
      j--;
      break;
    }
    color(11);
    printf("---------------\n");
    printf("请输入坐标>:");
    //x与y坐标范围 1~9
    scanf("%d %d", &x, &y);
    printf("---------------\n");
    //判断坐标的合法性
    if (x >= 1 && x <= row && y >= 1 && y <= col)
    {
      if (Layouts[x][y] == '1')
      {
        printf("|══════════════════|\n");
        printf("|很遗憾,你被炸死了!|\n");
        printf("|══════════════════|\n");
        Print_board(Layouts, ROW, COL);
        Sleep(5000);
        break;
      }
      if (Findout[x][y] == '0')
      {
        color(6);
        printf("|═══════════════════════════════════|\n");
        printf("|宁已经在这里输入过坐标了,请重新输入!|\n");
        printf("|═══════════════════════════════════|\n");
      }
      if (Findout[x][y] == '1')
      {
        color(6);
        printf("|════════════════════════════════════|\n");
        printf("|宁已经在这里输入过坐标了,请重新输入!|\n");
        printf("|════════════════════════════════════|\n");
      }
      else
      {
        //不是雷情况下,统计x,y周围坐标有几个雷
        int Count = Statistics(Layouts, x, y);
        Findout[x][y] = Count + '0';
        Print_board(Findout, row, col);
        win++;
      }
    }
    else
    {
      printf("|═════════════════════════════|\n");
      printf("|宁输入的坐标范围错误!重新输入|\n");
      printf("|═════════════════════════════|\n");
    }
  }
  if (win == row*col - Thunder)
  {
    printf("|═══════════════════════|\n");
    printf("|恭喜你,排雷成功!太优秀了!|\n");
    printf("|═══════════════════════|\n");
    Print_board(Findout, ROW, COL);
  }
}

⑨ 排查 x,y 周围有多少雷

1.
static int Statistics(char Layouts[ROWS][COLS], int x, int y)
{
  return Layouts[x-1][y-1]+
    Layouts[x][y-1] +
    Layouts[x+1][y-1]+
    Layouts[x-1][y]+
    Layouts[x+1][y]+
    Layouts[x-1][y+1]+
    Layouts[x][y+1]+
    Layouts[x+1][y+1] - 8*'0';
}

🍁注意:静态局部变量去修饰函数的时候,让这个函数只能在自己所在的源文件内看到,其它的内部当中是看不到的。


Ⅴ 结果演示

被雷"砸死"的结果演示。

image.png

Ⅵ 模块化代码实现


(一)、test.c

测试游戏的逻辑。

//扫雷游戏的测试
#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include "game.h"
void menu()
{
  color(0);       //Black 黑色
  system("cls");  //清屏.
  color(10);
  printf("|-----------|扫雷游戏|-----------|\n");
  printf("|********************************|\n");
  printf("|★★★★★★★★★★★★★★★★|\n");
  printf("|★★   1.开始  0.退出       ★★|\n");
  printf("|★★★★★★★★★★★★★★★★|\n");
  printf("|0 = 不是雷 ---------- 1 = 它是雷|\n");
  printf("|--------------------------------|\n");
}
void game()
{
  printf(" ---------\n");
  printf("|PLAY GAME|\n");
  printf(" ---------\n");
  char Layouts[ROWS][COLS] = { 0 };//存放布置好雷的信息
  char Findout[ROWS][COLS] = { 0 };//存放排查出雷的信息
  //初始化棋盘
  Initialization(Layouts, ROWS, COLS, '0');//mine
  Initialization(Findout, ROWS, COLS, 'x');//show
  //打印棋盘
  /*Print_board(Layouts, ROW, COL);*/
  Print_board(Findout, ROW, COL);
  //布置雷
  Lay_thunder(Layouts, ROW, COL);
  /*Print_board(Findout, ROW, COL);*/
  //排查雷
  Check(Layouts,Findout,ROW,COL);
}
void test()
{
  int input = 0;
  srand((unsigned)time(NULL));
  do
  {
    menu(); 
    color(5);
    printf("\n");
    printf("|═════════════════════════════════|\n");
    printf("|Please enter the interface number|:");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      game();
      break;
    case 0:
      printf("|════════|\n");
      printf("|退出游戏|\n");
      printf("|════════|\n");
      break;
    default:
      printf("\n");
      printf("|═════════════════════════════════|\n");
      printf("|由于你输入错误罚你5s不能玩(→_→)|\n");
      printf("|═════════════════════════════════|\n");
      Sleep(5000);
    }
  } while (input);
}
int main(void)
{
  test();
  return 0;
}

(二)、game.h

关于游戏包含的函数声明,符号声明头文件的包含以及宏定义。

#define COL 9
#define ROWS ROW+2
#define COLS COL+2
//颜色函数
void color(short x);
//初始化函数,初始化11*11,因为 行 & 列 都需要加1
void Initialization(char board[ROWS][COLS], int rows, int cols, char set);
//打印棋盘,最终打印 9*9 棋盘即可
void Print_board(char board[ROWS][COLS], int row, int col);
//布置雷
void Lay_thunder(char Findout[ROWS][COLS], int row, int col);
//排查雷
void Check(char Layouts[ROWS][COLS], char Findout[ROWS][COLS], int row, int col);

(三)、game.c

游戏和相关函数实现。

#define _CRT_SECURE_NO_WARNINGS 1
//游戏的函数的实现
#include "game.h"
#include<stdio.h>
void color(short x)
{
  if (x >= 0 && x <= 15)
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), x);
  else
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 7);
}
void Initialization(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 Print_board(char board[ROWS][COLS], int row, int col)
{
  int i = 0; 
  int j = 0;
  color(7);
  printf("════════════════════\n");
  for (i = 0; i <= row; i++)
  {
    if (i == 0)
    {
      printf("%d|", i);
    }
    else
    {
      printf("%d|", i);
    }
  }
  printf("\n/|══════════════════");
  printf("\n");
  for (i = 1; i <= row; i++)
  {
    printf("%d|", i);
    for (j = 1; j <= col; j++)
    {
      printf("%c|", board[i][j]);
    }
    printf("\n");
  }
  color(6);
  printf("\n-----扫雷游戏------\n");
}
void Lay_thunder(char Findout[ROWS][COLS], int row, int col)
{
  //布置雷
  int count = Thunder;
  while (count)
  {
    int x = rand() % row + 1;
    int y = rand() % col + 1;
    if (Findout[x][y] = '0')
    {
      Findout[x][y] = '1';
      count--;
    }
  }
}
//静态局部变量去修饰函数的时候,让这个函数只能在自己所在的源文件内看到,其它的内部当中是看不到的。
static int Statistics(char Layouts[ROWS][COLS], int x, int y)
{
  return Layouts[x-1][y-1]+
    Layouts[x][y-1] +
    Layouts[x+1][y-1]+
    Layouts[x-1][y]+
    Layouts[x+1][y]+
    Layouts[x-1][y+1]+
    Layouts[x][y+1]+
    Layouts[x+1][y+1] - 8*'0';
}
void Check(char Layouts[ROWS][COLS], char Findout[ROWS][COLS], int row, int col)
{
  //1.输入排查雷的坐标
  //2.检查坐标处是不是雷,布置雷存放的是字符'1',没有放置雷存放的是字符'0'。
  int x, y;
  int win = 0;
  while (win<row*col - Thunder)
  {
    static int j = 1;//延长生明周期,
    while (j)
    {
      color(8);
      printf("--------------------------\n");
      printf("[输入第一个坐标记得空一格!]\n");
      printf("--------------------------\n");
      j--;
      break;
    }
    color(11);
    printf("---------------\n");
    printf("请输入坐标>:");
    //x与y坐标范围 1~9
    scanf("%d %d", &x, &y);
    printf("---------------\n");
    //判断坐标的合法性
    if (x >= 1 && x <= row && y >= 1 && y <= col)
    {
      if (Layouts[x][y] == '1')
      {
        printf("|══════════════════|\n");
        printf("|很遗憾,你被炸死了!|\n");
        printf("|══════════════════|\n");
        Print_board(Layouts, ROW, COL);
        Sleep(5000);
        break;
      }
      if (Findout[x][y] == '0')
      {
        color(6);
        printf("|═══════════════════════════════════|\n");
        printf("|宁已经在这里输入过坐标了,请重新输入!|\n");
        printf("|═══════════════════════════════════|\n");
      }
      if (Findout[x][y] == '1')
      {
        color(6);
        printf("|════════════════════════════════════|\n");
        printf("|宁已经在这里输入过坐标了,请重新输入!|\n");
        printf("|════════════════════════════════════|\n");
      }
      else
      {
        //不是雷情况下,统计x,y周围坐标有几个雷
        int Count = Statistics(Layouts, x, y);
        Findout[x][y] = Count + '0';
        Print_board(Findout, row, col);
        win++;
      }
    }
    else
    {
      printf("|═════════════════════════════|\n");
      printf("|宁输入的坐标范围错误!重新输入|\n");
      printf("|═════════════════════════════|\n");
    }
  }
  if (win == row*col - Thunder)
  {
    printf("|═══════════════════════|\n");
    printf("|恭喜你,排雷成功!太优秀了!|\n");
    printf("|═══════════════════════|\n");
    Print_board(Findout, ROW, COL);
  }
}
目录
相关文章
|
1月前
|
C语言
扫雷游戏(用C语言实现)
扫雷游戏(用C语言实现)
79 0
|
7天前
|
C语言 Windows
C语言课设项目之2048游戏源码
C语言课设项目之2048游戏源码,可作为课程设计项目参考,代码有详细的注释,另外编译可运行文件也已经打包,windows电脑双击即可运行效果
18 1
|
1月前
|
编译器 C语言
猜数字游戏实现#C语言
猜数字游戏实现#C语言
79 1
|
1月前
|
存储 C语言
揭秘C语言:泊舟的猜数字游戏
揭秘C语言:泊舟的猜数字游戏
|
1月前
|
存储 算法 安全
C语言实现扫雷游戏
C语言实现扫雷游戏
|
1月前
|
C语言
初学者指南:使用C语言实现简易版扫雷游戏
初学者指南:使用C语言实现简易版扫雷游戏
34 0
|
1月前
|
C语言
C语言扫雷游戏(详解)
C语言扫雷游戏(详解)
37 0
|
1月前
|
程序员 C语言
初识C语言之三子棋游戏
初识C语言之三子棋游戏
33 0
|
1月前
|
C语言
初识C语言3——函数(以猜数字游戏为例)
初识C语言3——函数(以猜数字游戏为例)
68 0
|
1月前
|
存储 编译器 C语言
【C语言篇】数组和函数的实践:扫雷游戏(附源码)
【C语言篇】数组和函数的实践:扫雷游戏(附源码)
35 0