扫雷游戏(简易版)

想完成一个扫雷游戏,需要思考以下的因素

  • 游戏界面(老生常谈的事了)

  • 扫雷实则有两个“棋盘”,一个是放置雷的,一个是显示给玩家的

  • 怎么布置随机的地雷

  • 若玩家选择的地方是地雷,该如何进行接下来的程序。若不是雷,怎么表示周围雷的个数?

  • 是雷和不是雷,怎么表示?

游戏菜单

首先,一个游戏应该有菜单,就是给玩家选择是否开始游戏,且需要保证在游戏结束后,跳回菜单,让玩家可以一直玩下去,而不用退出游戏后再进入——需要用到循环语句和条件语句

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);
        switch (input)
        {
        case 1:
            game();//扫雷游戏
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("选择错误,重新选择\n");
            break;
        }
    } while (input);//若输入0的时候就会退出游戏
    return 0;
}

游戏界面

现在就是制作游戏界面,由于扫雷游戏不仅要给玩家展示每一次选择后的结果,而且还需要定下雷

还有界面边界的时候,不需要读取“棋盘”外边的区域,那么怎么解决?

————用两个数组保存,一个数组show保存每次的游戏状态,另一个数组mine保存雷的位置,进行选择时会在mine数组里选择,但反馈的情况会在show里展现给玩家,其次为了解决边界的情况——9*9大的“棋盘”,我们选择用11*11的数组,这样的话边界的区域也能构成一个九宫格进行地雷的排查

初始化数组

void game()
{
    char mine[11][11] = { 0 };//存放布置好的雷的信息
    char show[11][11] = { 0 };//存放排查出的雷的信息
}

初始化棋盘

现在应该将棋盘初始化,我们应该设想,如果是雷,用什么表示,不是雷,又用什么表示?

除此之外,由于排除一块区域后,我们需要显示周围地雷数,所以我们干脆将雷设置为'1',

不是雷就设置为'0'。且初始化时要对11*11的数组全部设置

InitBoard(mine, 11, 11, '0');//'0'表示不放置雷,初始化时,默认没有雷
InitBoard(show, 11, 11, '*');//'*'表示此处未知
void InitBoard(char board[11][11], int row, int col, char set)//set表示该区域的状态
{
    int i = 0;
    int j = 0;
    for (i = 0; i < row; i++)//控制行数
    {
        for (j = 0; j < col; j++)//控制列数
        {
            board[i][j] = set;//先设置好每个方块的状态—* 
        }
    }
}

且用11*11的数组,可以减少代码量,超出部分默认为0,设置雷的时候就放在9*9里即可

展示棋盘

在游戏开始到结束,每进行一次步骤,都需要显示一次棋盘给玩家,让玩家知道情况,但只显示9*9的棋盘

DisplayBoard(show, 9, 9);

void DisplayBoard(char board[11][11], int row, int col)//对应整个数组11*11
{
    int i = 0;
    int j = 0;
    printf("---------扫雷游戏-----------\n");
    for(int i=0;i<=row;i++)//控制行数
    {
      for(int j=0;j<=col;j++)//控制列情况
      {
        printf("%c ",board[i][j]);
      }
      printf("\n");
    }
    printf("---------扫雷游戏-----------\n");
}

显示出来的就是这种表格,但是,这样会给玩家进行选择的时候,造成一定程度上的干扰2,那我们是否可以增添行列数,来实现游戏的简化?

void DisplayBoard(char board[ROWS][COLS], int row, int col)//对应整个数组11*11
{
    int i = 0;
    int j = 0;
    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");//空行
    }
    printf("---------扫雷游戏-----------\n");
}

简化后的,会更加简洁

布置雷

地雷一定是随机的,所以需要随机数的帮忙

随机数怎么布置?——就用rand函数,然后还需要用srand函数来设置时间戳

主函数就变为

int main()
{
    int input = 0;
    srand((unsigned int)time(NULL));//在主函数里使用,这样每次调用的时候随机值就已经定好

    do
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            game();//扫雷游戏
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("选择错误,重新选择\n");
            break;
        }
    } while (input);

    return 0;
}

将时间戳在主函数里设置,那么每次刚开始运行时,随机数就已经被设置好

接着,我们需要布置雷——怎么布置——在mine数组上实现,且仅作用于9*9的数组

//布置雷
SetMine(mine, 9, 9);

