trioskosmos's picture
Upload folder using huggingface_hub
2113a6a verified
# -*- coding: utf-8 -*-
"""Effect patterns.
Effects are the actions that occur when an ability activates.
Examples: DRAW, ADD_BLADES, RECOVER_MEMBER, etc.
"""
from .base import Pattern, PatternPhase
EFFECT_PATTERNS = [
# ==========================================================================
# Card manipulation effects
# ==========================================================================
Pattern(
name="draw_cards",
phase=PatternPhase.EFFECT,
regex=r"カード.*?(\d+)枚.*?引",
priority=20,
excludes=["引き入れ"], # "bring under" is not draw
consumes=True,
output_type="EffectType.DRAW",
),
Pattern(
name="draw_pseudocode",
phase=PatternPhase.EFFECT,
regex=r"DRAW\((.*?)\)",
priority=5,
consumes=True,
output_type="EffectType.DRAW",
extractor=lambda text, m: {
"type": "EffectType.DRAW",
"value": int(m.group(1)) if m.group(1).isdigit() else 0,
"value_cond": m.group(1) if not m.group(1).isdigit() else None,
},
),
Pattern(
name="draw_one",
phase=PatternPhase.EFFECT,
regex=r"引く",
priority=50,
excludes=["引き入れ", "置いた枚数分"],
consumes=True,
output_type="EffectType.DRAW",
output_value=1,
),
Pattern(
name="look_deck_top",
phase=PatternPhase.EFFECT,
regex=r"(?:デッキ|山札)の一番上.*?見る",
priority=19,
output_type="EffectType.LOOK_DECK",
output_value=1,
),
Pattern(
name="look_deck",
phase=PatternPhase.EFFECT,
regex=r"(?:デッキ|山札).*?(\d+)枚.*?(?:見る|見て)",
priority=20,
output_type="EffectType.LOOK_DECK",
),
Pattern(
name="search_deck",
phase=PatternPhase.EFFECT,
any_keywords=["探", "サーチ"],
requires=["デッキ"],
priority=30,
output_type="EffectType.SEARCH_DECK",
output_value=1,
),
Pattern(
name="search_deck_from",
phase=PatternPhase.EFFECT,
# "デッキから...手札に加える" (search deck for card, add to hand)
regex=r"(?:デッキ|山札)から.*?手札に加",
priority=5, # Very high priority to catch before ADD_TO_HAND
consumes=True,
output_type="EffectType.SEARCH_DECK",
output_value=1,
output_params={"to": "hand"},
),
Pattern(
name="reveal_cards",
phase=PatternPhase.EFFECT,
regex=r"(\d+)枚.*?公開",
priority=30,
excludes=["エール"], # Not cheer-based reveal
output_type="EffectType.REVEAL_CARDS",
),
Pattern(
name="reveal_top",
phase=PatternPhase.EFFECT,
regex=r"デッキの一番上.*?公開",
priority=20,
output_type="EffectType.REVEAL_CARDS",
output_value=1,
),
Pattern(
name="choose_heart_icons",
phase=PatternPhase.EFFECT,
regex=r"({{heart_.*?}}か{{heart_.*?}}).*?選ぶ",
priority=15,
output_type="EffectType.COLOR_SELECT",
),
Pattern(
name="return_discard_to_deck_bottom",
phase=PatternPhase.EFFECT,
regex=r"デッキの一番下に置く",
priority=15,
output_type="EffectType.MOVE_TO_DECK",
output_params={"to": "deck_bottom"},
),
Pattern(
name="add_hearts_color_text",
phase=PatternPhase.EFFECT,
regex=r"[(赤|青|黄|緑|紫|桃)ハート].*?得る",
priority=15,
output_type="EffectType.ADD_HEARTS",
extractor=lambda text, m: {"type": "EffectType.ADD_HEARTS", "value": 1, "params": {"color_text": m.group(1)}},
),
Pattern(
name="look_and_choose_order",
phase=PatternPhase.EFFECT,
regex=r"LOOK_AND_CHOOSE_ORDER\((\d+)\)",
priority=20,
output_type="EffectType.ORDER_DECK",
extractor=lambda text, m: {
"type": "EffectType.ORDER_DECK",
"value": int(m.group(1)),
},
),
Pattern(
name="select_from_pool",
phase=PatternPhase.EFFECT,
regex=r"の中から.*?(\d+)?(枚|人).*?選ぶ",
priority=20,
output_type="EffectType.LOOK_AND_CHOOSE",
extractor=lambda text, m: {
"type": "EffectType.LOOK_AND_CHOOSE",
"value": int(m.group(1)) if m.group(1) else 1,
},
),
# ==========================================================================
# Recovery effects
# ==========================================================================
Pattern(
name="add_self_to_hand",
phase=PatternPhase.EFFECT,
regex=r"この(カード|メンバー)を手札に加",
priority=15,
output_type="EffectType.ADD_TO_HAND",
output_params={"target": "self", "to": "hand"},
),
Pattern(
name="place_member_to_hand",
phase=PatternPhase.EFFECT,
regex=r"メンバーを?.*?手札に加",
priority=30, # Lower precedence
consumes=True,
output_type="EffectType.ADD_TO_HAND",
output_params={"to": "hand"},
),
Pattern(
name="recover_member",
phase=PatternPhase.EFFECT,
regex=r"控え室から.*?メンバーを?.*?手札に加",
priority=10, # High precedence to consume early
consumes=True,
output_type="EffectType.RECOVER_MEMBER",
output_value=1,
output_params={"from": "discard", "to": "hand"},
),
Pattern(
name="recover_from_success",
phase=PatternPhase.EFFECT,
regex=r"成功ライブカード(?:置き場)?[\s\S]*?手札に加",
priority=20,
output_type="EffectType.RECOVER_LIVE",
output_value=1,
output_params={"from": "success_zone", "to": "hand"},
),
Pattern(
name="recover_live",
phase=PatternPhase.EFFECT,
regex=r"控え室から.*?ライブカードを?.*?手札に加",
priority=10,
consumes=True,
output_type="EffectType.RECOVER_LIVE",
output_value=1,
output_params={"from": "discard", "to": "hand"},
),
Pattern(
name="add_to_hand_from_deck",
phase=PatternPhase.EFFECT,
keywords=["デッキ", "手札に加え"],
excludes=["見る", "選"], # Not look and choose
priority=25,
consumes=True,
output_type="EffectType.ADD_TO_HAND",
output_params={"from": "deck"},
),
Pattern(
name="look_and_choose",
phase=PatternPhase.EFFECT,
# Relaxed regex to allow filters between "look" and "choose"
regex=r"(?:カードを?|[\{\[].*?[\}\]])?(\d+)枚(見て|見る).*?(?:その中から|その中にある|公開された).*?(?:カードを?)?(\d+)?(?:枚|つ)?.*?手札に加",
priority=10, # High priority to catch before LOOK_DECK
consumes=True,
output_type="EffectType.LOOK_AND_CHOOSE",
extractor=lambda text, m: {
"type": "EffectType.LOOK_AND_CHOOSE",
"value": int(m.group(3)) if m.group(3) else 1,
"params": {"look_count": int(m.group(1))},
},
),
Pattern(
name="discard_looked",
phase=PatternPhase.EFFECT,
# "そのカードを控え室に置く" (referring to looked card)
regex=r"(?:その|公開した|見た)カードを?.*?控え室に置",
priority=25,
consumes=True,
output_type="EffectType.LOOK_AND_CHOOSE",
output_value=1,
output_params={"look_count": 1, "destination": "discard"},
),
Pattern(
name="mill_to_discard",
phase=PatternPhase.EFFECT,
# "デッキの上からカードをX枚控え室に置く"
regex=r"(?:デッキ|山札).*?(\d+)枚.*?控え室に置",
priority=15, # Higher priority than generic swap_to_discard
consumes=True,
output_type="EffectType.SWAP_CARDS",
extractor=lambda text, m: {
"type": "EffectType.SWAP_CARDS",
"value": int(m.group(1)),
"params": {"from": "deck", "target": "discard"},
},
),
Pattern(
name="discard_remainder",
phase=PatternPhase.EFFECT,
regex=r"残りを?控え室に",
priority=20,
consumes=True,
output_type="EffectType.SWAP_CARDS",
output_params={"target": "discard"},
),
Pattern(
name="choose_player",
phase=PatternPhase.EFFECT,
regex=r"自分か相手を選",
priority=5, # Very high priority
output_type="EffectType.META_RULE",
output_params={"target": "PLAYER_SELECT"},
),
# ==========================================================================
# Stat modification effects
# ==========================================================================
Pattern(
name="add_blades",
phase=PatternPhase.EFFECT,
regex=r"(?:{{icon_blade.*?}})?ブレード[^スコア場合]*?[++](\d+|1|2|3)",
priority=20,
output_type="EffectType.ADD_BLADES",
),
Pattern(
name="add_blades_gain",
phase=PatternPhase.EFFECT,
regex=r"(?:{{icon_blade.*?}})?ブレード.*?を得る",
priority=30,
output_type="EffectType.ADD_BLADES",
output_value=1,
),
Pattern(
name="add_hearts",
phase=PatternPhase.EFFECT,
regex=r"ハート[^スコア場合]*?[++](\d+|1|2|3)",
priority=20,
output_type="EffectType.ADD_HEARTS",
),
Pattern(
name="add_hearts_gain",
phase=PatternPhase.EFFECT,
regex=r"(?:{{(?:heart_\d+|icon_heart).*?}})?ハートを?(\d+|1|2|3)?(つ|個|枚)?(を)?得る",
priority=20,
output_type="EffectType.ADD_HEARTS",
extractor=lambda text, m: {
"type": "EffectType.ADD_HEARTS",
"value": int(m.group(1)) if m.group(1) else 1,
},
),
Pattern(
name="add_hearts_icon",
phase=PatternPhase.EFFECT,
# Heart icons: {{heart_XX.png|heartXX}}を得る
regex=r"({{heart_\d+\.png\|heart\d+}})+(を)?得る",
priority=10, # Very high priority
consumes=True,
output_type="EffectType.ADD_HEARTS",
extractor=lambda text, m: {
"type": "EffectType.ADD_HEARTS",
"value": text[: m.end()].count("{{heart_"), # Count heart icons
"params": {},
},
),
Pattern(
name="boost_score",
phase=PatternPhase.EFFECT,
regex=r"スコア.*?[++](\d+|1|2|3)",
priority=20,
output_type="EffectType.BOOST_SCORE",
),
Pattern(
name="reduce_heart_req",
phase=PatternPhase.EFFECT,
any_keywords=["必要ハート", "ハート条件"],
requires=["減", "少なく"],
priority=25,
output_type="EffectType.REDUCE_HEART_REQ",
),
# ==========================================================================
# Energy effects
# ==========================================================================
Pattern(
name="energy_charge",
phase=PatternPhase.EFFECT,
regex=r"エネルギー(?:カード)?を?(\d+)?枚.*?(?:置く|加える|チャージ)",
priority=25,
excludes=["控え室", "の上から", "を公開", "公開された"],
output_type="EffectType.ENERGY_CHARGE",
),
# ==========================================================================
# Movement/Position effects
# ==========================================================================
Pattern(
name="move_member",
phase=PatternPhase.EFFECT,
any_keywords=["ポジションチェンジ", "移動させ", "移動する", "場所を入れ替える"],
priority=25,
output_type="EffectType.MOVE_MEMBER",
output_value=1,
),
Pattern(
name="tap_opponent",
phase=PatternPhase.EFFECT,
regex=r"相手.*?(\d+)?人?.*?(?:ウェイト|休み)",
priority=25,
output_type="EffectType.TAP_OPPONENT",
),
Pattern(
name="activate_member",
phase=PatternPhase.EFFECT,
keywords=["アクティブに"],
excludes=["手札", "加え"], # Not "add card with active ability"
priority=25,
output_type="EffectType.ACTIVATE_MEMBER",
output_value=1,
),
# ==========================================================================
# Zone transfer effects
# ==========================================================================
Pattern(
name="swap_to_discard",
phase=PatternPhase.EFFECT,
any_keywords=["控え室に置", "控え室に送"],
priority=30,
output_type="EffectType.SWAP_CARDS",
output_params={"target": "discard"},
extractor=lambda text, m: {
"type": "EffectType.SWAP_CARDS",
"params": {"target": "discard", "from": "deck" if "デッキ" in text or "山札" in text else "field"},
},
),
Pattern(
name="move_to_deck",
phase=PatternPhase.EFFECT,
any_keywords=["デッキに戻す", "山札に置く"],
priority=30,
output_type="EffectType.MOVE_TO_DECK",
),
Pattern(
name="return_discard_to_deck",
phase=PatternPhase.EFFECT,
# "控え室にある...デッキの一番上に置く" (place from discard to top of deck)
regex=r"控え室.*?(\d+)枚.*?(?:デッキ|山札).*?一番上に置",
priority=15,
consumes=True,
output_type="EffectType.MOVE_TO_DECK",
extractor=lambda text, m: {
"type": "EffectType.MOVE_TO_DECK",
"value": int(m.group(1)),
"params": {"from": "discard", "to": "deck_top"},
},
),
Pattern(
name="place_under",
phase=PatternPhase.EFFECT,
keywords=["の下に置"],
excludes=["コスト", "払"], # Not cost
priority=30,
output_type="EffectType.PLACE_UNDER",
),
# ==========================================================================
# Meta/Rule effects
# ==========================================================================
Pattern(
name="select_mode",
phase=PatternPhase.EFFECT,
regex=r"(?:以下から|のうち、)(\d+|1|2|3)(つ|枚|回)?を選ぶ",
priority=20,
output_type="EffectType.SELECT_MODE",
),
Pattern(
name="color_select",
phase=PatternPhase.EFFECT,
any_keywords=["ハートの色を1つ指定", "好きなハートの色を"],
priority=25,
output_type="EffectType.COLOR_SELECT",
output_value=1,
),
Pattern(
name="negate_effect",
phase=PatternPhase.EFFECT,
any_keywords=["無効", "キャンセル"],
priority=25,
output_type="EffectType.NEGATE_EFFECT",
output_value=1,
),
Pattern(
name="shuffle_deck",
phase=PatternPhase.EFFECT,
keywords=["シャッフル"],
priority=40,
output_type="EffectType.META_RULE",
output_params={"type": "shuffle", "deck": True},
),
Pattern(
name="play_member_from_hand",
phase=PatternPhase.EFFECT,
regex=r"手札から.*?登場させ",
priority=15,
output_type="EffectType.PLAY_MEMBER_FROM_HAND",
output_value=1,
),
Pattern(
name="play_member_from_discard",
phase=PatternPhase.EFFECT,
regex=r"控え室から.*?登場させ",
priority=15,
output_type="EffectType.PLAY_MEMBER_FROM_DISCARD",
output_value=1,
),
Pattern(
name="tap_self",
phase=PatternPhase.EFFECT,
regex=r"このメンバーをウェイトにする",
priority=20,
output_type="EffectType.TAP_MEMBER",
output_params={"target": "self"},
),
Pattern(
name="card_selection",
phase=PatternPhase.EFFECT,
regex=r"(?:控え室にある|デッキにある|ステージにいる)?.*?(\d+)枚選ぶ",
priority=50, # Low priority
output_type="EffectType.LOOK_AND_CHOOSE",
),
# ==========================================================================
# Cost/Constant modifiers parsed as effects
# ==========================================================================
Pattern(
name="reduce_cost_self",
phase=PatternPhase.EFFECT,
# "コストは...X少なくなる" OR "コストはX減る" (cost is reduced by X)
regex=r"コストは.*?(\d+)(?:少なく|減る|減)",
priority=15,
consumes=True,
output_type="EffectType.REDUCE_COST",
),
Pattern(
name="reduce_cost_per_card",
phase=PatternPhase.EFFECT,
# "手札1枚につき、1少なくなる" (reduced by 1 per card in hand)
regex=r"手札.*?(\d+)枚につき.*?(\d+)少なく",
priority=10, # Higher than reduce_cost_self
consumes=True,
output_type="EffectType.REDUCE_COST",
extractor=lambda text, m: {
"type": "EffectType.REDUCE_COST",
"value": int(m.group(2)),
"params": {"per_card": int(m.group(1)), "zone": "hand"},
},
),
Pattern(
name="grant_ability",
phase=PatternPhase.EFFECT,
# Ability granting: "能力を得る" / "」を得る"
regex=r"(?:」|{{.*?}}).*?を得る",
priority=25,
consumes=False, # Allow inner effects to be parsed
output_type="EffectType.BUFF_POWER",
output_value=1,
),
Pattern(
name="grant_stat_buff",
phase=PatternPhase.EFFECT,
# "ブレード+X」を得る" / "ハート+X」を得る" (gain Blade+X / Heart+X)
regex=r"(ブレード|ハート)[++](\d+)[」」].*?得る",
priority=15,
consumes=True,
output_type="EffectType.BUFF_POWER",
extractor=lambda text, m: {
"type": "EffectType.BUFF_POWER" if "ブレード" in m.group(1) else "EffectType.ADD_HEARTS",
"value": int(m.group(2)),
"params": {"stat": "blade" if "ブレード" in m.group(1) else "heart"},
},
),
Pattern(
name="tap_member_cost",
phase=PatternPhase.EFFECT,
# Tap member as cost/effect: "メンバーXをウェイトにしてもよい"
regex=r"メンバー.*?(\d+)人?.*?ウェイトにして",
priority=25,
consumes=True,
output_type="EffectType.TAP_MEMBER",
extractor=lambda text, m: {"type": "EffectType.TAP_MEMBER", "value": int(m.group(1)), "params": {"cost": True}},
),
Pattern(
name="draw_equal_to_discarded",
phase=PatternPhase.EFFECT,
regex=r"置いた枚数分カードを引く",
priority=15,
consumes=True,
output_type="EffectType.DRAW",
output_params={"multiplier": "discarded_count"},
),
Pattern(
name="trigger_ability",
phase=PatternPhase.EFFECT,
regex=r"(能力1つ)?を?発動させる",
priority=20,
consumes=True,
output_type="EffectType.TRIGGER_REMOTE",
),
Pattern(
name="treat_as_all_colors",
phase=PatternPhase.EFFECT,
regex=r"属性を全ての属性として扱う",
priority=20,
output_type="EffectType.META_RULE",
output_params={"type": "all_colors"},
),
Pattern(
name="transform_base_hearts",
phase=PatternPhase.EFFECT,
regex=r"元々持つハートはすべて.*?({{heart_.*?}})?になる",
priority=20,
output_type="EffectType.TRANSFORM_COLOR",
output_params={"target": "base_hearts"},
),
Pattern(
name="add_from_reveal_to_hand",
phase=PatternPhase.EFFECT,
regex=r"(?:公開された|公開される).*?手札に加",
priority=20,
output_type="EffectType.ADD_TO_HAND",
output_params={"from": "reveal_zone", "to": "hand"},
),
Pattern(
name="recover_live_to_zone",
phase=PatternPhase.EFFECT,
regex=r"控え室からライブカードを.*?ライブカード置き場に置",
priority=20,
output_type="EffectType.PLAY_LIVE_FROM_DISCARD",
output_value=1,
),
Pattern(
name="increase_cost",
phase=PatternPhase.EFFECT,
regex=r"コストを?[++](\d+)する",
priority=20,
output_type="EffectType.REDUCE_COST", # Use negative for increase in engine or separate Opcode
extractor=lambda text, m: {
"type": "EffectType.REDUCE_COST",
"value": -int(m.group(1)),
},
),
Pattern(
name="increase_heart_req",
phase=PatternPhase.EFFECT,
regex=r"必要ハートが.*?多くなる",
priority=20,
output_type="EffectType.REDUCE_HEART_REQ",
output_value=1, # Default increase by 1 if value not specified
),
Pattern(
name="modify_cheer_count",
phase=PatternPhase.EFFECT,
regex=r"エールによって公開される.*?枚数が.*?(\d+)枚(減る|増える)",
priority=20,
output_type="EffectType.META_RULE",
extractor=lambda text, m: {
"type": "EffectType.META_RULE",
"params": {"type": "cheer_mod"},
"value": -int(m.group(1)) if m.group(2) == "減る" else int(m.group(1)),
},
),
Pattern(
name="play_member_from_discard",
phase=PatternPhase.EFFECT,
regex=r"控え室(?:にある|から).*?登場させ",
priority=15,
output_type="EffectType.PLAY_MEMBER_FROM_DISCARD",
output_value=1,
),
Pattern(
name="baton_touch_mod",
phase=PatternPhase.EFFECT,
regex=r"(\d+)人のメンバーとバトンタッチ",
priority=20,
output_type="EffectType.BATON_TOUCH_MOD",
extractor=lambda text, m: {
"type": "EffectType.BATON_TOUCH_MOD",
"value": int(m.group(1)),
},
),
Pattern(
name="rule_equivalence",
phase=PatternPhase.EFFECT,
regex=r"についても同じこと(として扱う|を行う)",
priority=20,
output_type="EffectType.META_RULE",
output_params={"type": "rule_equivalence"},
),
Pattern(
name="restriction_no_live",
phase=PatternPhase.EFFECT,
regex=r"自分はライブ(できない|出来ません)",
priority=20,
output_type="EffectType.RESTRICTION",
output_params={"type": "no_live"},
),
]