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

 

目录

汉诺塔游戏

完整游戏

后期展望


汉诺塔游戏

汉诺塔(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()

后期展望

之后有空再优化一下代码,再添加上音效、回放等功能,游戏效果会理想些。还能把上期的自动演示功能也加进去,就更加完美了。


本文完,以下仅为凑字数,请忽略:

自动演示功能,即把以下递归函数的结果展现出来即可:

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)]

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/451754.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

js【详解】Promise

为什么需要使用 Promise &#xff1f; 传统回调函数的代码层层嵌套&#xff0c;形成回调地狱&#xff0c;难以阅读和维护&#xff0c;为了解决回调地狱的问题&#xff0c;诞生了 Promise 什么是 Promise &#xff1f; Promise 是一种异步编程的解决方案&#xff0c;本身是一个构…

套接字的地址结构,IP地址转换函数,网络编程的接口

目录 一、套接字的地址结构 1.1 通用socket地址结构 1.2 专用socket地址结构 1.2.1 tcp协议族 1.2.3 IP协议族 二、IP地址转换函数 三、网络编程接口 3.1 socket() 3.2 bind() 3.3 listen() 3.4 accept() 3.5 connect() 3.6 close() 3.7 recv()、send() 3.8 recv…

手写简易操作系统(五)--获得物理内存容量

前情提要 上一章中我们进入了保护模式&#xff0c;并且跳转到了32位模式下执行。这一章较为简单&#xff0c;我们来获取物理内存的实际容量。 一、获得内存容量的方式 在Linux中有多种方法获取内存容量&#xff0c;如果一种方法失败&#xff0c;就会试用其他方法。其本质上是…

考研数学|汤家凤《1800》vs 张宇《1000》,怎么选?

汤家凤的1800题和张宇的1000题都是备考数学考研的热门选择&#xff0c;但究竟哪个更适合备考呢&#xff1f;下面分享一些见解。 首先&#xff0c;让我们来看看传统习题册存在的一些问题。虽然传统习题册通常会覆盖考试的各个知识点和题型&#xff0c;但其中一些问题在于它们可…

JDBC连接MysqL

