- Improved Yawgmoth's Will AI.

- Moved it to SpecialCardAi and named the AI logic after the card for now because it looks like the effect is rather unique in the card pool.
This commit is contained in:
Agetian
2017-01-19 14:46:33 +00:00
parent 5277ab4627
commit 69261f48fe
3 changed files with 61 additions and 29 deletions

View File

@@ -38,6 +38,8 @@ import forge.game.phase.PhaseType;
import forge.game.player.Player;
import forge.game.player.PlayerPredicates;
import forge.game.spellability.SpellAbility;
import forge.game.spellability.SpellAbilityRestriction;
import forge.game.spellability.SpellPermanent;
import forge.game.zone.ZoneType;
import forge.util.Aggregates;
import java.util.Collections;
@@ -459,4 +461,58 @@ public class SpecialCardAi {
}
}
// Yawgmoth's Will (can potentially be expanded for other broadly similar effects too)
public static class YawgmothsWill {
public static boolean consider(Player ai, SpellAbility sa) {
CardCollectionView cardsInGY = ai.getCardsIn(ZoneType.Graveyard);
if (cardsInGY.size() == 0) {
return false;
}
int minManaAdj = 2; // we want the AI to have some spare mana for possible other spells to cast
float minCastableInGY = 3.0f; // we want the AI to have several castable cards in GY before attempting this effect
List<SpellAbility> saList = ComputerUtilAbility.getSpellAbilities(cardsInGY, ai);
int selfCMC = sa.getPayCosts().getCostMana().getMana().getCMC();
float numCastable = 0.0f;
for (SpellAbility ab : saList) {
final Card src = ab.getHostCard();
if (ab.getApi() == ApiType.Counter) {
// cut short considering to play counterspells via Yawgmoth's Will
continue;
}
if (ab.getHostCard().getName().equals(sa.getHostCard().getName())) {
// prevent infinitely recursing own ability when testing AI play decision
continue;
}
// check to see if the AI is willing to play this card
final SpellAbility testAb = ab.copy();
testAb.getRestrictions().setZone(ZoneType.Graveyard);
testAb.setActivatingPlayer(ai);
boolean willPlayAb = ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(testAb) == AiPlayDecision.WillPlay;
// Land drops are generally made by the AI in main 1 before casting spells, so testing for them is iffy.
if (!src.getType().isLand() && willPlayAb) {
int CMC = ab.getPayCosts().getTotalMana() != null ? ab.getPayCosts().getTotalMana().getCMC() : 0;
int Xcount = ab.getPayCosts().getTotalMana() != null ? ab.getPayCosts().getTotalMana().countX() : 0;
if ((Xcount == 0 && CMC == 0) || ComputerUtilMana.canPayManaCost(ab, ai, selfCMC + minManaAdj)) {
if (src.isInstant() || src.isSorcery()) {
// instants and sorceries are one-shot, so only treat them as 1/2 value for the purpose of meeting minimum
// castable cards in graveyard requirements
numCastable += 0.5f;
} else {
numCastable += 1.0f;
}
}
}
}
return numCastable >= minCastableInGY;
}
}
}

View File

@@ -7,7 +7,9 @@ import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilAbility;
import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCombat;
import forge.ai.ComputerUtilDeck;
import forge.ai.ComputerUtilMana;
import forge.ai.SpecialCardAi;
import forge.ai.SpellAbilityAi;
import forge.ai.SpellApiToAi;
import forge.game.Game;
@@ -216,34 +218,8 @@ public class EffectAi extends SpellAbilityAi {
// for DamageDeal sub-abilities (eg. Wild Slash, Skullcrack)
SpellAbility burn = sa.getSubAbility();
return SpellApiToAi.Converter.get(burn.getApi()).canPlayAIWithSubs(ai, burn);
} else if (logic.equals("CastFromGraveyardUntilEOT")) {
// Effects that allow you to play stuff from graveyard until end of turn (e.g. Yawgmoth's Will)
CardCollectionView cardsInGY = ai.getCardsIn(ZoneType.Graveyard);
if (cardsInGY.size() == 0) {
return false;
}
int minManaAdj = 2; // we want the AI to have some spare mana for possible other spells to cast from GY/hand
int minCastableInGY = 3; // we want the AI to have several castable cards in GY before attempting an effect
List<SpellAbility> saList = ComputerUtilAbility.getSpellAbilities(cardsInGY, ai);
int selfCMC = sa.getPayCosts().getCostMana().getMana().getCMC();
// Currently limited to considering nonland permanents, since instants and sorceries may be very contextual
// and land drops are generally made by the AI in main 1 before casting spells, so testing for them is iffy.
int numCastable = 0;
for (SpellAbility ab : saList) {
final Card src = ab.getHostCard();
if (ab instanceof SpellPermanent && !src.getType().isLand()) {
int CMC = ab.getPayCosts().getTotalMana() != null ? ab.getPayCosts().getTotalMana().getCMC() : 0;
int Xcount = ab.getPayCosts().getTotalMana() != null ? ab.getPayCosts().getTotalMana().countX() : 0;
if ((Xcount == 0 && CMC == 0) || ComputerUtilMana.canPayManaCost(ab, ai, selfCMC + minManaAdj)) {
numCastable++;
}
}
}
return numCastable >= minCastableInGY;
} else if (logic.equals("YawgmothsWill")) {
return SpecialCardAi.YawgmothsWill.consider(ai, sa);
}
} else { //no AILogic
return false;

View File

@@ -1,7 +1,7 @@
Name:Yawgmoth's Will
ManaCost:2 B
Types:Sorcery
A:SP$ Effect | Cost$ 2 B | Name$ Yawgmoth's Will Effect | ReplacementEffects$ GraveToExile | StaticAbilities$ STPlay | SVars$ Exile | AILogic$ CastFromGraveyardUntilEOT | SpellDescription$ Until end of turn, you may play cards from your graveyard. If a card would be put into your graveyard from anywhere this turn, exile that card instead.
A:SP$ Effect | Cost$ 2 B | Name$ Yawgmoth's Will Effect | ReplacementEffects$ GraveToExile | StaticAbilities$ STPlay | SVars$ Exile | AILogic$ YawgmothsWill | SpellDescription$ Until end of turn, you may play cards from your graveyard. If a card would be put into your graveyard from anywhere this turn, exile that card instead.
SVar:STPlay:Mode$ Continuous | EffectZone$ Command | Affected$ Card.YouCtrl | AffectedZone$ Graveyard | MayPlay$ True | Description$ You may play cards from your graveyard.
SVar:GraveToExile:Event$ Moved | ActiveZones$ Command | Destination$ Graveyard | ValidCard$ Card.nonToken+YouOwn | ReplaceWith$ Exile | Description$ If a card would be put into your graveyard from anywhere, exile it instead.
SVar:Exile:AB$ ChangeZone | Cost$ 0 | Hidden$ True | Origin$ All | Destination$ Exile | Defined$ ReplacedCard