From 7dd216545db786c73c7980f482acab2b6ac1ad77 Mon Sep 17 00:00:00 2001 From: excessum Date: Sun, 11 Oct 2015 07:25:08 +0000 Subject: [PATCH] - Implemented AI for auto-payment of Converge/Sunburst costs --- .../main/java/forge/ai/ComputerUtilMana.java | 67 ++++++++++++++++--- .../src/main/java/forge/game/card/Card.java | 4 ++ .../res/cardsfolder/t/tajuru_stalwart.txt | 1 - .../res/cardsfolder/w/woodland_wanderer.txt | 1 - 4 files changed, 63 insertions(+), 10 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java index 002f5cf94f0..6d2ff443e2c 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilMana.java @@ -238,7 +238,7 @@ public class ComputerUtilMana { } // 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 (ai.getManaPool().tryPayCostWithMana(sa, cost, mana)) { manaSpentToPay.add(0, mana); @@ -337,6 +337,20 @@ public class ComputerUtilMana { // select which abilities may be used for each shard ListMultimap 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); @@ -350,7 +364,25 @@ public class ComputerUtilMana { while (!cost.isPaid()) { toPay = getNextShardToPay(cost); - Collection saList = sourcesForShards.get(toPay); + Collection 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) { break; } @@ -404,6 +436,17 @@ public class ComputerUtilMana { // no need to remove abilities from resource map, // 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 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, final boolean test, List manaSpentToPay) { + final boolean hasConverge = sa.getHostCard().hasConverge(); List unpaidShards = cost.getUnpaidShards(); Collections.sort(unpaidShards); // most difficult shards must come first for (ManaCostShard part : unpaidShards) { @@ -458,7 +502,7 @@ public class ComputerUtilMana { } // 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 (ai.getManaPool().tryPayCostWithMana(sa, cost, mana)) { manaSpentToPay.add(0, mana); @@ -487,8 +531,8 @@ public class ComputerUtilMana { * a {@link forge.game.spellability.SpellAbility} object. * @return a {@link forge.game.mana.Mana} object. */ - private static Mana getMana(final Player ai, final ManaCostShard shard, final SpellAbility saBeingPaidFor, String restriction) { - final List> weightedOptions = selectManaToPayFor(ai.getManaPool(), shard, saBeingPaidFor, restriction); + private static Mana getMana(final Player ai, final ManaCostShard shard, final SpellAbility saBeingPaidFor, String restriction, final byte colorsPaid) { + final List> weightedOptions = selectManaToPayFor(ai.getManaPool(), shard, saBeingPaidFor, restriction, colorsPaid); // Exclude border case if (weightedOptions.isEmpty()) { @@ -536,7 +580,8 @@ public class ComputerUtilMana { return ai.getController().chooseManaFromPool(manaChoices); } - private static List> selectManaToPayFor(final ManaPool manapool, final ManaCostShard shard, final SpellAbility saBeingPaidFor, String restriction) { + private static List> selectManaToPayFor(final ManaPool manapool, final ManaCostShard shard, + final SpellAbility saBeingPaidFor, String restriction, final byte colorsPaid) { final List> weightedOptions = new ArrayList<>(); for (final Mana thisMana : manapool) { if (!manapool.canPayForShardWithColor(shard, thisMana.getColor())) { @@ -556,8 +601,14 @@ public class ComputerUtilMana { continue; } - // prefer colorless mana to spend - int weight = thisMana.isColorless() ? 5 : 0; + int weight = 0; + if (colorsPaid == -1) { + // prefer colorless mana to spend + weight += thisMana.isColorless() ? 5 : 0; + } else { + // get more colors for converge + weight += (thisMana.getColor() | colorsPaid) != colorsPaid ? 5 : 0; + } // prefer restricted mana to spend if (thisMana.isRestricted()) { diff --git a/forge-game/src/main/java/forge/game/card/Card.java b/forge-game/src/main/java/forge/game/card/Card.java index ace29f6dd43..30c71645983 100644 --- a/forge-game/src/main/java/forge/game/card/Card.java +++ b/forge-game/src/main/java/forge/game/card/Card.java @@ -873,6 +873,10 @@ public class Card extends GameEntity implements Comparable { public final boolean hasSecondStrike() { 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) { if (hasKeyword("CARDNAME can't have counters placed on it.")) { diff --git a/forge-gui/res/cardsfolder/t/tajuru_stalwart.txt b/forge-gui/res/cardsfolder/t/tajuru_stalwart.txt index f7434097c7b..2e4f8cd5bcc 100644 --- a/forge-gui/res/cardsfolder/t/tajuru_stalwart.txt +++ b/forge-gui/res/cardsfolder/t/tajuru_stalwart.txt @@ -4,6 +4,5 @@ Types:Creature Elf Scout Ally 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. SVar:X:Count$Converge -SVar:RemAIDeck:True 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. diff --git a/forge-gui/res/cardsfolder/w/woodland_wanderer.txt b/forge-gui/res/cardsfolder/w/woodland_wanderer.txt index bc12e96a7b6..1f4536e2ba1 100644 --- a/forge-gui/res/cardsfolder/w/woodland_wanderer.txt +++ b/forge-gui/res/cardsfolder/w/woodland_wanderer.txt @@ -6,6 +6,5 @@ K:Vigilance 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. SVar:X:Count$Converge -SVar:RemAIDeck:True 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.