120 lines
4.2 KiB
Python
120 lines
4.2 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Find cards with specific synergies from a hydrated collection.
|
|
Usage:
|
|
python find_synergies.py --collection collection_hydrated/deck.json --colors U R --keywords "landfall" "enter the battlefield"
|
|
python find_synergies.py --collection collection_hydrated/deck.json --colors U R --creature-type Bird
|
|
python find_synergies.py --collection collection_hydrated/deck.json --colors U R --cmc-min 4 --type instant
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import re
|
|
import sys
|
|
from typing import Optional
|
|
|
|
|
|
def load_collection(path: str) -> dict:
|
|
"""Load hydrated collection JSON."""
|
|
with open(path, 'r') as f:
|
|
cards = json.load(f)
|
|
return {c['name']: c for c in cards}
|
|
|
|
|
|
def matches_colors(card_identity: set, allowed_colors: set) -> bool:
|
|
"""Check if card identity fits within allowed colors."""
|
|
if not card_identity:
|
|
return True
|
|
return card_identity <= allowed_colors
|
|
|
|
|
|
def extract_creature_types(type_line: str) -> list:
|
|
"""Extract creature types from type line."""
|
|
if not type_line:
|
|
return []
|
|
parts = type_line.split('—')
|
|
if len(parts) < 2:
|
|
return []
|
|
types_part = parts[1].strip()
|
|
return [t.strip().lower() for t in types_part.split()]
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='Find synergistic cards in collection')
|
|
parser.add_argument('--collection', '-c', required=True, help='Hydrated collection JSON')
|
|
parser.add_argument('--colors', nargs='+', default=[], help='Allowed colors (e.g., U R for Izzet)')
|
|
parser.add_argument('--exclude', nargs='+', default=[], help='Cards to exclude (already in deck)')
|
|
parser.add_argument('--keywords', '-k', nargs='+', help='Keywords to search in oracle text')
|
|
parser.add_argument('--creature-type', '-t', help='Creature type to search for')
|
|
parser.add_argument('--type', help='Card type to filter (creature, instant, sorcery, etc.)')
|
|
parser.add_argument('--cmc-min', type=int, help='Minimum CMC')
|
|
parser.add_argument('--cmc-max', type=int, help='Maximum CMC')
|
|
parser.add_argument('--output', '-o', help='Output JSON file')
|
|
parser.add_argument('--format', choices=['json', 'text'], default='text', help='Output format')
|
|
args = parser.parse_args()
|
|
|
|
collection = load_collection(args.collection)
|
|
allowed_colors = set(c.upper() for c in args.colors)
|
|
exclude = set(args.exclude)
|
|
|
|
matches = []
|
|
for name, card in collection.items():
|
|
if name in exclude:
|
|
continue
|
|
|
|
ci = set(card.get('color_identity', []))
|
|
if not matches_colors(ci, allowed_colors):
|
|
continue
|
|
|
|
type_line = card.get('type_line', '').lower() if card.get('type_line') else ''
|
|
|
|
if args.type and args.type.lower() not in type_line:
|
|
continue
|
|
|
|
cmc = card.get('cmc', 0) or 0
|
|
if args.cmc_min is not None and cmc < args.cmc_min:
|
|
continue
|
|
if args.cmc_max is not None and cmc > args.cmc_max:
|
|
continue
|
|
|
|
if args.creature_type:
|
|
creature_types = extract_creature_types(type_line)
|
|
if args.creature_type.lower() not in creature_types:
|
|
continue
|
|
|
|
oracle = card.get('oracle_text', '').lower() if card.get('oracle_text') else ''
|
|
|
|
if args.keywords:
|
|
if not any(kw.lower() in oracle for kw in args.keywords):
|
|
continue
|
|
|
|
matches.append({
|
|
'name': name,
|
|
'mana_cost': card.get('mana_cost', ''),
|
|
'cmc': cmc,
|
|
'type_line': card.get('type_line', ''),
|
|
'colors': card.get('colors', []),
|
|
'color_identity': list(ci),
|
|
'oracle_text': card.get('oracle_text', ''),
|
|
'count': card.get('count', 1)
|
|
})
|
|
|
|
matches.sort(key=lambda x: (x['cmc'], x['name']))
|
|
|
|
if args.format == 'json':
|
|
output = json.dumps(matches, indent=2)
|
|
if args.output:
|
|
with open(args.output, 'w') as f:
|
|
f.write(output)
|
|
else:
|
|
print(output)
|
|
else:
|
|
for card in matches:
|
|
print(f"{card['mana_cost']} {card['name']} ({int(card['cmc'])}cmc) x{card['count']}")
|
|
if card['oracle_text']:
|
|
print(f" {card['oracle_text'][:100]}...")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|