From fe9aabbce9407685e142455120ab4b08ff8fbf61 Mon Sep 17 00:00:00 2001 From: mwisnowski Date: Mon, 1 Sep 2025 20:20:04 -0700 Subject: [PATCH] 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 --- CHANGELOG.md | 7 +++++++ RELEASE_NOTES_TEMPLATE.md | 4 +++- code/deck_builder/builder.py | 28 +++++++++++++++++++--------- code/tagging/tag_constants.py | 13 ++++++++++++- code/tagging/tagger.py | 5 +++-- config/card_lists/combo.json | 1 + docker-compose.yml | 2 +- dockerhub-docker-compose.yml | 2 +- pyproject.toml | 2 +- 9 files changed, 48 insertions(+), 16 deletions(-) create mode 120000 config/card_lists/combo.json diff --git a/CHANGELOG.md b/CHANGELOG.md index f88915c..c7afba4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,13 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning ## [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 ### Fixed - Ensure default config files are available when running with bind-mounted config directories: diff --git a/RELEASE_NOTES_TEMPLATE.md b/RELEASE_NOTES_TEMPLATE.md index 7f50477..875ce75 100644 --- a/RELEASE_NOTES_TEMPLATE.md +++ b/RELEASE_NOTES_TEMPLATE.md @@ -33,6 +33,8 @@ - Existing completed pairs are counted toward the target; only missing partners are added. - 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. +- Logic for removal tagging causing self-targetting cards (e.g. Conjurer's Closet) to be tagged as removal (2.2.3) ## Fixes -- Fixed an issue with the Docker Hub image not having the config files for combos/synergies/default deck json example \ No newline at end of file +- 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) \ No newline at end of file diff --git a/code/deck_builder/builder.py b/code/deck_builder/builder.py index a33c217..ab886e2 100644 --- a/code/deck_builder/builder.py +++ b/code/deck_builder/builder.py @@ -1030,15 +1030,25 @@ class DeckBuilder( # Allow the commander to bypass this check. try: if not is_commander: - 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[df_src['name'].astype(str).str.lower() == str(card_name).lower()].empty: - # Not in the legal pool (likely off-color or unavailable) - try: - self.output_func(f"Skipped illegal/off-pool card: {card_name}") - except Exception: - pass - return + # 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 + 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: + # Not in the legal pool (likely off-color or unavailable) + try: + self.output_func(f"Skipped illegal/off-pool card: {card_name}") + except Exception: + pass + return except Exception: # If any unexpected error occurs, fall through (do not block legitimate adds) pass diff --git a/code/tagging/tag_constants.py b/code/tagging/tag_constants.py index 232e040..729849a 100644 --- a/code/tagging/tag_constants.py +++ b/code/tagging/tag_constants.py @@ -496,7 +496,18 @@ REMOVAL_TEXT_PATTERNS: List[str] = [ 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] = [] diff --git a/code/tagging/tagger.py b/code/tagging/tagger.py index 4d9bb3b..ed7e2fb 100644 --- a/code/tagging/tagger.py +++ b/code/tagging/tagger.py @@ -6775,9 +6775,10 @@ def tag_for_removal(df: pd.DataFrame, color: str) -> None: # Create masks for different removal patterns text_mask = create_removal_text_mask(df) + exclude_mask = create_removal_exclusion_mask(df) - # Combine masks - final_mask = text_mask + # Combine masks (and exclude self-targeting effects like 'target permanent you control') + final_mask = text_mask & (~exclude_mask) # Apply tags via rules engine tag_utils.apply_rules(df, rules=[ diff --git a/config/card_lists/combo.json b/config/card_lists/combo.json new file mode 120000 index 0000000..5dd994f --- /dev/null +++ b/config/card_lists/combo.json @@ -0,0 +1 @@ +combos.json \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 9c38a13..a0f3a02 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -47,7 +47,7 @@ services: # Enable virtualization + lazy image tweaks in Step 5 WEB_VIRTUALIZE: "1" # Version label (optional; shown in footer/diagnostics) - APP_VERSION: "v2.2.2" + APP_VERSION: "v2.2.3" volumes: - ${PWD}/deck_files:/app/deck_files - ${PWD}/logs:/app/logs diff --git a/dockerhub-docker-compose.yml b/dockerhub-docker-compose.yml index e9d4249..1add3d4 100644 --- a/dockerhub-docker-compose.yml +++ b/dockerhub-docker-compose.yml @@ -21,7 +21,7 @@ services: # Note: THEME still applies as the default even if selector is hidden # Version label (optional; shown in footer/diagnostics) - APP_VERSION: "v2.2.2" + APP_VERSION: "v2.2.3" volumes: # Persist app data locally; ensure these directories exist next to this compose file diff --git a/pyproject.toml b/pyproject.toml index 91422fa..72063f6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "mtg-deckbuilder" -version = "2.2.2" +version = "2.2.3" description = "A command-line tool for building and analyzing Magic: The Gathering decks" readme = "README.md" license = {file = "LICENSE"}