def on_draw():
window.clear()
batch.draw()
hann = Hann(window.width/2, 100, 7)
hann.move(0,1)
hann.move(0,2)
hann.move(1,2)
hann.move(0,1)
hann.move(2,0)
hann.move(2,1)
hann.move(0,1)
hann.move(0,2)
pyglet.app.run()
### 9. 移动演示
pyglet.clock.schedule\_interval(function, seconds) 是 Pyglet 库中的一个函数调用,用于定期调度另一个函数function在指定的时间间隔seconds内执行。
pyglet.clock.unschedule(function) 任务完成后使用此函数来取消调度,以避免不必要的资源消耗。
以下展示一个四层汉诺塔的移动演示:
![](https://img-blog.csdnimg.cn/direct/54261adc4ae44259885ee07db6b47083.gif)
代码:
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-ispace, y, x-ispace, y+height, width=thickness, color=Color[0], batch=batch) for i in range(-1,2)]
self.disk = [Disk(x+ispace, 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.height = (height-thickness2)/order
self.step = (width-thickness)/(order+1)
coordinates = [(self.x-space, self.y+(i+1)*self.height-(self.height-thickness)/2) for i in range(order)]
self.beads = [Disk(xy, Color[i%8+1], width=self.width-iself.step, height=self.height) 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]:
print(‘大盘不能搬到小盘上’)
return False
else:
print(‘所选择的塔架为空!’)
return None
self.beads[bead].move((pole2-pole1)*self.space, (len(self.array[pole2])-len(self.array[pole1]))*self.height)
self.array[pole2].append(bead)
return True
@window.event
def on_draw():
window.clear()
batch.draw()
def on_move(event):
global moves
if moves:
x, y = moves.pop(0)
hann.move(x, y)
label.text = f’盘架{x+1}的圆盘移到盘架{y+1}上’
else:
label.text = ‘演示完毕!’
pyglet.clock.unschedule(on_move)
hann = Hann(window.width/2, 120, 4)
moves = [(0,1), (0,2), (1,2), (0,1), (2,0), (2,1), (0,1), (0,2), (1,2), (1,0), (2,0), (1,2), (0,1), (0,2), (1,2)]
label = pyglet.text.Label(‘汉诺塔圆盘自动移动演示’, font_size=24, color=(0,0,0,255), x=window.width/2, y=50, anchor_x=‘center’, batch=batch)
pyglet.clock.schedule_interval(on_move, 1)
pyglet.app.run()
### 10. 递归问题
虽然解决了自动演示的功能,但继续增加层数怎么办?还是要自己解决这个问题,本篇前言里就说了汉诺塔问题就是一个递归问题,所以就用递归函数来解决,通常写法:
def hanoi(n, start, mid, end):
if n == 1:
print(‘Move disk 1 from’, start, ‘to’, end)
else:
hanoi(n-1, start, end, mid)
print(‘Move disk’, n, ‘from’, start, ‘to’, end)
hanoi(n-1, mid, start, end)
hanoi(4, 0, 1, 2)
运行的打印结果为:
Move disk 1 from 0 to 1
Move disk 2 from 0 to 2
Move disk 1 from 1 to 2
Move disk 3 from 0 to 1
Move disk 1 from 2 to 0
Move disk 2 from 2 to 1
Move disk 1 from 0 to 1
Move disk 4 from 0 to 2
Move disk 1 from 1 to 2
Move disk 2 from 1 to 0
Move disk 1 from 2 to 0
Move disk 3 from 1 to 2
Move disk 1 from 0 to 1
Move disk 2 from 0 to 2
Move disk 1 from 1 to 2
移动的坐标刚好与上一步指定的相同,但要改造这个函数,我们需要的是坐标返回值不是打印信息,所以指定一列表用于存放这些坐标,改造后的函数:
def hanoi(n, start, mid, end, moves=None):
if moves is None:
moves = []
if n == 1:
moves.append((start, end))
else:
hanoi(n-1, start, end, mid, moves)
moves.append((start, end))
hanoi(n-1, mid, start, end, moves)
return moves
for order in (4,7,8):
moves = hanoi(order, 0, 1, 2)
print(len(moves)==2**order-1)
print(moves)
运行结果:
True
[(0, 1), (0, 2), (1, 2), (0, 1), (2, 0), (2, 1), (0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (1, 2), (0, 1), (0, 2), (1, 2)]
True
[(0, 2), (0, 1), (2, 1), (0, 2), (1, 0), (1, 2), (0, 2), (0, 1), (2, 1), (2, 0), (1, 0), (2, 1), (0, 2), (0, 1), (2, 1), (0, 2), (1, 0), (1, 2), (0, 2), (1, 0), (2, 1), (2, 0), (1, 0), (1, 2), (0, 2), (0, 1), (2, 1), (0, 2), (1, 0), (1, 2), (0, 2), (0, 1), (2, 1), (2, 0), (1, 0), (2, 1), (0, 2), (0, 1), (2, 1), (2, 0), (1, 0), (1, 2), (0, 2), (1, 0), (2, 1), (2, 0), (1, 0), (2, 1), (0, 2), (0, 1), (2, 1), (0, 2), (1, 0), (1, 2), (0, 2), (0, 1), (2, 1), (2, 0), (1, 0), (2, 1), (0, 2), (0, 1), (2, 1), (0, 2), (1, 0), (1, 2), (0, 2), (1, 0), (2, 1), (2, 0), (1, 0), (1, 2), (0, 2), (0, 1), (2, 1), (0, 2), (1, 0), (1, 2), (0, 2), (1, 0), (2, 1), (2, 0), (1, 0), (2, 1), (0, 2), (0, 1), (2, 1), (2, 0), (1, 0), (1, 2), (0, 2), (1, 0), (2, 1), (2, 0), (1, 0), (1, 2), (0, 2), (0, 1), (2, 1), (0, 2), (1, 0), (1, 2), (0, 2), (0, 1), (2, 1), (2, 0), (1, 0), (2, 1), (0, 2), (0, 1), (2, 1), (0, 2), (1, 0), (1, 2), (0, 2), (1, 0), (2, 1), (2, 0), (1, 0), (1, 2), (0, 2), (0, 1), (2, 1), (0, 2), (1, 0), (1, 2), (0, 2)]
True
[(0, 1), (0, 2), (1, 2), (0, 1), (2, 0), (2, 1), (0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (1, 2), (0, 1), (0, 2), (1, 2), (0, 1), (2, 0), (2, 1), (0, 1), (2, 0), (1, 2), (1, 0), (2, 0), (2, 1), (0, 1), (0, 2), (1, 2), (0, 1), (2, 0), (2, 1), (0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (1, 2), (0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (2, 1), (0, 1), (2, 0), (1, 2), (1, 0), (2, 0), (1, 2), (0, 1), (0, 2), (1, 2), (0, 1), (2, 0), (2, 1), (0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (1, 2), (0, 1), (0, 2), (1, 2), (0, 1), (2, 0), (2, 1), (0, 1), (2, 0), (1, 2), (1, 0), (2, 0), (2, 1), (0, 1), (0, 2), (1, 2), (0, 1), (2, 0), (2, 1), (0, 1), (2, 0), (1, 2), (1, 0), (2, 0), (1, 2), (0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (2, 1), (0, 1), (2, 0), (1, 2), (1, 0), (2, 0), (2, 1), (0, 1), (0, 2), (1, 2), (0, 1), (2, 0), (2, 1), (0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (1, 2), (0, 1), (0, 2), (1, 2), (0, 1), (2, 0), (2, 1), (0, 1), (2, 0), (1, 2), (1, 0), (2, 0), (2, 1), (0, 1), (0, 2), (1, 2), (0, 1), (2, 0), (2, 1), (0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (1, 2), (0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (2, 1), (0, 1), (2, 0), (1, 2), (1, 0), (2, 0), (1, 2), (0, 1), (0, 2), (1, 2), (0, 1), (2, 0), (2, 1), (0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (1, 2), (0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (2, 1), (0, 1), (2, 0), (1, 2), (1, 0), (2, 0), (2, 1), (0, 1), (0, 2), (1, 2), (0, 1), (2, 0), (2, 1), (0, 1), (2, 0), (1, 2), (1, 0), (2, 0), (1, 2), (0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (2, 1), (0, 1), (2, 0), (1, 2), (1, 0), (2, 0), (1, 2), (0, 1), (0, 2), (1, 2), (0, 1), (2, 0), (2, 1), (0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (1, 2), (0, 1), (0, 2), (1, 2), (0, 1), (2, 0), (2, 1), (0, 1), (2, 0), (1, 2), (1, 0), (2, 0), (2, 1), (0, 1), (0, 2), (1, 2), (0, 1), (2, 0), (2, 1), (0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (1, 2), (0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (2, 1), (0, 1), (2, 0), (1, 2), (1, 0), (2, 0), (1, 2), (0, 1), (0, 2), (1, 2), (0, 1), (2, 0), (2, 1), (0, 1), (0, 2), (1, 2), (1, 0), (2, 0), (1, 2), (0, 1), (0, 2), (1, 2)]
由此,也验证了总步骤数 steps = 2^n - 1,n为层数。
### 11. 任意展示
解决递归问题后,就可以展示任意层数的移动演示了,就用上一步得到的7层汉诺塔的移动坐标来展示一下它的移动过程:
![](https://img-blog.csdnimg.cn/direct/9f5a98a1e63a43fab69e422259aa4591.gif)
完整代码:
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-ispace, y, x-ispace, y+height, width=thickness, color=Color[0], batch=batch) for i in range(-1,2)]
self.disk = [Disk(x+ispace, 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.height = (height-thickness2)/order
self.step = (width-thickness)/(order+1)
coordinates = [(self.x-space, self.y+(i+1)*self.height-(self.height-thickness)/2) for i in range(order)]
self.beads = [Disk(xy, Color[i%8+1], width=self.width-iself.step, height=self.height) 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)
print(‘大盘不能搬到小盘上’)
return False
else:
print(‘所选择的塔架为空!’)
return None
self.beads[bead].move((pole2-pole1)*self.space, (len(self.array[pole2])-len(self.array[pole1]))*self.height)
self.array[pole2].append(bead)
return True
@window.event
def on_draw():
window.clear()
batch.draw()
@window.event
def on_key_press(symbol, modifiers):
global start
if not start:
start = True
pyglet.clock.schedule_interval(on_move, 0.3)
@window.event
def on_mouse_press(x, y, button, modifier):
global start
if not start:
start = True
pyglet.clock.schedule_interval(on_move, 0.3)
def hanoi(n, start, mid, end, moves=None):
if moves is None:
moves = []
if n == 1:
moves.append((start, end))
else:
hanoi(n-1, start, end, mid, moves)
moves.append((start, end))
hanoi(n-1, mid, start, end, moves)
return moves
def on_move(event):
global moves,steps
if moves:
x, y = moves.pop(0)
hanns.move(x, y)
label.text = f’盘架{x+1}的圆盘移到盘架{y+1}上’
message.text = f’总步骤数:{steps}\t当前步数:{steps-len(moves)}’
else:
label.text = ‘演示完毕!’
pyglet.clock.unschedule(on_move)
order = 7
start = False
hanns = Hann(window.width/2, 120, order)
label = pyglet.text.Label(‘汉诺塔圆盘自动移动演示,任意按键开始…’, font_size=24, color=(0,0,0,255), x=window.width/2, y=50, anchor_x=‘center’, batch=batch)
moves = hanoi(order, 0, 1, 2)
steps = len(moves)
message = pyglet.text.Label(f’总步骤数:{steps}\t当前步数:{steps-len(moves)}', font_size=24, color=(0,0,0,255), x=100, y=450, batch=batch)
pyglet.app.run()
### 12. 鼠标操作
增加一个新的属性.poleheight,把原来的.height属性改为.beadheight以示区别;再给塔类增加两个方法判断鼠标点击和是否成功:
>
> 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\_mouse\_press(x, y, dx, dy):
> global moves
> if not hanns.success():
> pole = hanns.on\_mouse\_over(x, y)
> if pole is not None:
> print(pole)
> moves.append(pole)
> if len(moves)==2:
> hanns.move(\*moves)
> moves.clear()
> if hanns.success():
> print('Success!')
>
>
>
![](https://img-blog.csdnimg.cn/direct/bebde2e90b52400fb92ae97e38fec0c8.gif)
完整代码:
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):
感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的:
① 2000多本Python电子书(主流和经典的书籍应该都有了)
② Python标准库资料(最全中文版)
③ 项目源码(四五十个有趣且经典的练手项目及源码)
④ Python基础入门、爬虫、web开发、大数据分析方面的视频(适合小白学习)
⑤ Python学习路线图(告别不入流的学习)
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!