from dataclasses import dataclass from typing import Literal PrimitiveId = Literal[ "deal", "burn", "bomb", "block", "ward", "weak", "draw", "energy", "initiative", "multi_hit", "vulnerable", "conditional", "scaling", ] School = Literal["fire", "ice", "earth"] @dataclass(frozen=True) class PrimitiveSpec: id: PrimitiveId name: str category: str schools: frozenset[School] @dataclass(frozen=True) class Effect: primitive_id: PrimitiveId amount: int = 0 duration: int = 0 delay: int = 0 hits: int = 1 condition: str = "" PRIMITIVES: dict[PrimitiveId, PrimitiveSpec] = { "deal": PrimitiveSpec("deal", "Deal", "damage", frozenset(("fire",))), "burn": PrimitiveSpec("burn", "Burn", "damage", frozenset(("fire",))), "bomb": PrimitiveSpec("bomb", "Bomb", "damage", frozenset(("fire",))), "block": PrimitiveSpec("block", "Block", "defense", frozenset(("earth",))), "ward": PrimitiveSpec("ward", "Ward", "defense", frozenset(("earth",))), "weak": PrimitiveSpec("weak", "Weak", "defense", frozenset()), "draw": PrimitiveSpec("draw", "Draw", "tempo", frozenset()), "energy": PrimitiveSpec("energy", "Energy", "tempo", frozenset()), "initiative": PrimitiveSpec("initiative", "Initiative", "tempo", frozenset(("ice",))), "multi_hit": PrimitiveSpec("multi_hit", "Multi-hit", "combo", frozenset(("ice",))), "vulnerable": PrimitiveSpec("vulnerable", "Vulnerable", "combo", frozenset(("ice",))), "conditional": PrimitiveSpec("conditional", "Conditional", "combo", frozenset(("ice", "earth"))), "scaling": PrimitiveSpec("scaling", "Scaling", "combo", frozenset(("fire", "earth"))), } SCHOOL_PRIMITIVES: dict[School, tuple[PrimitiveId, ...]] = { "fire": ("deal", "burn", "bomb", "scaling", "draw", "energy", "block", "conditional"), "ice": ("deal", "initiative", "vulnerable", "multi_hit", "conditional", "draw", "energy", "block"), "earth": ("deal", "ward", "block", "weak", "scaling", "conditional", "draw", "energy"), } SCHOOL_BIAS: dict[School, str] = { "fire": "Prefer immediate Deal first, then Burn/Scaling, then occasional Bomb, Draw, Energy, or Block.", "ice": "Prefer tempo pressure through Deal, Initiative, Vulnerable, Multi-hit, and Conditional, with occasional Draw or Block.", "earth": "Prefer Block and Ward to bank shield charge, then Scaling to release that charge as burst damage. Add some Deal, Weak, and Draw.", } # Return all primitive identifiers in canonical order. def primitive_ids() -> tuple[PrimitiveId, ...]: return tuple(PRIMITIVES) # Return a primitive spec by id. def get_primitive(primitive_id: PrimitiveId) -> PrimitiveSpec: return PRIMITIVES[primitive_id] # Return the primitive ids available to a school. def school_primitives(school: School) -> tuple[PrimitiveId, ...]: return SCHOOL_PRIMITIVES[school] # Return whether a primitive belongs in a school's draft pool. def primitive_allowed_for_school(primitive_id: PrimitiveId, school: School) -> bool: return primitive_id in SCHOOL_PRIMITIVES[school] # Return a school's generation bias. def school_bias(school: School) -> str: return SCHOOL_BIAS[school] # Render a costed effect into fixed card text. def render_effect(effect: Effect) -> str: match effect.primitive_id: case "deal": return f"Deal {effect.amount} damage." case "burn": return f"Burn {effect.amount} damage for {effect.duration} turns." case "bomb": return f"Bomb: deal {effect.amount} damage in {effect.delay} turns." case "block": return f"Gain {effect.amount} block until your next turn." case "ward": return f"Gain {effect.amount} ward." case "weak": return f"Opponent deals {effect.amount} less damage until your next turn." case "draw": return f"Draw {effect.amount} card{'s' if effect.amount != 1 else ''}." case "energy": return f"Gain {effect.amount} energy this turn." case "initiative": return "Opponent acts second next round." case "multi_hit": return f"Deal {effect.amount} damage {effect.hits} times." case "vulnerable": return f"Opponent takes {effect.amount} more damage for {effect.duration} turns." case "conditional": return f"Deal up to {effect.amount} damage based on opponent's missing HP." case "scaling": if effect.condition == "shield_charge": return f"Deal {effect.amount} damage plus your banked shield charge, then empty it." return f"Deal {effect.amount} damage plus cards played this turn."