Files
mtg-builder/scripts/find_synergies.py
Tuan-Dat Tran eea3a6a659 Refactor project structure and update documentation
- Migrated data files to 'data/collection/' and 'data/decks/'.
- Moved 'card_cache.json' to 'cache/'.
- Reorganized 'collection_hydrated/' and 'deck_analysis.json' into 'output/'.
- Updated 'hydrate.py' and script defaults to match the new paths.
- Updated 'README.template.md' and 'AGENTS.template.md' templates.
- Regenerated 'README.md' and 'AGENTS.md'.
2026-02-26 14:51:48 +01:00

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 output/hydrated/deck.json --colors U R --keywords "landfall" "enter the battlefield"
python find_synergies.py --collection output/hydrated/deck.json --colors U R --creature-type Bird
python find_synergies.py --collection output/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()