From 5e7442af9df216477f98f9cb51e6bead09ae2068 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. git-svn-id: http://svn.slightlymagic.net/forge/trunk@35719 269b9781-a132-4a9b-9d4e-f004f1b56b58 --- .../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 + 13 files changed, 124 insertions(+), 33 deletions(-) create mode 100644 forge-gui/res/quest/duels/The Great and Powerful Trixie 2.dck diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 3166a27d..64218470 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 ef496d59..1d560a30 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 13cd7339..41358158 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 d0763f3b..8365da21 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 ae44e7ed..836344b6 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 abb89d7d..6c221362 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 5de775da..0b70a4da 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 befdf40f..d7dba4f9 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 2394b21a..7ba82d5f 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 b9999bcf..9696ce28 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 00000000..5d79651e --- /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 f4c2dd97..ceade0b8 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 33635684..88b93a71 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