C语言零基础项目:六边形扫雷寻宝模式,详细思路+源码分享

程序简介

六边形扫雷,寻宝模式,稍稍介绍一下。

他也是要把所有安全的地方点出来。

他没有扫雷模式的消零算法。每一个安全的点都需要单独挖出来,一次显示一个格子。

添加了生命值的概念,也就是说存在一定的容错。

显示的数字有别于扫雷模式。点击宝藏点,会显示周围宝藏点数量,绿色;点击地雷,会显示周围地雷数量,黑色。注意,这个数字不包括自己,显示的范围自然就是 0~6 了。点击地雷会减生命值,生命值归零则结束。

所以雷和宝藏都是有价值的,都是能给准确信息的。

我能给一个参考难度:占总格子数 40%的地雷,占总地雷数 50 %的生命值。

程序运行展示

完整源代码

# include <math.h>
# include <graphics.h>
# include <string>
# include <time.h>

static double pi = acos (-1.0);            // 圆周率 π
static HWND hOut;                        // 画布

// 定义一个结构体,按钮
struct Node1
{
    int posx1, posy1, posx2, posy2;        // 坐标
    LPTSTR text;                        // 文字
    int mod;                            // 状态
};

// 定义一个结构体,六边形格子
struct Node2
{
    int i, j, k;                        // 特征值
    int mod_life;                        // 翻开
    int mod_mine;                        // 雷
    int mod_flag;                        // 标记
    int posx, posy;                        // 坐标
    int num_mine;                        // 周围雷数
    int num_peace;                        // 周围空地块
};

// 定义一个类
class Gary
{
public:
    void carry ();                        // 主进程
    void initialization ();                // 初始化
    void draw_scene ();                    // 绘制界面函数
    void draw_box (int num_box);        // 绘制格子
    void draw_flag (int num_box);        // 绘制标记
    void draw_num (int num_box, int num);    // 绘制数字
    void move ();                        // 窗口主视角
    void create ();                        // 地雷生成
    void check_over ();                    // 结束判定

    int num_button;                        // 按钮数量参数
    int exit_carry;                        // 主循函数控制参数
    int exit_move;                        // 开始界面控制参数
    int exit_game;                        // 游戏进行控制参数
    int num_life;                        // 生命值
    int num_size;                        // 边长
    int num_mine;                        // 总雷数
    int num_box;                        // 总地块数
    int num_flag;                        // 标记数
    COLORREF color_text[2];                // 按钮绘制填充
    Node1 boxm[30];                        // 按钮,预制 30 个
    Node2 box[1000];                    // 地块
};

// 标记绘制函数
void Gary::draw_flag (int num_box)
{
    setlinestyle (PS_SOLID, 1);
    setlinecolor (BLACK);
    line (box[num_box].posx + 2, box[num_box].posy + 7, box[num_box].posx + 2, box[num_box].posy - 7);
    setfillcolor (LIGHTRED);
    setlinecolor (LIGHTRED);
    fillrectangle (box[num_box].posx - 7 + 2, box[num_box].posy - 7, box[num_box].posx + 2, box[num_box].posy - 1);
}

// 数字绘制函数
void Gary::draw_num (int num_box, int num)
{
    int i;
    // 画六边形,格子处于点击后状态
    setfillcolor (RGB (170, 170, 170));
    setlinecolor (RGB (85, 85, 85));
    POINT pts[6];
    setlinestyle (PS_SOLID, 1);
    for (i = 0; i < 6; i++)
    {
        pts[i].x = long(box[num_box].posx + 14.0 * cos (60.0 * double (i) * pi / 180.0));
        pts[i].y = long(box[num_box].posy + 14.0 * sin (60.0 * double (i) * pi / 180.0));
    }
    fillpolygon (pts, 6);

    // 数字绘制
    TCHAR s[15];
    settextstyle (20, 0, _T ("Consolas"));
    _stprintf_s (s, _T ("%0.1d"), num);
    outtextxy (box[num_box].posx - 5, box[num_box].posy - 10, s);
}

