import tkinter as tk
from tkinter import ttk, messagebox
import random
from collections import deque

CELL_SIZE = 30
MAZE_ROWS = 15
MAZE_COLS = 21
PLAYER_COLOR = "#111111"
WALL_COLOR = "#3333aa"
PATH_COLOR = "#ffffff"
GOAL_COLOR = "#ffd700"
START_COLOR = "#66cc66"

class Maze:
    def __init__(self, rows, cols):
        self.rows = rows if rows % 2 == 1 else rows + 1
        self.cols = cols if cols % 2 == 1 else cols + 1
        self.grid = [[1 for _ in range(self.cols)] for _ in range(self.rows)]
        self.start = (1,1)
        self.goal = (self.rows-2, self.cols-2)
        self.generate()

    def generate(self):
        rows, cols = self.rows, self.cols
        grid = [[1]*cols for _ in range(rows)]
        def carve(r, c):
            grid[r][c] = 0
            dirs = [(0,2),(0,-2),(2,0),(-2,0)]
            random.shuffle(dirs)
            for dr, dc in dirs:
                nr, nc = r+dr, c+dc
                if 1 <= nr < rows-1 and 1 <= nc < cols-1 and grid[nr][nc] == 1:
                    grid[r+dr//2][c+dc//2] = 0
                    carve(nr, nc)
        carve(1,1)
        self.grid = grid
        self.start = (1,1)
        self.goal = (rows-2, cols-2)

    def is_free(self, r, c):
        if 0 <= r < self.rows and 0 <= c < self.cols:
            return self.grid[r][c] == 0
        return False

    def neighbors(self, r, c):
        for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)]:
            nr, nc = r+dr, c+dc
            if 0 <= nr < self.rows and 0 <= nc < self.cols and self.grid[nr][nc] == 0:
                yield (nr, nc)

    def shortest_path(self):
        sr, sc = self.start
        gr, gc = self.goal
        q = deque()
        q.append((sr, sc))
        prev = { (sr,sc): None }
        while q:
            r, c = q.popleft()
            if (r,c) == (gr,gc):
                break
            for nr, nc in self.neighbors(r,c):
                if (nr,nc) not in prev:
                    prev[(nr,nc)] = (r,c)
                    q.append((nr,nc))
        if (gr,gc) not in prev:
            return []
        path = []
        cur = (gr,gc)
        while cur:
            path.append(cur)
            cur = prev[cur]
        path.reverse()
        return path

class MazeApp(tk.Tk):
    def __init__(self, rows=MAZE_ROWS, cols=MAZE_COLS, cell_size=CELL_SIZE):
        super().__init__()
        self.title("罗小黑迷宫")
        self.rows = rows if rows % 2 == 1 else rows + 1
        self.cols = cols if cols % 2 == 1 else cols + 1
        self.cell_size = cell_size
        self.maze = Maze(self.rows, self.cols)
        self.player_pos = self.maze.start
        self.steps = 0
        self.show_path = False
        self.create_widgets()
        self.draw_maze()
        self.bind_events()

    def create_widgets(self):
        top = ttk.Frame(self, padding=6)
        top.pack(side="top", fill="x")
        ttk.Button(top, text="重新生成", command=self.regen).pack(side="left", padx=4)
        ttk.Button(top, text="重置位置", command=self.reset_player).pack(side="left", padx=4)
        self.path_btn = ttk.Button(top, text="显示最短路径", command=self.toggle_path)
        self.path_btn.pack(side="left", padx=4)
        ttk.Label(top, text="步数：").pack(side="left", padx=(12,2))
        self.steps_var = tk.StringVar(value="0")
        ttk.Label(top, textvariable=self.steps_var).pack(side="left")
        canvas_w = self.cols * self.cell_size
        canvas_h = self.rows * self.cell_size
        self.canvas = tk.Canvas(self, width=canvas_w, height=canvas_h, bg="black")
        self.canvas.pack(padx=6, pady=6)

    def bind_events(self):
        self.bind("<Up>", lambda e: self.move(-1,0))
        self.bind("<Down>", lambda e: self.move(1,0))
        self.bind("<Left>", lambda e: self.move(0,-1))
        self.bind("<Right>", lambda e: self.move(0,1))
        self.bind("w", lambda e: self.move(-1,0))
        self.bind("s", lambda e: self.move(1,0))
        self.bind("a", lambda e: self.move(0,-1))
        self.bind("d", lambda e: self.move(0,1))

    def regen(self):
        self.maze.generate()
        self.player_pos = self.maze.start
        self.steps = 0
        self.steps_var.set(str(self.steps))
        self.show_path = False
        self.path_btn.config(text="显示最短路径")
        self.draw_maze()

    def reset_player(self):
        self.player_pos = self.maze.start
        self.steps = 0
        self.steps_var.set(str(self.steps))
        self.draw_maze()

    def toggle_path(self):
        self.show_path = not self.show_path
        self.path_btn.config(text="隐藏最短路径" if self.show_path else "显示最短路径")
        self.draw_maze()

    def move(self, dr, dc):
        r, c = self.player_pos
        nr, nc = r+dr, c+dc
        if self.maze.is_free(nr, nc):
            self.player_pos = (nr, nc)
            self.steps += 1
            self.steps_var.set(str(self.steps))
            self.draw_maze()
            if self.player_pos == self.maze.goal:
                messagebox.showinfo("到达终点", f"恭喜你，罗小黑到达终点！共用步数：{self.steps}")
        return

    def draw_maze(self):
        self.canvas.delete("all")
        cs = self.cell_size
        for r in range(self.rows):
            for c in range(self.cols):
                x0 = c*cs; y0 = r*cs; x1 = x0+cs; y1 = y0+cs
                if self.maze.grid[r][c] == 1:
                    self.canvas.create_rectangle(x0, y0, x1, y1, fill=WALL_COLOR, outline="")
                else:
                    self.canvas.create_rectangle(x0, y0, x1, y1, fill=PATH_COLOR, outline="")
        # 起点与终点
        sr, sc = self.maze.start; gr, gc = self.maze.goal
        self.canvas.create_rectangle(sc*cs, sr*cs, sc*cs+cs, sr*cs+cs, fill=START_COLOR, outline="")
        self.canvas.create_rectangle(gc*cs, gr*cs, gc*cs+cs, gr*cs+cs, fill=GOAL_COLOR, outline="")
        # 最短路径（可选）
        if self.show_path:
            path = self.maze.shortest_path()
            for (pr, pc) in path:
                self.canvas.create_rectangle(pc*cs+cs*0.2, pr*cs+cs*0.2, pc*cs+cs*0.8, pr*cs+cs*0.8, fill=VISITED_COLOR, outline="")
        # 玩家（罗小黑）
        pr, pc = self.player_pos
        self.canvas.create_oval(pc*cs+cs*0.15, pr*cs+cs*0.15, pc*cs+cs*0.85, pr*cs+cs*0.85, fill=PLAYER_COLOR, outline="")

if __name__ == "__main__":
    app = MazeApp()
    app.mainloop()