欢迎光临
我们一直在努力

C 语言实现经典控制台扫雷游戏

扫雷是经典的休闲益智小游戏,本文将基于 C 语言,从需求分析、设计思路、代码实现三个维度,手把手教你实现一个控制台版的 9*9 扫雷游戏,包含菜单选择、随机布雷、排雷判断、输赢检测等核心功能,同时也会讲解游戏的扩展思路,让你在实践中吃透 C 语言数组和函数的综合运用。

一、扫雷游戏核心需求分析

本次实现的控制台扫雷游戏,还原经典端游的核心玩法,具体功能要求如下:

  • 游戏入口提供菜单,支持开始游戏和退出游戏,可重复游玩;
  • 游戏棋盘为 9*9 规格,默认随机布置 10 个雷;
  • 支持玩家输入坐标排查雷,包含三种排查结果:
    • 排查位置是雷:游戏结束,玩家被炸死,展示所有雷的位置;
    • 排查位置非雷:显示该位置周围 8 个格子的雷的数量;
    • 排查出所有非雷位置(共 71 个):排雷成功,游戏胜利,展示雷的位置;
  • 输入非法坐标(超出棋盘范围)时,提示玩家重新输入。
  • 二、游戏整体设计思路

    2.1 数据结构设计:双数组 + 扩大棋盘,解决核心问题

    扫雷的核心是雷的存储和排雷信息的展示,同时要避免排查边缘位置时出现数组越界,因此采用双 11*11 数组的设计方案,而非直接使用 9*9 数组:

  • 双数组分离职责

    • mine[11][11]:雷盘数组,专门存放雷的信息,初始化为字符'0',布置雷的位置设为字符'1',非雷位置保持'0';
    • show[11][11]:展示数组,专门存放玩家排雷的结果,初始化为字符'*'(保持神秘),排查非雷位置后,替换为周围雷的数量字符。双数组设计避免了雷的信息和排雷信息混杂,便于逻辑处理和页面展示。
  • 11*11 数组解决越界问题实际游戏区域为 99(数组下标 1~9),将数组扩大为 1111(下标 0~10),让边缘位置的周围 8 个格子都能正常访问,无需额外的越界判断,简化代码逻辑。

  • 2.2 文件结构设计:分文件编程,解耦代码

    采用 C 语言分文件编程的思想,将代码分为 3 个文件,各司其职,便于后续维护和扩展,这也是实际项目开发的常用规范:

  • game.h:头文件,存放宏定义、函数声明、所需头文件包含;
  • game.c:功能实现文件,存放游戏所有核心函数的具体实现;
  • test.c:测试逻辑文件,存放主函数、菜单函数,控制游戏的整体流程。
  • 2.3 核心函数设计

    结合游戏流程,设计 5 个核心函数,覆盖初始化、展示、布雷、排雷全流程:

  • InitBoard:棋盘初始化函数,为雷盘 / 展示数组设置初始值;
  • DisplayBoard:棋盘打印函数,展示当前的游戏棋盘(玩家视角仅展示 show 数组);
  • SetMine:布雷函数,随机在 9*9 区域布置 10 个雷;
  • GetMineCount:统计雷数函数,计算指定坐标周围 8 个格子的雷的数量;
  • FindMine:排雷函数,处理玩家的坐标输入,实现排雷逻辑和输赢判断。
  • 三、扫雷游戏完整代码实现

    本次实现的代码严格遵循分文件编程规范,需创建三个文件并分别编写代码,所有代码可直接复制编译运行(VS/Dev-C++/Clion 均可)。

    3.1 头文件:game.h

    该文件是游戏的 “全局配置中心”,定义了棋盘规格、雷的数量,声明了所有核心函数,同时包含所需的系统头文件。

    c

    运行

    #pragma once
    // 防止头文件重复包含
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>

    // 游戏核心配置宏定义
    #define EASY_COUNT 10 // 简单难度雷的数量
    #define ROW 9 // 棋盘实际行数
    #define COL 9 // 棋盘实际列数
    #define ROWS ROW+2 // 扩大后的行数(解决越界)
    #define COLS COL+2 // 扩大后的列数(解决越界)

    // 棋盘初始化函数:board-数组,rows/cols-数组规格,set-初始值
    void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);

    // 棋盘打印函数:board-数组,row/col-实际游戏棋盘规格
    void DisplayBoard(char board[ROWS][COLS], int row, int col);

    // 布雷函数:board-雷盘数组,row/col-实际游戏棋盘规格
    void SetMine(char board[ROWS][COLS], int row, int col);

    // 排雷函数:mine-雷盘数组,show-展示数组,row/col-实际游戏棋盘规格
    void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

    3.2 功能实现文件:game.c

    该文件实现game.h中声明的所有函数,是游戏的核心逻辑层,包含棋盘初始化、打印、随机布雷、雷数统计、排雷判断的具体代码。

    c

    运行

    #include "game.h"

    // 初始化棋盘
    void InitBoard(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 DisplayBoard(char board[ROWS][COLS], int row, int col)
    {
    int i = 0;
    int j = 0;
    printf("——–扫雷游戏——–\\n");
    // 打印列号(0~9),方便玩家输入坐标
    for (i = 0; i <= col; i++)
    {
    printf("%d ", i);
    }
    printf("\\n");
    // 打印行号(1~9)+ 棋盘内容
    for (i = 1; i <= row; i++)
    {
    printf("%d ", i);
    for (j = 1; j <= col; j++)
    {
    printf("%c ", board[i][j]);
    }
    printf("\\n");
    }
    }

    // 随机布置雷
    void SetMine(char board[ROWS][COLS], int row, int col)
    {
    // 需要布置的雷的数量
    int count = EASY_COUNT;
    // 循环布雷,直到雷的数量为0
    while (count)
    {
    // 生成1~9的随机行/列坐标(对应实际游戏区域)
    int x = rand() % row + 1;
    int y = rand() % col + 1;
    // 确保该位置未布雷,避免重复布雷
    if (board[x][y] == '0')
    {
    board[x][y] = '1';
    count–;
    }
    }
    }

    // 统计指定坐标周围的雷数(内部辅助函数,无需在头文件声明)
    int GetMineCount(char mine[ROWS][COLS], int x, int y)
    {
    // 周围8个格子的字符'0'/'1'相加,减去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 FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
    {
    int x = 0;
    int y = 0;
    int win = 0; // 记录玩家排查出的非雷数量,初始为0
    // 排雷成功条件:排查出的非雷数量 = 总格子数 – 雷的数量
    while (win < row * col – EASY_COUNT)
    {
    printf("请输入要排查的坐标:>");
    scanf("%d %d", &x, &y);
    // 判断坐标是否合法(在1~9范围内)
    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++; // 非雷数量+1
    }
    }
    // 坐标非法,提示重新输入
    else
    {
    printf("坐标非法,请重新输入!\\n");
    }
    }
    // 排雷成功,展示雷的位置
    if (win == row * col – EASY_COUNT)
    {
    printf("恭喜你,排雷成功!\\n");
    DisplayBoard(mine, ROW, COL);
    }
    }

    3.3 测试逻辑文件:test.c

    该文件控制游戏的整体流程,包含菜单函数和主函数,实现 “菜单选择→开始游戏→游戏循环→重新选择” 的逻辑,是玩家和游戏的交互入口。

    c

    运行

    #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];
    // 初始化棋盘:雷盘初始为'0',展示盘初始为'*'
    InitBoard(mine, ROWS, COLS, '0');
    InitBoard(show, ROWS, COLS, '*');
    // 打印初始展示棋盘(玩家视角)
    DisplayBoard(show, ROW, COL);
    // 随机布置雷
    SetMine(mine, ROW, COL);
    // 开始排查雷
    FindMine(mine, show, ROW, COL);
    }

    int main()
    {
    int input = 0;
    // 设置随机数种子,让布雷的随机坐标真正随机
    srand((unsigned int)time(NULL));
    // do-while循环:支持重复游玩游戏
    do
    {
    // 打印菜单
    menu();
    printf("请选择:>");
    scanf("%d", &input);
    // 分支选择:开始游戏/退出游戏/选择错误
    switch (input)
    {
    case 1:
    game(); // 开始游戏
    break;
    case 0:
    printf("退出游戏!\\n"); // 退出游戏
    break;
    default:
    printf("选择错误,请重新选择!\\n"); // 非法选择
    break;
    }
    } while (input); // input为0时退出循环,否则重新显示菜单

    return 0;
    }

    四、游戏编译运行说明

    4.1 编译运行步骤

  • 在 C 语言编译器中(以 VS 为例),新建空项目,命名为 Minesweeper;
  • 在项目中依次新建 3 个C++ 文件(或 C 文件),分别命名为game.h、game.c、test.c;
  • 将上述对应代码分别复制到三个文件中,保存代码;
  • 编译并运行项目,即可进入扫雷游戏菜单。
  • 4.2 游戏操作示例

  • 运行后显示菜单,输入1开始游戏,输入0退出游戏;
  • 开始游戏后,显示 9*9 的*棋盘,提示 “请输入要排查的坐标”,格式为行号 列号(如2 5,表示第 2 行第 5 列);
  • 若输入坐标是雷,提示 “被炸死”,并展示所有雷的位置(1表示雷);
  • 若输入坐标非雷,棋盘更新该位置为周围雷的数量,继续排查;
  • 若排查出所有 71 个非雷位置,提示 “排雷成功”,并展示雷的位置。
  • 五、扫雷游戏扩展思路

    本次实现的是基础版 9*9 扫雷,基于该代码,可进行多种功能扩展,提升游戏的可玩性,也是锻炼 C 语言编程能力的好方式,推荐几个经典扩展方向:

    5.1 增加游戏难度选择

    支持简单、中等、困难三种难度,通过宏定义和分支判断实现,核心修改点:

    • 简单:9*9 棋盘,10 个雷(基础版);
    • 中等:16*16 棋盘,40 个雷;
    • 困难:30*16 棋盘,99 个雷;
    • 在菜单后增加难度选择界面,根据玩家选择设置对应的棋盘规格和雷数。

    5.2 实现 “空白区域自动展开”

    当玩家排查的位置非雷且周围雷数为 0 时,自动展开周围的所有空白区域(直到遇到有雷数的格子),核心思路:

    • 采用递归实现,判断当前坐标周围雷数为 0 时,遍历周围 8 个坐标,依次递归排查;
    • 递归时需增加去重判断,避免重复展开同一个坐标。

    5.3 增加雷的标记功能

    支持玩家用指定字符(如#)标记疑似雷的位置,避免误点,核心修改点:

    • 增加坐标操作选择:“排查雷” 或 “标记雷”;
    • 标记雷时,将 show 数组对应位置设为#,取消标记时恢复为*;
    • 排雷成功条件需排除标记的位置,避免统计错误。

    5.4 增加排雷计时功能

    统计玩家的排雷时间,游戏结束时展示,核心思路:

    • 利用time()函数,在游戏开始时记录开始时间start = time(NULL);
    • 游戏结束(胜利 / 失败)时记录结束时间end = time(NULL);
    • 计算时间差end – start,即为玩家的排雷耗时(秒)。

    5.5 增加排雷步数统计

    统计玩家排查雷 / 标记雷的步数,游戏结束时展示,核心思路:

    • 定义步数变量step = 0,每次玩家有效输入坐标(排查 / 标记)时,step++;
    • 游戏结束时打印步数,实现 “计时 + 计步” 的双重统计。

    六、核心知识点总结

    本次扫雷游戏的实现,综合运用了 C 语言的多个核心知识点,也是 C 语言入门阶段的经典综合实践案例,重点掌握:

  • 二维数组的使用:二维数组的遍历、初始化、值的修改,是存储棋盘信息的核心;
  • 分文件编程:头文件和源文件的配合,函数声明与实现分离,提升代码的可读性和维护性;
  • 随机数生成:srand()设置随机数种子,rand()生成随机数,实现随机布雷;
  • 循环与分支:do-while、for循环实现遍历,if-else、switch实现逻辑判断;
  • 函数的封装:将核心逻辑封装为独立函数,降低代码耦合度,符合 “高内聚、低耦合” 的编程思想;
  • 数组越界解决:通过扩大数组规格,简化边缘位置的判断逻辑,是编程中解决越界问题的常用技巧。
  • 七、写在最后

    本次实现的控制台扫雷游戏,是 C 语言数组和函数的经典实践,从需求分析到代码实现,层层递进,核心是把复杂的游戏逻辑拆解为一个个简单的函数,逐个实现后再整合。

    在此基础上,大家可以尝试上述扩展功能,比如空白区域自动展开、难度选择、计时计步等,在扩展的过程中,你会对 C 语言的递归、分支判断、变量作用域等知识点有更深刻的理解。

    编程的核心是解决问题,扫雷游戏的实现过程,就是不断发现问题、分析问题、解决问题的过程,希望大家通过这个案例,提升自己的 C 语言编程思维和实战能力!

    赞(0)
    未经允许不得转载:171主机测评 » C 语言实现经典控制台扫雷游戏
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址