- 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
This commit is contained in:
excessum
2015-10-17 07:00:45 +00:00
parent e2ba8fe0de
commit db3286bec6
4 changed files with 43 additions and 10 deletions

View File

@@ -64,6 +64,8 @@ import forge.game.combat.CombatUtil;
import forge.game.cost.Cost; import forge.game.cost.Cost;
import forge.game.cost.CostDiscard; import forge.game.cost.CostDiscard;
import forge.game.cost.CostPart; import forge.game.cost.CostPart;
import forge.game.cost.CostPutCounter;
import forge.game.cost.CostRemoveCounter;
import forge.game.mana.ManaCostBeingPaid; import forge.game.mana.ManaCostBeingPaid;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; import forge.game.player.Player;
@@ -857,8 +859,14 @@ public class AiController {
if ("True".equals(source.getSVar("NonStackingEffect")) && ai.isCardInPlay(source.getName())) { if ("True".equals(source.getSVar("NonStackingEffect")) && ai.isCardInPlay(source.getName())) {
p -= 9; p -= 9;
} }
// sort planeswalker abilities for ultimate // sort planeswalker abilities with most costly first
if (sa.getRestrictions().isPwAbility()) { 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")) { if (sa.hasParam("Ultimate")) {
p += 9; p += 9;
} }
@@ -867,12 +875,9 @@ public class AiController {
if (ApiType.DestroyAll == sa.getApi()) { if (ApiType.DestroyAll == sa.getApi()) {
p += 4; p += 4;
} }
if (ApiType.Token == sa.getApi() && noCreatures) {
//hack to force planeswalkers to defend themselves
p += 3;
}
else if (ApiType.Mana == sa.getApi()) { else if (ApiType.Mana == sa.getApi()) {
p -= 9; p -= 9;
} }
return p; return p;

View File

@@ -391,6 +391,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) return ComputerUtilMana.canPayManaCost(sa, player, extraManaNeeded)
&& CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa); && CostPayment.canPayAdditionalCosts(sa.getPayCosts(), sa);
} // canPayCost() } // canPayCost()

View File

@@ -21,6 +21,8 @@ import forge.game.card.CardFactory;
import forge.game.card.CardLists; import forge.game.card.CardLists;
import forge.game.card.CardPredicates; import forge.game.card.CardPredicates;
import forge.game.cost.Cost; import forge.game.cost.Cost;
import forge.game.cost.CostPart;
import forge.game.cost.CostRemoveCounter;
import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseHandler;
import forge.game.phase.PhaseType; import forge.game.phase.PhaseType;
import forge.game.player.Player; 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(); PhaseHandler ph = game.getPhaseHandler();
// Don't generate tokens without haste before main 2 if possible // Don't generate tokens without haste before main 2 if possible
if (ph.getPhase().isBefore(PhaseType.MAIN2) if (ph.getPhase().isBefore(PhaseType.MAIN2)
@@ -133,13 +151,13 @@ public class TokenAi extends SpellAbilityAi {
buff = true; buff = true;
} }
} }
if (!buff && !sacOnStack) { if (!buff && !sacOnStack && !pwAbility) {
return false; return false;
} }
} }
if ((ph.isPlayerTurn(ai) || ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) if ((ph.isPlayerTurn(ai) || ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS))
&& !sa.hasParam("ActivationPhases") && !sa.hasParam("PlayerTurn") && !sa.hasParam("ActivationPhases") && !sa.hasParam("PlayerTurn")
&& !SpellAbilityAi.isSorcerySpeed(sa) && !haste && !sacOnStack) { && !SpellAbilityAi.isSorcerySpeed(sa) && !haste && !sacOnStack && !pwAbility) {
return false; return false;
} }
if ((ph.getPhase().isAfter(PhaseType.COMBAT_BEGIN) || game.getPhaseHandler().isPlayerTurn(opp)) if ((ph.getPhase().isAfter(PhaseType.COMBAT_BEGIN) || game.getPhaseHandler().isPlayerTurn(opp))

View File

@@ -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$ 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." 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: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 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." 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."