C language zero-based project: hexagonal minesweeping and treasure hunting mode, detailed ideas + source code sharing

Program Introduction

Hexagon minesweeper, treasure hunting mode, a little introduction.

He also wants to point out all the safe places.

He does not have a zero-sweeping algorithm for minesweeper mode. Each safe spot needs to be dug out individually, one grid at a time.

The concept of life value has been added, which means that there is a certain fault tolerance.

The numbers displayed are different from minesweeper mode. Click on the treasure point, it will display the number of surrounding treasure points, green; click on the mine, it will display the number of surrounding mines, in black. Note that this number does not include yourself, and the displayed range is naturally 0~6. Clicking on the mine will reduce the life value, and the life value will end when the life value reaches zero.

Therefore, both mine and treasure are valuable, and both can give accurate information.

I can give a reference difficulty: the mines account for 40% of the total number of grids, and the health value accounts for 50% of the total number of mines.

Program running display

full source code

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

Everyone hurry up and try it out!

In addition, I will also share with you other resources I have collected, from the most basic tutorials to C language C++ project cases, to help you overcome obstacles on the road to learning C language!

Organize and share (source code learned for many years, project actual combat videos, project notes, basic introductory tutorials) the most important thing is that you can communicate and ask programming questions in the group!

Welcome to partners who change careers and learn programming, use more materials to learn and grow faster than thinking about it yourself!

[C language] Five-hour quick introduction to C language: https://nxv.xet.tech/s/1tvM22

[C language] From zero foundation to project actual combat (switch project): https://nxv.xet.tech/s/2mF2w6

【C++】Introduction to actual combat: Smart dating and dating system: https://nxv.xet.tech/s/1eP6Qq

[C/C++] Intensive lecture on pointers: https://nxv.xet.tech/s/3w6L3x

Guess you like

Origin blog.csdn.net/xiangxin1030/article/details/128651446