- Implemented AI for auto-payment of Converge/Sunburst costs

This commit is contained in:
excessum
2015-10-11 07:25:08 +00:00
parent ea8f15345e
commit 7dd216545d
4 changed files with 63 additions and 10 deletions

View File

@@ -238,7 +238,7 @@ public class ComputerUtilMana {
} }
// get a mana of this type from floating, bail if none available // get a mana of this type from floating, bail if none available
final Mana mana = getMana(ai, part, sa, cost.getSourceRestriction()); final Mana mana = getMana(ai, part, sa, cost.getSourceRestriction(), (byte) -1);
if (mana != null) { if (mana != null) {
if (ai.getManaPool().tryPayCostWithMana(sa, cost, mana)) { if (ai.getManaPool().tryPayCostWithMana(sa, cost, mana)) {
manaSpentToPay.add(0, mana); manaSpentToPay.add(0, mana);
@@ -337,6 +337,20 @@ public class ComputerUtilMana {
// select which abilities may be used for each shard // select which abilities may be used for each shard
ListMultimap<ManaCostShard, SpellAbility> sourcesForShards = ComputerUtilMana.groupAndOrderToPayShards(ai, manaAbilityMap, cost); ListMultimap<ManaCostShard, SpellAbility> sourcesForShards = ComputerUtilMana.groupAndOrderToPayShards(ai, manaAbilityMap, cost);
boolean hasConverge = sa.getHostCard().hasConverge();
if (hasConverge) { // add extra colors for paying converge
final int unpaidColors = cost.getUnpaidColors() + cost.getColorsPaid() ^ ManaCostShard.COLORS_SUPERPOSITION;
for (final byte b : ColorSet.fromMask(unpaidColors)) {
final ManaCostShard shard = ManaCostShard.valueOf(b);
if (!sourcesForShards.containsKey(shard)) {
if (ai.getManaPool().canPayForShardWithColor(shard, b)) {
for (SpellAbility saMana : manaAbilityMap.get((int)b)) {
sourcesForShards.get(shard).add(sourcesForShards.get(shard).size(), saMana);
}
}
}
}
}
sortManaAbilities(sourcesForShards); sortManaAbilities(sourcesForShards);
@@ -350,7 +364,25 @@ public class ComputerUtilMana {
while (!cost.isPaid()) { while (!cost.isPaid()) {
toPay = getNextShardToPay(cost); toPay = getNextShardToPay(cost);
Collection<SpellAbility> saList = sourcesForShards.get(toPay); Collection<SpellAbility> saList = null;
if (hasConverge &&
(toPay == ManaCostShard.COLORLESS || toPay == ManaCostShard.X)) {
final int unpaidColors = cost.getUnpaidColors() + cost.getColorsPaid() ^ ManaCostShard.COLORS_SUPERPOSITION;
for (final byte b : ColorSet.fromMask(unpaidColors)) { // try and pay other colors for converge
final ManaCostShard shard = ManaCostShard.valueOf(b);
saList = sourcesForShards.get(shard);
if (saList != null && !saList.isEmpty()) {
toPay = shard;
break;
}
}
if (saList == null || saList.isEmpty()) { // failed to converge, revert to paying colorless
saList = sourcesForShards.get(toPay);
hasConverge = false;
}
} else {
saList = sourcesForShards.get(toPay);
}
if (saList == null) { if (saList == null) {
break; break;
} }
@@ -404,6 +436,17 @@ public class ComputerUtilMana {
// no need to remove abilities from resource map, // no need to remove abilities from resource map,
// once their costs are paid and consume resources, they can not be used again // once their costs are paid and consume resources, they can not be used again
if (hasConverge) { // hack to prevent converge re-using sources
// remove from available lists
Iterator<SpellAbility> itSa = sourcesForShards.values().iterator();
while (itSa.hasNext()) {
SpellAbility srcSa = itSa.next();
if (srcSa.getHostCard().equals(saPayment.getHostCard())) {
itSa.remove();
}
}
}
} }
} }
@@ -449,6 +492,7 @@ public class ComputerUtilMana {
*/ */
private static boolean payManaCostFromPool(final ManaCostBeingPaid cost, final SpellAbility sa, final Player ai, private static boolean payManaCostFromPool(final ManaCostBeingPaid cost, final SpellAbility sa, final Player ai,
final boolean test, List<Mana> manaSpentToPay) { final boolean test, List<Mana> manaSpentToPay) {
final boolean hasConverge = sa.getHostCard().hasConverge();
List<ManaCostShard> unpaidShards = cost.getUnpaidShards(); List<ManaCostShard> unpaidShards = cost.getUnpaidShards();
Collections.sort(unpaidShards); // most difficult shards must come first Collections.sort(unpaidShards); // most difficult shards must come first
for (ManaCostShard part : unpaidShards) { for (ManaCostShard part : unpaidShards) {
@@ -458,7 +502,7 @@ public class ComputerUtilMana {
} }
// get a mana of this type from floating, bail if none available // get a mana of this type from floating, bail if none available
final Mana mana = getMana(ai, part, sa, cost.getSourceRestriction()); final Mana mana = getMana(ai, part, sa, cost.getSourceRestriction(), hasConverge ? cost.getColorsPaid() : -1);
if (mana != null) { if (mana != null) {
if (ai.getManaPool().tryPayCostWithMana(sa, cost, mana)) { if (ai.getManaPool().tryPayCostWithMana(sa, cost, mana)) {
manaSpentToPay.add(0, mana); manaSpentToPay.add(0, mana);
@@ -487,8 +531,8 @@ public class ComputerUtilMana {
* a {@link forge.game.spellability.SpellAbility} object. * a {@link forge.game.spellability.SpellAbility} object.
* @return a {@link forge.game.mana.Mana} object. * @return a {@link forge.game.mana.Mana} object.
*/ */
private static Mana getMana(final Player ai, final ManaCostShard shard, final SpellAbility saBeingPaidFor, String restriction) { private static Mana getMana(final Player ai, final ManaCostShard shard, final SpellAbility saBeingPaidFor, String restriction, final byte colorsPaid) {
final List<Pair<Mana, Integer>> weightedOptions = selectManaToPayFor(ai.getManaPool(), shard, saBeingPaidFor, restriction); final List<Pair<Mana, Integer>> weightedOptions = selectManaToPayFor(ai.getManaPool(), shard, saBeingPaidFor, restriction, colorsPaid);
// Exclude border case // Exclude border case
if (weightedOptions.isEmpty()) { if (weightedOptions.isEmpty()) {
@@ -536,7 +580,8 @@ public class ComputerUtilMana {
return ai.getController().chooseManaFromPool(manaChoices); return ai.getController().chooseManaFromPool(manaChoices);
} }
private static List<Pair<Mana, Integer>> selectManaToPayFor(final ManaPool manapool, final ManaCostShard shard, final SpellAbility saBeingPaidFor, String restriction) { private static List<Pair<Mana, Integer>> selectManaToPayFor(final ManaPool manapool, final ManaCostShard shard,
final SpellAbility saBeingPaidFor, String restriction, final byte colorsPaid) {
final List<Pair<Mana, Integer>> weightedOptions = new ArrayList<>(); final List<Pair<Mana, Integer>> weightedOptions = new ArrayList<>();
for (final Mana thisMana : manapool) { for (final Mana thisMana : manapool) {
if (!manapool.canPayForShardWithColor(shard, thisMana.getColor())) { if (!manapool.canPayForShardWithColor(shard, thisMana.getColor())) {
@@ -556,8 +601,14 @@ public class ComputerUtilMana {
continue; continue;
} }
int weight = 0;
if (colorsPaid == -1) {
// prefer colorless mana to spend // prefer colorless mana to spend
int weight = thisMana.isColorless() ? 5 : 0; weight += thisMana.isColorless() ? 5 : 0;
} else {
// get more colors for converge
weight += (thisMana.getColor() | colorsPaid) != colorsPaid ? 5 : 0;
}
// prefer restricted mana to spend // prefer restricted mana to spend
if (thisMana.isRestricted()) { if (thisMana.isRestricted()) {

View File

@@ -874,6 +874,10 @@ public class Card extends GameEntity implements Comparable<Card> {
return hasDoubleStrike() || !hasFirstStrike(); return hasDoubleStrike() || !hasFirstStrike();
} }
public final boolean hasConverge() {
return "Count$Converge".equals(getSVar("X")) || "Count$Converge".equals(getSVar("Y")) || hasKeyword("Sunburst");
}
public final boolean canReceiveCounters(final CounterType type) { public final boolean canReceiveCounters(final CounterType type) {
if (hasKeyword("CARDNAME can't have counters placed on it.")) { if (hasKeyword("CARDNAME can't have counters placed on it.")) {
return false; return false;

View File

@@ -4,6 +4,5 @@ Types:Creature Elf Scout Ally
PT:0/1 PT:0/1
K:etbCounter:P1P1:X:no Condition:CARDNAME enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it. K:etbCounter:P1P1:X:no Condition:CARDNAME enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.
SVar:X:Count$Converge SVar:X:Count$Converge
SVar:RemAIDeck:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/tajuru_beastmaster.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/tajuru_beastmaster.jpg
Oracle:Converge - Tajuru Stalwart enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it. Oracle:Converge - Tajuru Stalwart enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.

View File

@@ -6,6 +6,5 @@ K:Vigilance
K:Trample K:Trample
K:etbCounter:P1P1:X:no Condition:Converge — CARDNAME enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it. K:etbCounter:P1P1:X:no Condition:Converge — CARDNAME enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.
SVar:X:Count$Converge SVar:X:Count$Converge
SVar:RemAIDeck:True
SVar:Picture:http://www.wizards.com/global/images/magic/general/woodland_wanderer.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/woodland_wanderer.jpg
Oracle:Vigilance, trample\nConverge — Woodland Wanderer enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it. Oracle:Vigilance, trample\nConverge — Woodland Wanderer enters the battlefield with a +1/+1 counter on it for each color of mana spent to cast it.