【C语言】扫雷(包括展开和标记)(二)

简介: 【C语言】扫雷(包括展开和标记)(二)

排查雷🍊

我们在一开始就说过,我们会在mine数组根据玩家输入的坐标排查周围的雷,然后把雷的数量返回到show数组,所以我们在传参的时候两个数组都应该传。

void game()
{
  //定义二维数组
  char mine[ROWS][COLS];
  char show[ROWS][COLS];
  //初始化二维数组
  init_arr(mine, ROWS, COLS, '0');
  init_arr(show, ROWS, COLS, '*');
  //打印二维数组
  display (show, ROW, COL);
  //埋雷
  set_mine (mine, ROW, COL);
  //display(mine, ROW, COL);
  //排查雷
  find_mine(mine, show, ROW, COL);
}

这就是我们整个游戏的过程了。

int mine_count(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';
/*for (i = -1; i <= 1; i++)
      {
        for (j = -1; j <= 1; j++)
        {
        也可以用这种方式
                }
           }*/
 }
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
  int x = 0;
  int y = 0;
  int win = 0;
  int sign = EASY;
  int input = 0;
    printf("请选择排查(1)或者标记(2)O(∩_∩)O\n");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      while (win < row * col - EASY)
      {
        printf("排查\n");
        printf("请输入你要排查的坐标\n");
        scanf("%d %d", &x, &y);
        if (x > 0 && x <= row && y > 0 && y <= col)
        { //判断合法
          if (mine[x][y] == '1')
          {  //如果是雷
            printf("很遗憾,艺术就是派大星\n");
            display(mine, ROW, COL);
            break;
          }
          else //不是雷
          {
            show[x][y] = mine_count(mine, x, y) + '0';
            display(show, ROW, COL);
            win++;
          }
        }
        else
        {
          printf("坐标不合法,请重新输入\n");
        }
      }
      system("cls");
      if (win == row * col - EASY)
      {
        printf("恭喜通关了\n");
        display(show, ROW, COL);
      }
      break;
    case 2:
      if (sign != 0)
      {
          printf("标记\n");
          printf("请输入你要标记的坐标\n");
          scanf("%d %d", &x, &y);
        if (x > 0 && x <= row && y > 0 && y <= col)
        { //判断合法
            show[x][y] = '#';
            sign--;
            display(show, ROW, COL);
            break;
        }
        else
        {
            printf("坐标不合法,请重新输入\n");
        }
         }
      else
      {
        printf("标记已经用完了\n");
        break;
      }
      }
  }

现在来看看这个查找雷的函数,

我写了两个功能,一个是标记,一个是排查(排查里面有TNT式展开,我们下一个标题讲,大家就当没看到 ^ _ ^ )

我们先讲排查:我先定义了一个sign表示可标记的数量,因为只有10个雷,所以你只能用10个标记,用完了就提示一下,我们也要判断你要标记的坐标是否合法,合法就把这个坐标变成’#'表示被标记了,当然不要忘记再打印一遍show数组。

我们再说最重要的排查,还有要你输入一个坐标,然后判断是否合法,合法之后就判断你这个位置的坐标是不是雷,是雷就提示你爆炸了,表示就在show数组的对应坐标上显示雷的数量。

但是我们会有一个排查的上限,我们81个坐标有10个雷也就是说我们最多只能排查71个位置,所以用了一个int win 来记录已经被排查的非雷的区域,当win到达71了就说明你排完了所有雷。

再来仔细说说怎么查找那周围8个坐标的雷的个数,当然我们还是要用函数来实现,int mine_count(char mine[ROWS][COLS], int x, int y), 这个函数就是看有几个雷,那就用8个位置的雷数减去8个位置都没有雷,结果就是有几个雷,也就是-8*‘0’的含义

大家看这个函数的返回值,我们想要的是显示字符数字但是这个函数返回的是整形那怎么办呢?
show[x][y] = mine_count(mine, x, y) + ‘0’;这就是转换的方法

当一个整型数值与一个字符型的 ASCII 码值相加时,

会自动将整型数值转换为字符型例如,

如果 mine_count(mine, x, y) 的值为 3,

那么 mine_count(mine, x, y) + ‘0’ 的结果就是字符型的 ‘3’。

TNT式展开🍊

这是我们没有展开的排查雷的一个部分,我在上个标题删去了展开,方便大家理解。

else //不是雷
          {
            show[x][y] = mine_count(mine, x, y) + '0';
            display(show, ROW, COL);
            win++;
          }
else //不是雷
          {
            expend(mine, show, x, y, &win);//递归展开
            /*show[x][y] = mine_count(mine, x, y) + '0';*/
            display(show, ROW, COL);
            win++;
          }

这个才是我们真正的展开代码。但是想完成这个代码,我们需要仔细研究一下展开的这个过程。再说一句这其实是一个递归的过程,我们要靠向递归模式思考

我们还是简单的分析一下这个过程,我们点击的红色坐标周围如果没有雷,就再从这个坐标周围的八个坐标分别开始递归,然后就再递归······。

我们先简单分析然后我们来继续细致的分析。

