2024年Python最新Python 一步一步教你用pyglet制作汉诺塔游戏(续)

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-thickness
2)/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-thickness
2)/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):
self.cir1.x += dx; self.cir1.y += dy

最后

🍅 硬核资料:关注即可领取PPT模板、简历模板、行业经典书籍PDF。
🍅 技术互助:技术群大佬指点迷津,你的问题可能不是问题,求资源在群里喊一声。
🍅 面试题库:由技术群里的小伙伴们共同投稿,热乎的大厂面试真题,持续更新中。
🍅 知识体系:含编程语言、算法、大数据生态圈组件(Mysql、Hive、Spark、Flink)、数据仓库、Python、前端等等。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里无偿获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 12
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值