mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 20:58:03 +00:00
- Make the AI try to determine whether to play a shockland untapped or tapped in a smarter way by evaluating the availability of mana colors to pay for the spell as well as potential targets for auras.
This commit is contained in:
@@ -1942,4 +1942,44 @@ public class ComputerUtil {
|
|||||||
return bestBoardPosition;
|
return bestBoardPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean hasGoodTargetForAura(final Player ai, final Card aura) {
|
||||||
|
// This is currently used by ComputerUtilCost.willPayUnlessCost to determine if there's a viable target for a spell
|
||||||
|
// that can be paid for with an untapped shockland.
|
||||||
|
|
||||||
|
if (ai == null || aura == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aura.getFirstAttachSpell() == null) {
|
||||||
|
// Something went majorly wrong here, should never happen
|
||||||
|
System.err.println("Unexpected behavior: first attach spell for Aura card " + aura.getName() + " was null in ComputerUtil::hasGoodTargetForAura.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean hasTarget = false;
|
||||||
|
boolean aiHasTargets = false, oppHasTargets = false;
|
||||||
|
|
||||||
|
aiHasTargets = !CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.isTargetableBy(aura.getFirstAttachSpell())).isEmpty();
|
||||||
|
for (Player p : ai.getOpponents()) {
|
||||||
|
if (!CardLists.filter(p.getCardsIn(ZoneType.Battlefield), CardPredicates.isTargetableBy(aura.getFirstAttachSpell())).isEmpty()) {
|
||||||
|
oppHasTargets = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isCurse = false;
|
||||||
|
for (SpellAbility ability : aura.getAllSpellAbilities()) {
|
||||||
|
if (ability.isCurse() || (ability.hasParam("AILogic") && ability.getParam("AILogic").equals("Curse"))) {
|
||||||
|
isCurse = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isCurse && oppHasTargets) {
|
||||||
|
hasTarget = true;
|
||||||
|
} else if (!isCurse && aiHasTargets) {
|
||||||
|
hasTarget = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasTarget;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package forge.ai;
|
package forge.ai;
|
||||||
|
|
||||||
|
import com.google.common.collect.Sets;
|
||||||
|
import forge.card.ColorSet;
|
||||||
|
import forge.game.GameActionUtil;
|
||||||
import forge.game.ability.AbilityUtils;
|
import forge.game.ability.AbilityUtils;
|
||||||
import forge.game.card.Card;
|
import forge.game.card.Card;
|
||||||
import forge.game.card.CardCollection;
|
import forge.game.card.CardCollection;
|
||||||
|
import forge.game.card.CardFactory;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CounterType;
|
import forge.game.card.CounterType;
|
||||||
import forge.game.combat.Combat;
|
import forge.game.combat.Combat;
|
||||||
@@ -14,6 +18,10 @@ import forge.game.zone.ZoneType;
|
|||||||
import forge.util.collect.FCollectionView;
|
import forge.util.collect.FCollectionView;
|
||||||
import forge.util.MyRandom;
|
import forge.util.MyRandom;
|
||||||
import forge.util.TextUtil;
|
import forge.util.TextUtil;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
@@ -414,7 +422,15 @@ public class ComputerUtilCost {
|
|||||||
// If the new land size would equal the CMC of a card in AIs hand, play it untapped
|
// If the new land size would equal the CMC of a card in AIs hand, play it untapped
|
||||||
final int landsize = payer.getLandsInPlay().size() + 1;
|
final int landsize = payer.getLandsInPlay().size() + 1;
|
||||||
for (Card c : payer.getCardsIn(ZoneType.Hand)) {
|
for (Card c : payer.getCardsIn(ZoneType.Hand)) {
|
||||||
if (landsize == c.getCMC()) {
|
int cmc = c.getCMC();
|
||||||
|
// try to determine if the mana shards provided by the lands would be applicable to pay the mana cost
|
||||||
|
boolean canPay = c.getManaCost().canBePaidWithAvaliable(ColorSet.fromNames(getAvailableManaColors(payer, source)).getColor());
|
||||||
|
// try to determine that there is a valid target for a spell (very basic, consider expanding)
|
||||||
|
boolean requirementsMet = true;
|
||||||
|
if (c.isAura()) {
|
||||||
|
requirementsMet = ComputerUtil.hasGoodTargetForAura(payer, c);
|
||||||
|
}
|
||||||
|
if (landsize == cmc && canPay && requirementsMet) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -466,4 +482,29 @@ public class ComputerUtilCost {
|
|||||||
&& (!source.getName().equals("Chain of Vapor") || (payer.getOpponent().getCreaturesInPlay().size() > 0 && payer.getLandsInPlay().size() > 3));
|
&& (!source.getName().equals("Chain of Vapor") || (payer.getOpponent().getCreaturesInPlay().size() > 0 && payer.getLandsInPlay().size() > 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Set<String> getAvailableManaColors(Player ai, Card additionalLand) {
|
||||||
|
return getAvailableManaColors(ai, Sets.newHashSet(additionalLand));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<String> getAvailableManaColors(Player ai, Set<Card> additionalLands) {
|
||||||
|
CardCollection lands = ai.getLandsInPlay();
|
||||||
|
Set<String> colorsAvailable = new HashSet<>();
|
||||||
|
|
||||||
|
if (additionalLands != null) {
|
||||||
|
List<Card> additionalLandCopies = new ArrayList<>();
|
||||||
|
for (Card c : additionalLands) {
|
||||||
|
additionalLandCopies.add(CardFactory.copyCard(c, true));
|
||||||
|
}
|
||||||
|
GameActionUtil.grantBasicLandsManaAbilities(additionalLandCopies);
|
||||||
|
lands.addAll(additionalLandCopies);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Card c : lands) {
|
||||||
|
for (SpellAbility sa : c.getManaAbilities()) {
|
||||||
|
colorsAvailable.add(sa.getManaPart().getOrigProduced());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return colorsAvailable;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user