mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2025-12-17 08:00:13 +01:00
Consolidated constants that are duplicated in various constant files. Implemented dataclass methodoligies for tagging counter-related cards tagging
This commit is contained in:
parent
27ee13fb54
commit
039b8fe89e
6 changed files with 228 additions and 125 deletions
|
|
@ -1,4 +1,9 @@
|
||||||
from typing import Dict, List, Optional, Final, Tuple, Pattern, Union, Callable
|
from typing import Dict, List, Final, Tuple, Union, Callable
|
||||||
|
from settings import CARD_DATA_COLUMNS as CSV_REQUIRED_COLUMNS # unified
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'CSV_REQUIRED_COLUMNS'
|
||||||
|
]
|
||||||
import ast
|
import ast
|
||||||
|
|
||||||
# Commander selection configuration
|
# Commander selection configuration
|
||||||
|
|
@ -390,12 +395,7 @@ CSV_VALIDATION_RULES: Final[Dict[str, Dict[str, Union[str, int, float]]]] = {
|
||||||
'toughness': {'type': ('str', 'int', 'float', 'object'), 'pattern': r'^[\d*+-]+$'}
|
'toughness': {'type': ('str', 'int', 'float', 'object'), 'pattern': r'^[\d*+-]+$'}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Required columns for CSV validation
|
# (CSV_REQUIRED_COLUMNS imported from settings to avoid duplication)
|
||||||
CSV_REQUIRED_COLUMNS: Final[List[str]] = [
|
|
||||||
'name', 'faceName', 'edhrecRank', 'colorIdentity', 'colors',
|
|
||||||
'manaCost', 'manaValue', 'type', 'creatureTypes', 'text',
|
|
||||||
'power', 'toughness', 'keywords', 'themeTags', 'layout', 'side'
|
|
||||||
]
|
|
||||||
|
|
||||||
# DataFrame processing configuration
|
# DataFrame processing configuration
|
||||||
BATCH_SIZE: Final[int] = 1000 # Number of records to process at once
|
BATCH_SIZE: Final[int] = 1000 # Number of records to process at once
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,40 @@
|
||||||
from typing import Dict, List, Optional, Final, Tuple, Pattern, Union, Callable
|
from typing import Dict, List
|
||||||
|
from settings import (
|
||||||
|
SETUP_COLORS,
|
||||||
|
COLOR_ABRV,
|
||||||
|
CARD_DATA_COLUMNS as COLUMN_ORDER, # backward compatible alias
|
||||||
|
CARD_DATA_COLUMNS as TAGGED_COLUMN_ORDER,
|
||||||
|
)
|
||||||
|
|
||||||
BANNED_CARDS: List[str] = [# in commander
|
__all__ = [
|
||||||
'Ancestral Recall', 'Balance', 'Biorhythm', 'Black Lotus',
|
'SETUP_COLORS', 'COLOR_ABRV', 'COLUMN_ORDER', 'TAGGED_COLUMN_ORDER',
|
||||||
'Braids, Cabal Minion', 'Chaos Orb', 'Coalition Victory',
|
'BANNED_CARDS', 'MTGJSON_API_URL', 'LEGENDARY_OPTIONS', 'NON_LEGAL_SETS',
|
||||||
'Channel', 'Dockside Extortionist', 'Emrakul, the Aeons Torn',
|
'CARD_TYPES_TO_EXCLUDE', 'CSV_PROCESSING_COLUMNS', 'SORT_CONFIG',
|
||||||
'Erayo, Soratami Ascendant', 'Falling Star', 'Fastbond',
|
'FILTER_CONFIG'
|
||||||
'Flash', 'Gifts Ungiven', 'Golos, Tireless Pilgrim',
|
]
|
||||||
'Griselbrand', 'Hullbreacher', 'Iona, Shield of Emeria',
|
|
||||||
'Karakas', 'Jeweled Lotus', 'Leovold, Emissary of Trest',
|
|
||||||
'Library of Alexandria', 'Limited Resources', 'Lutri, the Spellchaser',
|
|
||||||
'Mana Crypt', 'Mox Emerald', 'Mox Jet', 'Mox Pearl', 'Mox Ruby',
|
|
||||||
'Mox Sapphire', 'Nadu, Winged Wisdom', 'Panoptic Mirror',
|
|
||||||
'Paradox Engine', 'Primeval Titan', 'Prophet of Kruphix',
|
|
||||||
'Recurring Nightmare', 'Rofellos, Llanowar Emissary', 'Shahrazad',
|
|
||||||
'Sundering Titan', 'Sway of the Stars', 'Sylvan Primordial',
|
|
||||||
'Time Vault', 'Time Walk', 'Tinker', 'Tolarian Academy',
|
|
||||||
'Trade Secrets', 'Upheaval', 'Yawgmoth\'s Bargain',
|
|
||||||
|
|
||||||
# In constructed
|
# Banned cards consolidated here (remains specific to setup concerns)
|
||||||
'Invoke Prejudice', 'Cleanse', 'Stone-Throwing Devils', 'Pradesh Gypsies',
|
BANNED_CARDS: List[str] = [
|
||||||
'Jihad', 'Imprison', 'Crusade'
|
# Commander banned list
|
||||||
]
|
'Ancestral Recall', 'Balance', 'Biorhythm', 'Black Lotus',
|
||||||
|
'Braids, Cabal Minion', 'Chaos Orb', 'Coalition Victory',
|
||||||
SETUP_COLORS: List[str] = ['colorless', 'white', 'blue', 'black', 'green', 'red',
|
'Channel', 'Dockside Extortionist', 'Emrakul, the Aeons Torn',
|
||||||
'azorius', 'orzhov', 'selesnya', 'boros', 'dimir',
|
'Erayo, Soratami Ascendant', 'Falling Star', 'Fastbond',
|
||||||
'simic', 'izzet', 'golgari', 'rakdos', 'gruul',
|
'Flash', 'Gifts Ungiven', 'Golos, Tireless Pilgrim',
|
||||||
'bant', 'esper', 'grixis', 'jund', 'naya',
|
'Griselbrand', 'Hullbreacher', 'Iona, Shield of Emeria',
|
||||||
'abzan', 'jeskai', 'mardu', 'sultai', 'temur',
|
'Karakas', 'Jeweled Lotus', 'Leovold, Emissary of Trest',
|
||||||
'dune', 'glint', 'ink', 'witch', 'yore', 'wubrg']
|
'Library of Alexandria', 'Limited Resources', 'Lutri, the Spellchaser',
|
||||||
|
'Mana Crypt', 'Mox Emerald', 'Mox Jet', 'Mox Pearl', 'Mox Ruby',
|
||||||
COLOR_ABRV: List[str] = ['Colorless', 'W', 'U', 'B', 'G', 'R',
|
'Mox Sapphire', 'Nadu, Winged Wisdom', 'Panoptic Mirror',
|
||||||
'U, W', 'B, W', 'G, W', 'R, W', 'B, U',
|
'Paradox Engine', 'Primeval Titan', 'Prophet of Kruphix',
|
||||||
'G, U', 'R, U', 'B, G', 'B, R', 'G, R',
|
'Recurring Nightmare', 'Rofellos, Llanowar Emissary', 'Shahrazad',
|
||||||
'G, U, W', 'B, U, W', 'B, R, U', 'B, G, R', 'G, R, W',
|
'Sundering Titan', 'Sway of the Stars', 'Sylvan Primordial',
|
||||||
'B, G, W', 'R, U, W', 'B, R, W', 'B, G, U', 'G, R, U',
|
'Time Vault', 'Time Walk', 'Tinker', 'Tolarian Academy',
|
||||||
'B, G, R, W', 'B, G, R, U', 'G, R, U, W', 'B, G, U, W',
|
'Trade Secrets', 'Upheaval', "Yawgmoth's Bargain",
|
||||||
'B, R, U, W', 'B, G, R, U, W']
|
# Problematic / culturally sensitive or banned in other formats
|
||||||
|
'Invoke Prejudice', 'Cleanse', 'Stone-Throwing Devils', 'Pradesh Gypsies',
|
||||||
|
'Jihad', 'Imprison', 'Crusade'
|
||||||
|
]
|
||||||
|
|
||||||
# Constants for setup and CSV processing
|
# Constants for setup and CSV processing
|
||||||
MTGJSON_API_URL: str = 'https://mtgjson.com/api/v5/csv/cards.csv'
|
MTGJSON_API_URL: str = 'https://mtgjson.com/api/v5/csv/cards.csv'
|
||||||
|
|
@ -105,14 +104,4 @@ FILTER_CONFIG: Dict[str, Dict[str, List[str]]] = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
COLUMN_ORDER: List[str] = [
|
# COLUMN_ORDER and TAGGED_COLUMN_ORDER now sourced from settings via CARD_DATA_COLUMNS
|
||||||
'name', 'faceName', 'edhrecRank', 'colorIdentity', 'colors',
|
|
||||||
'manaCost', 'manaValue', 'type', 'creatureTypes', 'text',
|
|
||||||
'power', 'toughness', 'keywords', 'themeTags', 'layout', 'side'
|
|
||||||
]
|
|
||||||
|
|
||||||
TAGGED_COLUMN_ORDER: List[str] = [
|
|
||||||
'name', 'faceName', 'edhrecRank', 'colorIdentity', 'colors',
|
|
||||||
'manaCost', 'manaValue', 'type', 'creatureTypes', 'text',
|
|
||||||
'power', 'toughness', 'keywords', 'themeTags', 'layout', 'side'
|
|
||||||
]
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from settings import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
# Create logs directory if it doesn't exist
|
# Create logs directory if it doesn't exist
|
||||||
|
|
@ -27,3 +27,12 @@ file_handler.setFormatter(NoDunderFormatter(LOG_FORMAT))
|
||||||
# Stream handler
|
# Stream handler
|
||||||
stream_handler = logging.StreamHandler()
|
stream_handler = logging.StreamHandler()
|
||||||
stream_handler.setFormatter(NoDunderFormatter(LOG_FORMAT))
|
stream_handler.setFormatter(NoDunderFormatter(LOG_FORMAT))
|
||||||
|
|
||||||
|
# Root logger assembly helper (idempotent)
|
||||||
|
def get_logger(name: str = 'deck_builder') -> logging.Logger:
|
||||||
|
logger = logging.getLogger(name)
|
||||||
|
if not logger.handlers:
|
||||||
|
logger.setLevel(LOG_LEVEL)
|
||||||
|
logger.addHandler(file_handler)
|
||||||
|
logger.addHandler(stream_handler)
|
||||||
|
return logger
|
||||||
103
code/settings.py
103
code/settings.py
|
|
@ -1,27 +1,94 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
# Standard library imports
|
# Standard library imports
|
||||||
import os
|
from typing import Dict, List, Optional
|
||||||
from sys import exit
|
|
||||||
from typing import Dict, List, Optional, Final, Tuple, Pattern, Union, Callable
|
|
||||||
|
|
||||||
# Third-party imports
|
# ----------------------------------------------------------------------------------
|
||||||
|
# COLOR CONSTANTS
|
||||||
|
# ----------------------------------------------------------------------------------
|
||||||
|
# NOTE:
|
||||||
|
# Existing code in setup uses an ordered list (green before red) to align indices
|
||||||
|
# with the parallel COLOR_ABRV list. The previously defined COLORS list had a
|
||||||
|
# different ordering (red before green) which made it unsuitable for index based
|
||||||
|
# mapping. To avoid subtle bugs we expose two explicit constants:
|
||||||
|
# SETUP_COLORS -> ordering required for setup / abbreviation mapping
|
||||||
|
# COLORS -> legacy superset including 'commander' (kept for compatibility)
|
||||||
|
|
||||||
COLORS = ['colorless', 'white', 'blue', 'black', 'red', 'green',
|
SETUP_COLORS: List[str] = [
|
||||||
'azorius', 'orzhov', 'selesnya', 'boros', 'dimir',
|
'colorless', 'white', 'blue', 'black', 'green', 'red',
|
||||||
'simic', 'izzet', 'golgari', 'rakdos', 'gruul',
|
'azorius', 'orzhov', 'selesnya', 'boros', 'dimir',
|
||||||
'bant', 'esper', 'grixis', 'jund', 'naya',
|
'simic', 'izzet', 'golgari', 'rakdos', 'gruul',
|
||||||
'abzan', 'jeskai', 'mardu', 'sultai', 'temur',
|
'bant', 'esper', 'grixis', 'jund', 'naya',
|
||||||
'dune', 'glint', 'ink', 'witch', 'yore', 'wubrg',
|
'abzan', 'jeskai', 'mardu', 'sultai', 'temur',
|
||||||
'commander']
|
'dune', 'glint', 'ink', 'witch', 'yore', 'wubrg'
|
||||||
|
]
|
||||||
|
|
||||||
COLOR_ABRV: List[str] = ['Colorless', 'W', 'U', 'B', 'G', 'R',
|
# Legacy constant (includes 'commander', preserves previous external usage)
|
||||||
'U, W', 'B, W', 'G, W', 'R, W', 'B, U',
|
COLORS: List[str] = [
|
||||||
'G, U', 'R, U', 'B, G', 'B, R', 'G, R',
|
*SETUP_COLORS,
|
||||||
'G, U, W', 'B, U, W', 'B, R, U', 'B, G, R', 'G, R, W',
|
'commander'
|
||||||
'B, G, W', 'R, U, W', 'B, R, W', 'B, G, U', 'G, R, U',
|
]
|
||||||
'B, G, R, W', 'B, G, R, U', 'G, R, U, W', 'B, G, U, W',
|
|
||||||
'B, R, U, W', 'B, G, R, U, W']
|
COLOR_ABRV: List[str] = [
|
||||||
|
'Colorless', 'W', 'U', 'B', 'G', 'R',
|
||||||
|
'U, W', 'B, W', 'G, W', 'R, W', 'B, U',
|
||||||
|
'G, U', 'R, U', 'B, G', 'B, R', 'G, R',
|
||||||
|
'G, U, W', 'B, U, W', 'B, R, U', 'B, G, R', 'G, R, W',
|
||||||
|
'B, G, W', 'R, U, W', 'B, R, W', 'B, G, U', 'G, R, U',
|
||||||
|
'B, G, R, W', 'B, G, R, U', 'G, R, U, W', 'B, G, U, W',
|
||||||
|
'B, R, U, W', 'B, G, R, U, W'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Convenience mapping from long color name to primary abbreviation
|
||||||
|
PRIMARY_COLOR_ABBR_MAP: Dict[str, str] = {
|
||||||
|
'colorless': 'Colorless', 'white': 'W', 'blue': 'U', 'black': 'B', 'green': 'G', 'red': 'R'
|
||||||
|
}
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------------
|
||||||
|
# CARD / DATAFRAME COLUMN CONSTANTS
|
||||||
|
# ----------------------------------------------------------------------------------
|
||||||
|
# Unified column definition used across setup, tagging, and deck building modules.
|
||||||
|
# This consolidates previously duplicated lists: COLUMN_ORDER, TAGGED_COLUMN_ORDER,
|
||||||
|
# REQUIRED_COLUMNS (tag_constants), and CSV_REQUIRED_COLUMNS (builder_constants).
|
||||||
|
CARD_DATA_COLUMNS: List[str] = [
|
||||||
|
'name', 'faceName', 'edhrecRank', 'colorIdentity', 'colors',
|
||||||
|
'manaCost', 'manaValue', 'type', 'creatureTypes', 'text',
|
||||||
|
'power', 'toughness', 'keywords', 'themeTags', 'layout', 'side'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Alias for semantic clarity in different contexts
|
||||||
|
REQUIRED_CARD_COLUMNS = CARD_DATA_COLUMNS # Validation
|
||||||
|
CARD_COLUMN_ORDER = CARD_DATA_COLUMNS # Output / ordering
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------------
|
||||||
|
# MENU / UI CONSTANTS
|
||||||
|
# ----------------------------------------------------------------------------------
|
||||||
|
MAIN_MENU_ITEMS: List[str] = ['Build A Deck', 'Setup CSV Files', 'Tag CSV Files', 'Quit']
|
||||||
|
SETUP_MENU_ITEMS: List[str] = ['Initial Setup', 'Regenerate CSV', 'Main Menu']
|
||||||
|
|
||||||
|
CSV_DIRECTORY: str = 'csv_files'
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------------
|
||||||
|
# DATAFRAME NA HANDLING
|
||||||
|
# ----------------------------------------------------------------------------------
|
||||||
|
FILL_NA_COLUMNS: Dict[str, Optional[str]] = {
|
||||||
|
'colorIdentity': 'Colorless', # Default color identity for cards without one
|
||||||
|
'faceName': None # Use card's name column value when face name is not available
|
||||||
|
}
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------------
|
||||||
|
# SPECIAL CARD EXCEPTIONS
|
||||||
|
# ----------------------------------------------------------------------------------
|
||||||
|
MULTIPLE_COPY_CARDS = [
|
||||||
|
'Dragon\'s Approach', 'Hare Apparent', 'Nazgûl', 'Persistent Petitioners',
|
||||||
|
'Rat Colony', 'Relentless Rats', 'Seven Dwarves', 'Shadowborn Apostle',
|
||||||
|
'Slime Against Humanity', 'Templar Knight'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Backwards compatibility exports (older modules may still import these names)
|
||||||
|
COLUMN_ORDER = CARD_COLUMN_ORDER
|
||||||
|
TAGGED_COLUMN_ORDER = CARD_COLUMN_ORDER
|
||||||
|
REQUIRED_COLUMNS = REQUIRED_CARD_COLUMNS
|
||||||
|
|
||||||
MAIN_MENU_ITEMS: List[str] = ['Build A Deck', 'Setup CSV Files', 'Tag CSV Files', 'Quit']
|
MAIN_MENU_ITEMS: List[str] = ['Build A Deck', 'Setup CSV Files', 'Tag CSV Files', 'Quit']
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,16 @@
|
||||||
from typing import Dict, List, Final
|
from typing import Dict, List, Final, Iterable
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from settings import REQUIRED_CARD_COLUMNS as REQUIRED_COLUMNS # unified column list
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
'TRIGGERS', 'NUM_TO_SEARCH', 'TAG_GROUPS', 'PATTERN_GROUPS', 'PHRASE_GROUPS',
|
||||||
|
'CREATE_ACTION_PATTERN', 'COUNTER_TYPES', 'CREATURE_TYPES', 'NON_CREATURE_TYPES',
|
||||||
|
'OUTLAW_TYPES', 'ENCHANTMENT_TOKENS', 'ARTIFACT_TOKENS', 'REQUIRED_COLUMNS',
|
||||||
|
'TYPE_TAG_MAPPING', 'DRAW_RELATED_TAGS', 'DRAW_EXCLUSION_PATTERNS',
|
||||||
|
'EQUIPMENT_EXCLUSIONS', 'EQUIPMENT_SPECIFIC_CARDS', 'EQUIPMENT_RELATED_TAGS',
|
||||||
|
'EQUIPMENT_TEXT_PATTERNS', 'AURA_SPECIFIC_CARDS', 'VOLTRON_COMMANDER_CARDS',
|
||||||
|
'VOLTRON_PATTERNS'
|
||||||
|
]
|
||||||
|
|
||||||
TRIGGERS: List[str] = ['when', 'whenever', 'at']
|
TRIGGERS: List[str] = ['when', 'whenever', 'at']
|
||||||
|
|
||||||
|
|
@ -56,41 +68,75 @@ PHRASE_GROUPS: Dict[str, List[str]] = {
|
||||||
CREATE_ACTION_PATTERN: Final[str] = r"create|put"
|
CREATE_ACTION_PATTERN: Final[str] = r"create|put"
|
||||||
|
|
||||||
# Creature/Counter types
|
# Creature/Counter types
|
||||||
COUNTER_TYPES: List[str] = [r'\+0/\+1', r'\+0/\+2', r'\+1/\+0', r'\+1/\+2', r'\+2/\+0', r'\+2/\+2',
|
"""Counter type vocabularies."""
|
||||||
'-0/-1', '-0/-2', '-1/-0', '-1/-2', '-2/-0', '-2/-2',
|
|
||||||
'Acorn', 'Aegis', 'Age', 'Aim', 'Arrow', 'Arrowhead','Awakening',
|
# Power/Toughness modifier counters (regex fragments already escaped where needed)
|
||||||
'Bait', 'Blaze', 'Blessing', 'Blight',' Blood', 'Bloddline',
|
PT_COUNTER_TYPES: List[str] = [
|
||||||
'Bloodstain', 'Book', 'Bounty', 'Brain', 'Bribery', 'Brick',
|
r'\+0/\+1', r'\+0/\+2', r'\+1/\+0', r'\+1/\+2', r'\+2/\+0', r'\+2/\+2',
|
||||||
'Burden', 'Cage', 'Carrion', 'Charge', 'Coin', 'Collection',
|
'-0/-1', '-0/-2', '-1/-0', '-1/-2', '-2/-0', '-2/-2'
|
||||||
'Component', 'Contested', 'Corruption', 'CRANK!', 'Credit',
|
]
|
||||||
'Croak', 'Corpse', 'Crystal', 'Cube', 'Currency', 'Death',
|
|
||||||
'Defense', 'Delay', 'Depletion', 'Descent', 'Despair', 'Devotion',
|
# Named counters (alphabetical within rough thematic blocks)
|
||||||
'Divinity', 'Doom', 'Dream', 'Duty', 'Echo', 'Egg', 'Elixir',
|
NAMED_COUNTER_TYPES: List[str] = [
|
||||||
'Ember', 'Energy', 'Enlightened', 'Eon', 'Eruption', 'Everything',
|
'Acorn', 'Aegis', 'Age', 'Aim', 'Arrow', 'Arrowhead', 'Awakening',
|
||||||
'Experience', 'Eyeball', 'Eyestalk', 'Fade', 'Fate', 'Feather',
|
'Bait', 'Blaze', 'Blessing', 'Blight', 'Blood', 'Bloodline', 'Bloodstain', 'Book',
|
||||||
'Feeding', 'Fellowship', 'Fetch', 'Filibuster', 'Finality', 'Flame',
|
'Bounty', 'Brain', 'Bribery', 'Brick', 'Burden', 'Cage', 'Carrion', 'Charge', 'Coin',
|
||||||
'Flood', 'Foreshadow', 'Fungus', 'Fury', 'Fuse', 'Gem', 'Ghostform',
|
'Collection', 'Component', 'Contested', 'Corruption', 'CRANK!', 'Credit', 'Croak',
|
||||||
'Glpyh', 'Gold', 'Growth', 'Hack', 'Harmony', 'Hatching', 'Hatchling',
|
'Corpse', 'Crystal', 'Cube', 'Currency', 'Death', 'Defense', 'Delay', 'Depletion',
|
||||||
'Healing', 'Hit', 'Hope',' Hone', 'Hoofprint', 'Hour', 'Hourglass',
|
'Descent', 'Despair', 'Devotion', 'Divinity', 'Doom', 'Dream', 'Duty', 'Echo', 'Egg',
|
||||||
'Hunger', 'Ice', 'Imposter', 'Incarnation', 'Incubation', 'Infection',
|
'Elixir', 'Ember', 'Energy', 'Enlightened', 'Eon', 'Eruption', 'Everything',
|
||||||
'Influence', 'Ingenuity', 'Intel', 'Intervention', 'Invitation',
|
'Experience', 'Eyeball', 'Eyestalk', 'Fade', 'Fate', 'Feather', 'Feeding',
|
||||||
'Isolation', 'Javelin', 'Judgment', 'Keyword', 'Ki', 'Kick',
|
'Fellowship', 'Fetch', 'Filibuster', 'Finality', 'Flame', 'Flood', 'Foreshadow',
|
||||||
'Knickknack', 'Knowledge', 'Landmark', 'Level', 'Loot', 'Lore',
|
'Fungus', 'Fury', 'Fuse', 'Gem', 'Ghostform', 'Glyph', 'Gold', 'Growth', 'Hack',
|
||||||
'Loyalty', 'Luck', 'Magnet', 'Manabond', 'Manifestation', 'Mannequin',
|
'Harmony', 'Hatching', 'Hatchling', 'Healing', 'Hit', 'Hope', 'Hone', 'Hoofprint',
|
||||||
'Mask', 'Matrix', 'Memory', 'Midway', 'Mine', 'Mining', 'Mire',
|
'Hour', 'Hourglass', 'Hunger', 'Ice', 'Imposter', 'Incarnation', 'Incubation',
|
||||||
'Music', 'Muster', 'Necrodermis', 'Nest', 'Net', 'Night', 'Oil',
|
'Infection', 'Influence', 'Ingenuity', 'Intel', 'Intervention', 'Invitation',
|
||||||
'Omen', 'Ore', 'Page', 'Pain', 'Palliation', 'Paralyzing', 'Pause',
|
'Isolation', 'Javelin', 'Judgment', 'Keyword', 'Ki', 'Kick', 'Knickknack',
|
||||||
'Petal', 'Petrification', 'Phyresis', 'Phylatery', 'Pin', 'Plague',
|
'Knowledge', 'Landmark', 'Level', 'Loot', 'Lore', 'Loyalty', 'Luck', 'Magnet',
|
||||||
'Plot', 'Point', 'Poison', 'Polyp', 'Possession', 'Pressure', 'Prey',
|
'Manabond', 'Manifestation', 'Mannequin', 'Mask', 'Matrix', 'Memory', 'Midway',
|
||||||
'Pupa', 'Quest', 'Rad', 'Rejection', 'Reprieve', 'Rev', 'Revival',
|
'Mine', 'Mining', 'Mire', 'Music', 'Muster', 'Necrodermis', 'Nest', 'Net', 'Night',
|
||||||
'Ribbon', 'Ritual', 'Rope', 'Rust', 'Scream', 'Scroll', 'Shell',
|
'Oil', 'Omen', 'Ore', 'Page', 'Pain', 'Palliation', 'Paralyzing', 'Pause', 'Petal',
|
||||||
'Shield', 'Silver', 'Shred', 'Sleep', 'Sleight', 'Slime', 'Slumber',
|
'Petrification', 'Phyresis', 'Phylactery', 'Pin', 'Plague', 'Plot', 'Point', 'Poison',
|
||||||
'Soot', 'Soul', 'Spark', 'Spite', 'Spore', 'Stash', 'Storage',
|
'Polyp', 'Possession', 'Pressure', 'Prey', 'Pupa', 'Quest', 'Rad', 'Rejection',
|
||||||
'Story', 'Strife', 'Study', 'Stun', 'Supply', 'Suspect', 'Takeover',
|
'Reprieve', 'Rev', 'Revival', 'Ribbon', 'Ritual', 'Rope', 'Rust', 'Scream', 'Scroll',
|
||||||
'Task', 'Ticket', 'Tide', 'Time', 'Tower', 'Training', 'Trap',
|
'Shell', 'Shield', 'Silver', 'Shred', 'Sleep', 'Sleight', 'Slime', 'Slumber', 'Soot',
|
||||||
'Treasure', 'Unity', 'Unlock', 'Valor', 'Velocity', 'Verse',
|
'Soul', 'Spark', 'Spite', 'Spore', 'Stash', 'Storage', 'Story', 'Strife', 'Study',
|
||||||
'Vitality', 'Void', 'Volatile', 'Vortex', 'Vow', 'Voyage', 'Wage',
|
'Stun', 'Supply', 'Suspect', 'Takeover', 'Task', 'Ticket', 'Tide', 'Time', 'Tower',
|
||||||
'Winch', 'Wind', 'Wish']
|
'Training', 'Trap', 'Treasure', 'Unity', 'Unlock', 'Valor', 'Velocity', 'Verse',
|
||||||
|
'Vitality', 'Void', 'Volatile', 'Vortex', 'Vow', 'Voyage', 'Wage', 'Winch', 'Wind',
|
||||||
|
'Wish'
|
||||||
|
]
|
||||||
|
|
||||||
|
# Dataclass describing a counter pattern and display label
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class CounterSpec:
|
||||||
|
pattern: str # Regex fragment (without trailing " counter")
|
||||||
|
label: str # Human-readable label (used in tag text)
|
||||||
|
group: str # 'pt' or 'named' (for future filtering)
|
||||||
|
|
||||||
|
def search_pattern(self) -> str:
|
||||||
|
"""Full regex used for searching (matches singular/plural)."""
|
||||||
|
return rf"{self.pattern} counter[s]?"
|
||||||
|
|
||||||
|
# Helper to derive label from pattern (unescape common sequences)
|
||||||
|
def _derive_label(p: str) -> str:
|
||||||
|
return p.replace('\\+','+')
|
||||||
|
|
||||||
|
def _build_counter_specs(pt_list: Iterable[str], named_list: Iterable[str]) -> List[CounterSpec]:
|
||||||
|
specs: List[CounterSpec] = []
|
||||||
|
specs.extend(CounterSpec(pattern=p, label=_derive_label(p), group='pt') for p in pt_list)
|
||||||
|
specs.extend(CounterSpec(pattern=p, label=p, group='named') for p in named_list)
|
||||||
|
return specs
|
||||||
|
|
||||||
|
ALL_COUNTER_SPECS: List[CounterSpec] = _build_counter_specs(PT_COUNTER_TYPES, NAMED_COUNTER_TYPES)
|
||||||
|
|
||||||
|
# Backward-compatible flat list (legacy usage)
|
||||||
|
COUNTER_TYPES: List[str] = [s.pattern for s in ALL_COUNTER_SPECS]
|
||||||
|
|
||||||
|
# Basic duplication guard (fails fast during import if misconfigured)
|
||||||
|
if len(COUNTER_TYPES) != len(set(COUNTER_TYPES)):
|
||||||
|
duplicate = sorted({p for p in COUNTER_TYPES if COUNTER_TYPES.count(p) > 1})
|
||||||
|
raise ValueError(f"Duplicate counter patterns detected: {duplicate}")
|
||||||
|
|
||||||
CREATURE_TYPES: List[str] = ['Advisor', 'Aetherborn', 'Alien', 'Ally', 'Angel', 'Antelope', 'Ape', 'Archer', 'Archon', 'Armadillo',
|
CREATURE_TYPES: List[str] = ['Advisor', 'Aetherborn', 'Alien', 'Ally', 'Angel', 'Antelope', 'Ape', 'Archer', 'Archon', 'Armadillo',
|
||||||
'Army', 'Artificer', 'Assassin', 'Assembly-Worker', 'Astartes', 'Atog', 'Aurochs', 'Automaton',
|
'Army', 'Artificer', 'Assassin', 'Assembly-Worker', 'Astartes', 'Atog', 'Aurochs', 'Automaton',
|
||||||
|
|
@ -145,12 +191,7 @@ ENCHANTMENT_TOKENS: List[str] = ['Cursed Role', 'Monster Role', 'Royal Role', 'S
|
||||||
ARTIFACT_TOKENS: List[str] = ['Blood', 'Clue', 'Food', 'Gold', 'Incubator',
|
ARTIFACT_TOKENS: List[str] = ['Blood', 'Clue', 'Food', 'Gold', 'Incubator',
|
||||||
'Junk','Map','Powerstone', 'Treasure']
|
'Junk','Map','Powerstone', 'Treasure']
|
||||||
|
|
||||||
# Constants for DataFrame validation and processing
|
# (REQUIRED_COLUMNS imported from settings to avoid duplication)
|
||||||
REQUIRED_COLUMNS: List[str] = [
|
|
||||||
'name', 'faceName', 'edhrecRank', 'colorIdentity', 'colors',
|
|
||||||
'manaCost', 'manaValue', 'type', 'creatureTypes', 'text',
|
|
||||||
'power', 'toughness', 'keywords', 'themeTags', 'layout', 'side'
|
|
||||||
]
|
|
||||||
|
|
||||||
# Mapping of card types to their corresponding theme tags
|
# Mapping of card types to their corresponding theme tags
|
||||||
TYPE_TAG_MAPPING: Dict[str, List[str]] = {
|
TYPE_TAG_MAPPING: Dict[str, List[str]] = {
|
||||||
|
|
|
||||||
|
|
@ -3044,18 +3044,15 @@ def tag_for_special_counters(df: pd.DataFrame, color: str) -> None:
|
||||||
start_time = pd.Timestamp.now()
|
start_time = pd.Timestamp.now()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Process each counter type
|
# Process each counter type (supports singular/plural)
|
||||||
counter_counts = {}
|
counter_counts = {}
|
||||||
for counter_type in tag_constants.COUNTER_TYPES:
|
for spec in tag_constants.ALL_COUNTER_SPECS:
|
||||||
# Create pattern for this counter type
|
pattern = spec.search_pattern()
|
||||||
pattern = f'{counter_type} counter'
|
|
||||||
mask = tag_utils.create_text_mask(df, pattern)
|
mask = tag_utils.create_text_mask(df, pattern)
|
||||||
|
|
||||||
if mask.any():
|
if mask.any():
|
||||||
# Apply tags via rules engine
|
tags = [f'{spec.label} Counters', 'Counters Matter']
|
||||||
tags = [f'{counter_type} Counters', 'Counters Matter']
|
tag_utils.apply_rules(df, [ {'mask': mask, 'tags': tags} ])
|
||||||
tag_utils.apply_rules(df, [ { 'mask': mask, 'tags': tags } ])
|
counter_counts[spec.label] = int(mask.sum())
|
||||||
counter_counts[counter_type] = mask.sum()
|
|
||||||
|
|
||||||
# Log results
|
# Log results
|
||||||
duration = (pd.Timestamp.now() - start_time).total_seconds()
|
duration = (pd.Timestamp.now() - start_time).total_seconds()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue