欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

简单实现扫雷游戏!

程序员文章站 2022-04-07 13:57:19
...
本来想着可以写出与电脑上扫雷差异不大的东西,但由于技术不精,虽然模拟实现了扫雷,但写出来的东西与真正扫雷还有一定差距,不过这些都不影响整个游戏的体验。话不多说,直入主题:

先附上头文件game.h的代码,在头文件中我定义了游戏界面的大小 R 和 C 。根据游戏的规则,如果输入人一个坐标,那么显示出来的就是这个坐标所在的九宫格的雷数,因此为了方便计算雷数,虽然显示出来的游戏界面是R * C ,但是实际数组的大小为(R + 2) * (C + 2)。如下图:
简单实现扫雷游戏!
同时,在头文件中声明了定义的所有函数,函数后面的注释对应它们的功能。

#ifndef __GAME_H__
#define __GAME_H__

#define EASY_COUNT 10     //雷数
#define R 9    //行
#define C 9    //列

#define ROWS R + 2
#define COLS C + 2


#include<stdio.h>

void Choose_Menu(int a[ROWS][COLS]);      //选择菜单界面
void Game_Initialize(int a[ROWS][COLS], int row, int col);  //初始化
void Set_Mine(int a[ROWS][COLS], int row, int col);         //布置雷
void Move_Mine(int a[ROWS][COLS], int row, int col, int x, int y);  //移动雷  如果第一次输入的坐标就是雷,则将此处的雷移动至别处,游戏继续。
void Game_Menu_Print(const int a[ROWS][COLS], int row, int col); //游戏中界面输出
void Game_Result_Print(const int a[ROWS][COLS], int row, int col);//游戏结束输出
void Find_Mine(int a[ROWS][COLS], int row, int col); //进行排雷的过程
int Get_Win(const int a[ROWS][COLS], int row, int col);     //判断游戏状态 未点开的坐标数
void Get_Mine(int a[ROWS][COLS], int row, int col);  //计算雷数
void Play_Game(int a[ROWS][COLS]); //游戏开始调用的总函数

#endif //__GAME_H__

下面附上game.c的代码,它是对game.h中函数的实现。
需要特别说明的是:

  • 移动雷函数的作用在于:如果没有移动雷的函数,那么如果在第一次输入坐标时,不幸输入了有雷的坐标,游戏就会立即结束,这样毫无游戏体验。因此,为了提升玩家游戏体验,当第一次输入坐标时,如果输入了有雷的坐标,那么程序就会在后台悄悄地将此处的雷移动至别处。当然这仅限第一次。
  • 由于在计算时,传入的是数组名,因此可以直接在调用的函数中修改数组的值,因此,Get_mine不需要返回值。
  • 整个代码的最难点在于当输入一个坐标后,由于该坐标所在的九宫格中雷数为0,因此会从此坐标向外蔓延,显示一片,知道边界全为数字为止。在这里我使用了递归的方法由中心点向外蔓延,具体是在函数Get_Mine中实现的。

以下为 game.c中的代码(下面为扫雷的整体模拟思路!):

