Spaces:
Sleeping
Sleeping
File size: 5,543 Bytes
c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 b743918 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 c8f1b74 971af66 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
"""
MTG Draft Assistant Streamlit App
---------------------------------
A booster-draft helper written with Streamlit and deployable on Hugging Face Spaces.
Changes in this revision
~~~~~~~~~~~~~~~~~~~~~~~~
* **supported_sets.txt** β one set code per line.
* Sidebar now shows a **single-choice list** (radio buttons) sourced from that
file instead of a free-text box, so users can only draft sets you actually
support.
* Fallback to the old text input if the text-file is missing or empty.
Replace the three stub functions (`load_model`, `suggest_pick`,
`generate_booster`) with your real model-/API-calls when you are ready.
"""
from __future__ import annotations
import os
import random
from pathlib import Path
from typing import Dict, List
import requests
import streamlit as st
# -----------------------------------------------------------------------------
# 0. Constants & helpers
# -----------------------------------------------------------------------------
SUPPORTED_SETS_PATH = Path("helper_files/supported_sets.txt")
@st.cache_data(show_spinner="Reading supported sets β¦")
def get_supported_sets(path: Path = SUPPORTED_SETS_PATH) -> List[str]:
"""Return a list of legal set codes read from *supported_sets.txt*.
The file should contain **one set tag per line**, e.g.::
WOE
LCI
MKM
If the file is missing we fall back to an empty list so the UI
degrades gracefully.
"""
if path.is_file():
return [ln.strip() for ln in path.read_text().splitlines() if ln.strip()]
return []
# -----------------------------------------------------------------------------
# 1. Model loading (stub)
# -----------------------------------------------------------------------------
@st.cache_resource(show_spinner="Loading draft model β¦")
def load_model():
"""Load and return the trained drafting model.
Adapt to your own pipeline; see previous revision for an example that pulls
an artefact from *huggingface_hub*. Returning *None* leaves us in demo
mode and will pick random cards.
"""
return None
model = load_model()
# -----------------------------------------------------------------------------
# 2. Draft-logic helpers (stubs)
# -----------------------------------------------------------------------------
def suggest_pick(pack: List[Dict], picks: List[Dict]) -> Dict:
"""Return the card the model recommends from *pack*."""
if model is None:
return random.choice(pack)
return model.predict(pack=pack, picks=picks) # type: ignore[attr-defined]
def fetch_card_image(card_name: str) -> str:
"""Fetch card art URL from Scryfall (normal size)."""
r = requests.get(
"https://api.scryfall.com/cards/named", params={"exact": card_name, "format": "json"}
)
r.raise_for_status()
data = r.json()
if "image_uris" in data:
return data["image_uris"]["normal"]
return data["card_faces"][0]["image_uris"]["normal"]
def generate_booster(set_code: str) -> List[Dict]:
"""Return a pseudo-random 15-card booster using Scryfall search."""
url = f"https://api.scryfall.com/cards/search?q=set%3A{set_code}+is%3Abooster+unique%3Aprints"
cards: List[Dict] = []
while url:
resp = requests.get(url)
resp.raise_for_status()
payload = resp.json()
cards += payload["data"]
url = payload.get("next_page") if payload.get("has_more") else None
return random.sample(cards, 15)
# -----------------------------------------------------------------------------
# 3. Streamlit UI
# -----------------------------------------------------------------------------
st.set_page_config(page_title="MTG Draft Assistant", page_icon="π")
st.title("π MTG Draft Assistant")
# ---------------- Sidebar -----------------------------------------------------
with st.sidebar:
st.header("Draft setup")
supported_sets = get_supported_sets()
if supported_sets:
set_code = st.radio("Choose a set to draft", supported_sets, index=0)
else:
st.warning(
"*supported_sets.txt* not found or empty. Using free-text input instead.",
icon="β οΈ",
)
set_code = st.text_input("Set code", value="WOE")
if st.button("Start new draft", type="primary"):
st.session_state["pack"] = generate_booster(set_code)
st.session_state["picks"] = []
# ---------------- Session state guards ---------------------------------------
st.session_state.setdefault("pack", [])
st.session_state.setdefault("picks", [])
if not st.session_state["pack"]:
st.info("Choose **Start new draft** in the sidebar to open pack 1.")
st.stop()
pack: List[Dict] = st.session_state["pack"]
picks: List[Dict] = st.session_state["picks"]
st.subheader(f"Pack {len(picks) // 15 + 1} β Pick {len(picks) % 15 + 1}")
suggested = suggest_pick(pack, picks)
st.success(f"**Model suggests:** {suggested['name']}")
# Display current pack as a 5-column grid of card images
cols = st.columns(5)
for idx, card in enumerate(pack):
col = cols[idx % 5]
col.image(fetch_card_image(card["name"]), use_column_width=True)
if col.button(f"Pick {card['name']}", key=f"pick-{idx}"):
picks.append(card)
pack.remove(card)
if not pack: # end of pack β open a fresh booster
st.session_state["pack"] = generate_booster(set_code)
st.experimental_rerun()
with st.expander("Current picks", expanded=False):
st.write("\n".join([c["name"] for c in picks]))
|