云剪贴板

云剪贴板 hv93534e

公开

云剪贴板操作

dingshengyang2026/01/02 14:29 创建,当前公开

当前快照
1 份
快照标识符
@mjwqyocg
此快照首次捕获于
2026/01/02 18:44
2 个月前
此快照最后确认于
2026/01/02 18:44
2 个月前
查看原剪贴板
版本 v1.5.3 更新摘要:
  1. \ceSO3\ce{SO3} (三氧化硫) 重做:由“指定目标”改为**“全场随机目标”**。强酸的腐蚀是不可控的,使用不当可能误伤友军。
  2. \ceAl\ce{Al} (铝) 机制优化:移除了“是否驻场”的询问。若环境导致溶解,强制反应并补偿抽牌;若环境适宜,强制进场。
  3. \ceCl2\ce{Cl2} (氯气) 结算顺序:亡语先增加酸性,再结算AOE伤害(确保伤害享受酸性环境加成)。
  4. 实时环境响应\ceMnO4\ce{MnO4-}\ceHNO3\ce{HNO3} 的数值现在会在同回合内随环境变化即时刷新。
版本 v1.5.4 更新摘要:
  1. 后手贴目机制 (Buffer Capacity):为了平衡先手优势,后手玩家 (Player 2) 初始获得 +2 HP 上限+2 当前 HP
  2. 继承 v1.5.3 所有特性

Chem-Cards 规则手册 (v1.5.4)

1. 基础规则

1.1 卡牌分类

  • [N] 驻场卡 (Normal/Unit):拥有 HP (生命) 和 DP (攻击)。放置在卡槽中战斗。
  • [S] 机制卡 (Spell/Mechanism):打出后执行效果,随即进入弃牌堆。

1.2 环境参数

  • 酸碱度 [H]:整数范围 [6,+6][-6, +6]>0>0 为酸性,<0<0 为碱性,=0=0 为中性。
  • 氧浓度 [O]:整数范围 [0,+][0, +\infty]

1.3 核心流程

  • 消耗 (Consume):[N]卡攻击敌方本体后,视为耗尽,移出游戏。不触发亡语
  • 死亡 (Die):[N]卡 HP 0\le 0 或被效果销毁(如酸蚀、爆炸)。触发亡语
  • 替换/献祭:卡槽满时打出新卡,旧卡视为死亡,触发亡语

1.4 游戏区域与配置

  • 玩家本体:先手玩家 (Player 1) 初始生命值 (HP) 20,上限 30。后手玩家 (Player 2) 初始获得 +2 HP 上限+2 当前 HP,即初始 HP 22,上限 32。HP \le 0 时判定为负,游戏结束。
  • 卡槽:每位玩家拥有 3 个卡槽。
  • 手牌与牌堆:游戏开始时,先手抽 6 张,后手抽 6 张。

1.5 流程与动作

1.5.1 出牌 (Play)

  • 每回合玩家通常只能打出一张牌(无论 [N] 还是 [S])。
  • 替换规则:若卡槽已满(3张),玩家仍可打出新的 [N] 卡。此时必须指定己方场上一张旧卡进行“献祭”(视为死亡,触发亡语),腾出空位放置新卡。

1.5.2 攻击结算 (Combat)

  • 单位对战:攻击方 A 选定 敌方 B。
    • 过程视为同时发生:AHPAHPBDPA_{HP} \leftarrow A_{HP} - B_{DP},且 BHPBHPADPB_{HP} \leftarrow B_{HP} - A_{DP}
    • 双方卡牌持续进行上述交换,直到一方或双方 HP 0\le 0,或触发特殊机制(如 \ceCu\ce{Cu} 的减伤锁血)强制中止。
    • HP 0\le 0 的卡牌进入弃牌堆,并触发亡语
  • 直接攻击:若敌方前场无卡,攻击方 A 可攻击敌方本体。
    • 敌方本体受到 ADPA_{DP} 点伤害(这是防御减免之前的伤害。如果有减免,应考虑减免)。
    • 结算后,卡牌 A 视为“消耗”,移入弃牌堆,不触发亡语

1.5.3 抽牌规则 (Draw)

回合结束阶段,按以下优先级依次结算:
  1. 卡牌特效:结算回合内打出的抽牌效果(如 \ceH2O\ce{H2O})。
  2. 牌差补偿:若双方“总牌数”(手牌+场上卡牌)差值 >2>2,劣势方(总牌数少的一方)抽 1 张。
  3. 空手补偿:若某一方手牌数为 0,该方抽 1 张。

1.5.4 弃牌 (Discard)

当效果要求弃牌时,若无特殊指定,由系统随机选择手牌丢弃。

2. 卡牌图鉴 (完整版 v1.5.4)

2.1 基础环境组

  1. \ceH+\ce{H+} [S]:令 \ce[H][H]+1\ce{[H] \leftarrow [H] + 1}
  2. \ceOH\ce{OH-} [S]:令 \ce[H][H]1\ce{[H] \leftarrow [H] - 1}
  3. \ceH2O\ce{H2O} [S]:抽 1 张牌。
  4. \ceH2O2\ce{H2O2} [S]:令 \ce[O][O]+1\ce{[O] \leftarrow [O] + 1},并触发两次 \ceH2O\ce{H2O} 效果(抽 2 张)。

2.2 金属与氧化还原组

  1. \ceAl\ce{Al} [N] (HP=5, DP=3\text{HP=5, DP=3}) [v1.5.3 重做]
    • 两性反应:打出时立即检测环境。
      • \ce[H]4\ce{[H] \le -4}:溶解(\ce[H][H]+4\ce{[H] \leftarrow [H] + 4}),玩家抽 1 张牌,卡牌去弃牌堆。
      • \ce[H]+3\ce{[H] \ge +3}:溶解(\ce[H][H]3\ce{[H] \leftarrow [H] - 3}),玩家抽 1 张牌,卡牌去弃牌堆。
      • 否则:卡牌进场驻守。
  2. \ceCu\ce{Cu} [N] (HP=8, DP=2\text{HP=8, DP=2}):
    • 惰性盾牌:在单次单位对攻中,最多受到 3 点伤害(超过部分无效)。
    • 物理屏障:己方本体受到的伤害 -2。
  3. \ceFe3+\ce{Fe^3+} [S]:
    • 沉淀/蚀刻:若 \ce[H]3\ce{[H] \le -3},生成沉淀(\ce[H][H]+3\ce{[H] \leftarrow [H] + 3})。否则,若敌方场上有 \ceCu\ce{Cu},直接销毁之(同归于尽)。
  4. \ceCl2\ce{Cl2} [N] (HP=2, DP=3\text{HP=2, DP=3}) [v1.5.3 机制更新]
    • 亡语被击败或被效果销毁时(直接攻击对方本体导致的消耗不触发)
      1. \ce[H][H]+1\ce{[H] \leftarrow [H] + 1}
      2. 对敌方全场(单位+本体)造成 2 点伤害。
  5. \ceMnO4\ce{MnO4-} [N] (HP=2, DP=动态\text{HP=2, DP=动态}) [v1.5.3 实时响应]
    • 强氧化性:攻击力 DP=2+max(0,\ce[H])\text{DP} = 2 + \max(0, \ce{[H]})。攻击力随环境实时变化。
  6. \ceF2\ce{F2} [N] (HP=2, DP=5\text{HP=2, DP=5}):
    • 亡语被击败或被效果销毁时(直接攻击对方本体导致的消耗不触发),对敌方本体造成 4 点伤害,并弃掉敌方 3 张手牌。

2.3 有机与特殊组

  1. \ceSCN\ce{SCN-} [S]:检视敌方手牌,强制弃掉手中的 \ceFe3+\ce{Fe^3+}
  2. \ceC2H5OH\ce{C2H5OH} [S]:对敌方本体造成 3 点伤害。
  3. \ceC6H12O6\ce{C6H12O6} [S]:三选一(需弃一半手牌,向下取整):
      1. 无氧呼吸:触发两次 \ceC2H5OH\ce{C2H5OH} 的效果,本体额外回复 1 点 HP。
      1. 有氧呼吸(需 [O]6\text{[O]} \ge 6):触发六次 \ceH2O\ce{H2O} 的效果(抽6张),本体额外回复 1 点 HP。
      1. 糖原储存:己方本体回复 4 点 HP。

2.4 精英/稀有组

  1. \ceV\ce{V} (钒) [N] (HP=7, DP=3\text{HP=7, DP=3}):
    • 合金:进场时,己方其他金属卡牌 HP上限+2 并回满。
  2. \ceAu\ce{Au} [N] (HP=6, DP=2\text{HP=6, DP=2}):
    • 金身:己方本体免疫所有伤害。
    • 保值:回合结束时若本卡在场且己方本体 HP<24\text{HP}<24,回 2 血。
  3. \ceHNO3\ce{HNO3} [N] (HP=5, DP=动态\text{HP=5, DP=动态}) [v1.5.3 实时响应]
    • 酸雾:进场时 \ce[H][H]+1\ce{[H] \leftarrow [H] + 1}
    • 氧化酸:基础攻击力 DP=1+max(1,\ce[H])\text{DP} = 1 + \max(1, \ce{[H]})。攻击力随环境实时变化。攻击金属卡牌时 DP+2。

2.5 混乱平衡组 (Chaos Balance)

  1. \ceNH3\ce{NH3} [N] (HP=3, DP=2\text{HP=3, DP=2}):
    • 碱源:进场时 \ce[H][H]1\ce{[H] \leftarrow [H] - 1}
    • 喷泉:回合结束若环境为碱性(\ce[H]<0\ce{[H]}<0),己方本体回 2 血。
  2. \ceMg\ce{Mg} [N] (HP=5, DP=3\text{HP=5, DP=3}):
    • 钝化:环境首次变碱性(\ce[H]<0\ce{[H]}<0)时,获得永久 +3 HP上限(只触发一次)。
    • 酸蚀:回合结束若 \ce[H]3\ce{[H] \ge 3},自身受 3 点伤害。
  3. \ceNa\ce{Na} [S]:
    • 剧烈反应\ce[H][H]1\ce{[H] \leftarrow [H] - 1};己方本体受 1 伤;对敌方随机单位造成 4 伤(若空场则对本体 3 伤)。
  4. \ceSO3\ce{SO3} [S] [v1.5.3 重做]
    • 酸雨:打出后,\ce[H][H]+2\ce{[H] \leftarrow [H] + 2}
    • 无差别脱水:系统从全场所有存活单位(包含己方和敌方)中随机选择一个,对其造成 4 点伤害。
    • 溅射:若该单位被此伤害击杀,其持有者受到 2 点溅射伤害。

