赞
踩
目录
汉诺塔(Tower of Hanoi),是一个源于印度古老传说的益智玩具。这个传说讲述了大梵天创造世界的时候,他做了三根金刚石柱子,并在其中一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门将这些圆盘从下面开始按大小顺序重新摆放在另一根柱子上,并规定在小圆盘上不能放大圆盘,同时在三根柱子之间一次只能移动一个圆盘。当盘子的数量增加时,移动步骤的数量会呈指数级增长,圆盘数为n时,总步骤数steps为2^n - 1。
n = 64, steps = 2^64 - 1 = 18446744073709551616 ≈ 1.845 x 10^19
汉诺塔问题是一个递归问题,也可以使用非递归法来解决,例如使用栈来模拟递归过程。这个问题不仅是一个数学和逻辑问题,也是一个很好的教学工具,可以用来教授递归、算法和逻辑思考等概念。同时,汉诺塔游戏也具有一定的娱乐性,人们可以通过解决不同规模的汉诺塔问题来挑战自己的智力和耐心。
本篇接着上期讲下去,前2篇的链接地址:
Python 一步一步教你用pyglet制作汉诺塔游戏(续)-CSDN博客
Python 一步一步教你用pyglet制作汉诺塔游戏-CSDN博客
前2期代码的基础上,添加了完整的提示功能,一个汉诺塔游戏作品终于完工了,效果如下:
信息提示功能都放在了鼠标事件中:
@window.event
def on_mouse_press(x, y, dx, dy):
global xy, hanns, gamecompleted
if not hanns.success():
pole = hanns.on_mouse_over(x, y)
if pole is not None:
xy.append(pole)
if len(xy)==1:
hanns.setdiskcolor(xy[0], (255,0,0))
if not hanns.array[pole]:
hanns.setdiskcolor(xy[0])
xy.pop()
return
if len(xy)==2:
if xy[0]!=xy[1]:
info = hanns.move(*xy)
hanns.setdiskcolor(xy[0])
if info is False:
info1.text = ‘起始圆盘大于目标位置的圆盘’
elif info is None:
info1.text = ‘所选起始位置的塔架不能为空’
else:
info1.text = f’{hanns.order-hanns.array[xy[1]][-1]}号圆盘从{xy[0]+1}号塔架移动到{xy[1]+1}号塔架’
hanns.setdiskcolor(xy[0])
xy.clear()
info2.text = f’当前层数:{hanns.order}\t最佳步数:{2**hanns.order-1}\t当前步数:{hanns.steps}’
if hanns.success():
if hanns.order<24:
info1.text = f’恭喜您完成 {hanns.order} 层汉诺塔!任意点击层数加一!’
else:
info1.text = f’太棒了!您已完成 {hanns.order} 层汉诺塔,游戏结束!’
gamecompleted = True
return
elif not gamecompleted:
hanns = Hann(window.width/2, 120, hanns.order+1)
info1.text = f’ {hanns.order} 层汉诺塔,游戏开始!’
info2.text = f’当前层数:{hanns.order}\t最佳步数:{2**hanns.order-1}\t当前步数:{hanns.steps}’
Hann 类中增加一个改色的方法,用于标注被点击的要移动的源塔架:
def setdiskcolor(self, n, color=Color[0]):
self.disk[n].cir1.color = color
self.disk[n].cir2.color = color
self.disk[n].rect.color = color
完整代码:
import pyglet window = pyglet.window.Window(800, 500, caption='汉诺塔') pyglet.gl.glClearColor(1, 1, 1, 1) batch = pyglet.graphics.Batch() Color = (182,128,18),(25,65,160),(56,170,210),(16,188,78),(20,240,20),(240,240,20),(255,128,20),(240,20,20),(245,60,138) class Disk: def __init__(self, x, y, color=(0,0,0), width=200, height=20): self.cir1 = pyglet.shapes.Circle(x+width/2-height/2, y, radius=height/2, color=color, batch=batch) self.cir2 = pyglet.shapes.Circle(x-width/2+height/2, y, radius=height/2, color=color, batch=batch) self.rect = pyglet.shapes.Rectangle(x-width/2+height/2, y-height/2, width-height, height, color=color, batch=batch) def move(self, dx, dy): self.cir1.x += dx; self.cir1.y += dy self.cir2.x += dx; self.cir2.y += dy self.rect.x += dx; self.rect.y += dy class Hann: def __init__(self, x, y, order=2, space=250, thickness=20, width=200, height=300): assert(order>1) self.pole = [pyglet.shapes.Line(x-i*space, y, x-i*space, y+height, width=thickness, color=Color[0], batch=batch) for i in range(-1,2)] self.disk = [Disk(x+i*space, y, color=Color[0], width=width+thickness, height=thickness) for i in range(-1,2)] self.x, self.y = x, y self.order = order self.space = space self.thickness = thickness self.width = width self.poleheight = height self.beadheight = (height-thickness*2)/order self.step = (width-thickness)/(order+1) self.steps = 0 self.macro = [] coordinates = [(self.x-space, self.y+(i+1)*self.beadheight-(self.beadheight-thickness)/2) for i in range(order)] self.beads = [Disk(*xy, Color[i%8+1], width=self.width-i*self.step, height=self.beadheight) for i,xy in enumerate(coordinates)] self.array = [[*range(order)], [], []] def move(self, pole1, pole2): if self.array[pole1]: bead = self.array[pole1].pop() if self.array[pole2] and bead<self.array[pole2][-1]: self.array[pole1].append(bead) return False else: return None self.beads[bead].move((pole2-pole1)*self.space, (len(self.array[pole2])-len(self.array[pole1]))*self.beadheight) self.array[pole2].append(bead) self.steps += 1 self.macro.append((pole1, pole2)) return True def setdiskcolor(self, n, color=Color[0]): self.disk[n].cir1.color = color self.disk[n].cir2.color = color self.disk[n].rect.color = color def on_mouse_over(self, x, y): for i in range(-1,2): if hanns.x-hanns.width/2 < x-i*hanns.space < hanns.x+hanns.width/2 and hanns.y-hanns.thickness/2 < y < hanns.y+hanns.poleheight: return i+1 def success(self): return len(self.array[2]) == self.order @window.event def on_draw(): window.clear() batch.draw() @window.event def on_mouse_press(x, y, dx, dy): global xy, hanns, gamecompleted if not hanns.success(): pole = hanns.on_mouse_over(x, y) if pole is not None: xy.append(pole) if len(xy)==1: hanns.setdiskcolor(xy[0], (255,0,0)) if not hanns.array[pole]: hanns.setdiskcolor(xy[0]) xy.pop() return if len(xy)==2: if xy[0]!=xy[1]: info = hanns.move(*xy) hanns.setdiskcolor(xy[0]) if info is False: info1.text = '起始圆盘大于目标位置的圆盘' elif info is None: info1.text = '所选起始位置的塔架不能为空' else: info1.text = f'{hanns.order-hanns.array[xy[1]][-1]}号圆盘从{xy[0]+1}号塔架移动到{xy[1]+1}号塔架' hanns.setdiskcolor(xy[0]) xy.clear() info2.text = f'当前层数:{hanns.order}\t最佳步数:{2**hanns.order-1}\t当前步数:{hanns.steps}' if hanns.success(): if hanns.order<24: info1.text = f'恭喜您完成 {hanns.order} 层汉诺塔!任意点击层数加一!' else: info1.text = f'太棒了!您已完成 {hanns.order} 层汉诺塔,游戏结束!' gamecompleted = True return elif not gamecompleted: hanns = Hann(window.width/2, 120, hanns.order+1) info1.text = f' {hanns.order} 层汉诺塔,游戏开始!' info2.text = f'当前层数:{hanns.order}\t最佳步数:{2**hanns.order-1}\t当前步数:{hanns.steps}' xy = [] order = 2 hanns = Hann(window.width/2, 120, order) info1 = pyglet.text.Label('操作方法:鼠标先后点击起始和目标位置就能移动圆盘', font_size=21, color=(0,0,0,255), x=window.width/2, y=50, anchor_x='center', batch=batch) info2 = pyglet.text.Label(f'当前层数:{order}\t最佳步数:{2**order-1}\t当前步数:0', font_size=18, color=(0,0,0,255), x=80, y=450, batch=batch) gamecompleted = False pyglet.app.run()
优化代码,布局稍作调整,并加入帮助、演示和回放等功能。演示和回放的区别:演示功能是提供如何移动的教学帮助;回放功能则是展示用户自己的移动过程。
运行效果如下:
import pyglet from pyglet.window import key Width, Height = 800, 500 window = pyglet.window.Window(Width, Height, caption='Tower of Hanoi') screen = pyglet.canvas.Display().get_default_screen() window.set_location((screen.width-Width)//2, (screen.height-Height)//2) pyglet.gl.glClearColor(0.78, 0.88, 0.98, 1) batch = pyglet.graphics.Batch() group = pyglet.graphics.Group() Color = (182,128,18),(25,65,160),(56,170,210),(16,188,78),(20,240,20),(240,240,20),(255,128,20),(240,20,20),(245,60,138) class Disk: def __init__(self, x, y, color=(0,0,0), width=200, height=20): self.cir1 = pyglet.shapes.Circle(x+width/2-height/2, y, radius=height/2, color=color, batch=batch) self.cir2 = pyglet.shapes.Circle(x-width/2+height/2, y, radius=height/2, color=color, batch=batch) self.rect = pyglet.shapes.Rectangle(x-width/2+height/2, y-height/2, width-height, height, color=color, batch=batch) def move(self, dx, dy): self.cir1.x += dx; self.cir1.y += dy self.cir2.x += dx; self.cir2.y += dy self.rect.x += dx; self.rect.y += dy class Hann: def __init__(self, order=2, x=window.width/2, y=110, space=250, thickness=20, width=200, height=300): assert(order>1) self.pole = [pyglet.shapes.Line(x+i*space, y, x+i*space, y+height, width=thickness, color=Color[0], batch=batch) for i in range(-1,2)] self.disk = [Disk(x+i*space, y, color=Color[0], width=width+thickness, height=thickness) for i in range(-1,2)] self.x, self.y = x, y self.order = order self.ordermax = 24 self.space = space self.thickness = thickness self.width = width self.poleheight = height self.beadheight = (height-thickness*2)/order self.step = (width-thickness)/(order+1) self.steps = 0 self.macro = [] self.posxy = [] self.click = False self.ctrlz = False self.playback = False coordinates = [(self.x-space, self.y+(i+1)*self.beadheight-(self.beadheight-thickness)/2) for i in range(order)] self.beads = [Disk(*pos, Color[i%8+1], width=self.width-i*self.step, height=self.beadheight) for i,pos in enumerate(coordinates)] self.array = [[*range(order)], [], []] self.arraymacro = [] def move(self, pole1, pole2): if self.array[pole1]: bead = self.array[pole1].pop() if self.array[pole2] and bead<self.array[pole2][-1]: self.array[pole1].append(bead) return False else: return None self.beads[bead].move((pole2-pole1)*self.space, (len(self.array[pole2])-len(self.array[pole1]))*self.beadheight) self.array[pole2].append(bead) self.steps += 1 self.click = True self.macro.append((pole1, pole2)) self.arraymacro.append([array[:] for array in self.array]) return True def set_color(self, n, color=Color[0]): self.click = color==Color[0] self.disk[n].cir1.color = color self.disk[n].cir2.color = color self.disk[n].rect.color = color self.pole[n].color = color def on_mouse_over(self, x, y): for i in range(-1,2):  **感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的:** ① 2000多本Python电子书(主流和经典的书籍应该都有了) ② Python标准库资料(最全中文版) ③ 项目源码(四五十个有趣且经典的练手项目及源码) ④ Python基础入门、爬虫、web开发、大数据分析方面的视频(适合小白学习) ⑤ Python学习路线图(告别不入流的学习) **网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。** **[需要这份系统化学习资料的朋友,可以戳这里无偿获取](https://bbs.csdn.net/topics/618317507)** **一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。