汉诺塔游戏C++版(可自动玩)

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

汉诺塔游戏
汉诺塔游戏是一个看起来简单的游戏,实际玩起来却不那么简单,类似九连环一样。关于汉诺塔,法国数学家爱德华·卢卡斯曾编写过一个印度的古老传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针。印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔。不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面。僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔、庙宇和众生也都将同归于尽。
不管这个传说的可信度有多大,如果考虑一下把64片金片,由一根针上移到另一根针上,并且始终保持上小下大的顺序。这需要多少次移动呢?这里需要递归的方法。假设有n片,移动次数是f(n).显然f(1)=1,f(2)=3,f(3)=7,且f(k+1)=2*f(k)+1。此后不难证明f(n)=2^n-1。n=64时,
假如每秒钟一次,共需多长时间呢?一个平年365天有31536000 秒,闰年366天有31622400秒,平均每年31556952秒,而2^64-1=18446744073709551615秒,换算成年等于
18446744073709551615 / 31556952 = 584554049252。
这表明移完这些金片需要5845.54亿年以上,而地球存在至今不过45亿年,太阳系的预期寿命据说也就是数百亿年。真的过了5845.54亿年,不说太阳系和银河系,至少地球上的一切生命,连同梵塔、庙宇等,都早已经灰飞烟灭。
我们玩几个盘子还是没问题的,开始吧!程序不大,但是用这个程序,既可以手动一步一步玩,也可以让计算机自动快速玩,每一步都会打印出当前状态。
//陈宗权
#include
#include
using namespace std;

class HanoTowers{
vector places[3];//abc三个位置
size_t height;//目前各个位置的最大高度
unsigned int steps;//步数
public:
void Start()
{
//输入盘子数量
int n = -1, mode = -1;
cout << "请输入盘子数量(1~9)和游戏方式(0自动1手动):";
cin >> n >> mode;
//检查有效性
if( n > 9 || n < 1 || mode < 0 || mode > 1)
{
cout << "输入无效" << endl;
return;
}
places[0].clear();
places[1].clear();
places[2].clear();
height = n;
steps = 0;
while(n > 0)
places[0].push_back(n--);
Show();
//进入游戏
if(mode == 1)
Play();
else
AutoPlay();
}
//显示各个位置盘子的情况,towers表示3个位置上的盘子数及其各个盘子编号
void Show()
{
//高度递减,从高到低
for(size_t i = height; i > 0; i--)
{
//依次查看3个位置
for(int j = 0; j < 3; j++)
{
//画每个位置的左分界线
c

out << "|";
//如果这个位置的高度不够就显示空白
if(places[j].size() < i)
cout << " ";
//高度足够就显示这个位置当前这个高度的盘子编号
else
cout << "-" << places[j][i-1] << "-";
}
//画位置的右分界线
cout << "|" << endl;
}
//画底部位置名abc
cout << "^=a=^=b=^=c=^" << endl;
}
//移动盘子,towers表示3个位置上的盘子数及其各个盘子编号,index_from表示源位置下标,index_to表示目标位置下标
bool CanMove(char from, char to)
{
//计算源位置和目标位置的下标
int index_from = from - 'a', index_to = to - 'a';
//如果源位置上有盘子,而且目标位置为空位置或者源位置顶上的盘子编号比目标位置顶上的盘子编号小就可以这样移动
return (!places[index_from].empty() && (places[index_to].empty() || places[index_from].back() < places[index_to].back()));
}
//移动盘子,from表示源位置,to表示目标位置
void Move(char from, char to)
{
//计算源位置和目标位置的下标
int index_from = from - 'a', index_to = to - 'a';
int tower = places[index_from].back();
places[index_from].pop_back();
places[index_to].push_back(tower);//设置目标位置顶上的盘子编号
cout << "第" << ++steps << "步:盘子" << tower << "从" << from << "移动到" << to << endl;
//显示一下移动后各个位置上盘子的情况
Show();
}
//游戏函数
void Play()
{
//为玩家显示游戏任务和游戏规则
cout << "**请想办法把a位置的" << height << "个盘子移动到c位置,\n**每一步移动只能把一个位置最上面的盘子移到另一个空位置或者另一个顶上盘子编号比这个大的位置。" << endl;
//只要a、b位置还有盘子就表示还没有完成,反复:
while(!places[0].empty() || !places[1].empty())
{
//提示玩家操作
cout << "请输入移动盘子方向(用abc中的两个字母表示):";
//接收玩家的操作
char from, to;
cin >> from >> to;
//检查操作的有效性
if(from < 'a' || from > 'c' || to < 'a' || to > 'c' || !CanMove(from, to))
{
cout << "无效的移动方向" << endl;
continue;
}
//按玩家的操作移动盘子
Move(from, to);
}
//游戏结束
cout << "~~~恭喜你完成任务!!!~~~" << endl;
}
//从一个位置移动若干个盘子到另一个位置
//from表示源位置,n表示要移动的盘子个数,to表示目标位置,tmp表示另一个位置,用来当成临时位置用
void MoveTowers(char from, int n, char to, char tmp)
{
//0个盘子自然什么都不用做
if(n <= 0)
return;
//如果只需要移动1个盘子就直接移动到目标位置
if(n == 1)
{
Move(from, to);
}
//如果要移动的是多个盘子,无法一步直接移动过去,拆分一下
else
{
//先把上面n-1个盘子移动到临时

位置
MoveTowers(from, n-1, tmp, to);
//现在把剩下的1个盘子移动到目标位置
Move(from, to);
//再把临时位置上那n-1个盘子移动到目标位置
MoveTowers(tmp, n-1, to, from);
}
}
//自动玩游戏
void AutoPlay()
{
//自动把全部盘子从a位置移动到c位置
MoveTowers('a', height, 'c', 'b');
}
};

int main()
{
HanoTowers game;
game.Start();
return 0;
}

相关文档
最新文档