From 614e067bc990e78f68146bf88c3bbb6422ef3f1d Mon Sep 17 00:00:00 2001 From: Agetian Date: Sun, 2 Oct 2022 19:56:35 +0300 Subject: [PATCH] Update Gideon Blackblade AI logic (#1630) * - Update Gideon Blackblade AI logic. * - Update imports. * - Update imports. * - Modify the method name to make more sense in the updated context. --- .../java/forge/ai/PlayerControllerAi.java | 6 +++ .../src/main/java/forge/ai/SpecialCardAi.java | 54 ++++++++----------- .../ai/ability/ChooseGenericEffectAi.java | 18 ++----- .../main/java/forge/ai/ability/PumpAi.java | 2 + 4 files changed, 33 insertions(+), 47 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index ae767b3b994..e02372f9217 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -943,6 +943,12 @@ public class PlayerControllerAi extends PlayerController { @Override public String chooseKeywordForPump(final List options, final SpellAbility sa, final String prompt) { + final String aiLogic = sa.getParamOrDefault("AILogic", ""); + + if (aiLogic.equals("GideonBlackblade")) { + return SpecialCardAi.GideonBlackblade.chooseKeyword(player, sa, options); + } + return Iterables.getFirst(options, null); } diff --git a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java index 612695cab97..1509b827b5a 100644 --- a/forge-ai/src/main/java/forge/ai/SpecialCardAi.java +++ b/forge-ai/src/main/java/forge/ai/SpecialCardAi.java @@ -17,33 +17,20 @@ */ package forge.ai; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import forge.game.GameEntity; -import org.apache.commons.lang3.tuple.Pair; - import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; - import forge.ai.ability.AnimateAi; import forge.card.ColorSet; import forge.card.MagicColor; import forge.card.mana.ManaCost; import forge.game.Game; +import forge.game.GameEntity; import forge.game.GameType; import forge.game.ability.AbilityUtils; import forge.game.ability.ApiType; -import forge.game.card.Card; -import forge.game.card.CardCollection; -import forge.game.card.CardCollectionView; -import forge.game.card.CardLists; -import forge.game.card.CardPredicates; -import forge.game.card.CardUtil; -import forge.game.card.CounterEnumType; +import forge.game.card.*; import forge.game.combat.Combat; import forge.game.combat.CombatUtil; import forge.game.cost.CostPart; @@ -64,6 +51,11 @@ import forge.util.MyRandom; import forge.util.TextUtil; import forge.util.maps.LinkedHashMapToAmount; import forge.util.maps.MapToAmount; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; /** * Special logic for individual cards @@ -610,35 +602,31 @@ public class SpecialCardAi { return true; } - public static SpellAbility chooseSpellAbility(final Player ai, final SpellAbility sa, final List spells) { + public static String chooseKeyword(final Player ai, final SpellAbility sa, final List options) { // TODO: generalize and improve this so that it acts in a more reasonable way and can potentially be used for other cards too - List best = Lists.newArrayList(); - List possible = Lists.newArrayList(); + List possible = Lists.newArrayList(); Card tgtCard = sa.getTargetCard(); if (tgtCard != null) { - for (SpellAbility sp : spells) { - if (SpellApiToAi.Converter.get(sp.getApi()).canPlayAIWithSubs(ai, sp)) { - best.add(sp); // these SAs are prioritized since the AI sees a reason to play them now - } - final List keywords = sp.hasParam("KW") ? Arrays.asList(sp.getParam("KW").split(" & ")) - : Lists.newArrayList(); - for (String kw : keywords) { - if (!tgtCard.hasKeyword(kw)) { - if ("Indestructible".equals(kw) && ai.getOpponents().getCreaturesInPlay().isEmpty()) { + CardCollection oppUntappedCreatures = CardLists.filter(ai.getOpponents().getCreaturesInPlay(), CardPredicates.Presets.UNTAPPED); + for (String kw : options) { + if (!tgtCard.hasKeyword(kw)) { + if ("Indestructible".equals(kw)) { + if (oppUntappedCreatures.isEmpty()) { continue; // nothing to damage or kill the creature with + } else { + possible.clear(); + possible.add(kw); // prefer Indestructible above all else + break; } - possible.add(sp); // these SAs at least don't duplicate a keyword on the card - break; } + possible.add(kw); // these SAs at least don't duplicate a keyword on the card } } } - if (!best.isEmpty()) { - return Aggregates.random(best); - } else if (!possible.isEmpty()) { + if (!possible.isEmpty()) { return Aggregates.random(possible); } else { - return Aggregates.random(spells); // if worst comes to worst, it's a PW +1 ability, so do at least something + return Aggregates.random(options); // if worst comes to worst, it's a PW +1 ability, so do at least something } } } diff --git a/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java b/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java index fdb9634414e..1c7116ff6ed 100644 --- a/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/ChooseGenericEffectAi.java @@ -1,25 +1,16 @@ package forge.ai.ability; -import java.util.List; -import java.util.Map; - import com.google.common.base.Predicate; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; - import forge.ai.ComputerUtilAbility; import forge.ai.ComputerUtilCost; -import forge.ai.SpecialCardAi; import forge.ai.SpellAbilityAi; import forge.ai.SpellApiToAi; import forge.card.MagicColor; import forge.game.Game; -import forge.game.card.Card; -import forge.game.card.CardCollection; -import forge.game.card.CardCollectionView; -import forge.game.card.CardUtil; -import forge.game.card.CounterEnumType; +import forge.game.card.*; import forge.game.combat.Combat; import forge.game.cost.Cost; import forge.game.keyword.Keyword; @@ -32,6 +23,9 @@ import forge.game.zone.ZoneType; import forge.util.Aggregates; import forge.util.collect.FCollection; +import java.util.List; +import java.util.Map; + public class ChooseGenericEffectAi extends SpellAbilityAi { @@ -47,8 +41,6 @@ public class ChooseGenericEffectAi extends SpellAbilityAi { return true; } } - } else if ("GideonBlackblade".equals(aiLogic)) { - return SpecialCardAi.GideonBlackblade.consider(ai, sa); } else if ("AtOppEOT".equals(aiLogic)) { PhaseHandler ph = ai.getGame().getPhaseHandler(); return ph.is(PhaseType.END_OF_TURN) && ph.getNextTurn() == ai; @@ -99,8 +91,6 @@ public class ChooseGenericEffectAi extends SpellAbilityAi { return spells.get(0); } else if ("Random".equals(logic)) { return Aggregates.random(spells); - } else if ("GideonBlackblade".equals(logic)) { - return SpecialCardAi.GideonBlackblade.chooseSpellAbility(player, sa, spells); } else if ("Phasing".equals(logic)) { // Teferi's Realm : keep aggressive List filtered = Lists.newArrayList(Iterables.filter(spells, new Predicate() { @Override diff --git a/forge-ai/src/main/java/forge/ai/ability/PumpAi.java b/forge-ai/src/main/java/forge/ai/ability/PumpAi.java index 69e6a07c58d..9b64adfbfc3 100644 --- a/forge-ai/src/main/java/forge/ai/ability/PumpAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/PumpAi.java @@ -143,6 +143,8 @@ public class PumpAi extends PumpAiBase { return SpecialCardAi.ElectrostaticPummeler.consider(ai, sa); } else if (aiLogic.startsWith("AristocratCounters")) { return true; // the preconditions to this are already tested in checkAiLogic + } else if ("GideonBlackblade".equals(aiLogic)) { + return SpecialCardAi.GideonBlackblade.consider(ai, sa); } else if ("MoveCounter".equals(aiLogic)) { final SpellAbility moveSA = sa.findSubAbilityByType(ApiType.MoveCounter);