这学期人工智能实验课要做汉诺塔,老师说最好实现可视化,于是想起了我大二那会儿刚学c++时编的一个小玩意,正好用在这,效果确实比较简陋,不过功能基本都实现了。在这里分享一波,希望能帮到大家。
求解思路不多说,就是汉诺塔最常规的递归三步走策略,重点说下可视化。
我采用easyx图形库进行绘图,调用了其中的graphics.h头文件。为了尽可能简便,只设置了一个柱子类,类函数包括void InitialDraw(int n)初始化绘图、void setpillar(int num, int n)设置柱子参数、void fill(int num, int n1, int n2)清除盘子和void drawplate(int num, int n1, int n2)画柱子。
也是简化起见,柱子用线段表示,盘子用矩形表示,绘制的时候分别使用line()函数和fillrectangle()函数。汉诺塔可视化的核心在于怎样动态地表示从一个状态到下一个状态盘子分布情况的改变,经过在草稿纸上的分析,我采取了这种思路:每次只改变一个柱子最上方的盘子,状态改变的过程包括擦去一个柱子顶部的盘子、然后在另一个柱子顶部绘制移动后的盘子,用Sleep()函数拖慢移动的过程,就实现了动态显示。debug过程中发现了以下几个问题,个人觉得也算是这次可视化中的难点:注意设置好不同盘子的规格,每次精准地擦除和填充,不要涉及到多余的区域、每次擦除部分盘子后,柱子都要重新绘一次,否则看上去会短一截。以及:以对象为参数给总函数传参时,我们当然希望每一次改变都确确实实发生了,所以一定不要忘记加引用!!!
上代码:
#include
#include
#include
#include //为了调用Sleep()函数
#include // 引用图形库头文件
#include
using namespace std;
class pillar
{
public:
pillar(int num,int n); //构造函数
void InitialDraw(int n); //初始化
void setpillar(int num, int n); //设置柱子
void fill(int num, int n1, int n2); //清除某个柱子最上方的盘子
void drawplate(int num, int n1, int n2); //画出某个柱子最上方的盘子(n1代表移动的盘子编号,
//n2代表柱子上的盘子数量)
public:
int m_x1, m_y1;
int m_x2 = m_x1;
int m_y2 = 330;//柱子坐标
int m_num;//柱的位置
int m_n;//柱上的盘子数量
int left=0, right=0;
int top=0, bottom=0;
};
pillar::pillar(int num,int n) //num代表柱子编号,n代表盘子总数
{
m_num = num;
if (num == 1)
m_n = n;
else
m_n = 0;
m_x1 = 220 * num - 110;
m_y1 = 310 - 30 * n;
m_x2 = m_x1;
m_y2 = 330;
line(m_x1, m_y1, m_x2, m_y2);
}
void pillar::InitialDraw(int n) //初始化盘子
{
for (int i = n; i > 0; i --)
{
left = 110 - 10 * i;
right = left + 20 * i;
top = 310 - 20 * (n - i);
bottom = 330 - 20 * (n - i);
fillrectangle(left, top, right, bottom);
}
}
void pillar::setpillar(int num, int n) //num代表柱子编号,n代表盘子总数
{
m_x1 = 220 * num - 110;
m_y1 = 310 - 30 *n;
m_x2 = m_x1;
m_y2 = 330;
line(m_x1, m_y1, m_x2, m_y2);
}
void pillar::fill(int num, int n1, int n2) //n1代表移动的盘子编号,n2代表柱子上的盘子数量
{
m_n = n2;
left = 220 * num - 10 * n1 - 110;
right = 220 * num + 10 * n1 - 110;
top = 310 - 20 * (m_n - 1);
bottom = top + 20;
clearrectangle(left, top, right, bottom);
m_n--;
}
void pillar::drawplate(int num, int n1, int n2) //n1代表移动的盘子编号,n2代表目标柱子上的盘子数量
{
m_n = n2;
m_n++;
left = 220 * num - 10 * n1 - 110;
right = 220 * num + 10 * n1 - 110;
top = 310 - 20 * (m_n - 1);
bottom = top + 20;
fillrectangle(left, top, right, bottom);
}
void move(pillar &a, pillar &b, int n)//起始柱,终点柱,移动的盘子编号
{
Sleep(5);
a.fill(a.m_num, n, a.m_n);
a.setpillar(a.m_num, a.m_n);
Sleep(300);
b.drawplate(b.m_num, n, b.m_n);
}
void hanoi(int n, pillar &x, pillar &y, pillar &z)
{
if (n == 1)
{
move(x, z, n);
}
else
{
hanoi(n - 1, x, z, y);
move(x, z, n);
hanoi(n - 1, y, x, z);
}
}
int main()
{
int n;
cout << " 汉诺塔演示程序" << endl;
cout << "------------------------------------------------------------------------------------------------------------------------" << endl;
cout << "请输入盘子总数:(上限为9)" << endl;
cin >> n; //输入盘子总数
initgraph(680, 480); // 创建绘图窗口,大小为 640x480 像素
setbkcolor(RGB(28, 115, 119));
cleardevice();
pillar p1(1,n), p2(2,n), p3(3,n);
p1.InitialDraw(n);
Sleep(300);
hanoi(n,p1,p2,p3);
system("pause");
}
截图:
运行代码需要先安装easyx图形库,网上一搜很多教程,记得放对位置就行了。