C语言扫雷小游戏

199 阅读10分钟

前言

想必大家都玩过扫雷小游戏,博主也是从小就开始玩扫雷小游戏的,毕竟在很久以前,科技不怎么发达的时候,有一款游戏就已经是非常令人心动的了。

如果读者没有玩过扫雷小游戏,博主这边也非常热情的推荐一个游戏网址:扫雷游戏网页版 - Minesweeper供读者体验。

完整代码自提看本文的最后。

游戏设计

那么我们的正篇正式开始,首先我们先联想一下,扫雷游戏需要一个9x9的方格供我们进行排查,那么我们需要使用一个二维数组进行存放,考虑到展示的数组与真实雷所在的数组不同,我们这边需要使用两个数组进行存放,一个用于存放雷的信息,一个用于存放排查出的信息。

微信截图_20250519213425.png

考虑在排查雷的过程中,我们需要计算所选择位置的周围8个位置的雷的数目,并且我们还需要考虑到边界值的情况,那么为了方便计算,我们统一将二维数组的长度设置为11x11。

并且考虑到难度选择上,我们将雷的数目设为简单10,中等20,困难30。

我们这里使用预处理指令#define将这些固定数字进行预处理。

// 本文所需要引用的库
#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
#define MEDIUM_COUNT 20
#define DIFFICULTY_COUNT 30

入口函数

然后我们进行主函数main进行设置,main函数主要是一个入口函数,我们只需将进入游戏以及选择难度编写进main函数即可,其余功能统一抽象成方法。

int main() {
    // 选择是否游戏
    int input = 0;
    // 选择难度
    int level = 0;
    // 设置随机值
    srand((unsigned int)time(NULL));

    // 循环游戏
    do {
        printf("*******************\n");
        printf("***** 1. play *****\n");
        printf("***** 0. exit *****\n");
        printf("*******************\n");
        printf("请选择游戏:>\n");
        scanf("%d", &input);
        // 判断是否进行游戏
        switch (input) {
            case 1:
                printf("请选择难度(1简单 2中等 3困难):>\n");
                scanf("%d", &level);
                // 当难度不符合预设值的时候,提示用户重新选择
                if (level < 1 || level > 3) {
                    printf("选择错误,请重新选择\n");
                } else {
                    // 当进入游戏并且选择难度成功后,正式进入游戏
                    game(level);
                    break;
                }
            case 0:
                printf("结束游戏\n");
                break;
            default:
                printf("选择错误,请重新选择\n");
                break;
        } while (input);
    return 0;
}

微信截图_20250520163137.png

微信截图_20250520163147.png

棋盘初始化

当我们完成游戏的入口后,便要开始正式进入游戏功能的编写了。

首先,我们进入游戏后,屏幕上应该展示出的是一个9x9的正方形棋盘,并且展示的是未知数,毕竟在我们进行选择之前,我们是不知道这个位置是否是雷。那么我们首先需要一个数组进行展示,我们为其进行创建char show[ROWS][COLS],以及我们还需要一个数组,用于存放真实雷的信息,我们也对其进行创建char mine[ROWS][COLS]

那么我们接下来需要对棋盘进行初始化,用于存放真实数据,对于展示的棋盘,我们统一使用*来展示,保留出一点的神秘感哈哈。而真实存放雷的数组呢,我们使用0进行存放,后续在雷的位置上,我们使用1来表示雷,这样可以方便我们计算雷的数目。

// 初始化数组代码
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;
        }
    }
}

// 初始化棋盘
InitBoard(mine, ROWS, COLS, '0');
InitBoard(show, ROWS, COLS, '*');

展示棋盘

对数组进行设置成功后,我们需要将数组展示出来查看,这里便是简单的两次循环即可,我们需要注意的一点是我们只需要将棋盘的中间9x9展示出来即可,其余位置并不需要展示。而为了方便后续对于雷位置的选择,我们需要在上边栏和左边栏输出当前的行列号。

// 展示数组
void DisplayBoard(char board[ROWS][COLS], int row, int col) {
    int i = 0;
    printf("-------扫雷-------\n");
    for (i = 0; i <= col; 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");
    }
}

微信截图_20250520173102.png

那么棋盘初始化成功,我们便要根据难度对存放雷的数组进行随机存放雷,我们首先,根据我们上述进入游戏时选择的游戏难度123进行判断,判断返回所存放雷的数目,只需要简单的if语句或者switch语句判断即可。

// 根据选择难度返回雷的数目
int SelectLevel(int level) {
    if (level == 1) {
        return EASY_COUNT;
    } else if(level == 2) {
        return MEDIUM_COUNT;
    } else {
        return DIFFICULTY_COUNT;
    }
}

布置雷

成功确认雷的数目后,我们就需要向存放雷的数组中进行存放雷,我们这里使用坐标来进行存放雷,并且数组长宽为11x11,因此我们所存放雷的位置也就是1-9。那么接下来,我们可以使用到rand()函数产生随机数来确定雷所存放的坐标,并且我们在存放之前,需要判断该位置是否已经存放了雷,如果没来,我们便将该位置设为1,用于表示雷,并且将雷的总数-1即可。

// 随机在棋盘坐标上布置雷
void SetMine(char mine[ROWS][COLS], int count) {
    int x = 0;
    int y = 0;
    while (count) {
        x = rand() % row + 1;
        y = rand() % col + 1;
        if (mine[x][y] != '1') {
            mine[x][y] = '1'; // 布置一个雷
            count--;
        }
    }
}

排查雷

我们将雷设置成功后,便要开始我们的正戏了,那便是排查雷。排查雷是一个重复的过程,我们需要不断去判断是否踩到雷,这里直接使用死循环即可,如果踩到雷或者将所有非雷排查成功,便可以结束循环,告诉玩家游戏成功或失败。

首先我们需要提示玩家输入所要排查的坐标,因为我们的棋盘所展示的是中间的9x9,因此玩家在输入坐标的范围也要控制在1-9的范围内,如果越界,便提示用户越界,并将坐标范围重新输出来提示玩家。如果输入坐标符合范围,那么我们就需要进行判断该位置是否是雷,如果是雷,那么恭喜玩家,游戏失败,游戏失败后我们便需要将该棋盘展示,让玩家输的明明白白,此后便结束死循环;如果该坐标不是雷,我们便统计该坐标周围雷的数目,由于我们在初始化棋盘的时候是将所有位置使用0来表示,而存放雷的位置使用1来表示,所以我们这里判断也是非常的easy,只需要将排查坐标周围的数相加即可,考虑到我们使用的是字符数,那么我们相加后-8*0即可统计出雷的总数。当我们统计出雷的总数后,我们便需要将展示的棋盘上该坐标的位置用周围雷的数目来填充,然后将该棋盘打印出来告诉玩家。

最后,当所排查的坐标数目=81-雷的数目时,游戏便成功了,此时也需要提示玩家游戏成功,并且跳出死循环。

// 统计周围雷的数目
int GetMineCount(char mine[ROWS][COLS], int x, int y) {
    int count = 0;
    for (int i = -1; i <= 1; i++) {
        for (int j = -1; j <= 1; j++) {
            count += mine[x + i][y + j] - '0';
        }
    }
    return count;
}
// 排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int level) {
    int x = 0;
    int y = 0;
    int count = 0;
    int win = 0;
    while (1) {
        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++;
            }
            if (win == 81 - level) {
                printf("踩雷成功,游戏结束\n");
                break;
            }
        } else {
            printf("输入坐标越界,请重新输入,x取值范围(1-9),y取值范围(1-9)");
        }
    }
}

微信截图_20250520173344.png

总结

到此为止,我们的小游戏便正式结束啦,其实该游戏还是有很多可以改进的地方,例如可以根据难度调整棋盘大小(由于博主使用的是Visual Studio,不支持变长数组,便没有展示出来),以及选择排查坐标后,根据当前坐标周围雷的数目是否为0,如果为0,则会将周围坐标同样为0的情况下一起展示出来,这里使用到了递归算法,这一功能还是非常值得尝试的,博主实力不济,便不献丑了。同样的,也可以将游戏时间,游戏步数,以及可能是雷的位置使用一个特殊符合标记,这些都是可以完成的额外功能,有待读者自行完成哦~~

那么最后,博主也将完整代码展示在最后啦。

完整代码展示

main.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

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

void game(int level) {
    char mine[ROWS][COLS]; // 存放雷的信息
    char show[ROWS][COLS]; // 存放排查出的雷的信息

    // 初始化棋盘
    InitBoard(mine, ROWS, COLS, '0');
    InitBoard(show, ROWS, COLS, '*');

    // 打印棋盘 (检验所用
    //DisplayBoard(show, ROW, COL);
    //DisplayBoard(mine, ROW, COL);

    // 选择难度
    int count = SelectLevel(level);

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

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

}

int main() {
    int input = 0;
    int level = 0;
    srand((unsigned int)time(NULL));

    do {
        menu();
        printf("请选择游戏:>\n");
        scanf("%d", &input);
        switch (input) {
            case 1:
                printf("请选择难度(1简单 2中等 3困难):>\n");
                scanf("%d", &level);
                if (level < 1 || level > 3) {
                    printf("选择错误,请重新选择\n");
                } else {
                    game(level);
                    break;
                }
            case 0:
                printf("结束游戏\n");
                break;
            default:
                printf("选择错误,请重新选择\n");
                break;
         }
     } while (input);
    return 0;
}

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
#define MEDIUM_COUNT 20
#define DIFFICULTY_COUNT 30


//初始化棋盘
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, int level);

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

// 选择难度
int SelectLevel(int level);

game.c文件

#define _CRT_SECURE_NO_WARNINGS 1

#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 <= col; 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");
    }
}

int SelectLevel(int level) {
    if (level == 1) {
        return EASY_COUNT;
    } else if(level == 2) {
        return MEDIUM_COUNT;
    } else {
        return DIFFICULTY_COUNT;
    }
}

// 随机在棋盘坐标上布置雷
void SetMine(char mine[ROWS][COLS], int row, int col, int count) {
    int x = 0;
    int y = 0;
    while (count) {
        x = rand() % row + 1;
        y = rand() % col + 1;
		
        if (mine[x][y] != '1') {
            mine[x][y] = '1'; // 布置一个雷
            count--;
        }
    }
}

int GetMineCount(char mine[ROWS][COLS], int x, int y) {
    int count = 0;
    for (int i = -1; i <= 1; i++) {
        for (int j = -1; j <= 1; j++) {
            count += mine[x + i][y + j] - '0';
        }
    }
    return count;
}

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int level) {
    int x = 0;
    int y = 0;
    int count = 0;
    int win = 0;

    while (1) {
        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++;
            }
            if (win == row * col - level) {
                printf("踩雷成功,游戏结束\n");
                break;
            }
        } else {
            printf("输入坐标越界,请重新输入,x取值范围(1-%d),y取值范围(1-%d)", row, col);
        }
    }
}