mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-12 00:38:44 +00:00
LandEvaluator for AI
This commit is contained in:
@@ -6,12 +6,12 @@
|
|||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
@@ -75,7 +75,7 @@ import java.util.*;
|
|||||||
* <p>
|
* <p>
|
||||||
* AiController class.
|
* AiController class.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @author Forge
|
* @author Forge
|
||||||
* @version $Id$
|
* @version $Id$
|
||||||
*/
|
*/
|
||||||
@@ -108,6 +108,7 @@ public class AiController {
|
|||||||
public boolean usesSimulation() {
|
public boolean usesSimulation() {
|
||||||
return this.useSimulation;
|
return this.useSimulation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setUseSimulation(boolean value) {
|
public void setUseSimulation(boolean value) {
|
||||||
this.useSimulation = value;
|
this.useSimulation = value;
|
||||||
}
|
}
|
||||||
@@ -153,7 +154,7 @@ public class AiController {
|
|||||||
private List<SpellAbility> getPossibleETBCounters() {
|
private List<SpellAbility> getPossibleETBCounters() {
|
||||||
CardCollection all = new CardCollection(player.getCardsIn(ZoneType.Hand));
|
CardCollection all = new CardCollection(player.getCardsIn(ZoneType.Hand));
|
||||||
CardCollectionView ccvPlayerLibrary = player.getCardsIn(ZoneType.Library);
|
CardCollectionView ccvPlayerLibrary = player.getCardsIn(ZoneType.Library);
|
||||||
|
|
||||||
all.addAll(player.getCardsIn(ZoneType.Exile));
|
all.addAll(player.getCardsIn(ZoneType.Exile));
|
||||||
all.addAll(player.getCardsIn(ZoneType.Graveyard));
|
all.addAll(player.getCardsIn(ZoneType.Graveyard));
|
||||||
if (!ccvPlayerLibrary.isEmpty()) {
|
if (!ccvPlayerLibrary.isEmpty()) {
|
||||||
@@ -175,7 +176,7 @@ public class AiController {
|
|||||||
}
|
}
|
||||||
return spellAbilities;
|
return spellAbilities;
|
||||||
}
|
}
|
||||||
|
|
||||||
// look for cards on the battlefield that should prevent the AI from using that spellability
|
// look for cards on the battlefield that should prevent the AI from using that spellability
|
||||||
private boolean checkCurseEffects(final SpellAbility sa) {
|
private boolean checkCurseEffects(final SpellAbility sa) {
|
||||||
CardCollectionView ccvGameBattlefield = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), CardPredicates.hasSVar("AICurseEffect"));
|
CardCollectionView ccvGameBattlefield = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), CardPredicates.hasSVar("AICurseEffect"));
|
||||||
@@ -194,7 +195,7 @@ public class AiController {
|
|||||||
} else if ("ChaliceOfTheVoid".equals(curse) && sa.isSpell() && CardFactoryUtil.isCounterable(host)
|
} else if ("ChaliceOfTheVoid".equals(curse) && sa.isSpell() && CardFactoryUtil.isCounterable(host)
|
||||||
&& host.getCMC() == c.getCounters(CounterEnumType.CHARGE)) {
|
&& host.getCMC() == c.getCounters(CounterEnumType.CHARGE)) {
|
||||||
return true;
|
return true;
|
||||||
} else if ("BazaarOfWonders".equals(curse) && sa.isSpell() && CardFactoryUtil.isCounterable(host)) {
|
} else if ("BazaarOfWonders".equals(curse) && sa.isSpell() && CardFactoryUtil.isCounterable(host)) {
|
||||||
String hostName = host.getName();
|
String hostName = host.getName();
|
||||||
for (Card card : ccvGameBattlefield) {
|
for (Card card : ccvGameBattlefield) {
|
||||||
if (!card.isToken() && card.sharesNameWith(host)) {
|
if (!card.isToken() && card.sharesNameWith(host)) {
|
||||||
@@ -217,6 +218,7 @@ public class AiController {
|
|||||||
card.setCastSA(null);
|
card.setCastSA(null);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean checkETBEffectsPreparedCard(final Card card, final SpellAbility sa, final ApiType api) {
|
private boolean checkETBEffectsPreparedCard(final Card card, final SpellAbility sa, final ApiType api) {
|
||||||
final Player activator = sa.getActivatingPlayer();
|
final Player activator = sa.getActivatingPlayer();
|
||||||
|
|
||||||
@@ -301,7 +303,7 @@ public class AiController {
|
|||||||
String s = validCard.split("kicked ")[1];
|
String s = validCard.split("kicked ")[1];
|
||||||
if ("1".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker1)) continue;
|
if ("1".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker1)) continue;
|
||||||
if ("2".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker2)) continue;
|
if ("2".equals(s) && !sa.isOptionalCostPaid(OptionalCost.Kicker2)) continue;
|
||||||
} else if (!sa.isKicked()) {
|
} else if (!sa.isKicked()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -476,6 +478,8 @@ public class AiController {
|
|||||||
// If nothing is done here, proceeds to the default land picking strategy
|
// If nothing is done here, proceeds to the default land picking strategy
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ComputerUtilCard.getBestLandToPlayAI(landList);
|
||||||
|
/*
|
||||||
//Skip reflected lands.
|
//Skip reflected lands.
|
||||||
CardCollection unreflectedLands = new CardCollection(landList);
|
CardCollection unreflectedLands = new CardCollection(landList);
|
||||||
for (Card l : landList) {
|
for (Card l : landList) {
|
||||||
@@ -574,7 +578,7 @@ public class AiController {
|
|||||||
landList = CardLists.filter(landList, Predicates.not(CardPredicates.Presets.BASIC_LANDS));
|
landList = CardLists.filter(landList, Predicates.not(CardPredicates.Presets.BASIC_LANDS));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return landList.get(0);
|
return landList.get(0);*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// if return true, go to next phase
|
// if return true, go to next phase
|
||||||
@@ -600,7 +604,7 @@ public class AiController {
|
|||||||
} else {
|
} else {
|
||||||
// Compare bestSA with this SA
|
// Compare bestSA with this SA
|
||||||
final int restrictionLevel = ComputerUtil.counterSpellRestriction(player, currentSA);
|
final int restrictionLevel = ComputerUtil.counterSpellRestriction(player, currentSA);
|
||||||
|
|
||||||
if (restrictionLevel > bestRestriction) {
|
if (restrictionLevel > bestRestriction) {
|
||||||
bestRestriction = restrictionLevel;
|
bestRestriction = restrictionLevel;
|
||||||
bestSA = currentSA;
|
bestSA = currentSA;
|
||||||
@@ -617,20 +621,20 @@ public class AiController {
|
|||||||
public SpellAbility predictSpellToCastInMain2(ApiType exceptSA) {
|
public SpellAbility predictSpellToCastInMain2(ApiType exceptSA) {
|
||||||
return predictSpellToCastInMain2(exceptSA, true);
|
return predictSpellToCastInMain2(exceptSA, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SpellAbility predictSpellToCastInMain2(ApiType exceptSA, boolean handOnly) {
|
private SpellAbility predictSpellToCastInMain2(ApiType exceptSA, boolean handOnly) {
|
||||||
if (!getBooleanProperty(AiProps.PREDICT_SPELLS_FOR_MAIN2)) {
|
if (!getBooleanProperty(AiProps.PREDICT_SPELLS_FOR_MAIN2)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final CardCollectionView cards = handOnly ? player.getCardsIn(ZoneType.Hand) :
|
final CardCollectionView cards = handOnly ? player.getCardsIn(ZoneType.Hand) :
|
||||||
ComputerUtilAbility.getAvailableCards(game, player);
|
ComputerUtilAbility.getAvailableCards(game, player);
|
||||||
|
|
||||||
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, saComparator); // put best spells first
|
||||||
}
|
} 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(saComparator, all);
|
||||||
Sentry.captureMessage(ex.getMessage() + "\nAssertionError [verifyTransitivity]: " + assertex);
|
Sentry.captureMessage(ex.getMessage() + "\nAssertionError [verifyTransitivity]: " + assertex);
|
||||||
@@ -638,7 +642,7 @@ public class AiController {
|
|||||||
|
|
||||||
for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, player)) {
|
for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, player)) {
|
||||||
ApiType saApi = sa.getApi();
|
ApiType saApi = sa.getApi();
|
||||||
|
|
||||||
if (saApi == ApiType.Counter || saApi == exceptSA) {
|
if (saApi == ApiType.Counter || saApi == exceptSA) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -657,12 +661,15 @@ public class AiController {
|
|||||||
public boolean reserveManaSourcesForNextSpell(SpellAbility sa, SpellAbility exceptForSa) {
|
public boolean reserveManaSourcesForNextSpell(SpellAbility sa, SpellAbility exceptForSa) {
|
||||||
return reserveManaSources(sa, null, false, true, exceptForSa);
|
return reserveManaSources(sa, null, false, true, exceptForSa);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean reserveManaSources(SpellAbility sa) {
|
public boolean reserveManaSources(SpellAbility sa) {
|
||||||
return reserveManaSources(sa, PhaseType.MAIN2, false, false, null);
|
return reserveManaSources(sa, PhaseType.MAIN2, false, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean reserveManaSources(SpellAbility sa, PhaseType phaseType, boolean enemy) {
|
public boolean reserveManaSources(SpellAbility sa, PhaseType phaseType, boolean enemy) {
|
||||||
return reserveManaSources(sa, phaseType, enemy, true, null);
|
return reserveManaSources(sa, phaseType, enemy, true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean reserveManaSources(SpellAbility sa, PhaseType phaseType, boolean enemy, boolean forNextSpell, SpellAbility exceptForThisSa) {
|
public boolean reserveManaSources(SpellAbility sa, PhaseType phaseType, boolean enemy, boolean forNextSpell, SpellAbility exceptForThisSa) {
|
||||||
ManaCostBeingPaid cost = ComputerUtilMana.calculateManaCost(sa, true, 0);
|
ManaCostBeingPaid cost = ComputerUtilMana.calculateManaCost(sa, true, 0);
|
||||||
CardCollection manaSources = ComputerUtilMana.getManaSourcesToPayCost(cost, sa, player);
|
CardCollection manaSources = ComputerUtilMana.getManaSourcesToPayCost(cost, sa, player);
|
||||||
@@ -784,8 +791,9 @@ public class AiController {
|
|||||||
return AiPlayDecision.CantAfford;
|
return AiPlayDecision.CantAfford;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SpellAbilityAi topAI = new SpellAbilityAi() {};
|
SpellAbilityAi topAI = new SpellAbilityAi() {
|
||||||
if (!topAI.willPayCosts(player, sa , wardCost, host)) {
|
};
|
||||||
|
if (!topAI.willPayCosts(player, sa, wardCost, host)) {
|
||||||
return AiPlayDecision.CostNotAcceptable;
|
return AiPlayDecision.CostNotAcceptable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -899,7 +907,7 @@ public class AiController {
|
|||||||
return AiPlayDecision.AnotherTime;
|
return AiPlayDecision.AnotherTime;
|
||||||
}
|
}
|
||||||
if (sa instanceof SpellPermanent) {
|
if (sa instanceof SpellPermanent) {
|
||||||
return canPlayFromEffectAI((SpellPermanent)sa, false, true);
|
return canPlayFromEffectAI((SpellPermanent) sa, false, true);
|
||||||
}
|
}
|
||||||
if (sa.usesTargeting()) {
|
if (sa.usesTargeting()) {
|
||||||
if (!sa.isTargetNumberValid() && sa.getTargetRestrictions().getNumCandidates(sa, true) == 0) {
|
if (!sa.isTargetNumberValid() && sa.getTargetRestrictions().getNumCandidates(sa, true) == 0) {
|
||||||
@@ -1037,7 +1045,7 @@ public class AiController {
|
|||||||
} else if (a1 > 0 && b1 == 0 && ApiType.Mana != b.getApi()) {
|
} else if (a1 > 0 && b1 == 0 && ApiType.Mana != b.getApi()) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (a.getHostCard() != null && a.getHostCard().hasSVar("FreeSpellAI")) {
|
if (a.getHostCard() != null && a.getHostCard().hasSVar("FreeSpellAI")) {
|
||||||
return -1;
|
return -1;
|
||||||
} else if (b.getHostCard() != null && b.getHostCard().hasSVar("FreeSpellAI")) {
|
} else if (b.getHostCard() != null && b.getHostCard().hasSVar("FreeSpellAI")) {
|
||||||
@@ -1060,7 +1068,7 @@ public class AiController {
|
|||||||
|
|
||||||
return b1 - a1;
|
return b1 - a1;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getSpellAbilityPriority(SpellAbility sa) {
|
private int getSpellAbilityPriority(SpellAbility sa) {
|
||||||
int p = 0;
|
int p = 0;
|
||||||
Card source = sa.getHostCard();
|
Card source = sa.getHostCard();
|
||||||
@@ -1142,6 +1150,7 @@ public class AiController {
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CardCollection getCardsToDiscard(final int numDiscard, final String[] uTypes, final SpellAbility sa, final CardCollectionView exclude) {
|
public CardCollection getCardsToDiscard(final int numDiscard, final String[] uTypes, final SpellAbility sa, final CardCollectionView exclude) {
|
||||||
boolean noFiltering = sa != null && "DiscardCMCX".equals(sa.getParam("AILogic")); // list AI logic for which filtering is taken care of elsewhere
|
boolean noFiltering = sa != null && "DiscardCMCX".equals(sa.getParam("AILogic")); // list AI logic for which filtering is taken care of elsewhere
|
||||||
CardCollection hand = new CardCollection(player.getCardsIn(ZoneType.Hand));
|
CardCollection hand = new CardCollection(player.getCardsIn(ZoneType.Hand));
|
||||||
@@ -1193,7 +1202,9 @@ public class AiController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Card c : inHand) {
|
for (Card c : inHand) {
|
||||||
if (c.hasSVar("DoNotDiscardIfAble") || c.hasSVar("IsReanimatorCard")) { continue; }
|
if (c.hasSVar("DoNotDiscardIfAble") || c.hasSVar("IsReanimatorCard")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (c.isCreature() && !ComputerUtilMana.hasEnoughManaSourcesToCast(c.getSpellPermanent(), player)) {
|
if (c.isCreature() && !ComputerUtilMana.hasEnoughManaSourcesToCast(c.getSpellPermanent(), player)) {
|
||||||
discards.add(c);
|
discards.add(c);
|
||||||
}
|
}
|
||||||
@@ -1232,8 +1243,7 @@ public class AiController {
|
|||||||
discardList.add(prefCard);
|
discardList.add(prefCard);
|
||||||
validCards.remove(prefCard);
|
validCards.remove(prefCard);
|
||||||
count++;
|
count++;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1248,16 +1258,15 @@ public class AiController {
|
|||||||
final int numLandsInPlay = CardLists.count(player.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS_PRODUCING_MANA);
|
final int numLandsInPlay = CardLists.count(player.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS_PRODUCING_MANA);
|
||||||
final CardCollection landsInHand = CardLists.filter(validCards, CardPredicates.Presets.LANDS);
|
final CardCollection landsInHand = CardLists.filter(validCards, CardPredicates.Presets.LANDS);
|
||||||
final int numLandsInHand = landsInHand.size();
|
final int numLandsInHand = landsInHand.size();
|
||||||
|
|
||||||
// Discard a land
|
// Discard a land
|
||||||
boolean canDiscardLands = numLandsInHand > 3 || (numLandsInHand > 2 && numLandsInPlay > 0)
|
boolean canDiscardLands = numLandsInHand > 3 || (numLandsInHand > 2 && numLandsInPlay > 0)
|
||||||
|| (numLandsInHand > 1 && numLandsInPlay > 2) || (numLandsInHand > 0 && numLandsInPlay > 5);
|
|| (numLandsInHand > 1 && numLandsInPlay > 2) || (numLandsInHand > 0 && numLandsInPlay > 5);
|
||||||
|
|
||||||
if (canDiscardLands) {
|
if (canDiscardLands) {
|
||||||
discardList.add(landsInHand.get(0));
|
discardList.add(landsInHand.get(0));
|
||||||
validCards.remove(landsInHand.get(0));
|
validCards.remove(landsInHand.get(0));
|
||||||
}
|
} else { // Discard other stuff
|
||||||
else { // Discard other stuff
|
|
||||||
CardLists.sortByCmcDesc(validCards);
|
CardLists.sortByCmcDesc(validCards);
|
||||||
int numLandsAvailable = numLandsInPlay;
|
int numLandsAvailable = numLandsInPlay;
|
||||||
if (numLandsInHand > 0) {
|
if (numLandsInHand > 0) {
|
||||||
@@ -1346,7 +1355,7 @@ public class AiController {
|
|||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1448,7 +1457,9 @@ public class AiController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<SpellAbility> singleSpellAbilityList(SpellAbility sa) {
|
private List<SpellAbility> singleSpellAbilityList(SpellAbility sa) {
|
||||||
if (sa == null) { return null; }
|
if (sa == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
final List<SpellAbility> abilities = Lists.newArrayList();
|
final List<SpellAbility> abilities = Lists.newArrayList();
|
||||||
abilities.add(sa);
|
abilities.add(sa);
|
||||||
@@ -1470,17 +1481,17 @@ public class AiController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CardCollection playBeforeLand = CardLists.filter(
|
CardCollection playBeforeLand = CardLists.filter(
|
||||||
player.getCardsIn(ZoneType.Hand), CardPredicates.hasSVar("PlayBeforeLandDrop")
|
player.getCardsIn(ZoneType.Hand), CardPredicates.hasSVar("PlayBeforeLandDrop")
|
||||||
);
|
);
|
||||||
if (!playBeforeLand.isEmpty()) {
|
if (!playBeforeLand.isEmpty()) {
|
||||||
SpellAbility wantToPlayBeforeLand = chooseSpellAbilityToPlayFromList(
|
SpellAbility wantToPlayBeforeLand = chooseSpellAbilityToPlayFromList(
|
||||||
ComputerUtilAbility.getSpellAbilities(playBeforeLand, player), false
|
ComputerUtilAbility.getSpellAbilities(playBeforeLand, player), false
|
||||||
);
|
);
|
||||||
if (wantToPlayBeforeLand != null) {
|
if (wantToPlayBeforeLand != null) {
|
||||||
return singleSpellAbilityList(wantToPlayBeforeLand);
|
return singleSpellAbilityList(wantToPlayBeforeLand);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CardCollection landsWannaPlay = ComputerUtilAbility.getAvailableLandsToPlay(game, player);
|
CardCollection landsWannaPlay = ComputerUtilAbility.getAvailableLandsToPlay(game, player);
|
||||||
if (landsWannaPlay != null) {
|
if (landsWannaPlay != null) {
|
||||||
landsWannaPlay = filterLandsToPlay(landsWannaPlay);
|
landsWannaPlay = filterLandsToPlay(landsWannaPlay);
|
||||||
@@ -1672,7 +1683,7 @@ public class AiController {
|
|||||||
if (!game.getStack().isEmpty()) {
|
if (!game.getStack().isEmpty()) {
|
||||||
SpellAbility counter = chooseCounterSpell(getPlayableCounters(cards));
|
SpellAbility counter = chooseCounterSpell(getPlayableCounters(cards));
|
||||||
if (counter != null) return counter;
|
if (counter != null) return counter;
|
||||||
|
|
||||||
SpellAbility counterETB = chooseSpellAbilityToPlayFromList(getPossibleETBCounters(), false);
|
SpellAbility counterETB = chooseSpellAbilityToPlayFromList(getPossibleETBCounters(), false);
|
||||||
if (counterETB != null)
|
if (counterETB != null)
|
||||||
return counterETB;
|
return counterETB;
|
||||||
@@ -1705,8 +1716,7 @@ public class AiController {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
Collections.sort(all, saComparator); // put best spells first
|
Collections.sort(all, saComparator); // put best spells first
|
||||||
}
|
} 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(saComparator, all);
|
||||||
Sentry.captureMessage(ex.getMessage() + "\nAssertionError [verifyTransitivity]: " + assertex);
|
Sentry.captureMessage(ex.getMessage() + "\nAssertionError [verifyTransitivity]: " + assertex);
|
||||||
@@ -1755,7 +1765,7 @@ public class AiController {
|
|||||||
public CardCollection chooseCardsToDelve(int genericCost, CardCollection grave) {
|
public CardCollection chooseCardsToDelve(int genericCost, CardCollection grave) {
|
||||||
CardCollection toExile = new CardCollection();
|
CardCollection toExile = new CardCollection();
|
||||||
int numToExile = Math.min(grave.size(), genericCost);
|
int numToExile = Math.min(grave.size(), genericCost);
|
||||||
|
|
||||||
for (int i = 0; i < numToExile; i++) {
|
for (int i = 0; i < numToExile; i++) {
|
||||||
Card chosen = null;
|
Card chosen = null;
|
||||||
for (final Card c : grave) { // Exile noncreatures first in
|
for (final Card c : grave) { // Exile noncreatures first in
|
||||||
@@ -1783,7 +1793,7 @@ public class AiController {
|
|||||||
|
|
||||||
public boolean doTrigger(SpellAbility spell, boolean mandatory) {
|
public boolean doTrigger(SpellAbility spell, boolean mandatory) {
|
||||||
if (spell instanceof WrappedAbility)
|
if (spell instanceof WrappedAbility)
|
||||||
return doTrigger(((WrappedAbility)spell).getWrappedAbility(), mandatory);
|
return doTrigger(((WrappedAbility) spell).getWrappedAbility(), mandatory);
|
||||||
if (spell.getApi() != null)
|
if (spell.getApi() != null)
|
||||||
return SpellApiToAi.Converter.get(spell.getApi()).doTriggerAI(player, spell, mandatory);
|
return SpellApiToAi.Converter.get(spell.getApi()).doTriggerAI(player, spell, mandatory);
|
||||||
if (spell.getPayCosts() == Cost.Zero && !spell.usesTargeting()) {
|
if (spell.getPayCosts() == Cost.Zero && !spell.usesTargeting()) {
|
||||||
@@ -1929,7 +1939,7 @@ public class AiController {
|
|||||||
} else if ("LowestLoseLife".equals(logic)) {
|
} else if ("LowestLoseLife".equals(logic)) {
|
||||||
return MyRandom.getRandom().nextInt(Math.min(player.getLife() / 3, player.getWeakestOpponent().getLife())) + 1;
|
return MyRandom.getRandom().nextInt(Math.min(player.getLife() / 3, player.getWeakestOpponent().getLife())) + 1;
|
||||||
} else if ("HighestLoseLife".equals(logic)) {
|
} else if ("HighestLoseLife".equals(logic)) {
|
||||||
return Math.min(player.getLife() -1,MyRandom.getRandom().nextInt(Math.max(player.getLife() / 3, player.getWeakestOpponent().getLife())) + 1);
|
return Math.min(player.getLife() - 1, MyRandom.getRandom().nextInt(Math.max(player.getLife() / 3, player.getWeakestOpponent().getLife())) + 1);
|
||||||
} else if ("HighestGetCounter".equals(logic)) {
|
} else if ("HighestGetCounter".equals(logic)) {
|
||||||
return MyRandom.getRandom().nextInt(3);
|
return MyRandom.getRandom().nextInt(3);
|
||||||
} else if (sa.hasSVar("EnergyToPay")) {
|
} else if (sa.hasSVar("EnergyToPay")) {
|
||||||
@@ -1949,8 +1959,7 @@ public class AiController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int chooseNumber(SpellAbility sa, String title, List<Integer> options, Player relatedPlayer) {
|
public int chooseNumber(SpellAbility sa, String title, List<Integer> options, Player relatedPlayer) {
|
||||||
switch(sa.getApi())
|
switch (sa.getApi()) {
|
||||||
{
|
|
||||||
case SetLife: // Reverse the Sands
|
case SetLife: // Reverse the Sands
|
||||||
if (relatedPlayer.equals(sa.getHostCard().getController())) {
|
if (relatedPlayer.equals(sa.getHostCard().getController())) {
|
||||||
return Collections.max(options);
|
return Collections.max(options);
|
||||||
@@ -2076,14 +2085,13 @@ public class AiController {
|
|||||||
|
|
||||||
// this is where the computer cheats
|
// this is where the computer cheats
|
||||||
// changes AllZone.getComputerPlayer().getZone(Zone.Library)
|
// changes AllZone.getComputerPlayer().getZone(Zone.Library)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* smoothComputerManaCurve.
|
* smoothComputerManaCurve.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param in
|
* @param in an array of {@link forge.game.card.Card} objects.
|
||||||
* an array of {@link forge.game.card.Card} objects.
|
|
||||||
* @return an array of {@link forge.game.card.Card} objects.
|
* @return an array of {@link forge.game.card.Card} objects.
|
||||||
*/
|
*/
|
||||||
public CardCollectionView cheatShuffle(CardCollectionView in) {
|
public CardCollectionView cheatShuffle(CardCollectionView in) {
|
||||||
@@ -2111,7 +2119,7 @@ public class AiController {
|
|||||||
library.add(8, land.get(2));
|
library.add(8, land.get(2));
|
||||||
library.add(9, land.get(3));
|
library.add(9, land.get(3));
|
||||||
library.add(10, land.get(4));
|
library.add(10, land.get(4));
|
||||||
|
|
||||||
library.add(12, land.get(5));
|
library.add(12, land.get(5));
|
||||||
library.add(15, land.get(6));
|
library.add(15, land.get(6));
|
||||||
} catch (final IndexOutOfBoundsException e) {
|
} catch (final IndexOutOfBoundsException e) {
|
||||||
@@ -2183,7 +2191,7 @@ public class AiController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Card chooseCardToHiddenOriginChangeZone(ZoneType destination, List<ZoneType> origin, SpellAbility sa,
|
public Card chooseCardToHiddenOriginChangeZone(ZoneType destination, List<ZoneType> origin, SpellAbility sa,
|
||||||
CardCollection fetchList, Player player2, Player decider) {
|
CardCollection fetchList, Player player2, Player decider) {
|
||||||
if (useSimulation) {
|
if (useSimulation) {
|
||||||
return simPicker.chooseCardToHiddenOriginChangeZone(destination, origin, sa, fetchList, player2, decider);
|
return simPicker.chooseCardToHiddenOriginChangeZone(destination, origin, sa, fetchList, player2, decider);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import java.util.Map;
|
|||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import com.google.common.base.Function;
|
||||||
|
import forge.ai.simulation.GameStateEvaluator;
|
||||||
import forge.card.mana.ManaCost;
|
import forge.card.mana.ManaCost;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.tuple.MutablePair;
|
import org.apache.commons.lang3.tuple.MutablePair;
|
||||||
@@ -84,7 +86,7 @@ public class ComputerUtilCard {
|
|||||||
* Sorts a List<Card> by "best" using the EvaluateCreature function.
|
* Sorts a List<Card> by "best" using the EvaluateCreature function.
|
||||||
* the best creatures will be first in the list.
|
* the best creatures will be first in the list.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param list
|
* @param list
|
||||||
*/
|
*/
|
||||||
public static void sortByEvaluateCreature(final CardCollection list) {
|
public static void sortByEvaluateCreature(final CardCollection list) {
|
||||||
@@ -92,11 +94,12 @@ public class ComputerUtilCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The AI doesn't really pick the best artifact, just the most expensive.
|
// The AI doesn't really pick the best artifact, just the most expensive.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* getBestArtifactAI.
|
* getBestArtifactAI.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param list
|
* @param list
|
||||||
* @return a {@link forge.game.card.Card} object.
|
* @return a {@link forge.game.card.Card} object.
|
||||||
*/
|
*/
|
||||||
@@ -111,6 +114,7 @@ public class ComputerUtilCard {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the best Planeswalker from a given list
|
* Returns the best Planeswalker from a given list
|
||||||
|
*
|
||||||
* @param list list of cards to evaluate
|
* @param list list of cards to evaluate
|
||||||
* @return best Planeswalker
|
* @return best Planeswalker
|
||||||
*/
|
*/
|
||||||
@@ -125,6 +129,7 @@ public class ComputerUtilCard {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the worst Planeswalker from a given list
|
* Returns the worst Planeswalker from a given list
|
||||||
|
*
|
||||||
* @param list list of cards to evaluate
|
* @param list list of cards to evaluate
|
||||||
* @return best Planeswalker
|
* @return best Planeswalker
|
||||||
*/
|
*/
|
||||||
@@ -188,16 +193,15 @@ public class ComputerUtilCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// The AI doesn't really pick the best enchantment, just the most expensive.
|
// The AI doesn't really pick the best enchantment, just the most expensive.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* getBestEnchantmentAI.
|
* getBestEnchantmentAI.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param list
|
* @param list
|
||||||
* @param spell
|
* @param spell a {@link forge.game.card.Card} object.
|
||||||
* a {@link forge.game.card.Card} object.
|
* @param targeted a boolean.
|
||||||
* @param targeted
|
|
||||||
* a boolean.
|
|
||||||
* @return a {@link forge.game.card.Card} object.
|
* @return a {@link forge.game.card.Card} object.
|
||||||
*/
|
*/
|
||||||
public static Card getBestEnchantmentAI(final List<Card> list, final SpellAbility spell, final boolean targeted) {
|
public static Card getBestEnchantmentAI(final List<Card> list, final SpellAbility spell, final boolean targeted) {
|
||||||
@@ -219,7 +223,7 @@ public class ComputerUtilCard {
|
|||||||
* <p>
|
* <p>
|
||||||
* getBestLandAI.
|
* getBestLandAI.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param list
|
* @param list
|
||||||
* @return a {@link forge.game.card.Card} object.
|
* @return a {@link forge.game.card.Card} object.
|
||||||
*/
|
*/
|
||||||
@@ -231,7 +235,7 @@ public class ComputerUtilCard {
|
|||||||
|
|
||||||
// prefer to target non basic lands
|
// prefer to target non basic lands
|
||||||
final List<Card> nbLand = CardLists.filter(land, Predicates.not(CardPredicates.Presets.BASIC_LANDS));
|
final List<Card> nbLand = CardLists.filter(land, Predicates.not(CardPredicates.Presets.BASIC_LANDS));
|
||||||
|
|
||||||
if (!nbLand.isEmpty()) {
|
if (!nbLand.isEmpty()) {
|
||||||
// TODO - Rank non basics?
|
// TODO - Rank non basics?
|
||||||
return Aggregates.random(nbLand);
|
return Aggregates.random(nbLand);
|
||||||
@@ -267,7 +271,7 @@ public class ComputerUtilCard {
|
|||||||
* <p>
|
* <p>
|
||||||
* getWorstLand.
|
* getWorstLand.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param lands
|
* @param lands
|
||||||
* @return a {@link forge.game.card.Card} object.
|
* @return a {@link forge.game.card.Card} object.
|
||||||
*/
|
*/
|
||||||
@@ -324,12 +328,10 @@ public class ComputerUtilCard {
|
|||||||
* <p>
|
* <p>
|
||||||
* getCheapestPermanentAI.
|
* getCheapestPermanentAI.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param all
|
* @param all
|
||||||
* @param spell
|
* @param spell a {@link forge.game.card.Card} object.
|
||||||
* a {@link forge.game.card.Card} object.
|
* @param targeted a boolean.
|
||||||
* @param targeted
|
|
||||||
* a boolean.
|
|
||||||
* @return a {@link forge.game.card.Card} object.
|
* @return a {@link forge.game.card.Card} object.
|
||||||
*/
|
*/
|
||||||
public static Card getCheapestPermanentAI(Iterable<Card> all, final SpellAbility spell, final boolean targeted) {
|
public static Card getCheapestPermanentAI(Iterable<Card> all, final SpellAbility spell, final boolean targeted) {
|
||||||
@@ -347,7 +349,7 @@ public class ComputerUtilCard {
|
|||||||
|
|
||||||
// get cheapest card:
|
// get cheapest card:
|
||||||
Card cheapest = null;
|
Card cheapest = null;
|
||||||
|
|
||||||
for (Card c : all) {
|
for (Card c : all) {
|
||||||
if (cheapest == null || c.getManaCost().getCMC() <= cheapest.getManaCost().getCMC()) {
|
if (cheapest == null || c.getManaCost().getCMC() <= cheapest.getManaCost().getCMC()) {
|
||||||
cheapest = c;
|
cheapest = c;
|
||||||
@@ -358,11 +360,12 @@ public class ComputerUtilCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// returns null if list.size() == 0
|
// returns null if list.size() == 0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* getBestAI.
|
* getBestAI.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param list
|
* @param list
|
||||||
* @return a {@link forge.game.card.Card} object.
|
* @return a {@link forge.game.card.Card} object.
|
||||||
*/
|
*/
|
||||||
@@ -380,9 +383,8 @@ public class ComputerUtilCard {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* getBestCreatureAI.
|
* getBestCreatureAI.
|
||||||
*
|
*
|
||||||
* @param list
|
* @param list the list
|
||||||
* the list
|
|
||||||
* @return the card
|
* @return the card
|
||||||
*/
|
*/
|
||||||
public static Card getBestCreatureAI(final Iterable<Card> list) {
|
public static Card getBestCreatureAI(final Iterable<Card> list) {
|
||||||
@@ -392,11 +394,24 @@ public class ComputerUtilCard {
|
|||||||
return Aggregates.itemWithMax(Iterables.filter(list, CardPredicates.Presets.CREATURES), ComputerUtilCard.creatureEvaluator);
|
return Aggregates.itemWithMax(Iterables.filter(list, CardPredicates.Presets.CREATURES), ComputerUtilCard.creatureEvaluator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getBestLandToPlayAI.
|
||||||
|
*
|
||||||
|
* @param list the list
|
||||||
|
* @return the card
|
||||||
|
*/
|
||||||
|
public static Card getBestLandToPlayAI(final Iterable<Card> list) {
|
||||||
|
if (Iterables.size(list) == 1) {
|
||||||
|
return Iterables.get(list, 0);
|
||||||
|
}
|
||||||
|
return Aggregates.itemWithMax(Iterables.filter(list, CardPredicates.Presets.LANDS), ComputerUtilCard.landEvaluator);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* getWorstCreatureAI.
|
* getWorstCreatureAI.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param list
|
* @param list
|
||||||
* @return a {@link forge.game.card.Card} object.
|
* @return a {@link forge.game.card.Card} object.
|
||||||
*/
|
*/
|
||||||
@@ -408,11 +423,12 @@ public class ComputerUtilCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This selection rates tokens higher
|
// This selection rates tokens higher
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* getBestCreatureToBounceAI.
|
* getBestCreatureToBounceAI.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param list
|
* @param list
|
||||||
* @return a {@link forge.game.card.Card} object.
|
* @return a {@link forge.game.card.Card} object.
|
||||||
*/
|
*/
|
||||||
@@ -438,7 +454,7 @@ public class ComputerUtilCard {
|
|||||||
|
|
||||||
// For ability of Oracle en-Vec, return the first card that are going to attack next turn
|
// For ability of Oracle en-Vec, return the first card that are going to attack next turn
|
||||||
public static Card getBestCreatureToAttackNextTurnAI(final Player aiPlayer, final Iterable<Card> list) {
|
public static Card getBestCreatureToAttackNextTurnAI(final Player aiPlayer, final Iterable<Card> list) {
|
||||||
AiController aic = ((PlayerControllerAi)aiPlayer.getController()).getAi();
|
AiController aic = ((PlayerControllerAi) aiPlayer.getController()).getAi();
|
||||||
for (final Card card : list) {
|
for (final Card card : list) {
|
||||||
if (aic.getPredictedCombatNextTurn().isAttacking(card)) {
|
if (aic.getPredictedCombatNextTurn().isAttacking(card)) {
|
||||||
return card;
|
return card;
|
||||||
@@ -451,7 +467,7 @@ public class ComputerUtilCard {
|
|||||||
* <p>
|
* <p>
|
||||||
* getWorstAI.
|
* getWorstAI.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param list
|
* @param list
|
||||||
* @return a {@link forge.game.card.Card} object.
|
* @return a {@link forge.game.card.Card} object.
|
||||||
*/
|
*/
|
||||||
@@ -463,20 +479,16 @@ public class ComputerUtilCard {
|
|||||||
* <p>
|
* <p>
|
||||||
* getWorstPermanentAI.
|
* getWorstPermanentAI.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param list
|
* @param list
|
||||||
* @param biasEnch
|
* @param biasEnch a boolean.
|
||||||
* a boolean.
|
* @param biasLand a boolean.
|
||||||
* @param biasLand
|
* @param biasArt a boolean.
|
||||||
* a boolean.
|
* @param biasCreature a boolean.
|
||||||
* @param biasArt
|
|
||||||
* a boolean.
|
|
||||||
* @param biasCreature
|
|
||||||
* a boolean.
|
|
||||||
* @return a {@link forge.game.card.Card} object.
|
* @return a {@link forge.game.card.Card} object.
|
||||||
*/
|
*/
|
||||||
public static Card getWorstPermanentAI(final Iterable<Card> list, final boolean biasEnch, final boolean biasLand,
|
public static Card getWorstPermanentAI(final Iterable<Card> list, final boolean biasEnch, final boolean biasLand,
|
||||||
final boolean biasArt, final boolean biasCreature) {
|
final boolean biasArt, final boolean biasCreature) {
|
||||||
if (Iterables.isEmpty(list)) {
|
if (Iterables.isEmpty(list)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -486,7 +498,7 @@ public class ComputerUtilCard {
|
|||||||
return getCheapestPermanentAI(CardLists.filter(list, CardPredicates.Presets.ENCHANTMENTS), null, false);
|
return getCheapestPermanentAI(CardLists.filter(list, CardPredicates.Presets.ENCHANTMENTS), null, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
final boolean hasArtifacts = Iterables.any(list, CardPredicates.Presets.ARTIFACTS);
|
final boolean hasArtifacts = Iterables.any(list, CardPredicates.Presets.ARTIFACTS);
|
||||||
if (biasArt && hasArtifacts) {
|
if (biasArt && hasArtifacts) {
|
||||||
return getCheapestPermanentAI(CardLists.filter(list, CardPredicates.Presets.ARTIFACTS), null, false);
|
return getCheapestPermanentAI(CardLists.filter(list, CardPredicates.Presets.ARTIFACTS), null, false);
|
||||||
}
|
}
|
||||||
@@ -557,14 +569,14 @@ public class ComputerUtilCard {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private static final CreatureEvaluator creatureEvaluator = new CreatureEvaluator();
|
private static final CreatureEvaluator creatureEvaluator = new CreatureEvaluator();
|
||||||
|
private static final LandEvaluator landEvaluator = new LandEvaluator();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* evaluateCreature.
|
* evaluateCreature.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param c
|
* @param c a {@link forge.game.card.Card} object.
|
||||||
* a {@link forge.game.card.Card} object.
|
|
||||||
* @return a int.
|
* @return a int.
|
||||||
*/
|
*/
|
||||||
public static int evaluateCreature(final Card c) {
|
public static int evaluateCreature(final Card c) {
|
||||||
@@ -603,14 +615,15 @@ public class ComputerUtilCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean doesCreatureAttackAI(final Player aiPlayer, final Card card) {
|
public static boolean doesCreatureAttackAI(final Player aiPlayer, final Card card) {
|
||||||
AiController aic = ((PlayerControllerAi)aiPlayer.getController()).getAi();
|
AiController aic = ((PlayerControllerAi) aiPlayer.getController()).getAi();
|
||||||
return aic.getPredictedCombat().isAttacking(card);
|
return aic.getPredictedCombat().isAttacking(card);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension of doesCreatureAttackAI() for "virtual" creatures that do not actually exist on the battlefield yet
|
* Extension of doesCreatureAttackAI() for "virtual" creatures that do not actually exist on the battlefield yet
|
||||||
* such as unanimated manlands.
|
* such as unanimated manlands.
|
||||||
* @param ai controller of creature
|
*
|
||||||
|
* @param ai controller of creature
|
||||||
* @param card creature to be evaluated
|
* @param card creature to be evaluated
|
||||||
* @return creature will be attack
|
* @return creature will be attack
|
||||||
*/
|
*/
|
||||||
@@ -622,8 +635,9 @@ public class ComputerUtilCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a mock combat where ai is being attacked and returns the list of likely blockers.
|
* Create a mock combat where ai is being attacked and returns the list of likely blockers.
|
||||||
* @param ai blocking player
|
*
|
||||||
|
* @param ai blocking player
|
||||||
* @param blockers list of additional blockers to be considered
|
* @param blockers list of additional blockers to be considered
|
||||||
* @return list of creatures assigned to block in the simulation
|
* @return list of creatures assigned to block in the simulation
|
||||||
*/
|
*/
|
||||||
@@ -654,7 +668,8 @@ public class ComputerUtilCard {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Decide if a creature is going to be used as a blocker.
|
* Decide if a creature is going to be used as a blocker.
|
||||||
* @param ai controller of creature
|
*
|
||||||
|
* @param ai controller of creature
|
||||||
* @param blocker creature to be evaluated
|
* @param blocker creature to be evaluated
|
||||||
* @return creature will be a blocker
|
* @return creature will be a blocker
|
||||||
*/
|
*/
|
||||||
@@ -664,7 +679,8 @@ public class ComputerUtilCard {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if an attacker can be blocked profitably (ie. kill attacker)
|
* Check if an attacker can be blocked profitably (ie. kill attacker)
|
||||||
* @param ai controller of attacking creature
|
*
|
||||||
|
* @param ai controller of attacking creature
|
||||||
* @param attacker attacking creature to evaluate
|
* @param attacker attacking creature to evaluate
|
||||||
* @return attacker will die
|
* @return attacker will die
|
||||||
*/
|
*/
|
||||||
@@ -708,9 +724,8 @@ public class ComputerUtilCard {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* getMostExpensivePermanentAI.
|
* getMostExpensivePermanentAI.
|
||||||
*
|
*
|
||||||
* @param all
|
* @param all the all
|
||||||
* the all
|
|
||||||
* @return the card
|
* @return the card
|
||||||
*/
|
*/
|
||||||
public static Card getMostExpensivePermanentAI(final Iterable<Card> all) {
|
public static Card getMostExpensivePermanentAI(final Iterable<Card> all) {
|
||||||
@@ -742,7 +757,7 @@ public class ComputerUtilCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final Map<String, Integer> map = Maps.newHashMap();
|
final Map<String, Integer> map = Maps.newHashMap();
|
||||||
|
|
||||||
for (final Card c : list) {
|
for (final Card c : list) {
|
||||||
final String name = c.getName();
|
final String name = c.getName();
|
||||||
Integer currentCnt = map.get(name);
|
Integer currentCnt = map.get(name);
|
||||||
@@ -901,7 +916,7 @@ public class ComputerUtilCard {
|
|||||||
* <p>
|
* <p>
|
||||||
* getMostProminentColor.
|
* getMostProminentColor.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param list
|
* @param list
|
||||||
* @return a {@link java.lang.String} object.
|
* @return a {@link java.lang.String} object.
|
||||||
*/
|
*/
|
||||||
@@ -926,22 +941,23 @@ public class ComputerUtilCard {
|
|||||||
|
|
||||||
public static List<String> getColorByProminence(final List<Card> list) {
|
public static List<String> getColorByProminence(final List<Card> list) {
|
||||||
int cntColors = MagicColor.WUBRG.length;
|
int cntColors = MagicColor.WUBRG.length;
|
||||||
final List<Pair<Byte,Integer>> map = new ArrayList<>();
|
final List<Pair<Byte, Integer>> map = new ArrayList<>();
|
||||||
for (int i = 0; i < cntColors; i++) {
|
for (int i = 0; i < cntColors; i++) {
|
||||||
map.add(MutablePair.of(MagicColor.WUBRG[i], 0));
|
map.add(MutablePair.of(MagicColor.WUBRG[i], 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final Card crd : list) {
|
for (final Card crd : list) {
|
||||||
ColorSet color = crd.getColor();
|
ColorSet color = crd.getColor();
|
||||||
if (color.hasWhite()) map.get(0).setValue(Integer.valueOf(map.get(0).getValue()+1));
|
if (color.hasWhite()) map.get(0).setValue(Integer.valueOf(map.get(0).getValue() + 1));
|
||||||
if (color.hasBlue()) map.get(1).setValue(Integer.valueOf(map.get(1).getValue()+1));
|
if (color.hasBlue()) map.get(1).setValue(Integer.valueOf(map.get(1).getValue() + 1));
|
||||||
if (color.hasBlack()) map.get(2).setValue(Integer.valueOf(map.get(2).getValue()+1));
|
if (color.hasBlack()) map.get(2).setValue(Integer.valueOf(map.get(2).getValue() + 1));
|
||||||
if (color.hasRed()) map.get(3).setValue(Integer.valueOf(map.get(3).getValue()+1));
|
if (color.hasRed()) map.get(3).setValue(Integer.valueOf(map.get(3).getValue() + 1));
|
||||||
if (color.hasGreen()) map.get(4).setValue(Integer.valueOf(map.get(4).getValue()+1));
|
if (color.hasGreen()) map.get(4).setValue(Integer.valueOf(map.get(4).getValue() + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
Collections.sort(map, new Comparator<Pair<Byte,Integer>>() {
|
Collections.sort(map, new Comparator<Pair<Byte, Integer>>() {
|
||||||
@Override public int compare(Pair<Byte, Integer> o1, Pair<Byte, Integer> o2) {
|
@Override
|
||||||
|
public int compare(Pair<Byte, Integer> o1, Pair<Byte, Integer> o2) {
|
||||||
return o2.getValue() - o1.getValue();
|
return o2.getValue() - o1.getValue();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -978,64 +994,52 @@ public class ComputerUtilCard {
|
|||||||
|
|
||||||
if (logic.equals("MostProminentInHumanDeck")) {
|
if (logic.equals("MostProminentInHumanDeck")) {
|
||||||
chosen.add(getMostProminentColor(CardLists.filterControlledBy(game.getCardsInGame(), opp), colorChoices));
|
chosen.add(getMostProminentColor(CardLists.filterControlledBy(game.getCardsInGame(), opp), colorChoices));
|
||||||
}
|
} else if (logic.equals("MostProminentInComputerDeck")) {
|
||||||
else if (logic.equals("MostProminentInComputerDeck")) {
|
|
||||||
chosen.add(getMostProminentColor(CardLists.filterControlledBy(game.getCardsInGame(), ai), colorChoices));
|
chosen.add(getMostProminentColor(CardLists.filterControlledBy(game.getCardsInGame(), ai), colorChoices));
|
||||||
}
|
} else if (logic.equals("MostProminentDualInComputerDeck")) {
|
||||||
else if (logic.equals("MostProminentDualInComputerDeck")) {
|
|
||||||
List<String> prominence = getColorByProminence(CardLists.filterControlledBy(game.getCardsInGame(), ai));
|
List<String> prominence = getColorByProminence(CardLists.filterControlledBy(game.getCardsInGame(), ai));
|
||||||
chosen.add(prominence.get(0));
|
chosen.add(prominence.get(0));
|
||||||
chosen.add(prominence.get(1));
|
chosen.add(prominence.get(1));
|
||||||
}
|
} else if (logic.equals("MostProminentInGame")) {
|
||||||
else if (logic.equals("MostProminentInGame")) {
|
|
||||||
chosen.add(getMostProminentColor(game.getCardsInGame(), colorChoices));
|
chosen.add(getMostProminentColor(game.getCardsInGame(), colorChoices));
|
||||||
}
|
} else if (logic.equals("MostProminentHumanCreatures")) {
|
||||||
else if (logic.equals("MostProminentHumanCreatures")) {
|
|
||||||
CardCollectionView list = opp.getCreaturesInPlay();
|
CardCollectionView list = opp.getCreaturesInPlay();
|
||||||
if (list.isEmpty()) {
|
if (list.isEmpty()) {
|
||||||
list = CardLists.filter(CardLists.filterControlledBy(game.getCardsInGame(), opp), CardPredicates.Presets.CREATURES);
|
list = CardLists.filter(CardLists.filterControlledBy(game.getCardsInGame(), opp), CardPredicates.Presets.CREATURES);
|
||||||
}
|
}
|
||||||
chosen.add(getMostProminentColor(list, colorChoices));
|
chosen.add(getMostProminentColor(list, colorChoices));
|
||||||
}
|
} else if (logic.equals("MostProminentComputerControls")) {
|
||||||
else if (logic.equals("MostProminentComputerControls")) {
|
|
||||||
chosen.add(getMostProminentColor(ai.getCardsIn(ZoneType.Battlefield), colorChoices));
|
chosen.add(getMostProminentColor(ai.getCardsIn(ZoneType.Battlefield), colorChoices));
|
||||||
}
|
} else if (logic.equals("MostProminentHumanControls")) {
|
||||||
else if (logic.equals("MostProminentHumanControls")) {
|
|
||||||
chosen.add(getMostProminentColor(opp.getCardsIn(ZoneType.Battlefield), colorChoices));
|
chosen.add(getMostProminentColor(opp.getCardsIn(ZoneType.Battlefield), colorChoices));
|
||||||
}
|
} else if (logic.equals("MostProminentPermanent")) {
|
||||||
else if (logic.equals("MostProminentPermanent")) {
|
|
||||||
chosen.add(getMostProminentColor(game.getCardsIn(ZoneType.Battlefield), colorChoices));
|
chosen.add(getMostProminentColor(game.getCardsIn(ZoneType.Battlefield), colorChoices));
|
||||||
}
|
} else if (logic.equals("MostProminentAttackers") && game.getPhaseHandler().inCombat()) {
|
||||||
else if (logic.equals("MostProminentAttackers") && game.getPhaseHandler().inCombat()) {
|
|
||||||
chosen.add(getMostProminentColor(game.getCombat().getAttackers(), colorChoices));
|
chosen.add(getMostProminentColor(game.getCombat().getAttackers(), colorChoices));
|
||||||
}
|
} else if (logic.equals("MostProminentInActivePlayerHand")) {
|
||||||
else if (logic.equals("MostProminentInActivePlayerHand")) {
|
|
||||||
chosen.add(getMostProminentColor(game.getPhaseHandler().getPlayerTurn().getCardsIn(ZoneType.Hand), colorChoices));
|
chosen.add(getMostProminentColor(game.getPhaseHandler().getPlayerTurn().getCardsIn(ZoneType.Hand), colorChoices));
|
||||||
}
|
} else if (logic.equals("MostProminentInComputerDeckButGreen")) {
|
||||||
else if (logic.equals("MostProminentInComputerDeckButGreen")) {
|
List<String> prominence = getColorByProminence(CardLists.filterControlledBy(game.getCardsInGame(), ai));
|
||||||
List<String> prominence = getColorByProminence(CardLists.filterControlledBy(game.getCardsInGame(), ai));
|
if (prominence.get(0).equals(MagicColor.Constant.GREEN)) {
|
||||||
if (prominence.get(0).equals(MagicColor.Constant.GREEN)) {
|
|
||||||
chosen.add(prominence.get(1));
|
chosen.add(prominence.get(1));
|
||||||
} else {
|
} else {
|
||||||
chosen.add(prominence.get(0));
|
chosen.add(prominence.get(0));
|
||||||
}
|
}
|
||||||
}
|
} else if (logic.equals("MostExcessOpponentControls")) {
|
||||||
else if (logic.equals("MostExcessOpponentControls")) {
|
int maxExcess = 0;
|
||||||
int maxExcess = 0;
|
String bestColor = Constant.GREEN;
|
||||||
String bestColor = Constant.GREEN;
|
for (byte color : MagicColor.WUBRG) {
|
||||||
for (byte color : MagicColor.WUBRG) {
|
CardCollectionView ailist = ai.getColoredCardsInPlay(color);
|
||||||
CardCollectionView ailist = ai.getColoredCardsInPlay(color);
|
CardCollectionView opplist = opp.getColoredCardsInPlay(color);
|
||||||
CardCollectionView opplist = opp.getColoredCardsInPlay(color);
|
|
||||||
|
|
||||||
int excess = evaluatePermanentList(opplist) - evaluatePermanentList(ailist);
|
int excess = evaluatePermanentList(opplist) - evaluatePermanentList(ailist);
|
||||||
if (excess > maxExcess) {
|
if (excess > maxExcess) {
|
||||||
maxExcess = excess;
|
maxExcess = excess;
|
||||||
bestColor = MagicColor.toLongString(color);
|
bestColor = MagicColor.toLongString(color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chosen.add(bestColor);
|
chosen.add(bestColor);
|
||||||
}
|
} else if (logic.equals("MostProminentKeywordInComputerDeck")) {
|
||||||
else if (logic.equals("MostProminentKeywordInComputerDeck")) {
|
|
||||||
CardCollectionView list = ai.getAllCards();
|
CardCollectionView list = ai.getAllCards();
|
||||||
int m1 = 0;
|
int m1 = 0;
|
||||||
String chosenColor = MagicColor.Constant.WHITE;
|
String chosenColor = MagicColor.Constant.WHITE;
|
||||||
@@ -1048,8 +1052,7 @@ public class ComputerUtilCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
chosen.add(chosenColor);
|
chosen.add(chosenColor);
|
||||||
}
|
} else if (logic.equals("HighestDevotionToColor")) {
|
||||||
else if (logic.equals("HighestDevotionToColor")) {
|
|
||||||
int curDevotion = 0;
|
int curDevotion = 0;
|
||||||
String chosenColor = MagicColor.Constant.WHITE;
|
String chosenColor = MagicColor.Constant.WHITE;
|
||||||
CardCollectionView hand = ai.getCardsIn(ZoneType.Hand);
|
CardCollectionView hand = ai.getCardsIn(ZoneType.Hand);
|
||||||
@@ -1075,7 +1078,7 @@ public class ComputerUtilCard {
|
|||||||
|
|
||||||
public static boolean useRemovalNow(final SpellAbility sa, final Card c, final int dmg, ZoneType destination) {
|
public static boolean useRemovalNow(final SpellAbility sa, final Card c, final int dmg, ZoneType destination) {
|
||||||
final Player ai = sa.getActivatingPlayer();
|
final Player ai = sa.getActivatingPlayer();
|
||||||
final AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
final AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
final PhaseHandler ph = game.getPhaseHandler();
|
final PhaseHandler ph = game.getPhaseHandler();
|
||||||
final PhaseType phaseType = ph.getPhase();
|
final PhaseType phaseType = ph.getPhase();
|
||||||
@@ -1085,7 +1088,7 @@ public class ComputerUtilCard {
|
|||||||
final int costTarget = c.getCMC();
|
final int costTarget = c.getCMC();
|
||||||
|
|
||||||
if (!sa.isSpell()) {
|
if (!sa.isSpell()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Check for cards that profit from spells - for example Prowess or Threshold
|
//Check for cards that profit from spells - for example Prowess or Threshold
|
||||||
@@ -1099,14 +1102,14 @@ public class ComputerUtilCard {
|
|||||||
final Combat combat = new Combat(ai);
|
final Combat combat = new Combat(ai);
|
||||||
aiAtk.removeBlocker(c);
|
aiAtk.removeBlocker(c);
|
||||||
aiAtk.declareAttackers(combat);
|
aiAtk.declareAttackers(combat);
|
||||||
if (!combat.getAttackers().isEmpty()) {
|
if (!combat.getAttackers().isEmpty()) {
|
||||||
AiAttackController aiAtk2 = new AiAttackController(ai);
|
AiAttackController aiAtk2 = new AiAttackController(ai);
|
||||||
final Combat combat2 = new Combat(ai);
|
final Combat combat2 = new Combat(ai);
|
||||||
aiAtk2.declareAttackers(combat2);
|
aiAtk2.declareAttackers(combat2);
|
||||||
if (combat.getAttackers().size() > combat2.getAttackers().size()) {
|
if (combat.getAttackers().size() > combat2.getAttackers().size()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// interrupt 2: remove blocker to save my attacker
|
// interrupt 2: remove blocker to save my attacker
|
||||||
@@ -1152,7 +1155,7 @@ public class ComputerUtilCard {
|
|||||||
if (!stack.isEmpty()) {
|
if (!stack.isEmpty()) {
|
||||||
final SpellAbility topStack = stack.peekAbility();
|
final SpellAbility topStack = stack.peekAbility();
|
||||||
if (topStack.getActivatingPlayer().equals(opp) && c.equals(topStack.getTargetCard()) && topStack.isSpell()) {
|
if (topStack.getActivatingPlayer().equals(opp) && c.equals(topStack.getTargetCard()) && topStack.isSpell()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1168,7 +1171,7 @@ public class ComputerUtilCard {
|
|||||||
valueBurn /= 2; //preserve option to burn to the face
|
valueBurn /= 2; //preserve option to burn to the face
|
||||||
}
|
}
|
||||||
if (valueBurn >= 0.8 && phaseType.isBefore(PhaseType.COMBAT_END)) {
|
if (valueBurn >= 0.8 && phaseType.isBefore(PhaseType.COMBAT_END)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1193,33 +1196,33 @@ public class ComputerUtilCard {
|
|||||||
if (c.isLand()) {
|
if (c.isLand()) {
|
||||||
valueTempo += 0.5f / opp.getLandsInPlay().size(); //set back opponent's mana
|
valueTempo += 0.5f / opp.getLandsInPlay().size(); //set back opponent's mana
|
||||||
if ("Land".equals(sa.getParam("ValidTgts")) && ph.getPhase().isAfter(PhaseType.COMBAT_END)) {
|
if ("Land".equals(sa.getParam("ValidTgts")) && ph.getPhase().isAfter(PhaseType.COMBAT_END)) {
|
||||||
valueTempo += 0.5; // especially when nothing else can be targeted
|
valueTempo += 0.5; // especially when nothing else can be targeted
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!ph.isPlayerTurn(ai) && ph.getPhase().equals(PhaseType.END_OF_TURN)) {
|
if (!ph.isPlayerTurn(ai) && ph.getPhase().equals(PhaseType.END_OF_TURN)) {
|
||||||
valueTempo *= 2; //prefer to cast at opponent EOT
|
valueTempo *= 2; //prefer to cast at opponent EOT
|
||||||
}
|
}
|
||||||
if (valueTempo >= 0.8 && ph.getPhase().isBefore(PhaseType.COMBAT_END)) {
|
if (valueTempo >= 0.8 && ph.getPhase().isBefore(PhaseType.COMBAT_END)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//evaluate threat of targeted card
|
//evaluate threat of targeted card
|
||||||
float threat = 0;
|
float threat = 0;
|
||||||
if (c.isCreature()) {
|
if (c.isCreature()) {
|
||||||
// the base value for evaluate creature is 100
|
// the base value for evaluate creature is 100
|
||||||
threat += (-1 + 1.0f * evaluateCreature(c) / 100) / costRemoval;
|
threat += (-1 + 1.0f * evaluateCreature(c) / 100) / costRemoval;
|
||||||
if (ai.getLife() > 0 && ComputerUtilCombat.canAttackNextTurn(c)) {
|
if (ai.getLife() > 0 && ComputerUtilCombat.canAttackNextTurn(c)) {
|
||||||
Combat combat = game.getCombat();
|
Combat combat = game.getCombat();
|
||||||
threat += 1.0f * ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) / ai.getLife();
|
threat += 1.0f * ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) / ai.getLife();
|
||||||
//TODO:add threat from triggers and other abilities (ie. Master of Cruelties)
|
//TODO:add threat from triggers and other abilities (ie. Master of Cruelties)
|
||||||
}
|
}
|
||||||
if (ph.isPlayerTurn(ai) && phaseType.isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
if (ph.isPlayerTurn(ai) && phaseType.isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
|
||||||
threat *= 0.1f;
|
threat *= 0.1f;
|
||||||
}
|
}
|
||||||
if (!ph.isPlayerTurn(ai) &&
|
if (!ph.isPlayerTurn(ai) &&
|
||||||
(phaseType.isBefore(PhaseType.COMBAT_BEGIN) || phaseType.isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS))) {
|
(phaseType.isBefore(PhaseType.COMBAT_BEGIN) || phaseType.isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS))) {
|
||||||
threat *= 0.1f;
|
threat *= 0.1f;
|
||||||
}
|
}
|
||||||
} else if (c.isPlaneswalker()) {
|
} else if (c.isPlaneswalker()) {
|
||||||
threat = 1;
|
threat = 1;
|
||||||
} else if (aic.getBooleanProperty(AiProps.ACTIVELY_DESTROY_ARTS_AND_NONAURA_ENCHS) && ((c.isArtifact() && !c.isCreature()) || (c.isEnchantment() && !c.isAura()))) {
|
} else if (aic.getBooleanProperty(AiProps.ACTIVELY_DESTROY_ARTS_AND_NONAURA_ENCHS) && ((c.isArtifact() && !c.isCreature()) || (c.isEnchantment() && !c.isAura()))) {
|
||||||
@@ -1296,20 +1299,22 @@ public class ComputerUtilCard {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Decides if the "pump" is worthwhile
|
* Decides if the "pump" is worthwhile
|
||||||
* @param ai casting player
|
*
|
||||||
* @param sa Pump* or CounterPut*
|
* @param ai casting player
|
||||||
* @param c target of sa
|
* @param sa Pump* or CounterPut*
|
||||||
|
* @param c target of sa
|
||||||
* @param toughness +T
|
* @param toughness +T
|
||||||
* @param power +P
|
* @param power +P
|
||||||
* @param keywords additional keywords from sa (only for Pump)
|
* @param keywords additional keywords from sa (only for Pump)
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static boolean shouldPumpCard(final Player ai, final SpellAbility sa, final Card c, final int toughness,
|
public static boolean shouldPumpCard(final Player ai, final SpellAbility sa, final Card c, final int toughness,
|
||||||
final int power, final List<String> keywords) {
|
final int power, final List<String> keywords) {
|
||||||
return shouldPumpCard(ai, sa, c, toughness, power, keywords, false);
|
return shouldPumpCard(ai, sa, c, toughness, power, keywords, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean shouldPumpCard(final Player ai, final SpellAbility sa, final Card c, final int toughness,
|
public static boolean shouldPumpCard(final Player ai, final SpellAbility sa, final Card c, final int toughness,
|
||||||
final int power, final List<String> keywords, boolean immediately) {
|
final int power, final List<String> keywords, boolean immediately) {
|
||||||
final Game game = ai.getGame();
|
final Game game = ai.getGame();
|
||||||
final PhaseHandler phase = game.getPhaseHandler();
|
final PhaseHandler phase = game.getPhaseHandler();
|
||||||
final Combat combat = phase.getCombat();
|
final Combat combat = phase.getCombat();
|
||||||
@@ -1452,7 +1457,9 @@ public class ComputerUtilCard {
|
|||||||
boolean pumpedWillDie = false;
|
boolean pumpedWillDie = false;
|
||||||
final boolean isAttacking = combat.isAttacking(c);
|
final boolean isAttacking = combat.isAttacking(c);
|
||||||
|
|
||||||
if ((isBerserk && isAttacking) || loseCardAtEOT) { pumpedWillDie = true; }
|
if ((isBerserk && isAttacking) || loseCardAtEOT) {
|
||||||
|
pumpedWillDie = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (isAttacking) {
|
if (isAttacking) {
|
||||||
pumpedCombat.addAttacker(pumped, opp);
|
pumpedCombat.addAttacker(pumped, opp);
|
||||||
@@ -1475,7 +1482,7 @@ public class ComputerUtilCard {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//1. save combatant
|
//1. save combatant
|
||||||
if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat) && !pumpedWillDie
|
if (ComputerUtilCombat.combatantWouldBeDestroyed(ai, c, combat) && !pumpedWillDie
|
||||||
&& !c.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
&& !c.hasKeyword(Keyword.INDESTRUCTIBLE)) {
|
||||||
// hack because attackerWouldBeDestroyed()
|
// hack because attackerWouldBeDestroyed()
|
||||||
// does not check for Indestructible when computing lethal damage
|
// does not check for Indestructible when computing lethal damage
|
||||||
@@ -1492,8 +1499,8 @@ public class ComputerUtilCard {
|
|||||||
}
|
}
|
||||||
if (survivor) {
|
if (survivor) {
|
||||||
for (Card o : opposing) {
|
for (Card o : opposing) {
|
||||||
if (!ComputerUtilCombat.combatantWouldBeDestroyed(opp, o, combat)
|
if (!ComputerUtilCombat.combatantWouldBeDestroyed(opp, o, combat)
|
||||||
&& !(o.hasSVar("SacMe") && Integer.parseInt(o.getSVar("SacMe")) > 2)) {
|
&& !(o.hasSVar("SacMe") && Integer.parseInt(o.getSVar("SacMe")) > 2)) {
|
||||||
if (isAttacking) {
|
if (isAttacking) {
|
||||||
if (ComputerUtilCombat.blockerWouldBeDestroyed(opp, o, pumpedCombat)) {
|
if (ComputerUtilCombat.blockerWouldBeDestroyed(opp, o, pumpedCombat)) {
|
||||||
return true;
|
return true;
|
||||||
@@ -1526,9 +1533,9 @@ public class ComputerUtilCard {
|
|||||||
dmg = 0;
|
dmg = 0;
|
||||||
}
|
}
|
||||||
if (c.hasKeyword(Keyword.TRAMPLE) || keywords.contains("Trample")) {
|
if (c.hasKeyword(Keyword.TRAMPLE) || keywords.contains("Trample")) {
|
||||||
for (Card b : combat.getBlockers(c)) {
|
for (Card b : combat.getBlockers(c)) {
|
||||||
pumpedDmg -= ComputerUtilCombat.getDamageToKill(b, false);
|
pumpedDmg -= ComputerUtilCombat.getDamageToKill(b, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pumpedDmg = 0;
|
pumpedDmg = 0;
|
||||||
}
|
}
|
||||||
@@ -1615,7 +1622,7 @@ public class ComputerUtilCard {
|
|||||||
// if we got here, Berserk will result in the pumped creature dying at EOT and the opponent will not lose
|
// if we got here, Berserk will result in the pumped creature dying at EOT and the opponent will not lose
|
||||||
// (other similar cards with AILogic$ Berserk that do not die only when attacking are excluded from consideration)
|
// (other similar cards with AILogic$ Berserk that do not die only when attacking are excluded from consideration)
|
||||||
if (ai.getController() instanceof PlayerControllerAi) {
|
if (ai.getController() instanceof PlayerControllerAi) {
|
||||||
boolean aggr = ((PlayerControllerAi)ai.getController()).getAi().getBooleanProperty(AiProps.USE_BERSERK_AGGRESSIVELY)
|
boolean aggr = ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.USE_BERSERK_AGGRESSIVELY)
|
||||||
|| sa.hasParam("AtEOT");
|
|| sa.hasParam("AtEOT");
|
||||||
if (!aggr) {
|
if (!aggr) {
|
||||||
return false;
|
return false;
|
||||||
@@ -1635,44 +1642,45 @@ public class ComputerUtilCard {
|
|||||||
boolean isHeldCombatTrick = combatTrick && wantToHoldTrick;
|
boolean isHeldCombatTrick = combatTrick && wantToHoldTrick;
|
||||||
|
|
||||||
if (isHeldCombatTrick) {
|
if (isHeldCombatTrick) {
|
||||||
if (AiCardMemory.isMemorySetEmpty(ai, AiCardMemory.MemorySet.TRICK_ATTACKERS)) {
|
if (AiCardMemory.isMemorySetEmpty(ai, AiCardMemory.MemorySet.TRICK_ATTACKERS)) {
|
||||||
// Attempt to hold combat tricks until blockers are declared, and try to lure the opponent into blocking
|
// Attempt to hold combat tricks until blockers are declared, and try to lure the opponent into blocking
|
||||||
// (The AI will only do it for one attacker at the moment, otherwise it risks running his attackers into
|
// (The AI will only do it for one attacker at the moment, otherwise it risks running his attackers into
|
||||||
// an army of opposing blockers with only one combat trick in hand)
|
// an army of opposing blockers with only one combat trick in hand)
|
||||||
// Reserve the mana until Declare Blockers such that the AI doesn't tap out before having a chance to use
|
// Reserve the mana until Declare Blockers such that the AI doesn't tap out before having a chance to use
|
||||||
// the combat trick
|
// the combat trick
|
||||||
boolean reserved = false;
|
boolean reserved = false;
|
||||||
if (ai.getController().isAI()) {
|
if (ai.getController().isAI()) {
|
||||||
reserved = ((PlayerControllerAi) ai.getController()).getAi().reserveManaSources(sa, PhaseType.COMBAT_DECLARE_BLOCKERS, false);
|
reserved = ((PlayerControllerAi) ai.getController()).getAi().reserveManaSources(sa, PhaseType.COMBAT_DECLARE_BLOCKERS, false);
|
||||||
// Only proceed with this if we could actually reserve mana
|
// Only proceed with this if we could actually reserve mana
|
||||||
if (reserved) {
|
if (reserved) {
|
||||||
AiCardMemory.rememberCard(ai, c, AiCardMemory.MemorySet.MANDATORY_ATTACKERS);
|
AiCardMemory.rememberCard(ai, c, AiCardMemory.MemorySet.MANDATORY_ATTACKERS);
|
||||||
AiCardMemory.rememberCard(ai, c, AiCardMemory.MemorySet.TRICK_ATTACKERS);
|
AiCardMemory.rememberCard(ai, c, AiCardMemory.MemorySet.TRICK_ATTACKERS);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Don't try to mix "lure" and "precast" paradigms for combat tricks, since that creates issues with
|
// Don't try to mix "lure" and "precast" paradigms for combat tricks, since that creates issues with
|
||||||
// the AI overextending the attack
|
// the AI overextending the attack
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return simAI || MyRandom.getRandom().nextFloat() < chance;
|
return simAI || MyRandom.getRandom().nextFloat() < chance;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Apply "pump" ability and return modified creature
|
* Apply "pump" ability and return modified creature
|
||||||
* @param ai casting player
|
*
|
||||||
* @param sa Pump* or CounterPut*
|
* @param ai casting player
|
||||||
* @param c target of sa
|
* @param sa Pump* or CounterPut*
|
||||||
|
* @param c target of sa
|
||||||
* @param toughness +T
|
* @param toughness +T
|
||||||
* @param power +P
|
* @param power +P
|
||||||
* @param keywords additional keywords from sa (only for Pump)
|
* @param keywords additional keywords from sa (only for Pump)
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public static Card getPumpedCreature(final Player ai, final SpellAbility sa,
|
public static Card getPumpedCreature(final Player ai, final SpellAbility sa,
|
||||||
final Card c, int toughness, int power, final List<String> keywords) {
|
final Card c, int toughness, int power, final List<String> keywords) {
|
||||||
Card pumped = CardFactory.copyCard(c, false);
|
Card pumped = CardFactory.copyCard(c, false);
|
||||||
pumped.setSickness(c.hasSickness());
|
pumped.setSickness(c.hasSickness());
|
||||||
final long timestamp = c.getGame().getNextTimestamp();
|
final long timestamp = c.getGame().getNextTimestamp();
|
||||||
@@ -1740,8 +1748,9 @@ public class ComputerUtilCard {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Applies static continuous Power/Toughness effects to a (virtual) creature.
|
* Applies static continuous Power/Toughness effects to a (virtual) creature.
|
||||||
* @param game game instance to work with
|
*
|
||||||
* @param vCard creature to work with
|
* @param game game instance to work with
|
||||||
|
* @param vCard creature to work with
|
||||||
* @param exclude list of cards to exclude when considering ability sources, accepts null
|
* @param exclude list of cards to exclude when considering ability sources, accepts null
|
||||||
*/
|
*/
|
||||||
public static void applyStaticContPT(final Game game, Card vCard, final CardCollectionView exclude) {
|
public static void applyStaticContPT(final Game game, Card vCard, final CardCollectionView exclude) {
|
||||||
@@ -1787,6 +1796,7 @@ public class ComputerUtilCard {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluate if the ability can save a target against removal
|
* Evaluate if the ability can save a target against removal
|
||||||
|
*
|
||||||
* @param ai casting player
|
* @param ai casting player
|
||||||
* @param sa Pump* or CounterPut*
|
* @param sa Pump* or CounterPut*
|
||||||
* @return
|
* @return
|
||||||
@@ -1889,7 +1899,7 @@ public class ComputerUtilCard {
|
|||||||
int priorityRemovalThreshold = 0;
|
int priorityRemovalThreshold = 0;
|
||||||
int lifeInDanger = 5;
|
int lifeInDanger = 5;
|
||||||
if (ai.getController().isAI()) {
|
if (ai.getController().isAI()) {
|
||||||
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
AiController aic = ((PlayerControllerAi) ai.getController()).getAi();
|
||||||
enablePriorityRemoval = aic.getBooleanProperty(AiProps.ACTIVELY_DESTROY_IMMEDIATELY_UNBLOCKABLE);
|
enablePriorityRemoval = aic.getBooleanProperty(AiProps.ACTIVELY_DESTROY_IMMEDIATELY_UNBLOCKABLE);
|
||||||
priorityRemovalThreshold = aic.getIntProperty(AiProps.DESTROY_IMMEDIATELY_UNBLOCKABLE_THRESHOLD);
|
priorityRemovalThreshold = aic.getIntProperty(AiProps.DESTROY_IMMEDIATELY_UNBLOCKABLE_THRESHOLD);
|
||||||
priorityRemovalOnlyInDanger = aic.getBooleanProperty(AiProps.DESTROY_IMMEDIATELY_UNBLOCKABLE_ONLY_IN_DNGR);
|
priorityRemovalOnlyInDanger = aic.getBooleanProperty(AiProps.DESTROY_IMMEDIATELY_UNBLOCKABLE_ONLY_IN_DNGR);
|
||||||
@@ -1973,7 +1983,7 @@ public class ComputerUtilCard {
|
|||||||
if (needsToPlay.equalsIgnoreCase("WillAttack")) {
|
if (needsToPlay.equalsIgnoreCase("WillAttack")) {
|
||||||
if (sa != null && game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) {
|
if (sa != null && game.getPhaseHandler().isPlayerTurn(sa.getActivatingPlayer())) {
|
||||||
return doesSpecifiedCreatureAttackAI(sa.getActivatingPlayer(), card) ?
|
return doesSpecifiedCreatureAttackAI(sa.getActivatingPlayer(), card) ?
|
||||||
AiPlayDecision.WillPlay : AiPlayDecision.BadEtbEffects;
|
AiPlayDecision.WillPlay : AiPlayDecision.BadEtbEffects;
|
||||||
} else {
|
} else {
|
||||||
return AiPlayDecision.WillPlay; // not our turn, skip this check for the possible Flash use etc.
|
return AiPlayDecision.WillPlay; // not our turn, skip this check for the possible Flash use etc.
|
||||||
}
|
}
|
||||||
@@ -2083,10 +2093,19 @@ public class ComputerUtilCard {
|
|||||||
public static boolean isCardRemAIDeck(final Card card) {
|
public static boolean isCardRemAIDeck(final Card card) {
|
||||||
return card.getRules() != null && card.getRules().getAiHints().getRemAIDecks();
|
return card.getRules() != null && card.getRules().getAiHints().getRemAIDecks();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isCardRemRandomDeck(final Card card) {
|
public static boolean isCardRemRandomDeck(final Card card) {
|
||||||
return card.getRules() != null && card.getRules().getAiHints().getRemRandomDecks();
|
return card.getRules() != null && card.getRules().getAiHints().getRemRandomDecks();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isCardRemNonCommanderDeck(final Card card) {
|
public static boolean isCardRemNonCommanderDeck(final Card card) {
|
||||||
return card.getRules() != null && card.getRules().getAiHints().getRemNonCommanderDecks();
|
return card.getRules() != null && card.getRules().getAiHints().getRemNonCommanderDecks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class LandEvaluator implements Function<Card, Integer> {
|
||||||
|
@Override
|
||||||
|
public Integer apply(Card card) {
|
||||||
|
return GameStateEvaluator.evaluateLand(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user