程序简介
华容道,画风参考的是手机程序:超级华容道。玩法是将大块移动至下层的中间。随机盘面,难度适中,自动解题。
程序随机了横块纵块数量,所以会有无解或难解,生成函数保证了加载出来的盘面是可玩的,但不保证每次生成的都可玩,所以需要有个等待过程。编码函数和解码函数是辅助解题的,解题思路是移动仅有的两个空格,包括两个空格移动,和一个空格移动。游戏下方从左往右是播放,重置,刷新按钮。点击播放开始输出解题过程;点击重置返回盘面初始状态,在游戏过程和播放完毕后使用;刷新即重新生成盘面。没有剪枝,面对类似横刀立马这类难解,只能分段求解才可以。这个程序直接舍弃这种题目了。
更新过加入了站内同学给出的十二张标准谱面,将求解作为按钮单独隔开,这十二关都可以解出来;因此随机生成谱面会有无解的可能,按求解会判断出来。明确了玩法,将出口标记了出来;点击棋子选中,点击相邻空格移动。
程序执行效果:
完整源代码:
// 程序:华容道
// 编译环境:Visual C++ 2010,EasyX_20211109
// 编写日期:2023.2.18
# include <math.h>
# include <graphics.h>
# include <time.h>
# include <string>
static HWND hOut; // 画布
// 定义一个结构体,树
struct Node1
{
int num; // 编号
int num_transverse; // 横向特征值
int num_portrait; // 纵向特征值
int num_other; // 其他特征值
int num_father; // 父结点
};
Node1 box[200001]; // 预制结点
// 定义一个结构体,临时存放
struct Node2
{
int num_transverse; // 横向特征值
int num_portrait; // 纵向特征值
int num_other; // 其他特征值
};
// 定义一个结构体,按钮
struct Node3
{
int posx1, posy1, posx2, posy2; // 坐标
};
// 定义一个类
class Gary
{
public:
void carry(); // 主进程
void initialization(); // 初始化
void move(); // 窗口主视角函数
void draw_scene(int num_box); // 绘制界面函数
void create(); // 生成函数
void decode(); // 解码函数
void code(); // 编码函数
void check(int a, int b, int c); // 检测重复函数
void single_space(int mode); // 单格移动函数
void double_space(); // 双哥移动函数
void over(); // 结束判断函数
void game(); // 游戏函数
void solve(); // 求解函数
int exit_carry; // 主循函数控制参数
int exit_move; // 开始界面控制参数
int num_double[31]; // 辅助数据存入
int num_step; // 树层数
int num_eat; // 求解链长度参数
int num_start; // 求解起点参数
int num_over; // 求解终点参数
int num_mod; // 游戏模式
int box_transverse[6]; // 横向
int box_portrait[6]; // 纵向
int box_big; // 大格
int box_space[2]; // 空格
int num_transverse; // 横向数量
int num_portrait; // 纵向数量
int pan[30]; // 棋盘信息
double num_flag; // 树结点标记系数
double num_flag1; // 树结点标记系数
IMAGE* img_transverse; // 横块图片
IMAGE* img_portrait; // 纵块图片
IMAGE* img_small; // 小块图片
IMAGE* img_big; // 大块图片
IMAGE* img_space; // 空格图片
ExMessage m; // 鼠标定义
Node2 eat[200]; // 临时存放结点
Node3 box_button[20]; // 按钮,预制
};
// 场景绘制函数
void Gary::draw_scene(int num_box)
{
int i, j;
// 横向
for(i = 0; i < num_transverse; i++)
{
box_transverse[i] = ( eat[num_box].num_transverse & ( num_double[i * 5 + 5] - num_double[i * 5] ) ) / num_double[i * 5];
}
// 纵向
for(i = 0; i < num_portrait; i++)
{
box_portrait[i] = ( eat[num_box].num_portrait & ( num_double[i * 5 + 5] - num_double[i * 5] ) ) / num_double[i * 5];
}
// 大格
box_big = ( eat[num_box].num_other & ( num_double[21] - num_double[16] ) ) / num_double[16];
// 空格
box_space[0] = ( eat[num_box].num_other & ( num_double[13] - num_double[8] ) ) / num_double[8];
box_space[1] = ( eat[num_box].num_other & ( num_double[5] - num_double[0] ) );
// 初始化
for(i = 0; i < 20; i++)
{
pan[i] = -1;
}
// 横向
for(i = 0; i < num_transverse; i++)
{
pan[box_transverse[i]] = 2;
pan[box_transverse[i] + 1] = 5;
}
// 纵向
for(i = 0; i < num_portrait; i++)
{
pan[box_portrait[i]] = 3;
pan[box_portrait[i] + 4] = 5;
}
// 大格
pan[box_big] = 1;
pan[box_big + 1] = 5;
pan[box_big + 4] = 5;
pan[box_big + 5] = 5;
// 空格
pan[box_space[0]] = 0;
pan[box_space[1]] = 0;
// 小块
for(j = 0; j < 20; j++)
{
if(pan[j] == -1)
{
pan[j] = 4;
}
}
// 背景绘制
setbkcolor(RGB(136, 192, 160));
cleardevice();
// 棋盘绘制
setlinestyle(PS_SOLID, 3);
setfillcolor(RGB(73, 130, 120));
setlinecolor(RGB(168, 226, 195));
fillroundrect(20, 15, 25 + 102 * 4, 30 + 102 * 5, 30, 30);
for(i = 0; i < 20; i++)
{
// 根据格子类型绘制
switch(pan[i])
{
case 0:putimage(25 + i % 4 * 102, 25 + i / 4 * 102, img_space); break;
case 1:putimage(25 + i % 4 * 102, 25 + i / 4 * 102, img_big); break;
case 2:putimage(25 + i % 4 * 102, 25 + i / 4 * 102, img_transverse); break;
case 3:putimage(25 + i % 4 * 102, 25 + i / 4 * 102, img_portrait); break;
case 4:putimage(25 + i % 4 * 102, 25 + i / 4 * 102, img_small); break;
default:break;
}
// 出口绘制
if(pan[i] == 0 && ( i == 13 || i == 14 || i == 17 || i == 18 ))
{
setlinecolor(RGB(230, 230, 230));
setlinestyle(PS_SOLID, 10);
setfillcolor(RGB(112, 180, 167));
fillcircle(25 + 51 + i % 4 * 102, 25 + 51 + i / 4 * 102, 20);
setlinecolor(RGB(168, 226, 195));
setfillcolor(RGB(73, 130, 120));
setlinestyle(PS_SOLID, 3);
}
}
// 按钮绘制
TCHAR s[25];
setlinecolor(RGB(155, 195, 230));
setfillcolor(RGB(80, 189, 222));
settextcolor(RGB(230, 230, 230));
settextstyle(30, 20, _T("Times New Roman"));
setbkcolor(RGB(80, 189, 222));
// 标准棋盘按钮
for(i = 0; i < 12; i++)
{
fillroundrect(box_button[i].posx1, box_button[i].posy1, box_button[i].posx2, box_button[i].posy2, 20, 20);
_stprintf_s(s, _T("%d"), i);
outtextxy(box_button[i].posx1 + 13 - 11 * ( i / 10 ), box_button[i].posy1 + 10, s);
}
// 其他按钮
settextstyle(100, 100, _T("Webdings"));
settextcolor(RGB(73, 130, 120));
setbkmode(TRANSPARENT);
// 播放,求解
outtextxy(102 - 50, 580 - 30, 0x34);
// 重置,恢复初始盘面
outtextxy(214 - 50, 580 - 30, 0x33);
// 刷新,随机盘面按钮
outtextxy(326 - 50, 580 - 30, 0x71);
// 恢复填充
setbkmode(OPAQUE);
settextcolor(RGB(230, 230, 230));
settextstyle(30, 20, _T("宋体"));
setbkcolor(RGB(136, 192, 160));
outtextxy(102 - 50, 655, _T("求解"));
outtextxy(214 - 50, 655, _T("恢复"));
outtextxy(326 - 50, 655, _T("刷新"));
FlushBatchDraw();
}
// 解码
void Gary::decode()
{
int i;
// 根据特征值画棋面
// 横向
for(i = 0; i < num_transverse; i++)
{
box_transverse[i] = ( box[int(num_flag)].num_transverse & ( num_double[i * 5 + 5] - num_double[i * 5] ) ) / num_double[i * 5];
}
// 纵向
for(i = 0; i < num_portrait; i++)
{
box_portrait[i] = ( box[int(num_flag)].num_portrait & ( num_double[i * 5 + 5] - num_double[i * 5] ) ) / num_double[i * 5];
}
// 大格
box_big = ( box[int(num_flag)].num_other & ( num_double[21] - num_double[16] ) ) / num_double[16];
// 空格
box_space[0] = ( box[int(num_flag)].num_other & ( num_double[13] - num_double[8] ) ) / num_double[8];
box_space[1] = ( box[int(num_flag)].num_other & ( num_double[5] - num_double[0] ) );
}
// 检查重复函数
void Gary::check(int a, int b, int c)
{
int k, t;
// 全局检测
k = 0;
for(t = int(num_flag1 - 1); t >= 0; t--)
{
if(box[t].num_transverse == a && box[t].num_portrait == b && box[t].num_other == c) { k = 1; break; }
}
// 不重复则生成子结点
if(k == 0)
{
// 特征值录入,父结点录入
box[int(num_flag1)].num_transverse = a;
box[int(num_flag1)].num_portrait = b;
box[int(num_flag1)].num_other = c;
box[int(num_flag1)].num_father = int(num_flag);
// 编号
box[int(num_flag1)].num = box[box[int(num_flag1)].num_father].num + 1;
// 层数
if(box[int(num_flag1)].nu