兴趣学C:扫雷小游戏

前言

在上一节中我们成功实现了三子棋小游戏,玩上几遍是不是感觉有些把子无聊了,没有关系,今天给家人们整个难度大一点的---扫雷,同样这个游戏也是和三子棋一样,不需要很多的基础便可以做出来,也是只需要系统性的学习到数组便可以实现。

新建工程

这次工程一样也不是什么大工程,分为三个文件来实现哦!

分别为

新建头文件 game.h(用于进行函数声明,引用头文件)

新建源文件 game.c(游戏接口函数的实现)

test.c(用于测试接口函数功能,主函数)

如下图所示

游戏逻辑分析

扫雷游戏想必大多数人都应该玩过吧,首先引入眼帘的是一片雷区,有一定数量的雷在其中,接下来便要在这一片雷区中找到这些雷所在的位置,胜利的条件便是标记出雷所在的位置或者把所有没有雷的位置都排开便可获得胜利

游戏实现

宏定义游戏规模

这次我们准备将游戏分为三个难度等级,分别为Eaxy,Medium,Expert,游戏规模也分别为9x9大小,10个雷、16x16大小,40个雷、16x30大小,99个雷。考虑到游戏四条边上的雷若要计算周围雷的数量较为麻烦,所以我们创建的二维数组要比展示出来的雷区多两行多两列,这样多消耗了一点空间但换来了代码效率的提升,代码如下

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<Windows.h>
#define EAXYNUM 9
#define EAXYNUMEXTRA (EAXYNUM+2)
#define MEDIUMNUM 16
#define MEDIUMNUMEXTRA (MEDIUM+2)
#define EXPERT_ROW 16
#define EXPERT_ROWEXTRA (EXPERT_ROW+2)
#define EXPERT_COL 30
#define EXPERT_COLEXTRA (EXPERT_COL+2)
#define EAXYBOOMNUM 10
#define MEDIUMBOOMNUM 40
#define EXPERTBOOMNUM 99

菜单

在打开程序后首先展现出来的便是主界面菜单,在选择开始游戏之后,便开始继续选择难度,这便需要我们编辑出两个菜单并定义相应的枚举变量。

//定义菜单枚举变量
enum menu
{
    PLAY = 1,
    EXIT
};
//定义游戏菜单枚举变量
enum game_menu
{
    EAXY=1,
    MEDIUM,
    EXPERT
};
//主菜单
void menu()
{
        printf("*****************\n");
        printf("*****  扫雷  ****\n");
        printf("******1.PLAY*****\n");
        printf("******2.EIXT*****\n");
        printf("*****************\n");
}
//游戏菜单
void game_menu()
{
    printf("********************\n");
    printf("**请选择游戏规模****\n");
    printf("***1.Easy(9x9)******\n");
    printf("***2.Medium(16x16)**\n");
    printf("***3.Expert(30x16)**\n");
    printf("********************\n");
}

生成雷区

我们将雷区和游戏画面分别存放至两个数组当中,用字符’0‘代表没有雷,用字符’1‘代表此处有雷存放进mine的二维数组中,用’*‘表示当前此处没有被排查过,被排查过后那一块区域就会显示出周围雷的数量,若周围没有雷则会显示一片空白。

初始化雷区

在开始游戏之前我们会将雷区初始化为一开始想要展示的样子,下面为相应的代码实现

//简单模式初始化
void IniEaxyGame(char board[EAXYNUMEXTRA][EAXYNUMEXTRA], int rows, int cols,char flag)
{
    int i, j;
    for (i = 0; i < rows; i++)
        for (j = 0; j < cols; j++)
            board[i][j] = flag;
}
//简单模式展示雷区
void ShowEaxyGame(char board[EAXYNUMEXTRA][EAXYNUMEXTRA], int row, int col)
{
    int i, j;
    for (i = 0; i <= row; i++)
        printf("%d ", i);
    printf("\n");
    for (i = 1; i < row + 1; i++)
    {
        printf("%d ", i);
        for (j = 1; j < col + 1; j++)
            printf("%c ", board[i][j]);
        printf("\n");
    }
}

随机生成地雷

通过srand()和rand()函数生成随机数,从而实现在一片雷区中随机的生成地雷,srand()函数用一次即可达到很好的生成效果,便放在主函数中使用一次,代码实现如下

//简单模式生成随机炸弹
void EaxyBoom(char board[EAXYNUMEXTRA][EAXYNUMEXTRA], int row, int col, int boomnums)
{
    int x, y;
    while (boomnums--)
    {
        x = rand() % row + 1;
        y = rand() % col + 1;
        board[x][y] = '1';
    }
}

游戏操作

在扫雷游戏中操作也是很简单的只有三种操作,分别为排雷,标记,取消标记,我们在这里分别用’p‘来表示排雷,’b‘来表示标记,’q‘来表示取消标记。若全程不标记,则定义一个变量计数,若排开的数量等于所有没有雷的地区的数量,则游戏胜利。若所有的雷都被标记,那么游戏也可以胜利,以下为代码实现。

//简单模式操作
void EaxyOprate(char mine[EAXYNUMEXTRA][EAXYNUMEXTRA], char show[EAXYNUMEXTRA][EAXYNUMEXTRA], int row, int col, int boomnum)
{
    int x, y;
    char set;
    int flag = EAXYNUM * EAXYNUM - boomnum;
    do
    {
    Begin:
        printf("请选择想要排雷或者想要标记的坐标\n");
        printf("         排雷格式(p x,y)\n");
        printf("         标记格式(b x,y)\n");
        printf("        取消标记格式(q x,y)\n");
        getchar();
        scanf("%c", &set);
        scanf("%d,%d", &y, &x);
        if (judge(show, set, x, y, EAXYNUM, EAXYNUM) == 0)
        {
            printf("输入非法,请重新输入\n");
            system("pause");
            system("cls");
            goto Begin;
        }
        switch (set)
        {
        case 'p':
            if (mine[x][y] == '1')
            {
                printf("踩雷了!!!\n");
                printf("You Lose!!!\n");
                system("pause");
                goto End;
            }
            else
            {
                EaxyDeming(mine, show, x, y, &flag);
                break;
            }
        case 'b':
            if (show[x][y] != 'P')
            {
                show[x][y] = 'P';
                if (mine[x][y] == '1')
                    --boomnum;
            }
            break;
        case 'q':
            if (show[x][y] != 'P')
            {
                printf("此位置未曾被标记,请重新输入\n");
                system("pause");
                goto Begin;
            }
            else
            {
                show[x][y] = '*';
                if (mine[x][y] == '1')
                    ++boomnum;
                break;
            }
        }
        system("cls");
        ShowEaxyGame(show, EAXYNUM, EAXYNUM);
    } while (boomnum > 0 || flag > 0);
    printf("真是个排雷大师!!!\n");
    printf("  You Win!!!\n");
End:;
}
//简单模式排雷
void EaxyDeming(char mine[EAXYNUMEXTRA][EAXYNUMEXTRA], char show[EAXYNUMEXTRA][EAXYNUMEXTRA], int x, int y, int* flag)
{
    if (mine[x][y] != '1')
    {
        show[x][y] = count(mine, x, y);
        if (show[x][y] == '0')
            show[x][y] = ' ';
        (*flag)--;
    }
    if (show[x][y] == ' ')
    {
        if (show[x - 1][y - 1] == '*')
            EaxyDeming(mine, show, x - 1, y - 1, flag);
        if (show[x][y - 1] == '*')
            EaxyDeming(mine, show, x, y - 1, flag);
        if (show[x + 1][y - 1] == '*')
            EaxyDeming(mine, show, x + 1, y - 1, flag);
        if (show[x - 1][y + 1] == '*')
            EaxyDeming(mine, show, x - 1, y + 1, flag);
        if (show[x - 1][y + 1] == '*')
            EaxyDeming(mine, show, x - 1, y + 1, flag);
        if (show[x][y + 1] == '*')
            EaxyDeming(mine, show, x, y + 1, flag);
        if (show[x + 1][y] == '*')
            EaxyDeming(mine, show, x + 1, y, flag);
        if (show[x - 1][y] == '*')
            EaxyDeming(mine, show, x - 1, y, flag);
    }
}
//判断输入合法性
int judge(char show[EAXYNUMEXTRA][EAXYNUMEXTRA], char set, int x, int y, int row, int col)
{
    if (x > col || x < 1)
        return 0;
    if (y > row || y < 1)
        return 0;
    if (set != 'p' && set != 'b' && set != 'q')
        return 0;
    if (show[x][y] != '*' && show[x][y] != 'q')
        return 0;
    return 1;
}
//获取周围雷的个数
char count(char mine[EAXYNUMEXTRA][EAXYNUMEXTRA], int x, int y)
{
    return mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x - 1][y + 1] + mine[x][y + 1] + mine[x + 1][y + 1] + mine[x + 1][y] + mine[x - 1][y] - 336;
}

主函数连接接口函数

接下来的任务就是将这么多的接口函数组装完整啦!代码如下。

#include"game.h"
int main()
{
    int input;
    srand((unsigned)time(NULL));
    do
    {
        menu();
            scanf("%d", &input);
        switch (input)
        {
        case PLAY:
            system("cls");
            game_menu();
            scanf("%d", &input);
            system("cls");
            switch (input)
            {
            case EAXY:
                EaxyGame(); 
                break;
            case MEDIUM:
            case EXPERT:
            default:
                break;
            }
            break;
        case EXIT:
            printf("退出\n");
            break;
        default:
            system("cls");
            printf("非法输入,请重新输入\n");
            break;
        }
    } while (input != EXIT);
    return 0;
}

