C语言实战项目—扫雷小程序

Wesley13
• 阅读 695

扫雷游戏是微软自带的一款小游戏。扫雷游戏的玩法是,以9*9棋盘为例,棋盘上随机分布着10个地雷,玩家在棋盘上进行点击,如果被点击的格子是地雷,则玩家被炸“死”,游戏结束;如果被点击的格子上没有地雷,与被点击的格子相邻的格子(被点击格子的上下左右还有斜向,共八个格子)有地雷,则在被点击的格子上显示这些地雷的总数,如果与被点击的格子相邻的八个格子都没有地雷,则棋盘自动展开,直到与展开的格子相邻的格子有地雷才停止。此时最后被展开的格子显示其相邻格子共有的地雷数。

除上述功能外,如果玩家认为某个格子有地雷,则在该格子上插入旗子。后期如果确定某个插了旗子的格子没有地雷,则可以将旗子拔掉。

扫雷是一款集运气与逻辑的游戏,一般为了保证可玩性,不至于刚上来就游戏结束,所以还需确保玩家点击的第一步不是雷。

下面将使用C语言实现上述功能。因为初学C语言,对许多功能了解的不够深入,代码写的难免有瑕疵,如有问题,还望批评指正。

本例使用Win10系统下的 Visual Studio 2019 进行编写的,对于所有使用scanf()函数的文档,需在首行加入如下代码(必须是在首行,加入其他行无效):

#define _CRT_SECURE_NO_WARNINGS 1

游戏框架

对于任意游戏,都需要菜单来让玩家进行选择,其次还有游戏的主体部分。所以建立三个文件,分别为”game.c”(用于存放游戏中各个部分功能的函数),”game.h”(用于声明”game.c”中的函数),以及主文件”main.c”.

菜单部分

游戏开始,首先在屏幕上打印菜单,供玩家进行选择。程序根据玩家的选择进行下一步操作。菜单功能使用menu()函数进行实现,用户输入则用scanf()函数来接收。规定:菜单打印两个选项,当用户键入1时,则开始进行游戏(游戏部分由game()函数实现);当用户键入0时,则退出程序;若用户键入其他字符,则提示用户输入有误,需重新出入。因此可以使用switch()函数来实现此部分功能。具体实现代码如下:

//扫雷游戏
#include "game.h"

int main(void)
{
    int input;
    srand((unsigned int)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);
    return 0;
}

其中menu()代码如下:

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

游戏部分

首先要生成两个棋盘,其中一个为后台,玩家看不见,用于存储地雷的信息和判断游戏的输赢。一个为对玩家显示的棋盘,供玩家进行操作。后台棋盘使用数组mineboard()实现,对玩家显示的棋盘用showboard()实现。

生成棋盘后,则需对棋盘进行初始化,规定:后台的棋盘用字符0表示没有地雷,用字符1表示有地雷;玩家的棋盘用*表示没有被点击,用#表示插入旗子。初始化则将后台的棋盘全部置为没有地雷,玩家棋盘全部置为*。初始化棋盘使用InitBoard()实现。

初始化棋盘后需要在后台棋盘上布置地雷同时向玩家展示玩家棋盘,布置地雷使用随机值布置10个地雷。布置地雷使用SetMine()函数,展示棋盘使用DisplayBoard()函数。

布置地雷后,开始游戏,为确保玩家第一次点击(由于程序限制,本例全部使用坐标输入代替玩家点击)不踩到地雷,所以需要对第一次点击进行判断,如果第一次刚好点到地雷,则将此处改为没有地雷,并在其他位置随机生成地雷,以确保地雷总数不变。此功能使用First_SafeMine()函数实现。

从第二步开始,每次都询问玩家的操作(是点击还是插旗还是拔旗)。同时进行输赢判断。从第二部开始为真正排雷过程,使用FindMine()函数进行实现。

输赢判断包括以下几个方面:

(1)玩家点到了地雷,游戏直接结束;

(2)玩家运气足够好,在棋盘上插得旗子数等于地雷数,并且这些旗子下刚好都有地雷,玩家胜利,游戏结束;

(3)玩家将棋盘上没有地雷的区域全部点开,玩家胜利,游戏结束。

对于玩家的操作,又分为以下几部分:

玩家点击:以输入坐标的形式代替点击,需判断坐标是否合法,如果坐标超出了棋盘范围,则提示玩家坐标超出范围并等待玩家重新输入;如果玩家输入的坐标已经被排查过(包括该位置为空,该位置上有数字,该位置上有旗子),则提示玩家该位置已经被排查过,并让玩家对照此三种情况进行检查然后等待玩家重新输入;如果坐标没有被排查过,则判断该位置是否有地雷,如果有,则游戏直接结束;如果没有地雷,则需判断该位置相邻棋盘是否有地雷,如果没有,则棋盘自动展开,直到相邻格子有地雷为止;如果相邻棋盘有地雷,则在该位置显示地雷数。如果棋盘已全部排查完,则提示玩家成功并打印后台棋盘然后退出游戏。

其中判断相邻棋盘是否有地雷用GetMineCount()函数实现,棋盘自动展开用OpenMine()实现,判断棋盘是否排查完使用Countmine()函数实现。

插入旗子:同玩家点击一样,需判断坐标是否合法以及插旗位置是否已经被排查过。此外,需判断插入旗子位置对应的真雷数,如果旗子数等于真雷数并且旗子对应的位置都是真雷,则认为玩家胜利。判断旗子对应的真雷数使用TrueMineCount()函数实现。

拔掉旗子:首先要判断棋盘是否有旗子,没有旗子则提示玩家棋盘上没有旗子同时退出拔旗操作,如果有旗子则判断坐标的合法性以及拔旗位置是否已经排查过或拔旗位置没有旗子。同时也需要判断剩余旗子位置对应的真雷数,如果旗子数等于真雷数并且旗子对应的位置都是真雷,则认为玩家胜利。

代码实现

下面则对上述各个函数进行实现。

在统计某处雷的个数时,对于边界还需判断是否越界,这样每次判断都会麻烦。因此把棋盘设置的比实际玩的棋盘大一圈,这一圈上不放置地雷,统计时可以省去判断边界的麻烦。

1.初始化棋盘:遍历棋盘,对后台的棋盘全部置0,对玩家看到的棋盘置*。函数可以接收棋盘和棋盘大小,同时接收相应的字符。

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

2.显示棋盘:遍历玩家棋盘,将玩家棋盘的内容输出在屏幕上。为防止坐标轴与棋盘上信息混在一起,将其分开打印。

//显示棋盘
void DisplayBoard(char board[ROWS][COLS], int col, int row)
{
    int i, j;
    printf("    ");
    for (j = 1; j <= col; j++)
    {
        printf("%d ", j);
    }
    printf("\n\n");
    for (i = 1; i <= col; i++)
    {
        printf("%d   ",i);
        for (j = 1; j <= row; j++)
        {
            printf("%c ",board[i][j]);
        }
        printf("\n");
    }
}

生成的棋盘如下图所示:

![5X]Q0[%]6V66NAKGEU7}KD.png

      3.初始化地雷位置:使用随机函数在后台棋盘上生成10个地雷,规定字符1是地雷,字符0是安全地带。

//初始化地雷的位置
void SetMine(char board[ROWS][COLS], int col, int row)
{
    int x, y;
    int count = EASY_CONST;
    while (count)
    {
        x = rand() % col + 1;
        y = rand() % row + 1;
        if (board[x][y] == '0')
        {
            board[x][y] = '1';
            count--;
        }
    }
}

4.确保第一个不是地雷

//保证第一个不踩雷
void First_SafeMine(char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int row, int col)
{
    int x = 0;
    int y = 0;
    int ret = 1;
    int count = 0;
    int a, b;
    printf("请输入要排查的坐标:");
    scanf("%d%d", &x, &y);
    if (mineboard[x][y] == '1')//如果为雷,则改为没有雷
    {
        mineboard[x][y] = '0';
        while (ret)//随机生成一个雷
        {
            a = rand() % ROW + 1;
            b = rand() % COL + 1;
            if (mineboard[a][b] == '0')
            {
                mineboard[a][b] = '1';
            }
            ret--;
        }
    }
    count = GetMineCount(mineboard, x, y);//在上述步骤完成后,需统计第一个坐标周围雷的个数
    showboard[x][y] = count + '0';
    OpenMine(mineboard, showboard, row, col, x, y);//周围没有雷需要自动展开棋盘
    DisplayBoard(showboard, row, col);
}

    5.统计某位置处周围雷的个数。

    雷在棋盘上是字符1,所以需判断字符1的个数。字符1在内存中存放的实际上是ASCII码值,所以可以用一个整型变量来统计有几个字符1.

//统计坐标周围雷的个数
int GetMineCount(const char mineboard[ROWS][COLS], int x, int y)
{
    return mineboard[x - 1][y + 1] + mineboard[x][y + 1] + mineboard[x + 1][y + 1] +
        mineboard[x - 1][y] + mineboard[x + 1][y] +
        mineboard[x - 1][y - 1] + mineboard[x][y - 1] + mineboard[x + 1][y - 1] - 8 * '0';
}

6.棋盘自动展开:棋盘不展开的条件是某位置的周围有雷。所以先要判断是否有雷。如果雷的个数为0就展开,采用递归来实现。

//展开无雷区:如果该坐标周围没有雷就自动展开。
void OpenMine(const char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int col, int row,int x,int y)
{
    int ret = 0;
    ret=GetMineCount(mineboard, x, y);
    if (ret == 0)
    {
        showboard[x][y] = ' ';
        if (x > 0 && y + 1 <= col && showboard[x][y + 1] == '*')
        {
            OpenMine(mineboard, showboard, col, row, x, y + 1);
        }
        if (x - 1 > 0 && y + 1 <= col && showboard[x - 1][y + 1] == '*')
        {
            OpenMine(mineboard, showboard, col, row, x - 1, y + 1);
        }
        if (x - 1 > 0 && y >0 && showboard[x - 1][y] == '*')
        {
            OpenMine(mineboard, showboard, col, row, x - 1, y);
        }
        if (x - 1 > 0 && y - 1 >0 && showboard[x - 1][y - 1] == '*')
        {
            OpenMine(mineboard, showboard, col, row, x - 1, y - 1);
        }
        if (x > 0 && y - 1 > 0 && showboard[x][y - 1] == '*')
        {
            OpenMine(mineboard, showboard, col, row, x, y - 1);
        }
        if (x + 1 <= row && y - 1 > 0 && showboard[x + 1][y - 1] == '*')
        {
            OpenMine(mineboard, showboard, col, row, x + 1, y - 1);
        }
        if (x + 1 <= row && y > 0 && showboard[x + 1][y] == '*')
        {
            OpenMine(mineboard, showboard, col, row, x + 1, y);
        }
        if (x + 1 <=row && y + 1 <= col && showboard[x + 1][y + 1] == '*')
        {
            OpenMine(mineboard, showboard, col, row, x + 1, y + 1);
        }
    }
    else
    {
        showboard[x][y] = '0' + ret;
    }
}

7.统计棋盘上旗子对应真雷的个数

//统计棋盘上旗子下对应的真雷个数
int TrueMineCount(const char mineboard[ROWS][COLS],const char showboard[ROWS][COLS], int cols, int rows,int* pcount)
{
    int i, j;
    int count = 0;
    if (*pcount > 0)
    {
        for (i = 0; i < cols; i++)
        {
            for (j = 0; j < rows; j++)
            {
                if (showboard[i][j] == '#')
                {
                    if (mineboard[i][j] == '1')
                    {
                        count++;
                    }
                }
            }
        }
    }
    return count;
}

8.统计棋盘上除(已排除的区域)和(雷区)以外的其他区域大小

//统计棋盘上已排除的区域
int Countmine(char showboard[ROWS][COLS],int col ,int row)
{
    int i,j;
    int check;
    int win = col * row - EASY_CONST;
    for (i = 1; i <= row; i++)
    {
        for (j = 1; j <= row; j++)
        {
            check = showboard[i][j] - '0';
            if (showboard[i][j] == ' ' || (check > 0 && check < 9))
            {
                win--;
            }
        }
    }
    return win;
}

    9.排雷实现,具体步骤看代码注释

//排雷
void FindMine(const char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int col, int row)
{
    int x = 0;
    int y = 0;
    int win = 0;
    char input1;
    int bool = 1;
    int ret_trueminecount = 0; //用于接收旗子对应的真雷数
    int flag_count = 0;        //用于统计棋盘上旗子的数量
    do
    {
            //提示玩家进行选择模式
        printf("请选择(输入a,b或c):\n");
        printf("a.排雷 b.插旗 c.取消插旗\n");
        //scanf("%s", &input1);
        input1 = getchar();
        switch (input1)
        {
        case 'a':
        {
            printf("请输入要检查的坐标:>\n");
            int bool0 = 1;
            //玩家输入正确坐标或者玩家胜利或者玩家失败退出循环
            while (bool0) 
            {
                scanf("%d %d", &x, &y);
                if (x > 0 && x <= row && y > 0 && y <= col)  //玩家输入的坐标在合法范围内
                {
                    if (showboard[x][y] != '*')  //先判断是否已经排查
                    {
                        if (showboard[x][y] == '#')
                        {
                            printf("此处有旗子,请拔掉后再排查。\n");
                        }
                        else
                        {
                            printf("\n此处已经排查过了,请重新输入。\n");
                        }
                    }
                    else if (mineboard[x][y] == '1') //如果没有排查,判断是否踩到了雷
                    {
                        printf("\n很遗憾,你踩到了地雷,游戏结束!\n");
                        DisplayBoard(mineboard, col, row);
                        bool = 0;
                        break;
                    }
                    else
                    {
                        int count = GetMineCount(mineboard, x, y);  //判断输入坐标的周围雷的个数
                        if (0 == count)
                        {
                            OpenMine(mineboard, showboard, COL, ROW, x, y);
                            bool0 = 0;
                            //判断棋盘上除真雷区域和已排查区域之外的格子数,如果为0,说明玩家排了所有的雷,win置1.
                            int ret_win = Countmine(showboard, COL, ROW); 
                            if (ret_win == 0)
                            {
                                bool = 0;
                                win = 1;
                                break;
                            }
                        }
                        else
                        {
                            showboard[x][y] = count + '0';
                            int ret_win = Countmine(showboard, COL, ROW);
                            bool0 = 0;
                            if (ret_win == 0)
                            {
                                bool = 0;
                                win = 1;
                                break;
                            }
                        }
                        DisplayBoard(showboard, COL, ROW);
                    }
                }
                //玩家坐标输入不在棋盘范围内,则提示玩家输入错误,重新输入
                else
                {
                    printf("\n您输入的坐标有误,请重新输入\n");
                }
            }
            break;
        }
        //插旗
        case 'b':
        {
            int bool1 = 1;
            while (bool1)
            {
                printf("请输入要插旗的坐标:>");
                scanf("%d %d", &x, &y);
                if (x > 0 && x <= row && y > 0 && y <= col)
                {
                    if (showboard[x][y] == '*')
                    {
                        showboard[x][y] = '#';
                        flag_count++;         //每插入一个旗子,旗子数+1
                        bool1 = 0;            
                        ret_trueminecount = TrueMineCount(mineboard, showboard, COLS, ROWS, &flag_count);
                        //如果旗子数与对应的雷数相等,且旗子下都是真雷,则胜利,win置1.
                        if (flag_count==EASY_CONST && ret_trueminecount == EASY_CONST)
                        {
                            bool = 0;
                            win = 1;
                        }
                    }
                    else
                    {
                        printf("此处无法放置旗子。请按下列情况进行检查:\n(1)坐标是否有误;\n(2)此处已经有旗子;\n(3)此处是否已经排查过了。\n");
                    }
                }
                else
                {
                    printf("输入的坐标有误,请重新输入。\n");
                }
            }
            DisplayBoard(showboard, COL, ROW);
            break;
        }
        
        //拔掉旗子
        case 'c':
        {
            int bool2 = 1;
            int ret = FlagCount(showboard, COLS, ROWS);
            while (bool2)
            {
                if (ret == 0)
                {
                    printf("\n棋盘上没有旗子;请选择排雷或插旗。\n");
                    bool2 = 0;
                }
                else
                {
                    printf("\n请输入要拔旗的坐标:>");
                    scanf("%d %d", &x, &y);
                    if (x > 0 && x <= row && y > 0 && y <= col)
                    {
                        if (showboard[x][y] == '#')
                        {
                            showboard[x][y] = '*';
                            flag_count--;    //旗子拔掉后,旗子数要-1
                            bool2 = 0;
                            //如果旗子数与对应的雷数相等,且旗子下都是真雷,则胜利,win置1.
                            ret_trueminecount = TrueMineCount(mineboard, showboard, COLS, ROWS, &flag_count);
                            if (flag_count == EASY_CONST && ret_trueminecount == EASY_CONST)
                            {
                                bool = 0;
                                win = 1;
                            }
                        }
                        else
                        {
                            printf("\n此处无法移除旗子。请按下列情况进行检查:\n(1)坐标是否有误;\n(2)此处没有旗子;\n(3)此处旗子是否已经拔掉。\n");
                        }
                    }
                    else
                    {
                        printf("输入的坐标有误,请重新输入。\n");
                    }
                }
            }
            DisplayBoard(showboard, COL, ROW);
            break;
            }
        }
        
        //根据上述代码,win等于1时,表示玩家排出了全部的雷。
        if (win == 1)
        {
            printf("恭喜你,你已排出全部的雷。\n");
            bool = 0;
            DisplayBoard(mineboard,COL,ROW);
        }
    }while ( bool == 1);
}

