您的位置:逆风者 VC++ 正文
原作者:苍竹先生 添加时间:2007-09-08 原文发表:2007-09-08 人气:88 来源:vckbase.com

本文章共2278字,分2页,当前第1页,快速翻页:
 
下载源代码

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

逆风者
首先建立了类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);
		}
	}
}; 
 
本文章更多内容1 - 2 - 下一页>>
相关文章

关于控件注册和使用许可问题的解决办法
基于MFC对话框的NT服务程序框架
计算MDI子窗口数,仅显示文件夹的打开对话框
PE文件格式详解(下)
GDI和GDI 对象的相互转换
软件框架的利器、TangramMini组件应用教程二
C语言高效编程的几招
VC 操作 SQL Server 主从表
软件测试悖论
如何在 C# 中加载自己编写的动态链接库
Spy++原理初探
WIN32程序挂钩SetLastError,输出错误描述到
超强仿QQ自动伸缩窗口
VC实用小知识总结 (一)
VC实用小知识总结 (二)
BMP图象解析
让你的软件界面更漂亮(五)
MFC中基于对话框程序快捷键的实现
用递归的方法画分形图
利用IJG JPEG Library压缩图像为jpg格式

相关评论


本文章所属分类:首页 VC++

  热门关键字: