# -*- coding: utf-8 -*-
"""
KARDS 二战卡牌图鉴 & 创新实验室
--------------------------------------------------
运行:  python kards_app.py
访问:  http://127.0.0.1:5000/
依赖:  pip install flask
"""
import json, math, random
from flask import Flask, request, render_template_string

app = Flask(__name__)

# ============ 卡牌数据库 ============
CARDS = [
    # ---------------- 德国 ----------------
    {"id": 1, "name": "闪电突击兵", "faction": "德国", "type": "步兵", "deploy": 2, "action": 1,
     "atk": 2, "def": 2, "kw": ["闪击"], "desc": "具有闪击能力，下场即可移动/攻击。", "tier": "普通"},
    {"id": 2, "name": "三号突击炮", "faction": "德国", "type": "坦克", "deploy": 4, "action": 2,
     "atk": 3, "def": 4, "kw": ["闪击"], "desc": "经典的突击炮支援单位。", "tier": "稀有"},
    {"id": 3, "name": "虎式坦克", "faction": "德国", "type": "坦克", "deploy": 7, "action": 3,
     "atk": 6, "def": 7, "kw": ["奋战"], "desc": "一回合可攻击两次，装甲厚实。", "tier": "史诗"},
    {"id": 4, "name": "斯图卡俯冲轰炸机", "faction": "德国", "type": "轰炸机", "deploy": 3, "action": 1,
     "atk": 2, "def": 1, "kw": ["俯冲"], "desc": "进场时对敌方单位造成 2 点伤害。", "tier": "稀有"},
    {"id": 5, "name": "88mm 高炮", "faction": "德国", "type": "火炮", "deploy": 5, "action": 1,
     "atk": 4, "def": 2, "kw": ["防空"], "desc": "可攻击空军单位，对轰炸机威胁巨大。", "tier": "稀有"},
    {"id": 6, "name": "装甲掷弹兵", "faction": "德国", "type": "步兵", "deploy": 3, "action": 1,
     "atk": 3, "def": 3, "kw": ["闪击"], "desc": "摩托化步兵，配合坦克推进。", "tier": "普通"},
    {"id": 7, "name": "Bf-109 战斗机", "faction": "德国", "type": "战斗机", "deploy": 3, "action": 1,
     "atk": 2, "def": 2, "kw": ["拦截"], "desc": "可拦截敌方轰炸机，阻止其攻击。", "tier": "普通"},
    {"id": 8, "name": "装甲集群", "faction": "德国", "type": "指令", "deploy": 2, "action": 0,
     "atk": 0, "def": 0, "kw": ["闪击"], "desc": "本回合你所有坦克获得闪击。", "tier": "稀有"},
    {"id": 9, "name": "元帅参谋部", "faction": "德国", "type": "指令", "deploy": 4, "action": 0,
     "atk": 0, "def": 0, "kw": [], "desc": "抽 2 张牌，本回合指挥点 +2。", "tier": "史诗"},

    # ---------------- 苏联 ----------------
    {"id": 10, "name": "动员兵", "faction": "苏联", "type": "步兵", "deploy": 1, "action": 1,
     "atk": 1, "def": 2, "kw": ["动员"], "desc": "部署时若前线为空，召唤一个复制。", "tier": "普通"},
    {"id": 11, "name": "T-34 坦克", "faction": "苏联", "type": "坦克", "deploy": 5, "action": 2,
     "atk": 4, "def": 5, "kw": ["闪击"], "desc": "产量惊人的主力中型坦克。", "tier": "稀有"},
    {"id": 12, "name": "喀秋莎火箭炮", "faction": "苏联", "type": "火炮", "deploy": 4, "action": 1,
     "atk": 3, "def": 1, "kw": ["齐射"], "desc": "进场时对随机 2 个敌方单位各造成 1 点伤害。", "tier": "史诗"},
    {"id": 13, "name": "伊尔-2 强击机", "faction": "苏联", "type": "轰炸机", "deploy": 4, "action": 2,
     "atk": 3, "def": 2, "kw": [], "desc": "对地面单位造成有效打击。", "tier": "稀有"},
    {"id": 14, "name": "雅克战斗机", "faction": "苏联", "type": "战斗机", "deploy": 2, "action": 1,
     "atk": 2, "def": 1, "kw": ["拦截"], "desc": "廉价拦截机，保护前线。", "tier": "普通"},
    {"id": 15, "name": "政委动员令", "faction": "苏联", "type": "指令", "deploy": 1, "action": 0,
     "atk": 0, "def": 0, "kw": [], "desc": "一个友军步兵本回合 +2/+2。", "tier": "普通"},
    {"id": 16, "name": "乌拉冲锋", "faction": "苏联", "type": "指令", "deploy": 3, "action": 0,
     "atk": 0, "def": 0, "kw": [], "desc": "前线所有友军步兵获得闪击。", "tier": "稀有"},
    {"id": 17, "name": "KV-2 重型坦克", "faction": "苏联", "type": "坦克", "deploy": 6, "action": 3,
     "atk": 5, "def": 6, "kw": [], "desc": "厚装甲的重型突破坦克。", "tier": "史诗"},

    # ---------------- 美国 ----------------
    {"id": 18, "name": "美国大兵", "faction": "美国", "type": "步兵", "deploy": 2, "action": 1,
     "atk": 2, "def": 2, "kw": [], "desc": "标准步兵，可靠的火力支援。", "tier": "普通"},
    {"id": 19, "name": "谢尔曼坦克", "faction": "美国", "type": "坦克", "deploy": 4, "action": 2,
     "atk": 3, "def": 4, "kw": ["闪击"], "desc": "数量众多、易于量产的中型坦克。", "tier": "稀有"},
    {"id": 20, "name": "B-17 空中堡垒", "faction": "美国", "type": "轰炸机", "deploy": 6, "action": 2,
     "atk": 4, "def": 3, "kw": ["护航"], "desc": "空军优势，进场时抽 1 张牌。", "tier": "史诗"},
    {"id": 21, "name": "P-51 野马", "faction": "美国", "type": "战斗机", "deploy": 3, "action": 1,
     "atk": 3, "def": 2, "kw": ["拦截"], "desc": "长航程护航战斗机。", "tier": "稀有"},
    {"id": 22, "name": "M7 牧师火炮", "faction": "美国", "type": "火炮", "deploy": 4, "action": 1,
     "atk": 3, "def": 2, "kw": [], "desc": "机动自行火炮，支援前线。", "tier": "普通"},
    {"id": 23, "name": "补给运输", "faction": "美国", "type": "指令", "deploy": 1, "action": 0,
     "atk": 0, "def": 0, "kw": [], "desc": "抽 2 张牌。", "tier": "普通"},
    {"id": 24, "name": "舰炮支援", "faction": "美国", "type": "指令", "deploy": 5, "action": 0,
     "atk": 0, "def": 0, "kw": [], "desc": "对一个敌方单位造成 5 点伤害。", "tier": "史诗"},

    # ---------------- 英国 ----------------
    {"id": 25, "name": "掷弹兵卫队", "faction": "英国", "type": "步兵", "deploy": 3, "action": 1,
     "atk": 2, "def": 4, "kw": ["守护"], "desc": "守护单位，保护相邻友军。", "tier": "稀有"},
    {"id": 26, "name": "丘吉尔坦克", "faction": "英国", "type": "坦克", "deploy": 6, "action": 2,
     "atk": 4, "def": 7, "kw": ["守护"], "desc": "重装甲守护坦克。", "tier": "史诗"},
    {"id": 27, "name": "喷火战斗机", "faction": "英国", "type": "战斗机", "deploy": 2, "action": 1,
     "atk": 2, "def": 2, "kw": ["拦截"], "desc": "传奇的不列颠守护者。", "tier": "稀有"},
    {"id": 28, "name": "皇家炮兵", "faction": "英国", "type": "火炮", "deploy": 3, "action": 1,
     "atk": 2, "def": 2, "kw": [], "desc": "精准的远程火力支援。", "tier": "普通"},
    {"id": 29, "name": "雷达预警", "faction": "英国", "type": "指令", "deploy": 2, "action": 0,
     "atk": 0, "def": 0, "kw": ["反制"], "desc": "反制：当对手打出空军单位时，使其费用 +1。", "tier": "稀有"},
    {"id": 30, "name": "坚守防线", "faction": "英国", "type": "指令", "deploy": 2, "action": 0,
     "atk": 0, "def": 0, "kw": [], "desc": "一个友军单位获得守护直到回合结束。", "tier": "普通"},

    # ---------------- 日本 ----------------
    {"id": 31, "name": "精锐步兵", "faction": "日本", "type": "步兵", "deploy": 1, "action": 1,
     "atk": 2, "def": 1, "kw": ["闪击"], "desc": "廉价的激进步兵，擅长突袭。", "tier": "普通"},
    {"id": 32, "name": "零式战斗机", "faction": "日本", "type": "战斗机", "deploy": 2, "action": 1,
     "atk": 2, "def": 1, "kw": ["拦截"], "desc": "高机动性的轻型战斗机。", "tier": "稀有"},
    {"id": 33, "name": "神风特攻", "faction": "日本", "type": "指令", "deploy": 1, "action": 0,
     "atk": 0, "def": 0, "kw": ["反制"], "desc": "反制：当一个友军单位死亡时，对随机敌方单位造成 2 点伤害。", "tier": "史诗"},
    {"id": 34, "name": "九七式坦克", "faction": "日本", "type": "坦克", "deploy": 3, "action": 2,
     "atk": 2, "def": 3, "kw": ["闪击"], "desc": "轻型快速坦克，适合突进。", "tier": "普通"},
    {"id": 35, "name": "一式陆攻", "faction": "日本", "type": "轰炸机", "deploy": 4, "action": 1,
     "atk": 3, "def": 1, "kw": [], "desc": "双发重型陆上攻击机。", "tier": "稀有"},
    {"id": 36, "name": "玉碎冲锋", "faction": "日本", "type": "指令", "deploy": 2, "action": 0,
     "atk": 0, "def": 0, "kw": [], "desc": "前线所有友军 +1 攻击力，本回合获得闪击。", "tier": "史诗"},

    # ---------------- 通用 / 盟国 ----------------
    {"id": 37, "name": "战地医院", "faction": "通用", "type": "指令", "deploy": 2, "action": 0,
     "atk": 0, "def": 0, "kw": [], "desc": "为一个友军单位恢复 3 点防御。", "tier": "普通"},
    {"id": 38, "name": "战术轰炸", "faction": "通用", "type": "指令", "deploy": 3, "action": 0,
     "atk": 0, "def": 0, "kw": [], "desc": "对一个敌方单位造成 3 点伤害。", "tier": "普通"},
    {"id": 39, "name": "物资空投", "faction": "通用", "type": "指令", "deploy": 1, "action": 0,
     "atk": 0, "def": 0, "kw": [], "desc": "抽 1 张牌，本回合指挥点 +1。", "tier": "普通"},
    {"id": 40, "name": "战略撤退", "faction": "通用", "type": "指令", "deploy": 1, "action": 0,
     "atk": 0, "def": 0, "kw": ["反制"], "desc": "反制：当一个友军单位将要受到 3+ 伤害时，改为 1。", "tier": "稀有"},
    {"id": 41, "name": "情报窃取", "faction": "通用", "type": "指令", "deploy": 2, "action": 0,
     "atk": 0, "def": 0, "kw": ["情报"], "desc": "窥视对手 2 张手牌。", "tier": "稀有"},
    {"id": 42, "name": "烟幕掩护", "faction": "通用", "type": "指令", "deploy": 1, "action": 0,
     "atk": 0, "def": 0, "kw": ["烟幕"], "desc": "一个友军单位获得烟幕（不能被敌方单位攻击）。", "tier": "普通"},
]

