mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2025-09-21 20:40:47 +02:00
A deckbuilder for the commander format of Magic: The Gathering
.github/workflows | ||
code | ||
config | ||
.dockerignore | ||
.env.example | ||
.gitignore | ||
CHANGELOG.md | ||
docker-compose.yml | ||
DOCKER.md | ||
Dockerfile | ||
LICENSE | ||
mtg_deckbuilder.spec | ||
mypy.ini | ||
pyproject.toml | ||
README.md | ||
RELEASE_NOTES_TEMPLATE.md | ||
requirements.txt | ||
run-from-dockerhub.bat | ||
run-from-dockerhub.sh | ||
WINDOWS_DOCKER_GUIDE.md |
# 🃏 MTG Python Deckbuilder
A fast, menu-driven deckbuilder for MTG Commander/EDH with both CLI and Web UI. Supports interactive and headless runs, CSV/TXT exports, owned-card libraries, and Docker.
🔹 Features
- Web UI and CLI, powered by the same builder
- Commander search with smart theme tagging (primary/secondary/tertiary)
- Power bracket selection and ideal count prompts
- CSV and TXT exports with stable filenames
- Headless mode (non-interactive) and a headless submenu in the main menu
- Config precedence: CLI > env > JSON > defaults
- Visual summaries in Web UI: Mana curve, Color pips and Sources with hover-to-highlight and copyable tooltips
✨ Highlights
- Smart tagging and suggestions for commander + themes, with AND/OR combine modes
- Exports CSV and TXT decklists to
deck_files/
- Owned-cards mode: point to one or more
.txt
/.csv
files inowned_cards/
and choose "Use only owned cards?" - If you opt out, final CSV marks owned cards with an
Owned
column - Prefer-owned option (Web Review step): bias picks toward owned cards while allowing unowned fallback
- Interactive menu with a headless submenu
- Works locally or in Docker (Windows PowerShell friendly)
- Card images and data via Scryfall (attribution shown in the Web UI)
- Web visual summaries: cross-highlight cards from charts; sources include non-lands and colorless 'C' with an on/off toggle
🚀 Quick start
Docker Hub (PowerShell)
docker run -it --rm `
-v "${PWD}/deck_files:/app/deck_files" `
-v "${PWD}/logs:/app/logs" `
-v "${PWD}/csv_files:/app/csv_files" `
-v "${PWD}/owned_cards:/app/owned_cards" `
-v "${PWD}/config:/app/config" `
mwisnowski/mtg-python-deckbuilder:latest
From source
pip install -r requirements.txt
python code/main.py
From this repo with Docker Compose (PowerShell)
docker compose build
docker compose run --rm mtg-deckbuilder
🌐 Web UI
Run the browser-based UI backed by the same deckbuilding engine.
From source
pip install -r requirements.txt
uvicorn code.web.app:app --host 127.0.0.1 --port 8080
Docker Compose (PowerShell)
docker compose build web
docker compose up --no-deps web
Then open http://localhost:8080
Notes:
- Exports/logs/configs use the same folders and will appear under your mounted volumes (see compose file).
- The footer includes Scryfall attribution per their guidelines.
- Favicon is bundled; browsers load
/favicon.ico
(auto-falls back to PNG).
Setup speed: parallel tagging (Web)
- On first run or when data is stale, the Web UI prepares and tags the card database.
- This tagging runs in parallel by default for faster setup.
- Tuning via env vars (set on the
web
service indocker-compose.yml
):WEB_TAG_PARALLEL=1|0
— enable/disable parallel workers (default: 1/enabled)WEB_TAG_WORKERS=<N>
— number of worker processes (default: 4 in compose; omit to auto-pick)
- The UI shows progress and falls back to sequential tagging automatically if parallel init fails.
Review step toggles (Web)
- Use only owned cards: builds strictly from your
owned_cards/
lists (commander exempt). - Prefer owned cards: gently prioritizes owned cards across creatures and spells but allows unowned when they’re better fits.
- Implemented via a stable owned-first reorder and small weight boosts; preserves existing sort intent.
Staged build visibility
- Step 5 can optionally “Show skipped stages” so you can see phases that added no cards with a clear annotation.
Visual summaries (Web)
- Mana Curve bars with hover-to-highlight of matching cards in both list and thumbnail views
- Color Pips (requirements) and Sources (generation) bars with per-color tooltips listing cards
- Cross-highlighting from charts to the card views; list-mode highlights only the name span
- Sources detection includes non-land producers (artifacts/creatures/etc.) and colorless 'C'
- Fetch lands are not counted as mana sources; basic lands and Wastes are handled reliably
- Optional: Show colorless (C) toggle for Sources; persisted per browser
- Tooltips include a Copy button to copy the card list
Owned page (Web)
- View and manage your owned lists with:
- Export TXT/CSV, sort controls, and a live “N shown” counter
- Color identity dots and exact color-identity combo filters (including 4-color)
- Viewport-filling list with styled scrollbar
- Uploading
.txt
or.csv
lists enriches and deduplicates entries at upload-time and persists them, so page loads are fast.
Finished Decks (Web)
- Theme filters are a dropdown with shareable state for easier browsing of saved builds.
Diagnostics (Web)
- Health endpoint:
GET /healthz
returns{ status, version, uptime_seconds }
- Responses include
X-Request-ID
for easier error correlation; unhandled errors return JSON withrequest_id
🤖 Headless mode (no prompts)
- Auto-headless: set
DECK_MODE=headless
- Example (PowerShell):
docker compose run --rm -e DECK_MODE=headless mtg-deckbuilder
- Example (PowerShell):
- Use a JSON config: mount
./config
to/app/config
and setDECK_CONFIG=/app/config/deck.json
- Example (PowerShell):
docker compose run --rm ` -e DECK_MODE=headless ` -e DECK_CONFIG=/app/config/deck.json ` mtg-deckbuilder
- Example (PowerShell):
- Override via env vars (subset):
DECK_COMMANDER
,DECK_PRIMARY_CHOICE
,DECK_SECONDARY_CHOICE
,DECK_TERTIARY_CHOICE
,DECK_ADD_LANDS
,DECK_FETCH_COUNT
,DECK_BRACKET_LEVEL
- Precedence: CLI > env > JSON > defaults
Headless submenu notes:
- If one JSON exists in
config/
, it auto-runs it - If multiple exist, they’re listed as "Commander - Theme1, Theme2, Theme3";
deck.json
shows as "Default" - CSV/TXT are exported as usual; JSON run-config is exported only in interactive runs
Run locally (no Docker)
# Show resolved settings (no run)
python code/headless_runner.py --config config/deck.json --dry-run
# Run with a specific config file
python code/headless_runner.py --config config/deck.json
# Point to a folder; if exactly one config exists, it's auto-used
python code/headless_runner.py --config config
# Override via CLI
python code/headless_runner.py --commander "Pantlaza, Sun-Favored" --primary-choice 2 --secondary-choice 0 --add-lands true --fetch-count 3
CLI flags
- --config Path to JSON config file or a folder to discover configs (uses DECK_CONFIG by default)
- --commander Commander name to search and select
- --primary-choice Primary theme index
- --secondary-choice <n|none> Secondary theme index or omit with "none"
- --tertiary-choice <n|none> Tertiary theme index or omit with "none"
- --add-lands Include land building steps (true/false)
- --fetch-count Requested number of fetch lands
- --dual-count Requested number of dual lands (optional; not exported)
- --triple-count Requested number of triple lands (optional; not exported)
- --utility-count Requested number of utility lands (optional; not exported)
- --add-creatures Add creatures
- --add-non-creature-spells Add non-creature spells orchestrator
- --add-ramp Add ramp (when not using orchestrator)
- --add-removal Add removal (when not using orchestrator)
- --add-wipes Add board wipes (when not using orchestrator)
- --add-card-advantage Add card draw (when not using orchestrator)
- --add-protection Add protection (when not using orchestrator)
- --bracket-level <1-5> Power bracket selection (or use DECK_BRACKET_LEVEL)
- --dry-run Print resolved config and exit
Booleans accept: 1/0, true/false, yes/no, on/off.
JSON export in headless
- By default, headless runs do not export a JSON run-config to avoid duplicates.
- Opt-in with:
$env:HEADLESS_EXPORT_JSON = "1" python code/headless_runner.py --config config/deck.json
- Tip: when opting in, prefer using
--config
instead of aDECK_CONFIG
file path to avoid creating both a stem-based JSON and a second explicit-path JSON.
Example JSON (config/deck.json
):
{
"commander": "Pantlaza",
"bracket_level": 3,
"primary_choice": 2,
"secondary_choice": 2,
"tertiary_choice": 2,
"tag_mode": "OR", // OR or AND; Web UI default is OR
"add_lands": true,
"fetch_count": 3,
"ideal_counts": { "ramp": 10, "lands": 36, "basic_lands": 16, "creatures": 28, "removal": 8, "wipes": 3, "card_advantage": 8, "protection": 3 }
}
Notes: headless honors ideal_counts
(leave prompts blank to accept). Only fetch_count
is tracked/exported for lands.
🧩 Theme combine mode (AND/OR)
- OR (default): Recommend cards that match any selected themes, prioritizing overlap.
- AND: Prioritize multi-theme intersections. For creatures, an AND pre-pass first picks "all selected themes" creatures up to a cap, then fills by weighted overlap.
UI tips:
- Step 2 includes AND/OR radios with a tooltip explaining trade-offs.
- In staged build view, "Creatures: All-Theme" shows which selected themes each card hits.
🕹️ Usage (interactive)
- Start the app (Docker or from source)
- Pick Build a New Deck
- Search/confirm commander
- Pick primary/secondary/tertiary themes (or stop at primary); choose AND/OR combine mode
- Choose power bracket and review ideal counts
- Deck builds; CSV/TXT export to
deck_files/
⚙️ Environment variables (common)
- DECK_MODE=headless
- DECK_CONFIG=/app/config/deck.json
- DECK_COMMANDER, DECK_PRIMARY_CHOICE, DECK_SECONDARY_CHOICE, DECK_TERTIARY_CHOICE
- DECK_ADD_LANDS, DECK_FETCH_COUNT
- DECK_ADD_CREATURES, DECK_ADD_NON_CREATURE_SPELLS, DECK_ADD_RAMP, DECK_ADD_REMOVAL, DECK_ADD_WIPES, DECK_ADD_CARD_ADVANTAGE, DECK_ADD_PROTECTION
- DECK_BRACKET_LEVEL
Optional name-based tag overrides (mapped to indices for the chosen commander):
- DECK_PRIMARY_TAG, DECK_SECONDARY_TAG, DECK_TERTIARY_TAG
Combine mode in headless:
- JSON: set
"tag_mode": "AND" | "OR"
- Env var:
DECK_TAG_MODE=AND|OR
(if configured in your environment)
Web UI performance tuning:
- WEB_TAG_PARALLEL=1|0
- WEB_TAG_WORKERS=
📁 Folders
deck_files/
— CSV/TXT exportscsv_files/
— card datalogs/
— logsconfig/
— JSON configs (optional)owned_cards/
— your owned cards lists (.txt
/.csv
); used for owned-only builds and Owned flagging
🧰 Troubleshooting
- Use
docker compose run --rm
(notup
) for interactive sessions - Ensure volumes are mounted so files persist (
deck_files
,logs
,csv_files
) - For headless with config, mount
./config:/app/config
and setDECK_CONFIG
- Card data refresh: if
csv_files/cards.csv
is missing or older than 7 days, the app refreshes data and re-tags automatically. A.tagging_complete.json
file incsv_files/
indicates tagging completion.
📦 Releases
- Release notes are maintained in
RELEASE_NOTES_TEMPLATE.md
. Automated workflows read from this file to populate Docker Hub and GitHub releases.
🧩 Owned cards library
- Place
.txt
or.csv
lists inowned_cards/
(one card name per line for.txt
; any.csv
with aname
column works). - After commander selection, you’ll be prompted: "Use only owned cards?"
- Yes: deck builds from owned cards only; if fewer than 100 cards, it stays incomplete and prints a note.
- No: the build uses the full pool, but the final CSV marks owned cards with an
Owned
column.
- If an owned-only build is incomplete, a recommendations file is exported:
deck_files/[stem]_recommendations.csv
and.txt
. The app prints: "Recommended but unowned cards in deck_files/[stem]_recommendations.csv". - Web uploads: owned lists are parsed/enriched at upload-time, header rows are skipped, and duplicates are deduped. The enriched result is stored so subsequent page loads don’t re-parse your files.
📄 License & help
- License: MIT (see
LICENSE
) - Issues/Requests: GitHub Issues/Discussions
- Docker details: see
DOCKER.md