import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import random
import math
import time
import json
from collections import deque
from heapq import heappush, heappop
import threading

class Complex3DMaze:
    """3D迷宫生成器"""
    
    def __init__(self, width=15, height=15, depth=3):
        self.width = width
        self.height = height
        self.depth = depth
        self.maze = self._create_empty_maze()
        self.fractal_level = 2
        self.algorithm = "recursive_backtracking_3d"
        
    def _create_empty_maze(self):
        """创建3D迷宫结构"""
        return [[[1 for _ in range(self.width)] 
                for _ in range(self.height)] 
                for _ in range(self.depth)]
    
    def generate_recursive_backtracking_3d(self):
        """3D递归回溯算法"""
        stack = [(0, 0, 0)]
        self.maze[0][0][0] = 0
        directions = [(0, -1, 0), (0, 1, 0), (0, 0, -1), (0, 0, 1), (-1, 0, 0), (1, 0, 0)]
        
        while stack:
            z, y, x = stack[-1]
            random.shuffle(directions)
            
            moved = False
            for dz, dy, dx in directions:
                nz, ny, nx = z + dz*2, y + dy*2, x + dx*2
                
                if (0 <= nz < self.depth and 0 <= ny < self.height and 0 <= nx < self.width and
                    self.maze[nz][ny][nx] == 1):
                    # 打通当前和中间的墙
                    if 0 <= z + dz < self.depth and 0 <= y + dy < self.height and 0 <= x + dx < self.width:
                        self.maze[z + dz][y + dy][x + dx] = 0
                    self.maze[nz][ny][nx] = 0
                    stack.append((nz, ny, nx))
                    moved = True
                    break
            
            if not moved:
                stack.pop()
        
        # 确保起点和终点
        self.maze[0][0][0] = 0
        self.maze[self.depth-1][self.height-1][self.width-1] = 0
        
    def generate_fractal_maze(self, level=None):
        """分形迷宫生成"""
        if level is None:
            level = self.fractal_level
        
        # 基础迷宫
        self.generate_recursive_backtracking_3d()
        
        # 在每一层内部递归生成子迷宫
        for z in range(1, self.depth-1, 2):
            for y in range(2, self.height-4, 4):
                for x in range(2, self.width-4, 4):
                    if random.random() < 0.6 and self.maze[z][y][x] == 0:
                        # 创建小型通道
                        for dz, dy, dx in [(0, 1, 0), (0, 0, 1), (0, 1, 1)]:
                            nz, ny, nx = z, y + dy, x + dx
                            if (0 <= nz < self.depth and 0 <= ny < self.height and 0 <= nx < self.width):
                                self.maze[nz][ny][nx] = 0
    
    def generate_prim_3d(self):
        """3D Prim算法"""
        # 随机起点
        start_z = random.randint(0, self.depth-1)
        start_y = random.randint(0, self.height-1)
        start_x = random.randint(0, self.width-1)
        self.maze[start_z][start_y][start_x] = 0
        
        walls = []
        directions = [(0, -1, 0), (0, 1, 0), (0, 0, -1), (0, 0, 1), (-1, 0, 0), (1, 0, 0)]
        
        # 添加初始边界
        for dz, dy, dx in directions:
            nz, ny, nx = start_z + dz, start_y + dy, start_x + dx
            if 0 <= nz < self.depth and 0 <= ny < self.height and 0 <= nx < self.width:
                walls.append((nz, ny, nx, start_z, start_y, start_x))
        
        while walls:
            z, y, x, pz, py, px = random.choice(walls)
            walls.remove((z, y, x, pz, py, px))
            
            if self.maze[z][y][x] == 1:
                # 计算相邻的已访问单元格数量
                visited_neighbors = 0
                for dz, dy, dx in directions:
                    nz, ny, nx = z + dz, y + dy, x + dx
                    if (0 <= nz < self.depth and 0 <= ny < self.height and 0 <= nx < self.width and
                        self.maze[nz][ny][nx] == 0):
                        visited_neighbors += 1
                
                if visited_neighbors == 1:
                    # 打通墙壁
                    self.maze[z][y][x] = 0
                    # 打通中间的墙
                    mz, my, mx = (z + pz) // 2, (y + py) // 2, (x + px) // 2
                    if 0 <= mz < self.depth and 0 <= my < self.height and 0 <= mx < self.width:
                        self.maze[mz][my][mx] = 0
                    
                    # 添加新的边界
                    for dz, dy, dx in directions:
                        nz, ny, nx = z + dz, y + dy, x + dx
                        if (0 <= nz < self.depth and 0 <= ny < self.height and 0 <= nx < self.width and
                            self.maze[nz][ny][nx] == 1):
                            walls.append((nz, ny, nx, z, y, x))
    
    def generate_recursive_division(self):
        """递归分割算法"""
        # 初始化所有为通路
        for z in range(self.depth):
            for y in range(self.height):
                for x in range(self.width):
                    self.maze[z][y][x] = 0
        
        # 添加外边界
        for z in range(self.depth):
            for y in range(self.height):
                self.maze[z][y][0] = 1
                self.maze[z][y][self.width-1] = 1
            for x in range(self.width):
                self.maze[z][0][x] = 1
                self.maze[z][self.height-1][x] = 1
        for y in range(self.height):
            for x in range(self.width):
                self.maze[0][y][x] = 1
                self.maze[self.depth-1][y][x] = 1
        
        # 递归分割
        self._divide(1, 1, 1, self.depth-2, self.height-2, self.width-2)
        
        # 确保起点和终点
        self.maze[0][1][1] = 0
        self.maze[self.depth-1][self.height-2][self.width-2] = 0
    
    def _divide(self, z1, y1, x1, z2, y2, x2):
        """递归分割子函数"""
        if z2 - z1 < 2 or y2 - y1 < 2 or x2 - x1 < 2:
            return
        
        # 随机选择分割面
        axis = random.choice([0, 1, 2])
        
        if axis == 0:  # 水平分割（z轴）
            split_z = random.randrange(z1 + 1, z2, 2)
            for y in range(y1, y2 + 1):
                for x in range(x1, x2 + 1):
                    self.maze[split_z][y][x] = 1
            
            # 随机打通一个洞
            hole_y = random.randrange(y1, y2 + 1, 2)
            hole_x = random.randrange(x1, x2 + 1, 2)
            self.maze[split_z][hole_y][hole_x] = 0
            
            self._divide(z1, y1, x1, split_z-1, y2, x2)
            self._divide(split_z+1, y1, x1, z2, y2, x2)
        
        elif axis == 1:  # 垂直分割（y轴）
            split_y = random.randrange(y1 + 1, y2, 2)
            for z in range(z1, z2 + 1):
                for x in range(x1, x2 + 1):
                    self.maze[z][split_y][x] = 1
            
            hole_z = random.randrange(z1, z2 + 1, 2)
            hole_x = random.randrange(x1, x2 + 1, 2)
            self.maze[hole_z][split_y][hole_x] = 0
            
            self._divide(z1, y1, x1, z2, split_y-1, x2)
            self._divide(z1, split_y+1, x1, z2, y2, x2)
        
        else:  # 深度分割（x轴）
            split_x = random.randrange(x1 + 1, x2, 2)
            for z in range(z1, z2 + 1):
                for y in range(y1, y2 + 1):
                    self.maze[z][y][split_x] = 1
            
            hole_z = random.randrange(z1, z2 + 1, 2)
            hole_y = random.randrange(y1, y2 + 1, 2)
            self.maze[hole_z][hole_y][split_x] = 0
            
            self._divide(z1, y1, x1, z2, y2, split_x-1)
            self._divide(z1, y1, split_x+1, z2, y2, x2)
    
    def generate_maze(self, algorithm=None):
        """生成迷宫"""
        if algorithm:
            self.algorithm = algorithm
        
        self.maze = self._create_empty_maze()
        
        if algorithm == "recursive_backtracking_3d":
            self.generate_recursive_backtracking_3d()
        elif algorithm == "fractal":
            self.generate_fractal_maze()
        elif algorithm == "prim_3d":
            self.generate_prim_3d()
        elif algorithm == "recursive_division":
            self.generate_recursive_division()
        else:
            self.generate_recursive_backtracking_3d()