void SetMine(char mine[11][11], int row, int col)
{
    //布置10个雷
    int count = EASY_COUNT;//为什么要设置成EASY_COUNT?——因为宏定义里,EASY_COUNT就是雷的个数—10
    while (count)
    {
        //生成随机的下标,11*11--有效部分就是1-9
        int x = rand()%row+1;//rand%9--0-8  +1 --> 1-9
        int y = rand()%col+1;
        if (mine[x][y] == '0')//如果该地方没有雷,则放置,放置反复放置雷
        {
            mine[x][y] = '1';
            count--;
        }
    }
}

我们这里将雷的个数宏定义成EASY_COUNT,原因在后面讲

因为随机数是0-32767,且膜上9后,结果必定是0-8,加上1后就是1-9

且每次设置前,需要判断该地方是否已经设置过。若设置过,则进行又一轮的循环

排查雷

设置好雷后,我们需要进行排查,选定一个区域

  • 选的区域是雷,则提示游戏失败并且结束游戏

  • 选的区域不是雷,则统计周围雷的个数,并且打印出来

与其同时,输入的是11*11的数组,输出的是9*9的数组

判断时用mine数组判断,反馈情况用show数组表示出来

//排查雷
FindMine(mine, show, 9, 9);

void FindMine(char mine[11][11], char show[11][11], int row, int col)
{
    //1. 输入排查的坐标
    //2. 检查坐标处是不是雷
       // (1) 是雷   - 很遗憾炸死了 - 游戏结束
       // (2) 不是雷  - 统计坐标周围有几个雷 - 存储排查雷的信息到show数组,游戏继续
    int x = 0;
    int y = 0;
    int win = 0;
    while (win<row*col- EASY_COUNT)//最多行走71次,因为有10个雷
    {
        printf("请输入要排查的坐标:>");
        scanf("%d%d", &x, &y);//x--(1,9)  y--(1,9)

        //判断坐标的合法性
        if (x >= 1 && x <= row && y >= 1 && y <= col)
        {
            if (mine[x][y] == '1')//如果该位置是雷
            {
                printf("很遗憾,你被炸死了\n");
                DisplayBoard(mine, row, col);//展示一次游戏
                break;//停止循环跳出
            }
            else//若不是雷
            {
                //不是雷情况下,统计x,y坐标周围有几个雷
                int count = get_mine_count(mine, x, y);
                show[x][y] = count+'0';
                //mine函数里的情况会反馈给show,再用show,展示出来
                //然后且有static函数来保留每次的结果  
                //会将常数转化为对应的数字字符
                //字符加上'0'就会转化为相对的数字
                //显示排查出的信息,展示棋盘
                DisplayBoard(show, row, col);
                win++;
            }
        }
        else
        {
            printf("坐标不合法,请重新输入\n");
        }
    }
    if (win == row * col - EASY_COUNT)//排查次数为71次的时候且没有碰到雷,则胜利
    {
        printf("恭喜你,排雷成功\n");
        DisplayBoard(mine, row, col);
    }
}

那我们怎么统计雷的个数呢?前面提到“是雷为1,不是则为0”,那么周围有3个雷,那么反馈的就是3,所以雷的个数就是选定区域周围八个区域的数字和

且,我们每次输出后,就需要保留当时展现的棋盘的状况,因此需要一个函数

——static函数,具体用法可以用msdn或者csdn上搜索

static int get_mine_count(char mine[11][11], 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';//一个字符对应一个数字,所以需要乘'0'
}

重点是:由于数组里存放的是字符,那么我们可以用字符-'0'的形式来使字符转化为数字!!!!!!!!

实现多格的扫雷

上述显示的是9*9的情况,那如果我们想实现11*11,10*10,20*20,且改变雷的个数怎么办?

难道要一个一个修改吗?

这时候我们需要用宏定义,设立两个文件game.c(函数的实现),game.h(函数的声明以及宏定义)

给大家展示最后的代码

//'game.h'
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
//在宏定义里改变数字,就能轻易地改变雷的数量以及行列数

#define EASY_COUNT 10

#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);

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

//'game.c'
#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

//初始化全部的方块
//为什么是11*11,因为在排查雷的时候,边界排查时会超出数组,为了减少代码量
//直接用11*11,超出部分直接默认为0
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;//先设置好每个方块的状态—* 
        }
    }

}

