""" MTG Rules Engine - Keyword Validator Module Validates keyword usage in card text, spell resolution, and game actions. Uses the hardcoded keyword database from keywords_db.py. """ from .keywords_db import ( KEYWORD_ACTIONS, KEYWORD_ABILITIES, ABILITY_WORDS, KEYWORD_VARIANTS, KEYWORD_TYPES, get_keyword_definition, get_all_keywords, get_keyword_by_rule, search_keywords, ) class KeywordValidationError(Exception): """Raised when a keyword usage violates Magic: The Gathering rules.""" def __init__(self, message: str, keyword: str = None, rule: str = None): super().__init__(message) self.keyword = keyword self.rule = rule class KeywordValidator: """ Validates keyword usage in card text and game actions. This validator ensures: 1. Keywords are valid MTG keywords 2. Keywords are used in appropriate contexts (actions vs abilities) 3. Keyword variants are properly recognized 4. Ability words are distinguished from keyword abilities """ def __init__(self): self._all_keywords = get_all_keywords() def validate_keyword(self, keyword: str) -> dict: """ Validate a keyword and return its metadata. Args: keyword: The keyword to validate. Returns: Dictionary with keyword metadata. Raises: KeywordValidationError: If the keyword is not recognized. """ definition = get_keyword_definition(keyword) if definition is None: raise KeywordValidationError( f"Unknown keyword: '{keyword}'. " f"Valid keywords: {len(self._all_keywords)} total.", keyword=keyword, ) return definition def validate_keyword_in_context(self, keyword: str, context: str) -> str: """ Validate a keyword in a specific context (action or ability). Args: keyword: The keyword to validate. context: The context ('action' or 'ability'). Returns: A validation message. Raises: KeywordValidationError: If the keyword is invalid in context. """ definition = get_keyword_definition(keyword) if definition is None: raise KeywordValidationError( f"Unknown keyword: '{keyword}'.", keyword=keyword, ) if context == "action" and definition["type"] == "keyword_ability": raise KeywordValidationError( f"Keyword '{keyword}' is a keyword ability, not a keyword action. " f"Rule reference: {definition['rule']}", keyword=keyword, rule=definition["rule"], ) if context == "ability" and definition["type"] == "keyword_action": raise KeywordValidationError( f"Keyword '{keyword}' is a keyword action, not a keyword ability. " f"Rule reference: {definition['rule']}", keyword=keyword, rule=definition["rule"], ) return f"Valid keyword: '{keyword}' ({definition['type']})" def validate_card_text(self, text: str) -> list[dict]: """ Validate keyword usage in card text. Args: text: The card text to validate. Returns: List of validation results for each keyword found. """ keywords_found = [] results = [] # Extract keywords from text (simple word-based matching) for kw in self._all_keywords: # Check for exact matches (case-insensitive) if kw.lower() in text.lower(): definition = get_keyword_definition(kw) if definition: keywords_found.append(kw) results.append({ "keyword": kw, "type": definition["type"], "rule": definition["rule"], "valid": True, }) return results def validate_action(self, action: str) -> bool: """ Check if a string is a valid keyword action. Args: action: The action to validate. Returns: True if valid, False otherwise. """ return action.lower() in [kw.lower() for kw in KEYWORD_ACTIONS] def validate_ability(self, ability: str) -> bool: """ Check if a string is a valid keyword ability. Args: ability: The ability to validate. Returns: True if valid, False otherwise. """ return ability.lower() in [kw.lower() for kw in KEYWORD_ABILITIES] def is_evasion_ability(self, ability: str) -> bool: """ Check if an ability is an evasion ability. Args: ability: The ability to check. Returns: True if the ability is an evasion ability. """ definition = get_keyword_definition(ability) if definition and definition["ability_type"] == "evasion": return True return False def get_variant_base(self, variant: str) -> str | None: """ Get the base keyword for a variant. Args: variant: The variant keyword. Returns: The base keyword or None if not a variant. """ return KEYWORD_VARIANTS.get(variant.lower(), None) def validate_card_keywords(card_text: str) -> list[dict]: """ Validate all keywords found in a card's text. Args: card_text: The full text of the card. Returns: List of validation results. """ validator = KeywordValidator() return validator.validate_card_text(card_text) def is_valid_keyword(keyword: str) -> bool: """ Quick check if a keyword is valid. Args: keyword: The keyword to check. Returns: True if the keyword is valid. """ return get_keyword_definition(keyword) is not None def get_keyword_info(keyword: str) -> dict | None: """ Get detailed information about a keyword. Args: keyword: The keyword to look up. Returns: Dictionary with keyword information or None. """ return get_keyword_definition(keyword)