From d3fe888238abb0d896b6ece5fb4bae68ca7fd7bc Mon Sep 17 00:00:00 2001 From: Agetian Date: Wed, 11 Apr 2018 07:16:58 +0300 Subject: [PATCH] - Initial implementation: for the AI, consider dealing damage to planeswalkers directly when applicable. --- .../main/java/forge/ai/ComputerUtilCard.java | 14 ++ .../java/forge/ai/ability/DamageDealAi.java | 121 +++++++++++++++--- 2 files changed, 115 insertions(+), 20 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index ccd53683d25..d158414a879 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -104,6 +104,20 @@ public class ComputerUtilCard { return Aggregates.itemWithMax(all, CardPredicates.Accessors.fnGetCmc); } + /** + * Returns the worst Planeswalker from a given list + * @param list list of cards to evaluate + * @return best Planeswalker + */ + public static Card getWorstPlaneswalkerAI(final List list) { + List all = CardLists.filter(list, CardPredicates.Presets.PLANEWALKERS); + if (all.size() == 0) { + return null; + } + // no AI logic, just return least expensive + return Aggregates.itemWithMin(all, CardPredicates.Accessors.fnGetCmc); + } + // The AI doesn't really pick the best enchantment, just the most expensive. /** *

diff --git a/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java b/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java index 9aaf676ebb6..56aaed39853 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java @@ -271,25 +271,7 @@ public class DamageDealAi extends DamageAiBase { final Player activator = sa.getActivatingPlayer(); final Card source = sa.getHostCard(); final Game game = source.getGame(); - List hPlay = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), activator, source, sa); - - if (activator.equals(ai)) { - hPlay = CardLists.filterControlledBy(hPlay, pl); - } - - final List objects = Lists.newArrayList(sa.getTargets().getTargets()); - if (sa.hasParam("TargetUnique")) { - objects.addAll(sa.getUniqueTargets()); - } - for (final Object o : objects) { - if (o instanceof Card) { - final Card c = (Card) o; - if (hPlay.contains(c)) { - hPlay.remove(c); - } - } - } - hPlay = CardLists.getTargetableCards(hPlay, sa); + List hPlay = getTargetableCards(ai, sa, pl, tgt, activator, source, game); List killables = CardLists.filter(hPlay, new Predicate() { @Override @@ -338,6 +320,84 @@ public class DamageDealAi extends DamageAiBase { return null; } + /** + *

+ * dealDamageChooseTgtPW. + *

+ * + * @param d + * a int. + * @param noPrevention + * a boolean. + * @param pl + * a {@link forge.game.player.Player} object. + * @param mandatory + * a boolean. + * @return a {@link forge.game.card.Card} object. + */ + private Card dealDamageChooseTgtPW(final Player ai, final SpellAbility sa, final int d, final boolean noPrevention, + final Player pl, final boolean mandatory) { + + final TargetRestrictions tgt = sa.getTargetRestrictions(); + final Player activator = sa.getActivatingPlayer(); + final Card source = sa.getHostCard(); + final Game game = source.getGame(); + List hPlay = getTargetableCards(ai, sa, pl, tgt, activator, source, game); + + List killables = CardLists.filter(hPlay, new Predicate() { + @Override + public boolean apply(final Card c) { + return c.getSVar("Targeting").equals("Dies") + || (ComputerUtilCombat.getEnoughDamageToKill(c, d, source, false, noPrevention) <= d) + && !ComputerUtil.canRegenerate(ai, c) + && !(c.getSVar("SacMe").length() > 0); + } + }); + + // Filter AI-specific targets if provided + killables = ComputerUtil.filterAITgts(sa, ai, new CardCollection(killables), true); + + Card targetCard = null; + if (pl.isOpponentOf(ai) && activator.equals(ai) && !killables.isEmpty()) { + return ComputerUtilCard.getBestPlaneswalkerAI(killables); + } + + if (!hPlay.isEmpty()) { + if (pl.isOpponentOf(ai) && activator.equals(ai)) { + return ComputerUtilCard.getBestPlaneswalkerAI(hPlay); + } else if (mandatory) { + return ComputerUtilCard.getWorstPlaneswalkerAI(hPlay); + } + + return targetCard; + } + + return null; + } + + private List getTargetableCards(Player ai, SpellAbility sa, Player pl, TargetRestrictions tgt, Player activator, Card source, Game game) { + List hPlay = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), activator, source, sa); + + if (activator.equals(ai)) { + hPlay = CardLists.filterControlledBy(hPlay, pl); + } + + final List objects = Lists.newArrayList(sa.getTargets().getTargets()); + if (sa.hasParam("TargetUnique")) { + objects.addAll(sa.getUniqueTargets()); + } + for (final Object o : objects) { + if (o instanceof Card) { + final Card c = (Card) o; + if (hPlay.contains(c)) { + hPlay.remove(c); + } + } + } + hPlay = CardLists.getTargetableCards(hPlay, sa); + return hPlay; + } + /** *

* damageTargetAI. @@ -475,7 +535,27 @@ public class DamageDealAi extends DamageAiBase { return targetingPlayer.getController().chooseTargetsFor(sa); } + if (tgt.canTgtPlaneswalker()) { + // We can damage planeswalkers with this, consider targeting. + Card c = this.dealDamageChooseTgtPW(ai, sa, dmg, noPrevention, enemy, false); + if (c != null) { + tcs.add(c); + if (divided) { + final int assignedDamage = ComputerUtilCombat.getEnoughDamageToKill(c, dmg, source, false, noPrevention); + if (assignedDamage <= dmg) { + tgt.addDividedAllocation(c, assignedDamage); + } + dmg = dmg - assignedDamage; + if (dmg <= 0) { + break; + } + } + continue; + } + } + if (tgt.canTgtCreatureAndPlayer()) { + Card c = null; if (this.shouldTgtP(ai, sa, dmg, noPrevention)) { tcs.add(enemy); @@ -489,7 +569,8 @@ public class DamageDealAi extends DamageAiBase { dmg = dmg * sa.getTargets().getNumTargeted() / (sa.getTargets().getNumTargeted() +1); } - final Card c = this.dealDamageChooseTgtC(ai, sa, dmg, noPrevention, enemy, false); + // look for creature targets; currently also catches planeswalkers that can be killed immediately + c = this.dealDamageChooseTgtC(ai, sa, dmg, noPrevention, enemy, false); if (c != null) { //option to hold removal instead only applies for single targeted removal if (sa.isSpell() && !divided && !immediately && tgt.getMaxTargets(sa.getHostCard(), sa) == 1) {