C语言小游戏--扫雷

这期我们来分享一个我们小时候都玩过的小游戏--扫雷,关于编写扫雷同样也需要有一定的代码能力,和对基础知识的掌握,以及对大型代码的组织能力,好滴,话不多说,正文开始:

💓扫雷:

扫雷都基本玩过,但是这期我们做出来扫雷肯定不会像网页或者电脑自带的那样精致,只是实现游戏的逻辑,和运行,当然,还有许多比较复杂的功能就不写入了,感兴趣的老铁可以自己来琢磨琢磨,我们先来看扫雷的基本效果:

基本的游戏效果展现在这里,接下来就是庖丁解牛,一点一点的进行编写代码来实现这样的一个扫雷游戏

💓文件的创建:

为了逻辑关系清晰,我们同样采用多个文件的方式编写,方法和三子棋的方法一样

创建头文件game.h:用来包含头文件、函数的声明、以及一些变量的定义

创建源文件game.c:用来实现关于游戏函数的实现

创建源文件test.c:用来测试游戏的基本逻辑

(文件的命名可以随便,但是得有意义)

💓游戏基本逻辑:

创建好文件之后,先在源文件test.c中实现基本的逻辑关系:

首先,无论怎样,先打印菜单供玩家选择,如果进行完一把之后不过瘾,还可以再来一把,如果不想玩就可以退出,所以使用一个do while 循环

源文件:test.c:

#define _CRT_SECURE_NO_WARNINGS 1
//源文件test.c
#include "game.h"

void menu()
{
    printf("************************\n");
    printf("*******  1. Play  ******\n");
    printf("*******  0. Exit  ******\n");
    printf("************************\n");
}
int main()
{
    int input = 0;
    do          
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        
    } while(input);   //进入循环的条件是input为真
    return 0; 
}

头文件:game.h:

#pragma once
//头文件game.h

//头文件的包含

#include<stdio.h>

先打印出基本的菜单,然后就需要进行选择玩游戏还是退出游戏,所以需要用到switch case语句来进行选择

源文件:test.c:

int main()
{
    int input = 0;
    do          
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        switch(input)
        {
        case 1:
            printf("扫雷游戏\n");
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default :
            printf("输入错误,请重新输入\n");    
            break;
        }
        
    } while(input);   //进入循环的条件是input为真
    return 0; 
}

可以看到,游戏的基本的底层逻辑实现了,跟三子棋的底层逻辑大同小异,这个问题应该不大,接下来就是关于扫雷这个函数实现的基本逻辑

💓游戏函数的实现:

当我们选1,进入扫雷游戏,不只是简单的打印扫雷游戏,我们要分装一系列的函数对扫雷逐步编写,所以我们设置一个game函数里面来进行关于游戏函数的编写

源文件:test.c:

void game()
{
    //创建一个二维数组用来放置我们排雷时所看到的棋盘
    //再创建一个二维数组用来布置雷
    //初始化棋盘
    //打印棋盘
    //布置雷
    //排雷
}
int main()
{
    int input = 0;
    do          
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        switch(input)
        {
        case 1:
            printf("扫雷游戏\n");
            game();          //用game函数来实现游戏逻辑
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default :
            printf("输入错误,请重新输入\n");
            break;
        }
        
    } while(input);   //进入循环的条件是input为真
    return 0; 
}

接下来就是关于这个game函数的编写,我们就可以按照上面的基本思路来逐步编写

先来创建这两个棋盘:

在创建棋盘之前呢?我们来思考这样一个问题,当我们排雷时,如果排到一个坐标没有雷,那么这个坐标周围有几个雷就会显示在这个坐标上,但是呢,当我们排到最外层时,检测的时候最外面的会产生越界访问的结果,所以呢我们就需要再将这个棋盘再扩大一圈来防止越界访问的问题,但是呢,在布置雷的时候只需要布置在没有扩大的棋盘上,并且我们在打印时也只需要打印没有扩大的棋盘,所以我们在定义二维数组的行和列时直接用#define来定义,同时也方便后期的修改

头文件:game.h:

#pragma once
//头文件game.h

//头文件的包含

#include<stdio.h>

//变量的定义
#define ROW 9
#define COL 9

#define ROWS ROW+2       //大一圈的行和列
#define COLS COL+2

源文件:test.c:

void game()
{
    //创建一个二维数组用来放置我们排雷时所看到的棋盘
    //再创建一个二维数组用来布置雷
    char mine[ROWS][COLS] = { 0 };   //用来显示雷的位置
    char show[ROWS][COLS] = { 0 };   //用来排雷
    //初始化棋盘
    //打印棋盘
    //布置雷
    //排雷
}

