[C语言][游戏][扫雷]

前言:

为了巩固C语言,我运用所学的知识,写了一篇关于扫雷游戏的博客。如果有大佬看到这篇文章,如有不足之处,请你一定要指出来。
游戏的规则:
我们在棋盘格中任意点开一个格子(输入行和列确认这个格子),若这个格子不是雷就排除了这个位置,排除后这个格子会显示将它围起来的几个格子中有几颗雷,如果点开的格子埋有雷则为游戏失败。

一、模块化编程

模块化编程:把各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数的声明,其它.c文件想使用其中的代码时,只需要#include "XXX.h"文件即可。使用模块化编程可极大的提高代码的可阅读性、可维护性、可移植性等。
这里我创建了三个文件:game.h文件用来写自定义函数的声明;game.c文件用来写定义;test.c文件用了写整个程序的实现。(这里c文件中只需#include “game.h”,就能使头文件和源文件连接起来)
如下:
在这里插入图片描述

二、游戏的思路

1.游戏的设计:

1).扫雷游戏需要存储布置好的雷的信息,需要一个二维数组
2).给两个9* 9的二维数组:
一个存放雷的信息,另一个存放布置好雷的信息
3).为了防止在统计坐标周围的雷的个数的时候数组越界,我们把数组设计成11* 11的二维数组
4).数组是11*11,并且是字符数组(这里我们把‘0’表示非雷,‘1’表示雷)

2.游戏的逻辑:

1).创建菜单函数选择 进入游戏 以及 退出游戏
2).创建游戏函数,初始化棋盘(游戏在走的过程中要进行数据的存储,可以使用11*11的二维数组char board[ROWS][COLS];)
3).打印棋盘
4).布置雷
5).排查雷

三、实现游戏步骤/过程

1…创建菜单函数选择进入游戏以及退出游戏

1).创建菜单

void menu()
{
    
    
    printf("**********************\n");
    printf("****  1.开始游戏  ****\n");
    printf("****  0.退出游戏  ****\n");
    printf("**********************\n");
}

测试:
在这里插入图片描述
2).选择进入游戏以及退出游戏(实现玩一把不过瘾就会在来一次用到do while)

#include "game.h"
void menu()
{
    
    
    printf("**********************\n");
    printf("****  1.开始游戏  ****\n");
    printf("****  0.退出游戏  ****\n");
    printf("**********************\n");
}
int main()
{
    
    
    int input;
    do
    {
    
    
        menu();
    scanf("%d", &input);
    switch (input)//用来进行一次操作
    {
    
    
    case 0:
        break;
    case 1:
    {
    
    
        printf("扫雷游戏\n");//可以进入游戏
        break;
    }
    default:
    {
    
    
        printf("笨蛋输错了,重新输入:\n");
        break;
    }
    }
    }while (input);//可以进行多次游戏
    return 0;
}

测试:
在这里插入图片描述

2.创建游戏函数,初始化棋盘

1).创建游戏函数:
创建两个相同的数组:
因为用户在玩游戏的时候看到的是被覆盖起来的棋盘,看不到哪些地方都埋着雷的,这是显示棋盘。我们还需要一个埋雷的棋盘,这样才能实现玩家在玩游戏时通关或者未通关,将埋雷的点位呈现给玩家。
创建11*11数组的原因:
在扫雷中,我们排查的一个位置上要是显示了数字,就说明在此位置的周围 8 个格子中存在这个数字个雷,那么我们在排四个边上的位置时候格子是不够 8 个的,但是我们的功能在实现的时候还是会去找这 8 个格子的,这样就会造成数组的越界访问的问题
在这里插入图片描述
为了方便后期我们能更改棋盘的大小和雷的个数,使用宏定义define

#define ROW 9//  显示棋盘的行数
#define COL 9//  显示棋盘的列数
#define ROWS ROW+2//  实际棋盘的行数
#define COLS COL+2//  实际棋盘的列数
#define mine_count 10//设置雷的数量

2).初始化棋盘
在这里插入图片描述

扫描二维码关注公众号,回复: 15626109 查看本文章
void InitBoard(char board[][COLS], int rows, int cols, char set)//初始化棋盘
{
    
    
    int i, j;
    for(i=0;i<rows;i++)//遍历的方法,给每个数组元素赋值
        for (j = 0; j < cols; j++)
        {
    
    
            board[i][j] = set;
        }
        printf("\n";
}

3.打印棋盘

void DisPlayBoard(char board[][COLS], int row, int col)
{
    
    
    int i, j;
    printf("******* 扫雷游戏 *******\n");
    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");
    }
}

测试两个棋盘:
在这里插入图片描述
测试:
存放雷的棋盘
在这里插入图片描述
用户操作的棋盘
在这里插入图片描述

4.布置雷

void SetMine(char mine[][COLS], int row, int col)
{
    
    
    int i=mine_count,x,y;
    while(i)
    {
    
    
        x = rand() % row + 1;
        y = rand() % col + 1;
        if (mine[x][y] == '0')
        {
    
    
            mine[x][y] = '1';//字符1就是雷
            i--;
        }
    }
}

测试(10个雷放数组mine_1里):
在这里插入图片描述

5.排查雷

首先我们对玩家输入的坐标位置进行判断,是否合法,坐标必须在 1~row/col 之间(包含 1 和 row/col),如果不在此范围,代码会走第一个 else 语句,提醒玩家“笨蛋,坐标输错了,请重新输入:”的字样。如果合法就会走第一个 if 语句,进去之后我们对该位置进行判断,看是否该位置为字符 ‘1’,如果是字符 ‘1’ 就是踩到了雷,就会提醒玩家“很遗憾,你被炸死了”的字样,并将雷的分布图给玩家呈现出来如果是字符 ‘0’ 就走 else 语句,对该位置的周围 8 个格子的雷的个数进行计算并标注出来,这时我们就调用GetMine()函数来计算:

void FindMine(char mine_1[][COLS],char mine_2[][COLS],int row,int col)
{
    
    
    int x,y,count;
    while (1)
    {
    
    
        printf("请输入排查雷的坐标:\n");
        scanf("%d %d", &x, &y);
        if (x >= 1 && x <= row && y >= 1 && y <= col)//坐标输入正确
        {
    
    
            if (mine_1[x][y] == '1')//踩雷了
            {
    
    
                printf("你被炸死了\n");
                DisPlayBoard(mine_1, ROW, COL);
                break;
              }
            else//没踩雷
            {
    
    
                count = GetMine(mine_1, x, y);//统计雷的四周有几个雷
                mine_2[x][y] = '0' + count;
                //if (count == 0)
                  //  expend(mine_1, mine_2, row, col, x, y);
                DisPlayBoard(mine_2, ROW, COL); 
            }
        }
        else
            printf("笨蛋,坐标输错了,请重新输入:\n");
    }
}

测试结果:
在这里插入图片描述

6.判断输赢

mine_2数组中没有被排查的坐标为‘ * ’,所以只要统计剩下的‘ * ’是否等于雷的个数,等于则游戏胜利。

lswin(char mine_2[][COLS], int row, int col)//判断有几个符号‘*’然后返回
{
    
    
    int i,j,c;
    for(i=0;i<row;i++)
        for (j = 0; j < col; j++)
        {
    
    
            if (mine_2[i][j] == '*')
                c++;
        }
    return c;
}

在这里插入图片描述

7.扩展

说明:扩展坐标的范围应该加以限制,如果我要排查的坐标有雷,那么直接游戏结束,没雷就要向周围8个坐标扩展,并且没雷就把这个坐标设置为空格,只有设置了才能防止被重复递归!然后要限制坐标范围,如果到了边界,就没必要向边界外再继续递归,会越界的,递归出后就是找到了周围有雷,显示雷的个数就可以了

void expend(char mine_1[][COLS], char mine_2[][COLS], int row, int  col, int x, int  y)
{
    
    
    int count = GetMine(mine_1, x, y);//统计雷的四周有几个雷
    if (count == 0)
    {
    
    
        mine_1[x][y] = ' ';//如果周围地雷数为零则把这个位置赋值为空格
        int i = 0;
        int j = 0;
        for (i = x - 1; i <= x + 1; i++)
        {
    
    
            for (j = y - 1; j <= y + 1; j++)
            {
    
    
                if (mine_2[i][j] == '*' && i > 0 && i <= row && j > 0 && j <= col)
                {
    
    
                    expend(mine_1, mine_2, row, col, i, j);
                }
            }
        }
    }
    else
    {
    
    //递归的出口就是我统计到周围有几个雷
        mine_2[x][y] = count + '0';
}
}

四、总代码量

1.测试游戏test.c文件的代码

#include "game.h"
void menu()
{
    
    
    printf("**********************\n");
    printf("****  1.开始游戏  ****\n");
    printf("****  0.退出游戏  ****\n");
    printf("**********************\n");
}
void game()//创建游戏
{
    
    
    system("cls");//每次玩游戏,都把前面的记录全部清掉
    srand((unsigned)time(NULL));
    //创建雷的数组(mine_1)显示数组(mine_2)两个数组的类型,大小相同
    char mine_1[ROWS][COLS];//存放布置的雷的信息
    char mine_2[ROWS][COLS];//存放排查出雷的信息
    //初始化棋盘
    InitBoard(mine_1, ROWS, COLS,'0');//初始化棋盘里全部赋‘0’
    InitBoard(mine_2, ROWS, COLS, '*');//初始化棋盘里全部赋‘*’
    //打印棋盘
    //DisPlayBoard(mine_1, ROW, COL);//打印存放雷信息的9*9的棋盘
    DisPlayBoard(mine_2, ROW, COL);//打印排查出雷信息的9*9的棋盘
    //布置雷
    SetMine(mine_1, ROW, COL);

    //排查雷
    FindMine(mine_1,mine_2, ROW, COL);
}
int main()
{
    
    
    int input;
    do
    {
    
    
        menu();
    scanf("%d", &input);
    switch (input)//用来进行一次操作
    {
    
    
    case 0:
    {
    
    
        printf("退出游戏\n");
    break;
    }
    case 1:
    {
    
    
        game();//可以进入游戏
        break;
    }
    default:
    {
    
    
        printf("笨蛋输错了,重新输入:\n");
        break;
    }
    }
    }while (input);//可以进行多次游戏
    return 0;
}

