祝!精霊幻想記!! 2021年1月18日晚上10点46分 by MOKE-NEKO pid:87147319

简介

使用C语言数组来实现扫雷

《扫雷》是一款大众类的益智小游戏,于1992年发行。游戏目标是在最短的时间内根据点击格子出现的数字找出所有非雷格子,同时避免踩雷,踩到一个雷即全盘皆输。
(说白了就是点击位置周围8格有几颗雷)

你扫你码呢?

此篇文章讲述
9*9简单模式扫雷

test.c - 测试
game.c - 游戏函数的实现
game.h - 游戏函数的声明

源文件



Tools

VS2019
VScode
Typora

代码实现

菜单

游戏界面

1.打印菜单

void menu()//菜单
{
    printf("****************************************\n");
    printf("**********        扫雷        **********\n");
    printf("**********       1.play       **********\n");
    printf("**********       0.exit       **********\n");
    printf("**********   by MyWifeAsuna   **********\n");
    printf("****************************************\n");
}

2.做出选择

1.开始扫雷 0.退出游戏
选择错误则重新选择

int input;//使用一个input控制主菜单
srand((unsigned)time(NULL));//时间控制随机变量
do
{
    menu();
    printf("请选择:");
    scanf("%d", &input);
    switch (input)
    {
    case 1:
        game();//游戏主控
        break;
    case 0:
        printf("退出游戏\n");
        break;
    default:
        printf("选择错误,重新选择!\n");
        break;
    }
} while (input);

棋盘

1.建设想法

假设:

设定1为雷,0不是雷,点击出现的1表示周围有一颗雷

这样的话点击出现1会和表示雷的1一样,所以可以设置*为雷#为非雷

假设

但是,当我们排雷的时候每一个*和#都要判断一次,9*9则要判断81次,在同一个棋盘上做布置判断控制等操作也不是很合适

所以这里提供一种方法

布置和排查雷的信息

设定1还是雷,0是非雷
1是雷的话之后计算雷的数量就很方便

再创建一个一模一样的数组

方案

当我们排雷的时候

应该访问(遍历)点击坐标的周围8个坐标来判断

但是有一个比较遗憾的地方,当统计边角的周围坐标时,会出现数组越界的问题

越界

而且9*9有81个格子 每一个格子又要判断周围8个格子,相当于81*8次的判断,又要尽可能的判断,是不是越界等问题

所以应该让数组扩充一圈,行多2行,列多2列,只不过这一圈都不是雷,这样排查就不会越界了

实际上9*9的棋盘需要11*11的数组,只判断其中9*9的格子,当然2个数组一样

2.打印棋盘,部署实施

布置雷和排查雷的2个11*11数组

打印2个棋盘

char mine[ROWS][COLS] = { 0 };//存放雷的信息
char show[ROWS][COLS] = { 0 };//存放排查出雷的信息
DisplayBoard(mine, ROW, COL);//打印布置雷的棋盘
DisplayBoard(show, ROW, COL);//打印排查雷的棋盘
//显示棋盘 game.h声明
void DisplayBoard(char board[ROWS][COLS], int row, int col);//因为是11*11棋盘,所以传参过来必须使用ROWS和COLS
void DisplayBoard(char board[ROWS][COLS], int row, int col)//打印棋盘
{
    int i;
    printf("--------------------\n");//区分线
    for (i = 0; i <= 9; i++)
    {
        printf("%d ", i);
    }
    printf("\n");
    for (i = 1; i <= row; i++)//行从下标1开始
    {
        int j;
        printf("%d ", i);
        for (j = 1; j <= col; j++)//列从下标1开始
        {
            printf("%c ", board[i][j]);//数组的特点,取11*11棋盘下标(0,0)-(10,10)的中间9*9棋盘则为(1,1)-(9,9)
        }
        printf("\n");
    }
    printf("--------------------\n");
}

初始化2个棋盘

//初始化棋盘
InitBoard(mine, ROWS, COLS, '0');//'0'
InitBoard(show, ROWS, COLS, '*');//'*'未点击的排查棋盘为*来保持神秘
//初始化棋盘 game.h声明
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)//初始化棋盘
{
    int i = 0;
    for (i = 0; i < rows; i++)
    {
        int j;
        for (j = 0; j < cols; j++)
        {
            board[i][j] = set;
        }
    }
}

设定布置雷的棋盘数组为字符0,其中雷设定为1

//布置雷 game.h声明
void SetMine(char board[ROWS][COLS], int row, int col);//因为是11*11棋盘,所以传参过来必须使用ROWS和COLS
void SetMine(char board[ROWS][COLS], int row, int col)//布置雷
{
    int count = EASY_COUNT;
    while(count)
    {
        //1.生成随机下标(1,1)-(9,9)
        int x = rand() % row + 1;
        int y = rand() % col + 1;
        if (board[x][y] != '1')//(x,y)处不是雷,再布置进去
        {
            board[x][y] = '1';//设定1是雷
            //雷布置一次后不能再布置,不然会覆盖,所以使用一个if判断下标是否有雷
            count--; //还要把雷的个数count加进去,绝不能放在其他的位置
        }
    }
}

3.排查雷

mine和show2个数组都涉及

//排查雷
FindMine(mine, show, ROW, COL);
//排查雷 game.h声明
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);//因为是11*11棋盘,所以传参过来必须使用ROWS和COLS
int GetMineCount(char mine[ROWS][COLS], int x, int y)//计算排查坐标周围8个坐标有几颗雷,把8个坐标的字符加起来减去8个‘0’就可以了(字符加减基础)
{
    return (mine[x - 1][y] +
        mine[x - 1][y - 1] +
        mine[x][y - 1] +
        mine[x + 1][y - 1] +
        mine[x + 1][y] +
        mine[x + 1][y + 1] +
        mine[x][y + 1] +
        mine[x - 1][y + 1] - 8 * '0');//循环也可以,但是循环很明显没有直接加起来合适
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)//排查雷
{
    int x, y, win = 0;
    while (win<row*col-EASY_COUNT)//判断是否排完9*9-10=71,81个格子减去10个雷
    {
        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';//字符加减基础,a+'0'='a','a'-'0'=a
                DisplayBoard(show, ROW, COL);
                win++;
            }
        }
        else//非法
        {
            printf("坐标非法,重新输入\n");
        }
    }
    if (win == row * col - EASY_COUNT)
    {
        printf("恭喜你,排雷成功\n");
        DisplayBoard(mine, ROW, COL);
    }
}

4.扩展功能

这些问题留下今后更新扩展研究

1.防止第一步就踩雷

把雷移走或者先走第一步再布置雷

2.当周围所有坐标全都没有雷,就自动展开到周围有雷为止

该坐标:1.不是雷2.周围也没有雷3.该坐标没有被排查过

3.标记雷

可以标记已经确定的雷,且标记后总雷数-1

4.游戏时间

游戏总时间、倒计时、每排一颗雷用时和其他数据时间等

5.重来

夏祭り浴衣シノン 2021年7月28日晚上11点00分 by 我美蘭☆FGOAC概念礼装 pid:91566275

广告位招租
最后修改:2021 年 09 月 10 日 02 : 12 PM
如果觉得我的文章对你有用,请喂饱我!(理直气壮)