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; - 高级变频器集成…

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

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

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

目录 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博客

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

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

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

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

【异常处理】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从节点需要能水平扩展所以写操作只能在主节点上执行读操作可以在所有节点…

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

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

HPE ProLiant MicroServer Gen8 RAID 1 扩容、故障恢复探索

声明&#xff1a;本探索只针对HP Microserver Gen8 Smart Array B120i板载阵列卡组建RAID 1&#xff08;不是RAID 10&#xff09;&#xff0c;不同阵列卡结果可能会不同&#xff0c;请谨慎操作&#xff0c;本人不对你操作后导致的任何数据丢失或者损失承担任何责任&#xff0c;…

Java SE入门及基础(29)

第三节 访问修饰符 1. 概念 访问修饰符就是控制访问权限的修饰符号 2. 类的访问修饰符 类的访问修饰符只有两种&#xff1a;public 修饰符和默认修饰符&#xff08;不写修饰符就是默认&#xff09; public 修饰符修饰类表示类可以公开访问。默认修饰符修饰类表示该类只能…

融资项目——网关微服务

1. 网关的路由转发功能 在前后端分离的项目中&#xff0c;网关服务可以将前端的相关请求转发到相应的后端微服务中。 2. 网关微服务的配置 首先需要创建一个网关微服务&#xff0c;并添加依赖。 <!-- 网关 --><dependency><groupId>org.springframework.cl…

FreeRTOS学习笔记-基于stm32(3)中断管理

一、什么是中断 通俗点讲就是让CPU停止当前在做的事&#xff0c;转而去做更紧急的事。 二、中断优先级分组 这个紧急的事也有一个等级之分&#xff0c;优先级越高越先执行。stm32使用中断优先配置寄存器的高4位&#xff0c;共16级的中断优先等级。 stm32的中断优先等级可以分为…

让开源浏览器Chromium正常显示中文

什么是 Chromium &#xff1f; Chromium 是一个开源浏览器项目&#xff0c;旨在为所有用户构建一种更安全、更快、更稳定的网络体验方式。 和老苏之前介绍的 Firefox 的作用是一样的 文章传送门&#xff1a;给群晖安装firefox浏览器 因为是基于 vnc 的应用&#xff0c;感觉资源…

Elasticsearch 通过索引阻塞实现数据保护深入解析

Elasticsearch 是一种强大的搜索和分析引擎&#xff0c;被广泛用于各种应用中&#xff0c;以其强大的全文搜索能力而著称。 不过&#xff0c;在日常管理 Elasticsearch 时&#xff0c;我们经常需要对索引进行保护&#xff0c;以防止数据被意外修改或删除&#xff0c;特别是在进…

动态规划 第一期 背包问题

前言 动态规划难度较高&#xff0c;但是也十分重要&#xff0c;希望大家能够好好的理解&#xff01;&#xff01;&#xff01; 一、背包问题 思维导图&#xff1a; 背包问题(Knapsack problem)是一种组合优化的NP完全问题。问题可以描述为&#xff1a;给定一组物品&#xff…

实现QT中qDebug()的日志重定向

背景&#xff1a; 在项目开发过程中&#xff0c;为了方便分析和排查问题&#xff0c;我们需要将原本输出到控制台的调试信息写入日志文件&#xff0c;进行持久化存储&#xff0c;还可以实现日志分级等。 日志输出格式&#xff1a; 我们需要的格式包括以下内容&#xff1a; 1.…

MySQL死锁详细介绍

首先死锁产生的原因&#xff1a;两个及以上事务争夺资源导致互相等待造成的 比如事务A先修改id为1的数据再去修改id为2的数据&#xff0c;事务B先修改id为2的数据再去修改id为1的数据&#xff0c;因为事务A先拿到id1的锁再去拿id2的锁&#xff0c;而事务B先拿到id2的锁又去拿id…