C语言制作简单扫雷

前言

扫雷,这个小游戏相信大家应该不会很陌生吧,今天我们就来用C语言制作一个简易版的扫雷游戏,主要运用的是C语言二维数组的知识点和应用。

初步设计思路
在制作扫雷之前,我们要先理一下制作的初步思路。

这里我们制作一个棋盘大小为9*9的扫雷,里面放上10个雷,棋盘就用二维数组来表示。但是,在我们布置好雷之后要将棋盘打印出来,并且不能让雷暴露,这里如果只用一个二维数组表示的话很困难,所以可以使用两个二维数组,一个用来布置雷,另一个将雷隐藏并打印棋盘给玩家看。

在扫雷游戏中,没有踩到雷时会显示周围8个格子中的雷的个数,这里我们设置的棋盘是9*9,踩在边界处时检测周围8个格子会超出数组的范围,所以我们在定义数组时,可以将行与列各+2,也就是创建11*11的数组,但打印棋盘时纸打印中间9*9的部分。

为了让代码看起来更美观,我们可以用三个代码块来实现扫雷小游戏,分别是test.c(测试)、game.c()(游戏主体)、game.h(头文件)。因为在test.c和game.c中会同时使用一些全局变量或函数声明,所以我们把这些都放在game.h中,在test.c和game.c中包含game.h。

#include"game.h"
主函数
现在就可以开始编写程序了,首先是主函数,在主函数中,我们定义一个名为test的函数来调用游戏

/* test.c */
int main()
{
    test();
    return 0;
}


菜单
在函数test中定义一个名为menu的函数用来打印菜单,然后在函数menu中发挥想象力制作一个好康的菜单。之后在函数test中调用switch语句来对玩家的选项做出反馈,在switch语句中定义一个名为game的函数,当玩家要玩游戏时,便通过它来进入游戏。

/* test.c */
//菜单

void menu()
{
    printf("**************************\n");
    printf("*******   1.play   *******\n");
    printf("*******   0.exit   *******\n");
    printf("**************************\n");
}
 
void test()
{
    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);
}

游戏主体设计思路
在函数game中,要编写出游戏的主体,这里可以将游戏分为4个模块来实现,分别是:初始化棋盘、打印棋盘、布置雷、检测雷。

因为在最开始设计时的思路就是两个棋盘,所以我们要先创建两个二维数组。为了后面编写代码时可以减少代码量,在game.h中用宏定义来控制棋盘

/* game.h */
#define ROW 9
#define COL 9
 
#define ROWS ROW+2
#define COLS COL+2
 

/* test.c */
char mine[ROWS][COLS] = { 0 };//布置雷的
char show[ROWS][COLS] = { 0 };//给玩家看的


初始化棋盘
首先是初始化棋盘,数组mine是用来布置雷的,为了后面排查雷的代码更方便编写,这里将数组mine中所有元素初始化为字符'0';数组show是用来给玩家看的,将其初始化为字符'*',这里为了减少代码量,在函数声明时就可以将两个数组对应的字符放进去。

/* game.h */
//初始化棋盘的函数声明
void init_board(char board[ROWS][COLS], int rows, int cols, char set);
/* test.c */
//初始化棋盘
init_board(mine, ROWS, COLS, '0');
init_board(show, ROWS, COLS, '*');
/* game.c */
//初始化棋盘的函数体
void init_board(char board[ROWS][COLS], int rows, int cols, char set)
{
    int i = 0;
    for (i = 0; i < rows; i++)
    {
        int j = 0;
        for (j = 0; j < cols; j++)
        {
            board[i][j] = set;
        }
    }
}


打印棋盘
其次是打印棋盘,这里要打印的棋盘是9*9大小的,所以将ROW和COL放进函数中,而不是ROWS和COLS(头文件中宏定义的常量),不过,放进函数中的数组还是要11*11的,因为这样初始化后的数组可以直接在函数中使用,不需要进行下标计算,打印时只需要打印从[1][1]到[9][9]就可以了,这也是将ROW和COL放进函数中的原因。这里为了让棋盘看上去更美观,我加了一些符号,老铁们可以自行发挥。

/* game.h */
//打印棋盘的函数声明
void print_board(char board[ROWS][COLS], int row, int col);
/* test.c */
//打印棋盘
//print_board(mine, ROW, COL);//在调试代码时使用,游戏时注释掉,否则就将布置的雷打印出来了
print_board(show, ROW, COL);
/* game.c */
//打印棋盘的函数体
void print_board(char board[ROWS][COLS], int row, int col)
{
    int i = 0;
    printf(" ---------扫雷----------\n");
    printf("|   | ");
    for (i = 1; i <= row; i++)
    {
        printf("%d ", i);
    }
    printf("|");
    printf("\n|---|-------------------|\n");
    for (i = 1; i <= row; i++)
    {
        printf("| %d | ", i);
        int j = 0;
        for (j = 1; j <= col; j++)
        {      
            printf("%c ", board[i][j]);
        }
        printf("|");
        printf("\n");
    }
}

布置雷
然后是布置雷,这里用随机数来布置雷,范围是1~9,代码中row(col)的值是9,所以对其取余后的值就是0~8,再+1,这样就满足要求了。为了避免精度丢失的问题,不要忘了强制转换为unsigned int类型。

在布置雷的函数体中,while循环的条件设置为剩下雷的数量count,每布置一个雷就-1,这样将所有的雷都布置好后循环就结束了。为了后面排查雷更方便一点,这里将是雷的元素变为字符'1',在布置好之后可以调用打印棋盘的函数来看一下效果。

/* game.h */
//布置雷的函数声明