最后游戏的主体部分代码如下:

void game()
{
    //存储雷的位置:生成两个棋盘,一个用于打印
    char mineboard[COLS][ROWS] ;
    char showboard[COLS][ROWS];

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

    //显示棋盘
    DisplayBoard(showboard, COL, ROW);

    //布置雷
    SetMine(mineboard,COL,ROW);
    //第一次点击
    First_SafeMine(mineboard, showboard, ROW, COL);
    //排雷
    FindMine(mineboard, showboard, COL, ROW);
}

总结

最后,对3个文件的代码进行汇总,如下所示。

头文件“game.h”:

//头文件引用
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#ifndef __GAME_H__
#define __GAME_H__

//常量定义区
#define EASY_CONST 10
#define COL 9
#define ROW 9
#define COLS COL+2
#define ROWS ROW+2

//函数声明
void menu();
void game();
void InitBoard(char board[ROWS][COLS], int cols,int rows, char set);
void DisplayBoard(const char board[ROWS][COLS], int cols, int rows);
void SetMine(char board[ROWS][COLS], int col, int row);
void FindMine(const char mineboard[ROWS][COLS], char showboard[ROWS][COLS],int col,int row);
int GetMineCount(const char mineboard[ROWS][COLS], int x, int y);
void OpenMine(const char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int col, int row, int x, int y);
void First_SafeMine(char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int row, int col);
int TrueMineCount(const char mineboard[ROWS][COLS], const char showboard[ROWS][COLS], int cols, int rows, int* pcount);
int Countmine(char showboard[ROWS][COLS], int col, int row);
#endif

game.c文件代码:

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

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

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

//显示棋盘
void DisplayBoard(char board[ROWS][COLS], int col, int row)
{
    int i, j;
    printf("    ");
    for (j = 1; j <= col; j++)
    {
        printf("%d ", j);
    }
    printf("\n\n");
    for (i = 1; i <= col; i++)
    {
        printf("%d   ",i);
        for (j = 1; j <= row; j++)
        {
            printf("%c ",board[i][j]);
        }
        printf("\n");
    }
}

//初始化地雷的位置
void SetMine(char board[ROWS][COLS], int col, int row)
{
    int x, y;
    int count = EASY_CONST;
    while (count)
    {
        x = rand() % col + 1;
        y = rand() % row + 1;
        if (board[x][y] == '0')
        {
            board[x][y] = '1';
            count--;
        }
    }
}
//保证第一个不踩雷
void First_SafeMine(char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int row, int col)
{
    int x = 0;
    int y = 0;
    int ret = 1;
    int count = 0;
    int a, b;
    printf("请输入要排查的坐标:");
    scanf("%d%d", &x, &y);
    if (mineboard[x][y] == '1')//如果为雷,则改为没有雷
    {
        mineboard[x][y] = '0';
        while (ret)//随机生成一个雷
        {
            a = rand() % ROW + 1;
            b = rand() % COL + 1;
            if (mineboard[a][b] == '0')
            {
                mineboard[a][b] = '1';
            }
            ret--;
        }
    }
    count = GetMineCount(mineboard, x, y);
    showboard[x][y] = count + '0';
    OpenMine(mineboard, showboard, row, col, x, y);
    DisplayBoard(showboard, row, col);
}

//统计坐标周围雷的个数
int GetMineCount(const char mineboard[ROWS][COLS], int x, int y)
{
    return mineboard[x - 1][y + 1] + mineboard[x][y + 1] + mineboard[x + 1][y + 1] +
        mineboard[x - 1][y] + mineboard[x + 1][y] +
        mineboard[x - 1][y - 1] + mineboard[x][y - 1] + mineboard[x + 1][y - 1] - 8 * '0';
}

//展开无雷区:如果该坐标周围没有雷就自动展开。
void OpenMine(const char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int col, int row,int x,int y)
{
    int ret = 0;
    ret=GetMineCount(mineboard, x, y);
    if (ret == 0)
    {
        showboard[x][y] = ' ';
        if (x > 0 && y + 1 <= col && showboard[x][y + 1] == '*')
        {
            OpenMine(mineboard, showboard, col, row, x, y + 1);
        }
        if (x - 1 > 0 && y + 1 <= col && showboard[x - 1][y + 1] == '*')
        {
            OpenMine(mineboard, showboard, col, row, x - 1, y + 1);
        }
        if (x - 1 > 0 && y >0 && showboard[x - 1][y] == '*')
        {
            OpenMine(mineboard, showboard, col, row, x - 1, y);
        }
        if (x - 1 > 0 && y - 1 >0 && showboard[x - 1][y - 1] == '*')
        {
            OpenMine(mineboard, showboard, col, row, x - 1, y - 1);
        }
        if (x > 0 && y - 1 > 0 && showboard[x][y - 1] == '*')
        {
            OpenMine(mineboard, showboard, col, row, x, y - 1);
        }
        if (x + 1 <= row && y - 1 > 0 && showboard[x + 1][y - 1] == '*')
        {
            OpenMine(mineboard, showboard, col, row, x + 1, y - 1);
        }
        if (x + 1 <= row && y > 0 && showboard[x + 1][y] == '*')
        {
            OpenMine(mineboard, showboard, col, row, x + 1, y);
        }
        if (x + 1 <=row && y + 1 <= col && showboard[x + 1][y + 1] == '*')
        {
            OpenMine(mineboard, showboard, col, row, x + 1, y + 1);
        }
    }
    else
    {
        showboard[x][y] = '0' + ret;
    }
}

//统计棋盘上旗子下对应的真雷个数
int TrueMineCount(const char mineboard[ROWS][COLS],const char showboard[ROWS][COLS], int cols, int rows,int* pcount)
{
    int i, j;
    int count = 0;
    if (*pcount > 0)
    {
        for (i = 0; i < cols; i++)
        {
            for (j = 0; j < rows; j++)
            {
                if (showboard[i][j] == '#')
                {
                    if (mineboard[i][j] == '1')
                    {
                        count++;
                    }
                }
            }
        }
    }
    return count;
}

//统计棋盘上已排除的区域
int Countmine(char showboard[ROWS][COLS],int col ,int row)
{
    int i,j;
    int check;
    int win = col * row - EASY_CONST;
    for (i = 1; i <= row; i++)
    {
        for (j = 1; j <= row; j++)
        {
            check = showboard[i][j] - '0';
            if (showboard[i][j] == ' ' || (check > 0 && check < 9))
            {
                win--;
            }
        }
    }
    return win;
}

//排雷
void FindMine(const char mineboard[ROWS][COLS], char showboard[ROWS][COLS], int col, int row)
{
    int x = 0;
    int y = 0;
    int win = 0;
    char input1;
    int bool = 1;
    int ret_trueminecount = 0;
    int flag_count = 0;
    do
    {
        //ret_trueminecount = TrueMineCount(mineboard, showboard, COLS, ROWS,&flag_count);
        printf("请选择(输入a,b或c):\n");
        printf("a.排雷 b.插旗 c.取消插旗\n");
        //scanf("%s", &input1);
        input1 = getchar();
        switch (input1)
        {
        case 'a':
        {
            printf("请输入要检查的坐标:>\n");
            int bool0 = 1;
            while (bool0) 
            {
                scanf("%d %d", &x, &y);
                if (x > 0 && x <= row && y > 0 && y <= col)
                {
                    if (showboard[x][y] != '*')
                    {
                        if (showboard[x][y] == '#')
                        {
                            printf("此处有旗子,请拔掉后再排查。\n");
                        }
                        else
                        {
                            printf("\n此处已经排查过了,请重新输入。\n");
                        }
                    }
                    else if (mineboard[x][y] == '1')
                    {
                        printf("\n很遗憾,你踩到了地雷,游戏结束!\n");
                        DisplayBoard(mineboard, col, row);
                        bool = 0;
                        break;
                    }
                    else
                    {
                        int count = GetMineCount(mineboard, x, y);
                        if (0 == count)
                        {
                            OpenMine(mineboard, showboard, COL, ROW, x, y);
                            bool0 = 0;
                            int ret_win = Countmine(showboard, COL, ROW);
                            if (ret_win == 0)
                            {
                                bool = 0;
                                win = 1;
                                break;
                            }
                        }
                        else
                        {
                            showboard[x][y] = count + '0';
                            int ret_win = Countmine(showboard, COL, ROW);
                            bool0 = 0;
                            if (ret_win == 0)
                            {
                                bool = 0;
                                win = 1;
                                break;
                            }
                        }
                        DisplayBoard(showboard, COL, ROW);
                    }
                }
                else
                {
                    printf("\n您输入的坐标有误,请重新输入\n");
                }
            }
            break;
        }
        case 'b':
        {
            int bool1 = 1;
            while (bool1)
            {
                printf("请输入要插旗的坐标:>");
                scanf("%d %d", &x, &y);
                if (x > 0 && x <= row && y > 0 && y <= col)
                {
                    if (showboard[x][y] == '*')
                    {
                        showboard[x][y] = '#';
                        flag_count++;
                        bool1 = 0;
                        ret_trueminecount = TrueMineCount(mineboard, showboard, COLS, ROWS, &flag_count);
                        if (flag_count==EASY_CONST && ret_trueminecount == EASY_CONST)
                        {
                            bool = 0;
                            win = 1;
                        }
                    }
                    else
                    {
                        printf("此处无法放置旗子。请按下列情况进行检查:\n(1)坐标是否有误;\n(2)此处已经有旗子;\n(3)此处是否已经排查过了。\n");
                    }
                }
                else
                {
                    printf("输入的坐标有误,请重新输入。\n");
                }
            }
            DisplayBoard(showboard, COL, ROW);
            break;
        }
        case 'c':
        {
            int bool2 = 1;
            int ret = FlagCount(showboard, COLS, ROWS);
            while (bool2)
            {
                if (ret == 0)
                {
                    printf("\n棋盘上没有旗子;请选择排雷或插旗。\n");
                    bool2 = 0;
                }
                else
                {
                    printf("\n请输入要拔旗的坐标:>");
                    scanf("%d %d", &x, &y);
                    if (x > 0 && x <= row && y > 0 && y <= col)
                    {
                        if (showboard[x][y] == '#')
                        {
                            showboard[x][y] = '*';
                            flag_count--;
                            bool2 = 0;
                            ret_trueminecount = TrueMineCount(mineboard, showboard, COLS, ROWS, &flag_count);
                            if (flag_count == EASY_CONST && ret_trueminecount == EASY_CONST)
                            {
                                bool = 0;
                                win = 1;
                            }
                        }
                        else
                        {
                            printf("\n此处无法移除旗子。请按下列情况进行检查:\n(1)坐标是否有误;\n(2)此处没有旗子;\n(3)此处旗子是否已经拔掉。\n");
                        }
                    }
                    else
                    {
                        printf("输入的坐标有误,请重新输入。\n");
                    }
                }
            }
            DisplayBoard(showboard, COL, ROW);
            break;
            }
        }
        if (win == 1)
        {
            printf("恭喜你,你已排出全部的雷。\n");
            bool = 0;
            DisplayBoard(mineboard,COL,ROW);
        }
    }while ( bool == 1);
}

//游戏主体部分
void game()
{
    //存储雷的位置:生成两个棋盘,一个用于打印
    char mineboard[COLS][ROWS] ;
    char showboard[COLS][ROWS];

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

    //显示棋盘
    DisplayBoard(showboard, COL, ROW);

    //布置雷
    SetMine(mineboard,COL,ROW);
    First_SafeMine(mineboard, showboard, ROW, COL);
    //排雷
    FindMine(mineboard, showboard, COL, ROW);
}

main.c文件

#define _CRT_SECURE_NO_WARNINGS 1
//扫雷游戏
#include "game.h"

int main(void)
{
    int input;
    srand((unsigned int)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);
    return 0;
}
点赞
收藏
评论区
推荐文章
Python进阶者 Python进阶者
2年前
手把手教你使用JavaScript打造一款扫雷游戏
大家好,我是皮皮。扫雷大家都玩过,今天我们就是用JavaScript来打造扫雷游戏。废话不多说,直接看下效果;上图是失败后的结果。一、思路分析我们新建一个首页,在首页放置一个点击开始游戏的按钮,动态生成100个小格,即100div;然后通过点击div进行扫雷操作,然后扫雷成功或者失败显示对应的结果;二、静态页面搭建2.1结构层
Stella981 Stella981
3年前
Android RecyclerView使用GridLayoutManager间距设置
使用RecyclerView设置间距,需要重写RecyclerView.ItemDecoration这个类。有如下的效果图需要实现,间距只有中间的格子和底部的格式之间有。Paste\_Image.png实现方法很简单,因为这个效果是每一行有3个格子,只要每行的第一个格式左边间距为0即可以。其他都设置左边距和底部距离。代码如下:publ
Stella981 Stella981
3年前
Linux下的小游戏
Linux下的小游戏非常丰富,除了有扫雷、纸牌等Windows下常见小游戏外,还有一些富有Linux特色的游戏,如数独、gbrainy等智力游戏。我们今天要介绍的是为众人喜闻乐见、容易上手的小游戏:五子棋、中国象棋、麻将连连看、宝石迷阵。1.五子棋。在“Ubuntu软件中心”里搜索bovo,安装即可。点击“新建”,即可开始新游戏。右下角选择难度。
Stella981 Stella981
3年前
Docker 部署SpringBoot项目不香吗?
  公众号改版后文章乱序推荐,希望你可以点击上方“Java进阶架构师”,点击右上角,将我们设为★“星标”!这样才不会错过每日进阶架构文章呀。  !(http://dingyue.ws.126.net/2020/0920/b00fbfc7j00qgy5xy002kd200qo00hsg00it00cj.jpg)  2
Wesley13 Wesley13
3年前
Unity3D学习笔记(二十六):MVC框架下的背包系统(1)
MVC背包!(https://oscimg.oschina.net/oscnet/dd09a8a61054c24d33e9fd2e8c3850eab02.png)需求:1、背包格子的装备是可以拖动的2、装备栏的装备也是可以拖动的3、当背包格子的装备拖动到装备栏时,如果是装备类型和装备栏类型是一致的能装上4、背包的装备是按照顺序放在
Stella981 Stella981
3年前
FZU2150 Fire Game
题目:_两个熊孩子在n\m的平地上放火玩,表示草,两个熊孩子分别选一个格子点火,火可以向上向下向左向右在有草的格子蔓延,点火的地方时间为0,蔓延至下一格的时间依次加一。求烧完所有的草需要的最少时间。如不能烧完输出1。_输入:_第一行,输入一个T,表示有T组测试数据。每组数据由一个n,m分别表示行列_1
Stella981 Stella981
3年前
200的大额人民币即将面世?央行:Yes!
点击上方蓝字关注我们!(https://oscimg.oschina.net/oscnet/2a1c2ac00bf54458a78c48a6c2e547d5.png)点击上方“印象python”,选择“星标”公众号重磅干货,第一时间送达!!(
Stella981 Stella981
3年前
Codeforces997C Sky Full of Stars 【FMT】【组合数】
题目大意:一个$n\n$的格子,每个格子由你填色,有三种允许填色的方法,问有一行或者一列相同的方案数。题目分析:标题的FMT是我吓人用的。一行或一列的问题不好解决,转成它的反面,没有一行和一列相同的方案数。从一个方向入手,比如列,把一列看成一个整体。把颜色看成二进制数,$001$,$010$,$100$。那么一列构成了一个长度为$3
可莉 可莉
3年前
200的大额人民币即将面世?央行:Yes!
点击上方蓝字关注我们!(https://oscimg.oschina.net/oscnet/2a1c2ac00bf54458a78c48a6c2e547d5.png)点击上方“印象python”,选择“星标”公众号重磅干货,第一时间送达!!(
Wesley13 Wesley13
3年前
C语言简易版扫雷
扫雷一、问题描述二、基本流程三、步骤1.菜单界面2.创建地图3.初始化地图4.打印地图5.玩家翻开坐标6.判断是否为地雷7.更新地图8.判断是否胜利四、代码实现一