c语言实现扫雷(详细讲解)

本篇介绍,讲解如何使用c语言实现扫雷小游戏.

金句分享:

✨✨✨爱你所爱,行你所行✨✨✨

目录

前言:

游戏规则:
我们随便点一个格子,方格即被打开并显示出方格中的数字,方格中数字则表示其周围的8个方格隐藏雷的数目.根据数字,排查出所有的雷即为游戏成功,当点击到有雷的格子时,会被炸死,游戏失败.

一、游戏设计思路介绍:

  1. 设置游戏的菜单(自由设计):
  2. 游戏函数的创建:
  3. 创建雷盘
  4. 初始化雷盘
  5. 打印雷盘
  6. 模式选择:(用于确定雷的个数)
  7. 布置雷
  8. 排查雷
  9. 自动递归循环排雷
  10. 判断输赢

效果展示

c语言实现扫雷(详细讲解)

; 二、游戏的分步讲解

2.1、主函数测试区(test.c)基本构成

主函数测试区的作用是.设计菜单,和game函数的调用.

菜单可自由设计,牛牛就不过多介绍了.

主要介绍一下,game函数的实现:
通过调用各函数来实现游戏的总体结构,具体函数的实现放在game.c文件中.主要作用是完成游戏的总体框架.合理的调用相应的函数.

void game()
{

    char secret[ROWS][COLS] = { 0 };
    char show[ROWS][COLS] = { 0 };

    initboard(secret, ROWS, COLS,'0');
    initboard(show, ROWS, COLS,'*');

    printboard(show, ROW, COL);

    int num = c_pattern();
    setmine(secret, ROW, COL,num);

    findmine(secret, show, ROW, COL,num);

}

2.2、游戏中函数实现区(game.c) (重点)

2.21、雷盘的创建与初始化函数

如果只有一个雷盘,那么该雷盘既要保存雷的信息,又不能显示给玩家看雷的位置.这边不能很好的进行初始化雷盘.所以我们需要创建两个雷盘:
1.” 秘密雷盘“:布置雷的雷盘(只给牛牛自己看的)
2.” 展示雷盘“:玩家所看到的雷盘

问题:1
了解扫雷规则的小伙伴知道,当我们输入一个坐标的时候,该坐标就会显示出统计的周围八个坐标雷的个数.所以在创建雷盘的时候会遇到一个问题,玩家在排查雷盘的边角坐标时,周围八个坐标的位置很有可能会越界.

解决方法:
我们可以创建一个更大的数组,比如,当我们需要9×9的数组时,我们创建一个11×11的数组.这样就可以防止越界访问,

越界情况: _ _ _ _ _ _ _ _ _ _ _ _ _ _解决方法:

c语言实现扫雷(详细讲解)

雷盘的创建:

ROWS是一个宏定义的值,在函数声明区中定义.暂时可以理解为数值11.


    char secret[ROWS][COLS] = { 0 };
    char show[ROWS][COLS] = { 0 };

雷盘的初始化:
雷盘创建好之后,我们怎样为棋盘进行合理的初始化呢?

秘密雷盘“的初始化:
我们用’字符1’表示雷的坐标
字符’0’表示不是雷的坐标.

至于为什么用字符’0’和字符’1′,后面会妙用.

“‘ 展示雷盘“的初始化:
为了有神秘感,又不能让玩家看见雷的坐标,我们可以全部初始化为’ * ‘(字符星号).

    initboard(secret, ROWS, COLS,'0');
    initboard(show, ROWS, COLS,'*');

void initboard(char board[ROWS][COLS], int rows, int cols, char ret)
{

    int i = 0,  j = 0;
    for (i = 0; i < rows; i++)
    {
        for (j = 0; j < cols; j++)
        {
            board[i][j] = ret;
        }
    }
}

2.22、雷盘的打印函数

以9×9大小的雷盘为例子.

重点在于,函数接收的数组大小为11×11,但是我们只需要使用其中中间的9×9雷盘,所以在打印雷盘时,打印坐标的起始值为1而并非0,刚好又符号玩家的坐标需要(非程序员认为是从1开始).

简易雷盘的打印:

void printboard(char board[ROWS][COLS], int row, int col)
{
    printf("------扫雷游戏------\n");
    int i = 0, j = 0;
    for (i = 0; i  row;i++)
    {
        printf("%2d", i);
    }
    printf("\n");
    for (i = 1; i  row; i++)
    {
        printf("%2d", i);
        for (j = 1; j  col; j++)
        {
            printf("%2c",board[i][j]);
        }
        printf("\n");
    }
    printf("------扫雷游戏------\n");
    printf("\n");
}

效果图:

c语言实现扫雷(详细讲解)

美观雷盘的打印:
与前面三子棋打印方法类似.

牛牛都留好注释了,没看懂备注的,可以点这里,有分步骤教学讲解:

三子棋棋盘打印


void printboard(char board[ROWS][COLS], int row, int col)
{
    printf("-----------------扫雷游戏-------------\n");
    int i = 0, j = 0;
    printf(" ");

    for (i = 1; i  row; i++)
    {
        printf("%3d ", i);
    }
    printf("\n");
    printf("  +");
    for (i = 0; i < row; i++)
    {
        printf("---+");
    }
    printf("\n");

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

        printf("  +");
        for (j = 0; j < col; j++)
        {
            printf("---+");
        }
        printf("\n");
    }
    printf("-----------------扫雷游戏-------------\n");
}

效果图:

c语言实现扫雷(详细讲解)

2.23、模式选择函数

为了让玩家可以控制难度,牛牛设置了一个 难度选择函数,根据玩家的选择来设置相应的雷的数量.

此函数重点在于,要使用 getchar()函数将缓存区的清除,否则影响下面的难度选择的输入.(牛牛当时疏忽了,找了好久才找到原因,缓存区有一个换行符被直接读取给了scanf(“%c”, &pattern);😭😭😭)


int c_pattern()
{
    int num = 0;
again:
    printf("欢迎玩家进入游戏:\n");
    printf("请新选择难度:(num代表雷的数量)\n");
    printf("A.简单模式:num=5    B.中等模式:num=15   C.困难模式:num=30   D.自定义难度(自由输入雷的个数)\n ");
    char pattern = 0;
    getchar();
    scanf("%c", &pattern);
    switch (pattern)
    {
    case 'A':
    case 'a':
        printf("简单模式:num=5\n");
        num = 5;
        return num;
    case 'B':
    case 'b':
        printf("中等模式:num=10\n");
        num = 15;
        return num;
    case 'C':
    case 'c':
        printf("困难模式:num=15\n");
        num = 30;
        return num;
    case 'D':
    case 'd':
        printf("自定义难度:");
        printf("请输入布置雷的个数:\n");
        getchar();
        int intput = 0;
        scanf("%d", &intput);
        num = intput;
        return num;
    default:
        printf("不好意思,牛牛还没有开发此模式,请重新选择:\n\n");
        goto again;
        break;
    }
}

2.24、布置雷函数

布置雷的逻辑与三子棋的电脑落子逻辑上是一样的.

通过生成两个随机数,将其作为坐标,修改(秘密棋盘)该坐标的值为’1′(表示雷).


void setmine(char board[ROWS][COLS], int row, int col,int num)
{
    int x = 0, y = 0;
    int count = 0;
    for (count = 0; count < num; )
    {
        x = 1 + rand() % row;
        y = 1 + rand() % col;
        if (board[x][y] == '0')
        {
            board[x][y] = '1';
            count++;
        }
    }
}

2.25、排查雷函数

让玩家输入要排查雷的坐标,先判断坐标的合法性,是否越界.

如果坐标合法, 统计该坐标周围八个坐标有多少个雷.

如果该坐标周围没有雷,就将该坐标设置为空格,并 递归排查周围八个坐标的值.

如果该坐标是雷,则游戏结束.

每次排查一个坐标后, 判断玩家是否取得胜利.


void findmine(char secret[ROWS][COLS], char show[ROWS][COLS], int row, int col,int num)
{
    int x = 0, y = 0;
    int win = 0;
    while (win < (row * col -num))
    {
        printf("请输入排查雷的坐标:\n格式为:行号 列标\n");
        scanf("%d%d", &x, &y);
        if (show[x][y] != '*')
        {
            printf("该坐标已经被排查过了.请重新输入:");
            continue;
        }
        if (x >= 1 && x  row && y >= 1 && y  col)
        {
            if (secret[x][y] == '1')
            {
                printf("很遗憾,你失败了\n");
                printf("请看答案:\n");
                printboard(secret, ROW, COL);
                printf("很遗憾,你失败了\n上面是答案:\n");
                break;
            }
            else
            {

                digui(secret, show, ROW, COL,x,y,&win);
                win=is_win(show, ROW, COL);
                printboard(show, ROW, COL);
            }
        }
        else
        {

            printf("坐标非法,请重新输入:\n");
        }
    }
    if (win == (row * col - num))
    {
        printf("恭喜你排雷成功\n");
        printf("牛牛为你点赞!!!\n");
    }
}

2.26、统计坐标周围雷的个数函数

c语言实现扫雷(详细讲解)

由于是存放的都是字符,所以计算结果-8×’0′,得到数值.


int countmine(char secret[ROWS][COLS], int x, int y)
{
    int ret = secret[x - 1][y - 1]+ secret[x - 1][y] + secret[x - 1][y + 1]
        + secret[x][y - 1] + secret[x][y + 1]
        + secret[x + 1][y - 1] + secret[x + 1][y] + secret[x + 1][y + 1] - 8 * '0';
    return ret;
}

2.27、自动递归排雷函数

如果一次只能排查一个坐标,那这游戏是不是太难了?

我们可以通过递归的方式,从这个坐标的周围八个坐标展开,进行排雷.

当然使用递归的时候一定要记住,要有限制条件,否则就会死循环调用,直到栈空间耗尽.

这里,我们执行递归的条件是:
1.周围没有雷,否则显示雷的个数,不进入递归.

2.此坐标并没有被排查过(状态是:’ * ‘).


digui(char secret[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{

    if (x >= 1 && x  row && y >= 1 && y  col)
    {
        int count = countmine(secret, x, y);
        if (count == 0)
        {
            show[x][y] = ' ';
            int i = 0, j = 0;
            for (i = x - 1; i  x +1; i++)
            {
                for (j = y - 1; j  y+1 ; j++)
                {
                    if (show[i][j] == '*' && (i != x || j != y))
                    {
                        digui(secret, show, ROW, COL, i, j);
                    }

                }
            }
        }
        else
        {
            show[x][y] = count + '0';
        }
    }
}

2.28、判断输赢

玩家每次输入一个坐标,进行递归排雷之后,统计目前有多少坐标已经被排查了.

计算个数后,返回值.

if (win == (row * col – num))//行号*列标表示总共的坐标数-已经被排查的坐标数=雷的数量
则玩家胜利通关.


int is_win(char show[ROWS][COLS],int row,int col)
{
    int count1 = 0;
    int i = 0, j = 0;
    for (i = 1; i  row; i++)
    {
        for (j = 1; j  col; j++)
        {
            if (show[i][j] != '*')
            {
                count1++;
            }
        }
    }
    return count1;
}

好了,以上是牛牛对用c语言扫雷的理解,有不足之处,欢迎评论区指出,当然如果有不理解的小伙伴也可以私信提问哦,牛牛会一 一解答的.

小伙伴们的点赞就是给牛牛最大的支持,能不能给牛牛来一个一键三连呢?谢谢支持。

c语言实现扫雷(详细讲解)

最后附上总代码.

三、游戏总代码

主函数测试区(test.c) :

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
void menu()
{
    printf("*******************************************************************\n");
    printf("************                     *                    *************\n");
    printf("******************               *                *****************\n");
    printf("************************         *          ***********************\n");
    printf("*******************************  *   ******************************\n");
    printf("********    1.玩游戏         *        2.退出游戏          *********\n");
    printf("*******************************  *   ******************************\n");
    printf("************************         *          ***********************\n");
    printf("******************               *                *****************\n");
    printf("************                     *                     ************\n");
    printf("*******************************************************************\n");
    printf("请选择:\n");
}
void game()
{

    char secret[ROWS][COLS] = { 0 };
    char show[ROWS][COLS] = { 0 };

    initboard(secret, ROWS, COLS,'0');
    initboard(show, ROWS, COLS,'*');

    printboard(show, ROW, COL);

    int num = c_pattern();
    setmine(secret, ROW, COL,num);

    findmine(secret, show, ROW, COL,num);

}
int main()
{
    int n = 0;
    srand((unsigned int)time(NULL));
    do
    {
        menu();
        scanf("%d", &n);
        switch (n)
        {
        case 1:
            game();
            printf("再来一局吗?\n");
            printf("1.再来一局   2. 没意思不玩了\n");
            int again = 0;
            scanf("%d", &again);
            if (again == 1)
            {
                break;
            }
            else
                n = 2;
            break;
        case 2:
            printf("退出游戏");
            break;
        default:
            system("cls");
            printf("没有这个选项哦。请重新选择:\n\n");
            getchar();
            continue;
        }
    } while (n - 2);
}

函数实现区(game.c)

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

void initboard(char board[ROWS][COLS], int rows, int cols, char ret)
{
    int i = 0,  j = 0;
    for (i = 0; i < rows; i++)
    {
        for (j = 0; j < cols; j++)
        {
            board[i][j] = ret;
        }
    }
}

void printboard(char board[ROWS][COLS], int row, int col)
{
    printf("-----------------扫雷游戏-------------\n");
    int i = 0, j = 0;
    printf(" ");

    for (i = 1; i  row; i++)
    {
        printf("%3d ", i);
    }
    printf("\n");
    printf("  +");
    for (i = 0; i < row; i++)
    {
        printf("---+");
    }
    printf("\n");

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

        printf("  +");
        for (j = 0; j < col; j++)
        {
            printf("---+");
        }
        printf("\n");
    }
    printf("-----------------扫雷游戏-------------\n");
}

int c_pattern()
{
    int num = 0;
again:
    printf("欢迎玩家进入游戏:\n");
    printf("请新选择难度:(num代表雷的数量)\n");
    printf("A.简单模式:num=5    B.中等模式:num=15   C.困难模式:num=30   D.自定义难度(自由输入雷的个数)\n ");
    char pattern = 0;
    getchar();
    scanf("%c", &pattern);
    switch (pattern)
    {
    case 'A':
    case 'a':
        printf("简单模式:num=5\n");
        num = 5;
        return num;
    case 'B':
    case 'b':
        printf("中等模式:num=10\n");
        num = 15;
        return num;
    case 'C':
    case 'c':
        printf("困难模式:num=15\n");
        num = 30;
        return num;
    case 'D':
    case 'd':
        printf("自定义难度:");
        printf("请输入布置雷的个数:\n");
        getchar();
        int intput = 0;
        scanf("%d", &intput);
        num = intput;
        return num;
    default:
        printf("不好意思,牛牛还没有开发此模式,请重新选择:\n\n");
        goto again;
        break;
    }
}

void setmine(char board[ROWS][COLS], int row, int col,int num)
{
    int x = 0, y = 0;
    int count = 0;
    for (count = 0; count < num; )
    {
        x = 1 + rand() % row;
        y = 1 + rand() % col;
        if (board[x][y] == '0')
        {
            board[x][y] = '1';
            count++;
        }
    }
}

int countmine(char secret[ROWS][COLS], int x, int y)
{
    int ret = secret[x - 1][y - 1]+ secret[x - 1][y] + secret[x - 1][y + 1]
        + secret[x][y - 1] + secret[x][y + 1]
        + secret[x + 1][y - 1] + secret[x + 1][y] + secret[x + 1][y + 1] - 8 * '0';
    return ret;
}

digui(char secret[ROWS][COLS], char show[ROWS][COLS], int row, int col, int x, int y)
{

    if (x >= 1 && x  row && y >= 1 && y  col)
    {
        int count = countmine(secret, x, y);
        if (count == 0)
        {
            show[x][y] = ' ';
            int i = 0, j = 0;
            for (i = x - 1; i  x +1; i++)
            {
                for (j = y - 1; j  y+1 ; j++)
                {
                    if (show[i][j] == '*' && (i != x || j != y))
                    {
                        digui(secret, show, ROW, COL, i, j);
                    }

                }
            }
        }
        else
        {
            show[x][y] = count + '0';
        }
    }
}

int is_win(char show[ROWS][COLS],int row,int col)
{
    int count1 = 0;
    int i = 0, j = 0;
    for (i = 1; i  row; i++)
    {
        for (j = 1; j  col; j++)
        {
            if (show[i][j] != '*')
            {
                count1++;
            }
        }
    }
    return count1;
}

void findmine(char secret[ROWS][COLS], char show[ROWS][COLS], int row, int col,int num)
{
    int x = 0, y = 0;
    int win = 0;
    while (win < (row * col -num))
    {
        printf("请输入排查雷的坐标:\n格式为:行号 列标\n");
        scanf("%d%d", &x, &y);
        if (show[x][y] != '*')
        {
            printf("该坐标已经被排查过了.请重新输入:");
            continue;
        }
        if (x >= 1 && x  row && y >= 1 && y  col)
        {
            if (secret[x][y] == '1')
            {
                printf("很遗憾,你失败了\n");
                printf("请看答案:\n");
                printboard(secret, ROW, COL);
                printf("很遗憾,你失败了\n上面是答案:\n");
                break;
            }
            else
            {

                digui(secret, show, ROW, COL,x,y,&win);
                win=is_win(show, ROW, COL);
                printboard(show, ROW, COL);
            }
        }
        else
        {

            printf("坐标非法,请重新输入:\n");
        }
    }
    if (win == (row * col - num))
    {
        printf("恭喜你排雷成功\n");
        printf("牛牛为你点赞!!!\n");
    }
}

函数声明区(game.h):

#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include

#define ROW 12
#define COL 12

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

void initboard(char board[ROWS][COLS], int rows, int cols, char ret);

void printboard(char board[ROWS][COLS],int row, int col);

void setmine(char board[ROWS][COLS], int row,int col,int num);

void findmine(char secret[ROWS][COLS], char show[ROWS][COLS], int row,int col,int num);

Original: https://blog.csdn.net/qq_67276605/article/details/128686157
Author: 初阶牛
Title: c语言实现扫雷(详细讲解)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/812154/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球