From 3309a48710d3fad8b7ca7615a740edcbb4f70701 Mon Sep 17 00:00:00 2001 From: Hans Mackowiak Date: Thu, 16 May 2024 19:22:57 +0200 Subject: [PATCH] AI: Basic Logic for Destroy RE --- .../src/main/java/forge/ai/AiController.java | 28 +++++++++++++++++++ .../java/forge/ai/PlayerControllerAi.java | 2 +- .../src/main/java/forge/game/card/Card.java | 2 +- .../forge/game/player/PlayerController.java | 2 +- .../game/replacement/ReplacementHandler.java | 4 +-- .../util/PlayerControllerForTests.java | 2 +- .../forge/player/PlayerControllerHuman.java | 3 +- 7 files changed, 36 insertions(+), 7 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index f3185d80df9..f756401eb97 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -70,6 +70,7 @@ import io.sentry.Breadcrumb; import io.sentry.Sentry; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -2293,6 +2294,33 @@ public class AiController { if (!prevention.isEmpty()) { return Iterables.getFirst(prevention, null); } + } else if (mode.equals(ReplacementType.Destroy)) { + List shield = filterList(list, CardTraitPredicates.hasParam("ShieldCounter")); + List regeneration = filterList(list, CardTraitPredicates.hasParam("Regeneration")); + List umbraArmor = filterList(list, CardTraitPredicates.isKeyword(Keyword.UMBRA_ARMOR)); + List umbraArmorIndestructible = filterList(umbraArmor, Predicates.compose(CardPredicates.hasKeyword(Keyword.INDESTRUCTIBLE), CardTraitBase::getHostCard)); + + // Indestructible umbra armor is the best + if (!umbraArmorIndestructible.isEmpty()) { + return Iterables.getFirst(umbraArmorIndestructible, null); + } + + // then it might be better to remove shield counter if able? + if (!shield.isEmpty()) { + return Iterables.getFirst(shield, null); + } + + // TODO get the RunParams for Affected to check if the creature already dealt combat damage for Regeneration effects + // is using a Regeneration Effect better than using a Umbra Armor? + if (!regeneration.isEmpty()) { + return Iterables.getFirst(regeneration, null); + } + + if (!umbraArmor.isEmpty()) { + // sort them by cmc + umbraArmor.sort(Comparator.comparing(CardTraitBase::getHostCard, Comparator.comparing(Card::getCMC))); + return Iterables.getFirst(umbraArmor, null); + } } // TODO always lower counters with Vorinclex first, might turn it from 1 to 0 as final diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 5eeefb0243b..d96e26b7b9a 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -1130,7 +1130,7 @@ public class PlayerControllerAi extends PlayerController { } @Override - public ReplacementEffect chooseSingleReplacementEffect(String prompt, List possibleReplacers) { + public ReplacementEffect chooseSingleReplacementEffect(List possibleReplacers) { return brains.chooseSingleReplacementEffect(possibleReplacers); } diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index 536d24883d8..2ee4111a75f 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -6942,7 +6942,7 @@ public class Card extends GameEntity implements Comparable, IHasSVars { shieldCounterReplaceDamage.setOverridingAbility(AbilityFactory.getAbility(sa, this)); } if (shieldCounterReplaceDestroy == null) { - String reStr = "Event$ Destroy | ActiveZones$ Battlefield | ValidCard$ Card.Self | ValidCause$ SpellAbility | Secondary$ True " + String reStr = "Event$ Destroy | ActiveZones$ Battlefield | ValidCard$ Card.Self | ValidCause$ SpellAbility | Secondary$ True | ShieldCounter$ True " + "| Description$ If this permanent would be destroyed as the result of an effect, instead remove a shield counter from it."; shieldCounterReplaceDestroy = ReplacementHandler.parseReplacement(reStr, this, false, null); shieldCounterReplaceDestroy.setOverridingAbility(AbilityFactory.getAbility(sa, this)); diff --git a/forge-game/src/main/java/forge/game/player/PlayerController.java b/forge-game/src/main/java/forge/game/player/PlayerController.java index 0a8202d005a..426fd7b314e 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerController.java +++ b/forge-game/src/main/java/forge/game/player/PlayerController.java @@ -251,7 +251,7 @@ public abstract class PlayerController { public abstract String chooseKeywordForPump(List options, SpellAbility sa, String prompt, Card tgtCard); public abstract boolean confirmPayment(CostPart costPart, String string, SpellAbility sa); - public abstract ReplacementEffect chooseSingleReplacementEffect(String prompt, List possibleReplacers); + public abstract ReplacementEffect chooseSingleReplacementEffect(List possibleReplacers); public abstract StaticAbility chooseSingleStaticAbility(String prompt, List possibleReplacers); public abstract String chooseProtectionType(String string, SpellAbility sa, List choices); diff --git a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java index ad07ad14bf5..4165439877a 100644 --- a/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java +++ b/forge-game/src/main/java/forge/game/replacement/ReplacementHandler.java @@ -215,7 +215,7 @@ public class ReplacementHandler { if (layer == ReplacementLayer.CantHappen) { chosenRE = possibleReplacers.get(0); } else { - chosenRE = decider.getController().chooseSingleReplacementEffect(Localizer.getInstance().getMessage("lblChooseFirstApplyReplacementEffect"), possibleReplacers); + chosenRE = decider.getController().chooseSingleReplacementEffect(possibleReplacers); } possibleReplacers.remove(chosenRE); @@ -657,7 +657,7 @@ public class ReplacementHandler { } List possibleReplacers = new ArrayList<>(replaceCandidateMap.keySet()); - ReplacementEffect chosenRE = decider.getController().chooseSingleReplacementEffect(Localizer.getInstance().getMessage("lblChooseFirstApplyReplacementEffect"), possibleReplacers); + ReplacementEffect chosenRE = decider.getController().chooseSingleReplacementEffect(possibleReplacers); List> runParamList = replaceCandidateMap.get(chosenRE); if (!executedDamageMap.containsKey(chosenRE)) { diff --git a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java index a78289e22b2..26db194dc9d 100644 --- a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java +++ b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java @@ -541,7 +541,7 @@ public class PlayerControllerForTests extends PlayerController { } @Override - public ReplacementEffect chooseSingleReplacementEffect(String prompt, List possibleReplacers) { + public ReplacementEffect chooseSingleReplacementEffect(List possibleReplacers) { // TODO Auto-generated method stub return Iterables.getFirst(possibleReplacers, null); } diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 1428e284826..e408b5b896f 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -1891,11 +1891,12 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } @Override - public ReplacementEffect chooseSingleReplacementEffect(final String prompt, final List possibleReplacers) { + public ReplacementEffect chooseSingleReplacementEffect(final List possibleReplacers) { final ReplacementEffect first = possibleReplacers.get(0); if (possibleReplacers.size() == 1) { return first; } + String prompt = localizer.getMessage("lblChooseFirstApplyReplacementEffect"); final String firstStr = first.toString(); for (int i = 1; i < possibleReplacers.size(); i++) { // prompt user if there are multiple different options