【C语言】函数递归实现——扫雷小游戏(详细步骤)

简介: 【C语言】函数递归实现——扫雷小游戏(详细步骤)

【C语言】函数递归实现——扫雷小游戏(详细步骤)


image.png

有手就行

image.png

——————————————————————————————————————————

相信大家都是玩过扫雷的,本篇博客就来记录一下如何用非常基础的c语言写一个简单的扫雷代码

——————————————————————————————————————————

思路概览

1.游戏菜单模块

void menu()函数

image.png

2.游戏模块

void game()函数

image.png

2.1.初始化棋盘模块

先初始化两个二维数组,一个用于存放雷的信息,一个用于存放排查出的信息

image.png

2.1.1设置棋盘

void InitBoard()函数

写一个void InitBoard函数,用于对棋盘进行初始化

因为当我们排雷时,是对周围一圈的点进行排查,当排到边界上的点时,如果不设置多余的空间,将无法正常对雷计数,虽然9×9的棋盘,实际上是一个11×11的空间,不管是雷点还是排查点,都不能在最外围这一圈内,外围这一圈空间仅仅用来实现边界点的排雷计数功能

image.png

然后分别将一开始初始化的两个二维数组传入此函数,存放雷信息的棋盘全部初始化成字符 0,存放排查信息的棋盘都初始化成字符 *

image.png

2.1.2打印棋盘

void DisplayBoard ( )函数

打印棋盘,即使其能在控制台上显示出棋盘,这里用到一个打印函数

void DisplayBoard(char board[ROWS][COLS], int row, int col)

image.png

游戏设置是9×9的棋盘,为了方便玩家输入坐标,故打印棋盘时设置上行和列的序号

image.png

2.2.布置地雷模块

void SetMine()函数

布置地雷需要一个埋雷函数

void SetMine(char board[ROWS][COLS], int row, int col)

这里需要用到时间戳,要在main()函数加上

image.png


从而在埋雷函数里就可以使用rand()函数生成随机坐标来进行地雷的设置

将地雷位置设置成字符1,方便后续排雷时使用

image.png

2.3.玩家排雷模块

void FindMine()函数

排雷函数FindMine()里包含计数函数int GetMineCount()和特殊展开函数Clear()

image.png

2.3.1对排查点进行分类判断

2.3.1.1对排查点周围的雷进行计数

int GetMineCount()函数

这里设置一个计数函数:

因为雷是‘1’,所以将排查点周围的点相加

因为‘1’-‘0’=1,Ascii码

所以周围八个字符相加减去8*‘0’等于几

说明有几颗雷在旁边

image.png

2.3.1.2如果排查点周围都没有雷
void Clear()函数

这属于特殊情况,可以对此点采用函数递归展开大片区域

用到这个递归展开函数:

image.png

设置一个win变量,当win=9×9 - 10的时候,游戏就胜利了

排查非雷点有两种情况:

①每发现一个周围没有雷的点,就将其设置成空格,同时给win++

②每发现一个周围有雷的点,就调用计数函数,将结果返回给这个点,同时win++

注意,由于是递归的方式来排查点,一定不要重复对排过的点进行win++

void Clear(char mine[ROWS][COLS], char show[ROWS][COLS] , int x , int y , int row , int col, int*win)
 {
   if (x >= 1 && x <= row && y >= 1 && y <= col)
   {
     if (show[x][y] == ' ' || show[x][y]!='*')//排除已经检查过的点
       return;
     else if (GetMineCount(mine, x, y) != 0)
     {
       show[x][y] = GetMineCount(mine, x, y) + '0';
       (*win)++;
       return;
     }
     else
     {
       show[x][y] = ' ';
       (*win)++;
       Clear(mine, show, x - 1, y, ROW, COL, win);
       Clear(mine, show, x + 1, y, ROW, COL, win);
       Clear(mine, show, x, y - 1, ROW, COL, win);
       Clear(mine, show, x, y + 1, ROW, COL, win);
     }
   }return;
 }

image.png

2.4.判断游戏输赢

在void FindMine种设置有一个变量win,将win通过指针传址的方式与void Clear建立联系,用来统计排查点的数目,当win=9×9-10时

即打印游戏胜利

image.png

3.完整代码

头文件game.h

#pragma once
#include<stdio.h>
#include <stdlib.h>
#include <time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
//显示棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
//布置雷
void SetMine(char board[ROWS][COLS], int row, int col);
//排查雷的
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
//特殊情况大片展开
void Clear(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y, int row, int col, int* win);

game.c

