C语言初阶之扫雷代码详解(含递归展开)

简介: 在game头文件中,首先包含会使用到的库头文件,这里的ROW以及COL是雷区的行和列大小,也就是说这是玩家实际能看到的行数及列数,而ROWS及COLS是实际棋盘大小

fa2e4c1ddb6c46f690211c934b061d91.png


扫雷代码思路


主要分为下面几个过程:

1、建立棋盘

2、初始化棋盘

3、设置棋盘雷数

4、打印棋盘

5、玩家找雷

6、判定胜负


头文件解析


文件名:game.h

代码如下:


#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 9//展示的雷区棋盘行数
#define COL 9//展示的雷区棋盘列数
#define ROWS ROW+2//实际的雷区棋盘行数
#define COLS COL+2//实际的雷区棋盘列数
void InitBoard(char board[ROWS][COLS],char ret);//初始化棋盘
int Select();//难易选项函数
void SetMine(char board[ROWS][COLS], int num);//布雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int num);//找雷


在game头文件中,首先包含会使用到的库头文件,这里的ROW以及COL是雷区的行和列大小,也就是说这是玩家实际能看到的行数及列数,而ROWS及COLS是实际棋盘大小,这样做的目的是为了方便后面对找到周围雷数函数的编写,剩下的函数声明是在test文件需要调用的函数声明,也就是说,在test文件中需要调用什么函数,在头文件中就声明什么函数,比如,在代码调试过程中,如果需要使用打印函数,则需要在此文件中写声明,否则就调用不了,因为每个人的写法不一样,作者的打印函数都是被找雷函数调用,所以声明中并没有添加,如果需要使用,小伙伴记得自己添加哦!!!


主函数解析


首先先看主函数test.c文件

代码如下:


#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
  printf("*******************************************************************\n");
  printf("********       1.玩游戏               2.退出游戏          *********\n");
  printf("*******************************************************************\n");
  printf("请输入你的选项:");
}
void game()
{
  char mine[ROWS][COLS] = { 0 };
  char show[ROWS][COLS] = { 0 };
  InitBoard(mine, '0');
  InitBoard(show, '*');
  int num = Select();
  SetMine(mine, num);
  FindMine(mine, show, num);
}
int main()
{
  int n = 0;
  srand((unsigned int)time(NULL));
  do
  {
    menu();
    scanf("%d", &n);
    switch (n)
    {
    case 1:
      game();
      printf("再来一局吗?\n");
      printf("1.再来一局   2. 退出游戏\n");
      int again = 0;
      scanf("%d", &again);
      if (again == 1)
      {
        break;
      }
      else
        n = 0;
      break;
    case 2:
      printf("退出游戏");
      break;
    default:
      printf("选择错误,请重新选择\n");
      break;
    }
  } while (n);
}


第一个menu是菜单函数,其目的为引导玩家选择选项,这里作者没有做这么多花里胡哨的菜单界面,简单一点,重点放在后面

第二个game是游戏函数,其中包括建立棋盘,初始化棋盘、布雷、找雷的函数调用以及难易选项函数的调用;这里使用num记录难易值,也就是雷数,然后再进行布雷和找雷的传参,至于打印棋盘,作者放在找雷函数中,后面会提到

第三个为主函数,首先设立了一个随机数种子,类似之前的三子棋,同样的,这里我们使用一个do…while循环,首先调用菜单打印,再让玩家输入菜单选项,case1进入游戏,case2退出游戏,default重新选;需要注意的是,在game函数结束时,会再次提示玩家是否进入游戏,输入1则再玩一把,其他数字则退出。

菜单效果:


642720c523c541e88ec6e693e27319ee.png


函数文件解析


①初始化函数(InitBoard)


代码如下:


void InitBoard(char board[ROWS][COLS],char ret)
{
  int i = 0, j = 0;
  for (i = 0; i < ROWS; i++)
  {
    for (j = 0; j < COLS; j++)
    {
      board[i][j] = ret;
    }
  }
}


这个函数的作用是将棋盘数组的值利用一个嵌套循环初始化为传过来的值。


②打印函数


这个棋盘的打印可以有多种设计,以下有两个版本,一个是简易版的,另一个是稍微优化的,你也可以发动自己的想象力,设计出更美观的棋盘。

简易版代码如下:


void DisplayBoard(char board[ROWS][COLS])
{
  printf("--------扫雷游戏--------\n");
  int i = 0, j = 0;
  for (i = 0; i <= COL; i++)//打印列标
  {
    printf("%2d", i);
  }
  printf("\n");
  for (i = 1; i <= ROW; i++)
  {
    printf("%2d", i);
    for (j = 1; j <= COL; j++)
    {
      printf("%2c", board[i][j]);
    }
    printf("\n");//打印一行后换行
  }
  printf("\n");
}


效果如下:


165d63743cb44699975d9d55e0f67b8e.png


优化版代码如下:


void DisplayBoard(char board[ROWS][COLS])
{
  printf("   --------------扫雷游戏--------------\n");
  int i = 0, j = 0;
  printf("   ");
  for (i = 1; i <= COL; i++)
  {
    printf("%3d ", i);
  }
  printf("\n");
  printf("   +");
  for (i = 0; i < COL; i++)
  {
    printf("---+");
  }
  printf("\n");
  for (i = 1; i <= ROW; i++)
  {
    printf("%2d", i);
    printf(" |");
    for (j = 1; j <= COL; j++)
    {
      printf(" %c |", board[i][j]);
    }
    printf("\n");
    printf("   +");
    for (j = 0; j < COL; j++)
    {
      printf("---+");
    }
    printf("\n");
  }
}


效果图如下:


cdbb1733818346cabb50428d7374be83.png


其实具体的代码实现并不复杂,看你自己的需要,可以不断调试这个排版。


③难易选项函数(Select)


代码如下:


int Select()
{
  system("cls");//清空屏幕
  int num = 0;
  while (1)
  {
    printf("请新选择难度:\n");
    printf("1.简单模式:5个雷  2.中等模式:10个雷 3.困难模式:15个雷 4.自定义难度(自由输入雷的个数)\n ");
    int select = 0;
    getchar();
    scanf("%d", &select);
    switch (select)
    {
    case 1:
      printf("简单模式:5个雷\n");
      num = 5;
      return num;
    case 2:
      printf("中等模式:10个雷\n");
      num = 15;
      return num;
    case 3:
      printf("困难模式:15个雷\n");
      num = 30;
      return num;
    case 4:
      printf("自定义难度:");
      printf("请输入布置雷的个数:\n");
      getchar();
      int intput = 0;
      scanf("%d", &intput);
      num = intput;
      return num;
    default:
      printf("输入错误,请重新选择:\n");
      break;
    }
  }
}


这段代码主要目的是通过玩家选择这四个选项进而返回雷值,然后就可以作为布雷和找雷的雷参数,主要有简易5个雷、中等10个雷、困难15个雷、自定义雷4中。

效果如下:


50bf62bc9f4242308a05c074c868774f.png


④布雷(SetMine)


代码如下:


void SetMine(char board[ROWS][COLS], int num)
{
  int x = 0, y = 0;
  int count = 0;
  while(count < num)
  {
    x = 1 + rand() % ROW;//1-9随机值
    y = 1 + rand() % COL;//1-9随机值
    if (board[x][y] == '0')
    {
      board[x][y] = '1';//布置雷点
      count++;
    }
  }
}


这里使用了随机数1-9来生成雷的位置,设置为字符0才能布雷即可,字符1为雷。

效果如下:

简单模式


bb81ec0ca4aa4ad69f2da986ec8ef8fc.png


中等模式


07554e45ff44413996a6ee2c22e0b90a.png


困难模式


6d9226eeced14069adcb30435bbba736.png


自定义模式


9e11049ed82144fdb310a7943a9de5d9.png


⑤获取周边雷数(GetMine)


代码如下:


int GetMine(char mine[ROWS][COLS], int x, int y)
{
  return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1]
      + mine[x][y - 1] + mine[x][y + 1]+ mine[x + 1][y - 1] 
      + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0';
}


这里我们看下面这个图:


1e544df61a3f4bfe9e0ab3ecdd22b24b.jpg


这是该点位相对位置的表示,而且棋盘表示的是字符型数组所以这里返回的是周围8个点位元素值相加再减去8乘上字符0⃣️的ascii码值,即为周围雷个数的雷数的字符ascii码值。

比如下面这个标识的位置周围有1个雷:


611bdd5f51be485c9d151a7763aabd1e.png


⑥展开函数(Expand)


代码如下:


Expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
  if (x >= 1 && x <= ROW && y >= 1 && y <= COL)//防止越界
  {
    int count = GetMine(mine, x, y);//获取该位置周围雷数
    if (count == 0)//周围没雷
    {
      show[x][y] = ' ';//赋值为空字符
      int i = 0, j = 0;
      for (i = x - 1; i <= x + 1; i++)//限制行
      {
        for (j = y - 1; j <= y + 1; j++)//限制列
        {
          if (show[i][j] == '*' && (i != x || j != y))//防止重复访问
          {
            Expand(mine, show, i, j);//递归调用
          }
        }
      }
    }
    else
    {
      show[x][y] = count + '0';//显示雷数
    }
  }
}


展开函数的作用是为了达到周围没雷时直接展开周围所有空间,利用递归不断向外展开,直至周围有雷为止

效果如下:


3a2ebf7b767943f8acf854b35aecc166.jpg


判定胜负函数(is_win)


代码如下:


int is_win(char show[ROWS][COLS])
{
  int count1 = 0;
  int i = 0, j = 0;
  for (i = 1; i <= ROW; i++)
  {
    for (j = 1; j <= COL; j++)
    {
      if (show[i][j] != '*')
      {
        count1++;
      }
    }
  }
  return count1;
}


此函数是用于每次玩家输入坐标后,调用此函数返回不等于字符*的值,以此判定胜负。


找雷函数(FindMine)


代码如下:


void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int num)
{
  system("cls");//清空屏幕
  DisplayBoard(show);//打印展示的棋盘
  int x = 0, y = 0;
  int win = 0;
  while (win < (ROW * COL - num))//条件为win值是否到达界限值
  {
    printf("请输入排查雷的坐标:");
    scanf("%d%d", &x, &y);
    if (x >= 1 && x <= ROW && y >= 1 && y <= COL)//防止越界
    {
      if (mine[x][y] == '1')//踩雷
      {
        system("cls");
        printf("很遗憾,你被炸死了\n");
        DisplayBoard(mine);//打印布雷棋盘
        break;
      }
      else//不是雷
      {
        Expand(mine, show, x, y);//展开周围
        system("cls");//清空屏幕
        win = is_win(show);//更新win值
        DisplayBoard(show);//打印玩家棋盘
      }
    }
    else
    {
      printf("坐标非法,请重新输入:\n");//越界重新输入
    }
  }
  if (win == (ROW * COL - num))//判定win值是否到达界限值
  {
    system("cls");//清空屏幕
    printf("恭喜你排雷成功\n");
    DisplayBoard(show);//打印玩家棋盘
  }
}


这里的找雷函数包含了游戏结束的判定,解析已在备注中

效果图如下:

简单模式


746a848625ce44ed9b8bf00c0abffeeb.jpg


自定义模式


90a4443bbb614e0a9e6b5248e9201c61.jpg


全部代码


game.h


#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>//清屏函数的头文件
#include <time.h>//srand函数的头文件
#define ROW 9//方便修改棋盘大小
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
//声明初始化雷盘函数
void InitBoard(char board[ROWS][COLS],char ret);
int Select();
//声明布置雷函数
void SetMine(char board[ROWS][COLS], int num);
//声明排查雷的函数
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS],int num);


game.c


#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void InitBoard(char board[ROWS][COLS],char ret)
{
  int i = 0, j = 0;
  for (i = 0; i < ROWS; i++)
  {
    for (j = 0; j < COLS; j++)
    {
      board[i][j] = ret;
    }
  }
}
//void DisplayBoard(char board[ROWS][COLS])
//{
//  printf("--------扫雷游戏--------\n");
//  int i = 0, j = 0;
//  for (i = 0; i <= COL; i++)//打印列标
//  {
//    printf("%2d", i);
//  }
//  printf("\n");
//  for (i = 1; i <= ROW; i++)
//  {
//    printf("%2d", i);
//    for (j = 1; j <= COL; j++)
//    {
//      printf("%2c", board[i][j]);
//    }
//    printf("\n");//打印一行后换行
//  }
//  printf("\n");
//}
void DisplayBoard(char board[ROWS][COLS])
{
  printf("   --------------扫雷游戏--------------\n");
  int i = 0, j = 0;
  printf("   ");
  for (i = 1; i <= COL; i++)
  {
    printf("%3d ", i);
  }
  printf("\n");
  printf("   +");
  for (i = 0; i < COL; i++)
  {
    printf("---+");
  }
  printf("\n");
  for (i = 1; i <= ROW; i++)
  {
    printf("%2d", i);
    printf(" |");
    for (j = 1; j <= COL; j++)
    {
      printf(" %c |", board[i][j]);
    }
    printf("\n");
    printf("   +");
    for (j = 0; j < COL; j++)
    {
      printf("---+");
    }
    printf("\n");
  }
}
int Select()
{
  system("cls");
  int num = 0;
  while (1)
  {
    printf("请新选择难度:\n");
    printf("1.简单模式:5个雷  2.中等模式:10个雷 3.困难模式:15个雷 4.自定义难度(自由输入雷的个数)\n ");
    int select = 0;
    getchar();
    scanf("%d", &select);
    switch (select)
    {
    case 1:
      printf("简单模式:5个雷\n");
      num = 5;
      return num;
    case 2:
      printf("中等模式:10个雷\n");
      num = 15;
      return num;
    case 3:
      printf("困难模式:15个雷\n");
      num = 30;
      return num;
    case 4:
      printf("自定义难度:");
      printf("请输入布置雷的个数:\n");
      getchar();//清楚缓存区
      int intput = 0;
      scanf("%d", &intput);
      num = intput;
      return num;
    default:
      printf("输入错误,请重新选择:\n");
      break;
    }
  }
}
void SetMine(char board[ROWS][COLS], int num)
{
  int x = 0, y = 0;
  int count = 0;
  while(count < num)
  {
    x = 1 + rand() % ROW;
    y = 1 + rand() % COL;
    if (board[x][y] == '0')
    {
      board[x][y] = '1';
      count++;
    }
  }
}
int GetMine(char mine[ROWS][COLS], int x, int y)
{
  return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1]
      + mine[x][y - 1] + mine[x][y + 1]+ mine[x + 1][y - 1] 
      + mine[x + 1][y] + mine[x + 1][y + 1] - 8 * '0';
}
Expand(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
  if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
  {
    int count = GetMine(mine, x, y);
    if (count == 0)
    {
      show[x][y] = ' ';
      int i = 0, j = 0;
      for (i = x - 1; i <= x + 1; i++)
      {
        for (j = y - 1; j <= y + 1; j++)
        {
          if (show[i][j] == '*' && (i != x || j != y))
          {
            Expand(mine, show, i, j);
          }
        }
      }
    }
    else
    {
      show[x][y] = count + '0';
    }
  }
}
int is_win(char show[ROWS][COLS])
{
  int count1 = 0;
  int i = 0, j = 0;
  for (i = 1; i <= ROW; i++)
  {
    for (j = 1; j <= COL; j++)
    {
      if (show[i][j] != '*')
      {
        count1++;
      }
    }
  }
  return count1;
}
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int num)
{
  system("cls");
  DisplayBoard(show);
  int x = 0, y = 0;
  int win = 0;
  while (win < (ROW * COL - num))
  {
    printf("请输入排查雷的坐标:");
    scanf("%d%d", &x, &y);
    if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
    {
      if (mine[x][y] == '1')
      {
        system("cls");
        printf("很遗憾,你被炸死了\n");
        DisplayBoard(mine);
        break;
      }
      else
      {
        Expand(mine, show, x, y);
        system("cls");
        win = is_win(show);
        DisplayBoard(show);
      }
    }
    else
    {
      printf("坐标非法,请重新输入:\n");
    }
  }
  if (win == (ROW * COL - num))
  {
    system("cls");
    printf("恭喜你排雷成功\n");
    DisplayBoard(show);
  }
}