测试

本人技术不佳,带给大家输的结果还望谅解(〃>_<;〃)

好气好气!!!

源代码展示

game.c

#include"game.h"
//主菜单
void menu()
{
        printf("*****************\n");
        printf("*****  扫雷  ****\n");
        printf("******1.PLAY*****\n");
        printf("******2.EIXT*****\n");
        printf("*****************\n");
}
//游戏菜单
void game_menu()
{
    printf("********************\n");
    printf("**请选择游戏规模****\n");
    printf("***1.Easy(9x9)******\n");
    printf("***2.Medium(16x16)**\n");
    printf("***3.Expert(30x16)**\n");
    printf("********************\n");
}
//简单模式
void EaxyGame()
{
    char show[EAXYNUMEXTRA][EAXYNUMEXTRA];
    char mine[EAXYNUMEXTRA][EAXYNUMEXTRA];
    IniEaxyGame(show, EAXYNUMEXTRA, EAXYNUMEXTRA, '*');
    IniEaxyGame(mine, EAXYNUMEXTRA, EAXYNUMEXTRA, '0');
    EaxyBoom(mine, EAXYNUM, EAXYNUM, EAXYBOOMNUM);
    ShowEaxyGame(show, EAXYNUM, EAXYNUM);
    EaxyOprate(mine, show, EAXYNUM, EAXYNUM, EAXYBOOMNUM);
}
//简单模式初始化
void IniEaxyGame(char board[EAXYNUMEXTRA][EAXYNUMEXTRA], int rows, int cols,char flag)
{
    int i, j;
    for (i = 0; i < rows; i++)
        for (j = 0; j < cols; j++)
            board[i][j] = flag;
}
//简单模式生成随机炸弹
void EaxyBoom(char board[EAXYNUMEXTRA][EAXYNUMEXTRA], int row, int col, int boomnums)
{
    int x, y;
    while (boomnums--)
    {
        x = rand() % row + 1;
        y = rand() % col + 1;
        board[x][y] = '1';
    }
}
//简单模式展示雷区
void ShowEaxyGame(char board[EAXYNUMEXTRA][EAXYNUMEXTRA], int row, int col)
{
    int i, j;
    for (i = 0; i <= row; i++)
        printf("%d ", i);
    printf("\n");
    for (i = 1; i < row + 1; i++)
    {
        printf("%d ", i);
        for (j = 1; j < col + 1; j++)
            printf("%c ", board[i][j]);
        printf("\n");
    }
}
//简单模式操作
void EaxyOprate(char mine[EAXYNUMEXTRA][EAXYNUMEXTRA], char show[EAXYNUMEXTRA][EAXYNUMEXTRA], int row, int col, int boomnum)
{
    int x, y;
    char set;
    int flag = EAXYNUM * EAXYNUM - boomnum;
    do
    {
    Begin:
        printf("请选择想要排雷或者想要标记的坐标\n");
        printf("         排雷格式(p x,y)\n");
        printf("         标记格式(b x,y)\n");
        printf("        取消标记格式(q x,y)\n");
        getchar();
        scanf("%c", &set);
        scanf("%d,%d", &y, &x);
        if (judge(show, set, x, y, EAXYNUM, EAXYNUM) == 0)
        {
            printf("输入非法,请重新输入\n");
            system("pause");
            system("cls");
            goto Begin;
        }
        switch (set)
        {
        case 'p':
            if (mine[x][y] == '1')
            {
                printf("踩雷了!!!\n");
                printf("You Lose!!!\n");
                system("pause");
                goto End;
            }
            else
            {
                EaxyDeming(mine, show, x, y, &flag);
                break;
            }
        case 'b':
            if (show[x][y] != 'P')
            {
                show[x][y] = 'P';
                if (mine[x][y] == '1')
                    --boomnum;
            }
            break;
        case 'q':
            if (show[x][y] != 'P')
            {
                printf("此位置未曾被标记,请重新输入\n");
                system("pause");
                goto Begin;
            }
            else
            {
                show[x][y] = '*';
                if (mine[x][y] == '1')
                    ++boomnum;
                break;
            }
        }
        system("cls");
        ShowEaxyGame(show, EAXYNUM, EAXYNUM);
    } while (boomnum > 0 || flag > 0);
    printf("真是个排雷大师!!!\n");
    printf("  You Win!!!\n");
End:;
}
//简单模式排雷
void EaxyDeming(char mine[EAXYNUMEXTRA][EAXYNUMEXTRA], char show[EAXYNUMEXTRA][EAXYNUMEXTRA], int x, int y, int* flag)
{
    if (mine[x][y] != '1')
    {
        show[x][y] = count(mine, x, y);
        if (show[x][y] == '0')
            show[x][y] = ' ';
        (*flag)--;
    }
    if (show[x][y] == ' ')
    {
        if (show[x - 1][y - 1] == '*')
            EaxyDeming(mine, show, x - 1, y - 1, flag);
        if (show[x][y - 1] == '*')
            EaxyDeming(mine, show, x, y - 1, flag);
        if (show[x + 1][y - 1] == '*')
            EaxyDeming(mine, show, x + 1, y - 1, flag);
        if (show[x - 1][y + 1] == '*')
            EaxyDeming(mine, show, x - 1, y + 1, flag);
        if (show[x - 1][y + 1] == '*')
            EaxyDeming(mine, show, x - 1, y + 1, flag);
        if (show[x][y + 1] == '*')
            EaxyDeming(mine, show, x, y + 1, flag);
        if (show[x + 1][y] == '*')
            EaxyDeming(mine, show, x + 1, y, flag);
        if (show[x - 1][y] == '*')
            EaxyDeming(mine, show, x - 1, y, flag);
    }
}
//判断输入合法性
int judge(char show[EAXYNUMEXTRA][EAXYNUMEXTRA], char set, int x, int y, int row, int col)
{
    if (x > col || x < 1)
        return 0;
    if (y > row || y < 1)
        return 0;
    if (set != 'p' && set != 'b' && set != 'q')
        return 0;
    if (show[x][y] != '*' && show[x][y] != 'q')
        return 0;
    return 1;
}
//获取周围雷的个数
char count(char mine[EAXYNUMEXTRA][EAXYNUMEXTRA], int x, int y)
{
    return mine[x - 1][y - 1] + mine[x][y - 1] + mine[x + 1][y - 1] + mine[x - 1][y + 1] + mine[x][y + 1] + mine[x + 1][y + 1] + mine[x + 1][y] + mine[x - 1][y] - 336;
}