# 阵营颜色映射
FACTION_COLOR = {
    "德国": "#2c3e50", "苏联": "#c0392b", "美国": "#2980b9",
    "英国": "#8e44ad", "日本": "#d35400", "通用": "#16a085",
}
FACTION_ICON = {
    "德国": "🦅", "苏联": "☭", "美国": "⭐", "英国": "👑", "日本": "☀", "通用": "⚙",
}

# ============ 卡组构建校验 ============
def validate_deck(deck):
    """校验卡组是否合法"""
    errs, warns = [], []
    total = len(deck)
    if total != 40:
        errs.append(f"卡组必须为 40 张，当前 {total} 张")
    counts = {}
    for c in deck:
        counts[c["id"]] = counts.get(c["id"], 0) + 1
    for cid, n in counts.items():
        if n > MAX_COPIES:
            errs.append(f"「{get_card(cid)['name']}」最多 {MAX_COPIES} 张，当前 {n} 张")
    over2 = [get_card(cid)["name"] for cid, n in counts.items() if 2 < n <= MAX_COPIES]
    if over2:
        warns.append(f"扩编模式：{', '.join(over2)} 带满 {MAX_COPIES} 张（小卡池阵营常态）")
    factions = {}
    for c in deck:
        factions[c["faction"]] = factions.get(c["faction"], 0) + 1
    if not factions:
        errs.append("卡组为空")
        return {"errors": errs, "warnings": warns, "stats": {}}
    main = max(factions, key=factions.get)
    # 盟国 = 除主国外、去掉跨阵营的通用卡
    non_main = {k: v for k, v in factions.items() if k != main and k != "通用"}
    ally_count = sum(non_main.values())
    if ally_count > 12:
        errs.append(f"盟国卡不得超过 12 张，当前 {ally_count} 张")
    if main == "通用":
        warns.append("主阵营不应为通用，建议选择德/苏/美/英/日之一")
    cost_curve = [0]*8
    for c in deck:
        cost_curve[min(c["deploy"], 7)] += 1
    low = sum(cost_curve[:3]); mid = sum(cost_curve[3:6]); high = sum(cost_curve[6:])
    if low < 10: warns.append(f"低费牌偏少({low}张)，前期可能卡手")
    if high > 8: warns.append(f"高费牌偏多({high}张)，容易卡费用")
    types = {}
    for c in deck:
        types[c["type"]] = types.get(c["type"], 0) + 1
    if types.get("指令", 0) < 6:
        warns.append(f"指令牌仅 {types.get('指令',0)} 张，解场/补牌能力不足")
    return {
        "errors": errs, "warnings": warns,
        "stats": {"main": main, "ally_count": ally_count, "factions": factions,
                  "curve": cost_curve, "types": types,
                  "low": low, "mid": mid, "high": high},
    }

MAX_COPIES = 3  # 扩编规则：每张卡最多 3 张（弥补小卡池凑不满 40 的问题）

def fill_deck(main_faction, ally_faction):
    """凑满 40 张：主阵营为主体，盟国 ≤12，其余用通用填充。
    采用「扩编」规则，同名卡最多 MAX_COPIES 张。"""
    main_pool = list({c["id"]: c for c in CARDS if c["faction"] == main_faction}.values())
    ally_pool = list({c["id"]: c for c in CARDS if c["faction"] == ally_faction}.values()) if ally_faction else []
    generic_pool = list({c["id"]: c for c in CARDS if c["faction"] == "通用"}.values())
    random.shuffle(main_pool); random.shuffle(ally_pool); random.shuffle(generic_pool)

    deck = []
    def pool_max(pool):
        """某阵营最多能提供的卡数（去重卡数 × MAX_COPIES）"""
        return len(pool) * MAX_COPIES

    def add_copies(pool, cap):
        """按 cap 配额填充：对每张卡循环放到第 MAX_COPIES 张，直到凑满 cap 张"""
        used = 0
        for c in pool:
            if used >= cap: break
            if sum(1 for x in deck if x["id"] == c["id"]) == 0:
                deck.append(c); used += 1
        for _ in range(MAX_COPIES - 1):
            for c in pool:
                if used >= cap: break
                if sum(1 for x in deck if x["id"] == c["id"]) < MAX_COPIES:
                    deck.append(c); used += 1
        return used

    # 配额按「该阵营实际能提供的最大卡数」与「理论配额」取较小值
    # 保证盟国永远不会超过 12 张
    main_max = pool_max(main_pool)
    main_quota = min(main_max, 40 - 12)      # 主阵营配额 = 40 − 盟国上限
    add_copies(main_pool, main_quota)        # 主阵营先上

    ally_quota = min(pool_max(ally_pool), 12, 40 - len(deck))
    add_copies(ally_pool, ally_quota)        # 盟国严格 ≤12

    add_copies(generic_pool, 40)             # 通用补剩余空位
    if len(deck) < 40:                       # 兜底
        add_copies(main_pool, 40)
    return deck[:40]

def get_card(cid):
    for c in CARDS:
        if c["id"] == cid: return c
    return None

# ============ 战斗模拟（简化版） ============
def simulate_turn(state):
    """简化的单回合伤害模拟：计算当前能造成的最大伤害"""
    dmg = 0
    notes = []
    for u in state.get("frontline", []):
        if u.get("can_attack"):
            dmg += u["atk"]
            notes.append(f"{u['name']} 攻击 → {u['atk']} 伤害")
    for c in state.get("orders", []):
        if "造成" in c.get("desc",""):
            try:
                import re
                m = re.search(r"造成\s*(\d+)\s*点伤害", c["desc"])
                if m: dmg += int(m.group(1)); notes.append(f"{c['name']} 指令 → {m.group(1)} 伤害")
            except: pass
    return dmg, notes

# ============ 创新设计：卡组评分 & 对局 AI ============
def deck_archetype(stats):
    """根据费用曲线判断卡组流派"""
    if stats.get("low",0) >= 14: return "⚡ 快攻抢脸"
    if stats.get("high",0) >= 7: return "🐢 控制反打"
    return "⚖️ 中速抢线"

def random_deck(faction=None, ally=None):
    """随机生成一套合法卡组（40 张，盟国 ≤12）"""
    primary = [c for c in CARDS if c["faction"] == faction] if faction else []
    allies = [c for c in CARDS if c["faction"] == ally] if ally else []
    generics = [c for c in CARDS if c["faction"] == "通用"]
    result, tries = [], 0
    while len(result) < 40 and tries < 10000:
        tries += 1
        r = random.random()
        if r < 0.55 and primary: pool = primary
        elif r < 0.80 and allies: pool = allies
        else: pool = generics if generics else primary
        c = random.choice(pool)
        if sum(1 for x in result if x["id"] == c["id"]) >= 2: continue
        if sum(1 for x in result if x["faction"] == "通用") > 6: continue  # 通用作填充，不过量
        result.append(c)
    # 保证盟国不超过 12
    if ally:
        ally_cards = [c for c in result if c["faction"] == ally]
        while len(ally_cards) > 12:
            for i,c in enumerate(result):
                if c["faction"] == ally:
                    result.pop(i); break
            ally_cards = [c for c in result if c["faction"] == ally]
    return result

