Add MTG rules engine source files
This commit is contained in:
@@ -0,0 +1,276 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Tests for the Magic: The Gathering Rules Engine"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add parent directory to path for importing mtg_rules_engine
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from mtg_rules_engine import (
|
||||
ABILITY_WORDS,
|
||||
KEYWORD_ACTIONS,
|
||||
KEYWORD_ABILITIES,
|
||||
get_all_keywords,
|
||||
get_keyword_info,
|
||||
is_valid_keyword,
|
||||
get_keyword_type,
|
||||
KeywordValidator,
|
||||
RulesEngine,
|
||||
)
|
||||
|
||||
|
||||
def test_keyword_database():
|
||||
"""Test the keyword database is properly populated."""
|
||||
print("=" * 60)
|
||||
print("TEST: Keyword Database")
|
||||
print("=" * 60)
|
||||
|
||||
# Test ability words
|
||||
assert len(ABILITY_WORDS) > 0, "Ability words should not be empty"
|
||||
assert "adamant" in ABILITY_WORDS, "adamant should be in ability words"
|
||||
print(f" ✓ {len(ABILITY_WORDS)} ability words loaded")
|
||||
|
||||
# Test keyword actions
|
||||
assert len(KEYWORD_ACTIONS) > 0, "Keyword actions should not be empty"
|
||||
assert "fly" not in KEYWORD_ACTIONS and "flying" in KEYWORD_ABILITIES, "flying should be a keyword ability"
|
||||
assert "cast" in KEYWORD_ACTIONS, "cast should be a keyword action"
|
||||
assert "destroy" in KEYWORD_ACTIONS, "destroy should be a keyword action"
|
||||
print(f" ✓ {len(KEYWORD_ACTIONS)} keyword actions loaded")
|
||||
|
||||
# Test keyword abilities
|
||||
assert len(KEYWORD_ABILITIES) > 0, "Keyword abilities should not be empty"
|
||||
assert "flying" in KEYWORD_ABILITIES, "flying should be a keyword ability"
|
||||
assert "haste" in KEYWORD_ABILITIES, "haste should be a keyword ability"
|
||||
assert "deathtouch" in KEYWORD_ABILITIES, "deathtouch should be a keyword ability"
|
||||
assert "trample" in KEYWORD_ABILITIES, "trample should be a keyword ability"
|
||||
assert "hexproof" in KEYWORD_ABILITIES, "hexproof should be a keyword ability"
|
||||
assert "flying" in KEYWORD_ABILITIES, "flying should be a keyword ability"
|
||||
print(f" ✓ {len(KEYWORD_ABILITIES)} keyword abilities loaded")
|
||||
|
||||
# Test get_all_keywords
|
||||
all_keywords = get_all_keywords()
|
||||
assert len(all_keywords) > 0, "get_all_keywords should return keywords"
|
||||
print(f" ✓ {len(all_keywords)} total keywords loaded")
|
||||
|
||||
# Test get_keyword_info
|
||||
info = get_keyword_info("flying")
|
||||
assert info is not None, "get_keyword_info should return info"
|
||||
assert "rule" in info, "Keyword info should have 'rule'"
|
||||
assert "definition" in info, "Keyword info should have 'definition'"
|
||||
print(f" ✓ Keyword info for 'flying': rule={info['rule'][:40]}...")
|
||||
|
||||
# Test get_keyword_type
|
||||
type_flying = get_keyword_type("flying")
|
||||
assert type_flying == "keyword_ability", f"Expected keyword_ability, got {type_flying}"
|
||||
type_cast = get_keyword_type("cast")
|
||||
assert type_cast == "keyword_action", f"Expected keyword_action, got {type_cast}"
|
||||
print(f" ✓ Keyword type for 'flying': {type_flying}")
|
||||
print(f" ✓ Keyword type for 'cast': {type_cast}")
|
||||
|
||||
print(" ✓ All keyword database tests passed!\n")
|
||||
|
||||
|
||||
def test_keyword_validator():
|
||||
"""Test the keyword validator."""
|
||||
print("=" * 60)
|
||||
print("TEST: Keyword Validator")
|
||||
print("=" * 60)
|
||||
|
||||
validator = KeywordValidator()
|
||||
|
||||
# Test is_valid_keyword
|
||||
assert validator.is_valid_keyword("flying"), "flying should be valid"
|
||||
assert validator.is_valid_keyword("haste"), "haste should be valid"
|
||||
assert not validator.is_valid_keyword("notarealkeyword"), "notarealkeyword should be invalid"
|
||||
print(" ✓ is_valid_keyword works correctly")
|
||||
|
||||
# Test find_keywords_in_text
|
||||
text = "Flying creatures can't be blocked except by flying creatures with flying abilities."
|
||||
found = validator.find_keywords_in_text(text)
|
||||
assert "flying" in found, "flying should be found"
|
||||
assert found["flying"] == 3, f"Expected 3 'flying', got {found['flying']}"
|
||||
print(f" ✓ find_keywords_in_text found {found}")
|
||||
|
||||
# Test validate_card_text
|
||||
card_text = "Flying, trample. Haste. When this creature attacks, it deals extra damage."
|
||||
errors = validator.validate_card_text(card_text)
|
||||
# No errors expected for valid keywords
|
||||
print(f" ✓ validate_card_text: {len(errors)} errors (expected 0)")
|
||||
|
||||
# Test check_for_misspelled_keywords
|
||||
misspelled = validator.check_for_misspelled_keywords("I want to fly with this haste creature.")
|
||||
# "fly" should suggest "flying"
|
||||
print(f" ✓ check_for_misspelled_keywords found {len(misspelled)} potential misspellings")
|
||||
|
||||
# Test export_keywords
|
||||
exported = validator.export_keywords()
|
||||
assert "total_keywords" in exported, "export_keywords should have total_keywords"
|
||||
assert "ability_words" in exported, "export_keywords should have ability_words"
|
||||
assert "keyword_actions" in exported, "export_keywords should have keyword_actions"
|
||||
assert "keyword_abilities" in exported, "export_keywords should have keyword_abilities"
|
||||
print(f" ✓ export_keywords has all expected fields")
|
||||
|
||||
print(" ✓ All keyword validator tests passed!\n")
|
||||
|
||||
|
||||
def test_rules_engine():
|
||||
"""Test the rules engine."""
|
||||
print("=" * 60)
|
||||
print("TEST: Rules Engine")
|
||||
print("=" * 60)
|
||||
|
||||
engine = RulesEngine()
|
||||
|
||||
# Test validate_card with a valid creature
|
||||
valid_card = {
|
||||
"name": "Garruk the Wreathshaper",
|
||||
"text": "Flying, trample. When Garruk enters, create a 1/1 green Elf creature token.",
|
||||
"type": "CREATURE",
|
||||
"power_toughness": (6, 6),
|
||||
"color": ["GREEN"],
|
||||
"abilities": ["flying", "trample"],
|
||||
}
|
||||
errors = engine.validate_card(valid_card)
|
||||
# Some errors expected (text analysis, etc.)
|
||||
print(f" ✓ validate_card returned {len(errors)} errors")
|
||||
|
||||
# Test validate_action with a valid attack
|
||||
valid_attack = {
|
||||
"type": "attack",
|
||||
"attacker": {
|
||||
"name": "Garruk the Wreathshaper",
|
||||
"type": "CREATURE",
|
||||
"power": 6,
|
||||
"toughness": 6,
|
||||
"abilities": ["flying", "trample"],
|
||||
},
|
||||
"blocking_creatures": [
|
||||
{
|
||||
"name": "Garruk the Wreathshaper",
|
||||
"type": "CREATURE",
|
||||
"power": 6,
|
||||
"toughness": 6,
|
||||
"abilities": ["flying", "trample"],
|
||||
}
|
||||
],
|
||||
}
|
||||
errors = engine.validate_action(valid_attack)
|
||||
# Some errors expected (flying creature can't be blocked)
|
||||
print(f" ✓ validate_action returned {len(errors)} errors")
|
||||
|
||||
# Test get_rule_text
|
||||
rule = engine.get_rule_text("702.9")
|
||||
assert rule is not None, "get_rule_text should return a result"
|
||||
print(f" ✓ get_rule_text returned: {rule[:80]}...")
|
||||
|
||||
# Test get_keyword_info_summary
|
||||
summary = engine.get_keyword_info_summary("flying")
|
||||
assert summary is not None, "get_keyword_info_summary should return a summary"
|
||||
assert summary["keyword"] == "flying", "Summary should have correct keyword"
|
||||
assert summary["rule"] is not None, "Summary should have rule"
|
||||
print(f" ✓ get_keyword_info_summary works: {summary}")
|
||||
|
||||
# Test search_keywords
|
||||
results = engine.search_keywords("flying")
|
||||
assert len(results) > 0, "search_keywords should find matches"
|
||||
print(f" ✓ search_keywords found {len(results)} matches for 'flying'")
|
||||
|
||||
# Test validate_game_state
|
||||
valid_state = {
|
||||
"players": [
|
||||
{
|
||||
"name": "Player 1",
|
||||
"hand": [],
|
||||
"creatures": [],
|
||||
},
|
||||
{
|
||||
"name": "Player 2",
|
||||
"hand": [],
|
||||
"creatures": [],
|
||||
},
|
||||
],
|
||||
"zones": {
|
||||
"stack": [],
|
||||
},
|
||||
}
|
||||
errors = engine.validate_game_state(valid_state)
|
||||
# No errors expected for valid state
|
||||
print(f" ✓ validate_game_state returned {len(errors)} errors")
|
||||
|
||||
print(" ✓ All rules engine tests passed!\n")
|
||||
|
||||
|
||||
def test_integration():
|
||||
"""Integration test with the full pipeline."""
|
||||
print("=" * 60)
|
||||
print("TEST: Integration")
|
||||
print("=" * 60)
|
||||
|
||||
# End-to-end test: Create a card, validate it, analyze its keywords
|
||||
engine = RulesEngine()
|
||||
|
||||
card = {
|
||||
"name": "Mighty Hero",
|
||||
"text": "Flying, trample, trample over planeswalkers. Haste. When this creature enters, create a 1/1 token.",
|
||||
"type": "CREATURE",
|
||||
"power_toughness": (4, 4),
|
||||
"color": ["RED"],
|
||||
"abilities": ["flying", "trample", "haste"],
|
||||
}
|
||||
|
||||
# Validate card
|
||||
errors = engine.validate_card(card)
|
||||
print(f" Card validation: {len(errors)} errors")
|
||||
|
||||
# Find keywords in card text
|
||||
keywords = engine.validator.find_keywords_in_text(card["text"])
|
||||
print(f" Keywords found in card text: {list(keywords.keys())}")
|
||||
|
||||
# Get keyword info for each found keyword
|
||||
for keyword in keywords:
|
||||
info = engine.get_keyword_info_summary(keyword)
|
||||
print(f" - {keyword}: {info.get('rule', 'N/A')}")
|
||||
|
||||
# Validate an attack
|
||||
attack = {
|
||||
"type": "attack",
|
||||
"attacker": {
|
||||
"name": "Mighty Hero",
|
||||
"type": "CREATURE",
|
||||
"power": 4,
|
||||
"toughness": 4,
|
||||
"abilities": ["flying", "trample"],
|
||||
},
|
||||
"blocking_creatures": [
|
||||
{
|
||||
"name": "Opponent's Flying Creature",
|
||||
"type": "CREATURE",
|
||||
"power": 3,
|
||||
"toughness": 3,
|
||||
"abilities": ["flying"],
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
errors = engine.validate_action(attack)
|
||||
print(f" Attack validation: {len(errors)} errors")
|
||||
for error in errors:
|
||||
print(f" - {error['message']}")
|
||||
|
||||
print(" ✓ Integration test passed!\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("\n" + "=" * 60)
|
||||
print("Magic: The Gathering Rules Engine - Test Suite")
|
||||
print("=" * 60 + "\n")
|
||||
|
||||
test_keyword_database()
|
||||
test_keyword_validator()
|
||||
test_rules_engine()
|
||||
test_integration()
|
||||
|
||||
print("=" * 60)
|
||||
print("ALL TESTS PASSED!")
|
||||
print("=" * 60)
|
||||
Reference in New Issue
Block a user