From 4f7d39acba816eb4a0d169e3c825879606f2a9bc Mon Sep 17 00:00:00 2001 From: matt Date: Mon, 29 Sep 2025 23:00:57 -0700 Subject: [PATCH] chore: prep 2.3.1 docs and note Hero creature handling --- .env.example | 15 +- CHANGELOG.md | 13 + README.md | Bin 109268 -> 121656 bytes RELEASE_NOTES_TEMPLATE.md | 7 + code/file_setup/setup_constants.py | 11 +- code/headless_runner.py | 692 +++++++++++++++++- .../themes/description_fallback_history.jsonl | 4 + config/themes/theme_list.json | 486 ++++++------ docker-compose.yml | 15 +- dockerhub-docker-compose.yml | 17 +- pyproject.toml | 2 +- 11 files changed, 1036 insertions(+), 226 deletions(-) diff --git a/.env.example b/.env.example index add183d..5ae994f 100644 --- a/.env.example +++ b/.env.example @@ -13,7 +13,7 @@ # HOST=0.0.0.0 # Uvicorn bind host (only when APP_MODE=web). # PORT=8080 # Uvicorn port. # WORKERS=1 # Uvicorn worker count. -APP_VERSION=v2.2.10 # Matches dockerhub compose. +APP_VERSION=v2.3.1 # Matches dockerhub compose. ############################ # Theming @@ -50,6 +50,19 @@ WEB_THEME_PICKER_DIAGNOSTICS=0 # 1=enable uncapped synergies, diagnostics f # RANDOM_UI=1 # Show Surprise/Reroll/Share controls in UI # RANDOM_MAX_ATTEMPTS=5 # Cap retry attempts for constrained random builds # RANDOM_TIMEOUT_MS=5000 # Per-attempt timeout (ms) +# HEADLESS_RANDOM_MODE=1 # Force headless runner to invoke random flow instead of scripted build +# RANDOM_THEME=Treasure # Legacy single-theme alias (maps to primary theme if others unset) +# RANDOM_PRIMARY_THEME=Treasure # Primary theme slug (case-insensitive) +# RANDOM_SECONDARY_THEME=Artifacts # Secondary theme slug +# RANDOM_TERTIARY_THEME=Tokens # Tertiary theme slug +# RANDOM_AUTO_FILL=1 # Auto-fill missing secondary/tertiary slots +# RANDOM_AUTO_FILL_SECONDARY=1 # Explicit secondary auto-fill override (fallback from RANDOM_AUTO_FILL) +# RANDOM_AUTO_FILL_TERTIARY=1 # Explicit tertiary auto-fill override (fallback from RANDOM_AUTO_FILL) +# RANDOM_STRICT_THEME_MATCH=0 # Require strict commander theme matches +# RANDOM_CONSTRAINTS= # Inline JSON or path to JSON constraints for random selection +# RANDOM_CONSTRAINTS_PATH= # Alternate path-based constraints override (takes precedence if set) +# RANDOM_SEED= # Optional deterministic seed (int or string) +# RANDOM_OUTPUT_JSON=deck_files/random_build.json # Where to write random build metadata payload ############################ # Automation & Performance (Web) diff --git a/CHANGELOG.md b/CHANGELOG.md index d01bd5c..cfbbc00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,10 +14,22 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning ## [Unreleased] ### Added +- _No changes yet._ + +### Changed +- _No changes yet._ + +### Fixed +- _No changes yet._ + +## [2.3.1] - 2025-09-29 +### Added +- Headless runner parity: added `--random-mode` and accompanying `--random-*` flags to mirror the web Surprise/Reroll builder (multi-theme inputs, auto-fill overrides, deterministic seeds, constraints, and optional JSON payload export). - Tests: added `test_headless_skips_owned_prompt_when_files_present` to guard the headless runner against regressions when owned card lists are present. - Included the tiny `csv_files/testdata` fixture set so CI fast determinism tests have consistent sample data. ### Changed +- Configuration docs: docker compose manifests, `.env.example`, and README now enumerate all supported random-mode environment variables with sensible defaults and refreshed flag documentation for the headless runner. - Owned Cards library tiles now use larger thumbnails and wider columns, and virtualization only activates when more than 800 cards are present to keep scrolling smooth. - Theme catalog schema now accepts optional `id` values on entries so refreshed catalogs validate cleanly. - CI installs `httpx` with the rest of the web stack and runs pytest via `python -m pytest` so FastAPI tests resolve the local `code` package correctly. @@ -40,6 +52,7 @@ This format follows Keep a Changelog principles and aims for Semantic Versioning - Duplicate overlap highlighting on desktop hover has been removed; theme pills now render once without stray bullets even when multiple overlaps are present. - Headless runner no longer loops on the power bracket prompt when owned card files exist; scripted responses now auto-select defaults with optional `HEADLESS_USE_OWNED_ONLY` / `HEADLESS_OWNED_SELECTION` overrides for automation flows. - Regenerated `logs/perf/theme_preview_warm_baseline.json` to repair preview performance CI regressions caused by a malformed baseline file and verified the regression gate passes with the refreshed data. +- File setup now keeps cards with the Hero creature type; previously they were filtered out alongside the non-Commander-legal Hero card type. ## [2.3.0] - 2025-09-26 ### Added diff --git a/README.md b/README.md index 8cbe75c817a3e2dbee4a1d11c0710b7ea544130d..44f520ae889041e886fc179791f7474f98fb9bd6 100644 GIT binary patch delta 11786 zcmcgyYiwK99lvKw3oT9B#5E@KQro1Dq={`pTgq0d5Yj*ceWZDemx7%pX$Vd1G>>*2 zWgcQ%P1+YW?e{z99^Z$r z?Z6~-<@m<;JpPa0|9S2o{Z*YYrVzKfHh%FT8~;u>x4Cj z`x)FFx2ADlw9epu-kP-*aTW1d!v7OiLB8{nh_|1{?IIpdVE7y+B&`noEm)^B&~a`5$mXR#OlVQe(R7m$Oq8@ZdA0Ev3aMJ!J|>@thInOxXnD)U3Lh|TWMU6 zDxwywJf0S<6+B(wUpw_u^jN7!}dA+c6XU!{%3Yi zTKLayexP-RzZ%k$)=8{<7QbZi8SwGR8C;j+NogA2tGidTseVtQE9hyI?gcNl^R+L8(J5PbiWqc;>hc~r0p1^48rjr!O=4VA) zqnFsc{hxodCXQg=!(fSm4cXUD?TFbgH#BYJvWrlR9+kIGJslHSE*i0ae0h_9VRw>3J+y0NqK&BI*7r?mMg=aB&qNP=S>@N4=<(>fjBU!Pn z#xm!#BE5#f%FFnEk#MMLyOIqo12IJ9Yg@(j^`tB*lf4D-O}Y4WN?TY^RzV$m<)!qh zq!V37(>20L$XjN`+KASufu=Ge%E*58>iW9$Kv3;Bxb#qDanBwfUTz;51#s?h)a%2JN;r zyBq~RbBBnpbC-H)uh=#iSc>QZ7&R-40BxbXqU$vYharqHV54K3woknql{;@28$Q2c zEy`3xM0XJAv*1A6(dMMVF-6$!TuJMI-E}Rsm#%9PO6QKtfB?rBQP*)_CDQX5xG(IY z9rU6C?*b&HC_dH#x_KLRFOnE>+H*!2F z;=Q$YVT_r;YDs($zM#mY`ehLoqxaT(bY{u}mK^;WS!)ipTbi{dWiBaN`>XGHSn)Xv zTV7EPvpms5gUcKCbqC~X(O_TgipV>YqUoOMd-p?7)8KO%d-Ki*Wg?) zMw9+iuSkfjed@}NUB>;XXsSRr1?1L*8)JIO>q!ncGrSf~9G$}N%qNYr%|o1e?VQK0 zQl!l4^40;QLH*W!Rt|S#h;D~)*N@b7%#+ACF5z-f*b}^&G@QFg*$P<27!iuiMbRn8au75;5fb!1#@ub`@Jw}?Gh zn<}u3t44BxXD(GH1u5?v8&*ee+3vWn9m zjfl5U+PB#9TH4_H6iFU+PI^z*f@f~O; zD1zBPpwmgiQVa5pN5IVwo><$=_%n}d0)sdtDX%6)w2!W80Y6g7lx(>+hjB2jxRPQB z(+P|(eUe3xwrw?}4aiH6h}bUIXgpHK&Q;b2xq12G&0@zrXVmru;Lxzmc92$cMPCKe z4q`mZ0>(StZ8@WAx5QhqG+8O&()lbqV_bnEn8i06EEI(UGx^HRB38<&l$n@uk)=(7 zWh65j-HW2!c|VPqr|Xm2P#?lbHeht|s9k8JzYnui;`X@Al@5xN6glZrK(vXxfDCQY=jrP~_|d(5PkPKBgQgXtpQyPA+AB zOC82IDv-cs@34q_DyjqSXc`+Q9pvhCrZc>ZZc%=#q7A6X3O4-c3My;s|Emp%72P`8 zFau%lbht6_^*0>u##3#@NH^@~K8nj9=ERNYF)+KJxzaBhCwLG!V;@pm=8hw{a&kTg zymDooTF|!5*0oXW9m(zv6CK^Ts?*`YUWYK2h@Vv5%cYW|TpEIZDMFMjB!ad>fx+7b zTL6zNv5BCwrcnqPGG-qEmq*dn%)tecYt!GXWHV!Ry41V)a_!Z_sXUeukuG<568qHb zovN6`UE?S+s!S}1lRQE2_AZNOEm2lYCV$K_(OeI zm1NmBD_RqEgsR$%H?=g4*P#!{AyoaH!c}8QbqkNxpjGLA0D@(GvT6a_T`h^pdW5?z zcim)X?qAyt_5lRCN$<>%7uY+0^_%NU3@MBWUjOVzNgry^Q9wF}M1!T4EWk?Kg;9+2t(Ymf&pxddxwO6IvPX*=g%kM!^SOP;_=oI-YaKVS*$K0bvij6{7Wd>GLjj#=l zEE$3i*iW8sE13nf)O8n)L6B+Q3P3tr&Mo!XjGN^8o#+U*9zbSL9cp@~fI>=0OVZfR z&5d-owct=Zq13NYWs<@ieDyGVL7hWgrNsh#G$lbenfXR0OyNody>5@`W`1yE9&hV| z-f*YO7UmIE4pJT(HX|M>_@z`ax&%{2vgqR_Eb_2$43f4Qgm_N#A?L>+Ref%6WS7$V z2&>%ghgy)-%kSqHvzut zl>`hoOeo6beYcCp2ZlZP@zyB1rq-2-w_@6uiS)k}u{&eB9Il`oP4%j4xY^U^q=L%w$agMd~{ojbUj z4fs4!EvolaXoWTBVu#9njoz2%CADYztaW`E=}CF&+c^C3iXjWtGT`VFn2Z@}AA2ui zzq`F9&ah;T%e9CJ1h$dq?4F-S?9tzCw)3wwq>PfNIh$9R_%oJTPi#yCejQUm9Ktirsybili$shzjr}2*vs#>sJA{Ec?M4#Q17DIPj*E% zx<^2qjV$?6Nsjwbm*PHeh3 zG$CI2oSe&vyu5o#+{zyp<$F_NvwSQs9*{?-#cug_UL2QynHF1+R{A02Hahl0+dHv` z?m6rPjeIM{G)A2^5$I=*{i(Y|C1`>=ca4zsHY2Qlu~FlMV$=OWzQq?Lxq7cAoCT*- z1CcIT@`a4pB>zN}K7cgs!JvaNCd-O=_9J&}CqOqeybAwLC)CWntVV zuN6egebv^isN1dm)-C8gG4^Y9Rlr8KH?B2oy}{Gou)BUA?_BjJsyQ(1-AU)2ejruS zRw)DT8k>`La_8oax+Y31+yM#-+wIjz3zn-n|AOwxzNw%;c>!&*A;&GOiYDJT%+q u2Vx>E6Ek8&es@BA2SJA7(y3F@?)_z4?mjNIZX^Uv@{APb;?j(m=>H#0nR`?K delta 1971 zcmb_dU2M};6hDVJmd+s`rIz-;vHw;oo$EG+fL|L>wizJe=E7biVEP3#tnEfSKcXhQ zkf?E6;3gbmVgipmkg&u!69NR|g9)z+4?JOtZzdLbCjQRtT4vQaBdWF1c9cVAZvl6Obg~iS^#RUG`rH^K%V~sc3!~dWl=)S|2wx zRO0K4a;HzlALFNP!YrWh$*mC%scLg~-iDWlQyDfXb05J4j3d9%{mEV#W`^b`ra3TL2fUY9bOi%hIt zQ^RO`by*m|O#xTksfA)jN{%FwyirCY{ETPOMHoo$UgD%Puv`NgcLrHdlKi`o*Y|LH zEsDT0GjZc8J7%;LeCoEh<~DWXizCdGDR+6-YAya;Q?Ha_9Bj1AM3hJO@y_*2G@$QN zOs?#F0Q!+PI25X{nVl!L zdZ?e~3B{f7XJ1Tb;s2_JxnB->`l%1Z+G)pv&$tcS)V!ZUd)L&n0m@GE@k<-L?<}QiTZn&W_f?znc4PmvmBh`9b8zr z{W&kJGNsZw^JS@7F3#|2-x#v`77ft|9in$>oDR?c^+{-!v(S4{ZqD*C)A2AWZ_dG^ z2$ZL7w`6VCWZ+#V{EnH+Pn?jxJK9$|O7$JfLNbZ8r0r{l)KEd@h5cLg(bKRdteQ5F zCTP+o9!k`Mlf#skTX#8Nj(xv+jbdo8YJa88(XoKp^-Hk3@`mc`sfpJYQ+4#E)Q-+T bJ4aDSbwn4XeVn~=?E>%Ro`rkD@xea;JAkst diff --git a/RELEASE_NOTES_TEMPLATE.md b/RELEASE_NOTES_TEMPLATE.md index c39c9cc..9a65868 100644 --- a/RELEASE_NOTES_TEMPLATE.md +++ b/RELEASE_NOTES_TEMPLATE.md @@ -6,6 +6,7 @@ - Fast-path catalog validation now tolerates empty synergy lists while still flagging missing fields or non-string entries. - Committed deterministic CSV fixtures under `csv_files/testdata` so CI random-mode checks have a stable dataset. - Owned Cards library tiles are larger, virtualization only kicks in for very large libraries, and popovers no longer show empty role pills. +- File setup now keeps Hero creature type cards instead of filtering them with the non-Commander-legal Hero card type. - Random Mode fallback warning remains hidden when no theme filters are provided, keeping Surprise Me runs noise-free. - Hover previews regained the double-faced card flip button with state synced to the main tile, highlight only the themes that triggered inclusion, and present a mobile-friendly tap layout with centered positioning plus a close control. - Deck summary text view exposes inline flip toggles for double-faced cards so counts and face switching stay in sync. @@ -16,6 +17,7 @@ - Added opt-in telemetry counters, reroll throttling safeguards, and structured diagnostics exports. - Expanded tooling, documentation, and QA coverage for theme governance, performance profiling, and seed history management. - Hardened the headless runner against owned-card prompt loops with optional automation overrides and regression coverage. +- Headless runner random mode now mirrors the web UI configuration surface with CLI flag documentation, dry-run summaries, and JSON export parity. ## Highlights ### Multi-theme random builds @@ -43,6 +45,7 @@ - Random theme exclusion catalog with reporting script and documentation, alongside a multi-theme performance profiler and regression guard. - Taxonomy snapshot tooling, splash penalty analytics, and governance documentation updated for strict alias and example enforcement. - README, CHANGELOG, and release notes refreshed to cover the random modes feature set. +- Headless runner documentation now covers random mode CLI flags, env precedence, and parity with the web builder. ### Observability & QA - Diagnostics badge polish, recent/favorite seeds panel, seed history API, and structured logging for random builds. @@ -55,6 +58,7 @@ - GitHub Actions now includes `httpx` in the default dependency install and executes pytest via `python -m` so FastAPI TestClient suites run without import errors. - Fast path validator treats empty synergy arrays as acceptable and only warns on missing or malformed data, reducing noise during automated catalog generation. - Tracked the tiny `csv_files/testdata` dataset in Git to guarantee fast determinism tests run against a consistent fixture set. +- File setup retains cards tagged with the Hero creature type while continuing to skip the non-Commander-legal Hero card type. ## Detailed changes ### Added @@ -92,6 +96,7 @@ - Cache bust hooks now clear filter/preview caches on catalog refresh or tagging completion; metrics expose `preview_last_bust_at` and warm cache stats. - Theme normalization standardizes terms (ETB → Enter the Battlefield, Pillow Fort → Pillowfort, etc.), with synergy output capped at five entries (curated > enforced > inferred ordering). - README, CHANGELOG, and governance docs updated to reflect new workflows, taxonomy snapshots, and telemetry controls. +- Headless runner random mode uses shared configuration resolution, emits summary/payload artifacts, and exposes CLI flags matching the web UI. - Theme catalog schema now allows optional `id` fields on entries so regenerated catalogs validate cleanly. ### Deprecated @@ -106,10 +111,12 @@ - Corrected commander eligibility rules to restrict non-creature legendary permanents and honor “can be your commander” text. - Refreshed `logs/perf/theme_preview_warm_baseline.json` to fix preview performance CI failures stemming from malformed baseline data. - Prevented the headless runner from looping on bracket selection when owned card files exist by scripting prompt responses and exposing `HEADLESS_USE_OWNED_ONLY` / `HEADLESS_OWNED_SELECTION` overrides. +- Fixed file setup filtering so Hero creature type cards remain available even though the Hero card type stays disallowed for Commander. ## Upgrade notes - Enable multi-theme random builds via existing Random Mode flags; strict matching persists automatically across UI, API, permalink, and export contexts. - Opt into telemetry by setting `RANDOM_TELEMETRY=1`; reroll throttle defaults are active but can be tuned through environment overrides. +- Run headless random builds with `HEADLESS_RANDOM_MODE=1` or the new CLI `--random-*` flags to mirror Surprise Me behavior, optionally persisting outputs via `--random-output-json`. - Refresh performance baselines with `code/scripts/check_random_theme_perf.py --update-baseline` when catalog changes materially affect timings. ## Testing diff --git a/code/file_setup/setup_constants.py b/code/file_setup/setup_constants.py index 5226b9d..ccd6b4d 100644 --- a/code/file_setup/setup_constants.py +++ b/code/file_setup/setup_constants.py @@ -33,7 +33,15 @@ BANNED_CARDS: List[str] = [ 'Trade Secrets', 'Upheaval', "Yawgmoth's Bargain", # Problematic / culturally sensitive or banned in other formats 'Invoke Prejudice', 'Cleanse', 'Stone-Throwing Devils', 'Pradesh Gypsies', - 'Jihad', 'Imprison', 'Crusade' + 'Jihad', 'Imprison', 'Crusade', + # Cards of the Hero type (non creature) + "The Protector", "The Hunter", "The Savant", "The Explorer", + "The Philosopher", "The Harvester", "The Tyrant", "The Vanquisher", + "The Avenger", "The Slayer", "The Warmonger", "The Destined", + "The Warrior", "The General", "The Provider", "The Champion", + # Hero Equipment + "Spear of the General", "Lash of the Tyrant", "Bow of the Hunter", + "Cloak of the Philosopher", "Axe of the Warmonger" ] # Constants for setup and CSV processing @@ -60,7 +68,6 @@ CARD_TYPES_TO_EXCLUDE: List[str] = [ 'Phenomenon', 'Stickers', 'Attraction', - 'Hero', 'Contraption' ] diff --git a/code/headless_runner.py b/code/headless_runner.py index 530369b..9bc282e 100644 --- a/code/headless_runner.py +++ b/code/headless_runner.py @@ -3,7 +3,8 @@ from __future__ import annotations import argparse import json import os -from typing import Any, Dict, List, Optional +from dataclasses import asdict, dataclass, field +from typing import Any, Dict, List, Optional, Tuple from deck_builder.builder import DeckBuilder from deck_builder import builder_constants as bc @@ -65,6 +66,25 @@ def _headless_list_owned_files() -> List[str]: return [] return sorted(entries) + +@dataclass +class RandomRunConfig: + """Runtime options for the headless random build flow.""" + + legacy_theme: Optional[str] = None + primary_theme: Optional[str] = None + secondary_theme: Optional[str] = None + tertiary_theme: Optional[str] = None + auto_fill_missing: bool = False + auto_fill_secondary: Optional[bool] = None + auto_fill_tertiary: Optional[bool] = None + strict_theme_match: bool = False + attempts: int = 5 + timeout_ms: int = 5000 + seed: Optional[int | str] = None + constraints: Dict[str, Any] = field(default_factory=dict) + output_json: Optional[str] = None + def run( command_name: str = "", add_creatures: bool = True, @@ -446,6 +466,574 @@ def _load_json_config(path: Optional[str]) -> Dict[str, Any]: raise +def _load_constraints_spec(spec: Any) -> Dict[str, Any]: + """Load random constraints from a dict, JSON string, or file path.""" + + if not spec: + return {} + if isinstance(spec, dict): + return dict(spec) + + try: + text = str(spec).strip() + except Exception: + return {} + + if not text: + return {} + + # Treat existing file paths as JSON documents + if os.path.isfile(text): + try: + with open(text, "r", encoding="utf-8") as fh: + loaded = json.load(fh) + if isinstance(loaded, dict): + return loaded + except Exception as exc: + print(f"Warning: failed to load constraints from '{text}': {exc}") + return {} + + # Fallback: parse inline JSON + try: + parsed = json.loads(text) + if isinstance(parsed, dict): + return parsed + except Exception as exc: + print(f"Warning: failed to parse inline constraints '{text}': {exc}") + return {} + + +def _try_convert_seed(value: Any) -> Optional[int | str]: + if value is None: + return None + if isinstance(value, int): + return value + try: + text = str(value).strip() + except Exception: + return None + if not text: + return None + try: + return int(text) + except ValueError: + return text + + +def _resolve_pathish_target(target: str, seed: Any) -> str: + """Return a concrete file path for an output target, creating directories as needed.""" + + if not target: + raise ValueError("Empty output path provided") + + normalized = target.strip() + if not normalized: + raise ValueError("Blank output path provided") + + looks_dir = normalized.endswith(("/", "\\")) + if os.path.isdir(normalized) or looks_dir: + base_dir = normalized.rstrip("/\\") or "." + os.makedirs(base_dir, exist_ok=True) + seed_suffix = str(seed) if seed is not None else "latest" + filename = f"random_build_{seed_suffix}.json" + return os.path.join(base_dir, filename) + + base_dir = os.path.dirname(normalized) + if base_dir: + os.makedirs(base_dir, exist_ok=True) + return normalized + + +def _resolve_random_bool( + cli_value: Optional[bool], + env_name: str, + random_section: Dict[str, Any], + json_key: str, + default: Optional[bool], +) -> Optional[bool]: + if cli_value is not None: + return bool(cli_value) + env_val = os.getenv(env_name) + result = _parse_bool(env_val) if env_val is not None else None + if result is not None: + return result + if json_key in random_section: + result = _parse_bool(random_section.get(json_key)) + if result is not None: + return result + return default + + +def _resolve_random_str( + cli_value: Optional[str], + env_name: str, + random_section: Dict[str, Any], + json_key: str, + default: Optional[str] = None, +) -> Optional[str]: + candidates: Tuple[Any, ...] = ( + cli_value, + os.getenv(env_name), + random_section.get(json_key), + default, + ) + for candidate in candidates: + if candidate is None: + continue + try: + text = str(candidate).strip() + except Exception: + continue + if text: + return text + return None + + +def _resolve_random_int( + cli_value: Optional[int], + env_name: str, + random_section: Dict[str, Any], + json_key: str, + default: int, +) -> int: + if cli_value is not None: + try: + return int(cli_value) + except Exception: + pass + + env_val = os.getenv(env_name) + if env_val is not None and str(env_val).strip() != "": + try: + return int(float(str(env_val).strip())) + except Exception: + pass + + if json_key in random_section: + value = random_section.get(json_key) + try: + if isinstance(value, str): + value = value.strip() + if value: + return int(float(value)) + elif value is not None: + return int(value) + except Exception: + pass + return default + + +def _resolve_random_seed(cli_value: Optional[str], random_section: Dict[str, Any]) -> Optional[int | str]: + seed = _try_convert_seed(cli_value) + if seed is not None: + return seed + seed = _try_convert_seed(os.getenv("RANDOM_SEED")) + if seed is not None: + return seed + return _try_convert_seed(random_section.get("seed")) + + +def _extract_random_section(json_cfg: Dict[str, Any]) -> Dict[str, Any]: + section = json_cfg.get("random") + if isinstance(section, dict): + return dict(section) + alt = json_cfg.get("random_config") + if isinstance(alt, dict): + return dict(alt) + return {} + + +def _should_run_random_mode(args: argparse.Namespace, json_cfg: Dict[str, Any], random_section: Dict[str, Any]) -> bool: + if getattr(args, "random_mode", False): + return True + if _parse_bool(os.getenv("HEADLESS_RANDOM_MODE")): + return True + if (os.getenv("DECK_MODE") or "").strip().lower() == "random": + return True + if _parse_bool(json_cfg.get("random_mode")): + return True + if _parse_bool(random_section.get("enabled")): + return True + + # Detect CLI or env hints that imply random mode even without explicit flag + cli_indicators = ( + getattr(args, "random_theme", None), + getattr(args, "random_primary_theme", None), + getattr(args, "random_secondary_theme", None), + getattr(args, "random_tertiary_theme", None), + getattr(args, "random_seed", None), + getattr(args, "random_auto_fill", None), + getattr(args, "random_auto_fill_secondary", None), + getattr(args, "random_auto_fill_tertiary", None), + getattr(args, "random_strict_theme_match", None), + getattr(args, "random_attempts", None), + getattr(args, "random_timeout_ms", None), + getattr(args, "random_constraints", None), + getattr(args, "random_output_json", None), + ) + if any(value is not None for value in cli_indicators): + return True + + for env_name in ( + "RANDOM_THEME", + "RANDOM_PRIMARY_THEME", + "RANDOM_SECONDARY_THEME", + "RANDOM_TERTIARY_THEME", + "RANDOM_CONSTRAINTS", + "RANDOM_CONSTRAINTS_PATH", + "RANDOM_OUTPUT_JSON", + ): + if os.getenv(env_name): + return True + + noteworthy_keys = ( + "theme", + "primary_theme", + "secondary_theme", + "tertiary_theme", + "seed", + "auto_fill", + "auto_fill_secondary", + "auto_fill_tertiary", + "strict_theme_match", + "attempts", + "timeout_ms", + "constraints", + "constraints_path", + "output_json", + ) + if any(random_section.get(key) for key in noteworthy_keys): + return True + + return False + + +def _resolve_random_config(args: argparse.Namespace, json_cfg: Dict[str, Any]) -> Tuple[RandomRunConfig, Dict[str, Any]]: + random_section = _extract_random_section(json_cfg) + cfg = RandomRunConfig() + + cfg.legacy_theme = _resolve_random_str( + getattr(args, "random_theme", None), + "RANDOM_THEME", + random_section, + "theme", + None, + ) + cfg.primary_theme = _resolve_random_str( + getattr(args, "random_primary_theme", None), + "RANDOM_PRIMARY_THEME", + random_section, + "primary_theme", + cfg.legacy_theme, + ) + cfg.secondary_theme = _resolve_random_str( + getattr(args, "random_secondary_theme", None), + "RANDOM_SECONDARY_THEME", + random_section, + "secondary_theme", + None, + ) + cfg.tertiary_theme = _resolve_random_str( + getattr(args, "random_tertiary_theme", None), + "RANDOM_TERTIARY_THEME", + random_section, + "tertiary_theme", + None, + ) + + auto_fill_flag = _resolve_random_bool( + getattr(args, "random_auto_fill", None), + "RANDOM_AUTO_FILL", + random_section, + "auto_fill", + False, + ) + cfg.auto_fill_missing = bool(auto_fill_flag) + cfg.auto_fill_secondary = _resolve_random_bool( + getattr(args, "random_auto_fill_secondary", None), + "RANDOM_AUTO_FILL_SECONDARY", + random_section, + "auto_fill_secondary", + None, + ) + cfg.auto_fill_tertiary = _resolve_random_bool( + getattr(args, "random_auto_fill_tertiary", None), + "RANDOM_AUTO_FILL_TERTIARY", + random_section, + "auto_fill_tertiary", + None, + ) + + cfg.strict_theme_match = bool( + _resolve_random_bool( + getattr(args, "random_strict_theme_match", None), + "RANDOM_STRICT_THEME_MATCH", + random_section, + "strict_theme_match", + False, + ) + ) + + cfg.attempts = max( + 1, + _resolve_random_int( + getattr(args, "random_attempts", None), + "RANDOM_MAX_ATTEMPTS", + random_section, + "attempts", + 5, + ), + ) + cfg.timeout_ms = max( + 100, + _resolve_random_int( + getattr(args, "random_timeout_ms", None), + "RANDOM_TIMEOUT_MS", + random_section, + "timeout_ms", + 5000, + ), + ) + + cfg.seed = _resolve_random_seed(getattr(args, "random_seed", None), random_section) + + # Resolve constraints in precedence order: CLI > env JSON > env path > config dict > config path + constraints_candidates: Tuple[Any, ...] = ( + getattr(args, "random_constraints", None), + os.getenv("RANDOM_CONSTRAINTS"), + os.getenv("RANDOM_CONSTRAINTS_PATH"), + random_section.get("constraints"), + random_section.get("constraints_path"), + ) + for candidate in constraints_candidates: + loaded = _load_constraints_spec(candidate) + if loaded: + cfg.constraints = loaded + break + + cfg.output_json = _resolve_random_str( + getattr(args, "random_output_json", None), + "RANDOM_OUTPUT_JSON", + random_section, + "output_json", + None, + ) + + if cfg.primary_theme is None: + cfg.primary_theme = cfg.legacy_theme + if cfg.primary_theme and not cfg.legacy_theme: + cfg.legacy_theme = cfg.primary_theme + + if cfg.auto_fill_missing: + if cfg.auto_fill_secondary is None: + cfg.auto_fill_secondary = True + if cfg.auto_fill_tertiary is None: + cfg.auto_fill_tertiary = True + + return cfg, random_section + + +def _print_random_summary(result: Any, config: RandomRunConfig) -> None: + print("\n" + "=" * 60) + print("RANDOM MODE BUILD") + print("=" * 60) + commander = getattr(result, "commander", None) or "(unknown)" + print(f"Commander : {commander}") + seed_value = getattr(result, "seed", config.seed) + print(f"Seed : {seed_value}") + + display_themes = list(getattr(result, "display_themes", []) or []) + if not display_themes: + primary = getattr(result, "primary_theme", config.primary_theme) + if primary: + display_themes.append(primary) + for extra in ( + getattr(result, "secondary_theme", config.secondary_theme), + getattr(result, "tertiary_theme", config.tertiary_theme), + ): + if extra: + display_themes.append(extra) + if display_themes: + print(f"Themes : {', '.join(display_themes)}") + else: + print("Themes : (none)") + + fallback_kinds: List[str] = [] + if getattr(result, "combo_fallback", False): + fallback_kinds.append("combo") + if getattr(result, "synergy_fallback", False): + fallback_kinds.append("synergy") + fallback_reason = getattr(result, "fallback_reason", None) + print(f"Fallback : {('/'.join(fallback_kinds)) if fallback_kinds else 'none'}") + if fallback_reason: + print(f"Fallback reason : {fallback_reason}") + + auto_secondary = getattr(result, "auto_fill_secondary_enabled", config.auto_fill_secondary or False) + auto_tertiary = getattr(result, "auto_fill_tertiary_enabled", config.auto_fill_tertiary or False) + print( + "Auto-fill : secondary={} | tertiary={}".format( + "on" if auto_secondary else "off", + "on" if auto_tertiary else "off", + ) + ) + print(f"Strict match : {'on' if config.strict_theme_match else 'off'}") + + attempts_used = getattr(result, "attempts_tried", None) + if attempts_used is None: + attempts_used = config.attempts + print(f"Attempts used : {attempts_used} / {config.attempts}") + timeout_hit = getattr(result, "timeout_hit", False) + print(f"Timeout (ms) : {config.timeout_ms} (timeout_hit={timeout_hit})") + + if config.constraints: + try: + print("Constraints :") + print(json.dumps(config.constraints, indent=2)) + except Exception: + print(f"Constraints : {config.constraints}") + + csv_path = getattr(result, "csv_path", None) + if csv_path: + print(f"Deck CSV : {csv_path}") + txt_path = getattr(result, "txt_path", None) + if txt_path: + print(f"Deck TXT : {txt_path}") + compliance = getattr(result, "compliance", None) + if compliance: + if isinstance(compliance, dict) and compliance.get("path"): + print(f"Compliance JSON : {compliance['path']}") + else: + try: + print("Compliance data :") + print(json.dumps(compliance, indent=2)) + except Exception: + print(f"Compliance data : {compliance}") + + summary = getattr(result, "summary", None) + if summary: + try: + rendered = json.dumps(summary, indent=2) + except Exception: + rendered = str(summary) + preview = rendered[:1000] + print("Summary preview :") + print(preview + ("..." if len(rendered) > len(preview) else "")) + + decklist = getattr(result, "decklist", None) + if decklist: + try: + print(f"Decklist cards : {len(decklist)}") + except Exception: + pass + + print("=" * 60) + + +def _write_random_payload(config: RandomRunConfig, result: Any) -> None: + if not config.output_json: + return + try: + path = _resolve_pathish_target(config.output_json, getattr(result, "seed", config.seed)) + except Exception as exc: + print(f"Warning: unable to resolve random output path '{config.output_json}': {exc}") + return + + seed_value = getattr(result, "seed", config.seed) + try: + normalized_seed = int(seed_value) if seed_value is not None else None + except Exception: + normalized_seed = seed_value + + payload: Dict[str, Any] = { + "seed": normalized_seed, + "commander": getattr(result, "commander", None), + "themes": { + "primary": getattr(result, "primary_theme", config.primary_theme), + "secondary": getattr(result, "secondary_theme", config.secondary_theme), + "tertiary": getattr(result, "tertiary_theme", config.tertiary_theme), + "resolved": list(getattr(result, "resolved_themes", []) or []), + "display": list(getattr(result, "display_themes", []) or []), + "auto_filled": list(getattr(result, "auto_filled_themes", []) or []), + }, + "strict_theme_match": bool(config.strict_theme_match), + "auto_fill": { + "missing": bool(config.auto_fill_missing), + "secondary": bool(getattr(result, "auto_fill_secondary_enabled", config.auto_fill_secondary or False)), + "tertiary": bool(getattr(result, "auto_fill_tertiary_enabled", config.auto_fill_tertiary or False)), + "applied": bool(getattr(result, "auto_fill_applied", False)), + }, + "attempts": { + "configured": config.attempts, + "used": int(getattr(result, "attempts_tried", config.attempts) or config.attempts), + "timeout_ms": config.timeout_ms, + "timeout_hit": bool(getattr(result, "timeout_hit", False)), + "retries_exhausted": bool(getattr(result, "retries_exhausted", False)), + }, + "fallback": { + "combo": bool(getattr(result, "combo_fallback", False)), + "synergy": bool(getattr(result, "synergy_fallback", False)), + "reason": getattr(result, "fallback_reason", None), + }, + "constraints": config.constraints, + "csv_path": getattr(result, "csv_path", None), + "txt_path": getattr(result, "txt_path", None), + "compliance": getattr(result, "compliance", None), + "summary": getattr(result, "summary", None), + "decklist": getattr(result, "decklist", None), + } + + try: + with open(path, "w", encoding="utf-8") as fh: + json.dump(payload, fh, indent=2) + print(f"Random build payload written to {path}") + except Exception as exc: + print(f"Warning: failed to write random payload '{path}': {exc}") + + +def _run_random_mode(config: RandomRunConfig) -> int: + try: + from deck_builder.random_entrypoint import ( + RandomConstraintsImpossibleError, + RandomThemeNoMatchError, + build_random_full_deck, + ) # type: ignore + except Exception as exc: + print(f"Random mode unavailable: {exc}") + return 1 + + timeout_ms = max(100, int(config.timeout_ms)) + attempts = max(1, int(config.attempts)) + + try: + result = build_random_full_deck( + theme=config.legacy_theme, + constraints=config.constraints or None, + seed=config.seed, + attempts=attempts, + timeout_s=float(timeout_ms) / 1000.0, + primary_theme=config.primary_theme, + secondary_theme=config.secondary_theme, + tertiary_theme=config.tertiary_theme, + auto_fill_missing=config.auto_fill_missing, + auto_fill_secondary=config.auto_fill_secondary, + auto_fill_tertiary=config.auto_fill_tertiary, + strict_theme_match=config.strict_theme_match, + ) + except RandomThemeNoMatchError as exc: + print(f"Random mode failed: strict theme match produced no results ({exc})") + return 3 + except RandomConstraintsImpossibleError as exc: + print(f"Random mode constraints impossible: {exc}") + return 4 + except Exception as exc: + print(f"Random mode encountered an unexpected error: {exc}") + return 1 + + _print_random_summary(result, config) + _write_random_payload(config, result) + return 0 + + def _build_arg_parser() -> argparse.ArgumentParser: p = argparse.ArgumentParser(description="Headless deck builder runner") p.add_argument("--config", metavar="PATH", default=os.getenv("DECK_CONFIG"), @@ -533,6 +1121,101 @@ def _build_arg_parser() -> argparse.ArgumentParser: include_group.add_argument("--fuzzy-matching", metavar="BOOL", type=_parse_bool, default=None, help="Enable fuzzy card name matching (bool: true/false/1/0)") + # Random mode configuration (parity with web random builder) + random_group = p.add_argument_group( + "Random Mode", + "Generate decks using the random web builder flow", + ) + random_group.add_argument( + "--random-mode", + action="store_true", + help="Force random-mode build even if other inputs are provided", + ) + random_group.add_argument( + "--random-theme", + metavar="THEME", + default=None, + help="Legacy random theme (maps to primary theme if unspecified)", + ) + random_group.add_argument( + "--random-primary-theme", + metavar="THEME", + default=None, + help="Primary theme slug for random mode", + ) + random_group.add_argument( + "--random-secondary-theme", + metavar="THEME", + default=None, + help="Secondary theme slug for random mode", + ) + random_group.add_argument( + "--random-tertiary-theme", + metavar="THEME", + default=None, + help="Tertiary theme slug for random mode", + ) + random_group.add_argument( + "--random-auto-fill", + metavar="BOOL", + type=_parse_bool, + default=None, + help="Enable auto-fill assistance for missing theme slots", + ) + random_group.add_argument( + "--random-auto-fill-secondary", + metavar="BOOL", + type=_parse_bool, + default=None, + help="Enable auto-fill specifically for secondary theme", + ) + random_group.add_argument( + "--random-auto-fill-tertiary", + metavar="BOOL", + type=_parse_bool, + default=None, + help="Enable auto-fill specifically for tertiary theme", + ) + random_group.add_argument( + "--random-strict-theme-match", + metavar="BOOL", + type=_parse_bool, + default=None, + help="Require strict theme matches when selecting commanders", + ) + random_group.add_argument( + "--random-attempts", + metavar="INT", + type=int, + default=None, + help="Maximum attempts before giving up (default 5)", + ) + random_group.add_argument( + "--random-timeout-ms", + metavar="INT", + type=int, + default=None, + help="Timeout in milliseconds for theme search (default 5000)", + ) + random_group.add_argument( + "--random-seed", + metavar="SEED", + default=None, + help="Seed value for deterministic random builds", + ) + random_group.add_argument( + "--random-constraints", + metavar="JSON_OR_PATH", + default=None, + help="Random constraints as JSON or a path to a JSON file", + ) + random_group.add_argument( + "--random-output-json", + metavar="PATH", + default=None, + help="Write random build payload JSON to PATH (directory or file)", + ) + # Utility p.add_argument("--dry-run", action="store_true", help="Print resolved configuration and exit without building") @@ -584,6 +1267,13 @@ def _main() -> int: os.environ["DECK_CONFIG"] = chosen break + random_config, random_section = _resolve_random_config(args, json_cfg) + if _should_run_random_mode(args, json_cfg, random_section): + if args.dry_run: + print(json.dumps({"random_mode": True, "config": asdict(random_config)}, indent=2)) + return 0 + return _run_random_mode(random_config) + # Defaults mirror run() signature defaults = dict( command_name="", diff --git a/config/themes/description_fallback_history.jsonl b/config/themes/description_fallback_history.jsonl index 66d6a40..a4516b5 100644 --- a/config/themes/description_fallback_history.jsonl +++ b/config/themes/description_fallback_history.jsonl @@ -16,3 +16,7 @@ {"timestamp": "2025-09-19T10:00:35", "total_themes": 733, "generic_total": 278, "generic_with_synergies": 260, "generic_plain": 18, "generic_pct": 37.93, "top_generic_by_frequency": [{"theme": "Little Fellas", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 7147, "description": "Builds around Little Fellas leveraging synergies with Banding and Licid Kindred."}, {"theme": "Combat Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 6391, "description": "Builds around Combat Matters leveraging synergies with Aggro and Voltron."}, {"theme": "Interaction", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 4160, "description": "Builds around Interaction leveraging synergies with Removal and Combat Tricks."}, {"theme": "Toughness Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3511, "description": "Builds around Toughness Matters leveraging synergies with Defender and Egg Kindred."}, {"theme": "Leave the Battlefield", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3113, "description": "Builds around Leave the Battlefield leveraging synergies with Blink and Enter the Battlefield."}, {"theme": "Enter the Battlefield", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3109, "description": "Builds around Enter the Battlefield leveraging synergies with Blink and Reanimate."}, {"theme": "Card Draw", "popularity_bucket": "Very Common", "synergy_count": 17, "total_frequency": 2708, "description": "Builds around Card Draw leveraging synergies with Loot and Wheels."}, {"theme": "Life Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 2423, "description": "Builds around Life Matters leveraging synergies with Lifegain and Lifedrain."}, {"theme": "Flying", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 2232, "description": "Builds around Flying leveraging synergies with Phoenix Kindred and Archon Kindred."}, {"theme": "Removal", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1601, "description": "Builds around Removal leveraging synergies with Soulshift and Interaction."}, {"theme": "Legends Matter", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1563, "description": "Builds around Legends Matter leveraging synergies with Historics Matter and Superfriends."}, {"theme": "Topdeck", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1112, "description": "Builds around Topdeck leveraging synergies with Scry and Surveil."}, {"theme": "Discard Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1055, "description": "Builds around Discard Matters leveraging synergies with Loot and Wheels."}, {"theme": "Unconditional Draw", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1050, "description": "Builds around Unconditional Draw leveraging synergies with Dredge and Learn."}, {"theme": "Combat Tricks", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 858, "description": "Builds around Combat Tricks leveraging synergies with Flash and Strive."}, {"theme": "Protection", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 810, "description": "Builds around Protection leveraging synergies with Ward and Hexproof."}, {"theme": "Exile Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 718, "description": "Builds around Exile Matters leveraging synergies with Impulse and Suspend."}, {"theme": "Board Wipes", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 649, "description": "Builds around Board Wipes leveraging synergies with Bracket:MassLandDenial and Pingers."}, {"theme": "Pingers", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 643, "description": "Builds around Pingers leveraging synergies with Extort and Devil Kindred."}, {"theme": "Loot", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 526, "description": "Builds around Loot leveraging synergies with Card Draw and Discard Matters."}, {"theme": "Cantrips", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 515, "description": "Builds around Cantrips leveraging synergies with Clue Token and Investigate."}, {"theme": "X Spells", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 506, "description": "Builds around X Spells leveraging synergies with Ravenous and Firebending."}, {"theme": "Conditional Draw", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 458, "description": "Builds around Conditional Draw leveraging synergies with Max speed and Start your engines!."}, {"theme": "Cost Reduction", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 433, "description": "Builds around Cost Reduction leveraging synergies with Affinity and Freerunning."}, {"theme": "Flash", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 427, "description": "Builds around Flash leveraging synergies with Evoke and Combat Tricks."}, {"theme": "Haste", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 402, "description": "Builds around Haste leveraging synergies with Hellion Kindred and Phoenix Kindred."}, {"theme": "Lifelink", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 401, "description": "Builds around Lifelink leveraging synergies with Lifegain Triggers and Lifegain."}, {"theme": "Vigilance", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 401, "description": "Builds around Vigilance leveraging synergies with Angel Kindred and Mount Kindred."}, {"theme": "Counterspells", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 397, "description": "Builds around Counterspells leveraging synergies with Control and Stax."}, {"theme": "Transform", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 366, "description": "Builds around Transform leveraging synergies with Incubator Token and Incubate."}, {"theme": "Super Friends", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 344, "description": "Builds around Super Friends leveraging synergies with Planeswalkers and Superfriends."}, {"theme": "Mana Dork", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 340, "description": "Builds around Mana Dork leveraging synergies with Firebending and Scion Kindred."}, {"theme": "Cycling", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 299, "description": "Builds around Cycling leveraging synergies with Landcycling and Basic landcycling."}, {"theme": "Bracket:TutorNonland", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 297, "description": "Builds around Bracket:TutorNonland leveraging synergies with Transmute and Bracket:GameChanger."}, {"theme": "Scry", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 284, "description": "Builds around Scry leveraging synergies with Topdeck and Role token."}, {"theme": "Clones", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 283, "description": "Builds around Clones leveraging synergies with Myriad and Populate."}, {"theme": "Reach", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 275, "description": "Builds around Reach leveraging synergies with Spider Kindred and Archer Kindred."}, {"theme": "First strike", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 252, "description": "Builds around First strike leveraging synergies with Banding and Kithkin Kindred."}, {"theme": "Defender", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 230, "description": "Builds around Defender leveraging synergies with Wall Kindred and Egg Kindred."}, {"theme": "Menace", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 226, "description": "Builds around Menace leveraging synergies with Warlock Kindred and Blood Token."}, {"theme": "Deathtouch", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 192, "description": "Builds around Deathtouch leveraging synergies with Basilisk Kindred and Scorpion Kindred."}, {"theme": "Equip", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 187, "description": "Builds around Equip leveraging synergies with Job select and For Mirrodin!."}, {"theme": "Land Types Matter", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 185, "description": "Builds around Land Types Matter leveraging synergies with Plainscycling and Mountaincycling."}, {"theme": "Spell Copy", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 184, "description": "Builds around Spell Copy leveraging synergies with Storm and Replicate."}, {"theme": "Landwalk", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 170, "description": "Builds around Landwalk leveraging synergies with Swampwalk and Islandwalk."}, {"theme": "Impulse", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 163, "description": "Builds around Impulse leveraging synergies with Junk Tokens and Junk Token."}, {"theme": "Morph", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 140, "description": "Builds around Morph leveraging synergies with Beast Kindred and Illusion Kindred."}, {"theme": "Devoid", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 114, "description": "Builds around Devoid leveraging synergies with Ingest and Processor Kindred."}, {"theme": "Resource Engine", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 101, "description": "Builds around Resource Engine leveraging synergies with Energy and Energy Counters."}, {"theme": "Ward", "popularity_bucket": "Niche", "synergy_count": 5, "total_frequency": 97, "description": "Builds around Ward leveraging synergies with Turtle Kindred and Protection."}]} {"timestamp": "2025-09-27T14:56:48", "total_themes": 735, "generic_total": 279, "generic_with_synergies": 262, "generic_plain": 17, "generic_pct": 37.96, "top_generic_by_frequency": [{"theme": "Little Fellas", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 7147, "description": "Builds around Little Fellas leveraging synergies with Banding and Licid Kindred."}, {"theme": "Combat Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 6391, "description": "Builds around Combat Matters leveraging synergies with Aggro and Voltron."}, {"theme": "Interaction", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 4160, "description": "Builds around Interaction leveraging synergies with Removal and Combat Tricks."}, {"theme": "Toughness Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3511, "description": "Builds around Toughness Matters leveraging synergies with Defender and Egg Kindred."}, {"theme": "Leave the Battlefield", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3113, "description": "Builds around Leave the Battlefield leveraging synergies with Blink and Enter the Battlefield."}, {"theme": "Enter the Battlefield", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3109, "description": "Builds around Enter the Battlefield leveraging synergies with Blink and Reanimate."}, {"theme": "Card Draw", "popularity_bucket": "Very Common", "synergy_count": 17, "total_frequency": 2708, "description": "Builds around Card Draw leveraging synergies with Loot and Wheels."}, {"theme": "Life Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 2423, "description": "Builds around Life Matters leveraging synergies with Lifegain and Lifedrain."}, {"theme": "Flying", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 2232, "description": "Builds around Flying leveraging synergies with Phoenix Kindred and Archon Kindred."}, {"theme": "Removal", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1601, "description": "Builds around Removal leveraging synergies with Soulshift and Interaction."}, {"theme": "Legends Matter", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1563, "description": "Builds around Legends Matter leveraging synergies with Historics Matter and Superfriends."}, {"theme": "Topdeck", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1112, "description": "Builds around Topdeck leveraging synergies with Scry and Surveil."}, {"theme": "Discard Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1055, "description": "Builds around Discard Matters leveraging synergies with Loot and Wheels."}, {"theme": "Unconditional Draw", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1050, "description": "Builds around Unconditional Draw leveraging synergies with Dredge and Learn."}, {"theme": "Combat Tricks", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 858, "description": "Builds around Combat Tricks leveraging synergies with Flash and Strive."}, {"theme": "Protection", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 810, "description": "Builds around Protection leveraging synergies with Ward and Hexproof."}, {"theme": "Exile Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 718, "description": "Builds around Exile Matters leveraging synergies with Impulse and Suspend."}, {"theme": "Board Wipes", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 649, "description": "Builds around Board Wipes leveraging synergies with Bracket:MassLandDenial and Pingers."}, {"theme": "Pingers", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 643, "description": "Builds around Pingers leveraging synergies with Extort and Devil Kindred."}, {"theme": "Loot", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 526, "description": "Builds around Loot leveraging synergies with Card Draw and Discard Matters."}, {"theme": "Cantrips", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 515, "description": "Builds around Cantrips leveraging synergies with Clue Token and Investigate."}, {"theme": "X Spells", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 506, "description": "Builds around X Spells leveraging synergies with Ravenous and Firebending."}, {"theme": "Conditional Draw", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 458, "description": "Builds around Conditional Draw leveraging synergies with Max speed and Start your engines!."}, {"theme": "Toolbox", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 453, "description": "Builds around Toolbox leveraging synergies with Entwine and Bracket:TutorNonland."}, {"theme": "Cost Reduction", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 433, "description": "Builds around Cost Reduction leveraging synergies with Affinity and Freerunning."}, {"theme": "Flash", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 427, "description": "Builds around Flash leveraging synergies with Evoke and Combat Tricks."}, {"theme": "Haste", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 402, "description": "Builds around Haste leveraging synergies with Hellion Kindred and Phoenix Kindred."}, {"theme": "Lifelink", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 401, "description": "Builds around Lifelink leveraging synergies with Lifegain Triggers and Lifegain."}, {"theme": "Vigilance", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 401, "description": "Builds around Vigilance leveraging synergies with Angel Kindred and Mount Kindred."}, {"theme": "Counterspells", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 397, "description": "Builds around Counterspells leveraging synergies with Control and Stax."}, {"theme": "Transform", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 366, "description": "Builds around Transform leveraging synergies with Incubator Token and Incubate."}, {"theme": "Super Friends", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 344, "description": "Builds around Super Friends leveraging synergies with Planeswalkers and Superfriends."}, {"theme": "Mana Dork", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 340, "description": "Builds around Mana Dork leveraging synergies with Firebending and Scion Kindred."}, {"theme": "Cycling", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 299, "description": "Builds around Cycling leveraging synergies with Landcycling and Basic landcycling."}, {"theme": "Bracket:TutorNonland", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 297, "description": "Builds around Bracket:TutorNonland leveraging synergies with Transmute and Bracket:GameChanger."}, {"theme": "Scry", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 284, "description": "Builds around Scry leveraging synergies with Topdeck and Role token."}, {"theme": "Clones", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 283, "description": "Builds around Clones leveraging synergies with Myriad and Populate."}, {"theme": "Reach", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 275, "description": "Builds around Reach leveraging synergies with Spider Kindred and Archer Kindred."}, {"theme": "First strike", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 252, "description": "Builds around First strike leveraging synergies with Banding and Kithkin Kindred."}, {"theme": "Politics", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 246, "description": "Builds around Politics leveraging synergies with Encore and Melee."}, {"theme": "Defender", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 230, "description": "Builds around Defender leveraging synergies with Wall Kindred and Egg Kindred."}, {"theme": "Menace", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 226, "description": "Builds around Menace leveraging synergies with Warlock Kindred and Blood Token."}, {"theme": "Deathtouch", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 192, "description": "Builds around Deathtouch leveraging synergies with Basilisk Kindred and Scorpion Kindred."}, {"theme": "Equip", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 187, "description": "Builds around Equip leveraging synergies with Job select and For Mirrodin!."}, {"theme": "Land Types Matter", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 185, "description": "Builds around Land Types Matter leveraging synergies with Plainscycling and Mountaincycling."}, {"theme": "Spell Copy", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 184, "description": "Builds around Spell Copy leveraging synergies with Storm and Replicate."}, {"theme": "Landwalk", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 170, "description": "Builds around Landwalk leveraging synergies with Swampwalk and Islandwalk."}, {"theme": "Impulse", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 163, "description": "Builds around Impulse leveraging synergies with Junk Tokens and Junk Token."}, {"theme": "Morph", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 140, "description": "Builds around Morph leveraging synergies with Beast Kindred and Illusion Kindred."}, {"theme": "Devoid", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 114, "description": "Builds around Devoid leveraging synergies with Ingest and Processor Kindred."}]} {"timestamp": "2025-09-27T15:09:05", "total_themes": 735, "generic_total": 279, "generic_with_synergies": 262, "generic_plain": 17, "generic_pct": 37.96, "top_generic_by_frequency": [{"theme": "Little Fellas", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 7147, "description": "Builds around Little Fellas leveraging synergies with Banding and Licid Kindred."}, {"theme": "Combat Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 6391, "description": "Builds around Combat Matters leveraging synergies with Aggro and Voltron."}, {"theme": "Interaction", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 4160, "description": "Builds around Interaction leveraging synergies with Removal and Combat Tricks."}, {"theme": "Toughness Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3511, "description": "Builds around Toughness Matters leveraging synergies with Defender and Egg Kindred."}, {"theme": "Leave the Battlefield", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3113, "description": "Builds around Leave the Battlefield leveraging synergies with Blink and Enter the Battlefield."}, {"theme": "Enter the Battlefield", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3109, "description": "Builds around Enter the Battlefield leveraging synergies with Blink and Reanimate."}, {"theme": "Card Draw", "popularity_bucket": "Very Common", "synergy_count": 17, "total_frequency": 2708, "description": "Builds around Card Draw leveraging synergies with Loot and Wheels."}, {"theme": "Life Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 2423, "description": "Builds around Life Matters leveraging synergies with Lifegain and Lifedrain."}, {"theme": "Flying", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 2232, "description": "Builds around Flying leveraging synergies with Phoenix Kindred and Archon Kindred."}, {"theme": "Removal", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1601, "description": "Builds around Removal leveraging synergies with Soulshift and Interaction."}, {"theme": "Legends Matter", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1563, "description": "Builds around Legends Matter leveraging synergies with Historics Matter and Superfriends."}, {"theme": "Topdeck", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1112, "description": "Builds around Topdeck leveraging synergies with Scry and Surveil."}, {"theme": "Discard Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1055, "description": "Builds around Discard Matters leveraging synergies with Loot and Wheels."}, {"theme": "Unconditional Draw", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1050, "description": "Builds around Unconditional Draw leveraging synergies with Dredge and Learn."}, {"theme": "Combat Tricks", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 858, "description": "Builds around Combat Tricks leveraging synergies with Flash and Strive."}, {"theme": "Protection", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 810, "description": "Builds around Protection leveraging synergies with Ward and Hexproof."}, {"theme": "Exile Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 718, "description": "Builds around Exile Matters leveraging synergies with Impulse and Suspend."}, {"theme": "Board Wipes", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 649, "description": "Builds around Board Wipes leveraging synergies with Bracket:MassLandDenial and Pingers."}, {"theme": "Pingers", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 643, "description": "Builds around Pingers leveraging synergies with Extort and Devil Kindred."}, {"theme": "Loot", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 526, "description": "Builds around Loot leveraging synergies with Card Draw and Discard Matters."}, {"theme": "Cantrips", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 515, "description": "Builds around Cantrips leveraging synergies with Clue Token and Investigate."}, {"theme": "X Spells", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 506, "description": "Builds around X Spells leveraging synergies with Ravenous and Firebending."}, {"theme": "Conditional Draw", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 458, "description": "Builds around Conditional Draw leveraging synergies with Max speed and Start your engines!."}, {"theme": "Toolbox", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 453, "description": "Builds around Toolbox leveraging synergies with Entwine and Bracket:TutorNonland."}, {"theme": "Cost Reduction", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 433, "description": "Builds around Cost Reduction leveraging synergies with Affinity and Freerunning."}, {"theme": "Flash", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 427, "description": "Builds around Flash leveraging synergies with Evoke and Combat Tricks."}, {"theme": "Haste", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 402, "description": "Builds around Haste leveraging synergies with Hellion Kindred and Phoenix Kindred."}, {"theme": "Lifelink", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 401, "description": "Builds around Lifelink leveraging synergies with Lifegain Triggers and Lifegain."}, {"theme": "Vigilance", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 401, "description": "Builds around Vigilance leveraging synergies with Angel Kindred and Mount Kindred."}, {"theme": "Counterspells", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 397, "description": "Builds around Counterspells leveraging synergies with Control and Stax."}, {"theme": "Transform", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 366, "description": "Builds around Transform leveraging synergies with Incubator Token and Incubate."}, {"theme": "Super Friends", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 344, "description": "Builds around Super Friends leveraging synergies with Planeswalkers and Superfriends."}, {"theme": "Mana Dork", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 340, "description": "Builds around Mana Dork leveraging synergies with Firebending and Scion Kindred."}, {"theme": "Cycling", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 299, "description": "Builds around Cycling leveraging synergies with Landcycling and Basic landcycling."}, {"theme": "Bracket:TutorNonland", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 297, "description": "Builds around Bracket:TutorNonland leveraging synergies with Transmute and Bracket:GameChanger."}, {"theme": "Scry", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 284, "description": "Builds around Scry leveraging synergies with Topdeck and Role token."}, {"theme": "Clones", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 283, "description": "Builds around Clones leveraging synergies with Myriad and Populate."}, {"theme": "Reach", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 275, "description": "Builds around Reach leveraging synergies with Spider Kindred and Archer Kindred."}, {"theme": "First strike", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 252, "description": "Builds around First strike leveraging synergies with Banding and Kithkin Kindred."}, {"theme": "Politics", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 246, "description": "Builds around Politics leveraging synergies with Encore and Melee."}, {"theme": "Defender", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 230, "description": "Builds around Defender leveraging synergies with Wall Kindred and Egg Kindred."}, {"theme": "Menace", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 226, "description": "Builds around Menace leveraging synergies with Warlock Kindred and Blood Token."}, {"theme": "Deathtouch", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 192, "description": "Builds around Deathtouch leveraging synergies with Basilisk Kindred and Scorpion Kindred."}, {"theme": "Equip", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 187, "description": "Builds around Equip leveraging synergies with Job select and For Mirrodin!."}, {"theme": "Land Types Matter", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 185, "description": "Builds around Land Types Matter leveraging synergies with Plainscycling and Mountaincycling."}, {"theme": "Spell Copy", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 184, "description": "Builds around Spell Copy leveraging synergies with Storm and Replicate."}, {"theme": "Landwalk", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 170, "description": "Builds around Landwalk leveraging synergies with Swampwalk and Islandwalk."}, {"theme": "Impulse", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 163, "description": "Builds around Impulse leveraging synergies with Junk Tokens and Junk Token."}, {"theme": "Morph", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 140, "description": "Builds around Morph leveraging synergies with Beast Kindred and Illusion Kindred."}, {"theme": "Devoid", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 114, "description": "Builds around Devoid leveraging synergies with Ingest and Processor Kindred."}]} +{"timestamp": "2025-09-29T21:52:29", "total_themes": 735, "generic_total": 279, "generic_with_synergies": 262, "generic_plain": 17, "generic_pct": 37.96, "top_generic_by_frequency": [{"theme": "Little Fellas", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 7147, "description": "Builds around Little Fellas leveraging synergies with Banding and Licid Kindred."}, {"theme": "Combat Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 6391, "description": "Builds around Combat Matters leveraging synergies with Aggro and Voltron."}, {"theme": "Interaction", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 4160, "description": "Builds around Interaction leveraging synergies with Removal and Combat Tricks."}, {"theme": "Toughness Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3511, "description": "Builds around Toughness Matters leveraging synergies with Defender and Egg Kindred."}, {"theme": "Leave the Battlefield", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3113, "description": "Builds around Leave the Battlefield leveraging synergies with Blink and Enter the Battlefield."}, {"theme": "Enter the Battlefield", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3109, "description": "Builds around Enter the Battlefield leveraging synergies with Blink and Reanimate."}, {"theme": "Card Draw", "popularity_bucket": "Very Common", "synergy_count": 17, "total_frequency": 2708, "description": "Builds around Card Draw leveraging synergies with Loot and Wheels."}, {"theme": "Life Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 2423, "description": "Builds around Life Matters leveraging synergies with Lifegain and Lifedrain."}, {"theme": "Flying", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 2232, "description": "Builds around Flying leveraging synergies with Phoenix Kindred and Archon Kindred."}, {"theme": "Removal", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1601, "description": "Builds around Removal leveraging synergies with Soulshift and Interaction."}, {"theme": "Legends Matter", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1563, "description": "Builds around Legends Matter leveraging synergies with Historics Matter and Superfriends."}, {"theme": "Topdeck", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1112, "description": "Builds around Topdeck leveraging synergies with Scry and Surveil."}, {"theme": "Discard Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1055, "description": "Builds around Discard Matters leveraging synergies with Loot and Wheels."}, {"theme": "Unconditional Draw", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1050, "description": "Builds around Unconditional Draw leveraging synergies with Dredge and Learn."}, {"theme": "Combat Tricks", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 858, "description": "Builds around Combat Tricks leveraging synergies with Flash and Strive."}, {"theme": "Protection", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 810, "description": "Builds around Protection leveraging synergies with Ward and Hexproof."}, {"theme": "Exile Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 718, "description": "Builds around Exile Matters leveraging synergies with Impulse and Suspend."}, {"theme": "Board Wipes", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 649, "description": "Builds around Board Wipes leveraging synergies with Bracket:MassLandDenial and Pingers."}, {"theme": "Pingers", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 643, "description": "Builds around Pingers leveraging synergies with Extort and Devil Kindred."}, {"theme": "Loot", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 526, "description": "Builds around Loot leveraging synergies with Card Draw and Discard Matters."}, {"theme": "Cantrips", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 515, "description": "Builds around Cantrips leveraging synergies with Clue Token and Investigate."}, {"theme": "X Spells", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 506, "description": "Builds around X Spells leveraging synergies with Ravenous and Firebending."}, {"theme": "Conditional Draw", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 458, "description": "Builds around Conditional Draw leveraging synergies with Max speed and Start your engines!."}, {"theme": "Toolbox", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 453, "description": "Builds around Toolbox leveraging synergies with Entwine and Bracket:TutorNonland."}, {"theme": "Cost Reduction", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 433, "description": "Builds around Cost Reduction leveraging synergies with Affinity and Freerunning."}, {"theme": "Flash", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 427, "description": "Builds around Flash leveraging synergies with Evoke and Combat Tricks."}, {"theme": "Haste", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 402, "description": "Builds around Haste leveraging synergies with Hellion Kindred and Phoenix Kindred."}, {"theme": "Lifelink", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 401, "description": "Builds around Lifelink leveraging synergies with Lifegain Triggers and Lifegain."}, {"theme": "Vigilance", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 401, "description": "Builds around Vigilance leveraging synergies with Angel Kindred and Mount Kindred."}, {"theme": "Counterspells", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 397, "description": "Builds around Counterspells leveraging synergies with Control and Stax."}, {"theme": "Transform", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 366, "description": "Builds around Transform leveraging synergies with Incubator Token and Incubate."}, {"theme": "Super Friends", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 344, "description": "Builds around Super Friends leveraging synergies with Planeswalkers and Superfriends."}, {"theme": "Mana Dork", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 340, "description": "Builds around Mana Dork leveraging synergies with Firebending and Scion Kindred."}, {"theme": "Cycling", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 299, "description": "Builds around Cycling leveraging synergies with Landcycling and Basic landcycling."}, {"theme": "Bracket:TutorNonland", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 297, "description": "Builds around Bracket:TutorNonland leveraging synergies with Transmute and Bracket:GameChanger."}, {"theme": "Scry", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 284, "description": "Builds around Scry leveraging synergies with Topdeck and Role token."}, {"theme": "Clones", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 283, "description": "Builds around Clones leveraging synergies with Myriad and Populate."}, {"theme": "Reach", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 275, "description": "Builds around Reach leveraging synergies with Spider Kindred and Archer Kindred."}, {"theme": "First strike", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 252, "description": "Builds around First strike leveraging synergies with Banding and Kithkin Kindred."}, {"theme": "Politics", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 246, "description": "Builds around Politics leveraging synergies with Encore and Melee."}, {"theme": "Defender", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 230, "description": "Builds around Defender leveraging synergies with Wall Kindred and Egg Kindred."}, {"theme": "Menace", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 226, "description": "Builds around Menace leveraging synergies with Warlock Kindred and Blood Token."}, {"theme": "Deathtouch", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 192, "description": "Builds around Deathtouch leveraging synergies with Basilisk Kindred and Scorpion Kindred."}, {"theme": "Equip", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 187, "description": "Builds around Equip leveraging synergies with Job select and For Mirrodin!."}, {"theme": "Land Types Matter", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 185, "description": "Builds around Land Types Matter leveraging synergies with Plainscycling and Mountaincycling."}, {"theme": "Spell Copy", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 184, "description": "Builds around Spell Copy leveraging synergies with Storm and Replicate."}, {"theme": "Landwalk", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 170, "description": "Builds around Landwalk leveraging synergies with Swampwalk and Islandwalk."}, {"theme": "Impulse", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 163, "description": "Builds around Impulse leveraging synergies with Junk Tokens and Junk Token."}, {"theme": "Morph", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 140, "description": "Builds around Morph leveraging synergies with Beast Kindred and Illusion Kindred."}, {"theme": "Devoid", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 114, "description": "Builds around Devoid leveraging synergies with Ingest and Processor Kindred."}]} +{"timestamp": "2025-09-29T21:54:34", "total_themes": 735, "generic_total": 279, "generic_with_synergies": 262, "generic_plain": 17, "generic_pct": 37.96, "top_generic_by_frequency": [{"theme": "Little Fellas", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 7147, "description": "Builds around Little Fellas leveraging synergies with Banding and Licid Kindred."}, {"theme": "Combat Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 6391, "description": "Builds around Combat Matters leveraging synergies with Aggro and Voltron."}, {"theme": "Interaction", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 4160, "description": "Builds around Interaction leveraging synergies with Removal and Combat Tricks."}, {"theme": "Toughness Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3511, "description": "Builds around Toughness Matters leveraging synergies with Defender and Egg Kindred."}, {"theme": "Leave the Battlefield", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3113, "description": "Builds around Leave the Battlefield leveraging synergies with Blink and Enter the Battlefield."}, {"theme": "Enter the Battlefield", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3109, "description": "Builds around Enter the Battlefield leveraging synergies with Blink and Reanimate."}, {"theme": "Card Draw", "popularity_bucket": "Very Common", "synergy_count": 17, "total_frequency": 2708, "description": "Builds around Card Draw leveraging synergies with Loot and Wheels."}, {"theme": "Life Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 2423, "description": "Builds around Life Matters leveraging synergies with Lifegain and Lifedrain."}, {"theme": "Flying", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 2232, "description": "Builds around Flying leveraging synergies with Phoenix Kindred and Archon Kindred."}, {"theme": "Removal", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1601, "description": "Builds around Removal leveraging synergies with Soulshift and Interaction."}, {"theme": "Legends Matter", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1563, "description": "Builds around Legends Matter leveraging synergies with Historics Matter and Superfriends."}, {"theme": "Topdeck", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1112, "description": "Builds around Topdeck leveraging synergies with Scry and Surveil."}, {"theme": "Discard Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1055, "description": "Builds around Discard Matters leveraging synergies with Loot and Wheels."}, {"theme": "Unconditional Draw", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1050, "description": "Builds around Unconditional Draw leveraging synergies with Dredge and Learn."}, {"theme": "Combat Tricks", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 858, "description": "Builds around Combat Tricks leveraging synergies with Flash and Strive."}, {"theme": "Protection", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 810, "description": "Builds around Protection leveraging synergies with Ward and Hexproof."}, {"theme": "Exile Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 718, "description": "Builds around Exile Matters leveraging synergies with Impulse and Suspend."}, {"theme": "Board Wipes", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 649, "description": "Builds around Board Wipes leveraging synergies with Bracket:MassLandDenial and Pingers."}, {"theme": "Pingers", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 643, "description": "Builds around Pingers leveraging synergies with Extort and Devil Kindred."}, {"theme": "Loot", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 526, "description": "Builds around Loot leveraging synergies with Card Draw and Discard Matters."}, {"theme": "Cantrips", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 515, "description": "Builds around Cantrips leveraging synergies with Clue Token and Investigate."}, {"theme": "X Spells", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 506, "description": "Builds around X Spells leveraging synergies with Ravenous and Firebending."}, {"theme": "Conditional Draw", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 458, "description": "Builds around Conditional Draw leveraging synergies with Max speed and Start your engines!."}, {"theme": "Toolbox", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 453, "description": "Builds around Toolbox leveraging synergies with Entwine and Bracket:TutorNonland."}, {"theme": "Cost Reduction", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 433, "description": "Builds around Cost Reduction leveraging synergies with Affinity and Freerunning."}, {"theme": "Flash", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 427, "description": "Builds around Flash leveraging synergies with Evoke and Combat Tricks."}, {"theme": "Haste", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 402, "description": "Builds around Haste leveraging synergies with Hellion Kindred and Phoenix Kindred."}, {"theme": "Lifelink", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 401, "description": "Builds around Lifelink leveraging synergies with Lifegain Triggers and Lifegain."}, {"theme": "Vigilance", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 401, "description": "Builds around Vigilance leveraging synergies with Angel Kindred and Mount Kindred."}, {"theme": "Counterspells", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 397, "description": "Builds around Counterspells leveraging synergies with Control and Stax."}, {"theme": "Transform", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 366, "description": "Builds around Transform leveraging synergies with Incubator Token and Incubate."}, {"theme": "Super Friends", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 344, "description": "Builds around Super Friends leveraging synergies with Planeswalkers and Superfriends."}, {"theme": "Mana Dork", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 340, "description": "Builds around Mana Dork leveraging synergies with Firebending and Scion Kindred."}, {"theme": "Cycling", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 299, "description": "Builds around Cycling leveraging synergies with Landcycling and Basic landcycling."}, {"theme": "Bracket:TutorNonland", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 297, "description": "Builds around Bracket:TutorNonland leveraging synergies with Transmute and Bracket:GameChanger."}, {"theme": "Scry", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 284, "description": "Builds around Scry leveraging synergies with Topdeck and Role token."}, {"theme": "Clones", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 283, "description": "Builds around Clones leveraging synergies with Myriad and Populate."}, {"theme": "Reach", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 275, "description": "Builds around Reach leveraging synergies with Spider Kindred and Archer Kindred."}, {"theme": "First strike", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 252, "description": "Builds around First strike leveraging synergies with Banding and Kithkin Kindred."}, {"theme": "Politics", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 246, "description": "Builds around Politics leveraging synergies with Encore and Melee."}, {"theme": "Defender", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 230, "description": "Builds around Defender leveraging synergies with Wall Kindred and Egg Kindred."}, {"theme": "Menace", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 226, "description": "Builds around Menace leveraging synergies with Warlock Kindred and Blood Token."}, {"theme": "Deathtouch", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 192, "description": "Builds around Deathtouch leveraging synergies with Basilisk Kindred and Scorpion Kindred."}, {"theme": "Equip", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 187, "description": "Builds around Equip leveraging synergies with Job select and For Mirrodin!."}, {"theme": "Land Types Matter", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 185, "description": "Builds around Land Types Matter leveraging synergies with Plainscycling and Mountaincycling."}, {"theme": "Spell Copy", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 184, "description": "Builds around Spell Copy leveraging synergies with Storm and Replicate."}, {"theme": "Landwalk", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 170, "description": "Builds around Landwalk leveraging synergies with Swampwalk and Islandwalk."}, {"theme": "Impulse", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 163, "description": "Builds around Impulse leveraging synergies with Junk Tokens and Junk Token."}, {"theme": "Morph", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 140, "description": "Builds around Morph leveraging synergies with Beast Kindred and Illusion Kindred."}, {"theme": "Devoid", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 114, "description": "Builds around Devoid leveraging synergies with Ingest and Processor Kindred."}]} +{"timestamp": "2025-09-29T21:55:05", "total_themes": 735, "generic_total": 279, "generic_with_synergies": 262, "generic_plain": 17, "generic_pct": 37.96, "top_generic_by_frequency": [{"theme": "Little Fellas", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 7147, "description": "Builds around Little Fellas leveraging synergies with Banding and Licid Kindred."}, {"theme": "Combat Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 6391, "description": "Builds around Combat Matters leveraging synergies with Aggro and Voltron."}, {"theme": "Interaction", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 4160, "description": "Builds around Interaction leveraging synergies with Removal and Combat Tricks."}, {"theme": "Toughness Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3511, "description": "Builds around Toughness Matters leveraging synergies with Defender and Egg Kindred."}, {"theme": "Leave the Battlefield", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3113, "description": "Builds around Leave the Battlefield leveraging synergies with Blink and Enter the Battlefield."}, {"theme": "Enter the Battlefield", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3109, "description": "Builds around Enter the Battlefield leveraging synergies with Blink and Reanimate."}, {"theme": "Card Draw", "popularity_bucket": "Very Common", "synergy_count": 17, "total_frequency": 2708, "description": "Builds around Card Draw leveraging synergies with Loot and Wheels."}, {"theme": "Life Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 2423, "description": "Builds around Life Matters leveraging synergies with Lifegain and Lifedrain."}, {"theme": "Flying", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 2232, "description": "Builds around Flying leveraging synergies with Phoenix Kindred and Archon Kindred."}, {"theme": "Removal", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1601, "description": "Builds around Removal leveraging synergies with Soulshift and Interaction."}, {"theme": "Legends Matter", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1563, "description": "Builds around Legends Matter leveraging synergies with Historics Matter and Superfriends."}, {"theme": "Topdeck", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1112, "description": "Builds around Topdeck leveraging synergies with Scry and Surveil."}, {"theme": "Discard Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1055, "description": "Builds around Discard Matters leveraging synergies with Loot and Wheels."}, {"theme": "Unconditional Draw", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1050, "description": "Builds around Unconditional Draw leveraging synergies with Dredge and Learn."}, {"theme": "Combat Tricks", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 858, "description": "Builds around Combat Tricks leveraging synergies with Flash and Strive."}, {"theme": "Protection", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 810, "description": "Builds around Protection leveraging synergies with Ward and Hexproof."}, {"theme": "Exile Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 718, "description": "Builds around Exile Matters leveraging synergies with Impulse and Suspend."}, {"theme": "Board Wipes", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 649, "description": "Builds around Board Wipes leveraging synergies with Bracket:MassLandDenial and Pingers."}, {"theme": "Pingers", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 643, "description": "Builds around Pingers leveraging synergies with Extort and Devil Kindred."}, {"theme": "Loot", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 526, "description": "Builds around Loot leveraging synergies with Card Draw and Discard Matters."}, {"theme": "Cantrips", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 515, "description": "Builds around Cantrips leveraging synergies with Clue Token and Investigate."}, {"theme": "X Spells", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 506, "description": "Builds around X Spells leveraging synergies with Ravenous and Firebending."}, {"theme": "Conditional Draw", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 458, "description": "Builds around Conditional Draw leveraging synergies with Max speed and Start your engines!."}, {"theme": "Toolbox", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 453, "description": "Builds around Toolbox leveraging synergies with Entwine and Bracket:TutorNonland."}, {"theme": "Cost Reduction", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 433, "description": "Builds around Cost Reduction leveraging synergies with Affinity and Freerunning."}, {"theme": "Flash", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 427, "description": "Builds around Flash leveraging synergies with Evoke and Combat Tricks."}, {"theme": "Haste", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 402, "description": "Builds around Haste leveraging synergies with Hellion Kindred and Phoenix Kindred."}, {"theme": "Lifelink", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 401, "description": "Builds around Lifelink leveraging synergies with Lifegain Triggers and Lifegain."}, {"theme": "Vigilance", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 401, "description": "Builds around Vigilance leveraging synergies with Angel Kindred and Mount Kindred."}, {"theme": "Counterspells", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 397, "description": "Builds around Counterspells leveraging synergies with Control and Stax."}, {"theme": "Transform", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 366, "description": "Builds around Transform leveraging synergies with Incubator Token and Incubate."}, {"theme": "Super Friends", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 344, "description": "Builds around Super Friends leveraging synergies with Planeswalkers and Superfriends."}, {"theme": "Mana Dork", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 340, "description": "Builds around Mana Dork leveraging synergies with Firebending and Scion Kindred."}, {"theme": "Cycling", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 299, "description": "Builds around Cycling leveraging synergies with Landcycling and Basic landcycling."}, {"theme": "Bracket:TutorNonland", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 297, "description": "Builds around Bracket:TutorNonland leveraging synergies with Transmute and Bracket:GameChanger."}, {"theme": "Scry", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 284, "description": "Builds around Scry leveraging synergies with Topdeck and Role token."}, {"theme": "Clones", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 283, "description": "Builds around Clones leveraging synergies with Myriad and Populate."}, {"theme": "Reach", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 275, "description": "Builds around Reach leveraging synergies with Spider Kindred and Archer Kindred."}, {"theme": "First strike", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 252, "description": "Builds around First strike leveraging synergies with Banding and Kithkin Kindred."}, {"theme": "Politics", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 246, "description": "Builds around Politics leveraging synergies with Encore and Melee."}, {"theme": "Defender", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 230, "description": "Builds around Defender leveraging synergies with Wall Kindred and Egg Kindred."}, {"theme": "Menace", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 226, "description": "Builds around Menace leveraging synergies with Warlock Kindred and Blood Token."}, {"theme": "Deathtouch", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 192, "description": "Builds around Deathtouch leveraging synergies with Basilisk Kindred and Scorpion Kindred."}, {"theme": "Equip", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 187, "description": "Builds around Equip leveraging synergies with Job select and For Mirrodin!."}, {"theme": "Land Types Matter", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 185, "description": "Builds around Land Types Matter leveraging synergies with Plainscycling and Mountaincycling."}, {"theme": "Spell Copy", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 184, "description": "Builds around Spell Copy leveraging synergies with Storm and Replicate."}, {"theme": "Landwalk", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 170, "description": "Builds around Landwalk leveraging synergies with Swampwalk and Islandwalk."}, {"theme": "Impulse", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 163, "description": "Builds around Impulse leveraging synergies with Junk Tokens and Junk Token."}, {"theme": "Morph", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 140, "description": "Builds around Morph leveraging synergies with Beast Kindred and Illusion Kindred."}, {"theme": "Devoid", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 114, "description": "Builds around Devoid leveraging synergies with Ingest and Processor Kindred."}]} +{"timestamp": "2025-09-29T21:58:21", "total_themes": 735, "generic_total": 279, "generic_with_synergies": 262, "generic_plain": 17, "generic_pct": 37.96, "top_generic_by_frequency": [{"theme": "Little Fellas", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 7147, "description": "Builds around Little Fellas leveraging synergies with Banding and Licid Kindred."}, {"theme": "Combat Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 6391, "description": "Builds around Combat Matters leveraging synergies with Aggro and Voltron."}, {"theme": "Interaction", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 4160, "description": "Builds around Interaction leveraging synergies with Removal and Combat Tricks."}, {"theme": "Toughness Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3511, "description": "Builds around Toughness Matters leveraging synergies with Defender and Egg Kindred."}, {"theme": "Leave the Battlefield", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3113, "description": "Builds around Leave the Battlefield leveraging synergies with Blink and Enter the Battlefield."}, {"theme": "Enter the Battlefield", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 3109, "description": "Builds around Enter the Battlefield leveraging synergies with Blink and Reanimate."}, {"theme": "Card Draw", "popularity_bucket": "Very Common", "synergy_count": 17, "total_frequency": 2708, "description": "Builds around Card Draw leveraging synergies with Loot and Wheels."}, {"theme": "Life Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 2423, "description": "Builds around Life Matters leveraging synergies with Lifegain and Lifedrain."}, {"theme": "Flying", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 2232, "description": "Builds around Flying leveraging synergies with Phoenix Kindred and Archon Kindred."}, {"theme": "Removal", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1601, "description": "Builds around Removal leveraging synergies with Soulshift and Interaction."}, {"theme": "Legends Matter", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1563, "description": "Builds around Legends Matter leveraging synergies with Historics Matter and Superfriends."}, {"theme": "Topdeck", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1112, "description": "Builds around Topdeck leveraging synergies with Scry and Surveil."}, {"theme": "Discard Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1055, "description": "Builds around Discard Matters leveraging synergies with Loot and Wheels."}, {"theme": "Unconditional Draw", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 1050, "description": "Builds around Unconditional Draw leveraging synergies with Dredge and Learn."}, {"theme": "Combat Tricks", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 858, "description": "Builds around Combat Tricks leveraging synergies with Flash and Strive."}, {"theme": "Protection", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 810, "description": "Builds around Protection leveraging synergies with Ward and Hexproof."}, {"theme": "Exile Matters", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 718, "description": "Builds around Exile Matters leveraging synergies with Impulse and Suspend."}, {"theme": "Board Wipes", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 649, "description": "Builds around Board Wipes leveraging synergies with Bracket:MassLandDenial and Pingers."}, {"theme": "Pingers", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 643, "description": "Builds around Pingers leveraging synergies with Extort and Devil Kindred."}, {"theme": "Loot", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 526, "description": "Builds around Loot leveraging synergies with Card Draw and Discard Matters."}, {"theme": "Cantrips", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 515, "description": "Builds around Cantrips leveraging synergies with Clue Token and Investigate."}, {"theme": "X Spells", "popularity_bucket": "Very Common", "synergy_count": 5, "total_frequency": 506, "description": "Builds around X Spells leveraging synergies with Ravenous and Firebending."}, {"theme": "Conditional Draw", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 458, "description": "Builds around Conditional Draw leveraging synergies with Max speed and Start your engines!."}, {"theme": "Toolbox", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 453, "description": "Builds around Toolbox leveraging synergies with Entwine and Bracket:TutorNonland."}, {"theme": "Cost Reduction", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 433, "description": "Builds around Cost Reduction leveraging synergies with Affinity and Freerunning."}, {"theme": "Flash", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 427, "description": "Builds around Flash leveraging synergies with Evoke and Combat Tricks."}, {"theme": "Haste", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 402, "description": "Builds around Haste leveraging synergies with Hellion Kindred and Phoenix Kindred."}, {"theme": "Lifelink", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 401, "description": "Builds around Lifelink leveraging synergies with Lifegain Triggers and Lifegain."}, {"theme": "Vigilance", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 401, "description": "Builds around Vigilance leveraging synergies with Angel Kindred and Mount Kindred."}, {"theme": "Counterspells", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 397, "description": "Builds around Counterspells leveraging synergies with Control and Stax."}, {"theme": "Transform", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 366, "description": "Builds around Transform leveraging synergies with Incubator Token and Incubate."}, {"theme": "Super Friends", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 344, "description": "Builds around Super Friends leveraging synergies with Planeswalkers and Superfriends."}, {"theme": "Mana Dork", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 340, "description": "Builds around Mana Dork leveraging synergies with Firebending and Scion Kindred."}, {"theme": "Cycling", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 299, "description": "Builds around Cycling leveraging synergies with Landcycling and Basic landcycling."}, {"theme": "Bracket:TutorNonland", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 297, "description": "Builds around Bracket:TutorNonland leveraging synergies with Transmute and Bracket:GameChanger."}, {"theme": "Scry", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 284, "description": "Builds around Scry leveraging synergies with Topdeck and Role token."}, {"theme": "Clones", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 283, "description": "Builds around Clones leveraging synergies with Myriad and Populate."}, {"theme": "Reach", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 275, "description": "Builds around Reach leveraging synergies with Spider Kindred and Archer Kindred."}, {"theme": "First strike", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 252, "description": "Builds around First strike leveraging synergies with Banding and Kithkin Kindred."}, {"theme": "Politics", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 246, "description": "Builds around Politics leveraging synergies with Encore and Melee."}, {"theme": "Defender", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 230, "description": "Builds around Defender leveraging synergies with Wall Kindred and Egg Kindred."}, {"theme": "Menace", "popularity_bucket": "Common", "synergy_count": 5, "total_frequency": 226, "description": "Builds around Menace leveraging synergies with Warlock Kindred and Blood Token."}, {"theme": "Deathtouch", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 192, "description": "Builds around Deathtouch leveraging synergies with Basilisk Kindred and Scorpion Kindred."}, {"theme": "Equip", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 187, "description": "Builds around Equip leveraging synergies with Job select and For Mirrodin!."}, {"theme": "Land Types Matter", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 185, "description": "Builds around Land Types Matter leveraging synergies with Plainscycling and Mountaincycling."}, {"theme": "Spell Copy", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 184, "description": "Builds around Spell Copy leveraging synergies with Storm and Replicate."}, {"theme": "Landwalk", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 170, "description": "Builds around Landwalk leveraging synergies with Swampwalk and Islandwalk."}, {"theme": "Impulse", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 163, "description": "Builds around Impulse leveraging synergies with Junk Tokens and Junk Token."}, {"theme": "Morph", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 140, "description": "Builds around Morph leveraging synergies with Beast Kindred and Illusion Kindred."}, {"theme": "Devoid", "popularity_bucket": "Uncommon", "synergy_count": 5, "total_frequency": 114, "description": "Builds around Devoid leveraging synergies with Ingest and Processor Kindred."}]} diff --git a/config/themes/theme_list.json b/config/themes/theme_list.json index e2ec963..ca73eb8 100644 --- a/config/themes/theme_list.json +++ b/config/themes/theme_list.json @@ -2598,9 +2598,9 @@ "synergies": [ "Beast Kindred", "Trample", + "Artifact Tokens", "Token Creation", - "Tokens Matter", - "Creature Tokens" + "Tokens Matter" ], "primary_color": "Green", "secondary_color": "Red", @@ -2608,8 +2608,8 @@ "Ilharg, the Raze-Boar", "Yasharn, Implacable Earth", "Raggadragga, Goreguts Boss", - "Loot, Exuberant Explorer - Synergy (Beast Kindred)", - "Questing Beast - Synergy (Beast Kindred)" + "Spider-Ham, Peter Porker", + "Loot, Exuberant Explorer - Synergy (Beast Kindred)" ], "example_cards": [ "Curse of the Swine", @@ -2622,10 +2622,11 @@ "Raggadragga, Goreguts Boss" ], "synergy_commanders": [ + "Questing Beast - Synergy (Beast Kindred)", "Kona, Rescue Beastie - Synergy (Beast Kindred)", "Ghalta, Primal Hunger - Synergy (Trample)", "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Trample)", - "Ragavan, Nimble Pilferer - Synergy (Token Creation)" + "Ragavan, Nimble Pilferer - Synergy (Artifact Tokens)" ], "popularity_bucket": "Niche", "editorial_quality": "draft", @@ -5362,7 +5363,7 @@ "Scorpion Kindred", "Gorgon Kindred", "Assassin Kindred", - "Spider Kindred" + "Snake Kindred" ], "primary_color": "Black", "secondary_color": "Green", @@ -5963,7 +5964,7 @@ "Elder Kindred", "Fight", "Trample", - "Cycling" + "Ward" ], "primary_color": "Green", "secondary_color": "Red", @@ -7314,7 +7315,7 @@ "synergy_commanders": [ "Syr Armont, the Redeemer - Synergy (Role token)", "King Macar, the Gold-Cursed - Synergy (Inspired)", - "G'raha Tia, Scion Reborn - Synergy (Hero Kindred)" + "Deadpool, Trading Card - Synergy (Hero Kindred)" ], "popularity_bucket": "Niche", "editorial_quality": "draft", @@ -7838,8 +7839,8 @@ "Reanimate", "Mill", "+1/+1 Counters", - "Counters Matter", - "Voltron" + "Voltron", + "Counters Matter" ], "primary_color": "Black", "secondary_color": "Red", @@ -7879,7 +7880,7 @@ "Zombie Kindred", "Reanimate", "Mill", - "Human Kindred" + "Blink" ], "primary_color": "White", "secondary_color": "Blue", @@ -8511,9 +8512,9 @@ "example_commanders": [ "Wort, Boggart Auntie", "Commander Greven il-Vec", + "Wraith, Vicious Vigilante", "Mondrak, Glory Dominus - Synergy (Horror Kindred)", - "Solphim, Mayhem Dominus - Synergy (Horror Kindred)", - "Zopandrel, Hunger Dominus - Synergy (Horror Kindred)" + "Solphim, Mayhem Dominus - Synergy (Horror Kindred)" ], "example_cards": [ "Shriekmaw", @@ -8526,6 +8527,7 @@ "Arcbound Fiend" ], "synergy_commanders": [ + "Zopandrel, Hunger Dominus - Synergy (Horror Kindred)", "Neheb, the Eternal - Synergy (Zombie Kindred)", "Mikaeus, the Unhallowed - Synergy (Zombie Kindred)", "Ashaya, Soul of the Wild - Synergy (Elemental Kindred)" @@ -8587,8 +8589,8 @@ "synergies": [ "Lore Counters", "Sagas Matter", - "Dinosaur Kindred", "Ore Counters", + "Dinosaur Kindred", "Burn" ], "primary_color": "Green", @@ -8616,7 +8618,7 @@ "Clive, Ifrit's Dominant // Ifrit, Warden of Inferno - Synergy (Lore Counters)", "Jhoira, Weatherlight Captain - Synergy (Sagas Matter)", "Teshar, Ancestor's Apostle - Synergy (Sagas Matter)", - "Etali, Primal Storm - Synergy (Dinosaur Kindred)" + "Vorinclex, Monstrous Raider - Synergy (Ore Counters)" ], "popularity_bucket": "Niche", "editorial_quality": "draft", @@ -9047,7 +9049,7 @@ "Food", "Halfling Kindred", "Squirrel Kindred", - "Peasant Kindred" + "Artifact Tokens" ], "primary_color": "Green", "secondary_color": "Black", @@ -10102,7 +10104,7 @@ "example_commanders": [ "Agent Frank Horrigan - Synergy (Mutant Kindred)", "The Wise Mothman - Synergy (Mutant Kindred)", - "The Master, Transcendent - Synergy (Mutant Kindred)", + "Deadpool, Trading Card - Synergy (Mutant Kindred)", "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)" ], @@ -10676,33 +10678,36 @@ "id": "hero-kindred", "theme": "Hero Kindred", "synergies": [ + "Web-slinging", "Job select", + "Spider Kindred", "Role token", - "Enchantment Tokens", - "Equip", - "Equipment" + "Enchantment Tokens" ], "primary_color": "White", "secondary_color": "Red", "example_commanders": [ + "Deadpool, Trading Card", + "Iron Man, Titan of Innovation", "G'raha Tia, Scion Reborn", - "Tellah, Great Sage", - "Flash Thompson, Spider-Fan", - "Ellivere of the Wild Court - Synergy (Role token)", - "Gylwain, Casting Director - Synergy (Role token)" + "Storm, Force of Nature", + "Wolverine, Best There Is" ], "example_cards": [ + "Deadpool, Trading Card", "Black Mage's Rod", "Dancer's Chakrams", "Zanarkand, Ancient Metropolis // Lasting Fayth", "Champions from Beyond", "Astrologian's Planisphere", - "G'raha Tia, Scion Reborn", - "Machinist's Arsenal", - "Samurai's Katana" + "Iron Man, Titan of Innovation", + "G'raha Tia, Scion Reborn" ], "synergy_commanders": [ - "Heliod, God of the Sun - Synergy (Enchantment Tokens)" + "Spider-Man, Brooklyn Visionary - Synergy (Web-slinging)", + "Spider-Man India - Synergy (Web-slinging)", + "Scarlet Spider, Ben Reilly - Synergy (Web-slinging)", + "Arasta of the Endless Web - Synergy (Spider Kindred)" ], "popularity_bucket": "Rare", "editorial_quality": "draft", @@ -12103,9 +12108,9 @@ "primary_color": "White", "secondary_color": "Black", "example_commanders": [ + "Deadpool, Trading Card - Synergy (Hero Kindred)", + "Iron Man, Titan of Innovation - Synergy (Hero Kindred)", "G'raha Tia, Scion Reborn - Synergy (Hero Kindred)", - "Tellah, Great Sage - Synergy (Hero Kindred)", - "Flash Thompson, Spider-Fan - Synergy (Hero Kindred)", "Halvar, God of Battle // Sword of the Realms - Synergy (Equip)", "Mithril Coat - Synergy (Equip)" ], @@ -14148,8 +14153,8 @@ "Chameleon, Master of Disguise", "Carnage, Crimson Chaos", "Ultimate Green Goblin", - "Swarm, Being of Bees", - "Solphim, Mayhem Dominus - Synergy (Discard Matters)" + "Scarlet Spider, Kaine", + "Swarm, Being of Bees" ], "example_cards": [ "Chameleon, Master of Disguise", @@ -14158,10 +14163,11 @@ "Oscorp Industries", "Rocket-Powered Goblin Glider", "Prison Break", - "Electro's Bolt", - "Swarm, Being of Bees" + "Scarlet Spider, Kaine", + "Electro's Bolt" ], "synergy_commanders": [ + "Solphim, Mayhem Dominus - Synergy (Discard Matters)", "Yawgmoth, Thran Physician - Synergy (Discard Matters)", "Nezahal, Primal Tide - Synergy (Discard Matters)", "Syr Konrad, the Grim - Synergy (Mill)", @@ -14367,9 +14373,9 @@ "example_commanders": [ "Kellogg, Dangerous Mind", "Cloud, Midgar Mercenary", + "Deadpool, Trading Card", "Rakdos, the Muscle", - "The Infamous Cruelclaw", - "Gev, Scaled Scorch" + "The Infamous Cruelclaw" ], "example_cards": [ "Black Market Connections", @@ -14377,9 +14383,9 @@ "Kellogg, Dangerous Mind", "Doomed Necromancer", "Cloud, Midgar Mercenary", + "Deadpool, Trading Card", "Rakdos, the Muscle", - "The Infamous Cruelclaw", - "Howlsquad Heavy" + "The Infamous Cruelclaw" ], "synergy_commanders": [ "Ragavan, Nimble Pilferer - Synergy (Outlaw Kindred)", @@ -15428,9 +15434,9 @@ "example_commanders": [ "Agent Frank Horrigan", "The Wise Mothman", + "Deadpool, Trading Card", "The Master, Transcendent", - "Raul, Trouble Shooter", - "Sliver Overlord" + "Raul, Trouble Shooter" ], "example_cards": [ "Evolution Witness", @@ -16869,8 +16875,8 @@ "Magar of the Magic Strings", "Myra the Magnificent", "Spinnerette, Arachnobat", - "Mary Jane Watson", - "MJ, Rising Star" + "Gwen Stacy // Ghost-Spider", + "Mary Jane Watson" ], "example_cards": [ "Dancer's Chakrams", @@ -16879,8 +16885,8 @@ "Centaur of Attention", "Spinnerette, Arachnobat", "Atomwheel Acrobats", - "Chicken Troupe", - "Done for the Day" + "Gwen Stacy // Ghost-Spider", + "Chicken Troupe" ], "synergy_commanders": [ "Kodama of the East Tree - Synergy (Partner)", @@ -18063,6 +18069,7 @@ "Glowing One" ], "synergy_commanders": [ + "Deadpool, Trading Card - Synergy (Mutant Kindred)", "Neheb, the Eternal - Synergy (Zombie Kindred)", "Mikaeus, the Unhallowed - Synergy (Zombie Kindred)", "Syr Konrad, the Grim - Synergy (Mill)" @@ -19023,23 +19030,24 @@ "primary_color": "Red", "secondary_color": "Green", "example_commanders": [ + "Spider-Punk", "Rishkar, Peema Renegade - Synergy (+1/+1 Counters)", "Krenko, Tin Street Kingpin - Synergy (+1/+1 Counters)", "Yawgmoth, Thran Physician - Synergy (+1/+1 Counters)", - "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)", - "Yahenni, Undying Partisan - Synergy (Counters Matter)" + "Etali, Primal Conqueror // Etali, Primal Sickness - Synergy (Counters Matter)" ], "example_cards": [ "Skarrgan Hellkite", "Arcbound Slasher", + "Spider-Punk", "Ravager Wurm", "Gruul Spellbreaker", "Clamor Shaman", "Burning-Tree Vandal", - "Wrecking Beast", - "Frenzied Arynx" + "Wrecking Beast" ], "synergy_commanders": [ + "Yahenni, Undying Partisan - Synergy (Counters Matter)", "Sram, Senior Edificer - Synergy (Voltron)" ], "popularity_bucket": "Rare", @@ -19177,8 +19185,8 @@ "Asinine Antics" ], "synergy_commanders": [ - "G'raha Tia, Scion Reborn - Synergy (Hero Kindred)", - "Tellah, Great Sage - Synergy (Hero Kindred)", + "Deadpool, Trading Card - Synergy (Hero Kindred)", + "Iron Man, Titan of Innovation - Synergy (Hero Kindred)", "Sram, Senior Edificer - Synergy (Equipment Matters)" ], "popularity_bucket": "Rare", @@ -20058,7 +20066,7 @@ "Clones", "Flash", "Little Fellas", - "Protection" + "Outlaw Kindred" ], "primary_color": "Blue", "secondary_color": "Green", @@ -20154,7 +20162,7 @@ "Counters Matter", "Lifegain", "Life Matters", - "Human Kindred" + "Blink" ], "primary_color": "White", "secondary_color": "Green", @@ -20882,6 +20890,7 @@ ], "synergy_commanders": [ "Aeve, Progenitor Ooze - Synergy (Storm)", + "Storm, Force of Nature - Synergy (Storm)", "Ral, Crackling Wit - Synergy (Storm)", "Ob Nixilis, the Adversary - Synergy (Casualty)" ], @@ -21086,11 +21095,11 @@ "id": "spider-kindred", "theme": "Spider Kindred", "synergies": [ + "Web-slinging", + "Hero Kindred", "Reach", "Deathtouch", - "Toughness Matters", - "Creature Tokens", - "+1/+1 Counters" + "Toughness Matters" ], "primary_color": "Green", "secondary_color": "Black", @@ -21112,12 +21121,12 @@ "Sweet-Gum Recluse" ], "synergy_commanders": [ - "Six - Synergy (Reach)", - "Kodama of the West Tree - Synergy (Reach)", - "Kodama of the East Tree - Synergy (Reach)", - "Sheoldred, the Apocalypse - Synergy (Deathtouch)", - "Elas il-Kor, Sadistic Pilgrim - Synergy (Deathtouch)", - "Azusa, Lost but Seeking - Synergy (Toughness Matters)" + "Spider-Man, Brooklyn Visionary - Synergy (Web-slinging)", + "Spider-Man India - Synergy (Web-slinging)", + "Scarlet Spider, Ben Reilly - Synergy (Web-slinging)", + "Deadpool, Trading Card - Synergy (Hero Kindred)", + "Iron Man, Titan of Innovation - Synergy (Hero Kindred)", + "Six - Synergy (Reach)" ], "popularity_bucket": "Niche", "editorial_quality": "draft", @@ -21646,6 +21655,7 @@ "secondary_color": "Blue", "example_commanders": [ "Aeve, Progenitor Ooze", + "Storm, Force of Nature", "Lotho, Corrupt Shirriff - Synergy (Spellslinger)", "Birgi, God of Storytelling // Harnfel, Horn of Bounty - Synergy (Spellslinger)", "Talrand, Sky Summoner - Synergy (Spellslinger)" @@ -21912,7 +21922,7 @@ "Reanimate", "Graveyard Matters", "Topdeck", - "Rogue Kindred" + "Menace" ], "primary_color": "Blue", "secondary_color": "Black", @@ -22892,7 +22902,7 @@ "Wurm Kindred", "Hydra Kindred", "Hellion Kindred", - "Boar Kindred" + "Leviathan Kindred" ], "primary_color": "Green", "secondary_color": "Red", @@ -23079,8 +23089,8 @@ "theme": "Treefolk Kindred", "synergies": [ "Druid Kindred", - "Reach", "Shaman Kindred", + "Reach", "Land Types Matter", "Trample" ], @@ -23107,8 +23117,8 @@ "Tatyova, Benthic Druid - Synergy (Druid Kindred)", "Rishkar, Peema Renegade - Synergy (Druid Kindred)", "Jaheira, Friend of the Forest - Synergy (Druid Kindred)", - "Kodama of the West Tree - Synergy (Reach)", - "Kiki-Jiki, Mirror Breaker - Synergy (Shaman Kindred)" + "Kiki-Jiki, Mirror Breaker - Synergy (Shaman Kindred)", + "Delina, Wild Mage - Synergy (Shaman Kindred)" ], "popularity_bucket": "Niche", "editorial_quality": "draft", @@ -24019,9 +24029,9 @@ "synergies": [ "Turtle Kindred", "Protection", + "Dinosaur Kindred", "Dragon Kindred", - "Interaction", - "Stax" + "Interaction" ], "primary_color": "Blue", "secondary_color": "Green", @@ -24048,7 +24058,7 @@ "Archelos, Lagoon Mystic - Synergy (Turtle Kindred)", "Toski, Bearer of Secrets - Synergy (Protection)", "Purphoros, God of the Forge - Synergy (Protection)", - "Niv-Mizzet, Parun - Synergy (Dragon Kindred)" + "Etali, Primal Storm - Synergy (Dinosaur Kindred)" ], "popularity_bucket": "Niche", "editorial_quality": "draft", @@ -24225,6 +24235,43 @@ "editorial_quality": "draft", "description": "Focuses on getting a high number of Weasel creatures into play with shared payoffs." }, + { + "id": "web-slinging", + "theme": "Web-slinging", + "synergies": [ + "Hero Kindred", + "Spider Kindred" + ], + "primary_color": "White", + "secondary_color": "Green", + "example_commanders": [ + "Spider-Man, Brooklyn Visionary", + "Spider-Man India", + "Scarlet Spider, Ben Reilly", + "Spider-Man, Web-Slinger", + "Silk, Web Weaver" + ], + "example_cards": [ + "Spider-Man, Brooklyn Visionary", + "Spider-Sense", + "Spider-Man India", + "Scarlet Spider, Ben Reilly", + "Spider-Man, Web-Slinger", + "Silk, Web Weaver", + "Spider-UK", + "Spiders-Man, Heroic Horde" + ], + "synergy_commanders": [ + "Deadpool, Trading Card - Synergy (Hero Kindred)", + "Iron Man, Titan of Innovation - Synergy (Hero Kindred)", + "G'raha Tia, Scion Reborn - Synergy (Hero Kindred)", + "Arasta of the Endless Web - Synergy (Spider Kindred)", + "Shelob, Dread Weaver - Synergy (Spider Kindred)" + ], + "popularity_bucket": "Rare", + "editorial_quality": "draft", + "description": "Builds around Web-slinging leveraging synergies with Hero Kindred and Spider Kindred." + }, { "id": "weird-kindred", "theme": "Weird Kindred", @@ -24947,74 +24994,74 @@ ], "frequencies_by_base_color": { "white": { - "Aggro": 1338, - "Artifacts Matter": 703, - "Combat Matters": 1338, + "Aggro": 1341, + "Artifacts Matter": 704, + "Combat Matters": 1341, "Equip": 55, "Equipment": 57, - "Equipment Matters": 211, - "Voltron": 931, - "Big Mana": 1009, + "Equipment Matters": 212, + "Voltron": 934, + "Big Mana": 1013, "Bird Kindred": 163, - "Blink": 737, - "Enter the Battlefield": 737, - "Flying": 685, + "Blink": 739, + "Enter the Battlefield": 739, + "Flying": 687, "Guest Kindred": 3, - "Leave the Battlefield": 741, - "Life Matters": 1099, - "Lifegain": 1098, - "Little Fellas": 1698, - "Toughness Matters": 908, - "Mill": 394, - "Spells Matter": 1156, - "Spellslinger": 1156, - "Auras": 371, - "Enchantments Matter": 956, + "Leave the Battlefield": 743, + "Life Matters": 1103, + "Lifegain": 1102, + "Little Fellas": 1699, + "Toughness Matters": 911, + "Mill": 395, + "Spells Matter": 1157, + "Spellslinger": 1157, + "Auras": 372, + "Enchantments Matter": 957, "Cantrips": 88, - "Card Draw": 308, + "Card Draw": 309, "Combat Tricks": 215, - "Interaction": 1061, + "Interaction": 1063, "Unconditional Draw": 133, - "Cost Reduction": 67, - "Flash": 111, + "Cost Reduction": 68, + "Flash": 112, "Scry": 60, "Topdeck": 141, "Waterbending": 1, "Ally Kindred": 48, "Avatar Kindred": 24, - "Historics Matter": 353, - "Human Kindred": 1140, - "Legends Matter": 353, + "Historics Matter": 361, + "Human Kindred": 1148, + "Legends Matter": 361, "Vigilance": 258, "Airbending": 4, - "Counters Matter": 676, + "Counters Matter": 679, "Creature Tokens": 499, "Exile Matters": 109, "Experience Counters": 1, "Token Creation": 581, "Tokens Matter": 590, - "Lifelink": 229, + "Lifelink": 230, "Beast Kindred": 30, "Sloth Kindred": 3, "Lands Matter": 169, "Gargoyle Kindred": 11, - "Protection": 332, + "Protection": 334, "Griffin Kindred": 43, "Cleric Kindred": 368, "Backgrounds Matter": 11, "Choose a background": 5, "Soldier Kindred": 634, "Warrior Kindred": 155, - "Control": 221, + "Control": 222, "Toolbox": 91, "Removal": 408, - "Aristocrats": 154, + "Aristocrats": 155, "Haunt": 4, - "Sacrifice Matters": 154, + "Sacrifice Matters": 155, "Thrull Kindred": 2, "Lammasu Kindred": 3, "Stax": 450, - "+1/+1 Counters": 459, + "+1/+1 Counters": 462, "Spirit Kindred": 224, "X Spells": 60, "Cat Kindred": 133, @@ -25059,8 +25106,8 @@ "Performer Kindred": 7, "Midrange": 103, "Support": 7, - "Lifegain Triggers": 35, - "Hero Kindred": 15, + "Lifegain Triggers": 36, + "Hero Kindred": 24, "Stun Counters": 5, "Take 59 Flights of Stairs": 1, "Take the Elevator": 1, @@ -25105,14 +25152,14 @@ "Elf Kindred": 17, "Partner with": 7, "Assassin Kindred": 4, - "Outlaw Kindred": 26, + "Outlaw Kindred": 27, "Warp": 8, "Buyback": 9, "Join forces": 1, "Rogue Kindred": 21, - "Draw Triggers": 33, + "Draw Triggers": 34, "Replacement Draw": 2, - "Wheels": 38, + "Wheels": 39, "Nymph Kindred": 4, "Coven": 10, "Peasant Kindred": 19, @@ -25136,7 +25183,7 @@ "Affinity": 8, "Citizen Kindred": 26, "Conditional Draw": 57, - "Mercenary Kindred": 13, + "Mercenary Kindred": 14, "-1/-1 Counters": 27, "Clue Token": 22, "Investigate": 20, @@ -25151,6 +25198,8 @@ "Food": 25, "Food Token": 20, "Bushido": 20, + "Spider Kindred": 7, + "Web-slinging": 3, "Enlist": 5, "Archer Kindred": 17, "Pegasus Kindred": 24, @@ -25194,7 +25243,6 @@ "Time Counters": 25, "Incubator Token": 12, "Shadow": 11, - "Spider Kindred": 2, "Atog Kindred": 1, "Disguise": 7, "Gold Counters": 1, @@ -25556,9 +25604,9 @@ "Blink": 576, "Enter the Battlefield": 576, "Guest Kindred": 3, - "Human Kindred": 550, + "Human Kindred": 551, "Leave the Battlefield": 576, - "Little Fellas": 1444, + "Little Fellas": 1445, "Outlaw Kindred": 218, "Rogue Kindred": 150, "Casualty": 5, @@ -25569,17 +25617,17 @@ "Bird Kindred": 149, "Flying": 779, "Toughness Matters": 917, - "Aggro": 903, + "Aggro": 904, "Aristocrats": 119, "Auras": 348, - "Combat Matters": 903, + "Combat Matters": 904, "Enchant": 305, "Enchantments Matter": 747, "Midrange": 54, "Sacrifice Matters": 110, "Theft": 115, "Voltron": 601, - "Big Mana": 1255, + "Big Mana": 1256, "Elf Kindred": 11, "Mill": 578, "Reanimate": 498, @@ -25600,8 +25648,8 @@ "Protection": 158, "Ward": 39, "Threshold": 9, - "Historics Matter": 299, - "Legends Matter": 299, + "Historics Matter": 301, + "Legends Matter": 301, "Noble Kindred": 13, "Octopus Kindred": 44, "Removal": 249, @@ -25616,7 +25664,7 @@ "Counters Matter": 482, "Drake Kindred": 75, "Kicker": 29, - "Card Draw": 1054, + "Card Draw": 1055, "Discard Matters": 327, "Loot": 247, "Wizard Kindred": 532, @@ -25624,7 +25672,7 @@ "Artifacts Matter": 632, "Equipment Matters": 91, "Lands Matter": 198, - "Conditional Draw": 194, + "Conditional Draw": 195, "Defender": 69, "Draw Triggers": 171, "Wall Kindred": 41, @@ -25647,7 +25695,7 @@ "Drone Kindred": 22, "Zombie Kindred": 83, "Turtle Kindred": 21, - "Avatar Kindred": 13, + "Avatar Kindred": 14, "Exile Matters": 143, "Suspend": 24, "Time Counters": 33, @@ -25756,7 +25804,7 @@ "Incubator Token": 4, "Phyrexian Kindred": 51, "Project Image": 1, - "Hero Kindred": 5, + "Hero Kindred": 7, "Job select": 4, "Shark Kindred": 10, "Oil Counters": 12, @@ -26029,7 +26077,7 @@ "Hatchling Counters": 1, "Werewolf Kindred": 1, "Wolf Kindred": 1, - "Spider Kindred": 1, + "Spider Kindred": 3, "Eon Counters": 1, "Dethrone": 2, "Lifegain Triggers": 1, @@ -26147,27 +26195,27 @@ "Enter the Battlefield": 763, "Guest Kindred": 6, "Leave the Battlefield": 763, - "Little Fellas": 1364, - "Mill": 986, + "Little Fellas": 1365, + "Mill": 987, "Open an Attraction": 5, - "Reanimate": 987, + "Reanimate": 988, "Roll to Visit Your Attractions": 2, "Zombie Kindred": 498, "Alien Kindred": 6, "Child Kindred": 1, - "Life Matters": 848, - "Lifegain": 845, - "Lifelink": 166, - "Big Mana": 1224, - "Spells Matter": 1379, - "Spellslinger": 1379, + "Life Matters": 849, + "Lifegain": 846, + "Lifelink": 167, + "Big Mana": 1226, + "Spells Matter": 1380, + "Spellslinger": 1380, "X Spells": 82, - "Aggro": 1220, - "Aristocrats": 660, - "Combat Matters": 1220, + "Aggro": 1223, + "Aristocrats": 661, + "Combat Matters": 1223, "First strike": 19, - "Sacrifice Matters": 656, - "Toughness Matters": 543, + "Sacrifice Matters": 657, + "Toughness Matters": 544, "Creature Tokens": 304, "Demon Kindred": 166, "Flying": 482, @@ -26179,8 +26227,8 @@ "Midrange": 70, "Horror Kindred": 184, "Basic landcycling": 2, - "Burn": 907, - "Card Draw": 643, + "Burn": 908, + "Card Draw": 644, "Cycling": 48, "Discard Matters": 230, "Landcycling": 2, @@ -26196,10 +26244,10 @@ "Outlaw Kindred": 373, "Warlock Kindred": 72, "Assassin Kindred": 84, - "Human Kindred": 476, + "Human Kindred": 477, "Nightstalker Kindred": 12, - "Draw Triggers": 280, - "Wheels": 298, + "Draw Triggers": 281, + "Wheels": 299, "Stax": 246, "Trample": 54, "Specter Kindred": 21, @@ -26214,19 +26262,19 @@ "Equipment Matters": 83, "Shaman Kindred": 61, "Transform": 76, - "Voltron": 652, + "Voltron": 653, "Auras": 239, "Enchant": 207, "Enchantments Matter": 606, "Pingers": 234, - "Historics Matter": 337, - "Legends Matter": 337, + "Historics Matter": 340, + "Legends Matter": 340, "Politics": 54, "Venture into the dungeon": 6, "Wizard Kindred": 114, - "+1/+1 Counters": 380, - "Counters Matter": 637, - "Deathtouch": 137, + "+1/+1 Counters": 381, + "Counters Matter": 638, + "Deathtouch": 138, "Dragon Kindred": 30, "Megamorph": 4, "Bat Kindred": 39, @@ -26235,11 +26283,11 @@ "Cleric Kindred": 121, "Vampire Kindred": 274, "Rogue Kindred": 180, - "Flash": 53, + "Flash": 54, "Phyrexian Kindred": 165, "Shapeshifter Kindred": 11, "Bracket:GameChanger": 11, - "Topdeck": 169, + "Topdeck": 171, "Crocodile Kindred": 12, "Druid Kindred": 6, "Renew": 4, @@ -26261,6 +26309,9 @@ "Mercenary Kindred": 43, "Heroic": 4, "Backgrounds Matter": 12, + "Hero Kindred": 5, + "Menace": 136, + "Soldier Kindred": 60, "Theft": 95, "Eye Kindred": 9, "Toolbox": 77, @@ -26272,7 +26323,6 @@ "Exile Matters": 124, "Scream Counters": 2, "Disguise": 4, - "Menace": 134, "Madness": 29, "Void": 10, "Ward": 17, @@ -26302,7 +26352,7 @@ "Morph": 24, "Bird Kindred": 33, "Cantrips": 82, - "Surveil": 41, + "Surveil": 42, "Modular": 1, "Gorgon Kindred": 18, "Unearth": 19, @@ -26330,7 +26380,6 @@ "Powerstone Token": 4, "Devil Kindred": 3, "Replacement Draw": 3, - "Soldier Kindred": 59, "Goblin Kindred": 45, "Prowl": 5, "Shade Kindred": 32, @@ -26389,7 +26438,6 @@ "Land Types Matter": 36, "Equip": 32, "Equipment": 35, - "Hero Kindred": 2, "Job select": 4, "Buy Information": 1, "Hire a Mercenary": 1, @@ -26552,7 +26600,7 @@ "Conspire": 1, "Reach": 2, "Soulbond": 1, - "Spider Kindred": 4, + "Spider Kindred": 6, "Junk Token": 1, "Skunk Kindred": 1, "Domain": 7, @@ -26802,60 +26850,60 @@ "Burning Chains": 1 }, "red": { - "Burn": 1556, + "Burn": 1557, "Enchantments Matter": 578, "Blink": 454, "Enter the Battlefield": 454, "Goblin Kindred": 394, "Guest Kindred": 4, "Leave the Battlefield": 454, - "Little Fellas": 1256, + "Little Fellas": 1260, "Mana Dork": 58, "Ramp": 99, - "Spells Matter": 1539, - "Spellslinger": 1539, - "Aggro": 1417, - "Combat Matters": 1417, + "Spells Matter": 1540, + "Spellslinger": 1540, + "Aggro": 1421, + "Combat Matters": 1421, "Combat Tricks": 159, - "Discard Matters": 303, + "Discard Matters": 305, "Interaction": 653, "Madness": 18, "Mill": 341, "Reanimate": 262, "Flashback": 45, "Artifacts Matter": 699, - "Exile Matters": 253, - "Human Kindred": 558, - "Impulse": 143, + "Exile Matters": 254, + "Human Kindred": 562, + "Impulse": 144, "Monk Kindred": 19, "Prowess": 20, "Removal": 211, "Toolbox": 87, - "Card Draw": 350, + "Card Draw": 352, "Learn": 5, - "Unconditional Draw": 154, + "Unconditional Draw": 155, "Intimidate": 5, "Warrior Kindred": 363, - "Cantrips": 78, + "Cantrips": 79, "Draw Triggers": 54, "Heavy Rock Cutter": 1, "Tyranid Kindred": 4, "Wheels": 58, - "+1/+1 Counters": 247, - "Counters Matter": 434, + "+1/+1 Counters": 249, + "Counters Matter": 436, "Renown": 5, - "Voltron": 537, + "Voltron": 539, "Auras": 196, "Enchant": 159, "Goad": 29, "Rad Counters": 2, - "Big Mana": 1244, + "Big Mana": 1245, "Stax": 333, "Theft": 130, "Lands Matter": 251, "Control": 154, - "Historics Matter": 311, - "Legends Matter": 311, + "Historics Matter": 315, + "Legends Matter": 315, "Spirit Kindred": 71, "Clash": 5, "Minotaur Kindred": 73, @@ -26863,7 +26911,7 @@ "Vehicles": 36, "Berserker Kindred": 88, "Rampage": 4, - "Toughness Matters": 472, + "Toughness Matters": 474, "Beast Kindred": 88, "Artifact Tokens": 176, "Artificer Kindred": 51, @@ -26876,7 +26924,7 @@ "Token Creation": 417, "Tokens Matter": 423, "Defender": 35, - "Reach": 45, + "Reach": 46, "Wall Kindred": 29, "Aetherborn Kindred": 1, "Revolt": 1, @@ -26888,10 +26936,10 @@ "Board Wipes": 264, "Lizard Kindred": 85, "Offspring": 5, - "Sacrifice to Draw": 37, + "Sacrifice to Draw": 38, "Insect Kindred": 19, "Exert": 11, - "Haste": 331, + "Haste": 332, "Aristocrats": 200, "Sacrifice Matters": 194, "Zombie Kindred": 16, @@ -26958,10 +27006,10 @@ "Bracket:MassLandDenial": 29, "Spellshaper Kindred": 12, "Ox Kindred": 7, - "Cat Kindred": 30, + "Cat Kindred": 31, "Modular": 3, - "Riot": 5, - "Menace": 90, + "Riot": 6, + "Menace": 91, "Verse Counters": 3, "Orc Kindred": 48, "Boast": 7, @@ -27164,7 +27212,7 @@ "Foretell": 9, "Coyote Kindred": 1, "Gold Token": 2, - "Hero Kindred": 6, + "Hero Kindred": 11, "Gift of Chaos": 1, "Warlock Kindred": 9, "Beholder Kindred": 1, @@ -27260,6 +27308,7 @@ "Persist": 2, "Finality Counters": 1, "Channel": 7, + "Spider Kindred": 7, "Stash Counters": 2, "Gnoll Kindred": 1, "Shrines Matter": 3, @@ -27324,7 +27373,6 @@ "Kinship": 3, "Divinity Counters": 1, "Banding": 1, - "Spider Kindred": 2, "Sonic Blaster": 1, "Elephant Kindred": 2, "Pangolin Kindred": 1, @@ -27401,29 +27449,29 @@ "Ninjutsu": 1 }, "green": { - "+1/+1 Counters": 788, - "Aggro": 1513, + "+1/+1 Counters": 789, + "Aggro": 1515, "Alien Kindred": 8, - "Big Mana": 1369, + "Big Mana": 1372, "Blink": 579, - "Combat Matters": 1513, - "Counters Matter": 993, - "Dinosaur Kindred": 87, + "Combat Matters": 1515, + "Counters Matter": 994, + "Dinosaur Kindred": 88, "Enter the Battlefield": 579, "Leave the Battlefield": 579, - "Trample": 341, - "Voltron": 1039, - "Creature Tokens": 423, + "Trample": 343, + "Voltron": 1040, + "Creature Tokens": 424, "Enchantments Matter": 670, "Goblin Kindred": 5, - "Human Kindred": 379, + "Human Kindred": 381, "Merfolk Kindred": 29, - "Token Creation": 523, - "Tokens Matter": 532, - "Artifacts Matter": 455, + "Token Creation": 525, + "Tokens Matter": 534, + "Artifacts Matter": 456, "Heavy Power Hammer": 1, - "Interaction": 666, - "Little Fellas": 1385, + "Interaction": 667, + "Little Fellas": 1387, "Mutant Kindred": 27, "Ravenous": 7, "Removal": 251, @@ -27434,7 +27482,7 @@ "Cumulative upkeep": 15, "Elemental Kindred": 159, "Card Draw": 353, - "Lands Matter": 614, + "Lands Matter": 615, "Topdeck": 259, "Unconditional Draw": 153, "Auras": 244, @@ -27444,26 +27492,26 @@ "Spellslinger": 1138, "Dog Kindred": 30, "Shaman Kindred": 117, - "Life Matters": 346, - "Lifegain": 346, + "Life Matters": 348, + "Lifegain": 348, "Lifelink": 5, "Warrior Kindred": 262, "Combat Tricks": 178, "Druid Kindred": 254, "Elf Kindred": 406, "Mana Dork": 199, - "Ramp": 510, - "Toughness Matters": 671, + "Ramp": 511, + "Toughness Matters": 672, "Doctor Kindred": 6, "Doctor's companion": 5, "Fight": 74, - "Historics Matter": 263, - "Legends Matter": 263, + "Historics Matter": 268, + "Legends Matter": 268, "Nitro-9": 1, "Rebel Kindred": 3, "Equipment Matters": 80, - "Reach": 219, - "Spider Kindred": 70, + "Reach": 220, + "Spider Kindred": 75, "Deathtouch": 55, "Ooze Kindred": 33, "Backgrounds Matter": 11, @@ -27479,13 +27527,13 @@ "Evolve": 9, "Lizard Kindred": 29, "Infect": 64, - "Midrange": 90, + "Midrange": 91, "Phyrexian Kindred": 71, "Planeswalkers": 69, "Proliferate": 21, "Super Friends": 69, "Toolbox": 128, - "Vigilance": 90, + "Vigilance": 91, "Burn": 218, "Archer Kindred": 50, "Megamorph": 8, @@ -27493,7 +27541,7 @@ "Ouphe Kindred": 14, "Persist": 2, "Sacrifice Matters": 165, - "Artifact Tokens": 111, + "Artifact Tokens": 112, "Artificer Kindred": 19, "Energy": 19, "Energy Counters": 19, @@ -27515,8 +27563,8 @@ "Vehicles": 25, "Revolt": 6, "Scout Kindred": 97, - "Stax": 282, - "Protection": 194, + "Stax": 283, + "Protection": 195, "Faerie Kindred": 13, "Soldier Kindred": 37, "Mount Kindred": 14, @@ -27548,7 +27596,7 @@ "Treasure": 26, "Treasure Token": 25, "Turtle Kindred": 12, - "Ward": 25, + "Ward": 26, "Elder Kindred": 3, "Flying": 49, "Mana Rock": 16, @@ -27577,7 +27625,7 @@ "Sloth Kindred": 3, "Hexproof": 34, "Defender": 40, - "Boar Kindred": 30, + "Boar Kindred": 31, "Landfall": 68, "Conditional Draw": 85, "Powerstone Token": 2, @@ -27616,8 +27664,8 @@ "Earthbend": 8, "Mole Kindred": 6, "Dwarf Kindred": 3, - "Food": 57, - "Food Token": 54, + "Food": 58, + "Food Token": 55, "Raccoon Kindred": 13, "Forestcycling": 8, "Land Types Matter": 58, @@ -27629,7 +27677,7 @@ "Pingers": 22, "Equip": 27, "Equipment": 29, - "Hero Kindred": 3, + "Hero Kindred": 8, "Job select": 2, "Berserker Kindred": 8, "Enlist": 3, @@ -27949,6 +27997,8 @@ "Multiple Copies": 1, "Slime Against Humanity": 1, "Slith Kindred": 1, + "Animal May-Ham": 1, + "Web-slinging": 2, "Spike Kindred": 10, "Armadillo Kindred": 1, "Spore Chimney": 1, @@ -27997,12 +28047,12 @@ "generated_from": "merge (analytics + curated YAML + whitelist)", "metadata_info": { "mode": "merge", - "generated_at": "2025-09-30T00:38:22", - "curated_yaml_files": 735, + "generated_at": "2025-09-30T05:56:13", + "curated_yaml_files": 736, "synergy_cap": 5, "inference": "pmi", "version": "phase-b-merge-v1", - "catalog_hash": "58d00ba9900f1036f00f8e831713ce53c1df5fde36899a71e9305e65e67d8f16" + "catalog_hash": "9e6e5499f3b39f4664f656b9b58f2d8228c4ef75a34041a14c330a8eb31564ba" }, "description_fallback_summary": null } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index f1d7717..ab68bdb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -51,6 +51,18 @@ services: RANDOM_UI: "1" # 1=show Surprise/Theme/Reroll/Share controls in UI RANDOM_MAX_ATTEMPTS: "5" # cap retry attempts RANDOM_TIMEOUT_MS: "5000" # per-build timeout in ms + RANDOM_THEME: "" # optional legacy theme alias (maps to primary theme) + RANDOM_PRIMARY_THEME: "" # optional primary theme slug override + RANDOM_SECONDARY_THEME: "" # optional secondary theme slug override + RANDOM_TERTIARY_THEME: "" # optional tertiary theme slug override + RANDOM_AUTO_FILL: "0" # when 1, auto-fill missing secondary/tertiary themes + RANDOM_AUTO_FILL_SECONDARY: "" # override secondary auto-fill (blank inherits from RANDOM_AUTO_FILL) + RANDOM_AUTO_FILL_TERTIARY: "" # override tertiary auto-fill (blank inherits from RANDOM_AUTO_FILL) + RANDOM_STRICT_THEME_MATCH: "0" # require strict theme matches for commanders when 1 + RANDOM_CONSTRAINTS: "" # inline JSON constraints for random builds (optional) + RANDOM_CONSTRAINTS_PATH: "" # path to JSON constraints file (takes precedence) + RANDOM_SEED: "" # deterministic random seed (int or string) + RANDOM_OUTPUT_JSON: "" # path or directory for random build output payload # RANDOM_BUILD_SUPPRESS_INITIAL_EXPORT: "1" # (now defaults to 1 automatically for random builds; set to 0 to force legacy double-export behavior) # Theming @@ -80,7 +92,7 @@ services: # WEB_THEME_FILTER_PREWARM: "0" WEB_AUTO_ENFORCE: "0" # 1=auto-run compliance export after builds WEB_CUSTOM_EXPORT_BASE: "" # Optional: custom base dir for deck export artifacts - APP_VERSION: "dev" # Displayed version label (set per release/tag) + APP_VERSION: "2.3.1" # Displayed version label (set per release/tag) # ------------------------------------------------------------------ # Misc / Land Selection (Step 7) Environment Tuning @@ -144,6 +156,7 @@ services: # DECK_TRIPLE_COUNT: "" # DECK_UTILITY_COUNT: "" # DECK_TAG_MODE: "AND" # AND|OR (if supported) + # HEADLESS_RANDOM_MODE: "0" # 1=force headless random mode instead of scripted build # Entrypoint knobs (only if you change the entrypoint behavior) # APP_MODE: "web" # web|cli — selects uvicorn vs CLI diff --git a/dockerhub-docker-compose.yml b/dockerhub-docker-compose.yml index e896512..dad58ff 100644 --- a/dockerhub-docker-compose.yml +++ b/dockerhub-docker-compose.yml @@ -42,6 +42,18 @@ services: RANDOM_UI: "0" # 1=UI Surprise/Reroll controls RANDOM_MAX_ATTEMPTS: "5" # Retry cap for constrained random builds RANDOM_TIMEOUT_MS: "5000" # Per-attempt timeout (ms) + RANDOM_THEME: "" # optional legacy theme alias (maps to primary theme) + RANDOM_PRIMARY_THEME: "" # optional primary theme slug override + RANDOM_SECONDARY_THEME: "" # optional secondary theme slug override + RANDOM_TERTIARY_THEME: "" # optional tertiary theme slug override + RANDOM_AUTO_FILL: "0" # when 1, auto-fill missing secondary/tertiary themes + RANDOM_AUTO_FILL_SECONDARY: "" # override secondary auto-fill (blank inherits from RANDOM_AUTO_FILL) + RANDOM_AUTO_FILL_TERTIARY: "" # override tertiary auto-fill (blank inherits from RANDOM_AUTO_FILL) + RANDOM_STRICT_THEME_MATCH: "0" # require strict theme matches when 1 + RANDOM_CONSTRAINTS: "" # inline JSON constraints for random builds (optional) + RANDOM_CONSTRAINTS_PATH: "" # path to JSON constraints file (takes precedence) + RANDOM_SEED: "" # deterministic random seed (int or string) + RANDOM_OUTPUT_JSON: "" # path or directory for random build payload export # Theming THEME: "system" # system|light|dark default theme @@ -61,7 +73,7 @@ services: # WEB_THEME_FILTER_PREWARM: "0" WEB_AUTO_ENFORCE: "0" # 1=auto compliance JSON export after builds WEB_CUSTOM_EXPORT_BASE: "" # Optional export base override - APP_VERSION: "v2.2.10" # Displayed in footer/health + APP_VERSION: "v2.3.1" # Displayed in footer/health # ------------------------------------------------------------------ # Misc Land Selection Tuning (Step 7) @@ -112,6 +124,7 @@ services: # DECK_TRIPLE_COUNT: "" # DECK_UTILITY_COUNT: "" # DECK_TAG_MODE: "AND" + # HEADLESS_RANDOM_MODE: "0" # 1=force headless random mode instead of scripted build # ------------------------------------------------------------------ # Entrypoint / Server knobs @@ -158,7 +171,7 @@ services: # THEME_PREVIEW_TTL_STEPS: "2,4,2,3,1" # step counts for band progression # Redis backend (optional) # THEME_PREVIEW_REDIS_URL: "redis://redis:6379/0" - # THEME_PREVIEW_REDIS_DISABLE: "0" # 1=force disable redis even if URL is set + # THEME_PREVIEW_REDIS_DISABLE: "0" # 1=force disable redis even if URL is set volumes: - ${PWD}/deck_files:/app/deck_files - ${PWD}/logs:/app/logs diff --git a/pyproject.toml b/pyproject.toml index 5702970..4698b8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta" [project] name = "mtg-deckbuilder" -version = "2.3.0" +version = "2.3.1" description = "A command-line tool for building and analyzing Magic: The Gathering decks" readme = "README.md" license = {file = "LICENSE"}