From fdddbbbbe693aa85eb68058f6f3c0e7c15e72b21 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 24 Jun 2018 01:23:49 +0200 Subject: [PATCH] * Some rework/fixes/optimizations of calculation of available mana. --- Mage.Sets/src/mage/cards/c/CabalCoffers.java | 5 +- .../org/mage/test/utils/ManaOptionsTest.java | 99 +++++++++++++++++++ Mage/src/main/java/mage/Mana.java | 48 +++++++-- .../java/mage/abilities/mana/ManaOptions.java | 66 +++++++++---- .../main/java/mage/players/PlayerImpl.java | 1 + 5 files changed, 190 insertions(+), 29 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/CabalCoffers.java b/Mage.Sets/src/mage/cards/c/CabalCoffers.java index 72b167b5e4b..15afb42b599 100644 --- a/Mage.Sets/src/mage/cards/c/CabalCoffers.java +++ b/Mage.Sets/src/mage/cards/c/CabalCoffers.java @@ -1,4 +1,3 @@ - package mage.cards.c; import java.util.UUID; @@ -28,9 +27,9 @@ public final class CabalCoffers extends CardImpl { } public CabalCoffers(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.LAND},""); + super(ownerId, setInfo, new CardType[]{CardType.LAND}, ""); - // {2}, {tap}: Add {B} for each Swamp you control. + // {2}, {T}: Add {B} for each Swamp you control. Ability ability = new DynamicManaAbility(Mana.BlackMana(1), new PermanentsOnBattlefieldCount(filter), new GenericManaCost(2)); ability.addCost(new TapSourceCost()); this.addAbility(ability); diff --git a/Mage.Tests/src/test/java/org/mage/test/utils/ManaOptionsTest.java b/Mage.Tests/src/test/java/org/mage/test/utils/ManaOptionsTest.java index b72003e40f6..e7d2c5e9461 100644 --- a/Mage.Tests/src/test/java/org/mage/test/utils/ManaOptionsTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/utils/ManaOptionsTest.java @@ -454,4 +454,103 @@ public class ManaOptionsTest extends CardTestPlayerBase { Assert.assertEquals("mana variations don't fit", 1, manaOptions.size()); assertManaOptions("{C}{C}", manaOptions); } + + @Test + public void testManaSourcesWithCosts() { + // {T}: Add {C} to your mana pool. + // {5}, {T}: Add {W}{U}{B}{R}{G} to your mana pool. + addCard(Zone.BATTLEFIELD, playerA, "Crystal Quarry", 1); + + // {T}: Add {C} to your mana pool. + // {W/B}, {T}: Add {W}{W}, {W}{B}, or {B}{B} to your mana pool. + addCard(Zone.BATTLEFIELD, playerA, "Fetid Heath", 3); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 3); + + setStopAt(1, PhaseStep.UPKEEP); + execute(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + assertDuplicatedManaOptions(manaOptions); + + Assert.assertEquals("mana variations don't fit", 16, manaOptions.size()); + assertManaOptions("{C}{C}{C}{C}{W}{W}{W}", manaOptions); + assertManaOptions("{C}{C}{C}{W}{W}{W}{W}", manaOptions); + assertManaOptions("{C}{C}{C}{W}{W}{W}{B}", manaOptions); + assertManaOptions("{C}{C}{C}{W}{W}{B}{B}", manaOptions); + assertManaOptions("{C}{C}{W}{W}{W}{W}{W}", manaOptions); + assertManaOptions("{C}{C}{W}{W}{W}{W}{B}", manaOptions); + assertManaOptions("{C}{C}{W}{W}{W}{B}{B}", manaOptions); + assertManaOptions("{C}{C}{W}{W}{B}{B}{B}", manaOptions); + assertManaOptions("{C}{C}{W}{B}{B}{B}{B}", manaOptions); + assertManaOptions("{C}{W}{W}{W}{W}{W}{W}", manaOptions); + assertManaOptions("{C}{W}{W}{W}{W}{W}{B}", manaOptions); + assertManaOptions("{C}{W}{W}{W}{W}{B}{B}", manaOptions); + assertManaOptions("{C}{W}{W}{W}{B}{B}{B}", manaOptions); + assertManaOptions("{C}{W}{W}{B}{B}{B}{B}", manaOptions); + assertManaOptions("{C}{W}{B}{B}{B}{B}{B}", manaOptions); + assertManaOptions("{C}{B}{B}{B}{B}{B}{B}", manaOptions); + } + + @Test + public void testSungrassPrairie() { + // {1}, {T}: Add {G}{W}. + addCard(Zone.BATTLEFIELD, playerA, "Sungrass Prairie", 1); + // {T}: Add one mana of any color to your mana pool. + addCard(Zone.BATTLEFIELD, playerA, "Alloy Myr", 2); + + setStopAt(3, PhaseStep.PRECOMBAT_MAIN); + execute(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + assertDuplicatedManaOptions(manaOptions); + + Assert.assertEquals("mana variations don't fit", 2, manaOptions.size()); + + assertManaOptions("{W}{G}{Any}", manaOptions); + assertManaOptions("{Any}{Any}", manaOptions); + } + + @Test + public void testSungrassPrairie2() { + // {1}, {T}: Add {G}{W}. + addCard(Zone.BATTLEFIELD, playerA, "Sungrass Prairie", 5); + // ({T}: Add {U} or {W} to your mana pool.) + addCard(Zone.BATTLEFIELD, playerA, "Tundra", 9); + // ({T}: Add {G} or {U} to your mana pool.) + addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 3); + + setStopAt(3, PhaseStep.PRECOMBAT_MAIN); + execute(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + assertDuplicatedManaOptions(manaOptions); + + Assert.assertEquals("mana variations don't fit", 88, manaOptions.size()); + + assertManaOptions("{W}{W}{W}{W}{W}{W}{W}{W}{W}{G}{G}{G}{G}{G}{G}{G}{G}", manaOptions); + assertManaOptions("{W}{W}{W}{W}{W}{W}{W}{W}{U}{G}{G}{G}{G}{G}{G}{G}{G}", manaOptions); + } + + @Test + public void testSungrassPrairie3() { + // {1}, {T}: Add {G}{W}. + addCard(Zone.BATTLEFIELD, playerA, "Sungrass Prairie", 1); + // ({T}: Add {U} or {W} to your mana pool.) + addCard(Zone.BATTLEFIELD, playerA, "Tundra", 1); + // ({T}: Add {G} or {U} to your mana pool.) + addCard(Zone.BATTLEFIELD, playerA, "Tropical Island", 1); + + setStopAt(3, PhaseStep.PRECOMBAT_MAIN); + execute(); + + ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame); + assertDuplicatedManaOptions(manaOptions); + + Assert.assertEquals("mana variations don't fit", 4, manaOptions.size()); + assertManaOptions("{U}{U}", manaOptions); + assertManaOptions("{W}{G}{G}", manaOptions); + assertManaOptions("{W}{U}{G}", manaOptions); + assertManaOptions("{W}{W}{G}", manaOptions); + } + } diff --git a/Mage/src/main/java/mage/Mana.java b/Mage/src/main/java/mage/Mana.java index 1788fd264e4..f6a594292fb 100644 --- a/Mage/src/main/java/mage/Mana.java +++ b/Mage/src/main/java/mage/Mana.java @@ -1051,7 +1051,8 @@ public class Mana implements Comparable, Serializable, Copyable { /** * Returns if this {@link Mana} object has more than or equal values of mana - * as the passed in {@link Mana} object. + * as the passed in {@link Mana} object. Ignores {Any} mana to prevent + * endless iterations. * * @param mana the mana to compare with * @return if this object has more than or equal mana to the passed in @@ -1090,13 +1091,44 @@ public class Mana implements Comparable, Serializable, Copyable { moreMana = mana1; lessMana = mana2; } - if (lessMana.getWhite() > moreMana.getWhite() - || lessMana.getRed() > moreMana.getRed() - || lessMana.getGreen() > moreMana.getGreen() - || lessMana.getBlue() > moreMana.getBlue() - || lessMana.getBlack() > moreMana.getBlack() - || lessMana.getColorless() > moreMana.getColorless() - || lessMana.getAny() > moreMana.getAny()) { + int anyDiff = mana2.getAny() - mana1.getAny(); + if (lessMana.getWhite() > moreMana.getWhite()) { + anyDiff -= lessMana.getWhite() - moreMana.getWhite(); + if (anyDiff < 0) { + return null; + } + } + if (lessMana.getRed() > moreMana.getRed()) { + anyDiff -= lessMana.getRed() - moreMana.getRed(); + if (anyDiff < 0) { + return null; + } + } + if (lessMana.getGreen() > moreMana.getGreen()) { + anyDiff -= lessMana.getGreen() - moreMana.getGreen(); + if (anyDiff < 0) { + return null; + } + } + if (lessMana.getBlue() > moreMana.getBlue()) { + anyDiff -= lessMana.getBlue() - moreMana.getBlue(); + if (anyDiff < 0) { + return null; + } + } + if (lessMana.getBlack() > moreMana.getBlack()) { + anyDiff -= lessMana.getBlack() - moreMana.getBlack(); + if (anyDiff < 0) { + return null; + } + } + if (lessMana.getColorless() > moreMana.getColorless()) { + anyDiff -= lessMana.getColorless() - moreMana.getColorless(); + if (anyDiff < 0) { + return null; + } + } + if (lessMana.getAny() > moreMana.getAny()) { return null; } return moreMana; diff --git a/Mage/src/main/java/mage/abilities/mana/ManaOptions.java b/Mage/src/main/java/mage/abilities/mana/ManaOptions.java index 077b77fad43..cd649cc976b 100644 --- a/Mage/src/main/java/mage/abilities/mana/ManaOptions.java +++ b/Mage/src/main/java/mage/abilities/mana/ManaOptions.java @@ -114,6 +114,7 @@ public class ManaOptions extends ArrayList { } public void addManaWithCost(List abilities, Game game) { + int replaces = 0; if (isEmpty()) { this.add(new Mana()); } @@ -187,7 +188,8 @@ public class ManaOptions extends ArrayList { Mana moreValuable = Mana.getMoreValuableMana(newMana, existingMana); if (moreValuable != null) { existingMana.setToMana(moreValuable); - logger.trace("mana replaced " + newMana.toString() + " <=> " + existingMana.toString() + " from " + ability.getRule()); + replaces++; + // logger.info("mana replaced " + newMana.toString() + " <=> " + existingMana.toString() + " from " + ability.getRule()); continue CombineWithExisting; } } @@ -203,6 +205,10 @@ public class ManaOptions extends ArrayList { } } } + if (this.size() > 5 || replaces > 5) { + logger.info("ManaOptionsCosts " + this.size() + " Ign:" + replaces + " => " + this.toString()); + logger.info("Abilities: " + abilities.toString()); + } } public void addMana(Mana addMana) { @@ -255,20 +261,28 @@ public class ManaOptions extends ArrayList { this.clear(); for (Mana mana : copy) { Mana oldMan = mana.copy(); - if (mana.includesMana(cost)) { - // colorless costs can be paid with different colored mana, can lead to different color combinations + if (mana.includesMana(cost)) { // it can be paid + // generic mana costs can be paid with different colored mana, can lead to different color combinations if (cost.getGeneric() > 0 && cost.getGeneric() > (mana.getGeneric() + mana.getColorless())) { Mana coloredCost = cost.copy(); coloredCost.setGeneric(0); mana.subtract(coloredCost); + boolean oldManaWasReplaced = false; for (Mana payCombination : getPossiblePayCombinations(cost.getGeneric(), mana)) { Mana newMana = mana.copy(); newMana.subtract(payCombination); newMana.add(addMana); - if (oldMan.contains(newMana) && oldMan.count() > newMana.count()) { - newMana.setToMana(oldMan); + Mana moreValuable = Mana.getMoreValuableMana(oldMan, newMana); + if (!oldMan.equals(moreValuable)) { + this.add(newMana); + if (moreValuable != null) { + oldManaWasReplaced = true; // the new mana includes all possibilities of the old one + } } - this.add(newMana); + + } + if (!oldManaWasReplaced) { + this.add(oldMan); } } else { while (mana.includesMana(cost)) { @@ -303,28 +317,33 @@ public class ManaOptions extends ArrayList { existingManas.add(new Mana()); } for (Mana existingMana : existingManas) { - Mana manaToPay = manaAvailable.copy(); - manaToPay.subtract(existingMana); - if (manaToPay.getBlack() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.BlackMana(1).toString())) { - manaToPay.subtract(Mana.BlackMana(1)); + Mana manaToPayFrom = manaAvailable.copy(); + manaToPayFrom.subtract(existingMana); + if (manaToPayFrom.getBlack() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.BlackMana(1).toString())) { + manaToPayFrom.subtract(Mana.BlackMana(1)); addManaCombination(Mana.BlackMana(1), existingMana, payCombinations, payCombinationsStrings); } - if (manaToPay.getBlue() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.BlueMana(1).toString())) { - manaToPay.subtract(Mana.BlueMana(1)); + if (manaToPayFrom.getBlue() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.BlueMana(1).toString())) { + manaToPayFrom.subtract(Mana.BlueMana(1)); addManaCombination(Mana.BlueMana(1), existingMana, payCombinations, payCombinationsStrings); } - if (manaToPay.getGreen() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.GreenMana(1).toString())) { - manaToPay.subtract(Mana.GreenMana(1)); + if (manaToPayFrom.getGreen() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.GreenMana(1).toString())) { + manaToPayFrom.subtract(Mana.GreenMana(1)); addManaCombination(Mana.GreenMana(1), existingMana, payCombinations, payCombinationsStrings); } - if (manaToPay.getRed() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.RedMana(1).toString())) { - manaToPay.subtract(Mana.RedMana(1)); + if (manaToPayFrom.getRed() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.RedMana(1).toString())) { + manaToPayFrom.subtract(Mana.RedMana(1)); addManaCombination(Mana.RedMana(1), existingMana, payCombinations, payCombinationsStrings); } - if (manaToPay.getWhite() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.WhiteMana(1).toString())) { - manaToPay.subtract(Mana.WhiteMana(1)); + if (manaToPayFrom.getWhite() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.WhiteMana(1).toString())) { + manaToPayFrom.subtract(Mana.WhiteMana(1)); addManaCombination(Mana.WhiteMana(1), existingMana, payCombinations, payCombinationsStrings); } + // Pay with any only needed if colored payment was not possible + if (payCombinations.isEmpty() && manaToPayFrom.getAny() > 0 && !payCombinationsStrings.contains(existingMana.toString() + Mana.AnyMana(1).toString())) { + manaToPayFrom.subtract(Mana.AnyMana(1)); + addManaCombination(Mana.AnyMana(1), existingMana, payCombinations, payCombinationsStrings); + } } } } else { @@ -352,5 +371,16 @@ public class ManaOptions extends ArrayList { list.add(s); } } + // Remove fully included variations + for (int i = this.size() - 1; i >= 0; i--) { + for (int ii = 0; ii < i; ii++) { + Mana moreValuable = Mana.getMoreValuableMana(this.get(i), this.get(ii)); + if (moreValuable != null) { + this.get(ii).setToMana(moreValuable); + this.remove(i); + break; + } + } + } } } diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 39d60b01ffd..1266ae494e8 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2647,6 +2647,7 @@ public abstract class PlayerImpl implements Player, Serializable { available.addMana(manaAbilities, game); } for (Abilities manaAbilities : sourceWithCosts) { + available.removeDuplicated(); available.addManaWithCost(manaAbilities, game); }