Chem-Cards 电子裁判代码 (v1.5.4)

请保存为 chem_cards_referee_v1.5.4.py
PYTHON
import random
import math

# ==========================================
# 基础架构类
# ==========================================

class Card:
    def __init__(self, name, card_type, hp=0, dp=0, desc=""):
        self.name = name
        self.type = card_type  # 'N' (Normal) or 'S' (Special)
        self.max_hp = hp
        self.hp = hp
        self._base_dp = dp
        self._dp = dp
        self.desc = desc
        self.is_metal = False 

    @property
    def dp(self):
        return self._dp

    @dp.setter
    def dp(self, value):
        self._dp = value

    def reset_stats(self):
        self.hp = self.max_hp
        self._dp = self._base_dp

    def __str__(self):
        if self.type == 'N':
            return f"[{self.name} HP:{self.hp}/{self.max_hp} DP:{self.dp}]"
        return f"[{self.name} (S)]"

    # --- 生命周期钩子 ---
    def on_play(self, game, player, opponent):
        """S卡打出时的效果"""
        pass

    def on_deploy(self, game, player):
        """N卡进场效果 (战吼)"""
        pass

    def on_death(self, game, player, opponent):
        """亡语:被移除/销毁时触发。"""
        pass

    def on_board_effect(self, game, player, dmg):
        """伤害拦截/减免"""
        return dmg
    
    def on_turn_end(self, game, player):
        """回合结束效果"""
        pass
    
    def update_continuous_state(self, game):
        """状态更新:环境改变时立即调用"""
        pass

# ==========================================
# 具体卡牌实现 (v1.5.3 标准)
# ==========================================

# --- 基础环境组 ---
class Card_H_Plus(Card):
    def __init__(self):
        super().__init__("H⁺", 'S', desc="环境 [H] + 1")
    def on_play(self, game, player, opponent):
        game.modify_h(1)

class Card_OH_Minus(Card):
    def __init__(self):
        super().__init__("OH⁻", 'S', desc="环境 [H] - 1")
    def on_play(self, game, player, opponent):
        game.modify_h(-1)

class Card_H2O(Card):
    def __init__(self):
        super().__init__("H₂O", 'S', desc="抽一张牌")
    def on_play(self, game, player, opponent):
        print(f"系统: 💧 {self.name} 润泽,抽牌。")
        game.player_draw(player, 1)

class Card_H2O2(Card):
    def __init__(self):
        super().__init__("H₂O₂", 'S', desc="抽两张牌; [O]+1")
    def on_play(self, game, player, opponent):
        print(f"系统: {self.name} 分解!环境 [O] + 1,并发动双倍水效。")
        game.modify_o(1)
        game.player_draw(player, 2)

# --- 金属与氧化还原组 ---

class Card_Al(Card):
    def __init__(self):
        super().__init__("Al", 'N', hp=5, dp=3, desc="强酸/强碱自动溶解抽1牌")
        self.is_metal = True
    
    def on_play(self, game, player, opponent):
        """Al卡打出时的处理"""
        pass
    
    def check_reaction_and_result(self, game, player):
        """
        自动判定,自动抽牌。
        返回 True 表示已反应并处理完毕(不进场)。
        """
        if game.H <= -4:
            print(f"系统: 🧪 {self.name} 在强碱环境([H]={game.H})下溶解生成偏铝酸根!")
            print("-> 环境 [H] + 4")
            game.modify_h(4)
            print("-> 反应产物回收:玩家抽 1 张牌。")
            game.player_draw(player, 1)
            return True 
        elif game.H >= 3:
            print(f"系统: 🧪 {self.name} 在酸性环境([H]={game.H})下溶解生成铝离子!")
            print("-> 环境 [H] - 3")
            game.modify_h(-3)
            print("-> 反应产物回收:玩家抽 1 张牌。")
            game.player_draw(player, 1)
            return True 
        return False

class Card_Cu(Card):
    def __init__(self):
        super().__init__("Cu", 'N', hp=8, dp=2, desc="单轮锁血3; 本体减伤2")
        self.is_metal = True

    def on_board_effect(self, game, player, dmg):
        reduced = max(0, dmg - 2)
        if dmg > 0 and reduced < dmg:
            print(f"系统: 🛡️ {self.name} 物理屏障,伤害 {dmg} -> {reduced}")
        return reduced

class Card_Fe3_Plus(Card):
    def __init__(self):
        super().__init__("Fe³⁺", 'S', desc="H<=-3沉淀; 遇Cu同归于尽")

    def on_play(self, game, player, opponent):
        if game.H <= -3:
            print(f"系统: {self.name} 在碱性环境([H]={game.H})下生成红褐色沉淀![H] + 3。")
            game.modify_h(3)
            return

        cu_indices = [i for i, c in enumerate(opponent.slots) if c and isinstance(c, Card_Cu)]
        if cu_indices:
            idx = cu_indices[0] 
            target_cu = opponent.slots[idx]
            print(f"系统: 🧪 {self.name} 蚀刻了 {target_cu.name}!双方同归于尽。")
            opponent.remove_slot_card(idx, game, trigger_deathrattle=True)
        else:
            print(f"系统: {self.name} 缺乏反应条件,无效挥发。")

