Add MTG rules engine source files
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
"""
|
||||
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)
|
||||
Reference in New Issue
Block a user