From 1eb9fa375e4acf39977309dd9af8f7cf8c7a182a Mon Sep 17 00:00:00 2001 From: excessum Date: Sat, 8 Oct 2016 06:14:44 +0000 Subject: [PATCH] - Separated vehicle crewing logic between "canPay" and "canPlay" --- .../src/main/java/forge/ai/ComputerUtil.java | 15 +++----- .../main/java/forge/ai/ComputerUtilCost.java | 36 +++++++++++++++++++ .../main/java/forge/ai/ability/AnimateAi.java | 4 ++- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 690d02032fb..1d9d995c3ff 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -32,7 +32,6 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; -import forge.ai.ability.AnimateAi; import forge.ai.ability.ProtectAi; import forge.card.CardType; import forge.card.MagicColor; @@ -45,7 +44,6 @@ import forge.game.ability.effects.CharmEffect; import forge.game.card.Card; import forge.game.card.CardCollection; import forge.game.card.CardCollectionView; -import forge.game.card.CardFactory; import forge.game.card.CardLists; import forge.game.card.CardPredicates; import forge.game.card.CardPredicates.Presets; @@ -504,7 +502,6 @@ public class ComputerUtil { // Used for Crewing vehicles, ideally we sort by useless creatures. Can't Attack/Defender int totalPower = 0; final Card activate = sa.getHostCard(); - int vehicleValue = 0; CardCollection all = new CardCollection(ai.getCardsIn(ZoneType.Battlefield)); all.removeAll(exclude); @@ -513,9 +510,6 @@ public class ComputerUtil { if (sa.hasParam("Crew")) { typeList = CardLists.getNotKeyword(typeList, "CARDNAME can't crew Vehicles."); - Card vehicle = CardFactory.copyCard(sa.getHostCard(), true); - AnimateAi.becomeAnimated(vehicle, false, sa); - vehicleValue = ComputerUtilCard.evaluateCreature(vehicle); } // is this needed? @@ -524,14 +518,15 @@ public class ComputerUtil { if (tap) { typeList.remove(activate); } - CardLists.sortByPowerAsc(typeList); - + ComputerUtilCard.sortByEvaluateCreature(typeList); + Collections.reverse(typeList); + final CardCollection tapList = new CardCollection(); - // Very very rudimentary + // Accumulate from "worst" creature for (Card next : typeList) { int pow = next.getNetPower(); - if (pow <= 0 || ComputerUtilCard.evaluateCreature(next) > vehicleValue) { + if (pow <= 0) { continue; } totalPower += pow; diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java index d716f4d5060..0ac125f6a68 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java @@ -1,11 +1,16 @@ package forge.ai; +import com.google.common.base.Predicate; import com.google.common.collect.Lists; + +import forge.ai.ability.AnimateAi; import forge.card.ColorSet; import forge.game.GameActionUtil; import forge.game.ability.AbilityUtils; +import forge.game.ability.ApiType; import forge.game.card.Card; import forge.game.card.CardCollection; +import forge.game.card.CardFactory; import forge.game.card.CardLists; import forge.game.card.CardPredicates.Presets; import forge.game.card.CounterType; @@ -296,8 +301,39 @@ public class ComputerUtilCost { if (cost == null) { return true; } + boolean isVehicle = source.hasStartOfKeyword("Crew"); for (final CostPart part : cost.getCostParts()) { if (part instanceof CostTapType) { + /* + * Only crew with creatures weaker than vehicle + * + * Possible improvements: + * - block against evasive (flyers, intimidate, etc.) + * - break board stall by racing with evasive vehicle + */ + if (isVehicle) { + for (SpellAbility sa : source.getSpellAbilities()) { + if (sa.getApi() == ApiType.Animate) { + Card vehicle = CardFactory.copyCard(sa.getHostCard(), true); + AnimateAi.becomeAnimated(vehicle, false, sa); + final int vehicleValue = ComputerUtilCard.evaluateCreature(vehicle); + String type = part.getType(); + String totalP = type.split("withTotalPowerGE")[1]; + type = type.replace("+withTotalPowerGE" + totalP, ""); + CardCollection exclude = CardLists.getValidCards( + new CardCollection(ai.getCardsIn(ZoneType.Battlefield)), type.split(";"), + source.getController(), source, sa); + exclude = CardLists.filter(exclude, new Predicate() { + @Override + public boolean apply(final Card c) { + return ComputerUtilCard.evaluateCreature(c) >= vehicleValue; + } + }); // exclude creatures >= vehicle + return ComputerUtil.chooseTapTypeAccumulatePower(ai, type, sa, true, + Integer.parseInt(totalP), exclude) != null; + } + } + } return false; } } 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 021d09c7eb8..79807b5c448 100644 --- a/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/AnimateAi.java @@ -137,10 +137,12 @@ public class AnimateAi extends SpellAbilityAi { if (sa.getConditions() != null && !sa.getConditions().areMet(sa) && sa.getSubAbility() == null) { return false; // what is this for? } - if (!game.getStack().isEmpty() && game.getStack().peekAbility().getApi() == ApiType.Sacrifice) { return true; // interrupt sacrifice } + if (!ComputerUtilCost.checkTapTypeCost(aiPlayer, sa.getPayCosts(), source)) { + return false; // prevent crewing with equal or better creatures + } if (null == tgt) { final List defined = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa); boolean bFlag = false;