From ebd3c330518c78f8ea5b04be79cd389a6bf4f4b9 Mon Sep 17 00:00:00 2001 From: Agetian Date: Sun, 17 Sep 2017 19:50:03 +0000 Subject: [PATCH] - Added some simple SVar-based prediction of Reanimator decks, currently used by the Survival of the Fittest AI code. - Added a worlds.txt entry for Kamigawa quest world. --- .../src/main/java/forge/ai/ComputerUtil.java | 17 +++++++++++++++++ .../src/main/java/forge/ai/SpecialCardAi.java | 14 ++++++++++++++ forge-gui/res/cardsfolder/a/all_hallows_eve.txt | 1 + forge-gui/res/cardsfolder/l/living_death.txt | 1 + forge-gui/res/cardsfolder/t/twilights_call.txt | 1 + 5 files changed, 34 insertions(+) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index b3654595dff..7c89cf5bd45 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -2736,4 +2736,21 @@ public class ComputerUtil { return count; } + + public static boolean isPlayingReanimator(final Player ai) { + CardCollectionView inHand = ai.getCardsIn(ZoneType.Hand); + CardCollectionView inDeck = ai.getCardsIn(new ZoneType[] {ZoneType.Hand, ZoneType.Library}); + + Predicate markedAsReanimator = new Predicate() { + @Override + public boolean apply(Card card) { + return "true".equalsIgnoreCase(card.getSVar("IsReanimatorCard")); + } + }; + + int numInHand = CardLists.filter(inHand, markedAsReanimator).size(); + int numInDeck = CardLists.filter(inDeck, markedAsReanimator).size(); + + return numInHand > 0 || numInDeck >= 3; + } } diff --git a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java index 05a649f7842..f39776cf885 100644 --- a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java +++ b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java @@ -889,6 +889,13 @@ public class SpecialCardAi { if (maxCMC != null && maxCMC.getCMC() < bestInLib.getCMC() && bestInLib.getCMC() >= 3) { return maxCMC; } + // We appear to be playing Reanimator (or we have a reanimator card in hand already), so it's + // worth to fill the graveyard now + if (ComputerUtil.isPlayingReanimator(ai)) { + CardCollection creatsInLibByCMC = new CardCollection(creatsInLib); + Collections.sort(creatsInLibByCMC, CardLists.CmcComparatorInv); + return creatsInLibByCMC.getFirst(); + } // probably nothing that is worth changing, so bail return null; @@ -915,6 +922,13 @@ public class SpecialCardAi { Card bestInLib = atTargetCMCInLib != null ? atTargetCMCInLib.getFirst() : null; + if (bestInLib == null && ComputerUtil.isPlayingReanimator(ai)) { + // For Reanimator, we don't mind grabbing the biggest thing possible to recycle it again with SotF later. + CardCollection creatsInLibByCMC = new CardCollection(creatsInLib); + Collections.sort(creatsInLibByCMC, CardLists.CmcComparatorInv); + return creatsInLibByCMC.getFirst(); + } + return bestInLib; } } diff --git a/forge-gui/res/cardsfolder/a/all_hallows_eve.txt b/forge-gui/res/cardsfolder/a/all_hallows_eve.txt index 29bed1d35c8..937cc32a473 100644 --- a/forge-gui/res/cardsfolder/a/all_hallows_eve.txt +++ b/forge-gui/res/cardsfolder/a/all_hallows_eve.txt @@ -9,4 +9,5 @@ SVar:TrigMoveToGraveyard:AB$ ChangeZone | Cost$ 0 | Origin$ Exile | Destination$ SVar:DBResurrection:DB$ ChangeZoneAll | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Creature SVar:DBPutCounter:DB$ PutCounter | Defined$ Remembered | CounterType$ SCREAM | CounterNum$ 2 SVar:Picture:http://www.wizards.com/global/images/magic/general/all_hallows_eve.jpg +SVar:IsReanimatorCard:TRUE Oracle:Exile All Hallow's Eve with two scream counters on it.\nAt the beginning of your upkeep, if All Hallow's Eve is exiled with a scream counter on it, remove a scream counter from it. If there are no more scream counters on it, put it into your graveyard and each player returns all creature cards from his or her graveyard to the battlefield. diff --git a/forge-gui/res/cardsfolder/l/living_death.txt b/forge-gui/res/cardsfolder/l/living_death.txt index 6155c23f15d..1bdd1758680 100644 --- a/forge-gui/res/cardsfolder/l/living_death.txt +++ b/forge-gui/res/cardsfolder/l/living_death.txt @@ -6,5 +6,6 @@ SVar:DBSacrifice:DB$SacrificeAll | ValidCards$ Creature | SubAbility$ DBReturn SVar:DBReturn:DB$ChangeZone | Defined$ Remembered | Origin$ Exile | Destination$ Battlefield | SubAbility$ DBCleanup SVar:DBCleanup:DB$Cleanup | ClearRemembered$ True SVar:RemRandomDeck:True +SVar:IsReanimatorCard:TRUE SVar:Picture:http://www.wizards.com/global/images/magic/general/living_death.jpg Oracle:Each player exiles all creature cards from his or her graveyard, then sacrifices all creatures he or she controls, then puts all cards he or she exiled this way onto the battlefield. diff --git a/forge-gui/res/cardsfolder/t/twilights_call.txt b/forge-gui/res/cardsfolder/t/twilights_call.txt index f203d51bfa7..874cd0e7c71 100644 --- a/forge-gui/res/cardsfolder/t/twilights_call.txt +++ b/forge-gui/res/cardsfolder/t/twilights_call.txt @@ -3,5 +3,6 @@ ManaCost:4 B B Types:Sorcery K:You may cast CARDNAME as though it had flash if you pay {2} more to cast it. A:SP$ ChangeZoneAll | Cost$ 4 B B | Origin$ Graveyard | Destination$ Battlefield | ChangeType$ Creature | SpellDescription$ Each player returns all creature cards from his or her graveyard to the battlefield. +SVar:IsReanimatorCard:TRUE SVar:Picture:http://www.wizards.com/global/images/magic/general/twilights_call.jpg Oracle:You may cast Twilight's Call as though it had flash if you pay {2} more to cast it. (You may cast it any time you could cast an instant.)\nEach player returns all creature cards from his or her graveyard to the battlefield.