- 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'.
175 lines
5.6 KiB
Python
175 lines
5.6 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Analyze decks against a hydrated collection to find upgrade options.
|
|
Usage:
|
|
python analyze_decks.py --collection output/hydrated/deck.json --decks deck1.json deck2.json ...
|
|
python analyze_decks.py --collection output/hydrated/deck.json --deck-dir data/decks/
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
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 load_deck(path: str) -> dict:
|
|
"""Load deck JSON (from parse_deck.py output or deck.json format)."""
|
|
with open(path, 'r') as f:
|
|
data = json.load(f)
|
|
if 'cards' in data:
|
|
return data['cards']
|
|
return data
|
|
|
|
|
|
def get_color_identity(card: dict) -> set:
|
|
"""Get color identity as a set."""
|
|
return set(card.get('color_identity', []))
|
|
|
|
|
|
def matches_colors(card_identity: set, deck_colors: set) -> bool:
|
|
"""Check if card identity fits within deck colors."""
|
|
return card_identity <= deck_colors or not card_identity
|
|
|
|
|
|
def find_upgrades(collection: dict[str, dict], deck_cards: dict, all_deck_cards: set,
|
|
deck_colors: set, archetype: str | None = None) -> dict:
|
|
"""Find potential upgrade cards for a deck."""
|
|
available = set(collection.keys()) - all_deck_cards
|
|
upgrades = {
|
|
'creatures': [],
|
|
'instants': [],
|
|
'sorceries': [],
|
|
'artifacts': [],
|
|
'enchantments': [],
|
|
'lands': [],
|
|
'other': []
|
|
}
|
|
|
|
for name in available:
|
|
card = collection.get(name, {})
|
|
if not card:
|
|
continue
|
|
|
|
ci = get_color_identity(card)
|
|
if not matches_colors(ci, deck_colors):
|
|
continue
|
|
|
|
type_line = card.get('type_line', '').lower() if card.get('type_line') else ''
|
|
entry = {
|
|
'name': name,
|
|
'mana_cost': card.get('mana_cost', ''),
|
|
'cmc': card.get('cmc', 0),
|
|
'type_line': card.get('type_line', ''),
|
|
'oracle_text': card.get('oracle_text', ''),
|
|
'count': card.get('count', 1)
|
|
}
|
|
|
|
if 'creature' in type_line:
|
|
upgrades['creatures'].append(entry)
|
|
elif 'instant' in type_line:
|
|
upgrades['instants'].append(entry)
|
|
elif 'sorcery' in type_line:
|
|
upgrades['sorceries'].append(entry)
|
|
elif 'artifact' in type_line:
|
|
upgrades['artifacts'].append(entry)
|
|
elif 'enchantment' in type_line:
|
|
upgrades['enchantments'].append(entry)
|
|
elif 'land' in type_line:
|
|
upgrades['lands'].append(entry)
|
|
else:
|
|
upgrades['other'].append(entry)
|
|
|
|
return upgrades
|
|
|
|
|
|
def find_synergies(collection: dict, available: set, deck_colors: set,
|
|
keywords: list) -> list:
|
|
"""Find cards with specific keyword synergies."""
|
|
synergies = []
|
|
for name in available:
|
|
card = collection.get(name, {})
|
|
if not card:
|
|
continue
|
|
ci = get_color_identity(card)
|
|
if not matches_colors(ci, deck_colors):
|
|
continue
|
|
oracle = card.get('oracle_text', '').lower() if card.get('oracle_text') else ''
|
|
if any(kw.lower() in oracle for kw in keywords):
|
|
synergies.append({
|
|
'name': name,
|
|
'type_line': card.get('type_line', ''),
|
|
'mana_cost': card.get('mana_cost', ''),
|
|
'oracle_snippet': oracle[:100]
|
|
})
|
|
return synergies
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description='Analyze decks for upgrade options')
|
|
parser.add_argument('--collection', '-c', required=True, help='Hydrated collection JSON')
|
|
parser.add_argument('--decks', '-d', nargs='+', help='Deck JSON files')
|
|
parser.add_argument('--deck-dir', help='Directory containing deck JSON files')
|
|
parser.add_argument('--output', '-o', help='Output JSON file')
|
|
parser.add_argument('--keywords', '-k', nargs='+', help='Keywords to search for synergies')
|
|
args = parser.parse_args()
|
|
|
|
collection = load_collection(args.collection)
|
|
|
|
decks = {}
|
|
if args.decks:
|
|
for path in args.decks:
|
|
name = Path(path).stem
|
|
decks[name] = load_deck(path)
|
|
elif args.deck_dir:
|
|
for path in Path(args.deck_dir).glob('*.json'):
|
|
decks[path.stem] = load_deck(str(path))
|
|
else:
|
|
print("Error: Provide --decks or --deck-dir", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
all_deck_cards = set()
|
|
for deck_cards in decks.values():
|
|
all_deck_cards.update(deck_cards.keys())
|
|
|
|
results = {}
|
|
for deck_name, deck_cards in decks.items():
|
|
deck_colors = set()
|
|
for name in deck_cards:
|
|
card = collection.get(name, {})
|
|
deck_colors |= get_color_identity(card)
|
|
|
|
upgrades = find_upgrades(collection, deck_cards, all_deck_cards, deck_colors)
|
|
|
|
available = set(collection.keys()) - all_deck_cards
|
|
synergies = {}
|
|
if args.keywords:
|
|
synergies = find_synergies(collection, available, deck_colors, args.keywords)
|
|
|
|
results[deck_name] = {
|
|
'total_cards': sum(deck_cards.values()),
|
|
'unique_cards': len(deck_cards),
|
|
'color_identity': list(deck_colors),
|
|
'available_upgrades': {k: len(v) for k, v in upgrades.items()},
|
|
'upgrades': upgrades,
|
|
'synergies': synergies
|
|
}
|
|
|
|
output = json.dumps(results, indent=2)
|
|
if args.output:
|
|
with open(args.output, 'w') as f:
|
|
f.write(output)
|
|
else:
|
|
print(output)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|