用C语言写一个扫雷游戏

相信大家都玩过扫雷游戏吧,那它究竟是怎样实现的呢,大家或许会把它想得很复杂,但它的C语言代码其实挺简单的,今天我们就来看看我们经常玩的扫雷游戏的源代码吧,探究探究它的内部实现,揭开它的神秘面纱。
首先,我们得明确设计者雷盘的大小和玩家看到的雷盘大小是不同的,因为在判断周围雷数的时候,我们得判断周围8个地方,如果设计者数组大小和玩家数组大小相同,在边上就会出现数组越界问题,下面我来画一个图帮助大家理解一下。

这里写图片描述

头文件 game.h

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

#define R 12
#define C 12
#define COUNT 10

show[R][C];//玩家数组
mine[R][C];//设计者数组
void init_board();//初始化雷盘
void print_player();//打印玩家棋盘
void print_mine();//打印设计者棋盘
void set_mine();//给设计者棋盘布雷
int count_mine(int x, int y);//检测周围8个区域雷的个数
void safe_mine();//避免第一次被雷炸死
void open_mine(int x, int y);//坐标周围展开函数
int sweep_mine();//扫雷函数,踩到雷返回1,没有踩到雷返回0
int count_show();//判断剩余未知区域的个数,个数为雷数时玩家赢

测试源文件 test.c

#include "game2.h"

void init_board()//初始化两个雷阵
{
    int i = 0;
    int j = 0;
    for (i = 0; i < R; i++)
    {
        for (j = 0; j < C; j++)
        {
            show[i][j] = '*';
            mine[i][j] = '0';
        }
    }
}

void print_player()//打印玩家棋盘
{
    int i = 0;
    int j = 0;
    printf("0  ");
    for (i = 1; i < R - 1; i++)
    {
        printf("%d ", i);//打印横标
    }
    printf("\n");
    for (i = 1; i < C - 2; i++)//打印竖标
    {
        printf("%d  ", i);
        for(j = 1; j < C - 1; j++)
        {
            printf("%c ", show[i][j]);
        }
        printf("\n");
    }
    printf("10 ");//开始打印最后一行
    for (i = 1; i < R - 1; i++)
    {
        printf("%c ", show[10][i]);
    }
    printf("\n");
}

void print_mine()//打印设计者棋盘
{
    int i = 0;
    int j = 0;
    printf("0  ");
    for (i = 1; i < R - 1; i++)
    {
        printf("%d ", i);//打印横标
    }
    printf("\n");
    for (i = 1; i < C - 2; i++)//打印竖标
    {
        printf("%d  ", i);
        for (j = 1; j < C - 1; j++)
        {
            printf("%c ", mine[i][j]);
        }
        printf("\n");
    }
    printf("10 ");//开始打印最后一行
    for (i = 1; i < R - 1; i++)
    {
        printf("%c ", mine[10][i]);
    }
    printf("\n");
}

void set_mine()//给设计者棋盘布雷
{
    int x = 0;
    int y = 0;
    int count = COUNT;//雷总数
    while (count)//雷布完后跳出循环
    {
        int x = rand() % 10 + 1;//产生1-10的随机数,在数组下标为1-10的范围内布雷
        int y = rand() % 10 + 1;//产生1-10的随机数,在数组下标为1-10的范围内布雷
        if (mine[x][y] == '0')//找不是雷的地方布雷
        {
            mine[x][y] = '1';
            count--;
        }
    }
}

int count_mine(int x, int y)//检测周围8个区域雷的个数
{
    int count = 0;
    if (mine[x - 1][y] == '1')
        count++;
    if (mine[x - 1][y - 1] == '1')
        count++;
    if (mine[x - 1][y + 1] == '1')
        count++;
    if (mine[x][y - 1] == '1')
        count++;
    if (mine[x][y + 1] == '1')
        count++;
    if (mine[x + 1][y - 1] == '1')
        count++;
    if (mine[x + 1][y] == '1')
        count++;
    if (mine[x + 1][y + 1] == '1')
        count++;
    return count;
}

void safe_mine()//避免第一次被雷炸死
{
    int x = 0;
    int y = 0;
    char ch = 0;
    int ret = 1;
    printf("输入坐标扫雷\n");
    while (1)
    {
        scanf("%d%d", &x, &y);//只能输入1-10,输入错误重新输入
        if (((x >= 1) && (x <= 10)) && ((y >= 1) && (y <= 10)))//判断坐标是否有误
        {
            if (mine[x][y] == '1')//第一次踩到雷后补救
            {
                mine[x][y] = '0';
                while (ret)//在其余有空的地方设置一个雷
                {
                    int x1 = rand() % 10 + 1;
                    int y1 = rand() % 10 + 1;
                    if (mine[x1][y1] == '0'&&(x!=x1||y!=y1))//找不是雷的地方布雷
                    {
                        mine[x1][y1] = '1';
                        ret--;
                        break;
                    }
                }break;//跳出此函数
                char ch = count_mine(x, y);
                open_mine(x, y);
            }
            if (mine[x][y] == '0')
            {
                char ch = count_mine(x, y);
                open_mine(x, y);
                break;
            }
        }
        else//坐标错误
        {
            printf("请重新输入坐标\n");
        }
    }
}