创建好了两个棋盘,那么就来对棋盘初始化

💓初始化棋盘:

我们在创建棋盘时,可以创建两个类型相同的棋盘,这样就可以用一个函数来对两个棋盘进行初始化,分装一个函数:InitBoard进行初始化,那初始化的时候,我们在排雷的棋盘上先全部初始化为‘*’,在显示雷的位置上的棋盘先初始化为‘0’,所以我们需要在传参的时候将它们所需要初始化的字符传给函数,这样才可以实现一个函数初始化两个数组

首先在头文件中声明函数

头文件:game.h:

#pragma once
//头文件game.h

//头文件的包含
#include<stdio.h>

//变量的定义
#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

//函数的声明
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set);  //用set来接收字符

源文件:test.c:

void game()
{
    //创建一个二维数组用来放置我们排雷时所看到的棋盘
    //再创建一个二维数组用来布置雷
    char mine[ROWS][COLS] = { 0 };   //用来显示雷的位置
    char show[ROWS][COLS] = { 0 };   //用来排雷
    //初始化棋盘
    InitBoard(mine, ROWS, COLS, '0');   //将要初始化的字符也传给函数
    InitBoard(show, ROWS, COLS, '*');    
    //打印棋盘
    //布置雷
    //排雷
}

源文件:game.c:

#define _CRT_SECURE_NO_WARNINGS 1

//源文件game.c
#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;      //将字符初始化给数组
        }
    }
}

将两个棋盘初始化之后,就可以打印出这两个棋盘

💓打印棋盘:

在打印棋盘的时候要注意,我们只需要打印出9x9的棋盘, 但是呢,这个9x9的棋盘是在11x11的棋盘中的,所以要注意形参在接收时的数组是11x11的,但是我们需要打印的是9x9的,所以形参接收的行和列是9行9列。所以,分装一个函数PrintfBoard来打印棋盘

头文件:game.h:

//函数的声明
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set);

//打印棋盘
void PrintfBoard(char board[ROWS][COLS], int row, int col);

源文件:test.c:

void game()
{
    //创建一个二维数组用来放置我们排雷时所看到的棋盘
    //再创建一个二维数组用来布置雷
    char mine[ROWS][COLS] = { 0 };   //用来显示雷的位置
    char show[ROWS][COLS] = { 0 };   //用来排雷
    //初始化棋盘
    InitBoard(mine, ROWS, COLS, '0');
    InitBoard(show, ROWS, COLS, '*');
    //打印棋盘
    PrintfBoard(show, ROW, COL);
    PrintfBoard(mine, ROW, COL); //这个棋盘在玩的时候不能打印,这里只是在测试一下打印出来的效果
    //布置雷
    //排雷
}

源文件:game.c:

//打印棋盘
void PrintfBoard(char board[ROWS][COLS], int row, int col)
{
    int i = 0;
    int j = 0;   //遍历每一个元素然后打印
    for (i = 1; i <= row; i++)
    {
        for (j = 1; j <= col; j++)
        {
            printf("%c ", board[i][j]);
        }
        printf("\n");
    }
}

我们可以看到打印出的棋盘很混乱,没有标识,没有分界线,所以还得完善一下,给两个棋盘加上标识的数字和分割的标志

完善后的棋盘:

源文件:game.c:

//打印棋盘
void PrintfBoard(char board[ROWS][COLS], int row, int col)
{
    int i = 0;
    int j = 0;
    printf(" ****** 扫雷 ******\n");  //添加分割标志
    for (i = 0; i <= row; 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");
    }
}

打印完棋盘接下来就到了布置雷的环节了

💓布置雷:

布置雷同样也是在9x9的棋盘中布置,所以和打印棋盘一样的道理,我们就让随机布置雷,就需要用到随机数,和三子棋中的电脑随机下棋是一样的。我们分装一个函数 SetMine来布置雷

头文件:game.h:

//函数的声明
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set);

//打印棋盘
void PrintfBoard(char board[ROWS][COLS], int row, int col);

//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col);

源文件:test.c:

void game()
{
    //创建一个二维数组用来放置我们排雷时所看到的棋盘
    //再创建一个二维数组用来布置雷
    char mine[ROWS][COLS] = { 0 };   //用来显示雷的位置
    char show[ROWS][COLS] = { 0 };   //用来排雷
    //初始化棋盘
    InitBoard(mine, ROWS, COLS, '0');
    InitBoard(show, ROWS, COLS, '*');
    //打印棋盘
    PrintfBoard(show, ROW, COL);
    //PrintfBoard(mine, ROW, COL);
    //布置雷
    SetMine(mine, ROW, COL);
    PrintfBoard(mine, ROW, COL);   //布置完雷之后将其打印以便展示

    //排雷
}

