From 9ee58451b54174675b703b25ff7c52279c944957 Mon Sep 17 00:00:00 2001 From: Agetian Date: Sun, 8 Oct 2017 14:16:41 +0000 Subject: [PATCH] - RemAIDeck update: next iteration (Last Rites, mostly for Reanimator). --- .../src/main/java/forge/ai/AiController.java | 30 +++++++++++++++++ .../main/java/forge/ai/ability/DiscardAi.java | 33 ++++++++++++++++++- forge-gui/res/cardsfolder/l/last_rites.txt | 4 +-- 3 files changed, 64 insertions(+), 3 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 64b2181169e..758cc1688c6 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -865,6 +865,36 @@ public class AiController { } else if ("VolrathsShapeshifter".equals(sa.getParam("AILogic"))) { return SpecialCardAi.VolrathsShapeshifter.targetBestCreature(player, sa); } + + if (sa.hasParam("AnyNumber")) { + if ("DiscardUncastableAndExcess".equals(sa.getParam("AILogic"))) { + // Note that at this point, there is no guarantee that the AI will discard specific cards found here, + // but since the AI generally discards things it doesn't need / can't immediately use, it's a relatively + // safe assumption that the follow-up discard action will be in line with what is considered here. + CardCollection discards = new CardCollection(); + final CardCollectionView inHand = player.getCardsIn(ZoneType.Hand); + final int numLandsOTB = CardLists.filter(player.getCardsIn(ZoneType.Hand), CardPredicates.Presets.LANDS).size(); + int numOppInHand = 0; + for (Player p : player.getGame().getPlayers()) { + if (p.getCardsIn(ZoneType.Hand).size() > numOppInHand) { + numOppInHand = p.getCardsIn(ZoneType.Hand).size(); + } + } + for (Card c : inHand) { + if (c.hasSVar("DoNotDiscardIfAble") || c.hasSVar("IsReanimatorCard")) { continue; } + if (c.isCreature() && !ComputerUtilMana.hasEnoughManaSourcesToCast(c.getSpellPermanent(), player)) { + discards.add(c); + } + if ((c.isLand() && numLandsOTB >= 5) || (c.getFirstSpellAbility() != null && !ComputerUtilMana.hasEnoughManaSourcesToCast(c.getFirstSpellAbility(), player))) { + if (discards.size() + 1 <= numOppInHand) { + discards.add(c); + } + } + } + return discards; + } + } + } // look for good discards diff --git a/forge-ai/src/main/java/forge/ai/ability/DiscardAi.java b/forge-ai/src/main/java/forge/ai/ability/DiscardAi.java index 554e8ba170b..9fb0f69106c 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DiscardAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DiscardAi.java @@ -3,6 +3,9 @@ package forge.ai.ability; import forge.ai.*; import forge.game.ability.AbilityUtils; import forge.game.card.Card; +import forge.game.card.CardCollectionView; +import forge.game.card.CardLists; +import forge.game.card.CardPredicates; import forge.game.cost.Cost; import forge.game.phase.PhaseType; import forge.game.player.Player; @@ -98,7 +101,35 @@ public class DiscardAi extends SpellAbilityAi { } } - // TODO: Implement support for Discard AI for cards with AnyNumber set to true. + // TODO: Improve support for Discard AI for cards with AnyNumber set to true. + if (sa.hasParam("AnyNumber")) { + if ("DiscardUncastableAndExcess".equals(aiLogic)) { + final CardCollectionView inHand = ai.getCardsIn(ZoneType.Hand); + final int numLandsOTB = CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.Presets.LANDS).size(); + int numDiscard = 0; + int numOppInHand = 0; + for (Player p : ai.getGame().getPlayers()) { + if (p.getCardsIn(ZoneType.Hand).size() > numOppInHand) { + numOppInHand = p.getCardsIn(ZoneType.Hand).size(); + } + } + for (Card c : inHand) { + if (c.equals(sa.getHostCard())) { continue; } + if (c.hasSVar("DoNotDiscardIfAble") || c.hasSVar("IsReanimatorCard")) { continue; } + if (c.isCreature() && !ComputerUtilMana.hasEnoughManaSourcesToCast(c.getSpellPermanent(), ai)) { + numDiscard++; + } + if ((c.isLand() && numLandsOTB >= 5) || (c.getFirstSpellAbility() != null && !ComputerUtilMana.hasEnoughManaSourcesToCast(c.getFirstSpellAbility(), ai))) { + if (numDiscard + 1 <= numOppInHand) { + numDiscard++; + } + } + } + if (numDiscard == 0) { + return false; + } + } + } // Don't use draw abilities before main 2 if possible if (ai.getGame().getPhaseHandler().getPhase().isBefore(PhaseType.MAIN2) diff --git a/forge-gui/res/cardsfolder/l/last_rites.txt b/forge-gui/res/cardsfolder/l/last_rites.txt index 548fa591407..38df1841f48 100644 --- a/forge-gui/res/cardsfolder/l/last_rites.txt +++ b/forge-gui/res/cardsfolder/l/last_rites.txt @@ -1,10 +1,10 @@ Name:Last Rites ManaCost:2 B Types:Sorcery -A:SP$ Discard | Cost$ 2 B | AnyNumber$ True | Optional$ True | Mode$ TgtChoose | RememberDiscarded$ True | SubAbility$ DBLastRitesDiscard | SpellDescription$ Discard any number of cards. Target player reveals his or her hand, then you choose a nonland card from it for each card discarded this way. That player discards those cards. +A:SP$ Discard | Cost$ 2 B | AnyNumber$ True | Optional$ True | Mode$ TgtChoose | RememberDiscarded$ True | SubAbility$ DBLastRitesDiscard | AILogic$ DiscardUncastableAndExcess | SpellDescription$ Discard any number of cards. Target player reveals his or her hand, then you choose a nonland card from it for each card discarded this way. That player discards those cards. SVar:DBLastRitesDiscard:DB$ Discard | Mode$ RevealYouChoose | NumCards$ X | DiscardValid$ Card.nonLand | ValidTgts$ Opponent | References$ X SVar:DBLastRitesCleanup:DB$ Cleanup | ClearRemembered$ True SVar:X:Remembered$Amount -SVar:RemAIDeck:True +SVar:RemRandomDeck:True SVar:Picture:http://www.wizards.com/global/images/magic/general/last_rites.jpg Oracle:Discard any number of cards. Target player reveals his or her hand, then you choose a nonland card from it for each card discarded this way. That player discards those cards.