After the hard filters narrow the candidate pool to cards that are cheaper, color-legal, and share a core type with the original, every surviving candidate receives a numerical similarity score. The scoring system uses explicit rules derived directly from Scryfall’s structured card data — keyword arrays, tokenized oracle text, converted mana cost, and EDHREC rank — rather than a machine-learning model. This keeps the tool fully deterministic, reproducible without a GPU, and easy to reason about when a suggestion looks surprising.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/invvd/mtg-cheaper-deck/llms.txt
Use this file to discover all available pages before exploring further.
The Scoring Formula
Each candidate is evaluated against the original card using four weighted terms:Score Interpretation
| Score | Meaning |
|---|---|
| 3.0 or higher | Strong match — shares real mechanical function |
| 1.5 to 3.0 | Moderate match — some overlap, not a clone |
| Below 1.5 | Weak match — mainly same type/color, popular on EDHREC |
Term 1: Keyword Similarity (weight 3.0)
Keyword similarity is the Jaccard similarity of the two cards’ Scryfallkeywords arrays — the structured list of evergreen and deciduous mechanics such as Flying, Trample, Lifelink, Deathtouch, and Haste.
Many synergy-driven archetypes — aristocrats, group slug, stax, control — have zero evergreen keywords. Their entire function lives in free-form oracle text. If keyword similarity were the only signal, the scorer would default to recommending generic staples that happen to share a type and a Scryfall keyword with the original, rather than cards that actually do the same job. The oracle text overlap term (Term 2) exists specifically to handle these cases.
Term 2: Oracle Text Overlap (weight 2.5)
Text overlap is the Jaccard similarity of the two cards’ tokenized oracle texts:- Lowercase the entire text.
- Strip reminder text in parentheses using the regex
\([^)]*\). - Extract all word tokens matching
[a-z']+. - Remove stopwords and words of two characters or fewer.
sacrifice, token, counter, draw, damage, graveyard, exile, library, and specific keyword actions (proliferate, convoke, cascade). Two cards that share a dense set of such terms are functionally related even if they share no evergreen keywords.
These token sets are pre-computed for every card in the CandidatePool at load time, so the Jaccard calculation during scoring is a set intersection on already-prepared data.
Term 3: CMC Proximity (weight 1.0)
CMC proximity measures how close the candidate’s converted mana cost is to the original’s:| CMC difference | Score |
|---|---|
| 0 | 1.000 |
| 1 | 0.500 |
| 2 | 0.333 |
| 3 | 0.250 |
Term 4: EDHREC Popularity (weight 1.0)
EDHREC popularity acts as a tiebreaker and a gentle quality signal — a well-known, widely-played card is more likely to be a good suggestion than an obscure one with similar text:Hard Filters Applied Before Scoring
Three filters are applied to the candidate pool before any scoring takes place. A candidate that fails any filter is excluded entirely — it never receives a score.- Price —
candidate.price_usd < original.price_usd(strictly cheaper; equal price is not accepted). - Color identity —
candidate.color_identity_mask & disallowed_mask == 0(the candidate’s color identity must be a subset of the original’s; see the bitmask section below). - Core type overlap —
extract_core_types(candidate) ∩ extract_core_types(original) ≠ ∅(the candidate must share at least one core card type with the original).
Core Types Recognized
Type overlap is determined by extracting words before the— separator in type_line that belong to the CORE_TYPES set:
"Legendary Creature — Elf Druid" yields {"Creature"}, while "Artifact Creature — Construct" yields {"Artifact", "Creature"}. A candidate Artifact Creature would pass the type filter for an original that is a plain Creature, because the intersection is non-empty.
Color Identity Bitmask
Color identity filtering uses a precomputed integer bitmask stored in thecolor_identity_mask column during Scryfall sync. Each color maps to a power of two:
| Color | Bit value |
|---|---|
| W (White) | 1 |
| U (Blue) | 2 |
| B (Black) | 4 |
| R (Red) | 8 |
| G (Green) | 16 |
6 (2 | 4); a Bant (WUG) card has mask 19 (1 | 2 | 16); a colorless card has mask 0.
The filter computes the disallowed mask as the complement of the original’s mask within the five-color space, then checks that the candidate uses none of those disallowed colors: