Add ai usage for Maze of Ith

This commit is contained in:
friarsol
2023-08-08 21:09:57 -04:00
committed by Chris H
parent 626a8bf020
commit efc3c2c159
5 changed files with 121 additions and 5 deletions

View File

@@ -223,7 +223,7 @@ public class ComputerUtilCombat {
int damage = attacker.getNetCombatDamage(); int damage = attacker.getNetCombatDamage();
int poison = 0; int poison = 0;
damage += predictPowerBonusOfAttacker(attacker, null, null, false); damage += predictPowerBonusOfAttacker(attacker, null, null, false);
if (attacker.hasKeyword(Keyword.INFECT)) { if (attacker.hasKeyword(Keyword.INFECT) || attacker.hasKeyword(Keyword.TOXIC)) {
int pd = predictDamageTo(attacked, damage, attacker, true); int pd = predictDamageTo(attacked, damage, attacker, true);
// opponent can always order it so that he gets 0 // opponent can always order it so that he gets 0
if (pd == 1 && Iterables.any(attacker.getController().getOpponents().getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Vorinclex, Monstrous Raider"))) { if (pd == 1 && Iterables.any(attacker.getController().getOpponents().getCardsIn(ZoneType.Battlefield), CardPredicates.nameEquals("Vorinclex, Monstrous Raider"))) {
@@ -2390,6 +2390,62 @@ public class ComputerUtilCombat {
return categorizedAttackers; return categorizedAttackers;
} }
public static Card mostDangerousAttacker(CardCollection list, Player ai, Combat combat, boolean withAbilities) {
Card damageCard = null;
Card poisonCard = null;
int damageScore = 0;
int poisonScore = 0;
for(Card c : list) {
int estimatedDmg = damageIfUnblocked(c, ai, combat, withAbilities);
int estimatedPoison = poisonIfUnblocked(c, ai);
if (combat.isBlocked(c)) {
if (!c.hasKeyword(Keyword.TRAMPLE)) {
continue;
}
int absorbedByToughness = 0;
for (Card blocker : combat.getBlockers(c)) {
absorbedByToughness += blocker.getNetToughness();
}
estimatedPoison -= absorbedByToughness;
estimatedDmg -= absorbedByToughness;
}
if (estimatedDmg > damageScore) {
damageScore = estimatedDmg;
damageCard = c;
}
if (estimatedPoison > poisonScore) {
poisonScore = estimatedPoison;
poisonCard = c;
}
}
if (damageCard == null && poisonCard == null) {
return null;
} else if (damageCard == null) {
return poisonCard;
} else if (poisonCard == null) {
return damageCard;
}
int life = ai.getLife();
int poisonLife = 10 - ai.getPoisonCounters();
double percentLife = life * 1.0 / damageScore;
double percentPoison = poisonLife * 1.0 / poisonScore;
if (percentLife >= percentPoison) {
return damageCard;
} else {
return poisonCard;
}
}
public static Card applyPotentialAttackCloneTriggers(Card attacker) { public static Card applyPotentialAttackCloneTriggers(Card attacker) {
// This method returns the potentially cloned card if the creature turns into something else during the attack // This method returns the potentially cloned card if the creature turns into something else during the attack
// (currently looks for the creature with maximum raw power since that's what the AI usually judges by when // (currently looks for the creature with maximum raw power since that's what the AI usually judges by when

View File

@@ -13,6 +13,7 @@ import forge.game.card.CardCollection;
import forge.game.card.CardLists; import forge.game.card.CardLists;
import forge.game.card.CardPredicates; import forge.game.card.CardPredicates;
import forge.game.card.CardPredicates.Presets; import forge.game.card.CardPredicates.Presets;
import forge.game.combat.Combat;
import forge.game.cost.Cost; import forge.game.cost.Cost;
import forge.game.cost.CostTap; import forge.game.cost.CostTap;
import forge.game.mana.ManaCostBeingPaid; import forge.game.mana.ManaCostBeingPaid;
@@ -37,6 +38,10 @@ public class UntapAi extends SpellAbilityAi {
return false; return false;
} else if ("PoolExtraMana".equals(aiLogic)) { } else if ("PoolExtraMana".equals(aiLogic)) {
return doPoolExtraManaLogic(ai, sa); return doPoolExtraManaLogic(ai, sa);
} else if ("PreventCombatDamage".equals(aiLogic)) {
return doPreventCombatDamageLogic(ai, sa);
// In the future if you want to give Pseudo vigilance to a creature you attacked with
// activate during your own during the end of combat step
} }
return !("Never".equals(aiLogic)); return !("Never".equals(aiLogic));
@@ -63,6 +68,8 @@ public class UntapAi extends SpellAbilityAi {
final List<Card> pDefined = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa); final List<Card> pDefined = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
return pDefined.isEmpty() || (pDefined.get(0).isTapped() && pDefined.get(0).getController() == ai); return pDefined.isEmpty() || (pDefined.get(0).isTapped() && pDefined.get(0).getController() == ai);
} else { } else {
// If we already selected a target just use that
return untapPrefTargeting(ai, sa, false); return untapPrefTargeting(ai, sa, false);
} }
} }
@@ -118,6 +125,13 @@ public class UntapAi extends SpellAbilityAi {
*/ */
private static boolean untapPrefTargeting(final Player ai, final SpellAbility sa, final boolean mandatory) { private static boolean untapPrefTargeting(final Player ai, final SpellAbility sa, final boolean mandatory) {
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
if (alreadyAssignedTarget(sa)) {
if (sa.getTargets().size() > 0) {
// If we selected something lets assume its valid
return true;
}
}
sa.resetTargets(); sa.resetTargets();
final PlayerCollection targetController = new PlayerCollection(); final PlayerCollection targetController = new PlayerCollection();
@@ -337,6 +351,50 @@ public class UntapAi extends SpellAbilityAi {
return null; return null;
} }
private boolean doPreventCombatDamageLogic(final Player ai, final SpellAbility sa) {
// Only Maze of Ith and Maze of Shadows uses this. Feel free to use it aggressively.
Game game = ai.getGame();
Card source = sa.getHostCard();
sa.resetTargets();
if (!game.getPhaseHandler().getPlayerTurn().isOpponentOf(ai)) {
return false;
}
// If damage can't be prevented. Just return false.
Combat activeCombat = game.getCombat();
if (activeCombat == null) {
return false;
}
CardCollection list = CardLists.getTargetableCards(activeCombat.getAttackers(), sa);
if (list.isEmpty()) {
return false;
}
if (game.getPhaseHandler().getPhase().equals(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
// Blockers already set. Are there any dangerous unblocked creatures? Sort by creature that will deal the most damage?
Card card = ComputerUtilCombat.mostDangerousAttacker(list, ai, activeCombat, true);
if (card == null) { return false; }
sa.getTargets().add(card);
return true;
}
return false;
}
private static boolean alreadyAssignedTarget(final SpellAbility sa) {
if (sa.hasParam("AILogic")) {
String aiLogic = sa.getParam("AILogic");
return "PreventCombatDamage".equals(aiLogic);
}
return false;
}
private boolean doPoolExtraManaLogic(final Player ai, final SpellAbility sa) { private boolean doPoolExtraManaLogic(final Player ai, final SpellAbility sa) {
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
final PhaseHandler ph = source.getGame().getPhaseHandler(); final PhaseHandler ph = source.getGame().getPhaseHandler();

View File

@@ -1,9 +1,9 @@
Name:Maze of Ith Name:Maze of Ith
ManaCost:no cost ManaCost:no cost
Types:Land Types:Land
A:AB$ Untap | Cost$ T | ValidTgts$ Creature.attacking | TgtPrompt$ Select target attacking creature | SubAbility$ DBPump | SpellDescription$ Untap target attacking creature. A:AB$ Untap | Cost$ T | ValidTgts$ Creature.attacking | TgtPrompt$ Select target attacking creature | AILogic$ PreventCombatDamage | SubAbility$ DBPump | SpellDescription$ Untap target attacking creature.
SVar:DBPump:DB$ Effect | ReplacementEffects$ RPrevent1,RPrevent2 | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | SpellDescription$ Prevent all combat damage that would be dealt to and dealt by that creature this turn. SVar:DBPump:DB$ Effect | ReplacementEffects$ RPrevent1,RPrevent2 | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | SpellDescription$ Prevent all combat damage that would be dealt to and dealt by that creature this turn.
SVar:RPrevent1:Event$ DamageDone | Prevent$ True | IsCombat$ True | ValidSource$ Card.IsRemembered | Description$ Prevent all combat damage that would be dealt to and dealt by that creature this turn. SVar:RPrevent1:Event$ DamageDone | Prevent$ True | IsCombat$ True | ValidSource$ Card.IsRemembered | Description$ Prevent all combat damage that would be dealt to and dealt by that creature this turn.
SVar:RPrevent2:Event$ DamageDone | Prevent$ True | IsCombat$ True | ValidTarget$ Card.IsRemembered | Description$ Prevent all combat damage that would be dealt to and dealt by that creature this turn. | Secondary$ True SVar:RPrevent2:Event$ DamageDone | Prevent$ True | IsCombat$ True | ValidTarget$ Card.IsRemembered | Description$ Prevent all combat damage that would be dealt to and dealt by that creature this turn. | Secondary$ True
AI:RemoveDeck:All AI:RemoveDeck:Random
Oracle:{T}: Untap target attacking creature. Prevent all combat damage that would be dealt to and dealt by that creature this turn. Oracle:{T}: Untap target attacking creature. Prevent all combat damage that would be dealt to and dealt by that creature this turn.

View File

@@ -2,9 +2,9 @@ Name:Maze of Shadows
ManaCost:no cost ManaCost:no cost
Types:Land Types:Land
A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}. A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}.
A:AB$ Untap | Cost$ T | ValidTgts$ Creature.attacking+withShadow | TgtPrompt$ Select target attacking creature with shadow | SubAbility$ DBPump | SpellDescription$ Untap target attacking creature with shadow. A:AB$ Untap | Cost$ T | ValidTgts$ Creature.attacking+withShadow | TgtPrompt$ Select target attacking creature with shadow | AILogic$ PreventCombatDamage | SubAbility$ DBPump | SpellDescription$ Untap target attacking creature with shadow.
SVar:DBPump:DB$ Effect | ReplacementEffects$ RPrevent1,RPrevent2 | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | SpellDescription$ Prevent all combat damage that would be dealt to and dealt by that creature this turn. SVar:DBPump:DB$ Effect | ReplacementEffects$ RPrevent1,RPrevent2 | RememberObjects$ Targeted | ExileOnMoved$ Battlefield | SpellDescription$ Prevent all combat damage that would be dealt to and dealt by that creature this turn.
SVar:RPrevent1:Event$ DamageDone | Prevent$ True | IsCombat$ True | ValidSource$ Card.IsRemembered | Description$ Prevent all combat damage that would be dealt to and dealt by that creature this turn. SVar:RPrevent1:Event$ DamageDone | Prevent$ True | IsCombat$ True | ValidSource$ Card.IsRemembered | Description$ Prevent all combat damage that would be dealt to and dealt by that creature this turn.
SVar:RPrevent2:Event$ DamageDone | Prevent$ True | IsCombat$ True | ValidTarget$ Card.IsRemembered | Description$ Prevent all combat damage that would be dealt to and dealt by that creature this turn. | Secondary$ True SVar:RPrevent2:Event$ DamageDone | Prevent$ True | IsCombat$ True | ValidTarget$ Card.IsRemembered | Description$ Prevent all combat damage that would be dealt to and dealt by that creature this turn. | Secondary$ True
AI:RemoveDeck:All AI:RemoveDeck:Random
Oracle:{T}: Add {C}.\n{T}: Untap target attacking creature with shadow. Prevent all combat damage that would be dealt to and dealt by that creature this turn. Oracle:{T}: Add {C}.\n{T}: Untap target attacking creature with shadow. Prevent all combat damage that would be dealt to and dealt by that creature this turn.

View File

@@ -4,4 +4,6 @@ Types:Land
A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}. A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}.
S:Mode$ Continuous | EffectZone$ Graveyard | Affected$ Land.YouCtrl | AddAbility$ AddMana | Description$ As long as Riftstone Portal is in your graveyard, lands you control have "{T}: Add {G} or {W}." S:Mode$ Continuous | EffectZone$ Graveyard | Affected$ Land.YouCtrl | AddAbility$ AddMana | Description$ As long as Riftstone Portal is in your graveyard, lands you control have "{T}: Add {G} or {W}."
SVar:AddMana:AB$ Mana | Cost$ T | Produced$ Combo G W | SpellDescription$ Add {G} or {W}. SVar:AddMana:AB$ Mana | Cost$ T | Produced$ Combo G W | SpellDescription$ Add {G} or {W}.
SVar:SacMe:2
SVar:DiscardMe:1
Oracle:{T}: Add {C}.\nAs long as Riftstone Portal is in your graveyard, lands you control have "{T}: Add {G} or {W}." Oracle:{T}: Add {C}.\nAs long as Riftstone Portal is in your graveyard, lands you control have "{T}: Add {G} or {W}."