class Card_Cl2(Card):
    def __init__(self):
        super().__init__("Cl₂", 'N', hp=2, dp=3, desc="亡语: [H]+1 & 全场AOE 2")

    def on_death(self, game, player, opponent):
        print(f"系统: ☠️ {self.name} 亡语触发!")
        
        # 先改环境,确保AOE伤害可能享受环境加成
        print("-> 气体水解,环境 [H] + 1。")
        game.modify_h(1)

        dmg = 2
        print(f"-> 毒气扩散!对敌方全员造成 {dmg} 点伤害。")
        
        # 攻击敌方单位
        for i in range(3):
            c = opponent.slots[i]
            if c:
                prev_hp = c.hp
                c.hp -= dmg
                print(f"   蚀刻 {c.name}: {prev_hp} -> {c.hp}")
                if c.hp <= 0:
                    print(f"   💀 {c.name} 被摧毁!")
                    opponent.remove_slot_card(i, game, trigger_deathrattle=True)
        
        # 攻击敌方本体
        final_dmg = game.calc_damage_mitigation(opponent, dmg)
        if final_dmg > 0:
            opponent.hp -= final_dmg
            print(f"   敌方本体受到 {final_dmg} 点毒气伤害。")
        else:
            print("   敌方本体免疫了毒气伤害。")

class Card_MnO4_Minus(Card):
    def __init__(self):
        super().__init__("MnO₄⁻", 'N', hp=2, dp=2, desc="DP = 2 + [H]")
    
    def update_continuous_state(self, game):
        # 动态更新:DP = 2 + max(0, H)
        base = 2
        bonus = game.H if game.H > 0 else 0
        new_dp = base + bonus
        if self.dp != new_dp:
            self.dp = new_dp

class Card_F2(Card):
    def __init__(self):
        super().__init__("F₂", 'N', hp=2, dp=5, desc="亡语: 敌方本体4伤+弃3牌")
    
    def on_death(self, game, player, opponent):
        print(f"系统: ☢️ {self.name} 亡语爆发!")
        dmg = 4
        final_dmg = game.calc_damage_mitigation(opponent, dmg)
        
        if final_dmg > 0:
            opponent.hp -= final_dmg
            print(f"-> 氟气重创敌方本体,造成 {final_dmg} 点伤害!")
        
        discard_cnt = min(3, len(opponent.hand))
        if discard_cnt > 0:
            print(f"-> 氟气迫使敌方弃掉 {discard_cnt} 张牌。")
            for _ in range(discard_cnt):
                if not opponent.hand: break
                idx = random.randint(0, len(opponent.hand)-1)
                c = opponent.hand.pop(idx)
                game.discard_pile.append(c)

# --- 有机与特殊组 ---
class Card_SCN_Minus(Card):
    def __init__(self):
        super().__init__("SCN⁻", 'S', desc="针对 Fe3+ 弃牌")

    def on_play(self, game, player, opponent):
        fe_indices = [i for i, c in enumerate(opponent.hand) if isinstance(c, Card_Fe3_Plus)]
        n = len(fe_indices)
        if n > 0:
            discard_cnt = 1 if n == 1 else n - 1
            print(f"系统: 检测到敌方手中有 {n} 张 Fe3+,络合反应迫使其丢弃 {discard_cnt} 张!")
            to_discard_indices = fe_indices[:discard_cnt]
            for idx in sorted(to_discard_indices, reverse=True):
                card = opponent.hand.pop(idx)
                game.discard_pile.append(card)
                print(f"-> 敌方丢弃了 {card.name}")
        else:
            print("系统: 敌方手中未检出 Fe3+。")

class Card_C2H5OH(Card):
    def __init__(self):
        super().__init__("C₂H₅OH", 'S', desc="对敌方造成 3 点伤害")

    def on_play(self, game, player, opponent):
        self.apply_effect(game, opponent)

    def apply_effect(self, game, target_player):
        raw_dmg = 3
        print(f"系统: 🍺 {self.name} 造成醉酒伤害。")
        final_dmg = game.calc_damage_mitigation(target_player, raw_dmg)
        if final_dmg > 0:
            target_player.hp -= final_dmg
            print(f"-> 敌方受到 {final_dmg} 点伤害!")

class Card_C6H12O6(Card):
    def __init__(self):
        super().__init__("C₆H₁₂O₆", 'S', desc="代谢抉择; 弃一半手牌")

    def on_play(self, game, player, opponent):
        print(f"\n[{self.name} 代谢路径选择]")
        print("1. 无氧呼吸 (2次乙醇伤害)")
        
        can_aerobic = (game.O >= 6)
        opt2_str = "2. 有氧呼吸 (抽6张牌)" if can_aerobic else "2. [不可用] 有氧呼吸 (需 [O] >= 6)"
        print(opt2_str)
        print("3. 糖原储存 (HP + 4)")
        
        valid_choices = ['1', '3']
        if can_aerobic: valid_choices.append('2')
        
        choice = ''
        while choice not in valid_choices:
            choice = input(f"请输入选项 ({'/'.join(valid_choices)}): ").strip()

        did_respire = False
        if choice == '1':
            print("系统: 进行无氧呼吸发酵...")
            eff = Card_C2H5OH()
            eff.apply_effect(game, opponent)
            eff.apply_effect(game, opponent)
            did_respire = True
        elif choice == '2':
            print("系统: 进行高效有氧呼吸...")
            game.player_draw(player, 6)
            did_respire = True
        elif choice == '3':
            print("系统: 转化为糖原储备,回复 4 HP。")
            player.heal(4)
        
        if did_respire:
            print("系统: 呼吸作用提供能量,额外回复 1 HP。")
            player.heal(1)

        cnt = math.floor(len(player.hand) / 2)
        if cnt > 0:
            print(f"系统: 代谢产物堆积,随机丢弃 {cnt} 张手牌。")
            for _ in range(cnt):
                if not player.hand: break
                idx = random.randint(0, len(player.hand)-1)
                c = player.hand.pop(idx)
                game.discard_pile.append(c)
                print(f"-> 丢弃了 {c.name}")

# --- 精英/稀有卡 ---
class Card_V(Card):
    def __init__(self):
        super().__init__("V", 'N', hp=7, dp=3, desc="战吼: 强化其他金属")
        self.is_metal = True 
    
    def on_deploy(self, game, player):
        print(f"系统: 🏗️ {self.name} 进场,添加合金元素!")
        found = False
        for c in player.slots:
            if c and c != self and c.is_metal:
                c.max_hp += 2
                c.hp = c.max_hp # 回满
                print(f"-> {c.name} 获得强化! (HP:{c.hp}/{c.max_hp})")
                found = True
        if not found:
            print("-> 场上无其他金属单质可强化。")

class Card_Au(Card):
    def __init__(self):
        super().__init__("Au", 'N', hp=6, dp=2, desc="本体免疫; 回合末回血")
        self.is_metal = True
    
    def on_board_effect(self, game, player, dmg):
        if dmg > 0:
            print(f"系统: 🌟 {self.name} 黄金屏障!伤害 {dmg} 被完全免疫!")
        return 0

    def on_turn_end(self, game, player):
        if player.hp < 24:
            print(f"系统: ✨ {self.name} 储备增值,回复 2 HP。")
            player.heal(2)

class Card_HNO3(Card):
    def __init__(self):
        super().__init__("HNO₃", 'N', hp=5, dp=2, desc="战吼:[H]+1; 对金属伤害+2")
    
    def on_deploy(self, game, player):
        print(f"系统: ⚗️ {self.name} 挥发酸雾!")
        game.modify_h(1)

    def update_continuous_state(self, game):
        new_dp = max(2, 1 + game.H)
        if self.dp != new_dp:
            self.dp = new_dp

# --- 混乱平衡组 ---

class Card_NH3(Card):
    def __init__(self):
        super().__init__("NH₃", 'N', hp=3, dp=2, desc="碱性源; 碱性环境回血")
    
    def on_deploy(self, game, player):
        print(f"系统: 💨 {self.name} 释放,环境碱性增强。")
        game.modify_h(-1)
        
    def on_turn_end(self, game, player):
        if game.H < 0:
            print(f"系统: ⛲ {self.name} 触发喷泉效应,本体回复 2 HP。")
            player.heal(2)

class Card_Mg(Card):
    def __init__(self):
        super().__init__("Mg", 'N', hp=5, dp=3, desc="碱性钝化(+3HP); 强酸受损")
        self.is_metal = True
        self._buffed = False
    
    def update_continuous_state(self, game):
        # 钝化膜是一次性生成的永久Buff
        if game.H < 0 and not self._buffed:
            self._buffed = True
            self.max_hp += 3
            self.hp += 3
            print(f"系统: 🛡️ {self.name} 在碱性环境中生成致密钝化膜!HP上限+3 (当前 {self.hp}/{self.max_hp})。")
    
    def on_turn_end(self, game, player):
        if game.H >= 3:
            dmg = 3
            print(f"系统: 🧪 {self.name} 在强酸([H]={game.H})中剧烈反应,受到 {dmg} 点腐蚀伤害!")
            self.hp -= dmg
            if self.hp <= 0:
                print(f"-> {self.name} 溶解殆尽。")
                try:
                    idx = player.slots.index(self)
                    player.remove_slot_card(idx, game, trigger_deathrattle=True)
                except ValueError:
                    pass

class Card_Na(Card):
    def __init__(self):
        super().__init__("Na", 'S', desc="[H]-1; 自伤1; 随机敌方4伤")
        
    def on_play(self, game, player, opponent):
        print(f"系统: 💥 {self.name} 投入水中!剧烈反应!")
        game.modify_h(-1)
        
        print(f"-> ⚠️ 爆炸波及操作者!己方本体受到 1 点伤害。")
        player.hp -= 1
        
        targets = [i for i, c in enumerate(opponent.slots) if c]
        if targets:
            target_idx = random.choice(targets)
            target_card = opponent.slots[target_idx]
            dmg = 4
            print(f"-> 爆炸随机命中了敌方卡牌 {target_card.name},造成 {dmg} 点伤害!")
            target_card.hp -= dmg
            if target_card.hp <= 0:
                print(f"   {target_card.name} 被炸毁!")
                opponent.remove_slot_card(target_idx, game, trigger_deathrattle=True)
        else:
            dmg = 3
            print(f"-> 敌方空场,爆炸直击敌方本体,造成 {dmg} 点伤害!")
            final_dmg = game.calc_damage_mitigation(opponent, dmg)
            if final_dmg > 0:
                opponent.hp -= final_dmg

class Card_SO3(Card):
    def __init__(self):
        super().__init__("SO₃", 'S', desc="[H]+2; 全场随机1单位4伤(击杀溅射2)")

    def on_play(self, game, player, opponent):
        # 1. 环境改变
        print(f"系统: ☁️ {self.name} 排放,形成酸雨!环境 [H] + 2。")
        game.modify_h(2)
        
        # 2. 收集全场所有目标
        targets = []
        for i, c in enumerate(player.slots):
            if c: targets.append((player, i, c))
        for i, c in enumerate(opponent.slots):
            if c: targets.append((opponent, i, c))
        
        if not targets:
            print("系统: 场上空无一物,酸液滴落在空地上无效。")
            return

        print("系统: 🎲 酸雨无差别坠落中...")
        target_owner, idx, target_card = random.choice(targets)
        
        dmg = 4
        print(f"-> 命中!酸液滴落在 {target_owner.name}{target_card.name} 上,造成 {dmg} 点脱水伤害!")
        
        target_card.hp -= dmg
        
        # 3. 击杀判定与溅射
        if target_card.hp <= 0:
            print(f"   💀 {target_card.name} 被彻底碳化销毁!")
            # 触发亡语
            target_owner.remove_slot_card(idx, game, trigger_deathrattle=True)
            
            # 溅射伤害:谁的卡死了,谁的本体受溅射
            print(f"-> 💧 强酸飞溅!{target_owner.name} 本体受到 2 点溅射伤害。")
            target_owner.hp -= 2
        else:
            print(f"   {target_card.name} 幸存 (剩余 HP:{target_card.hp})。")

# ==========================================
# 游戏核心逻辑
# ==========================================

class Player:
    def __init__(self, name, hp=20, max_hp=30):
        self.name = name
        self.hp = hp
        self.max_hp = max_hp
        self.slots = [None, None, None]
        self.hand = []

    def is_alive(self):
        return self.hp > 0

    def heal(self, amt):
        prev = self.hp
        self.hp = min(self.max_hp, self.hp + amt)
        print(f"-> {self.name} 回复 {amt} HP ({prev} -> {self.hp})")

    def remove_slot_card(self, idx, game, trigger_deathrattle=True):
        card = self.slots[idx]
        if card:
            self.slots[idx] = None
            if trigger_deathrattle:
                opponent = game.p2 if self == game.p1 else game.p1
                card.on_death(game, self, opponent)
            else:
                print(f"ℹ️ {card.name} 离场 (无亡语)。")
            game.discard_pile.append(card)

    def count_cards(self):
        return len(self.hand) + sum(1 for c in self.slots if c)

class Game:
    def __init__(self):
        self.H = 0 
        self.O = 0
        self.deck = []
        self.discard_pile = []
        
        # v1.5.4 Komi (贴目) 设定
        # P1 先手,标准数值
        self.p1 = Player("Player 1", hp=20, max_hp=30)
        # P2 后手,获得 HP+2/MaxHP+2 补偿
        self.p2 = Player("Player 2", hp=22, max_hp=32)
        
        self.current_player = None
        self.init_deck()
        
    def modify_h(self, val):
        self.H += val
        if self.H > 6: self.H = 6
        if self.H < -6: self.H = -6
        state = "酸性" if self.H > 0 else ("碱性" if self.H < 0 else "中性")
        print(f"== 环境变更: [H] = {self.H} ({state}) ==")
        self.update_field_stats()

    def modify_o(self, val):
        self.O += val
        if self.O < 0: self.O = 0
        print(f"== 环境变更: [O] = {self.O} ==")

    def init_deck(self):
        composition = [
            (Card_H_Plus, 6),
            (Card_OH_Minus, 10),
            (Card_H2O, 12),
            (Card_H2O2, 7),
            (Card_SO3, 4), 
            (Card_Al, 6),
            (Card_Cu, 6),
            (Card_Cl2, 6),
            (Card_MnO4_Minus, 6),
            (Card_V, 3),            
            (Card_Au, 3),           
            (Card_HNO3, 5),         
            (Card_Fe3_Plus, 5),
            (Card_SCN_Minus, 4),
            (Card_C2H5OH, 5),
            (Card_C6H12O6, 5),
            (Card_F2, 4),
            (Card_NH3, 5),
            (Card_Mg, 5),
            (Card_Na, 5)
        ]
        
        self.deck = []
        for cls, cnt in composition:
            for _ in range(cnt):
                self.deck.append(cls())
        random.shuffle(self.deck)
        print(f"牌堆初始化完成: 共 {len(self.deck)} 张牌。")

    def draw(self):
        if not self.deck:
            if not self.discard_pile:
                print("!! 警告: 牌堆与弃牌堆均耗尽 !!")
                return None
            print(">> 牌堆耗尽,重洗弃牌堆...")
            self.deck = self.discard_pile[:]
            self.discard_pile = []
            random.shuffle(self.deck)
            for c in self.deck: c.reset_stats()
        return self.deck.pop()

    def player_draw(self, player, num):
        for _ in range(num):
            c = self.draw()
            if c:
                player.hand.append(c)
                print(f"{player.name} 抽了一张牌。")

    def update_field_stats(self):
        """全场状态刷新"""
        for p in [self.p1, self.p2]:
            for c in p.slots:
                if c:
                    c.update_continuous_state(self)

    def calc_damage_mitigation(self, target_player, raw_dmg):
        final_dmg = raw_dmg
        for c in target_player.slots:
            if c and isinstance(c, Card_Au):
                return c.on_board_effect(self, target_player, raw_dmg)
        for c in target_player.slots:
            if c and final_dmg > 0:
                final_dmg = c.on_board_effect(self, target_player, final_dmg)
        return final_dmg

    def start(self):
        print("\n=== Chem-Cards v1.5.4 Referee System (Komi Update) ===")
        print("Update: Player 2 (后手) receives +2 Initial HP and +2 Max HP Komi.")
        print(f"P1 (先手) HP: {self.p1.hp}/{self.p1.max_hp}")
        print(f"P2 (后手) HP: {self.p2.hp}/{self.p2.max_hp}")
        
        self.player_draw(self.p1, 6)
        self.player_draw(self.p2, 6)
        
        self.current_player = self.p1
        turn_num = 1
        
        while self.p1.is_alive() and self.p2.is_alive():
            print(f"\n>>> 第 {turn_num} 回合 - {self.current_player.name} 行动 <<<")
            self.play_turn()
            self.current_player = self.p2 if self.current_player == self.p1 else self.p1
            turn_num += 1
            
        winner = self.p1.name if self.p1.is_alive() else self.p2.name
        print(f"\n*** 游戏结束! 获胜者: {winner} ***")

    def print_status(self):
        opp = self.p2 if self.current_player == self.p1 else self.p1
        curr = self.current_player
        
        print(f"\n[环境] H:{self.H} | O:{self.O}")
        
        print(f"--- 敌方 ({opp.name}) HP:{opp.hp}/{opp.max_hp} 手牌:{len(opp.hand)} ---")
        slots = []
        for c in opp.slots:
            if c: slots.append(f"[{c.name} {c.hp}/{c.dp}]")
            else: slots.append("[_]")
        print(f"敌方前场: {' '.join(slots)}")
        print("-" * 30)
        
        slots = []
        for c in curr.slots:
            if c: slots.append(f"[{c.name} {c.hp}/{c.dp}]")
            else: slots.append("[_]")
        print(f"我方前场: {' '.join(slots)}")
        print(f"--- 我方 ({curr.name}) HP:{curr.hp}/{curr.max_hp} ---")
        
        print("我方手牌:")
        for i, c in enumerate(curr.hand):
            print(f" {i}: {c.name} | {c.desc}")

    def play_turn(self):
        self.update_field_stats() 
        self.print_status()
        player = self.current_player
        opponent = self.p2 if player == self.p1 else self.p1
        
        # 1. 出牌阶段
        has_played = False
        while not has_played:
            print("\n指令: play <序号> | skip")
            raw_input = input("> ").strip().lower().split()
            if not raw_input: continue
            
            cmd = raw_input[0]
            
            if cmd == 'skip':
                has_played = True
                print("玩家选择跳过出牌。")
            elif cmd == 'play':
                if len(raw_input) < 2:
                    print("错误: 请输入手牌序号。")
                    continue
                try:
                    idx = int(raw_input[1])
                    if 0 <= idx < len(player.hand):
                        card = player.hand.pop(idx)
                        self.handle_card_played(player, opponent, card)
                        has_played = True
                    else:
                        print("错误: 序号超出范围。")
                except ValueError:
                    print("错误: 序号格式无效。")
        
        self.update_field_stats()
        
        # 2. 攻击阶段
        print("\n进入攻击阶段。指令: atk <我方槽位> <敌方槽位/face> | end")
        attacked_indices = set()
        
        while True:
            raw_input = input("⚔️ > ").strip().lower().split()
            if not raw_input: continue
            
            cmd = raw_input[0]
            if cmd == 'end': break
            
            if cmd == 'atk' and len(raw_input) == 3:
                try:
                    my_idx = int(raw_input[1])
                    target_str = raw_input[2]
                    
                    if not (0 <= my_idx < 3):
                        print("错误: 我方槽位无效。")
                        continue
                    if my_idx in attacked_indices:
                        print("提示: 该单位本回合已攻击过。")
                        continue
                    
                    my_card = player.slots[my_idx]
                    if not my_card:
                        print("错误: 该槽位为空。")
                        continue
                        
                    if target_str == 'face':
                        if any(c for c in opponent.slots if c):
                            print("阻挡: 敌方前场有单位,无法直接攻击本体!")
                            continue
                        self.combat_face(player, opponent, my_idx)
                    else:
                        target_idx = int(target_str)
                        if not (0 <= target_idx < 3):
                            print("错误: 敌方槽位无效。")
                            continue
                        target_card = opponent.slots[target_idx]
                        if not target_card:
                            print("错误: 敌方槽位为空。")
                            continue
                        self.combat_unit(player, opponent, my_idx, target_idx)
                        if player.slots[my_idx]:
                            attacked_indices.add(my_idx)
                except ValueError:
                    print("错误: 参数格式应为数字。")
            else:
                print("未知指令。")

        # 3. 回合结束
        print("\n--- 回合结束结算 ---")
        for i in range(3):
            c = player.slots[i]
            if c:
                c.on_turn_end(self, player)
                    
        # 4. 抽牌检查
        self.check_end_draw(player, opponent)

    def handle_card_played(self, player, opponent, card):
        print(f"✨ 玩家打出了 {card.name}")
        
        if card.type == 'S':
            card.on_play(self, player, opponent)
            self.discard_pile.append(card)
        else:
            # v1.5.3 Logic: Al 的特殊逻辑
            if isinstance(card, Card_Al):
                reacted = card.check_reaction_and_result(self, player)
                if reacted:
                    self.discard_pile.append(card)
                    return 

            slot_idx = -1
            empty_slots = [i for i, c in enumerate(player.slots) if c is None]
            
            if empty_slots:
                slot_idx = empty_slots[0]
                player.slots[slot_idx] = card
                print(f"-> 部署在槽位 {slot_idx}")
            else:
                print(f"⚠️ 卡槽已满: {[c.name for c in player.slots]}")
                while True:
                    try:
                        rep = int(input("请选择献祭的槽位 (0-2): "))
                        if 0 <= rep <= 2:
                            print(f"-> 献祭了旧卡,部署新卡。")
                            player.remove_slot_card(rep, self, trigger_deathrattle=True)
                            player.slots[rep] = card
                            slot_idx = rep
                            break
                    except ValueError: pass
            
            card.on_deploy(self, player)
            card.update_continuous_state(self)

    def combat_face(self, attacker, defender, slot_idx):
        card = attacker.slots[slot_idx]
        raw_dmg = card.dp
        print(f"⚔️ {card.name} 突袭敌方本体!基础伤害: {raw_dmg}")
        
        final_dmg = self.calc_damage_mitigation(defender, raw_dmg)
        
        if final_dmg > 0:
            defender.hp -= final_dmg
            print(f"-> 命中!敌方 HP -{final_dmg} (剩余 {defender.hp})")
        else:
            print(f"-> 攻击无效 (被阻挡/免疫)。")
        
        print(f"ℹ️ 规则: {card.name} 攻击本体后消耗 (不触发亡语)。")
        attacker.remove_slot_card(slot_idx, self, trigger_deathrattle=False)

    def combat_unit(self, p1, p2, idx1, idx2):
        c1 = p1.slots[idx1]
        c2 = p2.slots[idx2]
        print(f"⚔️ 对攻: {c1.name} vs {c2.name}")
        
        is_c1_cu = isinstance(c1, Card_Cu)
        is_c2_cu = isinstance(c2, Card_Cu)
        
        round_dmg_c1 = 0
        round_dmg_c2 = 0
        
        while c1.hp > 0 and c2.hp > 0:
            d1_final = c2.dp
            d2_final = c1.dp
            
            if isinstance(c2, Card_HNO3) and c1.is_metal: d1_final += 2
            if isinstance(c1, Card_HNO3) and c2.is_metal: d2_final += 2

            c1.hp -= d1_final
            c2.hp -= d2_final
            round_dmg_c1 += d1_final
            round_dmg_c2 += d2_final
            
            stop = False
            if is_c1_cu and round_dmg_c1 > 3:
                c1.hp += (round_dmg_c1 - 3)
                stop = True
            if is_c2_cu and round_dmg_c2 > 3:
                c2.hp += (round_dmg_c2 - 3)
                stop = True
            if stop: break
            
        print(f"结算: {c1.name}({c1.hp}) | {c2.name}({c2.hp})")
        
        if c1.hp <= 0:
            print(f"-> {c1.name} 战败。")
            p1.remove_slot_card(idx1, self, trigger_deathrattle=True)
        if c2.hp <= 0:
            print(f"-> {c2.name} 战败。")
            p2.remove_slot_card(idx2, self, trigger_deathrattle=True)

    def check_end_draw(self, current, opponent):
        cnt_cur = current.count_cards()
        cnt_opp = opponent.count_cards()
        diff = abs(cnt_cur - cnt_opp)
        
        if diff > 2:
            dis = current if cnt_cur < cnt_opp else opponent
            print(f"⚖️ 牌差补偿: {dis.name} 处于劣势,抽 1 张牌。")
            self.player_draw(dis, 1)
                
        if not current.hand:
            self.player_draw(current, 1)
        if not opponent.hand:
            self.player_draw(opponent, 1)

if __name__ == "__main__":
    g = Game()
    g.start()