且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

C语言扫雷游戏

更新时间:2022-10-01 14:07:35

一.  扫雷样板总览

C语言扫雷游戏

此图为大家在网页上就能搜到的扫雷模板,而在c语言中,我们也可以编写简易的代码从而实现上图情况。如图所示:


C语言扫雷游戏


该如何实现此界面呢,后续我会一一给大家详解:


二.  正文--->逻辑分析

通过上述简易的扫雷样式,我们可以看出,首先得先生成一个目录,告诉玩家play或exit,其次当玩家开始玩游戏时,会在玩家面前显示出一副棋盘,此时就需要创建棋盘,并对其赋予字符来初始化,后续我们就需要在棋盘内部设置好相应的雷数,最终通过用户输入坐标值来逐一排查该坐标处是否为雷。


为了使扫雷这个程序的内部代码看起来有条理 ,我们需要设置相应的文件来管理我们的代码,使其看起来更加整齐。如图所示:

C语言扫雷游戏

第一:用test.c文件来进行游戏的测试逻辑。第二:用game.c文件来进行游戏的实现逻辑。第三:用game.h文件来进行游戏实现函数的声明。在下列内容中,我们将会不断运用到这三个文件


       2.1 生成菜单

最重要的一步首先就是要在玩家面前呈现一个菜单,告诉玩家是选择玩游戏或退出游戏,则需要在test.c文件中实现这样的代码:

#include"game.h"  //引用总头文件,此做法简洁明了
void menu()
{
    printf("****************************************\n");
    printf("*******           1.play         *******\n");
    printf("*******           0.exit         *******\n");
    printf("****************************************\n");
}
void test()
{
    
    int input = 0;
    do
    {
        menu();
        printf("请输入:>");
        scanf("%d", &input);
        switch (input)  //用switch语句来处理玩家输入的信息
        {
        case 1:
            game();  //当玩家输入1时进入游戏,此时需要调用game()函数,后续会讲到
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("非法输入,请重新输入!\n");
            break;
        }
            
    } while (input);
}
int main()
{
    test();
    return 0;
}

在实现此代码内容之前,有个重要的前提:引用头文件。而在此代码的第一行#include"game.h"则就是引用了game.h的头文件,则在game.h文件中就需要完成这样的代码:

C语言扫雷游戏

当上述两个文件的代码完成后,我们来运行一下:

C语言扫雷游戏

菜单成功生成。


       2.2 创建棋盘

在编写代码前,我们先分析下棋盘真正的样子

C语言扫雷游戏

理论上来讲,真正的棋盘应该是里面所有带*的内容,可以采用创建9*9的二维数组来实现,但是我们假设一种情况,就是当玩家排查最左上角的*时,会出现越界访问,因为排查该位置是否有雷,并且有几个雷的时候,我们统计的是该坐标周围相邻8个元素,当访问最左上角*时,周围只有三个元素可供访问,其余5个则会出现越界访问,为了避免这一点,在创建数组的时候,就应该创建11*11从而规避越界访问的情况出现。如下列代码所示:

#include"game.h"
 
void game()
{
    //创建数组
    char mine[ROWS][COLS] = { 0 };   //存放布置好的雷的信息
    char show[ROWS][COLS] = { 0 };   //存放排查出的雷的信息
//此时此刻ROWS代表行数,COLS代表列数。
//ROWS和COLS并未直接赋值是为了方便以后改变棋盘大小更为便捷
 
 
 
}

这里细心的同学可能会发现,我这里竟然创建了两个数组,mine数组和show数组。其实这样创建数组是非常有必要的,因为在以后整个代码完成后,当我们运行时,真正出现在玩家眼前上的棋盘应该是show数组,mine数组只是为了以后布置雷,而show数组才是我们玩家真正要去排雷的地方。


当然光有此代码是远远不够的,因为还未定义ROWS,COLS。此时我们就需要在game.h文件中通过define定义来实现这一点:如下图代码所示:


C语言扫雷游戏


       2.3 初始化棋盘

当棋盘用二维数组创建好之后,我们就需要对棋盘进行初始化。在test.c文件中需要完成以下代码:

#include"game.h"
 
void game()
{
    //创建数组
    char mine[ROWS][COLS] = { 0 };   //存放布置好的雷的信息
    char show[ROWS][COLS] = { 0 };   //存放排查出的雷的信息
 
    //初始化数组mine全为‘0’
    InitBoard(mine, ROWS, COLS, '0');
    //初始化数组show全为‘*’
    InitBoard(show, ROWS, COLS, '*');
}

为了实现InitBoard函数的整个过程,我们需要在game.c文件中完成以下代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
    int i = 0;
    int j = 0;
    for (i = 0; i < rows; i++)
    {
        for (j = 0; j < cols; j++)
        {
            board[i][j] = set;   //初始化对应的数组
        }
    }
}
 
