From 1b1a56e77c633c5c8696aef94f2c2950b262ec43 Mon Sep 17 00:00:00 2001 From: Agetian Date: Sat, 23 Sep 2017 09:04:16 +0000 Subject: [PATCH] - Improved support for Illusions-Donate, added deck The Great and Powerful Trixie 2, changed the deck The Great and Powerful Trixie 3 to be a more standard Legacy-legal Trix. --- .gitattributes | 1 + .../src/main/java/forge/ai/AiController.java | 27 ++++++++++++++--- forge-ai/src/main/java/forge/ai/AiProps.java | 5 ++-- .../main/java/forge/ai/ComputerUtilCard.java | 29 ++++++++++++++++++- .../src/main/java/forge/ai/SpecialCardAi.java | 8 ++--- forge-gui/res/ai/Cautious.ai | 6 ++++ forge-gui/res/ai/Default.ai | 5 ++++ forge-gui/res/ai/Experimental.ai | 11 ++++--- forge-gui/res/ai/Reckless.ai | 6 ++++ forge-gui/res/cardsfolder/d/donate.txt | 1 + .../cardsfolder/i/illusions_of_grandeur.txt | 5 ++-- .../duels/The Great and Powerful Trixie 2.dck | 27 +++++++++++++++++ .../duels/The Great and Powerful Trixie 3.dck | 26 +++++++---------- .../duels/The Great and Powerful Trixie 4.dck | 1 + 14 files changed, 125 insertions(+), 33 deletions(-) create mode 100644 forge-gui/res/quest/duels/The Great and Powerful Trixie 2.dck diff --git a/.gitattributes b/.gitattributes index 894a5261320..118970a4208 100644 --- a/.gitattributes +++ b/.gitattributes @@ -20368,6 +20368,7 @@ forge-gui/res/quest/duels/Terminator[!!-~]3.dck -text forge-gui/res/quest/duels/Thanos[!!-~]3.dck -text forge-gui/res/quest/duels/The[!!-~]Great[!!-~]Gatsby[!!-~]1.dck -text forge-gui/res/quest/duels/The[!!-~]Great[!!-~]Gazoo[!!-~]3.dck -text +forge-gui/res/quest/duels/The[!!-~]Great[!!-~]and[!!-~]Powerful[!!-~]Trixie[!!-~]2.dck -text forge-gui/res/quest/duels/The[!!-~]Great[!!-~]and[!!-~]Powerful[!!-~]Trixie[!!-~]3.dck -text forge-gui/res/quest/duels/The[!!-~]Great[!!-~]and[!!-~]Powerful[!!-~]Trixie[!!-~]4.dck -text forge-gui/res/quest/duels/The[!!-~]Nac[!!-~]Mac[!!-~]Feegle[!!-~]3.dck -text diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 3166a27d9b8..64218470a1d 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -881,6 +881,9 @@ public class AiController { } if (prefCard == null) { prefCard = ComputerUtil.getCardPreference(player, sourceCard, "DiscardCost", validCards); + if (prefCard != null && prefCard.hasSVar("DoNotDiscardIfAble")) { + prefCard = null; + } } if (prefCard != null) { discardList.add(prefCard); @@ -917,13 +920,29 @@ public class AiController { if (numLandsInHand > 0) { numLandsAvailable++; } + //Discard unplayable card - if (validCards.get(0).getCMC() > numLandsAvailable) { - discardList.add(validCards.get(0)); - validCards.remove(validCards.get(0)); + boolean discardedUnplayable = false; + for (int j = 0; j < validCards.size(); j++) { + if (validCards.get(j).getCMC() > numLandsAvailable && !validCards.get(j).hasSVar("DoNotDiscardIfAble")) { + discardList.add(validCards.get(j)); + validCards.remove(validCards.get(j)); + discardedUnplayable = true; + break; + } else if (validCards.get(j).getCMC() <= numLandsAvailable) { + // cut short to avoid looping over cards which are guaranteed not to fit the criteria + break; + } } - else { //Discard worst card + + if (!discardedUnplayable) { + // discard worst card Card worst = ComputerUtilCard.getWorstAI(validCards); + if (worst == null) { + // there were only instants and sorceries, and maybe cards that are not good to discard, so look + // for more discard options + worst = ComputerUtilCard.getCheapestSpellAI(validCards); + } discardList.add(worst); validCards.remove(worst); } diff --git a/forge-ai/src/main/java/forge/ai/AiProps.java b/forge-ai/src/main/java/forge/ai/AiProps.java index ef496d59881..1d560a3023f 100644 --- a/forge-ai/src/main/java/forge/ai/AiProps.java +++ b/forge-ai/src/main/java/forge/ai/AiProps.java @@ -94,9 +94,10 @@ public enum AiProps { /** */ BOUNCE_ALL_TO_HAND_CREAT_EVAL_DIFF ("200"), /** */ BOUNCE_ALL_ELSEWHERE_CREAT_EVAL_DIFF ("200"), /** */ BOUNCE_ALL_TO_HAND_NONCREAT_EVAL_DIFF ("3"), /** */ - BOUNCE_ALL_ELSEWHERE_NONCREAT_EVAL_DIFF ("3"), /** */ + BOUNCE_ALL_ELSEWHERE_NONCREAT_EVAL_DIFF ("3"), + INTUITION_ALTERNATIVE_LOGIC ("false"); /** */ // Experimental features, must be removed after extensive testing and, ideally, defaulting - INTUITION_SPECIAL_LOGIC ("false"); /** */ + // <-- There are no experimental options here --> private final String strDefaultVal; diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index 13cd7339108..41358158f72 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -350,7 +350,12 @@ public class ComputerUtilCard { } if (hasEnchantmants || hasArtifacts) { - final List ae = CardLists.filter(list, Predicates.or(CardPredicates.Presets.ARTIFACTS, CardPredicates.Presets.ENCHANTMENTS)); + final List ae = CardLists.filter(list, Predicates.and(Predicates.or(CardPredicates.Presets.ARTIFACTS, CardPredicates.Presets.ENCHANTMENTS), new Predicate() { + @Override + public boolean apply(Card card) { + return !card.hasSVar("DoNotDiscardIfAble"); + } + })); return getCheapestPermanentAI(ae, null, false); } @@ -363,6 +368,28 @@ public class ComputerUtilCard { return getCheapestPermanentAI(list, null, false); } + public static final Card getCheapestSpellAI(final Iterable list) { + if (!Iterables.isEmpty(list)) { + CardCollection cc = CardLists.filter(new CardCollection(list), + Predicates.or(CardPredicates.isType("Instant"), CardPredicates.isType("Sorcery"))); + Collections.sort(cc, CardLists.CmcComparatorInv); + + Card cheapest = cc.getLast(); + if (cheapest.hasSVar("DoNotDiscardIfAble")) { + for (int i = cc.size() - 1; i >= 0; i--) { + if (!cc.get(i).hasSVar("DoNotDiscardIfAble")) { + cheapest = cc.get(i); + break; + } + } + } + + return cheapest; + } + + return null; + } + public static final Comparator EvaluateCreatureComparator = new Comparator() { @Override public int compare(final Card a, final Card b) { diff --git a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java index d0763f3b856..8365da21490 100644 --- a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java +++ b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java @@ -537,7 +537,7 @@ public class SpecialCardAi { public static class Intuition { public static CardCollection considerMultiple(final Player ai, final SpellAbility sa) { if (ai.getController().isAI()) { - if (!((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.INTUITION_SPECIAL_LOGIC)) { + if (!((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.INTUITION_ALTERNATIVE_LOGIC)) { return new CardCollection(); // fall back to standard ChangeZoneAi considerations } } @@ -556,14 +556,14 @@ public class SpecialCardAi { cardAmount.add(c.getName()); } - // Trix: see if we can complete the combo (if it looks like we might win shortly) + // Trix: see if we can complete the combo (if it looks like we might win shortly or if we need to get a Donate stat) boolean donateComboMightWin = false; - if (ai.getOpponentsSmallestLifeTotal() <= 20) { + int numIllusionsOTB = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Illusions of Grandeur")).size(); + if (ai.getOpponentsSmallestLifeTotal() < 20 || numIllusionsOTB > 0) { donateComboMightWin = true; int numIllusionsInHand = CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.nameEquals("Illusions of Grandeur")).size(); int numDonateInHand = CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.nameEquals("Donate")).size(); int numIllusionsInLib = CardLists.filter(ai.getCardsIn(ZoneType.Library), CardPredicates.nameEquals("Illusions of Grandeur")).size(); - int numIllusionsOTB = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Illusions of Grandeur")).size(); int numDonateInLib = CardLists.filter(ai.getCardsIn(ZoneType.Library), CardPredicates.nameEquals("Donate")).size(); CardCollection comboList = new CardCollection(); if ((numIllusionsInHand > 0 || numIllusionsOTB > 0) && numDonateInHand == 0 && numDonateInLib >= 3) { diff --git a/forge-gui/res/ai/Cautious.ai b/forge-gui/res/ai/Cautious.ai index ae44e7ed2e3..836344b6299 100644 --- a/forge-gui/res/ai/Cautious.ai +++ b/forge-gui/res/ai/Cautious.ai @@ -164,3 +164,9 @@ BOUNCE_ALL_ELSEWHERE_CREAT_EVAL_DIFF=200 # on both side of the battlefield BOUNCE_ALL_TO_HAND_NONCREAT_EVAL_DIFF=3 BOUNCE_ALL_ELSEWHERE_NONCREAT_EVAL_DIFF=3 + +# If enabled, the AI will try to pair up cards to present to the opponent so that a specific card may be picked, +# it'll also try to grab Accumulated Knowledge and Take Inventory more actively, as well as interact with the Trix +# combo deck more appropriately. In Reanimator decks, this logic will make the AI pick the fattest threats in the +# library to put some into the graveyard. +INTUITION_ALTERNATIVE_LOGIC=true diff --git a/forge-gui/res/ai/Default.ai b/forge-gui/res/ai/Default.ai index abb89d7d40e..6c221362c56 100644 --- a/forge-gui/res/ai/Default.ai +++ b/forge-gui/res/ai/Default.ai @@ -165,3 +165,8 @@ BOUNCE_ALL_ELSEWHERE_CREAT_EVAL_DIFF=200 BOUNCE_ALL_TO_HAND_NONCREAT_EVAL_DIFF=3 BOUNCE_ALL_ELSEWHERE_NONCREAT_EVAL_DIFF=3 +# If enabled, the AI will try to pair up cards to present to the opponent so that a specific card may be picked, +# it'll also try to grab Accumulated Knowledge and Take Inventory more actively, as well as interact with the Trix +# combo deck more appropriately. In Reanimator decks, this logic will make the AI pick the fattest threats in the +# library to put some into the graveyard. +INTUITION_ALTERNATIVE_LOGIC=true diff --git a/forge-gui/res/ai/Experimental.ai b/forge-gui/res/ai/Experimental.ai index 5de775da20a..0b70a4da6f0 100644 --- a/forge-gui/res/ai/Experimental.ai +++ b/forge-gui/res/ai/Experimental.ai @@ -165,11 +165,14 @@ BOUNCE_ALL_ELSEWHERE_CREAT_EVAL_DIFF=400 BOUNCE_ALL_TO_HAND_NONCREAT_EVAL_DIFF=5 BOUNCE_ALL_ELSEWHERE_NONCREAT_EVAL_DIFF=5 +# If enabled, the AI will try to pair up cards to present to the opponent so that a specific card may be picked, +# it'll also try to grab Accumulated Knowledge and Take Inventory more actively, as well as interact with the Trix +# combo deck more appropriately. In Reanimator decks, this logic will make the AI pick the fattest threats in the +# library to put some into the graveyard. +INTUITION_ALTERNATIVE_LOGIC=true + # -- Experimental feature toggles which only exist until the testing procedure for the relevant -- # -- features is over. These toggles will be removed later, or may be reintroduced under a -- # -- different name if necessary -- -# Experimental logic for Intuition that makes it work better in reanimator decks, combo decks like Trix, -# and generally tries to either grab cards that are also good to put in the graveyard or that are available -# in multiples in the deck. -INTUITION_SPECIAL_LOGIC=true +# <-- there are no options here at the moment --> diff --git a/forge-gui/res/ai/Reckless.ai b/forge-gui/res/ai/Reckless.ai index befdf40f0e4..d7dba4f9336 100644 --- a/forge-gui/res/ai/Reckless.ai +++ b/forge-gui/res/ai/Reckless.ai @@ -164,3 +164,9 @@ BOUNCE_ALL_ELSEWHERE_CREAT_EVAL_DIFF=200 # on both side of the battlefield BOUNCE_ALL_TO_HAND_NONCREAT_EVAL_DIFF=3 BOUNCE_ALL_ELSEWHERE_NONCREAT_EVAL_DIFF=3 + +# If enabled, the AI will try to pair up cards to present to the opponent so that a specific card may be picked, +# it'll also try to grab Accumulated Knowledge and Take Inventory more actively, as well as interact with the Trix +# combo deck more appropriately. In Reanimator decks, this logic will make the AI pick the fattest threats in the +# library to put some into the graveyard. +INTUITION_ALTERNATIVE_LOGIC=true diff --git a/forge-gui/res/cardsfolder/d/donate.txt b/forge-gui/res/cardsfolder/d/donate.txt index 2394b21a495..7ba82d5f6da 100644 --- a/forge-gui/res/cardsfolder/d/donate.txt +++ b/forge-gui/res/cardsfolder/d/donate.txt @@ -7,5 +7,6 @@ SVar:D2:DB$ Pump | ValidTgts$ Permanent.YouCtrl | TgtPrompt$ Select target perma SVar:D3:DB$ GainControl | Defined$ Targeted | NewController$ Remembered | SubAbility$ D4 SVar:D4:DB$ Cleanup | ClearRemembered$ True SVar:RemRandomDeck:True +SVar:DoNotDiscardIfAble:TRUE SVar:Picture:http://www.wizards.com/global/images/magic/general/donate.jpg Oracle:Target player gains control of target permanent you control. diff --git a/forge-gui/res/cardsfolder/i/illusions_of_grandeur.txt b/forge-gui/res/cardsfolder/i/illusions_of_grandeur.txt index b9999bcf4c8..9696ce28978 100644 --- a/forge-gui/res/cardsfolder/i/illusions_of_grandeur.txt +++ b/forge-gui/res/cardsfolder/i/illusions_of_grandeur.txt @@ -4,12 +4,13 @@ Types:Enchantment K:Cumulative upkeep:2 T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigGainLife | TriggerDescription$ When CARDNAME enters the battlefield, you gain 20 life. T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigLoseLife | TriggerController$ TriggeredCardController | TriggerDescription$ When CARDNAME leaves the battlefield, you lose 20 life. -SVar:TrigGainLife:AB$GainLife | Cost$ 0 | Defined$ TriggeredCardController | LifeAmount$ 20 -SVar:TrigLoseLife:AB$LoseLife | Cost$ 0 | Defined$ TriggeredCardController | LifeAmount$ 20 +SVar:TrigGainLife:DB$ GainLife | Defined$ TriggeredCardController | LifeAmount$ 20 +SVar:TrigLoseLife:DB$ LoseLife | Defined$ TriggeredCardController | LifeAmount$ 20 SVar:AICastPreference:MustHaveInHand$ Donate | MaxControlled$ 1 | NumManaSourcesNextTurn$ 5 | AlwaysCastIfLifeBelow$ 4 SVar:RemRandomDeck:True SVar:DeckNeeds:Name$Donate SVar:DonateMe:5 SVar:PlayMain1:TRUE +SVar:DoNotDiscardIfAble:TRUE SVar:Picture:http://www.wizards.com/global/images/magic/general/illusions_of_grandeur.jpg Oracle:Cumulative upkeep {2} (At the beginning of your upkeep, put an age counter on this permanent, then sacrifice it unless you pay its upkeep cost for each age counter on it.)\nWhen Illusions of Grandeur enters the battlefield, you gain 20 life.\nWhen Illusions of Grandeur leaves the battlefield, you lose 20 life. diff --git a/forge-gui/res/quest/duels/The Great and Powerful Trixie 2.dck b/forge-gui/res/quest/duels/The Great and Powerful Trixie 2.dck new file mode 100644 index 00000000000..5d79651e712 --- /dev/null +++ b/forge-gui/res/quest/duels/The Great and Powerful Trixie 2.dck @@ -0,0 +1,27 @@ +[duel] +[metadata] +Name=The Great and Powerful Trixie 2 +Title=The Great and Powerful Trixie +Difficulty=hard +Description=U splash W Trix combo deck with Illusions of Grandeur and Donate +Icon=The Great and Powerful Trixie.jpg +Deck Type=constructed +Profile=Cautious +[main] +3 Arcane Denial +2 Cancel +3 Disdainful Stroke +4 Donate +1 Enlightened Tutor +3 Essence Scatter +4 Glacial Fortress +4 Illusions of Grandeur +14 Island +3 Mana Leak +1 Mystical Tutor +1 Plains +4 Sapphire Medallion +2 Seachrome Coast +3 Unsummon +4 Vapor Snag +4 Wrath of God diff --git a/forge-gui/res/quest/duels/The Great and Powerful Trixie 3.dck b/forge-gui/res/quest/duels/The Great and Powerful Trixie 3.dck index f4c2dd9773e..ceade0b8923 100644 --- a/forge-gui/res/quest/duels/The Great and Powerful Trixie 3.dck +++ b/forge-gui/res/quest/duels/The Great and Powerful Trixie 3.dck @@ -3,25 +3,19 @@ Name=The Great and Powerful Trixie 3 Title=The Great and Powerful Trixie Difficulty=hard -Description=U splash W Trix combo deck with Illusions of Grandeur and Donate +Description=Mono U Trix combo deck with Illusions of Grandeur and Donate Icon=The Great and Powerful Trixie.jpg Deck Type=constructed [main] -1 Ancestral Recall -4 Arcane Denial -1 Black Lotus -4 Counterspell -3 Disdainful Stroke +4 Accumulated Knowledge +4 Capsize 4 Donate -3 Essence Scatter -4 Glacial Fortress +4 Frantic Search +4 Helm of Awakening +2 Hoodwink 4 Illusions of Grandeur -10 Island -1 Mox Pearl -1 Mox Sapphire +4 Intuition +21 Island +1 Merchant Scroll 4 Sapphire Medallion -1 Timetwister -4 Tundra -3 Unsummon -4 Vapor Snag -4 Wrath of God +4 Stroke of Genius diff --git a/forge-gui/res/quest/duels/The Great and Powerful Trixie 4.dck b/forge-gui/res/quest/duels/The Great and Powerful Trixie 4.dck index 33635684ad5..88b93a7164e 100644 --- a/forge-gui/res/quest/duels/The Great and Powerful Trixie 4.dck +++ b/forge-gui/res/quest/duels/The Great and Powerful Trixie 4.dck @@ -6,6 +6,7 @@ Difficulty=very hard Description=UB Necro-Donate Trix combo deck with Necropotence, Illusions of Grandeur and Donate Icon=The Great and Powerful Trixie.jpg Deck Type=constructed +Profile=Cautious [main] 1 Ancestral Recall 3 Arcane Denial