import pygame

# ===================== 基础配置 =====================
TILE_SIZE = 48
MAP_WIDTH = 14
MAP_HEIGHT = 10
WIDTH = TILE_SIZE * MAP_WIDTH
HEIGHT = TILE_SIZE * MAP_HEIGHT
FPS = 60

# 方块标识
EMPTY = 0
WALL = 1
PLAYER = 2
BOX = 3
TARGET = 4
BOX_ON_TARGET = 5
PLAYER_ON_TARGET = 6

# 配色
COLOR_BG = (30, 30, 40)
COLOR_WALL = (70, 70, 90)
COLOR_FLOOR = (210, 200, 180)
COLOR_TARGET = (220, 120, 60)
COLOR_PLAYER = (60, 180, 240)
COLOR_BOX = (150, 100, 60)
COLOR_BOX_OK = (60, 200, 100)

pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("推箱子 Sokoban")
clock = pygame.time.Clock()
font = pygame.font.Font(None, 38)

# 关卡地图
level_map = [
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,0,0,0,1,1,1,1,0,0,0,0,0,1],
    [1,0,0,0,1,0,0,1,0,0,0,0,0,1],
    [1,0,0,0,1,0,0,1,1,1,1,0,0,1],
    [1,1,1,1,1,0,0,0,0,0,1,0,0,1],
    [1,0,0,0,0,0,3,0,3,0,1,0,0,1],
    [1,0,0,0,1,0,0,2,0,0,1,0,0,1],
    [1,0,0,0,1,0,4,0,4,0,1,0,0,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1]
]

def copy_map(src):
    """复制地图，用于重置关卡"""
    return [row.copy() for row in src]

class SokobanGame:
    def __init__(self):
        self.map_data = copy_map(level_map)
        self.find_player()
        self.win = False

    def find_player(self):
        for y in range(MAP_HEIGHT):
            for x in range(MAP_WIDTH):
                if self.map_data[y][x] in (PLAYER, PLAYER_ON_TARGET):
                    self.px, self.py = x, y
                    return

    def check_win(self):
        """检查所有箱子是否都在目标点"""
        for y in range(MAP_HEIGHT):
            for x in range(MAP_WIDTH):
                if self.map_data[y][x] == BOX:
                    return False
        return True

    def move(self, dx, dy):
        if self.win:
            return
        nx = self.px + dx
        ny = self.py + dy
        next_tile = self.map_data[ny][nx]

        # 撞到墙壁，禁止移动
        if next_tile == WALL:
            return

        # 前方是箱子，需要继续看箱子下一格
        if next_tile in (BOX, BOX_ON_TARGET):
            box_nx = nx + dx
            box_ny = ny + dy
            box_next = self.map_data[box_ny][box_nx]
            # 箱子下一格是墙或另一个箱子，推不动
            if box_next in (WALL, BOX, BOX_ON_TARGET):
                return

            # 移动箱子
            if box_next == TARGET:
                self.map_data[box_ny][box_nx] = BOX_ON_TARGET
            else:
                self.map_data[box_ny][box_nx] = BOX

            # 原来箱子位置恢复地面/目标
            if next_tile == BOX_ON_TARGET:
                self.map_data[ny][nx] = TARGET
            else:
                self.map_data[ny][nx] = EMPTY

        # 移动玩家
        if self.map_data[self.py][self.px] == PLAYER_ON_TARGET:
            self.map_data[self.py][self.px] = TARGET
        else:
            self.map_data[self.py][self.px] = EMPTY

        if self.map_data[ny][nx] == TARGET:
            self.map_data[ny][nx] = PLAYER_ON_TARGET
        else:
            self.map_data[ny][nx] = PLAYER

        self.px, self.py = nx, ny
        if self.check_win():
            self.win = True

    def reset(self):
        self.map_data = copy_map(level_map)
        self.find_player()
        self.win = False

    def draw(self):
        screen.fill(COLOR_BG)
        for y in range(MAP_HEIGHT):
            for x in range(MAP_WIDTH):
                tile = self.map_data[y][x]
                rect = pygame.Rect(x*TILE_SIZE, y*TILE_SIZE, TILE_SIZE, TILE_SIZE)

                # 基础地板
                pygame.draw.rect(screen, COLOR_FLOOR, rect)

                if tile == WALL:
                    pygame.draw.rect(screen, COLOR_WALL, rect)
                elif tile == TARGET:
                    pygame.draw.circle(screen, COLOR_TARGET, rect.center, TILE_SIZE//4)
                elif tile == BOX:
                    pygame.draw.rect(screen, COLOR_BOX, rect.inflate(-6,-6), border_radius=6)
                elif tile == BOX_ON_TARGET:
                    pygame.draw.rect(screen, COLOR_BOX_OK, rect.inflate(-6,-6), border_radius=6)
                elif tile == PLAYER:
                    pygame.draw.circle(screen, COLOR_PLAYER, rect.center, TILE_SIZE//3)
                elif tile == PLAYER_ON_TARGET:
                    pygame.draw.circle(screen, COLOR_PLAYER, rect.center, TILE_SIZE//3)
                    pygame.draw.circle(screen, COLOR_TARGET, rect.center, TILE_SIZE//4, width=2)

        # 通关文字
        if self.win:
            text = font.render("🎉 恭喜通关！", True, (255,255,80))
            screen.blit(text, (WIDTH//2 - 110, 10))
        tip = font.render("方向键移动 | R重置关卡", True, (255,255,255))
        screen.blit(tip, (10, 10))

game = SokobanGame()
running = True
while running:
    clock.tick(FPS)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_UP:
                game.move(0, -1)
            elif event.key == pygame.K_DOWN:
                game.move(0, 1)
            elif event.key == pygame.K_LEFT:
                game.move(-1, 0)
            elif event.key == pygame.K_RIGHT:
                game.move(1, 0)
            elif event.key == pygame.K_r:
                game.reset()

    game.draw()
    pygame.display.update()

pygame.quit()