《C语言实现扫雷游戏:代码解析与博客分享》

引言

扫雷游戏作为一款经典的益智小游戏,承载了许多人的回忆。它不仅考验玩家的逻辑思维和推理能力,还充满了趣味性和挑战性。在这篇博客中,我们将详细探讨如何使用C语言实现一个简单的扫雷游戏,深入分析代码的各个部分,帮助你理解其中的原理和实现思路。

项目整体架构

本扫雷游戏项目采用模块化编程的思想,将不同的功能封装到不同的文件中,这样可以提高代码的可读性、可维护性和可扩展性。整个项目主要由三个文件组成:

  1. game.h:头文件,用于声明函数和定义常量。
  2. game.c:实现游戏中各个功能的函数。
  3. mytext.c:主程序文件,包含菜单和游戏流程的控制。

1. game.h 文件

#pragma once

#include 
#include 
#include 

#define ROW 9
#define COL 9

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

#define EASY_COUNT 10


//棋盘初始化的函数
void InitBoard(char arr[ROWS][COLS], int rows, int cols, char set);

//打印棋盘
void DisplayBoard(char arr[ROWS][COLS], int row, int col);

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

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

在这个头文件中,我们做了以下几件重要的事情:

  • 引入了必要的标准库头文件,如 stdio.h 用于输入输出操作,stdlib.h 用于动态内存分配和随机数生成,time.h 用于获取当前时间。
  • 定义了棋盘的基本参数,包括实际棋盘的行数 ROW 和列数 COL,以及扩展棋盘的行数 ROWS 和列数 COLS。扩展棋盘的目的是为了方便处理边界情况,避免越界访问。
  • 定义了简单难度下的雷的数量 EASY_COUNT
  • 声明了游戏中需要用到的四个函数,分别是初始化棋盘、打印棋盘、布置雷和排查雷。

2. game.c 文件

#include "game.h"

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

//打印棋盘
void DisplayBoard(char arr[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++)
    {
        int j = 0;
        //打印行号
        printf("%d ",i);
        for(j = 1; j <= col; j++)
        {
            printf("%c ",arr[i][j]);
        }
        printf("\n");
    }
}

//布置雷
void SetMine(char arr[ROWS][COLS], int row, int col)
{
    int  count = EASY_COUNT;
    while(count)
    {
        int x = rand() % row + 1;
        int y = rand() % col + 1;

        if(arr[x][y] == '0')
        {
            arr[x][y] = '1';
            count--;
        }
    }
}

//统计某坐标周围雷数量
static int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
    int i = 0;
    int count = 0;
    for (i = x - 1; i <= x + 1; i++)
    {
        int j = 0;
        for (j = y - 1; j <= y + 1; j++)
        {
            count += (mine[i][j] - '0');
        }
    }
    return count;
}

//排查雷
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 >= 0 && x <= row && y >= 0 && y <= col)
        {
            if(show[x][y] == '*')
            {
                if(mine[x][y] == '1' )
                {
                    printf("你被炸死了 !\n");
                    DisplayBoard(show, ROW, COL);
                    break;
                }
                else
                {
                    //该坐标不是雷,就得统计坐标周围有几个雷
                    int count = GetMineCount(mine, x, y);
                    show[x][y] = count + '0';
                    DisplayBoard(show, ROW, COL);
                    win++;
                }
            }
            else
            {
                printf("该坐标已经被排查了,请重新输入坐标\n");
            }
        }
        else
        {
            printf("坐标非法,请重新输入 !\n");
        }
    }

    if (win == row * col - EASY_COUNT)
    {
        printf("恭喜你,排雷成功\n");
        DisplayBoard(mine, ROW, COL);
    }
}

这个文件实现了游戏中各个核心功能的函数:

  • InitBoard 函数:该函数用于初始化棋盘,通过两层嵌套的 for 循环遍历棋盘的每一个元素,并将其赋值为指定的字符 set。这样可以方便地将棋盘初始化为不同的状态,例如将 mine 数组初始化为 '0' 表示没有雷,将 show 数组初始化为 '*' 表示未排查状态。
  • DisplayBoard 函数:此函数用于打印棋盘,首先打印列号,然后逐行打印棋盘的每一行,并在每行的开头打印行号。这样可以让玩家清楚地知道每个格子的坐标,方便输入排查的坐标。
  • SetMine 函数:该函数用于在棋盘上随机布置雷,使用 rand() 函数生成随机的坐标 (x, y),并检查该位置是否已经有雷。如果没有雷,则将该位置标记为雷('1'),并减少剩余需要布置的雷的数量 count,直到所有的雷都布置完毕。
  • GetMineCount 函数:这是一个静态函数,用于统计指定坐标周围的雷的数量。通过两层嵌套的 for 循环遍历指定坐标周围的 8 个格子(包括自身),将每个格子的值减去 '0' 转换为整数并累加到 count 中,最后返回 count
  • FindMine 函数:该函数是游戏的核心逻辑,负责玩家的排查操作。通过一个 while 循环不断获取玩家输入的坐标,检查坐标的合法性和该位置是否已经被排查过。如果排查到雷,则游戏失败;如果排查的位置不是雷,则统计周围雷的数量并更新 show 数组,同时增加已排查的非雷格子数量 win。当 win 等于非雷格子的总数时,游戏胜利。

3. mytext.c 文件

#include "game.h"

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

void game()
{
    char mine[ROWS][COLS] = {0};  //将arr1初始化为 '0'
    char show[ROWS][COLS] = {0};  //将arr2初始化为 '*'

    InitBoard(mine, ROWS, COLS, '0');
    InitBoard(show, ROWS, COLS, '*');

    SetMine(mine, ROW, COL);
    //DisplayBoard(mine, ROW, COL);

    //打印棋盘
    DisplayBoard(show, ROW, COL);

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

void test()
{
    int input;
    srand((unsigned int)time(NULL));

    do{
        menue();
        printf("请选择:> ");
        scanf("%d", &input);
        switch(input)
        {
        case 1:
            game();
            break;
        case 0:
            printf("游戏结束,退出游戏");
            break;
        default:
            printf("选择错误,请重新选择 !");
            break;
        }
    }while(input);
}

int main()
{
    test();
    return 0;
}

这个文件是主程序文件,主要负责游戏的菜单显示和流程控制:

  • menue 函数:用于显示游戏菜单,提供两个选项:开始游戏(输入 1)和退出游戏(输入 0)。
  • game 函数:该函数是游戏的入口,负责初始化 mineshow 数组,布置雷,打印初始棋盘,并调用 FindMine 函数开始排查雷。
  • test 函数:使用 do-while 循环不断显示菜单,获取玩家的选择,并根据选择执行相应的操作。使用 srand((unsigned int)time(NULL)) 初始化随机数种子,确保每次游戏的雷的布置都是随机的。
  • main 函数:调用 test 函数,启动游戏。

总结

通过以上三个文件的协同工作,我们成功实现了一个简单的扫雷游戏。模块化编程的思想使得代码结构清晰,易于理解和维护。在实现过程中,我们使用了数组、循环、条件判断、函数等C语言的基本知识点,同时还涉及到随机数生成和字符处理等技巧。

你可以将上述代码复制到你的C语言开发环境中编译运行,体验自己实现的扫雷游戏。如果你对代码有任何疑问或者想要进一步扩展功能,欢迎在评论区留言讨论。希望这篇博客对你有所帮助!

你可能感兴趣的:(算法,c语言,开发语言,开源)