mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2025-12-17 08:00:13 +01:00
feat(web,docs): visual summaries (curve, pips/sources incl. 'C', non‑land sources), tooltip copy, favicon; diagnostics (/healthz, request‑id, global handlers); fetches excluded, basics CSV fallback, list highlight polish; README/DOCKER/release-notes/CHANGELOG updated
This commit is contained in:
parent
625f6abb13
commit
8d1f6a8ac4
27 changed files with 1704 additions and 154 deletions
|
|
@ -66,13 +66,15 @@ def normalize_theme_list(raw) -> list[str]:
|
|||
|
||||
|
||||
def compute_color_source_matrix(card_library: Dict[str, dict], full_df) -> Dict[str, Dict[str, int]]:
|
||||
"""Build a matrix mapping land name -> {color: 0/1} indicating if that land
|
||||
can (reliably) produce each color.
|
||||
"""Build a matrix mapping card name -> {color: 0/1} indicating if that card
|
||||
can (reliably) produce each color of mana on the battlefield.
|
||||
|
||||
Heuristics:
|
||||
- Presence of basic land types in type line grants that color.
|
||||
- Text containing "add one mana of any color/colour" grants all colors.
|
||||
- Explicit mana symbols in rules text (e.g. "{R}") grant that color.
|
||||
Notes:
|
||||
- Includes lands and non-lands (artifacts/creatures/enchantments/planeswalkers) that produce mana.
|
||||
- Excludes instants/sorceries (rituals) by design; this is a "source" count, not ramp burst.
|
||||
- Any-color effects set W/U/B/R/G (not C). Colorless '{C}' is tracked separately.
|
||||
- For lands, we also infer from basic land types in the type line. For non-lands, we rely on text.
|
||||
- Fallback name mapping applies only to exact basic lands (incl. Snow-Covered) and Wastes.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
|
@ -89,29 +91,84 @@ def compute_color_source_matrix(card_library: Dict[str, dict], full_df) -> Dict[
|
|||
if nm and nm not in lookup:
|
||||
lookup[nm] = r
|
||||
for name, entry in card_library.items():
|
||||
if 'land' not in str(entry.get('Card Type', '')).lower():
|
||||
continue
|
||||
row = lookup.get(name, {})
|
||||
tline = str(row.get('type', row.get('type_line', ''))).lower()
|
||||
text_field = str(row.get('text', row.get('oracleText', ''))).lower()
|
||||
colors = {c: 0 for c in COLOR_LETTERS}
|
||||
if 'plains' in tline:
|
||||
colors['W'] = 1
|
||||
if 'island' in tline:
|
||||
colors['U'] = 1
|
||||
if 'swamp' in tline:
|
||||
colors['B'] = 1
|
||||
if 'mountain' in tline:
|
||||
colors['R'] = 1
|
||||
if 'forest' in tline:
|
||||
colors['G'] = 1
|
||||
if 'add one mana of any color' in text_field or 'add one mana of any colour' in text_field:
|
||||
for k in colors:
|
||||
entry_type = str(entry.get('Card Type') or entry.get('Type') or '').lower()
|
||||
tline_full = str(row.get('type', row.get('type_line', '')) or '').lower()
|
||||
# Land or permanent that could produce mana via text
|
||||
is_land = ('land' in entry_type) or ('land' in tline_full)
|
||||
text_field = str(row.get('text', row.get('oracleText', '')) or '').lower()
|
||||
# Skip obvious non-permanents (rituals etc.)
|
||||
if (not is_land) and ('instant' in entry_type or 'sorcery' in entry_type or 'instant' in tline_full or 'sorcery' in tline_full):
|
||||
continue
|
||||
# Keep only candidates that are lands OR whose text indicates mana production
|
||||
produces_from_text = False
|
||||
tf = text_field
|
||||
if tf:
|
||||
# Common patterns: "Add {G}", "Add {C}{C}", "Add one mana of any color/colour"
|
||||
produces_from_text = (
|
||||
('add one mana of any color' in tf) or
|
||||
('add one mana of any colour' in tf) or
|
||||
('add ' in tf and ('{w}' in tf or '{u}' in tf or '{b}' in tf or '{r}' in tf or '{g}' in tf or '{c}' in tf))
|
||||
)
|
||||
if not (is_land or produces_from_text):
|
||||
continue
|
||||
# Combine entry type and snapshot type line for robust parsing
|
||||
tline = (entry_type + ' ' + tline_full).strip()
|
||||
colors = {c: 0 for c in (COLOR_LETTERS + ['C'])}
|
||||
# Land type-based inference
|
||||
if is_land:
|
||||
if 'plains' in tline:
|
||||
colors['W'] = 1
|
||||
if 'island' in tline:
|
||||
colors['U'] = 1
|
||||
if 'swamp' in tline:
|
||||
colors['B'] = 1
|
||||
if 'mountain' in tline:
|
||||
colors['R'] = 1
|
||||
if 'forest' in tline:
|
||||
colors['G'] = 1
|
||||
# Text-based inference for both lands and non-lands
|
||||
if (
|
||||
'add one mana of any color' in tf or
|
||||
'add one mana of any colour' in tf or
|
||||
('add' in tf and ('mana of any color' in tf or 'mana of any one color' in tf or 'any color of mana' in tf))
|
||||
):
|
||||
for k in COLOR_LETTERS:
|
||||
colors[k] = 1
|
||||
for sym, c in [(' {w}', 'W'), (' {u}', 'U'), (' {b}', 'B'), (' {r}', 'R'), (' {g}', 'G')]:
|
||||
if sym in text_field:
|
||||
colors[c] = 1
|
||||
matrix[name] = colors
|
||||
# Explicit colored/colorless symbols in add context
|
||||
if 'add' in tf:
|
||||
if '{w}' in tf:
|
||||
colors['W'] = 1
|
||||
if '{u}' in tf:
|
||||
colors['U'] = 1
|
||||
if '{b}' in tf:
|
||||
colors['B'] = 1
|
||||
if '{r}' in tf:
|
||||
colors['R'] = 1
|
||||
if '{g}' in tf:
|
||||
colors['G'] = 1
|
||||
if '{c}' in tf or 'colorless' in tf:
|
||||
colors['C'] = 1
|
||||
# Fallback: infer only for exact basic land names (incl. Snow-Covered) and Wastes
|
||||
if not any(colors.values()) and is_land:
|
||||
nm = str(name)
|
||||
base = nm
|
||||
if nm.startswith('Snow-Covered '):
|
||||
base = nm[len('Snow-Covered '):]
|
||||
mapping = {
|
||||
'Plains': 'W',
|
||||
'Island': 'U',
|
||||
'Swamp': 'B',
|
||||
'Mountain': 'R',
|
||||
'Forest': 'G',
|
||||
'Wastes': 'C',
|
||||
}
|
||||
col = mapping.get(base)
|
||||
if col:
|
||||
colors[col] = 1
|
||||
# Only include cards that produced at least one color
|
||||
if any(colors.values()):
|
||||
matrix[name] = colors
|
||||
return matrix
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue