import pygame
import sys
import random
import math
from collections import deque

pygame.init()

CELL_SIZE = 28
COLS = 21
ROWS = 21
HEADER_HEIGHT = 60
WIDTH = COLS * CELL_SIZE
HEIGHT = ROWS * CELL_SIZE + HEADER_HEIGHT

BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
YELLOW = (255, 255, 0)
DOT_COLOR = (255, 183, 174)
WALL_COLOR = (33, 33, 222)
WALL_FILL = (20, 20, 80)
PACMAN_COLOR = (255, 255, 0)
POWER_DOT_COLOR = (255, 183, 174)
BG_COLOR = (0, 0, 0)
DOOR_COLOR = (255, 184, 82)

GHOST_COLORS = [
    (255, 0, 0),
    (255, 184, 255),
    (0, 255, 255),
    (255, 184, 82),
    (50, 205, 50),
]

SCARED_COLOR = (33, 33, 222)
SCARED_FLASH_COLOR = (255, 255, 255)

UP = (0, -1)
DOWN = (0, 1)
LEFT = (-1, 0)
RIGHT = (1, 0)
STOP = (0, 0)
DIRECTIONS = [UP, DOWN, LEFT, RIGHT]

LEVEL_CONFIG = [
    {"ghost_count": 2, "ghost_speed": 2.0, "scatter_time": 420, "chase_time": 1200, "power_time": 480},
    {"ghost_count": 2, "ghost_speed": 2.4, "scatter_time": 360, "chase_time": 1100, "power_time": 420},
    {"ghost_count": 3, "ghost_speed": 2.8, "scatter_time": 300, "chase_time": 1000, "power_time": 360},
    {"ghost_count": 3, "ghost_speed": 3.2, "scatter_time": 260, "chase_time": 900, "power_time": 300},
    {"ghost_count": 4, "ghost_speed": 3.6, "scatter_time": 220, "chase_time": 800, "power_time": 240},
    {"ghost_count": 5, "ghost_speed": 4.0, "scatter_time": 180, "chase_time": 700, "power_time": 180},
]

ENTER_KEYS = (pygame.K_RETURN, pygame.K_KP_ENTER)


def _bfs_connected_cells(grid):
    start = None
    for y in range(ROWS):
        for x in range(COLS):
            if grid[y][x] != '#':
                start = (x, y)
                break
        if start:
            break
    if not start:
        return set()

    visited = set()
    queue = deque([start])
    visited.add(start)
    while queue:
        x, y = queue.popleft()
        for dx, dy in DIRECTIONS:
            nx, ny = x + dx, y + dy
            if 0 <= nx < COLS and 0 <= ny < ROWS and (nx, ny) not in visited and grid[ny][nx] != '#':
                visited.add((nx, ny))
                queue.append((nx, ny))
    return visited


def _ensure_connectivity(grid):
    for _ in range(50):
        connected = _bfs_connected_cells(grid)
        isolated = []
        for y in range(ROWS):
            for x in range(COLS):
                if grid[y][x] != '#' and (x, y) not in connected:
                    isolated.append((x, y))
        if not isolated:
            return

        for ix, iy in isolated:
            queue = deque([(ix, iy, [])])
            seen = {(ix, iy)}
            found = False
            while queue and not found:
                cx, cy, path = queue.popleft()
                for dx, dy in DIRECTIONS:
                    nx, ny = cx + dx, cy + dy
                    if (nx, ny) in seen or nx < 0 or nx >= COLS or ny < 0 or ny >= ROWS:
                        continue
                    new_path = path + [(nx, ny)]
                    if (nx, ny) in connected:
                        for px, py in new_path:
                            if grid[py][px] == '#':
                                grid[py][px] = '.'
                            connected.add((px, py))
                        connected.add((ix, iy))
                        found = True
                        break
                    seen.add((nx, ny))
                    queue.append((nx, ny, new_path))


def generate_maze(level):
    seed = level * 1000 + 42
    rng = random.Random(seed)

    grid = [['#'] * COLS for _ in range(ROWS)]

    cx, cy = 1, 1
    grid[cy][cx] = '.'
    stack = [(cx, cy)]
    visited = {(cx, cy)}

    while stack:
        x, y = stack[-1]
        neighbors = []
        for dx, dy in DIRECTIONS:
            nx, ny = x + dx * 2, y + dy * 2
            if 1 <= nx < COLS - 1 and 1 <= ny < ROWS - 1 and (nx, ny) not in visited:
                neighbors.append((nx, ny, dx, dy))
        if neighbors:
            nx, ny, dx, dy = rng.choice(neighbors)
            grid[y + dy][x + dx] = '.'
            grid[ny][nx] = '.'
            visited.add((nx, ny))
            stack.append((nx, ny))
        else:
            stack.pop()

    extra = 18 + level * 4
    for _ in range(extra):
        wx = rng.randint(2, COLS - 3)
        wy = rng.randint(2, ROWS - 3)
        if grid[wy][wx] == '#':
            adj = 0
            for dx, dy in DIRECTIONS:
                nx, ny = wx + dx, wy + dy
                if 0 <= nx < COLS and 0 <= ny < ROWS and grid[ny][nx] != '#':
                    adj += 1
            if adj >= 2:
                grid[wy][wx] = '.'

    door_y = ROWS // 2
    door_x = COLS // 2
    grid[door_y][door_x] = 'S'
    for dy in range(-1, 2):
        for dx in range(-1, 2):
            ny, nx = door_y + dy, door_x + dx
            if 0 < nx < COLS - 1 and 0 < ny < ROWS - 1:
                if grid[ny][nx] == '#':
                    grid[ny][nx] = ' '

    for y in range(1, ROWS - 1):
        for x_off in [1, COLS - 2]:
            if grid[y][x_off] == '#':
                above = y > 0 and grid[y - 1][x_off] != '#'
                below = y < ROWS - 1 and grid[y + 1][x_off] != '#'
                if above or below:
                    grid[y][x_off] = '.'
    for x in range(1, COLS - 1):
        for y_off in [1, ROWS - 2]:
            if grid[y_off][x] == '#':
                left = x > 0 and grid[y_off][x - 1] != '#'
                right = x < COLS - 1 and grid[y_off][x + 1] != '#'
                if left or right:
                    grid[y_off][x] = '.'

    _ensure_connectivity(grid)

    power_positions = [(1, 1), (COLS - 2, 1), (1, ROWS - 2), (COLS - 2, ROWS - 2)]
    for px, py in power_positions:
        if 0 < px < COLS - 1 and 0 < py < ROWS - 1 and grid[py][px] == '.':
            grid[py][px] = 'P'

    return grid


