AiController & CountersProliferateAi: be more fieldly with ally controlled creatures

chooseProliferation now works with Energy counters on players and has extra logic for Vanishing and Fading
CountersProliferateAi only checks the Counters the card really has
This commit is contained in:
Hanmac
2016-10-30 09:24:17 +00:00
parent 37f791ab4b
commit f28424ea9b
2 changed files with 167 additions and 112 deletions

View File

@@ -18,23 +18,21 @@
package forge.ai;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.esotericsoftware.minlog.Log;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import forge.ai.simulation.SpellAbilityPicker;
import forge.card.CardStateName;
@@ -162,7 +160,7 @@ public class AiController {
all.addAll(opp.getCardsIn(ZoneType.Exile));
}
final List<SpellAbility> spellAbilities = new ArrayList<SpellAbility>();
final List<SpellAbility> spellAbilities = Lists.newArrayList();
for (final Card c : all) {
for (final SpellAbility sa : c.getNonManaAbilities()) {
if (sa instanceof SpellPermanent) {
@@ -363,7 +361,7 @@ public class AiController {
}
private static List<SpellAbility> getPlayableCounters(final CardCollection l) {
final List<SpellAbility> spellAbility = new ArrayList<SpellAbility>();
final List<SpellAbility> spellAbility = Lists.newArrayList();
for (final Card c : l) {
for (final SpellAbility sa : c.getNonManaAbilities()) {
// Check if this AF is a Counterpsell
@@ -528,7 +526,7 @@ public class AiController {
//play lands with a basic type that is needed the most
final CardCollectionView landsInBattlefield = player.getCardsIn(ZoneType.Battlefield);
final List<String> basics = new ArrayList<String>();
final List<String> basics = Lists.newArrayList();
// what types can I go get?
for (final String name : MagicColor.Constant.BASIC_LANDS) {
@@ -1267,7 +1265,7 @@ public class AiController {
if (ComputerUtil.getDamageFromETB(player, land) < player.getLife() || !player.canLoseLife()
|| player.cantLoseForZeroOrLessLife() ) {
game.PLAY_LAND_SURROGATE.setHostCard(land);
final List<SpellAbility> abilities = new ArrayList<SpellAbility>();
final List<SpellAbility> abilities = Lists.newArrayList();
abilities.add(game.PLAY_LAND_SURROGATE);
return abilities;
}
@@ -1279,7 +1277,7 @@ public class AiController {
// System.out.println("Chosen to play: " + sa);
final List<SpellAbility> abilities = new ArrayList<SpellAbility>();
final List<SpellAbility> abilities = Lists.newArrayList();
abilities.add(sa);
return abilities;
}
@@ -1471,7 +1469,7 @@ public class AiController {
public List<SpellAbility> chooseSaToActivateFromOpeningHand(List<SpellAbility> usableFromOpeningHand) {
// AI would play everything. But limits to one copy of (Leyline of Singularity) and (Gemstone Caverns)
List<SpellAbility> result = new ArrayList<SpellAbility>();
List<SpellAbility> result = Lists.newArrayList();
for(SpellAbility sa : usableFromOpeningHand) {
// Is there a better way for the AI to decide this?
if (doTrigger(sa, false)) {
@@ -1556,7 +1554,7 @@ public class AiController {
}
public Map<GameEntity, CounterType> chooseProliferation() {
final Map<GameEntity, CounterType> result = new HashMap<>();
final Map<GameEntity, CounterType> result = Maps.newHashMap();
final List<Player> allies = player.getAllies();
allies.add(player);
@@ -1564,7 +1562,31 @@ public class AiController {
final Function<Card, CounterType> predProliferate = new Function<Card, CounterType>() {
@Override
public CounterType apply(Card crd) {
//fast way out, no need to check other stuff
if (!crd.hasCounters()) {
return null;
}
// cards controlled by ai or ally with Vanishing or Fading
// and exaclty one counter of the specifice type gets high priority to keep the card
if (allies.contains(crd.getController())) {
// except if its a Chronozoa, because it WANTS to be removed to make more
if (crd.hasKeyword("Vanishing") && !"Chronozoa".equals(crd.getName())) {
if (crd.getCounters(CounterType.TIME) == 1) {
return CounterType.TIME;
}
} else if (crd.hasKeyword("Fading")) {
if (crd.getCounters(CounterType.FADE) == 1) {
return CounterType.FADE;
}
}
}
for (final Entry<CounterType, Integer> c1 : crd.getCounters().entrySet()) {
// if card can not recive the given counter, try another one
if (!crd.canReceiveCounters(c1.getKey())) {
continue;
}
if (ComputerUtil.isNegativeCounter(c1.getKey(), crd) && enemies.contains(crd.getController())) {
return c1.getKey();
}
@@ -1592,6 +1614,8 @@ public class AiController {
for (Player pl : allies) {
if (pl.getCounters(CounterType.EXPERIENCE) > 0) {
result.put(pl, CounterType.EXPERIENCE);
} else if (pl.getCounters(CounterType.ENERGY) > 0) {
result.put(pl, CounterType.ENERGY);
}
}
@@ -1645,7 +1669,7 @@ public class AiController {
}
public Collection<? extends PaperCard> complainCardsCantPlayWell(Deck myDeck) {
List<PaperCard> result = new ArrayList<PaperCard>();
List<PaperCard> result = Lists.newArrayList();
for (Entry<DeckSection, CardPool> ds : myDeck) {
for (Entry<PaperCard, Integer> cp : ds.getValue()) {
if (cp.getKey().getRules().getAiHints().getRemAIDecks())

View File

@@ -1,6 +1,8 @@
package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists;
import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
@@ -11,6 +13,7 @@ import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
import java.util.List;
import java.util.Map;
public class CountersProliferateAi extends SpellAbilityAi {
@@ -18,31 +21,59 @@ public class CountersProliferateAi extends SpellAbilityAi {
protected boolean canPlayAI(Player ai, SpellAbility sa) {
boolean chance = true;
List<Card> cperms = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
final List<Card> cperms = Lists.newArrayList();
final List<Player> allies = ai.getAllies();
allies.add(ai);
boolean allyExpOrEnergy = false;
for (final Player p : allies) {
// player has experience or energy counter
if (p.getCounters(CounterType.EXPERIENCE) + p.getCounters(CounterType.ENERGY) >= 1) {
allyExpOrEnergy = true;
}
cperms.addAll(CardLists.filter(p.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
@Override
public boolean apply(final Card crd) {
for (final CounterType c1 : CounterType.values()) {
if (crd.getCounters(c1) != 0 && !ComputerUtil.isNegativeCounter(c1, crd)) {
if (crd.hasCounters()) {
return false;
}
// iterate only over existing counters
for (final Map.Entry<CounterType, Integer> e : crd.getCounters().entrySet()) {
if (e.getValue() >= 1 && !ComputerUtil.isNegativeCounter(e.getKey(), crd)) {
return true;
}
}
return false;
}
});
}));
}
List<Card> hperms = CardLists.filter(ai.getOpponent().getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
final List<Card> hperms = Lists.newArrayList();
boolean opponentPoison = false;
for (final Player o : ai.getOpponents()) {
opponentPoison |= ai.getOpponent().getPoisonCounters() >= 1;
hperms.addAll(CardLists.filter(o.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() {
@Override
public boolean apply(final Card crd) {
for (final CounterType c1 : CounterType.values()) {
if (crd.getCounters(c1) != 0 && ComputerUtil.isNegativeCounter(c1, crd)) {
if (crd.hasCounters()) {
return false;
}
// iterate only over existing counters
for (final Map.Entry<CounterType, Integer> e : crd.getCounters().entrySet()) {
if (e.getValue() >= 1 && ComputerUtil.isNegativeCounter(e.getKey(), crd)) {
return true;
}
}
return false;
}
});
}));
}
if (cperms.isEmpty() && hperms.isEmpty() && ai.getOpponent().getPoisonCounters() == 0) {
if (cperms.isEmpty() && hperms.isEmpty() && !opponentPoison && !allyExpOrEnergy) {
return false;
}
return chance;