跨境派

跨境派

跨境派,专注跨境行业新闻资讯、跨境电商知识分享!

当前位置:首页 > 跨境学堂 > Python 一步一步教你用pyglet制作汉诺塔游戏(终篇)

Python 一步一步教你用pyglet制作汉诺塔游戏(终篇)

时间:2024-03-24 09:38:00 来源:网络cs 作者:璐璐 栏目:跨境学堂 阅读:

标签: 游戏 
阅读本书更多章节>>>>

 

目录

汉诺塔游戏

完整游戏

演示回放

完整代码


汉诺塔游戏

汉诺塔(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.eventdef on_draw():    window.clear()    batch.draw() @window.eventdef 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 = 2hanns = 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 = Falsepyglet.app.run()

演示回放

优化代码,布局稍作调整,并加入帮助、演示和回放等功能。演示和回放的区别:演示功能是提供如何移动的教学帮助;回放功能则是展示用户自己的移动过程。

运行效果如下:

完整代码

import pygletfrom pyglet.window import keyWidth, Height = 800, 500window = 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):            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.orderdef hanoi(n, start=0, mid=1, end=2, 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 movesdef hanoimacro(n, start=0, mid=1, end=2, moves=None):    global macro, array    if moves is None:        moves = []        array = [[*range(n)],[],[]]        macro = [[[*range(n)],[],[]]]    if n == 1:        moves.append((start, end))        array[end].append(array[start].pop())        macro.append([disk[:] for disk in array])    else:        hanoimacro(n-1, start, end, mid, moves)        moves.append((start, end))        array[end].append(array[start].pop())        macro.append([disk[:] for disk in array])        hanoimacro(n-1, mid, start, end, moves)    return macro@window.eventdef on_draw():    window.clear()    batch.draw() @window.eventdef on_mouse_press(x, y, dx, dy):    global hanns, gamestarting, gamecompleted    if not gamestarting:        gamestarting = True        show_message(False)        return    elif hanns.playback:        return    if not hanns.success():        pole = hanns.on_mouse_over(x, y)        if pole is not None:            hanns.posxy.append(pole)            if len(hanns.posxy)==1:                hanns.set_color(hanns.posxy[0], (200,150,0))                if not hanns.array[pole]:                    hanns.set_color(hanns.posxy[0])                    hanns.posxy.pop()                    return        if len(hanns.posxy)==2:            if hanns.posxy[0]!=hanns.posxy[1]:                info = hanns.move(*hanns.posxy)                hanns.set_color(hanns.posxy[0])                if info:                    info1.text = information(3)                else:                    info1.text = information(6)            hanns.set_color(hanns.posxy[0])            hanns.posxy.clear()            info2.text = information()        if hanns.success():            if hanns.order<24:                info1.text = information(4)            else:                info1.text = information(5)                gamecompleted = True                return    elif not gamecompleted:        hanns = Hann(hanns.order+1)        info1.text = information(0)        info2.text = information(2)@window.eventdef on_key_press(symbol, modifiers):    global hanns, macro, gamestarting    def show_how(event):        if macro:            hanns.move(*macro.pop(0))            info2.text = information()        else:            info1.text = info1.text.replace('中......', '完成!')            hanns.playback = False            pyglet.clock.unschedule(show_how)    if not gamestarting:        gamestarting = True        show_message(False)        return    if hanns.playback:        if symbol in (key.B, key.BREAK) and modifiers & key.MOD_CTRL:            macro.clear()            info1.text = info1.text.replace('中......', '终止,')+'游戏继续!'        return    if hanns.macro and hanns.click and not hanns.success() and symbol==key.Z and modifiers & key.MOD_CTRL:        hanns.ctrlz = not hanns.ctrlz        hanns.posxy = hanns.macro.pop()[::-1]        info = hanns.move(*hanns.posxy)        hanns.steps -= 2*hanns.ctrlz        hanns.posxy = []    elif symbol == key.H and modifiers & key.MOD_CTRL:        gamestarting = False        show_message(True)    elif symbol == key.R and modifiers & key.MOD_CTRL:        if hanns.steps:            hanns = Hann(hanns.order)            info1.text = information(0)    elif symbol == key.N and modifiers & (key.MOD_CTRL | key.MOD_SHIFT) and modifiers & key.MOD_SHIFT:        if hanns.order>2:            hanns = Hann(hanns.order-1)            info1.text = information(0)        else:            info1.text = information(7)    elif symbol == key.N and modifiers & key.MOD_CTRL and not modifiers & key.MOD_SHIFT:        if hanns.order<24:            hanns = Hann(hanns.order+1)            info1.text = information(0)        else:            info1.text = information(8)    elif symbol in (key.S, key.P) and modifiers & key.MOD_CTRL:        macro = hanns.macro        hanns = Hann(hanns.order)        hanns.playback = True        if symbol==key.S:            task = '演示中......'            macro = hanoi(hanns.order)        elif symbol==key.P:            task = '回放中......'        info1.text = f'{hanns.order} 层汉诺塔{task}'        pyglet.clock.schedule_interval(show_how, 9/(hanns.order**2+9))    info2.text = information()def show_message(visible=True):    box1.visible = box2.visible = html.visible = visible    info1.text = information(0)def messagebox():    global box1, box2, html    rect = 150, 90, 500, 330    box1 = pyglet.shapes.Rectangle(*rect, color=(255,255,255,200), batch=batch, group=group)    box2 = pyglet.shapes.Box(*rect, color=(255,0,0,255), thickness=3, batch=batch, group=group)    html = pyglet.text.HTMLLabel(text=hypertext, multiline=True, width=Width//2, x=Width//2, y=240, batch=batch, group=group)    html.anchor_x = html.anchor_y = 'center'def information(info=2):    if info==0:        return f'{hanns.order} 层汉诺塔,游戏开始!'    elif info==1:        return '点击任意键开始......'    elif info==2:        return f'当前层数:{hanns.order:<12}最佳步数:{2**hanns.order-1:<12}当前步数:{hanns.steps:>9}'    elif info==3:        return f'{hanns.order-hanns.array[hanns.posxy[1]][-1]}号圆盘从{hanns.posxy[0]+1}号塔架移动到{hanns.posxy[1]+1}号塔架'    elif info==4:        return f'恭喜您完成 {hanns.order} 层汉诺塔!任意点击层数加一!'    elif info==5:        return f'太棒了!您已完成 {hanns.order} 层汉诺塔,游戏全部通关!'    elif info==6:        return '起始圆盘大于目标位置的圆盘'    elif info==7:        return f'程序设置的最小层数是 2'    elif info==8:        return f'程序设置的最大层数是 {hanns.ordermax}'hanns = Hann(order=2)hypertext = '''<font color="red" size="+2">汉诺塔 Tower of Hanoi</font><br><br><font color="blue" size="24">汉诺塔是一个源于印度的古老传说的益智玩具。这个传说<br>讲述了大梵天创造世界的时候做了三根金刚石柱子,并在<br>一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大<br>梵天命令婆罗门将这些圆盘按照大小顺序重新摆放到另一<br>根柱子上(左边搬到右边,中间临时存放),规定大圆盘不<br>能放在小圆盘上,且一次只能移动一个圆盘。</font><br><br><font face="宋体" color="black" size="+1">操作方法:先后点击塔架,就能移动塔架上的圆盘<br><br>Ctrl+Z 取消操作    Ctrl+R 重新开始<br>Ctrl+S 自动演示    Ctrl+P 操作回放<br>Ctrl+B 终止操作    Ctrl+H 帮助消息<br>Ctrl+N 增减层数(同时按Shift键为减)<br></font>'''info1 = pyglet.text.Label(information(1), font_size=21, color=(0,0,0,255), x=400, y=40, anchor_x='center', batch=batch)info2 = pyglet.text.Label(information(2), font_size=16, font_name='宋体', color=(0,0,0,255), x=70, y=450, batch=batch)gamestarting, gamecompleted = False, Falsemessagebox()pyglet.app.run()

其中记录移动的宏暂未用到,进一步开发会用到:

def hanoimacro(n, start=0, mid=1, end=2, moves=None):
    global macro, array
    if moves is None:
        moves = []
        array = [[*range(n)],[],[]]
        macro = [[[*range(n)],[],[]]]
    if n == 1:
        moves.append((start, end))
        array[end].append(array[start].pop())
        macro.append([disk[:] for disk in array])
    else:
        hanoimacro(n-1, start, end, mid, moves)
        moves.append((start, end))
        array[end].append(array[start].pop())
        macro.append([disk[:] for disk in array])
        hanoimacro(n-1, mid, start, end, moves)
    return macro

阅读本书更多章节>>>>

本文链接:https://www.kjpai.cn/xuetang/2024-03-24/147757.html,文章来源:网络cs,作者:璐璐,版权归作者所有,如需转载请注明来源和作者,否则将追究法律责任!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。

文章评论