From 4d53b22a1237dacab8665a19126fdea7c932171f Mon Sep 17 00:00:00 2001 From: Hanmac Date: Mon, 17 Oct 2016 20:35:22 +0000 Subject: [PATCH] AnimateAi: add some logic for animateTgtAI and do some rewriting --- .../java/forge/ai/AiAttackController.java | 4 +- .../main/java/forge/ai/ComputerUtilCost.java | 5 +- .../main/java/forge/ai/ability/AnimateAi.java | 181 +++++++++++++----- 3 files changed, 134 insertions(+), 56 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index def2726ee4a..a9c92d3becb 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -31,7 +31,6 @@ import forge.game.ability.effects.ProtectEffect; import forge.game.card.Card; import forge.game.card.CardCollectionView; import forge.game.card.CardLists; -import forge.game.card.CardUtil; import forge.game.card.CounterType; import forge.game.combat.Combat; import forge.game.combat.CombatUtil; @@ -125,8 +124,7 @@ public class AiAttackController { if (sa.getApi() == ApiType.Animate) { if (ComputerUtilCost.canPayCost(sa, defender) && sa.getRestrictions().checkOtherRestrictions(c, sa, defender)) { - Card animatedCopy = CardUtil.getLKICopy(c); - AnimateAi.becomeAnimated(animatedCopy, c.hasSickness(), sa); + Card animatedCopy = AnimateAi.becomeAnimated(c, sa); defenders.add(animatedCopy); } } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java index 1352f458712..4a61579781e 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java @@ -10,7 +10,6 @@ import forge.game.ability.AbilityUtils; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardLists; -import forge.game.card.CardUtil; import forge.game.card.CardPredicates.Presets; import forge.game.card.CounterType; import forge.game.combat.Combat; @@ -328,9 +327,7 @@ public class ComputerUtilCost { * - break board stall by racing with evasive vehicle */ if (sa.hasParam("Crew")) { - //Card vehicle = CardFactory.copyCard(sa.getHostCard(), true); - Card vehicle = CardUtil.getLKICopy(source); - AnimateAi.becomeAnimated(vehicle, source.hasSickness(), sa); + Card vehicle = AnimateAi.becomeAnimated(source, sa); final int vehicleValue = ComputerUtilCard.evaluateCreature(vehicle); String type = part.getType(); String totalP = type.split("withTotalPowerGE")[1]; diff --git a/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java b/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java index aad822f0394..a7ff3bd9068 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java @@ -1,6 +1,8 @@ package forge.ai.ability; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCost; @@ -31,11 +33,12 @@ import forge.game.trigger.TriggerHandler; import forge.game.zone.ZoneType; import forge.util.collect.FCollectionView; -import java.util.ArrayList; + import java.util.Arrays; import java.util.List; import java.util.Map; + /** *

* AbilityFactoryAnimate class. @@ -87,8 +90,7 @@ public class AnimateAi extends SpellAbilityAi { list = CardLists.filter(list, CardPredicates.canBeSacrificedBy(topStack)); ComputerUtilCard.sortByEvaluateCreature(list); if (!list.isEmpty() && list.size() == nToSac && ComputerUtilCost.canPayCost(sa, ai)) { - Card animatedCopy = CardUtil.getLKICopy(source);//CardFactory.copyCard(source, false); - becomeAnimated(animatedCopy, source.hasSickness(), sa); + Card animatedCopy = becomeAnimated(source, sa); list.add(animatedCopy); list = CardLists.getValidCards(list, valid.split(","), ai.getOpponent(), topStack.getHostCard(), topStack); @@ -144,8 +146,8 @@ public class AnimateAi extends SpellAbilityAi { if (null == tgt) { final List defined = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa); boolean bFlag = false; - boolean givesHaste = sa.hasParam("Keywords") && sa.getParam("Keywords").contains("Haste"); - for (final Card c : defined) { + boolean givesHaste = sa.hasParam("Keywords") && sa.getParam("Keywords").contains("Haste"); + for (final Card c : defined) { bFlag |= !c.isCreature() && !c.isTapped() && (!c.hasSickness() || givesHaste || !ph.isPlayerTurn(aiPlayer)) && !c.isEquipping(); @@ -161,11 +163,11 @@ public class AnimateAi extends SpellAbilityAi { toughness = AbilityUtils.calculateAmount(source, sa.getParam("Toughness"), sa); } if (sa.hasParam("Keywords")) { - for (String keyword : sa.getParam("Keywords").split(" & ")) { - if (!source.hasKeyword(keyword)) { - bFlag = true; - } - } + for (String keyword : sa.getParam("Keywords").split(" & ")) { + if (!source.hasKeyword(keyword)) { + bFlag = true; + } + } } if (power + toughness > c.getCurrentPower() + c.getCurrentToughness()) { bFlag = true; @@ -173,8 +175,7 @@ public class AnimateAi extends SpellAbilityAi { } if (!SpellAbilityAi.isSorcerySpeed(sa) && !sa.hasParam("Permanent")) { - Card animatedCopy = CardUtil.getLKICopy(c); - AnimateAi.becomeAnimated(animatedCopy, c.hasSickness(), sa); + Card animatedCopy = becomeAnimated(c, sa); if (ph.isPlayerTurn(aiPlayer) && !ComputerUtilCard.doesSpecifiedCreatureAttackAI(aiPlayer, animatedCopy)) { return false; @@ -184,8 +185,8 @@ public class AnimateAi extends SpellAbilityAi { return false; } } - } - return bFlag; // All of the defined stuff is animated, not very useful + } + return bFlag; // All of the defined stuff is animated, not very useful } else { sa.resetTargets(); if (!animateTgtAI(sa)) { @@ -213,29 +214,105 @@ public class AnimateAi extends SpellAbilityAi { if (sa.usesTargeting() && !animateTgtAI(sa) && !mandatory) { return false; } - - // Improve AI for triggers. If source is a creature with: - // When ETB, sacrifice a creature. Check to see if the AI has something - // to sacrifice - - // Eventually, we can call the trigger of ETB abilities with - // not mandatory as part of the checks to cast something - if (sa.hasParam("AITgts")) { - final TargetRestrictions tgt = sa.getTargetRestrictions(); - final Card animateSource = sa.getHostCard(); - CardCollectionView list = aiPlayer.getGame().getCardsIn(tgt.getZone()); - list = CardLists.getValidCards(list, tgt.getValidTgts(), sa.getActivatingPlayer(), animateSource, sa); - CardCollection prefList = CardLists.getValidCards(list, sa.getParam("AITgts"), sa.getActivatingPlayer(), - animateSource); - if (!prefList.isEmpty()) { - CardLists.shuffle(prefList); - sa.getTargets().add(prefList.getFirst()); - } - } return true; } private boolean animateTgtAI(final SpellAbility sa) { + final Player ai = sa.getActivatingPlayer(); + final PhaseHandler ph = ai.getGame().getPhaseHandler(); + + final CardType types = new CardType(); + if (sa.hasParam("Types")) { + types.addAll(Arrays.asList(sa.getParam("Types").split(","))); + } + + // something is used for animate into creature + if (types.isCreature()) { + final TargetRestrictions tgt = sa.getTargetRestrictions(); + final Card source = sa.getHostCard(); + CardCollectionView list = ai.getGame().getCardsIn(tgt.getZone()); + list = CardLists.getValidCards(list, tgt.getValidTgts(), ai, source, sa); + // need to targetable + list = CardLists.getTargetableCards(list, sa); + + // try to look for AI targets if able + if (sa.hasParam("AITgts")) { + CardCollection prefList = CardLists.getValidCards(list, sa.getParam("AITgts").split(","), ai, source, sa); + if(!prefList.isEmpty()) { + list = prefList; + } + } + + // list is empty, no possible targets + if (list.isEmpty()) { + return false; + } + + Map data = Maps.newHashMap(); + for (final Card c : list) { + // don't use Permanent animate on something that would leave the field + if (c.hasSVar("EndOfTurnLeavePlay") && sa.hasParam("Permanent")) { + continue; + } + + // if tapped it might not attack or block + if (c.isTapped()) { + continue; + } + + // make Animated copy and evaluate it + final Card animatedCopy = becomeAnimated(c, sa); + int aValue = ComputerUtilCard.evaluateCreature(animatedCopy); + + // animated creature has zero toughness, don't do that + if (animatedCopy.getNetToughness() <= 0) { + continue; + } + + // if original is already a Creature, + // evaluate their value to check if it becomes better + if (c.isCreature()) { + int cValue = ComputerUtilCard.evaluateCreature(c); + if (cValue <= aValue) + continue; + } + + // if its player turn, + // check if its Permanent or that creature would attack + if (ph.isPlayerTurn(ai)) { + if (!sa.hasParam("Permanent") && + !ComputerUtilCard.doesSpecifiedCreatureAttackAI(ai, animatedCopy)) { + continue; + } + } + + // store in map + data.put(c, aValue); + } + + // data is empty, no good targets + if (data.isEmpty()) { + return false; + } + + // get the best creature to be animated + List maxList = Lists.newArrayList(); + int maxValue = 0; + for (final Map.Entry e : data.entrySet()) { + int v = e.getValue(); + if (v > maxValue) { + maxValue = v; + maxList.clear(); + } + maxList.add(e.getKey()); + } + + // select the worst of the best + final Card worst = ComputerUtilCard.getWorstAI(maxList); + sa.getTargets().add(worst); + return true; + } + // This is reasonable for now. Kamahl, Fist of Krosa and a sorcery or // two are the only things // that animate a target. Those can just use SVar:RemAIDeck:True until @@ -244,6 +321,12 @@ public class AnimateAi extends SpellAbilityAi { return false; } + public static Card becomeAnimated(final Card card, final SpellAbility sa) { + final Card copy = CardUtil.getLKICopy(card); + becomeAnimated(copy, card.hasSickness(), sa); + return copy; + } + public static void becomeAnimated(final Card card, final boolean hasOriginalCardSickness, final SpellAbility sa) { // duplicating AnimateEffect.resolve final Card source = sa.getHostCard(); @@ -278,17 +361,17 @@ public class AnimateAi extends SpellAbilityAi { types.add(source.getChosenType()); } - final List keywords = new ArrayList(); + final List keywords = Lists.newArrayList(); if (sa.hasParam("Keywords")) { keywords.addAll(Arrays.asList(sa.getParam("Keywords").split(" & "))); } - final List removeKeywords = new ArrayList(); + final List removeKeywords = Lists.newArrayList(); if (sa.hasParam("RemoveKeywords")) { removeKeywords.addAll(Arrays.asList(sa.getParam("RemoveKeywords").split(" & "))); } - final List hiddenKeywords = new ArrayList(); + final List hiddenKeywords = Lists.newArrayList(); if (sa.hasParam("HiddenKeywords")) { hiddenKeywords.addAll(Arrays.asList(sa.getParam("HiddenKeywords").split(" & "))); } @@ -308,37 +391,37 @@ public class AnimateAi extends SpellAbilityAi { if (colors.equals("ChosenColor")) { tmpDesc = CardUtil.getShortColorsString(source.getChosenColors()); } else { - tmpDesc = CardUtil.getShortColorsString(new ArrayList(Arrays.asList(colors.split(",")))); + tmpDesc = CardUtil.getShortColorsString(Lists.newArrayList(Arrays.asList(colors.split(",")))); } } final String finalDesc = tmpDesc; // abilities to add to the animated being - final List abilities = new ArrayList(); + final List abilities = Lists.newArrayList(); if (sa.hasParam("Abilities")) { abilities.addAll(Arrays.asList(sa.getParam("Abilities").split(","))); } // replacement effects to add to the animated being - final List replacements = new ArrayList(); + final List replacements = Lists.newArrayList(); if (sa.hasParam("Replacements")) { replacements.addAll(Arrays.asList(sa.getParam("Replacements").split(","))); } // triggers to add to the animated being - final List triggers = new ArrayList(); + final List triggers = Lists.newArrayList(); if (sa.hasParam("Triggers")) { triggers.addAll(Arrays.asList(sa.getParam("Triggers").split(","))); } // static abilities to add to the animated being - final List stAbs = new ArrayList(); + final List stAbs = Lists.newArrayList(); if (sa.hasParam("staticAbilities")) { stAbs.addAll(Arrays.asList(sa.getParam("staticAbilities").split(","))); } // sVars to add to the animated being - final List sVars = new ArrayList(); + final List sVars = Lists.newArrayList(); if (sa.hasParam("sVars")) { sVars.addAll(Arrays.asList(sa.getParam("sVars").split(","))); } @@ -401,7 +484,7 @@ public class AnimateAi extends SpellAbilityAi { // TODO will all these abilities/triggers/replacements/etc. lead to // memory leaks or unintended effects? // remove abilities - final List removedAbilities = new ArrayList(); + final List removedAbilities = Lists.newArrayList(); boolean clearAbilities = sa.hasParam("OverwriteAbilities"); boolean clearSpells = sa.hasParam("OverwriteSpells"); boolean removeAll = sa.hasParam("RemoveAllAbilities"); @@ -416,7 +499,7 @@ public class AnimateAi extends SpellAbilityAi { } // give abilities - final List addedAbilities = new ArrayList(); + final List addedAbilities = Lists.newArrayList(); if (abilities.size() > 0) { for (final String s : abilities) { final String actualAbility = source.getSVar(s); @@ -427,7 +510,7 @@ public class AnimateAi extends SpellAbilityAi { } // Grant triggers - final List addedTriggers = new ArrayList(); + final List addedTriggers = Lists.newArrayList(); if (triggers.size() > 0) { for (final String s : triggers) { final String actualTrigger = source.getSVar(s); @@ -437,7 +520,7 @@ public class AnimateAi extends SpellAbilityAi { } // give replacement effects - final List addedReplacements = new ArrayList(); + final List addedReplacements = Lists.newArrayList(); if (replacements.size() > 0) { for (final String s : replacements) { final String actualReplacement = source.getSVar(s); @@ -448,7 +531,7 @@ public class AnimateAi extends SpellAbilityAi { } // suppress triggers from the animated card - final List removedTriggers = new ArrayList(); + final List removedTriggers = Lists.newArrayList(); if (sa.hasParam("OverwriteTriggers") || removeAll) { final FCollectionView triggersToRemove = card.getTriggers(); for (final Trigger trigger : triggersToRemove) { @@ -486,7 +569,7 @@ public class AnimateAi extends SpellAbilityAi { } // suppress static abilities from the animated card - final List removedStatics = new ArrayList(); + final List removedStatics = Lists.newArrayList(); if (sa.hasParam("OverwriteStatics") || removeAll) { final FCollectionView staticsToRemove = card.getStaticAbilities(); for (final StaticAbility stAb : staticsToRemove) { @@ -496,7 +579,7 @@ public class AnimateAi extends SpellAbilityAi { } // suppress static abilities from the animated card - final List removedReplacements = new ArrayList(); + final List removedReplacements = Lists.newArrayList(); if (sa.hasParam("OverwriteReplacements") || removeAll) { for (final ReplacementEffect re : card.getReplacementEffects()) { re.setTemporarilySuppressed(true);