// 场景绘制函数
void Gary::draw_scene ()
{
    TCHAR s[15];
    int i, j;
    setlinecolor (BLACK);
    setfillcolor (WHITE);
    setlinestyle (PS_SOLID, 1);
    // 主界面
    fillrectangle (401, 0, 650, 400);
    // 根据按钮数量绘制    
    settextcolor (BLACK);
    for (i = 0; i < num_button; i++)
    {
        setfillcolor (color_text[boxm[i].mod]);
        setbkcolor (color_text[boxm[i].mod]);
        // 边框
        fillrectangle (boxm[i].posx1, boxm[i].posy1, boxm[i].posx2, boxm[i].posy2);
        // 文字
        outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + 4, boxm[i].text);
    }

    // 设置参数
    setbkcolor (WHITE);
    settextcolor (BLACK);
    setlinecolor (BLACK);

    // 变量绘制
    j = 25;
    // 生命值
    i = 1;
    setbkcolor (color_text[boxm[i].mod]);
    _stprintf_s (s, _T ("%0.1d"), num_life);
    outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + j, s);
    // 边长
    i = 2;
    setbkcolor (color_text[boxm[i].mod]);
    _stprintf_s (s, _T ("%0.1d"), num_size);
    outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + j, s);
    // 总地雷数
    i = 3;
    setbkcolor (color_text[boxm[i].mod]);
    _stprintf_s (s, _T ("%0.1d"), num_mine);
    outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + j, s);
    // 格子
    i = 4;
    setbkcolor (color_text[boxm[i].mod]);
    _stprintf_s (s, _T ("%0.1d"), num_box);
    outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + j, s);
    // 标记数
    i = 5;
    setbkcolor (color_text[boxm[i].mod]);
    _stprintf_s (s, _T ("%0.1d"), num_flag);
    outtextxy (boxm[i].posx1 + (boxm[i].posx2 - boxm[i].posx1) / 2 - textwidth (boxm[i].text) / 2, boxm[i].posy1 + j, s);

    FlushBatchDraw ();
}

// 地雷生成函数
void Gary::create ()
{
    int i, j;
    // 设置雷
    for (i = 0; i < num_mine; i++)
    {
        // 随机
        j = rand () % 1000;
        while (box[j].mod_mine == 1 || box[j].mod_life == 1)
        {
            // 随机
            j = rand () % 1000;
        }
        // 是雷
        box[j].mod_mine = 1;
    }
    // 周边雷数统计
    // 遍历
    for (i = 0; i <= 888; i++)
    {
        if (box[i].mod_life == 0)
        {
            // 遍历
            for (j = 0; j <= 999; j++)
            {
                // 排除自己
                if (j != i && box[j].mod_life == 0)
                {
                    // 周围六个
                    if ((box[j].posx - box[i].posx) * (box[j].posx - box[i].posx) + (box[j].posy - box[i].posy) * (box[j].posy - box[i].posy) <= 900)
                    {
                        // 是雷
                        if (box[j].mod_mine == 1)
                        {
                            // 周边雷数参数加一
                            box[i].num_mine++;
                        }
                        // 不是雷
                        else if (box[j].mod_mine == 0)
                        {
                            // 周边安全数参数加一
                            box[i].num_peace++;
                        }
                    }
                }
            }
        }
    }
}

// 结束判断函数
void Gary::check_over ()
{
    int i, k;
    k = 0;
    for (i = 0; i <= 888; i++)
    {
        // 每有一个翻开且不是雷的点,则加一
        if (box[i].mod_mine == 0 && box[i].mod_life == 1)
        {
            k++;
        }
    }
    // 全翻开则结束
    if (k == num_box - num_mine)
    {
        // 将所有未翻开雷做上标记
        for (i = 0; i <= 888; i++)
        {
            if (box[i].mod_mine == 1 && box[i].mod_life == 0)
            {
                draw_flag (i);
            }
        }
        // 胜利标志:笑脸
        setfillcolor (WHITE);
        setlinecolor (WHITE);
        fillrectangle (50, 20, 75, 45);
        settextstyle (30, 0, _T ("Wingdings"));
        setbkmode (TRANSPARENT);
        settextcolor (BLACK);
        outtextxy (50, 20, 0x4A);
        setbkmode (OPAQUE);
        settextstyle (20, 0, _T ("Consolas"));
        // 结束变化
        exit_game = 1;
        boxm[1].mod = 0;
        boxm[2].mod = 0;
        boxm[3].mod = 0;
        boxm[6].mod = 0;
        boxm[7].mod = 1;
        num_flag = 0;
        // 绘制
        draw_scene ();
    }
}