头文件:game.h:

我们可以定义一个简单版的雷的个数,设置为EASY_COUNT,方便后面修改雷的个数

//变量的定义
#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

#define EASY_COUNT 10       //设置10个雷

源文件:game.c:

//布置雷
void SetMine(char mine[ROWS][COLS], int row, int col)
{
    int x = 0;
    int y = 0;
    int count = EASY_COUNT;     
    while (count)   //循环进入的条件是count为真
    {
        x = rand() % row + 1;    //设置随机数,给rand%row的范围是0~8,但是加上1范围就是1~9
        y = rand() % col + 1;    //正好可以在这个9x9的棋盘中随机布置雷
        if (mine[x][y] == '0')
        {
            mine[x][y] = '1';
            count--;         //假如布置成功,则count--直到count为0
        }
    }
}

使用随机数函数要在主函数中设置,并且要包含对应的头文件

源文件:test.c :

int main()
{
    srand((unsigned int)time(NULL)); //包含对应的头文件
    int input = 0;
     ...
     ...
}

头文件:game.h:

#pragma once
//头文件game.h

//头文件的包含

#include<stdio.h>
#include<stdlib.h>        //使用随机数
#include<time.h>          //使用时间戳

//变量的定义
#define ROW 9
#define COL 9

#define ROWS ROW+2
#define COLS COL+2

#define EASY_COUNT 10     //设置简单难度10个雷

当我们后期要改变游戏难度时,就可以任意修改EASY_COUNT的值

💓排雷:

布置完雷,就到了排雷的部分了,所以我们分装一个函数FindMine用来排雷,这里要注意,我们排雷时候也是在排9x9棋盘中的雷,所以和布置雷的情况相同,但是呢,我们在排雷的时候需要用到两个数组,用来排雷的数组,和用来显示雷的数组,因为我们是在显示雷的数组中布置了雷,所以要把它传给这个函数,又因为我们需要排雷,所以得把这个用来排雷的数组也传给这个函数。,要注意排雷也是一个循环,只有被炸到或者是排雷成功才可以跳出这个循环,还要注意如果排查到的坐标不是雷,那么就需要统计周围一圈的坐标雷的个数。

头文件:game.h:

//函数的声明
//初始化棋盘
void InitBoard(char board[ROWS][COLS], int rows, int cols,char set);

//打印棋盘
void PrintfBoard(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);

源文件:test.c:

void game()
{
    //创建一个二维数组用来放置我们排雷时所看到的棋盘
    //再创建一个二维数组用来布置雷
    char mine[ROWS][COLS] = { 0 };   //用来显示雷的位置
    char show[ROWS][COLS] = { 0 };   //用来排雷
    //初始化棋盘
    InitBoard(mine, ROWS, COLS, '0');
    InitBoard(show, ROWS, COLS, '*');
    //打印棋盘
    PrintfBoard(show, ROW, COL);
    //PrintfBoard(mine, ROW, COL);
    //布置雷
    SetMine(mine, ROW, COL);
    PrintfBoard(mine, ROW, COL);
    //排雷
    FindMine(show, mine, ROW, COL);
}

源文件:game.c:

//排查雷
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(show[x][y]=='1')
            {
                printf("很遗憾,游戏失败\n");
                break;
            }
            else
            {   
                //统计雷的个数       //这里需要再分装一个函数来统计坐标周围有多少个雷
                GetMineCount();
                win++;
            }
        }
        else
        {
            printf("输入错误,请重新输入:");
        }
    }
    if (win = row * col - EASY_COUNT)       //如果每一次输入的坐标都不是雷
    {                                       //win++,直到win==row * col - EASY_COUNT
        printf("恭喜你,排雷成功!\n");      //则证明排雷成功
    }
}

💓统计雷的个数:

当我们输入一个坐标,假如它不是雷,那么需要统计这个坐标周围一共有多少个雷,所以需要分装一个函数GetMineCount 来统计雷的个数,在编写之前,我们需要了解一个知识点:

(1)‘ 0 ’- ‘ 0 ’= 0

(2)‘ 1 ’- ‘ 0 ’= 1

(3) 1 +‘ 0 ’ = 0

(1)表示的意思是字符0 - 字符0 = 数字0

(2)表示的意思是字符1 - 字符0 = 数字1

(3)表示的意思是数字1+字符0 = 数字0

那为什么是这样呢?因为字符1的ASCII码值是49,字符0的ASCII码值是48,所以刚好得到数字1

头文件:game.h:

//统计雷的个数
int GetMineCount(char mine[ROWS][COLS], int x, int y);

源文件game.c:

//统计雷的个数
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
    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 = 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 (show[x][y] == '*') //用来设置不能重复排查同一个坐标
            {
                if (mine[x][y] == '1')
                {
                    printf("很遗憾,游戏失败\n");
                    PrintfBoard(mine, ROW, COL);  //将布置雷的位置展示
                    break;
                }
                else
                {
                    //统计雷的个数
                    int ret = GetMineCount(mine, x, y);
                    show[x][y] = ret + '0';    //将统计好的雷的个数放置在该坐标
                    PrintfBoard(show, ROW, COL); 

                    win++;
                }
            }
            else
            {
                printf("该坐标已被排查,请重新输入:\n");
            }
        }
        else
        {
            printf("输入错误,请重新输入:\n");
        }
    }
    if (win == row * col - EASY_COUNT)
    {
        printf("恭喜你,排雷成功!\n");
        PrintfBoard(mine, ROW, COL);
    }
}

这个扫雷的小游戏就写完了,但其中还有许多的功能没有写进去,比如空排,计时器,这些功能大家可以自己琢磨琢磨,下面是整个程序的总代码:

💓总代码:

头文件:game.h:

#pragma once
//头文件game.h

//头文件的包含

#include<stdio.h>
#include<stdlib.h>
#include<time.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 PrintfBoard(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);

//统计雷的个数
int GetMineCount(char mine[ROWS][COLS], int x, int y);

源文件:test.c:

#define _CRT_SECURE_NO_WARNINGS 1
//源文件test.c
#include "game.h"


void game()
{
    //创建一个二维数组用来放置我们排雷时所看到的棋盘
    //再创建一个二维数组用来布置雷
    char mine[ROWS][COLS] = { 0 };   //用来显示雷的位置
    char show[ROWS][COLS] = { 0 };   //用来排雷
    //初始化棋盘
    InitBoard(mine, ROWS, COLS, '0');
    InitBoard(show, ROWS, COLS, '*');
    //打印棋盘
    PrintfBoard(show, ROW, COL);
    //PrintfBoard(mine, ROW, COL);
    //布置雷
    SetMine(mine, ROW, COL);
    PrintfBoard(mine, ROW, COL);
    //排雷
    FindMine(mine, show, ROW, COL);
}

void menu()
{
    printf("************************\n");
    printf("*******  1. Play  ******\n");
    printf("*******  0. Exit  ******\n");
    printf("************************\n");
}
int main()
{
    srand((unsigned int)time(NULL));
    int input = 0;
    do          
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        switch(input)
        {
        case 1:
            printf("扫雷游戏\n");
            game();
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default :
            printf("输入错误,请重新输入\n");
            break;
        }
        
    } while(input);   //进入循环的条件是input为真
    return 0; 
}

源文件:game.c:

#define _CRT_SECURE_NO_WARNINGS 1

//源文件game.c
#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 PrintfBoard(char board[ROWS][COLS], int row, int col)
{
    int i = 0;
    int j = 0;
    printf("****** 扫雷 ******\n");
    for (i = 0; i <= row; 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 x = 0;
    int y = 0;
    int count = EASY_COUNT;
    while (count)
    {
        x = rand() % row + 1;
        y = rand() % col + 1;
        if (mine[x][y] == '0')
        {
            mine[x][y] = '1';
            count--;
        }
    }
}
//统计雷的个数
int GetMineCount(char mine[ROWS][COLS], int x, int y)
{
    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 = 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 (show[x][y] == '*')
            {
                if (mine[x][y] == '1')
                {
                    printf("很遗憾,游戏失败\n");
                    PrintfBoard(mine, ROW, COL);
                    break;
                }
                else
                {
                    //统计雷的个数
                    int ret = GetMineCount(mine, x, y);
                    show[x][y] = ret + '0';
                    PrintfBoard(show, ROW, COL);

                    win++;
                }
            }
            else
            {
                printf("该坐标已被排查,请重新输入:\n");
            }
        }
        else
        {
            printf("输入错误,请重新输入:\n");
        }
    }
    if (win == row * col - EASY_COUNT)
    {
        printf("恭喜你,排雷成功!\n");
        PrintfBoard(mine, ROW, COL);
    }
}

那扫雷代码就写完了,如果大家有什么疑问或是文章有什么不足都可以分享出来,感谢大家支持!

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

stackY、

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值