# ============ HTML 模板 ============
BASE = """
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>KARDS 二战卡牌图鉴 & 创新实验室</title>
<style>
:root{
  --bg:#0f1419; --panel:#1a2129; --panel2:#232c36; --line:#2e3a47;
  --txt:#e8edf2; --txt2:#9fb0c0; --gold:#e0a800; --accent:#4aa3df;
}
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:-apple-system,"Segoe UI","Microsoft YaHei",sans-serif;
  background:var(--bg);color:var(--txt);line-height:1.7}
a{color:var(--accent);text-decoration:none}
.wrap{max-width:1180px;margin:0 auto;padding:0 24px}
/* header */
header{background:linear-gradient(135deg,#1c2b3a,#0f1419);
  border-bottom:3px solid var(--gold);padding:14px 0;position:sticky;top:0;z-index:100}
.hd{display:flex;align-items:center;gap:18px;flex-wrap:wrap}
.logo{font-size:26px;font-weight:900;letter-spacing:2px;color:var(--gold)}
.logo span{color:var(--txt2);font-weight:400;font-size:14px;margin-left:8px}
nav{margin-left:auto;display:flex;gap:6px;flex-wrap:wrap}
nav a{color:var(--txt2);padding:8px 14px;border-radius:6px;font-size:14px;transition:.2s}
nav a:hover,nav a.on{background:var(--panel2);color:var(--gold)}
/* hero */
.hero{padding:64px 0 56px;text-align:center;
  background:radial-gradient(ellipse at center,rgba(224,168,0,.12),transparent 70%)}
.hero h1{font-size:44px;font-weight:900;line-height:1.2;margin-bottom:16px}
.hero h1 em{color:var(--gold);font-style:normal}
.hero p{color:var(--txt2);max-width:680px;margin:0 auto 28px;font-size:17px}
.btn-group{display:flex;gap:14px;justify-content:center;flex-wrap:wrap}
.btn{display:inline-block;padding:13px 28px;border-radius:8px;font-weight:700;
  border:2px solid var(--gold);color:var(--gold);transition:.2s;cursor:pointer;background:transparent}
.btn:hover{background:var(--gold);color:#000}
.btn.primary{background:var(--gold);color:#000}
.btn.primary:hover{background:#fff}
/* sections */
section{padding:52px 0}
.sec-title{font-size:30px;font-weight:900;margin-bottom:8px}
.sec-sub{color:var(--txt2);margin-bottom:32px}
.grid{display:grid;gap:20px}
.g3{grid-template-columns:repeat(3,1fr)}
.g2{grid-template-columns:repeat(2,1fr)}
.g4{grid-template-columns:repeat(4,1fr)}
@media(max-width:860px){.g3,.g2,.g4{grid-template-columns:1fr}}
.card{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:24px;transition:.2s}
.card:hover{border-color:var(--gold);transform:translateY(-3px)}
.card h3{font-size:19px;margin-bottom:10px;color:var(--gold)}
.card p,.card li{color:var(--txt2);font-size:14.5px}
.card ul{padding-left:18px}.card li{margin-bottom:6px}
/* table */
table{width:100%;border-collapse:collapse;font-size:14px}
th,td{padding:10px 12px;text-align:left;border-bottom:1px solid var(--line)}
th{color:var(--gold);font-weight:700;background:var(--panel2)}
tr:hover td{background:rgba(74,163,223,.06)}
.tag{display:inline-block;padding:2px 9px;border-radius:20px;font-size:12px;font-weight:700}
.tag.infantry{background:#4a7c59;color:#fff}
.tag.tank{background:#7c5a2a;color:#fff}
.tag.artillery{background:#6c4a7c;color:#fff}
.tag.fighter{background:#2a6c8a;color:#fff}
.tag.bomber{background:#8a3a2a;color:#fff}
.tag.order{background:#555;color:#fff}
.rarity{font-size:12px}
.r-common{color:#9fb0c0}.r-rare{color:#4aa3df}.r-epic{color:#b14aed}
/* filters */
.filters{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:20px;align-items:center}
.filters select,.filters input{padding:9px 12px;background:var(--panel2);border:1px solid var(--line);
  color:var(--txt);border-radius:6px;font-size:14px}
.filters label{color:var(--txt2);font-size:14px}
/* deckbuilder */
.builder{display:grid;grid-template-columns:1.1fr 1fr;gap:24px}
@media(max-width:860px){.builder{grid-template-columns:1fr}}
#collection .card-item{background:var(--panel2);border:1px solid var(--line);border-radius:8px;
  padding:10px 14px;margin-bottom:8px;cursor:pointer;display:flex;justify-content:space-between;align-items:center;font-size:14px}
#collection .card-item:hover{border-color:var(--gold)}
#deck-list{min-height:300px}
.deck-row{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;
  border-bottom:1px solid var(--line);font-size:14px}
.deck-row button{background:#c0392b;color:#fff;border:none;border-radius:4px;padding:3px 10px;cursor:pointer}
.curve-bar{display:flex;align-items:flex-end;gap:6px;height:120px;margin-top:14px}
.curve-bar div{background:var(--gold);width:40px;text-align:center;font-size:12px;color:#000;font-weight:700}
.result-box{background:var(--panel2);border-radius:10px;padding:16px;margin-top:14px;font-size:14px}
.result-box .ok{color:#4a7c59}.result-box .bad{color:#c0392b}.result-box .warn{color:var(--gold)}
/* battle */
.battle-field{display:grid;grid-template-columns:1fr 60px 1fr;gap:14px;align-items:center;margin:20px 0}
.line{border:2px dashed var(--line);border-radius:10px;min-height:160px;padding:14px;position:relative}
.line.active{border-color:var(--gold);background:rgba(224,168,0,.05)}
.line h4{color:var(--gold);margin-bottom:8px;font-size:14px}
.unit-chip{background:var(--panel2);border:1px solid var(--line);border-radius:8px;padding:8px 12px;
  margin-bottom:8px;font-size:13px;display:flex;justify-content:space-between}
.unit-chip.attack{animation:atk .5s}
@keyframes atk{0%{transform:translateX(0)}50%{transform:translateX(20px)}100%{transform:translateX(0)}}
.hq{background:var(--panel);border:2px solid var(--gold);border-radius:10px;padding:14px;text-align:center}
.hq .hp{font-size:32px;font-weight:900;color:var(--gold)}
.hq.danger .hp{color:#c0392b}
.hq-label{font-size:12px;color:var(--txt2);margin-top:4px}
.vs{text-align:center;color:var(--gold);font-weight:900;font-size:22px}
.hint{background:var(--panel2);border-left:4px solid var(--gold);padding:12px 16px;border-radius:6px;margin:14px 0;font-size:14px}
.kw{color:var(--gold);font-weight:700}
footer{border-top:1px solid var(--line);padding:28px 0;color:var(--txt2);font-size:13px;text-align:center}
.flip{display:none}.flip.show{display:block}
.toolbar{margin:10px 0}
</style>
</head>
<body>
<header>
  <div class="wrap hd">
    <div class="logo">KARDS<span>二战卡牌图鉴 & 创新实验室</span></div>
    <nav>
      <a href="#home" class="on">主页</a>
      <a href="#rules">规则</a>
      <a href="#cards">卡牌库</a>
      <a href="#builder">卡组构建</a>
      <a href="#battle">战斗模拟</a>
      <a href="#innovate">创新实验室</a>
    </nav>
  </div>
</header>

<!-- HOME -->
<section id="home" class="hero">
  <div class="wrap">
    <h1>指挥你的<em>钢铁军团</em><br>决胜于三条战线</h1>
    <p>KARDS 是一款以第二次世界大战为背景的策略卡牌游戏。占领前线、调度单位、摧毁敌方总部——用真正的战术智慧而非单纯比拼卡牌稀有度来决定胜负。</p>
    <div class="btn-group">
      <a class="btn primary" href="#rules">了解规则</a>
      <a class="btn" href="#builder">构建我的卡组</a>
      <a class="btn" href="#innovate">查看创新设计</a>
    </div>
  </div>
</section>

<!-- 特色概览 -->
<section style="padding-top:20px">
  <div class="wrap">
    <div class="grid g4">
      <div class="card"><h3>🎯 总部决胜</h3><p>将敌方总部 20 点防御降至 0 即获胜。</p></div>
      <div class="card"><h3>🗺️ 三线战场</h3><p>支援区 / 前线 / 支援区，抢线才能打脸。</p></div>
      <div class="card"><h3>🎟️ 双费用系统</h3><p>部署费 + 行动费，每动一次都要精打细算。</p></div>
      <div class="card"><h3>🛡️ 五大阵营</h3><p>德 / 苏 / 美 / 英 / 日，各有独门战术。</p></div>
    </div>
  </div>
</section>

<!-- RULES -->
<section id="rules">
  <div class="wrap">
    <h2 class="sec-title">核心规则</h2>
    <p class="sec-sub">掌握这五块，你就从「会玩」跨越到「会赢」。</p>
    <div class="grid g2">
      <div class="card">
        <h3>① 胜利与战场</h3>
        <ul>
          <li>目标：把敌方总部防御从 <span class="kw">20</span> 降到 0 或以下。</li>
          <li>战场三线：<b>己方支援区</b> → <b>前线</b> → <b>敌方支援区</b>。</li>
          <li>前线同时只能由<b>一方</b>占领，最多容纳 5 个单位。</li>
          <li>只有<b>占领前线</b>的单位才能攻击敌方总部。</li>
        </ul>
      </div>
      <div class="card">
        <h3>② 资源系统</h3>
        <ul>
          <li><b>指挥点</b>：每回合槽位 +1，上限 12；回合开始按槽位补满。</li>
          <li><b>双费用</b>：大数字=部署费，小数字=行动费。移动/攻击都要再付行动费。</li>
          <li><b>手牌上限 9 张</b>，超抽自动弃牌——这是隐性资源。</li>
          <li>先手 4 张首回合不抽牌，后手 5 张首回合抽 1 张。</li>
        </ul>
      </div>
      <div class="card">
        <h3>③ 卡牌类型</h3>
        <ul>
          <li><b>步兵</b>：便宜守线，吃正面伤害。</li>
          <li><b>坦克</b>：移动+攻击同回合，行动费贵。</li>
          <li><b>火炮</b>：可跨线攻击，身板脆。</li>
          <li><b>战斗机</b>：拦截轰炸机，跨线作战。</li>
          <li><b>轰炸机</b>：打任意线且不被反击，怕战斗机。</li>
          <li><b>指令</b>：一次性生效，部分可<b>反制</b>。</li>
        </ul>
      </div>
      <div class="card">
        <h3>④ 卡组与流程</h3>
        <ul>
          <li>共 <b>40 张</b>，1 个主阵营 + 1 个盟国，盟国 ≤ 12 张，同名卡 ≤ 2 张。</li>
          <li>起手可调度：先手 4 张，后手 5 张。</li>
          <li>回合：抽牌 → 部署/移动/攻击 → 结束回合。</li>
          <li>核心循环：<span class="kw">铺场 → 抢线 → 激活 → 打脸/解场</span>，四件事抢同一笔指挥点。</li>
        </ul>
      </div>
    </div>

    <h3 style="margin:34px 0 14px;color:var(--gold)">关键机制速查表</h3>
    <table>
      <tr><th>机制</th><th>效果</th><th>典型单位</th></tr>
      <tr><td>闪击</td><td>下场当回合即可移动/攻击</td><td>闪电突击兵、T-34</td></tr>
      <tr><td>守护</td><td>相邻非守护单位只能被炮兵/轰炸机打</td><td>掷弹兵卫队、丘吉尔</td></tr>
      <tr><td>奋战</td><td>一回合可攻击两次</td><td>虎式坦克</td></tr>
      <tr><td>伏击</td><td>首次被攻击时先手结算伤害</td><td>反坦克伏击组</td></tr>
      <tr><td>压制</td><td>不能移动，拥有者下回合结束解除</td><td>压制火力</td></tr>
      <tr><td>烟幕</td><td>不能被敌方单位攻击，一动即失去</td><td>烟幕掩护指令</td></tr>
      <tr><td>钳击</td><td>与另一友军联动，双方吃加成</td><td>协同进攻单位</td></tr>
      <tr><td>反制</td><td>藏在手里，对手满足条件时触发</td><td>雷达预警、战略撤退</td></tr>
    </table>

    <div class="hint">💡 <b>赢的关键一句话</b>：线 = 第三种资源。抢到前线就抢到攻击权，这比单纯比拼单卡身材更贴近真实战场的推进逻辑。</div>
  </div>
</section>

<!-- CARDS -->
<section id="cards">
  <div class="wrap">
    <h2 class="sec-title">卡牌数据库（{{card_count}} 张）</h2>
    <p class="sec-sub">点击卡牌加入卡组，或直接筛选查看。</p>
    <div class="filters">
      <label>阵营
        <select id="fFaction" onchange="renderCards()">
          <option value="">全部</option>
          {% for f in factions %}<option value="{{f}}">{{f}}</option>{% endfor %}
        </select>
      </label>
      <label>类型
        <select id="fType" onchange="renderCards()">
          <option value="">全部</option>
          {% for t in types %}<option value="{{t}}">{{t}}</option>{% endfor %}
        </select>
      </label>
      <label>稀有度
        <select id="fRarity" onchange="renderCards()">
          <option value="">全部</option>
          <option>普通</option><option>稀有</option><option>史诗</option>
        </select>
      </label>
      <label>部署费 ≤
        <select id="fCost" onchange="renderCards()">
          <option value="9">不限</option>
          <option>1</option><option>2</option><option>3</option><option>4</option>
          <option>5</option><option>6</option><option>7</option>
        </select>
      </label>
      <input id="fKw" placeholder="关键词搜索，如 闪击" oninput="renderCards()" style="width:180px">
      <button class="btn" style="padding:9px 16px" onclick="resetFilters()">重置</button>
    </div>
    <div id="cardGrid" class="grid g3"></div>
  </div>
</section>

<!-- BUILDER -->
<section id="builder">
  <div class="wrap">
    <h2 class="sec-title">卡组构建器</h2>
    <p class="sec-sub">从上方卡牌库点击加入，或随机生成一套，系统自动校验合法性并给出流派建议。</p>
    <div class="builder">
      <div>
        <h3 style="color:var(--gold);margin-bottom:12px">卡牌库（点击加入）</h3>
        <div class="filters">
          <label>阵营<select id="bFaction" onchange="renderCollection()">
            <option value="">全部</option>{% for f in factions %}<option value="{{f}}">{{f}}</option>{% endfor %}
          </select></label>
          <label>类型<select id="bType" onchange="renderCollection()">
            <option value="">全部</option>{% for t in types %}<option value="{{t}}">{{t}}</option>{% endfor %}
          </select></label>
        </div>
        <div id="collection" style="max-height:560px;overflow-y:auto;padding-right:8px"></div>
      </div>
      <div>
        <h3 style="color:var(--gold);margin-bottom:12px">我的卡组 <span id="deckCount" style="color:var(--txt2);font-size:14px;font-weight:400">0/40</span></h3>
        <div class="toolbar">
          <button class="btn" style="padding:8px 14px" onclick="randomDeck()">🎲 随机生成</button>
          <button class="btn" style="padding:8px 14px" onclick="clearDeck()">🗑️ 清空</button>
          <button class="btn" style="padding:8px 14px" onclick="exportDeck()">📋 导出</button>
        </div>
        <div id="deck-list"></div>
        <div class="result-box" id="deckResult"></div>
        <canvas id="curveChart" width="400" height="160" style="width:100%;margin-top:16px"></canvas>
      </div>
    </div>
  </div>
</section>

<!-- BATTLE SIM -->
<section id="battle">
  <div class="wrap">
    <h2 class="sec-title">战斗模拟器</h2>
    <p class="sec-sub">拖入单位体验「抢线 → 打脸」的伤害结算，验证你的战术构想。</p>
    <div class="filters">
      <label>己方单位
        <select id="addUnit" onchange="addFriendly(this.value);this.value=''">
          <option value="">+ 添加单位</option>
          {% for c in cards if c.type not in ['指令'] %}
          <option value="{{c.id}}">{{c.name}}（{{c.deploy}}/{{c.action}} · {{c.atk}}/{{c.def}}）</option>
          {% endfor %}
        </select>
      </label>
      <label>敌方单位
        <select id="addEnemy" onchange="addEnemy(this.value);this.value=''">
          <option value="">+ 添加单位</option>
          {% for c in cards if c.type not in ['指令'] %}
          <option value="{{c.id}}">{{c.name}}（{{c.atk}}/{{c.def}}）</option>
          {% endfor %}
        </select>
      </label>
      <button class="btn" style="padding:9px 16px" onclick="runAttack()">⚔️ 执行攻击阶段</button>
      <button class="btn" style="padding:9px 16px" onclick="resetBattle()">🔄 重置</button>
    </div>

    <div class="battle-field">
      <div>
        <div class="hq" id="myHQ"><div class="hp" id="myHP">20</div><div class="hq-label">己方总部</div></div>
        <div class="line" id="mySupport" style="margin-top:14px"><h4>己方支援区</h4><div id="mySupportUnits"></div></div>
      </div>
      <div class="vs">⚔️</div>
      <div>
        <div class="hq" id="enHQ"><div class="hp" id="enHP">20</div><div class="hq-label">敌方总部</div></div>
        <div class="line" id="enSupport" style="margin-top:14px"><h4>敌方支援区</h4><div id="enSupportUnits"></div></div>
      </div>
    </div>
    <div class="line active" id="frontline">
      <h4>前线（点击单位切换「已激活」状态）</h4>
      <div class="grid g2">
        <div><p style="color:var(--txt2);font-size:13px">己方前线单位</p><div id="myFront"></div></div>
        <div><p style="color:var(--txt2);font-size:13px">敌方前线单位</p><div id="enFront"></div></div>
      </div>
    </div>
    <div class="result-box" id="battleLog" style="min-height:60px">战斗日志将在此显示。</div>
  </div>
</section>

<!-- INNOVATE -->
<section id="innovate">
  <div class="wrap">
    <h2 class="sec-title">创新实验室</h2>
    <p class="sec-sub">六个原创拓展机制 + 自制对局 AI，可单独开启验证玩法。</p>
    <div class="grid g2">
      <div class="card">
        <h3>🧪 创新一：天气系统（动态战场）</h3>
        <p>每 3 回合随机一种天气，持续 2 回合：</p>
        <ul>
          <li><b>暴雨</b>：轰炸机伤害 -1，火炮费用 +1。</li>
          <li><b>浓雾</b>：所有单位获得烟幕，前线单位不可被指定。</li>
          <li><b>寒冬</b>：步兵行动费 +1，坦克行动费 -1。</li>
          <li><b>晴天</b>：战斗机攻击 +1。</li>
        </ul>
        <p style="margin-top:8px;color:var(--txt2)">设计意图：打破固定最优解，让同一套卡组在不同对局中需要不同打法。</p>
      </div>
      <div class="card">
        <h3>🧪 创新二：士气值（第二血量）</h3>
        <p>双方各有一个 0–10 的士气值。连续两回合未占领前线则士气 -2；占领则 +1。</p>
        <p><b>士气归零</b>：本回合所有单位行动费 +1，抽牌少 1 张。</p>
        <p style="margin-top:8px;color:var(--txt2)">设计意图：把「抢线」从战术选择升级为资源压力，防止双方都缩在支援区互射。</p>
      </div>
      <div class="card">
        <h3>🧪 创新三：战区事件（PvE 剧情）</h3>
        <p>每个战役包（斯大林格勒 / 诺曼底 / 阿登）带 5 张<b>事件牌</b>，按回合自动触发：</p>
        <ul>
          <li>「斯大林格勒巷战」：第 5 回合起，所有步兵 +1/+1。</li>
          <li>「敦刻尔克撤退」：第 3 回合，双方可免费撤退 2 个单位回支援区。</li>
        </ul>
        <p style="margin-top:8px;color:var(--txt2)">设计意图：用历史叙事给对局增加确定性节点，适合做战役模式与短视频内容。</p>
      </div>
      <div class="card">
        <h3>🧪 创新四：装备槽（卡牌成长）</h3>
        <p>每张单位卡自带 1 个装备槽，可装备<b>武器 / 装甲 / 配件</b>三类配件卡：</p>
        <ul>
          <li>长 88mm 炮：+2 攻击，但行动费 +1。</li>
          <li>附加装甲：+3 防御，但不能闪击。</li>
          <li>短波电台：相邻单位获得闪击。</li>
        </ul>
        <p style="margin-top:8px;color:var(--txt2)">设计意图：在 CCG 框架里塞进轻量 RPG 成长感，提高单卡复玩价值。</p>
      </div>
      <div class="card">
        <h3>🧪 创新五：将领卡（身份系统）</h3>
        <p>卡组 40 张中可含 1 张<b>不可被解</b>的将领卡（绑定总部，20 血），自带被动：</p>
        <ul>
          <li>隆美尔：「沙漠之狐」——你在前线的坦克 +1 攻击。</li>
          <li>朱可夫：「防御反击」——每第三个友军死亡抽一张。</li>
          <li>蒙哥马利：「坚守」——友军获得守护。</li>
        </ul>
        <p style="margin-top:8px;color:var(--txt2)">设计意图：天然的内容化钩子，适合做角色介绍、对局节目效果。</p>
      </div>
      <div class="card">
        <h3>🧪 创新六：情报战（隐藏信息）</h3>
        <p>新增<b>情报点</b>（每回合 +1，上限 3），可消耗情报发动：</p>
        <ul>
          <li>侦察：窥视对手 2 张手牌。</li>
          <li>误导：本回合一张指令牌对对手显示为另一张（迷惑判断）。</li>
          <li>破译：复制对手上一张使用的指令牌。</li>
        </ul>
        <p style="margin-top:8px;color:var(--txt2)">设计意图：把 CCG 的完全信息博弈变成不完全信息博弈，提升心理博弈深度。</p>
      </div>
    </div>

    <h3 style="margin:40px 0 16px;color:var(--gold)">🤖 自制对局 AI 试玩</h3>
    <p class="sec-sub">选择双方阵营，AI 各随机组卡并自动对战一局，每步回合日志可见。</p>
    <div class="filters">
      <label>玩家阵营<select id="aiMe">
        {% for f in factions if f != '通用' %}<option value="{{f}}">{{f}}</option>{% endfor %}
      </select></label>
      <label>AI 阵营<select id="aiEnemy">
        {% for f in factions if f != '通用' %}<option value="{{f}}">{{f}}</option>{% endfor %}
      </select></label>
      <label>启用创新
        <select id="aiInno">
          <option value="">仅原版规则</option>
          <option value="weather">+ 天气系统</option>
          <option value="morale">+ 士气值</option>
          <option value="all">+ 全部创新</option>
        </select>
      </label>
      <button class="btn primary" style="padding:10px 22px" onclick="runAI()">▶️ 开始对局</button>
    </div>
    <div class="result-box" id="aiLog" style="max-height:420px;overflow-y:auto;font-size:13.5px;white-space:pre-wrap"></div>
  </div>
</section>

<footer>
  <div class="wrap">
    KARDS 二战卡牌图鉴 & 创新实验室 · 教学向项目，规则仅供参考，具体以官方最新公告为准。<br>
    Built with Flask · 可直接复制代码本地运行
  </div>
</footer>

<script>
const CARDS = {{cards_json|safe}};
const FCOLOR = {{faction_color|safe}};
const FICON = {{faction_icon|safe}};
let DECK = [];
let BATTLE = {my:[], enemy:[], myFront:[], enFront:[], myHP:20, enHP:20};

// ---------- 卡牌渲染 ----------
function typeClass(t){return ({'步兵':'infantry','坦克':'tank','火炮':'artillery','战斗机':'fighter','轰炸机':'bomber','指令':'order'})[t]||'';}
function rarityClass(r){return ({'普通':'r-common','稀有':'r-rare','史诗':'r-epic'})[r]||'';}
function cardHTML(c){
  const fc = FCOLOR[c.faction]||'#555';
  return `<div class="card" style="border-top:3px solid ${fc};padding:16px">
    <div style="display:flex;justify-content:space-between;align-items:flex-start;margin-bottom:8px">
      <div><div style="font-weight:800;font-size:16px">${FICON[c.faction]||''} ${c.name}</div>
      <div style="font-size:12px;color:var(--txt2)">${c.faction} · <span class="tag ${typeClass(c.type)}">${c.type}</span></div></div>
      <div style="text-align:right"><div style="font-size:20px;font-weight:900;color:var(--gold)">${c.deploy}<span style="font-size:12px;color:var(--txt2)">/${c.action}</span></div>
      <div class="rarity ${rarityClass(c.tier)}">${c.tier}</div></div>
    </div>
    ${c.type!=='指令'?`<div style="font-size:13px;margin:8px 0;color:var(--txt)">⚔️ ${c.atk} / 🛡️ ${c.def}</div>`:''}
    <div style="font-size:13px;color:var(--txt2)">${c.desc}</div>
    ${c.kw.length?`<div style="margin-top:8px;font-size:12px;color:var(--gold)">${c.kw.map(k=>'⚡'+k).join(' ')}</div>`:''}
  </div>`;
}
function renderCards(){
  const f=document.getElementById('fFaction').value, t=document.getElementById('fType').value,
        r=document.getElementById('fRarity').value, mx=parseInt(document.getElementById('fCost').value),
        kw=document.getElementById('fKw').value.trim().toLowerCase();
  const out=CARDS.filter(c=>
    (!f||c.faction===f)&&(!t||c.type===t)&&(!r||c.tier===r)&&
    c.deploy<=mx&&(!kw||(c.name+c.desc+c.kw.join('')).toLowerCase().includes(kw)));
  document.getElementById('cardGrid').innerHTML = out.length
    ? out.map(cardHTML).join('')
    : '<p style="color:var(--txt2)">没有符合条件的卡牌。</p>';
}
function resetFilters(){['fFaction','fType','fRarity'].forEach(i=>document.getElementById(i).value='');
  document.getElementById('fCost').value='9';document.getElementById('fKw').value='';renderCards();}

// ---------- 卡组构建 ----------
function renderCollection(){
  const f=document.getElementById('bFaction').value, t=document.getElementById('bType').value;
  const out=CARDS.filter(c=>(!f||c.faction===f)&&(!t||c.type===t));
  document.getElementById('collection').innerHTML = out.map(c=>
    `<div class="card-item" onclick='addToDeck(${c.id})'>
      <span><b>${c.name}</b> <span class="tag ${typeClass(c.type)}">${c.type}</span> <span class="rarity ${rarityClass(c.tier)}">${c.tier}</span></span>
      <span style="color:var(--gold)">${c.deploy}/${c.action} · ${c.atk?'⚔️'+c.atk+'/🛡️'+c.def:'指令'}</span>
    </div>`).join('');
}
function addToDeck(id){
  if(DECK.length>=40){alert('卡组已满 40 张');return;}
  const c=CARDS.find(x=>x.id===id); if(!c)return;
  if(DECK.filter(x=>x.id===id).length>=2){alert('同名卡最多 2 张');return;}
  DECK.push(c); renderDeck();
}
function removeFromDeck(i){DECK.splice(i,1);renderDeck();}
function clearDeck(){DECK=[];renderDeck();}
function randomDeck(){
  const fac=document.getElementById('bFaction').value||null;
  const ally=prompt('输入盟国阵营（德/苏/美/英/日，留空则随机）','美国')||undefined;
  fetch('/api/random_deck',{method:'POST',headers:{'Content-Type':'application/json'},
    body:JSON.stringify({faction:fac, ally:ally==='undefined'?undefined:ally})})
  .then(r=>r.json()).then(d=>{DECK=d.deck;renderDeck();});
}
function exportDeck(){
  if(!DECK.length){alert('卡组为空');return;}
  const txt=DECK.map(c=>c.name).join('\\n');
  const blob=new Blob([txt],{type:'text/plain'});
  const a=document.createElement('a');a.href=URL.createObjectURL(blob);
  a.download='kards_deck.txt';a.click();
}
function renderDeck(){
  document.getElementById('deckCount').textContent=DECK.length+'/40';
  document.getElementById('deck-list').innerHTML = DECK.length
    ? DECK.map((c,i)=>`<div class="deck-row"><span>${FICON[c.faction]||''} ${c.name} <span class="tag ${typeClass(c.type)}">${c.type}</span></span><button onclick="removeFromDeck(${i})">移除</button></div>`).join('')
    : '<p style="color:var(--txt2)">从左侧卡牌库点击加入卡牌。</p>';
  fetch('/api/validate',{method:'POST',headers:{'Content-Type':'application/json'},
    body:JSON.stringify({deck:DECK.map(c=>c.id)})})
  .then(r=>r.json()).then(d=>{
    let h='';
    if(d.stats.main) h+=`<div>主阵营：<b style="color:var(--gold)">${d.stats.main}</b>　盟国卡：<b>${d.stats.ally_count}</b>/12</div>`;
    if(d.stats.curve) h+=`<div>费用曲线 L${d.stats.low} / M${d.stats.mid} / H${d.stats.high}</div>`;
    if(d.stats.types) h+=`<div>类型：${Object.entries(d.stats.types).map(([k,v])=>k+' '+v).join(' · ')}</div>`;
    h+=d.errors.length?`<div class="bad">✖ ${d.errors.join('<br>✖ ')}</div>`:'<div class="ok">✔ 卡组合法，可上阵！</div>';
    h+=d.warnings.length?`<div class="warn">⚠ ${d.warnings.join('<br>⚠ ')}</div>`:'';
    if(d.archetype) h+=`<div>推荐流派：<b style="color:var(--gold)">${d.archetype}</b></div>`;
    document.getElementById('deckResult').innerHTML=h;
    drawCurve(d.stats.curve||[]);
  });
}
function drawCurve(curve){
  const cv=document.getElementById('curveChart'),ctx=cv.getContext('2d');
  ctx.clearRect(0,0,cv.width,cv.height);
  const labels=['1','2','3','4','5','6','7+'], maxv=Math.max(...curve,1);
  const step=cv.width/8, base=cv.height-20;
  ctx.fillStyle='#9fb0c0';ctx.font='12px sans-serif';
  curve.forEach((v,i)=>{
    const h=(v/maxv)*(cv.height-40);
    ctx.fillStyle='#e0a800';
    ctx.fillRect(i*step+18, base-h, step-26, h);
    ctx.fillStyle='#9fb0c0';ctx.fillText(labels[i], i*step+22, cv.height-4);
    ctx.fillText(v, i*step+24, base-h-6);
  });
}

// ---------- 战斗模拟 ----------
function unitChip(u,side,idx){
  return `<div class="unit-chip ${u.active?'attack':''}" style="border-left:4px solid ${FCOLOR[u.faction]}" onclick="toggleActive('${side}',${idx})">
    <span>${u.active?'🟢':'⚪'} ${u.name} ${u.canMove?'🚶':''}</span><span>⚔️${u.atk} 🛡️${u.def}</span></div>`;
}
function toggleActive(side,idx){
  const arr=side==='my'?BATTLE.myFront:BATTLE.enFront;
  arr[idx].active=!arr[idx].active; renderBattle();
}
function addFriendly(id){
  const c=CARDS.find(x=>x.id===+id); if(!c||c.type==='指令')return;
  BATTLE.myFront.push({name:c.name,faction:c.faction,atk:c.atk,def:c.def,active:true,canMove:true}); renderBattle();
}
function addEnemy(id){
  const c=CARDS.find(x=>x.id===+id); if(!c||c.type==='指令')return;
  BATTLE.enFront.push({name:c.name,faction:c.faction,atk:c.atk,def:c.def,active:false,canMove:true}); renderBattle();
}
function runAttack(){
  const log=[];
  BATTLE.myFront.filter(u=>u.active).forEach(u=>{
    if(BATTLE.enFront.length){
      const tgt=BATTLE.enFront[0];
      log.push(`${u.name} 攻击 ${tgt.name}：${u.atk} 伤害`);
      tgt.def-=u.atk;
      if(tgt.def<=0){log.push(`  → ${tgt.name} 被摧毁！`);BATTLE.enFront.shift();}
    }else{
      log.push(`${u.name} 直击敌方总部：+${u.atk} 伤害`);
      BATTLE.enHP-=u.atk;
    }
  });
  if(BATTLE.enHP<=0)log.push('🏆 己方获胜！');
  document.getElementById('battleLog').innerHTML=log.length?log.join('<br>'):'没有可激活的己方单位。';
  renderBattle();
}
function renderBattle(){
  document.getElementById('myHP').textContent=Math.max(BATTLE.myHP,0);
  document.getElementById('enHP').textContent=Math.max(BATTLE.enHP,0);
  document.getElementById('myHQ').className='hq'+(BATTLE.myHP<=6?' danger':'');
  document.getElementById('enHQ').className='hq'+(BATTLE.enHP<=6?' danger':'');
  document.getElementById('myFront').innerHTML=BATTLE.myFront.map((u,i)=>unitChip(u,'my',i)).join('')||'<p style="color:var(--txt2)">空</p>';
  document.getElementById('enFront').innerHTML=BATTLE.enFront.map((u,i)=>unitChip(u,'en',i)).join('')||'<p style="color:var(--txt2)">空</p>';
}
function resetBattle(){BATTLE={my:[],enemy:[],myFront:[],enFront:[],myHP:20,enHP:20};renderBattle();
  document.getElementById('battleLog').textContent='战场已重置。';}

// ---------- AI 对局 ----------
function runAI(){
  const log=document.getElementById('aiLog');
  log.textContent='对局进行中…';
  fetch('/api/ai_battle',{method:'POST',headers:{'Content-Type':'application/json'},
    body:JSON.stringify({me:document.getElementById('aiMe').value,
      enemy:document.getElementById('aiEnemy').value,
      inno:document.getElementById('aiInno').value})})
  .then(r=>r.json()).then(d=>{
    log.innerHTML=d.log.map(l=>`<div style="margin-bottom:3px">${l}</div>`).join('');
  });
}

// ---------- 导航高亮 ----------
document.querySelectorAll('nav a').forEach(a=>a.addEventListener('click',function(){
  document.querySelectorAll('nav a').forEach(x=>x.classList.remove('on'));
  this.classList.add('on');}));

renderCards();renderCollection();renderDeck();renderBattle();
</script>
</body>
</html>
"""

