【c语言】实现小游戏——扫雷

大家好!这篇博客要讲的是,用c语言实现一个简单游戏——扫雷。

实现扫雷的过程,其实与三字棋大同小异。也许这对初学者来说,仍是一个工程量较大的课题,但我们不妨参照上一个游戏三子棋,先弄清楚扫雷是怎样一个游戏,然后再把相关信息整理成一条流程,依照流程来开展课题。

(参照详见【c语言】保姆级教程实现小游戏 —— 三字棋

  • 所用知识:函数、数组、选择结构语句,循环结构语句等。

  • 所用编译器:VS 2019

一、扫雷游戏怎么玩

由图,

开局,系统会布置一个“雷区”,并展示给玩家看。每一次“扫雷”,就是点击相应的方格。

若玩家点击的方格里没有埋雷,方格处就会显示它周围一圈方格(去心的3x3九宫格)的雷的数量,而当周围一圈方格里都没有埋雷,系统就会对更大的无雷范围进行清扫;

若玩家点击的方格里埋了雷,则游戏失败。

由上,我们可以得到以下信息:

  1. 游戏开始,系统会布置好一个雷区并展示给玩家

  1. 玩家排雷的方式是点击雷区内某一方格

  1. 玩家点击某一方格后,系统会判定此处是否有雷

  1. 无雷则游戏继续,有雷则游戏结束

  1. 排雷直到整个雷区清空,则玩家胜利,游戏结束

而稍作整理,我们不难得到以下流程:

  1. 创建和初始化雷区

  1. 布置好雷区并展示给玩家看

  1. 玩家扫雷

下面,我们分别来看如何具体实现。

二、扫雷游戏的实现

0.准备工作

参照三子棋,我们仍然用创建一个test函数来实现游戏刚开始的流程,并在游戏开始时,打印一个游戏菜单,以便给玩家操作提示。我们创建一个menu函数来实现菜单。

void menu()
{
    printf("**********************\n");
    printf("******  1.play  ******\n");
    printf("******  0.exit  ******\n");
    printf("**********************\n");
}
void test()
{
    int input = 0;
    do {
        menu();
        printf("请选择\n");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            game();
            printf("开始游戏\n");
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("输入错误,请重新输入\n");
        }
    } while (input);
}
int main()
{
    test();
    return 0;
}

当玩家依照提示输入“1”,则进入游戏。我们创建一个game函数来实现游戏的流程。

1.创建和初始化雷区

不难想到,我们其实是需要两个雷区的,一个用来真正地布置雷,而另一个表面上没有雷。玩家在展示的雷区中选择扫雷的位置,而对扫雷的判断只需要核对另一个雷区的相应位置上是否有雷。

我们分别创建两个相同大小的9x9的二维数组当作雷区,并规定:在二维数组中字符“ * ”代表待排区域,字符“ 0 ”代表非雷,字符“ 1 ”代表雷

//宏定义雷区(二维数组)大小,以便后续按需修改
#define ROW 9
#define COL 9
//以下宏定义以防数组越界,原因见后
#define ROWS ROW+2
#define COLS COL+2
char mine[ROWS][COLS] = { 0 }; //真正布置雷的雷区
char show[ROWS][COLS] = { 0 }; //表面上没有雷的雷区

我们创建一个Init_board函数来对两个雷区进行初始化。

void Init_board(char arr[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++)
        {
            arr[i][j] = set;
        }
    }
}
//返回类型为void
//参数为雷区(二维数组)、预设的行、预设的列、初始化的内容

2.布置好雷区并展示给玩家看

我们创建一个set_mine函数来布置雷区。默认雷区中一共有十颗雷。

#define mine_count 10//宏定义雷的数量,以便后续按需修改
void set_mine(char mine[ROWS][COLS], int row, int col)
{
    int count = mine_count;
    int x = 0;int y = 0;
    while (count)
    {
        x = rand() % row + 1;//用rand函数生成随机数(详解见文首三子棋博客内)
        y = rand() % col + 1;
        if (mine[x][y] == '0')
        {
            mine[x][y] = '1'; 
            count--;
        }
    }    
}
//返回类型为void
//参数为雷区(二维数组)、预设的行、预设的列

然后,我们创建一个print__board函数来打印雷区。

void print_board(char arr[ROWS][COLS], int row, int col)
{
    int i = 0;int j = 0;
    int a = 0;
    for (a = 0; a <= col; a++)
    {
        printf("%d ", a);
    }
    printf("\n");
    for (i = 1; i <= row; i++)
    {
        printf("%d ", i);
        for (j = 1; j <= col; j++)
        {
            printf("%C ", arr[i][j]);
        }
        printf("\n");
    }
}
//返回类型为void
//参数为雷区(二维数组)、预设的行、预设的列

3.玩家扫雷

玩家扫雷的过程又可以细分为:

  1. 玩家输入要在雷区中扫雷的坐标;

  1. 系统判断坐标处是否有雷;

  1. 有雷则游戏结束;无雷,则显示,周围清空=雷的数量。

我们创建一个find_mine函数来实现扫雷。同时,创建一个mine_count函数来计算雷的数量,再创建一个spread_mine函数用来对无雷范围进行清扫,并在find_mine函数内部调用。


int mine_count(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] - 8 * '0');
}
//返回类型为int,便于下面代码中的count变量接收结果
//参数为雷区(二维数组)、所排查坐标的行、所排查坐标的列
void spread_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{

    int n = mine_count(mine, x, y);
    if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
    {
        if (show[x][y] == '*')
        {
            if (n == 0)
            {
                show[x][y] = '0';
                spread_mine(mine, show, x - 1, y);
                spread_mine(mine, show, x - 1, y - 1);
                spread_mine(mine, show, x, y - 1);
                spread_mine(mine, show, x + 1, y - 1);
                spread_mine(mine, show, x + 1, y);
                spread_mine(mine, show, x + 1, y + 1);
                spread_mine(mine, show, x, y + 1);
                spread_mine(mine, show, x - 1, y + 1);
            }
            else
            {
                show[x][y] = n + '0';
            }
        }
    }

}
//返回类型为void
//参数为真正布置雷的雷区(二维数组)、表面上没有雷的雷区(二维数组)、所排查坐标的行、所排查坐标的列
void find_mine(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 - MINE_COUNT))
    {
        printf("请输入扫雷的坐标:");
        scanf("%d %d", &x, &y);
        if (x >= 1 && x <= row && y >= 1 && y <= col)
        {
            if (show[x][y] != '*')
            {
                printf("该坐标非法,请重新输入!\n");
                continue;
            }
            if (mine[x][y] == '0')
            {
                spread_mine(mine, show, x, y);
                int n = mine_count(mine, x, y);
                show[x][y] = n + '0';
                print_board(show, ROW, COL);
                win++;
            }
            else
            {
                printf("雷爆炸了,你输了!\n");
                print_board(mine, ROW, COL);
                break;
            }
        }
        else
        {
            printf("该坐标非法,请重新输入!\n");
        }
    }
    if (win == (row * col - MINE_COUNT))
    {
        printf("恭喜你!扫雷成功!\n");
    }
}
//返回类型为void
//参数为真正布置雷的雷区(二维数组)、表面上没有雷的雷区(二维数组)、预设的行、预设的列

三、代码汇总

test.c文件 - 存放游戏的主体代码

#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 };//表面上没有雷的雷区
    //初始化真正布置雷的雷区
    Init_board(mine, ROWS, COLS, '0');
    //初始化表面上没有雷的雷区
    Init_board(show, ROWS, COLS, '*');
    //布置雷区
    set_mine(mine, ROW, COL);
    //打印雷区
    print_board(show, ROW, COL);
    //扫雷
    find_mine(mine, show, ROW, COL);
}
//游戏开始时的必要流程的实现
void test()
{
    int input = 0;
    do {
        menu();
        printf("请选择\n");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            game();
            printf("开始游戏\n");
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("输入错误,请重新输入\n");
        }
    } while (input);
}
int main()
{
    test();
    return 0;
}

game.h文件 - 存放所有头文件、宏定义和函数的声明

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>
#define ROW 9
#define COL 9
#define ROWS ROW+2
#define COLS COL+2
#define MINE_COUNT 10
//初始化雷区
void Init_board(char arr[ROWS][COLS], int rows, int cols, char set);
//打印雷区
void print_board(char arr[ROWS][COLS], int row, int col);
//布置雷区
void set_mine(char mine[ROWS][COLS], int row, int col);
//扫雷
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);

game.c文件 - 存放所有函数的本体

#include"game.h"

//初始化雷区
void Init_board(char arr[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++)
        {
            arr[i][j] = set;
        }
    }
}
//打印雷区
void print_board(char arr[ROWS][COLS], int row, int col)
{
    int i = 0;
    int j = 0;
    int a = 0;
    for (a = 0; a <= col; a++)
    {
        printf("%d ", a);
    }
    printf("\n");

    for (i = 1; i <= row; i++)
    {
        printf("%d ", i);
        for (j = 1; j <= col; j++)
        {
            printf("%C ", arr[i][j]);
        }
        printf("\n");
    }
}
//布置雷区
void set_mine(char mine[ROWS][COLS], int row, int col)
{
    int count = MINE_COUNT;
    int x = 0; int y = 0;
    while (count)
    {
        x = rand() % row + 1;
        y = rand() % col + 1;
        if (mine[x][y] == '0')
        {
            mine[x][y] = '1';
            count--;
        }
    }
}
//计数雷
int mine_count(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] - 8 * '0');
}
//清空区域
void spread_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{

    int n = mine_count(mine, x, y);
    if (x >= 1 && x <= ROW && y >= 1 && y <= COL)
    {
        if (show[x][y] == '*')
        {
            if (n == 0)
            {
                show[x][y] = '0';
                spread_mine(mine, show, x - 1, y);
                spread_mine(mine, show, x - 1, y - 1);
                spread_mine(mine, show, x, y - 1);
                spread_mine(mine, show, x + 1, y - 1);
                spread_mine(mine, show, x + 1, y);
                spread_mine(mine, show, x + 1, y + 1);
                spread_mine(mine, show, x, y + 1);
                spread_mine(mine, show, x - 1, y + 1);
            }
            else
            {
                show[x][y] = n + '0';
            }
        }
    }

}
//扫雷
void find_mine(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 - MINE_COUNT))
    {
        printf("请输入扫雷的坐标:");
        scanf("%d %d", &x, &y);
        if (x >= 1 && x <= row && y >= 1 && y <= col)
        {
            if (show[x][y] != '*')
            {
                printf("该坐标非法,请重新输入!\n");
                continue;
            }
            if (mine[x][y] == '0')
            {
                spread_mine(mine, show, x, y);
                int n = mine_count(mine, x, y);
                show[x][y] = n + '0';
                print_board(show, ROW, COL);
                win++;
            }
            else
            {
                printf("雷爆炸了,你输了!\n");
                print_board(mine, ROW, COL);
                break;
            }
        }
        else
        {
            printf("该坐标非法,请重新输入!\n");
        }
    }
    if (win == (row * col - MINE_COUNT))
    {
        printf("恭喜你!扫雷成功!\n");
    }
}

四、运行效果

以上。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值