汉诺塔游戏的设计(附代码)

汉诺塔游戏的设计

汉诺塔问题是最经典的递归问题,笔者就该问题设计了这个游戏,由用户交互游戏和自动演示两部分组成,支持撤销功能、选关、自动完成等功能。

  首先建立了类CMap,该类主要实现用户每一步的操作和画图显示功能,记录的时候只须记录每组盘子的个数和盘子的矩形。代码和注释如下:

//记录每一步的盘子的情况
class CMap
{
public:
  //每组盘子的个数
  int iCount[3];
  //3组盘子里面,每个盘子的位置,用矩形表示
  RECT *Rect[3];
  //构造函数
  CMap()
  {
    //三组盘子,每组盘子的矩形
    for(int i=0;i<3;i++)
      Rect[i]=new RECT[NUM];
    //初始化每组盘子的个数
    iCount[0]=NUM;
    iCount[1]=0;
    iCount[2]=0;
    //第一组盘子的矩形的位置
    for(i=0;i<NUM;i++)
    {
      Rect[0][i].left=Center[0]-(NUM-i)*Dx2;
      Rect[0][i].right=Center[0]+(NUM-i)*Dx2;
      Rect[0][i].bottom=(NUM+1-i)*Dx;
      Rect[0][i].top=(NUM-i)*Dx;
    }
    //第二组盘子的矩形初始化为空
    for(i=0;i<NUM;i++)
    {
      Rect[1][i].left=0;
      Rect[1][i].right=0;
      Rect[1][i].bottom=0;
      Rect[1][i].top=0;
    }
    //第三组盘子的矩形初始化为空
    for(i=0;i<NUM;i++)
    {
      Rect[2][i].left=0;
      Rect[2][i].right=0;
      Rect[2][i].bottom=0;
      Rect[2][i].top=0;
    }
  }
  //运算符重载
  CMap operator=(CMap Other)
  {
    //对新的CMap对象,应该重新分配内存
    for(int i=0;i<3;i++)
      Rect[i]=new RECT[NUM];
    //依次赋值
    for(i=0;i<3;i++)
    {
      iCount[i]=Other.iCount[i];
        for(int j=0;j<NUM;j++)
        Rect[i][j]=Other.Rect[i][j];
    }
    //返回
    return *(this);
  }
  //画图,显示盘子的情况
  void OnDraw(HDC hdc)
  {
    //画出每个盘子
    for(int i=0;i<3;i++)
      for(int j=0;j<iCount[i];j++)
        Rectangle(hdc,
          Rect[i][j].left,
          Rect[i][j].top,
          Rect[i][j].right,
          Rect[i][j].bottom);
  }
  //析构函数
  ~CMap()
  {
    //内存的释放
    for(int i=0;i<3;i++)
    {
      if(Rect[i]!=NULL)
      {
        Rect[i]=NULL;
        delete Rect[i];
      }
    }
  }
};
下面是汉诺塔的主类Hanio,该类的成员函数有OnDraw()Undo()Move()AutoMove()等,分别实现汉诺塔的画图显示、撤销、移动盘子、自动移动盘子等功能,代码及注释如下:class Hanio
{
public:
  //当前的步数
  int iStep;
  //记录每一步的盘子的情况
  CMap Record[MAXSTEP];
public:
  //构造函数
  Hanio()
  {
    //初始化,步数为0
    iStep=0;
    //初始化记录
    for(int i=0;i<MAXSTEP;i++)
    {
      Record[i]=CMap();
    }
  }
  //画图,显示汉诺塔的情况
  void OnDraw(HDC hdc)
  {
    Record[iStep].OnDraw(hdc);
  }
  //撤销
  void Undo()
  {
    if(iStep>0)
      iStep–;
    //重绘
    Draw();
  }
  //移动盘子
  void Move(int iStart,int iEnd)
  {
    //得到当前盘子的记录
    CMap Map=Record[iStep];
    //移动的情况判断,去除非法的移动
    if(iStart<0||iStart>=3)
      return;
    if(iEnd<0||iEnd>=3)
      return;
    if(iStart==iEnd)
      return;
    if(Map.iCount[iStart]<1)
      return;
    //得到移动前的开始组,结束组的盘子的个数
    int iStartRectNum=Map.iCount[iStart];
    int iEndRectNum=Map.iCount[iEnd];
    //从小盘子移动到大盘子上面的情况是不可以的。
    if(iEndRectNum>0)
      if(Width(Map.Rect[iStart][iStartRectNum-1])>=Width(Map.Rect[iEnd][iEndRectNum-1]))
        return;
    //步数累加
    iStep++;
    //记录新的盘子的情况
    Record[iStep]=Record[iStep-1];
    //移走的那一组盘子的个数减少
    Record[iStep].iCount[iStart]–;
    //被移到的那一组的盘子个数增加
    Record[iStep].iCount[iEnd]++;
    //重新计算移动后的盘子的矩形
    //主要是被移到的那一组的最上面那个盘子的矩形的计算
    RECT rect;
    rect.left=Center[iEnd]-Width(Map.Rect[iStart][iStartRectNum-1])/2;
    rect.right=Center[iEnd]+Width(Map.Rect[iStart][iStartRectNum-1])/2;
    rect.bottom=(NUM+1-Map.iCount[iEnd])*Dx;
    rect.top=(NUM-Map.iCount[iEnd])*Dx;
    Record[iStep].Rect[iEnd][iEndRectNum]=rect;
    //刷新
    SendMessage(hWnd,WM_PAINT,0,0);
  }
  //自动移盘子
  void AutoMove(int iA,int iB,int iC,int iNum)
  {
    //递归实现自动移盘子
    //递归的出口,如果个数为3,按如下进行移动。
    if(iNum==3)
    {
      Move(iA,iC);
      ::Sleep(500);
      Move(iA,iB);
      ::Sleep(500);
      Move(iC,iB);
      ::Sleep(500);
      Move(iA,iC);
      ::Sleep(500);
      Move(iB,iA);
      ::Sleep(500);
      Move(iB,iC);
      ::Sleep(500);
      Move(iA,iC);
      ::Sleep(500);
    }
    //个数大于3,递归实现移动。
    else
    {
      //递归自动移动。
      AutoMove(iA,iC,iB,iNum-1);
      Move(iA,iC);
      ::Sleep(500);
      AutoMove(iB,iA,iC,iNum-1);
    }
  }
};
程序实现的结果如下图:

《汉诺塔游戏的设计(附代码)》

  由于堆栈内存的限制,选关不可能是无限个盘子,本程序设计的最大关数是8。自动移动是用递归实现的,自动移动的过程中,其他消息无法响应,可以改成多线程或由用户控制的形式。上述的程序附有Visual C++源代码,并在Windows XPVisual C++6.0下调试成功。

 

    原文作者: 汉诺塔问题
    原文地址: https://blog.csdn.net/ecitnet/article/details/2073434
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