Timo commited on
Commit
c8f1b74
·
1 Parent(s): 971af66
src/helper_files/card_encodings_fin_llama_3_8b_embedding_normalized.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:dd419abd0dd8ff7620ff8c71e3623722e772e6b4d245634d2e8e80907c172145
3
+ size 279375708
src/helper_files/supported_sets.txt ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ DFT
2
+ PIO
3
+ FDN
4
+ DSK
5
+ BLB
6
+ MH3
7
+ OTJ
8
+ M19
9
+ SIR
10
+ VOW
11
+ ONE
12
+ LCI
13
+ MOM
14
+ MID
15
+ MKM
16
+ LTR
17
+ DMU
18
+ WOE
19
+ KTK
20
+ BRO
21
+ AFR
22
+ NEO
23
+ SNC
24
+ HBG
25
+ STX
src/streamlit_app.py CHANGED
@@ -1,115 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
  import random
3
- from typing import List, Dict
 
4
 
 
5
  import streamlit as st
6
 
7
  # -----------------------------------------------------------------------------
8
- # 1. Model loading
9
  # -----------------------------------------------------------------------------
10
 
11
- @st.cache_resource(show_spinner="Loading draft model…")
12
- def load_model():
13
- """Load and return your trained drafting model.
14
 
15
- Adapt this stub to your own ML pipeline. For example, if you have pushed a
16
- `draft_model.joblib` artifact to the Hub as `username/mtg-draft-model`, you
17
- could:
18
 
19
- from huggingface_hub import hf_hub_download
20
- import joblib, os
21
 
22
- local = hf_hub_download(
23
- repo_id="username/mtg-draft-model",
24
- filename="draft_model.joblib",
25
- token=os.getenv("HF_TOKEN"), # optional for private repos
26
- )
27
- return joblib.load(local)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  """
29
- return None # fallback: no-op model for demo purposes
30
 
31
  model = load_model()
32
 
33
  # -----------------------------------------------------------------------------
34
- # 2. Helper functions
35
  # -----------------------------------------------------------------------------
36
 
37
  def suggest_pick(pack: List[Dict], picks: List[Dict]) -> Dict:
38
- """Return the card dict the model recommends from the current pack."""
39
  if model is None:
40
  return random.choice(pack)
41
- # 🔄 Replace with your model's inference API
42
- return model.predict(pack=pack, picks=picks)
43
 
44
 
45
  def fetch_card_image(card_name: str) -> str:
46
- """Return a medium‑sized card image URL from the Scryfall API."""
47
- import requests
48
-
49
- response = requests.get(
50
- "https://api.scryfall.com/cards/named",
51
- params={"exact": card_name, "format": "json"},
52
  )
53
- response.raise_for_status()
54
- data = response.json()
55
- # multi‑faced cards may not have `image_uris`; fall back to the first face
56
  if "image_uris" in data:
57
  return data["image_uris"]["normal"]
58
  return data["card_faces"][0]["image_uris"]["normal"]
59
 
60
 
61
  def generate_booster(set_code: str) -> List[Dict]:
62
- """Very small demo booster generator using Scryfall search."""
63
- import requests
64
-
65
- url = (
66
- f"https://api.scryfall.com/cards/search?q=set%3A{set_code}+is%3Abooster+unique%3Aprints"
67
- )
68
  cards: List[Dict] = []
69
  while url:
70
- r = requests.get(url)
71
- r.raise_for_status()
72
- payload = r.json()
73
- cards.extend(payload["data"])
74
  url = payload.get("next_page") if payload.get("has_more") else None
75
  return random.sample(cards, 15)
76
 
77
-
78
  # -----------------------------------------------------------------------------
79
  # 3. Streamlit UI
80
  # -----------------------------------------------------------------------------
81
 
82
- st.set_page_config(
83
- page_title="MTG Draft Assistant",
84
- page_icon="🃏",
85
- )
86
 
87
  st.title("🃏 MTG Draft Assistant")
88
- st.caption("A minimal booster‑draft helper that runs entirely client‑side.")
 
89
 
90
  with st.sidebar:
91
  st.header("Draft setup")
92
- set_code = st.text_input("Set code (e.g. WOE, LCI, MKM)", value="WOE")
93
- if st.button("Start new draft"):
 
 
 
 
 
 
 
 
 
 
 
94
  st.session_state["pack"] = generate_booster(set_code)
95
  st.session_state["picks"] = []
96
 
97
- # Create session variables on first run
 
98
  st.session_state.setdefault("pack", [])
99
  st.session_state.setdefault("picks", [])
100
 
101
  if not st.session_state["pack"]:
102
- st.info("Choose **Start new draft** in the sidebar to open pack 1.")
103
  st.stop()
104
 
105
- pack = st.session_state["pack"]
106
- picks = st.session_state["picks"]
107
 
108
- st.subheader(f"Pack {len(picks)//15 + 1} — Pick {len(picks) % 15 + 1}")
109
  suggested = suggest_pick(pack, picks)
 
110
  st.success(f"**Model suggests:** {suggested['name']}")
111
 
112
- # Display pack as a grid of card images
113
  cols = st.columns(5)
114
  for idx, card in enumerate(pack):
115
  col = cols[idx % 5]
@@ -123,4 +158,3 @@ for idx, card in enumerate(pack):
123
 
124
  with st.expander("Current picks", expanded=False):
125
  st.write("\n".join([c["name"] for c in picks]))
126
-
 
1
+ """
2
+ MTG Draft Assistant Streamlit App
3
+ ---------------------------------
4
+ A booster-draft helper written with Streamlit and deployable on Hugging Face Spaces.
5
+
6
+ Changes in this revision
7
+ ~~~~~~~~~~~~~~~~~~~~~~~~
8
+ * **supported_sets.txt** – one set code per line.
9
+ * Sidebar now shows a **single-choice list** (radio buttons) sourced from that
10
+ file instead of a free-text box, so users can only draft sets you actually
11
+ support.
12
+ * Fallback to the old text input if the text-file is missing or empty.
13
+
14
+ Replace the three stub functions (`load_model`, `suggest_pick`,
15
+ `generate_booster`) with your real model-/API-calls when you are ready.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
  import os
21
  import random
22
+ from pathlib import Path
23
+ from typing import Dict, List
24
 
25
+ import requests
26
  import streamlit as st
27
 
28
  # -----------------------------------------------------------------------------
29
+ # 0. Constants & helpers
30
  # -----------------------------------------------------------------------------
31
 
32
+ SUPPORTED_SETS_PATH = Path("supported_sets.txt")
 
 
33
 
34
+ @st.cache_data(show_spinner="Reading supported sets …")
35
+ def get_supported_sets(path: Path = SUPPORTED_SETS_PATH) -> List[str]:
36
+ """Return a list of legal set codes read from *supported_sets.txt*.
37
 
38
+ The file should contain **one set tag per line**, e.g.::
 
39
 
40
+ WOE
41
+ LCI
42
+ MKM
43
+
44
+ If the file is missing we fall back to an empty list so the UI
45
+ degrades gracefully.
46
+ """
47
+ if path.is_file():
48
+ return [ln.strip() for ln in path.read_text().splitlines() if ln.strip()]
49
+ return []
50
+
51
+ # -----------------------------------------------------------------------------
52
+ # 1. Model loading (stub)
53
+ # -----------------------------------------------------------------------------
54
+
55
+ @st.cache_resource(show_spinner="Loading draft model …")
56
+ def load_model():
57
+ """Load and return the trained drafting model.
58
+
59
+ Adapt to your own pipeline; see previous revision for an example that pulls
60
+ an artefact from *huggingface_hub*. Returning *None* leaves us in demo
61
+ mode and will pick random cards.
62
  """
63
+ return None
64
 
65
  model = load_model()
66
 
67
  # -----------------------------------------------------------------------------
68
+ # 2. Draft-logic helpers (stubs)
69
  # -----------------------------------------------------------------------------
70
 
71
  def suggest_pick(pack: List[Dict], picks: List[Dict]) -> Dict:
72
+ """Return the card the model recommends from *pack*."""
73
  if model is None:
74
  return random.choice(pack)
75
+ return model.predict(pack=pack, picks=picks) # type: ignore[attr-defined]
 
76
 
77
 
78
  def fetch_card_image(card_name: str) -> str:
79
+ """Fetch card art URL from Scryfall (normal size)."""
80
+ r = requests.get(
81
+ "https://api.scryfall.com/cards/named", params={"exact": card_name, "format": "json"}
 
 
 
82
  )
83
+ r.raise_for_status()
84
+ data = r.json()
 
85
  if "image_uris" in data:
86
  return data["image_uris"]["normal"]
87
  return data["card_faces"][0]["image_uris"]["normal"]
88
 
89
 
90
  def generate_booster(set_code: str) -> List[Dict]:
91
+ """Return a pseudo-random 15-card booster using Scryfall search."""
92
+ url = f"https://api.scryfall.com/cards/search?q=set%3A{set_code}+is%3Abooster+unique%3Aprints"
 
 
 
 
93
  cards: List[Dict] = []
94
  while url:
95
+ resp = requests.get(url)
96
+ resp.raise_for_status()
97
+ payload = resp.json()
98
+ cards += payload["data"]
99
  url = payload.get("next_page") if payload.get("has_more") else None
100
  return random.sample(cards, 15)
101
 
 
102
  # -----------------------------------------------------------------------------
103
  # 3. Streamlit UI
104
  # -----------------------------------------------------------------------------
105
 
106
+ st.set_page_config(page_title="MTG Draft Assistant", page_icon="🃏")
 
 
 
107
 
108
  st.title("🃏 MTG Draft Assistant")
109
+
110
+ # ---------------- Sidebar -----------------------------------------------------
111
 
112
  with st.sidebar:
113
  st.header("Draft setup")
114
+
115
+ supported_sets = get_supported_sets()
116
+
117
+ if supported_sets:
118
+ set_code = st.radio("Choose a set to draft", supported_sets, index=0)
119
+ else:
120
+ st.warning(
121
+ "*supported_sets.txt* not found or empty. Using free-text input instead.",
122
+ icon="⚠️",
123
+ )
124
+ set_code = st.text_input("Set code", value="WOE")
125
+
126
+ if st.button("Start new draft", type="primary"):
127
  st.session_state["pack"] = generate_booster(set_code)
128
  st.session_state["picks"] = []
129
 
130
+ # ---------------- Session state guards ---------------------------------------
131
+
132
  st.session_state.setdefault("pack", [])
133
  st.session_state.setdefault("picks", [])
134
 
135
  if not st.session_state["pack"]:
136
+ st.info("Choose **Start new draft** in the sidebar to open pack 1.")
137
  st.stop()
138
 
139
+ pack: List[Dict] = st.session_state["pack"]
140
+ picks: List[Dict] = st.session_state["picks"]
141
 
142
+ st.subheader(f"Pack {len(picks) // 15 + 1} — Pick {len(picks) % 15 + 1}")
143
  suggested = suggest_pick(pack, picks)
144
+
145
  st.success(f"**Model suggests:** {suggested['name']}")
146
 
147
+ # Display current pack as a 5-column grid of card images
148
  cols = st.columns(5)
149
  for idx, card in enumerate(pack):
150
  col = cols[idx % 5]
 
158
 
159
  with st.expander("Current picks", expanded=False):
160
  st.write("\n".join([c["name"] for c in picks]))