//通过用for循环来初始化棋盘每一个元素为相应的字符

在实现用InitBoard函数初始化棋盘之前,需要对此函数进行声明:


C语言扫雷游戏


       2.4 打印棋盘

当我们对棋盘上的内容初始化后,我们需要打印下棋盘,来检测下是什么样的一个情况,此时此刻,就需要调用DisplayBoard函数来实现棋盘的打印。则在test.c文件中需要完成下列代码:


 
 
#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
    printf("****************************************\n");
    printf("*******           1.play         *******\n");
    printf("*******           0.exit         *******\n");
    printf("****************************************\n");
}
void game()
{
    //创建数组
    char mine[ROWS][COLS] = { 0 };   //存放布置好的雷的信息
    char show[ROWS][COLS] = { 0 };   //存放排查出的雷的信息
 
    //初始化数组mine全为‘0’
    InitBoard(mine, ROWS, COLS, '0');
    //初始化数组show全为‘*’
    InitBoard(show, ROWS, COLS, '*');
    //打印棋盘
    DisplayBoard(mine, ROW, COL);
    DisplayBoard(show, ROW, COL);
}

则在game.c文件中来实现用DisplayBoard函数来打印棋盘的整个过程。如下列代码所示:

void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
    int i = 0;
    int j = 0;
 
    //列号数字的打印
    for (i = 0; i <= col; i++)
    {
        printf("%d ", i);
    }
    printf("\n");
    for (i = 1; i <= row; i++)
    {
        printf("%d ", i);  //打印行号
        for (j = 1; j <= col; j++)
        {
            printf("%c ", board[i][j]);  //打印棋盘
        }
        printf("\n");
    }
}

为了能让玩家在看到棋盘想要更加便捷的知道自己想要排查第几行第几列的坐标时,我们可以将行号数,和列数统一输出在玩家面前。同样是使用for循环语句来完成此操作。完成后,该棋盘依旧是不能正常运行,原因是我们还没有在game.h头文件中声明DisplayBoard函数。我们可以这样操作:C语言扫雷游戏当上述操作均完成后,我们就可以正常运行代码从而观察棋盘的情况啦:我们将show数组和mine数组都打印下看看:

C语言扫雷游戏



       2.5 布置雷

在我们打印好棋盘时,就需要对mine数组上装雷了,此时就需要电脑自动生成一定数量的雷数,并分布在坐标的随机位置上,就需要用到一个重要函数srand((unsigned int)time(NULL));并将mine数组上的雷与show数组进行对应,此时就可以屏蔽掉mine数组的打印,只生成show数组的打印供玩家去排雷。


则在test.c文件上,我们需要完成这样的代码:


#include"game.h"
 
void game()
{
    //创建数组
    char mine[ROWS][COLS] = { 0 };   //存放布置好的雷的信息
    char show[ROWS][COLS] = { 0 };   //存放排查出的雷的信息
 
    //初始化数组mine全为‘0’
    InitBoard(mine, ROWS, COLS, '0');
    //初始化数组show全为‘*’
    InitBoard(show, ROWS, COLS, '*');
    //打印棋盘
    //DisplayBoard(mine, ROW, COL);
    //DisplayBoard(show, ROW, COL);
 
    //布置雷
    SetMine(mine, ROW, COL);
 
    DisplayBoard(show, ROW, COL);
 
    
}
 
 
void test()
{
    srand((unsigned int)time(NULL));
    
}

类似的,我们还需要在game.c文件上实现用SetMine函数来实现布置雷的整个过程:代码如下:

void SetMine(char mine[ROWS][COLS], int row, int col)
{
    int count = EASY_COUNT;
    while (count)
    {
        int x = rand() % row + 1;
        int y = rand() % col + 1;
        if (mine[x][y] == '0')
        {
            mine[x][y] = '1';  //随机自动生成雷
            count--;
        }
    }
 
}

与此同时,还需要在game.h文件中声明函数:


C语言扫雷游戏


完成上述操作后,我们来运行下看看:


C语言扫雷游戏


可以看到下面是mine数组,上面是show数组,mine数组已经自动生成雷了,并用字符1表示


       2.6 排查雷

最后一步即为排查雷,当玩家输入他想要排查的坐标时,系统将判断改位置是否为雷,用if语句来实现,若是,则挑战失败,若不是,那么就应该返回该位置周围有几个雷,并且输出在屏幕上供玩家参考,当所有的雷全部被排查完毕,则玩家排雷成功。


在test.c文件中,我们需要完成下列代码:

#include"game.h"
void game()
{
    //创建数组
    char mine[ROWS][COLS] = { 0 };   //存放布置好的雷的信息
    char show[ROWS][COLS] = { 0 };   //存放排查出的雷的信息
 
    //初始化数组mine全为‘0’
    InitBoard(mine, ROWS, COLS, '0');
    //初始化数组show全为‘*’
    InitBoard(show, ROWS, COLS, '*');
    //打印棋盘
    //DisplayBoard(mine, ROW, COL);
    //DisplayBoard(show, ROW, COL);
 
    //布置雷
    SetMine(mine, ROW, COL);
    //DisplayBoard(show, ROW, COL);
    DisplayBoard(show, ROW, COL);
 
 
    //排雷
    FindMine(mine,show, ROW, COL);
 
 
}

我们需要在game.c文件中来实现排查雷的整个过程:

#include"game.h"
int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
    return mine[x - 1][y] +
        mine[x - 1][y - 1] +
        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] - 8 * '0';         //返回该位置周围雷的个数
}
 
 
 
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    int x = 0;
    int y = 0;
    int win = 0;
    while (win<row*col- EASY_COUNT)
    {
        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
            {
                //计算x,y坐标周围有几个雷
                int n = get_mine_count(mine, x, y);
                show[x][y] = n + '0';
                DisplayBoard(show, row, col);
                win++;
            }
        }
        else
        {
            printf("输入坐标非法,请重新输入\n");
        }
    }
    if (win == row * col - EASY_COUNT)
    {
        printf("恭喜你,排雷成功\n");
        DisplayBoard(mine, row, col);
 
    }
    
    
}

同样,我们需要在game.h文件中进行函数声明

C语言扫雷游戏

当整个代码运行完毕后,我们来看看:

C语言扫雷游戏

三.  总代码

      3.1 test.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void menu()
{
    printf("****************************************\n");
    printf("*******           1.play         *******\n");
    printf("*******           0.exit         *******\n");
    printf("****************************************\n");
}
void game()
{
    //创建数组
    char mine[ROWS][COLS] = { 0 };   //存放布置好的雷的信息
    char show[ROWS][COLS] = { 0 };   //存放排查出的雷的信息
 
    //初始化数组mine全为‘0’
    InitBoard(mine, ROWS, COLS, '0');
    //初始化数组show全为‘*’
    InitBoard(show, ROWS, COLS, '*');
    //打印棋盘
    //DisplayBoard(mine, ROW, COL);
    //DisplayBoard(show, ROW, COL);
 
    //布置雷
    SetMine(mine, ROW, COL);
    DisplayBoard(mine, ROW, COL);
    DisplayBoard(show, ROW, COL);
 
 
    //排雷
    FindMine(mine,show, ROW, COL);
 
 
}
void test()
{
    srand((unsigned int)time(NULL));
    int input = 0;
    do
    {
        menu();
        printf("请输入:>");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            game();
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("非法输入,请重新输入!\n");
            break;
        }
            
    } while (input);
}
int main()
{
    test();
    return 0;
}

3.2 game.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set)
{
    int i = 0;
    int j = 0;
    for (i = 0; i < rows; i++)
    {
        for (j = 0; j < cols; j++)
        {
            board[i][j] = set;   //初始化对应的数组
        }
    }
}
 
void DisplayBoard(char board[ROWS][COLS], int row, int col)
{
    int i = 0;
    int j = 0;
 
    //列号数字的打印
    for (i = 0; i <= col; i++)
    {
        printf("%d ", i);
    }
    printf("\n");
    for (i = 1; i <= row; i++)
    {
        printf("%d ", i);  //打印行号
        for (j = 1; j <= col; j++)
        {
            printf("%c ", board[i][j]);  //打印棋盘
        }
        printf("\n");
    }
}
 
void SetMine(char mine[ROWS][COLS], int row, int col)
{
    int count = EASY_COUNT;
    while (count)
    {
        int x = rand() % row + 1;
        int y = rand() % col + 1;
        if (mine[x][y] == '0')
        {
            mine[x][y] = '1';  //随机自动生成雷
            count--;
        }
    }
 
}
 
 
int get_mine_count(char mine[ROWS][COLS], int x, int y)
{
    return mine[x - 1][y] +
        mine[x - 1][y - 1] +
        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] - 8 * '0';         //返回该位置周围雷的个数
}
 
 
 
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    int x = 0;
    int y = 0;
    int win = 0;
    while (win<row*col- EASY_COUNT)
    {
        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
            {
                //计算x,y坐标周围有几个雷
                int n = get_mine_count(mine, x, y);
                show[x][y] = n + '0';
                DisplayBoard(show, row, col);
                win++;
            }
        }
        else
        {
            printf("输入坐标非法,请重新输入\n");
        }
    }
    if (win == row * col - EASY_COUNT)
    {
        printf("恭喜你,排雷成功\n");
        DisplayBoard(mine, row, col);
 
    }
    
    
}
 

    3.3 game.h文件

#pragma once
//头文件的包含
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
 
//符号的声明
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define EASY_COUNT 10
 
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols, char set);
 
//打印棋盘
void DisplayBoard(char board[ROWS][COLS], int row, int col);
 
//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col);
 
//排查雷
void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);