mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 18:58:00 +00:00
Chain comparators correctly
This commit is contained in:
@@ -655,10 +655,11 @@ public class AiController {
|
|||||||
List<SpellAbility> all = ComputerUtilAbility.getSpellAbilities(cards, player);
|
List<SpellAbility> all = ComputerUtilAbility.getSpellAbilities(cards, player);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Collections.sort(all, saComparator); // put best spells first
|
Collections.sort(all, ComputerUtilAbility.saEvaluator); // put best spells first
|
||||||
|
ComputerUtilAbility.sortCreatureSpells(all);
|
||||||
} catch (IllegalArgumentException ex) {
|
} catch (IllegalArgumentException ex) {
|
||||||
System.err.println(ex.getMessage());
|
System.err.println(ex.getMessage());
|
||||||
String assertex = ComparatorUtil.verifyTransitivity(saComparator, all);
|
String assertex = ComparatorUtil.verifyTransitivity(ComputerUtilAbility.saEvaluator, all);
|
||||||
Sentry.captureMessage(ex.getMessage() + "\nAssertionError [verifyTransitivity]: " + assertex);
|
Sentry.captureMessage(ex.getMessage() + "\nAssertionError [verifyTransitivity]: " + assertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1016,167 +1017,6 @@ public class AiController {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// not sure "playing biggest spell" matters?
|
|
||||||
private final static Comparator<SpellAbility> saComparator = new Comparator<SpellAbility>() {
|
|
||||||
@Override
|
|
||||||
public int compare(final SpellAbility a, final SpellAbility b) {
|
|
||||||
// sort from highest cost to lowest
|
|
||||||
// we want the highest costs first
|
|
||||||
int a1 = a.getPayCosts().getTotalMana().getCMC();
|
|
||||||
int b1 = b.getPayCosts().getTotalMana().getCMC();
|
|
||||||
|
|
||||||
// deprioritize SAs explicitly marked as preferred to be activated last compared to all other SAs
|
|
||||||
if (a.hasParam("AIActivateLast") && !b.hasParam("AIActivateLast")) {
|
|
||||||
return 1;
|
|
||||||
} else if (b.hasParam("AIActivateLast") && !a.hasParam("AIActivateLast")) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// deprioritize planar die roll marked with AIRollPlanarDieParams:LowPriority$ True
|
|
||||||
if (ApiType.RollPlanarDice == a.getApi() && a.getHostCard() != null && a.getHostCard().hasSVar("AIRollPlanarDieParams") && a.getHostCard().getSVar("AIRollPlanarDieParams").toLowerCase().matches(".*lowpriority\\$\\s*true.*")) {
|
|
||||||
return 1;
|
|
||||||
} else if (ApiType.RollPlanarDice == b.getApi() && b.getHostCard() != null && b.getHostCard().hasSVar("AIRollPlanarDieParams") && b.getHostCard().getSVar("AIRollPlanarDieParams").toLowerCase().matches(".*lowpriority\\$\\s*true.*")) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// deprioritize pump spells with pure energy cost (can be activated last,
|
|
||||||
// since energy is generally scarce, plus can benefit e.g. Electrostatic Pummeler)
|
|
||||||
int a2 = 0, b2 = 0;
|
|
||||||
if (a.getApi() == ApiType.Pump && a.getPayCosts().getCostEnergy() != null) {
|
|
||||||
if (a.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) {
|
|
||||||
a2 = a.getPayCosts().getCostEnergy().convertAmount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (b.getApi() == ApiType.Pump && b.getPayCosts().getCostEnergy() != null) {
|
|
||||||
if (b.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) {
|
|
||||||
b2 = b.getPayCosts().getCostEnergy().convertAmount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (a2 == 0 && b2 > 0) {
|
|
||||||
return -1;
|
|
||||||
} else if (b2 == 0 && a2 > 0) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// cast 0 mana cost spells first (might be a Mox)
|
|
||||||
if (a1 == 0 && b1 > 0 && ApiType.Mana != a.getApi()) {
|
|
||||||
return -1;
|
|
||||||
} else if (a1 > 0 && b1 == 0 && ApiType.Mana != b.getApi()) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.getHostCard() != null && a.getHostCard().hasSVar("FreeSpellAI")) {
|
|
||||||
return -1;
|
|
||||||
} else if (b.getHostCard() != null && b.getHostCard().hasSVar("FreeSpellAI")) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.getHostCard().equals(b.getHostCard()) && a.getApi() == b.getApi()) {
|
|
||||||
// Cheaper Spectacle costs should be preferred
|
|
||||||
// FIXME: Any better way to identify that these are the same ability, one with Spectacle and one not?
|
|
||||||
// (looks like it's not a full-fledged alternative cost as such, and is not processed with other alt costs)
|
|
||||||
if (a.isSpectacle() && !b.isSpectacle() && a1 < b1) {
|
|
||||||
return 1;
|
|
||||||
} else if (b.isSpectacle() && !a.isSpectacle() && b1 < a1) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If both are permanent creature spells, prefer the one that evaluates higher
|
|
||||||
if (a.getApi() == ApiType.PermanentCreature && b.getApi() == ApiType.PermanentCreature) {
|
|
||||||
int evalA = ComputerUtilCard.evaluateCreature(a);
|
|
||||||
int evalB = ComputerUtilCard.evaluateCreature(b);
|
|
||||||
if (evalA > evalB) {
|
|
||||||
a1 += Math.max(1, Math.round(evalA / 100.0f));
|
|
||||||
} else if (evalB > evalA) {
|
|
||||||
b1 += Math.max(1, Math.round(evalB / 100.0f));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a1 += getSpellAbilityPriority(a);
|
|
||||||
b1 += getSpellAbilityPriority(b);
|
|
||||||
|
|
||||||
return b1 - a1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getSpellAbilityPriority(SpellAbility sa) {
|
|
||||||
int p = 0;
|
|
||||||
Card source = sa.getHostCard();
|
|
||||||
final Player ai = source == null ? sa.getActivatingPlayer() : source.getController();
|
|
||||||
if (ai == null) {
|
|
||||||
System.err.println("Error: couldn't figure out the activating player and host card for SA: " + sa);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
final boolean noCreatures = ai.getCreaturesInPlay().isEmpty();
|
|
||||||
|
|
||||||
if (source != null) {
|
|
||||||
// puts creatures in front of spells
|
|
||||||
if (source.isCreature()) {
|
|
||||||
p += 1;
|
|
||||||
}
|
|
||||||
if (source.hasSVar("AIPriorityModifier")) {
|
|
||||||
p += Integer.parseInt(source.getSVar("AIPriorityModifier"));
|
|
||||||
}
|
|
||||||
if (ComputerUtilCard.isCardRemAIDeck(sa.getOriginalHost() != null ? sa.getOriginalHost() : source)) {
|
|
||||||
p -= 10;
|
|
||||||
}
|
|
||||||
// don't play equipments before having any creatures
|
|
||||||
if (source.isEquipment() && noCreatures) {
|
|
||||||
p -= 9;
|
|
||||||
}
|
|
||||||
// don't equip stuff in main 2 if there's more stuff to cast at the moment
|
|
||||||
if (sa.getApi() == ApiType.Attach && !sa.isCurse() && source.getGame().getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
|
||||||
p -= 1;
|
|
||||||
}
|
|
||||||
// 1. increase chance of using Surge effects
|
|
||||||
// 2. non-surged versions are usually inefficient
|
|
||||||
if (source.getOracleText().contains("surge cost") && !sa.isSurged()) {
|
|
||||||
p -= 9;
|
|
||||||
}
|
|
||||||
// move snap-casted spells to front
|
|
||||||
if (source.isInZone(ZoneType.Graveyard)) {
|
|
||||||
if (sa.getMayPlay() != null && source.mayPlay(sa.getMayPlay()) != null) {
|
|
||||||
p += 50;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if the profile specifies it, deprioritize Storm spells in an attempt to build up storm count
|
|
||||||
if (source.hasKeyword(Keyword.STORM) && ai.getController() instanceof PlayerControllerAi) {
|
|
||||||
p -= (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.PRIORITY_REDUCTION_FOR_STORM_SPELLS));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// use Surge and Prowl costs when able to
|
|
||||||
if (sa.isSurged() || sa.isProwl()) {
|
|
||||||
p += 9;
|
|
||||||
}
|
|
||||||
// sort planeswalker abilities with most costly first
|
|
||||||
if (sa.isPwAbility()) {
|
|
||||||
final CostPart cost = sa.getPayCosts().getCostParts().get(0);
|
|
||||||
if (cost instanceof CostRemoveCounter) {
|
|
||||||
p += cost.convertAmount() == null ? 1 : cost.convertAmount();
|
|
||||||
} else if (cost instanceof CostPutCounter) {
|
|
||||||
p -= cost.convertAmount();
|
|
||||||
}
|
|
||||||
if (sa.hasParam("Ultimate")) {
|
|
||||||
p += 9;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ApiType.DestroyAll == sa.getApi()) {
|
|
||||||
p += 4;
|
|
||||||
} else if (ApiType.Mana == sa.getApi()) {
|
|
||||||
p -= 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to cast mana ritual spells before casting spells to maximize potential mana
|
|
||||||
if ("ManaRitual".equals(sa.getParam("AILogic"))) {
|
|
||||||
p += 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public CardCollection getCardsToDiscard(final int numDiscard, final String[] uTypes, final SpellAbility sa) {
|
public CardCollection getCardsToDiscard(final int numDiscard, final String[] uTypes, final SpellAbility sa) {
|
||||||
return getCardsToDiscard(numDiscard, uTypes, sa, CardCollection.EMPTY);
|
return getCardsToDiscard(numDiscard, uTypes, sa, CardCollection.EMPTY);
|
||||||
}
|
}
|
||||||
@@ -1744,10 +1584,11 @@ public class AiController {
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Collections.sort(all, saComparator); // put best spells first
|
Collections.sort(all, ComputerUtilAbility.saEvaluator); // put best spells first
|
||||||
|
ComputerUtilAbility.sortCreatureSpells(all);
|
||||||
} catch (IllegalArgumentException ex) {
|
} catch (IllegalArgumentException ex) {
|
||||||
System.err.println(ex.getMessage());
|
System.err.println(ex.getMessage());
|
||||||
String assertex = ComparatorUtil.verifyTransitivity(saComparator, all);
|
String assertex = ComparatorUtil.verifyTransitivity(ComputerUtilAbility.saEvaluator, all);
|
||||||
Sentry.captureMessage(ex.getMessage() + "\nAssertionError [verifyTransitivity]: " + assertex);
|
Sentry.captureMessage(ex.getMessage() + "\nAssertionError [verifyTransitivity]: " + assertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2292,14 +2133,14 @@ public class AiController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO move to more common place
|
// TODO move to more common place
|
||||||
private <T> List<T> filterList(List<T> input, Predicate<? super T> pred) {
|
private static <T> List<T> filterList(List<T> input, Predicate<? super T> pred) {
|
||||||
List<T> filtered = Lists.newArrayList(Iterables.filter(input, pred));
|
List<T> filtered = Lists.newArrayList(Iterables.filter(input, pred));
|
||||||
input.removeAll(filtered);
|
input.removeAll(filtered);
|
||||||
return filtered;
|
return filtered;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO move to more common place
|
// TODO move to more common place
|
||||||
private List<SpellAbility> filterListByApi(List<SpellAbility> input, ApiType type) {
|
public static List<SpellAbility> filterListByApi(List<SpellAbility> input, ApiType type) {
|
||||||
return filterList(input, SpellAbilityPredicates.isApi(type));
|
return filterList(input, SpellAbilityPredicates.isApi(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package forge.ai;
|
package forge.ai;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -15,6 +17,12 @@ import forge.game.card.CardCollection;
|
|||||||
import forge.game.card.CardCollectionView;
|
import forge.game.card.CardCollectionView;
|
||||||
import forge.game.card.CardLists;
|
import forge.game.card.CardLists;
|
||||||
import forge.game.card.CardPredicates.Presets;
|
import forge.game.card.CardPredicates.Presets;
|
||||||
|
import forge.game.cost.CostPart;
|
||||||
|
import forge.game.cost.CostPayEnergy;
|
||||||
|
import forge.game.cost.CostPutCounter;
|
||||||
|
import forge.game.cost.CostRemoveCounter;
|
||||||
|
import forge.game.keyword.Keyword;
|
||||||
|
import forge.game.phase.PhaseType;
|
||||||
import forge.game.player.Player;
|
import forge.game.player.Player;
|
||||||
import forge.game.spellability.OptionalCostValue;
|
import forge.game.spellability.OptionalCostValue;
|
||||||
import forge.game.spellability.SpellAbility;
|
import forge.game.spellability.SpellAbility;
|
||||||
@@ -223,4 +231,179 @@ public class ComputerUtilAbility {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final static saComparator saEvaluator = new saComparator();
|
||||||
|
|
||||||
|
// not sure "playing biggest spell" matters?
|
||||||
|
public final static class saComparator implements Comparator<SpellAbility> {
|
||||||
|
@Override
|
||||||
|
public int compare(final SpellAbility a, final SpellAbility b) {
|
||||||
|
return compareEvaluator(a, b, false);
|
||||||
|
}
|
||||||
|
public int compareEvaluator(final SpellAbility a, final SpellAbility b, boolean safeToEvaluateCreatures) {
|
||||||
|
// sort from highest cost to lowest
|
||||||
|
// we want the highest costs first
|
||||||
|
int a1 = a.getPayCosts().getTotalMana().getCMC();
|
||||||
|
int b1 = b.getPayCosts().getTotalMana().getCMC();
|
||||||
|
|
||||||
|
// deprioritize SAs explicitly marked as preferred to be activated last compared to all other SAs
|
||||||
|
if (a.hasParam("AIActivateLast") && !b.hasParam("AIActivateLast")) {
|
||||||
|
return 1;
|
||||||
|
} else if (b.hasParam("AIActivateLast") && !a.hasParam("AIActivateLast")) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// deprioritize planar die roll marked with AIRollPlanarDieParams:LowPriority$ True
|
||||||
|
if (ApiType.RollPlanarDice == a.getApi() && a.getHostCard() != null && a.getHostCard().hasSVar("AIRollPlanarDieParams") && a.getHostCard().getSVar("AIRollPlanarDieParams").toLowerCase().matches(".*lowpriority\\$\\s*true.*")) {
|
||||||
|
return 1;
|
||||||
|
} else if (ApiType.RollPlanarDice == b.getApi() && b.getHostCard() != null && b.getHostCard().hasSVar("AIRollPlanarDieParams") && b.getHostCard().getSVar("AIRollPlanarDieParams").toLowerCase().matches(".*lowpriority\\$\\s*true.*")) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// deprioritize pump spells with pure energy cost (can be activated last,
|
||||||
|
// since energy is generally scarce, plus can benefit e.g. Electrostatic Pummeler)
|
||||||
|
int a2 = 0, b2 = 0;
|
||||||
|
if (a.getApi() == ApiType.Pump && a.getPayCosts().getCostEnergy() != null) {
|
||||||
|
if (a.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) {
|
||||||
|
a2 = a.getPayCosts().getCostEnergy().convertAmount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (b.getApi() == ApiType.Pump && b.getPayCosts().getCostEnergy() != null) {
|
||||||
|
if (b.getPayCosts().hasOnlySpecificCostType(CostPayEnergy.class)) {
|
||||||
|
b2 = b.getPayCosts().getCostEnergy().convertAmount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (a2 == 0 && b2 > 0) {
|
||||||
|
return -1;
|
||||||
|
} else if (b2 == 0 && a2 > 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// cast 0 mana cost spells first (might be a Mox)
|
||||||
|
if (a1 == 0 && b1 > 0 && ApiType.Mana != a.getApi()) {
|
||||||
|
return -1;
|
||||||
|
} else if (a1 > 0 && b1 == 0 && ApiType.Mana != b.getApi()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.getHostCard() != null && a.getHostCard().hasSVar("FreeSpellAI")) {
|
||||||
|
return -1;
|
||||||
|
} else if (b.getHostCard() != null && b.getHostCard().hasSVar("FreeSpellAI")) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.getHostCard().equals(b.getHostCard()) && a.getApi() == b.getApi()) {
|
||||||
|
// Cheaper Spectacle costs should be preferred
|
||||||
|
// FIXME: Any better way to identify that these are the same ability, one with Spectacle and one not?
|
||||||
|
// (looks like it's not a full-fledged alternative cost as such, and is not processed with other alt costs)
|
||||||
|
if (a.isSpectacle() && !b.isSpectacle() && a1 < b1) {
|
||||||
|
return 1;
|
||||||
|
} else if (b.isSpectacle() && !a.isSpectacle() && b1 < a1) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a1 += getSpellAbilityPriority(a);
|
||||||
|
b1 += getSpellAbilityPriority(b);
|
||||||
|
|
||||||
|
int diff = b1 - a1;
|
||||||
|
|
||||||
|
// If both are creature spells with roughly the same priority sort them after
|
||||||
|
if (safeToEvaluateCreatures && Math.abs(diff) < 4 && a.getApi() == ApiType.PermanentCreature && b.getApi() == ApiType.PermanentCreature) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getSpellAbilityPriority(SpellAbility sa) {
|
||||||
|
int p = 0;
|
||||||
|
Card source = sa.getHostCard();
|
||||||
|
final Player ai = source == null ? sa.getActivatingPlayer() : source.getController();
|
||||||
|
if (ai == null) {
|
||||||
|
System.err.println("Error: couldn't figure out the activating player and host card for SA: " + sa);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
final boolean noCreatures = ai.getCreaturesInPlay().isEmpty();
|
||||||
|
|
||||||
|
if (source != null) {
|
||||||
|
// puts creatures in front of spells
|
||||||
|
if (source.isCreature()) {
|
||||||
|
p += 1;
|
||||||
|
}
|
||||||
|
if (source.hasSVar("AIPriorityModifier")) {
|
||||||
|
p += Integer.parseInt(source.getSVar("AIPriorityModifier"));
|
||||||
|
}
|
||||||
|
if (ComputerUtilCard.isCardRemAIDeck(sa.getOriginalHost() != null ? sa.getOriginalHost() : source)) {
|
||||||
|
p -= 10;
|
||||||
|
}
|
||||||
|
// don't play equipments before having any creatures
|
||||||
|
if (source.isEquipment() && noCreatures) {
|
||||||
|
p -= 9;
|
||||||
|
}
|
||||||
|
// don't equip stuff in main 2 if there's more stuff to cast at the moment
|
||||||
|
if (sa.getApi() == ApiType.Attach && !sa.isCurse() && source.getGame().getPhaseHandler().getPhase().isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
|
p -= 1;
|
||||||
|
}
|
||||||
|
// 1. increase chance of using Surge effects
|
||||||
|
// 2. non-surged versions are usually inefficient
|
||||||
|
if (source.getOracleText().contains("surge cost") && !sa.isSurged()) {
|
||||||
|
p -= 9;
|
||||||
|
}
|
||||||
|
// move snap-casted spells to front
|
||||||
|
if (source.isInZone(ZoneType.Graveyard)) {
|
||||||
|
if (sa.getMayPlay() != null && source.mayPlay(sa.getMayPlay()) != null) {
|
||||||
|
p += 50;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if the profile specifies it, deprioritize Storm spells in an attempt to build up storm count
|
||||||
|
if (source.hasKeyword(Keyword.STORM) && ai.getController() instanceof PlayerControllerAi) {
|
||||||
|
p -= (((PlayerControllerAi) ai.getController()).getAi().getIntProperty(AiProps.PRIORITY_REDUCTION_FOR_STORM_SPELLS));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use Surge and Prowl costs when able to
|
||||||
|
if (sa.isSurged() || sa.isProwl()) {
|
||||||
|
p += 9;
|
||||||
|
}
|
||||||
|
// sort planeswalker abilities with most costly first
|
||||||
|
if (sa.isPwAbility()) {
|
||||||
|
final CostPart cost = sa.getPayCosts().getCostParts().get(0);
|
||||||
|
if (cost instanceof CostRemoveCounter) {
|
||||||
|
p += cost.convertAmount() == null ? 1 : cost.convertAmount();
|
||||||
|
} else if (cost instanceof CostPutCounter) {
|
||||||
|
p -= cost.convertAmount();
|
||||||
|
}
|
||||||
|
if (sa.hasParam("Ultimate")) {
|
||||||
|
p += 9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ApiType.DestroyAll == sa.getApi()) {
|
||||||
|
p += 4;
|
||||||
|
} else if (ApiType.Mana == sa.getApi()) {
|
||||||
|
p -= 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to cast mana ritual spells before casting spells to maximize potential mana
|
||||||
|
if ("ManaRitual".equals(sa.getParam("AILogic"))) {
|
||||||
|
p += 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public static List<SpellAbility> sortCreatureSpells(List<SpellAbility> all) {
|
||||||
|
List<SpellAbility> creatures = AiController.filterListByApi(Lists.newArrayList(all), ApiType.PermanentCreature);
|
||||||
|
Collections.sort(creatures, ComputerUtilCard.EvaluateCreatureSpellComparator);
|
||||||
|
int idx = 0;
|
||||||
|
for (int i = 0; i < all.size(); i++) {
|
||||||
|
if (all.get(i).getApi() == ApiType.PermanentCreature) {
|
||||||
|
all.set(i, creatures.get(idx));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -567,6 +567,18 @@ public class ComputerUtilCard {
|
|||||||
return evaluateCreature(b) - evaluateCreature(a);
|
return evaluateCreature(b) - evaluateCreature(a);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
public static final Comparator<SpellAbility> EvaluateCreatureSpellComparator = new Comparator<SpellAbility>() {
|
||||||
|
@Override
|
||||||
|
public int compare(final SpellAbility a, final SpellAbility b) {
|
||||||
|
// only reorder if generic priorities can't decide
|
||||||
|
// TODO ideally we could reuse the value
|
||||||
|
int comp = ComputerUtilAbility.saEvaluator.compareEvaluator(a, b, true);
|
||||||
|
if (comp == 0) {
|
||||||
|
return evaluateCreature(b) - evaluateCreature(a);
|
||||||
|
}
|
||||||
|
return comp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private static final CreatureEvaluator creatureEvaluator = new CreatureEvaluator();
|
private static final CreatureEvaluator creatureEvaluator = new CreatureEvaluator();
|
||||||
private static final LandEvaluator landEvaluator = new LandEvaluator();
|
private static final LandEvaluator landEvaluator = new LandEvaluator();
|
||||||
@@ -596,7 +608,7 @@ public class ComputerUtilCard {
|
|||||||
host.setState(sa.getCardStateName(), false);
|
host.setState(sa.getCardStateName(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
int eval = creatureEvaluator.evaluateCreature(host);
|
int eval = evaluateCreature(host);
|
||||||
|
|
||||||
if (currentState != null) {
|
if (currentState != null) {
|
||||||
host.setState(currentState, false);
|
host.setState(currentState, false);
|
||||||
|
|||||||
Reference in New Issue
Block a user