Rework deckbuilding rules into a set of objects
- Dominant language
- Java
- Stars
- 2.7k
- Forks
- 1.1k
- Avg merge
- 1d 1h
- Merged PRs (30d)
- 158
Description
The logic for if a deck or card is legal is cumbersome to apply with flexibility. Most of it is wrapped up in huge methods that test many rules at once and return a single string describing the first violation, or null if there isn't one.
I suggest we redefine deck rules as objects, with methods for testing themselves against a deck list. Each would need, at minimum:
* `boolean isValid(Deck)` - Checks a deck and returns false if the rule is violated.
* `String getDescription()` - Gives a user-facing string describing this rule generally.
But we could also expand their functionality by letting them be more specific, with methods such as:
* `List getErrors(Deck)` - Returns a list of user-facing error messages describing each violation of this rule. Might come with a helper method `getError` that returns them all concatenated together.
* `Map getViolations(Deck)` - Returns a list of cards that are in violation of this rule. Could be used to do things like highlighting problem cards in a deck editor. Some rules might be violated with this being empty, if a deck requires something that is not there, for instance.
This system would want to account all deckbuilding rules, across all supported formats. Rules would need to be general enough to be flexible but specific enough to be useful. We might use multiple classes, with configurations and exclusions depending on game mode:
* `DeckRuleSize` - A rule that specifies that the total number of cards in one or more sections must fall in a certain range (or be zero, if the section is optional).
* `DeckRuleCopies` - A rule that specifies the upper limit of copies of each card across the whole deck. Maintains an ordered map of predicates (e.g. isBasicLand, isPhenomenon) to quantities. Cards can be tested against these predicates and use the quantity of the first one that matches.
* `DeckRuleCommander` - A rule that specifies the default criteria for a commander, and possibly also a signature spell.
* `DeckRuleColorIdentity` - Rule that all cards in the main deck and sideboard must fall within the color identity of the cards in the command zone.
* `DeckRuleTypes` - Specifies that all cards in the deck or in a given section must or must not fall within a given list of types.
* `DeckRuleVariants` - Forbids nontraditional cards in the main, side, and commander sections.
* `DeckRuleFormatPool` - A rule that forbids cards outside the format's pool, as specified by a list of editions, edition types, and/or individual cards to allow or deny.
* `DeckRuleFormatRestrictions` - Works similar to the above, but is defined by a list of individual cards or predicates that are either banned or limited to a certain quantity.
These could be derived from both the deck format and the game format, each able to generate a list of rule objects to apply when checking decks. Those could cover most format's decks rules work by default, but we also have individual cards that change the deck rules of the decks they're in:
* Cards which allow any number of copies.
* Cards which allow a specific number of copies.
* Cards that allow themselves to be your commander.
* "Partner with", which pairs commanders with specific other cards.
* "Partner" abilities, which pair with other cards that have the same keyword.
* Backgrounds, which cannot be a commander normally but can be a partner to one.
* Abilities like "Doctor's Companion", a one-sided partner ability that pairs based on the other card's characteristics.
* Advantageous Proclamation, which adjusts your minimum deck size.
* Sovereign's Realm, which forbids basic lands.
If we want to get exotic, we might also consider non-legal cards:
* Gleemox, which bans itself from all formats.
* Wizard from Beyond, and several others that allow cards that can't normally be commanders at all to partner with them.
* The rulebreaker mechanic, which lets you break color identity rules when using one as a commander.
Right now, these are mostly supported by hard-coding them all into various parts of the project. By splitting out rules into classes organized by purpose, though, we'd open up a new option - script syntax for interfacing with the deckbuilding rules:
```
# Advantageous Proclamation
DeckRule:Size:AdjustMin$ -5 | Cumulative$ True | ActiveSection$ Conspiracy | Description$ Your minimum deck size is reduced by five.
# Seven Dwarves
DeckRule:Copies:Limit$ 7 | Affected$ Name:CARDNAME | Description$ A deck can have up to seven cards named CARDNAME.
# Relentless Rats
DeckRule:Copies:Limit$ Unlimited | Affected$ Name:CARDNAME | Description$ A deck can have any number of cards named CARDNAME.
# Amy Pond
# Keywords for "Partner with" and "Doctor's Companion" would expand out to the following:
DeckRule:Commander:Partner$ Name:Rory Williams | ActiveSection$ Commander
DeckRule:Commander:Partner$ Type:Legendary+Creature+Time Lord+Doctor | ExactMatchType$ True | ActiveSection$ Commander | Description$ You can have two commanders if the other is the Doctor.
#(Plus the ETB effect for Partner with.)
# Tevesh Szat, Doom of Fools
DeckRule:Commander:Allow$ Name:CARDNAME | Description$ CARDNAME can be your commander.
DeckRule:Commander:Partner$ kw:Partner | You can have two commanders if both have partner.
# Sovereign's Realm
DeckRule:Types:Forbid$ Basic+Land | AffectedSection$ Main | ActiveSection$ Conspiracy | Description$ Your starting deck can’t have basic land cards.
# Gleemox
DeckRule:FormatRestrictions:Affected$ Name:CARDNAME | Banned$ True | Description$ This card is banned.
# The Knight of Land Drops (Partner with type keyword variant)
DeckRule:Commander:Partner$ Type:Legendary+Knight | Description$ You may select any legendary Knight to be this creature’s partner.
# Mothers Yamazaki
DeckRule:Commander:Partner$ Name:Mothers Yamazaki
DeckRule:Copies:Limit$ 2 | Affected$ Name:CARDNAME | ConditionFormat$ Commander | Description$ A Commander deck can include two of this card, and they can be your commanders.
# Arvad of the Weatherlight
DeckRule:ColorIdentity:Exempt$ Type:Permanent.Legendary | ActiveSection$ Commander | Description$ If CARDNAME is your Commander, you may include legendary permanents of any color in your deck regardless of color identity.
# The Paradise Bird
DeckRule:ColorIdentity:Disable$ True | ActiveSection$ Commander | Description$ If CARDNAME is your commander, your deck can include cards of any color identity.
```
(None of those identifiers or that syntax is final)
DeckRule modifiers get checked by their respective rule when evaluating the deck, and can alter the default behavior of those rules using parameters similar to ability APIs. Most parameters would be specific to the rule in question, though some such as `ActiveSection$` (the modifier is only applicable if the card is in the listed section) could apply to all of them.
Contributor guide
Assessment
This issue has not been assessed yet.