思考一🍊

由于画太多影响观感,我就只画了三个。

我们先点击红色区域,然后排查周围八个坐标,我们可以蓝,绿,橙,这三个区域的中心坐标都是用空格表示的,为什么呢?因为在蓝,绿,橙三个区域内没有一个雷,所以中心区域才能变成空格。

然后我们再仔细观察这个过程,我们在检查红色区域的时候会检查到(x-1,y),我们在蓝色区域检测的时候我们还是会检测(x,y),如果是这个的话,当又到(x,y)的时候不是又要检测一次吗?再检测(x,y)就又要检查

到(x-1,y),那这样不就是会一直死循环下去吗?

所以我们要设置一下我们的递归的第一个条件,这个坐标不能为空格,或者说只有没有被排查的坐标才可以被递归,可以想一下,我们如果是空格的坐标说明已经检查过它周围的坐标了,也就是不需要再检测。

思考二🍊

如果我们只是用上面的条件去实现递归是不可能成功的。

为什么?

我就直接讲了,因为还是越界访问(数组)。

还是请大家看图方便理解。

我们大块的绿色区域就是我们9x9的棋盘,我们之前在构思的时候确实解决了,在检查周围8个坐标的越界问题,但是现在是递归了,递归不会管你的二维数组有多大,它会一直递归下下去,大家看蓝色区域,蓝色区域之后还会有很多很多区域这里就不画出来了。

总结,递归要限制在我们的绿色区域内。

递归展开代码

void expend(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* win)
{
  if (mine_count(mine,x,y) == 0)
  {
    show[x][y] = ' ';
    (*win)++;
    int i, j;
    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 &&
            show[x+i][y+j]=='*')
          {
            expend(mine, show, x + i, y + j, win);
          }
        }
      }
  }
  else
  {   //如果九宫格有雷就算数
    show[x][y] = mine_count(mine, x, y) + '0';
  }
}

解释:首先还是没有雷才开始递归,再把我们选择的坐标赋值为space,再把(*win)++,说明你里胜利又进了一步,然后我们就用两个for循环开始递归, 当然递归的条件是在9x9棋盘内和该坐标没有被排查过。

test.c

#define  _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
  printf("********************\n");
  printf("*****  1.play  *****\n");
  printf("*****  0.exit  *****\n");
  printf("********************\n");
}
void game()
{
  //定义二维数组
  char mine[ROWS][COLS];
  char show[ROWS][COLS];
  //初始化二维数组
  init_arr(mine, ROWS, COLS, '0');
  init_arr(show, ROWS, COLS, '*');
  //打印二维数组
  display (show, ROW, COL);
  //埋雷
  set_mine (mine, ROW, COL);
  //display(mine, ROW, COL);
  //排查雷
  find_mine(mine, show, ROW, COL);
}
int main()
{
  int input = 0;
  srand((unsigned int)time(NULL));
  do
  {
    menu();
    printf("请输入—>\n");
    scanf("%d", &input);
    switch (input)
    {
    case 0:
      printf("退出游戏\n");
      break;
    case 1:
      game();
      break;
    default:
      printf("输入错误请重新输入\n");
      break;
    }
  } while (input);
  return 0;

game.c

#define  _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
//初始化二维数组
void init_arr(char arr[ROWS][COLS], int rows, int cols, char ch)
{
  int i = 0;
  int j = 0;
  for (i = 0; i < rows; i++)
  {
    for (j = 0; j < cols; j++)
    {
       arr[i][j] = ch;
    }
  }
}
void display(char arr[ROWS][COLS], int row, int col)
{
  printf("·················扫雷·················\n");
  int i = 0;
  int j = 0;
  for (i = 0; i <= row; i++)
  {
    printf(" %d ", i);
    if (i > 0)
    printf(" ");
  }
  printf("\n");
  printf("\n");
  for (i = 1; i <= row; i++)
  {
    printf(" %d ", i);
    for (j = 1; j <= col; j++)
    {
      printf(" %c ", arr[i][j]);
      if (j <= col -1)
      printf("|");
    }
    printf("\n");
    if (i <= row - 1)
    {
      printf("   ");
      for (j = 0; j < col; j++)
      {
        printf("---");
        if (j < col - 1)
          printf("|");
      }
      printf("\n");
    }
  }
  /*for (i = 0; i <= row; i++)
  {
    printf("%d ", i);
  }
  printf("\n");
  for (i = 1; i <= row; i++)
  {
    printf("%d ", i);
    for (j = 1; j <= col; j++)
    {
      printf("%c ", arr[i][j]);
    }
    printf("\n");
  }*/
  printf("·················扫雷··················\n");
}
void set_mine(char mine[ROWS][COLS], int row, int col)
{
  int x = 0;
  int y = 0;
  int count = EASY;
  while (count)
  {
    x = rand() % row + 1;
    y = rand() % col + 1;
    if (x > 0 && x <= row && y > 0 && y <= col && mine[x][y]!='1')
    {
      mine[x][y] = '1';
      count--;
    }
  }
}
int mine_count(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';
 }
void expend(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int* win)
{
  if (mine_count(mine,x,y) == 0)
  {
    show[x][y] = ' ';
    (*win)++;
      int i, j;
      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 &&
            show[x+i][y+j]=='*')
          {
            expend(mine, show, x + i, y + j, win);
          }
        }
      }
  }
  else
  {//如果九宫格有雷就算数
    show[x][y] = mine_count(mine, x, y) + '0';
  }
  //当一个整型数值与一个字符型的 ASCII 码值相加时,
  //会自动将整型数值转换为字符型例如,
  //如果 mine_count(mine, x, y) 的值为 3,
  //那么 mine_count(mine, x, y) + '0' 的结果就是字符型的 '3'。
}
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
  int x = 0;
  int y = 0;
  int win = 0;
  int sign = EASY;
  int input = 0;
    printf("请选择排查(1)或者标记(2)O(∩_∩)O\n");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
      while (win < row * col - EASY)
      {
        printf("排查\n");
        printf("请输入你要排查的坐标\n");
        scanf("%d %d", &x, &y);
        if (x > 0 && x <= row && y > 0 && y <= col)
        { //判断合法
          if (mine[x][y] == '1')
          {  //如果是雷
            printf("很遗憾,艺术就是派大星\n");
            display(mine, ROW, COL);
            break;
          }
          else //不是雷
          {
            expend(mine, show, x, y, &win);//递归展开
            /*show[x][y] = mine_count(mine, x, y) + '0';*/
            display(show, ROW, COL);
            win++;
          }
        }
        else
        {
          printf("坐标不合法,请重新输入\n");
        }
      }
      system("cls");
      if (win == row * col - EASY)
      {
        printf("恭喜通关了\n");
        display(show, ROW, COL);
      }
      break;
    case 2:
      if (sign != 0)
      {
          printf("标记\n");
          printf("请输入你要标记的坐标\n");
          scanf("%d %d", &x, &y);
        if (x > 0 && x <= row && y > 0 && y <= col)
        { //判断合法
            show[x][y] = '#';
            sign--;
            display(show, ROW, COL);
            break;
        }
        else
        {
            printf("坐标不合法,请重新输入\n");
        }
         }
      else
      {
        printf("标记已经用完了\n");
        break;
      }
      }
  }

game.h

#pragma once
#define ROW 9
#define COL 9
#define ROWS 9+2
#define COLS 9+2
#define  EASY 10
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<windows.h>
//初始化
void init_arr(char arr[ROWS][COLS], int row, int col, char ch);
//打印二维数组
void display(char mine[ROWS][COLS], int row, int col);
//埋雷
void set_mine( char mine[ROWS][COLS], int row, int col);
//排查雷
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

总结🍊

这篇博客我们系统的介绍了扫雷以及扫雷的TNT展开,我自己感觉我的代码还是有很多不足之处的,如果有兴趣的小伙伴可试一试。

最后如果这篇博客有帮助到你,欢迎点赞关注加收藏

如果本文有任何错误或者有疑点欢迎在评论区评论


目录
相关文章
|
2月前
|
C语言
扫雷游戏(用C语言实现)
扫雷游戏(用C语言实现)
110 0
|
4月前
|
机器学习/深度学习 C语言
九/十:《初学C语言》— 扫雷游戏实现和函数递归基础
【8月更文挑战第5天】本篇文章用C语言采用多文件编写实现了一个基础的扫雷游戏(附源码),并讲解了关于函数递归的基础概念及其相对应的习题练习(附源码)
45 1
九/十:《初学C语言》— 扫雷游戏实现和函数递归基础
|
3月前
|
存储 安全 算法
C 语言——实现扫雷小游戏
本文介绍了使用二维数组创建棋盘并实现扫雷游戏的方法。首先,通过初始化数组创建一个9x9的棋盘,并添加行列标识以便操作。接着,利用随机数在棋盘上布置雷。最后,通过判断玩家输入的坐标来实现扫雷功能,包括显示雷的数量和处理游戏胜利或失败的情况。文中提供了完整的代码实现。
54 1
C 语言——实现扫雷小游戏
|
2月前
|
存储 算法 安全
C语言实现扫雷游戏
C语言实现扫雷游戏
|
2月前
|
C语言
初学者指南:使用C语言实现简易版扫雷游戏
初学者指南:使用C语言实现简易版扫雷游戏
39 0
|
2月前
|
C语言
C语言扫雷游戏(详解)
C语言扫雷游戏(详解)
42 0
|
2月前
|
存储 编译器 C语言
【C语言篇】数组和函数的实践:扫雷游戏(附源码)
【C语言篇】数组和函数的实践:扫雷游戏(附源码)
39 0
|
4月前
|
C语言
扫雷(C语言)
扫雷(C语言)
46 4
|
5月前
|
存储 编译器 C语言
|
6月前
|
C语言
【海贼王编程冒险 - C语言海上篇】怎样用C语言实现简单的扫雷游戏?
【海贼王编程冒险 - C语言海上篇】怎样用C语言实现简单的扫雷游戏?
41 1