/*
//  扫雷的整体模拟思路
    只定义一个数组
    数组类型为整形
    数组默认值为-2 // 初始值
    进行布置雷,如果此处是雷则值为-1
    输入一个位置,会计算此处雷数
    如果此处周围雷数为0 显示空 同时向外蔓延炸开
    游戏中的输出界面 主要参考下面两个值:
    1.如果是-2 说明未排雷 输出‘*’  
    2.如果是0 或者 大于0的数字,说明排雷已经排过了这些坐标
    如果值为-1 不影响游戏中输出界面,只在最后输出游戏结果时,输出’@‘
    如果此处周围有雷,那么就只显示该位置,显示数字为周围雷数
    每次输入坐标后都检测剩余未点开的坐标数 如果只剩下了雷 则胜利  
    数组大小为显示界面横纵坐标各加2
*/
#include"game.h"
void Choose_Menu(int a[ROWS][COLS])
{
    int i = 1;
    while (i)
    {
        printf("\t\t\t******************************************************\n");
        printf("\t\t\t******************************************************\n");
        printf("\t\t\t*************请选择进行的操作:          *************\n");
        printf("\t\t\t*************1.开始游戏    0.退出游戏    *************\n");
        printf("\t\t\t******************************************************\n");
        printf("\t\t\t******************************************************\n");
        printf("\t\t\t输入:>_");
        scanf("%d", &i);
        switch (i)
        {
        case 1: Play_Game(a, R, C); break;
        case 0: printf("退出成功,请按任意键结束!\n"); break;
        default:printf("输入错误,请重新输入!\n"); break;
        }
    }
}
void Game_Initialize(int a[ROWS][COLS], int row, int col)
{
    int i = 0, j = 0;
    for (i = 0; i < row; i++)
    {
        for (j = 0; j < col; j++)
        {
            a[i][j] = -2;
        }
    }
}
void Set_Mine(int a[ROWS][COLS], int row, int col, int count)
{ 
    int x = 0;
    int y = 0;
    srand((unsigned int)time(NULL));
    while (count)
    {
        x = rand() % row + 1;
        y = rand() % col + 1;
        if (a[x][y] == -2)
        {
            a[x][y] = -1;
            count--;
        }
    }
}
void Move_Mine(int a[ROWS][COLS], int row, int col, int x, int y)
{
    int x0 = 0;
    int y0 = 0;
    srand((unsigned int)time(NULL));
    while (1)
    {
        x0 = rand() % row + 1;
        y0 = rand() % col + 1;
        if ((x0 != x || y0 != y) && a[x0][y0] != -1)
        {
            a[x0][y0] = -1;
            break;
        }
    }
}
void Game_Menu_Print(const int a[ROWS][COLS], int row, int col)
{
    int i = 0, j = 0;
    printf("\n\t\t\tGame_Menu:\n\n");
    //输出坐标

    printf("\t\t\t\t");
    for (i = 1; i <= col; i++)
    {
        if(i < 10)
        printf(" %d ", i);
        else printf("%d ", i);
    }
    printf("\n\n");

    //输出棋局
    for (i = 1; i <= row; i++)
    {
        printf("\t\t\t   %d ",i);
        for (j = 1; j <=col; j++)
        {
            if (a[i][j] == -2 || a[i][j] == -1)
                printf(" * ");
            else if(a[i][j] == 0)
                printf("   ");
            else printf(" %d ", a[i][j]);
        }
        printf("    %d\n", i);
    }
    //输出坐标
    printf("\n\t\t\t\t");
    for (i = 1; i <= col; i++)
    {
        if (i < 10)
            printf(" %d ", i);
        else printf("%d ", i);
    }
    printf("\n\n");
}
void Game_Result_Print(const int a[ROWS][COLS], int row, int col)
{
    int i = 0, j = 0;
    printf("\n\t\t\tGame_Result:\n\n");
    //输出坐标

    printf("\t\t\t\t");
    for (i = 1; i <= col; i++)
    {
        if (i < 10)
            printf(" %d ", i);
        else printf("%d ", i);
    }
    printf("\n\n");

    //输出棋局
    for (i = 1; i <= row; i++)
    {
        printf("\t\t\t   %d ", i);
        for (j = 1; j <= col; j++)
        {
            if (a[i][j] == -2)
            {
                printf(" * ");
            }
            else if (a[i][j] == -1)
            {
                printf(" @ ");
            }
            else if (a[i][j] == 0)
            {
                printf("   ");
            }
            else printf(" %d ", a[i][j]);
        }
                printf("    %d\n", i);
    }
    //输出坐标
    printf("\n\t\t\t\t");
    for (i = 1; i <= col; i++)
    {
        if (i < 10)
            printf(" %d ", i);
        else printf("%d ", i);
    }
    printf("\n\n");
}
int Get_Win(const int a[ROWS][COLS], int row, int col)
{
    int i = 0, j = 0;
    int ret = 0;
    for(i = 1; i <= row; i++)
        for (j = 1; j <= col; j++)
        {
            if (a[i][j] == -2)
                ret++;
        }
    return ret;
}
void Get_Mine(int a[ROWS][COLS], int row, int col)
{
    int i = 0;
    int j = 0;
    if ((a[row - 1][col - 1] == -1) + (a[row - 1][col] == -1) + (a[row - 1][col + 1] == -1) + (a[row][col - 1] == -1) + (a[row][col + 1] == -1) + (a[row + 1][col - 1] == -1) + (a[row + 1][col] == -1) + (a[row + 1][col + 1] == -1) != 0)
    {
        a[row][col] =  (a[row - 1][col - 1] == -1) + (a[row - 1][col] == -1) + (a[row - 1][col + 1] == -1) + (a[row][col - 1] == -1) + (a[row][col + 1] == -1) + (a[row + 1][col - 1] == -1) + (a[row + 1][col] == -1) + (a[row + 1][col + 1] == -1);
    }
    else
    {
        //只有在棋盘内才递归
        if (row >= 1 && row <= R && col >= 1 && col <= C)
        {
            a[row][col] = ((a[row - 1][col - 1] == -1) + (a[row - 1][col] == -1) + (a[row - 1][col + 1] == -1) + (a[row][col - 1] == -1) + (a[row][col + 1] == -1) + (a[row + 1][col - 1] == -1) + (a[row + 1][col] == -1) + (a[row + 1][col + 1] == -1));
            //向上
            if (a[row - 1][col] < 0)
                Get_Mine(a, row - 1, col);
            //向右
            if (a[row][col + 1] < 0)
                Get_Mine(a, row, col + 1);
            //向下
            if (a[row + 1][col] < 0)
                Get_Mine(a, row + 1, col);
            //向左
            if (a[row][col - 1] < 0)
                Get_Mine(a, row, col - 1);
        }
    }
}
void Find_Mine(int a[ROWS][COLS], int row, int col)
{
    int x = 0, y = 0;
    int win = Get_Win(a, row, col);;
    while (win)
    {
        //输入坐标
        Game_Menu_Print(a, row, col);
        printf("请输入要排查的坐标:");
        scanf("%d %d", &x, &y);
        //判断有效性
        if (x >= 1 && x <= row && y >= 1 && y <= col) //输入正确
        {

            //
            if (a[x][y] == -1)
            {
                if (win == row * col - EASY_COUNT)
                {
                    Move_Mine(a, row, col, x, y);
                    Get_Mine(a, x, y);
                    win = Get_Win(a, row, col);
                }
                else
                {
                    printf("\nGAME OVER!!!\n\n");
                    break;
                }
            }
            else
            {
                if (a[x][y] != -2)
                {
                    printf("坐标输入有误,请重新输入!\n");
                    continue;
                }
                Get_Mine(a, x, y);
                win = Get_Win(a, row, col);
            }

        }
        else
        {
            printf("坐标输入有误,请重新输入!\n");
        }
    }
    if (win == 0)
        printf("\nYou Win!\n\n");
}
void Play_Game(int a[ROWS][COLS], int row, int col)
{
    Game_Initialize(a, ROWS, COLS);
    Set_Mine(a, row, col,EASY_COUNT);
    Find_Mine(a, row, col);
    Game_Result_Print(a, row, col);
}

最后是主函数代码test.c

#include "game.h"
int main()
{
    int arr[ROWS][COLS] = { 0 };
    Choose_Menu(arr);
    return 0;
}

代码整体相比于真正的扫雷还相差很多,需要注意的问题就是:

  • 不能标记
  • 在炸开时,显示的边界不是连续的,如下图:
    简单实现扫雷游戏!
    不过这都不影响整体的游戏过程,如果有需要可自行修改。
以上即为我模拟实现的扫雷游戏,不足之处还望指正!
相关标签: 游戏 扫雷