test.c


#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
  printf("*******************************************************************\n");
  printf("********       1.玩游戏               2.退出游戏          *********\n");
  printf("*******************************************************************\n");
  printf("请输入你的选项:");
}
void game()
{
  char mine[ROWS][COLS] = { 0 };
  char show[ROWS][COLS] = { 0 };
  InitBoard(mine, '0');
  InitBoard(show, '*');
  int num = Select();
  SetMine(mine, num);
  FindMine(mine, show, num);
}
int main()
{
  int n = 0;
  srand((unsigned int)time(NULL));
  do
  {
    menu();
    scanf("%d", &n);
    switch (n)
    {
    case 1:
      game();
      printf("再来一局吗?\n");
      printf("1.再来一局   2. 退出游戏\n");
      int again = 0;
      scanf("%d", &again);
      if (again == 1)
      {
        break;
      }
      else
        n = 0;
      break;
    case 2:
      printf("退出游戏");
      break;
    default:
      printf("选择错误,请重新选择\n");
      break;
    }
  } while (n);
}


结语


近期更新大量C语言的博客,希望大家多支持!!!


有兴趣的小伙伴可以关注作者,如果觉得内容不错,请给个一键三连吧,蟹蟹你哟!!!

制作不易,如有不正之处敬请指出

感谢大家的来访,UU们的观看是我坚持下去的动力

在时间的催化剂下,让我们彼此都成为更优秀的人吧!!!


189ef709e19b41edacfc69ce9a14e5eb.png

相关文章
|
2天前
|
C语言
C语言——函数递归
C语言——函数递归
4 0
|
2天前
|
C语言
C语言扫雷代码(蹦蹦炸弹)(下)
C语言扫雷代码(蹦蹦炸弹)(下)
3 0
|
2天前
|
存储 C语言
关于我在C语言中玩扫雷(上)
关于我在C语言中玩扫雷(上)
4 0
|
3天前
|
C语言
每天一道C语言编程(递归:斐波那契数,母牛的故事)
每天一道C语言编程(递归:斐波那契数,母牛的故事)
5 0
|
3天前
|
C语言
【C语言/数据结构】排序(快速排序及多种优化|递归及非递归版本)
【C语言/数据结构】排序(快速排序及多种优化|递归及非递归版本)
11 0
|
3天前
|
传感器 算法 C语言
C语言在嵌入式系统开发中的优化策略与代码实现
C语言在嵌入式系统开发中的优化策略与代码实现
28 1
|
3天前
|
存储 算法 C语言
C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)
C语言进阶:顺序表(数据结构基础) (以通讯录项目为代码练习)
|
3天前
|
编译器 Linux C语言
C语言:预处理详解(知识点和代码演示)
C语言:预处理详解(知识点和代码演示)
|
3天前
|
机器学习/深度学习 C语言
函数递归深入解析(C语言)
函数递归深入解析(C语言)
|
3天前
|
C语言
换硬币问题(C语言代码练习)
换硬币问题(C语言代码练习)