程序简介
六边形扫雷,寻宝模式,稍稍介绍一下。
他也是要把所有安全的地方点出来。
他没有扫雷模式的消零算法。每一个安全的点都需要单独挖出来,一次显示一个格子。
添加了生命值的概念,也就是说存在一定的容错。
显示的数字有别于扫雷模式。点击宝藏点,会显示周围宝藏点数量,绿色;点击地雷,会显示周围地雷数量,黑色。注意,这个数字不包括自己,显示的范围自然就是 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