chore(release): v2.2.3 - fixed bug causing basic lands to not be added; updated removal tagging logic causing non-removal cards to be tagged due to wording

This commit is contained in:
mwisnowski 2025-09-01 20:20:04 -07:00
parent a0a12baa9b
commit fe9aabbce9
9 changed files with 48 additions and 16 deletions

View file

@ -12,6 +12,13 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning
## [Unreleased] ## [Unreleased]
## [2.2.3] - 2025-09-01
### Fixes
- Bug causing basic lands to no longer be added due to combined dataframe not including basics
### Changed
- Logic for removal tagging causing self-targetting cards (e.g. Conjurer's Closet) to be tagged as removal
## [2.2.2] - 2025-09-01 ## [2.2.2] - 2025-09-01
### Fixed ### Fixed
- Ensure default config files are available when running with bind-mounted config directories: - Ensure default config files are available when running with bind-mounted config directories:

View file

@ -33,6 +33,8 @@
- Existing completed pairs are counted toward the target; only missing partners are added. - Existing completed pairs are counted toward the target; only missing partners are added.
- No changes to CLI inputs for this feature in this release. - No changes to CLI inputs for this feature in this release.
- Headless: `tag_mode` supported from JSON/env and exported in interactive run-config JSON. - Headless: `tag_mode` supported from JSON/env and exported in interactive run-config JSON.
- Logic for removal tagging causing self-targetting cards (e.g. Conjurer's Closet) to be tagged as removal (2.2.3)
## Fixes ## Fixes
- Fixed an issue with the Docker Hub image not having the config files for combos/synergies/default deck json example - Fixed an issue with the Docker Hub image not having the config files for combos/synergies/default deck json example
- Bug causing basic lands to no longer be added due to combined dataframe not including basics (2.2.3)

View file

@ -1030,6 +1030,16 @@ class DeckBuilder(
# Allow the commander to bypass this check. # Allow the commander to bypass this check.
try: try:
if not is_commander: if not is_commander:
# Permit basic lands even if they aren't present in the current CSV pool.
# Some distributions may omit basics from the per-color card CSVs, but they are
# always legal within color identity. We therefore bypass pool filtering for
# basic/snow basic lands and Wastes.
try:
basic_names = bu.basic_land_names()
except Exception:
basic_names = set()
if str(card_name) not in basic_names:
df_src = self._full_cards_df if self._full_cards_df is not None else self._combined_cards_df df_src = self._full_cards_df if self._full_cards_df is not None else self._combined_cards_df
if df_src is not None and not df_src.empty and 'name' in df_src.columns: if df_src is not None and not df_src.empty and 'name' in df_src.columns:
if df_src[df_src['name'].astype(str).str.lower() == str(card_name).lower()].empty: if df_src[df_src['name'].astype(str).str.lower() == str(card_name).lower()].empty:

View file

@ -496,7 +496,18 @@ REMOVAL_TEXT_PATTERNS: List[str] = [
REMOVAL_SPECIFIC_CARDS: List[str] = ['from.*graveyard.*hand'] REMOVAL_SPECIFIC_CARDS: List[str] = ['from.*graveyard.*hand']
REMOVAL_EXCLUSION_PATTERNS: List[str] = [] REMOVAL_EXCLUSION_PATTERNS: List[str] = [
# Ignore self-targeting effects so they aren't tagged as spot removal
# Exile self
r'exile target.*you control',
r'exiles target.*you control',
# Destroy self
r'destroy target.*you control',
r'destroys target.*you control',
# Bounce self to hand
r'return target.*you control.*to.*hand',
r'returns target.*you control.*to.*hand',
]
REMOVAL_KEYWORDS: List[str] = [] REMOVAL_KEYWORDS: List[str] = []

View file

@ -6775,9 +6775,10 @@ def tag_for_removal(df: pd.DataFrame, color: str) -> None:
# Create masks for different removal patterns # Create masks for different removal patterns
text_mask = create_removal_text_mask(df) text_mask = create_removal_text_mask(df)
exclude_mask = create_removal_exclusion_mask(df)
# Combine masks # Combine masks (and exclude self-targeting effects like 'target permanent you control')
final_mask = text_mask final_mask = text_mask & (~exclude_mask)
# Apply tags via rules engine # Apply tags via rules engine
tag_utils.apply_rules(df, rules=[ tag_utils.apply_rules(df, rules=[

View file

@ -0,0 +1 @@
combos.json

View file

@ -47,7 +47,7 @@ services:
# Enable virtualization + lazy image tweaks in Step 5 # Enable virtualization + lazy image tweaks in Step 5
WEB_VIRTUALIZE: "1" WEB_VIRTUALIZE: "1"
# Version label (optional; shown in footer/diagnostics) # Version label (optional; shown in footer/diagnostics)
APP_VERSION: "v2.2.2" APP_VERSION: "v2.2.3"
volumes: volumes:
- ${PWD}/deck_files:/app/deck_files - ${PWD}/deck_files:/app/deck_files
- ${PWD}/logs:/app/logs - ${PWD}/logs:/app/logs

View file

@ -21,7 +21,7 @@ services:
# Note: THEME still applies as the default even if selector is hidden # Note: THEME still applies as the default even if selector is hidden
# Version label (optional; shown in footer/diagnostics) # Version label (optional; shown in footer/diagnostics)
APP_VERSION: "v2.2.2" APP_VERSION: "v2.2.3"
volumes: volumes:
# Persist app data locally; ensure these directories exist next to this compose file # Persist app data locally; ensure these directories exist next to this compose file

View file

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "mtg-deckbuilder" name = "mtg-deckbuilder"
version = "2.2.2" version = "2.2.3"
description = "A command-line tool for building and analyzing Magic: The Gathering decks" description = "A command-line tool for building and analyzing Magic: The Gathering decks"
readme = "README.md" readme = "README.md"
license = {file = "LICENSE"} license = {file = "LICENSE"}