九/十:《初学C语言》— 扫雷游戏实现和函数递归基础

简介: 【8月更文挑战第5天】本篇文章用C语言采用多文件编写实现了一个基础的扫雷游戏(附源码),并讲解了关于函数递归的基础概念及其相对应的习题练习(附源码)

九:数组和函数实践:扫雷游戏

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

(1)扫雷游戏功能说明:

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

游戏界面效果:

2.扫雷游戏的代码实现

  • test.c —— 专门测试游戏的逻辑
  • game.c —— 游戏实现
  • game.h —— 游戏函数的声明。。。
(1).test.c
#include "game.h"

void menu()
{
   
    printf("********************\n");
    printf("****** 1.play ******\n");
    printf("****** 0.exit ******\n");
    printf("********************\n");
}

void game()
{
   
    //数组的创建
    char mine[ROWS][COLS];//‘0’
    char show[ROWS][COLS];//‘*’
    InitBoard(mine, ROWS, COLS, '0');
    InitBoard(show, ROWS, COLS, '*');

    //界面的打印
    //布置雷
    //DisplayBoard(mine,ROW,COL);
    //排查雷
    DisplayBoard(show,ROW,COL);

    //布置雷
    SetMuine(mine, ROW, COL);

    //排查雷
    FindMine(mine,show,ROW,COL);
}

int main()
{
   
    int input = 0;
    srand((unsigned int)time(NULL));
    do
    {
   
        menu();
        printf("请选择 >>>");
        scanf("%d", &input);
        switch (input)
        {
   
        case 1:
            printf("开始扫雷游戏\n");
            game();
            break;
        case 0:
            printf("已退出游戏\n");
            break;
        default:
            printf("输入错误,请重新输入 >>>\n");
            break;
        }
    } while (input);
    return 0;
}
(2)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 DisplayBoard(char board[ROWS][COLS], int row, int col)
{
   
    //打印列号
    int i = 0;
    printf("------扫雷游戏------\n");
    for (i = 0; i <= row; i++)
    {
   
        printf("%d ", i);
    }
    printf("\n");
    //打印两个界面
    for (i = 1; i <= row; i++)
    {
   
        //打印每一行
        printf("%d ",i);
        int j = 0;
        for (j = 1; j <= col; j++)
        {
   
            printf("%c ",board[i][j]);
        }
        printf("\n");
    }
    printf("--------------------\n");
}

void SetMuine(char mine[ROWS][COLS], int row, int col)
{
   
    int count = EASY_COUNT;
    while (count)
    {
   
        int x = rand() % row + 1;
        int y = rand() % col + 1;
        if (mine[x][y] == '0')
        {
   
            mine[x][y] = '1';
            count--;
        }
    }
}

static int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
   
    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 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) 
    {
   
        printf("请输入要排查的坐标 >>>");
        scanf("%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
            {
   
                int count = GetMineCount(mine,x,y);
                show[x][y] = count + '0';
                DisplayBoard(show, ROW, COL);
                win++;
            }
        }
        else
        {
   
            printf("坐标输入错误,请重新输入 >>>");
        }
    }
    //循环结束
    if (win == row * col - EASY_COUNT)
    {
   
        printf("恭喜你!排雷成功~~~");
        DisplayBoard(mine, ROW, COL);
    }
}
(3)game.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW 9 //定义行为9
#define COL 9 //定义行为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 rows, int cols);

//布置雷
void SetMuine(char mine[ROWS][COLS],int row,int col);

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

十:函数递归

1.递归的概念

递归其实是一种解决问题的方法,在C语言中,递归就是函数自己调用自己

示例:

#include <stdio.h>
int main()
{
   
    printf("dudu\n");
    main();
    return 0;
}

注意: 上述的示例就是一个最简单的递归程序,但递归是为了解决一个实际的需求,而上述代码只是一个演示,代码最终会陷入一个死循环,导致栈溢出。这是一个错误的示范

递归的思想: 递归中的递就是递推的意思,归就是回归的意思

把一个大型复杂的问题层层转化为一个与原问题相似,但规模较小的子问题来求解;直到子问题不能再被拆分,递归就结束了。所以递归的思考方式就是把大问题化解为小问题的过程

2.递归的限制条件

在书写递归的时候,有2个必要的条件:

  • 递归存在限制条件,当满足这个限制条件的时候,递归便不再继续
  • 每次递归调用之后会越来越接近这个限制条件

示例1:求n的阶乘

计算n的阶乘,不考虑溢出,n的阶乘就是1~n的数学累积相乘

n的阶乘公式为:n! = n*(n-1)!

例子:

求5的阶乘: 5! = 1 2 3 4 5

求n的阶乘: n! = 1 2 3 ... n

#include <stdio.h>
int Fact(int n)
{
   
    if(n<=0)
    {
   
        return 1;
    }
    else //n>0
    {
   
        return n*Fact(n-1);
    }
}

int main()
{
   
    int n = 0;
    scanf("%d",&n);
    int ret = Fact(n);
    printf("%d\n",ret);
    return 0;
}

示例2:顺序打印一个整数的每一位

输入一个整数m,按照顺序打印这个整数的每一位

例子:

输入:m = 1234

打印:1 2 3 4

#include <stdio.h>
void Print(int m)
{
   
    if(m>9)
    {
   
        Print(m/10);
    }
    printf("%d ",m%10);
}

int main()
{
   
    int m = 0;
    scanf("%d",&m); //1234
    Print(m);
    return 0;
}

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