test.c

#include"game.h"
int main()
{
    int input;
    srand((unsigned)time(NULL));
    do
    {
        menu();
            scanf("%d", &input);
        switch (input)
        {
        case PLAY:
            system("cls");
            game_menu();
            scanf("%d", &input);
            system("cls");
            switch (input)
            {
            case EAXY:
                EaxyGame(); 
                break;
            case MEDIUM:
            case EXPERT:
            default:
                break;
            }
            break;
        case EXIT:
            printf("退出\n");
            break;
        default:
            system("cls");
            printf("非法输入,请重新输入\n");
            break;
        }
    } while (input != EXIT);
    return 0;
}

game.h

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<Windows.h>
#define EAXYNUM 9
#define EAXYNUMEXTRA (EAXYNUM+2)
#define MEDIUMNUM 16
#define MEDIUMNUMEXTRA (MEDIUM+2)
#define EXPERT_ROW 16
#define EXPERT_ROWEXTRA (EXPERT_ROW+2)
#define EXPERT_COL 30
#define EXPERT_COLEXTRA (EXPERT_COL+2)
#define EAXYBOOMNUM 10
#define MEDIUMBOOMNUM 40
#define EXPERTBOOMNUM 99
//定义菜单枚举变量
enum menu
{
    PLAY = 1,
    EXIT
};
//定义游戏菜单枚举变量
enum game_menu
{
    EAXY=1,
    MEDIUM,
    EXPERT
};
void menu();
void game_menu();
void EaxyGame();
void IniEaxyGame(char board[EAXYNUMEXTRA][EAXYNUMEXTRA], int rows, int cols, char flag);
void EaxyBoom(char board[EAXYNUMEXTRA][EAXYNUMEXTRA], int row, int col, int boomnums);
void ShowEaxyGame(char board[EAXYNUMEXTRA][EAXYNUMEXTRA], int row, int col);
void EaxyOprate(char mine[EAXYNUMEXTRA][EAXYNUMEXTRA], char show[EAXYNUMEXTRA][EAXYNUMEXTRA], int row, int col, int boomnum);
int judge(char show[EAXYNUMEXTRA][EAXYNUMEXTRA], char set, int x, int y, int row, int col);
void EaxyDeming(char mine[EAXYNUMEXTRA][EAXYNUMEXTRA], char show[EAXYNUMEXTRA][EAXYNUMEXTRA], int x, int y, int* boomnum);
char count(char mine[EAXYNUMEXTRA][EAXYNUMEXTRA], int x, int y);

小结

这次我们设定了三个难度,但是我们在此只完成了Eaxy难度,剩下的两个难度其实和这个实现的方法是一样的,但由于C语言函数传参时,对多维数组有一定的限制,不能更高效的实现另外两个功能,那就烦请各位,自己尝试,看能否实现咯!

  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值