C语言扫雷游戏简单实现

一、实现思路
1、布置雷
定义二维字符数组mine,用以存放雷,以‘1’表示雷,‘0’表示无雷;
定义二维字符数组show,用以保存扫雷需要的信息;
2、排雷
以输入坐标的形式排雷,可能出现四种结果:

坐标非法;
踩到雷,游戏结束;
踩到处周边有雷,显示周边雷的数量;
踩到处周边无雷,将周边无雷的区域展开;
3、检查雷是否被排完
排完则游戏成功,否则游戏继续。

二、函数代码实现
1、“雷”数组的初始化以及打印“雷区”
初始化:
将数组中的元素全部初始化为set;
//初始化数组
void Initarr(char arr[ROWS][COLS], int row, int col, char set)
{
    for (int i = 0; i < row; i++)
    {
        for (int j = 0; j < col; j++)
        {
            arr[i][j] = set;
        }
    }
}
打印雷区:
利用分割线分开每一次排雷,打印行标与列标以便选择位置。
//打印“雷区”,由于第一行、最后一行、第一列与最后一列作为辅助作用,
//因此不予打印,只打印中间9行。
void Display(char arr[ROWS][COLS], int row, int col)
{
    printf("------- 扫雷 ------\n");
    printf("0 1 2 3 4 5 6 7 8 9\n");//打印列标
    for (int i = 1; i < row + 1; i++)
    {
        printf("%d ", i);//打印行标
        for (int j = 1; j < col + 1; j++)
        {
            printf("%c ", arr[i][j]);
        }
        printf("\n");
    }
    printf("------- 扫雷 ------\n");
}

2、布置雷与扫雷
布置雷
以count表示游戏难度,即布置雷的数量,通过传参保证函数脱离全局变量后依旧可用;
//随机布置雷
void Setmine(char mine[ROWS][COLS], int row, int col, int count)
{
    int x = 0;
    int y = 0;
    while (count)
    {
        //mine为11×11大小,首行末行与首列末列均不布置雷
        x = rand() % row + 1;
        y = rand() % col + 1;
        if (mine[x][y] == '0')
        {
            mine[x][y] = '1';
            count--;
        }
    }
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
扫雷,根据出现的情况进行分类
在判断输赢时采用了循环检查雷是否排查完成,效率较低;
//扫雷可能出现的情况:
// 1、坐标非法->重新输入
// 2、坐标合法->A、踩雷->游戏结束
//              B、未踩雷->a、周边有雷->显示雷的数量
//                         b、周边无雷->大面积展开
//              C、该位置已经被扫过->坐标非法
void Sweep(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int count)
{
    int x = 0, y = 0;
    while (1)
    {
        printf("请输入坐标:\n");
        scanf("%d %d", &x, &y);
        if (x < 1 || x > row || y < 1 || y > col)
        {
            printf("坐标非法,请重新输入:\n");
        }
        else if (mine[x][y] == '1')
        {
            printf("此处有雷,游戏结束\n");
            Display(mine, row, col);
            break;
        }
        else if (show[x][y] != '*')
        {
            printf("该位置已被扫过,请重新输入:\n");
        }
        else
        {
            //周边无雷
            if (get_mine(mine, x, y) == 0)
            {
                unfold(mine, show, x, y);
                Display(show, row, col);
            }
            //周边有雷
            else
            {
                show[x][y] = get_mine(mine, x, y) + '0';
                Display(show, row, col);
            }
        }
        //判断是否完成
        int win = 0;
        for (int i = 1; i < row + 1; i++)
        {
            for (int j = 1; j < col + 1; j++)
            {
                if (show[i][j] == '*')
                    win++;
            }
        }
        if (win == EASY)
        {
            printf("扫雷成功!\n");
            Display(mine, row, col);
            break;
        }
    }
        
}
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
递归展开无雷处
采用递归,依次判断周边的八个位置,达到展开效果;
void unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
    if (x == 0 || x == ROW + 1 || y == 0 || y == COL + 1)
        return;
    if (show[x][y] == '*')
    {
        int ret = get_mine(mine, x, y);
        if (ret == 0)
        {
            show[x][y] = ' ';
            unfold(mine, show, x -1, y -1);
            unfold(mine, show, x - 1, y);
            unfold(mine, show, x - 1, y + 1);
            unfold(mine, show, x, y - 1);
            unfold(mine, show, x, y + 1);
            unfold(mine, show, x + 1, y - 1);
            unfold(mine, show, x + 1, y);
            unfold(mine, show, x + 1, y + 1);
        }
        else
        {
            show[x][y] = ret + '0';
        }
    }
}

获取周边雷的数量
检查周边8个位置的雷个数,利用两层嵌套循环检查;
//获取周边的雷的个数
int get_mine(char mine[ROWS][COLS], int x, int y)
{
    int count = 0;
    for (int i = -1; i < 2; i++)
    {
        for(int j = -1; j < 2; j++)
            if (mine[x + i][y + j] == '1')
            {
                count++;
            }
    }
    return count;
}

三、小tips
定义二维数组存放雷时,将数组定义大两个,9×9的棋盘采用11×11的数组,如此,即便是判断边缘格子时也可以采用一样的函数,不用担心越界;
定义游戏难度时,可将相关内容都放在头文件中,以#define定义的方式,方便以后修改;
定义函数时,可将只会在本.c文件中用到的函数前加上static,防止其他文件调用,同时也让在其他.c文件中定义函数时更为方便;
书写代码时,须首先严谨思考,考虑到各种结果,先思考再敲代码,会更有效率。
四、代码汇总
头文件game.h

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

#define ROW 9
#define COL 9

#define ROWS 11
#define COLS 11

//游戏难度
#define EASY 10


