扫雷游戏

运行环境以Dev-C++、Visual Studio 2022、MacOS的命令行和Xcode为主

1.扫雷游戏分析和设计

  • 1.1 功能说明

    • 在控制台通过菜单选项(继续 or 结束)完成扫雷

    • 扫雷区域为 8 * 8,默认布置雷的个数为 10

    • 排查雷区的逻辑

      • 如果当前位置恰好是雷区,表示踩雷,结束游戏

      • 如果当前位置不是雷区,则显示该位置外围绕一圈的 8 个位置中雷的数目

      • 如果所有非雷区的位置都找到了,则排雷成功,结束游戏

  • 1.2 游戏界面(以 4 * 4雷区,3 个雷为例)

    • 初始界面

    image

    • 排雷界面

    image

    • 踩雷界面

    image

    • 成功界面

    image

    image

  • 1.3 数据结构分析

    • 扫雷过程中,雷区的布置和排查掉的雷区信息(即哪个位置没有埋雷)均需要存储。由于扫雷区域为 8 * 8,比较容易想到 8 * 8的二维数组,如下图。假设设雷位置存储 1,非雷位置存储 0

    image

    • 以排查位置 (3, 4) 是否有雷为例,遍历它周围一圈的蓝色区域,统计周围雷的个数为2

    image

    • 以排查位置 (8, 3) 是否有雷为例,依然遍历它周围一圈的蓝色区域,统计周围雷的个数。此时发现最下方的3个区域已越界,若其中存在随机数,会对雷数统计产生影响

    image

    • 为此,将原有扫雷区域增加 2 行 2 列,扩为 ````10 * 10```,最外圈(绿底区域)都初始化为0但不计入布雷圈

    image

    • 再回到位置 (3, 4) ,它周边雷的个数 2 应当被记录下来,作为后续排雷的参考。倘若也记录在"雷区信息数组"中,雷的布置信息0 与 1与雷的个数信息0 1 2 ...容易混淆

    • 综上,使用 10 * 10数组mine来存储雷区信息的同时,再引入一个 10 * 10数组show用于存储排查出的雷的信息。如上图中mine[4][5] = 0,show[4][5] = 2;

    char min[10][10] = {0};  // 存储雷的信息
    char show[10][10] = {0};  // 存储排查出的非雷位置周围的雷的个数信息
    

    image

  • 1.4 文件结构分析

main.c  // 主函数,游戏的测试逻辑
game.c  // 游戏功能的实现
game.h  // 头文件,游戏需要的宏定义、数据类型与函数声明等

2.扫雷游戏代码实现

// 1.game.h

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW 8
#define COL 8

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

#define EASY_LEVEL 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 mine[ROWS][COLS], int row, int col);

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

#include "game.h"

// 初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set) {
    int i = 0, 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, j = 0;

    printf("-------Dig Mines-------\n");
    for (i = 0; i <= col; i++) {  // 打印列标
        printf("%d ", i);
    }
    printf("\n");

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

// 布置雷区,本质上就是从mine数组中随机选EASY_LEVEL个位置,将此处的值由0修改为1
void SetMine(char mine[ROWS][COLS], int row, int col) {
    int x = 0, y = 0;
    int count = EASY_LEVEL;

    while (count) {
        x = rand() % row + 1;  // 随机生成行标
        y = rand() % col + 1;  // 随机生成列标

        if (mine[x][y] != '1') {
            mine[x][y] = '1';  // 布置一个雷
            count--;
        }
    }
}

// 计算(x, y)周围的8个位置值的总和
int GetMineCount(char mine[ROWS][COLS], int x, int y) {
    int i = 0, j = 0;
    int count = 0;
    for (i = -1; i <= 1; i++) {
        for (j = -1; j <= 1; j++) {
            count += (mine[x + i][y + j] - '0');
        }
    }
    return count;
}

// 排查雷区,由用户输入合法的(x, y),读取mine中x行y列的数据,若为1则踩雷;否则统计x行y列四周8个位置的数据
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col) {
    int x = 0, y = 0;
    int count = 0;  // 某位置四周雷的个数
    int dst = 0;  // 非雷的个数,即成功排查的非雷的位置
    
    // 当 "成功排查的非雷位置累计数量 < 雷区位置总数 - 雷的个数" 时循环继续
    while (dst < row * col - EASY_LEVEL) {
        printf("输入 x(1~%d) 和 y(1~%d):", row, col);
        scanf("%d %d", &x, &y);

        if ((x >= 1 && x <= row) && (y >= 1 && y <= col)) {
            if (mine[x][y] == '1') {
                printf("踩雷了...Boom!\n");
                printf("雷区信息如下:\n");
                DisplayBoard(mine, row, col);
                break;
            }
            else {
                count = GetMineCount(mine, x, y);
                show[x][y] = count + '0';
                dst++;
                printf("Dig Mine info be like:\n");
                DisplayBoard(show, row, col);
            }
        }
        else {
            printf("illegal input!\n");
        }
    }
    
    if (dst == row * col - EASY_LEVEL) {
        printf("恭喜你,排雷成功!\n");
        DisplayBoard(mine, row, col);
    }
}
// main.c

#include "game.h"

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

void game(void) {
    char mine[ROWS][COLS] = { 0 };  // 存放棋盘中设置的雷的信息
    char show[ROWS][COLS] = { 0 };  // 存放从棋盘中排查出的雷的信息

    // 初始化棋盘,mine最初全为0,show最初全为*,行列均为ROWS和COLS
    InitBoard(mine, ROWS, COLS, '0');
    InitBoard(show, ROWS, COLS, '*');

    // 打印棋盘,行列为ROW和COL,鉴于扫雷期间要统计某位置四周8个位置雷的数量,
    // 为防止最上下左右行列的位置越界,因此空出了最上行、最下行和最左列、最右列
    DisplayBoard(show, ROW, COL);
    //DisplayBoard(mine, ROW, COL);  // 有雷区安排的棋盘不用打印

    // 布置雷区
    SetMine(mine, ROW, COL);
    //DisplayBoard(mine, ROW, COL);

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

void test(void) {
    int input = 0;
    srand((unsigned int)time(NULL));  // 以当前时间作为种子,生成不同的随机数

    do {
        menu();
        printf("请选择(1 or 0):");
        scanf("%d", &input);
        switch (input) {
            case 1:
                game();  // 扫雷的全部逻辑
                break;
            case 0:
                printf("结束游戏\n");
                break;
            default:
                printf("输入错误!\n");
                break;
        }

    } while (input);
}


int main(int argc, const char * argv[]) {
    // insert code here...
    test();
    return 0;
}
posted @ 2025-07-13 09:00  pycoder_666  阅读(92)  评论(0)    收藏  举报