// 格子绘制函数
void Gary::draw_box (int num_box)
{
    int i;
    int posx, posy;

    // 六边形绘制
    posx = box[num_box].posx;
    posy = box[num_box].posy;

    POINT pts[6];
    setlinestyle (PS_SOLID, 2);
    // 背景色
    setfillcolor (RGB (255, 255, 255));
    for (i = 0; i < 6; i++)
    {
        pts[i].x = long(posx + 14.0 * cos (60.0 * double (i) * pi / 180.0));
        pts[i].y = long(posy + 14.0 * sin (60.0 * double (i) * pi / 180.0));
    }
    solidpolygon (pts, 6);
    // 灰边
    setlinecolor (RGB (85, 85, 85));
    line (pts[0].x, pts[0].y, pts[1].x, pts[1].y);
    line (pts[5].x, pts[5].y, pts[0].x, pts[0].y);
    line (pts[1].x, pts[1].y, pts[2].x, pts[2].y);
    // 前景色
    setfillcolor (RGB (170, 170, 170));
    for (i = 0; i < 6; i++)
    {
        pts[i].x = long(posx + 12.0 * cos (60.0 * double (i) * pi / 180.0));
        pts[i].y = long(posy + 12.0 * sin (60.0 * double (i) * pi / 180.0));
    }
    solidpolygon (pts, 6);
    FlushBatchDraw ();
}

// 初始化函数
void Gary::initialization ()
{
    int i, j, k, t;
    // 随机初始化
    srand ((unsigned)time (NULL));
    // 颜色初始化
    color_text[0] = WHITE;
    color_text[1] = RGB (170, 170, 170);

    // 按钮的初始化
    num_button = 10;

    // 坐标
    for (i = 0; i < 10; i++)
    {
        boxm[i].posx1 = 410 + 120 * (i % 2);
        boxm[i].posy1 = 25 + 75 * (i / 2);
        boxm[i].posx2 = 520 + 120 * (i % 2);
        boxm[i].posy2 = 75 + 75 * (i / 2);
    }

    // 内容
    boxm[0].text = _T ("寻宝模式");    boxm[1].text = _T ("生命值");
    boxm[2].text = _T ("地图边长");    boxm[3].text = _T ("总地雷数");
    boxm[4].text = _T ("总地块数");    boxm[5].text = _T ("已标记数");
    boxm[6].text = _T ("开始");        boxm[7].text = _T ("重置");
    boxm[8].text = _T ("截图");        boxm[9].text = _T ("退出");

    // 状态
    boxm[0].mod = 1;
    boxm[1].mod = 1;
    boxm[2].mod = 1;
    boxm[3].mod = 1;
    boxm[4].mod = 1;
    boxm[5].mod = 1;
    boxm[6].mod = 1;
    boxm[7].mod = 0;
    boxm[8].mod = 0;
    boxm[9].mod = 0;

    num_box = 3 * num_size * (num_size - 1) + 1;
    num_flag = 0;

    // 绘制参数初始化
    setlinecolor (BLACK);
    setlinestyle (PS_SOLID, 1);
    settextstyle (20, 0, _T ("Consolas"));
    // 第一次绘制
    draw_scene ();

    // 重置
    setfillcolor (WHITE);
    fillrectangle (0, 0, 400, 400);

    // 平静脸
    setfillcolor (WHITE);
    setlinecolor (WHITE);
    fillrectangle (50, 20, 75, 45);
    settextstyle (30, 0, _T ("Wingdings"));
    setbkmode (TRANSPARENT);
    settextcolor (BLACK);
    outtextxy (50, 20, 0x4B);
    setbkmode (OPAQUE);
    settextstyle (20, 0, _T ("Consolas"));

    // 格子初始化
    for (t = 0; t <= 999; t++)
    {
        // 已翻开
        box[t].mod_life = 1;
        // 城墙
        box[t].mod_mine = 2;
        // 坐标,点不到
        box[t].posx = -200;
        box[t].posy = -200;
    }

    // 初始化
    for (i = 0; i < num_size; i++)
    {
        for (j = 0; j < num_size; j++)
        {
            for (k = 0; k < num_size; k++)
            {
                // 特征值至少一个为零
                if (i == 0 || j == 0 || k == 0)
                {
                    // 编号
                    t = i * 100 + j * 10 + k;
                    // 特征值
                    box[t].i = i;
                    box[t].j = j;
                    box[t].k = k;
                    // 未翻开
                    box[t].mod_life = 0;
                    // 不是雷
                    box[t].mod_mine = 0;
                    // 未标记
                    box[t].mod_flag = 0;
                    // 坐标
                    box[t].posx = 200 + 22 * (j - k);
                    box[t].posy = 200 - 25 * i + 13 * (j + k);
                    // 周围雷数初始化
                    box[t].num_mine = 0;
                    box[t].num_peace = 0;
                    // 绘制地块
                    draw_box (t);
                }
            }
        }
    }
    // 地雷生成函数
    create ();
}

// 窗口主视角函数,获取用户操作
void Gary::move ()
{
    // 鼠标定义
    ExMessage m;
    TCHAR ss[15];
    int i, t;
    exit_move = 0;
    exit_game = 0;
    while (exit_move == 0)
    {
        // 鼠标信息
        if (peekmessage (&m, EM_MOUSE | EM_KEY))
        {
            // 左键单击判断
            if (m.message == WM_LBUTTONDOWN)
            {
                // 判断是否点击了格子
                if (m.x > 0 && m.y > 0 && m.x < 400 && m.y < 400 && exit_game == 0)
                {
                    for (t = 0; t <= 888; t++)
                    {
                        // 成功点击未标记的空格子
                        if ((m.x - box[t].posx) * (m.x - box[t].posx) + (m.y - box[t].posy) * (m.y - box[t].posy) <= 144 && box[t].mod_life == 0 && box[t].mod_flag == 0)
                        {
                            // 点击的格子不是雷
                            if (box[t].mod_mine == 0)
                            {
                                // 绿色,安全,绘制
                                settextcolor (LIGHTGREEN);
                                draw_num (t, box[t].num_peace);
                                // 改为翻开
                                box[t].mod_life = 1;
                            }
                            // 点击的格子雷
                            else if (box[t].mod_mine == 1)
                            {
                                // 扣除生命值
                                num_life--;
                                // 黑色,危险,绘制
                                settextcolor (BLACK);
                                draw_num (t, box[t].num_mine);
                                // 改为翻开
                                box[t].mod_life = 1;
                                // 生命值减为零
                                if (num_life <= 0)
                                {
                                    // 失败标志:哭脸
                                    setfillcolor (WHITE);
                                    setlinecolor (WHITE);
                                    fillrectangle (50, 20, 75, 45);
                                    settextstyle (30, 0, _T ("Wingdings"));
                                    setbkmode (TRANSPARENT);
                                    settextcolor (BLACK);
                                    outtextxy (50, 20, 0x4C);
                                    setbkmode (OPAQUE);
                                    settextstyle (20, 0, _T ("Consolas"));
                                    // 失败
                                    exit_game = 1;
                                    boxm[1].mod = 0;
                                    boxm[2].mod = 0;
                                    boxm[3].mod = 0;
                                    boxm[6].mod = 0;
                                    boxm[7].mod = 1;
                                    num_flag = 0;
                                }
                                // 绘制
                                draw_scene ();
                            }
                            // 成功结束判断
                            check_over ();
                            break;
                        }
                    }
                }

                // 判断是否点击了可点击按钮
                for (i = 0; i < num_button; i++)
                {
                    if (m.x > boxm[i].posx1 && m.y > boxm[i].posy1 && m.x < boxm[i].posx2 && m.y < boxm[i].posy2 && boxm[i].mod == 0)
                    {
                        break;
                    }
                }

                // 点击矩形按钮
                switch (i)
                {
                // 生命值:num_life
                case 1:
                {
                    // 输入
                    InputBox (ss, 10, _T ("输入生命值(1 ~ 999)"));
                    _stscanf_s (ss, _T ("%d"), &i);
                    if (i > 0 && i <= 999)
                    {
                        num_life = i;
                    }
                    else { MessageBox (hOut, _T ("输入错误,不在范围内"), _T ("来自小豆子的提醒"), MB_OK); }
                    // 绘制
                    draw_scene ();
                    break;
                }
                // 地图边长:num_size
                case 2:
                {
                    // 输入
                    InputBox (ss, 10, _T ("输入边长(2 ~ 8)"));
                    _stscanf_s (ss, _T ("%d"), &i);
                    if (i > 1 && i <= 8)
                    {
                        num_size = i;
                        num_box = 3 * num_size * (num_size - 1) + 1;
                    }
                    else { MessageBox (hOut, _T ("输入错误,不在范围内"), _T ("来自小豆子的提醒"), MB_OK); }
                    // 绘制
                    draw_scene ();
                    break;
                }
                // 总地雷数:num_mine
                case 3:
                {
                    InputBox (ss, 10, _T ("输入地雷数(1 ~ 总格子数)"));
                    _stscanf_s (ss, _T ("%d"), &i);
                    if (i > 0 && i < num_box)
                    {
                        num_mine = i;
                    }
                    else { MessageBox (hOut, _T ("输入错误,不在范围内"), _T ("来自小豆子的提醒"), MB_OK); }
                    // 绘制
                    draw_scene ();
                    break;
                }
                // 开始
                case 6:
                {
                    num_box = 3 * num_size * (num_size - 1) + 1;
                    if (num_mine < num_box && num_life > 0)
                    {
                        exit_game = 0;
                        // 初始化
                        initialization ();
                    }
                    else
                    {
                        MessageBox (hOut, _T ("请将雷数修改为小于格子数或将生命值修改为大于零"), _T ("来自小豆子的提醒"), MB_OK);
                    }
                    break;
                }
                // 重置
                case 7:
                {
                    // 结束游戏进程,进入准备阶段
                    if (exit_game == 0)
                    {
                        exit_game = 1;
                        boxm[1].mod = 0;
                        boxm[2].mod = 0;
                        boxm[3].mod = 0;
                        boxm[6].mod = 0;
                        boxm[7].mod = 1;
                        num_flag = 0;
                        // 绘制
                        draw_scene ();
                    }
                    break;
                }
                // 截图
                case 8:
                {
                    saveimage (_T ("image.png"));
                    break;
                }
                // 退出
                case 9:
                {
                    exit_game = 1;
                    exit_move = 1;
                    exit_carry = 1;
                    break;
                }
                default:break;
                }
            }
            // 右键,且处于游戏进行状态
            else if (m.message == WM_RBUTTONDOWN && exit_game == 0)
            {
                for (t = 0; t <= 888; t++)
                {
                    // 成功点击空格子
                    if ((m.x - box[t].posx) * (m.x - box[t].posx) + (m.y - box[t].posy) * (m.y - box[t].posy) <= 144 && box[t].mod_life == 0)
                    {
                        // 标记状态转换
                        box[t].mod_flag = (box[t].mod_flag == 0 ? 1 : 0);
                        // 绘制
                        draw_box (t);
                        // 画小旗子
                        if (box[t].mod_flag == 1)
                        {
                            draw_flag (t);
                            num_flag++;
                        }
                        else
                        {
                            num_flag--;
                        }
                        // 绘制
                        draw_scene ();
                    }
                }
            }
        }
    }
}

