mirror of
https://github.com/correl/mage.git
synced 2024-12-26 03:00:11 +00:00
Fixes to Mana.enough: mana of any color (ManaType.Any) was being used to pay for colourless mana. Fixes for Mana.needed: mana of any color (ManaType.Any) was being used to pay for colourless mana. calculation for generic mana remaining was using min(0, available) instead of max(0, available) meaning that leftover mana of other types was never used to pay for any leftover generic costs. Other: Added tests for both .needed and .enough. Moved tests some tests from ManaUtilTest to ManaTest Simplified Mana.subtractCosts by calling Mana.substract first to make use of common functionality. Added more documentation Added tests for both .needed Added more tests for .enough to cover the changes with colourless mana.
This commit is contained in:
parent
16914632c4
commit
e679574a15
4 changed files with 293 additions and 196 deletions
|
@ -3,9 +3,7 @@ package org.mage.test.utils;
|
|||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.mana.BasicManaAbility;
|
||||
|
@ -15,7 +13,6 @@ import mage.abilities.mana.RedManaAbility;
|
|||
import mage.abilities.mana.WhiteManaAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.repository.CardRepository;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.ManaUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
@ -27,42 +24,42 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
|
|||
public class ManaUtilTest extends CardTestPlayerBase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
testManaToPayVsLand("{R}", "Blood Crypt", 2, 1); // should use {R}
|
||||
testManaToPayVsLand("{1}{R}", "Blood Crypt", 2, RedManaAbility.class); // should use {R}
|
||||
testManaToPayVsLand("{R}{B}", "Blood Crypt", 2, 2); // can't auto choose to pay
|
||||
testManaToPayVsLand("{2}{R}{B}", "Blood Crypt", 2, 2); // can't auto choose to pay
|
||||
testManaToPayVsLand("{R}{R}{B}{B}", "Blood Crypt", 2, 2); // can't auto choose to pay
|
||||
testManaToPayVsLand("{R}{G}{W}{W}{U}", "Blood Crypt", 2, RedManaAbility.class); // should use {R}
|
||||
public void testAutoPay() {
|
||||
testManaToPayVsLand("{R}", "Blood Crypt", 2, 1); // should use {R}
|
||||
testManaToPayVsLand("{1}{R}", "Blood Crypt", 2, RedManaAbility.class); // should use {R}
|
||||
testManaToPayVsLand("{R}{B}", "Blood Crypt", 2, 2); // can't auto choose to pay
|
||||
testManaToPayVsLand("{2}{R}{B}", "Blood Crypt", 2, 2); // can't auto choose to pay
|
||||
testManaToPayVsLand("{R}{R}{B}{B}", "Blood Crypt", 2, 2); // can't auto choose to pay
|
||||
testManaToPayVsLand("{R}{G}{W}{W}{U}", "Blood Crypt", 2, RedManaAbility.class); // should use {R}
|
||||
testManaToPayVsLand("{R}{R}{G}{W}{W}{U}", "Blood Crypt", 2, RedManaAbility.class); // should use {R}
|
||||
testManaToPayVsLand("{R}{R}", "Blood Crypt", 2, RedManaAbility.class); // should use {R}
|
||||
testManaToPayVsLand("{G}{W}", "Blood Crypt", 2, 2); // can't auto choose to pay
|
||||
testManaToPayVsLand("{1}{G}{W}", "Blood Crypt", 2, 1); // should use any but auto choose it
|
||||
testManaToPayVsLand("{2}{G}{W}{U}", "Blood Crypt", 2, 1); // should use any but auto choose it
|
||||
testManaToPayVsLand("{3}", "Blood Crypt", 2, 1); // should use any but auto choose it
|
||||
testManaToPayVsLand("{R}{R}", "Blood Crypt", 2, RedManaAbility.class); // should use {R}
|
||||
testManaToPayVsLand("{G}{W}", "Blood Crypt", 2, 2); // can't auto choose to pay
|
||||
testManaToPayVsLand("{1}{G}{W}", "Blood Crypt", 2, 1); // should use any but auto choose it
|
||||
testManaToPayVsLand("{2}{G}{W}{U}", "Blood Crypt", 2, 1); // should use any but auto choose it
|
||||
testManaToPayVsLand("{3}", "Blood Crypt", 2, 1); // should use any but auto choose it
|
||||
|
||||
testManaToPayVsLand("{R}{R}{G}{W}{W}{U}", "Watery Grave", 2, 1); // should use {U}
|
||||
testManaToPayVsLand("{R}{R}{G}{W}{W}", "Steam Vents", 2, 1); // should use {R}
|
||||
testManaToPayVsLand("{R}{R}{G}{B}{U}", "Temple Garden", 2, 1); // should use {G}
|
||||
testManaToPayVsLand("{W}{W}{G}{B}{U}", "Sacred Foundry", 2, 1); // should use {W}
|
||||
testManaToPayVsLand("{W}{W}{R}{B}{U}", "Overgrown Tomb", 2, BlackManaAbility.class); // should use {B}
|
||||
testManaToPayVsLand("{W}{W}{R}{B}{U}", "Swamp", 1, BlackManaAbility.class);
|
||||
testManaToPayVsLand("{W}{W}{R}{B}{U}", "Plains", 1, WhiteManaAbility.class);
|
||||
testManaToPayVsLand("{R}{R}{G}{W}{W}{U}", "Watery Grave", 2, 1); // should use {U}
|
||||
testManaToPayVsLand("{R}{R}{G}{W}{W}", "Steam Vents", 2, 1); // should use {R}
|
||||
testManaToPayVsLand("{R}{R}{G}{B}{U}", "Temple Garden", 2, 1); // should use {G}
|
||||
testManaToPayVsLand("{W}{W}{G}{B}{U}", "Sacred Foundry", 2, 1); // should use {W}
|
||||
testManaToPayVsLand("{W}{W}{R}{B}{U}", "Overgrown Tomb", 2, BlackManaAbility.class); // should use {B}
|
||||
testManaToPayVsLand("{W}{W}{R}{B}{U}", "Swamp", 1, BlackManaAbility.class);
|
||||
testManaToPayVsLand("{W}{W}{R}{B}{U}", "Plains", 1, WhiteManaAbility.class);
|
||||
|
||||
testManaToPayVsLand("{1}{R}", "Cavern of Souls", 2, 2); // can't auto choose to pay
|
||||
testManaToPayVsLand("{2}", "Cavern of Souls", 2, 2); // can't auto choose to pay
|
||||
testManaToPayVsLand("{1}{R}", "Cavern of Souls", 2, 2); // can't auto choose to pay
|
||||
testManaToPayVsLand("{2}", "Cavern of Souls", 2, 2); // can't auto choose to pay
|
||||
|
||||
testManaToPayVsLand("{2}", "Eldrazi Temple", 2, 2); // can't auto choose to pay
|
||||
|
||||
// hybrid mana
|
||||
testManaToPayVsLand("{W/R}{W/R}{W/R}", "Sacred Foundry", 2, 1); // auto choose for hybrid mana: choose any
|
||||
testManaToPayVsLand("{R}{W/R}", "Sacred Foundry", 2, RedManaAbility.class); // auto choose for hybrid mana: we should choose {R}
|
||||
testManaToPayVsLand("{G}{W/R}", "Sacred Foundry", 2, 1); // auto choose for hybrid mana: choose any
|
||||
testManaToPayVsLand("{G}{W/R}{W}", "Sacred Foundry", 2, WhiteManaAbility.class); // auto choose for hybrid mana: choose {W}
|
||||
testManaToPayVsLand("{W/B}{W/B}", "Swamp", 1, BlackManaAbility.class);
|
||||
testManaToPayVsLand("{W/R}{W/R}{W/R}", "Sacred Foundry", 2, 1); // auto choose for hybrid mana: choose any
|
||||
testManaToPayVsLand("{R}{W/R}", "Sacred Foundry", 2, RedManaAbility.class); // auto choose for hybrid mana: we should choose {R}
|
||||
testManaToPayVsLand("{G}{W/R}", "Sacred Foundry", 2, 1); // auto choose for hybrid mana: choose any
|
||||
testManaToPayVsLand("{G}{W/R}{W}", "Sacred Foundry", 2, WhiteManaAbility.class); // auto choose for hybrid mana: choose {W}
|
||||
testManaToPayVsLand("{W/B}{W/B}", "Swamp", 1, BlackManaAbility.class);
|
||||
|
||||
testManaToPayVsLand("{R}", "Glimmervoid", 1, 1);
|
||||
testManaToPayVsLand("{R}{1}", "Glimmervoid", 1, 1);
|
||||
testManaToPayVsLand("{R}", "Glimmervoid", 1, 1);
|
||||
testManaToPayVsLand("{R}{1}", "Glimmervoid", 1, 1);
|
||||
|
||||
// we can't auto choose here:
|
||||
// let say we auto choose {R}, then we have to use it to pay for {R} not {W/R} (as {W/R} is more generic cost)
|
||||
|
@ -75,100 +72,24 @@ public class ManaUtilTest extends CardTestPlayerBase {
|
|||
testManaToPayVsLand("{W/R}{R/G}", "Sacred Foundry", 2, 2); // can't auto choose to pay
|
||||
}
|
||||
|
||||
/**
|
||||
* Mana.condenseManaCostString is used to simplify the String representation of a mana cost to make it more readable.
|
||||
*/
|
||||
@Test
|
||||
public void testManaCondensing() {
|
||||
Assert.assertEquals("{5}{W}", ManaUtil.condenseManaCostString(("{1}{1}{1}{2}{W}")));
|
||||
Assert.assertEquals("{4}{B}{B}", ManaUtil.condenseManaCostString("{2}{B}{2}{B}"));
|
||||
Assert.assertEquals("{6}{R}{R}{R}{U}", ManaUtil.condenseManaCostString("{R}{1}{R}{2}{R}{3}{U}"));
|
||||
Assert.assertEquals("{5}{B}{U}{W}", ManaUtil.condenseManaCostString("{1}{B}{W}{4}{U}"));
|
||||
Assert.assertEquals("{8}{B}{G}{G}{U}", ManaUtil.condenseManaCostString("{1}{G}{1}{2}{3}{G}{B}{U}{1}"));
|
||||
Assert.assertEquals("{3}{R}{U}", ManaUtil.condenseManaCostString("{3}{R}{U}"));
|
||||
Assert.assertEquals("{10}", ManaUtil.condenseManaCostString("{1}{2}{3}{4}"));
|
||||
Assert.assertEquals("{B}{G}{R}{U}{W}", ManaUtil.condenseManaCostString("{B}{G}{R}{U}{W}"));
|
||||
Assert.assertEquals("{R}{R}", ManaUtil.condenseManaCostString("{R}{R}"));
|
||||
Assert.assertEquals("{U}", ManaUtil.condenseManaCostString("{U}"));
|
||||
Assert.assertEquals("{2}", ManaUtil.condenseManaCostString("{2}"));
|
||||
Assert.assertEquals("", ManaUtil.condenseManaCostString("{}"));
|
||||
Assert.assertEquals("{5}{C}{R}{R}{R}{U}", ManaUtil.condenseManaCostString("{R}{C}{R}{2}{R}{3}{U}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mana.enough is used to check if a spell can be cast with an given amount
|
||||
* of avalable mana
|
||||
*/
|
||||
@Test
|
||||
public void testManaEnough() {
|
||||
testManaAvailEnough("{G}", 1, "", true);
|
||||
testManaAvailEnough("{G}", 0, "{G}", true);
|
||||
testManaAvailEnough("{R}", 0, "{G}", false);
|
||||
testManaAvailEnough("{B}", 0, "{G}", false);
|
||||
testManaAvailEnough("{U}", 0, "{G}", false);
|
||||
testManaAvailEnough("{W}", 0, "{G}", false);
|
||||
|
||||
testManaAvailEnough("{R}", 1, "", true);
|
||||
testManaAvailEnough("{R}", 0, "{R}", true);
|
||||
testManaAvailEnough("{G}", 0, "{R}", false);
|
||||
testManaAvailEnough("{B}", 0, "{R}", false);
|
||||
testManaAvailEnough("{U}", 0, "{R}", false);
|
||||
testManaAvailEnough("{W}", 0, "{R}", false);
|
||||
|
||||
testManaAvailEnough("{U}{B}{W}{G}{R}", 4, "{R}", true);
|
||||
testManaAvailEnough("{U}{B}{W}{G}{R}", 3, "{R}{B}", true);
|
||||
|
||||
testManaAvailEnough("{U}{U}{U}{G}{G}{2}", 2, "{U}{U}{G}{R}{B}", true);
|
||||
|
||||
testManaAvailEnough("{2}{U}{U}", 0, "{U}{U}{U}{U}", true);
|
||||
testManaAvailEnough("{2}{U}{U}", 0, "{4}", false);
|
||||
testManaAvailEnough("{2}{U}{U}", 0, "{B}{B}{4}", false);
|
||||
|
||||
testManaAvailEnough("{G}", 0, "{G/W}", true);
|
||||
testManaAvailEnough("{G}{W}", 0, "{G/W}{G/W}", true);
|
||||
testManaAvailEnough("{W}{W}", 0, "{G/W}{G/W}", true);
|
||||
testManaAvailEnough("{G}{G}", 0, "{G/W}{G/W}", true);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Mana.enough is used to check if a spell can be cast with an given amount
|
||||
* of avalable mana
|
||||
*/
|
||||
@Test
|
||||
public void testManaIncrease() {
|
||||
// cost - reduction - rest
|
||||
testManaReduction("{G}{G}", "{G}", "{G}");
|
||||
testManaReduction("{1}{G}{G}", "{G}", "{1}{G}");
|
||||
testManaReduction("{B}{B}", "{B}", "{B}");
|
||||
testManaReduction("{1}{B}{B}", "{B}", "{1}{B}");
|
||||
testManaReduction("{W}{W}", "{W}", "{W}");
|
||||
testManaReduction("{1}{W}{W}", "{W}", "{1}{W}");
|
||||
testManaReduction("{U}{U}", "{U}", "{U}");
|
||||
testManaReduction("{1}{U}{U}", "{U}", "{1}{U}");
|
||||
testManaReduction("{R}{R}", "{R}", "{R}");
|
||||
testManaReduction("{1}{R}{R}", "{R}", "{1}{R}");
|
||||
|
||||
testManaReduction("{R}{G}{B}{U}{W}", "{R}{G}{B}{U}{W}", "{0}");
|
||||
|
||||
// Hybrid Mana
|
||||
testManaReduction("{2/B}{2/B}{2/B}", "{B}{B}", "{2/B}");
|
||||
testManaReduction("{2/B}{2/B}{2/B}", "{B}{B}{B}", "{0}");
|
||||
testManaReduction("{2/W}{2/W}{2/W}", "{W}{W}", "{2/W}");
|
||||
testManaReduction("{2/W}{2/W}{2/W}", "{W}{W}{W}", "{0}");
|
||||
|
||||
testManaReduction("{G/B}{G/B}{G/B}", "{B}{G}{B}", "{0}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given mana reduction left the expected amount of mana costs
|
||||
*
|
||||
* @param manaCostsToPay
|
||||
* @param availablyAny
|
||||
* @param available
|
||||
* @param expected
|
||||
*/
|
||||
private void testManaReduction(String manaCostsToPay, String manaToReduce, String restMana) {
|
||||
SpellAbility spellAbility = new SpellAbility(new ManaCostsImpl(manaCostsToPay), "Test");
|
||||
CardUtil.adjustCost(spellAbility, new ManaCostsImpl(manaToReduce), true);
|
||||
Assert.assertTrue("The mana cost to pay " + manaCostsToPay + " reduced by " + manaToReduce + " should left " + restMana + " but the rest was " + spellAbility.getManaCostsToPay().getText(), spellAbility.getManaCostsToPay().getText().equals(restMana));
|
||||
Assert.assertEquals("{5}{W}", ManaUtil.condenseManaCostString("{1}{1}{1}{2}{W}"));
|
||||
Assert.assertEquals("{4}{B}{B}", ManaUtil.condenseManaCostString("{2}{B}{2}{B}"));
|
||||
Assert.assertEquals("{6}{R}{R}{R}{U}", ManaUtil.condenseManaCostString("{R}{1}{R}{2}{R}{3}{U}"));
|
||||
Assert.assertEquals("{5}{B}{U}{W}", ManaUtil.condenseManaCostString("{1}{B}{W}{4}{U}"));
|
||||
Assert.assertEquals("{8}{B}{G}{G}{U}", ManaUtil.condenseManaCostString("{1}{G}{1}{2}{3}{G}{B}{U}{1}"));
|
||||
Assert.assertEquals("{3}{R}{U}", ManaUtil.condenseManaCostString("{3}{R}{U}"));
|
||||
Assert.assertEquals("{10}", ManaUtil.condenseManaCostString("{1}{2}{3}{4}"));
|
||||
Assert.assertEquals("{B}{G}{R}{U}{W}", ManaUtil.condenseManaCostString("{B}{G}{R}{U}{W}"));
|
||||
Assert.assertEquals("{R}{R}", ManaUtil.condenseManaCostString("{R}{R}"));
|
||||
Assert.assertEquals("{U}", ManaUtil.condenseManaCostString("{U}"));
|
||||
Assert.assertEquals("{2}", ManaUtil.condenseManaCostString("{2}"));
|
||||
Assert.assertEquals("", ManaUtil.condenseManaCostString("{}"));
|
||||
Assert.assertEquals("{5}{C}{R}{R}{R}{U}", ManaUtil.condenseManaCostString("{R}{C}{R}{2}{R}{3}{U}"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -184,7 +105,7 @@ public class ManaUtilTest extends CardTestPlayerBase {
|
|||
* should be returned after optimization.
|
||||
*/
|
||||
private void testManaToPayVsLand(String manaToPay, String landName, int expected1, int expected2) {
|
||||
ManaCost unpaid = new ManaCostsImpl(manaToPay);
|
||||
ManaCost unpaid = new ManaCostsImpl<>(manaToPay);
|
||||
Card card = CardRepository.instance.findCard(landName).getCard();
|
||||
Assert.assertNotNull(card);
|
||||
|
||||
|
@ -206,13 +127,13 @@ public class ManaUtilTest extends CardTestPlayerBase {
|
|||
* We get all mana abilities, then try to auto pay and compare to expected1
|
||||
* and expected2 params.
|
||||
*
|
||||
* @param manaToPay Mana that should be paid using land.
|
||||
* @param landName Land to use as mana producer.
|
||||
* @param expected1 The amount of mana abilities the land should have.
|
||||
* @param expectedChosen
|
||||
* @param manaToPay Mana that should be paid using land.
|
||||
* @param landName Land to use as mana producer.
|
||||
* @param expected1 The amount of mana abilities the land should have.
|
||||
* @param expectedChosen The mana ability expected to be chosen.
|
||||
*/
|
||||
private void testManaToPayVsLand(String manaToPay, String landName, int expected1, Class<? extends BasicManaAbility> expectedChosen) {
|
||||
ManaCost unpaid = new ManaCostsImpl(manaToPay);
|
||||
ManaCost unpaid = new ManaCostsImpl<>(manaToPay);
|
||||
Card card = CardRepository.instance.findCard(landName).getCard();
|
||||
Assert.assertNotNull(card);
|
||||
|
||||
|
@ -225,31 +146,11 @@ public class ManaUtilTest extends CardTestPlayerBase {
|
|||
Assert.assertTrue("Wrong mana ability has been chosen", expectedChosen.isInstance(ability));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given available Mana is enough to pay a given mana cost
|
||||
*
|
||||
* @param manaCostsToPay
|
||||
* @param availablyAny
|
||||
* @param available
|
||||
* @param expected
|
||||
*/
|
||||
private void testManaAvailEnough(String manaCostsToPay, int availablyAny, String available, boolean expected) {
|
||||
ManaCost unpaid = new ManaCostsImpl(manaCostsToPay);
|
||||
ManaCost costAvailable = new ManaCostsImpl(available);
|
||||
Mana manaAvailable = costAvailable.getMana();
|
||||
manaAvailable.setAny(availablyAny);
|
||||
if (expected) {
|
||||
Assert.assertTrue("The available Mana " + costAvailable.getText() + " should be enough to pay the costs " + unpaid.getText(), unpaid.getMana().enough(manaAvailable));
|
||||
} else {
|
||||
Assert.assertFalse("The available Mana " + costAvailable.getText() + " shouldn't be enough to pay the costs " + unpaid.getText(), unpaid.getMana().enough(manaAvailable));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts mana abilities from the card.
|
||||
*
|
||||
* @param card Card to extract mana abilities from.
|
||||
* @return
|
||||
* @param card Card to extract mana abilities from.
|
||||
* @return Map between the UUID of each ability on the card and the ability.
|
||||
*/
|
||||
private Map<UUID, ActivatedManaAbilityImpl> getManaAbilities(Card card) {
|
||||
Map<UUID, ActivatedManaAbilityImpl> useableAbilities = new LinkedHashMap<>();
|
||||
|
|
|
@ -76,7 +76,7 @@ public class Mana implements Comparable<Mana>, Serializable, Copyable<Mana> {
|
|||
* Copy constructor. Creates a {@link Mana} object from existing
|
||||
* {@link Mana}
|
||||
*
|
||||
* @param mana object to create copy from
|
||||
* @param mana object to create copy from.
|
||||
*/
|
||||
public Mana(final Mana mana) {
|
||||
Objects.requireNonNull(mana, "The passed in mana can not be null");
|
||||
|
@ -129,6 +129,11 @@ public class Mana implements Comparable<Mana>, Serializable, Copyable<Mana> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Mana} object of one mana of the passed {@link ManaType}.
|
||||
*
|
||||
* @param manaType The type of mana to set to one.
|
||||
*/
|
||||
public Mana(final ManaType manaType) {
|
||||
this();
|
||||
Objects.requireNonNull(manaType, "The passed in ManaType can not be null");
|
||||
|
@ -159,6 +164,12 @@ public class Mana implements Comparable<Mana>, Serializable, Copyable<Mana> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Mana} object of #num mana of the passed {@link ManaType}.
|
||||
*
|
||||
* @param manaType The type of mana to set.
|
||||
* @param num The number of mana available of the passed ManaType.
|
||||
**/
|
||||
public Mana(final ManaType manaType, int num) {
|
||||
this();
|
||||
Objects.requireNonNull(manaType, "The passed in ManaType can not be null");
|
||||
|
@ -311,7 +322,7 @@ public class Mana implements Comparable<Mana>, Serializable, Copyable<Mana> {
|
|||
/**
|
||||
* Increases the given mana by one.
|
||||
*
|
||||
* @param manaType
|
||||
* @param manaType the type of mana to increase by one.
|
||||
*/
|
||||
public void increase(ManaType manaType) {
|
||||
switch (manaType) {
|
||||
|
@ -411,18 +422,12 @@ public class Mana implements Comparable<Mana>, Serializable, Copyable<Mana> {
|
|||
*
|
||||
* @param mana mana values to subtract
|
||||
* @throws ArithmeticException thrown if there is not enough available
|
||||
* colored mana to pay the generic cost
|
||||
* mana to pay the generic cost
|
||||
*/
|
||||
public void subtractCost(final Mana mana) throws ArithmeticException {
|
||||
white = CardUtil.overflowDec(white, mana.white);
|
||||
blue = CardUtil.overflowDec(blue, mana.blue);
|
||||
black = CardUtil.overflowDec(black, mana.black);
|
||||
red = CardUtil.overflowDec(red, mana.red);
|
||||
green = CardUtil.overflowDec(green, mana.green);
|
||||
generic = CardUtil.overflowDec(generic, mana.generic);
|
||||
colorless = CardUtil.overflowDec(colorless, mana.colorless);
|
||||
any = CardUtil.overflowDec(any, mana.any);
|
||||
this.subtract(mana);
|
||||
|
||||
// Handle any unpaid generic mana costs
|
||||
while (generic < 0) {
|
||||
int oldColorless = generic;
|
||||
if (white > 0) {
|
||||
|
@ -666,17 +671,32 @@ public class Mana implements Comparable<Mana>, Serializable, Copyable<Mana> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns if there is enough available mana to pay the mana provided by the
|
||||
* passed in {@link Mana} object.
|
||||
* Returns if the cost (this) can be paid by the mana provided by the passed in {@link Mana} object.
|
||||
*
|
||||
* @param cost the cost to compare too.
|
||||
* @return if there is enough available mana to pay.
|
||||
* @param avail The mana to compare too.
|
||||
* @return boolean indicating if there is enough available mana to pay.
|
||||
*/
|
||||
public boolean enough(final Mana cost) {
|
||||
Mana compare = cost.copy();
|
||||
public boolean enough(final Mana avail) {
|
||||
Mana compare = avail.copy();
|
||||
|
||||
// Subtract the mana cost (this) from the mana available (compare).
|
||||
// This will only subtract like mana types from one another (e.g. green from green, coloreless from colorless).
|
||||
compare.subtract(this);
|
||||
|
||||
// A negative value for compare.X means that mana of type X from the cost could not be paid by mana
|
||||
// of the same kind from the available mana.
|
||||
// Check each of the types, and see if there is enough mana of any color left to pay for the colors.
|
||||
|
||||
if (compare.colorless < 0) { // Put first to shortcut the calculations
|
||||
// Colorless mana can only be paid by colorless mana.
|
||||
// If there's a negative value, then there's nothing else that can be used to pay for it.
|
||||
return false;
|
||||
}
|
||||
if (compare.white < 0) {
|
||||
compare.any = CardUtil.overflowInc(compare.any, compare.white);
|
||||
// A negatice value means that there was more mana of the given type required than there was mana of any
|
||||
// color to pay for it.
|
||||
// So, there is not enough mana to pay the avail.
|
||||
if (compare.any < 0) {
|
||||
return false;
|
||||
}
|
||||
|
@ -710,13 +730,6 @@ public class Mana implements Comparable<Mana>, Serializable, Copyable<Mana> {
|
|||
}
|
||||
compare.green = 0;
|
||||
}
|
||||
if (compare.colorless < 0) {
|
||||
compare.any = CardUtil.overflowInc(compare.any, compare.colorless);
|
||||
if (compare.any < 0) {
|
||||
return false;
|
||||
}
|
||||
compare.colorless = 0;
|
||||
}
|
||||
if (compare.generic < 0) {
|
||||
compare.generic = CardUtil.overflowInc(compare.generic, compare.white);
|
||||
compare.generic = CardUtil.overflowInc(compare.generic, compare.blue);
|
||||
|
@ -731,16 +744,27 @@ public class Mana implements Comparable<Mana>, Serializable, Copyable<Mana> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the total mana needed to meet the passed in {@link Mana} object.
|
||||
* Returns the total mana needed to meet the cost of this given the available mana passed in
|
||||
* as a {@link Mana} object.
|
||||
*
|
||||
* @param cost the mana cost
|
||||
* @return the total mana needed to meet the passes in {@link Mana} object.
|
||||
* Used by the AI to calculate what mana it needs to obtain for a spell to become playable.
|
||||
*
|
||||
* @param avail the mana available to pay the cost
|
||||
* @return the total mana needed to pay this given the available mana passed in as a {@link Mana} object.
|
||||
*/
|
||||
public Mana needed(final Mana cost) {
|
||||
Mana compare = cost.copy();
|
||||
public Mana needed(final Mana avail) {
|
||||
Mana compare = avail.copy();
|
||||
|
||||
// Subtract the mana cost (this) from the mana available (compare).
|
||||
// This will only subtract like mana types from one another (e.g. green from green, coloreless from colorless).
|
||||
compare.subtract(this);
|
||||
|
||||
// A negative value for compare.X means that mana of type X from the cost could not be paid by mana
|
||||
// of the same kind from the available mana (this).
|
||||
if (compare.white < 0 && compare.any > 0) {
|
||||
// Calculate how much of the unpaid colored mana can be covered by mana of any color
|
||||
int diff = Math.min(compare.any, Math.abs(compare.white));
|
||||
// Make the payment
|
||||
compare.any = CardUtil.overflowDec(compare.any, diff);
|
||||
compare.white = CardUtil.overflowInc(compare.white, diff);
|
||||
}
|
||||
|
@ -764,25 +788,30 @@ public class Mana implements Comparable<Mana>, Serializable, Copyable<Mana> {
|
|||
compare.any = CardUtil.overflowDec(compare.any, diff);
|
||||
compare.green = CardUtil.overflowInc(compare.green, diff);
|
||||
}
|
||||
if (compare.colorless < 0 && compare.any > 0) {
|
||||
int diff = Math.min(compare.any, Math.abs(compare.colorless));
|
||||
compare.any = CardUtil.overflowDec(compare.any, diff);
|
||||
compare.colorless = CardUtil.overflowInc(compare.colorless, diff);
|
||||
}
|
||||
|
||||
// Colorless mana can only be paid by colorless sources, so a check for it is not performed.
|
||||
|
||||
if (compare.generic < 0) {
|
||||
// Calculate total leftover mana available to pay for generic costs
|
||||
int remaining = 0;
|
||||
remaining = CardUtil.overflowInc(remaining, Math.min(0, compare.white));
|
||||
remaining = CardUtil.overflowInc(remaining, Math.min(0, compare.blue));
|
||||
remaining = CardUtil.overflowInc(remaining, Math.min(0, compare.black));
|
||||
remaining = CardUtil.overflowInc(remaining, Math.min(0, compare.red));
|
||||
remaining = CardUtil.overflowInc(remaining, Math.min(0, compare.green));
|
||||
remaining = CardUtil.overflowInc(remaining, Math.min(0, compare.colorless));
|
||||
remaining = CardUtil.overflowInc(remaining, Math.min(0, compare.any));
|
||||
remaining = CardUtil.overflowInc(remaining, Math.max(0, compare.white));
|
||||
remaining = CardUtil.overflowInc(remaining, Math.max(0, compare.blue));
|
||||
remaining = CardUtil.overflowInc(remaining, Math.max(0, compare.black));
|
||||
remaining = CardUtil.overflowInc(remaining, Math.max(0, compare.red));
|
||||
remaining = CardUtil.overflowInc(remaining, Math.max(0, compare.green));
|
||||
remaining = CardUtil.overflowInc(remaining, Math.max(0, compare.colorless));
|
||||
remaining = CardUtil.overflowInc(remaining, Math.max(0, compare.any));
|
||||
|
||||
if (remaining > 0) {
|
||||
// Calculate how much of the unpaid generic cost can be paid by the leftover mana
|
||||
int diff = Math.min(remaining, Math.abs(compare.generic));
|
||||
// Make the payment
|
||||
compare.generic = CardUtil.overflowInc(compare.generic, diff);
|
||||
}
|
||||
}
|
||||
|
||||
// Create the mana object holding the mana needed to pay the cost
|
||||
// If the value in compare is positive it means that there's excess mana of that type, and no more is needed.
|
||||
Mana needed = new Mana();
|
||||
if (compare.white < 0) {
|
||||
needed.white = CardUtil.overflowDec(needed.white, compare.white);
|
||||
|
|
|
@ -449,9 +449,14 @@ public final class ManaUtil {
|
|||
}
|
||||
|
||||
/**
|
||||
* Converts a collection of mana symbols into a single condensed string e.g.
|
||||
* {1}{1}{1}{1}{1}{W} = {5}{W} {2}{B}{2}{B}{2}{B} = {6}{B}{B}{B}
|
||||
* {1}{2}{R}{U}{1}{1} = {5}{R}{U} {B}{G}{R} = {B}{G}{R}
|
||||
* Converts a collection of mana symbols into a single condensed string e.g:
|
||||
* {1}{1}{1}{1}{1}{W} = {5}{W}
|
||||
* {2}{B}{2}{B}{2}{B} = {6}{B}{B}{B}
|
||||
* {1}{2}{R}{U}{1}{1} = {5}{R}{U}
|
||||
* {B}{G}{R} = {B}{G}{R}
|
||||
*
|
||||
* @param rawCost the uncondensed version of the mana String.
|
||||
* @return the condensed version of the mana String.
|
||||
*/
|
||||
public static String condenseManaCostString(String rawCost) {
|
||||
int total = 0;
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
package mage;
|
||||
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.costs.mana.ManaCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.constants.ColoredManaSymbol;
|
||||
import mage.constants.ManaType;
|
||||
import mage.filter.FilterMana;
|
||||
import mage.util.CardUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
@ -11,7 +16,7 @@ import static org.junit.Assert.*;
|
|||
|
||||
|
||||
/**
|
||||
* Custom unit tests for {link Mana}.
|
||||
* Custom unit tests for {@link Mana}.
|
||||
*
|
||||
* @author githubpoixen@github.com
|
||||
*/
|
||||
|
@ -686,4 +691,161 @@ public class ManaTest {
|
|||
assertEquals(Integer.MAX_VALUE, mana.getGeneric());
|
||||
assertEquals(Integer.MAX_VALUE, mana.getAny());
|
||||
}
|
||||
|
||||
/**
|
||||
* Mana.enough is used to check if a spell can be cast with an given amount
|
||||
* of avalable mana
|
||||
*/
|
||||
@Test
|
||||
public void testManaEnough() {
|
||||
assertAvailableManaEnough("{G}", 1, "", true);
|
||||
assertAvailableManaEnough("{G}", 0, "{G}", true);
|
||||
assertAvailableManaEnough("{R}", 0, "{G}", false);
|
||||
assertAvailableManaEnough("{B}", 0, "{G}", false);
|
||||
assertAvailableManaEnough("{U}", 0, "{G}", false);
|
||||
assertAvailableManaEnough("{W}", 0, "{G}", false);
|
||||
assertAvailableManaEnough("{W}", 0, "{C}", false);
|
||||
|
||||
assertAvailableManaEnough("{R}", 1, "", true);
|
||||
assertAvailableManaEnough("{R}", 0, "{R}", true);
|
||||
assertAvailableManaEnough("{G}", 0, "{R}", false);
|
||||
assertAvailableManaEnough("{B}", 0, "{R}", false);
|
||||
assertAvailableManaEnough("{U}", 0, "{R}", false);
|
||||
assertAvailableManaEnough("{W}", 0, "{R}", false);
|
||||
|
||||
assertAvailableManaEnough("{U}{B}{W}{G}{R}", 4, "{R}", true);
|
||||
assertAvailableManaEnough("{U}{B}{W}{G}{R}", 3, "{R}{B}", true);
|
||||
|
||||
assertAvailableManaEnough("{U}{U}{U}{G}{G}{2}", 2, "{U}{U}{G}{R}{B}", true);
|
||||
|
||||
assertAvailableManaEnough("{2}{U}{U}", 0, "{U}{U}{U}{U}", true);
|
||||
assertAvailableManaEnough("{2}{U}{U}", 0, "{4}", false);
|
||||
assertAvailableManaEnough("{2}{U}{U}", 0, "{B}{B}{4}", false);
|
||||
|
||||
assertAvailableManaEnough("{G}", 0, "{G/W}", true);
|
||||
assertAvailableManaEnough("{G}{W}", 0, "{G/W}{G/W}", true);
|
||||
assertAvailableManaEnough("{W}{W}", 0, "{G/W}{G/W}", true);
|
||||
assertAvailableManaEnough("{G}{G}", 0, "{G/W}{G/W}", true);
|
||||
|
||||
assertAvailableManaEnough("{C}", 1, "", false);
|
||||
assertAvailableManaEnough("{C}", 0, "{C}", true);
|
||||
assertAvailableManaEnough("{C}", 0, "{G}", false);
|
||||
assertAvailableManaEnough("{C}", 0, "{R}", false);
|
||||
assertAvailableManaEnough("{C}", 0, "{B}", false);
|
||||
assertAvailableManaEnough("{C}", 0, "{W}", false);
|
||||
assertAvailableManaEnough("{C}", 0, "{U}", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mana.enough is used to check if a spell can be cast with an given amount
|
||||
* of avalable mana
|
||||
*/
|
||||
@Test
|
||||
public void testManaReduction() {
|
||||
// cost - reduction - rest
|
||||
assertManaReduction("{G}{G}", "{G}", "{G}");
|
||||
assertManaReduction("{1}{G}{G}", "{G}", "{1}{G}");
|
||||
assertManaReduction("{B}{B}", "{B}", "{B}");
|
||||
assertManaReduction("{1}{B}{B}", "{B}", "{1}{B}");
|
||||
assertManaReduction("{W}{W}", "{W}", "{W}");
|
||||
assertManaReduction("{1}{W}{W}", "{W}", "{1}{W}");
|
||||
assertManaReduction("{U}{U}", "{U}", "{U}");
|
||||
assertManaReduction("{1}{U}{U}", "{U}", "{1}{U}");
|
||||
assertManaReduction("{R}{R}", "{R}", "{R}");
|
||||
assertManaReduction("{1}{R}{R}", "{R}", "{1}{R}");
|
||||
|
||||
assertManaReduction("{R}{G}{B}{U}{W}", "{R}{G}{B}{U}{W}", "{0}");
|
||||
|
||||
// Hybrid Mana
|
||||
assertManaReduction("{2/B}{2/B}{2/B}", "{B}{B}", "{2/B}");
|
||||
assertManaReduction("{2/B}{2/B}{2/B}", "{B}{B}{B}", "{0}");
|
||||
assertManaReduction("{2/W}{2/W}{2/W}", "{W}{W}", "{2/W}");
|
||||
assertManaReduction("{2/W}{2/W}{2/W}", "{W}{W}{W}", "{0}");
|
||||
|
||||
assertManaReduction("{G/B}{G/B}{G/B}", "{B}{G}{B}", "{0}");
|
||||
}
|
||||
|
||||
/**
|
||||
* Mana.needed is used by the AI to know how much mana it needs in order to be able to play a card.
|
||||
*/
|
||||
@Test
|
||||
public void should() {
|
||||
// TODO: How does it handle generic and any.
|
||||
// How *should* it handle them?
|
||||
testManaNeeded(
|
||||
new Mana(ManaType.COLORLESS, 1), // Available
|
||||
new Mana(ManaType.COLORLESS, 2), // Cost
|
||||
new Mana(ManaType.COLORLESS, 1) // Needed
|
||||
);
|
||||
testManaNeeded(
|
||||
new Mana(ManaType.RED, 1), // Avaiable
|
||||
new Mana(ManaType.GENERIC, 1), // Cost
|
||||
new Mana() // Needed
|
||||
);
|
||||
testManaNeeded(
|
||||
new Mana(ManaType.COLORLESS, 1), // Avaiable
|
||||
new Mana(ManaType.GENERIC, 1), // Cost
|
||||
new Mana() // Needed
|
||||
);
|
||||
testManaNeeded(
|
||||
new Mana(), // Available
|
||||
new Mana(2, 0, 0, 0, 0, 2, 0, 0), // Cost
|
||||
new Mana(2, 0, 0, 0, 0, 2, 0, 0) // Needed
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the mana needed calculations produces the expected needed mana amount.
|
||||
*
|
||||
* @param available The mana currently available.
|
||||
* @param cost The mana needed for a cost.
|
||||
* @param neededExpected The mana expected to be required to pay the cost.
|
||||
*/
|
||||
private void testManaNeeded(Mana available, Mana cost, Mana neededExpected) {
|
||||
Mana neededActual = cost.needed(available);
|
||||
Assert.assertTrue(
|
||||
"The mana needed to pay " + cost + " given " + available
|
||||
+ " should have been " + neededExpected + " but was calculate to be " + neededActual,
|
||||
neededActual.equalManaValue(neededExpected)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given available Mana is enough to pay a given mana cost
|
||||
*
|
||||
* @param manaCostsToPay The mana cost that needs to be paid.
|
||||
* @param availablyAny The amount of generic mana available.
|
||||
* @param available The colored and colorless mana available.
|
||||
* @param expected boolean indicating if the available mana is expected to cover the mana cost.
|
||||
*/
|
||||
private void assertAvailableManaEnough(String manaCostsToPay, int availablyAny, String available, boolean expected) {
|
||||
ManaCost unpaid = new ManaCostsImpl<>(manaCostsToPay);
|
||||
ManaCost costAvailable = new ManaCostsImpl<>(available);
|
||||
Mana manaAvailable = costAvailable.getMana();
|
||||
manaAvailable.setAny(availablyAny);
|
||||
if (expected) {
|
||||
Assert.assertTrue("The available Mana " + costAvailable.getText() + " should be enough to pay the costs " + unpaid.getText(), unpaid.getMana().enough(manaAvailable));
|
||||
} else {
|
||||
Assert.assertFalse("The available Mana " + costAvailable.getText() + " shouldn't be enough to pay the costs " + unpaid.getText(), unpaid.getMana().enough(manaAvailable));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a given mana reduction left the expected amount of mana costs
|
||||
*
|
||||
* @param manaCostsToPay The mana cost before reductions are applied.
|
||||
* @param manaToReduce The amount and types of many to reduced the cost by.
|
||||
* @param restMana The expected amount of mana left
|
||||
*/
|
||||
private void assertManaReduction(String manaCostsToPay, String manaToReduce, String restMana) {
|
||||
SpellAbility spellAbility = new SpellAbility(new ManaCostsImpl<>(manaCostsToPay), "Test");
|
||||
CardUtil.adjustCost(spellAbility, new ManaCostsImpl<>(manaToReduce), true);
|
||||
Assert.assertEquals(
|
||||
"The mana cost to pay " + manaCostsToPay + " reduced by " + manaToReduce +
|
||||
" should left " + restMana + " but the rest was " + spellAbility.getManaCostsToPay(),
|
||||
spellAbility.getManaCostsToPay().getText(),
|
||||
restMana
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue