Python实现汉诺塔演示程序

Python实现汉诺塔演示程序

汉诺塔问题

一个板子上有三根柱子以及一些大小各不相同的圆盘。我们分别把这三根柱子叫做起始柱A、辅助柱B及目标柱C,汉诺塔移动圆盘的规则如下:

把起始柱A上所有的圆盘都移动到C柱,且在移动过程中始终保持圆盘从小到大排列,即大盘在下、小盘在上。

换句话说,用户在移动圆盘时遵循以下规则:

(1)每次只能移动一个圆盘。

(2)每次移动只能将柱子顶部的圆盘移动到另一个柱子的顶部。

(3)圆盘只能放在更大的圆盘上面或空柱子上。

汉诺塔问题是用递归方法求解的一个典型问题,在此给出算法的模拟演示,使学生更直观的了解递归算法的思想。

这里给出Python实现,用鼠标拖动圆盘。

先给出运行效果示意图:

源码如下:

import tkinter as tk
from tkinter import ttk, messageboxclass HanoiGame(tk.Tk):def __init__(self):super().__init__()self.title("汉诺塔游戏")self.geometry("600x550")self.canvas = tk.Canvas(self, width=600, height=400, bg="#f0f0f0")self.canvas.pack()# 初始化游戏变量self.numDisks = 3  # 圆盘数量self.towers = [[], [], []]  # 代表三个塔的列表self.draggingDisk = None  # 当前正在拖动的圆盘self.draggingDiskSize = 0  # 拖动的圆盘的大小self.init_controls()  # 初始化控制面板self.init_game()  # 初始化游戏def init_controls(self):control_frame = tk.Frame(self) # 创建控制面板框架control_frame.pack(fill=tk.X, pady=5)tk.Label(control_frame, text="选择圆盘数量:").pack(side=tk.LEFT, padx=5)self.numDisksVar = tk.IntVar(value=3)numDisksCombo = ttk.Combobox(control_frame, textvariable=self.numDisksVar, values=[1, 2, 3, 4, 5, 6, 7, 8], width=3)numDisksCombo.pack(side=tk.LEFT, padx=5)tk.Button(control_frame, text="开始游戏", command=self.init_game).pack(side=tk.LEFT, padx=5)tk.Button(control_frame, text="提示", command=self.show_hints).pack(side=tk.LEFT, padx=5)#用一个新的Frame用于包含文本框和滚动条。这样做是为了确保它们能够一起正确地布局hints_frame = tk.Frame(self)  hints_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)self.hintsBox = tk.Text(hints_frame, height=5, state='disabled') # 初始化为禁用状self.hintsBox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)scrollbar = tk.Scrollbar(hints_frame, command=self.hintsBox.yview)scrollbar.pack(side=tk.RIGHT, fill=tk.Y)self.hintsBox.config(yscrollcommand=scrollbar.set)def init_game(self):"""初始化或重置游戏状态"""self.numDisks = self.numDisksVar.get()  # 获取用户选择的圆盘数量self.towers = [[i for i in range(self.numDisks, 0, -1)], [], []]  # 重置塔self.draw_game()  # 绘制游戏界面self.clear_hints()  # 清空提示信息def clear_hints(self):self.hintsBox.config(state='normal')  # 允许编辑文本框以清空内容self.hintsBox.delete(1.0, tk.END)  # 清空文本框self.hintsBox.config(state='disabled')  # 再次禁用文本框以防止编辑def draw_game(self):self.canvas.delete("all")for i, label in enumerate(["A", "B", "C"]):x = 100 + i * 200self.canvas.create_rectangle(x - 10, 300, x + 10, 150, fill="gray")self.canvas.create_text(x, 140, text=label, font=("Arial", 16))for i, tower in enumerate(self.towers):for j, disk in enumerate(tower):self.draw_disk(i, j, disk)self.canvas.tag_bind("disk", "<ButtonPress-1>", self.start_drag)self.canvas.tag_bind("disk", "<B1-Motion>", self.drag)self.canvas.tag_bind("disk", "<ButtonRelease-1>", self.stop_drag)def draw_disk(self, tower_index, disk_index, disk_size):x = 100 + tower_index * 200y = 300 - disk_index * 20self.canvas.create_rectangle(x - disk_size * 10, y, x + disk_size * 10, y - 20, fill="blue", tags=("disk", f"{tower_index}-{disk_index}", f"size{disk_size}"))def start_drag(self, event):closest_disk = self.canvas.find_closest(event.x, event.y)[0]tags = self.canvas.gettags(closest_disk)for tag in tags:if tag.startswith("size"):self.draggingDiskSize = int(tag.replace("size", ""))if "-" in tag:tower_index, disk_index = map(int, tag.split("-"))# 检查是否是塔顶的圆盘if disk_index == len(self.towers[tower_index]) - 1:self.draggingDisk = closest_diskelse:self.draggingDisk = Nonereturn  # 如果不是塔顶的圆盘,则不允许拖动def drag(self, event):if self.draggingDisk:self.canvas.coords(self.draggingDisk, event.x - self.draggingDiskSize * 10, event.y - 10, event.x + self.draggingDiskSize * 10, event.y + 10)def stop_drag(self, event):if self.draggingDisk:tower_index = min(max((event.x - 100) // 200, 0), 2)disk_tag = self.canvas.gettags(self.draggingDisk)[1]source_tower, disk_index = map(int, disk_tag.split("-"))disk_size = self.towers[source_tower][disk_index]if not self.towers[tower_index] or disk_size < self.towers[tower_index][-1]:self.towers[tower_index].append(self.towers[source_tower].pop(disk_index))self.draw_game()self.check_win()else:self.draw_game()  # 重新绘制以修正位置self.draggingDisk = Nonedef check_win(self):# 检查是否所有圆盘都移动到了最右边的柱子上if len(self.towers[2]) == self.numDisks:messagebox.showinfo("成功", "恭喜你成功完成了游戏!")def show_hints(self):"""显示解决游戏的步骤提示"""steps = []self.generate_hanoi_steps(self.numDisks, 'A', 'C', 'B', steps)self.hintsBox.config(state='normal')self.hintsBox.delete(1.0, tk.END)for i, step in enumerate(steps, start=1):  # start=1 表示序号从1开始self.hintsBox.insert(tk.END, f"{i}. {step}\n")  # 在每条提示信息前加入序号self.hintsBox.config(state='disabled')def generate_hanoi_steps(self, n, source, target, auxiliary, steps):if n > 0:self.generate_hanoi_steps(n - 1, source, auxiliary, target, steps)steps.append(f"{source} → {target}")self.generate_hanoi_steps(n - 1, auxiliary, target, source, steps)if __name__ == "__main__":app = HanoiGame()app.mainloop()

附录

汉诺塔递归算法文字版

# 参数:n 层数,a 起点 , b 辅助(中转) c 目标
def  move(n,a,b,c):if n==1:                    #当只有一个圆盘时只需从最左端移向最右端print(a,'-->',c)          #面板可视化,可以通过列举图标表现移动的步骤形式else:move(n-1,a,c,b)      #将前n-1个盘子从a移动到b上move(1,a,b,c)        #将最底下的最后一个盘子从a移动到c上move(n-1,b,a,c)      #将b上的n-1个盘子移动到c上n=int(input("请输入汉诺塔的层数并回车:\n"))
move(n,'A','B','C')

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

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

相关文章

先进电机技术 —— 伺服驱动器与变频器

一、变频器与伺服驱动器发展趋势 在近年来的技术发展中&#xff0c;变频器和伺服驱动器均呈现出显著的先进性提升和技术融合趋势&#xff0c;以下是一些主要的发展方向&#xff1a; ### 变频器的发展趋势&#xff1a; 1. **智能化与网络化**&#xff1a; - 高级变频器集成…

react hook:useMemo

useMemo在每次重新渲染的时候能够缓存计算的结果。 在初次渲染时&#xff0c;useMemo 返回调用 calculateValue 的结果。 在接下来的渲染中&#xff0c;如果依赖项没有发生改变&#xff0c;它将返回上次缓存的值&#xff1b;否则将再次调用 calculateValue&#xff0c;并返回最…

Git本地新项目推送到远程仓库

假设开发了一个新项目&#xff0c;想推送到远程&#xff0c;具体的操作方式和命令如下&#xff1a; &#xff08;使用 git bash&#xff09; 1、切到项目目录中 2、初始化git仓库并在本地提交 //初始化git仓库 git init//将当前目录下的文件添加到仓库&#xff08;缓冲区&…

【知识分享】自动化测试首选接口自动化?

在分层测试的“金字塔”模型中&#xff0c;接口测试属于第二层服务集成测试范畴。 相比UI自动化测试而言&#xff0c;接口自动化测试收益更大&#xff0c;且容易实现&#xff0c;维护成本低&#xff0c;有着更高的投入产出比。因此&#xff0c;项目开展自动化测试的首选一般为接…

Python 基础语法:基本数据类型(字符串)

在日常生活中&#xff0c;我们经常会用文字来记录事物的名称、或记录事情的发展过程等&#xff0c;这些文字类型的数据&#xff0c;就叫做字符串。在未来的写代码的工作中&#xff0c;无论你从事的是哪个方面的编程&#xff0c;字符串操作必不可少&#xff0c;非常重要。 1 字…

linux系统安装docker

docker安装 docker安装自带源安装docker版本和官方源安装国内源安装新版docker生产docker的环境配置登入登出docker hub国内镜像源 docker安装 自带源安装 CentOS 7 中 Docker 的安装: Docker 软件包已经包括在默认的 CentOS-Extras 软件源(联网使用centos7u2自带网络Yum源)里…

计算机网络(基础篇)复习笔记——体系结构/协议基础(持续更新中......)

目录 1 计算机网络基础相关技术Rip 路由更新操作 2 体系结构(OSI 7层, TCP/IP4层)应用层运输层网络层IPv4无分类域间路由选择 CIDRIPV6 数据链路层循环冗余校验CRC协议设备 物理层传输媒体信道复用技术宽带接入技术数据通信 3 网络局域网(以太网Ethernet) 4 通信过程编码:信道极…

vue router 解决路由带参数跳转时出现404问题

我的页面是从一个vue页面router跳转到另一个vue页面&#xff0c;并且利用windows.open() 浏览器重新创建一个页签。但是不知道为什么有时候可以有时候又不行&#xff0c;经过反复测试与分析&#xff0c;最终发现是因为有一个参数的值里包含了小数点., 小数点是浏览器合法字符&a…

Pytest测试中的临时目录与文件管理!

在Pytest测试框架中&#xff0c;使用临时目录与文件是一种有效的测试管理方式&#xff0c;它能够确保测试的独立性和可重复性。在本文中&#xff0c;我们将深入探讨如何在Pytest中利用临时目录与文件进行测试&#xff0c;并通过案例演示实际应用。 为什么需要临时目录与文件&a…

#KEIL使用

关于在调试时&#xff0c;有些局部变量值无法在窗口中查看报错“not in scope"&#xff0c;是被优化掉了&#xff0c;降低优化等级即可。 参考博客&#xff1a; KeilMDK 开发过程中遇到一些奇怪问题记录_keil遇到了不正当的冲突-CSDN博客

Java高频面试之异常篇

有需要互关的小伙伴,关注一下,有关必回关,争取今年认证早日拿到博客专家 finally 块中的代码什么时候被执行&#xff1f; &#xff08;1&#xff09;如果try{}语句块中有return语句&#xff0c;而finally{}语句块中没有return语句时&#xff0c;finally{}块中的代码在return语…

全国保护性耕作/常规耕作农田分类数据集

基于Sentinel-2遥感产品&#xff0c;使用来自文献调研和目视解译产生的保护性/常规耕作样本点&#xff0c;通过交叉验证方法训练随机森林分类器&#xff0c;生成了2016-2020年全国保护性耕作/常规耕作农田分类数据集。分类代码&#xff1a;0值代表非农田&#xff0c;1值表示第一…

“轻松入门Electron:一步步构建梦想中的桌面软件

在数字化的浪潮中&#xff0c;桌面应用依旧占据着其独特而重要的位置&#xff0c;不论是在企业解决方案、专业工具软件还是个性化应用领域中都是如此。随着技术的演进&#xff0c;创建这些应用的过程已经变得更为简单和可行&#xff0c;尤其是随着Electron等框架的出现。Electr…

k8s中pod组件简介

背景 k8s中pod是很重要的概念&#xff0c;他是k8s调度的最小单位&#xff0c;和我们以往的以docker容器为调度单位是不一样的&#xff0c;那么为什么k8s会以pod为调度单位而不是以更小的docker容器为调度单位呢? pod组件 pod是一个逻辑的概念&#xff0c;其中可以包含多个d…

R语言及其开发环境简介

R语言及其开发环境简介 R 语言历史 R 语言来自 S 语言&#xff0c;是 S 语言的一个变种。S语言由贝尔实验室研究开发&#xff0c;著名的 C 语言、Unix 系统也是贝尔实验室开发的。R 属于 GNU 开源软件&#xff0c;最初发布于1997年&#xff0c;实现了与 S 语言基本相同的功能…

【异常处理】sbt构建Chisel库时出现extracting structure failed:build status:error的解决办法

文章目录 报错背景&#xff1a;解决思路&#xff1a;①IDEA中配置本地的SBT进行下载②更改下载源为华为的镜像站1. 修改sbtconfig.txt2. 增加repositories文件 ③查看报错信息 总结整理的Scala-Chisel-Chiseltest版本信息对应表 报错背景&#xff1a; 最近在写Chisel时&#x…

K8s-MySQL主从集群

K8s-MySQL主从集群 引言 该案例代码均可从https://github.com/WeiXiao-Hyy/k8s_example 获取&#xff0c;欢迎Star&#xff01; 需求 一个“主从复制”的MySQL集群有一个主节点Master有多个从节点Slave从节点需要能水平扩展所以写操作只能在主节点上执行读操作可以在所有节点…

Java组合模式在项目中使用场景

组合模式 一、介绍二、人员管理系统使用案例 一、介绍 将对象组合成树形结构以表示“部分-整体”的层次关系。组合模式使得用户对单个对象和组合对象的使用都是相同的。 组合模式主要由以下几个部分构成&#xff1a; Component&#xff08;组件&#xff09;&#xff1a;定义…

Navicat Premium:掌控数据库的强大工具

在当今数字化的时代&#xff0c;数据管理变得越来越重要。无论您是数据库管理员、开发人员还是普通用户&#xff0c;找到一个高效、易用的数据库管理工具是至关重要的。Navicat Premium for Mac/Win 就是这样一款强大的多协议数据库管理工具&#xff0c;它将为您的数据库管理体…

突破编程_C++_设计模式(组合模式)

1 组合模式的基本概念 C中的组合模式是一种对象结构型模式&#xff0c;它将多个对象组合成树形结构&#xff0c;以表示具有整体-部分关系的层次结构。在这个模式中&#xff0c;对单个对象&#xff08;叶子对象&#xff09;与组合对象&#xff08;容器对象&#xff09;的使用具…