import tkinter as tk
import random
import math
import copy

# ========================
# 全局配置
# ========================
CELL = 56
GRID_W, GRID_H = 12, 10
WIDTH = GRID_W * CELL + 240
HEIGHT = GRID_H * CELL + 100

# ========================
# 兵种定义
# ========================
UNIT_TYPES = {
    "infantry":  {"name": "步兵", "icon": "🪖", "hp": 10, "atk": 3, "def": 1, "mov": 3, "cost": 5,   "color": "#8D6E63"},
    "archer":    {"name": "弓箭手", "icon": "🏹", "hp": 7,  "atk": 4, "def": 0, "mov": 3, "cost": 8,   "color": "#66BB6A"},
    "cavalry":   {"name": "骑兵", "icon": "🐴", "hp": 14, "atk": 5, "def": 2, "mov": 5, "cost": 12,  "color": "#42A5F5"},
    "knight":    {"name": "骑士", "icon": "⚔️", "hp": 18, "atk": 6, "def": 3, "mov": 4, "cost": 15,  "color": "#FFA726"},
    "mage":      {"name": "法师", "icon": "🔮", "hp": 8,  "atk": 7, "def": 0, "mov": 3, "cost": 15,  "color": "#AB47BC"},
    "catapult":  {"name": "投石车", "icon": "🎯", "hp": 12, "atk": 8, "def": 1, "mov": 2, "cost": 18,  "color": "#78909C"},
    "healer":    {"name": "牧师", "icon": "✚", "hp": 10, "atk": 1, "def": 1, "mov": 3, "cost": 10,  "color": "#EF5350"},
    "spy":       {"name": "侦察兵", "icon": "🦅", "hp": 6,  "atk": 2, "def": 0, "mov": 6, "cost": 6,   "color": "#D4E157"},
}

# ========================
# 地形定义
# ========================
TERRAIN = {
    "plain":   {"name": "平原", "icon": "",    "def_bonus": 0,  "mov_cost": 1, "color": "#AED581"},
    "forest":  {"name": "森林", "icon": "🌲", "def_bonus": 2,  "mov_cost": 2, "color": "#388E3C"},
    "mountain":{"name": "山地", "icon": "⛰️", "def_bonus": 3,  "mov_cost": 3, "color": "#795548"},
    "water":   {"name": "水域", "icon": "🌊", "def_bonus": 0,  "mov_cost": 99,"color": "#42A5F5"},
    "road":    {"name": "道路", "icon": "━",  "def_bonus": 0,  "mov_cost": 1, "color": "#D7CCC8"},
    "city":    {"name": "城池", "icon": "🏰", "def_bonus": 4,  "mov_cost": 1, "color": "#FF8A65"},
    "bridge":  {"name": "桥梁", "icon": "═",  "def_bonus": 0,  "mov_cost": 1, "color": "#A1887F"},
}

# ========================
# 主题
# ========================
THEMES = {
    "🗺️ 古典地图": {"bg": "#D7CCC8", "fg": "#3E2723", "panel": "#BCAAA4", "btn": "#8D6E63", "accent": "#5D4037", "grid": "#A1887F", "log": "#EFEBE9"},
    "🌙 暗夜战场": {"bg": "#263238", "fg": "#ECEFF1", "panel": "#37474F", "btn": "#546E7A", "accent": "#FF6E40", "grid": "#455A64", "log": "#1C262B"},
    "🏜️ 沙漠之狐": {"bg": "#FFF3E0", "fg": "#4E342E", "panel": "#FFE0B2", "btn": "#FF8F00", "accent": "#BF360C", "grid": "#FFB74D", "log": "#FFF8E1"},
    "❄️ 冰雪战场": {"bg": "#E3F2FD", "fg": "#0D47A1", "panel": "#BBDEFB", "btn": "#1976D2", "accent": "#D32F2F", "grid": "#90CAF9", "log": "#E1F5FE"},
    "🌸 樱花战场": {"bg": "#FFF0F5", "fg": "#880E4F", "panel": "#F8BBD0", "btn": "#C2185B", "accent": "#4A148C", "grid": "#F48FB1", "log": "#FCE4EC"},
    "🔥 烈焰战场": {"bg": "#FBE9E7", "fg": "#BF360C", "panel": "#FFAB91", "btn": "#E64A19", "accent": "#FF3D00", "grid": "#FF8A65", "log": "#FFF3E0"},
}
cur_theme = "🗺️ 古典地图"

# ========================
# 游戏状态
# ========================
grid = []         # 地形网格
units = []        # 所有单位
cur_player = 0    # 当前玩家 0=红 1=蓝
phase = "move"    # move / attack / end
selected = None   # 选中单位
move_range = []   # 可移动范围
attack_range = [] # 可攻击范围
turn = 1
max_turns = 30
gold = [50, 50]  # 双方金币
unit_id_counter = 0
animations = []
game_over = False
winner = None

# ========================
# UI 引用
# ========================
root = None
canvas = None
lbl_turn = None
lbl_phase = None
lbl_gold = None
lbl_info = None
lbl_unit_info = None
lbl_log = None
panel_frame = None
theme_btns = []
all_widgets = []

# ========================
# 工具
# ========================
def get_unit_at(x, y):
    for u in units:
        if u["x"] == x and u["y"] == y and u["hp"] > 0:
            return u
    return None

def manhattan(ax, ay, bx, by):
    return abs(ax - bx) + abs(ay - by)

def get_terrain(x, y):
    if 0 <= x < GRID_W and 0 <= y < GRID_H:
        return grid[y][x]
    return "water"

# ========================
# 地图生成
# ========================
def generate_map():
    """生成地图"""
    global grid
    grid = []
    for y in range(GRID_H):
        row = []
        for x in range(GRID_W):
            r = random.random()
            if x == 0 or x == GRID_W - 1 or y == 0 or y == GRID_H - 1:
                row.append("water")
            elif r < 0.15:
                row.append("forest")
            elif r < 0.25:
                row.append("mountain")
            elif r < 0.30:
                row.append("water")
            elif r < 0.33:
                row.append("road")
            else:
                row.append("plain")
        grid.append(row)

    # 确保中间有路
    mid_y = GRID_H // 2
    for x in range(GRID_W):
        grid[mid_y][x] = "road" if random.random() < 0.7 else "plain"

    # 城池
    grid[1][1] = "city"
    grid[GRID_H-2][GRID_W-2] = "city"
    grid[1][GRID_W//2] = "city"
    grid[GRID_H-2][GRID_W//2] = "city"

def spawn_initial_units():
    """初始兵力"""
    global units, unit_id_counter
    units = []

    # 红方（左）
    for i in range(3):
        add_unit(0, 1, 1 + i, "infantry")
    add_unit(0, 2, 2, "archer")
    add_unit(0, 1, 3, "cavalry")
    add_unit(0, 3, 1, "knight")

    # 蓝方（右）
    for i in range(3):
        add_unit(1, GRID_W-2, 1 + i, "infantry")
    add_unit(1, GRID_W-3, 2, "archer")
    add_unit(1, GRID_W-2, 3, "cavalry")
    add_unit(1, GRID_W-4, 1, "knight")

def add_unit(player, x, y, utype):
    global unit_id_counter
    t = UNIT_TYPES[utype]
    u = {
        "id": unit_id_counter,
        "player": player,
        "x": x, "y": y,
        "type": utype,
        "hp": t["hp"], "max_hp": t["hp"],
        "atk": t["atk"], "def": t["def"],
        "mov": t["mov"], "max_mov": t["mov"],
        "acted": False,
    }
    unit_id_counter += 1
    units.append(u)
    return u

# ========================
# 战斗计算
# ========================
def calc_damage(attacker, defender):
    """计算伤害"""
    ax, ay = attacker["x"], attacker["y"]
    dx, dy = defender["x"], defender["y"]
    dist = manhattan(ax, ay, dx, dy)

    atk_type = UNIT_TYPES[attacker["type"]]
    base_atk = atk_type["atk"]

    # 近战/远程
    if atk_type["type"] in ("archer", "mage", "catapult"):
        range_max = 4 if atk_type["type"] == "archer" else (5 if atk_type["type"] == "mage" else 6)
        if dist > range_max:
            return 0
        dmg = base_atk
    else:
        if dist > 1:
            return 0
        dmg = base_atk

    # 地形防御加成
    terr = get_terrain(defender["x"], defender["y"])
    def_bonus = TERRAIN[terr]["def_bonus"]

    # 兵种克制
    counter = 1.0
    at = attacker["type"]
    dt = defender["type"]
    if at == "cavalry" and dt == "infantry": counter = 1.5
    if at == "archer" and dt == "cavalry": counter = 1.5
    if at == "mage" and dt == "infantry": counter = 1.3
    if at == "infantry" and dt == "archer": counter = 1.2

    # 防御方防御力
    dmg = max(1, int(dmg * counter - (defender["def"] + def_bonus)))

    # 暴击 (10%)
    crit = random.random() < 0.1
    if crit:
        dmg = int(dmg * 1.5)

    return dmg, crit

# ========================
# 移动范围
# ========================
def calc_move_range(unit):
    """BFS 计算可移动范围"""
    x, y = unit["x"], unit["y"]
    mov = unit["mov"]
    visited = {(x, y): 0}
    queue = [(x, y, 0)]
    result = []

    while queue:
        cx, cy, cost = queue.pop(0)
        if cost > 0:
            result.append((cx, cy))
        if cost >= mov:
            continue
        for dx, dy in [(0,1),(0,-1),(1,0),(-1,0)]:
            nx, ny = cx+dx, cy+dy
            if not (0 <= nx < GRID_W and 0 <= ny < GRID_H):
                continue
            terr = get_terrain(nx, ny)
            mc = TERRAIN[terr]["mov_cost"]
            if mc > 10:  # 不可通行
                continue
            new_cost = cost + mc
            if new_cost > mov:
                continue
            if (nx, ny) not in visited or visited[(nx, ny)] > new_cost:
                visited[(nx, ny)] = new_cost
                queue.append((nx, ny, new_cost))
                if not get_unit_at(nx, ny) or (nx == x and ny == y):
                    pass

    # 过滤掉有敌方/友方单位的格子（除了自己）
    final = []
    for nx, ny in result:
        u = get_unit_at(nx, ny)
        if u is None or u["id"] == unit["id"]:
            final.append((nx, ny))
    return final

def calc_attack_range(unit):
    """可攻击范围"""
    x, y = unit["x"], unit["y"]
    atype = UNIT_TYPES[unit["type"]]
    is_ranged = unit["type"] in ("archer", "mage", "catapult")

    if is_ranged:
        max_d = {"archer": 4, "mage": 5, "catapult": 6}[unit["type"]]
        result = []
        for dx in range(-max_d, max_d + 1):
            for dy in range(-max_d, max_d + 1):
                nx, ny = x + dx, y + dy
                if 0 <= nx < GRID_W and 0 <= ny < GRID_H:
                    if 1 <= abs(dx) + abs(dy) <= max_d:
                        u = get_unit_at(nx, ny)
                        if u and u["player"] != unit["player"]:
                            result.append((nx, ny))
        return result
    else:
        result = []
        for dx, dy in [(0,1),(0,-1),(1,0),(-1,0)]:
            nx, ny = x+dx, y+dy
            if 0 <= nx < GRID_W and 0 <= ny < GRID_H:
                u = get_unit_at(nx, ny)
                if u and u["player"] != unit["player"]:
                    result.append((nx, ny))
        return result

# ========================
# 绘制
# ========================
def draw():
    """绘制整个游戏"""
    canvas.delete("all")
    t = THEMES[cur_theme]

    # 背景
    canvas.create_rectangle(0, 0, WIDTH, HEIGHT, fill=t["bg"], outline="")

    # 地形
    for y in range(GRID_H):
        for x in range(GRID_W):
            terr = grid[y][x]
            tc = TERRAIN[terr]["color"]
            px, py = x * CELL, y * CELL + 30
            canvas.create_rectangle(px, py, px+CELL, py+CELL, fill=tc, outline=t["grid"], width=1)

            icon = TERRAIN[terr]["icon"]
            if icon:
                canvas.create_text(px+CELL//2, py+CELL//2, text=icon,
                                   font=("", 16), fill=t["fg"])

    # 网格线
    for x in range(GRID_W + 1):
        px = x * CELL
        canvas.create_line(px, 30, px, GRID_H*CELL+30, fill=t["grid"], width=1)
    for y in range(GRID_H + 1):
        py = y * CELL + 30
        canvas.create_line(0, py, GRID_W*CELL, py, fill=t["grid"], width=1)

    # 移动范围
    for mx, my in move_range:
        px, py = mx * CELL, my * CELL + 30
        canvas.create_rectangle(px+2, py+2, px+CELL-2, py+CELL-2,
                                outline="#4CAF50", width=2, dash=(4, 2))

    # 攻击范围
    for ax, ay in attack_range:
        px, py = ax * CELL, ay * CELL + 30
        canvas.create_rectangle(px+2, py+2, px+CELL-2, py+CELL-2,
                                outline="#F44336", width=2, dash=(4, 2))

    # 单位
    for u in units:
        if u["hp"] <= 0:
            continue
        px = u["x"] * CELL + CELL // 2
        py = u["y"] * CELL + 30 + CELL // 2

        # 选中高亮
        if selected and selected["id"] == u["id"]:
            canvas.create_oval(px-CELL//2+2, py-CELL//2+2, px+CELL//2-2, py+CELL//2-2,
                                outline="#FFEB3B", width=3)

        # 兵种图标
        icon = UNIT_TYPES[u["type"]]["icon"]
        player_color = "#F44336" if u["player"] == 0 else "#2196F3"
        canvas.create_text(px, py - 5, text=icon, font=("", 20))

        # 血条
        hp_pct = u["hp"] / u["max_hp"]
        bar_w = CELL - 12
        bx = px - bar_w // 2
        by = py + 12
        canvas.create_rectangle(bx, by, bx + bar_w, by + 4, fill="#333", outline="")
        hp_color = "#4CAF50" if hp_pct > 0.5 else "#FF9800" if hp_pct > 0.25 else "#F44336"
        canvas.create_rectangle(bx, by, bx + int(bar_w * hp_pct), by + 4, fill=hp_color, outline="")

        # 玩家标识
        canvas.create_text(px, py - CELL//2 + 6, text="●", font=("", 8), fill=player_color)

        # 已行动标记
        if u["acted"]:
            canvas.create_text(px + CELL//2 - 6, py - CELL//2 + 6, text="✓", font=("", 8), fill="#FFEB3B")

    # 动画
    draw_animations()

    # 坐标提示
    canvas.create_text(WIDTH - 50, 15, text=f"回合 {turn}/{max_turns}",
                       font=("Comic Sans MS", 10, "bold"), fill=t["accent"])

    # Game Over
    if game_over:
        canvas.create_rectangle(WIDTH//2-150, HEIGHT//2-50, WIDTH//2+150, HEIGHT//2+50,
                                fill="#000", outline="#F44336", width=3)
        txt = f"🏆 玩家{winner+1} 胜利！" if winner is not None else "🤝 平局！"
        canvas.create_text(WIDTH//2, HEIGHT//2, text=txt,
                           font=("Comic Sans MS", 20, "bold"), fill="#FFEB3B")

def draw_animations():
    """绘制动画"""
    for anim in animations[:]:
        anim["life"] -= 1
        alpha = anim["life"] / anim["max_life"]

        if anim["type"] == "attack":
            x1, y1 = anim["from"]
            x2, y2 = anim["to"]
            t = 1 - alpha
            cx = x1 + (x2 - x1) * t
            cy = y1 + (y2 - y1) * t
            color = anim.get("color", "#FFEB3B")
            size = int(8 * alpha) + 2
            canvas.create_oval(cx-size, cy-size, cx+size, cy+size, fill=color, outline="")

        elif anim["type"] == "damage":
            x, y = anim["pos"]
            size = int(15 * alpha) + 5
            canvas.create_text(x, y - size, text=anim["text"],
                               font=("Comic Sans MS", 12, "bold"), fill=anim.get("color", "#F44336"))

        elif anim["type"] == "explosion":
            x, y = anim["pos"]
            r = int((1 - alpha) * 30) + 5
            color = anim.get("color", "#FF5722")
            canvas.create_oval(x-r, y-r, x+r, y+r, fill="", outline=color, width=2)

        if anim["life"] <= 0:
            animations.remove(anim)

# ========================
# 日志
# ========================
log_lines = []

def log(msg):
    global log_lines
    log_lines.append(msg)
    if len(log_lines) > 8:
        log_lines.pop(0)
    if lbl_log:
        lbl_log.config(state="normal")
        lbl_log.delete("1.0", "end")
        for line in log_lines:
            lbl_log.insert("end", line + "\n")
        lbl_log.config(state="disabled")

# ========================
# 点击处理
# ========================
def on_click(e):
    global selected, move_range, attack_range, phase

    if game_over:
        return

    gx = e.x // CELL
    gy = (e.y - 30) // CELL

    if not (0 <= gx < GRID_W and 0 <= gy < GRID_H):
        return

    clicked_unit = get_unit_at(gx, gy)

    if phase == "move":
        if selected is None:
            # 选单位
            if clicked_unit and clicked_unit["player"] == cur_player and not clicked_unit["acted"]:
                selected = clicked_unit
                move_range = calc_move_range(selected)
                attack_range = []
                update_unit_info(selected)
        else:
            # 尝试移动
            if (gx, gy) in move_range:
                selected["x"] = gx
                selected["y"] = gy
                move_range = []
                attack_range = calc_attack_range(selected)
                phase = "attack"
                log(f"🚶 {UNIT_TYPES[selected['type']]['name']} 移动到 ({gx},{gy})")
            elif clicked_unit and clicked_unit["player"] == cur_player:
                selected = clicked_unit
                move_range = calc_move_range(selected)
                attack_range = []
                update_unit_info(selected)
            else:
                selected = None
                move_range = []

    elif phase == "attack":
        if (gx, gy) in attack_range:
            defender = get_unit_at(gx, gy)
            if defender:
                do_attack(selected, defender)
                selected["acted"] = True
                attack_range = []
                move_range = []
                selected = None
                phase = "move"
                check_win()
                update_unit_info(None)
        else:
            # 取消或选其他
            selected = None
            attack_range = []
            move_range = []
            phase = "move"

    draw()

def do_attack(attacker, defender):
    """执行攻击"""
    result = calc_damage(attacker, defender)
    if result == 0:
        log(f"❌ {UNIT_TYPES[attacker['type']]['name']} 无法攻击该目标")
        return

    dmg, crit = result
    defender["hp"] -= dmg

    ax = attacker["x"] * CELL + CELL // 2
    ay = attacker["y"] * CELL + 30 + CELL // 2
    dx = defender["x"] * CELL + CELL // 2
    dy = defender["y"] * CELL + 30 + CELL // 2

    animations.append({"type": "attack", "from": (ax, ay), "to": (dx, dy),
                       "color": "#FFEB3B", "life": 15, "max_life": 15})
    animations.append({"type": "damage", "pos": (dx, dy - 10),
                       "text": f"-{dmg}{'!' if crit else ''}", "color": "#FF5722" if crit else "#F44336",
                       "life": 25, "max_life": 25})

    if crit:
        log(f"💥 暴击！{UNIT_TYPES[attacker['type']]['name']} 对 {UNIT_TYPES[defender['type']]['name']} 造成 {dmg} 伤害！")
    else:
        log(f"⚔️ {UNIT_TYPES[attacker['type']]['name']} → {UNIT_TYPES[defender['type']]['name']} 造成 {dmg} 伤害")

    if defender["hp"] <= 0:
        log(f"💀 {UNIT_TYPES[defender['type']]['name']} 被击毁！")
        animations.append({"type": "explosion", "pos": (dx, dy),
                            "color": "#FF5722", "life": 20, "max_life": 20})

    update_sidebar()

# ========================
# 回合管理
# ========================
def end_turn():
    """结束回合"""
    global cur_player, turn, phase, selected, move_range, attack_range

    # 金币产出
    gold[cur_player] += 10
    for u in units:
        if u["player"] == cur_player and u["hp"] > 0:
            u["acted"] = False
            u["mov"] = u["max_mov"]

    cur_player = 1 - cur_player
    selected = None
    move_range = []
    attack_range = []
    phase = "move"

    if cur_player == 0:
        turn += 1

    log(f"🔄 回合 {turn} 开始 | 玩家{cur_player+1} 行动")
    update_sidebar()
    draw()

def check_win():
    """检查胜负"""
    global game_over, winner
    red_alive = any(u["hp"] > 0 and u["player"] == 0 for u in units)
    blue_alive = any(u["hp"] > 0 and u["player"] == 1 for u in units)

    if not red_alive:
        game_over = True
        winner = 1
        log("🏆 蓝方胜利！")
    elif not blue_alive:
        game_over = True
        winner = 0
        log("🏆 红方胜利！")
    elif turn >= max_turns:
        game_over = True
        winner = None
        log("🤝 回合耗尽，平局！")

# ========================
# 招募
# ========================
def recruit(utype):
    """招募单位"""
    global gold
    cost = UNIT_TYPES[utype]["cost"]
    if gold[cur_player] < cost:
        log(f"❌ 金币不足（需要 {cost}，当前 {gold[cur_player]}）")
        return

    # 找己方城池
    spawn_points = []
    for u in units:
        if u["player"] == cur_player and UNIT_TYPES[u["type"]]["name"] == "城池":
            pass
    # 找己方阵地
    for y in range(GRID_H):
        for x in range(GRID_W):
            if get_terrain(x, y) == "city":
                u = get_unit_at(x, y)
                if not u:
                    spawn_points.append((x, y))

    if not spawn_points:
        # 找己方任意空格
        for y in range(GRID_H):
            for x in range(GRID_W):
                if 0 <= x < GRID_W and 0 <= y < GRID_H:
                    u = get_unit_at(x, y)
                    if not u:
                        terr = get_terrain(x, y)
                        if terr not in ("water", "mountain") and u is None:
                            # 检查附近有己方单位
                            near_own = False
                            for dx, dy in [(0,1),(0,-1),(1,0),(-1,0)]:
                                nu = get_unit_at(x+dx, y+dy)
                                if nu and nu["player"] == cur_player:
                                    near_own = True
                                    break
                            if near_own:
                                spawn_points.append((x, y))

    if not spawn_points:
        log("❌ 没有可招募的位置（需要在己方单位旁边）")
        return

    sx, sy = spawn_points[0]
    add_unit(cur_player, sx, sy, utype)
    gold[cur_player] -= cost
    log(f"✅ 招募 {UNIT_TYPES[utype]['name']}（花费 {cost} 金币）")
    update_sidebar()
    draw()

# ========================
# 侧边栏
# ========================
def update_sidebar():
    """更新侧边栏"""
    t = THEMES[cur_theme]
    lbl_turn.config(text=f"🔄 回合 {turn}/{max_turns}")
    player_name = "红方 🔴" if cur_player == 0 else "蓝方 🔵"
    lbl_phase.config(text=f"当前：{player_name} | 阶段：{phase}")
    lbl_gold.config(text=f"💰 红方:{gold[0]}  蓝方:{gold[1]}")

def update_unit_info(unit):
    """更新单位信息"""
    if unit is None:
        lbl_unit_info.config(text="")
        return
    t = UNIT_TYPES[unit["type"]]
    txt = f"📋 {t['icon']} {t['name']}\n"
    txt += f"❤️ HP: {unit['hp']}/{unit['max_hp']}\n"
    txt += f"⚔️ ATK: {unit['atk']}  🛡️ DEF: {unit['def']}\n"
    txt += f"👟 MOV: {unit['mov']}/{unit['max_mov']}"
    lbl_unit_info.config(text=txt)

# ========================
# 换肤
# ========================
def apply_theme(name):
    """换肤"""
    global cur_theme
    cur_theme = name
    t = THEMES[name]

    root.config(bg=t["bg"])
    for w in all_widgets:
        try:
            w.config(bg=t["bg"], fg=t["fg"])
        except:
            pass

    for btn in theme_btns:
        btn.config(bg=t["btn"], fg="white")

    if panel_frame:
        panel_frame.config(bg=t["panel"])
    for child in panel_frame.winfo_children() if panel_frame else []:
        try:
            child.config(bg=t["panel"], fg=t["fg"])
        except:
            pass

    draw()

# ========================
# 主循环
# ========================
def game_loop():
    """主循环"""
    draw()
    root.after(50, game_loop)

# ========================
# 构建界面
# ========================
def build_ui():
    """构建界面"""
    global root, canvas, lbl_turn, lbl_phase, lbl_gold, lbl_info
    global lbl_unit_info, lbl_log, panel_frame
    global theme_btns, all_widgets

    root = tk.Tk()
    root.title("⚔️ 战争兵棋推演")
    root.geometry(f"{WIDTH}x{HEIGHT}")
    root.resizable(False, False)

    # ====== 顶部状态栏 ======
    top = tk.Frame(root, height=30)
    top.pack(fill="x", side="top")

    lbl_turn = tk.Label(top, text="🔄 回合 1/30", font=("Comic Sans MS", 12, "bold"))
    lbl_turn.pack(side="left", padx=10)

    lbl_phase = tk.Label(top, text="当前：红方 🔴 | 阶段：move", font=("Comic Sans MS", 11))
    lbl_phase.pack(side="left", padx=10)

    lbl_gold = tk.Label(top, text="💰 红方:50  蓝方:50", font=("Comic Sans MS", 11, "bold"))
    lbl_gold.pack(side="left", padx=10)

    btn_end = tk.Button(top, text="⏭️ 结束回合", font=("Comic Sans MS", 10, "bold"),
                         bg="#F44336", fg="white", command=end_turn)
    btn_end.pack(side="right", padx=10)

    # ====== 主题栏 ======
    theme_bar = tk.Frame(root)
    theme_bar.pack(fill="x", pady=1)
    tk.Label(theme_bar, text="🎨 ", font=("", 8)).pack(side="left", padx=3)
    for name in THEMES:
        btn = tk.Button(theme_bar, text=name, font=("Comic Sans MS", 7, "bold"),
                         relief="raised", bd=1, padx=3,
                         command=lambda n=name: apply_theme(n))
        btn.pack(side="left", padx=1)
        theme_btns.append(btn)

    # ====== 主区域 ======
    main = tk.Frame(root)
    main.pack(fill="both", expand=True)

    # 左侧画布
    canvas_frame = tk.Frame(main)
    canvas_frame.pack(side="left", fill="both", expand=True)

    canvas = tk.Canvas(canvas_frame, width=GRID_W*CELL, height=GRID_H*CELL+30,
                         highlightthickness=0)
    canvas.pack(padx=2, pady=2)
    canvas.bind("<Button-1>", on_click)

    # 右侧面板
    panel_frame = tk.Frame(main, width=240)
    panel_frame.pack(side="right", fill="y", padx=3)

    # 单位信息
    tk.Label(panel_frame, text="📋 单位信息", font=("Comic Sans MS", 10, "bold"),
             anchor="w").pack(fill="x", padx=5, pady=(5, 0))

    lbl_unit_info = tk.Label(panel_frame, text="", font=("Comic Sans MS", 9),
                               justify="left", anchor="nw")
    lbl_unit_info.pack(fill="x", padx=5, pady=2)

    # 招募
    tk.Label(panel_frame, text="🏭 招募单位", font=("Comic Sans MS", 10, "bold"),
             anchor="w").pack(fill="x", padx=5, pady=(8, 0))

    recruit_frame = tk.Frame(panel_frame)
    recruit_frame.pack(fill="x", padx=5, pady=2)

    for utype, info in UNIT_TYPES.items():
        btn = tk.Button(recruit_frame, text=f"{info['icon']} {info['name']}\n💰{info['cost']}",
                         font=("Comic Sans MS", 7), width=8, height=2,
                         command=lambda u=utype: recruit(u))
        btn.pack(side="left", padx=1, pady=1)

    # 操作提示
    tk.Label(panel_frame, text="💡 操作提示", font=("Comic Sans MS", 10, "bold"),
             anchor="w").pack(fill="x", padx=5, pady=(8, 0))

    help_text = ("🖱️ 点击己方单位选中\n"
                 "🟢 绿框 = 可移动\n"
                 "🔴 红框 = 可攻击\n"
                 "⚔️ 先移动再攻击\n"
                 "🔄 每回合金币+10\n"
                 "🏰 在己方单位旁招募")
    tk.Label(panel_frame, text=help_text, font=("Comic Sans MS", 8),
             justify="left", anchor="nw").pack(fill="x", padx=5, pady=2)

    # 战斗日志
    tk.Label(panel_frame, text="📜 战斗日志", font=("Comic Sans MS", 10, "bold"),
             anchor="w").pack(fill="x", padx=5, pady=(8, 0))

    log_scroll = tk.Scrollbar(panel_frame)
    log_scroll.pack(side="right", fill="y")

    lbl_log = tk.Text(panel_frame, font=("Consolas", 8), height=10,
                        wrap="word", yscrollcommand=log_scroll.set)
    lbl_log.pack(fill="both", expand=True, padx=5, pady=2)
    log_scroll.config(command=lbl_log.yview)
    lbl_log.config(state="disabled")

    # 收集
    all_widgets.extend([top, theme_bar, main, canvas_frame, panel_frame,
                        recruit_frame, btn_end, lbl_turn, lbl_phase, lbl_gold])

# ========================
# 键盘
# ========================
def on_key(e):
    if e.keysym == "Escape":
        global selected, move_range, attack_range, phase
        selected = None
        move_range = []
        attack_range = []
        phase = "move"
        draw()
    elif e.keysym == "e" or e.keysym == "E":
        end_turn()

# ========================
# 启动
# ========================
def init():
    """初始化"""
    global cur_player, turn, phase, game_over, winner
    global gold, units, unit_id_counter, log_lines

    generate_map()
    spawn_initial_units()
    cur_player = 0
    turn = 1
    phase = "move"
    game_over = False
    winner = None
    gold = [50, 50]
    unit_id_counter = len(units)
    log_lines = []

    build_ui()
    apply_theme("🗺️ 古典地图")
    update_sidebar()
    log("⚔️ 战争开始！红方先行")
    log("🪖 点击己方单位开始行动")
    draw()

if __name__ == "__main__":
    root = None
    init()
    game_loop()
    root.mainloop()