import java.sql.*;public class Demo {public static void main(String[] args) throws ClassNotFoundException, SQLException {//1.注册驱动&#xff0c;加载驱动&#xff1b;Class.forName("com.mysql.jdbc.Driver");//2.获得连接,返回connection类型的对象&…

汤唯短发造型:保留经典和适合自己的风格,也许才是最重要的

汤唯短发造型&#xff1a;保留经典和适合自己的风格&#xff0c;也许才是最重要的 汤唯短发造型登上Vogue四月刊封面&#xff0c;引发网友热议。#李秘书讲写作#说说是怎么回事&#xff1f; 这次Vogue四月刊的封面大片&#xff0c;汤唯以一头短发亮相&#xff0c;身穿五颜六色的…

钉钉平台“智”领宠物界,开启萌宠智能新时代!

在当前数字化转型的浪潮中&#xff0c;钉钉用便捷的数字化解决方案推动了宠物业界的智能升级。一家宠物用品公司采用无雀科技数字化管理系统&#xff0c;与钉钉平台结合&#xff0c;解决了小型企业普遍存在的财务管理不清晰、业务流程不规范、客户信息核对繁琐等痛点问题。 针对…

一周学会Django5 Python Web开发-Django5内置模板引擎-模板继承

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计34条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

Linux中文件的权限

我们首先需要明白&#xff0c;权限 用户角色 文件的权限属性 一、拥有者、所属组和other&#xff08;用户角色&#xff09; 以文件file1为例 第一个箭头所指处即是文件的拥有者&#xff0c;拥有者为zz 第二个箭头所指处即使文件的所属组&#xff0c;所属组为zz 除去拥有者…

嵌入式系统工程师错题总结

笔者来介绍一下嵌入式系统工程师考试的一些易错题目 题目介绍  流水线指令计算公式&#xff1a;一条指令总时间max&#xff08;单个指令执行时间&#xff09;*&#xff08;指令数-1&#xff09;  平均故障间隔时间  ICMP协议&#xff1a;传送通信问题相关的消息。 …

电脑打开应用慢

电脑打开什么应用都很慢 我的电脑是i73060&#xff0c;但是打开应用很慢 解决办法&#xff1a; 在注册表[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\CI\Policy]将"VerifiedAndReputablePolicyState"的值设置为0&#xff0c;如果没有这一项&#xff0c;就…

SPI、Spring SPI、SpringFactoriesLoader

一、SPI技术 SPI全名Service Provider interface&#xff0c;翻译过来就是“服务提供接口”&#xff0c;再说简单就是提供某一个服务的接口&#xff0c; 提供给服务开发者或者服务生产商来进行实现。 Java SPI 是JDK内置的一种动态加载扩展点的实现。 这个机制在一般的业务代…

【数据集】2023自动驾驶开源数据集-学习笔记

文章目录 1. 自动驾驶有哪些公开数据集2. 预测相关的数据集有哪些 1. 自动驾驶有哪些公开数据集 waymo open dataset 适应任务: 域适应&#xff0c;2D追踪&#xff0c;2D检测&#xff0c;3D追踪&#xff0c;3D检测&#xff0c;实时2D检测&#xff0c;实时3D检测&#xff0c;交互…

深入解析Java内存模型

一、背景 并发编程本质问题是&#xff1a;CPU、内存以及IO三者之间的速度差异。CPU速度快于内存、内存访问速度又远远快于IO&#xff0c;根据木桶理论&#xff0c;程序性能取决于最慢的操作&#xff0c;即IO操作。这样会出现CPU和内存交互时&#xff0c;CPU性能无法被充分利用…

使用命令行查看同一局域网内所有ip地址

由于学科实践课程提供的局域网IP扫描软件在本机上运行时&#xff0c;无法扫描出树莓派&#xff08;可能和防火墙设置有关&#xff1f;&#xff09;&#xff0c;所以记录一种通过命令行查看同一局域网下设备IP地址的方法&#xff0c;以手机热点下查找树莓派IP为例。 Step1&#…

Hive面经

hive原理 Hive 内部表和外部表的区别Hive 有索引吗运维如何对 Hive 进行调度ORC、Parquet 等列式存储的优点数据建模用的哪些模型&#xff1f;1. 星型模型2. 雪花模型3. 星座模型 为什么要对数据仓库分层&#xff1f;使用过 Hive 解析 JSON 串吗sort by 和 order by 的区别数据…

读书笔记之《机器与人》:AI如何重构工作方式和流程?

《机器与人: 埃森哲论新人工智能》作者是【美】保罗•多尔蒂和詹姆斯•威尔逊 &#xff0c;原作名: Human Machine: Reimagining Work in the Age of AI&#xff0c;2018年出版。 保罗•多尔蒂&#xff08;PAUL DAUGHERTYH&#xff09;&#xff1a;埃森哲首席技术官和创新官、…

策略迭代和价值迭代

策略迭代价值迭代 策略迭代&#xff08;Policy Iteration&#xff09;基本步骤例子&#xff1a;公主的营救 价值迭代&#xff08;Value Iteration&#xff09;基本步骤例子&#xff1a;公主的营救 策略迭代与价值迭代的区别实现方式目标收敛速度与其他技术的交互 策略迭代&…

浅谈Redis 的 保护模式(protected-mode)

今天在一台服务器上面部署了redis,发现始终无法用工具远程连接,项目里面是正常的,就是工具不行,防火墙也关闭了.折腾了一会才突然想起来,是不是触发了保护模式. 什么时候触发保护模式protected-mode: 同时满足以下两个: 1.bind未指定ip 2.未配置密码 解决方案: 编辑redis…

Room+ViewModel+LiveData

Room框架支持的LiveData会自动监听数据库的变化&#xff0c;当数据库发生变化的时候&#xff0c;会调用onChanged函数更新UI 1.MainActivity package com.tiger.room2;import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.vie…