mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2025-12-18 08:30:13 +01:00
All themes refactored to use vector masks instead of iterrows
This commit is contained in:
parent
17379340a8
commit
6de2239a47
2 changed files with 205 additions and 64 deletions
42
settings.py
42
settings.py
|
|
@ -107,9 +107,49 @@ LANDS_MATTER_SPECIFIC_CARDS = [
|
||||||
'Wonderscape Sage'
|
'Wonderscape Sage'
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Constants for topdeck manipulation
|
||||||
|
TOPDECK_TEXT_PATTERNS = [
|
||||||
|
'from the top',
|
||||||
|
'look at the top',
|
||||||
|
'reveal the top',
|
||||||
|
'scries',
|
||||||
|
'surveils',
|
||||||
|
'top of your library',
|
||||||
|
'you scry',
|
||||||
|
'you surveil'
|
||||||
|
]
|
||||||
|
|
||||||
|
TOPDECK_KEYWORDS = [
|
||||||
|
'Miracle',
|
||||||
|
'Scry',
|
||||||
|
'Surveil'
|
||||||
|
]
|
||||||
|
|
||||||
|
TOPDECK_SPECIFIC_CARDS = [
|
||||||
|
'Aminatou, the Fateshifter',
|
||||||
|
'Brainstorm',
|
||||||
|
'Counterbalance',
|
||||||
|
'Delver of Secrets',
|
||||||
|
'Jace, the Mind Sculptor',
|
||||||
|
'Lantern of Insight',
|
||||||
|
'Melek, Izzet Paragon',
|
||||||
|
'Mystic Forge',
|
||||||
|
'Sensei\'s Divining Top',
|
||||||
|
'Soothsaying',
|
||||||
|
'Temporal Mastery',
|
||||||
|
'Vampiric Tutor'
|
||||||
|
]
|
||||||
|
|
||||||
|
TOPDECK_EXCLUSION_PATTERNS = [
|
||||||
|
'from the top of target player\'s library',
|
||||||
|
'from the top of their library',
|
||||||
|
'look at the top card of target player\'s library',
|
||||||
|
'reveal the top card of target player\'s library'
|
||||||
|
]
|
||||||
|
|
||||||
# Constants for stax functionality
|
# Constants for stax functionality
|
||||||
STAX_TEXT_PATTERNS = [
|
STAX_TEXT_PATTERNS = [
|
||||||
'an opponent controls',
|
'an opponent controls'
|
||||||
'can\'t attack',
|
'can\'t attack',
|
||||||
'can\'t be cast',
|
'can\'t be cast',
|
||||||
'can\'t be activated',
|
'can\'t be activated',
|
||||||
|
|
|
||||||
225
tagger.py
225
tagger.py
|
|
@ -5641,72 +5641,173 @@ def tag_for_toughness(df: pd.DataFrame, color: str) -> None:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f'Error in tag_for_toughness: {str(e)}')
|
logging.error(f'Error in tag_for_toughness: {str(e)}')
|
||||||
raise
|
raise
|
||||||
## Topdeck
|
|
||||||
def tag_for_topdeck(df, color):
|
|
||||||
print(f'Tagging cards in {color}_cards.csv that fit the "Topdeck" theme.')
|
|
||||||
for index, row in df.iterrows():
|
|
||||||
theme_tags = row['themeTags']
|
|
||||||
if pd.notna(row['text']):
|
|
||||||
if ('from the top' in row['text'].lower()
|
|
||||||
or 'look at the top' in row['text'].lower()
|
|
||||||
or 'reveal the top' in row['text'].lower()
|
|
||||||
or 'scries' in row['text'].lower()
|
|
||||||
or 'surveils' in row['text'].lower()
|
|
||||||
or 'top of your library' in row['text'].lower()
|
|
||||||
or 'you scry' in row['text'].lower()
|
|
||||||
or 'you surveil' in row['text'].lower()
|
|
||||||
):
|
|
||||||
tag_type = ['Topdeck']
|
|
||||||
for tag in tag_type:
|
|
||||||
if tag not in theme_tags:
|
|
||||||
theme_tags.extend([tag])
|
|
||||||
df.at[index, 'themeTags'] = theme_tags
|
|
||||||
if pd.notna(row['keywords']):
|
|
||||||
if ('Miracle' in row['keywords']
|
|
||||||
or 'Scry' in row['keywords']
|
|
||||||
or 'Surveil' in row['keywords']
|
|
||||||
):
|
|
||||||
tag_type = ['Topdeck']
|
|
||||||
for tag in tag_type:
|
|
||||||
if tag not in theme_tags:
|
|
||||||
theme_tags.extend([tag])
|
|
||||||
df.at[index, 'themeTags'] = theme_tags
|
|
||||||
|
|
||||||
print(f'"Topdeck" cards in {color}_cards.csv have been tagged.\n')
|
## Topdeck
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
return utility.create_text_mask(df, settings.TOPDECK_TEXT_PATTERNS)
|
||||||
|
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
return utility.create_keyword_mask(df, settings.TOPDECK_KEYWORDS)
|
||||||
|
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
return utility.create_name_mask(df, settings.TOPDECK_SPECIFIC_CARDS)
|
||||||
|
|
||||||
|
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
|
||||||
|
"""
|
||||||
|
return utility.create_text_mask(df, settings.TOPDECK_EXCLUSION_PATTERNS)
|
||||||
|
|
||||||
|
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()
|
||||||
|
logging.info(f'Starting topdeck effect tagging for {color}_cards.csv')
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Validate required columns
|
||||||
|
required_cols = {'text', 'themeTags', 'keywords'}
|
||||||
|
utility.validate_dataframe_columns(df, required_cols)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
utility.apply_tag_vectorized(df, final_mask, ['Topdeck'])
|
||||||
|
|
||||||
|
# Log results
|
||||||
|
duration = (pd.Timestamp.now() - start_time).total_seconds()
|
||||||
|
logging.info(f'Tagged {final_mask.sum()} cards with topdeck effects in {duration:.2f}s')
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f'Error in tag_for_topdeck: {str(e)}')
|
||||||
|
raise
|
||||||
|
|
||||||
## X Spells
|
## X Spells
|
||||||
def tag_for_x_spells(df, color):
|
def create_x_spells_text_mask(df: pd.DataFrame) -> pd.Series:
|
||||||
print(f'Tagging cards in {color}_cards.csv that fit the "X Spells" theme.')
|
"""Create a boolean mask for cards with X spell-related text patterns.
|
||||||
df['manaCost'] = df['manaCost'].astype(str)
|
|
||||||
for index, row in df.iterrows():
|
|
||||||
theme_tags = row['themeTags']
|
|
||||||
if pd.notna(row['text']):
|
|
||||||
if ('cost {x} less' in row['text'].lower()
|
|
||||||
or 'don\'t lose this' in row['text'].lower()
|
|
||||||
or 'don\'t lose unspent' in row['text'].lower()
|
|
||||||
or 'lose unused mana' in row['text'].lower()
|
|
||||||
or 'unused mana would empty' in row['text'].lower()
|
|
||||||
or 'with {x} in its' in row['text'].lower()
|
|
||||||
or 'you cast cost {1} less' in row['text'].lower()
|
|
||||||
or 'you cast cost {2} less' in row['text'].lower()
|
|
||||||
or 'you cast cost {3} less' in row['text'].lower()
|
|
||||||
or 'you cast cost {4} less' in row['text'].lower()
|
|
||||||
or 'you cast cost {5} less' in row['text'].lower()
|
|
||||||
):
|
|
||||||
tag_type = ['X Spells']
|
|
||||||
for tag in tag_type:
|
|
||||||
if tag not in theme_tags:
|
|
||||||
theme_tags.extend([tag])
|
|
||||||
df.at[index, 'themeTags'] = theme_tags
|
|
||||||
if ('{X}' in row['manaCost']
|
|
||||||
):
|
|
||||||
tag_type = ['X Spells']
|
|
||||||
for tag in tag_type:
|
|
||||||
if tag not in theme_tags:
|
|
||||||
theme_tags.extend([tag])
|
|
||||||
df.at[index, 'themeTags'] = theme_tags
|
|
||||||
|
|
||||||
print(f'"X Spells" cards in {color}_cards.csv have been tagged.\n')
|
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'
|
||||||
|
]
|
||||||
|
return utility.create_text_mask(df, text_patterns)
|
||||||
|
|
||||||
|
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()
|
||||||
|
logging.info(f'Starting X spells tagging for {color}_cards.csv')
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Validate required columns
|
||||||
|
required_cols = {'text', 'themeTags', 'manaCost'}
|
||||||
|
utility.validate_dataframe_columns(df, required_cols)
|
||||||
|
|
||||||
|
# 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
|
||||||
|
utility.apply_tag_vectorized(df, final_mask, ['X Spells'])
|
||||||
|
|
||||||
|
# Log results
|
||||||
|
duration = (pd.Timestamp.now() - start_time).total_seconds()
|
||||||
|
logging.info(f'Tagged {final_mask.sum()} cards with X spell effects in {duration:.2f}s')
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f'Error in tag_for_x_spells: {str(e)}')
|
||||||
|
raise
|
||||||
|
|
||||||
### Interaction
|
### Interaction
|
||||||
## Overall tag for interaction group
|
## Overall tag for interaction group
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue