From db3286bec60cb07684a8fd18d1a994b28fcdf99a Mon Sep 17 00:00:00 2001 From: excessum Date: Sat, 17 Oct 2015 07:00:45 +0000 Subject: [PATCH] - Revamped Planeswalkers logic: evaluate more expensive abilities first to prevent spamming of good "plus" abilities, try not to "minus" all loyalty if not under threat - Added flag in TokensAi to allow Planeswalkers to create tokens in Main1 - AI can now use all the abilities of Gideon, Ally of Zendikar --- .../src/main/java/forge/ai/AiController.java | 19 ++++++++++------ .../main/java/forge/ai/ComputerUtilCost.java | 11 ++++++++++ .../main/java/forge/ai/ability/TokenAi.java | 22 +++++++++++++++++-- .../cardsfolder/g/gideon_ally_of_zendikar.txt | 1 - 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index e26942e8f59..9fc6b51f8bf 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -64,6 +64,8 @@ import forge.game.combat.CombatUtil; import forge.game.cost.Cost; import forge.game.cost.CostDiscard; import forge.game.cost.CostPart; +import forge.game.cost.CostPutCounter; +import forge.game.cost.CostRemoveCounter; import forge.game.mana.ManaCostBeingPaid; import forge.game.phase.PhaseType; import forge.game.player.Player; @@ -857,22 +859,25 @@ public class AiController { if ("True".equals(source.getSVar("NonStackingEffect")) && ai.isCardInPlay(source.getName())) { p -= 9; } - // sort planeswalker abilities for ultimate + // sort planeswalker abilities with most costly first if (sa.getRestrictions().isPwAbility()) { + final CostPart cost = sa.getPayCosts().getCostParts().get(0); + if (cost instanceof CostRemoveCounter) { + p += cost.convertAmount(); + } else if (cost instanceof CostPutCounter) { + p -= cost.convertAmount(); + } if (sa.hasParam("Ultimate")) { p += 9; } } - + if (ApiType.DestroyAll == sa.getApi()) { p += 4; } - if (ApiType.Token == sa.getApi() && noCreatures) { - //hack to force planeswalkers to defend themselves - p += 3; - } + else if (ApiType.Mana == sa.getApi()) { - p -= 9; + p -= 9; } return p; diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java index 5e2251989a3..49e03cebca7 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCost.java @@ -390,6 +390,17 @@ public class ComputerUtilCost { } } } + + // Try not to lose Planeswalker if not threatened + if (sa.getRestrictions().isPwAbility()) { + final CostPart cost = sa.getPayCosts().getCostParts().get(0); + if (cost instanceof CostRemoveCounter && cost.convertAmount() == sa.getHostCard().getCurrentLoyalty()) { + // refuse to pay if opponent has no creature threats or + if (player.getOpponent().getCreaturesInPlay().isEmpty() || MyRandom.getRandom().nextFloat() < .5f) { + return false; + } + } + } return ComputerUtilMana.canPayManaCost(sa, player, extraManaNeeded) && CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa); diff --git a/forge-ai/src/main/java/forge/ai/ability/TokenAi.java b/forge-ai/src/main/java/forge/ai/ability/TokenAi.java index 29b3e067532..a8803e1707e 100644 --- a/forge-ai/src/main/java/forge/ai/ability/TokenAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/TokenAi.java @@ -21,6 +21,8 @@ import forge.game.card.CardFactory; import forge.game.card.CardLists; import forge.game.card.CardPredicates; import forge.game.cost.Cost; +import forge.game.cost.CostPart; +import forge.game.cost.CostRemoveCounter; import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseType; import forge.game.player.Player; @@ -121,6 +123,22 @@ public class TokenAi extends SpellAbilityAi { } } + boolean pwAbility = sa.getRestrictions().isPwAbility(); + if (pwAbility) { + // Planeswalker token ability with loyalty costs should be played in Main1 or it might + // never be used due to other positive abilities. AI is kept from spamming them by the + // loyalty cost of each usage. Zero/loyalty gain token abilities can be evaluated as + // per normal. + boolean hasCost = false; + for (CostPart c : sa.getPayCosts().getCostParts()) { + if (c instanceof CostRemoveCounter) { + hasCost = true; + break; + } + } + pwAbility = hasCost; + } + PhaseHandler ph = game.getPhaseHandler(); // Don't generate tokens without haste before main 2 if possible if (ph.getPhase().isBefore(PhaseType.MAIN2) @@ -133,13 +151,13 @@ public class TokenAi extends SpellAbilityAi { buff = true; } } - if (!buff && !sacOnStack) { + if (!buff && !sacOnStack && !pwAbility) { return false; } } if ((ph.isPlayerTurn(ai) || ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) && !sa.hasParam("ActivationPhases") && !sa.hasParam("PlayerTurn") - && !SpellAbilityAi.isSorcerySpeed(sa) && !haste && !sacOnStack) { + && !SpellAbilityAi.isSorcerySpeed(sa) && !haste && !sacOnStack && !pwAbility) { return false; } if ((ph.getPhase().isAfter(PhaseType.COMBAT_BEGIN) || game.getPhaseHandler().isPlayerTurn(opp)) diff --git a/forge-gui/res/cardsfolder/g/gideon_ally_of_zendikar.txt b/forge-gui/res/cardsfolder/g/gideon_ally_of_zendikar.txt index a517da3898a..377b3417b07 100644 --- a/forge-gui/res/cardsfolder/g/gideon_ally_of_zendikar.txt +++ b/forge-gui/res/cardsfolder/g/gideon_ally_of_zendikar.txt @@ -7,6 +7,5 @@ SVar:GideonPrevent:DB$ Pump | Defined$ Self | KW$ HIDDEN Prevent all damage that A:AB$ Token | Cost$ 0 | TokenAmount$ 1 | TokenName$ Knight Ally | TokenColors$ White | TokenTypes$ Creature,Knight,Ally | TokenOwner$ You | TokenPower$ 2 | TokenToughness$ 2 | Planeswalker$ True | SpellDescription$ Put a 2/2 white Knight Ally creature token onto the battlefield. A:AB$ Effect | Cost$ SubCounter<4/LOYALTY> | Name$ Gideon, Ally of Zendikad emblem | Image$ gideon_ally_of_zendikar_emblem | StaticAbilities$ STPump | Planeswalker$ True | Ultimate$ True | Duration$ Permanent | AILogic$ Always | SpellDescription$ You get an emblem with "Creatures you control get +1/+1." SVar:STPump:Mode$ Continuous | EffectZone$ Command | AffectedZone$ Battlefield | Affected$ Creature.YouCtrl | AddPower$ 1 | AddToughness$ 1 -SVar:RemAIDeck:True SVar:Picture:http://www.wizards.com/global/images/magic/general/gideon_ally_of_zendikar.jpg Oracle:[+1] Until end of turn, Gideon, Ally of Zendikar becomes a 5/5 Human Soldier Ally creature with indestructible that is still a planeswalker. Prevent all damage that would be dealt to him this turn.\n[0] Put a 2/2 white Knight Ally creature token onto the battlefield.\n[-4] You get an emblem with "Creatures you control get +1/+1."