From b16b7ce21a4ba3497ba93df775be9db5e3aac62b Mon Sep 17 00:00:00 2001 From: Eric Date: Sun, 2 Oct 2022 16:40:13 -0500 Subject: [PATCH] Add basic AI logic for cards that reduce costs for spells of a chosen card type. --- .../src/main/java/forge/ai/ComputerUtil.java | 21 ++++++++++- .../main/java/forge/ai/ComputerUtilCard.java | 35 +++++++++++++++++++ .../java/forge/ai/PlayerControllerAi.java | 4 +-- forge-gui/res/cardsfolder/c/cloud_key.txt | 1 - .../cardsfolder/s/stenn_paranoid_partisan.txt | 1 - .../res/cardsfolder/u/umori_the_collector.txt | 2 +- 6 files changed, 58 insertions(+), 6 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 304c825596c..4142cce621d 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -2309,7 +2309,9 @@ public class ComputerUtil { return getCardsToDiscardFromOpponent(aiChooser, p, sa, validCards, min, max); } - public static String chooseSomeType(Player ai, String kindOfType, String logic, Collection validTypes, List invalidTypes) { + public static String chooseSomeType(Player ai, String kindOfType, SpellAbility sa, Collection validTypes, List invalidTypes) { + final String logic = sa.getParam("AILogic"); + if (invalidTypes == null) { invalidTypes = ImmutableList.of(); } @@ -2336,6 +2338,23 @@ public class ComputerUtil { } } } + else { + // Are we picking a type to reduce costs for that type? + boolean reducingCost = false; + for (StaticAbility s : sa.getHostCard().getStaticAbilities()) { + if ("ReduceCost".equals(s.getParam("Mode")) && "Card.ChosenType".equals(s.getParam("ValidCard"))) { + reducingCost = true; + break; + } + } + + if (reducingCost) { + List valid = Lists.newArrayList(validTypes); + valid.removeAll(invalidTypes); + valid.remove("Land"); // Lands don't have costs to reduce + chosen = ComputerUtilCard.getMostProminentCardType(ai.getAllCards(), valid); + } + } if (StringUtils.isEmpty(chosen)) { chosen = validTypes.isEmpty() ? "Creature" : Aggregates.random(validTypes); } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index a01c635765a..2cb9a8cc4a3 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -864,6 +864,41 @@ public class ComputerUtilCard { return maxType; } + public static String getMostProminentCardType(final CardCollectionView list, final Collection valid) { + if (list.size() == 0 || valid.isEmpty()) { + return ""; + } + + final Map typesInDeck = Maps.newHashMap(); + for (String type : valid) { + typesInDeck.put(type, 0); + } + + for (final Card c : list) { + Iterable cardTypes = c.getType().getCoreTypes(); + for (CardType.CoreType type : cardTypes) { + Integer count = typesInDeck.get(type.toString()); + if (count != null) { + typesInDeck.put(type.toString(), count + 1); + } + } + } + + int max = 0; + String maxType = ""; + + for (final Entry entry : typesInDeck.entrySet()) { + final String type = entry.getKey(); + + if (max < entry.getValue()) { + max = entry.getValue(); + maxType = type; + } + } + + return maxType; + } + /** *

* getMostProminentColor. diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index e02372f9217..8c0a4d95bc3 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -577,10 +577,10 @@ public class PlayerControllerAi extends PlayerController { @Override public String chooseSomeType(String kindOfType, SpellAbility sa, Collection validTypes, List invalidTypes, boolean isOptional) { - String chosen = ComputerUtil.chooseSomeType(player, kindOfType, sa.getParam("AILogic"), validTypes, invalidTypes); + String chosen = ComputerUtil.chooseSomeType(player, kindOfType, sa, validTypes, invalidTypes); if (StringUtils.isBlank(chosen) && !validTypes.isEmpty()) { chosen = validTypes.iterator().next(); - System.err.println("AI has no idea how to choose " + kindOfType +", defaulting to arbitrary element: chosen"); + System.err.println("AI has no idea how to choose " + kindOfType +", defaulting to arbitrary element: " + chosen); } return chosen; } diff --git a/forge-gui/res/cardsfolder/c/cloud_key.txt b/forge-gui/res/cardsfolder/c/cloud_key.txt index 4de989eac0f..77b84b7b11c 100644 --- a/forge-gui/res/cardsfolder/c/cloud_key.txt +++ b/forge-gui/res/cardsfolder/c/cloud_key.txt @@ -4,5 +4,4 @@ Types:Artifact S:Mode$ ReduceCost | ValidCard$ Card.ChosenType | Type$ Spell | Activator$ You | Amount$ 1 | Description$ Spells you cast of the chosen type cost {1} less to cast. K:ETBReplacement:Other:ChooseCT SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Card | ValidTypes$ Artifact,Creature,Enchantment,Instant,Sorcery | SpellDescription$ As CARDNAME enters the battlefield, choose artifact, creature, enchantment, instant, or sorcery. -AI:RemoveDeck:All Oracle:As Cloud Key enters the battlefield, choose artifact, creature, enchantment, instant, or sorcery.\nSpells you cast of the chosen type cost {1} less to cast. diff --git a/forge-gui/res/cardsfolder/s/stenn_paranoid_partisan.txt b/forge-gui/res/cardsfolder/s/stenn_paranoid_partisan.txt index 3919f178e1e..f6f3f0378f2 100644 --- a/forge-gui/res/cardsfolder/s/stenn_paranoid_partisan.txt +++ b/forge-gui/res/cardsfolder/s/stenn_paranoid_partisan.txt @@ -9,5 +9,4 @@ A:AB$ ChangeZone | Cost$ 1 W U | Origin$ Battlefield | Destination$ Exile | SubA SVar:DelTrig:DB$ DelayedTrigger | Mode$ Phase | RememberObjects$ Remembered | Phase$ End of Turn | Execute$ TrigReturn | TriggerDescription$ Return it to the battlefield under its owner's control at the beginning of the next end step. | SubAbility$ DBCleanup SVar:TrigReturn:DB$ ChangeZone | Defined$ DelayTriggerRememberedLKI | Origin$ Exile | Destination$ Battlefield SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -AI:RemoveDeck:All Oracle:As Stenn, Paranoid Partisan enters the battlefield, choose a card type other than creature or land.\nSpells you cast of the chosen type cost {1} less to cast.\n{1}{W}{U}: Exile Stenn. Return it to the battlefield under its owner's control at the beginning of the next end step. diff --git a/forge-gui/res/cardsfolder/u/umori_the_collector.txt b/forge-gui/res/cardsfolder/u/umori_the_collector.txt index 5713f46ec75..e96860ef379 100644 --- a/forge-gui/res/cardsfolder/u/umori_the_collector.txt +++ b/forge-gui/res/cardsfolder/u/umori_the_collector.txt @@ -4,6 +4,6 @@ Types:Legendary Creature Ooze PT:4/5 K:Companion:Special:SharesCardType:Each nonland card in your starting deck shares a card type. K:ETBReplacement:Other:ChooseCT -SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Card | AILogic$ MostProminentInComputerDeck | SpellDescription$ As CARDNAME enters the battlefield, choose a card type. +SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Card | SpellDescription$ As CARDNAME enters the battlefield, choose a card type. S:Mode$ ReduceCost | ValidCard$ Card.ChosenType | Type$ Spell | Activator$ You | Amount$ 1 | Description$ Spells you cast of the chosen type cost {1} less to cast. Oracle:Companion — Each nonland card in your starting deck shares a card type. (If this card is your chosen companion, you may put it into your hand from outside the game for {3} any time you could cast a sorcery.)\nAs Umori, the Collector enters the battlefield, choose a card type.\nSpells you cast of the chosen type cost {1} less to cast.