mirror of
https://github.com/mwisnowski/mtg_python_deckbuilder.git
synced 2025-12-17 08:00:13 +01:00
Adjusted a number of print statements to be logging statements
This commit is contained in:
parent
6c0aaf26a0
commit
a5f6e4f09e
1 changed files with 62 additions and 77 deletions
119
deck_builder.py
119
deck_builder.py
|
|
@ -1040,8 +1040,8 @@ class DeckBuilder:
|
||||||
logging.info('Checking total land count to ensure it\'s within ideal count.\n\n')
|
logging.info('Checking total land count to ensure it\'s within ideal count.\n\n')
|
||||||
self.organize_library()
|
self.organize_library()
|
||||||
while self.land_cards > int(self.ideal_land_count):
|
while self.land_cards > int(self.ideal_land_count):
|
||||||
logging.info(f'Num land cards: {self.land_cards}\n'
|
logging.info(f'Total lands: {self.land_cards}')
|
||||||
f'Ideal num land cards {self.ideal_land_count}')
|
logging.info(f'Ideal lands: {self.ideal_land_count}')
|
||||||
self.remove_basic()
|
self.remove_basic()
|
||||||
self.organize_library()
|
self.organize_library()
|
||||||
|
|
||||||
|
|
@ -1098,7 +1098,7 @@ class DeckBuilder:
|
||||||
|
|
||||||
def add_standard_non_basics(self):
|
def add_standard_non_basics(self):
|
||||||
# Add lands that are good in most any commander deck
|
# Add lands that are good in most any commander deck
|
||||||
logging.info('Adding "standard" non-basics')
|
print('Adding "standard" non-basics')
|
||||||
self.staples = ['Reliquary Tower']
|
self.staples = ['Reliquary Tower']
|
||||||
if 'Landfall' not in self.commander_tags:
|
if 'Landfall' not in self.commander_tags:
|
||||||
self.staples.append('Ash Barrens')
|
self.staples.append('Ash Barrens')
|
||||||
|
|
@ -1210,7 +1210,7 @@ class DeckBuilder:
|
||||||
self.land_df.to_csv(f'{csv_directory}/test_lands.csv', index=False)
|
self.land_df.to_csv(f'{csv_directory}/test_lands.csv', index=False)
|
||||||
|
|
||||||
def add_kindred_lands(self):
|
def add_kindred_lands(self):
|
||||||
logging.info('Adding lands that care about the commander having a Kindred theme.')
|
print('Adding lands that care about the commander having a Kindred theme.')
|
||||||
logging.info('Adding general Kindred lands.')
|
logging.info('Adding general Kindred lands.')
|
||||||
|
|
||||||
def create_land(name: str, land_type: str) -> dict:
|
def create_land(name: str, land_type: str) -> dict:
|
||||||
|
|
@ -1359,7 +1359,7 @@ class DeckBuilder:
|
||||||
logging.info('Skipping adding Triome land cards.')
|
logging.info('Skipping adding Triome land cards.')
|
||||||
|
|
||||||
def add_misc_lands(self):
|
def add_misc_lands(self):
|
||||||
logging.info('Adding additional misc. lands to the deck that fit the color identity.')
|
print('Adding additional misc. lands to the deck that fit the color identity.')
|
||||||
# Add other remaining lands that match color identity
|
# Add other remaining lands that match color identity
|
||||||
|
|
||||||
logging.info('Grabbing lands in your commander\'s color identity that aren\'t already in the deck.')
|
logging.info('Grabbing lands in your commander\'s color identity that aren\'t already in the deck.')
|
||||||
|
|
@ -1433,77 +1433,62 @@ class DeckBuilder:
|
||||||
logging.info(f"{land}: {count}")
|
logging.info(f"{land}: {count}")
|
||||||
logging.info(f"Total basic lands: {self.total_basics}\n")
|
logging.info(f"Total basic lands: {self.total_basics}\n")
|
||||||
|
|
||||||
def remove_basic(self):
|
def remove_basic(self, max_attempts: int = 3):
|
||||||
"""
|
"""
|
||||||
Remove a basic land while maintaining color balance.
|
Remove a basic land while maintaining color balance.
|
||||||
Attempts to remove from colors with more basics first.
|
Attempts to remove from colors with more basics first.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
max_attempts: Maximum number of removal attempts before falling back to non-basics
|
||||||
"""
|
"""
|
||||||
logging.info('Land count over ideal count, removing a basic land.')
|
logging.info('Land count over ideal count, removing a basic land.')
|
||||||
|
|
||||||
# Map colors to basic land names
|
|
||||||
color_to_basic = {
|
color_to_basic = {
|
||||||
'W': 'Plains',
|
'W': 'Plains', 'U': 'Island', 'B': 'Swamp',
|
||||||
'U': 'Island',
|
'R': 'Mountain', 'G': 'Forest'
|
||||||
'B': 'Swamp',
|
|
||||||
'R': 'Mountain',
|
|
||||||
'G': 'Forest'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Count basics of each type
|
# Get current basic land counts using vectorized operations
|
||||||
basic_counts = {}
|
basic_counts = {
|
||||||
for color in self.colors:
|
basic: len(self.card_library[self.card_library['Card Name'] == basic])
|
||||||
basic = color_to_basic.get(color)
|
for color, basic in color_to_basic.items()
|
||||||
if basic:
|
if color in self.colors
|
||||||
count = len(self.card_library[self.card_library['Card Name'] == basic])
|
}
|
||||||
if count > 0:
|
|
||||||
basic_counts[basic] = count
|
|
||||||
|
|
||||||
|
sum_basics = sum(basic_counts.values())
|
||||||
|
attempts = 0
|
||||||
|
|
||||||
|
while attempts < max_attempts and sum_basics > self.min_basics:
|
||||||
if not basic_counts:
|
if not basic_counts:
|
||||||
logging.warning("No basic lands found to remove")
|
logging.warning("No basic lands found to remove")
|
||||||
return
|
break
|
||||||
sum_basics = sum(basic_counts.values())
|
|
||||||
|
|
||||||
# Try to remove from color with most basics
|
|
||||||
basic_land = max(basic_counts.items(), key=lambda x: x[1])[0]
|
basic_land = max(basic_counts.items(), key=lambda x: x[1])[0]
|
||||||
if sum_basics > self.min_basics:
|
|
||||||
try:
|
try:
|
||||||
logging.info(f'Attempting to remove {basic_land}')
|
# Use boolean indexing for efficiency
|
||||||
condition = self.card_library['Card Name'] == basic_land
|
mask = self.card_library['Card Name'] == basic_land
|
||||||
index_to_drop = self.card_library[condition].index[0]
|
if not mask.any():
|
||||||
|
basic_counts.pop(basic_land)
|
||||||
self.card_library = self.card_library.drop(index_to_drop)
|
continue
|
||||||
self.card_library = self.card_library.reset_index(drop=True)
|
|
||||||
|
|
||||||
|
index_to_drop = self.card_library[mask].index[0]
|
||||||
|
self.card_library = self.card_library.drop(index_to_drop).reset_index(drop=True)
|
||||||
logging.info(f'{basic_land} removed successfully')
|
logging.info(f'{basic_land} removed successfully')
|
||||||
self.check_basics()
|
return
|
||||||
|
|
||||||
except (IndexError, KeyError) as e:
|
except (IndexError, KeyError) as e:
|
||||||
logging.error(f"Error removing {basic_land}: {e}")
|
logging.error(f"Error removing {basic_land}: {e}")
|
||||||
# Iterative approach instead of recursion
|
basic_counts.pop(basic_land)
|
||||||
while basic_counts:
|
|
||||||
basic_counts.pop(basic_land, None)
|
|
||||||
if not basic_counts:
|
|
||||||
logging.error("Failed to remove any basic land")
|
|
||||||
break
|
|
||||||
|
|
||||||
basic_land = max(basic_counts.items(), key=lambda x: x[1])[0]
|
attempts += 1
|
||||||
try:
|
|
||||||
condition = self.card_library['Card Name'] == basic_land
|
# If we couldn't remove a basic land, try removing a non-basic
|
||||||
index_to_drop = self.card_library[condition].index[0]
|
logging.warning("Could not remove basic land, attempting to remove non-basic")
|
||||||
self.card_library = self.card_library.drop(index_to_drop)
|
|
||||||
self.card_library = self.card_library.reset_index(drop=True)
|
|
||||||
logging.info(f'{basic_land} removed successfully')
|
|
||||||
self.check_basics()
|
|
||||||
break
|
|
||||||
except (IndexError, KeyError):
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
print(f'Not enough basic lands to keep the minimum of {self.min_basics}.')
|
|
||||||
self.remove_land()
|
self.remove_land()
|
||||||
|
|
||||||
def remove_land(self):
|
def remove_land(self):
|
||||||
"""Remove a random non-basic, non-staple land from the deck."""
|
"""Remove a random non-basic, non-staple land from the deck."""
|
||||||
print('Removing a random nonbasic land.')
|
logging.info('Removing a random nonbasic land.')
|
||||||
|
|
||||||
# Define basic lands including snow-covered variants
|
# Define basic lands including snow-covered variants
|
||||||
basic_lands = [
|
basic_lands = [
|
||||||
|
|
@ -1520,25 +1505,25 @@ class DeckBuilder:
|
||||||
].copy()
|
].copy()
|
||||||
|
|
||||||
if len(library_filter) == 0:
|
if len(library_filter) == 0:
|
||||||
print("No suitable non-basic lands found to remove.")
|
logging.warning("No suitable non-basic lands found to remove.")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Select random land to remove
|
# Select random land to remove
|
||||||
card_index = np.random.choice(library_filter.index)
|
card_index = np.random.choice(library_filter.index)
|
||||||
card_name = self.card_library.loc[card_index, 'Card Name']
|
card_name = self.card_library.loc[card_index, 'Card Name']
|
||||||
|
|
||||||
print(f"Removing {card_name}")
|
logging.info(f"Removing {card_name}")
|
||||||
self.card_library.drop(card_index, inplace=True)
|
self.card_library.drop(card_index, inplace=True)
|
||||||
self.card_library.reset_index(drop=True, inplace=True)
|
self.card_library.reset_index(drop=True, inplace=True)
|
||||||
print("Card removed successfully.")
|
logging.info("Card removed successfully.")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(f"Error removing land: {e}")
|
logging.error(f"Error removing land: {e}")
|
||||||
print("Failed to remove land card.")
|
logging.warning("Failed to remove land card.")
|
||||||
|
|
||||||
def count_pips(self):
|
def count_pips(self):
|
||||||
"""Count and display the number of colored mana symbols in casting costs."""
|
"""Count and display the number of colored mana symbols in casting costs."""
|
||||||
print('Analyzing color pip distribution...')
|
logging.info('Analyzing color pip distribution...')
|
||||||
|
|
||||||
pip_counts = {
|
pip_counts = {
|
||||||
'W': 0, 'U': 0, 'B': 0, 'R': 0, 'G': 0
|
'W': 0, 'U': 0, 'B': 0, 'R': 0, 'G': 0
|
||||||
|
|
@ -1550,15 +1535,15 @@ class DeckBuilder:
|
||||||
|
|
||||||
total_pips = sum(pip_counts.values())
|
total_pips = sum(pip_counts.values())
|
||||||
if total_pips == 0:
|
if total_pips == 0:
|
||||||
print("No colored mana symbols found in casting costs.")
|
logging.error("No colored mana symbols found in casting costs.")
|
||||||
return
|
return
|
||||||
|
|
||||||
print("\nColor Pip Distribution:")
|
logging.info("\nColor Pip Distribution:")
|
||||||
for color, count in pip_counts.items():
|
for color, count in pip_counts.items():
|
||||||
if count > 0:
|
if count > 0:
|
||||||
percentage = (count / total_pips) * 100
|
percentage = (count / total_pips) * 100
|
||||||
print(f"{color}: {count} pips ({percentage:.1f}%)")
|
print(f"{color}: {count} pips ({percentage:.1f}%)")
|
||||||
print(f"Total colored pips: {total_pips}\n")
|
logging.info(f"Total colored pips: {total_pips}\n")
|
||||||
|
|
||||||
def get_cmc(self):
|
def get_cmc(self):
|
||||||
"""Calculate average converted mana cost of non-land cards."""
|
"""Calculate average converted mana cost of non-land cards."""
|
||||||
|
|
@ -1588,7 +1573,7 @@ class DeckBuilder:
|
||||||
# First grab the first 50/30/20 cards that match each theme
|
# First grab the first 50/30/20 cards that match each theme
|
||||||
"""Add cards with specific tag up to ideal_value count"""
|
"""Add cards with specific tag up to ideal_value count"""
|
||||||
ideal_value = math.ceil(ideal * weight * 0.9)
|
ideal_value = math.ceil(ideal * weight * 0.9)
|
||||||
logging.info(f'Finding {ideal_value} cards with the "{tag}" tag...')
|
print(f'Finding {ideal_value} cards with the "{tag}" tag...')
|
||||||
if 'Kindred' in tag:
|
if 'Kindred' in tag:
|
||||||
tags = [tag, 'Kindred Support']
|
tags = [tag, 'Kindred Support']
|
||||||
else:
|
else:
|
||||||
|
|
@ -1644,7 +1629,7 @@ class DeckBuilder:
|
||||||
|
|
||||||
elif (card['name'] not in multiple_copy_cards
|
elif (card['name'] not in multiple_copy_cards
|
||||||
and card['name'] in self.card_library['Card Name'].values):
|
and card['name'] in self.card_library['Card Name'].values):
|
||||||
print(f"{card['name']} already in Library, skipping it.")
|
logging.warning(f"{card['name']} already in Library, skipping it.")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# Add selected cards to library
|
# Add selected cards to library
|
||||||
|
|
@ -1660,7 +1645,7 @@ class DeckBuilder:
|
||||||
|
|
||||||
def add_by_tags(self, tag, ideal_value=1, df=None):
|
def add_by_tags(self, tag, ideal_value=1, df=None):
|
||||||
"""Add cards with specific tag up to ideal_value count"""
|
"""Add cards with specific tag up to ideal_value count"""
|
||||||
logging.info(f'Finding {ideal_value} cards with the "{tag}" tag...')
|
print(f'Finding {ideal_value} cards with the "{tag}" tag...')
|
||||||
|
|
||||||
# Filter cards with the given tag
|
# Filter cards with the given tag
|
||||||
skip_creatures = self.creature_cards > self.ideal_creature_count * 1.1
|
skip_creatures = self.creature_cards > self.ideal_creature_count * 1.1
|
||||||
|
|
@ -1730,22 +1715,22 @@ class DeckBuilder:
|
||||||
with error handling to ensure the deck building process continues even if
|
with error handling to ensure the deck building process continues even if
|
||||||
a particular theme encounters issues.
|
a particular theme encounters issues.
|
||||||
"""
|
"""
|
||||||
logging.info(f'Adding creatures to deck based on the ideal creature count of {self.ideal_creature_count}...')
|
print(f'Adding creatures to deck based on the ideal creature count of {self.ideal_creature_count}...')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.hidden_theme:
|
if self.hidden_theme:
|
||||||
logging.info(f'Processing Hidden theme: {self.hidden_theme}')
|
print(f'Processing Hidden theme: {self.hidden_theme}')
|
||||||
self.weight_by_theme(self.hidden_theme, self.ideal_creature_count, self.hidden_weight, self.creature_df)
|
self.weight_by_theme(self.hidden_theme, self.ideal_creature_count, self.hidden_weight, self.creature_df)
|
||||||
|
|
||||||
logging.info(f'Processing primary theme: {self.primary_theme}')
|
print(f'Processing primary theme: {self.primary_theme}')
|
||||||
self.weight_by_theme(self.primary_theme, self.ideal_creature_count, self.primary_weight, self.creature_df)
|
self.weight_by_theme(self.primary_theme, self.ideal_creature_count, self.primary_weight, self.creature_df)
|
||||||
|
|
||||||
if self.secondary_theme:
|
if self.secondary_theme:
|
||||||
logging.info(f'Processing secondary theme: {self.secondary_theme}')
|
print(f'Processing secondary theme: {self.secondary_theme}')
|
||||||
self.weight_by_theme(self.secondary_theme, self.ideal_creature_count, self.secondary_weight, self.creature_df)
|
self.weight_by_theme(self.secondary_theme, self.ideal_creature_count, self.secondary_weight, self.creature_df)
|
||||||
|
|
||||||
if self.tertiary_theme:
|
if self.tertiary_theme:
|
||||||
logging.info(f'Processing tertiary theme: {self.tertiary_theme}')
|
print(f'Processing tertiary theme: {self.tertiary_theme}')
|
||||||
self.weight_by_theme(self.tertiary_theme, self.ideal_creature_count, self.tertiary_weight, self.creature_df)
|
self.weight_by_theme(self.tertiary_theme, self.ideal_creature_count, self.tertiary_weight, self.creature_df)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue