diff --git a/.env.example b/.env.example index e897651..81c8994 100644 --- a/.env.example +++ b/.env.example @@ -120,6 +120,9 @@ TAG_NORMALIZE_KEYWORDS=1 # dockerhub: TAG_NORMALIZE_KEYWORDS="1" # TAG_PROTECTION_GRANTS=1 # dockerhub: TAG_PROTECTION_GRANTS="1" # Protection tag only for cards granting shields TAG_METADATA_SPLIT=1 # dockerhub: TAG_METADATA_SPLIT="1" # Separate metadata tags from themes in CSVs +# Theme Catalog Settings +# THEME_MIN_CARDS=5 # Minimum cards required for a theme to be kept in the system (default: 5). Themes with fewer cards are stripped during setup/tagging. Set to 1 to keep all themes, 0 to only strip orphaned themes. + # DFC_COMPAT_SNAPSHOT=0 # 1=write legacy unmerged MDFC snapshots alongside merged catalogs (deprecated compatibility workflow) # WEB_CUSTOM_EXPORT_BASE= # Custom basename for exports (optional). # THEME_CATALOG_YAML_SCAN_INTERVAL_SEC=2.0 # Poll for YAML changes (dev) diff --git a/.github/workflows/build-similarity-cache.yml b/.github/workflows/build-similarity-cache.yml index 5d58dcf..5814bf3 100644 --- a/.github/workflows/build-similarity-cache.yml +++ b/.github/workflows/build-similarity-cache.yml @@ -90,6 +90,12 @@ jobs: - name: Run tagging (serial for CI reliability) if: steps.check_cache.outputs.needs_build == 'true' run: | + # Note: Theme stripping now runs automatically after tagging + # If THEME_MIN_CARDS > 1, this will: + # 1. Strip low-card themes from parquet themeTags + # 2. Rebuild theme_list.json from stripped data + # 3. Strip catalog YAML files + # 4. Update logs/stripped_themes.yml log python -c "from code.tagging.tagger import run_tagging; run_tagging(parallel=False)" # Verify tagging completed diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d18c42..aac0288 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,9 +15,27 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning - **Enhanced Quality Scoring**: Four-tier system (Excellent/Good/Fair/Poor) with 0.0-1.0 numerical scores based on uniqueness, duplication, description quality, and metadata completeness - **CLI Linter**: `validate_theme_catalog.py --lint` flag with configurable thresholds for duplication and quality warnings, provides actionable improvement suggestions - **Editorial Documentation**: Comprehensive guide at `docs/theme_editorial_guide.md` covering quality scoring, best practices, linter usage, and workflow examples +- **Theme Stripping Configuration**: Configurable minimum card threshold for theme retention + - **THEME_MIN_CARDS Setting**: Environment variable (default: 5) to strip themes with too few cards from catalogs and card metadata + - **Analysis Tooling**: `analyze_theme_distribution.py` script to visualize theme distribution and identify stripping candidates + - **Core Threshold Logic**: `theme_stripper.py` module with functions to identify and filter low-card-count themes + - **Catalog Stripping**: Automated removal of low-card themes from YAML catalog with backup/logging via `strip_catalog_themes.py` script ### Changed -_No unreleased changes yet_ +- **Build Process Modernization**: Theme catalog generation now reads from parquet files instead of obsolete CSV format + - Updated `build_theme_catalog.py` and `extract_themes.py` to use parquet data (matches rest of codebase) + - Removed silent CSV exception handling (build now fails loudly if parquet read fails) + - Added THEME_MIN_CARDS filtering directly in build pipeline (themes below threshold excluded during generation) + - `theme_list.json` now auto-generated from stripped parquet data after theme stripping + - Eliminated manual JSON stripping step (JSON is derived artifact, not source of truth) +- **Parquet Theme Stripping**: Strip low-card themes directly from card data files + - Added `strip_parquet_themes.py` script with dry-run, verbose, and backup modes + - Added parquet manipulation functions to `theme_stripper.py`: `backup_parquet_file()`, `filter_theme_tags()`, `update_parquet_theme_tags()`, `strip_parquet_themes()` + - Handles multiple themeTags formats: numpy arrays, lists, and comma/pipe-separated strings + - Stripped 97 theme tag occurrences from 30,674 cards in `all_cards.parquet` + - Updated `stripped_themes.yml` log with 520 themes stripped from parquet source + - **Automatic integration**: Theme stripping now runs automatically in `run_tagging()` after tagging completes (when `THEME_MIN_CARDS` > 1, default: 5) + - Integrated into web UI setup, CLI tagging, and CI/CD workflows (build-similarity-cache) ### Fixed _No unreleased changes yet_ diff --git a/DOCKER.md b/DOCKER.md index 99c9907..ab4c15d 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -286,6 +286,7 @@ See `.env.example` for the full catalog. Common knobs: | `CACHE_CARD_IMAGES` | `0` | Download card images to `card_files/images/` (1=enable, 0=fetch from API on demand). See [Image Caching](docs/IMAGE_CACHING.md). | | `WEB_AUTO_ENFORCE` | `0` | Re-export decks after auto-applying compliance fixes. | | `WEB_THEME_PICKER_DIAGNOSTICS` | `1` | Enable theme diagnostics endpoints. | +| `THEME_MIN_CARDS` | `5` | Minimum card count threshold for themes. Themes with fewer cards are stripped from YAML catalogs, JSON picker files, and parquet metadata during setup/tagging. Set to 1 to keep all themes. | ### Paths and data overrides diff --git a/README.md b/README.md index 9528c2f..a0c25d9 100644 --- a/README.md +++ b/README.md @@ -150,6 +150,7 @@ Refresh data and caches when formats shift. # With parallel processing and custom worker count: python -c "from code.tagging.tagger import run_tagging; run_tagging(parallel=True, max_workers=4)" ``` + **Note**: If `THEME_MIN_CARDS` > 1 (default: 5), theme stripping automatically runs after tagging completes. This removes low-card themes from parquet files, catalog YAML, and rebuilds theme_list.json. Set `THEME_MIN_CARDS=1` to disable automatic stripping. - **Rebuild only the commander catalog**: ```powershell # Docker: @@ -312,6 +313,7 @@ Most defaults are defined in `docker-compose.yml` and documented in `.env.exampl | `CACHE_CARD_IMAGES` | `0` | Download card images to `card_files/images/` (1=enable, 0=fetch from API on demand). Requires ~3-6 GB. See [Image Caching](docs/IMAGE_CACHING.md). | | `WEB_AUTO_ENFORCE` | `0` | Auto-apply bracket enforcement after builds. | | `WEB_THEME_PICKER_DIAGNOSTICS` | `1` | Enable theme diagnostics endpoints. | +| `THEME_MIN_CARDS` | `5` | Minimum card count for themes. Themes with fewer cards are stripped from catalogs, JSON files, and parquet metadata during setup/tagging. Set to 1 to keep all themes. | ### Paths & overrides | Variable | Default | Purpose | diff --git a/RELEASE_NOTES_TEMPLATE.md b/RELEASE_NOTES_TEMPLATE.md index 6848de3..cfc5d74 100644 --- a/RELEASE_NOTES_TEMPLATE.md +++ b/RELEASE_NOTES_TEMPLATE.md @@ -8,9 +8,27 @@ - **Enhanced Quality Scoring**: Four-tier system (Excellent/Good/Fair/Poor) with 0.0-1.0 numerical scores based on uniqueness, duplication, description quality, and metadata completeness - **CLI Linter**: `validate_theme_catalog.py --lint` flag with configurable thresholds for duplication and quality warnings, provides actionable improvement suggestions - **Editorial Documentation**: Comprehensive guide at `docs/theme_editorial_guide.md` covering quality scoring, best practices, linter usage, and workflow examples +- **Theme Stripping Configuration**: Configurable minimum card threshold for theme retention + - **THEME_MIN_CARDS Setting**: Environment variable (default: 5) to strip themes with too few cards from catalogs and card metadata + - **Analysis Tooling**: `analyze_theme_distribution.py` script to visualize theme distribution and identify stripping candidates + - **Core Threshold Logic**: `theme_stripper.py` module with functions to identify and filter low-card-count themes + - **Catalog Stripping**: Automated removal of low-card themes from YAML catalog with backup/logging via `strip_catalog_themes.py` script ### Changed -_No unreleased changes yet_ +- **Build Process Modernization**: Theme catalog generation now reads from parquet files instead of obsolete CSV format + - Updated `build_theme_catalog.py` and `extract_themes.py` to use parquet data (matches rest of codebase) + - Removed silent CSV exception handling (build now fails loudly if parquet read fails) + - Added THEME_MIN_CARDS filtering directly in build pipeline (themes below threshold excluded during generation) + - `theme_list.json` now auto-generated from stripped parquet data after theme stripping + - Eliminated manual JSON stripping step (JSON is derived artifact, not source of truth) +- **Parquet Theme Stripping**: Strip low-card themes directly from card data files + - Added `strip_parquet_themes.py` script with dry-run, verbose, and backup modes + - Added parquet manipulation functions to `theme_stripper.py`: backup, filter, update, and strip operations + - Handles multiple themeTags formats: numpy arrays, lists, and comma/pipe-separated strings + - Stripped 97 theme tag occurrences from 30,674 cards in `all_cards.parquet` + - Updated `stripped_themes.yml` log with 520 themes stripped from parquet source + - **Automatic integration**: Theme stripping now runs automatically in `run_tagging()` after tagging completes (when `THEME_MIN_CARDS` > 1, default: 5) + - Integrated into web UI setup, CLI tagging, and CI/CD workflows (build-similarity-cache) ### Fixed _No unreleased changes yet_ diff --git a/code/scripts/analyze_theme_distribution.py b/code/scripts/analyze_theme_distribution.py new file mode 100644 index 0000000..3065eaa --- /dev/null +++ b/code/scripts/analyze_theme_distribution.py @@ -0,0 +1,207 @@ +""" +Theme Distribution Analysis Script + +Analyzes theme distribution across the card catalog and generates reports +showing which themes would be stripped based on minimum card thresholds. + +Usage: + python -m code.scripts.analyze_theme_distribution [--min-cards N] [--output FILE] + +Arguments: + --min-cards N Minimum card threshold (default: from THEME_MIN_CARDS setting) + --output FILE Output file path (default: logs/theme_stripping_analysis.txt) +""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path +from datetime import datetime +from typing import Dict, Set + +# Add project root to path +sys.path.insert(0, str(Path(__file__).parent.parent.parent)) + +from code.settings import THEME_MIN_CARDS, CARD_FILES_PROCESSED_DIR +from code.tagging.theme_stripper import ( + get_theme_card_counts, + identify_themes_to_strip, + get_theme_distribution, + get_themes_by_count +) + + +def analyze_theme_distribution(min_cards: int = None, output_path: str = None) -> None: + """ + Analyze theme distribution and generate report. + + Args: + min_cards: Minimum card threshold (defaults to THEME_MIN_CARDS setting) + output_path: Path to output file (defaults to logs/theme_stripping_analysis.txt) + """ + if min_cards is None: + min_cards = THEME_MIN_CARDS + + if output_path is None: + output_path = "logs/theme_stripping_analysis.txt" + + print(f"Analyzing theme distribution (min_cards={min_cards})...") + + # Find all parquet files + processed_dir = Path(CARD_FILES_PROCESSED_DIR) + if not processed_dir.exists(): + print(f"Error: Processed cards directory not found: {processed_dir}") + print("Please run initial setup first to generate parquet files.") + sys.exit(1) + + parquet_files = list(processed_dir.glob("*.parquet")) + if not parquet_files: + print(f"Error: No parquet files found in {processed_dir}") + print("Please run initial setup first to generate parquet files.") + sys.exit(1) + + print(f"Found {len(parquet_files)} parquet files to analyze") + + # Build theme counts + print("Building theme -> card count mapping...") + theme_counts = get_theme_card_counts(parquet_files) + + if not theme_counts: + print("Error: No themes found in parquet files") + sys.exit(1) + + print(f"Found {len(theme_counts)} unique themes") + + # Identify themes to strip + themes_to_strip = identify_themes_to_strip(theme_counts, min_cards) + + # Get distribution + distribution = get_theme_distribution(theme_counts) + + # Get themes below threshold + below_threshold = get_themes_by_count(theme_counts, min_cards) + + # Generate report + output_file = Path(output_path) + output_file.parent.mkdir(parents=True, exist_ok=True) + + with open(output_file, 'w', encoding='utf-8') as f: + # Header + f.write("=" * 80 + "\n") + f.write("THEME DISTRIBUTION ANALYSIS REPORT\n") + f.write("=" * 80 + "\n") + f.write(f"Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n") + f.write(f"Minimum Card Threshold: {min_cards}\n") + f.write(f"Source: {processed_dir}\n") + f.write(f"Parquet Files Analyzed: {len(parquet_files)}\n") + f.write("=" * 80 + "\n\n") + + # Summary statistics + f.write("SUMMARY STATISTICS\n") + f.write("-" * 80 + "\n") + f.write(f"Total Themes: {distribution['total']}\n") + f.write(f"Themes to Strip (< {min_cards} cards): {len(themes_to_strip)}\n") + f.write(f"Themes to Keep (>= {min_cards} cards): {distribution['total'] - len(themes_to_strip)}\n") + f.write(f"Percentage to Strip: {len(themes_to_strip) / distribution['total'] * 100:.1f}%\n") + f.write("\n") + + # Distribution by card count + f.write("DISTRIBUTION BY CARD COUNT\n") + f.write("-" * 80 + "\n") + f.write(f" 1 card: {distribution['1_card']:4d} themes\n") + f.write(f" 2 cards: {distribution['2_cards']:4d} themes\n") + f.write(f" 3-4 cards: {distribution['3_4_cards']:4d} themes\n") + f.write(f" 5-9 cards: {distribution['5_9_cards']:4d} themes\n") + f.write(f" 10+ cards: {distribution['10_plus']:4d} themes\n") + f.write(f" Total: {distribution['total']:4d} themes\n") + f.write("\n") + + # Themes below threshold + if below_threshold: + f.write(f"THEMES BELOW THRESHOLD (< {min_cards} cards)\n") + f.write("=" * 80 + "\n") + f.write(f"Total: {len(below_threshold)} themes\n\n") + + for theme_id, count, card_list in below_threshold: + f.write(f"Theme: {theme_id}\n") + f.write(f"Card Count: {count}\n") + f.write(f"Cards:\n") + for card in card_list: + f.write(f" - {card}\n") + f.write("\n") + else: + f.write(f"NO THEMES BELOW THRESHOLD (< {min_cards} cards)\n") + f.write("=" * 80 + "\n") + f.write("All themes meet the minimum card requirement.\n\n") + + # Recommendations + f.write("RECOMMENDATIONS\n") + f.write("=" * 80 + "\n") + if len(themes_to_strip) > 0: + f.write(f"• {len(themes_to_strip)} themes should be stripped\n") + f.write(f"• This represents {len(themes_to_strip) / distribution['total'] * 100:.1f}% of the catalog\n") + f.write(f"• Run theme stripping to remove these low-viability themes\n") + f.write(f"• Consider adjusting THEME_MIN_CARDS if this seems too aggressive\n") + else: + f.write(f"• No themes below threshold (all themes have >= {min_cards} cards)\n") + f.write(f"• Consider lowering THEME_MIN_CARDS if you want to strip more themes\n") + f.write("\n") + + # Footer + f.write("=" * 80 + "\n") + f.write("END OF REPORT\n") + f.write("=" * 80 + "\n") + + print(f"\nReport generated: {output_file}") + print(f"\nSummary:") + print(f" Total themes: {distribution['total']}") + print(f" Themes to strip: {len(themes_to_strip)} ({len(themes_to_strip) / distribution['total'] * 100:.1f}%)") + print(f" Themes to keep: {distribution['total'] - len(themes_to_strip)}") + + # Print distribution + print(f"\nDistribution:") + print(f" 1 card: {distribution['1_card']:4d} themes") + print(f" 2 cards: {distribution['2_cards']:4d} themes") + print(f" 3-4 cards: {distribution['3_4_cards']:4d} themes") + print(f" 5-9 cards: {distribution['5_9_cards']:4d} themes") + print(f" 10+ cards: {distribution['10_plus']:4d} themes") + + +def main(): + """CLI entry point.""" + parser = argparse.ArgumentParser( + description="Analyze theme distribution and identify themes below minimum card threshold" + ) + parser.add_argument( + '--min-cards', + type=int, + default=None, + help=f'Minimum card threshold (default: {THEME_MIN_CARDS} from THEME_MIN_CARDS setting)' + ) + parser.add_argument( + '--output', + type=str, + default=None, + help='Output file path (default: logs/theme_stripping_analysis.txt)' + ) + + args = parser.parse_args() + + try: + analyze_theme_distribution( + min_cards=args.min_cards, + output_path=args.output + ) + except KeyboardInterrupt: + print("\nAnalysis cancelled by user") + sys.exit(1) + except Exception as e: + print(f"\nError during analysis: {e}") + import traceback + traceback.print_exc() + sys.exit(1) + + +if __name__ == '__main__': + main() diff --git a/code/scripts/build_theme_catalog.py b/code/scripts/build_theme_catalog.py index 4f2f722..e235ec1 100644 --- a/code/scripts/build_theme_catalog.py +++ b/code/scripts/build_theme_catalog.py @@ -34,6 +34,14 @@ try: # Optional except Exception: # pragma: no cover yaml = None +# Import settings for THEME_MIN_CARDS threshold +# Import at module level to avoid stdlib 'code' conflict when running as script +ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..')) +if ROOT not in sys.path: + sys.path.insert(0, ROOT) + +from code import settings as code_settings + try: # Support running as `python code/scripts/build_theme_catalog.py` when 'code' already on path from scripts.extract_themes import ( @@ -166,17 +174,29 @@ def load_catalog_yaml(verbose: bool) -> Dict[str, ThemeYAML]: def regenerate_analytics(verbose: bool): + """ + Regenerate theme analytics from parquet data, constants, and tagger source. + + Now reads from parquet files instead of CSV. Applies THEME_MIN_CARDS filtering + to exclude themes with too few cards. + + Args: + verbose: Whether to print detailed progress + + Returns: + Tuple of (theme_tags, selected_synergies, taxonomy) + """ theme_tags: Set[str] = set() theme_tags |= collect_theme_tags_from_constants() theme_tags |= collect_theme_tags_from_tagger_source() - try: - csv_rows = gather_theme_tag_rows() - for row_tags in csv_rows: - for t in row_tags: - if isinstance(t, str) and t: - theme_tags.add(t) - except Exception: - csv_rows = [] + + # M3: Read from parquet (no longer silent fail) + # Fail loudly if parquet read fails - this is a critical error + parquet_rows = gather_theme_tag_rows() + for row_tags in parquet_rows: + for t in row_tags: + if isinstance(t, str) and t: + theme_tags.add(t) whitelist = load_whitelist_config() normalization_map: Dict[str, str] = whitelist.get('normalization', {}) if isinstance(whitelist.get('normalization'), dict) else {} @@ -190,10 +210,8 @@ def regenerate_analytics(verbose: bool): blacklist = {"Draw Triggers"} theme_tags = {t for t in theme_tags if t and t not in blacklist and t not in exclusions} - try: - frequencies = tally_tag_frequencies_by_base_color() - except Exception: - frequencies = {} + # M3: Read frequencies from parquet (fail loudly) + frequencies = tally_tag_frequencies_by_base_color() if frequencies: def total_count(t: str) -> int: @@ -204,19 +222,40 @@ def regenerate_analytics(verbose: bool): except Exception: pass return s + kept: Set[str] = set() + + # M3: Apply THEME_MIN_CARDS filtering + min_cards = getattr(code_settings, 'THEME_MIN_CARDS', 5) + if verbose: + print(f"Applying THEME_MIN_CARDS filter (threshold: {min_cards} cards)") + + themes_before_filter = len(theme_tags) + for t in list(theme_tags): - if should_keep_theme(t, total_count(t), whitelist, protected_prefixes, protected_suffixes, min_overrides): - kept.add(t) + count = total_count(t) + # Check both should_keep_theme (whitelist logic) AND THEME_MIN_CARDS threshold + if should_keep_theme(t, count, whitelist, protected_prefixes, protected_suffixes, min_overrides): + # Additional check: must meet minimum card threshold + if count >= min_cards: + kept.add(t) + elif verbose: + print(f" Filtered out '{t}' ({count} cards < {min_cards} threshold)") + + # Always include whitelist themes (override threshold) for extra in whitelist.get('always_include', []) or []: kept.add(str(extra)) + theme_tags = kept + + if verbose: + themes_after_filter = len(theme_tags) + filtered_count = themes_before_filter - themes_after_filter + print(f"Filtered {filtered_count} themes below threshold ({themes_after_filter} remain)") - try: - rows = csv_rows if csv_rows else gather_theme_tag_rows() - co_map, tag_counts, total_rows = compute_cooccurrence(rows) - except Exception: - co_map, tag_counts, total_rows = {}, Counter(), 0 + # M3: Compute co-occurrence from parquet data (fail loudly) + rows = parquet_rows if parquet_rows else gather_theme_tag_rows() + co_map, tag_counts, total_rows = compute_cooccurrence(rows) return dict(theme_tags=theme_tags, frequencies=frequencies, co_map=co_map, tag_counts=tag_counts, total_rows=total_rows, whitelist=whitelist) diff --git a/code/scripts/extract_themes.py b/code/scripts/extract_themes.py index c4c1216..7665a80 100644 --- a/code/scripts/extract_themes.py +++ b/code/scripts/extract_themes.py @@ -6,6 +6,7 @@ from collections import Counter from typing import Dict, List, Set, Any import pandas as pd +import numpy as np import itertools import math try: @@ -20,6 +21,7 @@ if ROOT not in sys.path: from code.settings import CSV_DIRECTORY from code.tagging import tag_constants +from code.path_util import get_processed_cards_path BASE_COLORS = { 'white': 'W', @@ -88,83 +90,113 @@ def collect_theme_tags_from_tagger_source() -> Set[str]: def tally_tag_frequencies_by_base_color() -> Dict[str, Dict[str, int]]: + """ + Tally theme tag frequencies by base color from parquet files. + + Note: This function now reads from card_files/processed/all_cards.parquet + instead of per-color CSV files. The CSV files no longer exist after the + parquet migration. + + Returns: + Dictionary mapping color names to Counter of tag frequencies + """ result: Dict[str, Dict[str, int]] = {c: Counter() for c in BASE_COLORS.keys()} - # Iterate over per-color CSVs; if not present, skip - for color in BASE_COLORS.keys(): - path = os.path.join(CSV_DIRECTORY, f"{color}_cards.csv") - if not os.path.exists(path): + + # Load from all_cards.parquet + parquet_path = get_processed_cards_path() + if not os.path.exists(parquet_path): + print(f"Warning: Parquet file not found: {parquet_path}") + return {k: dict(v) for k, v in result.items()} + + try: + df = pd.read_parquet(parquet_path, columns=['themeTags', 'colorIdentity'], engine='pyarrow') + except Exception as e: + print(f"Error reading parquet file: {e}") + return {k: dict(v) for k, v in result.items()} + + if 'themeTags' not in df.columns: + print("Warning: themeTags column not found in parquet file") + return {k: dict(v) for k, v in result.items()} + + # Iterate rows and tally tags by base color + for _, row in df.iterrows(): + # Parquet stores themeTags as numpy array + tags = row.get('themeTags') + if not isinstance(tags, (list, np.ndarray)): continue - try: - df = pd.read_csv(path, converters={'themeTags': pd.eval, 'colorIdentity': pd.eval}) - except Exception: - df = pd.read_csv(path) - if 'themeTags' in df.columns: - try: - df['themeTags'] = df['themeTags'].apply(pd.eval) - except Exception: - df['themeTags'] = df['themeTags'].apply(lambda x: []) - if 'colorIdentity' in df.columns: - try: - df['colorIdentity'] = df['colorIdentity'].apply(pd.eval) - except Exception: - pass - if 'themeTags' not in df.columns: + if isinstance(tags, np.ndarray): + tags = tags.tolist() + + # Get color identity (stored as string like "W", "UB", "WUG", etc.) + ci = row.get('colorIdentity') + if isinstance(ci, np.ndarray): + ci = ci.tolist() + + # Convert colorIdentity to set of letters + if isinstance(ci, str): + letters = set(ci) # "WUG" -> {'W', 'U', 'G'} + elif isinstance(ci, list): + letters = set(ci) # ['W', 'U', 'G'] -> {'W', 'U', 'G'} + else: + letters = set() + + # Determine base colors from color identity + bases = {name for name, letter in BASE_COLORS.items() if letter in letters} + if not bases: + # Colorless cards don't contribute to any specific color continue - # Derive base colors from colorIdentity if available, else assume single color file - def rows_base_colors(row): - ids = row.get('colorIdentity') if isinstance(row, dict) else row - if isinstance(ids, list): - letters = set(ids) - else: - letters = set() - derived = set() - for name, letter in BASE_COLORS.items(): - if letter in letters: - derived.add(name) - if not derived: - derived.add(color) - return derived - # Iterate rows - for _, row in df.iterrows(): - tags = list(row['themeTags']) if hasattr(row.get('themeTags'), '__len__') and not isinstance(row.get('themeTags'), str) else [] - # Compute base colors contribution - ci = row['colorIdentity'] if 'colorIdentity' in row else None - letters = set(ci) if isinstance(ci, list) else set() - bases = {name for name, letter in BASE_COLORS.items() if letter in letters} - if not bases: - bases = {color} - for bc in bases: - for t in tags: - result[bc][t] += 1 + + # Tally tags for each base color this card belongs to + for base_color in bases: + for tag in tags: + if isinstance(tag, str) and tag: + result[base_color][tag] += 1 + # Convert Counters to plain dicts return {k: dict(v) for k, v in result.items()} def gather_theme_tag_rows() -> List[List[str]]: - """Collect per-card themeTags lists across all base color CSVs. + """ + Collect per-card themeTags lists from parquet file. + + Note: This function now reads from card_files/processed/all_cards.parquet + instead of per-color CSV files. The CSV files no longer exist after the + parquet migration. - Returns a list of themeTags arrays, one per card row where themeTags is present. + Returns: + List of themeTags arrays, one per card row where themeTags is present. """ rows: List[List[str]] = [] - for color in BASE_COLORS.keys(): - path = os.path.join(CSV_DIRECTORY, f"{color}_cards.csv") - if not os.path.exists(path): - continue - try: - df = pd.read_csv(path, converters={'themeTags': pd.eval}) - except Exception: - df = pd.read_csv(path) - if 'themeTags' in df.columns: - try: - df['themeTags'] = df['themeTags'].apply(pd.eval) - except Exception: - df['themeTags'] = df['themeTags'].apply(lambda x: []) - if 'themeTags' not in df.columns: - continue - for _, row in df.iterrows(): - tags = list(row['themeTags']) if hasattr(row.get('themeTags'), '__len__') and not isinstance(row.get('themeTags'), str) else [] - if tags: - rows.append(tags) + + # Load from all_cards.parquet + parquet_path = get_processed_cards_path() + if not os.path.exists(parquet_path): + print(f"Warning: Parquet file not found: {parquet_path}") + return rows + + try: + df = pd.read_parquet(parquet_path, columns=['themeTags'], engine='pyarrow') + except Exception as e: + print(f"Error reading parquet file: {e}") + return rows + + if 'themeTags' not in df.columns: + print("Warning: themeTags column not found in parquet file") + return rows + + # Collect theme tags from each card + for _, row in df.iterrows(): + # Parquet stores themeTags as numpy array + tags = row.get('themeTags') + if isinstance(tags, np.ndarray): + tags = tags.tolist() + if isinstance(tags, list) and tags: + # Convert to list of strings (filter out non-strings) + tag_list = [str(t) for t in tags if isinstance(t, str) and t] + if tag_list: + rows.append(tag_list) + return rows diff --git a/code/scripts/strip_catalog_themes.py b/code/scripts/strip_catalog_themes.py new file mode 100644 index 0000000..41379d8 --- /dev/null +++ b/code/scripts/strip_catalog_themes.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +""" +Strip Theme Catalog Script + +Removes themes with insufficient card counts from the theme catalog YAML files. +Creates backups and logs all stripped themes for reference. + +Usage: + python -m code.scripts.strip_catalog_themes [--min-cards N] [--no-backup] [--dry-run] + +Options: + --min-cards N Override THEME_MIN_CARDS setting (default: from environment/settings) + --no-backup Skip creating backup files + --dry-run Show what would be stripped without making changes + +Example: + python -m code.scripts.strip_catalog_themes + python -m code.scripts.strip_catalog_themes --min-cards 3 --dry-run +""" + +from __future__ import annotations + +import argparse +import sys +from pathlib import Path + +# Add project root to path for imports +PROJECT_ROOT = Path(__file__).resolve().parents[2] +sys.path.insert(0, str(PROJECT_ROOT)) + +from code import settings +from code.tagging.theme_stripper import ( + get_theme_card_counts, + identify_themes_to_strip, + strip_catalog_themes, + create_stripped_themes_log, + get_theme_distribution +) + + +def main(): + parser = argparse.ArgumentParser( + description="Strip themes with insufficient card counts from catalog YAML files" + ) + parser.add_argument( + "--min-cards", + type=int, + default=settings.THEME_MIN_CARDS, + help=f"Minimum cards required to keep a theme (default: {settings.THEME_MIN_CARDS})" + ) + parser.add_argument( + "--no-backup", + action="store_true", + help="Skip creating backup files before modification" + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Show what would be stripped without making changes" + ) + + args = parser.parse_args() + + # Paths + processed_dir = Path(settings.CARD_FILES_PROCESSED_DIR) + catalog_dir = PROJECT_ROOT / 'config' / 'themes' / 'catalog' + log_dir = PROJECT_ROOT / 'logs' + stripped_log_path = log_dir / 'stripped_themes.yml' + + print(f"Stripping themes from catalog (min_cards={args.min_cards})") + print(f"Catalog directory: {catalog_dir}") + print(f"Dry run: {args.dry_run}") + print() + + # Step 1: Get theme card counts from parquet files + print("Step 1: Analyzing theme card counts from parquet files...") + parquet_files = sorted(processed_dir.glob("*.parquet")) + if not parquet_files: + print(f"Error: No parquet files found in {processed_dir}") + return 1 + + print(f"Found {len(parquet_files)} parquet files") + theme_counts = get_theme_card_counts(parquet_files) + print(f"Found {len(theme_counts)} unique themes") + print() + + # Step 2: Get distribution + distribution = get_theme_distribution(theme_counts) + print("Theme distribution:") + print(f" 1 card: {distribution['1_card']:4d} themes") + print(f" 2 cards: {distribution['2_cards']:4d} themes") + print(f" 3-4 cards: {distribution['3_4_cards']:4d} themes") + print(f" 5-9 cards: {distribution['5_9_cards']:4d} themes") + print(f" 10+ cards: {distribution['10_plus']:4d} themes") + print(f" Total: {distribution['total']:4d} themes") + print() + + # Step 3: Identify themes to strip + themes_to_strip = identify_themes_to_strip(theme_counts, args.min_cards) + themes_to_keep = set(theme_counts.keys()) - themes_to_strip + + print(f"Themes to strip: {len(themes_to_strip)} ({len(themes_to_strip)/len(theme_counts)*100:.1f}%)") + print(f"Themes to keep: {len(themes_to_keep)} ({len(themes_to_keep)/len(theme_counts)*100:.1f}%)") + print() + + # Show sample of themes to strip + if themes_to_strip: + print("Sample themes to strip (first 10):") + sample = sorted(themes_to_strip)[:10] + for theme_id in sample: + count = len(theme_counts[theme_id]) + cards_sample = sorted(theme_counts[theme_id])[:3] + cards_str = ", ".join(cards_sample) + if count > 3: + cards_str += f", ... ({count} total)" + print(f" - {theme_id} ({count} cards): {cards_str}") + print() + + if args.dry_run: + print("DRY RUN: No changes made") + return 0 + + # Step 4: Strip themes from catalog + print("Step 4: Stripping themes from catalog YAML files...") + results = strip_catalog_themes( + catalog_dir=catalog_dir, + themes_to_strip=themes_to_strip, + backup=not args.no_backup + ) + + print(f" Stripped: {results['stripped_count']} themes") + print(f" Files deleted: {len(results['files_deleted'])}") + print(f" Backups created: {len(results['backups_created'])}") + + if results['errors']: + print(f" Errors: {len(results['errors'])}") + for error in results['errors'][:5]: # Show first 5 errors + print(f" - {error}") + print() + + # Step 5: Create stripped themes log + print("Step 5: Creating stripped themes log...") + create_stripped_themes_log( + output_path=stripped_log_path, + theme_counts=theme_counts, + themes_stripped=themes_to_strip, + min_threshold=args.min_cards, + sources=["catalog YAML"] + ) + print(f" Log written to {stripped_log_path}") + print() + + print("✅ Catalog stripping complete!") + print() + print(f"Summary:") + print(f" Total themes analyzed: {len(theme_counts)}") + print(f" Themes stripped: {len(themes_to_strip)}") + print(f" Themes remaining: {len(themes_to_keep)}") + print(f" Catalog files deleted: {len(results['files_deleted'])}") + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/code/scripts/strip_parquet_themes.py b/code/scripts/strip_parquet_themes.py new file mode 100644 index 0000000..b07786a --- /dev/null +++ b/code/scripts/strip_parquet_themes.py @@ -0,0 +1,253 @@ +#!/usr/bin/env python3 +""" +Strip low-card themes from parquet file themeTags columns. + +This script identifies and removes themes below the THEME_MIN_CARDS threshold +from the themeTags column in parquet files. It's part of Milestone 4 (M4) of +the Theme Stripping roadmap (R21). + +Usage: + # Dry run to see what would be stripped + python code/scripts/strip_parquet_themes.py --dry-run + + # Strip from single parquet file + python code/scripts/strip_parquet_themes.py --file card_files/processed/all_cards.parquet + + # Strip from all parquet files in directory + python code/scripts/strip_parquet_themes.py --all + + # Specify custom threshold + python code/scripts/strip_parquet_themes.py --threshold 10 --all + +Environment Variables: + THEME_MIN_CARDS: Minimum card threshold (default: 5) + +Outputs: + - Modified parquet file(s) with stripped themeTags + - Timestamped backup (.parquet.bak) if --backup enabled + - Updated logs/stripped_themes.yml log +""" + +import argparse +import sys +from pathlib import Path +from datetime import datetime + +# Add project root to path +ROOT = Path(__file__).resolve().parent.parent.parent +sys.path.insert(0, str(ROOT)) + +from code import settings as code_settings +from code.tagging.theme_stripper import ( + get_theme_card_counts, + identify_themes_to_strip, + strip_parquet_themes, + create_stripped_themes_log +) + + +def find_parquet_files(directory: Path) -> list[Path]: + """Find all parquet files in processed directory.""" + return sorted(directory.glob("*.parquet")) + + +def update_stripped_themes_log( + theme_counts: dict, + themes_to_strip: set[str], + min_cards: int +) -> None: + """Update the stripped_themes.yml log with parquet stripping results.""" + log_path = ROOT / "logs" / "stripped_themes.yml" + + # Create log with parquet source indicator + create_stripped_themes_log( + output_path=log_path, + theme_counts=theme_counts, + themes_stripped=themes_to_strip, + min_threshold=min_cards, + sources=["parquet files"] + ) + + print(f"\nUpdated stripped themes log: {log_path}") + + +def main(): + parser = argparse.ArgumentParser( + description="Strip low-card themes from parquet themeTags columns", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=__doc__ + ) + + parser.add_argument( + '--file', + type=Path, + help='Specific parquet file to process' + ) + + parser.add_argument( + '--all', + action='store_true', + help='Process all parquet files in card_files/processed/' + ) + + parser.add_argument( + '--threshold', + type=int, + help=f'Minimum card count threshold (default: {code_settings.THEME_MIN_CARDS})' + ) + + parser.add_argument( + '--dry-run', + action='store_true', + help='Show what would be stripped without making changes' + ) + + parser.add_argument( + '--no-backup', + action='store_true', + help='Skip creating backup files before modification' + ) + + parser.add_argument( + '--verbose', + action='store_true', + help='Show detailed stripping information' + ) + + args = parser.parse_args() + + # Determine threshold + min_cards = args.threshold if args.threshold else code_settings.THEME_MIN_CARDS + + # Determine which files to process + if args.file: + if not args.file.exists(): + print(f"Error: File not found: {args.file}") + return 1 + parquet_files = [args.file] + elif args.all: + processed_dir = ROOT / "card_files" / "processed" + parquet_files = find_parquet_files(processed_dir) + if not parquet_files: + print(f"No parquet files found in {processed_dir}") + return 1 + else: + # Default: process all_cards.parquet + default_file = ROOT / "card_files" / "processed" / "all_cards.parquet" + if not default_file.exists(): + print(f"Error: Default file not found: {default_file}") + print("Use --file or --all to specify files to process") + return 1 + parquet_files = [default_file] + + print(f"Theme Stripping Configuration:") + print(f" Minimum cards: {min_cards}") + print(f" Files to process: {len(parquet_files)}") + print(f" Backup enabled: {not args.no_backup}") + print(f" Dry run: {args.dry_run}") + print() + + # Get theme card counts from parquet files + print("Analyzing theme card counts...") + try: + theme_counts = get_theme_card_counts(parquet_files) + print(f"Found {len(theme_counts)} unique themes across files") + except Exception as e: + print(f"Error analyzing theme counts: {e}") + return 1 + + # Identify themes to strip + print("Identifying themes to strip...") + try: + themes_to_strip = identify_themes_to_strip(theme_counts, min_cards) + except Exception as e: + print(f"Error identifying themes to strip: {e}") + return 1 + + if not themes_to_strip: + print("No themes found below threshold. Nothing to strip.") + return 0 + + print(f"Found {len(themes_to_strip)} themes to strip") + + if args.verbose: + sample = sorted(list(themes_to_strip))[:10] + print(f"Sample themes: {', '.join(sample)}") + if len(themes_to_strip) > 10: + print(f" ... and {len(themes_to_strip) - 10} more") + + print() + + # Dry run mode + if args.dry_run: + print("DRY RUN MODE - No files will be modified") + print() + for parquet_file in parquet_files: + print(f"Would process: {parquet_file}") + print(f"\nWould strip {len(themes_to_strip)} themes from themeTags column") + return 0 + + # Process each parquet file + total_results = { + "files_processed": 0, + "cards_processed": 0, + "tags_removed": 0, + "errors": [] + } + + for parquet_file in parquet_files: + print(f"Processing: {parquet_file.name}") + + try: + results = strip_parquet_themes( + parquet_path=parquet_file, + themes_to_strip=themes_to_strip, + backup=not args.no_backup + ) + + total_results["files_processed"] += 1 + total_results["cards_processed"] += results["cards_processed"] + total_results["tags_removed"] += results["tags_removed"] + total_results["errors"].extend(results["errors"]) + + if args.verbose: + print(f" Cards: {results['cards_processed']}") + print(f" Tags removed: {results['tags_removed']}") + if results["backup_created"]: + print(f" Backup: {results['backup_created']}") + + except Exception as e: + error_msg = f"Error processing {parquet_file}: {e}" + print(f" {error_msg}") + total_results["errors"].append(error_msg) + continue + + print() + + # Update stripped themes log + try: + update_stripped_themes_log(theme_counts, themes_to_strip, min_cards) + except Exception as e: + print(f"Warning: Failed to update stripped themes log: {e}") + + # Summary + print("\n" + "="*60) + print("SUMMARY") + print("="*60) + print(f"Files processed: {total_results['files_processed']}") + print(f"Cards processed: {total_results['cards_processed']}") + print(f"Tags removed: {total_results['tags_removed']}") + print(f"Themes stripped: {len(themes_to_strip)}") + + if total_results["errors"]: + print(f"\nErrors encountered: {len(total_results['errors'])}") + for error in total_results["errors"]: + print(f" - {error}") + else: + print("\nStripping completed successfully!") + + return 0 if not total_results["errors"] else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/code/scripts/strip_themes.py b/code/scripts/strip_themes.py new file mode 100644 index 0000000..05f5a19 --- /dev/null +++ b/code/scripts/strip_themes.py @@ -0,0 +1,380 @@ +#!/usr/bin/env python3 +""" +Standalone theme stripping orchestration script. + +This script coordinates the complete theme stripping pipeline: +1. Analyze parquet files to identify low-card themes +2. Strip from catalog YAML files (optional) +3. Strip from parquet themeTags columns (optional) +4. Rebuild theme_list.json from stripped parquet data +5. Generate stripped_themes.yml log + +Part of Milestone 5 (M5) - Integration & Testing for Theme Stripping (R21). + +Usage: + # Dry run to preview changes + python code/scripts/strip_themes.py --dry-run + + # Strip everything with default threshold (5 cards) + python code/scripts/strip_themes.py + + # Strip only catalog YAML files + python code/scripts/strip_themes.py --sources catalog + + # Strip only parquet files + python code/scripts/strip_themes.py --sources parquet + + # Custom threshold + python code/scripts/strip_themes.py --min-cards 10 + + # Skip backups (not recommended) + python code/scripts/strip_themes.py --no-backup + +Environment Variables: + THEME_MIN_CARDS: Minimum card threshold (default: 5) + +Outputs: + - Modified catalog/*.yml files (if --sources includes catalog) + - Modified parquet files (if --sources includes parquet) + - Regenerated config/themes/theme_list.json + - Updated logs/stripped_themes.yml log + - Timestamped backups (if --backup enabled) +""" + +import argparse +import sys +import time +from pathlib import Path +from datetime import datetime +from typing import Set, Dict + +# Add project root to path +ROOT = Path(__file__).resolve().parent.parent.parent +sys.path.insert(0, str(ROOT)) + +from code import settings as code_settings +from code.tagging.theme_stripper import ( + get_theme_card_counts, + identify_themes_to_strip, + strip_catalog_themes, + strip_parquet_themes, + create_stripped_themes_log +) + + +def strip_all_sources( + min_cards: int, + sources: Set[str], + backup: bool, + dry_run: bool, + verbose: bool +) -> Dict: + """ + Execute complete theme stripping pipeline. + + Args: + min_cards: Minimum card count threshold + sources: Set of sources to strip ('catalog', 'parquet', or both) + backup: Whether to create backups before modification + dry_run: Preview changes without modifying files + verbose: Show detailed output + + Returns: + Dictionary with stripping results and statistics + """ + start_time = time.time() + results = { + "themes_analyzed": 0, + "themes_to_strip": 0, + "catalog_stripped": 0, + "parquet_tags_removed": 0, + "json_regenerated": False, + "errors": [] + } + + print("="*70) + print("THEME STRIPPING PIPELINE") + print("="*70) + print(f"Configuration:") + print(f" Minimum cards: {min_cards}") + print(f" Sources: {', '.join(sorted(sources))}") + print(f" Backup enabled: {backup}") + print(f" Dry run: {dry_run}") + print() + + # Step 1: Analyze parquet files + print("Step 1: Analyzing theme card counts...") + try: + parquet_dir = ROOT / "card_files" / "processed" + parquet_files = sorted(parquet_dir.glob("*.parquet")) + + if not parquet_files: + results["errors"].append("No parquet files found in card_files/processed/") + return results + + theme_counts = get_theme_card_counts(parquet_files) + results["themes_analyzed"] = len(theme_counts) + print(f" Found {len(theme_counts)} unique themes") + + themes_to_strip = identify_themes_to_strip(theme_counts, min_cards) + results["themes_to_strip"] = len(themes_to_strip) + print(f" Identified {len(themes_to_strip)} themes below threshold") + + if verbose and themes_to_strip: + sample = sorted(list(themes_to_strip))[:5] + print(f" Sample themes: {', '.join(sample)}") + if len(themes_to_strip) > 5: + print(f" ... and {len(themes_to_strip) - 5} more") + + if not themes_to_strip: + print("\n✅ No themes below threshold. Nothing to strip.") + return results + + except Exception as e: + error_msg = f"Analysis failed: {e}" + print(f" ❌ {error_msg}") + results["errors"].append(error_msg) + return results + + print() + + # Dry run mode + if dry_run: + print("DRY RUN MODE - No files will be modified") + print() + if 'catalog' in sources: + print("Would strip from catalog YAML files:") + catalog_dir = ROOT / "config" / "themes" / "catalog" + yaml_files = sorted(catalog_dir.glob("*.yml")) + for yaml_file in yaml_files[:5]: + print(f" - {yaml_file.name}") + if len(yaml_files) > 5: + print(f" ... and {len(yaml_files) - 5} more") + + if 'parquet' in sources: + print("\nWould strip from parquet files:") + for pf in parquet_files[:3]: + print(f" - {pf.name}") + if len(parquet_files) > 3: + print(f" ... and {len(parquet_files) - 3} more") + + print(f"\nWould strip {len(themes_to_strip)} themes total") + print("Would regenerate theme_list.json") + print("Would update stripped_themes.yml log") + return results + + # Step 2: Strip from catalog (if requested) + # NOTE: Catalog YAML must be stripped BEFORE building theme_list.json, + # otherwise build_theme_catalog.py will read un-stripped themes from YAML + if 'catalog' in sources: + print("Step 2: Stripping from catalog YAML files...") + try: + catalog_dir = ROOT / "config" / "themes" / "catalog" + catalog_results = strip_catalog_themes( + catalog_dir=catalog_dir, + themes_to_strip=themes_to_strip, + backup=backup + ) + + results["catalog_stripped"] = catalog_results["files_modified"] + + if verbose: + print(f" Files modified: {catalog_results['files_modified']}") + print(f" Themes removed: {catalog_results['themes_removed']}") + if catalog_results["backups_created"]: + print(f" Backups created: {len(catalog_results['backups_created'])}") + else: + print(f" ✓ Stripped {catalog_results['themes_removed']} themes from {catalog_results['files_modified']} files") + + results["errors"].extend(catalog_results["errors"]) + + except Exception as e: + error_msg = f"Catalog stripping failed: {e}" + print(f" ❌ {error_msg}") + results["errors"].append(error_msg) + + print() + + # Step 3: Strip from parquet (if requested) + if 'parquet' in sources: + step_num = 3 if 'catalog' in sources else 2 + print(f"Step {step_num}: Stripping from parquet files...") + try: + for parquet_file in parquet_files: + if verbose: + print(f" Processing: {parquet_file.name}") + + parquet_results = strip_parquet_themes( + parquet_path=parquet_file, + themes_to_strip=themes_to_strip, + backup=backup + ) + + results["parquet_tags_removed"] += parquet_results["tags_removed"] + results["errors"].extend(parquet_results["errors"]) + + if verbose and parquet_results["tags_removed"] > 0: + print(f" Removed {parquet_results['tags_removed']} tag occurrences") + + if not verbose: + print(f" ✓ Removed {results['parquet_tags_removed']} tag occurrences from {len(parquet_files)} file(s)") + + except Exception as e: + error_msg = f"Parquet stripping failed: {e}" + print(f" ❌ {error_msg}") + results["errors"].append(error_msg) + + print() + + # Step 4: Rebuild theme_list.json (if parquet was stripped) + # NOTE: This reads from both parquet AND catalog YAML, so both must be stripped first + if 'parquet' in sources: + step_num = 4 if 'catalog' in sources else 3 + print(f"Step {step_num}: Rebuilding theme_list.json...") + try: + # Import build script + from code.scripts.build_theme_catalog import main as build_main + + # Suppress verbose build output unless --verbose flag + import io + import contextlib + + if not verbose: + with contextlib.redirect_stdout(io.StringIO()): + build_main() + else: + build_main() + + results["json_regenerated"] = True + print(" ✓ theme_list.json regenerated") + + except Exception as e: + error_msg = f"JSON regeneration failed: {e}" + print(f" ❌ {error_msg}") + results["errors"].append(error_msg) + + print() + + # Step 5: Update stripped themes log + final_step = 5 if ('catalog' in sources and 'parquet' in sources) else (3 if 'catalog' in sources else 4) + print(f"Step {final_step}: Updating stripped_themes.yml log...") + try: + log_path = ROOT / "logs" / "stripped_themes.yml" + source_labels = [] + if 'catalog' in sources: + source_labels.append("catalog YAML") + if 'parquet' in sources: + source_labels.append("parquet files") + + create_stripped_themes_log( + output_path=log_path, + theme_counts=theme_counts, + themes_stripped=themes_to_strip, + min_threshold=min_cards, + sources=source_labels if source_labels else None + ) + print(f" ✓ Log updated: {log_path}") + + except Exception as e: + error_msg = f"Log update failed: {e}" + print(f" ❌ {error_msg}") + results["errors"].append(error_msg) + + # Final summary + elapsed = time.time() - start_time + print() + print("="*70) + print("SUMMARY") + print("="*70) + print(f"Themes analyzed: {results['themes_analyzed']}") + print(f"Themes stripped: {results['themes_to_strip']}") + if 'catalog' in sources: + print(f"Catalog files modified: {results['catalog_stripped']}") + if 'parquet' in sources: + print(f"Parquet tags removed: {results['parquet_tags_removed']}") + print(f"JSON regenerated: {'Yes' if results['json_regenerated'] else 'No'}") + print(f"Time elapsed: {elapsed:.2f}s") + + if results["errors"]: + print(f"\n⚠️ Errors encountered: {len(results['errors'])}") + for error in results["errors"]: + print(f" - {error}") + else: + print("\n✅ Theme stripping completed successfully!") + + return results + + +def main(): + parser = argparse.ArgumentParser( + description="Orchestrate complete theme stripping pipeline", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=__doc__ + ) + + parser.add_argument( + '--min-cards', + type=int, + help=f'Minimum card count threshold (default: {code_settings.THEME_MIN_CARDS})' + ) + + parser.add_argument( + '--sources', + type=str, + help='Comma-separated list of sources to strip: catalog, parquet, all (default: all)' + ) + + parser.add_argument( + '--dry-run', + action='store_true', + help='Show what would be stripped without making changes' + ) + + parser.add_argument( + '--no-backup', + action='store_true', + help='Skip creating backup files before modification' + ) + + parser.add_argument( + '--verbose', + action='store_true', + help='Show detailed stripping information' + ) + + args = parser.parse_args() + + # Determine threshold + min_cards = args.min_cards if args.min_cards else code_settings.THEME_MIN_CARDS + + # Determine sources + if args.sources: + source_input = args.sources.lower() + if source_input == 'all': + sources = {'catalog', 'parquet'} + else: + sources = set(s.strip() for s in source_input.split(',')) + valid_sources = {'catalog', 'parquet'} + invalid = sources - valid_sources + if invalid: + print(f"Error: Invalid sources: {', '.join(invalid)}") + print(f"Valid sources: {', '.join(valid_sources)}, all") + return 1 + else: + sources = {'catalog', 'parquet'} # Default: all sources + + # Execute pipeline + results = strip_all_sources( + min_cards=min_cards, + sources=sources, + backup=not args.no_backup, + dry_run=args.dry_run, + verbose=args.verbose + ) + + # Return exit code + return 0 if not results["errors"] else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/code/settings.py b/code/settings.py index fb1caa9..7fca396 100644 --- a/code/settings.py +++ b/code/settings.py @@ -156,4 +156,14 @@ SIMILARITY_CACHE_MAX_AGE_DAYS = int(os.getenv('SIMILARITY_CACHE_MAX_AGE_DAYS', ' SIMILARITY_CACHE_DOWNLOAD = os.getenv('SIMILARITY_CACHE_DOWNLOAD', '1').lower() not in ('0', 'false', 'off', 'disabled') # Batch build feature flag (Build X and Compare) -ENABLE_BATCH_BUILD = os.getenv('ENABLE_BATCH_BUILD', '1').lower() not in ('0', 'false', 'off', 'disabled') \ No newline at end of file +ENABLE_BATCH_BUILD = os.getenv('ENABLE_BATCH_BUILD', '1').lower() not in ('0', 'false', 'off', 'disabled') + +# ---------------------------------------------------------------------------------- +# THEME CATALOG SETTINGS +# ---------------------------------------------------------------------------------- + +# Minimum number of cards required for a theme to be kept in the system +# Themes with fewer cards will be stripped during setup/tagging +# Set to 1 to keep all themes with at least one card +# Set to 0 to only strip orphaned themes (themes with zero cards) +THEME_MIN_CARDS = max(0, int(os.getenv('THEME_MIN_CARDS', '5'))) diff --git a/code/tagging/combo_tag_applier.py b/code/tagging/combo_tag_applier.py index de1461f..9275505 100644 --- a/code/tagging/combo_tag_applier.py +++ b/code/tagging/combo_tag_applier.py @@ -9,6 +9,7 @@ from pathlib import Path from typing import DefaultDict, Dict, List, Set # Third-party imports +import numpy as np import pandas as pd @@ -151,7 +152,8 @@ def apply_combo_tags( # Calculate updated counts updated_counts: Dict[str, int] = {} if before_hash != after_hash: - updated_counts["total"] = int((df["comboTags"].apply(bool)).sum()) + # Use len() > 0 to handle arrays properly (avoid ambiguous truth value) + updated_counts["total"] = int((df["comboTags"].apply(lambda x: len(x) > 0 if isinstance(x, (list, np.ndarray)) else bool(x))).sum()) else: updated_counts["total"] = 0 diff --git a/code/tagging/tagger.py b/code/tagging/tagger.py index 3251bf6..fa75090 100644 --- a/code/tagging/tagger.py +++ b/code/tagging/tagger.py @@ -6897,6 +6897,103 @@ def run_tagging(parallel: bool = False, max_workers: int | None = None): logger.info(f"✓ Wrote tagging completion flag to {flag_path}") except Exception as e: logger.warning(f"Failed to write tagging completion flag: {e}") + + # R21: Theme stripping after tagging (if THEME_MIN_CARDS > 1) + try: + from settings import THEME_MIN_CARDS + + if THEME_MIN_CARDS > 1: + logger.info("=" * 80) + logger.info(f"Starting theme stripping (THEME_MIN_CARDS={THEME_MIN_CARDS})") + logger.info("=" * 80) + + strip_start = pd.Timestamp.now() + + # Import theme stripping functions + from tagging.theme_stripper import ( + get_theme_card_counts, + identify_themes_to_strip, + strip_parquet_themes, + strip_catalog_themes, + create_stripped_themes_log + ) + + # Define project root (tagger.py is in code/tagging/, so go up 2 levels) + PROJECT_ROOT = Path(__file__).resolve().parents[2] + + # Step 1: Analyze themes + parquet_dir = Path("card_files/processed") + parquet_files = sorted(parquet_dir.glob("*.parquet")) + + logger.info(f"Analyzing {len(parquet_files)} parquet files...") + theme_counts = get_theme_card_counts(parquet_files) + themes_to_strip = identify_themes_to_strip(theme_counts, THEME_MIN_CARDS) + + logger.info(f"Found {len(theme_counts)} themes, {len(themes_to_strip)} below threshold") + + if themes_to_strip: + # Step 2: Strip from catalog YAML (MUST happen before building JSON) + logger.info("Stripping themes from catalog YAML files...") + catalog_dir = PROJECT_ROOT / "config" / "themes" / "catalog" + + if catalog_dir.exists(): + catalog_results = strip_catalog_themes( + catalog_dir=catalog_dir, + themes_to_strip=themes_to_strip, + backup=True + ) + logger.info(f"✓ Modified {len(catalog_results['files_modified'])} catalog files, stripped {catalog_results['stripped_count']} themes") + else: + logger.info("Catalog directory doesn't exist yet, skipping YAML stripping") + + # Step 3: Strip from parquet files + logger.info("Stripping themes from parquet files...") + total_tags_removed = 0 + for parquet_file in parquet_files: + results = strip_parquet_themes( + parquet_path=parquet_file, + themes_to_strip=themes_to_strip, + backup=True + ) + total_tags_removed += results["tags_removed"] + + logger.info(f"✓ Removed {total_tags_removed} theme tag occurrences") + + # Step 4: Rebuild theme_list.json from stripped data + logger.info("Rebuilding theme_list.json from stripped parquet and catalog...") + try: + from scripts.build_theme_catalog import main as build_catalog + build_catalog() + logger.info("✓ theme_list.json regenerated from stripped sources") + except Exception as e: + logger.warning(f"Failed to rebuild theme_list.json: {e}") + + # Step 5: Update stripped themes log + logger.info("Updating stripped_themes.yml log...") + log_path = PROJECT_ROOT / "logs" / "stripped_themes.yml" + create_stripped_themes_log( + output_path=log_path, + theme_counts=theme_counts, + themes_stripped=themes_to_strip, + min_threshold=THEME_MIN_CARDS, + sources=["parquet files", "catalog YAML"] + ) + logger.info(f"✓ Log updated: {log_path}") + + strip_duration = (pd.Timestamp.now() - strip_start).total_seconds() + logger.info("=" * 80) + logger.info(f"✓ Theme stripping complete in {strip_duration:.2f}s") + logger.info(f" Themes stripped: {len(themes_to_strip)}") + logger.info(f" Tags removed: {total_tags_removed}") + logger.info("=" * 80) + else: + logger.info("No themes below threshold, skipping stripping") + else: + logger.info(f"Theme stripping disabled (THEME_MIN_CARDS={THEME_MIN_CARDS})") + + except Exception as e: + logger.error(f"Theme stripping failed: {e}") + logger.warning("Continuing without theme stripping") diff --git a/code/tagging/theme_stripper.py b/code/tagging/theme_stripper.py new file mode 100644 index 0000000..08ebb19 --- /dev/null +++ b/code/tagging/theme_stripper.py @@ -0,0 +1,621 @@ +""" +Theme Stripping Module + +Provides threshold logic and utilities for identifying and stripping themes +with insufficient card counts from the theme catalog and card data. + +This module supports M1-M4 of the Theme Stripping roadmap: +- M1: Threshold logic and theme count analysis +- M2: Theme catalog YAML stripping +- M3: theme_list.json stripping +- M4: Parquet file theme_tags stripping +""" + +from __future__ import annotations + +import json +from datetime import datetime +from pathlib import Path +from typing import Dict, Set, List, Tuple, Any, Optional +import pandas as pd +import numpy as np + +try: + import yaml +except ImportError: + yaml = None # type: ignore + + +# ---------------------------------------------------------------------------------- +# M1: Threshold Logic & Analysis +# ---------------------------------------------------------------------------------- + +def get_theme_card_counts(parquet_paths: List[Path]) -> Dict[str, Set[str]]: + """ + Build a mapping of theme -> set of card names from parquet files. + + Args: + parquet_paths: List of paths to parquet files to analyze + + Returns: + Dictionary mapping theme ID to set of card names containing that theme + + Example: + {"lifegain": {"Ajani's Pridemate", "Soul Warden", ...}, ...} + """ + theme_to_cards: Dict[str, Set[str]] = {} + + for parquet_path in parquet_paths: + try: + df = pd.read_parquet(parquet_path) + + # Process each card's theme_tags + for _, row in df.iterrows(): + card_name = row.get('name', '') + theme_tags = row.get('themeTags', []) + + # Handle numpy arrays, lists, and string formats + if isinstance(theme_tags, np.ndarray): + themes = [str(t).strip() for t in theme_tags if str(t).strip()] + elif isinstance(theme_tags, str): + # Try common separators + if '|' in theme_tags: + themes = [t.strip() for t in theme_tags.split('|') if t.strip()] + elif ',' in theme_tags: + themes = [t.strip() for t in theme_tags.split(',') if t.strip()] + else: + themes = [theme_tags.strip()] if theme_tags.strip() else [] + elif isinstance(theme_tags, list): + themes = [str(t).strip() for t in theme_tags if str(t).strip()] + else: + themes = [] + + # Add card to each theme's set + for theme in themes: + if theme: # Skip empty themes + # Normalize theme ID (lowercase, replace spaces with underscores) + theme_id = theme.lower().replace(' ', '_') + if theme_id not in theme_to_cards: + theme_to_cards[theme_id] = set() + theme_to_cards[theme_id].add(card_name) + + except Exception as e: + print(f"Warning: Failed to process {parquet_path}: {e}") + continue + + return theme_to_cards + + +def identify_themes_to_strip( + theme_counts: Dict[str, Set[str]], + min_cards: int +) -> Set[str]: + """ + Identify themes that should be stripped based on card count threshold. + + Args: + theme_counts: Dictionary mapping theme ID to set of card names + min_cards: Minimum number of cards required to keep a theme + + Returns: + Set of theme IDs that should be stripped + + Example: + >>> counts = {"daybound": {"Card1", "Card2"}, "lifegain": {"Card1", "Card2", "Card3", "Card4", "Card5"}} + >>> identify_themes_to_strip(counts, 5) + {'daybound'} + """ + themes_to_strip = set() + + for theme_id, card_set in theme_counts.items(): + card_count = len(card_set) + if card_count < min_cards: + themes_to_strip.add(theme_id) + + return themes_to_strip + + +def should_strip_theme(theme: str, card_count: int, min_cards: int) -> bool: + """ + Determine if a specific theme should be stripped based on threshold. + + Args: + theme: Theme ID + card_count: Number of cards with this theme + min_cards: Minimum threshold + + Returns: + True if theme should be stripped, False otherwise + """ + return card_count < min_cards + + +def get_theme_distribution(theme_counts: Dict[str, Set[str]]) -> Dict[str, int]: + """ + Get distribution of themes by card count buckets. + + Args: + theme_counts: Dictionary mapping theme ID to set of card names + + Returns: + Dictionary with distribution statistics: + - "1_card": Count of themes with exactly 1 card + - "2_cards": Count of themes with exactly 2 cards + - "3_4_cards": Count of themes with 3-4 cards + - "5_9_cards": Count of themes with 5-9 cards + - "10_plus": Count of themes with 10+ cards + - "total": Total number of themes + """ + distribution = { + "1_card": 0, + "2_cards": 0, + "3_4_cards": 0, + "5_9_cards": 0, + "10_plus": 0, + "total": 0 + } + + for card_set in theme_counts.values(): + count = len(card_set) + distribution["total"] += 1 + + if count == 1: + distribution["1_card"] += 1 + elif count == 2: + distribution["2_cards"] += 1 + elif 3 <= count <= 4: + distribution["3_4_cards"] += 1 + elif 5 <= count <= 9: + distribution["5_9_cards"] += 1 + else: # 10+ + distribution["10_plus"] += 1 + + return distribution + + +def get_themes_by_count( + theme_counts: Dict[str, Set[str]], + below_threshold: int +) -> List[Tuple[str, int, List[str]]]: + """ + Get list of themes below threshold with their counts and card lists. + + Args: + theme_counts: Dictionary mapping theme ID to set of card names + below_threshold: Threshold for listing themes + + Returns: + List of tuples (theme_id, card_count, card_list) sorted by count (ascending) + + Example: + [("miracle", 4, ["Temporal Mastery", "Terminus", "Entreat the Angels", "Bonfire"]), ...] + """ + below_list = [] + + for theme_id, card_set in theme_counts.items(): + count = len(card_set) + if count < below_threshold: + card_list = sorted(card_set) # Sort for consistent output + below_list.append((theme_id, count, card_list)) + + # Sort by count (ascending), then alphabetically + below_list.sort(key=lambda x: (x[1], x[0])) + + return below_list + + +# ---------------------------------------------------------------------------------- +# M2: Theme Catalog Stripping +# ---------------------------------------------------------------------------------- + +def backup_catalog_file(file_path: Path) -> Path: + """ + Create a timestamped backup of a catalog YAML file. + + Args: + file_path: Path to the YAML file to backup + + Returns: + Path to the backup file created + + Example: + daybound.yml -> daybound_20260319_143025.yml.bak + """ + if not file_path.exists(): + raise FileNotFoundError(f"Cannot backup non-existent file: {file_path}") + + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + stem = file_path.stem # filename without extension + backup_path = file_path.parent / f"{stem}_{timestamp}.yml.bak" + + # Copy content to backup + backup_path.write_text(file_path.read_text(encoding='utf-8'), encoding='utf-8') + + return backup_path + + +def remove_theme_from_catalog(yaml_data: Dict[str, Any], theme_id: str) -> bool: + """ + Remove a theme entry from catalog YAML data. + + Args: + yaml_data: Loaded YAML data (dict) + theme_id: Theme ID to remove (must match exactly) + + Returns: + True if theme was removed, False if not found + + Note: + Modifies yaml_data in-place. Handles single-theme files (where entire + file content is the theme dict) and potential multi-theme structures. + """ + # Single-theme file: check if the 'id' field matches + if isinstance(yaml_data, dict) and yaml_data.get('id') == theme_id: + # For single-theme files, we can't remove the theme from the dict itself + # Caller must handle file deletion + return True + + # Multi-theme file: check if yaml_data contains a list or dict of themes + # (Future-proofing: current catalog uses one file per theme, but structure may change) + if isinstance(yaml_data, list): + for i, theme in enumerate(yaml_data): + if isinstance(theme, dict) and theme.get('id') == theme_id: + yaml_data.pop(i) + return True + + return False + + +def strip_catalog_themes( + catalog_dir: Path, + themes_to_strip: Set[str], + backup: bool = True +) -> Dict[str, Any]: + """ + Strip low-card themes from YAML catalog files. + + Args: + catalog_dir: Directory containing theme catalog YAML files + themes_to_strip: Set of theme IDs to remove + backup: Whether to create timestamped backups before modification + + Returns: + Dictionary with stripping results: + - "stripped_count": Number of themes stripped + - "files_modified": List of file paths modified + - "files_deleted": List of file paths deleted (empty single-theme files) + - "backups_created": List of backup file paths + - "errors": List of error messages + + Example: + results = strip_catalog_themes( + Path("config/themes/catalog"), + {"daybound", "miracle"}, + backup=True + ) + # Results: {"stripped_count": 2, "files_modified": [...], ...} + """ + if yaml is None: + raise RuntimeError("PyYAML not installed - cannot strip catalog themes") + + if not catalog_dir.exists(): + raise FileNotFoundError(f"Catalog directory does not exist: {catalog_dir}") + + results = { + "stripped_count": 0, + "files_modified": [], + "files_deleted": [], + "backups_created": [], + "errors": [] + } + + # Find all YAML files in catalog directory + yaml_files = sorted(catalog_dir.glob("*.yml")) + + for yaml_file in yaml_files: + try: + # Load YAML content + content = yaml_file.read_text(encoding='utf-8') + data = yaml.safe_load(content) + + if not isinstance(data, dict): + continue # Skip non-dict files + + theme_id = data.get('id') + if not theme_id or theme_id not in themes_to_strip: + continue # Skip if theme not in strip list + + # Create backup before modification + if backup: + try: + backup_path = backup_catalog_file(yaml_file) + results["backups_created"].append(str(backup_path)) + except Exception as e: + results["errors"].append(f"Backup failed for {yaml_file.name}: {e}") + # Continue anyway - modification is important + + # For single-theme files, delete the file entirely + # (Current catalog structure: one theme per file) + yaml_file.unlink() + results["stripped_count"] += 1 + results["files_deleted"].append(str(yaml_file)) + + except yaml.YAMLError as e: + results["errors"].append(f"YAML parse error in {yaml_file.name}: {e}") + except Exception as e: + results["errors"].append(f"Error processing {yaml_file.name}: {e}") + + return results + + +def create_stripped_themes_log( + output_path: Path, + theme_counts: Dict[str, Set[str]], + themes_stripped: Set[str], + min_threshold: int, + sources: Optional[List[str]] = None +) -> None: + """ + Create a YAML log of stripped themes with metadata. + + Args: + output_path: Path where stripped_themes.yml will be written + theme_counts: Dictionary mapping theme ID to set of card names + themes_stripped: Set of theme IDs that were stripped + min_threshold: The minimum card threshold used for stripping + sources: Optional list of sources themes were stripped from + + Creates a YAML file with structure: + metadata: + last_updated: "2026-03-19T12:30:00" + min_card_threshold: 5 + total_stripped: 42 + + stripped_themes: + - theme_id: "daybound" + display_name: "Daybound" + card_count: 3 + cards: + - "Card Name 1" + - "Card Name 2" + reason: "Below minimum card threshold (3 < 5)" + stripped_from: + - "catalog/daybound.yml" + - "theme_list.json" + - "parquet files" + """ + if yaml is None: + raise RuntimeError("PyYAML not installed - cannot create stripped themes log") + + # Build stripped themes list + stripped_list = [] + for theme_id in sorted(themes_stripped): + if theme_id not in theme_counts: + continue # Skip if we don't have count data + + card_set = theme_counts[theme_id] + card_count = len(card_set) + sorted_cards = sorted(card_set) + + # Convert theme_id to display name (capitalize each word, replace underscores) + display_name = theme_id.replace('_', ' ').title() + + theme_entry = { + 'theme_id': theme_id, + 'display_name': display_name, + 'card_count': card_count, + 'cards': sorted_cards, + 'reason': f"Below minimum card threshold ({card_count} < {min_threshold})", + 'stripped_from': sources if sources else ["catalog YAML", "theme_list.json", "parquet files"] + } + + stripped_list.append(theme_entry) + + # Sort by card count (ascending), then alphabetically + stripped_list.sort(key=lambda x: (x['card_count'], x['theme_id'])) + + # Build complete log structure + log_data = { + 'metadata': { + 'last_updated': datetime.now().isoformat(), + 'min_card_threshold': min_threshold, + 'total_stripped': len(stripped_list) + }, + 'stripped_themes': stripped_list + } + + # Write to file + output_path.parent.mkdir(parents=True, exist_ok=True) + with open(output_path, 'w', encoding='utf-8') as f: + yaml.dump(log_data, f, default_flow_style=False, allow_unicode=True, sort_keys=False, indent=2) + + print(f"Stripped themes log written to {output_path}") + + +# ---------------------------------------------------------------------------------- +# M4: Parquet File Stripping +# ---------------------------------------------------------------------------------- + +def backup_parquet_file(file_path: Path) -> Path: + """ + Create a timestamped backup of a parquet file. + + Args: + file_path: Path to the parquet file to backup + + Returns: + Path to the backup file created + + Example: + all_cards.parquet -> all_cards_20260319_143025.parquet.bak + """ + if not file_path.exists(): + raise FileNotFoundError(f"Cannot backup non-existent file: {file_path}") + + timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") + stem = file_path.stem # filename without extension + backup_path = file_path.parent / f"{stem}_{timestamp}.parquet.bak" + + # Copy file to backup + import shutil + shutil.copy2(file_path, backup_path) + + return backup_path + + +def filter_theme_tags(theme_tags: Any, themes_to_strip: Set[str]) -> List[str]: + """ + Remove specific themes from a themeTags value (handles multiple formats). + + Args: + theme_tags: Can be numpy array, list, or string + themes_to_strip: Set of theme IDs to remove (case-insensitive matching) + + Returns: + Filtered list of theme tags + + Note: + Matches themes case-insensitively for robustness. + """ + # Convert to list if needed + if isinstance(theme_tags, np.ndarray): + tags_list = theme_tags.tolist() + elif isinstance(theme_tags, list): + tags_list = theme_tags + elif isinstance(theme_tags, str): + # Handle string formats (comma or pipe separated) + if '|' in theme_tags: + tags_list = [t.strip() for t in theme_tags.split('|') if t.strip()] + elif ',' in theme_tags: + tags_list = [t.strip() for t in theme_tags.split(',') if t.strip()] + else: + tags_list = [theme_tags] if theme_tags else [] + else: + tags_list = [] + + # Normalize themes to strip (lowercase for case-insensitive matching) + normalized_strip_set = {theme.lower() for theme in themes_to_strip} + + # Filter themes + filtered = [tag for tag in tags_list if str(tag).lower() not in normalized_strip_set] + + return filtered + + +def update_parquet_theme_tags(df: pd.DataFrame, themes_to_strip: Set[str]) -> pd.DataFrame: + """ + Process entire dataframe to remove stripped themes from themeTags column. + + Args: + df: DataFrame with themeTags column + themes_to_strip: Set of theme IDs to remove + + Returns: + Modified DataFrame (in-place modification + return for convenience) + + Note: + Modifies df in-place and also returns it. + """ + if 'themeTags' not in df.columns: + print("Warning: themeTags column not found in dataframe") + return df + + # Apply filtering to each row + df['themeTags'] = df['themeTags'].apply( + lambda tags: filter_theme_tags(tags, themes_to_strip) + ) + + return df + + +def strip_parquet_themes( + parquet_path: Path, + themes_to_strip: Set[str], + backup: bool = True +) -> Dict[str, Any]: + """ + Strip low-card themes from parquet file's themeTags column. + + Args: + parquet_path: Path to parquet file + themes_to_strip: Set of theme IDs to remove + backup: Whether to create timestamped backup before modification + + Returns: + Dictionary with stripping results: + - "cards_processed": Total number of cards + - "cards_modified": Number of cards with tags removed + - "tags_removed": Total number of tag removals + - "backup_created": Backup file path (if backup=True) + - "errors": List of error messages + + Example: + results = strip_parquet_themes( + Path("card_files/processed/all_cards.parquet"), + {"fateseal", "gravestorm"}, + backup=True + ) + """ + if not parquet_path.exists(): + raise FileNotFoundError(f"Parquet file does not exist: {parquet_path}") + + results = { + "cards_processed": 0, + "cards_modified": 0, + "tags_removed": 0, + "backup_created": None, + "errors": [] + } + + try: + # Load parquet + df = pd.read_parquet(parquet_path, engine='pyarrow') + results["cards_processed"] = len(df) + + # Create backup before modification + if backup: + try: + backup_path = backup_parquet_file(parquet_path) + results["backup_created"] = str(backup_path) + print(f"Created backup: {backup_path}") + except Exception as e: + results["errors"].append(f"Backup failed: {e}") + # Continue anyway - modification is important + + # Track modifications + if 'themeTags' in df.columns: + # Count tags before stripping + tags_before = sum( + len(tags) if isinstance(tags, (list, np.ndarray)) else 0 + for tags in df['themeTags'] + ) + + # Apply filtering + update_parquet_theme_tags(df, themes_to_strip) + + # Count tags after stripping + tags_after = sum( + len(tags) if isinstance(tags, list) else 0 + for tags in df['themeTags'] + ) + + results["tags_removed"] = tags_before - tags_after + + # Count cards with modifications (cards that had at least one tag removed) + # This is approximate: tags_removed / ~avg_tags_per_card + if results["tags_removed"] > 0: + results["cards_modified"] = results["tags_removed"] # Conservative estimate + + print(f"Stripped {results['tags_removed']} tag occurrences from {results['cards_processed']} cards") + else: + results["errors"].append("themeTags column not found in parquet file") + return results + + # Write modified parquet back + df.to_parquet(parquet_path, engine='pyarrow', index=False) + print(f"Updated {parquet_path}") + + except Exception as e: + results["errors"].append(f"Error processing parquet: {e}") + + return results diff --git a/config/themes/theme_list.json b/config/themes/theme_list.json index 98c60a0..41765b8 100644 --- a/config/themes/theme_list.json +++ b/config/themes/theme_list.json @@ -1,5 +1,14 @@ { "themes": [ + { + "id": "blood-counters", + "theme": " Blood Counters", + "synergies": [], + "primary_color": "Black", + "secondary_color": "Blue", + "popularity_bucket": "Rare", + "description": "Uses Blood tokens to loot, set up graveyard recursion, and trigger discard/madness payoffs." + }, { "id": "plus1-plus1-counters", "theme": "+1/+1 Counters", @@ -83,18 +92,18 @@ "Infect", "Proliferate", "Counters Matter", + "Blight", "Poison Counters", + "Scarecrow Kindred", "Planeswalkers", "Superfriends", "Midrange", + "Toxic", "Phyrexian Kindred", "Ore Counters", - "Advisor Kindred", - "Horror Kindred", - "+1/+1 Counters", - "Insect Kindred", - "Burn", - "Elemental Kindred" + "Fungus Kindred", + "Treefolk Kindred", + "Kithkin Kindred" ], "primary_color": "Black", "secondary_color": "Green", @@ -129,7 +138,7 @@ "+1/+1 Counters", "Counters Matter", "Voltron", - "Human Kindred" + "Spells Matter" ], "primary_color": "Black", "secondary_color": "Blue", @@ -199,9 +208,9 @@ "id": "addendum", "theme": "Addendum", "synergies": [ - "Interaction", "Spells Matter", - "Spellslinger" + "Spellslinger", + "Interaction" ], "primary_color": "White", "secondary_color": "Blue", @@ -232,11 +241,11 @@ "id": "advisor-kindred", "theme": "Advisor Kindred", "synergies": [ - "-1/-1 Counters", - "Conditional Draw", + "Mutant Kindred", "Human Kindred", - "Midrange", - "Toughness Matters" + "Infect", + "Conditional Draw", + "-1/-1 Counters" ], "primary_color": "Blue", "secondary_color": "White", @@ -313,9 +322,9 @@ "synergies": [ "Cost Reduction", "X Spells", - "Artifacts Matter", - "Big Mana", - "Flying" + "Golem Kindred", + "Land Types Matter", + "Artifacts Matter" ], "primary_color": "Blue", "secondary_color": "White", @@ -417,10 +426,11 @@ "id": "aftermath", "theme": "Aftermath", "synergies": [ - "Mill", - "Big Mana", "Spells Matter", - "Spellslinger" + "Spellslinger", + "Big Mana", + "Interaction", + "Burn" ], "primary_color": "Black", "secondary_color": "Blue", @@ -453,9 +463,9 @@ "synergies": [ "Cumulative upkeep", "Storage Counters", + "Page Counters", "Counters Matter", - "Enchantments Matter", - "Lands Matter" + "Enchantments Matter" ], "primary_color": "Blue", "secondary_color": "Green", @@ -490,7 +500,7 @@ "Voltron", "+1/+1 Counters", "Auras", - "Enchant" + "Equipment Matters" ], "primary_color": "Green", "secondary_color": "White", @@ -519,10 +529,31 @@ "popularity_bucket": "Very Common", "description": "Applies early pressure and combat tempo to close the game before slower value engines stabilize. Synergies like Combat Matters and Voltron reinforce the plan." }, + { + "id": "airbend", + "theme": "Airbend", + "synergies": [ + "Airbending", + "Bending", + "Ally Kindred", + "Exile Matters", + "Leave the Battlefield" + ], + "primary_color": "White", + "secondary_color": "Blue", + "popularity_bucket": "Rare", + "description": "Builds around Airbend leveraging synergies with Airbending and Bending." + }, { "id": "airbending", "theme": "Airbending", - "synergies": [], + "synergies": [ + "Airbend", + "Bending", + "Ally Kindred", + "Exile Matters", + "Leave the Battlefield" + ], "primary_color": "White", "example_commanders": [ "Appa, Steadfast Guardian", @@ -544,11 +575,11 @@ "id": "alien-kindred", "theme": "Alien Kindred", "synergies": [ - "Clones", - "Horror Kindred", - "Exile Matters", - "Trample", - "Soldier Kindred" + "Suspend", + "Lore Counters", + "Ore Counters", + "Sagas Matter", + "Horror Kindred" ], "primary_color": "Blue", "secondary_color": "Green", @@ -586,9 +617,9 @@ "synergies": [ "Druid Kindred", "Elf Kindred", - "Little Fellas", - "Aggro", - "Combat Matters" + "+1/+1 Counters", + "Token Creation", + "Tokens Matter" ], "primary_color": "Green", "secondary_color": "White", @@ -622,9 +653,9 @@ "synergies": [ "Rally", "Cohort", - "Earthbending", - "Bending", - "Kor Kindred" + "Airbending", + "Waterbending", + "Earthbend" ], "primary_color": "White", "secondary_color": "Green", @@ -728,11 +759,11 @@ "id": "angel-kindred", "theme": "Angel Kindred", "synergies": [ - "Vigilance", "Lifegain", "Life Matters", + "Foretell", "Flying", - "Protection" + "Vigilance" ], "primary_color": "White", "secondary_color": "Black", @@ -767,7 +798,13 @@ { "id": "annihilator", "theme": "Annihilator", - "synergies": [], + "synergies": [ + "Eldrazi Kindred", + "Sacrifice Matters", + "Aristocrats", + "Big Mana", + "Aggro" + ], "primary_color": "Blue", "secondary_color": "Red", "example_commanders": [ @@ -821,10 +858,10 @@ "theme": "Ape Kindred", "synergies": [ "Removal", + "Toughness Matters", "Artifacts Matter", "Big Mana", - "Toughness Matters", - "Interaction" + "+1/+1 Counters" ], "primary_color": "Green", "secondary_color": "Black", @@ -902,9 +939,9 @@ "synergies": [ "Flying", "Big Mana", + "Stax", "Blink", - "Enter the Battlefield", - "Leave the Battlefield" + "Enter the Battlefield" ], "primary_color": "White", "secondary_color": "Black", @@ -1022,11 +1059,11 @@ "id": "artifact-tokens", "theme": "Artifact Tokens", "synergies": [ - "Treasure", "Servo Kindred", - "Powerstone Token", "Fabricate", - "Junk Token" + "Junk Token", + "Junk Tokens", + "Map Token" ], "primary_color": "Black", "secondary_color": "White", @@ -1098,9 +1135,9 @@ "synergies": [ "Fabricate", "Servo Kindred", - "Thopter Kindred", "Powerstone Token", - "Vedalken Kindred" + "Improvise", + "Thopter Kindred" ], "primary_color": "Blue", "secondary_color": "White", @@ -1136,9 +1173,9 @@ "synergies": [ "Creature Tokens", "Token Creation", + "Card Draw", "Tokens Matter", - "Little Fellas", - "Human Kindred" + "Little Fellas" ], "primary_color": "Blue", "secondary_color": "White", @@ -1174,8 +1211,8 @@ "Freerunning", "Outlaw Kindred", "Deathtouch", - "Cost Reduction", - "Vampire Kindred" + "Vampire Kindred", + "Menace" ], "primary_color": "Black", "secondary_color": "Blue", @@ -1210,7 +1247,11 @@ { "id": "assembly-worker-kindred", "theme": "Assembly-Worker Kindred", - "synergies": [], + "synergies": [ + "Artifacts Matter", + "Little Fellas", + "Big Mana" + ], "primary_color": "White", "example_cards": [ "Academy Manufactor", @@ -1267,10 +1308,10 @@ "theme": "Astartes Kindred", "synergies": [ "Warrior Kindred", - "Blink", - "Enter the Battlefield", - "Leave the Battlefield", - "Big Mana" + "Exile Matters", + "Creature Tokens", + "Token Creation", + "Tokens Matter" ], "primary_color": "White", "secondary_color": "Black", @@ -1341,8 +1382,8 @@ "Constellation", "Voltron", "Enchantments Matter", - "Enchant", - "Bestow" + "Bestow", + "Umbra armor" ], "primary_color": "White", "secondary_color": "Blue", @@ -1392,9 +1433,9 @@ "synergies": [ "Impending", "Time Counters", - "Politics", - "Horror Kindred", - "Cost Reduction" + "Bending", + "Ally Kindred", + "Horror Kindred" ], "primary_color": "White", "secondary_color": "Black", @@ -1461,7 +1502,12 @@ { "id": "azra-kindred", "theme": "Azra Kindred", - "synergies": [], + "synergies": [ + "Warrior Kindred", + "Aggro", + "Combat Matters", + "Little Fellas" + ], "primary_color": "Black", "secondary_color": "Red", "example_commanders": [ @@ -1488,8 +1534,8 @@ "Choose a background", "Treasure", "Treasure Token", - "Dragon Kindred", - "Enchantments Matter" + "Enchantments Matter", + "Dragon Kindred" ], "primary_color": "Blue", "secondary_color": "Black", @@ -1558,8 +1604,11 @@ "id": "badger-kindred", "theme": "Badger Kindred", "synergies": [ + "Lands Matter", "Aggro", - "Combat Matters" + "Combat Matters", + "Big Mana", + "Little Fellas" ], "primary_color": "Green", "example_commanders": [ @@ -1594,7 +1643,7 @@ "Soldier Kindred", "Human Kindred", "Little Fellas", - "Flying" + "Toughness Matters" ], "primary_color": "White", "secondary_color": "Green", @@ -1626,11 +1675,11 @@ "id": "barbarian-kindred", "theme": "Barbarian Kindred", "synergies": [ + "First strike", "Haste", "Human Kindred", "Discard Matters", - "Blink", - "Enter the Battlefield" + "Blink" ], "primary_color": "Red", "secondary_color": "Black", @@ -1666,11 +1715,11 @@ "id": "bard-kindred", "theme": "Bard Kindred", "synergies": [ - "Toughness Matters", - "Little Fellas", "Human Kindred", - "Blink", - "Enter the Battlefield" + "Toughness Matters", + "Ramp", + "Little Fellas", + "Token Creation" ], "primary_color": "Green", "secondary_color": "White", @@ -1706,10 +1755,10 @@ "id": "bargain", "theme": "Bargain", "synergies": [ - "Burn", "Blink", "Enter the Battlefield", "Leave the Battlefield", + "Burn", "Spells Matter" ], "primary_color": "Black", @@ -1744,9 +1793,9 @@ "theme": "Basic landcycling", "synergies": [ "Landcycling", + "Typecycling", "Cycling", "Loot", - "Ramp", "Discard Matters" ], "primary_color": "Green", @@ -1884,6 +1933,9 @@ "id": "battle-cry", "theme": "Battle Cry", "synergies": [ + "Creature Tokens", + "Token Creation", + "Tokens Matter", "Aggro", "Combat Matters" ], @@ -1918,10 +1970,10 @@ "theme": "Battles Matter", "synergies": [ "Transform", - "Card Draw", - "Burn", "Flying", - "Enchantments Matter" + "Card Draw", + "Stax", + "Token Creation" ], "primary_color": "Green", "secondary_color": "Black", @@ -1954,9 +2006,9 @@ "synergies": [ "Druid Kindred", "Trample", + "Spirit Kindred", "Creature Tokens", - "Token Creation", - "Tokens Matter" + "Token Creation" ], "primary_color": "Green", "secondary_color": "Black", @@ -1992,11 +2044,11 @@ "id": "beast-kindred", "theme": "Beast Kindred", "synergies": [ + "Sloth Kindred", "Mutate", "Goat Kindred", "Boar Kindred", - "Morph", - "Frog Kindred" + "Saddle" ], "primary_color": "Green", "secondary_color": "Blue", @@ -2055,8 +2107,10 @@ "theme": "Behold", "synergies": [ "Dragon Kindred", - "Spells Matter", - "Spellslinger" + "Blink", + "Enter the Battlefield", + "Leave the Battlefield", + "Spells Matter" ], "primary_color": "Black", "secondary_color": "Blue", @@ -2083,7 +2137,9 @@ { "id": "beholder-kindred", "theme": "Beholder Kindred", - "synergies": [], + "synergies": [ + "Big Mana" + ], "primary_color": "Black", "secondary_color": "Red", "example_commanders": [ @@ -2108,9 +2164,9 @@ "synergies": [ "Earthbending", "Waterbending", - "Firebending", - "Ally Kindred", - "Landfall" + "Airbending", + "Earthbend", + "Waterbend" ], "primary_color": "Blue", "secondary_color": "Green", @@ -2186,8 +2242,8 @@ "Nymph Kindred", "Equipment Matters", "Auras", - "Artifacts Matter", - "Enchantments Matter" + "Enchantments Matter", + "Spirit Kindred" ], "primary_color": "White", "secondary_color": "Green", @@ -2222,8 +2278,8 @@ "Cost Reduction", "Convoke", "Affinity", - "Delve", - "Leviathan Kindred" + "Discover", + "Delve" ], "primary_color": "Green", "secondary_color": "Blue", @@ -2259,10 +2315,10 @@ "theme": "Bird Kindred", "synergies": [ "Flying", - "Soldier Kindred", - "Protection", "Morph", - "Protection from Color" + "Protection", + "Soldier Kindred", + "Scout Kindred" ], "primary_color": "White", "secondary_color": "Blue", @@ -2306,6 +2362,21 @@ "popularity_bucket": "Rare", "description": "Accumulates blaze counters to unlock scaling payoffs, removal triggers, or delayed value conversions." }, + { + "id": "blight", + "theme": "Blight", + "synergies": [ + "-1/-1 Counters", + "Warlock Kindred", + "Goblin Kindred", + "Outlaw Kindred", + "Counters Matter" + ], + "primary_color": "Black", + "secondary_color": "Red", + "popularity_bucket": "Rare", + "description": "Builds around Blight leveraging synergies with -1/-1 Counters and Warlock Kindred." + }, { "id": "blink", "theme": "Blink", @@ -2313,8 +2384,8 @@ "Enter the Battlefield", "Flicker", "Token Creation", - "Exploit", - "Offspring" + "Persist", + "Exploit" ], "primary_color": "Black", "secondary_color": "White", @@ -2348,8 +2419,8 @@ "Midrange", "Warrior Kindred", "Sacrifice Matters", - "Conditional Draw", - "Aristocrats" + "Aristocrats", + "Unconditional Draw" ], "primary_color": "Black", "secondary_color": "Green", @@ -2452,8 +2523,8 @@ "Blood Token", "+1/+1 Counters", "Burn", - "Warrior Kindred", - "Counters Matter" + "Counters Matter", + "Warrior Kindred" ], "primary_color": "Green", "secondary_color": "Black", @@ -2487,9 +2558,9 @@ "synergies": [ "Beast Kindred", "Trample", + "Haste", "Artifact Tokens", - "Token Creation", - "Tokens Matter" + "Token Creation" ], "primary_color": "Green", "secondary_color": "Black", @@ -2524,8 +2595,8 @@ "id": "board-wipes", "theme": "Board Wipes", "synergies": [ - "Pingers", "Interaction", + "Pingers", "Lore Counters", "Sagas Matter", "Ore Counters" @@ -2637,7 +2708,9 @@ { "id": "bounty-counters", "theme": "Bounty Counters", - "synergies": [], + "synergies": [ + "Counters Matter" + ], "primary_color": "Black", "example_commanders": [ "Shay Cormac", @@ -2655,6 +2728,20 @@ "popularity_bucket": "Rare", "description": "Accumulates bounty counters to unlock scaling payoffs, removal triggers, or delayed value conversions." }, + { + "id": "bringer-kindred", + "theme": "Bringer Kindred", + "synergies": [ + "Trample", + "Big Mana", + "Aggro", + "Combat Matters" + ], + "primary_color": "Black", + "secondary_color": "Blue", + "popularity_bucket": "Rare", + "description": "Focuses on getting a high number of Bringer creatures into play with shared payoffs (e.g., Trample and Big Mana)." + }, { "id": "brushwagg-kindred", "theme": "Brushwagg Kindred", @@ -2674,10 +2761,10 @@ "theme": "Burn", "synergies": [ "Pingers", - "Bloodthirst", "Wither", - "Afflict", - "Extort" + "Bloodthirst", + "Extort", + "Spectacle" ], "primary_color": "Black", "secondary_color": "Red", @@ -2753,8 +2840,8 @@ "Spells Matter", "Spellslinger", "Control", - "Lands Matter", - "Removal" + "Removal", + "Lands Matter" ], "primary_color": "Black", "secondary_color": "Blue", @@ -2808,7 +2895,9 @@ { "id": "camel-kindred", "theme": "Camel Kindred", - "synergies": [], + "synergies": [ + "Little Fellas" + ], "primary_color": "White", "secondary_color": "Black", "example_cards": [ @@ -2829,10 +2918,10 @@ "theme": "Cantrips", "synergies": [ "Clue Token", - "Investigate", "Unconditional Draw", + "Investigate", "Sacrifice to Draw", - "Gates Matter" + "Gift" ], "primary_color": "Blue", "secondary_color": "White", @@ -2886,16 +2975,16 @@ "Conditional Draw", "Draw Triggers", "Cantrips", - "Cycling", "Sacrifice to Draw", - "Connive", + "Cycling", + "Typecycling", "Landcycling", - "Learn", + "Connive", "Hellbent", - "Blitz", - "Dredge", "Basic landcycling", - "Plainscycling" + "Learn", + "Dredge", + "Life to Draw" ], "primary_color": "Blue", "secondary_color": "Black", @@ -2932,8 +3021,8 @@ "Explore", "Map Token", "Scout Kindred", - "Pirate Kindred", - "Merfolk Kindred" + "Merfolk Kindred", + "Pirate Kindred" ], "primary_color": "Green", "secondary_color": "Black", @@ -3027,6 +3116,7 @@ "id": "cases-matter", "theme": "Cases Matter", "synergies": [ + "Solved", "Enchantments Matter" ], "primary_color": "White", @@ -3089,10 +3179,10 @@ "theme": "Cat Kindred", "synergies": [ "Forestwalk", - "Vigilance", - "Energy Counters", - "Energy", - "Resource Engine" + "Mutate", + "Max speed", + "Start your engines!", + "Nightmare Kindred" ], "primary_color": "White", "secondary_color": "Green", @@ -3130,9 +3220,9 @@ "synergies": [ "Discover", "Land Types Matter", - "Transform", "Lands Matter", - "Exile Matters" + "Exile Matters", + "Ramp" ], "primary_color": "Green", "secondary_color": "Black", @@ -3164,7 +3254,9 @@ "id": "celebration", "theme": "Celebration", "synergies": [ - "Little Fellas" + "Little Fellas", + "Aggro", + "Combat Matters" ], "primary_color": "White", "secondary_color": "Red", @@ -3193,10 +3285,10 @@ "theme": "Centaur Kindred", "synergies": [ "Archer Kindred", - "Scout Kindred", + "Threshold", "Druid Kindred", "Shaman Kindred", - "Warrior Kindred" + "Scout Kindred" ], "primary_color": "Green", "secondary_color": "White", @@ -3301,7 +3393,7 @@ "Cost Reduction", "X Spells", "Lands Matter", - "Artifacts Matter" + "Enchantments Matter" ], "primary_color": "Green", "secondary_color": "Blue", @@ -3336,10 +3428,10 @@ "theme": "Charge Counters", "synergies": [ "Station", + "Sunburst", "Mana Rock", "Counters Matter", - "Artifacts Matter", - "Ramp" + "Artifacts Matter" ], "primary_color": "Blue", "secondary_color": "White", @@ -3383,7 +3475,11 @@ "id": "chimera-kindred", "theme": "Chimera Kindred", "synergies": [ - "Big Mana" + "Flying", + "Topdeck", + "Counters Matter", + "Artifacts Matter", + "Toughness Matters" ], "primary_color": "Blue", "secondary_color": "Green", @@ -3411,9 +3507,9 @@ "theme": "Choose a background", "synergies": [ "Backgrounds Matter", - "Elf Kindred", - "Cleric Kindred", "Enchantments Matter", + "Cleric Kindred", + "Elf Kindred", "Artifact Tokens" ], "primary_color": "Blue", @@ -3464,10 +3560,10 @@ "id": "cipher", "theme": "Cipher", "synergies": [ - "Aggro", - "Combat Matters", "Spells Matter", - "Spellslinger" + "Spellslinger", + "Aggro", + "Combat Matters" ], "primary_color": "Blue", "secondary_color": "Black", @@ -3499,10 +3595,10 @@ "theme": "Citizen Kindred", "synergies": [ "Halfling Kindred", + "Shield Counters", "Food Token", "Food", - "Treasure", - "Treasure Token" + "Hero Kindred" ], "primary_color": "Green", "secondary_color": "White", @@ -3540,8 +3636,8 @@ "Warrior Kindred", "Control", "Removal", - "Stax", - "Spells Matter" + "Spells Matter", + "Spellslinger" ], "primary_color": "Blue", "secondary_color": "Green", @@ -3604,9 +3700,9 @@ "synergies": [ "Lifegain", "Life Matters", - "Fox Kindred", + "Kor Kindred", "Lifegain Triggers", - "Kor Kindred" + "Choose a background" ], "primary_color": "White", "secondary_color": "Black", @@ -3640,7 +3736,9 @@ "theme": "Cloak", "synergies": [ "Ward", - "Protective Effects" + "Protective Effects", + "Spells Matter", + "Spellslinger" ], "primary_color": "Blue", "secondary_color": "White", @@ -3673,9 +3771,9 @@ "theme": "Clones", "synergies": [ "Myriad", - "Populate", "Embalm", "Eternalize", + "Populate", "Shapeshifter Kindred" ], "primary_color": "Blue", @@ -3821,9 +3919,9 @@ "synergies": [ "Detective Kindred", "Mill", + "Flying", "Big Mana", - "Toughness Matters", - "Little Fellas" + "Toughness Matters" ], "primary_color": "Green", "secondary_color": "Blue", @@ -3859,7 +3957,7 @@ "Voltron", "+1/+1 Counters", "Auras", - "Enchant" + "Equipment Matters" ], "primary_color": "Green", "secondary_color": "White", @@ -3925,10 +4023,26 @@ "popularity_bucket": "Very Common", "description": "Builds around Combat Tricks leveraging synergies with Flash and Strive." }, + { + "id": "companion", + "theme": "Companion", + "synergies": [ + "Big Mana" + ], + "primary_color": "White", + "secondary_color": "Black", + "popularity_bucket": "Rare", + "description": "Builds around Companion leveraging synergies with Big Mana." + }, { "id": "compleated", "theme": "Compleated", - "synergies": [], + "synergies": [ + "Loyalty Counters", + "Planeswalkers", + "Superfriends", + "Counters Matter" + ], "primary_color": "Black", "secondary_color": "Blue", "example_cards": [ @@ -3988,10 +4102,10 @@ "theme": "Connive", "synergies": [ "Loot", - "Rogue Kindred", "Discard Matters", + "Rogue Kindred", "Outlaw Kindred", - "+1/+1 Counters" + "Reanimate" ], "primary_color": "Blue", "secondary_color": "Black", @@ -4063,8 +4177,8 @@ "Nymph Kindred", "Enchantments Matter", "Toughness Matters", - "Little Fellas", - "Reanimate" + "Human Kindred", + "Little Fellas" ], "primary_color": "Green", "secondary_color": "White", @@ -4100,9 +4214,9 @@ "synergies": [ "Prototype", "Unearth", - "Artifacts Matter", "Artifact Tokens", - "Scry" + "Artifacts Matter", + "Phyrexian Kindred" ], "primary_color": "Blue", "secondary_color": "White", @@ -4133,9 +4247,9 @@ "id": "control", "theme": "Control", "synergies": [ + "Council's dilemma", "Daybound", "Nightbound", - "Council's dilemma", "Soulshift", "Counterspells" ], @@ -4170,9 +4284,11 @@ "id": "converge", "theme": "Converge", "synergies": [ + "+1/+1 Counters", + "Counters Matter", "Spells Matter", "Spellslinger", - "Big Mana" + "Voltron" ], "primary_color": "Blue", "secondary_color": "Green", @@ -4203,11 +4319,11 @@ "id": "convert", "theme": "Convert", "synergies": [ - "Living metal", "More Than Meets the Eye", + "Living metal", "Eye Kindred", "Robot Kindred", - "Vehicles" + "Artifacts Matter" ], "primary_color": "Black", "secondary_color": "White", @@ -4239,10 +4355,10 @@ "theme": "Convoke", "synergies": [ "Big Mana", + "Vigilance", "Knight Kindred", - "Toolbox", - "Combat Tricks", - "Removal" + "Elemental Kindred", + "Combat Tricks" ], "primary_color": "White", "secondary_color": "Green", @@ -4298,7 +4414,7 @@ "Infect", "Phyrexian Kindred", "Counters Matter", - "Mill" + "Lifegain" ], "primary_color": "Black", "secondary_color": "Green", @@ -4331,10 +4447,10 @@ "theme": "Cost Reduction", "synergies": [ "Affinity", - "Freerunning", "Waterbending", + "Freerunning", "Undaunted", - "X Spells" + "Waterbend" ], "primary_color": "Blue", "secondary_color": "Green", @@ -4512,7 +4628,7 @@ "Blink", "Enter the Battlefield", "Leave the Battlefield", - "Little Fellas" + "Aggro" ], "primary_color": "White", "secondary_color": "Green", @@ -4544,7 +4660,10 @@ { "id": "coward-kindred", "theme": "Coward Kindred", - "synergies": [], + "synergies": [ + "Ward", + "Protective Effects" + ], "primary_color": "Red", "secondary_color": "Black", "example_commanders": [ @@ -4576,11 +4695,11 @@ "id": "crab-kindred", "theme": "Crab Kindred", "synergies": [ + "Flash", "Toughness Matters", "Protective Effects", - "Mill", - "Blink", - "Enter the Battlefield" + "Stax", + "+1/+1 Counters" ], "primary_color": "Blue", "secondary_color": "Black", @@ -4614,9 +4733,9 @@ "theme": "Craft", "synergies": [ "Graveyard Matters", - "Golem Kindred", "Transform", "Exile Matters", + "Mill", "Artifacts Matter" ], "primary_color": "Blue", @@ -4651,8 +4770,8 @@ "Token Creation", "Populate", "Tokens Matter", - "For Mirrodin!", - "Endure" + "Germ Kindred", + "For Mirrodin!" ], "primary_color": "White", "secondary_color": "Green", @@ -4688,10 +4807,10 @@ "theme": "Crew", "synergies": [ "Vehicles", - "Artifacts Matter", + "Exhaust", "Treasure Token", - "Trample", - "Vigilance" + "Artifacts Matter", + "Treasure" ], "primary_color": "Blue", "secondary_color": "White", @@ -4725,11 +4844,11 @@ "id": "crocodile-kindred", "theme": "Crocodile Kindred", "synergies": [ + "Zombie Kindred", + "Protective Effects", "Counters Matter", "+1/+1 Counters", - "Toughness Matters", - "Voltron", - "Big Mana" + "Toughness Matters" ], "primary_color": "Black", "secondary_color": "Green", @@ -4758,6 +4877,15 @@ "popularity_bucket": "Rare", "description": "Focuses on getting a high number of Crocodile creatures into play with shared payoffs (e.g., Counters Matter and +1/+1 Counters)." }, + { + "id": "crystal-counters", + "theme": "Crystal Counters", + "synergies": [], + "primary_color": "Black", + "secondary_color": "Blue", + "popularity_bucket": "Rare", + "description": "Accumulates crystal counters to unlock scaling payoffs, removal triggers, or delayed value conversions." + }, { "id": "cumulative-upkeep", "theme": "Cumulative upkeep", @@ -4766,7 +4894,7 @@ "Counters Matter", "Enchantments Matter", "Trample", - "Stax" + "Enchant" ], "primary_color": "Blue", "secondary_color": "Green", @@ -4809,7 +4937,9 @@ { "id": "cyberman-kindred", "theme": "Cyberman Kindred", - "synergies": [], + "synergies": [ + "Artifacts Matter" + ], "primary_color": "Black", "secondary_color": "Blue", "example_commanders": [ @@ -4834,11 +4964,11 @@ "id": "cycling", "theme": "Cycling", "synergies": [ + "Typecycling", "Landcycling", "Basic landcycling", "Plainscycling", - "Mountaincycling", - "Forestcycling" + "Mountaincycling" ], "primary_color": "Blue", "secondary_color": "White", @@ -4866,11 +4996,11 @@ "id": "cyclops-kindred", "theme": "Cyclops Kindred", "synergies": [ + "Trample", + "Warrior Kindred", "Big Mana", - "Blink", - "Enter the Battlefield", - "Leave the Battlefield", - "Aggro" + "Toughness Matters", + "Blink" ], "primary_color": "Red", "secondary_color": "Black", @@ -4904,7 +5034,9 @@ { "id": "dalek-kindred", "theme": "Dalek Kindred", - "synergies": [], + "synergies": [ + "Artifacts Matter" + ], "primary_color": "Black", "example_commanders": [ "The Dalek Emperor", @@ -4926,8 +5058,8 @@ "id": "dash", "theme": "Dash", "synergies": [ - "Orc Kindred", "Berserker Kindred", + "Orc Kindred", "Warrior Kindred", "Human Kindred", "Aggro" @@ -4995,9 +5127,9 @@ "synergies": [ "Nightbound", "Werewolf Kindred", + "Wolf Kindred", "Control", - "Stax", - "Human Kindred" + "Stax" ], "primary_color": "Green", "secondary_color": "Black", @@ -5025,8 +5157,8 @@ "id": "deathtouch", "theme": "Deathtouch", "synergies": [ - "Basilisk Kindred", "Scorpion Kindred", + "Basilisk Kindred", "Gorgon Kindred", "Assassin Kindred", "Snake Kindred" @@ -5065,8 +5197,8 @@ "Wall Kindred", "Egg Kindred", "Plant Kindred", - "Toughness Matters", - "Illusion Kindred" + "Gargoyle Kindred", + "Toughness Matters" ], "primary_color": "Blue", "secondary_color": "White", @@ -5129,8 +5261,8 @@ "Reanimate", "Mill", "Horror Kindred", - "Blink", - "Enter the Battlefield" + "Trample", + "Blink" ], "primary_color": "Green", "secondary_color": "Black", @@ -5167,9 +5299,9 @@ "synergies": [ "Mill", "Big Mana", + "Reanimate", "Spells Matter", - "Spellslinger", - "Flying" + "Spellslinger" ], "primary_color": "Black", "secondary_color": "Blue", @@ -5239,11 +5371,11 @@ "id": "demon-kindred", "theme": "Demon Kindred", "synergies": [ + "Elder Kindred", "Ogre Kindred", "Trample", - "Flying", "Sacrifice Matters", - "Aristocrats" + "Flying" ], "primary_color": "Black", "secondary_color": "Red", @@ -5341,8 +5473,11 @@ "id": "descend", "theme": "Descend", "synergies": [ + "Spirit Kindred", "Reanimate", - "Mill" + "Mill", + "Card Draw", + "Blink" ], "primary_color": "Blue", "secondary_color": "Black", @@ -5389,8 +5524,8 @@ "Land Types Matter", "Lands Matter", "Cycling", - "Loot", - "Ramp" + "Pingers", + "Loot" ], "primary_color": "Green", "secondary_color": "White", @@ -5427,7 +5562,8 @@ "Stax", "Blink", "Enter the Battlefield", - "Leave the Battlefield" + "Leave the Battlefield", + "Human Kindred" ], "primary_color": "White", "secondary_color": "Blue", @@ -5499,8 +5635,8 @@ "+1/+1 Counters", "Counters Matter", "Voltron", - "Aggro", - "Combat Matters" + "Human Kindred", + "Aggro" ], "primary_color": "Blue", "secondary_color": "Green", @@ -5531,9 +5667,9 @@ "synergies": [ "Pingers", "Haste", - "Conditional Draw", "Sacrifice Matters", - "Aristocrats" + "Aristocrats", + "Burn" ], "primary_color": "Red", "secondary_color": "Black", @@ -5571,8 +5707,8 @@ "synergies": [ "Ingest", "Processor Kindred", - "Scion Kindred", "Drone Kindred", + "Scion Kindred", "Eldrazi Kindred" ], "primary_color": "Blue", @@ -5649,9 +5785,9 @@ "synergies": [ "Enrage", "Elder Kindred", - "Fight", - "Trample", - "Cycling" + "Mutate", + "Landcycling", + "Typecycling" ], "primary_color": "Green", "secondary_color": "White", @@ -5724,10 +5860,10 @@ "theme": "Discover", "synergies": [ "Caves Matter", - "Land Types Matter", "Exile Matters", "Topdeck", - "Lands Matter" + "Land Types Matter", + "Big Mana" ], "primary_color": "Green", "secondary_color": "Blue", @@ -5765,8 +5901,8 @@ "Ward", "Detective Kindred", "Protective Effects", - "Flying", - "+1/+1 Counters" + "Outlaw Kindred", + "Rogue Kindred" ], "primary_color": "Green", "secondary_color": "White", @@ -5803,8 +5939,8 @@ "Transform", "Spirit Kindred", "Enchant", - "Reanimate", - "Auras" + "Mill", + "Flying" ], "primary_color": "Blue", "secondary_color": "White", @@ -5878,8 +6014,8 @@ "Prowess", "Monk Kindred", "Flying", - "Toughness Matters", - "Big Mana" + "Wizard Kindred", + "Trample" ], "primary_color": "Blue", "secondary_color": "Black", @@ -5918,8 +6054,8 @@ "Doctor's Companion", "Doctor's companion", "Sagas Matter", - "Lore Counters", - "Ore Counters" + "Investigate", + "Time Counters" ], "primary_color": "White", "secondary_color": "Blue", @@ -5950,6 +6086,21 @@ "popularity_bucket": "Rare", "description": "Focuses on getting a high number of Doctor creatures into play with shared payoffs (e.g., Doctor's Companion and Doctor's companion)." }, + { + "id": "doctors-companion", + "theme": "Doctor's Companion", + "synergies": [ + "Doctor's companion", + "Doctor Kindred", + "Sagas Matter", + "Human Kindred", + "Little Fellas" + ], + "primary_color": "White", + "secondary_color": "Blue", + "popularity_bucket": "Rare", + "description": "Builds around Doctor's Companion leveraging synergies with Doctor Kindred and Sagas Matter." + }, { "id": "doctors-companion", "theme": "Doctor's companion", @@ -6033,9 +6184,9 @@ "synergies": [ "Lands Matter", "Ramp", - "Topdeck", - "Big Mana", - "Spells Matter" + "Cost Reduction", + "X Spells", + "Warrior Kindred" ], "primary_color": "Green", "secondary_color": "Black", @@ -6084,11 +6235,11 @@ "id": "double-strike", "theme": "Double strike", "synergies": [ + "Dwarf Kindred", + "Noble Kindred", "Knight Kindred", "Warrior Kindred", - "Aggro", - "Combat Matters", - "Trample" + "Cat Kindred" ], "primary_color": "White", "secondary_color": "Red", @@ -6124,8 +6275,8 @@ "id": "dragon-kindred", "theme": "Dragon Kindred", "synergies": [ - "Behold", "Elder Kindred", + "Behold", "Megamorph", "Flying", "Backgrounds Matter" @@ -6208,14 +6359,23 @@ "popularity_bucket": "Rare", "description": "Focuses on getting a high number of Dreadnought creatures into play with shared payoffs." }, + { + "id": "dream-counters", + "theme": "Dream Counters", + "synergies": [], + "primary_color": "Blue", + "secondary_color": "White", + "popularity_bucket": "Rare", + "description": "Accumulates dream counters to unlock scaling payoffs, removal triggers, or delayed value conversions." + }, { "id": "dredge", "theme": "Dredge", "synergies": [ "Unconditional Draw", "Reanimate", - "Card Draw", "Mill", + "Card Draw", "Spells Matter" ], "primary_color": "Black", @@ -6248,8 +6408,8 @@ "theme": "Drone Kindred", "synergies": [ "Ingest", - "Spawn Kindred", "Devoid", + "Spawn Kindred", "Scion Kindred", "Eldrazi Kindred" ], @@ -6280,8 +6440,8 @@ "Alliance", "Mana Dork", "Elf Kindred", - "Ramp", - "Bear Kindred" + "Treefolk Kindred", + "Squirrel Kindred" ], "primary_color": "Green", "secondary_color": "Black", @@ -6354,10 +6514,10 @@ "theme": "Dwarf Kindred", "synergies": [ "Servo Kindred", + "Pilot Kindred", "Berserker Kindred", "Artificer Kindred", - "Treasure", - "Treasure Token" + "Double strike" ], "primary_color": "White", "secondary_color": "Red", @@ -6389,14 +6549,29 @@ "description": "Focuses on getting a high number of Dwarf creatures into play with shared payoffs (e.g., Servo Kindred and Artificer Kindred)." }, { - "id": "earthbending", - "theme": "Earthbending", + "id": "earthbend", + "theme": "Earthbend", "synergies": [ + "Earthbending", "Bending", "Landfall", "Ally Kindred", - "Lands Matter", - "+1/+1 Counters" + "Lands Matter" + ], + "primary_color": "Green", + "secondary_color": "Red", + "popularity_bucket": "Niche", + "description": "Builds around Earthbend leveraging synergies with Earthbending and Bending." + }, + { + "id": "earthbending", + "theme": "Earthbending", + "synergies": [ + "Earthbend", + "Bending", + "Landfall", + "Ally Kindred", + "Lands Matter" ], "primary_color": "Green", "secondary_color": "Black", @@ -6474,8 +6649,8 @@ "Rooms Matter", "Enchantments Matter", "Toughness Matters", - "Human Kindred", - "Little Fellas" + "Flying", + "Human Kindred" ], "primary_color": "Blue", "secondary_color": "White", @@ -6505,6 +6680,8 @@ "synergies": [ "Flying", "Burn", + "Toughness Matters", + "Interaction", "Little Fellas" ], "primary_color": "Blue", @@ -6537,15 +6714,24 @@ "popularity_bucket": "Rare", "description": "Focuses on getting a high number of Efreet creatures into play with shared payoffs (e.g., Flying)." }, + { + "id": "egg-counters", + "theme": "Egg Counters", + "synergies": [], + "primary_color": "Black", + "secondary_color": "Green", + "popularity_bucket": "Rare", + "description": "Accumulates egg counters to unlock scaling payoffs, removal triggers, or delayed value conversions." + }, { "id": "egg-kindred", "theme": "Egg Kindred", "synergies": [ "Defender", + "Creature Tokens", "Sacrifice Matters", "Aristocrats", - "Toughness Matters", - "Little Fellas" + "Toughness Matters" ], "primary_color": "Blue", "secondary_color": "Green", @@ -6580,11 +6766,11 @@ "id": "elder-kindred", "theme": "Elder Kindred", "synergies": [ - "Dinosaur Kindred", "Dragon Kindred", - "Flying", - "Big Mana", - "Aggro" + "Dinosaur Kindred", + "Demon Kindred", + "Vigilance", + "Trample" ], "primary_color": "Black", "secondary_color": "Blue", @@ -6617,11 +6803,11 @@ "id": "eldrazi-kindred", "theme": "Eldrazi Kindred", "synergies": [ - "Ingest", + "Annihilator", "Processor Kindred", + "Ingest", "Spawn Kindred", - "Scion Kindred", - "Drone Kindred" + "Scion Kindred" ], "primary_color": "Blue", "secondary_color": "Green", @@ -6655,8 +6841,8 @@ "Evoke", "Awaken", "Incarnation Kindred", - "Wither", - "Fear" + "Vivid", + "Wither" ], "primary_color": "Green", "secondary_color": "Blue", @@ -6687,11 +6873,11 @@ "id": "elephant-kindred", "theme": "Elephant Kindred", "synergies": [ - "Trample", "Cleric Kindred", + "Trample", "Soldier Kindred", "Creature Tokens", - "Blink" + "Vigilance" ], "primary_color": "Green", "secondary_color": "White", @@ -6729,8 +6915,8 @@ "synergies": [ "Alliance", "Druid Kindred", - "Ranger Kindred", "Archer Kindred", + "Ranger Kindred", "Scout Kindred" ], "primary_color": "Green", @@ -6764,11 +6950,11 @@ "id": "elk-kindred", "theme": "Elk Kindred", "synergies": [ + "Elemental Kindred", + "Ramp", + "Protective Effects", "Lifegain", - "Life Matters", - "Blink", - "Enter the Battlefield", - "Leave the Battlefield" + "Life Matters" ], "primary_color": "Green", "secondary_color": "White", @@ -6868,6 +7054,15 @@ "popularity_bucket": "Rare", "description": "Builds around Emerge leveraging synergies with Eldrazi Kindred and Big Mana." }, + { + "id": "eminence", + "theme": "Eminence", + "synergies": [], + "primary_color": "Black", + "secondary_color": "White", + "popularity_bucket": "Rare", + "description": "Builds around the Eminence theme and its supporting synergies." + }, { "id": "employee-kindred", "theme": "Employee Kindred", @@ -6946,7 +7141,7 @@ "Inspired", "Hero Kindred", "Equipment Matters", - "Scry" + "Auras" ], "primary_color": "White", "secondary_color": "Blue", @@ -6982,8 +7177,8 @@ "Auras", "Constellation", "Card Draw", - "Enchant", - "Sagas Matter" + "Sagas Matter", + "Lore Counters" ], "primary_color": "White", "secondary_color": "Blue", @@ -7016,11 +7211,11 @@ "id": "encore", "theme": "Encore", "synergies": [ + "Incarnation Kindred", "Politics", "Pirate Kindred", "Outlaw Kindred", - "Mill", - "Aggro" + "Elemental Kindred" ], "primary_color": "Black", "secondary_color": "Blue", @@ -7053,8 +7248,8 @@ "theme": "Endure", "synergies": [ "Spirit Kindred", - "Creature Tokens", "Soldier Kindred", + "Creature Tokens", "+1/+1 Counters", "Token Creation" ], @@ -7127,7 +7322,7 @@ "Resource Engine", "Servo Kindred", "Vedalken Kindred", - "Artificer Kindred" + "Robot Kindred" ], "primary_color": "Blue", "secondary_color": "White", @@ -7195,8 +7390,10 @@ "theme": "Enrage", "synergies": [ "Dinosaur Kindred", + "+1/+1 Counters", "Big Mana", - "Toughness Matters" + "Counters Matter", + "Burn" ], "primary_color": "Green", "secondary_color": "White", @@ -7233,8 +7430,8 @@ "Blink", "Reanimate", "Token Creation", - "Exploit", - "Offspring" + "Persist", + "Exploit" ], "primary_color": "Black", "secondary_color": "White", @@ -7347,8 +7544,8 @@ "theme": "Equip", "synergies": [ "Job select", - "For Mirrodin!", "Living weapon", + "For Mirrodin!", "Equipment", "Germ Kindred" ], @@ -7375,11 +7572,11 @@ "id": "equipment", "theme": "Equipment", "synergies": [ - "Equip", "Job select", + "Living weapon", "Reconfigure", "For Mirrodin!", - "Living weapon" + "Equip" ], "primary_color": "White", "secondary_color": "Black", @@ -7409,9 +7606,9 @@ "synergies": [ "Equipment", "Equip", - "Role token", + "Germ Kindred", "Job select", - "Reconfigure" + "Living weapon" ], "primary_color": "White", "secondary_color": "Blue", @@ -7588,11 +7785,11 @@ "id": "evolve", "theme": "Evolve", "synergies": [ + "Mutant Kindred", "+1/+1 Counters", "Counters Matter", "Voltron", - "Toughness Matters", - "Aggro" + "Toughness Matters" ], "primary_color": "Green", "secondary_color": "Blue", @@ -7624,11 +7821,11 @@ "id": "exalted", "theme": "Exalted", "synergies": [ - "Human Kindred", + "Knight Kindred", + "Soldier Kindred", "Aggro", "Combat Matters", - "Little Fellas", - "Toughness Matters" + "Human Kindred" ], "primary_color": "White", "secondary_color": "Black", @@ -7664,8 +7861,8 @@ "Jackal Kindred", "Warrior Kindred", "Human Kindred", - "Little Fellas", - "Aggro" + "Aggro", + "Combat Matters" ], "primary_color": "White", "secondary_color": "Green", @@ -7696,11 +7893,11 @@ "id": "exhaust", "theme": "Exhaust", "synergies": [ + "Bending", "Vehicles", - "+1/+1 Counters", - "Counters Matter", - "Voltron", - "Aggro" + "Crew", + "Goblin Kindred", + "+1/+1 Counters" ], "primary_color": "Green", "secondary_color": "Blue", @@ -7739,7 +7936,7 @@ "Suspend", "Foretell", "Warp", - "Plot" + "Cascade" ], "primary_color": "Blue", "secondary_color": "Black", @@ -7771,7 +7968,13 @@ { "id": "experience-counters", "theme": "Experience Counters", - "synergies": [], + "synergies": [ + "Counters Matter", + "Human Kindred", + "Little Fellas", + "Aggro", + "Combat Matters" + ], "primary_color": "Blue", "secondary_color": "White", "example_commanders": [ @@ -7835,11 +8038,11 @@ "id": "explore", "theme": "Explore", "synergies": [ - "Map Token", "Card Selection", + "Map Token", "Scout Kindred", - "Pirate Kindred", - "Merfolk Kindred" + "Merfolk Kindred", + "Pirate Kindred" ], "primary_color": "Green", "secondary_color": "Black", @@ -7875,7 +8078,7 @@ "Burn", "Spells Matter", "Spellslinger", - "Little Fellas" + "Lifegain" ], "primary_color": "Black", "secondary_color": "White", @@ -7907,11 +8110,11 @@ "id": "eye-kindred", "theme": "Eye Kindred", "synergies": [ - "Convert", - "Living metal", "More Than Meets the Eye", + "Living metal", + "Convert", "Robot Kindred", - "Vehicles" + "Artifacts Matter" ], "primary_color": "Black", "secondary_color": "White", @@ -7979,7 +8182,8 @@ "synergies": [ "Fading", "Counters Matter", - "Enchantments Matter" + "Enchantments Matter", + "Artifacts Matter" ], "primary_color": "Green", "secondary_color": "Black", @@ -8037,7 +8241,7 @@ "Flying", "Outlaw Kindred", "Flash", - "Wizard Kindred" + "Warlock Kindred" ], "primary_color": "Blue", "secondary_color": "Black", @@ -8097,22 +8301,13 @@ "popularity_bucket": "Rare", "description": "Builds around Fateful hour leveraging synergies with Spells Matter and Spellslinger." }, - { - "id": "fateseal", - "theme": "Fateseal", - "synergies": [], - "primary_color": "Blue", - "example_cards": [ - "Mesmeric Sliver", - "Spin into Myth" - ], - "popularity_bucket": "Rare", - "description": "Builds around the Fateseal theme and its supporting synergies." - }, { "id": "fathomless-descent", "theme": "Fathomless descent", - "synergies": [], + "synergies": [ + "Reanimate", + "Mill" + ], "primary_color": "Black", "secondary_color": "Blue", "example_cards": [ @@ -8164,6 +8359,15 @@ "popularity_bucket": "Rare", "description": "Builds around Fear leveraging synergies with Horror Kindred and Elemental Kindred." }, + { + "id": "feather-counters", + "theme": "Feather Counters", + "synergies": [], + "primary_color": "White", + "secondary_color": "Blue", + "popularity_bucket": "Rare", + "description": "Accumulates feather counters to unlock scaling payoffs, removal triggers, or delayed value conversions." + }, { "id": "ferocious", "theme": "Ferocious", @@ -8214,10 +8418,10 @@ "theme": "Fight", "synergies": [ "Lore Counters", - "Sagas Matter", "Dinosaur Kindred", "Ore Counters", - "Indestructible" + "Sagas Matter", + "Burn" ], "primary_color": "Green", "secondary_color": "Red", @@ -8254,9 +8458,9 @@ "synergies": [ "Mill", "Counters Matter", - "Reanimate", "Blink", - "Enter the Battlefield" + "Enter the Battlefield", + "Leave the Battlefield" ], "primary_color": "Black", "secondary_color": "Green", @@ -8293,10 +8497,10 @@ "theme": "Firebending", "synergies": [ "Bending", + "Noble Kindred", "Mana Dork", - "X Spells", - "Ramp", - "Human Kindred" + "Ally Kindred", + "X Spells" ], "primary_color": "Red", "secondary_color": "Black", @@ -8331,10 +8535,10 @@ "theme": "First strike", "synergies": [ "Banding", - "Kithkin Kindred", + "Barbarian Kindred", "Knight Kindred", - "Protection", - "Partner" + "Kithkin Kindred", + "Protection" ], "primary_color": "White", "secondary_color": "Red", @@ -8370,9 +8574,9 @@ "synergies": [ "Gift", "Loot", - "Stax", + "Creature Tokens", "Discard Matters", - "Creature Tokens" + "Token Creation" ], "primary_color": "Blue", "secondary_color": "Black", @@ -8452,11 +8656,11 @@ "id": "flash", "theme": "Flash", "synergies": [ - "Evoke", "Combat Tricks", + "Evoke", "Faerie Kindred", - "Hexproof", - "Wolf Kindred" + "Crab Kindred", + "Hexproof" ], "primary_color": "Blue", "secondary_color": "White", @@ -8491,9 +8695,9 @@ "synergies": [ "Reanimate", "Mill", - "Clones", "Spells Matter", - "Spellslinger" + "Spellslinger", + "Clones" ], "primary_color": "Green", "secondary_color": "Blue", @@ -8523,7 +8727,9 @@ { "id": "flood-counters", "theme": "Flood Counters", - "synergies": [], + "synergies": [ + "Counters Matter" + ], "primary_color": "Blue", "example_commanders": [ "Eluge, the Shoreless Sea", @@ -8547,6 +8753,7 @@ "Monk Kindred", "Spells Matter", "Spellslinger", + "Human Kindred", "Little Fellas" ], "primary_color": "White", @@ -8728,11 +8935,11 @@ "id": "forage", "theme": "Forage", "synergies": [ + "Squirrel Kindred", "Food Token", "Food", "Lifegain", - "Life Matters", - "Mill" + "Life Matters" ], "primary_color": "Green", "secondary_color": "Black", @@ -8792,11 +8999,11 @@ "id": "forestcycling", "theme": "Forestcycling", "synergies": [ + "Landcycling", + "Typecycling", "Land Types Matter", "Cycling", - "Loot", - "Ramp", - "Discard Matters" + "Loot" ], "primary_color": "Green", "example_commanders": [ @@ -8830,7 +9037,7 @@ "Landwalk", "Cat Kindred", "Lands Matter", - "Little Fellas" + "Warrior Kindred" ], "primary_color": "Green", "secondary_color": "Black", @@ -8865,10 +9072,10 @@ "theme": "Foretell", "synergies": [ "Exile Matters", + "Angel Kindred", "Cleric Kindred", "Spells Matter", - "Spellslinger", - "Control" + "Spellslinger" ], "primary_color": "Blue", "secondary_color": "White", @@ -8939,8 +9146,8 @@ "Bushido", "Samurai Kindred", "Cleric Kindred", - "Lifegain", - "Life Matters" + "Lifelink", + "Spirit Kindred" ], "primary_color": "White", "secondary_color": "Green", @@ -8976,11 +9183,11 @@ "id": "fractal-kindred", "theme": "Fractal Kindred", "synergies": [ + "Creature Tokens", "+1/+1 Counters", - "Counters Matter", - "Voltron", - "Aggro", - "Combat Matters" + "Token Creation", + "Tokens Matter", + "Counters Matter" ], "primary_color": "Green", "secondary_color": "Blue", @@ -9016,9 +9223,9 @@ "synergies": [ "Assassin Kindred", "Cost Reduction", + "Outlaw Kindred", "Big Mana", - "Spells Matter", - "Spellslinger" + "Human Kindred" ], "primary_color": "Black", "secondary_color": "Blue", @@ -9051,7 +9258,11 @@ { "id": "friends-forever", "theme": "Friends Forever", - "synergies": [], + "synergies": [ + "Human Kindred" + ], + "primary_color": "Red", + "secondary_color": "White", "example_commanders": [ "Wernog, Rider's Chaplain", "Bjorna, Nightfall Alchemist", @@ -9077,9 +9288,9 @@ "synergies": [ "Reach", "Beast Kindred", - "Wizard Kindred", - "+1/+1 Counters", - "Blink" + "Druid Kindred", + "Horror Kindred", + "Conditional Draw" ], "primary_color": "Green", "secondary_color": "Blue", @@ -9160,10 +9371,26 @@ "popularity_bucket": "Niche", "description": "Focuses on getting a high number of Fungus creatures into play with shared payoffs (e.g., Spore Counters and Saproling Kindred)." }, + { + "id": "fuse", + "theme": "Fuse", + "synergies": [ + "Spells Matter", + "Spellslinger", + "Big Mana", + "Interaction" + ], + "primary_color": "Red", + "secondary_color": "Blue", + "popularity_bucket": "Rare", + "description": "Builds around Fuse leveraging synergies with Spells Matter and Spellslinger." + }, { "id": "fuse-counters", "theme": "Fuse Counters", - "synergies": [], + "synergies": [ + "Counters Matter" + ], "primary_color": "Red", "example_cards": [ "Goblin Bomb", @@ -9179,11 +9406,11 @@ "id": "gargoyle-kindred", "theme": "Gargoyle Kindred", "synergies": [ + "Defender", "Flying", - "Blink", - "Enter the Battlefield", - "Leave the Battlefield", - "Big Mana" + "Protective Effects", + "Artifacts Matter", + "Toughness Matters" ], "primary_color": "White", "secondary_color": "Blue", @@ -9216,9 +9443,9 @@ "synergies": [ "Sacrifice to Draw", "Artifact Tokens", + "Doctor Kindred", "Land Types Matter", - "Lands Matter", - "Cantrips" + "Lands Matter" ], "primary_color": "Blue", "secondary_color": "Green", @@ -9255,9 +9482,9 @@ "theme": "Germ Kindred", "synergies": [ "Living weapon", + "Phyrexian Kindred", "Equip", "Equipment", - "Phyrexian Kindred", "Equipment Matters" ], "primary_color": "Black", @@ -9292,8 +9519,8 @@ "Monstrosity", "Berserker Kindred", "Warrior Kindred", - "Vigilance", - "Trample" + "Reach", + "Vigilance" ], "primary_color": "White", "secondary_color": "Green", @@ -9436,10 +9663,10 @@ "theme": "Gnome Kindred", "synergies": [ "Artifact Tokens", - "Ramp", "Creature Tokens", "Artifacts Matter", - "Token Creation" + "Token Creation", + "Tokens Matter" ], "primary_color": "White", "secondary_color": "Blue", @@ -9475,11 +9702,11 @@ "id": "goad", "theme": "Goad", "synergies": [ - "Theft", + "Politics", + "First strike", "Rogue Kindred", - "Enchant", - "Artifact Tokens", - "Auras" + "Theft", + "Haste" ], "primary_color": "Red", "secondary_color": "Blue", @@ -9515,10 +9742,10 @@ "theme": "Goat Kindred", "synergies": [ "Beast Kindred", - "+1/+1 Counters", "Creature Tokens", - "Token Creation", - "Tokens Matter" + "+1/+1 Counters", + "Lands Matter", + "Token Creation" ], "primary_color": "White", "secondary_color": "Black", @@ -9551,11 +9778,11 @@ "id": "goblin-kindred", "theme": "Goblin Kindred", "synergies": [ + "Blight", + "Exhaust", "Shaman Kindred", - "Echo", - "Haste", - "Warrior Kindred", - "Mutant Kindred" + "Berserker Kindred", + "Echo" ], "primary_color": "Red", "secondary_color": "Black", @@ -9589,8 +9816,8 @@ "theme": "God Kindred", "synergies": [ "Indestructible", - "Ward", "Protective Effects", + "Sagas Matter", "Transform", "Midrange" ], @@ -9631,7 +9858,7 @@ "Token Creation", "Tokens Matter", "Artifacts Matter", - "Aggro" + "Toughness Matters" ], "primary_color": "White", "secondary_color": "Black", @@ -9667,11 +9894,11 @@ "id": "golem-kindred", "theme": "Golem Kindred", "synergies": [ - "Craft", - "Graveyard Matters", + "Affinity", + "Phyrexian Kindred", "Artificer Kindred", - "Artifact Tokens", - "Phyrexian Kindred" + "Defender", + "Artifact Tokens" ], "primary_color": "Green", "secondary_color": "White", @@ -9708,10 +9935,10 @@ "theme": "Gorgon Kindred", "synergies": [ "Deathtouch", + "Outlaw Kindred", "Removal", "Toughness Matters", - "Stax", - "Reanimate" + "Interaction" ], "primary_color": "Black", "example_commanders": [ @@ -9748,9 +9975,9 @@ "synergies": [ "Mutant Kindred", "+1/+1 Counters", - "Counters Matter", "Blink", - "Enter the Battlefield" + "Enter the Battlefield", + "Leave the Battlefield" ], "primary_color": "Green", "secondary_color": "Blue", @@ -9800,17 +10027,13 @@ "description": "Builds around the Grandeur theme and its supporting synergies." }, { - "id": "gravestorm", - "theme": "Gravestorm", + "id": "graveborn-kindred", + "theme": "Graveborn Kindred", "synergies": [], "primary_color": "Black", - "secondary_color": "Blue", - "example_cards": [ - "Follow the Bodies", - "Bitter Ordeal" - ], + "secondary_color": "Red", "popularity_bucket": "Rare", - "description": "Builds storm count with cheap spells & mana bursts, converting it into a lethal payoff turn." + "description": "Focuses on getting a high number of Graveborn creatures into play with shared payoffs." }, { "id": "graveyard-matters", @@ -9852,8 +10075,9 @@ "id": "gremlin-kindred", "theme": "Gremlin Kindred", "synergies": [ - "Artifacts Matter", "Little Fellas", + "Counters Matter", + "Artifacts Matter", "Aggro", "Combat Matters" ], @@ -9888,8 +10112,8 @@ "synergies": [ "Flying", "Vigilance", - "Toughness Matters", "Little Fellas", + "Toughness Matters", "Big Mana" ], "primary_color": "White", @@ -9950,6 +10174,7 @@ "Blink", "Enter the Battlefield", "Leave the Battlefield", + "Toughness Matters", "Little Fellas" ], "primary_color": "Black", @@ -9983,7 +10208,9 @@ { "id": "hag-kindred", "theme": "Hag Kindred", - "synergies": [], + "synergies": [ + "Little Fellas" + ], "primary_color": "Blue", "secondary_color": "Black", "example_cards": [ @@ -10004,9 +10231,9 @@ "theme": "Halfling Kindred", "synergies": [ "Peasant Kindred", - "Citizen Kindred", "Food Token", "Food", + "Citizen Kindred", "Artifact Tokens" ], "primary_color": "White", @@ -10092,6 +10319,9 @@ "theme": "Harpy Kindred", "synergies": [ "Flying", + "Blink", + "Enter the Battlefield", + "Leave the Battlefield", "Little Fellas" ], "primary_color": "Black", @@ -10123,11 +10353,11 @@ "id": "haste", "theme": "Haste", "synergies": [ - "Hellion Kindred", "Phoenix Kindred", + "Hellion Kindred", "Echo", - "Barbarian Kindred", - "Minotaur Kindred" + "Berserker Kindred", + "Barbarian Kindred" ], "primary_color": "Red", "secondary_color": "Green", @@ -10226,6 +10456,7 @@ "Draw Triggers", "Wheels", "Card Draw", + "Pingers", "Burn" ], "primary_color": "Black", @@ -10259,9 +10490,9 @@ "synergies": [ "Haste", "Trample", + "Big Mana", "Blink", - "Enter the Battlefield", - "Leave the Battlefield" + "Enter the Battlefield" ], "primary_color": "Red", "example_commanders": [ @@ -10376,8 +10607,8 @@ "Hexproof from", "Protective Effects", "Indestructible", - "Flash", - "Stax" + "Sphinx Kindred", + "Flash" ], "primary_color": "Green", "secondary_color": "Blue", @@ -10414,7 +10645,10 @@ "theme": "Hexproof from", "synergies": [ "Hexproof", - "Protective Effects" + "Protective Effects", + "Flying", + "Lifegain", + "Life Matters" ], "primary_color": "Black", "secondary_color": "Green", @@ -10450,7 +10684,10 @@ "theme": "Hideaway", "synergies": [ "Topdeck", - "Lands Matter" + "Lands Matter", + "Enchantments Matter", + "Aggro", + "Combat Matters" ], "primary_color": "Blue", "secondary_color": "Green", @@ -10477,7 +10714,9 @@ { "id": "hippo-kindred", "theme": "Hippo Kindred", - "synergies": [], + "synergies": [ + "Big Mana" + ], "primary_color": "Green", "secondary_color": "Black", "example_commanders": [ @@ -10609,6 +10848,8 @@ "id": "homunculus-kindred", "theme": "Homunculus Kindred", "synergies": [ + "Draw Triggers", + "Wheels", "Little Fellas", "Toughness Matters", "Card Draw" @@ -10648,10 +10889,10 @@ "theme": "Horror Kindred", "synergies": [ "Impending", - "Alien Kindred", "Fear", "Nightmare Kindred", - "Eldrazi Kindred" + "Alien Kindred", + "Swampwalk" ], "primary_color": "Black", "secondary_color": "Blue", @@ -10686,9 +10927,9 @@ "synergies": [ "Saddle", "Mount Kindred", - "Blink", - "Enter the Battlefield", - "Leave the Battlefield" + "Nightmare Kindred", + "Haste", + "Blink" ], "primary_color": "White", "secondary_color": "Black", @@ -10778,7 +11019,7 @@ "synergies": [ "Horsemanship", "Training", - "Mystic Kindred", + "Friends Forever", "Daybound", "Nightbound" ], @@ -10815,11 +11056,11 @@ "id": "hydra-kindred", "theme": "Hydra Kindred", "synergies": [ + "Plant Kindred", "X Spells", - "Trample", "+1/+1 Counters", - "Reach", - "Counters Matter" + "Trample", + "Reach" ], "primary_color": "Green", "secondary_color": "Red", @@ -10901,7 +11142,7 @@ "Wall Kindred", "Defender", "Flying", - "Draw Triggers" + "Clones" ], "primary_color": "Blue", "secondary_color": "Black", @@ -10973,11 +11214,11 @@ "id": "impending", "theme": "Impending", "synergies": [ - "Avatar Kindred", "Time Counters", + "Avatar Kindred", "Horror Kindred", - "Counters Matter", - "Enchantments Matter" + "Enchantments Matter", + "Counters Matter" ], "primary_color": "Black", "secondary_color": "Blue", @@ -11003,7 +11244,13 @@ { "id": "imprint", "theme": "Imprint", - "synergies": [], + "synergies": [ + "Exile Matters", + "Clones", + "Removal", + "Artifacts Matter", + "Stax" + ], "primary_color": "Blue", "secondary_color": "White", "example_commanders": [ @@ -11026,6 +11273,7 @@ "id": "improvise", "theme": "Improvise", "synergies": [ + "Artificer Kindred", "Artifacts Matter", "Big Mana", "Spells Matter", @@ -11063,7 +11311,7 @@ "Junk Tokens", "Junk Token", "Exile Matters", - "Treasure", + "Artificer Kindred", "Spell Copy" ], "primary_color": "Red", @@ -11100,10 +11348,10 @@ "theme": "Incarnation Kindred", "synergies": [ "Evoke", + "Encore", "Elemental Kindred", - "Reanimate", - "Mill", - "Big Mana" + "Politics", + "Mill" ], "primary_color": "White", "secondary_color": "Green", @@ -11319,7 +11567,11 @@ { "id": "inkling-kindred", "theme": "Inkling Kindred", - "synergies": [], + "synergies": [ + "Creature Tokens", + "Token Creation", + "Tokens Matter" + ], "primary_color": "Black", "secondary_color": "White", "example_commanders": [ @@ -11340,15 +11592,24 @@ "popularity_bucket": "Rare", "description": "Focuses on getting a high number of Inkling creatures into play with shared payoffs." }, + { + "id": "inquisitor-kindred", + "theme": "Inquisitor Kindred", + "synergies": [], + "primary_color": "Black", + "secondary_color": "Blue", + "popularity_bucket": "Rare", + "description": "Focuses on getting a high number of Inquisitor creatures into play with shared payoffs." + }, { "id": "insect-kindred", "theme": "Insect Kindred", "synergies": [ "Landfall", - "Shroud", "Poison Counters", + "Shroud", "Druid Kindred", - "Horror Kindred" + "Time Counters" ], "primary_color": "Green", "secondary_color": "Black", @@ -11463,8 +11724,8 @@ "synergies": [ "Zombie Kindred", "Reanimate", - "Little Fellas", "Human Kindred", + "Little Fellas", "Voltron" ], "primary_color": "Black", @@ -11535,9 +11796,9 @@ "theme": "Islandcycling", "synergies": [ "Landcycling", + "Typecycling", "Cycling", "Loot", - "Ramp", "Discard Matters" ], "primary_color": "Blue", @@ -11640,8 +11901,8 @@ "Flying", "Toughness Matters", "Little Fellas", - "Blink", - "Enter the Battlefield" + "Counters Matter", + "+1/+1 Counters" ], "primary_color": "Blue", "secondary_color": "White", @@ -11727,7 +11988,13 @@ { "id": "juggernaut-kindred", "theme": "Juggernaut Kindred", - "synergies": [], + "synergies": [ + "Artifacts Matter", + "Aggro", + "Combat Matters", + "Big Mana", + "Toughness Matters" + ], "primary_color": "Blue", "example_commanders": [ "Graaz, Unstoppable Juggernaut" @@ -11787,7 +12054,8 @@ "Jump", "Mill", "Spells Matter", - "Spellslinger" + "Spellslinger", + "Card Draw" ], "primary_color": "Blue", "secondary_color": "Red", @@ -11857,8 +12125,8 @@ "synergies": [ "Junk Token", "Impulse", - "Artifact Tokens", "Exile Matters", + "Artifact Tokens", "Token Creation" ], "primary_color": "Green", @@ -11895,8 +12163,8 @@ "Kicker", "+1/+1 Counters", "Soldier Kindred", - "Lands Matter", - "Counters Matter" + "Little Fellas", + "Lands Matter" ], "primary_color": "Green", "secondary_color": "Red", @@ -11964,9 +12232,9 @@ "id": "kicker", "theme": "Kicker", "synergies": [ + "Volver Kindred", "Kavu Kindred", - "Merfolk Kindred", - "+1/+1 Counters", + "Wizard Kindred", "Removal", "Combat Tricks" ], @@ -12040,6 +12308,8 @@ "synergies": [ "Spirit Kindred", "Flying", + "Aggro", + "Combat Matters", "Little Fellas" ], "primary_color": "White", @@ -12078,9 +12348,9 @@ "synergies": [ "Soldier Kindred", "First strike", - "Cleric Kindred", + "Scout Kindred", "Knight Kindred", - "Little Fellas" + "Cleric Kindred" ], "primary_color": "White", "secondary_color": "Green", @@ -12116,9 +12386,9 @@ "synergies": [ "Flanking", "Adamant", + "Exalted", "Protection", - "First strike", - "Protection from Color" + "First strike" ], "primary_color": "White", "secondary_color": "Black", @@ -12155,7 +12425,8 @@ "Toughness Matters", "Little Fellas", "Aggro", - "Combat Matters" + "Combat Matters", + "Big Mana" ], "primary_color": "Red", "example_commanders": [ @@ -12227,11 +12498,11 @@ "id": "kraken-kindred", "theme": "Kraken Kindred", "synergies": [ + "Leviathan Kindred", + "Octopus Kindred", + "Serpent Kindred", "Draw Triggers", - "Wheels", - "Protective Effects", - "Creature Tokens", - "Stax" + "Protective Effects" ], "primary_color": "Blue", "secondary_color": "Black", @@ -12263,6 +12534,18 @@ "popularity_bucket": "Rare", "description": "Focuses on getting a high number of Kraken creatures into play with shared payoffs (e.g., Draw Triggers and Wheels)." }, + { + "id": "lairs-matter", + "theme": "Lairs Matter", + "synergies": [ + "Land Types Matter", + "Lands Matter" + ], + "primary_color": "Black", + "secondary_color": "Blue", + "popularity_bucket": "Rare", + "description": "Builds around Lairs Matter leveraging synergies with Land Types Matter and Lands Matter." + }, { "id": "lamia-kindred", "theme": "Lamia Kindred", @@ -12295,8 +12578,8 @@ "Plainscycling", "Mountaincycling", "Forestcycling", - "Swampcycling", - "Spheres Matter" + "Urza's Lands Matter", + "Lairs Matter" ], "primary_color": "Green", "secondary_color": "White", @@ -12325,10 +12608,10 @@ "theme": "Landcycling", "synergies": [ "Basic landcycling", - "Islandcycling", - "Cycling", - "Loot", - "Ramp" + "Plainscycling", + "Mountaincycling", + "Forestcycling", + "Islandcycling" ], "primary_color": "Blue", "secondary_color": "Green", @@ -12357,7 +12640,7 @@ "Ramp", "Token Creation", "Earthbending", - "Bending" + "Earthbend" ], "primary_color": "Green", "secondary_color": "White", @@ -12396,7 +12679,7 @@ "Domain", "Land Tutors", "Land Types Matter", - "Landwalk" + "Gates Matter" ], "primary_color": "Green", "secondary_color": "Blue", @@ -12503,9 +12786,9 @@ "synergies": [ "Blink", "Enter the Battlefield", + "Persist", "Exploit", - "Offspring", - "Fabricate" + "Offspring" ], "primary_color": "Black", "secondary_color": "White", @@ -12538,9 +12821,9 @@ "synergies": [ "Cost Reduction", "X Spells", + "Little Fellas", "Lifegain", - "Life Matters", - "Burn" + "Life Matters" ], "primary_color": "Black", "secondary_color": "Green", @@ -12670,11 +12953,11 @@ "id": "leviathan-kindred", "theme": "Leviathan Kindred", "synergies": [ + "Kraken Kindred", + "Octopus Kindred", + "Serpent Kindred", "Trample", - "Big Mana", - "Blink", - "Enter the Battlefield", - "Leave the Battlefield" + "Big Mana" ], "primary_color": "Blue", "secondary_color": "Green", @@ -12710,8 +12993,11 @@ "id": "lhurgoyf-kindred", "theme": "Lhurgoyf Kindred", "synergies": [ + "Reanimate", + "Mill", "Aggro", - "Combat Matters" + "Combat Matters", + "Big Mana" ], "primary_color": "Green", "secondary_color": "Black", @@ -12744,9 +13030,9 @@ "synergies": [ "Equipment Matters", "Auras", - "Artifacts Matter", "Enchantments Matter", - "Voltron" + "Voltron", + "Artifacts Matter" ], "primary_color": "Blue", "secondary_color": "Black", @@ -12817,7 +13103,7 @@ "Lifedrain", "Extort", "Cleric Kindred", - "Lifelink" + "Vampire Kindred" ], "primary_color": "White", "secondary_color": "Black", @@ -12884,7 +13170,7 @@ "Lifedrain", "Extort", "Cleric Kindred", - "Lifelink" + "Vampire Kindred" ], "primary_color": "White", "secondary_color": "Black", @@ -12916,10 +13202,10 @@ "theme": "Lifegain Triggers", "synergies": [ "Vampire Kindred", + "Food Token", "Lifelink", - "Lifegain", - "Life Matters", - "Cleric Kindred" + "Food", + "Lifegain" ], "primary_color": "White", "secondary_color": "Black", @@ -12954,11 +13240,11 @@ "id": "lifelink", "theme": "Lifelink", "synergies": [ - "Lifegain Triggers", "Vampire Kindred", + "Lifegain Triggers", "Lifegain", "Life Matters", - "Angel Kindred" + "Unicorn Kindred" ], "primary_color": "White", "secondary_color": "Black", @@ -12993,9 +13279,9 @@ "synergies": [ "Lifeloss Triggers", "Bat Kindred", + "Draw Triggers", "Life Matters", - "Lifegain", - "Flying" + "Wheels" ], "primary_color": "Black", "secondary_color": "White", @@ -13028,9 +13314,9 @@ "synergies": [ "Lifeloss", "Bat Kindred", + "Draw Triggers", "Life Matters", - "Lifegain", - "Flying" + "Wheels" ], "primary_color": "Black", "secondary_color": "White", @@ -13061,11 +13347,11 @@ "id": "little-fellas", "theme": "Little Fellas", "synergies": [ - "Banding", "Licid Kindred", "Spike Kindred", - "Soltari Kindred", - "Training" + "Training", + "Metathran Kindred", + "Slith Kindred" ], "primary_color": "White", "secondary_color": "Blue", @@ -13096,11 +13382,11 @@ "id": "living-metal", "theme": "Living metal", "synergies": [ - "Convert", "More Than Meets the Eye", + "Convert", "Eye Kindred", "Robot Kindred", - "Vehicles" + "Artifacts Matter" ], "primary_color": "Black", "secondary_color": "White", @@ -13132,9 +13418,9 @@ "theme": "Living weapon", "synergies": [ "Germ Kindred", + "Phyrexian Kindred", "Equip", "Equipment", - "Phyrexian Kindred", "Equipment Matters" ], "primary_color": "Black", @@ -13163,11 +13449,11 @@ "id": "lizard-kindred", "theme": "Lizard Kindred", "synergies": [ + "Mercenary Kindred", + "Warlock Kindred", "Menace", "Shaman Kindred", - "Outlaw Kindred", - "Haste", - "Warrior Kindred" + "Impulse" ], "primary_color": "Green", "secondary_color": "Black", @@ -13207,7 +13493,7 @@ "Discard Matters", "Reanimate", "Cycling", - "Connive" + "Typecycling" ], "primary_color": "Blue", "secondary_color": "Black", @@ -13244,9 +13530,9 @@ "theme": "Lore Counters", "synergies": [ "Read Ahead", - "Sagas Matter", "Ore Counters", - "Praetor Kindred", + "Sagas Matter", + "Alien Kindred", "Doctor Kindred" ], "primary_color": "White", @@ -13280,11 +13566,11 @@ "id": "loyalty-counters", "theme": "Loyalty Counters", "synergies": [ + "Compleated", "Planeswalkers", "Superfriends", "Counters Matter", - "Draw Triggers", - "Wheels" + "Draw Triggers" ], "primary_color": "White", "secondary_color": "Black", @@ -13357,10 +13643,10 @@ "theme": "Magecraft", "synergies": [ "Transform", + "Shaman Kindred", "Wizard Kindred", "Human Kindred", - "Spells Matter", - "Spellslinger" + "Spells Matter" ], "primary_color": "Blue", "secondary_color": "White", @@ -13398,8 +13684,8 @@ "Firebending", "Scion Kindred", "Spawn Kindred", - "Ramp", - "Myr Kindred" + "Powerstone Token", + "Ramp" ], "primary_color": "Green", "secondary_color": "Blue", @@ -13434,11 +13720,11 @@ "id": "mana-rock", "theme": "Mana Rock", "synergies": [ + "Ramp", "Myr Kindred", "Charge Counters", - "Ramp", - "Robot Kindred", - "Mana Dork" + "Mana Dork", + "Sacrifice to Draw" ], "primary_color": "Blue", "secondary_color": "Green", @@ -13475,8 +13761,8 @@ "synergies": [ "Manifest dread", "Topdeck", - "Equipment Matters", "Reanimate", + "Equipment Matters", "Mill" ], "primary_color": "Green", @@ -13512,7 +13798,7 @@ "Topdeck", "Reanimate", "Mill", - "+1/+1 Counters" + "Sacrifice Matters" ], "primary_color": "Green", "secondary_color": "Blue", @@ -13544,6 +13830,7 @@ "theme": "Manticore Kindred", "synergies": [ "Burn", + "Flying", "Blink", "Enter the Battlefield", "Leave the Battlefield" @@ -13575,8 +13862,8 @@ "id": "map-token", "theme": "Map Token", "synergies": [ - "Explore", "Card Selection", + "Explore", "Artifact Tokens", "Token Creation", "Tokens Matter" @@ -13611,10 +13898,10 @@ "theme": "Max speed", "synergies": [ "Start your engines!", - "Vehicles", "Scout Kindred", - "Conditional Draw", - "Burn" + "Vehicles", + "Cat Kindred", + "Conditional Draw" ], "primary_color": "Black", "secondary_color": "White", @@ -13648,7 +13935,9 @@ "theme": "Mayhem", "synergies": [ "Discard Matters", - "Mill" + "Mill", + "Aggro", + "Combat Matters" ], "primary_color": "Black", "secondary_color": "Blue", @@ -13746,6 +14035,7 @@ "theme": "Melee", "synergies": [ "Politics", + "Human Kindred", "Aggro", "Combat Matters", "Little Fellas" @@ -13783,10 +14073,10 @@ "theme": "Menace", "synergies": [ "Warlock Kindred", + "Rat Kindred", "Blood Token", - "Pirate Kindred", - "Werewolf Kindred", - "Dog Kindred" + "Mutant Kindred", + "Ninja Kindred" ], "primary_color": "Black", "secondary_color": "Red", @@ -13826,7 +14116,7 @@ "+1/+1 Counters", "Counters Matter", "Voltron", - "Aggro" + "Human Kindred" ], "primary_color": "White", "secondary_color": "Blue", @@ -13862,10 +14152,10 @@ "theme": "Mercenary Kindred", "synergies": [ "Outlaw Kindred", + "Lizard Kindred", "Horror Kindred", - "Phyrexian Kindred", - "Human Kindred", - "Zombie Kindred" + "Treasure", + "Treasure Token" ], "primary_color": "Black", "secondary_color": "White", @@ -13905,7 +14195,7 @@ "Explore", "Card Selection", "Wizard Kindred", - "Landwalk" + "Scout Kindred" ], "primary_color": "Blue", "secondary_color": "Green", @@ -13943,9 +14233,9 @@ "synergies": [ "Transform", "Artifacts Matter", + "Soldier Kindred", "Human Kindred", - "Flying", - "Toughness Matters" + "Flying" ], "primary_color": "White", "secondary_color": "Blue", @@ -14042,7 +14332,7 @@ "Surveil", "Threshold", "Delirium", - "Madness", + "Escape", "Delve" ], "primary_color": "Black", @@ -14081,8 +14371,8 @@ "Threshold", "Phyrexian Kindred", "Human Kindred", - "Reanimate", - "Sacrifice Matters" + "Little Fellas", + "Reanimate" ], "primary_color": "Black", "secondary_color": "Blue", @@ -14118,8 +14408,8 @@ "synergies": [ "Berserker Kindred", "Shaman Kindred", - "Haste", "Warrior Kindred", + "Haste", "First strike" ], "primary_color": "Red", @@ -14190,11 +14480,11 @@ "id": "mite-kindred", "theme": "Mite Kindred", "synergies": [ + "Toxic", "Poison Counters", "Infect", "Phyrexian Kindred", - "Artifact Tokens", - "Creature Tokens" + "Artifact Tokens" ], "primary_color": "White", "secondary_color": "Black", @@ -14231,7 +14521,7 @@ "Creature Tokens", "Token Creation", "Tokens Matter", - "Toughness Matters" + "Aggro" ], "primary_color": "Black", "secondary_color": "White", @@ -14295,8 +14585,8 @@ "Sacrifice Matters", "Aristocrats", "+1/+1 Counters", - "Artifacts Matter", - "Counters Matter" + "Counters Matter", + "Voltron" ], "primary_color": "Red", "secondary_color": "White", @@ -14329,7 +14619,11 @@ "id": "mole-kindred", "theme": "Mole Kindred", "synergies": [ - "Little Fellas" + "+1/+1 Counters", + "Blink", + "Enter the Battlefield", + "Leave the Battlefield", + "Counters Matter" ], "primary_color": "Green", "secondary_color": "Black", @@ -14359,8 +14653,8 @@ "Politics", "Group Hug", "Card Draw", - "Outlaw Kindred", - "Soldier Kindred" + "Knight Kindred", + "Outlaw Kindred" ], "primary_color": "White", "secondary_color": "Black", @@ -14432,8 +14726,8 @@ "Flurry", "Prowess", "Djinn Kindred", - "Lore Counters", - "Sagas Matter" + "Rhino Kindred", + "Human Kindred" ], "primary_color": "White", "secondary_color": "Green", @@ -14467,9 +14761,11 @@ "id": "monkey-kindred", "theme": "Monkey Kindred", "synergies": [ + "Token Creation", + "Tokens Matter", "Little Fellas", - "Aggro", - "Combat Matters" + "Toughness Matters", + "Artifacts Matter" ], "primary_color": "Green", "secondary_color": "Black", @@ -14508,7 +14804,7 @@ "+1/+1 Counters", "Counters Matter", "Voltron", - "Aggro" + "Protective Effects" ], "primary_color": "Green", "secondary_color": "Blue", @@ -14545,8 +14841,8 @@ "Wizard Kindred", "Flying", "Toughness Matters", - "Artifacts Matter", - "Stax" + "Little Fellas", + "Lands Matter" ], "primary_color": "Blue", "example_commanders": [ @@ -14583,9 +14879,9 @@ "synergies": [ "+1/+1 Counters", "Counters Matter", - "Voltron", - "Token Creation", - "Blink" + "Blink", + "Enter the Battlefield", + "Leave the Battlefield" ], "primary_color": "Green", "secondary_color": "Black", @@ -14616,11 +14912,11 @@ "id": "more-than-meets-the-eye", "theme": "More Than Meets the Eye", "synergies": [ - "Convert", "Living metal", + "Convert", "Eye Kindred", "Robot Kindred", - "Vehicles" + "Artifacts Matter" ], "primary_color": "Black", "secondary_color": "White", @@ -14653,9 +14949,9 @@ "synergies": [ "Beast Kindred", "Illusion Kindred", - "Wizard Kindred", "Cleric Kindred", - "Bird Kindred" + "Bird Kindred", + "Wizard Kindred" ], "primary_color": "Blue", "secondary_color": "Green", @@ -14691,7 +14987,7 @@ "Pilot Kindred", "Horse Kindred", "Vehicles", - "Vigilance" + "Beast Kindred" ], "primary_color": "White", "secondary_color": "Green", @@ -14724,11 +15020,11 @@ "id": "mountaincycling", "theme": "Mountaincycling", "synergies": [ + "Landcycling", + "Typecycling", "Land Types Matter", "Cycling", - "Loot", - "Ramp", - "Discard Matters" + "Loot" ], "primary_color": "Red", "example_commanders": [ @@ -14788,9 +15084,9 @@ "synergies": [ "Valiant", "Soldier Kindred", - "Little Fellas", - "Counters Matter", - "Toughness Matters" + "+1/+1 Counters", + "Voltron", + "Counters Matter" ], "primary_color": "White", "secondary_color": "Green", @@ -14887,9 +15183,9 @@ "synergies": [ "Graft", "Rad Counters", - "Goblin Kindred", - "Zombie Kindred", - "+1/+1 Counters" + "Turtle Kindred", + "Sneak", + "Ninja Kindred" ], "primary_color": "Green", "secondary_color": "Blue", @@ -14920,11 +15216,11 @@ "id": "mutate", "theme": "Mutate", "synergies": [ + "Nightmare Kindred", "Beast Kindred", - "Flying", - "Toughness Matters", - "Big Mana", - "Aggro" + "Dinosaur Kindred", + "Cat Kindred", + "Elemental Kindred" ], "primary_color": "Black", "secondary_color": "Blue", @@ -14961,10 +15257,10 @@ "theme": "Myr Kindred", "synergies": [ "Mana Rock", + "Phyrexian Kindred", "Mana Dork", - "Ramp", - "Artifacts Matter", - "Little Fellas" + "Artifact Tokens", + "Artifacts Matter" ], "primary_color": "Blue", "secondary_color": "White", @@ -15036,8 +15332,11 @@ "id": "mystic-kindred", "theme": "Mystic Kindred", "synergies": [ + "Nomad Kindred", + "Threshold", "Human Kindred", - "Little Fellas" + "Reanimate", + "Mill" ], "primary_color": "White", "secondary_color": "Green", @@ -15080,10 +15379,10 @@ "theme": "Necron Kindred", "synergies": [ "Unearth", + "Artifact Tokens", "Artifacts Matter", "Wizard Kindred", - "Mill", - "Blink" + "Warrior Kindred" ], "primary_color": "Black", "example_commanders": [ @@ -15111,6 +15410,15 @@ "popularity_bucket": "Rare", "description": "Focuses on getting a high number of Necron creatures into play with shared payoffs (e.g., Unearth and Artifacts Matter)." }, + { + "id": "nephilim-kindred", + "theme": "Nephilim Kindred", + "synergies": [], + "primary_color": "Black", + "secondary_color": "Blue", + "popularity_bucket": "Rare", + "description": "Focuses on getting a high number of Nephilim creatures into play with shared payoffs." + }, { "id": "net-counters", "theme": "Net Counters", @@ -15130,9 +15438,9 @@ "synergies": [ "Daybound", "Werewolf Kindred", + "Wolf Kindred", "Control", - "Stax", - "Human Kindred" + "Stax" ], "primary_color": "Green", "secondary_color": "Black", @@ -15160,11 +15468,11 @@ "id": "nightmare-kindred", "theme": "Nightmare Kindred", "synergies": [ + "Mutate", + "Horse Kindred", "Horror Kindred", "Beast Kindred", - "Draw Triggers", - "Blink", - "Enter the Battlefield" + "Deathtouch" ], "primary_color": "Black", "secondary_color": "Blue", @@ -15228,10 +15536,10 @@ "theme": "Ninja Kindred", "synergies": [ "Ninjutsu", - "Rat Kindred", - "Human Kindred", - "Big Mana", - "Aggro" + "Sneak", + "Turtle Kindred", + "Mutant Kindred", + "Rat Kindred" ], "primary_color": "Blue", "secondary_color": "Black", @@ -15301,11 +15609,11 @@ "id": "noble-kindred", "theme": "Noble Kindred", "synergies": [ - "Lore Counters", + "Firebending", + "Bending", + "Octopus Kindred", "Vampire Kindred", - "Sagas Matter", - "Ore Counters", - "Transform" + "Ally Kindred" ], "primary_color": "Black", "secondary_color": "White", @@ -15337,14 +15645,23 @@ "popularity_bucket": "Niche", "description": "Focuses on getting a high number of Noble creatures into play with shared payoffs (e.g., Lore Counters and Sagas Matter)." }, + { + "id": "noggle-kindred", + "theme": "Noggle Kindred", + "synergies": [], + "primary_color": "Red", + "secondary_color": "Blue", + "popularity_bucket": "Rare", + "description": "Focuses on getting a high number of Noggle creatures into play with shared payoffs." + }, { "id": "nomad-kindred", "theme": "Nomad Kindred", "synergies": [ + "Mystic Kindred", "Threshold", "Human Kindred", "Reanimate", - "Little Fellas", "Mill" ], "primary_color": "White", @@ -15409,11 +15726,11 @@ "id": "octopus-kindred", "theme": "Octopus Kindred", "synergies": [ - "Rogue Kindred", - "Loot", - "Outlaw Kindred", - "Wizard Kindred", - "Discard Matters" + "Leviathan Kindred", + "Kraken Kindred", + "Serpent Kindred", + "Noble Kindred", + "Rogue Kindred" ], "primary_color": "Blue", "secondary_color": "Black", @@ -15528,7 +15845,7 @@ "Warrior Kindred", "Shaman Kindred", "Rogue Kindred", - "Outlaw Kindred" + "Menace" ], "primary_color": "Black", "secondary_color": "Red", @@ -15565,10 +15882,10 @@ "theme": "Oil Counters", "synergies": [ "Phyrexian Kindred", + "Beast Kindred", "Counters Matter", "Artifacts Matter", - "Warrior Kindred", - "Wizard Kindred" + "Warrior Kindred" ], "primary_color": "Blue", "secondary_color": "Green", @@ -15615,9 +15932,9 @@ "theme": "Ooze Kindred", "synergies": [ "Clones", + "Midrange", "+1/+1 Counters", "Counters Matter", - "Creature Tokens", "Voltron" ], "primary_color": "Green", @@ -15658,7 +15975,7 @@ "Blink", "Enter the Battlefield", "Leave the Battlefield", - "Little Fellas" + "Toughness Matters" ], "primary_color": "Black", "secondary_color": "Blue", @@ -15706,8 +16023,8 @@ "Army Kindred", "Amass", "Dash", - "Pirate Kindred", - "Treasure" + "Raid", + "Pirate Kindred" ], "primary_color": "Black", "secondary_color": "Blue", @@ -15745,7 +16062,7 @@ "Spore Counters", "Read Ahead", "Sagas Matter", - "Praetor Kindred" + "Fungus Kindred" ], "primary_color": "Green", "secondary_color": "White", @@ -15788,11 +16105,11 @@ "id": "otter-kindred", "theme": "Otter Kindred", "synergies": [ + "Prowess", "Wizard Kindred", - "Artifacts Matter", - "Blink", - "Enter the Battlefield", - "Leave the Battlefield" + "Spells Matter", + "Spellslinger", + "Card Draw" ], "primary_color": "Blue", "secondary_color": "Red", @@ -15827,7 +16144,10 @@ "theme": "Ouphe Kindred", "synergies": [ "Stax", - "Little Fellas" + "Little Fellas", + "Counters Matter", + "Blink", + "Enter the Battlefield" ], "primary_color": "Green", "secondary_color": "Black", @@ -15858,8 +16178,8 @@ "+1/+1 Counters", "Counters Matter", "Voltron", - "Toughness Matters", - "Human Kindred" + "Human Kindred", + "Toughness Matters" ], "primary_color": "White", "secondary_color": "Black", @@ -15927,10 +16247,10 @@ "theme": "Overload", "synergies": [ "Combat Tricks", - "Removal", "Spells Matter", "Spellslinger", - "Interaction" + "Interaction", + "Removal" ], "primary_color": "Blue", "secondary_color": "Black", @@ -15965,11 +16285,11 @@ "id": "ox-kindred", "theme": "Ox Kindred", "synergies": [ + "Creature Tokens", "Token Creation", "Tokens Matter", - "Toughness Matters", - "Artifacts Matter", - "Aggro" + "Lands Matter", + "Toughness Matters" ], "primary_color": "White", "secondary_color": "Green", @@ -16014,7 +16334,8 @@ "theme": "Pack tactics", "synergies": [ "Aggro", - "Combat Matters" + "Combat Matters", + "Little Fellas" ], "primary_color": "Green", "secondary_color": "Red", @@ -16058,7 +16379,10 @@ "theme": "Paradox", "synergies": [ "Spells Matter", - "Spellslinger" + "Spellslinger", + "Aggro", + "Combat Matters", + "Little Fellas" ], "primary_color": "Blue", "secondary_color": "Green", @@ -16090,9 +16414,10 @@ "theme": "Parley", "synergies": [ "Politics", - "Draw Triggers", "Wheels", - "Card Draw" + "Draw Triggers", + "Card Draw", + "Token Creation" ], "primary_color": "Green", "secondary_color": "Blue", @@ -16127,9 +16452,9 @@ "synergies": [ "Partner with", "Pirate Kindred", - "Artificer Kindred", "First strike", - "Elf Kindred" + "Artificer Kindred", + "Planeswalkers" ], "primary_color": "Blue", "secondary_color": "White", @@ -16165,6 +16490,8 @@ "id": "partner-father-son", "theme": "Partner - Father & Son", "synergies": [], + "primary_color": "Red", + "secondary_color": "Blue", "example_commanders": [ "Atreus, Impulsive Son", "Kratos, Stoic Father" @@ -16176,6 +16503,15 @@ "popularity_bucket": "Rare", "description": "Builds around the Partner - Father & Son theme and its supporting synergies." }, + { + "id": "partner-survivors", + "theme": "Partner - Survivors", + "synergies": [], + "primary_color": "Red", + "secondary_color": "Black", + "popularity_bucket": "Rare", + "description": "Builds around the Partner - Survivors theme and its supporting synergies." + }, { "id": "partner-with", "theme": "Partner with", @@ -16295,7 +16631,7 @@ "Enter the Battlefield", "Leave the Battlefield", "Human Kindred", - "Aggro" + "Flying" ], "primary_color": "Green", "secondary_color": "Blue", @@ -16405,10 +16741,10 @@ "theme": "Phasing", "synergies": [ "Equipment Matters", - "Protective Effects", "Interaction", - "Removal", - "Artifacts Matter" + "Protective Effects", + "Planeswalkers", + "Superfriends" ], "primary_color": "Blue", "secondary_color": "White", @@ -16439,6 +16775,15 @@ "popularity_bucket": "Niche", "description": "Builds around Phasing leveraging synergies with Equipment Matters and Protective Effects." }, + { + "id": "phelddagrif-kindred", + "theme": "Phelddagrif Kindred", + "synergies": [], + "primary_color": "Blue", + "secondary_color": "Green", + "popularity_bucket": "Rare", + "description": "Focuses on getting a high number of Phelddagrif creatures into play with shared payoffs." + }, { "id": "phoenix-kindred", "theme": "Phoenix Kindred", @@ -16482,9 +16827,9 @@ "synergies": [ "Germ Kindred", "Carrier Kindred", - "Living weapon", "Incubator Token", - "Incubate" + "Incubate", + "Living weapon" ], "primary_color": "Black", "secondary_color": "Green", @@ -16519,8 +16864,8 @@ "Planeswalkers", "Superfriends", "Control", - "Stax", - "Pingers" + "Vigilance", + "Stax" ], "primary_color": "White", "secondary_color": "Green", @@ -16558,9 +16903,9 @@ "synergies": [ "Vehicles", "Mount Kindred", - "Artifacts Matter", - "Creature Tokens", - "Token Creation" + "Dwarf Kindred", + "Scry", + "Artifacts Matter" ], "primary_color": "White", "secondary_color": "Blue", @@ -16599,7 +16944,7 @@ "Devil Kindred", "Offspring", "Burn", - "Role token" + "Hellbent" ], "primary_color": "Black", "secondary_color": "Red", @@ -16683,11 +17028,11 @@ "id": "plainscycling", "theme": "Plainscycling", "synergies": [ + "Landcycling", + "Typecycling", "Land Types Matter", "Cycling", - "Loot", - "Ramp", - "Discard Matters" + "Loot" ], "primary_color": "White", "example_commanders": [ @@ -16713,30 +17058,15 @@ "popularity_bucket": "Rare", "description": "Builds around Plainscycling leveraging synergies with Land Types Matter and Cycling." }, - { - "id": "plainswalk", - "theme": "Plainswalk", - "synergies": [], - "primary_color": "White", - "secondary_color": "Green", - "example_cards": [ - "Zodiac Rooster", - "Graceful Antelope", - "Boggart Arsonists", - "Righteous Avengers" - ], - "popularity_bucket": "Rare", - "description": "Builds around the Plainswalk theme and its supporting synergies." - }, { "id": "planeswalkers", "theme": "Planeswalkers", "synergies": [ "Proliferate", "Superfriends", + "Compleated", "Myriad", - "Loyalty Counters", - "Infect" + "Loyalty Counters" ], "primary_color": "White", "secondary_color": "Blue", @@ -16770,9 +17100,9 @@ "synergies": [ "Defender", "Wall Kindred", + "Hydra Kindred", "Landfall", - "Reach", - "Mana Dork" + "Reach" ], "primary_color": "Green", "secondary_color": "Black", @@ -16812,7 +17142,7 @@ "Rogue Kindred", "Outlaw Kindred", "Unconditional Draw", - "Card Draw" + "Blink" ], "primary_color": "Blue", "secondary_color": "Green", @@ -16846,8 +17176,8 @@ "id": "poison-counters", "theme": "Poison Counters", "synergies": [ - "Toxic", "Corrupted", + "Toxic", "Mite Kindred", "Infect", "Phyrexian Kindred" @@ -16882,8 +17212,8 @@ "id": "politics", "theme": "Politics", "synergies": [ - "Encore", "Melee", + "Parley", "Council's dilemma", "Tempting offer", "Monger Kindred" @@ -16984,8 +17314,8 @@ "id": "powerstone-token", "theme": "Powerstone Token", "synergies": [ - "Artifact Tokens", "Artificer Kindred", + "Artifact Tokens", "Mana Dork", "Ramp", "Token Creation" @@ -17023,10 +17353,10 @@ "theme": "Praetor Kindred", "synergies": [ "Phyrexian Kindred", - "Lore Counters", - "Ore Counters", - "Sagas Matter", - "Transform" + "Transform", + "Stax", + "Blink", + "Enter the Battlefield" ], "primary_color": "Black", "secondary_color": "Blue", @@ -17078,7 +17408,9 @@ "synergies": [ "Devoid", "Eldrazi Kindred", - "Exile Matters" + "Exile Matters", + "Toughness Matters", + "Blink" ], "primary_color": "Blue", "secondary_color": "Black", @@ -17284,8 +17616,8 @@ "synergies": [ "Protection", "Protective Effects", - "Cleric Kindred", "Interaction", + "Cleric Kindred", "Spirit Kindred" ], "primary_color": "White", @@ -17427,9 +17759,9 @@ "synergies": [ "Spellslinger", "Noncreature Spells", + "Otter Kindred", "Monk Kindred", - "Djinn Kindred", - "Artifacts Matter" + "Djinn Kindred" ], "primary_color": "Blue", "secondary_color": "Red", @@ -17538,11 +17870,11 @@ "id": "rabbit-kindred", "theme": "Rabbit Kindred", "synergies": [ + "Vigilance", "Warrior Kindred", "Creature Tokens", - "Lands Matter", - "Token Creation", - "Tokens Matter" + "Tokens Matter", + "+1/+1 Counters" ], "primary_color": "White", "secondary_color": "Green", @@ -17578,11 +17910,11 @@ "id": "raccoon-kindred", "theme": "Raccoon Kindred", "synergies": [ + "Mana Dork", + "Outlaw Kindred", "Warrior Kindred", - "Blink", - "Enter the Battlefield", - "Leave the Battlefield", - "Aggro" + "Ramp", + "+1/+1 Counters" ], "primary_color": "Green", "secondary_color": "Red", @@ -17622,7 +17954,7 @@ "Zombie Kindred", "Mill", "Counters Matter", - "+1/+1 Counters" + "Reanimate" ], "primary_color": "Black", "secondary_color": "Green", @@ -17685,10 +18017,10 @@ "theme": "Raid", "synergies": [ "Pirate Kindred", + "Orc Kindred", "Outlaw Kindred", "Draw Triggers", - "Wheels", - "Warrior Kindred" + "Wheels" ], "primary_color": "Black", "secondary_color": "Blue", @@ -17723,7 +18055,9 @@ "theme": "Rally", "synergies": [ "Ally Kindred", - "Little Fellas" + "Human Kindred", + "Little Fellas", + "Big Mana" ], "primary_color": "White", "secondary_color": "Green", @@ -17758,7 +18092,7 @@ "Land Tutors", "Mana Dork", "Mana Rock", - "Landcycling" + "Basic landcycling" ], "primary_color": "Green", "secondary_color": "Blue", @@ -17791,7 +18125,10 @@ "id": "rampage", "theme": "Rampage", "synergies": [ - "Big Mana" + "Big Mana", + "Toughness Matters", + "Aggro", + "Combat Matters" ], "primary_color": "Green", "secondary_color": "Red", @@ -17825,8 +18162,8 @@ "Elf Kindred", "Scout Kindred", "Reach", - "Ramp", - "Lands Matter" + "Vigilance", + "Human Kindred" ], "primary_color": "Green", "secondary_color": "White", @@ -17865,8 +18202,8 @@ "Ninjutsu", "Ninja Kindred", "Threshold", - "Warlock Kindred", - "Poison Counters" + "Squirrel Kindred", + "Warlock Kindred" ], "primary_color": "Black", "secondary_color": "Blue", @@ -17937,8 +18274,8 @@ "synergies": [ "Spider Kindred", "Archer Kindred", - "Plant Kindred", "Ranger Kindred", + "Plant Kindred", "Frog Kindred" ], "primary_color": "Green", @@ -17975,10 +18312,10 @@ "theme": "Read Ahead", "synergies": [ "Lore Counters", - "Sagas Matter", "Ore Counters", - "Counters Matter", - "Creature Tokens" + "Sagas Matter", + "Enchantments Matter", + "Counters Matter" ], "primary_color": "Black", "secondary_color": "Blue", @@ -18049,10 +18386,10 @@ "theme": "Rebel Kindred", "synergies": [ "For Mirrodin!", + "Ally Kindred", "Equip", "Equipment", - "Equipment Matters", - "Human Kindred" + "Equipment Matters" ], "primary_color": "White", "secondary_color": "Black", @@ -18122,8 +18459,8 @@ "synergies": [ "Equipment", "Equipment Matters", - "Artifacts Matter", "Voltron", + "Artifacts Matter", "Aggro" ], "primary_color": "Black", @@ -18246,8 +18583,8 @@ "Soulshift", "Interaction", "Control", - "Gift", - "Replicate" + "Imprint", + "Phasing" ], "primary_color": "Black", "secondary_color": "White", @@ -18316,8 +18653,8 @@ "id": "renown", "theme": "Renown", "synergies": [ - "+1/+1 Counters", "Soldier Kindred", + "+1/+1 Counters", "Counters Matter", "Voltron", "Human Kindred" @@ -18352,7 +18689,8 @@ "id": "replacement-draw", "theme": "Replacement Draw", "synergies": [ - "Card Draw" + "Card Draw", + "Enchantments Matter" ], "primary_color": "Black", "secondary_color": "Blue", @@ -18451,8 +18789,10 @@ "theme": "Retrace", "synergies": [ "Mill", + "Creature Tokens", "Spells Matter", - "Spellslinger" + "Spellslinger", + "Token Creation" ], "primary_color": "Green", "secondary_color": "Black", @@ -18484,10 +18824,10 @@ "theme": "Revolt", "synergies": [ "Warrior Kindred", - "+1/+1 Counters", "Blink", "Enter the Battlefield", - "Leave the Battlefield" + "Leave the Battlefield", + "+1/+1 Counters" ], "primary_color": "Green", "secondary_color": "White", @@ -18518,11 +18858,11 @@ "id": "rhino-kindred", "theme": "Rhino Kindred", "synergies": [ + "Monk Kindred", "Trample", "Soldier Kindred", "Warrior Kindred", - "Creature Tokens", - "Big Mana" + "Blink" ], "primary_color": "Green", "secondary_color": "White", @@ -18626,9 +18966,9 @@ "id": "robot-kindred", "theme": "Robot Kindred", "synergies": [ - "Convert", - "Living metal", "More Than Meets the Eye", + "Living metal", + "Convert", "Clown Kindred", "Eye Kindred" ], @@ -18667,8 +19007,8 @@ "synergies": [ "Prowl", "Outlaw Kindred", - "Connive", "Aetherborn Kindred", + "Connive", "Tiefling Kindred" ], "primary_color": "Black", @@ -18754,9 +19094,9 @@ "synergies": [ "Eerie", "Enchantments Matter", - "Pingers", "Spirit Kindred", - "Big Mana" + "Big Mana", + "Topdeck" ], "primary_color": "Blue", "secondary_color": "Black", @@ -18785,10 +19125,10 @@ "theme": "Sacrifice Matters", "synergies": [ "Persist", - "Modular", "Zubera Kindred", "Aristocrats", - "Blitz" + "Afterlife", + "Exploit" ], "primary_color": "Black", "secondary_color": "Green", @@ -18820,10 +19160,10 @@ "id": "sacrifice-to-draw", "theme": "Sacrifice to Draw", "synergies": [ - "Spheres Matter", "Clue Token", "Investigate", "Gates Matter", + "Spheres Matter", "Blood Token" ], "primary_color": "Black", @@ -18859,9 +19199,9 @@ "synergies": [ "Mount Kindred", "Horse Kindred", + "Beast Kindred", "Aggro", - "Combat Matters", - "+1/+1 Counters" + "Combat Matters" ], "primary_color": "White", "secondary_color": "Green", @@ -18897,7 +19237,7 @@ "Read Ahead", "Ore Counters", "Doctor Kindred", - "Praetor Kindred" + "Doctor's Companion" ], "primary_color": "White", "secondary_color": "Blue", @@ -19065,9 +19405,9 @@ "synergies": [ "Ramp", "Sacrifice Matters", - "Lands Matter", "Aristocrats", - "Little Fellas" + "Little Fellas", + "Enchantments Matter" ], "primary_color": "Green", "secondary_color": "Red", @@ -19099,7 +19439,13 @@ { "id": "scarecrow-kindred", "theme": "Scarecrow Kindred", - "synergies": [], + "synergies": [ + "-1/-1 Counters", + "Artifacts Matter", + "Ramp", + "Little Fellas", + "Toughness Matters" + ], "primary_color": "Black", "secondary_color": "Blue", "example_commanders": [ @@ -19128,7 +19474,7 @@ "Mill", "Counters Matter", "Voltron", - "Aggro" + "Reanimate" ], "primary_color": "Green", "secondary_color": "Black", @@ -19159,9 +19505,11 @@ "id": "scientist-kindred", "theme": "Scientist Kindred", "synergies": [ - "Toughness Matters", + "Vigilance", "Human Kindred", - "Little Fellas" + "Draw Triggers", + "Wheels", + "Toughness Matters" ], "primary_color": "Blue", "secondary_color": "Green", @@ -19198,8 +19546,8 @@ "theme": "Scion Kindred", "synergies": [ "Devoid", - "Eldrazi Kindred", "Drone Kindred", + "Eldrazi Kindred", "Mana Dork", "Ramp" ], @@ -19230,10 +19578,10 @@ "theme": "Scorpion Kindred", "synergies": [ "Deathtouch", + "Little Fellas", "Blink", "Enter the Battlefield", - "Leave the Battlefield", - "Little Fellas" + "Leave the Battlefield" ], "primary_color": "Black", "secondary_color": "Green", @@ -19320,10 +19668,10 @@ "theme": "Scry", "synergies": [ "Topdeck", - "Role token", "Enchantment Tokens", + "Role token", "Sphinx Kindred", - "Construct Kindred" + "Pilot Kindred" ], "primary_color": "Blue", "secondary_color": "White", @@ -19401,11 +19749,11 @@ "id": "serpent-kindred", "theme": "Serpent Kindred", "synergies": [ - "Cycling", - "Cost Reduction", - "Stax", - "Loot", - "X Spells" + "Leviathan Kindred", + "Kraken Kindred", + "Octopus Kindred", + "Ward", + "Cycling" ], "primary_color": "Blue", "secondary_color": "Black", @@ -19477,9 +19825,9 @@ "theme": "Shade Kindred", "synergies": [ "Little Fellas", + "Lands Matter", "Flying", - "Toughness Matters", - "Counters Matter" + "Toughness Matters" ], "primary_color": "Black", "secondary_color": "White", @@ -19537,10 +19885,10 @@ "theme": "Shaman Kindred", "synergies": [ "Kinship", + "Magecraft", "Minotaur Kindred", "Troll Kindred", - "Treefolk Kindred", - "Goblin Kindred" + "Treefolk Kindred" ], "primary_color": "Green", "secondary_color": "Black", @@ -19577,7 +19925,7 @@ "Clones", "Flash", "Little Fellas", - "Outlaw Kindred" + "Phyrexian Kindred" ], "primary_color": "Blue", "secondary_color": "Green", @@ -19610,11 +19958,11 @@ "id": "shark-kindred", "theme": "Shark Kindred", "synergies": [ + "Outlaw Kindred", "Discard Matters", + "+1/+1 Counters", "Stax", - "Card Draw", - "Toughness Matters", - "Interaction" + "Card Draw" ], "primary_color": "Blue", "secondary_color": "Green", @@ -19648,7 +19996,12 @@ { "id": "sheep-kindred", "theme": "Sheep Kindred", - "synergies": [], + "synergies": [ + "Blink", + "Enter the Battlefield", + "Leave the Battlefield", + "Little Fellas" + ], "primary_color": "White", "secondary_color": "Blue", "example_cards": [ @@ -19667,11 +20020,11 @@ "id": "shield-counters", "theme": "Shield Counters", "synergies": [ + "Citizen Kindred", "Soldier Kindred", "Counters Matter", - "Lifegain", - "Life Matters", - "Blink" + "+1/+1 Counters", + "Aggro" ], "primary_color": "White", "secondary_color": "Green", @@ -19706,7 +20059,11 @@ "id": "shrines-matter", "theme": "Shrines Matter", "synergies": [ - "Enchantments Matter" + "Enchantments Matter", + "Blink", + "Enter the Battlefield", + "Leave the Battlefield", + "Burn" ], "primary_color": "White", "secondary_color": "Black", @@ -19741,9 +20098,9 @@ "synergies": [ "Protective Effects", "Insect Kindred", - "Beast Kindred", "Enchant", - "Auras" + "Equip", + "Equipment" ], "primary_color": "Blue", "secondary_color": "Green", @@ -19782,7 +20139,7 @@ "Pirate Kindred", "Outlaw Kindred", "Flying", - "Artifacts Matter", + "Little Fellas", "Toughness Matters" ], "primary_color": "Blue", @@ -19816,11 +20173,11 @@ "id": "skeleton-kindred", "theme": "Skeleton Kindred", "synergies": [ - "Exile Matters", "Mill", - "Outlaw Kindred", + "Haste", "Warrior Kindred", - "Blink" + "Zombie Kindred", + "Outlaw Kindred" ], "primary_color": "Black", "secondary_color": "Blue", @@ -19988,7 +20345,9 @@ { "id": "sloth-kindred", "theme": "Sloth Kindred", - "synergies": [], + "synergies": [ + "Beast Kindred" + ], "primary_color": "Green", "secondary_color": "White", "example_cards": [ @@ -20007,6 +20366,7 @@ "id": "slug-kindred", "theme": "Slug Kindred", "synergies": [ + "Big Mana", "Little Fellas" ], "primary_color": "Black", @@ -20054,7 +20414,7 @@ "Deathtouch", "Archer Kindred", "Poison Counters", - "Landwalk" + "Druid Kindred" ], "primary_color": "Green", "secondary_color": "Black", @@ -20086,15 +20446,30 @@ "popularity_bucket": "Uncommon", "description": "Focuses on getting a high number of Snake creatures into play with shared payoffs (e.g., Swampwalk and Deathtouch)." }, + { + "id": "sneak", + "theme": "Sneak", + "synergies": [ + "Ninja Kindred", + "Turtle Kindred", + "Mutant Kindred", + "Spells Matter", + "Spellslinger" + ], + "primary_color": "Black", + "secondary_color": "White", + "popularity_bucket": "Rare", + "description": "Builds around Sneak leveraging synergies with Ninja Kindred and Turtle Kindred." + }, { "id": "soldier-kindred", "theme": "Soldier Kindred", "synergies": [ "Horsemanship", "Battalion", + "Valiant", "Mentor", - "Endure", - "Banding" + "Endure" ], "primary_color": "White", "secondary_color": "Blue", @@ -20131,9 +20506,10 @@ "theme": "Soltari Kindred", "synergies": [ "Shadow", - "Little Fellas", + "Soldier Kindred", "Aggro", - "Combat Matters" + "Combat Matters", + "Little Fellas" ], "primary_color": "White", "example_commanders": [ @@ -20155,10 +20531,25 @@ "popularity_bucket": "Rare", "description": "Focuses on getting a high number of Soltari creatures into play with shared payoffs (e.g., Shadow and Aggro)." }, + { + "id": "solved", + "theme": "Solved", + "synergies": [ + "Cases Matter", + "Enchantments Matter" + ], + "primary_color": "Blue", + "secondary_color": "White", + "popularity_bucket": "Rare", + "description": "Builds around Solved leveraging synergies with Cases Matter and Enchantments Matter." + }, { "id": "soul-counters", "theme": "Soul Counters", - "synergies": [], + "synergies": [ + "Counters Matter", + "Artifacts Matter" + ], "primary_color": "Black", "example_cards": [ "Séance Board", @@ -20247,8 +20638,8 @@ "id": "spawn-kindred", "theme": "Spawn Kindred", "synergies": [ - "Eldrazi Kindred", "Drone Kindred", + "Eldrazi Kindred", "Devoid", "Mana Dork", "Ramp" @@ -20283,10 +20674,10 @@ "theme": "Spectacle", "synergies": [ "Burn", + "Card Draw", "Aggro", "Combat Matters", - "Spells Matter", - "Spellslinger" + "Spells Matter" ], "primary_color": "Black", "example_commanders": [ @@ -20320,7 +20711,7 @@ "Wheels", "Flying", "Card Draw", - "Burn" + "Little Fellas" ], "primary_color": "Black", "example_commanders": [ @@ -20354,8 +20745,8 @@ "Storm", "Replicate", "Casualty", - "Demonstrate", - "Conspire" + "Conspire", + "Demonstrate" ], "primary_color": "Blue", "secondary_color": "Red", @@ -20464,8 +20855,8 @@ "Discard Matters", "Human Kindred", "Little Fellas", - "Ramp", - "Removal" + "Removal", + "Ramp" ], "primary_color": "Black", "secondary_color": "Blue", @@ -20571,11 +20962,11 @@ "id": "sphinx-kindred", "theme": "Sphinx Kindred", "synergies": [ - "Scry", "Flying", - "Topdeck", - "Conditional Draw", - "Big Mana" + "Scry", + "Ward", + "Hexproof", + "Conditional Draw" ], "primary_color": "Blue", "secondary_color": "White", @@ -20615,7 +21006,7 @@ "Hero Kindred", "Reach", "Deathtouch", - "Toughness Matters" + "Insect Kindred" ], "primary_color": "Green", "secondary_color": "White", @@ -20687,10 +21078,10 @@ "theme": "Spirit Kindred", "synergies": [ "Soulshift", - "Disturb", - "Endure", + "Afterlife", "Ki Counters", - "Afterlife" + "Endure", + "Zubera Kindred" ], "primary_color": "White", "secondary_color": "Blue", @@ -20726,7 +21117,6 @@ "synergies": [ "Spells Matter", "Spellslinger", - "Removal", "Interaction" ], "primary_color": "Blue", @@ -20760,9 +21150,9 @@ "synergies": [ "Stax", "Combat Tricks", - "Interaction", "Spells Matter", - "Spellslinger" + "Spellslinger", + "Interaction" ], "primary_color": "Black", "secondary_color": "Blue", @@ -20875,7 +21265,7 @@ "Enter the Battlefield", "Leave the Battlefield", "Human Kindred", - "Tokens Matter" + "Artifacts Matter" ], "primary_color": "White", "secondary_color": "Black", @@ -20931,11 +21321,11 @@ "id": "squirrel-kindred", "theme": "Squirrel Kindred", "synergies": [ + "Forage", "Food Token", "Food", "Warlock Kindred", - "Tokens Matter", - "Token Creation" + "Rat Kindred" ], "primary_color": "Green", "secondary_color": "Black", @@ -20970,7 +21360,9 @@ { "id": "starfish-kindred", "theme": "Starfish Kindred", - "synergies": [], + "synergies": [ + "Little Fellas" + ], "primary_color": "Black", "secondary_color": "Blue", "example_commanders": [ @@ -20994,7 +21386,7 @@ "Vehicles", "Scout Kindred", "Conditional Draw", - "Burn" + "Cat Kindred" ], "primary_color": "Black", "secondary_color": "White", @@ -21045,10 +21437,10 @@ "theme": "Station", "synergies": [ "Charge Counters", - "Flying", - "Artifacts Matter", "Counters Matter", - "Lands Matter" + "Artifacts Matter", + "Lands Matter", + "Toughness Matters" ], "primary_color": "Blue", "secondary_color": "Green", @@ -21212,11 +21604,11 @@ "id": "stun-counters", "theme": "Stun Counters", "synergies": [ + "Lore Counters", + "Ore Counters", + "Sagas Matter", "Counters Matter", - "Stax", - "Loot", - "Wizard Kindred", - "Blink" + "Stax" ], "primary_color": "Blue", "secondary_color": "White", @@ -21255,8 +21647,8 @@ "Planeswalkers", "Proliferate", "Token Creation", - "Myriad", - "Loyalty Counters" + "Compleated", + "Myriad" ], "primary_color": "White", "secondary_color": "Blue", @@ -21374,7 +21766,7 @@ "Reanimate", "Graveyard Matters", "Topdeck", - "Menace" + "Detective Kindred" ], "primary_color": "Blue", "secondary_color": "Black", @@ -21441,9 +21833,9 @@ "synergies": [ "Survival", "Human Kindred", - "Aggro", - "Combat Matters", - "Little Fellas" + "Creature Tokens", + "Token Creation", + "Tokens Matter" ], "primary_color": "Green", "secondary_color": "White", @@ -21478,7 +21870,9 @@ "synergies": [ "Blink", "Enter the Battlefield", - "Leave the Battlefield" + "Leave the Battlefield", + "Aggro", + "Combat Matters" ], "primary_color": "Black", "secondary_color": "Red", @@ -21511,9 +21905,9 @@ "synergies": [ "Time Travel", "Time Counters", + "Alien Kindred", "Exile Matters", - "Counters Matter", - "Toolbox" + "Counters Matter" ], "primary_color": "Blue", "secondary_color": "White", @@ -21545,11 +21939,11 @@ "id": "swampcycling", "theme": "Swampcycling", "synergies": [ - "Land Types Matter", + "Landcycling", + "Typecycling", "Cycling", - "Loot", - "Ramp", - "Discard Matters" + "Land Types Matter", + "Loot" ], "primary_color": "Black", "example_commanders": [ @@ -21612,21 +22006,6 @@ "popularity_bucket": "Rare", "description": "Builds around Swampwalk leveraging synergies with Wraith Kindred and Landwalk." }, - { - "id": "sweep", - "theme": "Sweep", - "synergies": [], - "primary_color": "White", - "secondary_color": "Black", - "example_cards": [ - "Barrel Down Sokenzan", - "Charge Across the Araba", - "Sink into Takenuma", - "Plow Through Reito" - ], - "popularity_bucket": "Rare", - "description": "Builds around the Sweep theme and its supporting synergies." - }, { "id": "synth-kindred", "theme": "Synth Kindred", @@ -21722,10 +22101,10 @@ "theme": "Theft", "synergies": [ "Goad", - "Sacrifice to Draw", - "Sacrifice Matters", "Treasure Token", - "Aristocrats" + "Pirate Kindred", + "Sacrifice to Draw", + "Treasure" ], "primary_color": "Blue", "secondary_color": "Black", @@ -21800,11 +22179,11 @@ "id": "threshold", "theme": "Threshold", "synergies": [ + "Mystic Kindred", "Nomad Kindred", "Minion Kindred", "Rat Kindred", - "Reanimate", - "Protection from Color" + "Centaur Kindred" ], "primary_color": "Black", "secondary_color": "Green", @@ -21835,8 +22214,8 @@ "Sacrifice Matters", "Aristocrats", "Creature Tokens", - "Token Creation", - "Tokens Matter" + "Little Fellas", + "Token Creation" ], "primary_color": "Black", "secondary_color": "White", @@ -21917,6 +22296,19 @@ "popularity_bucket": "Rare", "description": "Focuses on getting a high number of Tiefling creatures into play with shared payoffs (e.g., Blink and Enter the Battlefield)." }, + { + "id": "tiered", + "theme": "Tiered", + "synergies": [ + "Interaction", + "Spells Matter", + "Spellslinger" + ], + "primary_color": "Red", + "secondary_color": "White", + "popularity_bucket": "Rare", + "description": "Builds around Tiered leveraging synergies with Interaction and Spells Matter." + }, { "id": "time-counters", "theme": "Time Counters", @@ -21956,8 +22348,8 @@ "id": "time-travel", "theme": "Time Travel", "synergies": [ - "Time Counters", "Suspend", + "Time Counters", "Exile Matters", "Counters Matter" ], @@ -21996,7 +22388,7 @@ "Populate", "Tokens Matter", "Artifact Tokens", - "Treasure" + "Enchantment Tokens" ], "primary_color": "White", "secondary_color": "Green", @@ -22071,7 +22463,7 @@ "Creature Tokens", "Populate", "Artifact Tokens", - "Treasure" + "Enchantment Tokens" ], "primary_color": "White", "secondary_color": "Green", @@ -22104,9 +22496,9 @@ "synergies": [ "Entwine", "Proliferate", - "Citizen Kindred", "Removal", - "Convoke" + "Counterspells", + "Combat Tricks" ], "primary_color": "Green", "secondary_color": "White", @@ -22179,10 +22571,10 @@ "theme": "Toughness Matters", "synergies": [ "Defender", + "Wall Kindred", + "Atog Kindred", "Egg Kindred", - "Convert", - "Living metal", - "More Than Meets the Eye" + "Islandcycling" ], "primary_color": "Blue", "secondary_color": "White", @@ -22217,11 +22609,11 @@ "id": "toxic", "theme": "Toxic", "synergies": [ + "Mite Kindred", "Poison Counters", "Infect", - "Phyrexian Kindred", - "Counters Matter", - "Artifacts Matter" + "Proliferate", + "Phyrexian Kindred" ], "primary_color": "Green", "secondary_color": "Black", @@ -22257,7 +22649,9 @@ "theme": "Toy Kindred", "synergies": [ "Artifacts Matter", - "Little Fellas" + "Little Fellas", + "Aggro", + "Combat Matters" ], "primary_color": "White", "secondary_color": "Black", @@ -22327,9 +22721,9 @@ "id": "trample", "theme": "Trample", "synergies": [ + "Bringer Kindred", "Rhino Kindred", "Wurm Kindred", - "Hydra Kindred", "Hellion Kindred", "Leviathan Kindred" ], @@ -22367,10 +22761,10 @@ "id": "transform", "theme": "Transform", "synergies": [ + "Metalcraft", "Incubator Token", "Incubate", - "Metalcraft", - "Battles Matter", + "Magecraft", "Graveyard Matters" ], "primary_color": "White", @@ -22407,7 +22801,8 @@ "synergies": [ "Toughness Matters", "Spells Matter", - "Spellslinger" + "Spellslinger", + "Little Fellas" ], "primary_color": "Blue", "secondary_color": "Black", @@ -22515,8 +22910,8 @@ "Druid Kindred", "Shaman Kindred", "Reach", - "Land Types Matter", - "Trample" + "Landwalk", + "Land Types Matter" ], "primary_color": "Green", "secondary_color": "Black", @@ -22639,11 +23034,11 @@ "id": "turtle-kindred", "theme": "Turtle Kindred", "synergies": [ + "Sneak", + "Ninja Kindred", + "Mutant Kindred", "Ward", - "Protective Effects", - "Toughness Matters", - "Stax", - "Little Fellas" + "Vigilance" ], "primary_color": "Blue", "secondary_color": "Green", @@ -22675,15 +23070,30 @@ "popularity_bucket": "Rare", "description": "Focuses on getting a high number of Turtle creatures into play with shared payoffs (e.g., Ward and Protective Effects)." }, + { + "id": "typecycling", + "theme": "Typecycling", + "synergies": [ + "Landcycling", + "Basic landcycling", + "Plainscycling", + "Mountaincycling", + "Forestcycling" + ], + "primary_color": "Blue", + "secondary_color": "Red", + "popularity_bucket": "Niche", + "description": "Builds around Typecycling leveraging synergies with Landcycling and Basic landcycling." + }, { "id": "tyranid-kindred", "theme": "Tyranid Kindred", "synergies": [ "Ravenous", "X Spells", - "Ramp", + "Mana Dork", "+1/+1 Counters", - "Counters Matter" + "Trample" ], "primary_color": "Green", "secondary_color": "Blue", @@ -22751,8 +23161,8 @@ "id": "unconditional-draw", "theme": "Unconditional Draw", "synergies": [ - "Dredge", "Learn", + "Dredge", "Blitz", "Cantrips", "Gift" @@ -22928,11 +23338,11 @@ "id": "unicorn-kindred", "theme": "Unicorn Kindred", "synergies": [ + "Lifelink", "Lifegain", "Life Matters", - "Toughness Matters", "Little Fellas", - "Enchantments Matter" + "+1/+1 Counters" ], "primary_color": "White", "secondary_color": "Green", @@ -23003,7 +23413,8 @@ "id": "valiant", "theme": "Valiant", "synergies": [ - "Mouse Kindred" + "Mouse Kindred", + "Soldier Kindred" ], "primary_color": "White", "example_commanders": [ @@ -23029,8 +23440,8 @@ "Blood Token", "Lifegain Triggers", "Madness", - "Noble Kindred", - "Lifelink" + "Lifelink", + "Noble Kindred" ], "primary_color": "Black", "secondary_color": "White", @@ -23154,8 +23565,8 @@ "Artifacts Matter", "Crew", "Pilot Kindred", - "Convert", - "Living metal" + "Mount Kindred", + "Exhaust" ], "primary_color": "White", "secondary_color": "Blue", @@ -23189,10 +23600,11 @@ "id": "venture-into-the-dungeon", "theme": "Venture into the dungeon", "synergies": [ + "Toughness Matters", "Aggro", "Combat Matters", - "Artifacts Matter", - "Toughness Matters" + "Human Kindred", + "Blink" ], "primary_color": "White", "secondary_color": "Blue", @@ -23228,8 +23640,8 @@ "id": "verse-counters", "theme": "Verse Counters", "synergies": [ - "Counters Matter", - "Enchantments Matter" + "Enchantments Matter", + "Counters Matter" ], "primary_color": "Blue", "secondary_color": "Green", @@ -23261,10 +23673,10 @@ "theme": "Vigilance", "synergies": [ "Angel Kindred", - "Mount Kindred", - "Griffin Kindred", - "Cat Kindred", - "Crew" + "Elder Kindred", + "Scientist Kindred", + "Alien Kindred", + "Rabbit Kindred" ], "primary_color": "White", "secondary_color": "Green", @@ -23296,6 +23708,21 @@ "popularity_bucket": "Common", "description": "Builds around Vigilance leveraging synergies with Angel Kindred and Mount Kindred." }, + { + "id": "vivid", + "theme": "Vivid", + "synergies": [ + "Elemental Kindred", + "Blink", + "Enter the Battlefield", + "Leave the Battlefield", + "Big Mana" + ], + "primary_color": "Green", + "secondary_color": "Blue", + "popularity_bucket": "Rare", + "description": "Builds around Vivid leveraging synergies with Elemental Kindred and Blink." + }, { "id": "void", "theme": "Void", @@ -23304,7 +23731,7 @@ "Exile Matters", "Card Draw", "Burn", - "Aggro" + "Voltron" ], "primary_color": "Black", "example_commanders": [ @@ -23350,7 +23777,7 @@ "Auras", "Double Strike", "+1/+1 Counters", - "Enchant" + "Equipment" ], "primary_color": "Green", "secondary_color": "White", @@ -23378,6 +23805,21 @@ "popularity_bucket": "Very Common", "description": "Stacks auras, equipment, and protection on a single threat to push commander damage with layered resilience. Synergies like Equipment Matters and Auras reinforce the plan." }, + { + "id": "volver-kindred", + "theme": "Volver Kindred", + "synergies": [ + "Kicker", + "+1/+1 Counters", + "Counters Matter", + "Voltron", + "Aggro" + ], + "primary_color": "Black", + "secondary_color": "Blue", + "popularity_bucket": "Rare", + "description": "Focuses on getting a high number of Volver creatures into play with shared payoffs (e.g., Kicker and +1/+1 Counters)." + }, { "id": "wall-kindred", "theme": "Wall Kindred", @@ -23417,11 +23859,11 @@ "id": "ward", "theme": "Ward", "synergies": [ - "Demigod Kindred", + "Hero's Reward", "Disguise", "Cloak", - "Protective Effects", - "Turtle Kindred" + "Coward Kindred", + "Demigod Kindred" ], "primary_color": "Blue", "secondary_color": "Green", @@ -23456,11 +23898,11 @@ "id": "warlock-kindred", "theme": "Warlock Kindred", "synergies": [ + "Blight", "Outlaw Kindred", "Squirrel Kindred", - "Food Token", "Rat Kindred", - "Food" + "Menace" ], "primary_color": "Black", "secondary_color": "Blue", @@ -23533,10 +23975,10 @@ "theme": "Warrior Kindred", "synergies": [ "Mobilize", - "Exert", "Astartes Kindred", "Blitz", - "Jackal Kindred" + "Exert", + "Azra Kindred" ], "primary_color": "Green", "secondary_color": "Black", @@ -23567,14 +24009,30 @@ "popularity_bucket": "Very Common", "description": "Focuses on getting a high number of Warrior creatures into play with shared payoffs (e.g., Mobilize and Exert)." }, + { + "id": "waterbend", + "theme": "Waterbend", + "synergies": [ + "Waterbending", + "Bending", + "Ally Kindred", + "Cost Reduction", + "Warrior Kindred" + ], + "primary_color": "Blue", + "secondary_color": "Black", + "popularity_bucket": "Rare", + "description": "Builds around Waterbend leveraging synergies with Waterbending and Bending." + }, { "id": "waterbending", "theme": "Waterbending", "synergies": [ + "Waterbend", "Bending", + "Ally Kindred", "Cost Reduction", - "Card Draw", - "Big Mana" + "Warrior Kindred" ], "primary_color": "Blue", "secondary_color": "White", @@ -23625,7 +24083,8 @@ "theme": "Web-slinging", "synergies": [ "Hero Kindred", - "Spider Kindred" + "Spider Kindred", + "Human Kindred" ], "primary_color": "White", "secondary_color": "Green", @@ -23659,7 +24118,13 @@ { "id": "weird-kindred", "theme": "Weird Kindred", - "synergies": [], + "synergies": [ + "Toughness Matters", + "Spells Matter", + "Spellslinger", + "Little Fellas", + "Big Mana" + ], "primary_color": "Blue", "secondary_color": "Black", "example_commanders": [ @@ -23687,7 +24152,7 @@ "Nightbound", "Wolf Kindred", "Transform", - "Eldrazi Kindred" + "Horror Kindred" ], "primary_color": "Green", "secondary_color": "Black", @@ -23718,8 +24183,8 @@ "Flying", "Big Mana", "Toughness Matters", - "Aggro", - "Combat Matters" + "Blink", + "Enter the Battlefield" ], "primary_color": "Blue", "example_commanders": [ @@ -23814,6 +24279,8 @@ "id": "will-of-the-council", "theme": "Will of the council", "synergies": [ + "Card Draw", + "Interaction", "Spells Matter", "Spellslinger" ], @@ -23912,8 +24379,8 @@ "Moonfolk Kindred", "Vedalken Kindred", "Otter Kindred", - "Magecraft", - "Merfolk Kindred" + "Merfolk Kindred", + "Magecraft" ], "primary_color": "Blue", "secondary_color": "Black", @@ -23945,27 +24412,15 @@ "popularity_bucket": "Very Common", "description": "Focuses on getting a high number of Wizard creatures into play with shared payoffs (e.g., Moonfolk Kindred and Vedalken Kindred)." }, - { - "id": "wizardcycling", - "theme": "Wizardcycling", - "synergies": [], - "primary_color": "Blue", - "example_cards": [ - "Step Through", - "Vedalken Aethermage" - ], - "popularity_bucket": "Rare", - "description": "Builds around the Wizardcycling theme and its supporting synergies." - }, { "id": "wolf-kindred", "theme": "Wolf Kindred", "synergies": [ "Werewolf Kindred", + "Daybound", + "Nightbound", "Flash", - "Creature Tokens", - "Token Creation", - "Tokens Matter" + "Creature Tokens" ], "primary_color": "Green", "secondary_color": "Black", @@ -24037,9 +24492,9 @@ "synergies": [ "Sacrifice Matters", "Aristocrats", - "Little Fellas", - "Aggro", - "Combat Matters" + "Creature Tokens", + "Token Creation", + "Tokens Matter" ], "primary_color": "Black", "secondary_color": "Blue", @@ -24074,7 +24529,8 @@ "Swampwalk", "Landwalk", "Lands Matter", - "Big Mana" + "Big Mana", + "Aggro" ], "primary_color": "Black", "example_commanders": [ @@ -24112,8 +24568,8 @@ "Trample", "Phyrexian Kindred", "Big Mana", - "+1/+1 Counters", - "Aggro" + "Aggro", + "Combat Matters" ], "primary_color": "Green", "secondary_color": "Black", @@ -24146,11 +24602,11 @@ "id": "x-spells", "theme": "X Spells", "synergies": [ - "Affinity", "Ravenous", + "Undaunted", + "Affinity", "Firebending", - "Cost Reduction", - "Undaunted" + "Cost Reduction" ], "primary_color": "Blue", "secondary_color": "Green", @@ -24304,7 +24760,10 @@ "id": "plus1-plus0-counters", "theme": "\\+1/\\+0 Counters", "synergies": [ - "Counters Matter" + "Counters Matter", + "Toughness Matters", + "Little Fellas", + "Big Mana" ], "primary_color": "Red", "secondary_color": "Black", @@ -24329,7 +24788,9 @@ { "id": "plus2-plus2-counters", "theme": "\\+2/\\+2 Counters", - "synergies": [], + "synergies": [ + "Counters Matter" + ], "primary_color": "Black", "secondary_color": "Green", "example_commanders": [ @@ -24350,21 +24811,3323 @@ } ], "frequencies_by_base_color": { - "white": {}, - "blue": {}, - "black": {}, - "red": {}, - "green": {} + "white": { + "Aggro": 2142, + "Combat Matters": 2142, + "Creature Tokens": 817, + "Nephilim Kindred": 4, + "Sand Kindred": 2, + "Token Creation": 961, + "Tokens Matter": 976, + "Burn": 498, + "Haste": 72, + "Historics Matter": 1227, + "Human Kindred": 1757, + "Legends Matter": 1227, + "Soldier Kindred": 837, + "Toughness Matters": 1371, + "Vigilance": 428, + "Angel Kindred": 289, + "Big Mana": 1729, + "Deathtouch": 25, + "Flying": 1011, + "Life Matters": 1649, + "Lifegain": 1648, + "Lifelink": 418, + "Phyrexian Kindred": 85, + "Topdeck": 298, + "+1/+1 Counters": 733, + "-1/-1 Counters": 66, + "Counters Matter": 1077, + "Horror Kindred": 22, + "Infect": 52, + "Midrange": 175, + "Planeswalkers": 158, + "Proliferate": 16, + "Superfriends": 158, + "Voltron": 1331, + "Little Fellas": 2434, + "Spells Matter": 1633, + "Spellslinger": 1633, + "Artifact Tokens": 215, + "Artifacts Matter": 1101, + "Thopter Kindred": 8, + "Toolbox": 143, + "Blink": 1077, + "Control": 372, + "Enter the Battlefield": 1077, + "Leave the Battlefield": 1088, + "Mill": 656, + "Aristocrats": 277, + "Enchantments Matter": 1219, + "Sacrifice Matters": 277, + "Spirit Kindred": 330, + "Card Draw": 651, + "Mana Rock": 68, + "Ramp": 194, + "Sacrifice to Draw": 74, + "Interaction": 1293, + "Removal": 580, + "Unconditional Draw": 224, + "Cleric Kindred": 466, + "Dog Kindred": 46, + "Mana Dork": 56, + "Morph": 27, + "Warrior Kindred": 306, + "Lands Matter": 467, + "Auras": 458, + "Clones": 86, + "Demigod Kindred": 3, + "Menace": 29, + "Reanimate": 345, + "Zombie Kindred": 51, + "Dragon Kindred": 83, + "Halfling Kindred": 26, + "Lifegain Triggers": 54, + "Outlaw Kindred": 98, + "Rogue Kindred": 54, + "Wheels": 123, + "Reach": 28, + "Shaman Kindred": 28, + "Treefolk Kindred": 17, + "Cycling": 101, + "Discard Matters": 185, + "Loot": 123, + "Cost Reduction": 132, + "Druid Kindred": 35, + "X Spells": 195, + "Board Wipes": 205, + "Trample": 90, + "Fungus Kindred": 3, + "Saproling Kindred": 13, + "Land Types Matter": 91, + "Elephant Kindred": 43, + "Stax": 641, + "Corrupted": 7, + "Poison Counters": 30, + "Toxic": 10, + "Centaur Kindred": 19, + "Hexproof": 84, + "Indestructible": 210, + "Insect Kindred": 12, + "Nightmare Kindred": 13, + "Protective Effects": 553, + "Advisor Kindred": 67, + "Elf Kindred": 88, + "Pingers": 97, + "God Kindred": 29, + "Bard Kindred": 14, + "Conditional Draw": 165, + "Sagas Matter": 84, + "Wizard Kindred": 214, + "Kicker": 51, + "Volver Kindred": 3, + "Beast Kindred": 60, + "Cat Kindred": 193, + "Mutate": 9, + "Fuse": 8, + "Citizen Kindred": 63, + "Lore Counters": 59, + "Ore Counters": 69, + "Djinn Kindred": 10, + "Rhino Kindred": 24, + "First strike": 184, + "Frog Kindred": 3, + "Landfall": 30, + "Plant Kindred": 5, + "Mite Kindred": 9, + "Scout Kindred": 90, + "Assassin Kindred": 22, + "Exile Matters": 175, + "Memory Counters": 1, + "Berserker Kindred": 4, + "Goblin Kindred": 16, + "Demon Kindred": 14, + "Mobilize": 7, + "Orc Kindred": 20, + "Draw Triggers": 113, + "Knight Kindred": 354, + "Theft": 32, + "Treasure": 29, + "Treasure Token": 32, + "Eminence": 4, + "Vampire Kindred": 85, + "Double strike": 68, + "Graveyard Matters": 7, + "Magecraft": 8, + "Transform": 121, + "Warlock Kindred": 27, + "Ally Kindred": 110, + "Bending": 21, + "Firebending": 3, + "Noble Kindred": 59, + "Samurai Kindred": 48, + "Artificer Kindred": 82, + "Construct Kindred": 25, + "Gnome Kindred": 15, + "Experience Counters": 8, + "Elder Kindred": 19, + "Giant Kindred": 64, + "Politics": 91, + "Enchant": 313, + "Combat Tricks": 269, + "Scry": 99, + "Raid": 4, + "Bounty Counters": 2, + "Convert": 9, + "Eye Kindred": 10, + "Living metal": 7, + "More Than Meets the Eye": 8, + "Robot Kindred": 35, + "Monarch": 18, + "Flashback": 37, + "Dinosaur Kindred": 52, + "Mercenary Kindred": 23, + "Equipment Matters": 295, + "Dwarf Kindred": 64, + "Elemental Kindred": 88, + "Atog Kindred": 4, + "Airbending": 12, + "Avatar Kindred": 52, + "Earthbending": 5, + "Waterbending": 5, + "Eldrazi Kindred": 8, + "Bringer Kindred": 5, + "Bestow": 13, + "Manticore Kindred": 1, + "Golem Kindred": 23, + "Hero Kindred": 50, + "Spider Kindred": 21, + "Illusion Kindred": 4, + "Earthbend": 5, + "Cantrips": 125, + "Food": 43, + "Freerunning": 2, + "Spell Copy": 29, + "Enchantment Tokens": 21, + "Shrines Matter": 6, + "Bear Kindred": 9, + "Mutant Kindred": 18, + "Ninja Kindred": 19, + "Turtle Kindred": 11, + "Charge Counters": 14, + "Station": 7, + "Battles Matter": 11, + "Kavu Kindred": 4, + "Companion": 6, + "Elk Kindred": 11, + "Flash": 144, + "Cascade": 9, + "Rooms Matter": 12, + "Crew": 25, + "Exhaust": 1, + "Vehicles": 83, + "Impulse": 24, + "Changeling": 14, + "Shapeshifter Kindred": 21, + "Age Counters": 21, + "Cumulative upkeep": 17, + "Hexproof from": 7, + "Converge": 3, + "Crystal Counters": 1, + "Hydra Kindred": 3, + "Protection": 85, + "Protection from Quality": 62, + "Scarecrow Kindred": 1, + "Sliver Kindred": 36, + "Encore": 6, + "Sphinx Kindred": 20, + "Wall Kindred": 49, + "Devoid": 4, + "Phasing": 19, + "Myr Kindred": 5, + "Hideaway": 4, + "Backgrounds Matter": 12, + "Phoenix Kindred": 2, + "Faerie Kindred": 20, + "Surveil": 20, + "Ward": 72, + "Astartes Kindred": 12, + "Rampage": 2, + "Loyalty Counters": 22, + "Counterspells": 54, + "Lairs Matter": 3, + "Vedalken Kindred": 10, + "Gates Matter": 49, + "Clue Token": 42, + "Inquisitor Kindred": 1, + "Investigate": 42, + "Connive": 7, + "Octopus Kindred": 3, + "Bird Kindred": 220, + "Warp": 9, + "Finality Counters": 9, + "Soul Counters": 1, + "Venture into the dungeon": 14, + "Protection from Color": 116, + "Rat Kindred": 5, + "Metathran Kindred": 1, + "Council's dilemma": 2, + "Gargoyle Kindred": 19, + "Affinity": 16, + "Scientist Kindred": 9, + "Doctor Kindred": 20, + "Egg Kindred": 2, + "Rabbit Kindred": 28, + "Backup": 7, + "Fox Kindred": 44, + "Raccoon Kindred": 1, + "Junk Token": 2, + "Junk Tokens": 3, + "Populate": 16, + "Lizard Kindred": 4, + "Deserts Matter": 14, + "Landwalk": 14, + "Token Modification": 14, + "Detective Kindred": 31, + "Dryad Kindred": 9, + "Goad": 12, + "Hamster Kindred": 2, + "Ranger Kindred": 17, + "Shroud": 26, + "Discover": 5, + "Parley": 4, + "Griffin Kindred": 50, + "Food Token": 35, + "Archer Kindred": 29, + "Start your engines!": 10, + "Boast": 5, + "Melee": 6, + "Monk Kindred": 81, + "Formidable": 1, + "Wolf Kindred": 7, + "Fight": 6, + "Shield Counters": 16, + "Exalted": 22, + "Alliance": 5, + "Yeti Kindred": 1, + "Plot": 5, + "Nymph Kindred": 6, + "Fish Kindred": 4, + "Hippo Kindred": 2, + "Phelddagrif Kindred": 2, + "Merfolk Kindred": 27, + "Mouse Kindred": 18, + "Fabricate": 5, + "Servo Kindred": 13, + "Energy": 31, + "Energy Counters": 29, + "Resource Engine": 31, + "Efreet Kindred": 3, + "Prowess": 13, + "Suspend": 23, + "Time Counters": 33, + "Chimera Kindred": 3, + "Performer Kindred": 7, + "Kirin Kindred": 8, + "Flurry": 6, + "Convoke": 39, + "Bolster": 15, + "Defender": 68, + "Minotaur Kindred": 10, + "Stun Counters": 12, + "Craft": 6, + "Alien Kindred": 5, + "Card Selection": 10, + "Explore": 9, + "Archon Kindred": 20, + "Coin Counters": 1, + "Kor Kindred": 88, + "Bat Kindred": 18, + "Haunt": 6, + "Inkling Kindred": 6, + "Incubate": 12, + "Incubator Token": 13, + "Exploit": 1, + "Overload": 3, + "Sneak": 7, + "Thrull Kindred": 10, + "Max speed": 9, + "Evoke": 9, + "Incarnation Kindred": 8, + "Mentor": 12, + "Partner": 28, + "Partner with": 13, + "Pilot Kindred": 28, + "Aftermath": 9, + "Hag Kindred": 3, + "Kithkin Kindred": 85, + "Wither": 2, + "Revolt": 8, + "Afterlife": 8, + "Pillowfort": 25, + "Equip": 68, + "Ghostform Counters": 1, + "Entwine": 7, + "Extort": 11, + "Life to Draw": 1, + "Lifeloss": 9, + "Lifeloss Triggers": 9, + "Will of the council": 4, + "Persist": 7, + "Peasant Kindred": 27, + "Cleave": 3, + "Horse Kindred": 14, + "Nomad Kindred": 21, + "Threshold": 23, + "Unearth": 6, + "Battle Cry": 7, + "Learn": 5, + "Disguise": 12, + "Skeleton Kindred": 3, + "Blood Token": 3, + "Miracle": 8, + "Fate Counters": 1, + "Constellation": 10, + "Eerie": 5, + "Friends Forever": 4, + "Exert": 10, + "Compleated": 2, + "Outlast": 8, + "Wurm Kindred": 7, + "Conspire": 3, + "Equipment": 69, + "Support": 9, + "Mount Kindred": 24, + "Troll Kindred": 2, + "Forestwalk": 1, + "Renown": 9, + "Myriad": 7, + "Hippogriff Kindred": 7, + "Saddle": 13, + "Coven": 13, + "Role token": 10, + "Unicorn Kindred": 31, + "Monstrosity": 5, + "Jellyfish Kindred": 2, + "Rally": 8, + "Umbra armor": 5, + "Protection from Creature Type": 8, + "Ouphe Kindred": 1, + "Mystic Kindred": 5, + "Forestcycling": 1, + "Landcycling": 16, + "Plainscycling": 12, + "Typecycling": 16, + "Pegasus Kindred": 25, + "Rebel Kindred": 64, + "Survival": 7, + "Survivor Kindred": 8, + "Storage Counters": 4, + "Satyr Kindred": 1, + "Flanking": 17, + "Enrage": 4, + "Web-slinging": 5, + "Basic landcycling": 4, + "Training": 7, + "Depletion Counters": 4, + "Hyena Kindred": 2, + "Boar Kindred": 4, + "Domain": 8, + "Suspect": 1, + "Heroic": 16, + "Toy Kindred": 5, + "Modular": 5, + "Celebration": 6, + "Muster Counters": 1, + "For Mirrodin!": 6, + "Battalion": 13, + "Radiance": 6, + "Ox Kindred": 16, + "Daybound": 1, + "Nightbound": 1, + "Werewolf Kindred": 2, + "Employee Kindred": 4, + "Squirrel Kindred": 3, + "Quest Counters": 10, + "Strive": 5, + "Cyclops Kindred": 1, + "Split second": 3, + "Metalcraft": 10, + "Partner - Father & Son": 1, + "Gremlin Kindred": 1, + "Spell mastery": 4, + "Escape": 3, + "Sloth Kindred": 4, + "Dash": 1, + "Valiant": 6, + "Shadow": 12, + "Soltari Kindred": 10, + "Clown Kindred": 3, + "Retrace": 2, + "Airbend": 12, + "Waterbend": 4, + "Detain": 9, + "Feather Counters": 3, + "Embalm": 8, + "Banding": 20, + "Filibuster Counters": 1, + "Disturb": 14, + "Squid Kindred": 1, + "Sleight Counters": 1, + "Cid, Timeless Artificer": 1, + "Multiple Copies": 3, + "Drake Kindred": 7, + "Addendum": 7, + "Foretell": 15, + "Moonfolk Kindred": 3, + "Gold Token": 6, + "Skulk": 1, + "Bribery Counters": 1, + "Glimmer Kindred": 5, + "Palliation Counters": 1, + "Starfish Kindred": 1, + "Forecast": 8, + "Homunculus Kindred": 3, + "Dream Counters": 2, + "Vanishing": 7, + "Ascend": 7, + "Map Token": 2, + "Brain Counters": 1, + "Crab Kindred": 1, + "Awaken": 5, + "Islandcycling": 1, + "Shark Kindred": 1, + "Drone Kindred": 1, + "Islandwalk": 1, + "Meld": 2, + "Fear": 2, + "Serpent Kindred": 1, + "Kick Counters": 1, + "Multikicker": 4, + "Guest Kindred": 2, + "Choose a background": 5, + "Lammasu Kindred": 3, + "Channel": 7, + "Emerge": 1, + "Eternalize": 4, + "Leech Kindred": 1, + "Buyback": 9, + "Join forces": 1, + "Replacement Draw": 2, + "Endure": 3, + "Delirium": 10, + "Lieutenant": 4, + "Bushido": 20, + "Enlist": 5, + "Assembly-Worker Kindred": 2, + "Arrow Counters": 1, + "Bargain": 2, + "Adamant": 3, + "Oil Counters": 3, + "Armadillo Kindred": 1, + "Rebound": 9, + "Storm": 3, + "Gold Counters": 1, + "Prototype": 3, + "Megamorph": 5, + "Amplify": 2, + "Spellshaper Kindred": 10, + "Doctor's Companion": 8, + "Doctor's companion": 8, + "Caves Matter": 2, + "Protection from Creatures": 7, + "Ninjutsu": 1, + "Escalate": 3, + "Splice": 5, + "Blessing Counters": 1, + "Fateful hour": 6, + "Reinforce": 5, + "Soulbond": 4, + "Sheep Kindred": 4, + "Weasel Kindred": 1, + "Possum Kindred": 1, + "Assist": 4, + "Unity Counters": 1, + "Licid Kindred": 2, + "Camel Kindred": 5, + "Lhurgoyf Kindred": 1, + "Devour": 1, + "Goat Kindred": 8, + "Level Counters": 8, + "Level Up": 7, + "Cases Matter": 4, + "Solved": 3, + "Omen Counters": 1, + "Behold": 3, + "Champion": 2, + "Collection Counters": 1, + "Ogre Kindred": 2, + "Jump": 1, + "Mountainwalk": 2, + "Tiered": 2, + "Reconfigure": 3, + "Flagbearer Kindred": 3, + "Open an Attraction": 2, + "Strife Counters": 1, + "Gift": 6, + "Coward Kindred": 2, + "Job select": 5, + "Hope Counters": 1, + "Fade Counters": 2, + "Fading": 2, + "Provoke": 3, + "Delay Counters": 1, + "Jackal Kindred": 1, + "Intervention Counters": 1, + "Sculpture Kindred": 1, + "Coyote Kindred": 1, + "Praetor Kindred": 3, + "Squad": 6, + "Epic": 1, + "Blight": 3, + "Demonstrate": 1, + "Imprint": 1, + "Judgment Counters": 1, + "Ki Counters": 2, + "Swampwalk": 2, + "Hunger Counters": 1, + "Cost Scaling": 5, + "Modal": 5, + "Spree": 5, + "Offspring": 4, + "Madness": 2, + "Healing Counters": 2, + "Gith Kindred": 2, + "Inspired": 2, + "Antelope Kindred": 3, + "Powerstone Token": 4, + "Horsemanship": 7, + "Snake Kindred": 1, + "Manifest": 6, + "Hare Apparent": 1, + "Task Counters": 1, + "Echo": 3, + "Slith Kindred": 2, + "Hoofprint Counters": 1, + "Soulshift": 5, + "Javelin Counters": 1, + "Credit Counters": 1, + "Tiefling Kindred": 1, + "Duty Counters": 1, + "Valor Counters": 1, + "-1/-0 Counters": 1, + "Ravenous": 1, + "Divinity Counters": 2, + "Vivid": 1, + "Kinship": 2, + "-0/-1 Counters": 1, + "Deserter Kindred": 1, + "Adapt": 1, + "Chroma": 2, + "Aegis Counters": 1, + "Read Ahead": 2, + "Reprieve Counters": 1, + "Germ Kindred": 1, + "Living weapon": 1, + "Cohort": 4, + "Morbid": 1, + "Spore Counters": 2, + "Incarnation Counters": 1, + "Clash": 5, + "Improvise": 1, + "Grandeur": 1, + "Tribute": 1, + "Carrion Counters": 1, + "Impending": 1, + "Synth Kindred": 1, + "Will of the Planeswalkers": 1, + "Offering": 1, + "Devotion Counters": 1, + "Vow Counters": 1, + "Study Counters": 1, + "Isolation Counters": 1, + "\\+0/\\+1 Counters": 3, + "Training Counters": 1, + "Verse Counters": 2, + "Shade Kindred": 1, + "Zubera Kindred": 1, + "Otter Kindred": 1, + "Echo Counters": 1, + "Intimidate": 1, + "Reflection Kindred": 1, + "Story Counters": 1, + "Undaunted": 1, + "Harpy Kindred": 1, + "Recover": 1, + "Ripple": 1, + "Tempest Hawk": 1, + "Tempting offer": 2, + "Collect evidence": 1, + "Enlightened Counters": 1, + "Spheres Matter": 1, + "Time Travel": 2, + "Currency Counters": 1, + "Trap Counters": 1, + "Cloak": 2, + "Manifest dread": 1, + "Custodes Kindred": 1, + "Invitation Counters": 1, + "Monger Kindred": 1, + "Ice Counters": 1 + }, + "blue": { + "Aggro": 1618, + "Card Draw": 1635, + "Combat Matters": 1618, + "Discard Matters": 481, + "Draw Triggers": 316, + "Little Fellas": 2214, + "Nephilim Kindred": 4, + "Wheels": 361, + "Big Mana": 2043, + "Cascade": 18, + "Exile Matters": 245, + "Historics Matter": 1165, + "Legends Matter": 1165, + "Ogre Kindred": 9, + "Topdeck": 656, + "Trample": 87, + "Wizard Kindred": 823, + "Angel Kindred": 31, + "Deathtouch": 34, + "Flying": 1190, + "Life Matters": 295, + "Lifegain": 294, + "Lifelink": 67, + "Phyrexian Kindred": 71, + "Vigilance": 147, + "+1/+1 Counters": 487, + "-1/-1 Counters": 70, + "Counters Matter": 884, + "Horror Kindred": 80, + "Infect": 49, + "Midrange": 102, + "Planeswalkers": 161, + "Proliferate": 36, + "Superfriends": 161, + "Voltron": 928, + "Spells Matter": 2403, + "Spellslinger": 2403, + "Artifact Tokens": 223, + "Artifacts Matter": 1038, + "Burn": 327, + "Creature Tokens": 417, + "Human Kindred": 1031, + "Thopter Kindred": 28, + "Token Creation": 579, + "Tokens Matter": 582, + "Toolbox": 118, + "Blink": 863, + "Control": 880, + "Enter the Battlefield": 863, + "Leave the Battlefield": 866, + "Mill": 933, + "Loot": 358, + "Morph": 50, + "Toughness Matters": 1455, + "Kicker": 64, + "Volver Kindred": 3, + "Lands Matter": 545, + "Shaman Kindred": 44, + "Turtle Kindred": 41, + "Enchantments Matter": 940, + "Interaction": 1132, + "Lore Counters": 48, + "Ore Counters": 55, + "Reanimate": 797, + "Removal": 393, + "Sagas Matter": 64, + "Beast Kindred": 78, + "Elemental Kindred": 185, + "Mutate": 12, + "Nightmare Kindred": 27, + "Gorgon Kindred": 2, + "Board Wipes": 104, + "Conditional Draw": 372, + "Ramp": 252, + "Counterspells": 419, + "Flash": 241, + "Snake Kindred": 45, + "Stax": 1176, + "Surveil": 85, + "Menace": 34, + "Ooze Kindred": 10, + "Outlaw Kindred": 369, + "Protective Effects": 351, + "Rogue Kindred": 247, + "Ward": 108, + "Clue Token": 69, + "Gates Matter": 75, + "Investigate": 69, + "Sacrifice to Draw": 134, + "Cycling": 108, + "Fungus Kindred": 2, + "Frog Kindred": 39, + "Noble Kindred": 55, + "Aetherborn Kindred": 2, + "Cost Reduction": 228, + "Theft": 166, + "X Spells": 312, + "Ranger Kindred": 6, + "Aristocrats": 187, + "Sacrifice Matters": 177, + "Scientist Kindred": 27, + "Treasure": 43, + "Treasure Token": 44, + "Dinosaur Kindred": 17, + "Hexproof": 110, + "Indestructible": 35, + "Mutant Kindred": 60, + "God Kindred": 26, + "Unconditional Draw": 623, + "Dragon Kindred": 116, + "Renew": 4, + "Zombie Kindred": 153, + "Warrior Kindred": 106, + "Impulse": 38, + "Bard Kindred": 9, + "Elf Kindred": 54, + "Faerie Kindred": 127, + "Clones": 221, + "Avatar Kindred": 48, + "Rat Kindred": 17, + "Spell Copy": 138, + "Demon Kindred": 19, + " Blood Counters": 2, + "Blood Token": 4, + "Vampire Kindred": 29, + "Ape Kindred": 2, + "Leviathan Kindred": 29, + "Mana Rock": 75, + "Druid Kindred": 35, + "Mana Dork": 114, + "Delve": 11, + "Ninja Kindred": 43, + "Pingers": 87, + "Spirit Kindred": 208, + "Rad Counters": 7, + "Insect Kindred": 18, + "Lhurgoyf Kindred": 2, + "Shapeshifter Kindred": 89, + "Pirate Kindred": 94, + "Populate": 2, + "Land Types Matter": 66, + "Hydra Kindred": 6, + "Dryad Kindred": 2, + "Atog Kindred": 5, + "Airbending": 3, + "Ally Kindred": 47, + "Bending": 28, + "Earthbending": 2, + "Firebending": 5, + "Landfall": 27, + "Transform": 118, + "Waterbending": 22, + "Eldrazi Kindred": 51, + "Experience Counters": 5, + "Bringer Kindred": 5, + "Construct Kindred": 27, + "Auras": 403, + "Bestow": 9, + "Equipment Matters": 118, + "First strike": 29, + "Manticore Kindred": 1, + "Golem Kindred": 16, + "Haste": 51, + "Hero Kindred": 32, + "Spider Kindred": 13, + "Citizen Kindred": 19, + "Illusion Kindred": 108, + "Knight Kindred": 53, + "Earthbend": 2, + "Soldier Kindred": 150, + "Cantrips": 259, + "Food": 14, + "Assassin Kindred": 30, + "Freerunning": 4, + "Enchant": 346, + "Enchantment Tokens": 16, + "Shrines Matter": 6, + "Bear Kindred": 5, + "Charge Counters": 17, + "Robot Kindred": 38, + "Station": 7, + "Battles Matter": 11, + "Cat Kindred": 29, + "Kavu Kindred": 4, + "Companion": 4, + "Elk Kindred": 7, + "Scry": 190, + "Combat Tricks": 176, + "Saproling Kindred": 3, + "Rooms Matter": 19, + "Warlock Kindred": 27, + "Crew": 34, + "Exhaust": 13, + "Vehicles": 62, + "Politics": 73, + "Changeling": 15, + "Age Counters": 37, + "Cumulative upkeep": 26, + "Hexproof from": 5, + "Converge": 7, + "Crystal Counters": 1, + "Protection": 30, + "Protection from Quality": 16, + "Scarecrow Kindred": 2, + "Sliver Kindred": 29, + "Encore": 6, + "Sphinx Kindred": 82, + "Wall Kindred": 47, + "Cleric Kindred": 45, + "Eminence": 3, + "Devoid": 44, + "Phasing": 44, + "Myr Kindred": 6, + "Hideaway": 4, + "Backgrounds Matter": 14, + "Phoenix Kindred": 1, + "Astartes Kindred": 6, + "Elder Kindred": 18, + "Rampage": 2, + "Loyalty Counters": 18, + "Lairs Matter": 3, + "Vedalken Kindred": 75, + "Inquisitor Kindred": 2, + "Samurai Kindred": 3, + "Connive": 21, + "Octopus Kindred": 54, + "Double strike": 8, + "Bird Kindred": 220, + "Warp": 11, + "Finality Counters": 5, + "Soul Counters": 1, + "Giant Kindred": 27, + "Lifegain Triggers": 5, + "Advisor Kindred": 60, + "Venture into the dungeon": 9, + "Convert": 5, + "Eye Kindred": 7, + "More Than Meets the Eye": 5, + "Protection from Color": 30, + "Metathran Kindred": 8, + "Artificer Kindred": 104, + "Council's dilemma": 3, + "Gargoyle Kindred": 9, + "Affinity": 25, + "Cyberman Kindred": 4, + "Serpent Kindred": 58, + "Fuse": 8, + "Friends Forever": 3, + "Dalek Kindred": 1, + "Alien Kindred": 14, + "Collection Counters": 1, + "Unearth": 9, + "Corruption Counters": 1, + "Goblin Kindred": 18, + "Wraith Kindred": 3, + "Goad": 20, + "Devil Kindred": 1, + "Cage Counters": 1, + "Dethrone": 4, + "Mayhem": 2, + "Amass": 22, + "Army Kindred": 21, + "Orc Kindred": 23, + "Specter Kindred": 5, + "Drake Kindred": 93, + "Landwalk": 50, + "Swampwalk": 2, + "Archer Kindred": 6, + "Equip": 26, + "Equipment": 30, + "Snail Kindred": 1, + "Tiefling Kindred": 4, + "Reach": 17, + "Doctor Kindred": 25, + "Elephant Kindred": 6, + "Cyclops Kindred": 4, + "Homunculus Kindred": 26, + "Enrage": 1, + "Merfolk Kindred": 289, + "Fight": 6, + "Scout Kindred": 57, + "Gremlin Kindred": 1, + "Raid": 11, + "Tyranid Kindred": 14, + "Harmonize": 5, + "Energy": 42, + "Energy Counters": 39, + "Resource Engine": 42, + "Monkey Kindred": 3, + "Ferocious": 4, + "Storm": 11, + "Defender": 88, + "Shroud": 48, + "Food Token": 10, + "Shield Counters": 11, + "Discover": 5, + "Exalted": 12, + "Alliance": 1, + "Yeti Kindred": 3, + "Mercenary Kindred": 3, + "Plot": 14, + "Nymph Kindred": 5, + "Detective Kindred": 47, + "Fish Kindred": 52, + "Fox Kindred": 8, + "Rabbit Kindred": 4, + "Rhino Kindred": 3, + "Hippo Kindred": 4, + "Phelddagrif Kindred": 2, + "Monk Kindred": 44, + "Token Modification": 8, + "Basic landcycling": 6, + "Landcycling": 16, + "Typecycling": 18, + "Djinn Kindred": 48, + "Islandwalk": 25, + "Replacement Draw": 3, + "Cipher": 10, + "Harpy Kindred": 1, + "Transmute": 9, + "Aftermath": 9, + "Flashback": 48, + "Living metal": 4, + "Ascend": 9, + "Evoke": 9, + "Incarnation Kindred": 7, + "Employee Kindred": 4, + "Open an Attraction": 5, + "Jump": 8, + "Jump-start": 8, + "Inspired": 7, + "Storage Counters": 4, + "Threshold": 13, + "Cloak": 4, + "Hit Counters": 1, + "Disguise": 8, + "Ninjutsu": 18, + "Drone Kindred": 29, + "Ingest": 5, + "Eerie": 9, + "Monarch": 12, + "Persist": 5, + "Kraken Kindred": 35, + "Waterbend": 22, + "Fear": 4, + "Islandcycling": 10, + "Swampcycling": 1, + "Leech Kindred": 3, + "Stun Counters": 61, + "Skeleton Kindred": 8, + "Azra Kindred": 1, + "Lord of the Nazgûl": 1, + "Multiple Copies": 3, + "Crocodile Kindred": 7, + "Clash": 8, + "Conspire": 4, + "Collect evidence": 8, + "Wither": 1, + "Minion Kindred": 3, + "Shark Kindred": 16, + "Depletion Counters": 4, + "Siren Kindred": 23, + "Partner": 29, + "Pillowfort": 7, + "Exploit": 9, + "Forestwalk": 2, + "Deserts Matter": 8, + "Page Counters": 2, + "Descend": 8, + "Body Thief": 1, + "Takeover Counters": 1, + "Slime Counters": 2, + "Slug Kindred": 1, + "Entwine": 6, + "Partner with": 15, + "Whale Kindred": 19, + "Wolf Kindred": 2, + "Processor Kindred": 5, + "Life to Draw": 1, + "Assist": 5, + "Intimidate": 1, + "Toxic": 4, + "Bat Kindred": 1, + "Poison Counters": 11, + "Beholder Kindred": 1, + "Mouse Kindred": 1, + "Dwarf Kindred": 4, + "Fabricate": 1, + "Servo Kindred": 4, + "Efreet Kindred": 10, + "Prowess": 47, + "Suspend": 31, + "Time Counters": 41, + "Chimera Kindred": 11, + "Performer Kindred": 7, + "Kirin Kindred": 2, + "Flurry": 4, + "Convoke": 20, + "Bolster": 1, + "Minotaur Kindred": 5, + "Craft": 7, + "Gnome Kindred": 5, + "Graveyard Matters": 7, + "Treefolk Kindred": 1, + "Adapt": 10, + "Guest Kindred": 4, + "Slumber Counters": 1, + "Troll Kindred": 3, + "Fractal Kindred": 10, + "Ox Kindred": 1, + "Lizard Kindred": 16, + "Flood Counters": 5, + "Channel": 9, + "Secret council": 4, + "Card Selection": 14, + "Explore": 13, + "Spawn Kindred": 8, + "Evolve": 11, + "Otter Kindred": 21, + "Manifest": 18, + "Constellation": 7, + "Cockatrice Kindred": 1, + "Monstrosity": 5, + "Will of the council": 4, + "Ouphe Kindred": 2, + "Vivid": 4, + "Salamander Kindred": 10, + "Halfling Kindred": 4, + "Croak Counters": 1, + "Manifest dread": 11, + "Jellyfish Kindred": 23, + "Magecraft": 12, + "Plant Kindred": 6, + "Study Counters": 1, + "Emerge": 7, + "Escape": 7, + "Growth Counters": 2, + "Domain": 8, + "Gift": 5, + "Everything Counters": 1, + "Graft": 6, + "Ravenous": 2, + "Crab Kindred": 42, + "Worm Kindred": 3, + "Retrace": 4, + "Daybound": 1, + "Nightbound": 1, + "Werewolf Kindred": 2, + "Compleated": 2, + "Moonfolk Kindred": 27, + "Paradox": 3, + "Wurm Kindred": 3, + "Cleave": 6, + "Partner - Father & Son": 1, + "Weird Kindred": 16, + "Foretell": 18, + "Overload": 11, + "Parley": 2, + "Splice": 10, + "Pilot Kindred": 12, + "Imprint": 2, + "Vanishing": 6, + "Training": 1, + "Ingenuity Counters": 2, + "Jackal Kindred": 1, + "Replicate": 11, + "Lifeloss": 2, + "Lifeloss Triggers": 2, + "Primarch Kindred": 1, + "Improvise": 9, + "Midway Counters": 1, + "Noggle Kindred": 4, + "Delirium": 7, + "Oil Counters": 13, + "Reflection Kindred": 3, + "Time Travel": 4, + " Hone Counters": 1, + "Airbend": 2, + "Unicorn Kindred": 1, + "Quest Counters": 6, + "Archon Kindred": 2, + "Detain": 7, + "Kithkin Kindred": 11, + "Feather Counters": 2, + "Embalm": 7, + "Banding": 1, + "Filibuster Counters": 1, + "Griffin Kindred": 6, + "Heroic": 11, + "Disturb": 14, + "Squid Kindred": 8, + "Sleight Counters": 1, + "Cid, Timeless Artificer": 1, + "Peasant Kindred": 5, + "Addendum": 5, + "Gold Token": 2, + "Skulk": 9, + "Bribery Counters": 1, + "Glimmer Kindred": 3, + "Demigod Kindred": 2, + "Rebel Kindred": 3, + "Max speed": 5, + "Start your engines!": 5, + "Palliation Counters": 1, + "Starfish Kindred": 3, + "Forecast": 6, + "Dream Counters": 2, + "Map Token": 6, + "Brain Counters": 1, + "Dog Kindred": 8, + "Awaken": 6, + "Plainscycling": 1, + "Flanking": 2, + "Meld": 1, + "Kick Counters": 1, + "Multikicker": 4, + "Casualty": 5, + "Scion Kindred": 6, + "Reconfigure": 3, + "Doctor's Companion": 7, + "Doctor's companion": 6, + "Strive": 4, + "Spellshaper Kindred": 11, + "Choose a background": 7, + "Manifestation Counters": 1, + "Prototype": 4, + "Learn": 4, + "Mount Kindred": 2, + "Saddle": 1, + "Metalcraft": 8, + "Role token": 6, + "Incubate": 4, + "Incubator Token": 4, + "Job select": 4, + "Sheep Kindred": 2, + "Beeble Kindred": 3, + "Megamorph": 9, + "Horse Kindred": 9, + "Egg Kindred": 2, + "For Mirrodin!": 1, + "Rebound": 9, + "Support": 2, + "Net Counters": 2, + "Hag Kindred": 2, + "Corrupted": 2, + "Madness": 7, + "Myriad": 2, + "Spell mastery": 4, + "Ki Counters": 2, + "Buyback": 9, + "Cases Matter": 3, + "Solved": 3, + "Shred Counters": 1, + "Nautilus Kindred": 3, + "Eternalize": 3, + "Level Counters": 9, + "Behold": 3, + "Fade Counters": 3, + "Fading": 3, + "Undaunted": 1, + "Roll to Visit Your Attractions": 1, + "Surge": 6, + "Recover": 1, + "Polyp Counters": 1, + "\\+0/\\+1 Counters": 1, + "Level Up": 7, + "Voyage Counters": 1, + "Umbra armor": 4, + "Protection from Creatures": 1, + "Boar Kindred": 1, + "Mentor": 1, + "Soulbond": 7, + "Homarid Kindred": 8, + "Dreadnought Kindred": 1, + "Miracle": 3, + "Bargain": 5, + "Wish Counters": 1, + "Licid Kindred": 3, + "Sneak": 3, + "Incubation Counters": 1, + "Shadow": 9, + "Trilobite Kindred": 3, + "Coward Kindred": 1, + "Vortex Counters": 1, + "Prowl": 5, + "Delay Counters": 1, + "-0/-1 Counters": 1, + "Epic": 1, + "Afflict": 2, + "Offspring": 3, + "Zubera Kindred": 2, + "Read Ahead": 2, + "Hippogriff Kindred": 2, + "Infection Counters": 1, + "Powerstone Token": 6, + "Undying": 4, + "Oyster Kindred": 1, + "Germ Kindred": 1, + "Fetch Counters": 1, + "Horsemanship": 7, + "Caves Matter": 3, + "Tide Counters": 2, + "Camarid Kindred": 1, + "Tiered": 1, + "Ice Counters": 3, + "Split second": 5, + "Kor Kindred": 2, + "Kinship": 2, + "Cost Scaling": 5, + "Modal": 5, + "Spree": 5, + "Praetor Kindred": 3, + "Treasure Counters": 1, + "Verse Counters": 3, + "Grandeur": 1, + "Lieutenant": 2, + "Hatchling Counters": 1, + "Eon Counters": 1, + "Hour Counters": 1, + "Join forces": 1, + "Champion": 3, + "Music Counters": 1, + "Divinity Counters": 1, + "Tentacle Kindred": 2, + "Synth Kindred": 2, + "Annihilator": 1, + "Foreshadow Counters": 1, + "Impending": 1, + "Will of the Planeswalkers": 1, + "Offering": 1, + "Persistent Petitioners": 1, + "Orb Kindred": 1, + "Echo": 1, + "Demonstrate": 1, + "Backup": 1, + "Monger Kindred": 1, + "Chroma": 1, + "Scorpion Kindred": 1, + "Squad": 2, + "Tribute": 1, + "Slith Kindred": 1, + "Fathomless descent": 1, + "Omen Counters": 1, + "Squirrel Kindred": 1, + "Fateful hour": 1, + "Web-slinging": 1, + "Ripple": 1, + "Surrakar Kindred": 2, + "Imp Kindred": 1, + "Hourglass Counters": 1, + "Tempting offer": 1, + "Juggernaut Kindred": 1, + "Thalakos Kindred": 7, + "Knowledge Counters": 1, + "Spheres Matter": 1, + "Sponge Kindred": 2, + "Rejection Counters": 1, + "Adamant": 3, + "Toy Kindred": 1, + "Possession Counters": 1, + "Sleep Counters": 1, + "Blight": 1, + "Coin Counters": 1 + }, + "black": { + "Aggro": 2029, + "Combat Matters": 2029, + "Creature Tokens": 565, + "Nephilim Kindred": 4, + "Sand Kindred": 1, + "Token Creation": 755, + "Tokens Matter": 762, + "Burn": 1341, + "Haste": 102, + "Historics Matter": 1147, + "Human Kindred": 908, + "Legends Matter": 1147, + "Soldier Kindred": 119, + "Toughness Matters": 956, + "Vigilance": 59, + "Card Draw": 1022, + "Discard Matters": 361, + "Draw Triggers": 387, + "Little Fellas": 2007, + "Wheels": 411, + "Big Mana": 1970, + "Cascade": 10, + "Exile Matters": 203, + "Ogre Kindred": 53, + "Topdeck": 325, + "Trample": 141, + "Wizard Kindred": 253, + "Angel Kindred": 39, + "Deathtouch": 222, + "Flying": 753, + "Life Matters": 1283, + "Lifegain": 1280, + "Lifelink": 298, + "Phyrexian Kindred": 207, + "+1/+1 Counters": 654, + "-1/-1 Counters": 163, + "Counters Matter": 1054, + "Horror Kindred": 232, + "Infect": 75, + "Midrange": 119, + "Planeswalkers": 135, + "Proliferate": 18, + "Superfriends": 135, + "Voltron": 987, + "Spells Matter": 1858, + "Spellslinger": 1858, + "Artifact Tokens": 241, + "Artifacts Matter": 772, + "Thopter Kindred": 2, + "Toolbox": 122, + "Blink": 1136, + "Control": 387, + "Enter the Battlefield": 1136, + "Leave the Battlefield": 1137, + "Mill": 1535, + "Interaction": 1196, + "Knight Kindred": 127, + "Removal": 719, + "Blight": 15, + "Goblin Kindred": 91, + "Outlaw Kindred": 587, + "Pingers": 364, + "Protective Effects": 262, + "Unconditional Draw": 243, + "Ward": 70, + "Warlock Kindred": 132, + "Auras": 281, + "Enchantments Matter": 798, + "Giant Kindred": 32, + "Warrior Kindred": 294, + "Aristocrats": 940, + "Goad": 18, + "God Kindred": 31, + "Indestructible": 121, + "Sacrifice Matters": 936, + "Dragon Kindred": 103, + "Encore": 8, + "Kavu Kindred": 5, + "Lizard Kindred": 36, + "Wurm Kindred": 12, + "Lands Matter": 488, + "Reanimate": 1435, + "Egg Counters": 2, + "Golem Kindred": 12, + "Mana Dork": 85, + "Mana Rock": 67, + "Ramp": 197, + "Lairs Matter": 3, + "Land Types Matter": 82, + "Combat Tricks": 206, + "Stax": 394, + "Lhurgoyf Kindred": 7, + "Scout Kindred": 21, + "Conditional Draw": 184, + "Sacrifice to Draw": 147, + "Hero Kindred": 13, + "Menace": 230, + "Transform": 114, + "Fuse": 7, + "Insect Kindred": 113, + "X Spells": 215, + "Clones": 62, + "Hydra Kindred": 5, + "Charge Counters": 12, + "Station": 6, + "Cost Reduction": 118, + "Devil Kindred": 20, + "Rogue Kindred": 278, + "Exalted": 7, + "Shaman Kindred": 115, + "Blood Token": 36, + "Bloodthirst": 5, + "Dinosaur Kindred": 13, + "Enrage": 1, + "Mutant Kindred": 42, + "Saproling Kindred": 18, + "Board Wipes": 206, + "Berserker Kindred": 36, + "Cycling": 82, + "Loot": 153, + "Theft": 155, + "Enchant": 233, + "Noble Kindred": 77, + "Cat Kindred": 34, + "Landwalk": 53, + "Cyclops Kindred": 4, + "Raccoon Kindred": 1, + "Rhino Kindred": 4, + "Treasure": 81, + "Treasure Token": 86, + "Cleric Kindred": 183, + "Orc Kindred": 53, + "Kobold Kindred": 3, + "Citizen Kindred": 15, + "Graveborn Kindred": 2, + "Artificer Kindred": 35, + "Fungus Kindred": 24, + "Avatar Kindred": 52, + "Druid Kindred": 35, + "Pillowfort": 8, + "Politics": 86, + "Reach": 22, + "Spider Kindred": 22, + "Scarecrow Kindred": 8, + "Kicker": 61, + "Elder Kindred": 25, + "Advisor Kindred": 36, + "Double strike": 6, + "Delirium": 25, + "Assassin Kindred": 128, + "Blitz": 5, + "Demon Kindred": 212, + "Morph": 27, + "Volver Kindred": 3, + "Turtle Kindred": 8, + "Lore Counters": 47, + "Ore Counters": 54, + "Sagas Matter": 53, + "Beast Kindred": 63, + "Elemental Kindred": 90, + "Mutate": 11, + "Nightmare Kindred": 65, + "Gorgon Kindred": 27, + "Counterspells": 31, + "Flash": 84, + "Snake Kindred": 42, + "Surveil": 85, + "Ooze Kindred": 14, + "Clue Token": 31, + "Gates Matter": 35, + "Investigate": 25, + "Frog Kindred": 17, + "Aetherborn Kindred": 19, + "Ranger Kindred": 2, + "Scientist Kindred": 10, + "Hexproof": 36, + "Renew": 5, + "Zombie Kindred": 633, + "Impulse": 29, + "Bard Kindred": 8, + "Elf Kindred": 113, + "Faerie Kindred": 53, + "Rat Kindred": 109, + "Spell Copy": 29, + " Blood Counters": 4, + "Vampire Kindred": 357, + "Ape Kindred": 3, + "Leviathan Kindred": 2, + "Delve": 15, + "Ninja Kindred": 43, + "Spirit Kindred": 215, + "Rad Counters": 11, + "Shapeshifter Kindred": 31, + "Pirate Kindred": 48, + "Populate": 2, + "Dryad Kindred": 5, + "Dog Kindred": 24, + "Demigod Kindred": 2, + "Halfling Kindred": 14, + "Lifegain Triggers": 32, + "Treefolk Kindred": 23, + "Elephant Kindred": 3, + "Corrupted": 9, + "Poison Counters": 53, + "Toxic": 12, + "Centaur Kindred": 5, + "Djinn Kindred": 7, + "First strike": 62, + "Landfall": 24, + "Plant Kindred": 15, + "Mite Kindred": 3, + "Memory Counters": 1, + "Mobilize": 7, + "Eminence": 4, + "Graveyard Matters": 6, + "Magecraft": 7, + "Ally Kindred": 34, + "Bending": 26, + "Firebending": 16, + "Samurai Kindred": 13, + "Construct Kindred": 24, + "Gnome Kindred": 1, + "Experience Counters": 6, + "Scry": 49, + "Raid": 14, + "Bounty Counters": 5, + "Convert": 6, + "Eye Kindred": 12, + "Living metal": 5, + "More Than Meets the Eye": 6, + "Robot Kindred": 18, + "Monarch": 12, + "Flashback": 44, + "Mercenary Kindred": 58, + "Equipment Matters": 112, + "Dwarf Kindred": 8, + "Atog Kindred": 4, + "Airbending": 1, + "Earthbending": 7, + "Waterbending": 6, + "Eldrazi Kindred": 41, + "Bringer Kindred": 5, + "Bestow": 9, + "Manticore Kindred": 2, + "Illusion Kindred": 5, + "Earthbend": 6, + "Cantrips": 110, + "Food": 49, + "Freerunning": 8, + "Enchantment Tokens": 15, + "Shrines Matter": 6, + "Bear Kindred": 6, + "Battles Matter": 10, + "Companion": 5, + "Elk Kindred": 1, + "Rooms Matter": 12, + "Crew": 19, + "Exhaust": 3, + "Vehicles": 42, + "Changeling": 11, + "Age Counters": 21, + "Cumulative upkeep": 15, + "Hexproof from": 7, + "Converge": 3, + "Crystal Counters": 1, + "Protection": 40, + "Protection from Quality": 6, + "Sliver Kindred": 29, + "Sphinx Kindred": 11, + "Wall Kindred": 23, + "Devoid": 42, + "Phasing": 5, + "Myr Kindred": 3, + "Hideaway": 3, + "Backgrounds Matter": 13, + "Phoenix Kindred": 2, + "Astartes Kindred": 11, + "Rampage": 1, + "Loyalty Counters": 15, + "Vedalken Kindred": 5, + "Inquisitor Kindred": 2, + "Connive": 18, + "Octopus Kindred": 7, + "Bird Kindred": 52, + "Warp": 16, + "Finality Counters": 21, + "Soul Counters": 5, + "Venture into the dungeon": 9, + "Protection from Color": 40, + "Metathran Kindred": 2, + "Council's dilemma": 2, + "Gargoyle Kindred": 4, + "Affinity": 7, + "Cyberman Kindred": 4, + "Serpent Kindred": 4, + "Friends Forever": 2, + "Dalek Kindred": 6, + "Alien Kindred": 12, + "Collection Counters": 1, + "Unearth": 27, + "Corruption Counters": 1, + "Wraith Kindred": 15, + "Cage Counters": 1, + "Dethrone": 1, + "Mayhem": 7, + "Amass": 30, + "Army Kindred": 28, + "Specter Kindred": 27, + "Drake Kindred": 9, + "Swampwalk": 29, + "Archer Kindred": 10, + "Equip": 43, + "Equipment": 45, + "Snail Kindred": 2, + "Tiefling Kindred": 8, + "Descend": 8, + "Monstrosity": 5, + "Defender": 34, + "Disguise": 10, + "Fight": 4, + "Boar Kindred": 5, + "Pest Kindred": 7, + "Badger Kindred": 1, + "Domain": 8, + "Troll Kindred": 15, + "Drone Kindred": 20, + "Scion Kindred": 6, + "Food Token": 43, + "Forage": 3, + "Squirrel Kindred": 19, + "Forestwalk": 5, + "Token Modification": 2, + "Learn": 5, + "Worm Kindred": 9, + "Adapt": 6, + "Wombat Kindred": 1, + "Skeleton Kindred": 78, + "Morbid": 11, + "Hag Kindred": 7, + "Aftermath": 9, + "Cleave": 4, + "Scavenge": 7, + "Incubate": 12, + "Incubator Token": 12, + "Role token": 9, + "Deserts Matter": 8, + "Wolf Kindred": 10, + "Dredge": 8, + "Convoke": 15, + "Partner": 27, + "Collect evidence": 5, + "Detective Kindred": 16, + "Undergrowth": 7, + "Partner - Survivors": 2, + "Survivor Kindred": 3, + "Crocodile Kindred": 16, + "Devour": 4, + "Wither": 11, + "Peasant Kindred": 8, + "Plot": 6, + "Partner with": 12, + "Plague Counters": 3, + "Escape": 12, + "Threshold": 32, + "Leech Kindred": 15, + "Persist": 9, + "Scorpion Kindred": 11, + "Open an Attraction": 9, + "Performer Kindred": 2, + "Fathomless descent": 4, + "Time Counters": 15, + "Mount Kindred": 3, + "Saddle": 2, + "Gamer Kindred": 1, + "Guest Kindred": 6, + "Spore Counters": 2, + "Meld": 3, + "Retrace": 3, + "Basic landcycling": 4, + "Landcycling": 14, + "Typecycling": 14, + "Merfolk Kindred": 17, + "Islandwalk": 2, + "Replacement Draw": 4, + "Cipher": 8, + "Harpy Kindred": 12, + "Transmute": 8, + "Ascend": 6, + "Evoke": 7, + "Incarnation Kindred": 6, + "Employee Kindred": 8, + "Jump": 2, + "Jump-start": 1, + "Inspired": 7, + "Storage Counters": 4, + "Cloak": 1, + "Hit Counters": 3, + "Ninjutsu": 19, + "Ingest": 4, + "Eerie": 5, + "Kraken Kindred": 3, + "Waterbend": 5, + "Fear": 37, + "Islandcycling": 1, + "Swampcycling": 10, + "Stun Counters": 4, + "Azra Kindred": 8, + "Lord of the Nazgûl": 1, + "Multiple Copies": 5, + "Clash": 6, + "Conspire": 3, + "Minion Kindred": 43, + "Homunculus Kindred": 1, + "Shark Kindred": 3, + "Depletion Counters": 3, + "Siren Kindred": 2, + "Exploit": 14, + "Page Counters": 1, + "Body Thief": 2, + "Takeover Counters": 1, + "Slime Counters": 1, + "Slug Kindred": 6, + "Entwine": 8, + "Whale Kindred": 1, + "Processor Kindred": 3, + "Life to Draw": 10, + "Assist": 4, + "Intimidate": 14, + "Bat Kindred": 46, + "Beholder Kindred": 6, + "Minotaur Kindred": 23, + "Hellbent": 15, + "Imp Kindred": 41, + "Bloodstain Counters": 1, + "Madness": 31, + "Junk Token": 2, + "Junk Tokens": 1, + "Horse Kindred": 11, + "Unleash": 9, + "Oil Counters": 4, + "Shade Kindred": 33, + "Rust Counters": 1, + "Germ Kindred": 3, + "Living weapon": 3, + "Manifest": 8, + "Manifest dread": 3, + "Max speed": 10, + "Start your engines!": 12, + "Rebound": 4, + "Intel Counters": 1, + "Spectacle": 8, + "Dreadnought Kindred": 1, + "Echo": 4, + "Mountaincycling": 1, + "Void": 11, + "Powerstone Token": 6, + "Dash": 8, + "Storm": 4, + "Casualty": 7, + "Hellion Kindred": 1, + "Barbarian Kindred": 5, + "Mystic Kindred": 1, + "Thrull Kindred": 31, + "Pilot Kindred": 6, + "Ouphe Kindred": 2, + "Weasel Kindred": 1, + "Myriad": 3, + "Undying": 9, + "Card Selection": 11, + "Explore": 11, + "Archon Kindred": 2, + "Coin Counters": 1, + "Kor Kindred": 5, + "Haunt": 6, + "Inkling Kindred": 7, + "Overload": 3, + "Sneak": 7, + "Mentor": 1, + "Kithkin Kindred": 2, + "Revolt": 4, + "Servo Kindred": 9, + "Afterlife": 6, + "Ghostform Counters": 1, + "Extort": 12, + "Lifeloss": 11, + "Lifeloss Triggers": 11, + "Will of the council": 3, + "Nomad Kindred": 1, + "Battle Cry": 2, + "Griffin Kindred": 1, + "Shroud": 4, + "Miracle": 3, + "Fate Counters": 1, + "Fox Kindred": 2, + "Constellation": 7, + "Roll to Visit Your Attractions": 2, + "Emerge": 3, + "Nightstalker Kindred": 12, + "Megamorph": 4, + "Energy": 8, + "Energy Counters": 8, + "Resource Engine": 8, + "Heroic": 4, + "Monkey Kindred": 2, + "Scream Counters": 2, + "Craft": 4, + "Fabricate": 5, + "Afflict": 4, + "Necron Kindred": 25, + "Modular": 1, + "Backup": 4, + "Squad": 3, + "\\+1/\\+2 Counters": 1, + "Zubera Kindred": 1, + "Prowl": 5, + "Bargain": 5, + "Daybound": 4, + "Nightbound": 4, + "Werewolf Kindred": 6, + "Suspect": 5, + "Boast": 5, + "\\+2/\\+2 Counters": 2, + "Improvise": 4, + "Rebel Kindred": 6, + "Job select": 4, + "Reconfigure": 3, + "Metalcraft": 1, + "Quest Counters": 5, + "Multikicker": 2, + "Devotion Counters": 1, + "Spellshaper Kindred": 11, + "Champion": 1, + "Escalate": 2, + "Read Ahead": 2, + "Buyback": 9, + "Choose a background": 6, + "Flanking": 4, + "Horsemanship": 7, + "Crab Kindred": 3, + "Cases Matter": 2, + "Solved": 2, + "Fish Kindred": 4, + "Adamant": 3, + "Behold": 3, + "Nymph Kindred": 3, + "Suspend": 11, + "Goat Kindred": 4, + "Gift": 4, + "Spawn Kindred": 4, + "Licid Kindred": 2, + "Disturb": 1, + "Soulshift": 9, + "Corpse Counters": 4, + "Strive": 2, + "Spite Counters": 1, + "Bushido": 6, + "Undaunted": 1, + "Survival": 1, + "Spell mastery": 4, + "Offspring": 4, + "Dauthi Kindred": 11, + "Shadow": 15, + "Jackal Kindred": 5, + "Void Counters": 2, + "Plot Counters": 1, + "Vanishing": 2, + "Cockatrice Kindred": 1, + "Lieutenant": 2, + "Despair Counters": 1, + "Verse Counters": 2, + "Satyr Kindred": 2, + "Infection Counters": 2, + "Outlast": 2, + "Soulbond": 1, + "Skunk Kindred": 1, + "Cohort": 3, + "Ice Counters": 1, + "Foretell": 9, + "Eternalize": 1, + "-2/-2 Counters": 1, + "Praetor Kindred": 6, + "\\+1/\\+0 Counters": 1, + "Amplify": 3, + "Glimmer Kindred": 2, + "Defense Counters": 1, + "Slith Kindred": 2, + "Salamander Kindred": 3, + "Hatchling Counters": 1, + "Replicate": 1, + "Split second": 5, + "Map Token": 1, + "Skulk": 5, + "Gremlin Kindred": 2, + "\\+0/\\+2 Counters": 1, + "Caves Matter": 3, + "Recover": 3, + "Channel": 3, + "Gold Token": 2, + "Pangolin Kindred": 2, + "Evolve": 1, + "Prototype": 2, + "Splice": 4, + "Lamia Kindred": 2, + "-0/-2 Counters": 2, + "Level Counters": 4, + "Level Up": 4, + "Ritual Counters": 1, + "Discover": 2, + "Ki Counters": 2, + "Demonstrate": 1, + "Kirin Kindred": 1, + "Cost Scaling": 4, + "Modal": 4, + "Spree": 4, + "Efreet Kindred": 1, + "Rally": 1, + "Rabbit Kindred": 1, + "Endure": 4, + "Grandeur": 1, + "-0/-1 Counters": 3, + "Monk Kindred": 1, + "Hippo Kindred": 1, + "Mannequin Counters": 1, + "Hour Counters": 1, + "Awaken": 3, + "Nautilus Kindred": 1, + "Rigger Kindred": 1, + "Primarch Kindred": 1, + "Divinity Counters": 1, + "Protection from Creature Type": 2, + "Feeding Counters": 1, + "Epic": 1, + "Kinship": 2, + "Revival Counters": 1, + "Weird Kindred": 1, + "Mole Kindred": 2, + "Eon Counters": 1, + "Impending": 1, + "Toy Kindred": 2, + "Fade Counters": 3, + "Fading": 3, + "Will of the Planeswalkers": 1, + "Offering": 1, + "Carrier Kindred": 5, + "Starfish Kindred": 2, + "Rat Colony": 1, + "Relentless Rats": 1, + "Blight Counters": 1, + "Monger Kindred": 1, + "Coward Kindred": 1, + "Serf Kindred": 1, + "Shadowborn Apostle": 1, + "C'tan Kindred": 2, + "Join forces": 1, + "Vivid": 1, + "Surrakar Kindred": 2, + "Tribute": 1, + "Spike Kindred": 1, + "Ripple": 1, + "Tempting offer": 1, + "Prey Counters": 1, + "Spheres Matter": 1, + "Necrodermis Counters": 1, + "Varmint Kindred": 1, + "Stash Counters": 1, + "Pegasus Kindred": 1, + "Chroma": 1, + "Tiered": 1, + "Doctor Kindred": 1, + "Doctor's Companion": 1, + "Doctor's companion": 1, + "Compleated": 1, + "Wish Counters": 1, + "Camel Kindred": 1, + "Petrification Counters": 1 + }, + "red": { + "Aggro": 2332, + "Combat Matters": 2332, + "Creature Tokens": 519, + "Nephilim Kindred": 4, + "Sand Kindred": 2, + "Token Creation": 761, + "Tokens Matter": 771, + "Burn": 2125, + "Haste": 504, + "Historics Matter": 1188, + "Human Kindred": 1075, + "Legends Matter": 1188, + "Soldier Kindred": 206, + "Toughness Matters": 843, + "Vigilance": 93, + "Card Draw": 712, + "Discard Matters": 457, + "Draw Triggers": 135, + "Little Fellas": 1878, + "Wheels": 142, + "Big Mana": 2040, + "Cascade": 28, + "Exile Matters": 347, + "Ogre Kindred": 94, + "Topdeck": 267, + "Trample": 336, + "Wizard Kindred": 231, + "Artifact Tokens": 298, + "Artifacts Matter": 1122, + "Life Matters": 318, + "Lifegain": 317, + "Thopter Kindred": 15, + "Toolbox": 136, + "Blink": 719, + "Control": 284, + "Enter the Battlefield": 719, + "Leave the Battlefield": 720, + "Mill": 613, + "Interaction": 938, + "Knight Kindred": 102, + "Removal": 342, + "-1/-1 Counters": 65, + "Blight": 11, + "Counters Matter": 838, + "Goblin Kindred": 478, + "Outlaw Kindred": 287, + "Pingers": 475, + "Protective Effects": 174, + "Unconditional Draw": 247, + "Ward": 54, + "Warlock Kindred": 31, + "Auras": 244, + "Enchantments Matter": 772, + "Giant Kindred": 125, + "Voltron": 902, + "Warrior Kindred": 567, + "+1/+1 Counters": 522, + "Aristocrats": 351, + "Goad": 49, + "God Kindred": 29, + "Indestructible": 70, + "Sacrifice Matters": 345, + "Dragon Kindred": 291, + "Flying": 495, + "Encore": 7, + "Kavu Kindred": 44, + "Lizard Kindred": 119, + "Wurm Kindred": 13, + "Lands Matter": 566, + "Reanimate": 471, + "Egg Counters": 2, + "Golem Kindred": 20, + "Mana Dork": 156, + "Mana Rock": 73, + "Ramp": 288, + "Lairs Matter": 3, + "Land Types Matter": 77, + "Combat Tricks": 205, + "Spells Matter": 2139, + "Spellslinger": 2139, + "Stax": 502, + "Lhurgoyf Kindred": 4, + "Scout Kindred": 42, + "Conditional Draw": 159, + "Sacrifice to Draw": 83, + "Hero Kindred": 32, + "Menace": 146, + "Transform": 108, + "Fuse": 9, + "Insect Kindred": 32, + "X Spells": 319, + "Clones": 86, + "Hydra Kindred": 11, + "Charge Counters": 24, + "Station": 7, + "Cost Reduction": 141, + "Devil Kindred": 62, + "Midrange": 61, + "Rogue Kindred": 142, + "Exalted": 2, + "Shaman Kindred": 256, + "Blood Token": 46, + "Bloodthirst": 12, + "Dinosaur Kindred": 90, + "Enrage": 9, + "Mutant Kindred": 36, + "Saproling Kindred": 9, + "Board Wipes": 355, + "Berserker Kindred": 116, + "Cycling": 92, + "Loot": 154, + "Theft": 176, + "Enchant": 181, + "Noble Kindred": 56, + "Cat Kindred": 59, + "Landwalk": 35, + "Planeswalkers": 136, + "Superfriends": 136, + "Cyclops Kindred": 33, + "Raccoon Kindred": 16, + "Rhino Kindred": 7, + "Treasure": 165, + "Treasure Token": 168, + "Cleric Kindred": 38, + "Orc Kindred": 80, + "Kobold Kindred": 15, + "Citizen Kindred": 30, + "Graveborn Kindred": 2, + "Artificer Kindred": 93, + "Fungus Kindred": 3, + "Avatar Kindred": 41, + "Druid Kindred": 26, + "Pillowfort": 5, + "Politics": 88, + "Reach": 67, + "Spider Kindred": 17, + "Scarecrow Kindred": 4, + "Kicker": 63, + "Elder Kindred": 20, + "Advisor Kindred": 18, + "Double strike": 59, + "Delirium": 16, + "Assassin Kindred": 38, + "Blitz": 9, + "Demon Kindred": 54, + "First strike": 161, + "Memory Counters": 1, + "Angel Kindred": 30, + "Lifelink": 56, + "Mobilize": 7, + "Volver Kindred": 3, + "Eminence": 3, + "Vampire Kindred": 95, + "Graveyard Matters": 7, + "Magecraft": 10, + "Ally Kindred": 60, + "Bending": 35, + "Firebending": 29, + "Samurai Kindred": 26, + "Construct Kindred": 26, + "Gnome Kindred": 6, + "Experience Counters": 8, + "Dog Kindred": 44, + "Scry": 67, + "Deathtouch": 22, + "Raid": 24, + "Bounty Counters": 1, + "Convert": 9, + "Eye Kindred": 7, + "Living metal": 6, + "More Than Meets the Eye": 7, + "Robot Kindred": 33, + "Spirit Kindred": 117, + "Morph": 28, + "Monarch": 13, + "Flashback": 69, + "Mutate": 8, + "Nightmare Kindred": 13, + "Mercenary Kindred": 35, + "Equipment Matters": 211, + "Lore Counters": 54, + "Ore Counters": 60, + "Sagas Matter": 62, + "Dwarf Kindred": 87, + "Elemental Kindred": 313, + "Atog Kindred": 5, + "Airbending": 1, + "Earthbending": 8, + "Landfall": 46, + "Waterbending": 1, + "Eldrazi Kindred": 31, + "Bringer Kindred": 5, + "Beast Kindred": 135, + "Bestow": 7, + "Manticore Kindred": 11, + "Illusion Kindred": 3, + "Earthbend": 7, + "Phyrexian Kindred": 63, + "Cantrips": 121, + "Food": 11, + "Freerunning": 3, + "Spell Copy": 127, + "Enchantment Tokens": 14, + "Shrines Matter": 6, + "Bear Kindred": 3, + "Ninja Kindred": 11, + "Turtle Kindred": 15, + "Battles Matter": 10, + "Companion": 3, + "Elk Kindred": 3, + "Flash": 45, + "Rooms Matter": 9, + "Crew": 23, + "Exhaust": 15, + "Vehicles": 64, + "Impulse": 194, + "Changeling": 9, + "Shapeshifter Kindred": 16, + "Age Counters": 19, + "Cumulative upkeep": 13, + "Hexproof": 15, + "Hexproof from": 4, + "Converge": 3, + "Crystal Counters": 1, + "Protection": 26, + "Protection from Quality": 8, + "Sliver Kindred": 36, + "Sphinx Kindred": 2, + "Wall Kindred": 36, + "Bard Kindred": 26, + "Devoid": 33, + "Phasing": 4, + "Myr Kindred": 4, + "Hideaway": 2, + "Backgrounds Matter": 14, + "Phoenix Kindred": 36, + "Astartes Kindred": 8, + "Pirate Kindred": 74, + "Finality Counters": 4, + "Cyberman Kindred": 2, + "Serpent Kindred": 3, + "Friends Forever": 4, + "Dalek Kindred": 2, + "Loyalty Counters": 13, + "Alien Kindred": 8, + "Scientist Kindred": 12, + "Connive": 2, + "Zombie Kindred": 56, + "Collection Counters": 1, + "Unearth": 17, + "Corruption Counters": 1, + "Wraith Kindred": 2, + "Horror Kindred": 32, + "Cage Counters": 1, + "Dethrone": 6, + "Frog Kindred": 3, + "Mayhem": 7, + "Counterspells": 27, + "Amass": 20, + "Army Kindred": 19, + "Specter Kindred": 2, + "Drake Kindred": 13, + "Swampwalk": 2, + "Archer Kindred": 25, + "Gates Matter": 29, + "Investigate": 18, + "Equip": 67, + "Equipment": 70, + "Rat Kindred": 13, + "Snail Kindred": 1, + "Tiefling Kindred": 7, + "Doctor Kindred": 13, + "Protection from Color": 24, + "Elephant Kindred": 8, + "Homunculus Kindred": 1, + "Merfolk Kindred": 9, + "Fight": 40, + "Gremlin Kindred": 14, + "Ranger Kindred": 10, + "Tyranid Kindred": 12, + "Harmonize": 5, + "Energy": 44, + "Energy Counters": 41, + "Resource Engine": 44, + "Elf Kindred": 40, + "Monkey Kindred": 9, + "Bird Kindred": 27, + "Ferocious": 7, + "Storm": 18, + "Defender": 43, + "Leviathan Kindred": 1, + "Snake Kindred": 5, + "Egg Kindred": 6, + "Rabbit Kindred": 3, + "Backup": 8, + "Fox Kindred": 3, + "Junk Token": 9, + "Junk Tokens": 9, + "Populate": 4, + "Deserts Matter": 14, + "Token Modification": 4, + "Detective Kindred": 14, + "Dryad Kindred": 1, + "Plant Kindred": 8, + "Treefolk Kindred": 4, + "Halfling Kindred": 3, + "Hamster Kindred": 2, + "Shroud": 5, + "Discover": 12, + "Parley": 2, + "Griffin Kindred": 2, + "Food Token": 8, + "Start your engines!": 11, + "Boast": 8, + "Melee": 5, + "Monk Kindred": 41, + "Formidable": 6, + "Wolf Kindred": 30, + "Scorpion Kindred": 3, + "Minotaur Kindred": 97, + "Hellbent": 11, + "Disguise": 12, + "Azra Kindred": 3, + "Clue Token": 19, + "Skeleton Kindred": 7, + "Imp Kindred": 7, + "Bloodstain Counters": 1, + "Madness": 20, + "Horse Kindred": 8, + "Unleash": 10, + "Minion Kindred": 3, + "Oil Counters": 17, + "Shade Kindred": 1, + "Aftermath": 11, + "Rust Counters": 1, + "Germ Kindred": 1, + "Living weapon": 1, + "Manifest": 6, + "Manifest dread": 3, + "Partner - Survivors": 3, + "Survivor Kindred": 5, + "Max speed": 8, + "Rebound": 6, + "Intel Counters": 1, + "Drone Kindred": 15, + "Surveil": 10, + "Basic landcycling": 5, + "Landcycling": 16, + "Typecycling": 17, + "Spectacle": 6, + "Dreadnought Kindred": 1, + "Echo": 25, + "Mountaincycling": 11, + "Swampcycling": 1, + "Affinity": 13, + "Void": 4, + "Warp": 15, + "Powerstone Token": 9, + "Beholder Kindred": 2, + "Dash": 14, + "Escape": 9, + "Wither": 12, + "Depletion Counters": 3, + "Fear": 3, + "Performer Kindred": 8, + "Meld": 2, + "Storage Counters": 4, + "Employee Kindred": 6, + "Persist": 4, + "Casualty": 4, + "Hellion Kindred": 22, + "Barbarian Kindred": 40, + "Threshold": 13, + "Mystic Kindred": 1, + "Thrull Kindred": 1, + " Blood Counters": 1, + "Faerie Kindred": 8, + "Troll Kindred": 7, + "Pilot Kindred": 20, + "Infect": 11, + "Ouphe Kindred": 1, + "Partner": 26, + "Partner with": 12, + "Stun Counters": 4, + "Weasel Kindred": 1, + "Myriad": 7, + "Conspire": 3, + "Undying": 7, + "Mouse Kindred": 14, + "Fabricate": 1, + "Proliferate": 4, + "Servo Kindred": 4, + "Efreet Kindred": 20, + "Djinn Kindred": 18, + "Prowess": 38, + "Suspend": 26, + "Time Counters": 32, + "Chimera Kindred": 6, + "Kirin Kindred": 2, + "Flurry": 6, + "Convoke": 11, + "Bolster": 1, + "Craft": 5, + "Lifegain Triggers": 2, + "Mole Kindred": 1, + "Daybound": 17, + "Nightbound": 17, + "Rebel Kindred": 26, + "Modular": 6, + "Centaur Kindred": 7, + "Boar Kindred": 22, + "Werewolf Kindred": 42, + "Ooze Kindred": 6, + "Domain": 9, + "Tribute": 5, + "Riot": 10, + "Satyr Kindred": 18, + "Bloodrush": 9, + "Devour": 10, + "Juggernaut Kindred": 2, + "Badger Kindred": 3, + "Intimidate": 6, + "Jackal Kindred": 15, + "Ape Kindred": 10, + "Legendary landwalk": 1, + "Compleated": 2, + "Rampage": 5, + "Ravenous": 3, + "Noggle Kindred": 5, + "Fetch Counters": 1, + "Monstrosity": 8, + "Basilisk Kindred": 1, + "Web-slinging": 1, + "Yeti Kindred": 10, + "Gnoll Kindred": 2, + "Pack tactics": 5, + "Guest Kindred": 4, + "Peasant Kindred": 8, + "Forestcycling": 1, + "Evoke": 8, + "Incarnation Kindred": 7, + "Spawn Kindred": 8, + "Otter Kindred": 14, + "Cleave": 2, + "Partner - Father & Son": 2, + "Jump": 7, + "Jump-start": 6, + "Vedalken Kindred": 3, + "Weird Kindred": 14, + "Foretell": 12, + "Retrace": 7, + "Shark Kindred": 5, + "Overload": 13, + "Fish Kindred": 5, + "Splice": 8, + "Imprint": 2, + "Vanishing": 3, + "Training": 2, + "Ingenuity Counters": 1, + "Collect evidence": 3, + "Replicate": 5, + "Plot": 10, + "Lifeloss": 1, + "Lifeloss Triggers": 1, + "Primarch Kindred": 1, + "Siren Kindred": 1, + "Improvise": 6, + "Jellyfish Kindred": 1, + "Midway Counters": 1, + "Open an Attraction": 2, + "Crab Kindred": 1, + "Vivid": 3, + "Reflection Kindred": 1, + "Time Travel": 3, + "Secret council": 2, + " Hone Counters": 1, + "Shield Counters": 2, + "Suspect": 5, + "Kor Kindred": 8, + "Heroic": 9, + "Toy Kindred": 4, + "Celebration": 6, + "Muster Counters": 1, + "Mentor": 11, + "For Mirrodin!": 6, + "Battalion": 12, + "Radiance": 6, + "Ox Kindred": 9, + "Kithkin Kindred": 2, + "Squirrel Kindred": 1, + "Quest Counters": 8, + "Strive": 5, + "Split second": 5, + "Card Selection": 3, + "Explore": 2, + "Metalcraft": 7, + "Mount Kindred": 10, + "Rally": 5, + "Spell mastery": 4, + "Sloth Kindred": 1, + "Exert": 13, + "Valiant": 5, + "Shadow": 1, + "Soltari Kindred": 1, + "Clown Kindred": 6, + "Miracle": 4, + "Learn": 5, + "Renown": 5, + "Rad Counters": 2, + "Clash": 5, + "Aetherborn Kindred": 1, + "Revolt": 1, + "Offspring": 5, + "Cohort": 2, + "Choose a background": 7, + "Doctor's Companion": 6, + "Doctor's companion": 6, + "Demigod Kindred": 1, + "Fade Counters": 1, + "Fading": 1, + "Spellshaper Kindred": 11, + "Verse Counters": 3, + "Megamorph": 5, + "Nomad Kindred": 6, + "Enlist": 4, + "\\+1/\\+0 Counters": 4, + "Horsemanship": 6, + "Entwine": 6, + "Gargoyle Kindred": 2, + "Goat Kindred": 7, + "Bushido": 8, + "Role token": 8, + "Ki Counters": 2, + "Offering": 2, + "Flanking": 6, + "Prototype": 4, + "Wolverine Kindred": 7, + "Gift": 4, + "Porcupine Kindred": 2, + "Death Counters": 1, + "Awaken": 1, + "Coward Kindred": 5, + "Fuse Counters": 4, + "Escalate": 3, + "Surge": 5, + "Reinforce": 1, + "Level Counters": 3, + "Level Up": 3, + "Morbid": 4, + "Reconfigure": 6, + "Zubera Kindred": 2, + "Orgg Kindred": 4, + "Caves Matter": 4, + "Saddle": 5, + "Mountainwalk": 14, + "Cases Matter": 2, + "Solved": 2, + "Cost Scaling": 4, + "Modal": 4, + "Spree": 4, + "Rev Counters": 1, + "Behold": 5, + "Luck Counters": 1, + "Champion": 3, + "Fury Counters": 1, + "Salamander Kindred": 4, + "Multikicker": 4, + "Licid Kindred": 2, + "Demonstrate": 2, + "Provoke": 2, + "Coyote Kindred": 1, + "Gold Token": 2, + "Descent Counters": 1, + "Alliance": 6, + "Undaunted": 1, + "Soulbond": 5, + "Dragon's Approach": 1, + "Multiple Copies": 2, + "\\+0/\\+1 Counters": 1, + "\\+2/\\+2 Counters": 1, + "Eternalize": 1, + "Trilobite Kindred": 1, + "Adamant": 3, + "Glimmer Kindred": 1, + "Cloak": 1, + "Poison Counters": 3, + "Buyback": 7, + "Inspired": 3, + "Chroma": 3, + "Tiered": 2, + "Blaze Counters": 2, + "Paradox": 4, + "Constellation": 1, + "Nymph Kindred": 3, + "Afflict": 4, + "Incubate": 3, + "Incubator Token": 3, + "Channel": 7, + "Stash Counters": 2, + "Islandwalk": 1, + "Battle Cry": 5, + "Dream Counters": 1, + "Aim Counters": 1, + "Embalm": 1, + "Pressure Counters": 1, + "Emerge": 1, + "Annihilator": 1, + "Hyena Kindred": 2, + "Recover": 1, + "Doom Counters": 2, + "Exploit": 1, + "Eerie": 1, + "Venture into the dungeon": 2, + "Amplify": 1, + "Forestwalk": 1, + "Assist": 2, + "Lieutenant": 3, + "Delve": 1, + "Join forces": 1, + "Worm Kindred": 2, + "Mine Counters": 1, + "Mongoose Kindred": 1, + "Kinship": 3, + "Divinity Counters": 1, + "Banding": 1, + "Pangolin Kindred": 1, + "Impending": 1, + "Will of the Planeswalkers": 1, + "Squad": 2, + "Support": 1, + "Sneak": 2, + "Bargain": 3, + "Job select": 3, + "Ice Counters": 1, + "Shell Counters": 1, + "Wage Counters": 1, + "Leech Kindred": 1, + "Seven Dwarves": 1, + "Dredge": 1, + "Grandeur": 2, + "Slith Kindred": 1, + "Ember Counters": 1, + "Ascend": 2, + "Ripple": 1, + "Synth Kindred": 1, + "Tempting offer": 2, + "Spheres Matter": 1, + "Read Ahead": 2, + "Slug Kindred": 1, + "Armadillo Kindred": 1, + "Contested Counters": 1, + "Epic": 1, + "Praetor Kindred": 3, + "Ingest": 1, + "Monger Kindred": 1, + "Child Kindred": 1, + "Ninjutsu": 1 + }, + "green": { + "Aggro": 2407, + "Combat Matters": 2407, + "Creature Tokens": 698, + "Nephilim Kindred": 4, + "Sand Kindred": 4, + "Token Creation": 884, + "Tokens Matter": 895, + "Burn": 442, + "Haste": 102, + "Historics Matter": 1066, + "Human Kindred": 775, + "Legends Matter": 1066, + "Soldier Kindred": 112, + "Toughness Matters": 1056, + "Vigilance": 188, + "Card Draw": 677, + "Discard Matters": 157, + "Draw Triggers": 106, + "Little Fellas": 2025, + "Wheels": 111, + "Big Mana": 2172, + "Cascade": 17, + "Exile Matters": 119, + "Ogre Kindred": 10, + "Topdeck": 427, + "Trample": 533, + "Wizard Kindred": 112, + "Angel Kindred": 21, + "Deathtouch": 92, + "Flying": 217, + "Life Matters": 641, + "Lifegain": 641, + "Lifelink": 75, + "Phyrexian Kindred": 91, + "+1/+1 Counters": 1235, + "-1/-1 Counters": 113, + "Counters Matter": 1537, + "Horror Kindred": 46, + "Infect": 89, + "Midrange": 171, + "Planeswalkers": 150, + "Proliferate": 34, + "Superfriends": 150, + "Voltron": 1548, + "Spells Matter": 1543, + "Spellslinger": 1543, + "Control": 259, + "Interaction": 821, + "Knight Kindred": 60, + "Mill": 864, + "Removal": 409, + "Blight": 2, + "Goblin Kindred": 35, + "Outlaw Kindred": 101, + "Pingers": 75, + "Protective Effects": 400, + "Unconditional Draw": 262, + "Ward": 88, + "Warlock Kindred": 25, + "Auras": 306, + "Enchantments Matter": 891, + "Giant Kindred": 49, + "Warrior Kindred": 435, + "Aristocrats": 319, + "Goad": 8, + "God Kindred": 22, + "Indestructible": 123, + "Sacrifice Matters": 300, + "Blink": 873, + "Dragon Kindred": 100, + "Enter the Battlefield": 873, + "Leave the Battlefield": 874, + "Encore": 4, + "Kavu Kindred": 26, + "Lizard Kindred": 59, + "Wurm Kindred": 96, + "Lands Matter": 1044, + "Reanimate": 599, + "Egg Counters": 2, + "Artifacts Matter": 742, + "Golem Kindred": 18, + "Mana Dork": 307, + "Mana Rock": 67, + "Ramp": 752, + "Lairs Matter": 3, + "Land Types Matter": 111, + "Combat Tricks": 219, + "Toolbox": 180, + "Stax": 424, + "Lhurgoyf Kindred": 8, + "Scout Kindred": 134, + "Conditional Draw": 186, + "Sacrifice to Draw": 82, + "Hero Kindred": 26, + "Menace": 35, + "Transform": 102, + "Fuse": 7, + "Insect Kindred": 153, + "X Spells": 276, + "Clones": 100, + "Hydra Kindred": 59, + "Charge Counters": 12, + "Station": 7, + "Cost Reduction": 125, + "Devil Kindred": 2, + "Rogue Kindred": 58, + "Exalted": 11, + "Shaman Kindred": 201, + "Blood Token": 20, + "Bloodthirst": 11, + "Dinosaur Kindred": 117, + "Enrage": 14, + "Mutant Kindred": 70, + "Saproling Kindred": 76, + "Board Wipes": 89, + "Berserker Kindred": 21, + "Cycling": 85, + "Loot": 97, + "Theft": 36, + "Enchant": 224, + "Noble Kindred": 49, + "Cat Kindred": 119, + "Landwalk": 71, + "Cyclops Kindred": 5, + "Raccoon Kindred": 19, + "Rhino Kindred": 51, + "Artifact Tokens": 206, + "Treasure": 50, + "Treasure Token": 51, + "Cleric Kindred": 62, + "Orc Kindred": 7, + "Kobold Kindred": 1, + "Citizen Kindred": 59, + "Graveborn Kindred": 1, + "Artificer Kindred": 32, + "Fungus Kindred": 60, + "Avatar Kindred": 51, + "Druid Kindred": 350, + "Pillowfort": 7, + "Politics": 63, + "Reach": 281, + "Spider Kindred": 101, + "Scarecrow Kindred": 8, + "Kicker": 72, + "Elder Kindred": 19, + "Advisor Kindred": 28, + "Double strike": 7, + "Delirium": 29, + "Assassin Kindred": 15, + "Blitz": 5, + "Demon Kindred": 8, + "Morph": 32, + "Volver Kindred": 3, + "Turtle Kindred": 31, + "Lore Counters": 54, + "Ore Counters": 81, + "Sagas Matter": 65, + "Beast Kindred": 345, + "Elemental Kindred": 236, + "Mutate": 12, + "Nightmare Kindred": 12, + "Gorgon Kindred": 10, + "Counterspells": 22, + "Flash": 96, + "Snake Kindred": 115, + "Surveil": 28, + "Ooze Kindred": 46, + "Clue Token": 34, + "Gates Matter": 48, + "Investigate": 34, + "Frog Kindred": 49, + "Aetherborn Kindred": 1, + "Ranger Kindred": 50, + "Scientist Kindred": 11, + "Hexproof": 120, + "Renew": 5, + "Zombie Kindred": 60, + "Impulse": 17, + "Bard Kindred": 20, + "Elf Kindred": 584, + "Faerie Kindred": 25, + "Rat Kindred": 2, + "Spell Copy": 35, + " Blood Counters": 1, + "Vampire Kindred": 4, + "Ape Kindred": 30, + "Leviathan Kindred": 9, + "Delve": 4, + "Ninja Kindred": 15, + "Spirit Kindred": 136, + "Rad Counters": 6, + "Shapeshifter Kindred": 27, + "Pirate Kindred": 2, + "Populate": 14, + "Dryad Kindred": 53, + "Dog Kindred": 38, + "Demigod Kindred": 2, + "Halfling Kindred": 20, + "Lifegain Triggers": 12, + "Treefolk Kindred": 112, + "Elephant Kindred": 57, + "Corrupted": 6, + "Poison Counters": 46, + "Toxic": 17, + "Centaur Kindred": 75, + "Djinn Kindred": 4, + "First strike": 31, + "Landfall": 113, + "Plant Kindred": 100, + "Mite Kindred": 2, + "Atog Kindred": 4, + "Airbending": 1, + "Ally Kindred": 55, + "Bending": 34, + "Earthbending": 31, + "Firebending": 3, + "Waterbending": 2, + "Eldrazi Kindred": 45, + "Experience Counters": 4, + "Bringer Kindred": 5, + "Construct Kindred": 12, + "Bestow": 11, + "Equipment Matters": 108, + "Manticore Kindred": 2, + "Illusion Kindred": 2, + "Earthbend": 31, + "Cantrips": 118, + "Food": 98, + "Freerunning": 2, + "Enchantment Tokens": 15, + "Shrines Matter": 6, + "Bear Kindred": 57, + "Robot Kindred": 11, + "Battles Matter": 10, + "Companion": 4, + "Elk Kindred": 29, + "Scry": 52, + "Rooms Matter": 6, + "Crew": 15, + "Exhaust": 15, + "Vehicles": 42, + "Changeling": 17, + "Age Counters": 26, + "Cumulative upkeep": 20, + "Hexproof from": 5, + "Converge": 8, + "Crystal Counters": 1, + "Protection": 42, + "Protection from Quality": 15, + "Sliver Kindred": 31, + "Sphinx Kindred": 1, + "Wall Kindred": 27, + "Eminence": 2, + "Devoid": 30, + "Phasing": 4, + "Myr Kindred": 2, + "Hideaway": 4, + "Backgrounds Matter": 12, + "Phoenix Kindred": 1, + "Doctor Kindred": 12, + "Protection from Color": 33, + "Homunculus Kindred": 3, + "Merfolk Kindred": 60, + "Fight": 110, + "Gremlin Kindred": 1, + "Raid": 1, + "Tyranid Kindred": 28, + "Harmonize": 4, + "Energy": 24, + "Energy Counters": 24, + "Resource Engine": 24, + "Monkey Kindred": 7, + "Bird Kindred": 42, + "Ferocious": 11, + "Storm": 6, + "Defender": 50, + "Egg Kindred": 3, + "Rabbit Kindred": 19, + "Backup": 7, + "Fox Kindred": 5, + "Mercenary Kindred": 10, + "Junk Token": 3, + "Junk Tokens": 3, + "Convert": 4, + "Deserts Matter": 21, + "Monarch": 6, + "Token Modification": 14, + "Detective Kindred": 19, + "Hamster Kindred": 2, + "Shroud": 42, + "Discover": 8, + "Parley": 6, + "Griffin Kindred": 2, + "Food Token": 91, + "Archer Kindred": 66, + "Start your engines!": 3, + "Boast": 2, + "Melee": 4, + "Monk Kindred": 33, + "Eye Kindred": 2, + "Formidable": 9, + "Living metal": 1, + "More Than Meets the Eye": 2, + "Wolf Kindred": 96, + "Descend": 5, + "Monstrosity": 15, + "Disguise": 12, + "Boar Kindred": 40, + "Pest Kindred": 6, + "Badger Kindred": 11, + "Domain": 24, + "Troll Kindred": 47, + "Drone Kindred": 17, + "Scion Kindred": 9, + "Forage": 6, + "Squirrel Kindred": 40, + "Forestwalk": 24, + "Bounty Counters": 1, + "Learn": 4, + "Worm Kindred": 5, + "Adapt": 15, + "Wombat Kindred": 2, + "Flashback": 51, + "Skeleton Kindred": 6, + "Morbid": 14, + "Hag Kindred": 4, + "Aftermath": 10, + "Cleave": 2, + "Scavenge": 10, + "Incubate": 6, + "Incubator Token": 6, + "Role token": 9, + "Loyalty Counters": 18, + "Dredge": 7, + "Convoke": 26, + "Partner": 28, + "Collect evidence": 8, + "Undergrowth": 9, + "Alien Kindred": 13, + "Partner - Survivors": 2, + "Survivor Kindred": 9, + "Crocodile Kindred": 19, + "Devour": 13, + "Wither": 7, + "Peasant Kindred": 15, + "Plot": 12, + "Partner with": 14, + "Plague Counters": 1, + "Escape": 7, + "Threshold": 26, + "Leech Kindred": 3, + "Persist": 9, + "Scorpion Kindred": 5, + "Open an Attraction": 5, + "Performer Kindred": 12, + "Fathomless descent": 1, + "Swampwalk": 11, + "Time Counters": 16, + "Mount Kindred": 20, + "Saddle": 13, + "Gamer Kindred": 1, + "Guest Kindred": 6, + "Spore Counters": 16, + "Meld": 2, + "Finality Counters": 6, + "Magecraft": 7, + "Retrace": 5, + "Shield Counters": 12, + "Alliance": 6, + "Yeti Kindred": 5, + "Nymph Kindred": 6, + "Fish Kindred": 5, + "Vedalken Kindred": 6, + "Hippo Kindred": 9, + "Phelddagrif Kindred": 2, + "Mole Kindred": 9, + "Powerstone Token": 4, + "Daybound": 18, + "Nightbound": 18, + "Rebel Kindred": 5, + "Modular": 1, + "Hellion Kindred": 4, + "Werewolf Kindred": 53, + "Samurai Kindred": 7, + "Oil Counters": 10, + "Minotaur Kindred": 3, + "Dwarf Kindred": 6, + "Friends Forever": 2, + "Tribute": 4, + "Riot": 7, + "Storage Counters": 4, + "Satyr Kindred": 22, + "Bloodrush": 7, + "Conspire": 4, + "Juggernaut Kindred": 1, + "Intimidate": 3, + "Jackal Kindred": 6, + "Legendary landwalk": 2, + "Compleated": 4, + "Equip": 30, + "Equipment": 32, + "Rampage": 4, + "Ravenous": 9, + "Noggle Kindred": 1, + "Fetch Counters": 1, + "Pilot Kindred": 6, + "Basilisk Kindred": 12, + "Web-slinging": 5, + "Gnoll Kindred": 2, + "Pack tactics": 4, + "Depletion Counters": 3, + "Basic landcycling": 6, + "Landcycling": 16, + "Typecycling": 16, + "Forestcycling": 10, + "Mountaincycling": 1, + "Evoke": 8, + "Incarnation Kindred": 7, + "Barbarian Kindred": 5, + "Spawn Kindred": 15, + "Serpent Kindred": 7, + "Kraken Kindred": 4, + "Slumber Counters": 1, + "Fractal Kindred": 12, + "Ox Kindred": 8, + "Flood Counters": 2, + "Islandwalk": 8, + "Channel": 12, + "Secret council": 2, + "Card Selection": 21, + "Explore": 21, + "Drake Kindred": 6, + "Stun Counters": 4, + "Evolve": 14, + "Otter Kindred": 1, + "Prowess": 1, + "Manifest": 19, + "Constellation": 13, + "Octopus Kindred": 7, + "Shark Kindred": 5, + "Cockatrice Kindred": 2, + "Will of the council": 2, + "Gargoyle Kindred": 2, + "Ouphe Kindred": 17, + "Vivid": 8, + "Salamander Kindred": 4, + "Croak Counters": 1, + "Manifest dread": 13, + "Chimera Kindred": 5, + "Jellyfish Kindred": 2, + "Study Counters": 1, + "Emerge": 4, + "Metathran Kindred": 1, + "Growth Counters": 4, + "Gift": 5, + "Everything Counters": 1, + "Graft": 9, + "Thopter Kindred": 1, + "Crab Kindred": 6, + "Moonfolk Kindred": 1, + "Paradox": 3, + "Cloak": 2, + "Exert": 7, + "Archon Kindred": 2, + "Outlast": 3, + "Affinity": 5, + "Kithkin Kindred": 16, + "Support": 9, + "Renown": 7, + "Myriad": 7, + "Hippogriff Kindred": 1, + "Coven": 10, + "Bolster": 9, + "Unicorn Kindred": 7, + "Rally": 3, + "Umbra armor": 7, + "Protection from Creature Type": 3, + "Mystic Kindred": 3, + "Nomad Kindred": 2, + "Plainscycling": 1, + "Pegasus Kindred": 1, + "Revolt": 7, + "Survival": 7, + "Flanking": 2, + "Training": 4, + "Mouse Kindred": 2, + "Horse Kindred": 10, + "Hyena Kindred": 3, + "Doctor's Companion": 5, + "Doctor's companion": 5, + "Echo": 13, + "Megamorph": 8, + "Servo Kindred": 6, + "Bargain": 5, + "Brushwagg Kindred": 4, + "Spell mastery": 3, + "Heroic": 6, + "Sloth Kindred": 3, + "Madness": 2, + "Aurochs Kindred": 4, + "Reinforce": 5, + "Job select": 2, + "Enlist": 3, + "Grandeur": 1, + "Capybara Kindred": 1, + "Foretell": 7, + "Level Counters": 4, + "Level Up": 4, + "Quest Counters": 5, + "Fade Counters": 5, + "Fading": 5, + "Miracle": 2, + "Ticket Counters": 1, + "Mongoose Kindred": 3, + "Soulshift": 12, + "Prototype": 6, + "Kirin Kindred": 1, + "Provoke": 3, + "Warp": 8, + "Ki Counters": 2, + "Offspring": 4, + "Amplify": 3, + "Goat Kindred": 3, + "Metalcraft": 3, + "Gnome Kindred": 2, + "Tiefling Kindred": 1, + "Cases Matter": 2, + "Solved": 2, + "Behold": 4, + "Champion": 3, + "Assist": 2, + "Acorn Counters": 1, + "Fateful hour": 2, + "Pupa Counters": 1, + "Ninjutsu": 3, + "Escalate": 1, + "Join forces": 1, + "Germ Kindred": 2, + "Living weapon": 2, + "Strive": 5, + "Buyback": 5, + "Caves Matter": 5, + "Suspend": 12, + "Fabricate": 4, + "Wind Counters": 2, + "Cost Scaling": 3, + "Modal": 3, + "Spree": 3, + "Spellshaper Kindred": 11, + "Vanishing": 3, + "Wolverine Kindred": 4, + "Soulbond": 8, + "Employee Kindred": 3, + "Zubera Kindred": 1, + "Choose a background": 6, + "Endure": 3, + "Awaken": 1, + "Venture into the dungeon": 6, + "Antelope Kindred": 7, + "Epic": 1, + "Glimmer Kindred": 1, + "Eternalize": 3, + "Overload": 2, + "Clash": 6, + "Entwine": 7, + "Waterbend": 1, + "\\+2/\\+2 Counters": 1, + "Squad": 1, + "Adamant": 3, + "Sheep Kindred": 1, + "Beaver Kindred": 1, + "Mountainwalk": 1, + "Multikicker": 4, + "Slime Counters": 1, + "Replicate": 2, + "Demonstrate": 1, + "Tower Counters": 1, + "Embalm": 1, + "Undying": 4, + "Efreet Kindred": 2, + "Harmony Counters": 1, + "Bushido": 2, + "Craft": 2, + "Graveyard Matters": 2, + "Ferret Kindred": 1, + "Splice": 4, + "Split second": 4, + "Horsemanship": 1, + "Kinship": 3, + "Awakening Counters": 1, + "Vitality Counters": 1, + "Max speed": 1, + "Lieutenant": 2, + "Unearth": 3, + "Sneak": 2, + "Verse Counters": 3, + "Fungus Counters": 2, + "Slug Kindred": 2, + "Divinity Counters": 1, + "Licid Kindred": 2, + "Council's dilemma": 3, + "Impending": 1, + "Dethrone": 1, + "Will of the Planeswalkers": 1, + "Offering": 1, + "Inspired": 2, + "Chroma": 2, + "Defense Counters": 1, + "Rebound": 3, + "Ribbon Counters": 1, + "Camel Kindred": 1, + "Possum Kindred": 2, + "Pangolin Kindred": 2, + "Recover": 1, + "Undaunted": 1, + "Map Token": 2, + "Multiple Copies": 1, + "Slime Against Humanity": 1, + "Slith Kindred": 1, + "Spike Kindred": 10, + "Armadillo Kindred": 1, + "Monger Kindred": 1, + "Supply Counters": 1, + "Ripple": 1, + "Replacement Draw": 1, + "For Mirrodin!": 1, + "Reconfigure": 2, + "Tempting offer": 1, + "Ascend": 2, + "Hatching Counters": 1, + "Gold Token": 1, + "Spheres Matter": 1, + "Read Ahead": 2, + "Tiered": 1, + "Banding": 1, + "Velocity Counters": 1, + "Dash": 1, + "Mentor": 1, + "Nest Counters": 1, + "Toy Kindred": 1, + "Processor Kindred": 1, + "Varmint Kindred": 1, + "Praetor Kindred": 3, + "-0/-1 Counters": 1 + } }, "generated_from": "merge (analytics + curated YAML + whitelist)", "metadata_info": { "mode": "merge", - "generated_at": "2026-02-20T17:27:58", - "curated_yaml_files": 740, + "generated_at": "2026-03-19T14:47:40", + "curated_yaml_files": 735, "synergy_cap": 5, "inference": "pmi", "version": "phase-b-merge-v1", - "catalog_hash": "78f24ccdca52d048d5325bd6a16dc2ad3ec3826119adbf75985c64617355b79b" + "catalog_hash": "f38d2067dcaa8a07a3d9789fdf6c23024f658b3c29f92c9c26a3945224d45e2a" }, "description_fallback_summary": null } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 40e7f52..5d35c71 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,6 +34,9 @@ services: SIMILARITY_CACHE_PATH: "card_files/similarity_cache.parquet" # Path to Parquet cache file ENABLE_BATCH_BUILD: "1" # 1=enable Build X and Compare feature; 0=hide build count slider + # Theme Catalog Settings + THEME_MIN_CARDS: "5" # Minimum cards required for a theme to be kept in system (default: 5) + # HEADLESS_EXPORT_JSON: "1" # 1=export resolved run config JSON ENABLE_PARTNER_MECHANICS: "1" # 1=unlock partner/background commander inputs ENABLE_PARTNER_SUGGESTIONS: "1" # 1=enable partner suggestion API/UI (requires dataset) diff --git a/dockerhub-docker-compose.yml b/dockerhub-docker-compose.yml index 038d8d4..d85804c 100644 --- a/dockerhub-docker-compose.yml +++ b/dockerhub-docker-compose.yml @@ -36,6 +36,9 @@ services: SIMILARITY_CACHE_PATH: "card_files/similarity_cache.parquet" # Path to Parquet cache file ENABLE_BATCH_BUILD: "1" # 1=enable Build X and Compare feature; 0=hide build count slider + # Theme Catalog Settings + THEME_MIN_CARDS: "5" # Minimum cards required for a theme to be kept in system (default: 5) + # HEADLESS_EXPORT_JSON: "1" # 1=export resolved run config JSON ENABLE_PARTNER_MECHANICS: "1" # 1=unlock partner/background commander inputs ENABLE_PARTNER_SUGGESTIONS: "1" # 1=enable partner suggestion API/UI (requires dataset) diff --git a/docs/theme_editorial_guide.md b/docs/theme_editorial_guide.md deleted file mode 100644 index 66d452e..0000000 --- a/docs/theme_editorial_guide.md +++ /dev/null @@ -1,383 +0,0 @@ -# Theme Editorial Workflow Guide - -**Version**: 1.0.0 -**Last Updated**: March 18, 2026 -**Audience**: Contributors, maintainers, theme curators - -## Overview - -This guide documents the theme editorial quality system introduced in Roadmap 12 (M1-M4). It explains how theme quality is measured, how to curate high-quality themes, and how to use the linter to maintain catalog standards. - -## Quality Scoring System - -### Scoring Components - -Theme editorial quality is measured using an **Enhanced Quality Score** (0.0-1.0 scale) with four weighted components: - -| Component | Weight | Description | -|-----------|--------|-------------| -| **Card Count** | 30 points | Number of example cards (target: 8+) | -| **Uniqueness** | 40 points | Ratio of cards appearing in <25% of themes | -| **Description** | 20 points | Manual (20) > Rule-based (10) > Generic (0) | -| **Curation** | 10 points | Presence of curated synergies | - -**Total**: 100 points → normalized to 0.0-1.0 scale - -### Quality Tiers - -Themes are classified into four quality tiers based on their score: - -| Tier | Score Range | Description | Priority | -|------|-------------|-------------|----------| -| **Excellent** | ≥0.75 | 8+ unique cards, manual description, well-curated | ✅ Maintain | -| **Good** | 0.60-0.74 | Strong card selection, rule-based description | ✅ Monitor | -| **Fair** | 0.40-0.59 | Adequate cards, may have some generic cards or description | ⚠️ Improve | -| **Poor** | <0.40 | Few cards, generic description, high duplication | 🔴 Urgent | - -### Uniqueness vs Duplication - -- **Uniqueness Ratio**: Fraction of example cards appearing in <25% of themes (higher is better) -- **Duplication Ratio**: Fraction of example cards appearing in >40% of themes (lower is better) - -**Example**: -- A theme with `["Sol Ring", "Command Tower", "Unique Card A", "Unique Card B"]` might have: - - Uniqueness: 0.50 (2/4 cards are unique) - - Duplication: 0.50 (2/4 cards are generic staples) - -## Curating High-Quality Themes - -### Best Practices for Example Cards - -#### DO ✅ -- **Use 8+ example cards** - Maximizes card count score (30 points) -- **Select unique, theme-specific cards** - Boosts uniqueness score (40 points) -- **Avoid generic staples** - Focus on cards that actually exemplify the theme -- **Include archetype-specific cards** - Not just "good cards in these colors" -- **Prioritize cards that demonstrate the strategy** - Help players understand the theme - -#### DON'T ❌ -- **Include generic ramp** - `Sol Ring`, `Arcane Signet`, `Commander's Sphere` -- **Include generic card draw** - `Rhystic Study`, `Esper Sentinel` (unless theme-relevant) -- **Include generic removal** - `Swords to Plowshares`, `Cyclonic Rift` (unless theme-relevant) -- **Use <5 example cards** - Severely impacts card count score -- **Copy example cards from similar themes** - Reduces uniqueness, increases duplication - -#### Example Comparison - -**Poor Quality** (Score: 0.35, Tier: Poor): -```yaml -display_name: Voltron Auras -example_cards: - - Sol Ring - - Lightning Greaves - - Swiftfoot Boots - - Sword of Feast and Famine -description_source: generic -``` -*Issues*: Generic cards, only 4 cards, generic description - -**Excellent Quality** (Score: 0.82, Tier: Excellent): -```yaml -display_name: Voltron Auras -example_cards: - - All That Glitters - - Ethereal Armor - - Ancestral Mask - - Bess, Soul Nourisher - - Sigil of the Empty Throne - - Mesa Enchantress - - Three Dreams - - Spirit Mantle -description_source: manual -description: "Enchants a single creature with multiple Auras to create a powerful, evasive threat while drawing cards and generating value from enchantress effects." -popularity_pinned: false -``` -*Strengths*: 8 unique aura-specific cards, manual description, clear strategy - -### Description Quality - -#### Description Sources - -- **`manual`**: Hand-written by a curator (20 points) - - Clear, concise, strategic explanation - - Explains *how* the theme wins, not just what it does - - Example: "Reanimates high-cost creatures from the graveyard early via discard/mill effects, bypassing mana costs to establish board dominance." - -- **`rule`**: Generated from external heuristics (10 points) - - Template-based with theme-specific keywords - - Example: "Leverages the graveyard for recursion and value." - -- **`generic`**: Fallback template (0 points) - - Minimal information, no strategic insight - - Example: "A theme focused on [theme name] strategies." - - ⚠️ **Should be upgraded to rule or manual** - -#### Writing Manual Descriptions - -Good manual descriptions answer: -1. **What** does the theme do? (mechanics) -2. **How** does it win? (strategy) -3. **Why** is it effective? (synergies/payoffs) - -**Template**: `[Action] [Key Cards/Mechanics] to [Strategic Goal] via [Synergies/Payoffs].` - -**Example**: -- Theme: Aristocrats -- Description: "Sacrifices creatures for value via death triggers, then recurs them to repeat the process while generating tokens, card advantage, and direct damage." - -### Popularity Pinning - -Use `popularity_pinned: true` to prevent automatic popularity_bucket updates when you've manually curated the bucket: - -```yaml -popularity_bucket: Common -popularity_pinned: true # Prevents overwriting during rebuilds -``` - -**When to pin**: -- Manual research shows different bucket than analytics -- Theme popularity has shifted but analytics lag -- Theme is intentionally promoted/demoted for editorial reasons - -**When NOT to pin**: -- Default/initial backfill state -- No strong reason to override analytics - -## Using the Linter - -### Running the Linter - -The linter is integrated into `validate_theme_catalog.py`: - -```bash -# Basic quality checks (M1) -python code/scripts/validate_theme_catalog.py --check-quality - -# Full linter with M4 rules -python code/scripts/validate_theme_catalog.py --check-quality --lint - -# Customize thresholds -python code/scripts/validate_theme_catalog.py --lint \ - --lint-duplication-threshold 0.4 \ - --lint-quality-threshold 0.5 -``` - -### Linter Rules - -| Rule | Severity | Threshold | Description | -|------|----------|-----------|-------------| -| **Missing description_source** | ERROR | N/A | Theme has description but no source metadata | -| **Generic description** | WARNING | N/A | Description_source is "generic" | -| **Missing popularity_pinned** | ERROR | N/A | popularity_pinned=True but no bucket set | -| **High duplication** | WARNING | >0.5 | >50% of example cards are generic staples | -| **Low quality score** | WARNING | <0.5 | Quality score below threshold | - -### Interpreting Linter Output - -#### Example Output - -``` -VALIDATION FAILED: - - [QUALITY] voltron-auras.yml has generic description_source - consider upgrading to rule-based or manual - - [LINT-WARNING] voltron-auras.yml has high duplication ratio (0.62 > 0.5). Generic cards: Sol Ring, Lightning Greaves, Swiftfoot Boots - - [LINT-WARNING] voltron-auras.yml has low quality score (0.38 < 0.5, tier=Poor). Suggestions: Add more example cards (target: 8+); Upgrade to manual or rule-based description; Replace generic staples with unique cards -``` - -#### Recommended Actions - -1. **High Duplication**: - - Review listed generic cards - - Replace with theme-specific alternatives - - Example: Replace `Sol Ring` with `All That Glitters` for Voltron Auras - -2. **Low Quality Score**: - - Follow suggestions in order: cards first, then description, then deduplicate - - **Add more cards** (fastest improvement) - - **Upgrade description** (if you understand the theme) - - **Replace generic cards** (if cards are already adequate) - -3. **Generic Description**: - - Write a manual description following the template - - Or wait for rule-based generation improvements - -## Workflow Examples - -### Example 1: Improving a Fair-Quality Theme - -**Starting Point**: -```yaml -display_name: Graveyard Value -example_cards: - - Command Tower - - Sol Ring - - Eternal Witness - - Greenwarden of Murasa - - Reanimate -description_source: generic -description: "A theme focused on graveyard strategies." -``` -**Score**: 0.45 (Fair) - -**Step 1: Add Example Cards** (target: 8+) -```yaml -example_cards: - - Command Tower # Keep for now - - Sol Ring # Keep for now - - Eternal Witness - - Greenwarden of Murasa - - Reanimate - - Living Death # Added - - Victimize # Added - - Animate Dead # Added - - Muldrotha, the Gravetide # Added -``` -**Score**: 0.53 (Fair → Good threshold) - -**Step 2: Upgrade Description** -```yaml -description_source: manual -description: "Fills the graveyard via self-mill and discard, then recurs high-value creatures and spells for repeated use, generating card advantage and overwhelming opponents with recursive threats." -``` -**Score**: 0.63 (Good) - -**Step 3: Replace Generic Cards** -```yaml -example_cards: - - World Shaper # Replace Command Tower - - Crucible of Worlds # Replace Sol Ring - - Eternal Witness - - Greenwarden of Murasa - - Reanimate - - Living Death - - Victimize - - Animate Dead - - Muldrotha, the Gravetide -``` -**Score**: 0.78 (Excellent!) - -### Example 2: Maintaining Excellent Quality - -**Theme**: Aristocrats -**Score**: 0.82 (Excellent) - -**Maintenance Checklist**: -- ✅ 10 unique example cards -- ✅ Manual description matches current strategy -- ✅ Popularity bucket accurate (review quarterly) -- ✅ No generic staples in example list -- ✅ Synergies reflect current meta cards - -**Action**: Pin popularity if manually verified: -```yaml -popularity_bucket: Very Common -popularity_pinned: true -``` - -## Common Pitfalls - -### ❌ Pitfall 1: "Good Cards" Aren't Always Theme Cards - -**Bad**: -```yaml -display_name: Enchantress -example_cards: - - Rhystic Study # Good card, but not enchantress-specific - - Sol Ring # Generic ramp - - Swords to Plowshares # Generic removal -``` - -**Good**: -```yaml -display_name: Enchantress -example_cards: - - Enchantress's Presence - - Setessan Champion - - Mesa Enchantress - - Argothian Enchantress -``` - -### ❌ Pitfall 2: Copying Example Cards - -Themes with high overlap hurt the catalog's uniqueness metrics: - -**Bad** (Voltron + Equipment themes share 6/8 cards): -```yaml -# voltron-equipment.yml -example_cards: [Sword of Fire and Ice, Batterskull, Colossus Hammer, Lightning Greaves, ...] - -# voltron-auras.yml -example_cards: [Sword of Fire and Ice, Batterskull, Colossus Hammer, Lightning Greaves, ...] -``` - -**Good** (Distinct card pools): -```yaml -# voltron-equipment.yml -example_cards: [Sword of Fire and Ice, Batterskull, Colossus Hammer, Sigarda's Aid, ...] - -# voltron-auras.yml -example_cards: [All That Glitters, Ethereal Armor, Ancestral Mask, Estrid, the Masked, ...] -``` - -### ❌ Pitfall 3: Ignoring Linter Warnings - -Linter warnings accumulate over time. Address them during regular maintenance: - -**Quarterly Workflow**: -1. Run `--lint --lint-quality-threshold 0.4` -2. Identify Poor/Fair themes (<0.5 score) -3. Improve 5-10 themes per session -4. Commit improvements incrementally - -## Quality Metrics Dashboard - -Track catalog health with these statistics: - -```bash -# Generate quality report (future tool) -python code/scripts/generate_quality_report.py - -# Expected Output: -# Total Themes: 740 -# Excellent: 220 (30%) -# Good: 300 (40%) -# Fair: 180 (24%) -# Poor: 40 ( 6%) -# Average Uniqueness: 0.62 -# Average Duplication: 0.28 -``` - -**Health Benchmarks**: -- **Excellent + Good**: >60% -- **Poor**: <10% -- **Average Uniqueness**: >0.50 -- **Average Duplication**: <0.35 - -## Resources - -- **Heuristics Config**: `config/themes/editorial_heuristics.yml` -- **Validation Script**: `code/scripts/validate_theme_catalog.py` -- **Backfill Script**: `code/scripts/backfill_editorial_fields.py` -- **Service Implementation**: `code/web/services/theme_editorial_service.py` -- **Tests**: `code/tests/test_theme_editorial_service.py`, `code/tests/test_theme_linter.py` - -## FAQ - -### Q: What's the minimum acceptable quality score? -**A**: Fair (0.40) is acceptable for niche themes. Aim for Good (0.60+) for common themes. - -### Q: Should I remove all generic staples? -**A**: Not necessarily. If a card like `Sol Ring` is *genuinely* theme-relevant (e.g., "Colorless Matters"), include it. But avoid generic ramp/removal that appears in 50%+ of decks. - -### Q: How do I handle themes with few unique cards? -**A**: Focus on description quality (manual) and curate the best available examples. Some themes (e.g., "Mulligan Matters") inherently have limited card pools. - -### Q: Can I batch-fix themes? -**A**: Yes, but commit incrementally and test after each batch to avoid introducing errors. Use `--lint` to validate before committing. - -### Q: What if the linter disagrees with my curation? -**A**: Linter warnings are suggestions, not hard requirements. Use judgment for niche/experimental themes, but document reasoning in the theme's `notes` field. - ---- - -**Maintained by**: MTG Python Deckbuilder Contributors -**Feedback**: Open an issue on GitHub with `[editorial]` prefix