class PathFinder3D:
    """3D路径查找器"""
    
    @staticmethod
    def bfs_3d(maze, start, end):
        """3D BFS"""
        depth, height, width = len(maze), len(maze[0]), len(maze[0][0])
        queue = deque([start])
        visited = {start: None}
        
        while queue:
            z, y, x = queue.popleft()
            
            if (z, y, x) == end:
                # 重建路径
                path = []
                while (z, y, x) is not None:
                    path.append((z, y, x))
                    if visited[(z, y, x)] is not None:
                        z, y, x = visited[(z, y, x)]
                    else:
                        break
                return list(reversed(path))
            
            for dz, dy, dx in [(0, -1, 0), (0, 1, 0), (0, 0, -1), (0, 0, 1), (-1, 0, 0), (1, 0, 0)]:
                nz, ny, nx = z + dz, y + dy, x + dx
                if (0 <= nz < depth and 0 <= ny < height and 0 <= nx < width and
                    maze[nz][ny][nx] == 0 and (nz, ny, nx) not in visited):
                    queue.append((nz, ny, nx))
                    visited[(nz, ny, nx)] = (z, y, x)
        
        return []
    
    @staticmethod
    def astar_3d(maze, start, end):
        """3D A*算法"""
        depth, height, width = len(maze), len(maze[0]), len(maze[0][0])
        
        def heuristic(a, b):
            return abs(a[0]-b[0]) + abs(a[1]-b[1]) + abs(a[2]-b[2])
        
        open_set = []
        heappush(open_set, (0, start))
        came_from = {start: None}
        g_score = {start: 0}
        
        while open_set:
            _, current = heappop(open_set)
            
            if current == end:
                path = []
                while current is not None:
                    path.append(current)
                    current = came_from[current]
                return list(reversed(path))
            
            z, y, x = current
            for dz, dy, dx in [(0, -1, 0), (0, 1, 0), (0, 0, -1), (0, 0, 1), (-1, 0, 0), (1, 0, 0)]:
                nz, ny, nx = z + dz, y + dy, x + dx
                neighbor = (nz, ny, nx)
                
                if 0 <= nz < depth and 0 <= ny < height and 0 <= nx < width and maze[nz][ny][nx] == 0:
                    tentative_g = g_score[current] + 1
                    
                    if neighbor not in g_score or tentative_g < g_score[neighbor]:
                        came_from[neighbor] = current
                        g_score[neighbor] = tentative_g
                        f_score = tentative_g + heuristic(neighbor, end)
                        heappush(open_set, (f_score, neighbor))
        
        return []

class FractalMazeApp:
    """主应用程序"""
    
    def __init__(self, root):
        self.root = root
        self.root.title("3D分形迷宫生成与可视化系统")
        self.root.geometry("1200x800")
        
        # 初始化变量
        self.maze_width = tk.IntVar(value=10)
        self.maze_height = tk.IntVar(value=10)
        self.maze_depth = tk.IntVar(value=3)
        self.cell_size = tk.IntVar(value=20)
        self.fractal_level = tk.IntVar(value=2)
        self.algorithm = tk.StringVar(value="recursive_backtracking_3d")
        self.path_algorithm = tk.StringVar(value="astar_3d")
        self.animation_speed = tk.DoubleVar(value=0.1)
        self.show_grid = tk.BooleanVar(value=True)
        self.show_path = tk.BooleanVar(value=True)
        self.show_3d = tk.BooleanVar(value=True)
        self.auto_solve = tk.BooleanVar(value=False)
        
        # 颜色设置 - 使用纯色，不带透明度
        self.colors = {
            'wall': '#2C3E50',
            'path': '#34495E',
            'start': '#27AE60',
            'end': '#E74C3C',
            'solution': '#9B59B6',
            'player': '#3498DB',
            'visited': '#2980B9',
            'bg': '#1C2833',
            'grid': '#566573',
            'text': '#ECF0F1',
            'light_start': '#52BE80',  # 更亮的起点颜色
            'light_end': '#EC7063',    # 更亮的终点颜色
            'light_player': '#5DADE2'  # 更亮的玩家颜色
        }
        
        # 创建界面
        self.create_widgets()
        
        # 初始化迷宫
        self.maze_gen = Complex3DMaze(
            self.maze_width.get(),
            self.maze_height.get(),
            self.maze_depth.get()
        )
        
        # 游戏状态
        self.player_pos = (0, 0, 0)
        self.goal_pos = (0, 0, 0)
        self.visited = set()
        self.solution_path = []
        self.is_animating = False
        
        # 视角参数
        self.camera_angle_x = 30
        self.camera_angle_y = 45
        self.camera_zoom = 1.0
        self.camera_offset_x = 0
        self.camera_offset_y = 0
        self.dragging = False
        self.last_mouse_x = 0
        self.last_mouse_y = 0
        
        # 生成初始迷宫
        self.generate_maze()
        
        # 绑定事件
        self.bind_events()
    
    def create_widgets(self):
        """创建界面组件"""
        # 主容器
        main_frame = ttk.Frame(self.root)
        main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
        
        # 左侧控制面板
        control_frame = ttk.Frame(main_frame, width=250)
        control_frame.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 5))
        control_frame.pack_propagate(False)
        
        # 标题
        title = tk.Label(control_frame, text="3D分形迷宫", 
                        font=('Arial', 16, 'bold'),
                        bg='#2C3E50', fg='#ECF0F1')
        title.pack(fill=tk.X, pady=(0, 10))
        
        # 迷宫设置面板
        maze_settings = tk.LabelFrame(control_frame, text="迷宫设置", 
                                     bg='#34495E', fg='#ECF0F1',
                                     font=('Arial', 10, 'bold'))
        maze_settings.pack(fill=tk.X, pady=(0, 10), padx=5)
        
        # 宽度设置
        tk.Label(maze_settings, text="宽度:", bg='#34495E', fg='#ECF0F1').grid(
            row=0, column=0, sticky=tk.W, pady=2, padx=5)
        width_scale = tk.Scale(maze_settings, from_=5, to=20, variable=self.maze_width, 
                             orient=tk.HORIZONTAL, length=150,
                             bg='#34495E', fg='#ECF0F1', highlightthickness=0)
        width_scale.grid(row=0, column=1, pady=2, padx=5)
        
        # 高度设置
        tk.Label(maze_settings, text="高度:", bg='#34495E', fg='#ECF0F1').grid(
            row=1, column=0, sticky=tk.W, pady=2, padx=5)
        height_scale = tk.Scale(maze_settings, from_=5, to=20, variable=self.maze_height,
                              orient=tk.HORIZONTAL, length=150,
                              bg='#34495E', fg='#ECF0F1', highlightthickness=0)
        height_scale.grid(row=1, column=1, pady=2, padx=5)
        
        # 深度设置
        tk.Label(maze_settings, text="深度:", bg='#34495E', fg='#ECF0F1').grid(
            row=2, column=0, sticky=tk.W, pady=2, padx=5)
        depth_scale = tk.Scale(maze_settings, from_=2, to=6, variable=self.maze_depth,
                             orient=tk.HORIZONTAL, length=150,
                             bg='#34495E', fg='#ECF0F1', highlightthickness=0)
        depth_scale.grid(row=2, column=1, pady=2, padx=5)
        
        # 单元格大小
        tk.Label(maze_settings, text="单元格大小:", bg='#34495E', fg='#ECF0F1').grid(
            row=3, column=0, sticky=tk.W, pady=2, padx=5)
        cell_scale = tk.Scale(maze_settings, from_=10, to=40, variable=self.cell_size,
                            orient=tk.HORIZONTAL, length=150,
                            bg='#34495E', fg='#ECF0F1', highlightthickness=0)
        cell_scale.grid(row=3, column=1, pady=2, padx=5)
        
        # 分形级别
        tk.Label(maze_settings, text="分形级别:", bg='#34495E', fg='#ECF0F1').grid(
            row=4, column=0, sticky=tk.W, pady=2, padx=5)
        fractal_scale = tk.Scale(maze_settings, from_=1, to=4, variable=self.fractal_level,
                               orient=tk.HORIZONTAL, length=150,
                               bg='#34495E', fg='#ECF0F1', highlightthickness=0)
        fractal_scale.grid(row=4, column=1, pady=2, padx=5)
        
        # 算法选择
        algo_frame = tk.LabelFrame(control_frame, text="算法选择", 
                                  bg='#34495E', fg='#ECF0F1',
                                  font=('Arial', 10, 'bold'))
        algo_frame.pack(fill=tk.X, pady=(0, 10), padx=5)
        
        algorithms = [
            ("3D递归回溯", "recursive_backtracking_3d"),
            ("3D Prim算法", "prim_3d"),
            ("递归分割", "recursive_division"),
            ("分形迷宫", "fractal")
        ]
        
        for i, (text, value) in enumerate(algorithms):
            rb = tk.Radiobutton(algo_frame, text=text, variable=self.algorithm,
                              value=value, command=self.generate_maze,
                              bg='#34495E', fg='#ECF0F1', selectcolor='#2C3E50',
                              activebackground='#34495E', activeforeground='#ECF0F1')
            rb.grid(row=i, column=0, sticky=tk.W, pady=2, padx=10)
        
        # 路径算法
        path_frame = tk.LabelFrame(control_frame, text="路径算法", 
                                  bg='#34495E', fg='#ECF0F1',
                                  font=('Arial', 10, 'bold'))
        path_frame.pack(fill=tk.X, pady=(0, 10), padx=5)
        
        tk.Radiobutton(path_frame, text="3D A*算法", variable=self.path_algorithm,
                      value="astar_3d", bg='#34495E', fg='#ECF0F1', selectcolor='#2C3E50',
                      activebackground='#34495E', activeforeground='#ECF0F1').grid(
                      row=0, column=0, sticky=tk.W, pady=2, padx=10)
        tk.Radiobutton(path_frame, text="3D BFS", variable=self.path_algorithm,
                      value="bfs_3d", bg='#34495E', fg='#ECF0F1', selectcolor='#2C3E50',
                      activebackground='#34495E', activeforeground='#ECF0F1').grid(
                      row=1, column=0, sticky=tk.W, pady=2, padx=10)
        
        # 控制按钮
        btn_frame = tk.Frame(control_frame, bg='#2C3E50')
        btn_frame.pack(fill=tk.X, pady=(0, 10))
        
        button_config = {
            'bg': '#3498DB', 'fg': 'white', 'activebackground': '#2980B9',
            'activeforeground': 'white', 'font': ('Arial', 10)
        }
        
        tk.Button(btn_frame, text="生成迷宫 (R)", command=self.generate_maze,
                 **button_config).pack(fill=tk.X, pady=2, padx=5)
        tk.Button(btn_frame, text="查找路径 (P)", command=self.find_path,
                 **button_config).pack(fill=tk.X, pady=2, padx=5)
        tk.Button(btn_frame, text="动画演示 (A)", command=self.animate_solution,
                 **button_config).pack(fill=tk.X, pady=2, padx=5)
        tk.Button(btn_frame, text="重置玩家 (Space)", command=self.reset_player,
                 **button_config).pack(fill=tk.X, pady=2, padx=5)
        
        # 显示选项
        display_frame = tk.LabelFrame(control_frame, text="显示选项", 
                                     bg='#34495E', fg='#ECF0F1',
                                     font=('Arial', 10, 'bold'))
        display_frame.pack(fill=tk.X, pady=(0, 10), padx=5)
        
        tk.Checkbutton(display_frame, text="显示网格", variable=self.show_grid,
                      command=self.draw_maze, bg='#34495E', fg='#ECF0F1',
                      selectcolor='#2C3E50', activebackground='#34495E',
                      activeforeground='#ECF0F1').grid(row=0, column=0, sticky=tk.W, pady=2, padx=10)
        tk.Checkbutton(display_frame, text="显示路径", variable=self.show_path,
                      command=self.draw_maze, bg='#34495E', fg='#ECF0F1',
                      selectcolor='#2C3E50', activebackground='#34495E',
                      activeforeground='#ECF0F1').grid(row=1, column=0, sticky=tk.W, pady=2, padx=10)
        tk.Checkbutton(display_frame, text="3D视图", variable=self.show_3d,
                      command=self.draw_maze, bg='#34495E', fg='#ECF0F1',
                      selectcolor='#2C3E50', activebackground='#34495E',
                      activeforeground='#ECF0F1').grid(row=2, column=0, sticky=tk.W, pady=2, padx=10)
        
        # 动画速度
        tk.Label(display_frame, text="动画速度:", bg='#34495E', fg='#ECF0F1').grid(
            row=3, column=0, sticky=tk.W, pady=2, padx=10)
        tk.Scale(display_frame, from_=0.01, to=1.0, variable=self.animation_speed,
                orient=tk.HORIZONTAL, length=150,
                bg='#34495E', fg='#ECF0F1', highlightthickness=0).grid(
                row=4, column=0, pady=2, padx=10)
        
        # 视角控制
        view_frame = tk.LabelFrame(control_frame, text="视角控制", 
                                  bg='#34495E', fg='#ECF0F1',
                                  font=('Arial', 10, 'bold'))
        view_frame.pack(fill=tk.X, pady=(0, 10), padx=5)
        
        view_btn_config = {
            'bg': '#7F8C8D', 'fg': 'white', 'activebackground': '#95A5A6',
            'font': ('Arial', 9)
        }
        
        tk.Button(view_frame, text="左旋转", command=lambda: self.rotate_camera(0, 5),
                 **view_btn_config).grid(row=0, column=0, padx=2, pady=2)
        tk.Button(view_frame, text="右旋转", command=lambda: self.rotate_camera(0, -5),
                 **view_btn_config).grid(row=0, column=1, padx=2, pady=2)
        tk.Button(view_frame, text="上旋转", command=lambda: self.rotate_camera(5, 0),
                 **view_btn_config).grid(row=1, column=0, padx=2, pady=2)
        tk.Button(view_frame, text="下旋转", command=lambda: self.rotate_camera(-5, 0),
                 **view_btn_config).grid(row=1, column=1, padx=2, pady=2)
        tk.Button(view_frame, text="放大", command=self.zoom_in,
                 **view_btn_config).grid(row=2, column=0, padx=2, pady=2)
        tk.Button(view_frame, text="缩小", command=self.zoom_out,
                 **view_btn_config).grid(row=2, column=1, padx=2, pady=2)
        tk.Button(view_frame, text="重置视角", command=self.reset_camera,
                 **view_btn_config).grid(row=3, column=0, columnspan=2, sticky=tk.EW, padx=2, pady=2)
        
        # 统计信息
        stats_frame = tk.LabelFrame(control_frame, text="统计信息", 
                                   bg='#34495E', fg='#ECF0F1',
                                   font=('Arial', 10, 'bold'))
        stats_frame.pack(fill=tk.X, padx=5)
        
        self.stats_labels = {}
        stats = [("尺寸", "0×0×0"), ("路径长度", "0"), ("已访问", "0"), ("复杂度", "0%")]
        
        for i, (name, default) in enumerate(stats):
            tk.Label(stats_frame, text=name + ":", bg='#34495E', fg='#ECF0F1').grid(
                row=i, column=0, sticky=tk.W, pady=2, padx=10)
            self.stats_labels[name] = tk.Label(stats_frame, text=default, 
                                              bg='#34495E', fg='#ECF0F1')
            self.stats_labels[name].grid(row=i, column=1, sticky=tk.E, padx=10)
        
        # 右侧画布
        canvas_frame = tk.Frame(main_frame, bg='#1C2833')
        canvas_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        # 状态栏
        self.status = tk.Label(canvas_frame, text="就绪", 
                              bg='#2C3E50', fg='#ECF0F1',
                              font=('Arial', 10), anchor=tk.W, padx=10)
        self.status.pack(side=tk.BOTTOM, fill=tk.X)
        
        # 主画布
        self.canvas = tk.Canvas(canvas_frame, bg=self.colors['bg'], 
                               highlightthickness=0, cursor="crosshair")
        self.canvas.pack(fill=tk.BOTH, expand=True)
        
        # 控制提示
        hint_frame = tk.Frame(canvas_frame, bg='#1C2833')
        hint_frame.pack(side=tk.BOTTOM, fill=tk.X, pady=(0, 5))
        
        hint_text = "控制提示: WASD/方向键移动 | Q/E上下层 | 鼠标拖拽旋转 | 滚轮缩放"
        tk.Label(hint_frame, text=hint_text, bg='#1C2833', fg='#95A5A6',
                font=('Arial', 9)).pack()
    
    def bind_events(self):
        """绑定事件"""
        self.root.bind('<KeyPress>', self.on_key_press)
        self.root.bind('<r>', lambda e: self.generate_maze())
        self.root.bind('<R>', lambda e: self.generate_maze())
        self.root.bind('<p>', lambda e: self.find_path())
        self.root.bind('<P>', lambda e: self.find_path())
        self.root.bind('<a>', lambda e: self.animate_solution())
        self.root.bind('<A>', lambda e: self.animate_solution())
        self.root.bind('<space>', lambda e: self.reset_player())
        
        self.canvas.bind('<ButtonPress-1>', self.on_mouse_down)
        self.canvas.bind('<B1-Motion>', self.on_mouse_drag)
        self.canvas.bind('<ButtonRelease-1>', self.on_mouse_up)
        self.canvas.bind('<MouseWheel>', self.on_mouse_wheel)
        self.canvas.bind('<Configure>', self.on_resize)
    
    def update_maze(self):
        """更新迷宫尺寸"""
        self.maze_gen = Complex3DMaze(
            self.maze_width.get(),
            self.maze_height.get(),
            self.maze_depth.get()
        )
        self.generate_maze()
    
    def generate_maze(self):
        """生成迷宫"""
        self.status.config(text="生成迷宫中...")
        self.root.update()
        
        self.maze_gen.fractal_level = self.fractal_level.get()
        self.maze_gen.generate_maze(self.algorithm.get())
        
        # 设置起点和终点
        self.player_pos = (0, 0, 0)
        self.goal_pos = (self.maze_gen.depth-1, self.maze_gen.height-1, self.maze_gen.width-1)
        self.visited = set([self.player_pos])
        self.solution_path = []
        
        # 确保起点和终点是通路
        self.maze_gen.maze[self.player_pos[0]][self.player_pos[1]][self.player_pos[2]] = 0
        self.maze_gen.maze[self.goal_pos[0]][self.goal_pos[1]][self.goal_pos[2]] = 0
        
        self.draw_maze()
        self.update_stats()
        self.status.config(text="迷宫生成完成")
    
    def draw_maze(self):
        """绘制迷宫"""
        self.canvas.delete("all")
        
        if not hasattr(self, 'maze_gen') or not self.maze_gen.maze:
            return
        
        width = self.canvas.winfo_width()
        height = self.canvas.winfo_height()
        
        if width <= 1 or height <= 1:
            return
        
        # 计算3D投影
        def project(x, y, z):
            # 转换为3D坐标
            x3d = x - self.maze_gen.width / 2
            y3d = y - self.maze_gen.height / 2
            z3d = z - self.maze_gen.depth / 2
            
            # 应用旋转
            angle_x = math.radians(self.camera_angle_x)
            angle_y = math.radians(self.camera_angle_y)
            
            # 绕Y轴旋转
            x_rot = x3d * math.cos(angle_y) - z3d * math.sin(angle_y)
            z_rot = x3d * math.sin(angle_y) + z3d * math.cos(angle_y)
            
            # 绕X轴旋转
            y_rot = y3d * math.cos(angle_x) - z_rot * math.sin(angle_x)
            z_final = y3d * math.sin(angle_x) + z_rot * math.cos(angle_x)
            
            # 透视投影
            scale = 300 * self.camera_zoom
            fov = 500
            factor = fov / (fov + z_final) if fov + z_final != 0 else 1
            
            x_proj = x_rot * factor * scale + width / 2 + self.camera_offset_x
            y_proj = y_rot * factor * scale + height / 2 + self.camera_offset_y
            
            return x_proj, y_proj
        
        cell_size = self.cell_size.get()
        
        # 绘制墙壁
        for z in range(self.maze_gen.depth):
            for y in range(self.maze_gen.height):
                for x in range(self.maze_gen.width):
                    if self.maze_gen.maze[z][y][x] == 1:  # 墙
                        # 绘制3D立方体
                        vertices = []
                        for dz, dy, dx in [(0,0,0), (0,0,1), (0,1,0), (0,1,1), 
                                          (1,0,0), (1,0,1), (1,1,0), (1,1,1)]:
                            px, py = project(x + dx, y + dy, z + dz)
                            vertices.append((px, py))
                        
                        # 绘制立方体的面
                        faces = [
                            [0,1,3,2],  # 前面
                            [4,5,7,6],  # 后面
                            [0,1,5,4],  # 下面
                            [2,3,7,6],  # 上面
                            [0,2,6,4],  # 左面
                            [1,3,7,5]   # 右面
                        ]
                        
                        # 计算面的深度
                        face_depths = []
                        for face in faces:
                            avg_z = sum(vertices[i][1] for i in face) / 4
                            face_depths.append((avg_z, face))
                        
                        # 从后往前绘制
                        face_depths.sort(key=lambda x: x[0], reverse=True)
                        
                        for depth_val, face in face_depths:
                            face_points = [vertices[i] for i in face]
                            
                            # 计算颜色亮度
                            brightness = 0.3 + 0.7 * (z / self.maze_gen.depth)
                            color = self.adjust_color(self.colors['wall'], brightness)
                            
                            # 绘制面
                            self.canvas.create_polygon(face_points, fill=color, outline='', width=0)
        
        # 绘制网格
        if self.show_grid.get():
            grid_color = self.colors['grid']
            for z in range(self.maze_gen.depth + 1):
                for y in range(self.maze_gen.height + 1):
                    for x in range(self.maze_gen.width + 1):
                        if x < self.maze_gen.width and y < self.maze_gen.height:
                            x1, y1 = project(x, y, z)
                            x2, y2 = project(x+1, y, z)
                            self.canvas.create_line(x1, y1, x2, y2, fill=grid_color, width=1)
                        
                        if x < self.maze_gen.width and z < self.maze_gen.depth:
                            x1, y1 = project(x, y, z)
                            x2, y2 = project(x, y, z+1)
                            self.canvas.create_line(x1, y1, x2, y2, fill=grid_color, width=1)
                        
                        if y < self.maze_gen.height and z < self.maze_gen.depth:
                            x1, y1 = project(x, y, z)
                            x2, y2 = project(x, y+1, z)
                            self.canvas.create_line(x1, y1, x2, y2, fill=grid_color, width=1)
        
        # 绘制起点
        start_x, start_y = project(self.player_pos[2] + 0.5, 
                                 self.player_pos[1] + 0.5, 
                                 self.player_pos[0] + 0.5)
        start_radius = cell_size * 0.4 * self.camera_zoom
        
        # 绘制内圈
        self.canvas.create_oval(start_x - start_radius, start_y - start_radius,
                              start_x + start_radius, start_y + start_radius,
                              fill=self.colors['start'], outline='white', width=2)
        
        # 绘制外圈
        outer_radius = start_radius + 3
        self.canvas.create_oval(start_x - outer_radius, start_y - outer_radius,
                              start_x + outer_radius, start_y + outer_radius,
                              fill='', outline=self.colors['light_start'], width=2)
        
        # 绘制终点
        end_x, end_y = project(self.goal_pos[2] + 0.5, 
                             self.goal_pos[1] + 0.5, 
                             self.goal_pos[0] + 0.5)
        end_radius = cell_size * 0.4 * self.camera_zoom
        
        # 绘制内圈
        self.canvas.create_oval(end_x - end_radius, end_y - end_radius,
                              end_x + end_radius, end_y + end_radius,
                              fill=self.colors['end'], outline='white', width=2)
        
        # 绘制外圈
        outer_radius = end_radius + 3
        self.canvas.create_oval(end_x - outer_radius, end_y - outer_radius,
                              end_x + outer_radius, end_y + outer_radius,
                              fill='', outline=self.colors['light_end'], width=2)
        
        # 绘制访问路径
        if len(self.visited) > 1:
            points = []
            for z, y, x in self.visited:
                px, py = project(x + 0.5, y + 0.5, z + 0.5)
                points.extend([px, py])
            
            if len(points) >= 4:
                self.canvas.create_line(points, fill=self.colors['visited'], 
                                       width=2, smooth=True, splinesteps=2)
        
        # 绘制解决方案路径
        if self.show_path.get() and self.solution_path and len(self.solution_path) > 1:
            points = []
            for z, y, x in self.solution_path:
                px, py = project(x + 0.5, y + 0.5, z + 0.5)
                points.extend([px, py])
            
            if len(points) >= 4:
                self.canvas.create_line(points, fill=self.colors['solution'], 
                                       width=3, smooth=True, splinesteps=2)
        
        # 绘制玩家
        player_x, player_y = project(self.player_pos[2] + 0.5, 
                                   self.player_pos[1] + 0.5, 
                                   self.player_pos[0] + 0.5)
        player_radius = cell_size * 0.3 * self.camera_zoom
        
        # 绘制内圈
        self.canvas.create_oval(player_x - player_radius, player_y - player_radius,
                              player_x + player_radius, player_y + player_radius,
                              fill=self.colors['player'], outline='white', width=2)
        
        # 绘制外圈
        outer_radius = player_radius + 4
        self.canvas.create_oval(player_x - outer_radius, player_y - outer_radius,
                              player_x + outer_radius, player_y + outer_radius,
                              fill='', outline=self.colors['light_player'], width=2)
        
        # 绘制坐标轴
        self.draw_coordinate_axes(width, height)
        
        # 绘制信息文字
        info_text = f"视角: X={self.camera_angle_x}° Y={self.camera_angle_y}° 缩放: {self.camera_zoom:.1f}x"
        self.canvas.create_text(10, 20, text=info_text, 
                               anchor=tk.NW, fill=self.colors['text'], 
                               font=('Arial', 10))
        
        # 绘制迷宫信息
        maze_info = f"迷宫: {self.maze_gen.width}×{self.maze_gen.height}×{self.maze_gen.depth}"
        self.canvas.create_text(width - 10, 20, text=maze_info,
                               anchor=tk.NE, fill=self.colors['text'], 
                               font=('Arial', 10))
        
        # 绘制玩家位置
        player_info = f"玩家: ({self.player_pos[2]}, {self.player_pos[1]}, {self.player_pos[0]})"
        self.canvas.create_text(width - 10, 40, text=player_info,
                               anchor=tk.NE, fill=self.colors['text'], 
                               font=('Arial', 10))
    
    def draw_coordinate_axes(self, width, height):
        """绘制3D坐标轴"""
        center_x, center_y = width / 2, height / 2
        axis_length = 30 * self.camera_zoom
        
        # 定义轴端点
        axes = [
            (center_x, center_y, center_x + axis_length, center_y, 'X', '#FF4444'),  # X轴 (红)
            (center_x, center_y, center_x, center_y - axis_length, 'Y', '#44FF44'),  # Y轴 (绿)
            (center_x, center_y, center_x - axis_length/2, center_y + axis_length/2, 'Z', '#4444FF')  # Z轴 (蓝)
        ]
        
        for x1, y1, x2, y2, label, color in axes:
            self.canvas.create_line(x1, y1, x2, y2, fill=color, width=2, arrow=tk.LAST)
            offset_x = 5 if label == 'X' else -5 if label == 'Z' else 0
            offset_y = -5 if label == 'Y' else 5 if label == 'Z' else 0
            self.canvas.create_text(x2 + offset_x, y2 + offset_y, 
                                  text=label, fill=color, 
                                  font=('Arial', 10, 'bold'))
    
    def adjust_color(self, color, factor):
        """调整颜色亮度"""
        if color.startswith('#'):
            r = int(color[1:3], 16)
            g = int(color[3:5], 16)
            b = int(color[5:7], 16)
            
            r = min(255, int(r * factor))
            g = min(255, int(g * factor))
            b = min(255, int(b * factor))
            
            return f'#{r:02x}{g:02x}{b:02x}'
        return color
    
    def find_path(self):
        """查找路径"""
        if not hasattr(self, 'maze_gen'):
            return
        
        self.status.config(text="计算路径中...")
        self.root.update()
        
        start_time = time.time()
        
        if self.path_algorithm.get() == "bfs_3d":
            self.solution_path = PathFinder3D.bfs_3d(
                self.maze_gen.maze, 
                self.player_pos, 
                self.goal_pos
            )
        else:  # A*
            self.solution_path = PathFinder3D.astar_3d(
                self.maze_gen.maze,
                self.player_pos,
                self.goal_pos
            )
        
        end_time = time.time()
        solve_time = (end_time - start_time) * 1000
        
        if self.solution_path:
            self.status.config(text=f"路径找到! 长度: {len(self.solution_path)} 步, 时间: {solve_time:.1f}ms")
        else:
            self.status.config(text="未找到路径!")
        
        self.draw_maze()
        self.update_stats()
    
    def animate_solution(self):
        """动画演示解决方案"""
        if not self.solution_path or self.is_animating:
            return
        
        self.is_animating = True
        self.animation_thread = threading.Thread(target=self._animate_path)
        self.animation_thread.daemon = True
        self.animation_thread.start()
    
    def _animate_path(self):
        """动画路径线程"""
        for i, pos in enumerate(self.solution_path):
            if not self.is_animating:
                break
            
            self.player_pos = pos
            self.visited.add(pos)
            
            self.root.after(0, self.draw_maze)
            time.sleep(self.animation_speed.get())
        
        self.is_animating = False
        
        if self.player_pos == self.goal_pos:
            self.root.after(0, lambda: self.status.config(text="到达终点!"))
    
    def reset_player(self):
        """重置玩家位置"""
        self.player_pos = (0, 0, 0)
        self.visited = set([self.player_pos])
        self.is_animating = False
        self.draw_maze()
        self.status.config(text="玩家已重置")
    
    def on_key_press(self, event):
        """键盘事件"""
        if self.is_animating:
            return
        
        moved = False
        dz, dy, dx = 0, 0, 0
        
        if event.keysym in ['Up', 'w', 'W']:
            dy = -1
        elif event.keysym in ['Down', 's', 'S']:
            dy = 1
        elif event.keysym in ['Left', 'a', 'A']:
            dx = -1
        elif event.keysym in ['Right', 'd', 'D']:
            dx = 1
        elif event.keysym in ['q', 'Q', 'Page_Up']:
            dz = -1
        elif event.keysym in ['e', 'E', 'Page_Down']:
            dz = 1
        elif event.keysym == 'Escape':
            self.is_animating = False
            return
        
        if dz != 0 or dy != 0 or dx != 0:
            new_z = self.player_pos[0] + dz
            new_y = self.player_pos[1] + dy
            new_x = self.player_pos[2] + dx
            
            if (0 <= new_z < self.maze_gen.depth and 
                0 <= new_y < self.maze_gen.height and 
                0 <= new_x < self.maze_gen.width and
                self.maze_gen.maze[new_z][new_y][new_x] == 0):
                
                self.player_pos = (new_z, new_y, new_x)
                self.visited.add(self.player_pos)
                moved = True
        
        if moved:
            self.draw_maze()
            self.update_stats()
            
            if self.player_pos == self.goal_pos:
                self.status.config(text="恭喜! 你到达了终点!")
    
    def on_mouse_down(self, event):
        """鼠标按下事件"""
        self.dragging = True
        self.last_mouse_x = event.x
        self.last_mouse_y = event.y
    
    def on_mouse_drag(self, event):
        """鼠标拖拽事件"""
        if self.dragging:
            dx = event.x - self.last_mouse_x
            dy = event.y - self.last_mouse_y
            
            self.camera_angle_y += dx * 0.5
            self.camera_angle_x -= dy * 0.5
            
            self.last_mouse_x = event.x
            self.last_mouse_y = event.y
            
            self.draw_maze()
    
    def on_mouse_up(self, event):
        """鼠标释放事件"""
        self.dragging = False
    
    def on_mouse_wheel(self, event):
        """鼠标滚轮事件"""
        if event.delta > 0:
            self.zoom_in()
        else:
            self.zoom_out()
    
    def on_resize(self, event):
        """窗口大小变化事件"""
        self.draw_maze()
    
    def rotate_camera(self, dx, dy):
        """旋转视角"""
        self.camera_angle_x = (self.camera_angle_x + dx) % 360
        self.camera_angle_y = (self.camera_angle_y + dy) % 360
        self.draw_maze()
    
    def zoom_in(self):
        """放大"""
        self.camera_zoom = min(2.0, self.camera_zoom * 1.1)
        self.draw_maze()
    
    def zoom_out(self):
        """缩小"""
        self.camera_zoom = max(0.1, self.camera_zoom * 0.9)
        self.draw_maze()
    
    def reset_camera(self):
        """重置视角"""
        self.camera_angle_x = 30
        self.camera_angle_y = 45
        self.camera_zoom = 1.0
        self.camera_offset_x = 0
        self.camera_offset_y = 0
        self.draw_maze()
    
    def update_stats(self):
        """更新统计信息"""
        if not hasattr(self, 'maze_gen'):
            return
        
        # 计算统计数据
        total_cells = self.maze_gen.width * self.maze_gen.height * self.maze_gen.depth
        wall_cells = 0
        for z in range(self.maze_gen.depth):
            for y in range(self.maze_gen.height):
                wall_cells += sum(self.maze_gen.maze[z][y])
        
        path_cells = total_cells - wall_cells
        
        # 路径长度
        path_length = len(self.solution_path) - 1 if self.solution_path else 0
        
        # 复杂度估算
        complexity = (wall_cells / total_cells) * 100 if total_cells > 0 else 0
        
        # 更新标签
        self.stats_labels["尺寸"].config(
            text=f"{self.maze_gen.width}×{self.maze_gen.height}×{self.maze_gen.depth}"
        )
        self.stats_labels["路径长度"].config(text=str(path_length))
        self.stats_labels["已访问"].config(text=str(len(self.visited)))
        self.stats_labels["复杂度"].config(text=f"{complexity:.1f}%")

# 运行应用程序
if __name__ == "__main__":
    root = tk.Tk()
    app = FractalMazeApp(root)
    root.mainloop()