Tuan-Dat Tran 31a4995bbe feat: add deck builder with multiple collection support
- Add build_deck.py script for automated deck building
- Support multiple collection files for comprehensive deck building
- Rename main collection file to full_collection.json
- Add comprehensive documentation and usage examples
- Include design and implementation plans
- Enhance synergy detection and commander suggestion
2026-03-03 23:23:44 +01:00

MTG EDH Deck Manager

Command-line toolkit for managing Magic: The Gathering Commander (EDH) decks using the Scryfall API.

Installation

git clone <repo-url>
cd Decks

# Install git hook for auto-updating docs
cp scripts/pre-commit .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit

No dependencies required - pure Python 3 standard library.

Usage

Hydrate a Collection

Fetch card data from Scryfall for a decklist:

python hydrate.py hydrate data/collection/decklist.txt -o output/hydrated/ -c cache/card_cache.json

Create a New Deck

python hydrate.py new my_deck

Analyze Decks

Find upgrade options from your collection:

python scripts/analyze_decks.py --collection output/hydrated/deck.json --deck-dir data/decks/

Find Synergies

Search for cards by keywords, colors, and type:

# Find landfall cards in Simic colors
python scripts/find_synergies.py --collection output/hydrated/deck.json --colors U G --keywords landfall

# Find Bird creatures in Bant colors
python scripts/find_synergies.py --collection output/hydrated/deck.json --colors U G W --creature-type Bird

# Find instants with CMC 2 or less
python scripts/find_synergies.py --collection output/hydrated/deck.json --type instant --cmc-max 2

Generate Reports

python scripts/deck_report.py --collection output/hydrated/deck.json --decks-dir data/decks/ --output output/report.md

Decks

Deck Colors Commander Archetype
my_choco_deck BUW G'raha Tia, Scion Reborn Auto-generated
Choco UGW Choco, Seeker of Paradise Bird Tribal Landfall
Hazel BG Hazel of the Rootbloom Golgari Aristocrats
Palamecia UR The Emperor of Palamecia // The Lord Master of Hell Izzet Self-Mill Storm
Yshtola UBW Y'shtola, Night's Blessed Esper Stax Drain

Workflow

Quick Reference

  1. Import - Place text decklists in data/collection/
  2. Hydrate - Run hydrate.py to fetch Scryfall data → output/hydrated/
  3. Explore - Find synergies and commanders in your collection
  4. Define - Create deck JSON files in data/decks/
  5. Analyze - Run analyze_decks.py to find upgrade options
  6. Report - Use deck_report.py for markdown summaries

Runbook: Build a Deck From Your Collection

Step 1: Import Your Collection

Create a text file in data/collection/ with your cards (one per line):

1 Sol Ring (CMA) 1
3 Birds of Paradise (M12) 165
1 Eternal Witness (EMA) 183

Format: <count> <Card Name> (<Set Code>) <Collector Number>

The set code and collector number are optional but help with accuracy.

Step 2: Hydrate Your Collection

Fetch card data from Scryfall:

python hydrate.py hydrate data/collection/my_cards.txt -o output/hydrated/ -c cache/card_cache.json

This creates:

  • output/hydrated/deck.json - All cards with full data
  • output/hydrated/creatures.json - Creatures only
  • output/hydrated/instants.json, sorceries.json, etc.

Step 3: Explore Your Collection

View color distribution:

python -c "
import json
from collections import Counter
cards = json.load(open('output/hydrated/deck.json'))
colors = Counter(c for card in cards for c in card.get('color_identity', []))
for c, n in colors.most_common(): print(f'{c}: {n}')
"

Find creature types you own:

python scripts/find_synergies.py --collection output/hydrated/deck.json --creature-type Elf
python scripts/find_synergies.py --collection output/hydrated/deck.json --creature-type Elemental

Find cards by keyword/mechanic:

# Find all landfall cards
python scripts/find_synergies.py --collection output/hydrated/deck.json --keywords landfall

# Find lifegain synergies
python scripts/find_synergies.py --collection output/hydrated/deck.json --keywords "gain life"

# Find -1/-1 counter cards
python scripts/find_synergies.py --collection output/hydrated/deck.json --keywords "-1/-1 counter"

Find cards by color identity:

# Simic (UG) cards
python scripts/find_synergies.py --collection output/hydrated/deck.json --colors U G

# Esper (WUB) instants
python scripts/find_synergies.py --collection output/hydrated/deck.json --colors W U B --type instant

Step 4: Choose a Commander

Based on your collection strengths, pick a commander that matches:

  • Your most abundant colors
  • Creature types you have many of
  • Mechanics with good support in your collection

Step 5: Create the Deck File

Create data/decks/<deck_name>.json:

{
  "name": "My Deck Name",
  "commander": "Commander Name",
  "colors": ["U", "G"],
  "archetype": "Simic Value",
  "cards": {
    "Sol Ring": 1,
    "Birds of Paradise": 1,
    "Eternal Witness": 1
  }
}

Step 6: Analyze for Upgrades

Find cards in your collection that fit your deck:

python scripts/analyze_decks.py --collection output/hydrated/deck.json --deck-dir data/decks/

This compares your deck against your collection and suggests:

  • Cards matching your commander's color identity
  • Cards with synergistic mechanics
  • Cards not yet in the deck

Step 7: Generate a Report

python scripts/deck_report.py --collection output/hydrated/deck.json --decks-dir data/decks/ --output output/report.md

Example: Building a Deck From Scratch

# 1. Add your collection
echo "1 Ashling, Rekindled // Ashling, Rimebound (ECL) 124" >> data/collection/my_cards.txt

# 2. Hydrate
python hydrate.py hydrate data/collection/my_cards.txt -o output/hydrated/ -c cache/card_cache.json

# 3. Find red instants/sorceries (spellslinger support)
python scripts/find_synergies.py --collection output/hydrated/deck.json --colors R --type instant
python scripts/find_synergies.py --collection output/hydrated/deck.json --colors R --type sorcery

# 4. Create deck file
cat > data/decks/ashling.json << 'EOF'
{
  "name": "Ashling Spellslinger",
  "commander": "Ashling, Rekindled // Ashling, Rimebound",
  "colors": ["R"],
  "archetype": "Mono-Red Spellslinger",
  "cards": {}
}
EOF

# 5. Analyze and find synergies
python scripts/analyze_decks.py --collection output/hydrated/deck.json --deck-dir data/decks/

Runbook: Update Your Collection

Scenario 1: Add New Cards (Single File)

If you opened new packs and want to add cards:

# 1. Create a new file with the new cards
cat > data/collection/new_cards_2026-02-20.txt << 'EOF'
1 Bre of Clan Stoutarm (ECL) 8
3 Mulldrifter (ECC) 67
1 Sol Ring (ECC) 57
EOF

# 2. Hydrate the new file (merges into existing cache)
python hydrate.py hydrate data/collection/new_cards_2026-02-20.txt -o output/hydrated/ -c cache/card_cache.json

Scenario 2: Add Cards to Existing File

# Append to existing collection file
echo "1 The Reaper, King No More (ECC) 4" >> "data/collection/Box1 2026-01-30.txt"

# Re-hydrate that file
python hydrate.py hydrate "data/collection/Box1 2026-01-30.txt" -o output/hydrated/ -c cache/card_cache.json

Scenario 3: Merge Multiple Collection Files

If you have multiple files to combine:

# Hydrate each file separately (cache persists)
python hydrate.py hydrate data/collection/Box1.txt -o output/hydrated/ -c cache/card_cache.json
python hydrate.py hydrate data/collection/Box2.txt -o output/hydrated/ -c cache/card_cache.json

# Note: Each run overwrites deck.json. To merge, combine files first:
cat data/collection/Box1.txt data/collection/Box2.txt > data/collection/combined.txt
python hydrate.py hydrate data/collection/combined.txt -o output/hydrated/ -c cache/card_cache.json

Scenario 4: Re-hydrate Entire Collection

If cache is corrupted or you want fresh data:

# Option A: Keep cache, just re-run
python hydrate.py hydrate data/collection/combined.txt -o output/hydrated/ -c cache/card_cache.json

# Option B: Clear cache and fetch fresh (slow, ~100ms per unique card)
rm cache/card_cache.json
python hydrate.py hydrate data/collection/combined.txt -o output/hydrated/ -c cache/card_cache.json

Scenario 5: Update After Set Release

When a new set releases and you want to add those cards:

# 1. Add new set cards to collection file
echo "# Lorwyn Eclipsed - 2026-01-23" >> data/collection/my_collection.txt
cat data/collection/new_ecl_cards.txt >> data/collection/my_collection.txt

# 2. Hydrate (cache speeds up existing cards, fetches new ones)
python hydrate.py hydrate data/collection/my_collection.txt -o output/hydrated/ -c cache/card_cache.json

# 3. Verify new cards are present
python -c "
import json
cards = json.load(open('output/hydrated/deck.json'))
print(f'Total unique cards: {len(cards)}')
"

Collection File Format

# Comments start with #
# Format: <count> <Card Name> (<Set Code>) <Collector Number>

1 Sol Ring (CMA) 1
3 Birds of Paradise
1 Eternal Witness (EMA) 183

# Set code and collector number are optional but recommended
# They help Scryfall find the exact printing

Troubleshooting

Card not found:

Error fetching 'Some Card Name': HTTP 404

→ Check spelling, or try without set code. Use the exact name from Scryfall.

Duplicate cards: If the same card appears multiple times in your file, they're stored separately. Dedupe first:

# Combine duplicates
python -c "
from collections import Counter
cards = Counter()
with open('data/collection/my_cards.txt') as f:
    for line in f:
        line = line.strip()
        if line and not line.startswith('#'):
            parts = line.split(None, 1)
            if parts:
                count = int(parts[0]) if parts[0].isdigit() else 1
                name = parts[1] if len(parts) > 1 else parts[0]
                cards[name.split('(')[0].strip()] += count
for name, count in sorted(cards.items()):
    print(f'{count} {name}')
" > data/collection/deduped.txt

Slow hydration:

  • First run fetches all cards (~100ms each)
  • Subsequent runs use cache (instant)
  • 866 unique cards ≈ 87 seconds first run

Collection Stats

  • Total cards: 1288
  • Unique cards: 892
  • By type:
    • Artifacts: 86
    • Creatures: 419
    • Enchantments: 67
    • Instants: 134
    • Lands: 76
    • Planeswalkers: 2
    • Sorceries: 107

This file is auto-generated. Edit docs/templates/README.template.md instead.

Description
No description provided
Readme 1.1 MiB
Languages
Python 99.7%
Shell 0.3%