mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2025-09-22 04:50:46 +02: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
|
||||
|
||||
# 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*+-]+$'}
|
||||
}
|
||||
|
||||
# Required columns for CSV validation
|
||||
CSV_REQUIRED_COLUMNS: Final[List[str]] = [
|
||||
'name', 'faceName', 'edhrecRank', 'colorIdentity', 'colors',
|
||||
'manaCost', 'manaValue', 'type', 'creatureTypes', 'text',
|
||||
'power', 'toughness', 'keywords', 'themeTags', 'layout', 'side'
|
||||
]
|
||||
# (CSV_REQUIRED_COLUMNS imported from settings to avoid duplication)
|
||||
|
||||
# DataFrame processing configuration
|
||||
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
|
||||
'Ancestral Recall', 'Balance', 'Biorhythm', 'Black Lotus',
|
||||
'Braids, Cabal Minion', 'Chaos Orb', 'Coalition Victory',
|
||||
'Channel', 'Dockside Extortionist', 'Emrakul, the Aeons Torn',
|
||||
'Erayo, Soratami Ascendant', 'Falling Star', 'Fastbond',
|
||||
'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
|
||||
'Invoke Prejudice', 'Cleanse', 'Stone-Throwing Devils', 'Pradesh Gypsies',
|
||||
'Jihad', 'Imprison', 'Crusade'
|
||||
]
|
||||
__all__ = [
|
||||
'SETUP_COLORS', 'COLOR_ABRV', 'COLUMN_ORDER', 'TAGGED_COLUMN_ORDER',
|
||||
'BANNED_CARDS', 'MTGJSON_API_URL', 'LEGENDARY_OPTIONS', 'NON_LEGAL_SETS',
|
||||
'CARD_TYPES_TO_EXCLUDE', 'CSV_PROCESSING_COLUMNS', 'SORT_CONFIG',
|
||||
'FILTER_CONFIG'
|
||||
]
|
||||
|
||||
SETUP_COLORS: List[str] = ['colorless', 'white', 'blue', 'black', 'green', 'red',
|
||||
'azorius', 'orzhov', 'selesnya', 'boros', 'dimir',
|
||||
'simic', 'izzet', 'golgari', 'rakdos', 'gruul',
|
||||
'bant', 'esper', 'grixis', 'jund', 'naya',
|
||||
'abzan', 'jeskai', 'mardu', 'sultai', 'temur',
|
||||
'dune', 'glint', 'ink', 'witch', 'yore', 'wubrg']
|
||||
|
||||
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']
|
||||
# Banned cards consolidated here (remains specific to setup concerns)
|
||||
BANNED_CARDS: List[str] = [
|
||||
# Commander banned list
|
||||
'Ancestral Recall', 'Balance', 'Biorhythm', 'Black Lotus',
|
||||
'Braids, Cabal Minion', 'Chaos Orb', 'Coalition Victory',
|
||||
'Channel', 'Dockside Extortionist', 'Emrakul, the Aeons Torn',
|
||||
'Erayo, Soratami Ascendant', 'Falling Star', 'Fastbond',
|
||||
'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",
|
||||
# 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
|
||||
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] = [
|
||||
'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'
|
||||
]
|
||||
# COLUMN_ORDER and TAGGED_COLUMN_ORDER now sourced from settings via CARD_DATA_COLUMNS
|
|
@ -1,6 +1,6 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from settings import os
|
||||
import os
|
||||
import logging
|
||||
|
||||
# Create logs directory if it doesn't exist
|
||||
|
@ -26,4 +26,13 @@ file_handler.setFormatter(NoDunderFormatter(LOG_FORMAT))
|
|||
|
||||
# Stream handler
|
||||
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
|
||||
|
||||
# Standard library imports
|
||||
import os
|
||||
from sys import exit
|
||||
from typing import Dict, List, Optional, Final, Tuple, Pattern, Union, Callable
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
# 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',
|
||||
'azorius', 'orzhov', 'selesnya', 'boros', 'dimir',
|
||||
'simic', 'izzet', 'golgari', 'rakdos', 'gruul',
|
||||
'bant', 'esper', 'grixis', 'jund', 'naya',
|
||||
'abzan', 'jeskai', 'mardu', 'sultai', 'temur',
|
||||
'dune', 'glint', 'ink', 'witch', 'yore', 'wubrg',
|
||||
'commander']
|
||||
SETUP_COLORS: List[str] = [
|
||||
'colorless', 'white', 'blue', 'black', 'green', 'red',
|
||||
'azorius', 'orzhov', 'selesnya', 'boros', 'dimir',
|
||||
'simic', 'izzet', 'golgari', 'rakdos', 'gruul',
|
||||
'bant', 'esper', 'grixis', 'jund', 'naya',
|
||||
'abzan', 'jeskai', 'mardu', 'sultai', 'temur',
|
||||
'dune', 'glint', 'ink', 'witch', 'yore', 'wubrg'
|
||||
]
|
||||
|
||||
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']
|
||||
# Legacy constant (includes 'commander', preserves previous external usage)
|
||||
COLORS: List[str] = [
|
||||
*SETUP_COLORS,
|
||||
'commander'
|
||||
]
|
||||
|
||||
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']
|
||||
|
||||
|
|
|
@ -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']
|
||||
|
||||
|
@ -56,41 +68,75 @@ PHRASE_GROUPS: Dict[str, List[str]] = {
|
|||
CREATE_ACTION_PATTERN: Final[str] = r"create|put"
|
||||
|
||||
# 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',
|
||||
'-0/-1', '-0/-2', '-1/-0', '-1/-2', '-2/-0', '-2/-2',
|
||||
'Acorn', 'Aegis', 'Age', 'Aim', 'Arrow', 'Arrowhead','Awakening',
|
||||
'Bait', 'Blaze', 'Blessing', 'Blight',' Blood', 'Bloddline',
|
||||
'Bloodstain', 'Book', 'Bounty', 'Brain', 'Bribery', 'Brick',
|
||||
'Burden', 'Cage', 'Carrion', 'Charge', 'Coin', 'Collection',
|
||||
'Component', 'Contested', 'Corruption', 'CRANK!', 'Credit',
|
||||
'Croak', 'Corpse', 'Crystal', 'Cube', 'Currency', 'Death',
|
||||
'Defense', 'Delay', 'Depletion', 'Descent', 'Despair', 'Devotion',
|
||||
'Divinity', 'Doom', 'Dream', 'Duty', 'Echo', 'Egg', 'Elixir',
|
||||
'Ember', 'Energy', 'Enlightened', 'Eon', 'Eruption', 'Everything',
|
||||
'Experience', 'Eyeball', 'Eyestalk', 'Fade', 'Fate', 'Feather',
|
||||
'Feeding', 'Fellowship', 'Fetch', 'Filibuster', 'Finality', 'Flame',
|
||||
'Flood', 'Foreshadow', 'Fungus', 'Fury', 'Fuse', 'Gem', 'Ghostform',
|
||||
'Glpyh', 'Gold', 'Growth', 'Hack', 'Harmony', 'Hatching', 'Hatchling',
|
||||
'Healing', 'Hit', 'Hope',' Hone', 'Hoofprint', 'Hour', 'Hourglass',
|
||||
'Hunger', 'Ice', 'Imposter', 'Incarnation', 'Incubation', 'Infection',
|
||||
'Influence', 'Ingenuity', 'Intel', 'Intervention', 'Invitation',
|
||||
'Isolation', 'Javelin', 'Judgment', 'Keyword', 'Ki', 'Kick',
|
||||
'Knickknack', 'Knowledge', 'Landmark', 'Level', 'Loot', 'Lore',
|
||||
'Loyalty', 'Luck', 'Magnet', 'Manabond', 'Manifestation', 'Mannequin',
|
||||
'Mask', 'Matrix', 'Memory', 'Midway', 'Mine', 'Mining', 'Mire',
|
||||
'Music', 'Muster', 'Necrodermis', 'Nest', 'Net', 'Night', 'Oil',
|
||||
'Omen', 'Ore', 'Page', 'Pain', 'Palliation', 'Paralyzing', 'Pause',
|
||||
'Petal', 'Petrification', 'Phyresis', 'Phylatery', 'Pin', 'Plague',
|
||||
'Plot', 'Point', 'Poison', 'Polyp', 'Possession', 'Pressure', 'Prey',
|
||||
'Pupa', 'Quest', 'Rad', 'Rejection', 'Reprieve', 'Rev', 'Revival',
|
||||
'Ribbon', 'Ritual', 'Rope', 'Rust', 'Scream', 'Scroll', 'Shell',
|
||||
'Shield', 'Silver', 'Shred', 'Sleep', 'Sleight', 'Slime', 'Slumber',
|
||||
'Soot', 'Soul', 'Spark', 'Spite', 'Spore', 'Stash', 'Storage',
|
||||
'Story', 'Strife', 'Study', 'Stun', 'Supply', 'Suspect', 'Takeover',
|
||||
'Task', 'Ticket', 'Tide', 'Time', 'Tower', 'Training', 'Trap',
|
||||
'Treasure', 'Unity', 'Unlock', 'Valor', 'Velocity', 'Verse',
|
||||
'Vitality', 'Void', 'Volatile', 'Vortex', 'Vow', 'Voyage', 'Wage',
|
||||
'Winch', 'Wind', 'Wish']
|
||||
"""Counter type vocabularies."""
|
||||
|
||||
# Power/Toughness modifier counters (regex fragments already escaped where needed)
|
||||
PT_COUNTER_TYPES: List[str] = [
|
||||
r'\+0/\+1', r'\+0/\+2', r'\+1/\+0', r'\+1/\+2', r'\+2/\+0', r'\+2/\+2',
|
||||
'-0/-1', '-0/-2', '-1/-0', '-1/-2', '-2/-0', '-2/-2'
|
||||
]
|
||||
|
||||
# Named counters (alphabetical within rough thematic blocks)
|
||||
NAMED_COUNTER_TYPES: List[str] = [
|
||||
'Acorn', 'Aegis', 'Age', 'Aim', 'Arrow', 'Arrowhead', 'Awakening',
|
||||
'Bait', 'Blaze', 'Blessing', 'Blight', 'Blood', 'Bloodline', 'Bloodstain', 'Book',
|
||||
'Bounty', 'Brain', 'Bribery', 'Brick', 'Burden', 'Cage', 'Carrion', 'Charge', 'Coin',
|
||||
'Collection', 'Component', 'Contested', 'Corruption', 'CRANK!', 'Credit', 'Croak',
|
||||
'Corpse', 'Crystal', 'Cube', 'Currency', 'Death', 'Defense', 'Delay', 'Depletion',
|
||||
'Descent', 'Despair', 'Devotion', 'Divinity', 'Doom', 'Dream', 'Duty', 'Echo', 'Egg',
|
||||
'Elixir', 'Ember', 'Energy', 'Enlightened', 'Eon', 'Eruption', 'Everything',
|
||||
'Experience', 'Eyeball', 'Eyestalk', 'Fade', 'Fate', 'Feather', 'Feeding',
|
||||
'Fellowship', 'Fetch', 'Filibuster', 'Finality', 'Flame', 'Flood', 'Foreshadow',
|
||||
'Fungus', 'Fury', 'Fuse', 'Gem', 'Ghostform', 'Glyph', 'Gold', 'Growth', 'Hack',
|
||||
'Harmony', 'Hatching', 'Hatchling', 'Healing', 'Hit', 'Hope', 'Hone', 'Hoofprint',
|
||||
'Hour', 'Hourglass', 'Hunger', 'Ice', 'Imposter', 'Incarnation', 'Incubation',
|
||||
'Infection', 'Influence', 'Ingenuity', 'Intel', 'Intervention', 'Invitation',
|
||||
'Isolation', 'Javelin', 'Judgment', 'Keyword', 'Ki', 'Kick', 'Knickknack',
|
||||
'Knowledge', 'Landmark', 'Level', 'Loot', 'Lore', 'Loyalty', 'Luck', 'Magnet',
|
||||
'Manabond', 'Manifestation', 'Mannequin', 'Mask', 'Matrix', 'Memory', 'Midway',
|
||||
'Mine', 'Mining', 'Mire', 'Music', 'Muster', 'Necrodermis', 'Nest', 'Net', 'Night',
|
||||
'Oil', 'Omen', 'Ore', 'Page', 'Pain', 'Palliation', 'Paralyzing', 'Pause', 'Petal',
|
||||
'Petrification', 'Phyresis', 'Phylactery', 'Pin', 'Plague', 'Plot', 'Point', 'Poison',
|
||||
'Polyp', 'Possession', 'Pressure', 'Prey', 'Pupa', 'Quest', 'Rad', 'Rejection',
|
||||
'Reprieve', 'Rev', 'Revival', 'Ribbon', 'Ritual', 'Rope', 'Rust', 'Scream', 'Scroll',
|
||||
'Shell', 'Shield', 'Silver', 'Shred', 'Sleep', 'Sleight', 'Slime', 'Slumber', 'Soot',
|
||||
'Soul', 'Spark', 'Spite', 'Spore', 'Stash', 'Storage', 'Story', 'Strife', 'Study',
|
||||
'Stun', 'Supply', 'Suspect', 'Takeover', 'Task', 'Ticket', 'Tide', 'Time', 'Tower',
|
||||
'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',
|
||||
'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',
|
||||
'Junk','Map','Powerstone', 'Treasure']
|
||||
|
||||
# Constants for DataFrame validation and processing
|
||||
REQUIRED_COLUMNS: List[str] = [
|
||||
'name', 'faceName', 'edhrecRank', 'colorIdentity', 'colors',
|
||||
'manaCost', 'manaValue', 'type', 'creatureTypes', 'text',
|
||||
'power', 'toughness', 'keywords', 'themeTags', 'layout', 'side'
|
||||
]
|
||||
# (REQUIRED_COLUMNS imported from settings to avoid duplication)
|
||||
|
||||
# Mapping of card types to their corresponding theme tags
|
||||
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()
|
||||
|
||||
try:
|
||||
# Process each counter type
|
||||
# Process each counter type (supports singular/plural)
|
||||
counter_counts = {}
|
||||
for counter_type in tag_constants.COUNTER_TYPES:
|
||||
# Create pattern for this counter type
|
||||
pattern = f'{counter_type} counter'
|
||||
for spec in tag_constants.ALL_COUNTER_SPECS:
|
||||
pattern = spec.search_pattern()
|
||||
mask = tag_utils.create_text_mask(df, pattern)
|
||||
|
||||
if mask.any():
|
||||
# Apply tags via rules engine
|
||||
tags = [f'{counter_type} Counters', 'Counters Matter']
|
||||
tag_utils.apply_rules(df, [ { 'mask': mask, 'tags': tags } ])
|
||||
counter_counts[counter_type] = mask.sum()
|
||||
tags = [f'{spec.label} Counters', 'Counters Matter']
|
||||
tag_utils.apply_rules(df, [ {'mask': mask, 'tags': tags} ])
|
||||
counter_counts[spec.label] = int(mask.sum())
|
||||
|
||||
# Log results
|
||||
duration = (pd.Timestamp.now() - start_time).total_seconds()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue