2024-12-09 12:56:01 -08:00
from __future__ import annotations
2025-01-14 09:06:59 -08:00
# Standard library imports
2024-12-31 10:48:11 -08:00
import logging
2024-12-26 09:38:21 -08:00
import os
2024-12-31 12:04:10 -08:00
import re
2025-01-14 09:06:59 -08:00
from typing import Union
2024-12-31 12:04:10 -08:00
2025-01-14 09:06:59 -08:00
# Third-party imports
2025-01-17 17:04:02 -08:00
import pandas as pd
2024-12-09 12:56:01 -08:00
2025-01-17 17:04:02 -08:00
import settings
import tag_utils
2024-12-10 12:13:44 -08:00
2025-01-14 09:06:59 -08:00
# Local application imports
2025-01-14 12:07:49 -08:00
from settings import CSV_DIRECTORY , multiple_copy_cards , num_to_search , triggers
2025-01-14 09:06:59 -08:00
from setup import regenerate_csv_by_color
2024-12-31 12:04:10 -08:00
# Constants for common tag groupings
TAG_GROUPS = {
" Cantrips " : [ " Cantrips " , " Card Draw " , " Spellslinger " , " Spells Matter " ] ,
" Tokens " : [ " Token Creation " , " Tokens Matter " ] ,
" Counters " : [ " Counters Matter " ] ,
" Combat " : [ " Combat Matters " , " Combat Tricks " ] ,
" Artifacts " : [ " Artifacts Matter " , " Artifact Tokens " ] ,
" Enchantments " : [ " Enchantments Matter " , " Enchantment Tokens " ] ,
" Lands " : [ " Lands Matter " ] ,
" Spells " : [ " Spellslinger " , " Spells Matter " ]
}
# Common regex patterns
PATTERN_GROUPS = {
" draw " : r " draw[s]? a card|draw[s]? one card " ,
" combat " : r " attack[s]?|block[s]?|combat damage " ,
" tokens " : r " create[s]? .* token|put[s]? .* token " ,
" counters " : r " \ +1/ \ +1 counter| \ -1/ \ -1 counter|loyalty counter " ,
" sacrifice " : r " sacrifice[s]? .*|sacrificed " ,
2025-01-02 13:00:52 -08:00
" exile " : r " exile[s]? .*|exiled " ,
" cost_reduction " : r " cost[s]? \ { [ \ d \ w] \ } less|affinity for|cost[s]? less to cast|chosen type cost|copy cost|from exile cost|from exile this turn cost|from your graveyard cost|has undaunted|have affinity for artifacts|other than your hand cost|spells cost|spells you cast cost|that target .* cost|those spells cost|you cast cost|you pay cost "
2024-12-31 12:04:10 -08:00
}
2024-12-10 12:13:44 -08:00
2025-01-14 10:10:30 -08:00
# Create logs directory if it doesn't exist
if not os . path . exists ( ' logs ' ) :
os . makedirs ( ' logs ' )
2024-12-31 10:48:11 -08:00
logging . basicConfig (
level = logging . INFO ,
2024-12-31 12:04:10 -08:00
format = ' %(asctime)s - %(levelname)s - %(message)s ' ,
handlers = [
logging . StreamHandler ( ) ,
2025-01-14 10:10:30 -08:00
logging . FileHandler ( ' logs/tagger.log ' , mode = ' w ' , encoding = ' utf-8 ' )
2024-12-31 12:04:10 -08:00
]
2024-12-31 10:48:11 -08:00
)
2025-01-14 10:10:30 -08:00
logger = logging . getLogger ( __name__ )
2024-12-31 10:48:11 -08:00
2024-12-16 22:58:02 -08:00
### Setup
## Load the dataframe
2025-01-14 09:06:59 -08:00
def load_dataframe ( color : str ) - > None :
2024-12-31 10:48:11 -08:00
"""
Load and validate the card dataframe for a given color .
2024-12-31 12:04:10 -08:00
2024-12-31 10:48:11 -08:00
Args :
color ( str ) : The color of cards to load ( ' white ' , ' blue ' , etc )
2024-12-31 12:04:10 -08:00
2024-12-31 10:48:11 -08:00
Raises :
FileNotFoundError : If CSV file doesn ' t exist and can ' t be regenerated
ValueError : If required columns are missing
"""
2024-12-26 09:38:21 -08:00
try :
2025-01-14 12:07:49 -08:00
filepath = f ' { CSV_DIRECTORY } / { color } _cards.csv '
2024-12-31 12:04:10 -08:00
2024-12-26 09:38:21 -08:00
# Check if file exists, regenerate if needed
if not os . path . exists ( filepath ) :
2025-01-14 10:10:30 -08:00
logger . warning ( f ' { color } _cards.csv not found, regenerating it. ' )
2024-12-12 12:27:29 -08:00
regenerate_csv_by_color ( color )
2024-12-31 10:48:11 -08:00
if not os . path . exists ( filepath ) :
raise FileNotFoundError ( f " Failed to generate { filepath } " )
2024-12-31 12:04:10 -08:00
2024-12-26 09:38:21 -08:00
# Load initial dataframe for validation
check_df = pd . read_csv ( filepath )
2024-12-31 12:04:10 -08:00
2024-12-26 09:38:21 -08:00
# Validate required columns
2024-12-31 10:48:11 -08:00
required_columns = [ ' creatureTypes ' , ' themeTags ' ]
2024-12-26 09:38:21 -08:00
missing_columns = [ col for col in required_columns if col not in check_df . columns ]
2024-12-31 12:04:10 -08:00
2024-12-26 09:38:21 -08:00
# Handle missing columns
if missing_columns :
2025-01-14 10:10:30 -08:00
logger . warning ( f " Missing columns: { missing_columns } " )
2024-12-26 09:38:21 -08:00
if ' creatureTypes ' not in check_df . columns :
kindred_tagging ( check_df , color )
if ' themeTags ' not in check_df . columns :
create_theme_tags ( check_df , color )
2024-12-31 12:04:10 -08:00
2024-12-31 10:48:11 -08:00
# Verify columns were added successfully
check_df = pd . read_csv ( filepath )
still_missing = [ col for col in required_columns if col not in check_df . columns ]
if still_missing :
raise ValueError ( f " Failed to add required columns: { still_missing } " )
2024-12-31 12:04:10 -08:00
2024-12-26 09:38:21 -08:00
# Load final dataframe with proper converters
df = pd . read_csv ( filepath , converters = { ' themeTags ' : pd . eval , ' creatureTypes ' : pd . eval } )
2024-12-31 12:04:10 -08:00
2024-12-26 09:38:21 -08:00
# Process the dataframe
tag_by_color ( df , color )
2024-12-31 12:04:10 -08:00
2024-12-26 09:38:21 -08:00
except FileNotFoundError as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error: { e } ' )
2024-12-31 12:04:10 -08:00
raise
except pd . errors . ParserError as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error parsing the CSV file: { e } ' )
2024-12-31 12:04:10 -08:00
raise
2024-12-26 09:38:21 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' An unexpected error occurred: { e } ' )
2024-12-31 12:04:10 -08:00
raise
2024-12-11 11:36:29 -08:00
2024-12-16 22:58:02 -08:00
## Tag cards on a color-by-color basis
2025-01-14 09:06:59 -08:00
def tag_by_color ( df : pd . DataFrame , color : str ) - > None :
2024-12-16 22:58:02 -08:00
#load_dataframe()
#answer = input('Would you like to regenerate the CSV file?\n')
#if answer.lower() in ['yes', 'y']:
# regenerate_csv_by_color(color)
# kindred_tagging(df, color)
# create_theme_tags(df, color)
#else:
# pass
kindred_tagging ( df , color )
2025-01-02 13:00:52 -08:00
print ( ' \n ==================== \n ' )
2024-12-16 22:58:02 -08:00
create_theme_tags ( df , color )
2025-01-02 13:00:52 -08:00
print ( ' \n ==================== \n ' )
# Go through each type of tagging
add_creatures_to_tags ( df , color )
print ( ' \n ==================== \n ' )
2024-12-16 22:58:02 -08:00
tag_for_card_types ( df , color )
2025-01-02 13:00:52 -08:00
print ( ' \n ==================== \n ' )
tag_for_keywords ( df , color )
print ( ' \n ==================== \n ' )
2024-12-31 12:04:10 -08:00
## Tag for various effects
2025-01-02 13:00:52 -08:00
tag_for_cost_reduction ( df , color )
print ( ' \n ==================== \n ' )
tag_for_card_draw ( df , color )
print ( ' \n ==================== \n ' )
2025-01-08 08:29:00 -08:00
tag_for_artifacts ( df , color )
print ( ' \n ==================== \n ' )
tag_for_enchantments ( df , color )
print ( ' \n ==================== \n ' )
tag_for_exile_matters ( df , color )
print ( ' \n ==================== \n ' )
tag_for_tokens ( df , color )
print ( ' \n ==================== \n ' )
2025-01-08 12:06:33 -08:00
tag_for_life_matters ( df , color )
print ( ' \n ==================== \n ' )
tag_for_counters ( df , color )
print ( ' \n ==================== \n ' )
tag_for_voltron ( df , color )
print ( ' \n ==================== \n ' )
tag_for_lands_matter ( df , color )
print ( ' \n ==================== \n ' )
tag_for_spellslinger ( df , color )
print ( ' \n ==================== \n ' )
tag_for_ramp ( df , color )
print ( ' \n ==================== \n ' )
tag_for_themes ( df , color )
print ( ' \n ==================== \n ' )
2025-01-08 17:26:02 -08:00
tag_for_interaction ( df , color )
print ( ' \n ==================== \n ' )
2024-12-17 12:02:53 -08:00
2024-12-16 22:58:02 -08:00
# Lastly, sort all theme tags for easier reading
sort_theme_tags ( df , color )
2025-01-14 12:07:49 -08:00
df . to_csv ( f ' { CSV_DIRECTORY } / { color } _cards.csv ' , index = False )
2024-12-18 20:21:45 -08:00
#print(df)
2025-01-08 17:26:02 -08:00
print ( ' \n ==================== \n ' )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tags are done being set on { color } _cards.csv ' )
2024-12-18 20:21:45 -08:00
#keyboard.wait('esc')
2024-12-16 22:58:02 -08:00
## Determine any non-creature cards that have creature types mentioned
2024-12-31 12:04:10 -08:00
def kindred_tagging ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with creature types and related types.
Args :
df : DataFrame containing card data
color : Color identifier for logging
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Setting creature type tags on { color } _cards.csv ' )
2024-12-31 12:04:10 -08:00
try :
# Initialize creatureTypes column vectorized
df [ ' creatureTypes ' ] = pd . Series ( [ [ ] for _ in range ( len ( df ) ) ] )
# Detect creature types using mask
2025-01-14 10:10:30 -08:00
creature_mask = tag_utils . create_type_mask ( df , ' Creature ' )
2024-12-31 12:04:10 -08:00
if creature_mask . any ( ) :
creature_rows = df [ creature_mask ]
for idx , row in creature_rows . iterrows ( ) :
2025-01-14 10:10:30 -08:00
types = tag_utils . extract_creature_types (
2024-12-31 12:04:10 -08:00
row [ ' type ' ] ,
settings . creature_types ,
settings . non_creature_types
)
if types :
df . at [ idx , ' creatureTypes ' ] = types
creature_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Creature type detection completed in { ( creature_time - start_time ) . total_seconds ( ) : .2f } s ' )
2025-01-02 13:00:52 -08:00
print ( ' \n ========== \n ' )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Setting Outlaw creature type tags on { color } _cards.csv ' )
2024-12-31 12:04:10 -08:00
# Process outlaw types
outlaws = settings . OUTLAW_TYPES
df [ ' creatureTypes ' ] = df . apply (
2025-01-14 10:10:30 -08:00
lambda row : tag_utils . add_outlaw_type ( row [ ' creatureTypes ' ] , outlaws )
2024-12-31 12:04:10 -08:00
if isinstance ( row [ ' creatureTypes ' ] , list ) else row [ ' creatureTypes ' ] ,
axis = 1
)
outlaw_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Outlaw type processing completed in { ( outlaw_time - creature_time ) . total_seconds ( ) : .2f } s ' )
2024-12-31 12:04:10 -08:00
# Find creature types in text
2025-01-14 10:10:30 -08:00
logger . info ( ' Checking for creature types in card text ' )
2024-12-31 12:04:10 -08:00
# Check for creature types in text (i.e. how 'Voja, Jaws of the Conclave' cares about Elves)
2025-01-14 10:10:30 -08:00
logger . info ( f ' Checking for and setting creature types found in the text of cards in { color } _cards.csv ' )
2024-12-31 12:04:10 -08:00
ignore_list = [
' Elite Inquisitor ' , ' Breaker of Armies ' ,
' Cleopatra, Exiled Pharaoh ' , ' Nath \' s Buffoon '
]
for idx , row in df . iterrows ( ) :
if row [ ' name ' ] not in ignore_list :
2025-01-14 10:10:30 -08:00
text_types = tag_utils . find_types_in_text (
2024-12-31 12:04:10 -08:00
row [ ' text ' ] ,
row [ ' name ' ] ,
settings . creature_types
)
if text_types :
current_types = row [ ' creatureTypes ' ]
if isinstance ( current_types , list ) :
df . at [ idx , ' creatureTypes ' ] = sorted (
list ( set ( current_types + text_types ) )
)
text_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Text-based type detection completed in { ( text_time - outlaw_time ) . total_seconds ( ) : .2f } s ' )
2024-12-31 12:04:10 -08:00
# Save results
try :
columns_to_keep = [
' name ' , ' faceName ' , ' edhrecRank ' , ' colorIdentity ' ,
' colors ' , ' manaCost ' , ' manaValue ' , ' type ' ,
' creatureTypes ' , ' text ' , ' power ' , ' toughness ' ,
' keywords ' , ' layout ' , ' side '
]
df = df [ columns_to_keep ]
2025-01-14 12:07:49 -08:00
df . to_csv ( f ' { settings . CSV_DIRECTORY } / { color } _cards.csv ' , index = False )
2024-12-31 12:04:10 -08:00
total_time = pd . Timestamp . now ( ) - start_time
2025-01-14 10:10:30 -08:00
logger . info ( f ' Creature type tagging completed in { total_time . total_seconds ( ) : .2f } s ' )
2024-12-31 12:04:10 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error saving results: { e } ' )
2024-12-31 12:04:10 -08:00
2024-12-16 22:58:02 -08:00
# Overwrite file with creature type tags
2024-12-31 12:04:10 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in kindred_tagging: { e } ' )
2024-12-31 12:04:10 -08:00
raise
2024-12-09 12:56:01 -08:00
2024-12-31 10:48:11 -08:00
def create_theme_tags ( df : pd . DataFrame , color : str ) - > None :
2024-12-31 12:04:10 -08:00
""" Initialize and configure theme tags for a card DataFrame.
This function initializes the themeTags column , validates the DataFrame structure ,
and reorganizes columns in an efficient manner . It uses vectorized operations
for better performance .
Args :
df : DataFrame containing card data to process
color : Color identifier for logging purposes ( e . g . ' white ' , ' blue ' )
Returns :
The processed DataFrame with initialized theme tags and reorganized columns
Raises :
ValueError : If required columns are missing or color is invalid
TypeError : If inputs are not of correct type
2024-12-31 10:48:11 -08:00
"""
2024-12-31 12:04:10 -08:00
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Initializing theme tags for %s cards ' , color )
2024-12-31 12:04:10 -08:00
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
2025-01-17 11:39:27 -08:00
if color not in settings . COLORS :
2024-12-31 12:04:10 -08:00
raise ValueError ( f " Invalid color: { color } " )
try :
# Initialize themeTags column using vectorized operation
df [ ' themeTags ' ] = pd . Series ( [ [ ] for _ in range ( len ( df ) ) ] , index = df . index )
# Define expected columns
required_columns = {
' name ' , ' text ' , ' type ' , ' keywords ' ,
' creatureTypes ' , ' power ' , ' toughness '
}
# Validate required columns
missing = required_columns - set ( df . columns )
if missing :
raise ValueError ( f " Missing required columns: { missing } " )
# Define column order
columns_to_keep = settings . REQUIRED_COLUMNS
# Reorder columns efficiently
available_cols = [ col for col in columns_to_keep if col in df . columns ]
df = df . reindex ( columns = available_cols )
# Save results
try :
2025-01-14 12:07:49 -08:00
df . to_csv ( f ' { settings . CSV_DIRECTORY } / { color } _cards.csv ' , index = False )
2024-12-31 12:04:10 -08:00
total_time = pd . Timestamp . now ( ) - start_time
2025-01-14 10:10:30 -08:00
logger . info ( f ' Creature type tagging completed in { total_time . total_seconds ( ) : .2f } s ' )
2024-12-31 12:04:10 -08:00
# Log performance metrics
end_time = pd . Timestamp . now ( )
duration = ( end_time - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Theme tags initialized in %.2f seconds ' , duration )
2024-12-31 12:04:10 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error saving results: { e } ' )
2024-12-31 12:04:10 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( ' Error initializing theme tags: %s ' , str ( e ) )
2024-12-31 12:04:10 -08:00
raise
def tag_for_card_types ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards based on their types using vectorized operations.
This function efficiently applies tags based on card types using vectorized operations .
It handles special cases for different card types and maintains compatibility with
the existing tagging system .
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Setting card type tags on %s _cards.csv ' , color )
2024-12-31 12:04:10 -08:00
2024-12-31 10:48:11 -08:00
try :
2024-12-31 12:04:10 -08:00
# Validate required columns
required_cols = { ' type ' , ' themeTags ' }
if not required_cols . issubset ( df . columns ) :
raise ValueError ( f " Missing required columns: { required_cols - set ( df . columns ) } " )
# Define type-to-tag mapping
type_tag_map = settings . TYPE_TAG_MAPPING
# Process each card type
for card_type , tags in type_tag_map . items ( ) :
2025-01-14 10:10:30 -08:00
mask = tag_utils . create_type_mask ( df , card_type )
2024-12-31 12:04:10 -08:00
if mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , mask , tags )
logger . info ( ' Tagged %d cards with %s type ' , mask . sum ( ) , card_type )
2024-12-31 12:04:10 -08:00
# Log completion
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Card type tagging completed in %.2f s ' , duration )
2024-12-31 12:04:10 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( ' Error in tag_for_card_types: %s ' , str ( e ) )
2024-12-31 12:04:10 -08:00
raise
2024-12-16 22:58:02 -08:00
# Overwrite file with artifact tag added
2025-01-14 10:10:30 -08:00
logger . info ( f ' Card type tags set on { color } _cards.csv. ' )
2024-12-16 22:58:02 -08:00
## Add creature types to the theme tags
2025-01-02 13:00:52 -08:00
def add_creatures_to_tags ( df : pd . DataFrame , color : str ) - > None :
""" Add kindred tags to theme tags based on creature types using vectorized operations.
2024-12-31 12:04:10 -08:00
2025-01-02 13:00:52 -08:00
This function efficiently processes creature types and adds corresponding kindred tags
using pandas vectorized operations instead of row - by - row iteration .
Args :
df : DataFrame containing card data with creatureTypes and themeTags columns
color : Color identifier for logging purposes
Raises :
ValueError : If required columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Adding creature types to theme tags in { color } _cards.csv ' )
2025-01-02 13:00:52 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' creatureTypes ' , ' themeTags ' }
missing = required_cols - set ( df . columns )
if missing :
raise ValueError ( f " Missing required columns: { missing } " )
# Create mask for rows with non-empty creature types
has_creatures_mask = df [ ' creatureTypes ' ] . apply ( lambda x : bool ( x ) if isinstance ( x , list ) else False )
if has_creatures_mask . any ( ) :
# Get rows with creature types
creature_rows = df [ has_creatures_mask ]
2024-12-31 12:04:10 -08:00
2025-01-02 13:00:52 -08:00
# Generate kindred tags vectorized
def add_kindred_tags ( row ) :
current_tags = row [ ' themeTags ' ]
kindred_tags = [ f " { ct } Kindred " for ct in row [ ' creatureTypes ' ] ]
return sorted ( list ( set ( current_tags + kindred_tags ) ) )
# Update tags for matching rows
df . loc [ has_creatures_mask , ' themeTags ' ] = creature_rows . apply ( add_kindred_tags , axis = 1 )
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Added kindred tags to { has_creatures_mask . sum ( ) } cards in { duration : .2f } s ' )
2025-01-02 13:00:52 -08:00
else :
2025-01-14 10:10:30 -08:00
logger . info ( ' No cards with creature types found ' )
2025-01-02 13:00:52 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in add_creatures_to_tags: { str ( e ) } ' )
2025-01-02 13:00:52 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( f ' Creature types added to theme tags in { color } _cards.csv ' )
2024-12-16 22:58:02 -08:00
## Add keywords to theme tags
2025-01-02 13:00:52 -08:00
def tag_for_keywords ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards based on their keywords using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagging cards with keywords in %s _cards.csv ' , color )
2025-01-02 13:00:52 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create mask for valid keywords
has_keywords = pd . notna ( df [ ' keywords ' ] )
if has_keywords . any ( ) :
# Process cards with keywords
keywords_df = df [ has_keywords ] . copy ( )
# Split keywords into lists
keywords_df [ ' keyword_list ' ] = keywords_df [ ' keywords ' ] . str . split ( ' , ' )
# Add each keyword as a tag
for idx , row in keywords_df . iterrows ( ) :
if isinstance ( row [ ' keyword_list ' ] , list ) :
current_tags = df . at [ idx , ' themeTags ' ]
new_tags = sorted ( list ( set ( current_tags + row [ ' keyword_list ' ] ) ) )
df . at [ idx , ' themeTags ' ] = new_tags
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d cards with keywords in %.2f seconds ' , has_keywords . sum ( ) , duration )
2025-01-02 13:00:52 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( ' Error tagging keywords: %s ' , str ( e ) )
2025-01-02 13:00:52 -08:00
raise
2024-12-16 22:58:02 -08:00
## Sort any set tags
def sort_theme_tags ( df , color ) :
2025-01-14 10:10:30 -08:00
logger . info ( f ' Alphabetically sorting theme tags in { color } _cards.csv. ' )
2024-12-16 22:58:02 -08:00
2025-01-14 10:10:30 -08:00
df [ ' themeTags ' ] = df [ ' themeTags ' ] . apply ( tag_utils . sort_list )
2024-12-16 22:58:02 -08:00
2024-12-26 11:12:56 -08:00
columns_to_keep = [ ' name ' , ' faceName ' , ' edhrecRank ' , ' colorIdentity ' , ' colors ' , ' manaCost ' , ' manaValue ' , ' type ' , ' creatureTypes ' , ' text ' , ' power ' , ' toughness ' , ' keywords ' , ' themeTags ' , ' layout ' , ' side ' ]
2024-12-16 22:58:02 -08:00
df = df [ columns_to_keep ]
2025-01-14 10:10:30 -08:00
logger . info ( f ' Theme tags alphabetically sorted in { color } _cards.csv. ' )
2024-12-16 22:58:02 -08:00
### Cost reductions
2025-01-02 13:00:52 -08:00
def tag_for_cost_reduction ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that reduce spell costs using vectorized operations.
2024-12-16 22:58:02 -08:00
2025-01-02 13:00:52 -08:00
This function identifies cards that reduce casting costs through various means including :
- General cost reduction effects
- Artifact cost reduction
- Enchantment cost reduction
- Affinity and similar mechanics
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagging cost reduction cards in %s _cards.csv ' , color )
2025-01-02 13:00:52 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create masks for different cost reduction patterns
2025-01-14 10:10:30 -08:00
cost_mask = tag_utils . create_text_mask ( df , PATTERN_GROUPS [ ' cost_reduction ' ] )
2025-01-02 13:00:52 -08:00
# Add specific named cards
named_cards = [
' Ancient Cellarspawn ' , ' Beluna Grandsquall ' , ' Cheering Fanatic ' ,
' Cloud Key ' , ' Conduit of Ruin ' , ' Eluge, the Shoreless Sea ' ,
' Goblin Anarchomancer ' , ' Goreclaw, Terror of Qal Sisma ' ,
' Helm of Awakening ' , ' Hymn of the Wilds ' , ' It that Heralds the End ' ,
' K \' rrik, Son of Yawgmoth ' , ' Killian, Ink Duelist ' , ' Krosan Drover ' ,
' Memory Crystal ' , ' Myth Unbound ' , ' Mistform Warchief ' ,
' Ranar the Ever-Watchful ' , ' Rowan, Scion of War ' , ' Semblence Anvil ' ,
' Spectacle Mage ' , ' Spellwild Ouphe ' , ' Strong Back ' ,
' Thryx, the Sudden Storm ' , ' Urza \' s Filter ' , ' Will, Scion of Peace ' ,
' Will Kenrith '
]
2025-01-14 10:10:30 -08:00
named_mask = tag_utils . create_name_mask ( df , named_cards )
2025-01-02 13:00:52 -08:00
# Combine masks
final_mask = cost_mask | named_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Cost Reduction ' ] )
2025-01-02 13:00:52 -08:00
# Add spellslinger tags for noncreature spell cost reduction
2025-01-14 10:10:30 -08:00
spell_mask = final_mask & tag_utils . create_text_mask ( df , r " Sorcery|Instant|noncreature " )
tag_utils . apply_tag_vectorized ( df , spell_mask , [ ' Spellslinger ' , ' Spells Matter ' ] )
2025-01-02 13:00:52 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d cost reduction cards in %.2f s ' , final_mask . sum ( ) , duration )
2025-01-02 13:00:52 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( ' Error tagging cost reduction cards: %s ' , str ( e ) )
2025-01-02 13:00:52 -08:00
raise
2024-12-16 22:58:02 -08:00
### Card draw/advantage
## General card draw/advantage
2025-01-02 13:00:52 -08:00
def tag_for_card_draw ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that have card draw effects or care about drawing cards.
2024-12-09 12:56:01 -08:00
2025-01-02 13:00:52 -08:00
This function identifies and tags cards with various types of card draw effects including :
- Conditional draw ( triggered / activated abilities )
- Looting effects ( draw + discard )
- Cost - based draw ( pay life / sacrifice )
- Replacement draw effects
- Wheel effects
- Unconditional draw
The function maintains proper tag hierarchy and ensures consistent application
of related tags like ' Card Draw ' , ' Spellslinger ' , etc .
Args :
df : DataFrame containing card data to process
color : Color identifier for logging purposes ( e . g . ' white ' , ' blue ' )
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting card draw effect tagging for { color } _cards.csv ' )
2025-01-02 13:00:52 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' text ' , ' themeTags ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-02 13:00:52 -08:00
# Process each type of draw effect
tag_for_conditional_draw ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed conditional draw tagging ' )
2025-01-02 13:00:52 -08:00
print ( ' \n ========== \n ' )
tag_for_loot_effects ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed loot effects tagging ' )
2025-01-02 13:00:52 -08:00
print ( ' \n ========== \n ' )
tag_for_cost_draw ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed cost-based draw tagging ' )
2025-01-02 13:00:52 -08:00
print ( ' \n ========== \n ' )
tag_for_replacement_draw ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed replacement draw tagging ' )
2025-01-02 13:00:52 -08:00
print ( ' \n ========== \n ' )
tag_for_wheels ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed wheel effects tagging ' )
2025-01-02 13:00:52 -08:00
print ( ' \n ========== \n ' )
tag_for_unconditional_draw ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed unconditional draw tagging ' )
2025-01-02 13:00:52 -08:00
print ( ' \n ========== \n ' )
# Log completion and performance metrics
duration = pd . Timestamp . now ( ) - start_time
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed all card draw tagging in { duration . total_seconds ( ) : .2f } s ' )
2025-01-02 13:00:52 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_card_draw: { str ( e ) } ' )
2025-01-02 13:00:52 -08:00
raise
## Conditional card draw (i.e. Rhystic Study or Trouble In Pairs)
def create_unconditional_draw_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with unconditional draw effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have unconditional draw effects
"""
# Create pattern for draw effects using num_to_search
draw_patterns = [ f ' draw { num } card ' for num in num_to_search ]
2025-01-14 10:10:30 -08:00
draw_mask = tag_utils . create_text_mask ( df , draw_patterns )
2025-01-02 13:00:52 -08:00
# Create exclusion mask for conditional effects
excluded_tags = settings . DRAW_RELATED_TAGS
2025-01-14 10:10:30 -08:00
tag_mask = tag_utils . create_tag_mask ( df , excluded_tags )
2025-01-02 13:00:52 -08:00
# Create text-based exclusions
text_patterns = settings . DRAW_EXCLUSION_PATTERNS
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , text_patterns )
2025-01-02 13:00:52 -08:00
return draw_mask & ~ ( tag_mask | text_mask )
def tag_for_unconditional_draw ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that have unconditional draw effects using vectorized operations.
This function identifies and tags cards that draw cards without conditions or
additional costs . It excludes cards that already have conditional draw tags
or specific keywords .
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging unconditional draw effects in { color } _cards.csv ' )
2025-01-02 13:00:52 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create mask for unconditional draw effects
draw_mask = create_unconditional_draw_mask ( df )
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , draw_mask , [ ' Unconditional Draw ' , ' Card Draw ' ] )
2025-01-02 13:00:52 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { draw_mask . sum ( ) } cards with unconditional draw effects in { duration : .2f } s ' )
2025-01-02 13:00:52 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging unconditional draw effects: { str ( e ) } ' )
2025-01-02 13:00:52 -08:00
raise
2024-12-09 12:56:01 -08:00
2024-12-16 22:58:02 -08:00
## Conditional card draw (i.e. Rhystic Study or Trouble In Pairs)
2025-01-02 13:00:52 -08:00
def create_conditional_draw_exclusion_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that should be excluded from conditional draw effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards should be excluded
"""
# Create tag-based exclusions
excluded_tags = settings . DRAW_RELATED_TAGS
2025-01-14 10:10:30 -08:00
tag_mask = tag_utils . create_tag_mask ( df , excluded_tags )
2025-01-02 13:00:52 -08:00
# Create text-based exclusions
text_patterns = settings . DRAW_EXCLUSION_PATTERNS + [ ' whenever you draw a card ' ]
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , text_patterns )
2025-01-02 13:00:52 -08:00
# Create name-based exclusions
excluded_names = [ ' relic vial ' , ' vexing bauble ' ]
2025-01-14 10:10:30 -08:00
name_mask = tag_utils . create_name_mask ( df , excluded_names )
2025-01-02 13:00:52 -08:00
return tag_mask | text_mask | name_mask
def create_conditional_draw_trigger_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with conditional draw triggers.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have trigger patterns
"""
# Build trigger patterns
trigger_patterns = [ ]
for trigger in triggers :
# Permanent/creature/player triggers
trigger_patterns . extend ( [
f ' { trigger } a permanent ' ,
f ' { trigger } a creature ' ,
f ' { trigger } a player ' ,
f ' { trigger } an opponent ' ,
f ' { trigger } another creature ' ,
f ' { trigger } enchanted player ' ,
f ' { trigger } one or more creatures ' ,
f ' { trigger } one or more other creatures ' ,
f ' { trigger } you '
] )
2024-12-09 12:56:01 -08:00
2025-01-02 13:00:52 -08:00
# Name-based attack triggers
trigger_patterns . append ( f ' { trigger } .* attacks ' )
# Create trigger mask
2025-01-14 10:10:30 -08:00
trigger_mask = tag_utils . create_text_mask ( df , trigger_patterns )
2025-01-02 13:00:52 -08:00
# Add other trigger patterns
other_patterns = [ ' created a token ' , ' draw a card for each ' ]
2025-01-14 10:10:30 -08:00
other_mask = tag_utils . create_text_mask ( df , other_patterns )
2025-01-02 13:00:52 -08:00
return trigger_mask | other_mask
def create_conditional_draw_effect_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with draw effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have draw effects
"""
# Create draw patterns using num_to_search
draw_patterns = [ f ' draw { num } card ' for num in num_to_search ]
# Add token and 'draw for each' patterns
draw_patterns . extend ( [
' created a token.*draw ' ,
' draw a card for each '
] )
return df [ ' text ' ] . str . contains ( ' | ' . join ( draw_patterns ) , case = False , na = False )
def tag_for_conditional_draw ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that have conditional draw effects using vectorized operations.
This function identifies and tags cards that draw cards based on triggers or conditions .
It handles various patterns including :
- Permanent / creature triggers
- Player - based triggers
- Token creation triggers
- ' Draw for each ' effects
The function excludes cards that :
- Already have certain tags ( Cycling , Imprint , etc . )
- Contain specific text patterns ( annihilator , ravenous )
- Have specific names ( relic vial , vexing bauble )
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging conditional draw effects in { color } _cards.csv ' )
2025-01-02 13:00:52 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create exclusion mask
exclusion_mask = create_conditional_draw_exclusion_mask ( df )
# Create trigger mask
trigger_mask = create_conditional_draw_trigger_mask ( df )
# Create draw effect mask
2025-01-08 08:29:00 -08:00
draw_patterns = [ f ' draw { num } card ' for num in num_to_search ]
# Add token and 'draw for each' patterns
draw_patterns . extend ( [
' created a token.*draw ' ,
' draw a card for each '
] )
2025-01-14 10:10:30 -08:00
draw_mask = tag_utils . create_text_mask ( df , draw_patterns )
2025-01-02 13:00:52 -08:00
# Combine masks
final_mask = trigger_mask & draw_mask & ~ exclusion_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Conditional Draw ' , ' Card Draw ' ] )
2025-01-02 13:00:52 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with conditional draw effects in { duration : .2f } s ' )
2025-01-02 13:00:52 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging conditional draw effects: { str ( e ) } ' )
2025-01-02 13:00:52 -08:00
raise
2024-12-16 22:58:02 -08:00
## Loot effects, I.E. draw a card, discard a card. Or discard a card, draw a card
2025-01-02 13:00:52 -08:00
def create_loot_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with standard loot effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have loot effects
"""
# Exclude cards that already have other loot-like effects
2025-01-14 10:10:30 -08:00
has_other_loot = tag_utils . create_tag_mask ( df , [ ' Cycling ' , ' Connive ' ] ) | df [ ' text ' ] . str . contains ( ' blood token ' , case = False , na = False )
2024-12-16 22:58:02 -08:00
2025-01-02 13:00:52 -08:00
# Match draw + discard patterns
draw_patterns = [ f ' draw { num } card ' for num in num_to_search ]
discard_patterns = [
' discard the rest ' ,
' for each card drawn this way, discard ' ,
' if you do, discard ' ,
' then discard '
]
2025-01-14 10:10:30 -08:00
has_draw = tag_utils . create_text_mask ( df , draw_patterns )
has_discard = tag_utils . create_text_mask ( df , discard_patterns )
2025-01-02 13:00:52 -08:00
return ~ has_other_loot & has_draw & has_discard
2024-12-16 22:58:02 -08:00
2025-01-02 13:00:52 -08:00
def create_connive_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with connive effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have connive effects
"""
2025-01-14 10:10:30 -08:00
has_keyword = tag_utils . create_keyword_mask ( df , ' Connive ' )
has_text = tag_utils . create_text_mask ( df , ' connives? ' )
2025-01-02 13:00:52 -08:00
return has_keyword | has_text
def create_cycling_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with cycling effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have cycling effects
"""
2025-01-14 10:10:30 -08:00
has_keyword = tag_utils . create_keyword_mask ( df , ' Cycling ' )
has_text = tag_utils . create_text_mask ( df , ' cycling ' )
2025-01-02 13:00:52 -08:00
return has_keyword | has_text
def create_blood_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with blood token effects.
2024-12-09 12:56:01 -08:00
2025-01-02 13:00:52 -08:00
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have blood token effects
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , ' blood token ' )
2025-01-02 13:00:52 -08:00
def tag_for_loot_effects ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with loot-like effects using vectorized operations.
This function handles tagging of all loot - like effects including :
- Standard loot ( draw + discard )
- Connive
- Cycling
- Blood tokens
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging loot-like effects in { color } _cards.csv ' )
2025-01-02 13:00:52 -08:00
# Create masks for each effect type
loot_mask = create_loot_mask ( df )
connive_mask = create_connive_mask ( df )
cycling_mask = create_cycling_mask ( df )
blood_mask = create_blood_mask ( df )
# Apply tags based on masks
if loot_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , loot_mask , [ ' Loot ' , ' Card Draw ' ] )
logger . info ( f ' Tagged { loot_mask . sum ( ) } cards with standard loot effects ' )
2025-01-02 13:00:52 -08:00
if connive_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , connive_mask , [ ' Connive ' , ' Loot ' , ' Card Draw ' ] )
logger . info ( f ' Tagged { connive_mask . sum ( ) } cards with connive effects ' )
2025-01-02 13:00:52 -08:00
if cycling_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , cycling_mask , [ ' Cycling ' , ' Loot ' , ' Card Draw ' ] )
logger . info ( f ' Tagged { cycling_mask . sum ( ) } cards with cycling effects ' )
2025-01-02 13:00:52 -08:00
if blood_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , blood_mask , [ ' Blood Tokens ' , ' Loot ' , ' Card Draw ' ] )
logger . info ( f ' Tagged { blood_mask . sum ( ) } cards with blood token effects ' )
2025-01-02 13:00:52 -08:00
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed tagging loot-like effects ' )
2025-01-02 13:00:52 -08:00
## Sacrifice or pay life to draw effects
def tag_for_cost_draw ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that draw cards by paying life or sacrificing permanents.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagging cost-based draw effects in %s _cards.csv ' , color )
2025-01-02 13:00:52 -08:00
# Split into life and sacrifice patterns
life_pattern = ' life: draw '
life_mask = df [ ' text ' ] . str . contains ( life_pattern , case = False , na = False )
sac_patterns = [
r ' sacrifice (?:a|an) (?:artifact|creature|permanent)(?:[^,]*),?[^,]*draw ' ,
r ' sacrifice [^:]+: draw ' ,
r ' sacrificed[^,]+, draw '
]
sac_mask = df [ ' text ' ] . str . contains ( ' | ' . join ( sac_patterns ) , case = False , na = False , regex = True )
# Apply life draw tags
if life_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , life_mask , [ ' Life to Draw ' , ' Card Draw ' ] )
logger . info ( ' Tagged %d cards with life payment draw effects ' , life_mask . sum ( ) )
2025-01-02 13:00:52 -08:00
# Apply sacrifice draw tags
if sac_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , sac_mask , [ ' Sacrifice to Draw ' , ' Card Draw ' ] )
logger . info ( ' Tagged %d cards with sacrifice draw effects ' , sac_mask . sum ( ) )
2025-01-02 13:00:52 -08:00
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed tagging cost-based draw effects ' )
2025-01-02 13:00:52 -08:00
## Replacement effects, that might have you draw more cards
def create_replacement_draw_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with replacement draw effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have replacement draw effects
"""
# Create trigger patterns
trigger_patterns = [ ]
for trigger in triggers :
trigger_patterns . extend ( [
f ' { trigger } a player.*instead.*draw ' ,
f ' { trigger } an opponent.*instead.*draw ' ,
f ' { trigger } the beginning of your draw step.*instead.*draw ' ,
f ' { trigger } you.*instead.*draw '
] )
# Create other replacement patterns
replacement_patterns = [
' if a player would.*instead.*draw ' ,
' if an opponent would.*instead.*draw ' ,
' if you would.*instead.*draw '
]
# Combine all patterns
all_patterns = ' | ' . join ( trigger_patterns + replacement_patterns )
# Create base mask for replacement effects
2025-01-14 10:10:30 -08:00
base_mask = tag_utils . create_text_mask ( df , all_patterns )
2025-01-02 13:00:52 -08:00
# Add mask for specific card numbers
number_patterns = [ f ' draw { num } card ' for num in num_to_search ]
2025-01-14 10:10:30 -08:00
number_mask = tag_utils . create_text_mask ( df , number_patterns )
2025-01-02 13:00:52 -08:00
# Add mask for non-specific numbers
2025-01-14 10:10:30 -08:00
nonspecific_mask = tag_utils . create_text_mask ( df , ' draw that many plus|draws that many plus ' ) # df['text'].str.contains('draw that many plus|draws that many plus', case=False, na=False)
2025-01-02 13:00:52 -08:00
return base_mask & ( number_mask | nonspecific_mask )
def create_replacement_draw_exclusion_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that should be excluded from replacement draw effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards should be excluded
"""
# Create tag-based exclusions
excluded_tags = settings . DRAW_RELATED_TAGS
2025-01-14 10:10:30 -08:00
tag_mask = tag_utils . create_tag_mask ( df , excluded_tags )
2025-01-02 13:00:52 -08:00
# Create text-based exclusions
text_patterns = settings . DRAW_EXCLUSION_PATTERNS + [ ' skips that turn instead ' ]
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , text_patterns )
2025-01-02 13:00:52 -08:00
return tag_mask | text_mask
def tag_for_replacement_draw ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that have replacement draw effects using vectorized operations.
This function identifies and tags cards that modify or replace card draw effects ,
such as drawing additional cards or replacing normal draw effects with other effects .
2024-12-31 12:04:10 -08:00
Args :
2025-01-02 13:00:52 -08:00
df : DataFrame containing card data
color : Color identifier for logging purposes
Example patterns tagged :
- Trigger - based replacement effects ( " whenever you draw...instead " )
- Conditional replacement effects ( " if you would draw...instead " )
- Specific card number replacements
- Non - specific card number replacements ( " draw that many plus " )
2024-12-31 12:04:10 -08:00
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging replacement draw effects in { color } _cards.csv ' )
2024-12-31 12:04:10 -08:00
try :
2025-01-02 13:00:52 -08:00
# Create replacement draw mask
replacement_mask = create_replacement_draw_mask ( df )
2024-12-31 12:04:10 -08:00
2025-01-02 13:00:52 -08:00
# Create exclusion mask
exclusion_mask = create_replacement_draw_exclusion_mask ( df )
2024-12-31 12:04:10 -08:00
2025-01-02 13:00:52 -08:00
# Add specific card names
2025-01-14 10:10:30 -08:00
specific_cards_mask = tag_utils . create_name_mask ( df , ' sylvan library ' )
2024-12-31 12:04:10 -08:00
2025-01-02 13:00:52 -08:00
# Combine masks
final_mask = ( replacement_mask & ~ exclusion_mask ) | specific_cards_mask
2024-12-31 12:04:10 -08:00
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Replacement Draw ' , ' Card Draw ' ] )
2024-12-31 12:04:10 -08:00
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with replacement draw effects ' )
2024-12-31 12:04:10 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging replacement draw effects: { str ( e ) } ' )
2024-12-31 12:04:10 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed tagging replacement draw effects in { color } _cards.csv ' )
2024-12-16 22:58:02 -08:00
2025-01-02 13:00:52 -08:00
## Wheels
def tag_for_wheels ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that have wheel effects or care about drawing/discarding cards.
This function identifies and tags cards that :
- Force excess draw and discard
- Have payoffs for drawing / discarding
- Care about wheel effects
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging " Wheel " effects in { color } _cards.csv ' )
2025-01-02 13:00:52 -08:00
try :
# Create masks for different wheel conditions
2025-01-08 08:29:00 -08:00
# Define text patterns for wheel effects
wheel_patterns = [
' an opponent draws a card ' ,
' cards you \' ve drawn ' ,
' draw your second card ' ,
' draw that many cards ' ,
' draws an additional card ' ,
' draws a card ' ,
' draws cards ' ,
' draws half that many cards ' ,
' draws their first second card ' ,
' draws their second second card ' ,
' draw two cards instead ' ,
' draws two additional cards ' ,
' discards that card ' ,
' discards their hand, then draws ' ,
' each card your opponents have drawn ' ,
' each draw a card ' ,
' each opponent draws a card ' ,
' each player draws ' ,
' has no cards in hand ' ,
' have no cards in hand ' ,
' may draw a card ' ,
' maximum hand size ' ,
' no cards in it, you win the game instead ' ,
' opponent discards ' ,
' you draw a card ' ,
' whenever you draw a card '
]
wheel_cards = [
' arcane denial ' , ' bloodchief ascension ' , ' dark deal ' , ' elenda and azor ' , ' elixir of immortality ' ,
' forced fruition ' , ' glunch, the bestower ' , ' kiora the rising tide ' , ' kynaios and tiro of meletis ' ,
' library of leng ' , ' loran of the third path ' , ' mr. foxglove ' , ' raffine, scheming seer ' ,
' sauron, the dark lord ' , ' seizan, perverter of truth ' , ' triskaidekaphile ' , ' twenty-toed toad ' ,
' waste not ' , ' wedding ring ' , ' whispering madness '
]
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , wheel_patterns )
name_mask = tag_utils . create_name_mask ( df , wheel_cards )
2025-01-02 13:00:52 -08:00
# Combine masks
final_mask = text_mask | name_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Card Draw ' , ' Wheels ' ] )
2025-01-02 13:00:52 -08:00
# Add Draw Triggers tag for cards with trigger words
trigger_pattern = ' | ' . join ( triggers )
trigger_mask = final_mask & df [ ' text ' ] . str . contains ( trigger_pattern , case = False , na = False )
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , trigger_mask , [ ' Draw Triggers ' ] )
2025-01-02 13:00:52 -08:00
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with " Wheel " effects ' )
2025-01-02 13:00:52 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging " Wheel " effects: { str ( e ) } ' )
2025-01-02 13:00:52 -08:00
raise
2024-12-16 22:58:02 -08:00
### Artifacts
2025-01-08 08:29:00 -08:00
def tag_for_artifacts ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about Artifacts or are specific kinds of Artifacts
( i . e . Equipment or Vehicles ) .
This function identifies and tags cards with Artifact - related effects including :
- Creating Artifact tokens
- Casting Artifact spells
- Equipment
- Vehicles
The function maintains proper tag hierarchy and ensures consistent application
of related tags like ' Card Draw ' , ' Spellslinger ' , etc .
Args :
df : DataFrame containing card data to process
color : Color identifier for logging purposes ( e . g . ' white ' , ' blue ' )
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting " Artifact " and " Artifacts Matter " tagging for { color } _cards.csv ' )
2025-01-02 13:00:52 -08:00
print ( ' \n ========== \n ' )
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' text ' , ' themeTags ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 08:29:00 -08:00
# Process each type of draw effect
tag_for_artifact_tokens ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Artifact token tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
tag_equipment ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Equipment tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
tag_vehicles ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Vehicle tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
# Log completion and performance metrics
duration = pd . Timestamp . now ( ) - start_time
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed all " Artifact " and " Artifacts Matter " tagging in { duration . total_seconds ( ) : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_artifacts: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2024-12-16 22:58:02 -08:00
## Artifact Tokens
2025-01-02 13:00:52 -08:00
def tag_for_artifact_tokens ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that create or care about artifact tokens using vectorized operations.
2024-12-16 22:58:02 -08:00
2025-01-02 13:00:52 -08:00
This function handles tagging of :
- Generic artifact token creation
- Predefined artifact token types ( Treasure , Food , etc )
- Fabricate keyword
2025-01-08 12:06:33 -08:00
The function applies both generic artifact token tags and specific token type tags
( e . g . , ' Treasure Token ' , ' Food Token ' ) based on the tokens created .
2025-01-02 13:00:52 -08:00
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( ' Setting artifact token tags on %s _cards.csv ' , color )
2025-01-02 13:00:52 -08:00
start_time = pd . Timestamp . now ( )
try :
# Tag generic artifact tokens
generic_mask = create_generic_artifact_mask ( df )
if generic_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , generic_mask ,
2025-01-02 13:00:52 -08:00
[ ' Artifact Tokens ' , ' Artifacts Matter ' , ' Token Creation ' , ' Tokens Matter ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d cards with generic artifact token effects ' , generic_mask . sum ( ) )
2025-01-02 13:00:52 -08:00
# Tag predefined artifact tokens
2025-01-08 12:06:33 -08:00
predefined_mask , token_map = create_predefined_artifact_mask ( df )
2025-01-02 13:00:52 -08:00
if predefined_mask . any ( ) :
2025-01-08 12:06:33 -08:00
# Apply base artifact token tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , predefined_mask ,
2025-01-02 13:00:52 -08:00
[ ' Artifact Tokens ' , ' Artifacts Matter ' , ' Token Creation ' , ' Tokens Matter ' ] )
2025-01-08 12:06:33 -08:00
# Track token type counts
2025-01-08 16:04:42 -08:00
token_counts = { } # type: dict
2025-01-08 12:06:33 -08:00
# Apply specific token type tags
for idx , token_type in token_map . items ( ) :
specific_tag = f ' { token_type } Token '
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df . loc [ idx : idx ] , pd . Series ( [ True ] , index = [ idx ] ) , [ specific_tag ] )
2025-01-08 12:06:33 -08:00
token_counts [ token_type ] = token_counts . get ( token_type , 0 ) + 1
# Log results with token type counts
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d cards with predefined artifact tokens: ' , predefined_mask . sum ( ) )
2025-01-08 12:06:33 -08:00
for token_type , count in token_counts . items ( ) :
2025-01-14 10:10:30 -08:00
logger . info ( ' - %s : %d cards ' , token_type , count )
2025-01-02 13:00:52 -08:00
# Tag fabricate cards
fabricate_mask = create_fabricate_mask ( df )
if fabricate_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , fabricate_mask ,
2025-01-02 13:00:52 -08:00
[ ' Artifact Tokens ' , ' Artifacts Matter ' , ' Token Creation ' , ' Tokens Matter ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d cards with Fabricate ' , fabricate_mask . sum ( ) )
2025-01-02 13:00:52 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed artifact token tagging in %.2f s ' , duration )
2025-01-02 13:00:52 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( ' Error in tag_for_artifact_tokens: %s ' , str ( e ) )
2025-01-02 13:00:52 -08:00
raise
# Generic Artifact tokens, such as karnstructs, or artifact soldiers
def create_generic_artifact_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that create non-predefined artifact tokens.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards create generic artifact tokens
"""
# Exclude specific cards
excluded_cards = [
' diabolical salvation ' ,
' lifecraft awakening ' ,
' sandsteppe war riders ' ,
' transmutation font '
]
2025-01-14 10:10:30 -08:00
name_exclusions = tag_utils . create_name_mask ( df , excluded_cards )
2025-01-02 13:00:52 -08:00
# Create text pattern matches
create_pattern = r ' create|put '
2025-01-14 10:10:30 -08:00
has_create = tag_utils . create_text_mask ( df , create_pattern )
2025-01-02 13:00:52 -08:00
token_patterns = [
' artifact creature token ' ,
' artifact token ' ,
' construct artifact ' ,
' copy of enchanted artifact ' ,
' copy of target artifact ' ,
' copy of that artifact '
]
2025-01-14 10:10:30 -08:00
has_token = tag_utils . create_text_mask ( df , token_patterns )
2025-01-02 13:00:52 -08:00
# Named cards that create artifact tokens
named_cards = [
2025-01-08 12:06:33 -08:00
' bloodforged battle-axe ' , ' court of vantress ' , ' elmar, ulvenwald informant ' ,
2025-01-02 13:00:52 -08:00
' faerie artisans ' , ' feldon of the third path ' , ' lenoardo da vinci ' ,
' march of progress ' , ' nexus of becoming ' , ' osgir, the reconstructor ' ,
' prototype portal ' , ' red sun \' s twilight ' , ' saheeli, the sun \' s brilliance ' ,
' season of weaving ' , ' shaun, father of synths ' , ' sophia, dogged detective ' ,
' vaultborn tyrant ' , ' wedding ring '
]
2025-01-14 10:10:30 -08:00
named_matches = tag_utils . create_name_mask ( df , named_cards )
2025-01-02 13:00:52 -08:00
# Exclude fabricate cards
2025-01-14 10:10:30 -08:00
has_fabricate = tag_utils . create_text_mask ( df , ' fabricate ' )
2025-01-02 13:00:52 -08:00
return ( has_create & has_token & ~ name_exclusions & ~ has_fabricate ) | named_matches
2025-01-08 12:06:33 -08:00
def create_predefined_artifact_mask ( df : pd . DataFrame ) - > tuple [ pd . Series , dict [ int , str ] ] :
""" Create a boolean mask for cards that create predefined artifact tokens and track token types.
2025-01-02 13:00:52 -08:00
Args :
df : DataFrame to search
Returns :
2025-01-08 12:06:33 -08:00
Tuple containing :
- Boolean Series indicating which cards create predefined artifact tokens
- Dictionary mapping row indices to their matched token types
2025-01-02 13:00:52 -08:00
"""
# Create base mask for 'create' text
2025-01-08 08:29:00 -08:00
create_pattern = r ' create|put '
2025-01-14 10:10:30 -08:00
has_create = tag_utils . create_text_mask ( df , create_pattern )
2025-01-02 13:00:52 -08:00
2025-01-08 12:06:33 -08:00
# Initialize token mapping dictionary
token_map = { }
2025-01-02 13:00:52 -08:00
# Create masks for each token type
token_masks = [ ]
2025-01-08 08:29:00 -08:00
2025-01-02 13:00:52 -08:00
for token in settings . artifact_tokens :
2025-01-14 10:10:30 -08:00
token_mask = tag_utils . create_text_mask ( df , token . lower ( ) )
2025-01-02 13:00:52 -08:00
# Handle exclusions
if token == ' Blood ' :
token_mask & = df [ ' name ' ] != ' Bloodroot Apothecary '
elif token == ' Gold ' :
token_mask & = ~ df [ ' name ' ] . isin ( [ ' Goldspan Dragon ' , ' The Golden-Gear Colossus ' ] )
elif token == ' Junk ' :
token_mask & = df [ ' name ' ] != ' Junkyard Genius '
2025-01-08 12:06:33 -08:00
# Store token type for matching rows
matching_indices = df [ token_mask ] . index
for idx in matching_indices :
if idx not in token_map : # Only store first match
token_map [ idx ] = token
2025-01-02 13:00:52 -08:00
token_masks . append ( token_mask )
# Combine all token masks
2025-01-08 12:06:33 -08:00
final_mask = has_create & pd . concat ( token_masks , axis = 1 ) . any ( axis = 1 )
2025-01-02 13:00:52 -08:00
2025-01-08 12:06:33 -08:00
return final_mask , token_map
2025-01-02 13:00:52 -08:00
def create_fabricate_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with fabricate keyword.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have fabricate
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , ' fabricate ' )
2024-12-16 22:58:02 -08:00
## Artifact Triggers
2025-01-08 08:29:00 -08:00
def create_artifact_triggers_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that care about artifacts.
2024-12-11 12:03:39 -08:00
2025-01-08 08:29:00 -08:00
Args :
df : DataFrame to search
2024-12-11 18:29:38 -08:00
2025-01-08 08:29:00 -08:00
Returns :
Boolean Series indicating which cards care about artifacts
"""
# Define artifact-related patterns
ability_patterns = [
' abilities of artifact ' , ' ability of artifact '
]
2024-12-10 15:32:25 -08:00
2025-01-08 08:29:00 -08:00
artifact_state_patterns = [
' are artifacts in addition ' , ' artifact enters ' , ' number of artifacts ' ,
' number of other artifacts ' , ' number of tapped artifacts ' ,
' number of artifact '
]
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
artifact_type_patterns = [
' all artifact ' , ' another artifact ' , ' another target artifact ' ,
' artifact card ' , ' artifact creature you control ' ,
' artifact creatures you control ' , ' artifact you control ' ,
' artifacts you control ' , ' each artifact ' , ' target artifact '
]
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
casting_patterns = [
' affinity for artifacts ' , ' artifact spells as though they had flash ' ,
' artifact spells you cast ' , ' cast an artifact ' , ' choose an artifact ' ,
' whenever you cast a noncreature ' , ' whenever you cast an artifact '
]
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
counting_patterns = [
' mana cost among artifact ' , ' mana value among artifact ' ,
' artifact with the highest mana value ' ,
]
2024-12-11 16:56:31 -08:00
2025-01-08 08:29:00 -08:00
search_patterns = [
' search your library for an artifact '
]
2024-12-31 12:04:10 -08:00
2025-01-08 08:29:00 -08:00
trigger_patterns = [
' whenever a nontoken artifact ' , ' whenever an artifact ' ,
' whenever another nontoken artifact ' , ' whenever one or more artifact '
]
2024-12-31 12:04:10 -08:00
2025-01-08 08:29:00 -08:00
# Combine all patterns
all_patterns = (
ability_patterns + artifact_state_patterns + artifact_type_patterns +
casting_patterns + counting_patterns + search_patterns + trigger_patterns +
[ ' metalcraft ' , ' prowess ' , ' copy of any artifact ' ]
)
2024-12-31 12:04:10 -08:00
2025-01-08 08:29:00 -08:00
# Create pattern string
pattern = ' | ' . join ( all_patterns )
2024-12-11 18:29:38 -08:00
2025-01-08 08:29:00 -08:00
# Create mask
return df [ ' text ' ] . str . contains ( pattern , case = False , na = False , regex = True )
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
def tag_for_artifact_triggers ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about artifacts using vectorized operations.
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
This function identifies and tags cards that :
- Have abilities that trigger off artifacts
- Care about artifact states or counts
- Interact with artifact spells or permanents
- Have metalcraft or similar mechanics
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging cards that care about artifacts in { color } _cards.csv ' )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
try :
# Create artifact triggers mask
triggers_mask = create_artifact_triggers_mask ( df )
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , triggers_mask , [ ' Artifacts Matter ' ] )
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { triggers_mask . sum ( ) } cards with artifact triggers in { duration : .2f } s ' )
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging artifact triggers: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2024-12-16 22:58:02 -08:00
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed tagging cards that care about artifacts in { color } _cards.csv ' )
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
## Equipment
def create_equipment_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that are Equipment
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
This function identifies cards that :
- Have the Equipment subtype
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
Args :
df : DataFrame containing card data
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
Returns :
Boolean Series indicating which cards are Equipment
"""
# Create type-based mask
2025-01-14 10:10:30 -08:00
type_mask = tag_utils . create_type_mask ( df , ' Equipment ' )
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
return type_mask
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
def create_equipment_cares_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that care about Equipment.
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
This function identifies cards that :
- Have abilities that trigger off Equipment
- Care about equipped creatures
- Modify Equipment or equipped creatures
- Have Equipment - related keywords
Args :
df : DataFrame containing card data
Returns :
Boolean Series indicating which cards care about Equipment
"""
# Create text pattern mask
text_patterns = [
' equipment you control ' ,
' equipped creature ' ,
' attach ' ,
' equip ' ,
' equipment spells ' ,
' equipment abilities ' ,
' modified ' ,
' reconfigure '
]
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 08:29:00 -08:00
# Create keyword mask
keyword_patterns = [ ' Modified ' , ' Equip ' , ' Reconfigure ' ]
2025-01-14 10:10:30 -08:00
keyword_mask = tag_utils . create_keyword_mask ( df , keyword_patterns )
2025-01-08 08:29:00 -08:00
# Create specific cards mask
specific_cards = settings . EQUIPMENT_SPECIFIC_CARDS
2025-01-14 10:10:30 -08:00
name_mask = tag_utils . create_name_mask ( df , specific_cards )
2025-01-08 08:29:00 -08:00
return text_mask | keyword_mask | name_mask
def tag_equipment ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that are Equipment or care about Equipment using vectorized operations.
This function identifies and tags :
- Equipment cards
- Cards that care about Equipment
- Cards with Equipment - related abilities
- Cards that modify Equipment or equipped creatures
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagging Equipment cards in %s _cards.csv ' , color )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create equipment mask
equipment_mask = create_equipment_mask ( df )
if equipment_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , equipment_mask , [ ' Equipment ' , ' Equipment Matters ' , ' Voltron ' ] )
logger . info ( ' Tagged %d Equipment cards ' , equipment_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
# Create equipment cares mask
cares_mask = create_equipment_cares_mask ( df )
if cares_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , cares_mask ,
2025-01-08 08:29:00 -08:00
[ ' Artifacts Matter ' , ' Equipment Matters ' , ' Voltron ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d cards that care about Equipment ' , cares_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Equipment tagging in %.2f s ' , duration )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( ' Error tagging Equipment cards: %s ' , str ( e ) )
2025-01-08 08:29:00 -08:00
raise
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
## Vehicles
def create_vehicle_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that are Vehicles or care about Vehicles.
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
This function identifies cards that :
- Have the Vehicle subtype
- Have crew abilities
- Care about Vehicles or Pilots
Args :
df : DataFrame containing card data
Returns :
Boolean Series indicating which cards are Vehicles or care about them
"""
# Create type-based mask
2025-01-14 10:10:30 -08:00
type_mask = tag_utils . create_type_mask ( df , [ ' Vehicle ' , ' Pilot ' ] )
2025-01-08 08:29:00 -08:00
# Create text-based mask
text_patterns = [
' vehicle ' , ' crew ' , ' pilot ' ,
]
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 08:29:00 -08:00
return type_mask | text_mask
def tag_vehicles ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that are Vehicles or care about Vehicles using vectorized operations.
This function identifies and tags :
- Vehicle cards
- Pilot cards
- Cards that care about Vehicles
- Cards with crew abilities
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagging Vehicle cards in %s _cards.csv ' , color )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create vehicle mask
vehicle_mask = create_vehicle_mask ( df )
if vehicle_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , vehicle_mask ,
2025-01-08 08:29:00 -08:00
[ ' Artifacts Matter ' , ' Vehicles ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d Vehicle-related cards ' , vehicle_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Vehicle tagging in %.2f s ' , duration )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( ' Error tagging Vehicle cards: %s ' , str ( e ) )
2025-01-08 08:29:00 -08:00
raise
### Enchantments
def tag_for_enchantments ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about Enchantments or are specific kinds of Enchantments
( i . e . Equipment or Vehicles ) .
This function identifies and tags cards with Enchantment - related effects including :
- Creating Enchantment tokens
- Casting Enchantment spells
- Auras
- Constellation
- Cases
- Rooms
- Classes
- Backrounds
- Shrines
The function maintains proper tag hierarchy and ensures consistent application
of related tags like ' Card Draw ' , ' Spellslinger ' , etc .
Args :
df : DataFrame containing card data to process
color : Color identifier for logging purposes ( e . g . ' white ' , ' blue ' )
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting " Enchantment " and " Enchantments Matter " tagging for { color } _cards.csv ' )
2025-01-02 13:00:52 -08:00
print ( ' \n ========== \n ' )
2025-01-08 08:29:00 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' text ' , ' themeTags ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 08:29:00 -08:00
# Process each type of enchantment effect
tag_for_enchantment_tokens ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Enchantment token tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
tag_for_enchantments_matter ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed " Enchantments Matter " tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
tag_auras ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Aura tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
tag_constellation ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Constellation tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
2024-12-12 12:27:29 -08:00
2025-01-08 08:29:00 -08:00
tag_sagas ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Saga tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
2024-12-10 16:43:03 -08:00
2025-01-08 08:29:00 -08:00
tag_cases ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Case tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
tag_rooms ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Room tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
tag_backgrounds ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Background tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
2024-12-10 16:43:03 -08:00
2025-01-08 08:29:00 -08:00
tag_shrines ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Shrine tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
# Log completion and performance metrics
duration = pd . Timestamp . now ( ) - start_time
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed all " Enchantment " and " Enchantments Matter " tagging in { duration . total_seconds ( ) : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_artifacts: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
## Enchantment tokens
def tag_for_enchantment_tokens ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that create or care about enchantment tokens using vectorized operations.
This function handles tagging of :
- Generic enchantmeny token creation
- Predefined enchantment token types ( Roles , Shards , etc )
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( ' Setting ehcantment token tags on %s _cards.csv ' , color )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Tag generic artifact tokens
generic_mask = create_generic_enchantment_mask ( df )
if generic_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , generic_mask ,
2025-01-08 08:29:00 -08:00
[ ' Enchantment Tokens ' , ' Enchantments Matter ' , ' Token Creation ' , ' Tokens Matter ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d cards with generic enchantment token effects ' , generic_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
# Tag predefined artifact tokens
predefined_mask = create_predefined_enchantment_mask ( df )
if predefined_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , predefined_mask ,
2025-01-08 08:29:00 -08:00
[ ' Enchantment Tokens ' , ' Enchantments Matter ' , ' Token Creation ' , ' Tokens Matter ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d cards with predefined enchantment tokens ' , predefined_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed enchantment token tagging in %.2f s ' , duration )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( ' Error in tag_for_enchantment_tokens: %s ' , str ( e ) )
2025-01-08 08:29:00 -08:00
raise
def create_generic_enchantment_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that create non-predefined enchantment tokens.
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
Args :
df : DataFrame to search
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
Returns :
Boolean Series indicating which cards create generic enchantmnet tokens
"""
# Create text pattern matches
create_pattern = r ' create|put '
2025-01-14 10:10:30 -08:00
has_create = tag_utils . create_text_mask ( df , create_pattern )
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
token_patterns = [
' copy of enchanted enchantment ' ,
' copy of target enchantment ' ,
' copy of that enchantment ' ,
' enchantment creature token ' ,
' enchantment token '
]
2025-01-14 10:10:30 -08:00
has_token = tag_utils . create_text_mask ( df , token_patterns )
2025-01-08 08:29:00 -08:00
# Named cards that create enchantment tokens
named_cards = [
' court of vantress ' ,
' fellhide spiritbinder ' ,
' hammer of purphoros '
]
2025-01-14 10:10:30 -08:00
named_matches = tag_utils . create_name_mask ( df , named_cards )
2025-01-08 08:29:00 -08:00
return ( has_create & has_token ) | named_matches
def create_predefined_enchantment_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that create non-predefined enchantment tokens.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards create generic enchantmnet tokens
"""
# Create text pattern matches
has_create = df [ ' text ' ] . str . contains ( ' create ' , case = False , na = False )
# Create masks for each token type
token_masks = [ ]
for token in settings . enchantment_tokens :
2025-01-14 10:10:30 -08:00
token_mask = tag_utils . create_text_mask ( df , token . lower ( ) )
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
token_masks . append ( token_mask )
2024-12-11 18:29:38 -08:00
2025-01-08 08:29:00 -08:00
return has_create & pd . concat ( token_masks , axis = 1 ) . any ( axis = 1 )
## General enchantments matter
def tag_for_enchantments_matter ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about enchantments using vectorized operations.
This function identifies and tags cards that :
- Have abilities that trigger off enchantments
- Care about enchantment states or counts
- Interact with enchantment spells or permanents
- Have constellation or similar mechanics
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging cards that care about enchantments in { color } _cards.csv ' )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create enchantment triggers mask
# Define enchantment-related patterns
ability_patterns = [
' abilities of enchantment ' , ' ability of enchantment '
]
state_patterns = [
' are enchantments in addition ' , ' enchantment enters '
]
type_patterns = [
' all enchantment ' , ' another enchantment ' , ' enchantment card ' ,
' enchantment creature you control ' , ' enchantment creatures you control ' ,
' enchantment you control ' , ' enchantments you control '
]
casting_patterns = [
' cast an enchantment ' , ' enchantment spells as though they had flash ' ,
' enchantment spells you cast '
]
counting_patterns = [
' mana value among enchantment ' , ' number of enchantment '
]
search_patterns = [
' search your library for an enchantment '
]
trigger_patterns = [
' whenever a nontoken enchantment ' , ' whenever an enchantment ' ,
' whenever another nontoken enchantment ' , ' whenever one or more enchantment '
]
# Combine all patterns
all_patterns = (
ability_patterns + state_patterns + type_patterns +
casting_patterns + counting_patterns + search_patterns + trigger_patterns
)
2025-01-14 10:10:30 -08:00
triggers_mask = tag_utils . create_text_mask ( df , all_patterns )
2025-01-08 08:29:00 -08:00
# Create exclusion mask
2025-01-14 10:10:30 -08:00
exclusion_mask = tag_utils . create_name_mask ( df , ' luxa river shrine ' )
2025-01-08 08:29:00 -08:00
# Combine masks
final_mask = triggers_mask & ~ exclusion_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Enchantments Matter ' ] )
2025-01-08 08:29:00 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with enchantment triggers in { duration : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging enchantment triggers: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed tagging cards that care about enchantments in { color } _cards.csv ' )
2025-01-08 08:29:00 -08:00
## Aura
def tag_auras ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that are Auras or care about Auras using vectorized operations.
This function identifies cards that :
- Have abilities that trigger off Auras
- Care about enchanted permanents
- Modify Auras or enchanted permanents
- Have Aura - related keywords
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagging Aura cards in %s _cards.csv ' , color )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create Aura mask
2025-01-14 10:10:30 -08:00
aura_mask = tag_utils . create_type_mask ( df , ' Aura ' )
2025-01-08 08:29:00 -08:00
if aura_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , aura_mask ,
2025-01-08 08:29:00 -08:00
[ ' Auras ' , ' Enchantments Matter ' , ' Voltron ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d Aura cards ' , aura_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
# Create cares mask
text_patterns = [
' aura ' ,
' aura enters ' ,
' aura you control enters ' ,
' enchanted '
]
2025-01-14 10:10:30 -08:00
cares_mask = tag_utils . create_text_mask ( df , text_patterns ) | tag_utils . create_name_mask ( df , settings . AURA_SPECIFIC_CARDS )
2025-01-08 08:29:00 -08:00
if cares_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , cares_mask ,
2025-01-08 08:29:00 -08:00
[ ' Auras ' , ' Enchantments Matter ' , ' Voltron ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d cards that care about Auras ' , cares_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Aura tagging in %.2f s ' , duration )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( ' Error tagging Aura cards: %s ' , str ( e ) )
2025-01-08 08:29:00 -08:00
raise
## Constellation
def tag_constellation ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with Constellation using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging Constellation cards in { color } _cards.csv ' )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create mask for constellation keyword
2025-01-14 10:10:30 -08:00
constellation_mask = tag_utils . create_keyword_mask ( df , ' Constellation ' )
2025-01-08 08:29:00 -08:00
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , constellation_mask , [ ' Constellation ' , ' Enchantments Matter ' ] )
2025-01-08 08:29:00 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { constellation_mask . sum ( ) } Constellation cards in { duration : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging Constellation cards: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed tagging Constellation cards ' )
2025-01-08 08:29:00 -08:00
## Sagas
def tag_sagas ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with the Saga type using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : if required DataFramecolumns are missing
"""
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagging Saga cards in %s _cards.csv ' , color )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create mask for Saga type
2025-01-14 10:10:30 -08:00
saga_mask = tag_utils . create_type_mask ( df , ' Saga ' )
2025-01-08 08:29:00 -08:00
if saga_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , saga_mask ,
2025-01-08 08:29:00 -08:00
[ ' Enchantments Matter ' , ' Sagas Matter ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d Saga cards ' , saga_mask . sum ( ) )
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
# Create mask for cards that care about Sagas
text_patterns = [
' saga ' ,
' put a saga ' ,
' final chapter ' ,
' lore counter '
]
2025-01-14 10:10:30 -08:00
cares_mask = tag_utils . create_text_mask ( df , text_patterns ) # create_saga_cares_mask(df)
2025-01-08 08:29:00 -08:00
if cares_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , cares_mask ,
2025-01-08 08:29:00 -08:00
[ ' Enchantments Matter ' , ' Sagas Matter ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d cards that care about Sagas ' , cares_mask . sum ( ) )
2024-12-16 22:58:02 -08:00
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Saga tagging in %.2f s ' , duration )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging Saga cards: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed tagging Saga cards ' )
2025-01-08 08:29:00 -08:00
## Cases
def tag_cases ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with the Case subtype using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : if required DataFramecolumns are missing
"""
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagging Case cards in %s _cards.csv ' , color )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create mask for Case type
2025-01-14 10:10:30 -08:00
saga_mask = tag_utils . create_type_mask ( df , ' Case ' )
2025-01-08 08:29:00 -08:00
if saga_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , saga_mask ,
2025-01-08 08:29:00 -08:00
[ ' Enchantments Matter ' , ' Cases Matter ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d Saga cards ' , saga_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
# Create Case cares_mask
2025-01-14 10:10:30 -08:00
cares_mask = tag_utils . create_text_mask ( df , ' solve a case ' )
2025-01-08 08:29:00 -08:00
if cares_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , cares_mask ,
2025-01-08 08:29:00 -08:00
[ ' Enchantments Matter ' , ' Cases Matter ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d cards that care about Cases ' , cares_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Case tagging in %.2f s ' , duration )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging Case cards: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed tagging Case cards ' )
2025-01-08 08:29:00 -08:00
## Rooms
def tag_rooms ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with the room subtype using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : if required DataFramecolumns are missing
"""
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagging Room cards in %s _cards.csv ' , color )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create mask for Room type
2025-01-14 10:10:30 -08:00
room_mask = tag_utils . create_type_mask ( df , ' Room ' )
2025-01-08 08:29:00 -08:00
if room_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , room_mask ,
2025-01-08 08:29:00 -08:00
[ ' Enchantments Matter ' , ' Rooms Matter ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d Room cards ' , room_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
# Create keyword mask for rooms
2025-01-14 10:10:30 -08:00
keyword_mask = tag_utils . create_keyword_mask ( df , ' Eerie ' )
2025-01-08 08:29:00 -08:00
if keyword_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , keyword_mask ,
2025-01-08 08:29:00 -08:00
[ ' Enchantments Matter ' , ' Rooms Matter ' ] )
# Create rooms care mask
2025-01-14 10:10:30 -08:00
cares_mask = tag_utils . create_text_mask ( df , ' target room ' )
2025-01-08 08:29:00 -08:00
if cares_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , cares_mask ,
2025-01-08 08:29:00 -08:00
[ ' Enchantments Matter ' , ' Rooms Matter ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d cards that care about Rooms ' , cares_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Room tagging in %.2f s ' , duration )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging Room cards: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed tagging Room cards ' )
2025-01-08 08:29:00 -08:00
## Classes
def tag_classes ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with the Class subtype using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : if required DataFramecolumns are missing
"""
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagging Class cards in %s _cards.csv ' , color )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create mask for class type
2025-01-14 10:10:30 -08:00
class_mask = tag_utils . create_type_mask ( df , ' Class ' )
2025-01-08 08:29:00 -08:00
if class_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , class_mask ,
2025-01-08 08:29:00 -08:00
[ ' Enchantments Matter ' , ' Classes Matter ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d Class cards ' , class_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Class tagging in %.2f s ' , duration )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging Class cards: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed tagging Class cards ' )
2025-01-08 08:29:00 -08:00
## Background
def tag_backgrounds ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with the Background subtype or which let you choose a background using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : if required DataFramecolumns are missing
"""
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagging Background cards in %s _cards.csv ' , color )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create mask for background type
2025-01-14 10:10:30 -08:00
class_mask = tag_utils . create_type_mask ( df , ' Background ' )
2025-01-08 08:29:00 -08:00
if class_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , class_mask ,
2025-01-08 08:29:00 -08:00
[ ' Enchantments Matter ' , ' Backgrounds Matter ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d Background cards ' , class_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
# Create mask for Choose a Background
2025-01-14 10:10:30 -08:00
cares_mask = tag_utils . create_text_mask ( df , ' Background ' )
2025-01-08 08:29:00 -08:00
if cares_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , cares_mask ,
2025-01-08 08:29:00 -08:00
[ ' Enchantments Matter ' , ' Backgroundss Matter ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d cards that have Choose a Background ' , cares_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Background tagging in %.2f s ' , duration )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging Background cards: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed tagging Background cards ' )
2025-01-08 08:29:00 -08:00
## Shrines
def tag_shrines ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with the Shrine subtype using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : if required DataFramecolumns are missing
"""
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagging Shrine cards in %s _cards.csv ' , color )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create mask for shrine type
2025-01-14 10:10:30 -08:00
class_mask = tag_utils . create_type_mask ( df , ' Shrine ' )
2025-01-08 08:29:00 -08:00
if class_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , class_mask ,
2025-01-08 08:29:00 -08:00
[ ' Enchantments Matter ' , ' Shrines Matter ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d Shrine cards ' , class_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Shrine tagging in %.2f s ' , duration )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging Shrine cards: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed tagging Shrine cards ' )
2025-01-08 08:29:00 -08:00
### Exile Matters
## Exile Matter effects, such as Impuse draw, foretell, etc...
def tag_for_exile_matters ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about exiling cards and casting them from exile.
This function identifies and tags cards with cast - from exile effects such as :
- Cascade
- Discover
- Foretell
- Imprint
- Impulse
- Plot
- Susend
The function maintains proper tag hierarchy and ensures consistent application
of related tags like ' Card Draw ' , ' Spellslinger ' , etc .
Args :
df : DataFrame containing card data to process
color : Color identifier for logging purposes ( e . g . ' white ' , ' blue ' )
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting " Exile Matters " tagging for { color } _cards.csv ' )
2025-01-08 12:06:33 -08:00
print ( ' \n ========== \n ' )
2025-01-08 08:29:00 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' text ' , ' themeTags ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 08:29:00 -08:00
# Process each type of Exile matters effect
tag_for_general_exile_matters ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed general Exile Matters tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
tag_for_cascade ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Cascade tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
tag_for_discover ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Disxover tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
tag_for_foretell ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Foretell tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
tag_for_imprint ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Imprint tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
2024-12-12 12:27:29 -08:00
2025-01-08 08:29:00 -08:00
tag_for_impulse ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Impulse tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
tag_for_plot ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Plot tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
tag_for_suspend ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Suspend tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
# Log completion and performance metrics
duration = pd . Timestamp . now ( ) - start_time
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed all " Exile Matters " tagging in { duration . total_seconds ( ) : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_exile_matters: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
def tag_for_general_exile_matters ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that have a general care about casting from Exile theme.
This function identifies cards that :
- Trigger off casting a card from exile
- Trigger off playing a land from exile
- Putting cards into exile to later play
Args :
df : DataFrame containing card data
color : Color identifier for logging purpposes
Raises :
ValueError : if required DataFrame columns are missing
"""
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagging Exile Matters cards in %s _cards.csv ' , color )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create exile mask
text_patterns = [
' cards in exile ' ,
' cast a spell from exile ' ,
' cast but don \' t own ' ,
' cast from exile ' ,
' casts a spell from exile ' ,
' control but don \' t own ' ,
' exiled with ' ,
' from anywhere but their hand ' ,
' from anywhere but your hand ' ,
' from exile ' ,
' own in exile ' ,
' play a card from exile ' ,
' plays a card from exile ' ,
' play a land from exile ' ,
' plays a land from exile ' ,
' put into exile ' ,
' remains exiled '
]
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 08:29:00 -08:00
if text_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , text_mask , [ ' Exile Matters ' ] )
logger . info ( ' Tagged %d Exile Matters cards ' , text_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Exile Matters tagging in %.2f s ' , duration )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( ' Error tagging Exile Matters cards: %s ' , str ( e ) )
2025-01-08 08:29:00 -08:00
raise
## Cascade cards
def tag_for_cascade ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that have or otherwise give the Cascade ability
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagging Cascade cards in %s _cards.csv ' , color )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create Cascade mask
text_patterns = [
' gain cascade ' ,
' has cascade ' ,
' have cascade ' ,
' have " cascade ' ,
' with cascade ' ,
]
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 08:29:00 -08:00
if text_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , text_mask , [ ' Cascade ' , ' Exile Matters ' ] )
logger . info ( ' Tagged %d cards relating to Cascade ' , text_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
2025-01-14 10:10:30 -08:00
keyword_mask = tag_utils . create_keyword_mask ( df , ' Cascade ' )
2025-01-08 08:29:00 -08:00
if keyword_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , text_mask , [ ' Cascade ' , ' Exile Matters ' ] )
logger . info ( ' Tagged %d cards that have Cascade ' , keyword_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed Cascade tagging in %.2f s ' , duration )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( ' Error tagging Cacade cards: %s ' , str ( e ) )
2025-01-08 08:29:00 -08:00
raise
## Dsicover cards
def tag_for_discover ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with Discover using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging Discover cards in { color } _cards.csv ' )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create mask for Discover keyword
2025-01-14 10:10:30 -08:00
keyword_mask = tag_utils . create_keyword_mask ( df , ' Discover ' )
2025-01-08 08:29:00 -08:00
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , keyword_mask , [ ' Discover ' , ' Exile Matters ' ] )
2025-01-08 08:29:00 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { keyword_mask . sum ( ) } Discover cards in { duration : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging Discover cards: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed tagging Discover cards ' )
2025-01-08 08:29:00 -08:00
## Foretell cards, and cards that care about foretell
def tag_for_foretell ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with Foretell using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging Foretell cards in { color } _cards.csv ' )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create mask for Foretell keyword
2025-01-14 10:10:30 -08:00
keyword_mask = tag_utils . create_keyword_mask ( df , ' Foretell ' )
2025-01-08 08:29:00 -08:00
# Create mask for Foretell text
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , ' Foretell ' )
2025-01-08 08:29:00 -08:00
final_mask = keyword_mask | text_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Foretell ' , ' Exile Matters ' ] )
2025-01-08 08:29:00 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } Foretell cards in { duration : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging Foretell cards: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed tagging Foretell cards ' )
2025-01-08 08:29:00 -08:00
## Cards that have or care about imprint
def tag_for_imprint ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with Imprint using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging Imprint cards in { color } _cards.csv ' )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create mask for Imprint keyword
2025-01-14 10:10:30 -08:00
keyword_mask = tag_utils . create_keyword_mask ( df , ' Imprint ' )
2025-01-08 08:29:00 -08:00
# Create mask for Imprint text
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , ' Imprint ' )
2025-01-08 08:29:00 -08:00
final_mask = keyword_mask | text_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Imprint ' , ' Exile Matters ' ] )
2025-01-08 08:29:00 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } Imprint cards in { duration : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging Imprint cards: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed tagging Imprint cards ' )
2025-01-08 08:29:00 -08:00
## Cards that have or care about impulse
def create_impulse_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with impulse-like effects.
This function identifies cards that exile cards from the top of libraries
and allow playing them for a limited time , including :
- Exile top card ( s ) with may cast / play effects
- Named cards with similar effects
- Junk token creation
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have Impulse effects
"""
# Define text patterns
exile_patterns = [
' exile the top ' ,
' exiles the top '
]
play_patterns = [
' may cast ' ,
' may play '
]
# Named cards with Impulse effects
impulse_cards = [
' daxos of meletis ' , ' bloodsoaked insight ' , ' florian, voldaren scion ' ,
' possibility storm ' , ' ragava, nimble pilferer ' , ' rakdos, the muscle ' ,
' stolen strategy ' , ' urabrask, heretic praetor ' , ' valakut exploration ' ,
' wild wasteland '
]
# Create exclusion patterns
exclusion_patterns = [
' damage to each ' , ' damage to target ' , ' deals combat damage ' ,
' raid ' , ' target opponent \' s hand ' ,
]
secondary_exclusion_patterns = [
' each opponent ' , ' morph ' , ' opponent \' s library ' ,
' skip your draw ' , ' target opponent ' , ' that player \' s ' ,
' you may look at the top card '
]
# Create masks
2025-01-14 10:10:30 -08:00
tag_mask = tag_utils . create_tag_mask ( df , ' Imprint ' )
exile_mask = tag_utils . create_text_mask ( df , exile_patterns )
play_mask = tag_utils . create_text_mask ( df , play_patterns )
named_mask = tag_utils . create_name_mask ( df , impulse_cards )
junk_mask = tag_utils . create_text_mask ( df , ' junk token ' )
first_exclusion_mask = tag_utils . create_text_mask ( df , exclusion_patterns )
2025-01-08 08:29:00 -08:00
planeswalker_mask = df [ ' type ' ] . str . contains ( ' Planeswalker ' , case = False , na = False )
2025-01-14 10:10:30 -08:00
second_exclusion_mask = tag_utils . create_text_mask ( df , secondary_exclusion_patterns )
2025-01-08 08:29:00 -08:00
exclusion_mask = ( ~ first_exclusion_mask & ~ planeswalker_mask ) & second_exclusion_mask
# Combine masks
impulse_mask = ( ( exile_mask & play_mask & ~ exclusion_mask & ~ tag_mask ) |
named_mask | junk_mask )
return impulse_mask
def tag_for_impulse ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that have impulse-like effects using vectorized operations.
This function identifies and tags cards that exile cards from library tops
and allow playing them for a limited time , including :
- Exile top card ( s ) with may cast / play effects
- Named cards with similar effects
- Junk token creation
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging Impulse effects in { color } _cards.csv ' )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create impulse mask
impulse_mask = create_impulse_mask ( df )
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , impulse_mask , [ ' Exile Matters ' , ' Impulse ' ] )
2025-01-08 08:29:00 -08:00
# Add Junk Tokens tag where applicable
2025-01-14 10:10:30 -08:00
junk_mask = impulse_mask & tag_utils . create_text_mask ( df , ' junk token ' )
tag_utils . apply_tag_vectorized ( df , junk_mask , [ ' Junk Tokens ' ] )
2025-01-08 08:29:00 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { impulse_mask . sum ( ) } cards with Impulse effects in { duration : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging Impulse effects: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed tagging Impulse effects ' )
2025-01-08 08:29:00 -08:00
## Cards that have or care about plotting
def tag_for_plot ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with Plot using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging Plot cards in { color } _cards.csv ' )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create mask for Plot keyword
2025-01-14 10:10:30 -08:00
keyword_mask = tag_utils . create_keyword_mask ( df , ' Plot ' )
2025-01-08 08:29:00 -08:00
# Create mask for Plot keyword
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , ' Plot ' )
2025-01-08 08:29:00 -08:00
final_mask = keyword_mask | text_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Plot ' , ' Exile Matters ' ] )
2025-01-08 08:29:00 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } Plot cards in { duration : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging Plot cards: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed tagging Plot cards ' )
2025-01-08 08:29:00 -08:00
## Cards that have or care about suspend
def tag_for_suspend ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with Suspend using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging Suspend cards in { color } _cards.csv ' )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create mask for Suspend keyword
2025-01-14 10:10:30 -08:00
keyword_mask = tag_utils . create_keyword_mask ( df , ' Suspend ' )
2025-01-08 08:29:00 -08:00
# Create mask for Suspend keyword
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , ' Suspend ' )
2025-01-08 08:29:00 -08:00
final_mask = keyword_mask | text_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Suspend ' , ' Exile Matters ' ] )
2025-01-08 08:29:00 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } Suspend cards in { duration : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging Suspend cards: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed tagging Suspend cards ' )
2025-01-08 08:29:00 -08:00
### Tokens
def create_creature_token_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that create creature tokens.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards create creature tokens
"""
# Create base pattern for token creation
create_pattern = r ' create|put '
2025-01-14 10:10:30 -08:00
has_create = tag_utils . create_text_mask ( df , create_pattern )
2025-01-08 08:29:00 -08:00
# Create pattern for creature tokens
token_patterns = [
' artifact creature token ' ,
' creature token ' ,
' enchantment creature token '
]
2025-01-14 10:10:30 -08:00
has_token = tag_utils . create_text_mask ( df , token_patterns )
2025-01-08 08:29:00 -08:00
# Create exclusion mask
exclusion_patterns = [ ' fabricate ' , ' modular ' ]
2025-01-14 10:10:30 -08:00
exclusion_mask = tag_utils . create_text_mask ( df , exclusion_patterns )
2025-01-08 08:29:00 -08:00
# Create name exclusion mask
excluded_cards = [ ' agatha \' s soul cauldron ' ]
2025-01-14 10:10:30 -08:00
name_exclusions = tag_utils . create_name_mask ( df , excluded_cards )
2025-01-08 08:29:00 -08:00
return has_create & has_token & ~ exclusion_mask & ~ name_exclusions
def create_token_modifier_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that modify token creation.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards modify token creation
"""
# Create patterns for token modification
modifier_patterns = [
' create one or more ' ,
' one or more creature ' ,
' one or more tokens would be created ' ,
' one or more tokens would be put ' ,
' one or more tokens would enter ' ,
' one or more tokens you control ' ,
' put one or more '
]
2025-01-14 10:10:30 -08:00
has_modifier = tag_utils . create_text_mask ( df , modifier_patterns )
2025-01-08 08:29:00 -08:00
# Create patterns for token effects
effect_patterns = [ ' instead ' , ' plus ' ]
2025-01-14 10:10:30 -08:00
has_effect = tag_utils . create_text_mask ( df , effect_patterns )
2025-01-08 08:29:00 -08:00
# Create name exclusion mask
excluded_cards = [
' cloakwood swarmkeeper ' ,
' neyali, sun \' s vanguard ' ,
' staff of the storyteller '
]
2025-01-14 10:10:30 -08:00
name_exclusions = tag_utils . create_name_mask ( df , excluded_cards )
2025-01-08 08:29:00 -08:00
return has_modifier & has_effect & ~ name_exclusions
2025-01-17 11:39:27 -08:00
def create_tokens_matter_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that care about tokens.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards care about tokens
"""
# Create patterns for token matters
text_patterns = [
' tokens.*you.*control ' ,
' that \' s a token ' ,
]
text_mask = tag_utils . create_text_mask ( df , text_patterns )
return text_mask
2025-01-08 08:29:00 -08:00
def tag_for_tokens ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that create or modify tokens using vectorized operations.
This function identifies and tags :
- Cards that create creature tokens
- Cards that modify token creation ( doublers , replacement effects )
- Cards that care about tokens
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagging token-related cards in %s _cards.csv ' , color )
2025-01-08 12:06:33 -08:00
print ( ' \n ========== \n ' )
2025-01-08 08:29:00 -08:00
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 08:29:00 -08:00
# Create creature token mask
creature_mask = create_creature_token_mask ( df )
if creature_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , creature_mask ,
2025-01-08 08:29:00 -08:00
[ ' Creature Tokens ' , ' Token Creation ' , ' Tokens Matter ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d cards that create creature tokens ' , creature_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
# Create token modifier mask
modifier_mask = create_token_modifier_mask ( df )
if modifier_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , modifier_mask ,
2025-01-08 08:29:00 -08:00
[ ' Token Modification ' , ' Token Creation ' , ' Tokens Matter ' ] )
2025-01-14 10:10:30 -08:00
logger . info ( ' Tagged %d cards that modify token creation ' , modifier_mask . sum ( ) )
2025-01-17 11:39:27 -08:00
# Create tokens matter mask
matters_mask = create_tokens_matter_mask ( df )
if matters_mask . any ( ) :
tag_utils . apply_tag_vectorized ( df , matters_mask ,
[ ' Tokens Matter ' ] )
logger . info ( ' Tagged %d cards that care about tokens ' , modifier_mask . sum ( ) )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed token tagging in %.2f s ' , duration )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( ' Error tagging token cards: %s ' , str ( e ) )
2025-01-08 08:29:00 -08:00
raise
### Life Matters
def tag_for_life_matters ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about life totals, life gain/loss, and related effects using vectorized operations.
This function coordinates multiple subfunctions to handle different life - related aspects :
- Lifegain effects and triggers
- Lifelink and lifelink - like abilities
- Life loss triggers and effects
- Food token creation and effects
2025-01-08 12:06:33 -08:00
- Life - related kindred synergies
2025-01-08 08:29:00 -08:00
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting " Life Matters " tagging for { color } _cards.csv ' )
2025-01-08 12:06:33 -08:00
print ( ' \n ========== \n ' )
2025-01-08 08:29:00 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' type ' , ' creatureTypes ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 08:29:00 -08:00
# Process each type of life effect
tag_for_lifegain ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed lifegain tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
tag_for_lifelink ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed lifelink tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
tag_for_life_loss ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed life loss tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
2025-01-08 12:06:33 -08:00
tag_for_food ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed food token tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
2025-01-08 12:06:33 -08:00
tag_for_life_kindred ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed life kindred tagging ' )
2025-01-08 08:29:00 -08:00
print ( ' \n ========== \n ' )
# Log completion and performance metrics
duration = pd . Timestamp . now ( ) - start_time
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed all " Life Matters " tagging in { duration . total_seconds ( ) : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_life_matters: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
def tag_for_lifegain ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with lifegain effects using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging lifegain effects in { color } _cards.csv ' )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create masks for different lifegain patterns
gain_patterns = [ f ' gain { num } life ' for num in settings . num_to_search ]
gain_patterns . extend ( [ f ' gains { num } life ' for num in settings . num_to_search ] )
gain_patterns . extend ( [ ' gain life ' , ' gains life ' ] )
2025-01-14 10:10:30 -08:00
gain_mask = tag_utils . create_text_mask ( df , gain_patterns )
2025-01-08 08:29:00 -08:00
# Exclude replacement effects
2025-01-14 10:10:30 -08:00
replacement_mask = tag_utils . create_text_mask ( df , [ ' if you would gain life ' , ' whenever you gain life ' ] )
2025-01-08 08:29:00 -08:00
# Apply lifegain tags
final_mask = gain_mask & ~ replacement_mask
if final_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Lifegain ' , ' Life Matters ' ] )
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with lifegain effects ' )
2025-01-08 08:29:00 -08:00
# Tag lifegain triggers
2025-01-14 10:10:30 -08:00
trigger_mask = tag_utils . create_text_mask ( df , [ ' if you would gain life ' , ' whenever you gain life ' ] )
2025-01-08 08:29:00 -08:00
if trigger_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , trigger_mask , [ ' Lifegain ' , ' Lifegain Triggers ' , ' Life Matters ' ] )
logger . info ( f ' Tagged { trigger_mask . sum ( ) } cards with lifegain triggers ' )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed lifegain tagging in { duration : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging lifegain effects: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
def tag_for_lifelink ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with lifelink and lifelink-like effects using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging lifelink effects in { color } _cards.csv ' )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create masks for different lifelink patterns
2025-01-14 10:10:30 -08:00
lifelink_mask = tag_utils . create_text_mask ( df , ' lifelink ' )
lifelike_mask = tag_utils . create_text_mask ( df , [
2025-01-08 08:29:00 -08:00
' deals damage, you gain that much life ' ,
' loses life.*gain that much life '
] )
# Exclude combat damage references for life loss conversion
2025-01-14 10:10:30 -08:00
damage_mask = tag_utils . create_text_mask ( df , ' deals damage ' )
2025-01-08 08:29:00 -08:00
life_loss_mask = lifelike_mask & ~ damage_mask
# Combine masks
final_mask = lifelink_mask | lifelike_mask | life_loss_mask
# Apply tags
if final_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Lifelink ' , ' Lifegain ' , ' Life Matters ' ] )
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with lifelink effects ' )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed lifelink tagging in { duration : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging lifelink effects: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
def tag_for_life_loss ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about life loss using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging life loss effects in { color } _cards.csv ' )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create masks for different life loss patterns
text_patterns = [
' you lost life ' ,
' you gained and lost life ' ,
' you gained or lost life ' ,
' you would lose life ' ,
' you \' ve gained and lost life this turn ' ,
' you \' ve lost life ' ,
' whenever you gain or lose life ' ,
' whenever you lose life '
]
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 08:29:00 -08:00
# Apply tags
if text_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , text_mask , [ ' Lifeloss ' , ' Lifeloss Triggers ' , ' Life Matters ' ] )
logger . info ( f ' Tagged { text_mask . sum ( ) } cards with life loss effects ' )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed life loss tagging in { duration : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging life loss effects: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-08 12:06:33 -08:00
def tag_for_food ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that create or care about Food using vectorized operations.
2025-01-08 08:29:00 -08:00
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging Food token in { color } _cards.csv ' )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create masks for Food tokens
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , ' food ' )
type_mask = tag_utils . create_type_mask ( df , ' food ' )
2025-01-08 08:29:00 -08:00
# Combine masks
final_mask = text_mask | type_mask
# Apply tags
if final_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Food ' , ' Lifegain ' , ' Life Matters ' ] )
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with Food effects ' )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed Food tagging in { duration : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging Food effects: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-08 12:06:33 -08:00
def tag_for_life_kindred ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with life-related kindred synergies using vectorized operations.
2025-01-08 08:29:00 -08:00
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging life-related kindred effects in { color } _cards.csv ' )
2025-01-08 08:29:00 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create mask for life-related creature types
life_tribes = [ ' Angel ' , ' Bat ' , ' Cleric ' , ' Vampire ' ]
2025-01-08 12:06:33 -08:00
kindred_mask = df [ ' creatureTypes ' ] . apply ( lambda x : any ( tribe in x for tribe in life_tribes ) )
2025-01-08 08:29:00 -08:00
# Apply tags
2025-01-08 12:06:33 -08:00
if kindred_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , kindred_mask , [ ' Lifegain ' , ' Life Matters ' ] )
logger . info ( f ' Tagged { kindred_mask . sum ( ) } cards with life-related kindred effects ' )
2025-01-08 08:29:00 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed life kindred tagging in { duration : .2f } s ' )
2025-01-08 08:29:00 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging life kindred effects: { str ( e ) } ' )
2025-01-08 08:29:00 -08:00
raise
2025-01-08 12:06:33 -08:00
### Counters
def tag_for_counters ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about or interact with counters using vectorized operations.
2024-12-11 23:08:42 -08:00
2025-01-08 12:06:33 -08:00
This function identifies and tags cards that :
- Add or remove counters ( + 1 / + 1 , - 1 / - 1 , special counters )
- Care about counters being placed or removed
- Have counter - based abilities ( proliferate , undying , etc )
- Create or modify counters
2024-12-16 22:58:02 -08:00
2025-01-08 12:06:33 -08:00
The function maintains proper tag hierarchy and ensures consistent application
of related tags like ' Counters Matter ' , ' +1/+1 Counters ' , etc .
2024-12-16 22:58:02 -08:00
2025-01-08 12:06:33 -08:00
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
2024-12-12 12:27:29 -08:00
2025-01-08 12:06:33 -08:00
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting counter-related tagging for { color } _cards.csv ' )
2025-01-02 13:00:52 -08:00
print ( ' \n ========== \n ' )
2024-12-16 22:58:02 -08:00
2025-01-08 12:06:33 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
2024-12-16 22:58:02 -08:00
2025-01-08 12:06:33 -08:00
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' name ' , ' creatureTypes ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2024-12-16 22:58:02 -08:00
2025-01-08 12:06:33 -08:00
# Process each type of counter effect
tag_for_general_counters ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed general counter tagging ' )
2025-01-08 12:06:33 -08:00
print ( ' \n ========== \n ' )
2024-12-17 12:02:53 -08:00
2025-01-08 12:06:33 -08:00
tag_for_plus_counters ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed +1/+1 counter tagging ' )
2025-01-08 12:06:33 -08:00
print ( ' \n ========== \n ' )
2024-12-16 22:58:02 -08:00
2025-01-08 12:06:33 -08:00
tag_for_minus_counters ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed -1/-1 counter tagging ' )
2025-01-08 12:06:33 -08:00
print ( ' \n ========== \n ' )
2024-12-16 22:58:02 -08:00
2025-01-08 12:06:33 -08:00
tag_for_special_counters ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( ' Completed special counter tagging ' )
2025-01-08 12:06:33 -08:00
print ( ' \n ========== \n ' )
2024-12-16 22:58:02 -08:00
2025-01-08 12:06:33 -08:00
# Log completion and performance metrics
duration = pd . Timestamp . now ( ) - start_time
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed all counter-related tagging in { duration . total_seconds ( ) : .2f } s ' )
2024-12-16 22:58:02 -08:00
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_counters: { str ( e ) } ' )
2025-01-08 12:06:33 -08:00
raise
2024-12-16 22:58:02 -08:00
2025-01-08 12:06:33 -08:00
def tag_for_general_counters ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about counters in general using vectorized operations.
2024-12-16 22:58:02 -08:00
2025-01-08 12:06:33 -08:00
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging general counter effects in { color } _cards.csv ' )
2025-01-08 12:06:33 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create masks for different counter patterns
text_patterns = [
' choose a kind of counter ' ,
' if it had counters ' ,
' move a counter ' ,
' one or more counters ' ,
' proliferate ' ,
' remove a counter ' ,
' with counters on them '
]
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 12:06:33 -08:00
# Create mask for specific cards
specific_cards = [
' banner of kinship ' ,
' damning verdict ' ,
' ozolith '
]
2025-01-14 10:10:30 -08:00
name_mask = tag_utils . create_name_mask ( df , specific_cards )
2025-01-08 12:06:33 -08:00
# Combine masks
final_mask = text_mask | name_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Counters Matter ' ] )
2025-01-08 12:06:33 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with general counter effects in { duration : .2f } s ' )
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging general counter effects: { str ( e ) } ' )
2025-01-08 12:06:33 -08:00
raise
def tag_for_plus_counters ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about +1/+1 counters using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging +1/+1 counter effects in { color } _cards.csv ' )
2025-01-08 12:06:33 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create text pattern mask
text_patterns = [
r ' \ +1/ \ +1 counter ' ,
r ' if it had counters ' ,
r ' one or more counters ' ,
r ' one or more \ +1/ \ +1 counter ' ,
r ' proliferate ' ,
r ' undying ' ,
r ' with counters on them '
]
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 12:06:33 -08:00
# Create creature type mask
type_mask = df [ ' creatureTypes ' ] . apply ( lambda x : ' Hydra ' in x if isinstance ( x , list ) else False )
# Combine masks
final_mask = text_mask | type_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' +1/+1 Counters ' , ' Counters Matter ' , ' Voltron ' ] )
2025-01-08 12:06:33 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with +1/+1 counter effects in { duration : .2f } s ' )
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging +1/+1 counter effects: { str ( e ) } ' )
2025-01-08 12:06:33 -08:00
raise
def tag_for_minus_counters ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about -1/-1 counters using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging -1/-1 counter effects in { color } _cards.csv ' )
2025-01-08 12:06:33 -08:00
start_time = pd . Timestamp . now ( )
try :
# Create text pattern mask
text_patterns = [
' -1/-1 counter ' ,
' if it had counters ' ,
' infect ' ,
' one or more counter ' ,
' one or more -1/-1 counter ' ,
' persist ' ,
' proliferate ' ,
' wither '
]
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , text_patterns )
2024-12-17 12:02:53 -08:00
2025-01-08 12:06:33 -08:00
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , text_mask , [ ' -1/-1 Counters ' , ' Counters Matter ' ] )
2025-01-08 12:06:33 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { text_mask . sum ( ) } cards with -1/-1 counter effects in { duration : .2f } s ' )
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging -1/-1 counter effects: { str ( e ) } ' )
2025-01-08 12:06:33 -08:00
raise
def tag_for_special_counters ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about special counters using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
"""
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagging special counter effects in { color } _cards.csv ' )
2025-01-08 12:06:33 -08:00
start_time = pd . Timestamp . now ( )
try :
# Process each counter type
counter_counts = { }
for counter_type in settings . counter_types :
# Create pattern for this counter type
pattern = f ' { counter_type } counter '
2025-01-14 10:10:30 -08:00
mask = tag_utils . create_text_mask ( df , pattern )
2025-01-08 12:06:33 -08:00
if mask . any ( ) :
# Apply tags
tags = [ f ' { counter_type } Counters ' , ' Counters Matter ' ]
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , mask , tags )
2025-01-08 12:06:33 -08:00
counter_counts [ counter_type ] = mask . sum ( )
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
total_cards = sum ( counter_counts . values ( ) )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { total_cards } cards with special counter effects in { duration : .2f } s ' )
2025-01-08 12:06:33 -08:00
for counter_type , count in counter_counts . items ( ) :
if count > 0 :
2025-01-14 10:10:30 -08:00
logger . info ( f ' - { counter_type } : { count } cards ' )
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging special counter effects: { str ( e ) } ' )
2025-01-08 12:06:33 -08:00
raise
### Voltron
def create_voltron_commander_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that are Voltron commanders.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards are Voltron commanders
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_name_mask ( df , settings . VOLTRON_COMMANDER_CARDS )
2025-01-08 12:06:33 -08:00
def create_voltron_support_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that support Voltron strategies.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards support Voltron strategies
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , settings . VOLTRON_PATTERNS )
2025-01-08 12:06:33 -08:00
def create_voltron_equipment_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for Equipment-based Voltron cards.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards are Equipment - based Voltron cards
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_type_mask ( df , ' Equipment ' )
2025-01-08 12:06:33 -08:00
def create_voltron_aura_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for Aura-based Voltron cards.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards are Aura - based Voltron cards
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_type_mask ( df , ' Aura ' )
2025-01-08 12:06:33 -08:00
def tag_for_voltron ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that fit the Voltron strategy.
This function identifies and tags cards that support the Voltron strategy including :
- Voltron commanders
- Equipment and Auras
- Cards that care about equipped / enchanted creatures
- Cards that enhance single creatures
The function uses vectorized operations for performance and follows patterns
established in other tagging functions .
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting Voltron strategy tagging for { color } _cards.csv ' )
2025-01-08 12:06:33 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' type ' , ' name ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 12:06:33 -08:00
# Create masks for different Voltron aspects
commander_mask = create_voltron_commander_mask ( df )
support_mask = create_voltron_support_mask ( df )
equipment_mask = create_voltron_equipment_mask ( df )
aura_mask = create_voltron_aura_mask ( df )
# Combine masks
final_mask = commander_mask | support_mask | equipment_mask | aura_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Voltron ' ] )
2025-01-08 12:06:33 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with Voltron strategy in { duration : .2f } s ' )
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_voltron: { str ( e ) } ' )
2025-01-08 12:06:33 -08:00
raise
duration = pd . Timestamp . now ( ) - start_time
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed all " Life Matters " tagging in { duration . total_seconds ( ) : .2f } s ' )
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_voltron: { str ( e ) } ' )
2025-01-08 12:06:33 -08:00
raise
### Lands matter
def create_lands_matter_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that care about lands in general.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have lands matter effects
"""
# Create mask for named cards
2025-01-14 10:10:30 -08:00
name_mask = tag_utils . create_name_mask ( df , settings . LANDS_MATTER_SPECIFIC_CARDS )
2025-01-08 12:06:33 -08:00
# Create text pattern masks
2025-01-14 10:10:30 -08:00
play_mask = tag_utils . create_text_mask ( df , settings . LANDS_MATTER_PATTERNS [ ' land_play ' ] )
search_mask = tag_utils . create_text_mask ( df , settings . LANDS_MATTER_PATTERNS [ ' land_search ' ] )
state_mask = tag_utils . create_text_mask ( df , settings . LANDS_MATTER_PATTERNS [ ' land_state ' ] )
2025-01-08 12:06:33 -08:00
# Combine all masks
return name_mask | play_mask | search_mask | state_mask
def create_domain_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with domain effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have domain effects
"""
2025-01-14 10:10:30 -08:00
keyword_mask = tag_utils . create_keyword_mask ( df , settings . DOMAIN_PATTERNS [ ' keyword ' ] )
text_mask = tag_utils . create_text_mask ( df , settings . DOMAIN_PATTERNS [ ' text ' ] )
2025-01-08 12:06:33 -08:00
return keyword_mask | text_mask
def create_landfall_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with landfall triggers.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have landfall effects
"""
2025-01-14 10:10:30 -08:00
keyword_mask = tag_utils . create_keyword_mask ( df , settings . LANDFALL_PATTERNS [ ' keyword ' ] )
trigger_mask = tag_utils . create_text_mask ( df , settings . LANDFALL_PATTERNS [ ' triggers ' ] )
2025-01-08 12:06:33 -08:00
return keyword_mask | trigger_mask
def create_landwalk_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with landwalk abilities.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have landwalk abilities
"""
2025-01-14 10:10:30 -08:00
basic_mask = tag_utils . create_text_mask ( df , settings . LANDWALK_PATTERNS [ ' basic ' ] )
nonbasic_mask = tag_utils . create_text_mask ( df , settings . LANDWALK_PATTERNS [ ' nonbasic ' ] )
2025-01-08 12:06:33 -08:00
return basic_mask | nonbasic_mask
def create_land_types_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that care about specific land types.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards care about specific land types
"""
# Create type-based mask
2025-01-14 10:10:30 -08:00
type_mask = tag_utils . create_type_mask ( df , settings . LAND_TYPES )
2025-01-08 12:06:33 -08:00
# Create text pattern masks for each land type
text_masks = [ ]
for land_type in settings . LAND_TYPES :
patterns = [
f ' search your library for a { land_type . lower ( ) } ' ,
f ' search your library for up to two { land_type . lower ( ) } ' ,
f ' { land_type } you control '
]
2025-01-14 10:10:30 -08:00
text_masks . append ( tag_utils . create_text_mask ( df , patterns ) )
2025-01-08 12:06:33 -08:00
# Combine all masks
return type_mask | pd . concat ( text_masks , axis = 1 ) . any ( axis = 1 )
def tag_for_lands_matter ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about lands using vectorized operations.
This function identifies and tags cards with land - related effects including :
- General lands matter effects ( searching , playing additional lands , etc )
- Domain effects
- Landfall triggers
- Landwalk abilities
- Specific land type matters
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting lands matter tagging for { color } _cards.csv ' )
2025-01-08 12:06:33 -08:00
print ( ' \n ========== \n ' )
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' type ' , ' name ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 12:06:33 -08:00
# Create masks for different land effects
lands_mask = create_lands_matter_mask ( df )
domain_mask = create_domain_mask ( df )
landfall_mask = create_landfall_mask ( df )
landwalk_mask = create_landwalk_mask ( df )
types_mask = create_land_types_mask ( df )
# Apply tags based on masks
if lands_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , lands_mask , [ ' Lands Matter ' ] )
logger . info ( f ' Tagged { lands_mask . sum ( ) } cards with general lands matter effects ' )
2025-01-08 12:06:33 -08:00
if domain_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , domain_mask , [ ' Domain ' , ' Lands Matter ' ] )
logger . info ( f ' Tagged { domain_mask . sum ( ) } cards with domain effects ' )
2025-01-08 12:06:33 -08:00
if landfall_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , landfall_mask , [ ' Landfall ' , ' Lands Matter ' ] )
logger . info ( f ' Tagged { landfall_mask . sum ( ) } cards with landfall effects ' )
2025-01-08 12:06:33 -08:00
if landwalk_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , landwalk_mask , [ ' Landwalk ' , ' Lands Matter ' ] )
logger . info ( f ' Tagged { landwalk_mask . sum ( ) } cards with landwalk abilities ' )
2025-01-08 12:06:33 -08:00
if types_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , types_mask , [ ' Land Types Matter ' , ' Lands Matter ' ] )
logger . info ( f ' Tagged { types_mask . sum ( ) } cards with specific land type effects ' )
2025-01-08 12:06:33 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed lands matter tagging in { duration : .2f } s ' )
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_lands_matter: { str ( e ) } ' )
2025-01-08 12:06:33 -08:00
raise
### Spells Matter
def create_spellslinger_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with spellslinger text patterns.
This function identifies cards that care about casting spells through text patterns like :
- Casting modal spells
- Casting spells from anywhere
- Casting instant / sorcery spells
- Casting noncreature spells
- First / next spell cast triggers
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have spellslinger text patterns
"""
text_patterns = [
' cast a modal ' ,
' cast a spell from anywhere ' ,
' cast an instant ' ,
' cast a noncreature ' ,
' casts an instant ' ,
' casts a noncreature ' ,
' first instant ' ,
' first spell ' ,
' next cast an instant ' ,
' next instant ' ,
' next spell ' ,
' second instant ' ,
' second spell ' ,
' you cast an instant ' ,
' you cast a spell '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 12:06:33 -08:00
def create_spellslinger_keyword_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with spellslinger-related keywords.
This function identifies cards with keywords that indicate they care about casting spells :
- Magecraft
- Storm
- Prowess
- Surge
2024-12-16 22:58:02 -08:00
2025-01-08 12:06:33 -08:00
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have spellslinger keywords
"""
keyword_patterns = [
' Magecraft ' ,
' Storm ' ,
' Prowess ' ,
' Surge '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_keyword_mask ( df , keyword_patterns )
2025-01-08 12:06:33 -08:00
def create_spellslinger_type_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for instant/sorcery type cards.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards are instants or sorceries
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_type_mask ( df , [ ' Instant ' , ' Sorcery ' ] )
2025-01-08 12:06:33 -08:00
def create_spellslinger_exclusion_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that should be excluded from spellslinger tagging.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards should be excluded
"""
# Add specific exclusion patterns here if needed
excluded_names = [
' Possibility Storm ' ,
' Wild-Magic Sorcerer '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_name_mask ( df , excluded_names )
2025-01-08 12:06:33 -08:00
def tag_for_spellslinger ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about casting spells using vectorized operations.
This function identifies and tags cards that care about spellcasting including :
- Cards that trigger off casting spells
- Instant and sorcery spells
- Cards with spellslinger - related keywords
- Cards that care about noncreature spells
2024-12-16 22:58:02 -08:00
2025-01-08 12:06:33 -08:00
The function maintains proper tag hierarchy and ensures consistent application
of related tags like ' Spellslinger ' , ' Spells Matter ' , etc .
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting Spellslinger tagging for { color } _cards.csv ' )
2025-01-08 12:06:33 -08:00
print ( ' \n ========== \n ' )
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' type ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 12:06:33 -08:00
# Create masks for different spellslinger patterns
text_mask = create_spellslinger_text_mask ( df )
keyword_mask = create_spellslinger_keyword_mask ( df )
type_mask = create_spellslinger_type_mask ( df )
exclusion_mask = create_spellslinger_exclusion_mask ( df )
# Combine masks
final_mask = ( text_mask | keyword_mask | type_mask ) & ~ exclusion_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Spellslinger ' , ' Spells Matter ' ] )
logger . info ( f ' Tagged { final_mask . sum ( ) } general Spellslinger cards ' )
2025-01-08 12:06:33 -08:00
# Run non-generalized tags
tag_for_storm ( df , color )
tag_for_magecraft ( df , color )
tag_for_cantrips ( df , color )
tag_for_spell_copy ( df , color )
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed Spellslinger tagging in { duration : .2f } s ' )
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_spellslinger: { str ( e ) } ' )
2025-01-08 12:06:33 -08:00
raise
def create_storm_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with storm effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have storm effects
"""
# Create keyword mask
2025-01-14 10:10:30 -08:00
keyword_mask = tag_utils . create_keyword_mask ( df , ' Storm ' )
2025-01-08 12:06:33 -08:00
# Create text mask
text_patterns = [
' gain storm ' ,
' has storm ' ,
' have storm '
]
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 12:06:33 -08:00
return keyword_mask | text_mask
def tag_for_storm ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with storm effects using vectorized operations.
This function identifies and tags cards that :
- Have the storm keyword
- Grant or care about storm
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 12:06:33 -08:00
# Create storm mask
storm_mask = create_storm_mask ( df )
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , storm_mask , [ ' Storm ' , ' Spellslinger ' , ' Spells Matter ' ] )
2025-01-08 12:06:33 -08:00
# Log results
storm_count = storm_mask . sum ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { storm_count } cards with Storm effects ' )
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging Storm effects: { str ( e ) } ' )
2025-01-08 12:06:33 -08:00
raise
## Tag for Cantrips
def tag_for_cantrips ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards in the DataFrame as cantrips based on specific criteria.
Cantrips are defined as low - cost spells ( mana value < = 2 ) that draw cards .
The function excludes certain card types , keywords , and specific named cards
from being tagged as cantrips .
Args :
df : The DataFrame containing card data
color : The color identifier for logging purposes
"""
try :
# Convert mana value to numeric
df [ ' manaValue ' ] = pd . to_numeric ( df [ ' manaValue ' ] , errors = ' coerce ' )
# Create exclusion masks
2025-01-14 10:10:30 -08:00
excluded_types = tag_utils . create_type_mask ( df , ' Land|Equipment ' )
excluded_keywords = tag_utils . create_keyword_mask ( df , [ ' Channel ' , ' Cycling ' , ' Connive ' , ' Learn ' , ' Ravenous ' ] )
2025-01-08 12:06:33 -08:00
has_loot = df [ ' themeTags ' ] . apply ( lambda x : ' Loot ' in x )
# Define name exclusions
EXCLUDED_NAMES = {
' Archivist of Oghma ' , ' Argothian Enchantress ' , ' Audacity ' , ' Betrayal ' , ' Bequeathal ' , ' Blood Scrivener ' , ' Brigon, Soldier of Meletis ' ,
' Compost ' , ' Concealing curtains // Revealing Eye ' , ' Cryptbreaker ' , ' Curiosity ' , ' Cuse of Vengeance ' , ' Cryptek ' , ' Dakra Mystic ' ,
' Dawn of a New Age ' , ' Dockside Chef ' , ' Dreamcatcher ' , ' Edgewall Innkeeper ' , ' Eidolon of Philosophy ' , ' Evolved Sleeper ' ,
' Femeref Enchantress ' , ' Finneas, Ace Archer ' , ' Flumph ' , ' Folk Hero ' , ' Frodo, Adventurous Hobbit ' , ' Goblin Artisans ' ,
' Goldberry, River-Daughter ' , ' Gollum, Scheming Guide ' , ' Hatching Plans ' , ' Ideas Unbound ' , ' Ingenius Prodigy ' , ' Ior Ruin Expedition ' ,
" Jace ' s Erasure " , ' Keeper of the Mind ' , ' Kor Spiritdancer ' , ' Lodestone Bauble ' , ' Puresteel Paladin ' , ' Jeweled Bird ' , ' Mindblade Render ' ,
" Multani ' s Presence " , " Nahiri ' s Lithoforming " , ' Ordeal of Thassa ' , ' Pollywog Prodigy ' , ' Priest of Forgotten Gods ' , ' Ravenous Squirrel ' ,
' Read the Runes ' , ' Red Death, Shipwrecker ' , ' Roil Cartographer ' , ' Sage of Lat-Name ' , ' Saprazzan Heir ' , ' Scion of Halaster ' , ' See Beyond ' ,
' Selhoff Entomber ' , ' Shielded Aether Theif ' , ' Shore Keeper ' , ' silverquill Silencer ' , ' Soldevi Sage ' , ' Soldevi Sentry ' , ' Spiritual Focus ' ,
' Sram, Senior Edificer ' , ' Staff of the Storyteller ' , ' Stirge ' , ' Sylvan Echoes ' , " Sythis Harvest ' s Hand " , ' Sygg, River Cutthroat ' ,
' Tenuous Truce ' , ' Test of Talents ' , ' Thalakos seer ' , " Tribute to Horobi // Echo of Deaths Wail " , ' Vampire Gourmand ' , ' Vampiric Rites ' ,
' Vampirism ' , ' Vessel of Paramnesia ' , " Witch ' s Caultron " , ' Wall of Mulch ' , ' Waste Not ' , ' Well Rested '
# Add other excluded names here
}
excluded_names = df [ ' name ' ] . isin ( EXCLUDED_NAMES )
# Create cantrip condition masks
2025-01-14 10:10:30 -08:00
has_draw = tag_utils . create_text_mask ( df , PATTERN_GROUPS [ ' draw ' ] )
2025-01-08 12:06:33 -08:00
low_cost = df [ ' manaValue ' ] . fillna ( float ( ' inf ' ) ) < = 2
# Combine conditions
cantrip_mask = (
~ excluded_types &
~ excluded_keywords &
~ has_loot &
~ excluded_names &
has_draw &
low_cost
)
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , cantrip_mask , TAG_GROUPS [ ' Cantrips ' ] )
2025-01-08 12:06:33 -08:00
# Log results
cantrip_count = cantrip_mask . sum ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { cantrip_count } Cantrip cards ' )
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( ' Error tagging Cantrips in %s _cards.csv: %s ' , color , str ( e ) )
2025-01-08 12:06:33 -08:00
raise
2025-01-17 11:39:27 -08:00
## Magecraft
2025-01-08 12:06:33 -08:00
def create_magecraft_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with magecraft effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have magecraft effects
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_keyword_mask ( df , ' Magecraft ' )
2025-01-08 12:06:33 -08:00
def tag_for_magecraft ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards with magecraft using vectorized operations.
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
try :
# Validate required columns
required_cols = { ' themeTags ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 12:06:33 -08:00
# Create magecraft mask
magecraft_mask = create_magecraft_mask ( df )
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , magecraft_mask , [ ' Magecraft ' , ' Spellslinger ' , ' Spells Matter ' ] )
2025-01-08 12:06:33 -08:00
# Log results
magecraft_count = magecraft_mask . sum ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { magecraft_count } cards with Magecraft effects ' )
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error tagging Magecraft effects: { str ( e ) } ' )
2025-01-08 12:06:33 -08:00
raise
## Spell Copy
def create_spell_copy_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with spell copy text patterns.
This function identifies cards that copy spells through text patterns like :
- Copy target spell
- Copy that spell
- Copy the next spell
- Create copies of spells
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have spell copy text patterns
"""
text_patterns = [
' copy a spell ' ,
' copy it ' ,
' copy that spell ' ,
' copy target ' ,
' copy the next ' ,
' create a copy ' ,
' creates a copy '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 12:06:33 -08:00
def create_spell_copy_keyword_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with spell copy related keywords.
This function identifies cards with keywords that indicate they copy spells :
- Casualty
- Conspire
- Replicate
- Storm
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have spell copy keywords
"""
keyword_patterns = [
' Casualty ' ,
' Conspire ' ,
' Replicate ' ,
' Storm '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_keyword_mask ( df , keyword_patterns )
2025-01-08 12:06:33 -08:00
def tag_for_spell_copy ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that copy spells using vectorized operations.
This function identifies and tags cards that copy spells including :
- Cards that directly copy spells
- Cards with copy - related keywords
- Cards that create copies of spells
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 12:06:33 -08:00
# Create masks for different spell copy patterns
text_mask = create_spell_copy_text_mask ( df )
keyword_mask = create_spell_copy_keyword_mask ( df )
# Combine masks
final_mask = text_mask | keyword_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Spell Copy ' , ' Spellslinger ' , ' Spells Matter ' ] )
2025-01-08 12:06:33 -08:00
# Log results
spellcopy_count = final_mask . sum ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { spellcopy_count } spell copy cards ' )
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_spell_copy: { str ( e ) } ' )
2025-01-08 12:06:33 -08:00
raise
### Ramp
def create_mana_dork_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for creatures that produce mana.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards are mana dorks
"""
# Create base creature mask
2025-01-14 10:10:30 -08:00
creature_mask = tag_utils . create_type_mask ( df , ' Creature ' )
2025-01-08 12:06:33 -08:00
# Create text pattern masks
2025-01-14 10:10:30 -08:00
tap_mask = tag_utils . create_text_mask ( df , [ ' {T} : Add ' , ' {T} : Untap ' ] )
sac_mask = tag_utils . create_text_mask ( df , [ ' creature: add ' , ' control: add ' ] )
2025-01-08 12:06:33 -08:00
# Create mana symbol mask
mana_patterns = [ f ' add {{ { c } }} ' for c in [ ' C ' , ' W ' , ' U ' , ' B ' , ' R ' , ' G ' ] ]
2025-01-14 10:10:30 -08:00
mana_mask = tag_utils . create_text_mask ( df , mana_patterns )
2025-01-08 12:06:33 -08:00
# Create specific cards mask
specific_cards = [ ' Awaken the Woods ' , ' Forest Dryad ' ]
2025-01-14 10:10:30 -08:00
name_mask = tag_utils . create_name_mask ( df , specific_cards )
2025-01-08 12:06:33 -08:00
return creature_mask & ( tap_mask | sac_mask | mana_mask ) | name_mask
2025-01-08 17:26:02 -08:00
2025-01-08 12:06:33 -08:00
def create_mana_rock_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for artifacts that produce mana.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards are mana rocks
"""
# Create base artifact mask
2025-01-14 10:10:30 -08:00
artifact_mask = tag_utils . create_type_mask ( df , ' Artifact ' )
2025-01-08 12:06:33 -08:00
# Create text pattern masks
2025-01-14 10:10:30 -08:00
tap_mask = tag_utils . create_text_mask ( df , [ ' {T} : Add ' , ' {T} : Untap ' ] )
sac_mask = tag_utils . create_text_mask ( df , [ ' creature: add ' , ' control: add ' ] )
2025-01-08 12:06:33 -08:00
# Create mana symbol mask
mana_patterns = [ f ' add {{ { c } }} ' for c in [ ' C ' , ' W ' , ' U ' , ' B ' , ' R ' , ' G ' ] ]
2025-01-14 10:10:30 -08:00
mana_mask = tag_utils . create_text_mask ( df , mana_patterns )
2025-01-08 12:06:33 -08:00
# Create token mask
2025-01-14 10:10:30 -08:00
token_mask = tag_utils . create_tag_mask ( df , [ ' Powerstone Tokens ' , ' Treasure Tokens ' , ' Gold Tokens ' ] ) | \
tag_utils . create_text_mask ( df , ' token named meteorite ' )
2025-01-08 12:06:33 -08:00
return ( artifact_mask & ( tap_mask | sac_mask | mana_mask ) ) | token_mask
2025-01-08 17:26:02 -08:00
2025-01-08 12:06:33 -08:00
def create_extra_lands_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that allow playing additional lands.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards allow playing extra lands
"""
text_patterns = [
' additional land ' ,
' play an additional land ' ,
' play two additional lands ' ,
' put a land ' ,
' put all land ' ,
' put those land ' ,
' return all land ' ,
' return target land '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 17:26:02 -08:00
2025-01-08 12:06:33 -08:00
def create_land_search_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that search for lands.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards search for lands
"""
# Create basic search patterns
search_patterns = [
' search your library for a basic ' ,
' search your library for a land ' ,
' search your library for up to ' ,
' each player searches ' ,
' put those land '
]
# Create land type specific patterns
land_types = [ ' Plains ' , ' Island ' , ' Swamp ' , ' Mountain ' , ' Forest ' , ' Wastes ' ]
for land_type in land_types :
search_patterns . extend ( [
f ' search your library for a basic { land_type . lower ( ) } ' ,
f ' search your library for a { land_type . lower ( ) } ' ,
f ' search your library for an { land_type . lower ( ) } '
] )
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , search_patterns )
2025-01-08 17:26:02 -08:00
2025-01-08 12:06:33 -08:00
def tag_for_ramp ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that provide mana acceleration using vectorized operations.
This function identifies and tags cards that provide mana acceleration through :
- Mana dorks ( creatures that produce mana )
- Mana rocks ( artifacts that produce mana )
- Extra land effects
- Land search effects
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting ramp tagging for { color } _cards.csv ' )
2025-01-08 12:06:33 -08:00
print ( ' \n ========== \n ' )
try :
# Create masks for different ramp categories
dork_mask = create_mana_dork_mask ( df )
rock_mask = create_mana_rock_mask ( df )
lands_mask = create_extra_lands_mask ( df )
search_mask = create_land_search_mask ( df )
# Apply tags for each category
if dork_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , dork_mask , [ ' Mana Dork ' , ' Ramp ' ] )
logger . info ( f ' Tagged { dork_mask . sum ( ) } mana dork cards ' )
2025-01-08 12:06:33 -08:00
if rock_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , rock_mask , [ ' Mana Rock ' , ' Ramp ' ] )
logger . info ( f ' Tagged { rock_mask . sum ( ) } mana rock cards ' )
2025-01-08 12:06:33 -08:00
if lands_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , lands_mask , [ ' Lands Matter ' , ' Ramp ' ] )
logger . info ( f ' Tagged { lands_mask . sum ( ) } extra lands cards ' )
2025-01-08 12:06:33 -08:00
if search_mask . any ( ) :
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , search_mask , [ ' Lands Matter ' , ' Ramp ' ] )
logger . info ( f ' Tagged { search_mask . sum ( ) } land search cards ' )
2025-01-08 12:06:33 -08:00
# Log completion
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed ramp tagging in { duration : .2f } s ' )
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_ramp: { str ( e ) } ' )
2025-01-08 12:06:33 -08:00
raise
2025-01-08 17:26:02 -08:00
2025-01-08 12:06:33 -08:00
### Other Misc Themes
def tag_for_themes ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that fit other themes that haven ' t been done so far.
This function will call on functions to tag for :
- Aggo
- Aristocrats
- Big Mana
- Blink
- Burn
- Clones
- Control
- Energy
- Infect
- Legends Matter
- Little Creatures
- Mill
- Monarch
- Multiple Copy Cards ( i . e . Hare Apparent or Dragon ' s Approach)
- Superfriends
- Reanimate
- Stax
- Theft
- Toughess Matters
- Topdeck
- X Spells
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting tagging for remaining themes in { color } _cards.csv ' )
2025-01-08 12:06:33 -08:00
print ( ' \n =============== \n ' )
tag_for_aggro ( df , color )
print ( ' \n ========== \n ' )
tag_for_aristocrats ( df , color )
print ( ' \n ========== \n ' )
tag_for_big_mana ( df , color )
print ( ' \n ========== \n ' )
tag_for_blink ( df , color )
print ( ' \n ========== \n ' )
tag_for_burn ( df , color )
print ( ' \n ========== \n ' )
tag_for_clones ( df , color )
print ( ' \n ========== \n ' )
tag_for_control ( df , color )
print ( ' \n ========== \n ' )
tag_for_energy ( df , color )
print ( ' \n ========== \n ' )
tag_for_infect ( df , color )
print ( ' \n ========== \n ' )
2025-01-08 16:04:42 -08:00
tag_for_legends_matter ( df , color )
2025-01-08 12:06:33 -08:00
print ( ' \n ========== \n ' )
tag_for_little_guys ( df , color )
print ( ' \n ========== \n ' )
tag_for_mill ( df , color )
print ( ' \n ========== \n ' )
tag_for_monarch ( df , color )
print ( ' \n ========== \n ' )
tag_for_multiple_copies ( df , color )
print ( ' \n ========== \n ' )
tag_for_planeswalkers ( df , color )
print ( ' \n ========== \n ' )
tag_for_reanimate ( df , color )
print ( ' \n ========== \n ' )
tag_for_stax ( df , color )
print ( ' \n ========== \n ' )
tag_for_theft ( df , color )
print ( ' \n ========== \n ' )
tag_for_toughness ( df , color )
print ( ' \n ========== \n ' )
tag_for_topdeck ( df , color )
print ( ' \n ========== \n ' )
tag_for_x_spells ( df , color )
2025-01-08 17:26:02 -08:00
print ( ' \n ========== \n ' )
2025-01-08 12:06:33 -08:00
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed theme tagging in { duration : .2f } s ' )
2025-01-08 12:06:33 -08:00
## Aggro
def create_aggro_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with aggro-related text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have aggro text patterns
"""
text_patterns = [
' a creature attacking ' ,
' deal combat damage ' ,
' deals combat damage ' ,
' have riot ' ,
' this creature attacks ' ,
' whenever you attack ' ,
' whenever .* attack ' ,
' whenever .* deals combat ' ,
' you control attack ' ,
' you control deals combat ' ,
' untap all attacking creatures '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 12:06:33 -08:00
def create_aggro_keyword_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with aggro-related keywords.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have aggro keywords
"""
keyword_patterns = [
' Blitz ' ,
' Deathtouch ' ,
' Double Strike ' ,
' First Strike ' ,
' Fear ' ,
' Haste ' ,
' Menace ' ,
' Myriad ' ,
' Prowl ' ,
' Raid ' ,
' Shadow ' ,
' Spectacle ' ,
' Trample '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_keyword_mask ( df , keyword_patterns )
2025-01-08 12:06:33 -08:00
def create_aggro_theme_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with aggro-related themes.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have aggro themes
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_tag_mask ( df , [ ' Voltron ' ] )
2025-01-08 12:06:33 -08:00
def tag_for_aggro ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that fit the Aggro theme using vectorized operations.
This function identifies and tags cards that support aggressive strategies including :
- Cards that care about attacking
- Cards with combat - related keywords
- Cards that deal combat damage
- Cards that support Voltron strategies
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting Aggro strategy tagging for { color } _cards.csv ' )
2025-01-08 12:06:33 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 12:06:33 -08:00
# Create masks for different aggro aspects
text_mask = create_aggro_text_mask ( df )
keyword_mask = create_aggro_keyword_mask ( df )
theme_mask = create_aggro_theme_mask ( df )
# Combine masks
final_mask = text_mask | keyword_mask | theme_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Aggro ' , ' Combat Matters ' ] )
2025-01-08 12:06:33 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with Aggro strategy in { duration : .2f } s ' )
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_aggro: { str ( e ) } ' )
2025-01-08 12:06:33 -08:00
raise
2025-01-08 17:26:02 -08:00
2025-01-08 12:06:33 -08:00
## Aristocrats
2025-01-08 17:26:02 -08:00
def create_aristocrat_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with aristocrat-related text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have aristocrat text patterns
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , settings . ARISTOCRAT_TEXT_PATTERNS )
2025-01-08 17:26:02 -08:00
def create_aristocrat_name_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for specific aristocrat-related cards.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards are specific aristocrat cards
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_name_mask ( df , settings . ARISTOCRAT_SPECIFIC_CARDS )
2025-01-08 17:26:02 -08:00
def create_aristocrat_self_sacrifice_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for creatures with self-sacrifice effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which creatures have self - sacrifice effects
"""
# Create base creature mask
2025-01-14 10:10:30 -08:00
creature_mask = tag_utils . create_type_mask ( df , ' Creature ' )
2025-01-08 17:26:02 -08:00
# Create name-based patterns
def check_self_sacrifice ( row ) :
if pd . isna ( row [ ' text ' ] ) or pd . isna ( row [ ' name ' ] ) :
return False
name = row [ ' name ' ] . lower ( )
text = row [ ' text ' ] . lower ( )
return f ' sacrifice { name } ' in text or f ' when { name } dies ' in text
# Apply patterns to creature cards
return creature_mask & df . apply ( check_self_sacrifice , axis = 1 )
def create_aristocrat_keyword_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with aristocrat-related keywords.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have aristocrat keywords
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_keyword_mask ( df , ' Blitz ' )
2025-01-08 17:26:02 -08:00
def create_aristocrat_exclusion_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that should be excluded from aristocrat effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards should be excluded
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , settings . ARISTOCRAT_EXCLUSION_PATTERNS )
2025-01-08 17:26:02 -08:00
2025-01-08 12:06:33 -08:00
def tag_for_aristocrats ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that fit the Aristocrats or Sacrifice Matters themes using vectorized operations.
This function identifies and tags cards that care about sacrificing permanents or creatures dying , including :
- Cards with sacrifice abilities or triggers
- Cards that care about creatures dying
- Cards with self - sacrifice effects
- Cards with Blitz or similar mechanics
2025-01-08 17:26:02 -08:00
The function uses efficient vectorized operations and separate mask creation functions
for different aspects of the aristocrats theme . It handles :
- Text - based patterns for sacrifice and death triggers
- Specific named cards known for aristocrats strategies
- Self - sacrifice effects on creatures
- Relevant keywords like Blitz
- Proper exclusions to avoid false positives
2025-01-08 12:06:33 -08:00
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting aristocrats effect tagging for { color } _cards.csv ' )
2025-01-08 12:06:33 -08:00
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' name ' , ' type ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 12:06:33 -08:00
2025-01-08 17:26:02 -08:00
# Create masks for different aristocrat patterns
text_mask = create_aristocrat_text_mask ( df )
name_mask = create_aristocrat_name_mask ( df )
self_sacrifice_mask = create_aristocrat_self_sacrifice_mask ( df )
keyword_mask = create_aristocrat_keyword_mask ( df )
exclusion_mask = create_aristocrat_exclusion_mask ( df )
2025-01-08 12:06:33 -08:00
# Combine masks
2025-01-08 17:26:02 -08:00
final_mask = ( text_mask | name_mask | self_sacrifice_mask | keyword_mask ) & ~ exclusion_mask
2025-01-08 12:06:33 -08:00
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Aristocrats ' , ' Sacrifice Matters ' ] )
2025-01-08 12:06:33 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with aristocrats effects in { duration : .2f } s ' )
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_aristocrats: { str ( e ) } ' )
2025-01-08 12:06:33 -08:00
raise
2025-01-08 17:26:02 -08:00
2025-01-08 12:06:33 -08:00
## Big Mana
def create_big_mana_cost_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with high mana costs or X costs.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have high / X mana costs
"""
# High mana value mask
high_cost = df [ ' manaValue ' ] . fillna ( 0 ) . astype ( float ) > = 5
# X cost mask
x_cost = df [ ' manaCost ' ] . fillna ( ' ' ) . str . contains ( ' {X} ' , case = False , regex = False )
return high_cost | x_cost
def tag_for_big_mana ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about or generate large amounts of mana using vectorized operations.
This function identifies and tags cards that :
- Have high mana costs ( 5 or greater )
- Care about high mana values or power
- Generate large amounts of mana
- Have X costs
- Have keywords related to mana generation
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting big mana tagging for { color } _cards.csv ' )
2025-01-08 12:06:33 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' manaValue ' , ' manaCost ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 12:06:33 -08:00
# Create masks for different big mana patterns
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , settings . BIG_MANA_TEXT_PATTERNS )
keyword_mask = tag_utils . create_keyword_mask ( df , settings . BIG_MANA_KEYWORDS )
2025-01-08 12:06:33 -08:00
cost_mask = create_big_mana_cost_mask ( df )
2025-01-14 10:10:30 -08:00
specific_mask = tag_utils . create_name_mask ( df , settings . BIG_MANA_SPECIFIC_CARDS )
tag_mask = tag_utils . create_tag_mask ( df , ' Cost Reduction ' )
2025-01-08 12:06:33 -08:00
# Combine all masks
final_mask = text_mask | keyword_mask | cost_mask | specific_mask | tag_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Big Mana ' ] )
2025-01-08 12:06:33 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with big mana effects in { duration : .2f } s ' )
2025-01-08 12:06:33 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_big_mana: { str ( e ) } ' )
2025-01-08 12:06:33 -08:00
raise
2025-01-08 16:04:42 -08:00
2024-12-16 22:58:02 -08:00
## Blink
2025-01-08 16:04:42 -08:00
def create_etb_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with enter-the-battlefield effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have ETB effects
"""
text_patterns = [
' creature entering causes ' ,
' permanent entering the battlefield ' ,
' permanent you control enters ' ,
' whenever another creature enters ' ,
' whenever another nontoken creature enters ' ,
' when this creature enters ' ,
' whenever this creature enters '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 16:04:42 -08:00
def create_ltb_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with leave-the-battlefield effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have LTB effects
"""
text_patterns = [
' when this creature leaves ' ,
' whenever this creature leaves '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 16:04:42 -08:00
def create_blink_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with blink/flicker text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have blink / flicker effects
"""
text_patterns = [
' exile any number of other ' ,
' exile one or more cards from your hand ' ,
' permanent you control, then return ' ,
' permanents you control, then return ' ,
' return it to the battlefield ' ,
' return that card to the battlefield ' ,
' return them to the battlefield ' ,
' return those cards to the battlefield ' ,
' triggered ability of a permanent '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 16:04:42 -08:00
def tag_for_blink ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that have blink/flicker effects using vectorized operations.
This function identifies and tags cards with blink / flicker effects including :
- Enter - the - battlefield ( ETB ) triggers
- Leave - the - battlefield ( LTB ) triggers
- Exile and return effects
- Permanent flicker effects
The function maintains proper tag hierarchy and ensures consistent application
of related tags like ' Blink ' , ' Enter the Battlefield ' , and ' Leave the Battlefield ' .
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting blink/flicker effect tagging for { color } _cards.csv ' )
2025-01-08 16:04:42 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' name ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:04:42 -08:00
# Create masks for different blink patterns
etb_mask = create_etb_mask ( df )
ltb_mask = create_ltb_mask ( df )
blink_mask = create_blink_text_mask ( df )
# Create name-based masks
name_patterns = df . apply (
lambda row : f ' when { row [ " name " ] } enters|whenever { row [ " name " ] } enters|when { row [ " name " ] } leaves|whenever { row [ " name " ] } leaves ' ,
axis = 1
)
name_mask = df . apply (
lambda row : bool ( re . search ( name_patterns [ row . name ] , row [ ' text ' ] , re . IGNORECASE ) ) if pd . notna ( row [ ' text ' ] ) else False ,
axis = 1
)
# Combine all masks
final_mask = etb_mask | ltb_mask | blink_mask | name_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Blink ' , ' Enter the Battlefield ' , ' Leave the Battlefield ' ] )
2025-01-08 16:04:42 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with blink/flicker effects in { duration : .2f } s ' )
2025-01-08 16:04:42 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_blink: { str ( e ) } ' )
2025-01-08 16:04:42 -08:00
raise
2024-12-16 22:58:02 -08:00
## Burn
2025-01-08 16:04:42 -08:00
def create_burn_damage_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with damage-dealing effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have damage effects
"""
# Create damage number patterns using list comprehension
damage_patterns = [ f ' deals { i } damage ' for i in range ( 1 , 101 ) ] + [ ' deals x damage ' ]
2025-01-14 10:10:30 -08:00
damage_mask = tag_utils . create_text_mask ( df , damage_patterns )
2025-01-08 16:04:42 -08:00
# Create general damage trigger patterns
trigger_patterns = [
' deals combat damage ' ,
' deals damage ' ,
' deals noncombat damage ' ,
' deals that much damage ' ,
' excess damage ' ,
' excess noncombat damage ' ,
' would deal an amount of noncombat damage ' ,
' would deal damage ' ,
' would deal noncombat damage '
]
2025-01-14 10:10:30 -08:00
trigger_mask = tag_utils . create_text_mask ( df , trigger_patterns )
2025-01-08 16:04:42 -08:00
# Create pinger patterns
pinger_patterns = [ ' deals 1 damage ' , ' exactly 1 damage ' ]
2025-01-14 10:10:30 -08:00
pinger_mask = tag_utils . create_text_mask ( df , pinger_patterns )
2025-01-08 16:04:42 -08:00
return damage_mask | trigger_mask | pinger_mask
def create_burn_life_loss_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with life loss effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have life loss effects
"""
# Create life loss number patterns
life_patterns = [ f ' lose { i } life ' for i in range ( 1 , 101 ) ]
life_patterns . extend ( [ f ' loses { i } life ' for i in range ( 1 , 101 ) ] )
life_patterns . append ( ' lose x life ' )
life_patterns . append ( ' loses x life ' )
2025-01-14 10:10:30 -08:00
life_mask = tag_utils . create_text_mask ( df , life_patterns )
2025-01-08 16:04:42 -08:00
# Create general life loss trigger patterns
trigger_patterns = [
' each 1 life ' ,
' loses that much life ' ,
' opponent lost life ' ,
' opponent loses life ' ,
' player loses life ' ,
' unspent mana causes that player to lose that much life ' ,
' would lose life '
]
2025-01-14 10:10:30 -08:00
trigger_mask = tag_utils . create_text_mask ( df , trigger_patterns )
2025-01-08 16:04:42 -08:00
return life_mask | trigger_mask
def create_burn_keyword_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with burn-related keywords.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have burn keywords
"""
keyword_patterns = [ ' Bloodthirst ' , ' Spectacle ' ]
2025-01-14 10:10:30 -08:00
return tag_utils . create_keyword_mask ( df , keyword_patterns )
2025-01-08 16:04:42 -08:00
def create_burn_exclusion_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that should be excluded from burn effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards should be excluded
"""
# Add specific exclusion patterns here if needed
return pd . Series ( False , index = df . index )
def tag_for_burn ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that deal damage or cause life loss using vectorized operations.
This function identifies and tags cards with burn effects including :
- Direct damage dealing
- Life loss effects
- Burn - related keywords ( Bloodthirst , Spectacle )
- Pinger effects ( 1 damage )
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting burn effect tagging for { color } _cards.csv ' )
2025-01-08 16:04:42 -08:00
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:04:42 -08:00
# Create masks for different burn patterns
damage_mask = create_burn_damage_mask ( df )
life_mask = create_burn_life_loss_mask ( df )
keyword_mask = create_burn_keyword_mask ( df )
exclusion_mask = create_burn_exclusion_mask ( df )
# Combine masks
burn_mask = ( damage_mask | life_mask | keyword_mask ) & ~ exclusion_mask
2025-01-14 10:10:30 -08:00
pinger_mask = tag_utils . create_text_mask ( df , [ ' deals 1 damage ' , ' exactly 1 damage ' , ' loses 1 life ' ] )
2025-01-08 16:04:42 -08:00
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , burn_mask , [ ' Burn ' ] )
tag_utils . apply_tag_vectorized ( df , pinger_mask & ~ exclusion_mask , [ ' Pingers ' ] )
2025-01-08 16:04:42 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { burn_mask . sum ( ) } cards with burn effects in { duration : .2f } s ' )
2025-01-08 16:04:42 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_burn: { str ( e ) } ' )
2025-01-08 16:04:42 -08:00
raise
## Clones
def create_clone_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with clone-related text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have clone text patterns
"""
text_patterns = [
' a copy of a creature ' ,
' a copy of an aura ' ,
' a copy of a permanent ' ,
' a token that \' s a copy of ' ,
' as a copy of ' ,
' becomes a copy of ' ,
' " legend rule " doesn \' t apply ' ,
' twice that many of those tokens '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 16:04:42 -08:00
def create_clone_keyword_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with clone-related keywords.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have clone keywords
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_keyword_mask ( df , ' Myriad ' )
2025-01-08 16:04:42 -08:00
def create_clone_exclusion_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that should be excluded from clone effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards should be excluded
"""
# Add specific exclusion patterns here if needed
return pd . Series ( False , index = df . index )
def tag_for_clones ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that create copies or have clone effects using vectorized operations.
This function identifies and tags cards that :
- Create copies of creatures or permanents
- Have copy - related keywords like Myriad
- Ignore the legend rule
- Double token creation
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting clone effect tagging for { color } _cards.csv ' )
2025-01-08 16:04:42 -08:00
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:04:42 -08:00
# Create masks for different clone patterns
text_mask = create_clone_text_mask ( df )
keyword_mask = create_clone_keyword_mask ( df )
exclusion_mask = create_clone_exclusion_mask ( df )
# Combine masks
final_mask = ( text_mask | keyword_mask ) & ~ exclusion_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Clones ' ] )
2025-01-08 16:04:42 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with clone effects in { duration : .2f } s ' )
2025-01-08 16:04:42 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_clones: { str ( e ) } ' )
2025-01-08 16:04:42 -08:00
raise
## Control
def create_control_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with control-related text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have control text patterns
"""
text_patterns = [
' a player casts ' ,
' can \' t attack you ' ,
' cast your first spell during each opponent \' s turn ' ,
' choose new target ' ,
' choose target opponent ' ,
' counter target ' ,
' of an opponent \' s choice ' ,
' opponent cast ' ,
' return target ' ,
' tap an untapped creature ' ,
' your opponents cast '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 16:04:42 -08:00
def create_control_keyword_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with control-related keywords.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have control keywords
"""
keyword_patterns = [ ' Council \' s dilemma ' ]
2025-01-14 10:10:30 -08:00
return tag_utils . create_keyword_mask ( df , keyword_patterns )
2025-01-08 16:04:42 -08:00
def create_control_specific_cards_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for specific control-related cards.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards are specific control cards
"""
specific_cards = [
' Azor \' s Elocutors ' ,
' Baral, Chief of Compliance ' ,
' Dragonlord Ojutai ' ,
' Grand Arbiter Augustin IV ' ,
' Lavinia, Azorius Renegade ' ,
' Talrand, Sky Summoner '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_name_mask ( df , specific_cards )
2025-01-08 16:04:42 -08:00
def tag_for_control ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that fit the Control theme using vectorized operations.
This function identifies and tags cards that control the game through :
- Counter magic
- Bounce effects
- Tap effects
- Opponent restrictions
- Council ' s dilemma effects
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting control effect tagging for { color } _cards.csv ' )
2025-01-08 16:04:42 -08:00
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' keywords ' , ' name ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:04:42 -08:00
# Create masks for different control patterns
text_mask = create_control_text_mask ( df )
keyword_mask = create_control_keyword_mask ( df )
specific_mask = create_control_specific_cards_mask ( df )
# Combine masks
final_mask = text_mask | keyword_mask | specific_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Control ' ] )
2025-01-08 16:04:42 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with control effects in { duration : .2f } s ' )
2025-01-08 16:04:42 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_control: { str ( e ) } ' )
2025-01-08 16:04:42 -08:00
raise
## Energy
def tag_for_energy ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about energy counters using vectorized operations.
This function identifies and tags cards that :
- Use energy counters ( { E } )
- Care about energy counters
- Generate or spend energy
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting energy counter tagging for { color } _cards.csv ' )
2025-01-08 16:04:42 -08:00
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:04:42 -08:00
# Create mask for energy text
energy_mask = df [ ' text ' ] . str . contains ( ' {e} ' , case = False , na = False )
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , energy_mask , [ ' Energy ' ] )
2025-01-08 16:04:42 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { energy_mask . sum ( ) } cards with energy effects in { duration : .2f } s ' )
2025-01-08 16:04:42 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_energy: { str ( e ) } ' )
2025-01-08 16:04:42 -08:00
raise
## Infect
def create_infect_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with infect-related text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have infect text patterns
"""
text_patterns = [
' one or more counter ' ,
' poison counter ' ,
' toxic [1-10] ' ,
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 16:04:42 -08:00
def create_infect_keyword_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with infect-related keywords.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have infect keywords
"""
keyword_patterns = [
' Infect ' ,
' Proliferate ' ,
' Toxic ' ,
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_keyword_mask ( df , keyword_patterns )
2025-01-08 16:04:42 -08:00
def create_infect_exclusion_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that should be excluded from infect effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards should be excluded
"""
# Add specific exclusion patterns here if needed
return pd . Series ( False , index = df . index )
def tag_for_infect ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that have infect-related effects using vectorized operations.
This function identifies and tags cards with infect effects including :
- Infect keyword ability
- Toxic keyword ability
- Proliferate mechanic
- Poison counter effects
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting infect effect tagging for { color } _cards.csv ' )
2025-01-08 16:04:42 -08:00
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:04:42 -08:00
# Create masks for different infect patterns
text_mask = create_infect_text_mask ( df )
keyword_mask = create_infect_keyword_mask ( df )
exclusion_mask = create_infect_exclusion_mask ( df )
# Combine masks
final_mask = ( text_mask | keyword_mask ) & ~ exclusion_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Infect ' ] )
2025-01-08 16:04:42 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with infect effects in { duration : .2f } s ' )
2025-01-08 16:04:42 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_infect: { str ( e ) } ' )
2025-01-08 16:04:42 -08:00
raise
## Legends Matter
def create_legends_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with legendary/historic text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have legendary / historic text patterns
"""
text_patterns = [
' a legendary creature ' ,
' another legendary ' ,
' cast a historic ' ,
' cast a legendary ' ,
' cast legendary ' ,
' equip legendary ' ,
' historic cards ' ,
' historic creature ' ,
' historic permanent ' ,
' historic spells ' ,
' legendary creature you control ' ,
' legendary creatures you control ' ,
' legendary permanents ' ,
' legendary spells you ' ,
' number of legendary ' ,
' other legendary ' ,
' play a historic ' ,
' play a legendary ' ,
' target legendary ' ,
' the " legend rule " doesn \' t '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 16:04:42 -08:00
def create_legends_type_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with Legendary in their type line.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards are Legendary
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_type_mask ( df , ' Legendary ' )
2025-01-08 16:04:42 -08:00
def tag_for_legends_matter ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about legendary permanents using vectorized operations.
This function identifies and tags cards that :
- Are legendary permanents
- Care about legendary permanents
- Care about historic spells / permanents
- Modify the legend rule
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting legendary/historic tagging for { color } _cards.csv ' )
2025-01-08 16:04:42 -08:00
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' type ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:04:42 -08:00
# Create masks for different legendary patterns
text_mask = create_legends_text_mask ( df )
type_mask = create_legends_type_mask ( df )
# Combine masks
final_mask = text_mask | type_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Historics Matter ' , ' Legends Matter ' ] )
2025-01-08 16:04:42 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with legendary/historic effects in { duration : .2f } s ' )
2025-01-08 16:04:42 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_legends_matter: { str ( e ) } ' )
2025-01-08 16:04:42 -08:00
raise
## Little Fellas
def create_little_guys_power_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for creatures with power 2 or less.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have power 2 or less
"""
# Create mask for valid power values
valid_power = pd . to_numeric ( df [ ' power ' ] , errors = ' coerce ' )
# Create mask for power <= 2
return ( valid_power < = 2 ) & pd . notna ( valid_power )
def tag_for_little_guys ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that are or care about low-power creatures using vectorized operations.
This function identifies and tags :
- Creatures with power 2 or less
- Cards that care about creatures with low power
- Cards that reference power thresholds of 2 or less
The function handles edge cases like ' * ' in power values and maintains proper
tag hierarchy .
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting low-power creature tagging for { color } _cards.csv ' )
2025-01-08 16:04:42 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' power ' , ' text ' , ' themeTags ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:04:42 -08:00
# Create masks for different patterns
power_mask = create_little_guys_power_mask ( df )
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , ' power 2 or less ' )
2025-01-08 16:04:42 -08:00
# Combine masks
final_mask = power_mask | text_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Little Fellas ' ] )
2025-01-08 16:04:42 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with Little Fellas in { duration : .2f } s ' )
2025-01-08 16:04:42 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_little_guys: { str ( e ) } ' )
2025-01-08 16:04:42 -08:00
raise
## Mill
def create_mill_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with mill-related text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have mill text patterns
"""
# Create text pattern masks
text_patterns = [
' descended ' ,
' from a graveyard ' ,
' from your graveyard ' ,
' in your graveyard ' ,
' into his or her graveyard ' ,
' into their graveyard ' ,
' into your graveyard ' ,
' mills that many cards ' ,
' opponent \' s graveyard ' ,
' put into a graveyard ' ,
' put into an opponent \' s graveyard ' ,
' put into your graveyard ' ,
' rad counter ' ,
' surveil ' ,
' would mill '
]
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 16:04:42 -08:00
# Create mill number patterns
mill_patterns = [ f ' mill { num } ' for num in settings . num_to_search ]
mill_patterns . extend ( [ f ' mills { num } ' for num in settings . num_to_search ] )
2025-01-14 10:10:30 -08:00
number_mask = tag_utils . create_text_mask ( df , mill_patterns )
2025-01-08 16:04:42 -08:00
return text_mask | number_mask
def create_mill_keyword_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with mill-related keywords.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have mill keywords
"""
keyword_patterns = [ ' Descend ' , ' Mill ' , ' Surveil ' ]
2025-01-14 10:10:30 -08:00
return tag_utils . create_keyword_mask ( df , keyword_patterns )
2025-01-08 16:04:42 -08:00
def tag_for_mill ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that mill cards or care about milling using vectorized operations.
This function identifies and tags cards with mill effects including :
- Direct mill effects ( putting cards from library to graveyard )
- Mill - related keywords ( Descend , Mill , Surveil )
- Cards that care about graveyards
- Cards that track milled cards
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting mill effect tagging for { color } _cards.csv ' )
2025-01-08 16:04:42 -08:00
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:04:42 -08:00
# Create masks for different mill patterns
text_mask = create_mill_text_mask ( df )
keyword_mask = create_mill_keyword_mask ( df )
# Combine masks
final_mask = text_mask | keyword_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Mill ' ] )
2025-01-08 16:04:42 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with mill effects in { duration : .2f } s ' )
2025-01-08 16:04:42 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_mill: { str ( e ) } ' )
2025-01-08 16:04:42 -08:00
raise
def tag_for_monarch ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about the monarch mechanic using vectorized operations.
This function identifies and tags cards that interact with the monarch mechanic , including :
- Cards that make you become the monarch
- Cards that prevent becoming the monarch
- Cards with monarch - related triggers
- Cards with the monarch keyword
The function uses vectorized operations for performance and follows patterns
established in other tagging functions .
Args :
df : DataFrame containing card data with text and keyword columns
color : Color identifier for logging purposes ( e . g . ' white ' , ' blue ' )
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting monarch mechanic tagging for { color } _cards.csv ' )
2025-01-08 16:04:42 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:04:42 -08:00
# Create text pattern mask
text_patterns = [
' becomes? the monarch ' ,
' can \' t become the monarch ' ,
' is the monarch ' ,
' was the monarch ' ,
' you are the monarch ' ,
' you become the monarch ' ,
' you can \' t become the monarch ' ,
' you \' re the monarch '
]
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 16:04:42 -08:00
# Create keyword mask
2025-01-14 10:10:30 -08:00
keyword_mask = tag_utils . create_keyword_mask ( df , ' Monarch ' )
2025-01-08 16:04:42 -08:00
# Combine masks
final_mask = text_mask | keyword_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Monarch ' ] )
2025-01-08 16:04:42 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with monarch effects in { duration : .2f } s ' )
2025-01-08 16:04:42 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_monarch: { str ( e ) } ' )
2025-01-08 16:04:42 -08:00
raise
## Multi-copy cards
def tag_for_multiple_copies ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that allow having multiple copies in a deck using vectorized operations.
This function identifies and tags cards that can have more than 4 copies in a deck ,
like Seven Dwarves or Persistent Petitioners . It uses the multiple_copy_cards list
from settings to identify these cards .
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting multiple copies tagging for { color } _cards.csv ' )
2025-01-08 16:04:42 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' name ' , ' themeTags ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:04:42 -08:00
# Create mask for multiple copy cards
2025-01-14 10:10:30 -08:00
multiple_copies_mask = tag_utils . create_name_mask ( df , multiple_copy_cards )
2025-01-08 16:04:42 -08:00
# Apply tags
if multiple_copies_mask . any ( ) :
# Get matching card names
matching_cards = df [ multiple_copies_mask ] [ ' name ' ] . unique ( )
2024-12-17 12:02:53 -08:00
2025-01-08 16:04:42 -08:00
# Apply base tag
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , multiple_copies_mask , [ ' Multiple Copies ' ] )
2024-12-17 12:02:53 -08:00
2025-01-08 16:04:42 -08:00
# Apply individual card name tags
for card_name in matching_cards :
card_mask = df [ ' name ' ] == card_name
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , card_mask , [ card_name ] )
2025-01-08 16:04:42 -08:00
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { multiple_copies_mask . sum ( ) } cards with multiple copies effects ' )
2025-01-08 16:04:42 -08:00
# Log completion
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed multiple copies tagging in { duration : .2f } s ' )
2025-01-08 16:04:42 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_multiple_copies: { str ( e ) } ' )
2025-01-08 16:04:42 -08:00
raise
## Planeswalkers
def create_planeswalker_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with planeswalker-related text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have planeswalker text patterns
"""
text_patterns = [
' a planeswalker ' ,
' affinity for planeswalker ' ,
' enchant planeswalker ' ,
' historic permanent ' ,
' legendary permanent ' ,
' loyalty ability ' ,
' one or more counter ' ,
' planeswalker spells ' ,
' planeswalker type '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 16:04:42 -08:00
def create_planeswalker_type_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with Planeswalker type.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards are Planeswalkers
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_type_mask ( df , ' Planeswalker ' )
2025-01-08 16:04:42 -08:00
def create_planeswalker_keyword_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with planeswalker-related keywords.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have planeswalker keywords
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_keyword_mask ( df , ' Proliferate ' )
2025-01-08 16:04:42 -08:00
def tag_for_planeswalkers ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about planeswalkers using vectorized operations.
This function identifies and tags cards that :
- Are planeswalker cards
- Care about planeswalkers
- Have planeswalker - related keywords like Proliferate
- Interact with loyalty abilities
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting planeswalker tagging for { color } _cards.csv ' )
2025-01-08 16:04:42 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' type ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:04:42 -08:00
# Create masks for different planeswalker patterns
text_mask = create_planeswalker_text_mask ( df )
type_mask = create_planeswalker_type_mask ( df )
keyword_mask = create_planeswalker_keyword_mask ( df )
# Combine masks
final_mask = text_mask | type_mask | keyword_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Planeswalkers ' , ' Super Friends ' ] )
2025-01-08 16:04:42 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with planeswalker effects in { duration : .2f } s ' )
2025-01-08 16:04:42 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_planeswalkers: { str ( e ) } ' )
2025-01-08 16:04:42 -08:00
raise
## Reanimator
def create_reanimator_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with reanimator-related text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have reanimator text patterns
"""
text_patterns = [
' descended ' ,
' discard your hand ' ,
' from a graveyard ' ,
' in a graveyard ' ,
' into a graveyard ' ,
' leave a graveyard ' ,
' in your graveyard ' ,
' into your graveyard ' ,
' leave your graveyard '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 16:04:42 -08:00
def create_reanimator_keyword_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with reanimator-related keywords.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have reanimator keywords
"""
keyword_patterns = [
' Blitz ' ,
' Connive ' ,
' Descend ' ,
' Escape ' ,
' Flashback ' ,
' Mill '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_keyword_mask ( df , keyword_patterns )
2025-01-08 16:04:42 -08:00
def create_reanimator_type_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with reanimator-related creature types.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have reanimator creature types
"""
return df [ ' creatureTypes ' ] . apply ( lambda x : ' Zombie ' in x if isinstance ( x , list ) else False )
def tag_for_reanimate ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about graveyard recursion using vectorized operations.
This function identifies and tags cards with reanimator effects including :
- Cards that interact with graveyards
- Cards with reanimator - related keywords ( Blitz , Connive , etc )
- Cards that loot or mill
- Zombie tribal synergies
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting reanimator effect tagging for { color } _cards.csv ' )
2025-01-08 16:04:42 -08:00
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' keywords ' , ' creatureTypes ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:04:42 -08:00
# Create masks for different reanimator patterns
text_mask = create_reanimator_text_mask ( df )
keyword_mask = create_reanimator_keyword_mask ( df )
type_mask = create_reanimator_type_mask ( df )
# Combine masks
final_mask = text_mask | keyword_mask | type_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Reanimate ' ] )
2025-01-08 16:04:42 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with reanimator effects in { duration : .2f } s ' )
2025-01-08 16:04:42 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_reanimate: { str ( e ) } ' )
2025-01-08 16:04:42 -08:00
raise
## Stax
def create_stax_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with stax-related text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have stax text patterns
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , settings . STAX_TEXT_PATTERNS )
2025-01-08 16:04:42 -08:00
def create_stax_name_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards used in stax strategies.
Args :
df : DataFrame to search
2024-12-16 22:58:02 -08:00
2025-01-08 16:04:42 -08:00
Returns :
Boolean Series indicating which cards have stax text patterns
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , settings . STAX_SPECIFIC_CARDS )
2024-12-16 22:58:02 -08:00
2025-01-08 16:04:42 -08:00
def create_stax_tag_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with stax-related tags.
2024-12-16 22:58:02 -08:00
2025-01-08 16:04:42 -08:00
Args :
df : DataFrame to search
2024-12-16 22:58:02 -08:00
2025-01-08 16:04:42 -08:00
Returns :
Boolean Series indicating which cards have stax tags
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_tag_mask ( df , ' Control ' )
2024-12-16 22:58:02 -08:00
2025-01-08 16:04:42 -08:00
def create_stax_exclusion_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that should be excluded from stax effects.
2024-12-30 11:43:36 -08:00
2025-01-08 16:04:42 -08:00
Args :
df : DataFrame to search
2024-12-16 22:58:02 -08:00
2025-01-08 16:04:42 -08:00
Returns :
Boolean Series indicating which cards should be excluded
"""
# Add specific exclusion patterns here if needed
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , settings . STAX_EXCLUSION_PATTERNS )
2024-12-17 15:10:52 -08:00
2025-01-08 16:04:42 -08:00
def tag_for_stax ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that fit the Stax theme using vectorized operations.
2024-12-30 11:43:36 -08:00
2025-01-08 16:04:42 -08:00
This function identifies and tags cards that restrict or tax opponents including :
- Cards that prevent actions ( can ' t attack, can ' t cast , etc )
- Cards that tax actions ( spells cost more )
- Cards that control opponents ' resources
- Cards that create asymmetric effects
2024-12-16 22:58:02 -08:00
2025-01-08 16:04:42 -08:00
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
2024-12-16 22:58:02 -08:00
2025-01-08 16:04:42 -08:00
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting stax effect tagging for { color } _cards.csv ' )
2025-01-08 16:04:42 -08:00
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:04:42 -08:00
# Create masks for different stax patterns
text_mask = create_stax_text_mask ( df )
name_mask = create_stax_name_mask ( df )
tag_mask = create_stax_tag_mask ( df )
exclusion_mask = create_stax_exclusion_mask ( df )
# Combine masks
final_mask = ( text_mask | tag_mask | name_mask ) & ~ exclusion_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Stax ' ] )
2025-01-08 16:04:42 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with stax effects in { duration : .2f } s ' )
2025-01-08 16:04:42 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_stax: { str ( e ) } ' )
2025-01-08 16:04:42 -08:00
raise
2024-12-16 22:58:02 -08:00
2024-12-17 15:10:52 -08:00
## Theft
2025-01-08 16:04:42 -08:00
def create_theft_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with theft-related text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have theft text patterns
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , settings . THEFT_TEXT_PATTERNS )
2025-01-08 16:04:42 -08:00
def create_theft_name_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for specific theft-related cards.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards are specific theft cards
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_name_mask ( df , settings . THEFT_SPECIFIC_CARDS )
2025-01-08 16:04:42 -08:00
def tag_for_theft ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that steal or use opponents ' resources using vectorized operations.
This function identifies and tags cards that :
- Cast spells owned by other players
- Take control of permanents
- Use opponents ' libraries
- Create theft - related effects
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting theft effect tagging for { color } _cards.csv ' )
2025-01-08 16:04:42 -08:00
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' name ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:04:42 -08:00
# Create masks for different theft patterns
text_mask = create_theft_text_mask ( df )
name_mask = create_theft_name_mask ( df )
# Combine masks
final_mask = text_mask | name_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Theft ' ] )
2025-01-08 16:04:42 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with theft effects in { duration : .2f } s ' )
2025-01-08 16:04:42 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_theft: { str ( e ) } ' )
2025-01-08 16:04:42 -08:00
raise
2024-12-17 15:10:52 -08:00
2024-12-16 22:58:02 -08:00
## Toughness Matters
2025-01-08 16:04:42 -08:00
def create_toughness_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with toughness-related text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have toughness text patterns
"""
text_patterns = [
' card \' s toughness ' ,
' creature \' s toughness ' ,
' damage equal to its toughness ' ,
' lesser toughness ' ,
' total toughness ' ,
' toughness greater ' ,
' with defender '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 16:04:42 -08:00
def create_toughness_keyword_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with toughness-related keywords.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have toughness keywords
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_keyword_mask ( df , ' Defender ' )
2025-01-08 16:04:42 -08:00
def _is_valid_numeric_comparison ( power : Union [ int , str , None ] , toughness : Union [ int , str , None ] ) - > bool :
""" Check if power and toughness values allow valid numeric comparison.
Args :
power : Power value to check
toughness : Toughness value to check
Returns :
True if values can be compared numerically , False otherwise
"""
try :
if power is None or toughness is None :
return False
return True
except ( ValueError , TypeError ) :
return False
def create_power_toughness_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards where toughness exceeds power.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have toughness > power
"""
valid_comparison = df . apply (
lambda row : _is_valid_numeric_comparison ( row [ ' power ' ] , row [ ' toughness ' ] ) ,
axis = 1
)
numeric_mask = valid_comparison & ( pd . to_numeric ( df [ ' toughness ' ] , errors = ' coerce ' ) >
pd . to_numeric ( df [ ' power ' ] , errors = ' coerce ' ) )
return numeric_mask
def tag_for_toughness ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about toughness using vectorized operations.
This function identifies and tags cards that :
- Reference toughness in their text
- Have the Defender keyword
- Have toughness greater than power
- Care about high toughness values
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting toughness tagging for { color } _cards.csv ' )
2025-01-08 16:04:42 -08:00
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' keywords ' , ' power ' , ' toughness ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:04:42 -08:00
# Create masks for different toughness patterns
text_mask = create_toughness_text_mask ( df )
keyword_mask = create_toughness_keyword_mask ( df )
power_toughness_mask = create_power_toughness_mask ( df )
# Combine masks
final_mask = text_mask | keyword_mask | power_toughness_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Toughness Matters ' ] )
2025-01-08 16:04:42 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with toughness effects in { duration : .2f } s ' )
2024-12-16 22:58:02 -08:00
2025-01-08 16:04:42 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_toughness: { str ( e ) } ' )
2025-01-08 16:04:42 -08:00
raise
2025-01-08 16:16:41 -08:00
2024-12-16 22:58:02 -08:00
## Topdeck
2025-01-08 16:16:41 -08:00
def create_topdeck_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with topdeck-related text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have topdeck text patterns
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , settings . TOPDECK_TEXT_PATTERNS )
2025-01-08 16:16:41 -08:00
def create_topdeck_keyword_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with topdeck-related keywords.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have topdeck keywords
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_keyword_mask ( df , settings . TOPDECK_KEYWORDS )
2025-01-08 16:16:41 -08:00
def create_topdeck_specific_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for specific topdeck-related cards.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards are specific topdeck cards
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_name_mask ( df , settings . TOPDECK_SPECIFIC_CARDS )
2025-01-08 16:16:41 -08:00
def create_topdeck_exclusion_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that should be excluded from topdeck effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards should be excluded
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , settings . TOPDECK_EXCLUSION_PATTERNS )
2025-01-08 16:16:41 -08:00
def tag_for_topdeck ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that manipulate the top of library using vectorized operations.
This function identifies and tags cards that interact with the top of the library including :
- Cards that look at or reveal top cards
- Cards with scry or surveil effects
- Cards with miracle or similar mechanics
- Cards that care about the order of the library
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting topdeck effect tagging for { color } _cards.csv ' )
2025-01-08 16:16:41 -08:00
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:16:41 -08:00
# Create masks for different topdeck patterns
text_mask = create_topdeck_text_mask ( df )
keyword_mask = create_topdeck_keyword_mask ( df )
specific_mask = create_topdeck_specific_mask ( df )
exclusion_mask = create_topdeck_exclusion_mask ( df )
# Combine masks
final_mask = ( text_mask | keyword_mask | specific_mask ) & ~ exclusion_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Topdeck ' ] )
2025-01-08 16:16:41 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with topdeck effects in { duration : .2f } s ' )
2025-01-08 16:16:41 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_topdeck: { str ( e ) } ' )
2025-01-08 16:16:41 -08:00
raise
2024-12-16 22:58:02 -08:00
## X Spells
2025-01-08 16:16:41 -08:00
def create_x_spells_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with X spell-related text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have X spell text patterns
"""
text_patterns = [
' cost {x} less ' ,
' don \' t lose this ' ,
' don \' t lose unspent ' ,
' lose unused mana ' ,
' unused mana would empty ' ,
' with {x} in its ' ,
' you cast cost {1} less ' ,
' you cast cost {2} less ' ,
' you cast cost {3} less ' ,
' you cast cost {4} less ' ,
' you cast cost {5} less '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 16:16:41 -08:00
def create_x_spells_mana_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with X in their mana cost.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have X in mana cost
"""
return df [ ' manaCost ' ] . fillna ( ' ' ) . str . contains ( ' {X} ' , case = True , regex = False )
def tag_for_x_spells ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that care about X spells using vectorized operations.
This function identifies and tags cards that :
- Have X in their mana cost
- Care about X spells or mana values
- Have cost reduction effects for X spells
- Preserve unspent mana
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting X spells tagging for { color } _cards.csv ' )
2025-01-08 16:16:41 -08:00
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' manaCost ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 16:16:41 -08:00
# Create masks for different X spell patterns
text_mask = create_x_spells_text_mask ( df )
mana_mask = create_x_spells_mana_mask ( df )
# Combine masks
final_mask = text_mask | mana_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' X Spells ' ] )
2025-01-08 16:16:41 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with X spell effects in { duration : .2f } s ' )
2025-01-08 16:16:41 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_x_spells: { str ( e ) } ' )
2025-01-08 16:16:41 -08:00
raise
2024-12-16 22:58:02 -08:00
2024-12-18 12:14:45 -08:00
### Interaction
## Overall tag for interaction group
2025-01-08 17:26:02 -08:00
def tag_for_interaction ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that interact with the board state or stack.
This function coordinates tagging of different interaction types including :
- Counterspells
- Board wipes
- Combat tricks
- Protection effects
- Spot removal
The function maintains proper tag hierarchy and ensures consistent application
of interaction - related tags .
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting interaction effect tagging for { color } _cards.csv ' )
2025-01-02 13:00:52 -08:00
print ( ' \n ========== \n ' )
2024-12-18 12:14:45 -08:00
2025-01-08 17:26:02 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' name ' , ' type ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 17:26:02 -08:00
# Process each type of interaction
sub_start = pd . Timestamp . now ( )
tag_for_counterspells ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed counterspell tagging in { ( pd . Timestamp . now ( ) - sub_start ) . total_seconds ( ) : .2f } s ' )
2025-01-08 17:26:02 -08:00
print ( ' \n ========== \n ' )
sub_start = pd . Timestamp . now ( )
tag_for_board_wipes ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed board wipe tagging in { ( pd . Timestamp . now ( ) - sub_start ) . total_seconds ( ) : .2f } s ' )
2025-01-08 17:26:02 -08:00
print ( ' \n ========== \n ' )
sub_start = pd . Timestamp . now ( )
tag_for_combat_tricks ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed combat trick tagging in { ( pd . Timestamp . now ( ) - sub_start ) . total_seconds ( ) : .2f } s ' )
2025-01-08 17:26:02 -08:00
print ( ' \n ========== \n ' )
sub_start = pd . Timestamp . now ( )
tag_for_protection ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed protection tagging in { ( pd . Timestamp . now ( ) - sub_start ) . total_seconds ( ) : .2f } s ' )
2025-01-08 17:26:02 -08:00
print ( ' \n ========== \n ' )
2024-12-18 12:14:45 -08:00
2025-01-08 17:26:02 -08:00
sub_start = pd . Timestamp . now ( )
tag_for_removal ( df , color )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed removal tagging in { ( pd . Timestamp . now ( ) - sub_start ) . total_seconds ( ) : .2f } s ' )
2025-01-08 17:26:02 -08:00
print ( ' \n ========== \n ' )
# Log completion and performance metrics
duration = pd . Timestamp . now ( ) - start_time
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed all interaction tagging in { duration . total_seconds ( ) : .2f } s ' )
2025-01-08 17:26:02 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_interaction: { str ( e ) } ' )
2025-01-08 17:26:02 -08:00
raise
## Counterspells
def create_counterspell_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with counterspell text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have counterspell text patterns
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , settings . COUNTERSPELL_TEXT_PATTERNS )
2025-01-08 17:26:02 -08:00
def create_counterspell_specific_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for specific counterspell cards.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards are specific counterspell cards
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_name_mask ( df , settings . COUNTERSPELL_SPECIFIC_CARDS )
2025-01-08 17:26:02 -08:00
def create_counterspell_exclusion_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that should be excluded from counterspell effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards should be excluded
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , settings . COUNTERSPELL_EXCLUSION_PATTERNS )
2025-01-08 17:26:02 -08:00
def tag_for_counterspells ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that counter spells using vectorized operations.
This function identifies and tags cards that :
- Counter spells directly
- Return spells to hand / library
- Exile spells from the stack
- Care about countering spells
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting counterspell effect tagging for { color } _cards.csv ' )
2025-01-08 17:26:02 -08:00
try :
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' name ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 17:26:02 -08:00
# Create masks for different counterspell patterns
text_mask = create_counterspell_text_mask ( df )
specific_mask = create_counterspell_specific_mask ( df )
exclusion_mask = create_counterspell_exclusion_mask ( df )
# Combine masks
final_mask = ( text_mask | specific_mask ) & ~ exclusion_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Counterspells ' , ' Interaction ' , ' Spellslinger ' , ' Spells Matter ' ] )
2025-01-08 17:26:02 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with counterspell effects in { duration : .2f } s ' )
2025-01-08 17:26:02 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_counterspells: { str ( e ) } ' )
2025-01-08 17:26:02 -08:00
raise
## Board Wipes
def tag_for_board_wipes ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that have board wipe effects using vectorized operations.
This function identifies and tags cards with board wipe effects including :
- Mass destruction effects ( destroy all / each )
- Mass exile effects ( exile all / each )
- Mass bounce effects ( return all / each )
- Mass sacrifice effects ( sacrifice all / each )
- Mass damage effects ( damage to all / each )
The function uses helper functions to identify different types of board wipes
and applies tags consistently using vectorized operations .
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting board wipe effect tagging for { color } _cards.csv ' )
2025-01-08 17:26:02 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' name ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 17:26:02 -08:00
# Create masks for different board wipe types
2025-01-14 10:10:30 -08:00
destroy_mask = tag_utils . create_mass_effect_mask ( df , ' mass_destruction ' )
exile_mask = tag_utils . create_mass_effect_mask ( df , ' mass_exile ' )
bounce_mask = tag_utils . create_mass_effect_mask ( df , ' mass_bounce ' )
sacrifice_mask = tag_utils . create_mass_effect_mask ( df , ' mass_sacrifice ' )
damage_mask = tag_utils . create_mass_damage_mask ( df )
2025-01-08 17:26:02 -08:00
# Create exclusion mask
2025-01-14 10:10:30 -08:00
exclusion_mask = tag_utils . create_text_mask ( df , settings . BOARD_WIPE_EXCLUSION_PATTERNS )
2025-01-08 17:26:02 -08:00
# Create specific cards mask
2025-01-14 10:10:30 -08:00
specific_mask = tag_utils . create_name_mask ( df , settings . BOARD_WIPE_SPECIFIC_CARDS )
2025-01-08 17:26:02 -08:00
# Combine all masks
final_mask = (
destroy_mask | exile_mask | bounce_mask |
sacrifice_mask | damage_mask | specific_mask
) & ~ exclusion_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Board Wipes ' , ' Interaction ' ] )
2025-01-08 17:26:02 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with board wipe effects in { duration : .2f } s ' )
2025-01-08 17:26:02 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_board_wipes: { str ( e ) } ' )
2025-01-08 17:26:02 -08:00
raise
2025-01-14 10:10:30 -08:00
logger . info ( f ' Completed board wipe tagging for { color } _cards.csv ' )
2024-12-18 20:21:45 -08:00
## Combat Tricks
2025-01-08 17:26:02 -08:00
def create_combat_tricks_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with combat trick text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have combat trick text patterns
"""
# Create patterns for power/toughness modifiers
number_patterns = [ str ( x ) for x in range ( 11 ) ] + [ ' X ' ]
buff_patterns = [ ]
for num in number_patterns :
# Positive buffs
buff_patterns . extend ( [
fr ' gets \ + { num } / \ + { num } ' ,
fr ' get \ + { num } / \ + { num } ' ,
fr ' gets \ + { num } / \ +0 ' ,
fr ' get \ + { num } / \ +0 ' ,
fr ' gets \ +0/ \ + { num } ' ,
fr ' get \ +0/ \ + { num } '
] )
2024-12-18 12:14:45 -08:00
2025-01-08 17:26:02 -08:00
# Negative buffs
buff_patterns . extend ( [
fr ' gets - { num } /- { num } ' ,
fr ' get - { num } /- { num } ' ,
fr ' gets - { num } / \ +0 ' ,
fr ' get - { num } / \ +0 ' ,
fr ' gets \ +0/- { num } ' ,
fr ' get \ +0/- { num } '
] )
# Other combat trick patterns
other_patterns = [
' bolster ' ,
' double strike ' ,
' first strike ' ,
' has base power and toughness ' ,
' untap all creatures ' ,
' untap target creature ' ,
' with base power and toughness '
]
# Combine all patterns
all_patterns = buff_patterns + other_patterns
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , all_patterns )
2025-01-08 17:26:02 -08:00
def create_combat_tricks_type_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for instant-speed combat tricks.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards are instant - speed combat tricks
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_type_mask ( df , ' Instant ' )
2025-01-08 17:26:02 -08:00
def create_combat_tricks_flash_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for flash-based combat tricks.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have flash - based combat tricks
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_keyword_mask ( df , ' Flash ' )
2025-01-08 17:26:02 -08:00
def create_combat_tricks_exclusion_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that should be excluded from combat tricks.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards should be excluded
"""
# Specific cards to exclude
excluded_cards = [
' Assimilate Essence ' ,
' Mantle of Leadership ' ,
' Michiko \' s Reign of Truth // Portrait of Michiko '
]
2025-01-14 10:10:30 -08:00
name_mask = tag_utils . create_name_mask ( df , excluded_cards )
2025-01-08 17:26:02 -08:00
# Text patterns to exclude
text_patterns = [
' remains tapped ' ,
' only as a sorcery '
]
2025-01-14 10:10:30 -08:00
text_mask = tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 17:26:02 -08:00
return name_mask | text_mask
def tag_for_combat_tricks ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that function as combat tricks using vectorized operations.
This function identifies and tags cards that modify combat through :
- Power / toughness buffs at instant speed
- Flash creatures and enchantments with combat effects
- Tap abilities that modify power / toughness
- Combat - relevant keywords and abilities
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting combat trick tagging for { color } _cards.csv ' )
2025-01-08 17:26:02 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' type ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 17:26:02 -08:00
# Create masks for different combat trick patterns
text_mask = create_combat_tricks_text_mask ( df )
type_mask = create_combat_tricks_type_mask ( df )
flash_mask = create_combat_tricks_flash_mask ( df )
exclusion_mask = create_combat_tricks_exclusion_mask ( df )
# Combine masks
final_mask = ( ( text_mask & ( type_mask | flash_mask ) ) |
2025-01-14 10:10:30 -08:00
( flash_mask & tag_utils . create_type_mask ( df , ' Enchantment ' ) ) ) & ~ exclusion_mask
2025-01-08 17:26:02 -08:00
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Combat Tricks ' , ' Interaction ' ] )
2025-01-08 17:26:02 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with combat trick effects in { duration : .2f } s ' )
2025-01-08 17:26:02 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_combat_tricks: { str ( e ) } ' )
2025-01-08 17:26:02 -08:00
raise
2024-12-18 20:21:45 -08:00
## Protection/Safety spells
2025-01-08 17:26:02 -08:00
def create_protection_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with protection-related text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have protection text patterns
"""
text_patterns = [
' has indestructible ' ,
' has protection ' ,
' has shroud ' ,
' has ward ' ,
' have indestructible ' ,
' have protection ' ,
' have shroud ' ,
' have ward ' ,
' hexproof from ' ,
' gain hexproof ' ,
' gain indestructible ' ,
' gain protection ' ,
' gain shroud ' ,
' gain ward ' ,
' gains hexproof ' ,
' gains indestructible ' ,
' gains protection ' ,
' gains shroud ' ,
' gains ward ' ,
' phases out ' ,
' protection from '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , text_patterns )
2025-01-08 17:26:02 -08:00
def create_protection_keyword_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with protection-related keywords.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have protection keywords
"""
keyword_patterns = [
' Hexproof ' ,
' Indestructible ' ,
' Protection ' ,
' Shroud ' ,
' Ward '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_keyword_mask ( df , keyword_patterns )
2025-01-08 17:26:02 -08:00
def create_protection_exclusion_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that should be excluded from protection effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards should be excluded
"""
excluded_cards = [
' Out of Time ' ,
' The War Doctor '
]
2025-01-14 10:10:30 -08:00
return tag_utils . create_name_mask ( df , excluded_cards )
2025-01-08 17:26:02 -08:00
def tag_for_protection ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that provide or have protection effects using vectorized operations.
This function identifies and tags cards with protection effects including :
- Indestructible
- Protection from [ quality ]
- Hexproof / Shroud
- Ward
- Phase out
The function uses helper functions to identify different types of protection
and applies tags consistently using vectorized operations .
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting protection effect tagging for { color } _cards.csv ' )
2025-01-08 17:26:02 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 17:26:02 -08:00
# Create masks for different protection patterns
text_mask = create_protection_text_mask ( df )
keyword_mask = create_protection_keyword_mask ( df )
exclusion_mask = create_protection_exclusion_mask ( df )
# Combine masks
final_mask = ( text_mask | keyword_mask ) & ~ exclusion_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Protection ' , ' Interaction ' ] )
2025-01-08 17:26:02 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with protection effects in { duration : .2f } s ' )
2025-01-08 17:26:02 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_protection: { str ( e ) } ' )
2025-01-08 17:26:02 -08:00
raise
2024-12-18 12:14:45 -08:00
## Spot removal
2025-01-08 17:26:02 -08:00
def create_removal_text_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards with removal text patterns.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards have removal text patterns
"""
2025-01-14 10:10:30 -08:00
return tag_utils . create_text_mask ( df , settings . REMOVAL_TEXT_PATTERNS )
2025-01-08 17:26:02 -08:00
2025-01-17 11:39:27 -08:00
def create_removal_exclusion_mask ( df : pd . DataFrame ) - > pd . Series :
""" Create a boolean mask for cards that should be excluded from removal effects.
Args :
df : DataFrame to search
Returns :
Boolean Series indicating which cards should be excluded
"""
return tag_utils . create_text_mask ( df , settings . REMOVAL_EXCLUSION_PATTERNS )
2025-01-08 17:26:02 -08:00
def tag_for_removal ( df : pd . DataFrame , color : str ) - > None :
""" Tag cards that provide spot removal using vectorized operations.
This function identifies and tags cards that remove permanents through :
- Destroy effects
- Exile effects
- Bounce effects
- Sacrifice effects
2024-12-18 20:21:45 -08:00
2025-01-08 17:26:02 -08:00
The function uses helper functions to identify different types of removal
and applies tags consistently using vectorized operations .
Args :
df : DataFrame containing card data
color : Color identifier for logging purposes
Raises :
ValueError : If required DataFrame columns are missing
TypeError : If inputs are not of correct type
"""
start_time = pd . Timestamp . now ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Starting removal effect tagging for { color } _cards.csv ' )
2025-01-08 17:26:02 -08:00
try :
# Validate inputs
if not isinstance ( df , pd . DataFrame ) :
raise TypeError ( " df must be a pandas DataFrame " )
if not isinstance ( color , str ) :
raise TypeError ( " color must be a string " )
# Validate required columns
required_cols = { ' text ' , ' themeTags ' , ' keywords ' }
2025-01-14 10:10:30 -08:00
tag_utils . validate_dataframe_columns ( df , required_cols )
2025-01-08 17:26:02 -08:00
# Create masks for different removal patterns
text_mask = create_removal_text_mask ( df )
# Combine masks
final_mask = text_mask
# Apply tags
2025-01-14 10:10:30 -08:00
tag_utils . apply_tag_vectorized ( df , final_mask , [ ' Removal ' , ' Interaction ' ] )
2025-01-08 17:26:02 -08:00
# Log results
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-14 10:10:30 -08:00
logger . info ( f ' Tagged { final_mask . sum ( ) } cards with removal effects in { duration : .2f } s ' )
2025-01-08 17:26:02 -08:00
except Exception as e :
2025-01-14 10:10:30 -08:00
logger . error ( f ' Error in tag_for_removal: { str ( e ) } ' )
2025-01-08 17:26:02 -08:00
raise
2025-01-14 10:10:30 -08:00
def run_tagging ( ) :
start_time = pd . Timestamp . now ( )
2025-01-17 11:39:27 -08:00
for color in settings . COLORS :
2025-01-14 10:10:30 -08:00
load_dataframe ( color )
duration = ( pd . Timestamp . now ( ) - start_time ) . total_seconds ( )
2025-01-17 11:39:27 -08:00
logger . info ( f ' Tagged cards in { duration : .2f } s ' )