class Maze:
    def __init__(self, level):
        self.level = level
        self.grid_data = generate_maze(level)
        self.walls = set()
        self.dots = set()
        self.power_dots = set()
        self.paths = set()
        self.ghost_door = None
        self._parse()

    def _parse(self):
        for y in range(ROWS):
            for x in range(COLS):
                ch = self.grid_data[y][x]
                if ch == '#':
                    self.walls.add((x, y))
                else:
                    self.paths.add((x, y))
                    if ch == '.':
                        self.dots.add((x, y))
                    elif ch == 'P':
                        self.dots.add((x, y))
                        self.power_dots.add((x, y))
                    elif ch == 'S':
                        self.ghost_door = (x, y)

    def is_wall(self, x, y):
        if x < 0 or x >= COLS or y < 0 or y >= ROWS:
            return True
        return (x, y) in self.walls

    def is_path(self, x, y):
        if x < 0 or x >= COLS or y < 0 or y >= ROWS:
            return False
        return (x, y) in self.paths

    def remove_dot(self, x, y):
        if (x, y) in self.dots:
            is_power = (x, y) in self.power_dots
            self.dots.discard((x, y))
            self.power_dots.discard((x, y))
            return is_power
        return None

    def has_dots_remaining(self):
        return len(self.dots) > 0

    def find_player_start(self):
        for y in range(ROWS - 2, 0, -1):
            row_paths = []
            for x in range(1, COLS - 1):
                if (x, y) in self.paths and (x, y) not in self.power_dots:
                    row_paths.append((x, y))
            if row_paths:
                return row_paths[len(row_paths) // 2]
        return (1, 1)


class PacMan:
    def __init__(self, x, y):
        self.grid_x = x
        self.grid_y = y
        self.pixel_x = float(x * CELL_SIZE + CELL_SIZE // 2)
        self.pixel_y = float(y * CELL_SIZE + CELL_SIZE // 2)
        self.direction = STOP
        self.next_direction = STOP
        self.moving = False
        self.speed = 2.5
        self.mouth_angle = 45
        self.mouth_dir = 1
        self.lives = 3
        self.score = 0

    def set_direction(self, d):
        self.next_direction = d

    def _at_center(self):
        tx = self.grid_x * CELL_SIZE + CELL_SIZE // 2
        ty = self.grid_y * CELL_SIZE + CELL_SIZE // 2
        return abs(self.pixel_x - tx) < 1.0 and abs(self.pixel_y - ty) < 1.0

    def update(self, maze):
        self.mouth_angle += self.mouth_dir * 4
        if self.mouth_angle >= 45:
            self.mouth_dir = -1
        elif self.mouth_angle <= 5:
            self.mouth_dir = 1

        if self._at_center():
            self.pixel_x = float(self.grid_x * CELL_SIZE + CELL_SIZE // 2)
            self.pixel_y = float(self.grid_y * CELL_SIZE + CELL_SIZE // 2)

            if self.next_direction != STOP:
                nx = self.grid_x + self.next_direction[0]
                ny = self.grid_y + self.next_direction[1]
                if maze.is_path(nx, ny):
                    self.direction = self.next_direction
                    self.moving = True

            if self.direction != STOP:
                nx = self.grid_x + self.direction[0]
                ny = self.grid_y + self.direction[1]
                if not maze.is_path(nx, ny):
                    self.direction = STOP
                    self.moving = False
                    self.next_direction = STOP

        if self.direction != STOP and self.moving:
            target_px = (self.grid_x + self.direction[0]) * CELL_SIZE + CELL_SIZE // 2
            target_py = (self.grid_y + self.direction[1]) * CELL_SIZE + CELL_SIZE // 2
            dx = target_px - self.pixel_x
            dy = target_py - self.pixel_y
            dist = math.sqrt(dx * dx + dy * dy)

            if dist <= self.speed:
                self.grid_x += self.direction[0]
                self.grid_y += self.direction[1]
                self.pixel_x = float(self.grid_x * CELL_SIZE + CELL_SIZE // 2)
                self.pixel_y = float(self.grid_y * CELL_SIZE + CELL_SIZE // 2)
                self.moving = False
            else:
                if dx != 0:
                    self.pixel_x += self.direction[0] * self.speed
                if dy != 0:
                    self.pixel_y += self.direction[1] * self.speed

    def draw(self, screen):
        oy = HEADER_HEIGHT
        angle = 0
        if self.direction == RIGHT or self.direction == STOP:
            angle = 0
        elif self.direction == LEFT:
            angle = 180
        elif self.direction == UP:
            angle = 270
        elif self.direction == DOWN:
            angle = 90

        start_a = math.radians(angle + self.mouth_angle)
        end_a = math.radians(angle - self.mouth_angle + 360)
        r = CELL_SIZE // 2 - 2
        cx = self.pixel_x
        cy = self.pixel_y + oy

        points = [(cx, cy)]
        for i in range(24):
            a = start_a + (end_a - start_a) * i / 23
            points.append((cx + r * math.cos(a), cy + r * math.sin(a)))
        if len(points) > 2:
            pygame.draw.polygon(screen, PACMAN_COLOR, points)

    def reset_position(self, x, y):
        self.grid_x = x
        self.grid_y = y
        self.pixel_x = float(x * CELL_SIZE + CELL_SIZE // 2)
        self.pixel_y = float(y * CELL_SIZE + CELL_SIZE // 2)
        self.direction = STOP
        self.next_direction = STOP
        self.moving = False


class Ghost:
    def __init__(self, x, y, color, gid):
        self.start_x = x
        self.start_y = y
        self.grid_x = x
        self.grid_y = y
        self.pixel_x = float(x * CELL_SIZE + CELL_SIZE // 2)
        self.pixel_y = float(y * CELL_SIZE + CELL_SIZE // 2)
        self.color = color
        self.gid = gid
        self.direction = LEFT
        self.speed = 2.0
        self.scared = False
        self.scared_timer = 0
        self.eaten = False
        self.in_house = True
        self.release_timer = gid * 100
        self.mode = "scatter"
        self.mode_timer = 0
        self.scatter_target = [(1, 1), (COLS - 2, 1), (1, ROWS - 2), (COLS - 2, ROWS - 2), (COLS // 2, 1)][gid % 5]

    def set_scared(self, duration):
        self.scared = True
        self.scared_timer = duration
        if self.direction != STOP:
            self.direction = (-self.direction[0], -self.direction[1])

    def _at_center(self):
        tx = self.grid_x * CELL_SIZE + CELL_SIZE // 2
        ty = self.grid_y * CELL_SIZE + CELL_SIZE // 2
        return abs(self.pixel_x - tx) < 1.5 and abs(self.pixel_y - ty) < 1.5

    def update(self, maze, pacman, config):
        if self.eaten:
            self._move_toward(maze, self.start_x, self.start_y, 2.0)
            if self._at_center() and self.grid_x == self.start_x and self.grid_y == self.start_y:
                self.eaten = False
                self.scared = False
                self.in_house = True
                self.release_timer = 60
            return

        if self.in_house:
            self.release_timer -= 1
            if self.release_timer <= 0 and maze.ghost_door:
                self._move_toward(maze, maze.ghost_door[0], maze.ghost_door[1], 1.0)
                if self._at_center() and self.grid_x == maze.ghost_door[0] and self.grid_y == maze.ghost_door[1]:
                    self.in_house = False
                    self.direction = LEFT
            return

        if self.scared:
            self.scared_timer -= 1
            if self.scared_timer <= 0:
                self.scared = False

        self.mode_timer += 1
        if self.mode_timer < config["scatter_time"]:
            self.mode = "scatter"
        elif self.mode_timer < config["scatter_time"] + config["chase_time"]:
            self.mode = "chase"
        else:
            self.mode_timer = 0
            self.mode = "scatter"

        if self.scared:
            self._move_scared(maze)
        elif self.mode == "scatter":
            self._move_toward(maze, self.scatter_target[0], self.scatter_target[1], 1.0)
        else:
            tx, ty = self._chase_target(pacman)
            self._move_toward(maze, tx, ty, 1.0)

    def _chase_target(self, pacman):
        if self.gid % 4 == 0:
            return (pacman.grid_x, pacman.grid_y)
        elif self.gid % 4 == 1:
            return (pacman.grid_x + pacman.direction[0] * 4, pacman.grid_y + pacman.direction[1] * 4)
        elif self.gid % 4 == 2:
            ax = pacman.grid_x + pacman.direction[0] * 2
            ay = pacman.grid_y + pacman.direction[1] * 2
            return (ax * 2 - self.grid_x, ay * 2 - self.grid_y)
        else:
            d = abs(pacman.grid_x - self.grid_x) + abs(pacman.grid_y - self.grid_y)
            if d < 8:
                return self.scatter_target
            return (pacman.grid_x, pacman.grid_y)

    def _move_toward(self, maze, tx, ty, speed_mult):
        actual_speed = self.speed * speed_mult

        if self._at_center():
            self.pixel_x = float(self.grid_x * CELL_SIZE + CELL_SIZE // 2)
            self.pixel_y = float(self.grid_y * CELL_SIZE + CELL_SIZE // 2)
            self._choose_dir(maze, tx, ty)

        if self.direction != STOP:
            target_px = (self.grid_x + self.direction[0]) * CELL_SIZE + CELL_SIZE // 2
            target_py = (self.grid_y + self.direction[1]) * CELL_SIZE + CELL_SIZE // 2
            dx = target_px - self.pixel_x
            dy = target_py - self.pixel_y
            dist = math.sqrt(dx * dx + dy * dy)

            if dist <= actual_speed:
                self.grid_x += self.direction[0]
                self.grid_y += self.direction[1]
                self.pixel_x = float(self.grid_x * CELL_SIZE + CELL_SIZE // 2)
                self.pixel_y = float(self.grid_y * CELL_SIZE + CELL_SIZE // 2)
            else:
                if dx != 0:
                    self.pixel_x += self.direction[0] * actual_speed
                if dy != 0:
                    self.pixel_y += self.direction[1] * actual_speed

    def _choose_dir(self, maze, tx, ty):
        opposite = (-self.direction[0], -self.direction[1]) if self.direction != STOP else None
        possible = []
        for d in DIRECTIONS:
            if d == opposite:
                continue
            nx, ny = self.grid_x + d[0], self.grid_y + d[1]
            if maze.is_path(nx, ny):
                possible.append(d)
        if not possible:
            if opposite and maze.is_path(self.grid_x + opposite[0], self.grid_y + opposite[1]):
                possible = [opposite]
            else:
                self.direction = STOP
                return

        best = possible[0]
        best_d = float('inf')
        for d in possible:
            nx, ny = self.grid_x + d[0], self.grid_y + d[1]
            dd = (nx - tx) ** 2 + (ny - ty) ** 2
            if dd < best_d:
                best_d = dd
                best = d
        self.direction = best

    def _move_scared(self, maze):
        actual_speed = self.speed * 0.6

        if self._at_center():
            self.pixel_x = float(self.grid_x * CELL_SIZE + CELL_SIZE // 2)
            self.pixel_y = float(self.grid_y * CELL_SIZE + CELL_SIZE // 2)
            opposite = (-self.direction[0], -self.direction[1]) if self.direction != STOP else None
            possible = []
            for d in DIRECTIONS:
                if d == opposite:
                    continue
                nx, ny = self.grid_x + d[0], self.grid_y + d[1]
                if maze.is_path(nx, ny):
                    possible.append(d)
            if not possible:
                if opposite and maze.is_path(self.grid_x + opposite[0], self.grid_y + opposite[1]):
                    possible = [opposite]
            if possible:
                self.direction = random.choice(possible)
            else:
                self.direction = STOP

        if self.direction != STOP:
            target_px = (self.grid_x + self.direction[0]) * CELL_SIZE + CELL_SIZE // 2
            target_py = (self.grid_y + self.direction[1]) * CELL_SIZE + CELL_SIZE // 2
            dx = target_px - self.pixel_x
            dy = target_py - self.pixel_y
            dist = math.sqrt(dx * dx + dy * dy)
            if dist <= actual_speed:
                self.grid_x += self.direction[0]
                self.grid_y += self.direction[1]
                self.pixel_x = float(self.grid_x * CELL_SIZE + CELL_SIZE // 2)
                self.pixel_y = float(self.grid_y * CELL_SIZE + CELL_SIZE // 2)
            else:
                if dx != 0:
                    self.pixel_x += self.direction[0] * actual_speed
                if dy != 0:
                    self.pixel_y += self.direction[1] * actual_speed

    def draw(self, screen):
        oy = HEADER_HEIGHT
        if self.eaten:
            self._draw_eyes(screen, oy)
            return

        color = self.color
        if self.scared:
            color = SCARED_FLASH_COLOR if self.scared_timer < 120 and self.scared_timer % 20 < 10 else SCARED_COLOR

        cx = self.pixel_x
        cy = self.pixel_y + oy
        r = CELL_SIZE // 2 - 2

        pygame.draw.circle(screen, color, (int(cx), int(cy - r // 4)), r)
        rect = pygame.Rect(cx - r, cy - r // 4, r * 2, r + r // 2)
        pygame.draw.rect(screen, color, rect)

        for i in range(3):
            wx = cx - r + i * (2 * r // 3) + r // 3
            wy = cy + r // 4 + r // 2
            pygame.draw.circle(screen, color, (int(wx), int(wy)), r // 3 + 1)

        if self.scared:
            ey = cy - r // 4
            pygame.draw.circle(screen, WHITE, (int(cx - r // 3), int(ey)), r // 4)
            pygame.draw.circle(screen, WHITE, (int(cx + r // 3), int(ey)), r // 4)
            my = cy + r // 4
            pts = []
            for i in range(6):
                mx = cx - r // 2 + i * r // 5
                mmy = my if i % 2 == 0 else my + r // 4
                pts.append((int(mx), int(mmy)))
            if len(pts) > 1:
                pygame.draw.lines(screen, WHITE, False, pts, 2)
        else:
            self._draw_eyes(screen, oy)

    def _draw_eyes(self, screen, oy):
        cx = self.pixel_x
        cy = self.pixel_y + oy
        r = CELL_SIZE // 2 - 2
        ey = cy - r // 4

        for ex in [cx - r // 3, cx + r // 3]:
            pygame.draw.circle(screen, WHITE, (int(ex), int(ey)), r // 3)
            pdx = self.direction[0] * r // 6 if self.direction != STOP else 0
            pdy = self.direction[1] * r // 6 if self.direction != STOP else 0
            pygame.draw.circle(screen, (0, 0, 200), (int(ex + pdx), int(ey + pdy)), r // 5)

    def reset_position(self):
        self.grid_x = self.start_x
        self.grid_y = self.start_y
        self.pixel_x = float(self.start_x * CELL_SIZE + CELL_SIZE // 2)
        self.pixel_y = float(self.start_y * CELL_SIZE + CELL_SIZE // 2)
        self.direction = LEFT
        self.scared = False
        self.scared_timer = 0
        self.eaten = False
        self.in_house = True
        self.release_timer = self.gid * 100
        self.mode_timer = 0


class Game:
    def __init__(self):
        self.screen = pygame.display.set_mode((WIDTH, HEIGHT))
        pygame.display.set_caption("Pac-Man Maze")
        self.clock = pygame.time.Clock()
        self.font = pygame.font.SysFont("arial", 22, bold=True)
        self.big_font = pygame.font.SysFont("arial", 44, bold=True)
        self.small_font = pygame.font.SysFont("arial", 16)
        self.current_level = 0
        self.state = "menu"
        self.maze = None
        self.pacman = None
        self.ghosts = []
        self.total_score = 0
        self.high_score = 0
        self.ready_timer = 0
        self.score_display = 0

    def init_level(self):
        self.maze = Maze(self.current_level)
        cfg = LEVEL_CONFIG[self.current_level]

        sx, sy = self.maze.find_player_start()
        old_lives = 3
        old_score = 0
        if self.pacman:
            old_lives = self.pacman.lives
            old_score = self.pacman.score

        self.pacman = PacMan(sx, sy)
        self.pacman.lives = old_lives
        self.pacman.score = old_score
        self.score_display = old_score

        self.ghosts = []
        door = self.maze.ghost_door
        if door:
            for i in range(cfg["ghost_count"]):
                g = Ghost(door[0], door[1], GHOST_COLORS[i % len(GHOST_COLORS)], i)
                g.speed = cfg["ghost_speed"]
                g.release_timer = i * 90
                self.ghosts.append(g)

        self.ready_timer = 120
        self.state = "ready"

    def handle_events(self):
        for ev in pygame.event.get():
            if ev.type == pygame.QUIT:
                pygame.quit()
                sys.exit()
            if ev.type == pygame.KEYDOWN:
                if self.state == "menu":
                    if ev.key in ENTER_KEYS:
                        self.current_level = 0
                        self.total_score = 0
                        self.init_level()
                elif self.state == "gameover":
                    if ev.key in ENTER_KEYS:
                        self.state = "menu"
                elif self.state == "levelcomplete":
                    if ev.key in ENTER_KEYS:
                        self.current_level += 1
                        if self.current_level >= 6:
                            self.total_score = self.pacman.score
                            if self.total_score > self.high_score:
                                self.high_score = self.total_score
                            self.state = "win"
                        else:
                            self.init_level()
                elif self.state == "win":
                    if ev.key in ENTER_KEYS:
                        self.state = "menu"
                elif self.state in ("playing", "ready"):
                    if ev.key in (pygame.K_UP, pygame.K_w):
                        self.pacman.set_direction(UP)
                    elif ev.key in (pygame.K_DOWN, pygame.K_s):
                        self.pacman.set_direction(DOWN)
                    elif ev.key in (pygame.K_LEFT, pygame.K_a):
                        self.pacman.set_direction(LEFT)
                    elif ev.key in (pygame.K_RIGHT, pygame.K_d):
                        self.pacman.set_direction(RIGHT)

    def update(self):
        if self.state == "ready":
            self.ready_timer -= 1
            if self.ready_timer <= 0:
                self.state = "playing"
            return
        if self.state != "playing":
            return

        self.pacman.update(self.maze)

        gx, gy = self.pacman.grid_x, self.pacman.grid_y
        result = self.maze.remove_dot(gx, gy)
        if result is not None:
            if result:
                self.pacman.score += 50
                cfg = LEVEL_CONFIG[self.current_level]
                for g in self.ghosts:
                    if not g.eaten and not g.in_house:
                        g.set_scared(cfg["power_time"])
            else:
                self.pacman.score += 10

        if not self.maze.has_dots_remaining():
            self.total_score = self.pacman.score
            if self.total_score > self.high_score:
                self.high_score = self.total_score
            self.state = "levelcomplete"
            return

        cfg = LEVEL_CONFIG[self.current_level]
        for g in self.ghosts:
            g.update(self.maze, self.pacman, cfg)

        for g in self.ghosts:
            if g.in_house or g.eaten:
                continue
            dx = self.pacman.pixel_x - g.pixel_x
            dy = self.pacman.pixel_y - g.pixel_y
            if math.sqrt(dx * dx + dy * dy) < CELL_SIZE * 0.7:
                if g.scared:
                    g.eaten = True
                    self.pacman.score += 200
                else:
                    self.pacman.lives -= 1
                    if self.pacman.lives <= 0:
                        self.total_score = self.pacman.score
                        if self.total_score > self.high_score:
                            self.high_score = self.total_score
                        self.state = "gameover"
                        return
                    sx, sy = self.maze.find_player_start()
                    self.pacman.reset_position(sx, sy)
                    for g2 in self.ghosts:
                        g2.reset_position()
                    self.ready_timer = 90
                    self.state = "ready"
                    return

    def draw(self):
        self.screen.fill(BG_COLOR)
        if self.state == "menu":
            self._draw_menu()
        elif self.state == "gameover":
            self._draw_gameover()
        elif self.state == "win":
            self._draw_win()
        else:
            self._draw_game()
            if self.state == "ready":
                t = self.big_font.render("READY!", True, YELLOW)
                self.screen.blit(t, (WIDTH // 2 - t.get_width() // 2, HEIGHT // 2 - 20))
            elif self.state == "levelcomplete":
                self._draw_level_complete()
        pygame.display.flip()

    def _draw_menu(self):
        t = self.big_font.render("PAC-MAN MAZE", True, YELLOW)
        self.screen.blit(t, (WIDTH // 2 - t.get_width() // 2, HEIGHT // 4))
        for i in range(min(5, len(GHOST_COLORS))):
            pygame.draw.circle(self.screen, GHOST_COLORS[i], (WIDTH // 2 - 60 + i * 30, HEIGHT // 2 - 30), 12)
        st = self.font.render("Press ENTER to Start", True, WHITE)
        self.screen.blit(st, (WIDTH // 2 - st.get_width() // 2, HEIGHT // 2 + 30))
        i1 = self.small_font.render("Arrow Keys / WASD to Move", True, DOT_COLOR)
        self.screen.blit(i1, (WIDTH // 2 - i1.get_width() // 2, HEIGHT // 2 + 70))
        i2 = self.small_font.render("6 Levels - Increasing Difficulty!", True, DOT_COLOR)
        self.screen.blit(i2, (WIDTH // 2 - i2.get_width() // 2, HEIGHT // 2 + 100))
        if self.high_score > 0:
            hs = self.font.render(f"High Score: {self.high_score}", True, (255, 184, 82))
            self.screen.blit(hs, (WIDTH // 2 - hs.get_width() // 2, HEIGHT // 2 + 140))

    def _draw_gameover(self):
        t = self.big_font.render("GAME OVER", True, (255, 0, 0))
        self.screen.blit(t, (WIDTH // 2 - t.get_width() // 2, HEIGHT // 3))
        s = self.font.render(f"Final Score: {self.total_score}", True, WHITE)
        self.screen.blit(s, (WIDTH // 2 - s.get_width() // 2, HEIGHT // 2))
        r = self.font.render("Press ENTER to Return", True, DOT_COLOR)
        self.screen.blit(r, (WIDTH // 2 - r.get_width() // 2, HEIGHT // 2 + 50))

    def _draw_win(self):
        t = self.big_font.render("YOU WIN!", True, YELLOW)
        self.screen.blit(t, (WIDTH // 2 - t.get_width() // 2, HEIGHT // 3))
        s = self.font.render(f"Final Score: {self.total_score}", True, WHITE)
        self.screen.blit(s, (WIDTH // 2 - s.get_width() // 2, HEIGHT // 2))
        c = self.font.render("All 6 Levels Completed!", True, (0, 255, 0))
        self.screen.blit(c, (WIDTH // 2 - c.get_width() // 2, HEIGHT // 2 + 40))
        r = self.font.render("Press ENTER to Return", True, DOT_COLOR)
        self.screen.blit(r, (WIDTH // 2 - r.get_width() // 2, HEIGHT // 2 + 90))

    def _draw_level_complete(self):
        ov = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
        ov.fill((0, 0, 0, 150))
        self.screen.blit(ov, (0, 0))
        t = self.big_font.render(f"LEVEL {self.current_level + 1} CLEAR!", True, (0, 255, 0))
        self.screen.blit(t, (WIDTH // 2 - t.get_width() // 2, HEIGHT // 3))
        s = self.font.render(f"Score: {self.pacman.score}", True, WHITE)
        self.screen.blit(s, (WIDTH // 2 - s.get_width() // 2, HEIGHT // 2))
        if self.current_level < 5:
            nt = self.font.render("Press ENTER for Next Level", True, DOT_COLOR)
        else:
            nt = self.font.render("Press ENTER to Continue", True, DOT_COLOR)
        self.screen.blit(nt, (WIDTH // 2 - nt.get_width() // 2, HEIGHT // 2 + 50))

    def _draw_game(self):
        self._draw_header()
        self._draw_maze()
        self.pacman.draw(self.screen)
        for g in self.ghosts:
            g.draw(self.screen)

    def _draw_header(self):
        pygame.draw.rect(self.screen, (20, 20, 20), (0, 0, WIDTH, HEADER_HEIGHT))
        st = self.font.render(f"SCORE: {self.pacman.score}", True, WHITE)
        self.screen.blit(st, (10, 5))
        lt = self.font.render(f"LEVEL: {self.current_level + 1}/6", True, YELLOW)
        self.screen.blit(lt, (WIDTH // 2 - lt.get_width() // 2, 5))
        for i in range(self.pacman.lives):
            cx = WIDTH - 25 - i * 28
            cy = 18
            pygame.draw.circle(self.screen, PACMAN_COLOR, (cx, cy), 10)
        cfg = LEVEL_CONFIG[self.current_level]
        sp = self.small_font.render(f"Speed: {cfg['ghost_speed']:.1f}  Ghosts: {cfg['ghost_count']}  Dots: {len(self.maze.dots)}", True, DOT_COLOR)
        self.screen.blit(sp, (10, 38))

    def _draw_maze(self):
        oy = HEADER_HEIGHT
        for y in range(ROWS):
            for x in range(COLS):
                px = x * CELL_SIZE
                py = y * CELL_SIZE + oy
                if (x, y) in self.maze.walls:
                    pygame.draw.rect(self.screen, WALL_FILL, (px, py, CELL_SIZE, CELL_SIZE))
                    for dx, dy in DIRECTIONS:
                        nx, ny = x + dx, y + dy
                        if 0 <= nx < COLS and 0 <= ny < ROWS and (nx, ny) not in self.maze.walls:
                            if dx == 0 and dy == -1:
                                pygame.draw.line(self.screen, WALL_COLOR, (px, py), (px + CELL_SIZE, py), 2)
                            elif dx == 0 and dy == 1:
                                pygame.draw.line(self.screen, WALL_COLOR, (px, py + CELL_SIZE), (px + CELL_SIZE, py + CELL_SIZE), 2)
                            elif dx == -1 and dy == 0:
                                pygame.draw.line(self.screen, WALL_COLOR, (px, py), (px, py + CELL_SIZE), 2)
                            elif dx == 1 and dy == 0:
                                pygame.draw.line(self.screen, WALL_COLOR, (px + CELL_SIZE, py), (px + CELL_SIZE, py + CELL_SIZE), 2)
                elif (x, y) in self.maze.dots:
                    if (x, y) in self.maze.power_dots:
                        r = 5 + int(math.sin(pygame.time.get_ticks() / 200)) * 2
                        pygame.draw.circle(self.screen, POWER_DOT_COLOR, (px + CELL_SIZE // 2, py + CELL_SIZE // 2), r)
                    else:
                        pygame.draw.circle(self.screen, DOT_COLOR, (px + CELL_SIZE // 2, py + CELL_SIZE // 2), 2)
                elif self.maze.ghost_door and (x, y) == self.maze.ghost_door:
                    pygame.draw.rect(self.screen, DOOR_COLOR, (px + 2, py + CELL_SIZE // 2 - 2, CELL_SIZE - 4, 4))

    def run(self):
        while True:
            self.handle_events()
            self.update()
            self.draw()
            self.clock.tick(60)


if __name__ == "__main__":
    game = Game()
    game.run()