void set_mine(char mine[ROWS][COLS], int row, int col);
//雷的数量
#define COUNT 10
/* test.c */
//布置雷
set_mine(mine, ROW, COL);
//随机数
srand((unsigned int)time(NULL));
/* game.c */
//布置雷的函数体
void set_mine(char mine[ROWS][COLS], int row, int col)
{
    int count = COUNT;
    while (count)
    {
        int x = rand() % row + 1;
        int y = rand() % col + 1;
        if (mine[x][y] == '0')
        {
            mine[x][y] = '1';
            count--;
        }
    }
}

排查雷
最后就是最难的部分——排查雷。因为是将玩家输入的坐标放入数组mine中进行比较,并通过数组show打印出来,所以在定义函数find_mine时要将两个数组都放进去。通过if-else语句的嵌套来实现玩家输入坐标后的几种反馈,在玩家失败后将布置雷的棋盘打印出来,让玩家死的明白。

其中,当玩家没有踩到雷时,要对输入坐标周围8个格子的排查,将周围的雷的数量替换掉输入的坐标所代表的元素('0')。这里可以定义一个函数get_mine_count用来统计周围的雷的数量,将其返回值赋给变量ret。

通过对照ASCII表可以知道,字符'0'~'9'减去字符'0'所得到的值就是对应的数字。比如,字符'0'在ASCII表中的值是48,字符'1'在ASCII表中的值是49,,那么'1'减去'0'的值就是1,其他的也是同理。那么同理,代码中将输入坐标周围的8个元素加起来,再减去8*'0',所得到的值就是周围的雷的数量,将其赋值给ret,然后让ret加上字符'0',结果就是数字所对应的字符,用它替换掉输入的坐标在数组show中所代表的元素,打印出来的效果就是这个坐标周围有几个雷。

/* game.h */
//排查雷的函数声明
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
/* test.c */
//排查雷
find_mine(mine, show, ROW, COL);
/* game.c */
//没踩到雷时统计周围8个格子的雷总和
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');
}


 
//排查雷的函数体

/* game.h */
//排查雷的函数声明
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col);
/* test.c */
//排查雷
find_mine(mine, show, ROW, COL);
/* game.c */
//没踩到雷时统计周围8个格子的雷总和
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');
}

总代码
到此位置,程序的代码主体就完成了,剩下的就是拼接和补充。下面是总代码:

/* game.h */
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
 
#define ROW 9
#define COL 9
 
#define ROWS ROW+2
#define COLS COL+2
 
#define COUNT 10
 
//初始化棋盘的函数声明
void init_board(char board[ROWS][COLS], int rows, int cols, char set);
 
//打印棋盘的函数声明
void print_board(char board[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 board[ROWS][COLS], int rows, int cols, char set)
{
    int i = 0;
    for (i = 0; i < rows; i++)
    {
        int j = 0;
        for (j = 0; j < cols; j++)
        {
            board[i][j] = set;
        }
    }
}
 
//打印棋盘的函数体
void print_board(char board[ROWS][COLS], int row, int col)
{
    int i = 0;
    printf(" ---------扫雷----------\n");
    printf("|   | ");
    for (i = 1; i <= row; i++)
    {
        printf("%d ", i);
    }
    printf("|");
    printf("\n|---|-------------------|\n");
    for (i = 1; i <= row; i++)
    {
        printf("| %d | ", i);
        int j = 0;
        for (j = 1; j <= col; j++)
        {
            
            printf("%c ", board[i][j]);
        }
        printf("|");
        printf("\n");
    }
}
 
//布置雷的函数体
void set_mine(char mine[ROWS][COLS], int row, int col)
{
    int count = COUNT;
    while (count)
    {
        int x = rand() % row + 1;
        int y = rand() % col + 1;
        if (mine[x][y] == '0')
        {
            mine[x][y] = '1';
            count--;
        }
    }
}
 
//没踩到雷时统计周围8个格子的雷总和
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');
}
 
 
//排查雷的函数体
void find_mine(char mine[ROWS][COLS], char show[ROWS][COLS], int row, int col)
{
    int x = 0, y = 0;
    int win = 0;
    while (win < (row * col - 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] == '1')
            {
                printf("很遗憾,你被炸死了\n");
                print_board(mine, ROW, COL);
                break;
            }
            else
            {
                int ret = get_mine_count(mine, x, y);
                show[x][y] = ret + '0';
                print_board(show, ROW, COL);
                win++;
            }
        }
        else
        {
            printf("输入坐标不合法,请重新输入\n");
        }
    }
    if (win == (row * col - COUNT))
    {
        printf("恭喜你,排雷成功\n");
        print_board(mine, ROW, COL);
    }
}

/* test.c */
#include"game.h"
//菜单
void menu()
{
    printf("**************************\n");
    printf("*******   1.play   *******\n");
    printf("*******   0.exit   *******\n");
    printf("**************************\n");
}
//游戏
void game()
{
    //分两个二维数组,一个布置雷,一个显示
    //为了防止棋盘边缘检测出错,所以数组行与列各多2
    char mine[ROWS][COLS] = { 0 };
    char show[ROWS][COLS] = { 0 };
 
    //初始化棋盘
    init_board(mine, ROWS, COLS, '0');
    init_board(show, ROWS, COLS, '*');
    
    //打印棋盘
    //print_board(mine, ROW, COL);
    print_board(show, ROW, COL);
 
    //布置雷
    set_mine(mine, ROW, COL);
    //print_board(mine, ROW, COL);
 
    //排查雷
    find_mine(mine, show, ROW, COL);
 
 
}
 
void test()
{
    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);
 
}
 
int main()
{
    test();
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值