//仅显示9*9的部分,但接收11*11的数组,多余的地方用来打印行列数
void DisplayBoard(char board[ROWS][COLS], int row, int col)//对应整个数组11*11
{
    int i = 0;
    int j = 0;
    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");//空行
    }
    printf("---------扫雷游戏-----------\n");
}



void SetMine(char mine[ROWS][COLS], int row, int col)
{
    //布置10个雷
    int count = EASY_COUNT;//为什么要设置成EASY_COUNT?——因为宏定义里,EASY_COUNT就是雷的个数—10
    while (count)
    {
        //生成随机的下标,11*11--有效部分就是1-9
        int x = rand()%row+1;//rand%9--0-8  +1 --> 1-9
        int y = rand()%col+1;
        if (mine[x][y] == '0')//如果该地方没有雷,则放置,放置反复放置雷
        {
            mine[x][y] = '1';
            count--;
        }
    }
}

//static
//1. 修饰局部变量
//2. 修饰全局变量
//3. 修饰函数

//用static修饰,可以保证每次排查后的数字保存

//且有雷就是1,没有雷就是0,周围的数字相加后就是该地区周围雷的个数
//仅传入有效的部分9*9    超出9*9部分不用管,因为有雷才会是1,统计后的只会是雷的个数,
//超出部分未被定义就是0

static int get_mine_count(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';//一个字符对应一个数字,所以需要乘'0'
}

void FindMine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    //1. 输入排查的坐标
    //2. 检查坐标处是不是雷
       // (1) 是雷   - 很遗憾炸死了 - 游戏结束
       // (2) 不是雷  - 统计坐标周围有几个雷 - 存储排查雷的信息到show数组,游戏继续

    int x = 0;
    int y = 0;
    int win = 0;

    while (win<row*col- EASY_COUNT)//最多行走71次,因为有10个雷
    {
        printf("请输入要排查的坐标:>");
        scanf("%d%d", &x, &y);//x--(1,9)  y--(1,9)

        //判断坐标的合法性
        if (x >= 1 && x <= row && y >= 1 && y <= col)
        {
            if (mine[x][y] == '1')//如果该位置是雷
            {
                printf("很遗憾,你被炸死了\n");
                DisplayBoard(mine, row, col);//展示一次游戏
                break;//停止循环跳出
            }
            else//若不是雷
            {
                //不是雷情况下,统计x,y坐标周围有几个雷
                int count = get_mine_count(mine, x, y);
                show[x][y] = count+'0';//会将常数转化为对应的数字字符
                //字符加上'0'就会转化为相对的数字
                //显示排查出的信息,展示棋盘
                DisplayBoard(show, row, col);
                win++;
            }
        }
        else
        {
            printf("坐标不合法,请重新输入\n");
        }
    }

    if (win == row * col - EASY_COUNT)//排查次数为71次的时候且没有碰到雷,则胜利
    {
        printf("恭喜你,排雷成功\n");
        DisplayBoard(mine, row, col);
    }
}

//'扫雷'
#define _CRT_SECURE_NO_WARNINGS 1

#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};//存放排查出的雷的信息
    //初始化棋盘
    InitBoard(mine, ROWS, COLS, '0');//'0'表示不放置雷,初始化时,默认没有雷
    InitBoard(show, ROWS, COLS, '*');//'*',表示位置

    //打印一下棋盘
    DisplayBoard(show, ROW, COL);

    //布置雷
    SetMine(mine, ROW, COL);
    //DisplayBoard(mine, ROW, COL);
    
    //排查雷
    FindMine(mine, show, ROW, COL);
}

int main()
{
    int input = 0;
    srand((unsigned int)time(NULL));//在主函数里使用,这样每次调用的时候随机值就已经定好

    do
    {
        menu();
        printf("请选择:>");
        scanf("%d", &input);
        switch (input)
        {
        case 1:
            game();//扫雷游戏
            break;
        case 0:
            printf("退出游戏\n");
            break;
        default:
            printf("选择错误,重新选择\n");
            break;
        }
    } while (input);

    return 0;
}

这个扫雷比起我们以往玩过的游戏,还缺少了自动扫出为0区域的功能,所以需要大家可以思考,我会在后些日子发布出来,供大家参考讨论交流

祝:除夕快乐

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Nick-An

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

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

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

打赏作者

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

抵扣说明:

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

余额充值