#include"game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
  int i = 0;
  for (i = 0; i < rows; i++)
  {
    int j = 0;
    for (j = 0; j < cols; j++)
      board[i][j] = set;
  }
}
void SetMine(char board[ROWS][COLS], int row, int col)
{
  int count = EASY_COUNT;
  while (count)//一共设置了10个雷,没设置一颗雷,count就减一
  {
    //生成随机下标,这里利用时间戳和rand()函数
    int x = rand() % row + 1;
    int y = rand() % col + 1;
    if (board[x][y] == '0')
    {
      board[x][y] = '1';
      count--;
    }
  }
}
 void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
  int i = 0;
  printf("------------------------\n");
  for (i = 0; i <= 9; i++)//方便观看棋盘,给每行每列标上序号,标行号
  {
    printf("%d ", i);
  }
  printf("\n");
  for (i = 1; i <= row; i++)
  {
    int j = 0;
    printf("%d ", i);//标列号
    for (j = 1; j <= col; j++)
    {
      printf("%c ", board[i][j]);
    }
    printf("\n");
  }
  printf("------------------------\n");
}
 int GetMineCount(char mine[ROWS][COLS], int x, int y)//因为雷是‘1’,所以将排查点周围的点相加
                                                    //因为‘1’-‘0’=1,Ascii码
                                                    //所以周围八个字符相加减去8*‘0’等于几
                            //说明有几颗雷在旁边
 {
   return (mine[x - 1][y] +
     mine[x - 1][y - 1] +
     mine[x][y - 1] +
     mine[x + 1][y - 1] +
     mine[x + 1][y] +
     mine[x + 1][y + 1] +
     mine[x][y + 1] +
     mine[x - 1][y + 1] - 8 * '0');
 }
 void Clear(char mine[ROWS][COLS], char show[ROWS][COLS] , int x , int y , int row , int col, int*win)
 {
   if (x >= 1 && x <= row && y >= 1 && y <= col)
   {
     if (show[x][y] == ' ' || show[x][y]!='*')
       return;
     else if (GetMineCount(mine, x, y) != 0)
     {
       show[x][y] = GetMineCount(mine, x, y) + '0';
       (*win)++;
       return;
     }
     else
     {
       show[x][y] = ' ';
       (*win)++;
       Clear(mine, show, x - 1, y, ROW, COL, win);
       Clear(mine, show, x + 1, y, ROW, COL, win);
       Clear(mine, show, x, y - 1, ROW, COL, win);
       Clear(mine, show, x, y + 1, ROW, COL, win);
     }
   }return;
 }
 void FindMine(char mine[ROWS][COLS],char show[ROWS][COLS],int row,int col)
 {
   int x = 0;
   int y = 0;
   int win = 0;
   while (win < row * col - EASY_COUNT)//一共10颗雷,只要排过的点<所有格子减去10个雷,就继续
   {
     printf("请输入要排查的坐标:>");
     scanf_s("%d %d", &x, &y);
     if (x >= 1 && x <= row && y >= 1 && y <= col)
     {
       if (mine[x][y] == '1')
       {
         printf("很遗憾,你被炸死了\n");
         DisplayBoard(mine, ROW, COL);
         break;
       }
       else
       {
         Clear(mine, show, x, y, ROW, COL, &win);
         DisplayBoard(show, ROW, COL);
         printf("已排除位置数= %d\n", win);
     }
     else
     {
       printf("坐标非法,重新输入\n");
     }
   }
   if (win == row * col - EASY_COUNT)
   {
     printf("恭喜你,排雷成功\n");
     DisplayBoard(mine, ROW, COL);
   }
 }

扫雷.c

image.png


相关文章
|
4天前
|
存储 Serverless C语言
【C语言基础考研向】11 gets函数与puts函数及str系列字符串操作函数
本文介绍了C语言中的`gets`和`puts`函数,`gets`用于从标准输入读取字符串直至换行符,并自动添加字符串结束标志`\0`。`puts`则用于向标准输出打印字符串并自动换行。此外,文章还详细讲解了`str`系列字符串操作函数,包括统计字符串长度的`strlen`、复制字符串的`strcpy`、比较字符串的`strcmp`以及拼接字符串的`strcat`。通过示例代码展示了这些函数的具体应用及注意事项。
|
7天前
|
存储 C语言
C语言程序设计核心详解 第十章:位运算和c语言文件操作详解_文件操作函数
本文详细介绍了C语言中的位运算和文件操作。位运算包括按位与、或、异或、取反、左移和右移等六种运算符及其复合赋值运算符,每种运算符的功能和应用场景都有具体说明。文件操作部分则涵盖了文件的概念、分类、文件类型指针、文件的打开与关闭、读写操作及当前读写位置的调整等内容,提供了丰富的示例帮助理解。通过对本文的学习,读者可以全面掌握C语言中的位运算和文件处理技术。
|
7天前
|
存储 C语言
C语言程序设计核心详解 第七章 函数和预编译命令
本章介绍C语言中的函数定义与使用,以及预编译命令。主要内容包括函数的定义格式、调用方式和示例分析。C程序结构分为`main()`单框架或多子函数框架。函数不能嵌套定义但可互相调用。变量具有类型、作用范围和存储类别三种属性,其中作用范围分为局部和全局。预编译命令包括文件包含和宏定义,宏定义分为无参和带参两种形式。此外,还介绍了变量的存储类别及其特点。通过实例详细解析了函数调用过程及宏定义的应用。
|
12天前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
12天前
|
C语言
C语言 字符串操作函数
本文档详细介绍了多个常用的字符串操作函数,包括 `strlen`、`strcpy`、`strncpy`、`strcat`、`strncat`、`strcmp`、`strncpy`、`sprintf`、`itoa`、`strchr`、`strspn`、`strcspn`、`strstr` 和 `strtok`。每个函数均提供了语法说明、参数解释、返回值描述及示例代码。此外,还给出了部分函数的自实现版本,帮助读者深入理解其工作原理。通过这些函数,可以轻松地进行字符串长度计算、复制、连接、比较等操作。
|
13天前
|
SQL 关系型数据库 C语言
PostgreSQL SQL扩展 ---- C语言函数(三)
可以用C(或者与C兼容,比如C++)语言编写用户自定义函数(User-defined functions)。这些函数被编译到动态可加载目标文件(也称为共享库)中并被守护进程加载到服务中。“C语言函数”与“内部函数”的区别就在于动态加载这个特性,二者的实际编码约定本质上是相同的(因此,标准的内部函数库为用户自定义C语言函数提供了丰富的示例代码)
|
28天前
|
C语言
【C语言】字符串及其函数速览
【C语言】字符串及其函数速览
22 4
|
24天前
|
机器学习/深度学习 编译器 Serverless
C语言中函数
C语言中函数
19 0
|
24天前
|
存储 Serverless C语言
C语言中的标准库函数
C语言中的标准库函数
19 0
|
26天前
|
算法 编译器 C语言
【C语言】递归
【C语言】递归
11 0