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

news2024/11/26 8:20:38

 

目录

汉诺塔游戏

完整游戏

后期展望


汉诺塔游戏

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【OpenGL手册11】材质的模型

目录 一、说明二、材质表面和光照三、设置材质四、光的属性五、不同的光源颜色练习 一、说明 在现实世界里&#xff0c;每个物体会对光产生不同的反应。比如&#xff0c;钢制物体看起来通常会比陶土花瓶更闪闪发光&#xff0c;一个木头箱子也不会与一个钢制箱子反射同样程度的…

SQL注入-时间盲注

时间盲注 盲注就是在SQL注入过程中&#xff0c;SQL语句执行后&#xff0c;查询到的数据不能回显到前端页面。此时&#xff0c;我们需要利用一些方法进行判断或者尝试&#xff0c;这个过程称之为盲注。 时间盲注特性 在页面中&#xff0c;不管用户输入什么&#xff0c;数据交互完…

【Linux】文件系统和软硬链接

❤️前言 今天的这篇博客主要是总结前几天学习的关于Linux系统下的文件系统以及软硬链接的内容。希望能对大家有所帮助。 正文 我们今天要学习关于Linux下的文件系统——EXT2的知识&#xff0c;需要注意的是这里的文件和我们以前遇到的那些文件并不相同&#xff0c;以前我们主…

安装配置Spark集群

安装Spark集群主要包括以下步骤&#xff1a; 1、下载Spark安装包&#xff0c;在各节点中安装部署spark集群 2、配置整合 3、启动并测试 下载Spark 可以从官方网站下载合适的版本。当前环境已经提供了安装包&#xff0c;存放在 /opt/software目录下。 在node1节点上安装Sp…

日期问题 刷题笔记

思路 枚举 19600101 到20591231这个区间的数 获得年月日 判断是否合法 如果合法 关于题目给出的日期 有三种可能 年/月/日 日/月/年 月/日/年 判断 是否和题目给出的日期符合 如果符合 输出 闰年{ 1.被4整除不被100整除 2.被400整除} 补位写法“%02d" 如果不…

数据“隐领”未来!【隐私计算实训营】限时免费招募!

数智经济时代&#xff0c;为强化个人隐私信息保护&#xff0c;国家颁布了《国家安全法》、《网络安全法》、《数据安全法》等数据安全法律法规&#xff0c;并严厉处罚数据违规出海、侵权、滥用等问题。数据安全和隐私保护成为大家的共识。隐私计算技术在此背景下应运而生&#…

Linux——线程(3)

在上一篇博客中&#xff0c;我介绍了关于Linux系统中pthread库线程的接口使用以 及对于pthread库的理解。但是我们单单会使用多线程的接口还不够&#xff0c;因为 在使用多线程解决问题的时候&#xff0c;由于进程中的数据对于其中的线程来说大 多是共享的&#xff0c;这也势必…

12双体系Java学习之局部变量和作用域

局部变量 局部变量的作用域 参数变量

找不到mfc140u.dll怎么办?修复缺失mfc140u.dll的多种方案分享

mfc140u.dll文件是一个重要的动态链接库文件&#xff0c;它在Windows操作系统中发挥着关键的作用。由于各种原因&#xff0c;例如应用程序冲突或系统错误等&#xff0c;mfc140u.dll文件有时会出现丢失的情况。一旦发生这种问题&#xff0c;运行依赖此文件的应用程序将无法正常启…

【C语言】初步解决指针疑惑

✨✨ 欢迎大家来到莉莉的博文✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 目录 一.理解内存和编址 1.1理解内存 1.2理解编址 二.指针变量和地址 1.1取地址操作符 三.指针变量和解引用操作符&#xff08;*&#xff09; …

设计模式八:观察者模式

文章目录 1、观察者模式2、示例3、spring中的观察者模式3.1 spring观察者模式的使用3.2 spring观察者模式原理解析 1、观察者模式 观察者模式&#xff08;Observer Design Pattern&#xff09;,也叫做发布订阅模式&#xff08;Publish-Subscribe Design Pattern&#xff09;、模…

音视频学习笔记——c++多线程(二)

✊✊✊&#x1f308;大家好&#xff01;本篇文章是多线程系列第二篇文章&#x1f607;。首先讲解了利用mutex解决多线程数据共享问题&#xff0c;举例更好理解lock和unlock的使用方法&#xff0c;以及错误操作造成的死锁问题&#xff0c;最后讲解了lock_guard与unique_lock使用…

LEETCODE3

法一:记忆化递归 int climbStairsRecursive(int n, int* memo) {if (n < 2) {return n;}if (memo[n] > 0) {return memo[n];}memo[n] climbStairsRecursive(n - 1, memo) climbStairsRecursive(n - 2, memo);return memo[n]; }int climbStairs(int n) {int* memo (in…

QML 控件添加键盘事件

在QML中&#xff0c;可以使用Keys类型来处理键盘事件。以下是一个简单的示例&#xff0c;演示如何在QML控件中添加键盘事件&#xff1a; import QtQuick 2.12 import QtQuick.Window 2.12Window {visible: truewidth: 640height: 480title: qsTr("Hello World")Recta…

Linux的MySQL安装与卸载

安装与卸载 卸载安装配置yum源安装MySQL 声明一下本人用的Linux版本是CentOs7.9版本的。 卸载 如果我们用的云服务器&#xff0c;云服务器可能会自带MySQL或者mariadb&#xff08;其实就是MySQL的一个开源分支&#xff09;&#xff0c;如果我们不想用自带的&#xff0c;需要先…

理论学习 BatchNorm2d

import torch import torch.nn as nn# With Learnable Parameters m nn.BatchNorm2d(100) # Without Learnable Parameters m nn.BatchNorm2d(100, affineFalse) input torch.randn(20, 100, 35, 45) output m(input)print(output) print(output.shape)这段代码展示了如何使…

mybatis-plus-generator 使用 velocity 生成前后台代码

操作步骤 1&#xff09;准备mybatis-plus 生成代码的 vm文件 2&#xff09;添加依赖 mybatis-plus-generator 代码生成器的依赖 3&#xff09;执行工具方法生成代码 1、准备 mybatis-plus 生成代码的 vm文件 1&#xff09;找vm模板 去工程的 external Libraries 找到 mybati…

ES6基础6

Promise对象 Promise的含义 所谓Promise&#xff0c;简单说就是一个容器&#xff0c;里面保存着某个未来才会结束的事件&#xff08;通常是一个异步操作&#xff09;的结果。从语法上说&#xff0c;Promise是一个对象&#xff0c;从它可以获取异步操作的消息。Promise提供统一的…

深度学习笔记_8隐马尔可夫模型(HMM)

隐马尔可夫模型(Hidden Markov Model, HMM)是一种统计模型&#xff0c;在语音识别、行为识别、NLP、故障诊断等领域具有高效的性能。 HMM是关于时序的概率模型&#xff0c;描述一个含有未知参数的马尔可夫链所生成的不可观测的状态随机序列&#xff0c;再由各个状态生成观测随…

设计模式 -- 1:简单工厂模式

目录 代码记录代码部分 代码记录 设计模式的代码注意要运用到面向对象的思想 考虑到紧耦合和松耦合 把具体的操作类分开 不让其互相影响&#xff08;注意这点&#xff09; 下面是UML类图 代码部分 #include <iostream> #include <memory> // 引入智能指针的头文…