//初始化数组,初始arr数组中的值为set
void Initarr(char arr[ROWS][COLS], int row, int col, char set);

//打印“雷区”
void Display(char arr[ROWS][COLS], int row, int col);

//随机布置雷
void Setmine(char mine[ROWS][COLS], int row, int col, int count);

//扫雷
void Sweep(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int count);


函数文件game.c

#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"
//具体函数的实现

//选择菜单
void menu()
{
    printf("******************\n");
    printf("***** 1、开始 ****\n");
    printf("***** 0、退出 ****\n");
    printf("******************\n");
}

//初始化数组
void Initarr(char arr[ROWS][COLS], int row, int col, char set)
{
    for (int i = 0; i < row; i++)
    {
        for (int j = 0; j < col; j++)
        {
            arr[i][j] = set;
        }
    }
}

//打印“雷区”,由于第一行、最后一行、第一列与最后一列作为辅助作用,因此不予打印,只打印中间9行
void Display(char arr[ROWS][COLS], int row, int col)
{
    printf("------- 扫雷 ------\n");
    printf("0 1 2 3 4 5 6 7 8 9\n");//打印列标
    for (int i = 1; i < row + 1; i++)
    {
        printf("%d ", i);//打印行标
        for (int j = 1; j < col + 1; j++)
        {
            printf("%c ", arr[i][j]);
        }
        printf("\n");
    }
    printf("------- 扫雷 ------\n");
}

//随机布置雷
void Setmine(char mine[ROWS][COLS], int row, int col, int count)
{
    int x = 0;
    int y = 0;
    while (count)
    {
        //mine为11×11大小,首行末行与首列末列均不布置雷
        x = rand() % row + 1;
        y = rand() % col + 1;
        if (mine[x][y] == '0')
        {
            mine[x][y] = '1';
            count--;
        }
    }
}

//获取周边的雷的个数
static int get_mine(char mine[ROWS][COLS], int x, int y)
{
    int count = 0;
    for (int i = -1; i < 2; i++)
    {
        for(int j = -1; j < 2; j++)
            if (mine[x + i][y + j] == '1')
            {
                count++;
            }
    }
    return count;
}

//展开函数
static void unfold(char mine[ROWS][COLS], char show[ROWS][COLS], int x, int y)
{
    if (x == 0 || x == ROW + 1 || y == 0 || y == COL + 1)
        return;
    if (show[x][y] == '*')
    {
        int ret = get_mine(mine, x, y);
        if (ret == 0)
        {
            show[x][y] = ' ';
            unfold(mine, show, x -1, y -1);
            unfold(mine, show, x - 1, y);
            unfold(mine, show, x - 1, y + 1);
            unfold(mine, show, x, y - 1);
            unfold(mine, show, x, y + 1);
            unfold(mine, show, x + 1, y - 1);
            unfold(mine, show, x + 1, y);
            unfold(mine, show, x + 1, y + 1);

            
        }
        else
        {
            show[x][y] = ret + '0';
        }
    }
}
//扫雷可能出现的情况:
// 1、坐标非法->重新输入
// 2、坐标合法->A、踩雷->游戏结束
//              B、未踩雷->a、周边有雷->显示雷的数量
//                         b、周边无雷->大面积展开
//              C、该位置已经被扫过->坐标非法
void Sweep(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col, int count)
{
    int x = 0, y = 0;
    while (1)
    {
        printf("请输入坐标:\n");
        scanf("%d %d", &x, &y);
        if (x < 1 || x > row || y < 1 || y > col)
        {
            printf("坐标非法,请重新输入:\n");
        }
        else if (mine[x][y] == '1')
        {
            printf("此处有雷,游戏结束\n");
            Display(mine, row, col);
            break;
        }
        else if (show[x][y] != '*')
        {
            printf("该位置已被扫过,请重新输入:\n");
        }
        else
        {
            //周边无雷
            if (get_mine(mine, x, y) == 0)
            {
                unfold(mine, show, x, y);
                Display(show, row, col);
            }
            //周边有雷
            else
            {
                show[x][y] = get_mine(mine, x, y) + '0';
                Display(show, row, col);
            }
        }
        //判断是否完成
        int win = 0;
        for (int i = 1; i < row + 1; i++)
        {
            for (int j = 1; j < col + 1; j++)
            {
                if (show[i][j] == '*')
                    win++;
            }
        }
        if (win == EASY)
        {
            printf("扫雷成功!\n");
            Display(mine, row, col);
            break;
        }
    }    
}

void game()
{
    // mine数组用以存放雷
    char mine[ROWS][COLS] = { 0 };
    // show数组用以存放扫出的信息
    char show[ROWS][COLS] = { 0 };
    //初始化mine数组为字符0,show数组为字符'*'
    Initarr(mine, ROWS, COLS, '0');
    //Display(mine, ROW, COL);//检测初始化效果
    Initarr(show, ROWS, COLS, '*');
    //Display(show, ROW, COL);//检测初始化效果
    Setmine(mine, ROW, COL, EASY);
    //Display(mine, ROW, COL);//查看雷的布置位置
    Display(show, ROW, COL);
    Sweep(mine, show, ROW, COL, EASY);
}


测试文件test.h
#define _CRT_SECURE_NO_WARNINGS 1

#include"game.h"

int main()
{
    int x = 0;
    srand((unsigned int)time(NULL));
    do
    {
        menu();
        scanf("%d", &x);
        switch (x)
        {
        case 1:
            printf("游戏开始\n");
            game();
            break;
        case 0:
            printf("游戏退出\n");
            break;
        default:
            printf("输入错误,请重新输入\n");
            break;
        }
    } while (x);
}

最后,如果还想了解更多知识,请关注我,感谢啦。

猜你喜欢

转载自blog.csdn.net/m0_69824302/article/details/131504401