"""Condition patterns. Conditions are gating requirements that must be met for an ability to activate. Examples: COUNT_GROUP, SCORE_COMPARE, HAS_MEMBER, etc. """ from .base import Pattern, PatternPhase # Helper function for extracting Japanese numbers def _extract_number(text: str, match) -> int: """Extract a number from match, handling full-width and kanji numerals.""" val_map = { "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9, "0": 0, "一": 1, "二": 2, "三": 3, "四": 4, "五": 5, "六": 6, "七": 7, "八": 8, "九": 9, "〇": 0, } if match.lastindex and match.lastindex >= 1: val = match.group(1) return int(val_map.get(val, val)) if not str(val).isdigit() else int(val) return 1 CONDITION_PATTERNS = [ # ========================================================================== # Count conditions # ========================================================================== Pattern( name="count_group", phase=PatternPhase.CONDITION, regex=r"『(.*?)』.*?(\d+)(枚|人)以上", priority=20, output_type="ConditionType.COUNT_GROUP", extractor=lambda text, match: { "type": "ConditionType.COUNT_GROUP", "value": int(match.group(2)), "params": {"group": match.group(1), "min": int(match.group(2))}, }, ), Pattern( name="count_stage", phase=PatternPhase.CONDITION, regex=r"(\d+)(枚|人)以上", priority=50, requires=["ステージ"], output_type="ConditionType.COUNT_STAGE", ), Pattern( name="count_energy", phase=PatternPhase.CONDITION, regex=r"エネルギーが(\d+)枚以上", priority=30, output_type="ConditionType.COUNT_ENERGY", ), Pattern( name="count_success_live", phase=PatternPhase.CONDITION, regex=r"成功ライブカード置き場.*?(\d+)枚以上", priority=20, output_type="ConditionType.COUNT_SUCCESS_LIVE", ), Pattern( name="count_live_zone", phase=PatternPhase.CONDITION, regex=r"ライブ中のカード.*?(\d+)枚以上", priority=20, output_type="ConditionType.COUNT_LIVE_ZONE", ), Pattern( name="count_hearts", phase=PatternPhase.CONDITION, regex=r"(?:ハート|heart).*?(\d+)(つ|個)以上", priority=30, output_type="ConditionType.COUNT_HEARTS", ), Pattern( name="count_blades", phase=PatternPhase.CONDITION, regex=r"ブレード.*?(\d+)(つ|個)以上", priority=30, output_type="ConditionType.COUNT_BLADES", ), # ========================================================================== # Comparison conditions # ========================================================================== Pattern( name="score_compare_gt", phase=PatternPhase.CONDITION, regex=r"(?:スコア|コスト).*?相手.*?より(?:高い|多い)", priority=25, output_type="ConditionType.SCORE_COMPARE", output_params={"comparison": "GT", "target": "opponent"}, ), Pattern( name="score_compare_ge", phase=PatternPhase.CONDITION, regex=r"(?:スコア|コスト).*?同じか(?:高い|多い)", priority=25, output_type="ConditionType.SCORE_COMPARE", output_params={"comparison": "GE", "target": "opponent"}, ), Pattern( name="score_compare_lt", phase=PatternPhase.CONDITION, regex=r"(?:スコア|コスト).*?相手.*?より(?:低い|少ない)", priority=25, output_type="ConditionType.SCORE_COMPARE", output_params={"comparison": "LT", "target": "opponent"}, ), Pattern( name="score_compare_eq", phase=PatternPhase.CONDITION, regex=r"(?:スコア|コスト).*?同じ(?:場合|なら|とき)", priority=25, output_type="ConditionType.SCORE_COMPARE", output_params={"comparison": "EQ", "target": "opponent"}, ), Pattern( name="opponent_hand_diff", phase=PatternPhase.CONDITION, regex=r"相手の手札(?:の枚数)?が自分より(\d+)?枚?以上?多い", priority=25, output_type="ConditionType.OPPONENT_HAND_DIFF", ), Pattern( name="opponent_energy_diff", phase=PatternPhase.CONDITION, regex=r"相手のエネルギーが自分より(?:(\d+)枚以上)?多い", priority=25, output_type="ConditionType.OPPONENT_ENERGY_DIFF", ), Pattern( name="life_lead_gt", phase=PatternPhase.CONDITION, regex=r"ライフが相手より多い", priority=25, output_type="ConditionType.LIFE_LEAD", output_params={"comparison": "GT", "target": "opponent"}, ), Pattern( name="life_lead_lt", phase=PatternPhase.CONDITION, regex=r"ライフが相手より少ない", priority=25, output_type="ConditionType.LIFE_LEAD", output_params={"comparison": "LT", "target": "opponent"}, ), Pattern( name="opponent_choice_select", phase=PatternPhase.CONDITION, keywords=["相手", "選ぶ"], excludes=["自分か相手"], priority=30, output_type="ConditionType.OPPONENT_CHOICE", output_params={"type": "select"}, ), Pattern( name="opponent_choice_discard", phase=PatternPhase.CONDITION, keywords=["相手", "手札", "捨て"], priority=30, output_type="ConditionType.OPPONENT_CHOICE", output_params={"type": "discard"}, ), # ========================================================================== # State conditions # ========================================================================== Pattern( name="is_center", phase=PatternPhase.CONDITION, keywords=["センターエリア", "場合"], priority=40, output_type="ConditionType.IS_CENTER", ), Pattern( name="has_moved", phase=PatternPhase.CONDITION, keywords=["移動している場合"], priority=30, output_type="ConditionType.HAS_MOVED", ), Pattern( name="has_live_card", phase=PatternPhase.CONDITION, keywords=["ライブカードがある場合"], priority=30, output_type="ConditionType.HAS_LIVE_CARD", ), Pattern( name="has_choice", phase=PatternPhase.CONDITION, regex=r"(?:1つを選ぶ|どちらか.*?選ぶ|選んでもよい|のうち.*?選ぶ)", priority=40, output_type="ConditionType.HAS_CHOICE", ), Pattern( name="group_filter", phase=PatternPhase.CONDITION, regex=r"『(.*?)』", priority=60, requires=["場合", "なら", "がいる"], output_type="ConditionType.GROUP_FILTER", ), Pattern( name="has_member", phase=PatternPhase.CONDITION, regex=r"「(.*?)」.*?(?:がある|がいる|登場している)場合", priority=30, output_type="ConditionType.HAS_MEMBER", ), # ========================================================================== # Once per turn / Turn restrictions # ========================================================================== Pattern( name="turn_1", phase=PatternPhase.CONDITION, regex=r"\[Turn 1\]|1ターン目|ターン1(?!回)", priority=20, output_type="ConditionType.TURN_1", output_params={"turn": 1}, ), # ========================================================================== # Revealed/Milled card conditions # ========================================================================== Pattern( name="all_revealed_are_members", phase=PatternPhase.CONDITION, regex=r"それらがすべてメンバーカード.*?場合", priority=15, output_type="ConditionType.GROUP_FILTER", output_params={"filter_type": "all_revealed", "card_type": "member"}, ), Pattern( name="is_in_discard", phase=PatternPhase.CONDITION, regex=r"この(カード|メンバー)が控え室にある場合のみ起動できる", priority=10, output_type="ConditionType.IS_IN_DISCARD", ), ]