import pygame
import sys
import random
import heapq
from enum import Enum

# 初始化pygame
pygame.init()

# 屏幕设置
SCREEN_WIDTH, SCREEN_HEIGHT = 1200, 800
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("高级迷宫探险")

# 颜色定义


class Colors:
    WALL = (40, 40, 60)
    PATH = (20, 20, 30)
    PLAYER = (100, 200, 255)
    ENEMY = (255, 100, 100)
    EXIT = (100, 255, 100)
    TREASURE = (255, 215, 0)
    HEALTH = (255, 50, 50)
    KEY = (200, 100, 255)
    DOOR = (160, 120, 80)
    FOG = (0, 0, 0, 180)
    VISITED = (30, 30, 50)
    TEXT = (240, 240, 240)
    HUD_BG = (20, 20, 30, 200)
    BUTTON = (70, 130, 180)
    BUTTON_HOVER = (90, 160, 210)

# 方向枚举


class Direction(Enum):
    UP = (0, -1)
    DOWN = (0, 1)
    LEFT = (-1, 0)
    RIGHT = (1, 0)

# 迷宫生成算法


class MazeGenerator:
    def __init__(self, width, height):
        self.width = width
        self.height = height
        self.maze = [[1 for _ in range(width)] for _ in range(height)]

    def generate_dfs(self, start_x=1, start_y=1):
        """使用深度优先搜索生成迷宫"""
        stack = [(start_x, start_y)]
        self.maze[start_y][start_x] = 0

        while stack:
            x, y = stack[-1]
            directions = list(Direction)
            random.shuffle(directions)

            moved = False
            for direction in directions:
                dx, dy = direction.value
                nx, ny = x + dx*2, y + dy*2

                if (0 < nx < self.width-1 and 0 < ny < self.height-1 and
                        self.maze[ny][nx] == 1):
                    # 打通墙壁
                    self.maze[y+dy][x+dx] = 0
                    self.maze[ny][nx] = 0
                    stack.append((nx, ny))
                    moved = True
                    break

            if not moved:
                stack.pop()

        return self.maze

    def add_items(self, num_treasures, num_enemies, num_health):
        """在迷宫中添加物品"""
        items = []
        empty_cells = [(x, y) for y in range(self.height)
                       for x in range(self.width) if self.maze[y][x] == 0]

        # 添加宝藏
        for _ in range(num_treasures):
            if empty_cells:
                x, y = random.choice(empty_cells)
                items.append(('treasure', x, y))
                empty_cells.remove((x, y))

        # 添加敌人
        for _ in range(num_enemies):
            if empty_cells:
                x, y = random.choice(empty_cells)
                items.append(('enemy', x, y))
                empty_cells.remove((x, y))

        # 添加生命值
        for _ in range(num_health):
            if empty_cells:
                x, y = random.choice(empty_cells)
                items.append(('health', x, y))
                empty_cells.remove((x, y))

        # 添加钥匙和门
        if empty_cells:
            key_x, key_y = random.choice(empty_cells)
            items.append(('key', key_x, key_y))
            empty_cells.remove((key_x, key_y))

            door_x, door_y = random.choice(empty_cells)
            items.append(('door', door_x, door_y))

        return items

# A*寻路算法


class AStar:
    @staticmethod
    def heuristic(a, b):
        return abs(a[0] - b[0]) + abs(a[1] - b[1])

    @staticmethod
    def find_path(maze, start, goal):
        """使用A*算法寻找最短路径"""
        neighbors = [(0, 1), (0, -1), (1, 0), (-1, 0)]
        close_set = set()
        came_from = {}
        gscore = {start: 0}
        fscore = {start: AStar.heuristic(start, goal)}
        oheap = []

        heapq.heappush(oheap, (fscore[start], start))

        while oheap:
            current = heapq.heappop(oheap)[1]

            if current == goal:
                path = []
                while current in came_from:
                    path.append(current)
                    current = came_from[current]
                return path[::-1]

            close_set.add(current)

            for dx, dy in neighbors:
                neighbor = (current[0] + dx, current[1] + dy)

                if (0 <= neighbor[0] < len(maze[0]) and
                    0 <= neighbor[1] < len(maze) and
                        maze[neighbor[1]][neighbor[0]] == 0):

                    tentative_g = gscore[current] + 1

                    if neighbor in close_set and tentative_g >= gscore.get(neighbor, 0):
                        continue

                    if tentative_g < gscore.get(neighbor, 0) or neighbor not in [i[1] for i in oheap]:
                        came_from[neighbor] = current
                        gscore[neighbor] = tentative_g
                        fscore[neighbor] = tentative_g + \
                            AStar.heuristic(neighbor, goal)
                        heapq.heappush(oheap, (fscore[neighbor], neighbor))

        return []

# 玩家类


class Player:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.health = 100
        self.max_health = 100
        self.treasure = 0
        self.keys = 0
        self.vision_radius = 5
        self.visited = set()
        self.update_visited()

    def update_visited(self):
        """更新玩家视野范围内的已访问区域"""
        for dx in range(-self.vision_radius, self.vision_radius + 1):
            for dy in range(-self.vision_radius, self.vision_radius + 1):
                if dx*dx + dy*dy <= self.vision_radius*self.vision_radius:
                    self.visited.add((self.x + dx, self.y + dy))

    def move(self, dx, dy, maze):
        """移动玩家"""
        new_x, new_y = self.x + dx, self.y + dy

        if (0 <= new_x < len(maze[0]) and 0 <= new_y < len(maze) and
                maze[new_y][new_x] == 0):
            self.x = new_x
            self.y = new_y
            self.update_visited()
            return True
        return False

    def take_damage(self, amount):
        """受到伤害"""
        self.health = max(0, self.health - amount)
        return self.health > 0

    def heal(self, amount):
        """恢复生命值"""
        self.health = min(self.max_health, self.health + amount)

    def add_treasure(self):
        """收集宝藏"""
        self.treasure += 1

    def add_key(self):
        """收集钥匙"""
        self.keys += 1

    def use_key(self):
        """使用钥匙"""
        if self.keys > 0:
            self.keys -= 1
            return True
        return False

# 敌人AI类


class Enemy:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.health = 30
        self.damage = 10
        self.vision_range = 8
        self.path = []
        self.last_player_pos = None

    def update(self, maze, player_pos):
        """更新敌人状态和位置"""
        px, py = player_pos

        # 计算到玩家的距离
        distance = abs(self.x - px) + abs(self.y - py)

        if distance <= self.vision_range:
            self.last_player_pos = player_pos
            # 使用A*算法寻找路径
            self.path = AStar.find_path(maze, (self.x, self.y), player_pos)

            if self.path and len(self.path) > 1:
                next_pos = self.path[0]
                self.x, self.y = next_pos

        # 随机移动（如果看不到玩家）
        elif random.random() < 0.1:  # 10%概率随机移动
            directions = list(Direction)
            random.shuffle(directions)

            for direction in directions:
                dx, dy = direction.value
                new_x, new_y = self.x + dx, self.y + dy

                if (0 <= new_x < len(maze[0]) and 0 <= new_y < len(maze) and
                        maze[new_y][new_x] == 0):
                    self.x, self.y = new_x, new_y
                    break

    def can_attack(self, player_pos):
        """检查是否可以攻击玩家"""
        return abs(self.x - player_pos[0]) <= 1 and abs(self.y - player_pos[1]) <= 1

# 游戏主类


class MazeGame:
    def __init__(self):
        self.cell_size = 30
        self.maze_width = 40
        self.maze_height = 30
        self.level = 1
        self.max_level = 5
        self.game_state = "menu"  # menu, playing, game_over, level_complete
        self.camera_x = 0
        self.camera_y = 0
        self.font = pygame.font.SysFont(None, 36)
        self.small_font = pygame.font.SysFont(None, 24)

        self.init_level()

    def init_level(self):
        """初始化关卡"""
        # 生成迷宫
        self.generator = MazeGenerator(self.maze_width, self.maze_height)
        self.maze = self.generator.generate_dfs()

        # 添加物品
        num_treasures = min(10 + self.level * 2, 20)
        num_enemies = min(5 + self.level, 15)
        num_health = 3 + self.level

        self.items = self.generator.add_items(
            num_treasures, num_enemies, num_health)

        # 创建玩家（放在起点）
        self.player = Player(1, 1)

        # 创建敌人
        self.enemies = []
        for item_type, x, y in self.items:
            if item_type == 'enemy':
                self.enemies.append(Enemy(x, y))

        # 设置出口
        self.exit_pos = (self.maze_width - 2, self.maze_height - 2)
        self.maze[self.exit_pos[1]][self.exit_pos[0]] = 0

        # 找到门的位置
        self.door_pos = None
        for item_type, x, y in self.items:
            if item_type == 'door':
                self.door_pos = (x, y)
                self.maze[y][x] = 2  # 门是特殊类型的墙
                break

        # 更新相机位置
        self.update_camera()

    def update_camera(self):
        """更新相机位置以跟随玩家"""
        screen_cells_x = SCREEN_WIDTH // self.cell_size
        screen_cells_y = SCREEN_HEIGHT // self.cell_size

        self.camera_x = max(0, min(self.player.x - screen_cells_x // 2,
                                   self.maze_width - screen_cells_x))
        self.camera_y = max(0, min(self.player.y - screen_cells_y // 2,
                                   self.maze_height - screen_cells_y))

    def handle_input(self):
        """处理玩家输入"""
        keys = pygame.key.get_pressed()

        if self.game_state == "playing":
            moved = False

            if keys[pygame.K_UP] or keys[pygame.K_w]:
                moved = self.player.move(0, -1, self.maze)
            elif keys[pygame.K_DOWN] or keys[pygame.K_s]:
                moved = self.player.move(0, 1, self.maze)
            elif keys[pygame.K_LEFT] or keys[pygame.K_a]:
                moved = self.player.move(-1, 0, self.maze)
            elif keys[pygame.K_RIGHT] or keys[pygame.K_d]:
                moved = self.player.move(1, 0, self.maze)

            if moved:
                self.update_camera()
                self.check_collisions()
                self.update_enemies()

            # 检查是否到达出口
            if (self.player.x, self.player.y) == self.exit_pos:
                if self.level < self.max_level:
                    self.game_state = "level_complete"
                else:
                    self.game_state = "game_over"

    def check_collisions(self):
        """检查玩家与物品的碰撞"""
        player_pos = (self.player.x, self.player.y)

        # 检查物品
        items_to_remove = []
        for i, (item_type, x, y) in enumerate(self.items):
            if (x, y) == player_pos:
                if item_type == 'treasure':
                    self.player.add_treasure()
                    items_to_remove.append(i)
                elif item_type == 'health':
                    self.player.heal(30)
                    items_to_remove.append(i)
                elif item_type == 'key':
                    self.player.add_key()
                    items_to_remove.append(i)
                elif item_type == 'door' and self.player.use_key():
                    self.maze[y][x] = 0  # 打开门
                    items_to_remove.append(i)

        # 移除已收集的物品
        for i in reversed(items_to_remove):
            del self.items[i]

        # 检查敌人攻击
        for enemy in self.enemies:
            if enemy.can_attack(player_pos):
                if not self.player.take_damage(enemy.damage):
                    self.game_state = "game_over"
                break

    def update_enemies(self):
        """更新所有敌人"""
        for enemy in self.enemies:
            enemy.update(self.maze, (self.player.x, self.player.y))

            # 检查敌人是否攻击玩家
            if enemy.can_attack((self.player.x, self.player.y)):
                if not self.player.take_damage(enemy.damage):
                    self.game_state = "game_over"

    def draw(self):
        """绘制游戏画面"""
        screen.fill((10, 10, 20))

        if self.game_state == "menu":
            self.draw_menu()
        elif self.game_state == "playing":
            self.draw_maze()
            self.draw_hud()
        elif self.game_state == "level_complete":
            self.draw_maze()
            self.draw_hud()
            self.draw_level_complete()
        elif self.game_state == "game_over":
            self.draw_maze()
            self.draw_hud()
            self.draw_game_over()

    def draw_maze(self):
        """绘制迷宫"""
        screen_cells_x = SCREEN_WIDTH // self.cell_size
        screen_cells_y = SCREEN_HEIGHT // self.cell_size

        # 绘制迷宫单元格
        for y in range(self.camera_y, min(self.camera_y + screen_cells_y, self.maze_height)):
            for x in range(self.camera_x, min(self.camera_x + screen_cells_x, self.maze_width)):
                screen_x = (x - self.camera_x) * self.cell_size
                screen_y = (y - self.camera_y) * self.cell_size

                # 检查是否在玩家视野内
                if (x, y) in self.player.visited:
                    # 绘制墙壁或路径
                    if self.maze[y][x] == 1:
                        pygame.draw.rect(screen, Colors.WALL,
                                         (screen_x, screen_y, self.cell_size, self.cell_size))
                    elif self.maze[y][x] == 0:
                        pygame.draw.rect(screen, Colors.PATH,
                                         (screen_x, screen_y, self.cell_size, self.cell_size))
                    elif self.maze[y][x] == 2:  # 门
                        pygame.draw.rect(screen, Colors.DOOR,
                                         (screen_x, screen_y, self.cell_size, self.cell_size))

                    # 绘制物品
                    for item_type, item_x, item_y in self.items:
                        if item_x == x and item_y == y:
                            if item_type == 'treasure':
                                pygame.draw.circle(screen, Colors.TREASURE,
                                                   (screen_x + self.cell_size//2,
                                                    screen_y + self.cell_size//2),
                                                   self.cell_size//3)
                            elif item_type == 'health':
                                pygame.draw.rect(screen, Colors.HEALTH,
                                                 (screen_x + self.cell_size//4,
                                                  screen_y + self.cell_size//4,
                                                  self.cell_size//2,
                                                  self.cell_size//2))
                            elif item_type == 'key':
                                pygame.draw.polygon(screen, Colors.KEY, [
                                    (screen_x + self.cell_size//2,
                                     screen_y + self.cell_size//4),
                                    (screen_x + self.cell_size//4,
                                     screen_y + self.cell_size//2),
                                    (screen_x + self.cell_size//2,
                                     screen_y + self.cell_size*3//4),
                                    (screen_x + self.cell_size*3//4,
                                     screen_y + self.cell_size//2)
                                ])

                    # 绘制敌人
                    for enemy in self.enemies:
                        if enemy.x == x and enemy.y == y:
                            pygame.draw.circle(screen, Colors.ENEMY,
                                               (screen_x + self.cell_size//2,
                                                screen_y + self.cell_size//2),
                                               self.cell_size//2 - 2)

                    # 绘制出口
                    if (x, y) == self.exit_pos:
                        pygame.draw.rect(screen, Colors.EXIT,
                                         (screen_x + 5, screen_y + 5,
                                          self.cell_size - 10, self.cell_size - 10))

                else:
                    # 未探索区域
                    pygame.draw.rect(screen, Colors.FOG,
                                     (screen_x, screen_y, self.cell_size, self.cell_size))

        # 绘制玩家
        player_screen_x = (self.player.x - self.camera_x) * self.cell_size
        player_screen_y = (self.player.y - self.camera_y) * self.cell_size
        pygame.draw.circle(screen, Colors.PLAYER,
                           (player_screen_x + self.cell_size//2,
                            player_screen_y + self.cell_size//2),
                           self.cell_size//2 - 2)

    def draw_hud(self):
        """绘制游戏状态信息"""
        # 半透明背景
        hud_bg = pygame.Surface((SCREEN_WIDTH, 100), pygame.SRCALPHA)
        hud_bg.fill(Colors.HUD_BG)
        screen.blit(hud_bg, (0, 0))

        # 生命值条
        health_width = 200
        health_height = 20
        health_x = 20
        health_y = 20

        # 生命值条背景
        pygame.draw.rect(screen, (100, 100, 100),
                         (health_x, health_y, health_width, health_height))

        # 当前生命值
        health_percent = self.player.health / self.player.max_health
        pygame.draw.rect(screen, Colors.HEALTH,
                         (health_x, health_y, health_width * health_percent, health_height))

        # 生命值文字
        health_text = self.font.render(f"生命值: {self.player.health}/{self.player.max_health}",
                                       True, Colors.TEXT)
        screen.blit(health_text, (health_x + health_width + 10, health_y))

        # 其他状态信息
        stats_y = health_y + 30
        treasure_text = self.font.render(
            f"宝藏: {self.player.treasure}", True, Colors.TEXT)
        screen.blit(treasure_text, (health_x, stats_y))

        keys_text = self.font.render(
            f"钥匙: {self.player.keys}", True, Colors.TEXT)
        screen.blit(keys_text, (health_x + 150, stats_y))

        level_text = self.font.render(
            f"关卡: {self.level}/{self.max_level}", True, Colors.TEXT)
        screen.blit(level_text, (health_x + 300, stats_y))

        # 控制提示
        controls_y = SCREEN_HEIGHT - 80
        controls = [
            "WASD/方向键: 移动",
            "目标: 找到出口(绿色方块)",
            "收集钥匙(紫色)打开门(棕色)",
            "避开敌人(红色), 收集生命值(红色方块)"
        ]

        for i, control in enumerate(controls):
            control_text = self.small_font.render(control, True, Colors.TEXT)
            screen.blit(control_text, (20, controls_y + i * 20))

    def draw_menu(self):
        """绘制主菜单"""
        # 标题
        title = self.font.render("高级迷宫探险", True, Colors.TEXT)
        screen.blit(title, (SCREEN_WIDTH//2 - title.get_width()//2, 100))

        # 开始按钮
        button_rect = pygame.Rect(SCREEN_WIDTH//2 - 100, 200, 200, 50)
        mouse_pos = pygame.mouse.get_pos()
        button_color = Colors.BUTTON_HOVER if button_rect.collidepoint(
            mouse_pos) else Colors.BUTTON

        pygame.draw.rect(screen, button_color, button_rect, border_radius=10)
        pygame.draw.rect(screen, Colors.TEXT, button_rect, 2, border_radius=10)

        start_text = self.font.render("开始游戏", True, Colors.TEXT)
        screen.blit(start_text, (SCREEN_WIDTH//2 -
                    start_text.get_width()//2, 215))

        # 游戏说明
        instructions = [
            "游戏说明:",
            "1. 探索迷宫，找到绿色出口",
            "2. 收集宝藏(金色)获得分数",
            "3. 避开红色敌人",
            "4. 收集紫色钥匙打开棕色门",
            "5. 收集红色生命值恢复生命",
            "6. 完成所有关卡获胜"
        ]

        for i, instruction in enumerate(instructions):
            inst_text = self.small_font.render(instruction, True, Colors.TEXT)
            screen.blit(inst_text, (SCREEN_WIDTH//2 -
                        inst_text.get_width()//2, 300 + i * 30))

    def draw_level_complete(self):
        """绘制关卡完成界面"""
        overlay = pygame.Surface(
            (SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
        overlay.fill((0, 0, 0, 150))
        screen.blit(overlay, (0, 0))

        # 完成信息
        complete_text = self.font.render(
            f"关卡 {self.level} 完成!", True, Colors.TEXT)
        screen.blit(complete_text, (SCREEN_WIDTH//2 -
                    complete_text.get_width()//2, 200))

        stats_text = self.font.render(
            f"收集宝藏: {self.player.treasure}", True, Colors.TEXT)
        screen.blit(stats_text, (SCREEN_WIDTH//2 -
                    stats_text.get_width()//2, 250))

        # 下一关按钮
        button_rect = pygame.Rect(SCREEN_WIDTH//2 - 100, 320, 200, 50)
        mouse_pos = pygame.mouse.get_pos()
        button_color = Colors.BUTTON_HOVER if button_rect.collidepoint(
            mouse_pos) else Colors.BUTTON

        pygame.draw.rect(screen, button_color, button_rect, border_radius=10)
        pygame.draw.rect(screen, Colors.TEXT, button_rect, 2, border_radius=10)

        next_text = self.font.render("下一关" if self.level < self.max_level else "查看结果",
                                     True, Colors.TEXT)
        screen.blit(next_text, (SCREEN_WIDTH//2 -
                    next_text.get_width()//2, 335))

    def draw_game_over(self):
        """绘制游戏结束界面"""
        overlay = pygame.Surface(
            (SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
        overlay.fill((0, 0, 0, 150))
        screen.blit(overlay, (0, 0))

        # 游戏结束信息
        if self.player.health <= 0:
            game_over_text = self.font.render("游戏结束!", True, Colors.ENEMY)
        else:
            game_over_text = self.font.render("恭喜通关!", True, Colors.EXIT)

        screen.blit(game_over_text, (SCREEN_WIDTH//2 -
                    game_over_text.get_width()//2, 200))

        stats_text = self.font.render(
            f"最终分数: {self.player.treasure * 100}", True, Colors.TEXT)
        screen.blit(stats_text, (SCREEN_WIDTH//2 -
                    stats_text.get_width()//2, 250))

        # 重新开始按钮
        button_rect = pygame.Rect(SCREEN_WIDTH//2 - 100, 320, 200, 50)
        mouse_pos = pygame.mouse.get_pos()
        button_color = Colors.BUTTON_HOVER if button_rect.collidepoint(
            mouse_pos) else Colors.BUTTON

        pygame.draw.rect(screen, button_color, button_rect, border_radius=10)
        pygame.draw.rect(screen, Colors.TEXT, button_rect, 2, border_radius=10)

        restart_text = self.font.render("重新开始", True, Colors.TEXT)
        screen.blit(restart_text, (SCREEN_WIDTH//2 -
                    restart_text.get_width()//2, 335))

    def handle_mouse_click(self, pos):
        """处理鼠标点击事件"""
        if self.game_state == "menu":
            button_rect = pygame.Rect(SCREEN_WIDTH//2 - 100, 200, 200, 50)
            if button_rect.collidepoint(pos):
                self.game_state = "playing"

        elif self.game_state == "level_complete":
            button_rect = pygame.Rect(SCREEN_WIDTH//2 - 100, 320, 200, 50)
            if button_rect.collidepoint(pos):
                if self.level < self.max_level:
                    self.level += 1
                    self.init_level()
                    self.game_state = "playing"
                else:
                    self.game_state = "game_over"

        elif self.game_state == "game_over":
            button_rect = pygame.Rect(SCREEN_WIDTH//2 - 100, 320, 200, 50)
            if button_rect.collidepoint(pos):
                self.level = 1
                self.init_level()
                self.game_state = "playing"


# 创建游戏实例
game = MazeGame()
clock = pygame.time.Clock()

# 主游戏循环
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        elif event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:  # 左键点击
                game.handle_mouse_click(event.pos)
        elif event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE:
                if game.game_state == "playing":
                    game.game_state = "menu"
                else:
                    game.game_state = "playing"

    # 处理输入
    game.handle_input()

    # 绘制游戏
    game.draw()

    # 更新显示
    pygame.display.flip()
    clock.tick(60)

# 退出游戏
pygame.quit()
sys.exit()