void open_mine(int x, int y)//坐标周围展开函数
{
    if (show[x][y] == '*' && ((x >= 1) && (x <= 10)) && ((y >= 1) && (y <= 10)))
    {
        if (count_mine(x, y) != 0)
        {
            show[x][y] = count_mine(x, y) + '0';
        }
        else 
        {
            show[x][y] = ' ';
            open_mine(x - 1, y);
            open_mine(x - 1, y - 1);
            open_mine(x - 1, y + 1);
            open_mine(x, y - 1);
            open_mine(x, y + 1);
            open_mine(x + 1, y - 1);
            open_mine(x + 1, y);
            open_mine(x + 1, y + 1);
        }
    }
    return;
}

int sweep_mine()//扫雷函数,踩到雷返回1,没有踩到雷返回0
{
    int x = 0;
    int y = 0;
    printf("请输入坐标扫雷\n");
    scanf("%d%d", &x, &y);//只能输入1-10
    if (((x >= 1) && (x <= 10)) && ((y >= 1) && (y <= 10)))
    {
        if (mine[x][y] == '0')//没踩到雷
        {
            open_mine(x, y);
            if (count_show() == COUNT)//判断剩余未知区域的个数,个数为雷数时玩家赢
            {
                print_mine();
                printf("恭喜你,你赢了\n");
                return 0;
            }
        }
        else if (mine[x][y] == '1')//踩到雷
        {
            return 1;
        }
    }
    else
    {
        printf("请重新输入:\n");
    }
    return 0;//没踩到雷
}

int count_s#define _CRT_SECURE_NO_WARNINGS 1

#include "game2.h"

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

void game()
{
    int x = 0;
    int y = 0;
    int ret = 0;
    init_board();//初始化棋盘
    set_mine();//给设计者棋盘布雷
    //print_mine();//打印设计者棋盘
    print_player();//打印玩家棋盘
    safe_mine();//避免第一次被炸死
    if (count_show() == COUNT)//一步就赢的情况
    {
        print_mine();
        printf("恭喜你,你赢了\n");
        return;
    }print_player();//打印玩家棋盘
    while (1)//循环扫雷
    {
        int ret = sweep_mine();//扫雷,踩到雷返回1,没有踩到雷返回0
        if (count_show() == COUNT)
        {
            print_mine();//打印设计者数组
            printf("恭喜你,你赢了\n");
            break;
        }
        if (ret)//判断是否踩到雷
        {
            printf("很遗憾,你输了\n");
            print_mine();//打印设计者棋盘
            break;
        }print_player();//打印玩家棋盘
    }

}

int main()
{
    srand((unsigned int)time(NULL));//产生随机数生成器
    int num;
    do
    {
        menu();
        printf("请选择>\n");
        scanf("%d", &num);
        switch (num)
        {
        case 1:
            game();
            //printf("进行游戏\n");
            break;
        case 0:
            printf("推出游戏\n");
            break;
        default:
            printf("enter data error\n");
        }
    } while (num);

    return 0;
}

在测试文件里边我们最重要是明白游戏的核心逻辑,首先我们要进入game()函数后,我们要先初始化雷盘,然后再布雷,最后打印出玩家数组,我们在玩的时候还要避免第一次踩雷,如果第一次踩雷了我们就需要把雷挪开,用safe_mine()函数实现,这些工作做完之后我们要进行循环扫雷,如果赢了或输了,我们最后也要打印出设计者数组,让玩家们死的明白。

源文件 game.c

#include "game2.h"

void init_board()//初始化两个雷阵
{
    int i = 0;
    int j = 0;
    for (i = 0; i < R; i++)
    {
        for (j = 0; j < C; j++)
        {
            show[i][j] = '*';
            mine[i][j] = '0';
        }
    }
}

void print_player()//打印玩家棋盘
{
    int i = 0;
    int j = 0;
    printf("0  ");
    for (i = 1; i < R - 1; i++)
    {
        printf("%d ", i);//打印横标
    }
    printf("\n");
    for (i = 1; i < C - 2; i++)//打印竖标
    {
        printf("%d  ", i);
        for(j = 1; j < C - 1; j++)
        {
            printf("%c ", show[i][j]);
        }
        printf("\n");
    }
    printf("10 ");//开始打印最后一行
    for (i = 1; i < R - 1; i++)
    {
        printf("%c ", show[10][i]);
    }
    printf("\n");
}

void print_mine()//打印设计者棋盘
{
    int i = 0;
    int j = 0;
    printf("0  ");
    for (i = 1; i < R - 1; i++)
    {
        printf("%d ", i);//打印横标
    }
    printf("\n");
    for (i = 1; i < C - 2; i++)//打印竖标
    {
        printf("%d  ", i);
        for (j = 1; j < C - 1; j++)
        {
            printf("%c ", mine[i][j]);
        }
        printf("\n");
    }
    printf("10 ");//开始打印最后一行
    for (i = 1; i < R - 1; i++)
    {
        printf("%c ", mine[10][i]);
    }
    printf("\n");
}

void set_mine()//给设计者棋盘布雷
{
    int x = 0;
    int y = 0;
    int count = COUNT;//雷总数
    while (count)//雷布完后跳出循环
    {
        int x = rand() % 10 + 1;//产生1-10的随机数,在数组下标为1-10的范围内布雷
        int y = rand() % 10 + 1;//产生1-10的随机数,在数组下标为1-10的范围内布雷
        if (mine[x][y] == '0')//找不是雷的地方布雷
        {
            mine[x][y] = '1';
            count--;
        }
    }
}

int count_mine(int x, int y)//检测周围8个区域雷的个数
{
    int count = 0;
    if (mine[x - 1][y] == '1')
        count++;
    if (mine[x - 1][y - 1] == '1')
        count++;
    if (mine[x - 1][y + 1] == '1')
        count++;
    if (mine[x][y - 1] == '1')
        count++;
    if (mine[x][y + 1] == '1')
        count++;
    if (mine[x + 1][y - 1] == '1')
        count++;
    if (mine[x + 1][y] == '1')
        count++;
    if (mine[x + 1][y + 1] == '1')
        count++;
    return count;
}

void safe_mine()//避免第一次被雷炸死
{
    int x = 0;
    int y = 0;
    char ch = 0;
    int ret = 1;
    printf("输入坐标扫雷\n");
    while (1)
    {
        scanf("%d%d", &x, &y);//只能输入1-10,输入错误重新输入
        if (((x >= 1) && (x <= 10)) && ((y >= 1) && (y <= 10)))//判断坐标是否有误
        {
            if (mine[x][y] == '1')//第一次踩到雷后补救
            {
                mine[x][y] = '0';
                while (ret)//在其余有空的地方设置一个雷
                {
                    int x1 = rand() % 10 + 1;
                    int y1 = rand() % 10 + 1;
                    if (mine[x1][y1] == '0'&&(x!=x1||y!=y1))//找不是雷的地方布雷
                    {
                        mine[x1][y1] = '1';
                        ret--;
                        break;
                    }
                }break;//跳出此函数
                char ch = count_mine(x, y);
                open_mine(x, y);
            }
            if (mine[x][y] == '0')
            {
                char ch = count_mine(x, y);
                open_mine(x, y);
                break;
            }
        }
        else//坐标错误
        {
            printf("请重新输入坐标\n");
        }
    }
}

void open_mine(int x, int y)//坐标周围展开函数
{
    if (show[x][y] == '*' && ((x >= 1) && (x <= 10)) && ((y >= 1) && (y <= 10)))
    {
        if (count_mine(x, y) != 0)
        {
            show[x][y] = count_mine(x, y) + '0';
        }
        else 
        {
            show[x][y] = ' ';
            open_mine(x - 1, y);
            open_mine(x - 1, y - 1);
            open_mine(x - 1, y + 1);
            open_mine(x, y - 1);
            open_mine(x, y + 1);
            open_mine(x + 1, y - 1);
            open_mine(x + 1, y);
            open_mine(x + 1, y + 1);
        }
    }
    return;
}

int sweep_mine()//扫雷函数,踩到雷返回1,没有踩到雷返回0
{
    int x = 0;
    int y = 0;
    printf("请输入坐标扫雷\n");
    scanf("%d%d", &x, &y);//只能输入1-10
    if (((x >= 1) && (x <= 10)) && ((y >= 1) && (y <= 10)))
    {
        if (mine[x][y] == '0')//没踩到雷
        {
            open_mine(x, y);
            if (count_show() == COUNT)//判断剩余未知区域的个数,个数为雷数时玩家赢
            {
                print_mine();
                printf("恭喜你,你赢了\n");
                return 0;
            }
        }
        else if (mine[x][y] == '1')//踩到雷
        {
            return 1;
        }
    }
    else
    {
        printf("请重新输入:\n");
    }
    return 0;//没踩到雷
}

int count_show()//判断剩余未知区域的个数,个数为雷数时玩家赢
{
    int i = 0;
    int j = 0;
    int count = 0;
    for (i = 1; i <= R - 2; i++)
    {
        for (j = 1; j <= C - 2; j++)
        {
            if (show[i][j] == '*')
            {
                count++;
            }
        }
    }
    return count;
}

在game.c中最主要的就是空白区域的展开,要连片炸开,一直炸到格子显示数字为止,对此,我们可以使用递归算法来解决这个问题。并且我们还要显示该点周围雷的个数。

这里写图片描述

这里写图片描述

这里写图片描述

猜你喜欢

转载自blog.csdn.net/huaijiu123/article/details/80111601