2.游戏的实现game.c文件的代码

#include "game.h"
void InitBoard(char board[][COLS], int rows, int cols, char set)//初始化棋盘
{
    
    
    int i, j;
    for(i=0;i<rows;i++)
        for (j = 0; j < cols; j++)
        {
    
    
            board[i][j] = set;
        }
    printf("\n");
}
void DisPlayBoard(char board[][COLS], int row, int col)
{
    
    
    int i, j;
    printf("******* 扫雷游戏 *******\n");
    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[][COLS], int row, int col)
{
    
    
    int i=mine_count,x,y;
    while(i)
    {
    
    
        x = rand() % row + 1;
        y = rand() % col + 1;
        if (mine[x][y] == '0')
        {
    
    
            mine[x][y] = '1';//字符1就是雷
            i--;
        }
    }
}
int GetMine(char mine_1[][COLS], int x, int y)
{
    
    

    return mine_1[x - 1][y - 1] + mine_1[x - 1][y] + mine_1[x - 1][y + 1]
        + mine_1[x][y - 1] + mine_1[x][y + 1] + mine_1[x + 1][y - 1]
        + mine_1[x + 1][y] + mine_1[x + 1][y + 1] - 8 * '0';
}
void FindMine(char mine_1[][COLS],char mine_2[][COLS],int row,int col)
{
    
    
    int x,y,count;
    while (1)
    {
    
    
        printf("请输入排查雷的坐标:\n");
        scanf("%d %d", &x, &y);
        if (x >= 1 && x <= row && y >= 1 && y <= col)//坐标输入正确
        {
    
    
            if (mine_1[x][y] == '1')//踩雷了
            {
    
    
                printf("你被炸死了\n");
                DisPlayBoard(mine_1, ROW, COL);
                break;
              }
            else//没踩雷
            {
    
    
                   expend(mine_1, mine_2, row, col, x, y);
                DisPlayBoard(mine_2, ROW, COL); 
                int a=lswin(mine_2, row, col);//判断是否排查完
                if (a == ROW * COL - mine_count)//不需要排查了,赢啦
                {
    
    
                    printf("你赢了,真棒!\n");
                    break;
                }
            }
        }
        else
            printf("笨蛋,坐标输错了,请重新输入:\n");
    }
}
void expend(char mine_1[][COLS], char mine_2[][COLS], int row, int  col, int x, int  y)
{
    
    
    int count = GetMine(mine_1, x, y);//统计雷的四周有几个雷
    if (count == 0)
    {
    
    
        mine_1[x][y] = ' ';//如果周围地雷数为零则把这个位置赋值为空格
        int i = 0;
        int j = 0;
        for (i = x - 1; i <= x + 1; i++)
        {
    
    
            for (j = y - 1; j <= y + 1; j++)
            {
    
    
                if (mine_2[i][j] == '*' && i > 0 && i <= row && j > 0 && j <= col)
                {
    
    
                    expend(mine_1, mine_2, row, col, i, j);
                }
            }
        }
    }
    else
    {
    
    //递归的出口就是我统计到周围有几个雷
        mine_2[x][y] = count + '0';
}
}
lswin(char mine_2[][COLS], int row, int col)//判断有几个符号‘*
{
    
    
    int i,j,c=0;
    for(i=0;i<row;i++)
        for (j = 0; j < col; j++)
        {
    
    
            if (mine_2[i][j] == '*')
                c++;
        }
    return c;
}

3.游戏函数的声明game.h头文件代码

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#define ROW 9//  显示棋盘的行数
#define COL 9//  显示棋盘的列数
#define ROWS ROW+2//  实际棋盘的行数
#define COLS COL+2//  实际棋盘的列数
#define mine_count 10//设置雷的数量
void InitBoard(char board[][COLS], int rows, int cols, char set);//初始化棋盘
void DisPlayBoard(char board[][COLS], int row, int col);//布置棋盘
void SetMine(char mine[][COLS], int row, int col);//布置雷
void FindMine(char mine_1[][COLS], char mian_2[][COLS], int row, int col);//排查雷
int GetMine(char mine_1[][COLS], int x, int y);//统计雷的四周有几个雷
void expend(char mine_1[][COLS], char mine_2[][COLS], int row, int  col, int x, int  y);//扩展坐标的范围
int lswin(char mine_2[][COLS], int row, int col);//统计有几个字符‘*’

测试

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/plj521/article/details/131188953