// 主进程
void Gary::carry ()
{
    // 窗口定义
    hOut = initgraph (651, 401);
    SetWindowText (hOut, _T ("六边形扫雷:扫雷模式"));
    // 参数初始化
    num_size = 5;
    num_mine = 10;
    num_life = 3;
    // 背景绘制
    setbkcolor (WHITE);
    cleardevice ();
    // 进程控制
    exit_carry = 0;
    while (exit_carry == 0)
    {
        initialization ();
        move ();
    }
    closegraph ();
}

// 主函数
int main (void)
{
    Gary G;
    G.carry ();
    return 0;
}

大家赶紧去动手试试吧!

此外,我也给大家分享我收集的其他资源,从最零基础开始的教程到C语言C++项目案例,帮助大家在学习C语言的道路上披荆斩棘!

整理分享(多年学习的源码、项目实战视频、项目笔记,基础入门教程)最重要的是你可以在群里面交流提问编程问题哦!

欢迎转行和学习编程的伙伴,利用更多的资料学习成长比自己琢磨更快哦!

【C语言】五小时快速入门C语言:https://nxv.xet.tech/s/1tvM22

【C语言】零基础到项目实战(交换机项目):https://nxv.xet.tech/s/2mF2w6

【C++】实战入门:智能婚恋交友系统:https://nxv.xet.tech/s/1eP6Qq

【C/C++】指针精讲:https://nxv.xet.tech/s/3w6L3x

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

C语言小火车

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

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

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

打赏作者

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

抵扣说明:

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

余额充值