一、扫雷游戏分析

关键步骤:
两个二维数组的大小为11*11,但实际上操作的只有中心的9*9的棋盘,创建另外两行的原因是方便统计一个坐标周围3*3的雷的个数
1.创建两个二维数组一个存放布置好的雷(1号),另外一个存放空的棋盘(2号)
2.选手选出来的坐标传到1号棋盘上对坐标进行分析如果是雷就返回被炸死了,不是雷就算一下这个坐标周边一圈
有多少雷,并在2号棋盘上显示出来
结束标志:没被炸死,且棋盘上还剩下雷的个数的位置没被探索

二、代码

game.h

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define MINES 10

//初始化
void InitBoard(char board[ROWS][COLS], int rows, int cols, char ch);
//展示
void DisPlay(char board[ROWS][COLS], int row, int col);
//布置雷
void LayMine(char board[ROWS][COLS], int row, int col);
//开始游戏
void Play(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"

//初始化
void InitBoard(char board[ROWS][COLS], int rows, int cols, char ch)
{
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            board[i][j] = ch;
        }
    }
}
//展示
void DisPlay(char board[ROWS][COLS], int row, int col)
{
    for (int i = 0; i <= col; i++)
    {
        printf("%d ", i);
    }
    printf("\n");
    for (int i = 1; i <= row; i++)
    {
        printf("%d ", i);
        for (int j = 1; j <= col; j++)
        {
            printf("%c ", board[i][j]);
        }
        printf("\n");
    }
}
//布置雷
void LayMine(char board[ROWS][COLS], int row, int col)
{
    int x, y;
    int count = MINES;//雷的数量
    //要把雷布置在棋盘1-9的位置上
    while (count)
    {
        x = rand() % 9 + 1;
        y = rand() % 9 + 1;//生成1-9的随机坐标
        if (board[x][y] == '0')
        {
            board[x][y] = '1';
            count--;
        }
    }
}
//(x-1,y-1) (x-1,y) (x-1,y+1)
//(x,y-1)   (x,y)   (x,y+1)
//(x+1,y-1) (x+1,y) (x+1,y+1)
//这个函数是用来统计输入的坐标旁边一共有多少个雷的
int CountMine(char mine[ROWS][COLS], int x, int y)
{
    return mine[x - 1][y - 1] + mine[x - 1][y] + mine[x - 1][y + 1] + mine[x][y - 1] + mine[x][y + 1] +
        mine[x + 1][y - 1] + mine[x + 1][y] + mine[x + 1][y + 1] - 7*'0';
}
//该函数通过递归来实现一片空白区域的连续展开
void Expand(char show[ROWS][COLS], char mine[ROWS][COLS], int x, int y)
{
    //条件:如果show[x][y]处为'*'就进行展开求值
    if(show[x][y]=='*')
    {
        show[x][y] = CountMine(mine, x, y);
    }
    //&&x>1&&x<9&&y>1&&y<9  //用于调试
    if (show[x][y] == '0' && x >= 1 && x <= 9 && y>=1 && y <= 9)//这一句很重要,要不然一次性就把棋盘外面的一起展开了
    {
        if (show[x - 1][y - 1] == '*')//同样判断要展开的位置是否是'*'也很重要,要不然就会栈溢出陷入死递归
        {
            Expand(show, mine, x - 1, y - 1);
        }
        if(show[x - 1][y] == '*')
        {
            Expand(show, mine, x - 1, y);
        }
        if (show[x - 1][y+1] == '*')
        {
            Expand(show, mine, x - 1, y + 1);
        }
        if (show[x][y-1] == '*')
        {
            Expand(show, mine, x, y - 1);
        }
        if (show[x][y+1] == '*')
        {
            Expand(show, mine, x, y + 1);
        }
        if (show[x+1][y-1] == '*')
        {
            Expand(show, mine, x + 1, y-1);
        }
        if (show[x + 1][y] == '*')
        {
            Expand(show, mine, x + 1, y );
        }
        if (show[x + 1][y +1] == '*')
        {
            Expand(show, mine, x + 1, y + 1);
        }
    }
}
//正式游戏
void Play(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    int x, y;
    while (1)
    {
        int count = 0;
        printf("请输入坐标:>");
        scanf("%d%d", &x, &y);
        if (x >= 1 && x <=9 && y >= 1 && y <= 9)//确保合理输入范围
        {
            if (mine[x][y] == '1')
            {
                printf("Game Over\n");
                DisPlay(mine, ROW, COL); //用于显示失败后的雷区
                break;
            }
            //else if (mine[x][y] != '1')
            //{
            //    show[x][y] = CountMine(mine,x,y);
            //    /*printf("%d\n", CountMine(show, x, y));*///用于测试
            //    DisPlay(show, ROW, COL);
            //}
            else if (mine[x][y] != '1')
            {
                show[x][y] = CountMine(mine, x, y);
                if (show[x][y] == '0')
                {
                    Expand(show, mine, x, y);
                }
                DisPlay(show, ROW, COL);
            }
        }
        else
        {
            printf("Error Location\n");
        }
        //判断是否成功,当show中剩余的‘*’等于雷的个数时且还没被炸死时就赢了
        for (int i = 1; i <= row; i++)
        {
            for (int j = 1; j <= col; j++)
            {
                if (show[i][j] == '*')
                {
                    count++;
                }
            }
        }
        if (count == MINES)
        {
            printf("Win\n");
            DisPlay(mine, ROW, COL); //用于显示胜利后的雷区
            break;
        }
    }
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include "game.h"
menu() //选择菜单
{
    printf("                    \n");
    printf("   1.Play  0.Exit   \n");
    printf("                    \n");
}

void game()
{
    char mine[ROWS][COLS] = { 0 };//用于存放地雷
    char show[ROWS][COLS] = { 0 };//用于展示
    InitBoard(mine, ROWS, COLS, '0');//将第一个放雷的棋盘全部初始化成'0'
    InitBoard(show, ROWS, COLS, '*');//将第二个展示的棋盘全部初始化成'*'
    //DisPlay(mine, ROW, COL);//只展示中心9*9的棋盘
    DisPlay(show, ROW, COL);
    LayMine(mine, ROW, COL);//在mine棋盘里布置雷
    //DisPlay(mine, ROW, COL);//用于测试
    //开始玩扫雷
    Play(mine, show,ROW,COL);
}
int main()
{
    srand((unsigned int)time(NULL));//用于生成随机雷
    int input;//玩家要输入的选择
    do
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            game();//游戏主体
            break;
        case 0:
            printf("Exit Game\n");
            break;
        default:
            printf("Error Choice\n");
            break;
        }
    } while (input);
    return 0;
}

三、测试结果展示