# ============ 路由 ============
@app.route("/")
def home():
    factions = sorted(set(c["faction"] for c in CARDS))
    types = sorted(set(c["type"] for c in CARDS),
                   key=lambda t: ["步兵","坦克","火炮","战斗机","轰炸机","指令"].index(t))
    return render_template_string(BASE,
        cards_json=json.dumps(CARDS, ensure_ascii=False),
        faction_color=json.dumps(FACTION_COLOR, ensure_ascii=False),
        faction_icon=json.dumps(FACTION_ICON, ensure_ascii=False),
        cards=CARDS, factions=factions, types=types, card_count=len(CARDS))

@app.route("/api/validate", methods=["POST"])
def api_validate():
    data = request.get_json(force=True)
    deck = [get_card(cid) for cid in data.get("deck", []) if get_card(cid)]
    res = validate_deck(deck)
    res["archetype"] = deck_archetype(res["stats"])
    return res

@app.route("/api/random_deck", methods=["POST"])
def api_random():
    data = request.get_json(force=True) or {}
    faction = data.get("faction") or "德国"
    ally = data.get("ally")
    # 若未指定盟国，随机挑一个非通用非主国的
    if not ally:
        opts = [f for f in set(c["faction"] for c in CARDS) if f not in (faction, "通用")]
        ally = random.choice(opts)
    deck = fill_deck(faction, ally)
    random.shuffle(deck)
    return {"deck": deck, "main": faction, "ally": ally}

