欢迎光临
我们一直在努力

C语言-----扫雷游戏

扫雷游戏的功能说明 :

• 使⽤控制台实现经典的扫雷游戏
• 游戏可以通过菜单实现继续玩或者退出游戏
• 扫雷的棋盘是9*9的格⼦
• 默认随机布置10个雷
• 可以排查雷:
◦ 如果位置不是雷,就显⽰周围有⼏个雷
◦ 如果位置是雷,就炸死游戏结束
◦ 把除10个雷之外的所有⾮雷都找出来,排雷成功,游戏结束

test.c //⽂件中写游戏的测试逻辑
game.c //⽂件中写游戏中函数的实现等
game.h //⽂件中写游戏需要的数据类型和函数声明等

逻辑开始:

一、菜单

  • 输入1进入游戏,输入0退出游戏,输入其他数字显示输入错误,并且重新输入
  • test.c
    #include "game.h"

    int main()
    {
    menu();
    {
    regain:
    printf("请输入你的选择:");
    int input1;
    scanf("%d", &input1);
    switch (input1)
    {
    case 1:
    {
    printf("进入游戏\\n");
    game();
    break;
    }
    case 0:
    {
    printf("退出游戏\\n");
    break;

    }
    default:
    {
    printf("输入错误,请重新输入:");
    goto regain;
    }
    }
    }
    return 0;
    }

    game.c
    #include "game.h"

    void menu()
    {
    printf("****************\\n");
    printf("**** 1.Play ****\\n");
    printf("**** 0.Quit ****\\n");
    printf("****************\\n");

    }

    game.h
    #pragma once
    #include <stdio.h>
    #include "game.h"

    //菜单
    void menu();

    二、生成 9X9 的游戏界面

  • 使用二维数组实现
  • 运用两个棋盘,一个用于展示,一个用于设置雷,写出初始化棋盘的函数
  • 将展示的棋盘char show全部初始化为 '*',将布置雷的棋盘char mine全部初始化为0
  • 为方便后边测试,可以先把打印棋盘的函数写出
  • test.c文件增加了以下代码

    test.c
    #include "game.h"

    void game()
    {
    //用于布置雷的二维数组
    char mine[ROWS][COLS] = { 0 };

    //用于游戏界面的的二维数组
    char show[ROWS][COLS] = { 0 };

    //用于游戏界面的的二维数组全部初始为 '*'
    set_keyboard(show, ROWS, COLS, '*');

    //用于布置雷的二维数组全部初始化为 '0'
    set_keyboard(mine, ROWS, COLS, '0');

    //打印函数
    printf_keyboard(show, ROW, COL);
    printf_keyboard(mine, ROW, COL);
    }

    game.c文件增加了以下代码

    game.c
    #include "game.h"

    //初始化棋盘
    void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set)
    {
    for (int i = 0; i < rows; i++)
    {
    for (int j = 0; j < cols; j++)
    {
    board[i][j] = set;
    }
    }
    }

    //展示棋盘
    void printf_keyboard(char board[ROWS][COLS], int row, int col)
    {
    printf("——-扫雷——–\\n");
    for (int r = 0; r <= row; r++)
    {
    printf("%d ", r);
    }

    printf("\\n");

    for (int i = 1; i <= row; i++)
    {
    printf("%d ", i);
    for (int j = 1; j <= col; j++)
    {
    printf("%c ", board[i][j]);
    }
    printf("\\n");
    }
    }

    game.c文件增加了以下代码

    #pragma once
    #include <stdio.h>
    #include "game.h"

    #define ROW 9
    #define COL 9
    #define ROWS ROW+3
    #define COLS COL+2

    //菜单
    void menu();

    //初始化棋盘
    void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set);

    //展示棋盘
    void printf_keyboard(char board[ROWS][COLS], int row, int col);

    三、随机布置雷

  • 使用srand((unsigned int) time(NULL))和rand()
  • 将雷设置为1,雷只能布置在char mine[x][y] == '0'的地方
  • game.h文件增加了
    #include <time.h>
    #include "stdlib.h"
    #define MINE 10
    void set_mine(char board[ROWS][COLS], int row, int col, int mine);

    game.h

    #pragma once
    #include <stdio.h>
    #include "game.h"
    #include <time.h>
    #include "stdlib.h"

    #define ROW 9
    #define COL 9
    #define ROWS ROW+3
    #define COLS COL+2
    #define MINE 10

    //菜单
    void menu();

    //初始化棋盘
    void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set);

    //展示棋盘
    void printf_keyboard(char board[ROWS][COLS], int row, int col);

    //随机布置雷
    void set_mine(char board[ROWS][COLS], int row, int col, int mine);

    game.c文件增加以下代码
    game.c

    //随机布置雷
    void set_mine(char board[ROWS][COLS], int row, int col, int mine)
    {
    srand((unsigned int) time(NULL));

    while (mine)
    {
    int x = (rand() % row) + 1;
    int y = (rand() % col) + 1;
    if (board[x][y] == '0');
    {
    board[x][y] = '1';
    mine;
    }
    }

    }

    test.c文件增加以下代码

    test.c

    //随机布置雷
    set_mine(mine, ROW, COL, MINE);

    四、排雷

  • 注意输入的坐标,横纵坐标都只能是0~9,出现其他数字报错,并重新输入
  • 所排的坐标要显示周围雷的个数,如果为0,展开周围的棋盘(运用到递归)
  • 如果所排的坐标是雷,显示游戏结束
  • 如果输入的坐标是已经输入过的坐标,显示该坐标已经排除
  • 判断游戏胜利,排除的坐标个数与减掉雷后的格子数相等
  • test.c文件布局改为以下情况

    test.c
    void game()
    {
    //用于布置雷的二维数组
    char mine[ROWS][COLS] = { 0 };

    //用于游戏界面的的二维数组
    char show[ROWS][COLS] = { 0 };

    //用于游戏界面的的二维数组全部初始为 '*'
    set_keyboard(show, ROWS, COLS, '*');

    //用于布置雷的二维数组全部初始化为 '0'
    set_keyboard(mine, ROWS, COLS, '0');

    //随机布置雷
    set_mine(mine, ROW, COL, MINE);

    //打印函数
    printf_keyboard(show, ROW, COL);
    //printf_keyboard(mine, ROW, COL);

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

    game.c文件增加了以下代码

    game.c
    //计算周围雷的个数
    int Count_mine(char mine[ROWS][COLS], int x, int y)
    {
    return mine[x][y] '0';
    }

    //展开棋盘—-递归
    void Open_keyboard(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)
    {
    //写递归首先写结束条件

    //越界时,返回
    if ((x > (ROWS 2)) || (x < 1) || (y > (COLS 2)) || (y < 1))
    {
    return;
    }

    //遇到以及排过雷的坐标返回
    if (show[x][y] != '*')
    {
    return;
    }

    //计算雷的个数
    int count = 0;
    for (int i = 1; i <= 1; i++)
    {
    for (int j = 1; j <= 1; j++)
    {
    count += Count_mine(mine, x + i, y + j);
    }
    }
    show[x][y] = count + '0';

    //按照游戏规则,如果坐标显示雷的数目不为零,则返回
    if (show[x][y] != '0')
    {
    return;
    }

    //展开雷
    for (int i = 1; i <= 1; i++)
    {
    for (int j = 1; j <= 1; j++)
    {
    Open_keyboard(show, mine, x + i, y + j);
    }
    }

    }

    //排雷
    void move_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
    {
    int x, y;
    while (1)
    {
    printf("请输入要排查的坐标:");
    regain2:
    scanf("%d %d", &x, &y);
    if ((x >= 1 && x <= 9) && (y >= 1 && y <= 9))
    {
    if (show[x][y] == '*')
    {
    if (mine[x][y] == '0')
    {
    Open_keyboard(show,mine,x,y);
    printf_keyboard(show, ROW, COL);
    }
    else
    {
    printf("很遗憾,踩到雷了,游戏结束\\n以下是雷的位置:");
    printf_keyboard(mine, ROW, COL);
    break;
    }
    }
    else
    {
    printf("该坐标已经排查过了,请输入别的坐标:");
    goto regain2;
    }
    }
    else
    {
    printf("输入错误,重新输入:");
    goto regain2;
    }

    //判断赢
    int Remove_mine_count = 0;
    for (int i = 1; i <= row; i++)
    {
    for (int j = 1; j <= col; j++)
    {
    if (show[i][j] != '*')
    {
    Remove_mine_count++;
    }
    }
    }
    if (Remove_mine_count == ((ROW * COL) MINE))
    {
    printf("恭喜你,排除所有的雷,游戏胜利\\n");
    printf_keyboard(mine, ROW, COL);
    break;
    }
    }
    }

    game.h文件的代码不变

    game.h
    #pragma once
    #include <stdio.h>
    #include "game.h"
    #include <time.h>
    #include "stdlib.h"

    #define ROW 9
    #define COL 9
    #define ROWS ROW+3
    #define COLS COL+2
    #define MINE 10

    //菜单
    void menu();

    //初始化棋盘
    void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set);

    //展示棋盘
    void printf_keyboard(char board[ROWS][COLS], int row, int col);

    //随机布置雷
    void set_mine(char board[ROWS][COLS], int row, int col, int mine);

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

    五、扫雷游戏完整代码

    test.c

    #include "game.h" // 包含扫雷游戏所需的头文件(声明函数、宏定义等)

    // 游戏核心逻辑函数,负责初始化游戏数据、布置雷、处理排雷过程
    void game()
    {
    // 定义二维数组mine,用于存储雷的位置信息('1'表示有雷,'0'表示无雷)
    // ROWS和COLS是宏定义,通常比实际游戏区域大2(用于处理边界判断,避免越界)
    char mine[ROWS][COLS] = { 0 };

    // 定义二维数组show,用于展示给玩家的界面(初始为'*',排雷后显示周围雷数或雷)
    char show[ROWS][COLS] = { 0 };

    // 初始化show数组,全部元素设为'*'(表示未探索的格子)
    // set_keyboard是自定义函数,用于批量初始化二维数组
    set_keyboard(show, ROWS, COLS, '*');

    // 初始化mine数组,全部元素设为'0'(先默认所有格子无雷,后续再随机布置雷)
    set_keyboard(mine, ROWS, COLS, '0');

    // 在mine数组中随机布置雷,MINE是宏定义(雷的总数)
    // ROW和COL是宏定义,代表实际游戏区域的行数和列数(比ROWS、COLS小2)
    set_mine(mine, ROW, COL, MINE);

    // 打印玩家界面(show数组),展示当前未探索的格子(全为'*')
    printf_keyboard(show, ROW, COL);
    // 调试用:打印雷的位置(mine数组),实际游戏中不会显示给玩家
    // printf_keyboard(mine, ROW, COL);

    // 进入排雷逻辑,玩家输入坐标,处理排雷结果(显示周围雷数、踩雷结束等)
    move_mine(show, mine, ROW, COL);
    }

    // 主函数,程序入口,负责展示菜单和处理用户选择
    int main()
    {
    menu(); // 调用menu函数,打印游戏菜单(如"1. 开始游戏 0. 退出游戏")

    { // 局部作用域,隔离内部变量
    regain1: // 跳转标签,用于输入错误时重新回到输入步骤
    printf("请输入你的选择:"); // 提示用户输入选项(1或0)
    int input1; // 存储用户输入的选项
    scanf("%d", &input1); // 读取用户输入

    // 根据用户输入的选项执行对应操作
    switch (input1)
    {
    case 1: // 用户选择"开始游戏"
    {
    printf("进入游戏\\n"); // 提示进入游戏
    game(); // 调用game函数,启动扫雷游戏逻辑
    break; // 退出switch分支
    }
    case 0: // 用户选择"退出游戏"
    {
    printf("退出游戏\\n"); // 提示退出游戏
    break; // 退出switch分支
    }
    default: // 用户输入了无效选项(非1和0)
    {
    printf("输入错误,请重新输入:"); // 提示输入错误
    goto regain1; // 跳转到regain1标签,重新等待用户输入
    }
    }
    }

    return 0; // 程序正常结束
    }

    game.c

    #include "game.h" // 包含扫雷游戏所需的头文件(宏定义、函数声明等)

    // 打印游戏菜单
    void menu()
    {
    printf("****************\\n");
    printf("**** 1.Play ****\\n"); // 1表示开始游戏
    printf("**** 0.Quit ****\\n"); // 0表示退出游戏
    printf("****************\\n");
    }

    // 初始化棋盘(二维数组)
    // board:要初始化的二维数组
    // rows、cols:数组的行数和列数
    // set:初始化填充的字符(如'*'或'0')
    void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set)
    {
    // 遍历数组的每个元素,设置为指定字符set
    for (int i = 0; i < rows; i++)
    {
    for (int j = 0; j < cols; j++)
    {
    board[i][j] = set;
    }
    }
    }

    // 展示棋盘(打印到控制台)
    // board:要展示的二维数组(玩家界面或雷区)
    // row、col:实际游戏区域的行数和列数(不包含边界)
    void printf_keyboard(char board[ROWS][COLS], int row, int col)
    {
    printf("——-扫雷——–\\n");
    // 打印列号(0到col),方便玩家定位坐标
    for (int r = 0; r <= row; r++)
    {
    printf("%d ", r);
    }
    printf("\\n");

    // 打印每行内容(包含行号和对应格子的字符)
    for (int i = 1; i <= row; i++)
    {
    printf("%d ", i); // 打印行号(1到row)
    // 打印当前行的每个格子(从第1列到第col列)
    for (int j = 1; j <= col; j++)
    {
    printf("%c ", board[i][j]);
    }
    printf("\\n"); // 每行结束后换行
    }
    }

    // 随机布置雷到雷区数组
    // board:存储雷区信息的二维数组('1'表示有雷,'0'表示无雷)
    // row、col:实际游戏区域的行数和列数
    // mine:要布置的雷的总数
    void set_mine(char board[ROWS][COLS], int row, int col, int mine)
    {
    srand((unsigned int)time(NULL)); // 初始化随机数种子,确保每次雷的位置不同

    // 循环布置雷,直到雷的数量为0
    while (mine)
    {
    // 生成1到row范围内的随机行坐标x
    int x = (rand() % row) + 1;
    // 生成1到col范围内的随机列坐标y
    int y = (rand() % col) + 1;

    // 如果当前位置没有雷(为'0'),则布置雷(设为'1')
    if (board[x][y] == '0') // 注意:原代码此处多了一个分号,可能是笔误,实际应去掉
    {
    board[x][y] = '1';
    mine; // 雷的数量减1
    }
    }
    }

    // 计算指定坐标周围8个方向的雷的总数
    // mine:雷区数组
    // x、y:要检查的坐标
    int Count_mine(char mine[ROWS][COLS], int x, int y)
    {
    // 计算坐标(x,y)周围8个相邻格子中雷的总数
    // 原理:将周围8个格子的字符值('0'表示无雷,'1'表示有雷)转换为数字后求和
    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' // 减去8个'0'的ASCII值(将字符转为数字:'0'→0,'1'→1)
    );
    }

    // 递归展开无雷区域(当某个格子周围无雷时,自动展开周围所有无雷格子)
    // show:玩家界面数组
    // mine:雷区数组
    // x、y:当前要展开的坐标
    void Open_keyboard(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)
    {
    // 递归结束条件1:坐标越界(超出实际游戏区域)
    if ((x > (ROWS 2)) || (x < 1) || (y > (COLS 2)) || (y < 1))
    {
    return;
    }

    // 递归结束条件2:该坐标已被探索过(非'*')
    if (show[x][y] != '*')
    {
    return;
    }

    // 计算当前坐标周围8个方向的雷的总数
    int count = 0;
    for (int i = 1; i <= 1; i++) // 行方向:-1(上)、0(当前)、1(下)
    {
    for (int j = 1; j <= 1; j++) // 列方向:-1(左)、0(当前)、1(右)
    {
    count += Count_mine(mine, x + i, y + j); // 累加周围每个格子的雷数
    }
    }
    // 在玩家界面显示当前格子周围的雷数(字符形式,如'0'表示无雷)
    show[x][y] = count + '0';

    // 递归结束条件3:如果周围有雷(count≠0),则停止展开
    if (show[x][y] != '0')
    {
    return;
    }

    // 如果周围无雷(count=0),递归展开周围8个方向的格子
    for (int i = 1; i <= 1; i++)
    {
    for (int j = 1; j <= 1; j++)
    {
    Open_keyboard(show, mine, x + i, y + j);
    }
    }
    }

    // 排雷核心逻辑
    // show:玩家界面数组
    // mine:雷区数组
    // row、col:实际游戏区域的行数和列数
    void move_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col)
    {
    int x, y; // 存储玩家输入的排查坐标
    while (1) // 循环处理排雷操作,直到游戏结束
    {
    printf("请输入要排查的坐标:");
    regain2: // 跳转标签,用于输入错误时重新输入坐标
    scanf("%d %d", &x, &y); // 读取玩家输入的坐标(x为行,y为列)

    // 检查坐标是否在有效范围内(1到row行,1到col列)
    if ((x >= 1 && x <= 9) && (y >= 1 && y <= 9)) // 假设row=col=9,可改为x<=row && y<=col
    {
    // 检查该坐标是否未被排查过(仍为'*')
    if (show[x][y] == '*')
    {
    // 如果该位置无雷(mine[x][y]为'0')
    if (mine[x][y] == '0')
    {
    Open_keyboard(show, mine, x, y); // 展开周围无雷区域
    printf_keyboard(show, ROW, COL); // 刷新并显示玩家界面
    }
    // 如果该位置有雷(mine[x][y]为'1')
    else
    {
    printf("很遗憾,踩到雷了,游戏结束\\n以下是雷的位置:");
    printf_keyboard(mine, ROW, COL); // 显示所有雷的位置
    break; // 退出循环,游戏结束
    }
    }
    // 该坐标已被排查过
    else
    {
    printf("该坐标已经排查过了,请输入别的坐标:");
    goto regain2; // 跳转到regain2,重新输入坐标
    }
    }
    // 坐标输入无效(超出范围)
    else
    {
    printf("输入错误,重新输入:");
    goto regain2; // 跳转到regain2,重新输入坐标
    }

    // 判断玩家是否获胜(已排查所有非雷格子)
    int Remove_mine_count = 0; // 记录已排查的非雷格子数量
    for (int i = 1; i <= row; i++)
    {
    for (int j = 1; j <= col; j++)
    {
    if (show[i][j] != '*') // 非'*'表示已排查
    {
    Remove_mine_count++;
    }
    }
    }
    // 获胜条件:已排查的格子数 = 总格子数 – 雷的总数
    if (Remove_mine_count == ((ROW * COL) MINE))
    {
    printf("恭喜你,排除所有的雷,游戏胜利\\n");
    printf_keyboard(mine, ROW, COL); // 显示所有雷的位置
    break; // 退出循环,游戏结束
    }
    }
    }

    game.h

    #pragma once // 防止头文件被重复包含(只编译一次)

    // 包含所需的标准库头文件
    #include <stdio.h> // 提供输入输出函数(如printf、scanf)
    #include "game.h" // 包含游戏相关的其他声明(注意:此处可能存在循环包含,实际应避免)
    #include <time.h> // 提供时间相关函数(如time,用于初始化随机数种子)
    #include "stdlib.h" // 提供标准库函数(如rand、srand,用于生成随机数)

    // 宏定义:游戏核心参数
    #define ROW 9 // 实际游戏区域的行数(9行)
    #define COL 9 // 实际游戏区域的列数(9列)
    #define ROWS ROW+3 // 雷区数组的总行数(比实际行数多3,用于处理边界,避免越界访问)
    #define COLS COL+2 // 雷区数组的总列数(比实际列数多2,用于处理边界)
    #define MINE 10 // 游戏中雷的总数(10个)

    // 函数声明:声明游戏中用到的所有函数(供其他文件调用)

    // 打印游戏菜单(如开始/退出选项)
    void menu();

    // 初始化棋盘数组
    // 参数:board-要初始化的二维数组,rows-数组行数,cols-数组列数,set-初始化填充的字符
    void set_keyboard(char board[ROWS][COLS], int rows, int cols, char set);

    // 打印展示棋盘(玩家界面或雷区)
    // 参数:board-要展示的二维数组,row-实际游戏区域行数,col-实际游戏区域列数
    void printf_keyboard(char board[ROWS][COLS], int row, int col);

    // 在雷区数组中随机布置雷
    // 参数:board-雷区数组,row-实际游戏区域行数,col-实际游戏区域列数,mine-要布置的雷数
    void set_mine(char board[ROWS][COLS], int row, int col, int mine);

    // 处理玩家的排雷操作(核心游戏逻辑)
    // 参数:show-玩家界面数组,mine-雷区数组,row-实际游戏区域行数,col-实际游戏区域列数
    void move_mine(char show[ROWS][COLS], char mine[ROWS][COLS], int row, int col);

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

    评论 抢沙发

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