汉诺塔动态演示程序,可用于Python算法递归演示教学

汉诺塔动态演示程序,可用于Python算法递归演示教学

Python汉诺塔演示动图

Python汉诺塔演示动图

"""
   汉诺塔演示程序.py
   本程序需要最新版本sprites模块支持,请用cmd命令打开管理员窗口并且输入以下进行安装:
   pip install sprites
"""
import time
from sprites import Sprite ,Screen,txt2image

__author__ = '李兴球'
__date__ = '2021/12/1'
__blog__ = 'www.lixingqiu.com'

N = 2                                 # n个盘子 
SPEED = 5                             # 全局速度
stack = []
def _custom_setup(self,p=[]):
    if p:
      self.speed(0)
      self.stackid = 0
      self.shapesize(1,p[0])
      self.color('brown','blue')      
Sprite.custom_setup = _custom_setup

def _rise(self):
    """上升"""
    while self.ycor() < 240:self.addy(0.1*SPEED)
    self.wait(0.2*(1/SPEED))    
Sprite.rise = _rise

def _fall(self):
     """下降"""
     if stack[self.stackid]:
           pz = stack[self.stackid][-1]    # 即将下落的stack的最上面的盘子的y坐标
           y = pz.ycor()
     else:
           y = -20
     while self.ycor() > y+20: self.addy(-0.1 * SPEED)
Sprite.fall = _fall

class Stack(list):
    """装盘子的"""
    def __init__(self, values=[],x=0,y=0):
        list.__init__([])
        self.x = x
        self.y = y
        self.extend(values)
        for pz in values:               # 每一个盘子
            pz.goto(x,y)
            y = y + 20
            pz.show()

def hanota(n,a,b,c):
    """把n个盘子从a经过b移到c"""    
    if n>1:       
        hanota(n-1,a,c,b)
        hanota(1,a,b,c)
        hanota(n-1,b,a,c)
    else:
        print(a,'->',c)
        plate = stack[a].pop()
        plate.rise()                      # 盘子升起
        plate.stackid = c                 # 修改盘子所在的stack的id
        plate.slide(stack[c].x,delay=2000*(1/SPEED))   # 设定盘子到所在的stack同一x坐标,平移时y坐标不变
        plate.setx(stack[c].x)             # 校准坐标
        plate.wait(0.2*(1/SPEED))
        plate.fall()                      # 盘子下落
        stack[c].append(plate)            # 把盘子装在编号为c的stack里

def make_plates(n):
    """制作n个盘子放到列表中,不要太多了!!!"""
    plates = []
    for length in range(1,n+1):
        p = Sprite(shape='square',visible=False)
        p.custom_setup([length])
        plates.insert(0,p)               #  要让前面的盘子更大所以用插入0号索引
    return plates

def destory_plates(N):
    global stack
    if len(stack)==0:return   
    for pz in stack[2]: pz.kill()
    stack = []
        
def draw_line():
    # 画一根横线和三根竖线
    dummy = Sprite(visible=False)
    dummy.pensize(2)
    dummy.color('gray')
    dummy.sety(-10)
    dummy.pendown()
    dummy.bk(300)
    dummy.fd(600)
    dummy.bk(150)
    dummy.left(90)
    dummy.fd(220)                       # 升220
    dummy.bk(220)                       # 降220
    dummy.left(90)
    dummy.fd(150)
    dummy.right(90)
    dummy.fd(220)
    dummy.bk(220)
    dummy.left(90)
    dummy.fd(150)
    dummy.right(90)
    dummy.fd(220)

def panzi_zenjia(x,y,pz_n):
    global N
    N = N + 1
    N = min(10,N)
    pz_n.clear()
    pz_n.write(N,font=('',16,'normal'))
    
def panzi_jianshao(x,y,pz_n):
    global N
    N = N - 1
    N = max(1,N)
    pz_n.clear()
    pz_n.write(N,font=('',16,'normal'))

def sd_zenjia(x,y,sd_n):
    global SPEED
    SPEED = SPEED + 1
    SPEED = min(20,SPEED)
    sd_n.clear()
    sd_n.write(SPEED,font=('',16,'normal'))
    
def sd_jianshao(x,y,sd_n):
    global SPEED
    SPEED = SPEED - 1
    SPEED = max(1,SPEED)
    sd_n.clear()
    sd_n.write(SPEED,font=('',16,'normal'))  
    
def display_operation():
    pz_w = Sprite(visible=False,pos=(-200,-100))
    pz_w.write('盘子数:')
    
    pz_n = Sprite(visible=False,pos=(-130,-100))
    pz_n.write(N,font=('',16,'normal'))
    
    txt2image('增加','res/zj.png')
    pz_add = Sprite(shape='res/zj.png',pos=(-80,-90))
    pz_add.onclick(lambda x,y:panzi_zenjia(x,y,pz_n))
    
    txt2image('减少','res/js.png')
    pz_sub = Sprite(shape='res/js.png',pos=(-30,-90))
    pz_sub.onclick(lambda x,y:panzi_jianshao(x,y,pz_n))

    
    sd = Sprite(visible=False,pos=(-220,-150))
    sd.write('全局速度:')    
    
    sd_n = Sprite(visible=False,pos=(-130,-150))
    sd_n.write(SPEED,font=('',16,'normal'))
    
    txt2image('增加','res/zjsd.png')
    sd_add = Sprite(shape='res/zjsd.png',pos=(-80,-140))
    sd_add.onclick(lambda x,y:sd_zenjia(x,y,sd_n))
    
    txt2image('减少','res/jssd.png')
    sd_sub = Sprite(shape='res/jssd.png',pos=(-30,-140))
    sd_sub.onclick(lambda x,y:sd_jianshao(x,y,sd_n))

    txt2image('开始演示','res/begin0.png',fontsize=30)
    txt2image('演 示 中...','res/begin1.png',fontsize=30,color=(128,127,122))
    begin_button = Sprite(shape=['res/begin0.png','res/begin1.png'],pos=(-100,-200))

    addsubbiao = [pz_add,pz_sub,sd_add,sd_sub]
    begin_button.onclick(lambda x,y:start_demo(x,y,begin_button,addsubbiao))

def start_demo(x,y,begin_button,addsubbiao):
    global stack
    
    [b.hide() for b in addsubbiao]
    begin_button.onclick(None)
    begin_button.shape('res/begin1.png')
    
    destory_plates(N)             # 销毁以前的stack3中的N个盘子
    plates = make_plates(N)
    stack1 = Stack(plates,-150)   # 最左边的stack有n个盘子,最下面的是最长的            
    stack2 = Stack([])
    stack3 = Stack([],150)
    stack.extend([stack1,stack2,stack3])
    begin_button.wait(2)
    hanota(N,0,1,2)               # 0,1,2,分别表示3个stack的索引号     
    begin_button.shape('res/begin0.png')
    begin_button.onclick(lambda x,y:start_demo(x,y,begin_button,addsubbiao)) # 重新绑定,可以开始单击了
    [b.show() for b in addsubbiao]
    
def main():
    w = Sprite(visible=False,pos=(0,250))
    w.write("汉诺塔演示程序",align='center',font=('楷体',22,'underline'))
    draw_line()                  # 画线条  
    display_operation()
    w.screen.mainloop()
    
if __name__ == "__main__":

    main()

李兴球

李兴球的博客是Python创意编程原创博客