@app.route("/api/ai_battle", methods=["POST"])
def api_ai():
    data = request.get_json(force=True) or {}
    me, enemy = data.get("me", "德国"), data.get("enemy", "苏联")
    inno = data.get("inno", "")
    log = []
    log.append(f"⚔️ 对局开始：玩家【{me}】 vs AI【{enemy}】" + (f"  · 创新模式：{inno}" if inno else "  · 原版规则"))
    if inno in ("weather", "all"):
        log.append("🌦️ 天气系统已开启：每 3 回合切换暴雨/浓雾/寒冬/晴天。")
    if inno in ("morale", "all"):
        log.append("🎖️ 士气系统已开启：未占领前线士气 -2，士气归零惩罚行动费。")
    ally_for_me = random.choice([f for f in set(c["faction"] for c in CARDS) if f not in (me, "通用")])
    ally_for_en = random.choice([f for f in set(c["faction"] for c in CARDS) if f not in (enemy, "通用")])
    my_deck = fill_deck(me, ally_for_me)
    en_deck = fill_deck(enemy, ally_for_en)
    vme = validate_deck(my_deck); ven = validate_deck(en_deck)
    log.append(f"📋 玩家卡组（{me} + {ally_for_me}，{len(my_deck)} 张，流派 {deck_archetype(vme['stats'])}）")
    log.append(f"📋 AI 卡组（{enemy} + {ally_for_en}，{len(en_deck)} 张，流派 {deck_archetype(ven['stats'])}）")

    # 简易对局模拟
    my_hp, en_hp = 20, 20
    my_cp, en_cp = 1, 1
    morale = {"me": 5, "en": 5}
    weather = None
    for turn in range(1, 21):
        my_cp = min(my_cp + 1, 12)
        en_cp = min(en_cp + 1, 12)
        # 天气切换
        if inno in ("weather", "all") and turn % 3 == 0:
            weather = random.choice(["暴雨", "浓雾", "寒冬", "晴天"])
            log.append(f"—— 第 {turn} 回合天气：{weather} ——")
        # 士气
        front_owner = random.choice(["me", "en"])
        if inno in ("morale", "all"):
            if front_owner == "me": morale["me"] = min(10, morale["me"] + 1); morale["en"] = max(0, morale["en"] - 2)
            else: morale["en"] = min(10, morale["en"] + 1); morale["me"] = max(0, morale["me"] - 2)
            log.append(f"第 {turn} 回合：士气 玩家 {morale['me']} / AI {morale['en']}　前线归属：{'玩家' if front_owner=='me' else 'AI'}")
        else:
            log.append(f"—— 第 {turn} 回合 ——")

        # 双方各随机打出 1-3 张卡，结算伤害
        for owner, cp_ref in (("me", "my_cp"), ("en", "en_cp")):
            play = random.randint(1, 3)
            dmg = 0
            deck = my_deck if owner == "me" else en_deck
            for _ in range(play):
                c = random.choice(deck)
                if c["type"] == "指令":
                    import re
                    m = re.search(r"造成\s*(\d+)\s*点伤害", c.get("desc", ""))
                    if m: dmg += int(m.group(1))
                elif c["type"] in ("步兵", "坦克", "火炮", "战斗机", "轰炸机"):
                    dmg += c["atk"]
                    if "奋战" in c["kw"]: dmg += c["atk"]
            if owner == "me":
                en_hp = max(0, en_hp - dmg); log.append(f"  玩家 打出 {play} 张，造成 {dmg} 点伤害 → AI 总部 {en_hp}")
            else:
                my_hp = max(0, my_hp - dmg); log.append(f"  AI 打出 {play} 张，造成 {dmg} 点伤害 → 玩家总部 {my_hp}")
        if my_hp == 0 or en_hp == 0:
            break
    if en_hp == 0 and my_hp == 0:
        log.append("🤝 同归于尽！")
    elif en_hp == 0:
        log.append(f"🏆 玩家【{me}】获胜！剩余总部 {my_hp} 点。")
    else:
        log.append(f"💀 AI【{enemy}】获胜！剩余总部 {en_hp} 点。")
    return {"log": log}

if __name__ == "__main__":
    app.run(host="127.0.0.1", port=5000, debug=True)

