Python3.8.10每日一练(汉诺塔)

import tkinter as tk
from tkinter import messagebox, ttk
import time


class HanoiTower:
    def __init__(self, root):
        self.root = root
        self.root.title("汉诺塔")
        self.root.geometry("800x600")
        self.root.resizable(False, False)

        # 设置中文字体
        self.font = ('SimHei', 12)

        # 游戏参数
        self.num_disks = 3
        self.towers = [[], [], []]
        self.selected_tower = None
        self.animation_speed = 0.05  # 动画速度
        self.is_animating = False
        self.moves = 0

        # 创建界面
        self.create_widgets()
        self.reset_game()

    def create_widgets(self):
        # 顶部控制面板
        control_frame = ttk.Frame(self.root)
        control_frame.pack(fill=tk.X, padx=10, pady=10)

        ttk.Label(control_frame, text="圆盘数量:", font=self.font).pack(side=tk.LEFT, padx=5)
        self.disk_var = tk.IntVar(value=self.num_disks)
        disk_scale = ttk.Scale(control_frame, from_=3, to=8, orient=tk.HORIZONTAL,
                               variable=self.disk_var, command=self.on_disk_scale)
        disk_scale.pack(side=tk.LEFT, padx=5)
        self.disk_label = ttk.Label(control_frame, text=f"{self.num_disks}", font=self.font)
        self.disk_label.pack(side=tk.LEFT, padx=5)

        ttk.Label(control_frame, text="移动次数:", font=self.font).pack(side=tk.LEFT, padx=20)
        self.move_label = ttk.Label(control_frame, text="0", font=self.font)
        self.move_label.pack(side=tk.LEFT, padx=5)

        ttk.Button(control_frame, text="重置游戏", command=self.reset_game,
                   style='Accent.TButton').pack(side=tk.RIGHT, padx=5)
        ttk.Button(control_frame, text="自动演示", command=self.auto_solve,
                   style='Accent.TButton').pack(side=tk.RIGHT, padx=5)

        # 游戏画布
        self.canvas = tk.Canvas(self.root, bg="white", width=800, height=500)
        self.canvas.pack(pady=10)

        # 绑定点击事件
        self.canvas.bind("<Button-1>", self.on_canvas_click)

        # 设置主题样式
        style = ttk.Style()
        style.configure('Accent.TButton', font=self.font)

    def on_disk_scale(self, value):
        self.num_disks = int(float(value))
        self.disk_label.config(text=f"{self.num_disks}")

    def reset_game(self):
        if self.is_animating:
            return

        # 初始化塔
        self.towers = [[i for i in range(self.num_disks, 0, -1)], [], []]
        self.selected_tower = None
        self.moves = 0
        self.move_label.config(text="0")
        self.draw_towers()

    def draw_towers(self):
        self.canvas.delete("all")

        # 绘制底座
        base_y = 450
        self.canvas.create_rectangle(50, base_y, 750, base_y + 20, fill="brown")

        # 绘制三个塔
        tower_width = 20
        tower_height = 200
        tower_spacing = 200

        for i in range(3):
            tower_x = 200 + i * tower_spacing
            self.canvas.create_rectangle(tower_x - tower_width / 2, base_y - tower_height,
                                         tower_x + tower_width / 2, base_y, fill="gray")

            # 绘制选中状态
            if i == self.selected_tower:
                self.canvas.create_oval(tower_x - 40, base_y + 30,
                                        tower_x + 40, base_y + 50, fill="yellow")

        # 绘制圆盘
        disk_height = 20
        max_disk_width = 160

        for i in range(3):
            tower_x = 200 + i * tower_spacing
            for j, disk_size in enumerate(self.towers[i]):
                disk_width = disk_size * (max_disk_width / self.num_disks)
                disk_y = base_y - (j + 1) * disk_height
                self.canvas.create_rectangle(tower_x - disk_width / 2, disk_y,
                                             tower_x + disk_width / 2, disk_y + disk_height,
                                             fill=self.get_disk_color(disk_size))

    def get_disk_color(self, disk_size):
        colors = ["#FF6B6B", "#FFD166", "#06D6A0", "#118AB2", "#073B4C",
                  "#EF476F", "#FFD166", "#06D6A0"]
        return colors[(disk_size - 1) % len(colors)]

    def on_canvas_click(self, event):
        if self.is_animating:
            return

        # 确定点击的塔
        tower_x_positions = [200, 400, 600]
        tower_spacing = 150
        clicked_tower = None

        for i, x in enumerate(tower_x_positions):
            if x - tower_spacing < event.x < x + tower_spacing:
                clicked_tower = i
                break

        if clicked_tower is not None:
            self.process_tower_click(clicked_tower)

    def process_tower_click(self, tower_index):
        if self.selected_tower is None:
            # 如果没有选中的塔,且当前塔有圆盘,则选中它
            if self.towers[tower_index]:
                self.selected_tower = tower_index
                self.draw_towers()
        else:
            # 如果已经选中了一个塔,尝试移动圆盘
            if self.is_valid_move(self.selected_tower, tower_index):
                self.move_disk(self.selected_tower, tower_index)
                self.moves += 1
                self.move_label.config(text=str(self.moves))

                # 检查游戏是否完成
                if self.check_win():
                    messagebox.showinfo("恭喜", f"你用了{self.moves}步完成了汉诺塔!")

            self.selected_tower = None
            self.draw_towers()

    def is_valid_move(self, from_tower, to_tower):
        if not self.towers[from_tower]:
            return False

        if not self.towers[to_tower]:
            return True

        return self.towers[from_tower][-1] < self.towers[to_tower][-1]

    def move_disk(self, from_tower, to_tower):
        disk = self.towers[from_tower].pop()
        self.towers[to_tower].append(disk)
        self.animate_move(disk, from_tower, to_tower)

    def animate_move(self, disk, from_tower, to_tower):
        self.is_animating = True
        self.root.update()

        # 圆盘动画参数
        base_y = 450
        disk_height = 20
        max_disk_width = 160
        disk_width = disk * (max_disk_width / self.num_disks)

        # 从源塔移除圆盘
        self.draw_towers()

        # 获取起始和目标位置
        start_x = 200 + from_tower * 200
        end_x = 200 + to_tower * 200
        start_y = base_y - len(self.towers[from_tower]) * disk_height - disk_height
        end_y = base_y - (len(self.towers[to_tower]) - 1) * disk_height - disk_height

        # 创建动画圆盘
        disk_id = self.canvas.create_rectangle(start_x - disk_width / 2, start_y,
                                               start_x + disk_width / 2, start_y + disk_height,
                                               fill=self.get_disk_color(disk))

        # 动画过程
        # 上升
        while self.canvas.coords(disk_id)[1] > base_y - 250:
            self.canvas.move(disk_id, 0, -5)
            self.root.update()
            time.sleep(self.animation_speed)

        # 水平移动
        direction = 1 if end_x > start_x else -1
        while (direction == 1 and self.canvas.coords(disk_id)[0] < end_x - disk_width / 2) or 
                (direction == -1 and self.canvas.coords(disk_id)[2] > end_x + disk_width / 2):
            self.canvas.move(disk_id, direction * 5, 0)
            self.root.update()
            time.sleep(self.animation_speed)

        # 下降
        while self.canvas.coords(disk_id)[3] < end_y + disk_height:
            self.canvas.move(disk_id, 0, 5)
            self.root.update()
            time.sleep(self.animation_speed)

        # 移除动画圆盘并更新显示
        self.canvas.delete(disk_id)
        self.draw_towers()
        self.is_animating = False

    def check_win(self):
        return len(self.towers[0]) == 0 and len(self.towers[1]) == 0 and len(self.towers[2]) > 0

    def auto_solve(self):
        if self.is_animating:
            return

        # 复制当前游戏状态
        original_towers = [tower.copy() for tower in self.towers]
        original_selected = self.selected_tower
        original_moves = self.moves

        # 重置游戏
        self.reset_game()

        # 使用递归方法解决汉诺塔问题
        def hanoi(n, source, target, auxiliary):
            if n == 0:
                return

            hanoi(n - 1, source, auxiliary, target)
            self.move_disk(source, target)
            self.root.update()
            time.sleep(0.5)
            hanoi(n - 1, auxiliary, target, source)

        # 开始自动解决
        self.is_animating = True
        hanoi(self.num_disks, 0, 2, 1)
        self.is_animating = False

        # 恢复原始游戏状态
        # self.towers = original_towers
        # self.selected_tower = original_selected
        # self.moves = original_moves
        # self.move_label.config(text=str(self.moves))
        # self.draw_towers()


if __name__ == "__main__":
    root = tk.Tk()
    app = HanoiTower(root)
    root.mainloop()    

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容