From 73d37f0b70fc28c140d340d98f59cb2404bd103a Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Tue, 1 Sep 2020 17:36:40 -0400 Subject: [PATCH 1/8] added incomplete party count implementation --- .../dynamicvalue/common/PartyCount.java | 89 ++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/PartyCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/PartyCount.java index 58b5acce23..d2e0ed9492 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/PartyCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/PartyCount.java @@ -17,12 +17,99 @@ import java.util.stream.Collectors; public enum PartyCount implements DynamicValue { instance; + private static final List partyTypes = Arrays.asList( + SubType.CLERIC, + SubType.ROGUE, + SubType.WARRIOR, + SubType.WIZARD + ); + + private static final class CreatureTypes { + + private final Set subTypeSet = new HashSet<>(); + + private CreatureTypes(Permanent permanent, Game game) { + for (SubType subType : partyTypes) { + if (permanent.hasSubtype(subType, game)) { + subTypeSet.add(subType); + } + } + } + + private int count() { + return subTypeSet.size(); + } + + private void removeAll(Set subTypes) { + this.subTypeSet.removeAll(subTypes); + } + + public Set getSubTypeSet() { + return subTypeSet; + } + } + @Override public int calculate(Game game, Ability sourceAbility, Effect effect) { - // TODO: implement this (in separate branch for now) + List creatureTypesList = game.getBattlefield() + .getActivePermanents( + StaticFilters.FILTER_CONTROLLED_CREATURE, + sourceAbility.getControllerId(), sourceAbility.getSourceId(), game + ).stream() + .map(permanent -> new CreatureTypes(permanent, game)) + .collect(Collectors.toList()); + Set subTypeSet = new HashSet<>(); + boolean flag = true; + while (flag) { + List creatureTypesList2 = new ArrayList<>(); + // Find all creatures with only one of the creature types and eliminate those + for (CreatureTypes creatureTypes : creatureTypesList) { + switch (creatureTypes.count()) { + case 0: + break; + case 1: + subTypeSet.addAll(creatureTypes.getSubTypeSet()); + flag = false; + break; + default: + creatureTypesList2.add(creatureTypes); + } + } + // Now find all the creature types only represented by one creature and eliminate those + for (SubType subType : partyTypes) { + if (!subTypeSet.contains(subType) && creatureTypesList2.stream().map(CreatureTypes::getSubTypeSet).mapToInt(s -> s.contains(subType) ? 1 : 0).sum() == 1) { + subTypeSet.add(subType); + flag = false; + } + } + // Remove all the eliminated creature types from the creatures and now we have less to analyze + creatureTypesList.clear(); + for (CreatureTypes creatureTypes : creatureTypesList2) { + creatureTypes.removeAll(subTypeSet); + if (creatureTypes.count() > 0) { + creatureTypesList.add(creatureTypes); + } + } + } + if (creatureTypesList.isEmpty()) { + return subTypeSet.size(); + } + // Not sure what to do here return 0; } + private static int typeCount(Permanent permanent, Game game) { + return partyTypes.stream().mapToInt(subType -> permanent.hasSubtype(subType, game) ? 1 : 0).sum(); + } + + private static void addType(Permanent permanent, Set subTypeSet, Game game) { + for (SubType subType : partyTypes) { + if (permanent.hasSubtype(subType, game)) { + subTypeSet.add(subType); + } + } + } + @Override public PartyCount copy() { return instance; From e254371f912c5b71ac1d64380d49f7d7cfa77dde Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Thu, 3 Sep 2020 16:34:15 -0400 Subject: [PATCH 2/8] updated party count implementation --- .../dynamicvalue/common/PartyCount.java | 143 +++++++++--------- 1 file changed, 68 insertions(+), 75 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/PartyCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/PartyCount.java index d2e0ed9492..a0607f9dd4 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/PartyCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/PartyCount.java @@ -4,7 +4,9 @@ import mage.abilities.Ability; import mage.abilities.dynamicvalue.DynamicValue; import mage.abilities.effects.Effect; import mage.constants.SubType; -import mage.filter.StaticFilters; +import mage.filter.FilterPermanent; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.Predicates; import mage.game.Game; import mage.game.permanent.Permanent; @@ -16,6 +18,16 @@ import java.util.stream.Collectors; */ public enum PartyCount implements DynamicValue { instance; + private static final FilterPermanent filter = new FilterCreaturePermanent(); + + static { + filter.add(Predicates.or( + SubType.CLERIC.getPredicate(), + SubType.ROGUE.getPredicate(), + SubType.WARRIOR.getPredicate(), + SubType.WIZARD.getPredicate() + )); + } private static final List partyTypes = Arrays.asList( SubType.CLERIC, @@ -24,90 +36,71 @@ public enum PartyCount implements DynamicValue { SubType.WIZARD ); - private static final class CreatureTypes { - - private final Set subTypeSet = new HashSet<>(); - - private CreatureTypes(Permanent permanent, Game game) { - for (SubType subType : partyTypes) { - if (permanent.hasSubtype(subType, game)) { - subTypeSet.add(subType); - } + private void attemptRearrange(SubType subType, UUID uuid, Set creatureTypes, Map subTypeUUIDMap, Map> creatureTypesMap) { + UUID uuid1 = subTypeUUIDMap.get(subType); + if (uuid1 == null) { + return; + } + Set creatureTypes1 = creatureTypesMap.get(uuid1); + for (SubType subType1 : creatureTypes1) { + if (subType == subType1) { + continue; } - } - - private int count() { - return subTypeSet.size(); - } - - private void removeAll(Set subTypes) { - this.subTypeSet.removeAll(subTypes); - } - - public Set getSubTypeSet() { - return subTypeSet; + if (!subTypeUUIDMap.containsKey(subType1)) { + subTypeUUIDMap.put(subType, uuid); + subTypeUUIDMap.put(subType1, uuid1); + continue; + } + attemptRearrange(subType1, uuid1, creatureTypes, subTypeUUIDMap, creatureTypesMap); } } - @Override - public int calculate(Game game, Ability sourceAbility, Effect effect) { - List creatureTypesList = game.getBattlefield() - .getActivePermanents( - StaticFilters.FILTER_CONTROLLED_CREATURE, - sourceAbility.getControllerId(), sourceAbility.getSourceId(), game - ).stream() - .map(permanent -> new CreatureTypes(permanent, game)) - .collect(Collectors.toList()); + private Set makeSet(Permanent permanent, Game game) { Set subTypeSet = new HashSet<>(); - boolean flag = true; - while (flag) { - List creatureTypesList2 = new ArrayList<>(); - // Find all creatures with only one of the creature types and eliminate those - for (CreatureTypes creatureTypes : creatureTypesList) { - switch (creatureTypes.count()) { - case 0: - break; - case 1: - subTypeSet.addAll(creatureTypes.getSubTypeSet()); - flag = false; - break; - default: - creatureTypesList2.add(creatureTypes); - } - } - // Now find all the creature types only represented by one creature and eliminate those - for (SubType subType : partyTypes) { - if (!subTypeSet.contains(subType) && creatureTypesList2.stream().map(CreatureTypes::getSubTypeSet).mapToInt(s -> s.contains(subType) ? 1 : 0).sum() == 1) { - subTypeSet.add(subType); - flag = false; - } - } - // Remove all the eliminated creature types from the creatures and now we have less to analyze - creatureTypesList.clear(); - for (CreatureTypes creatureTypes : creatureTypesList2) { - creatureTypes.removeAll(subTypeSet); - if (creatureTypes.count() > 0) { - creatureTypesList.add(creatureTypes); - } - } - } - if (creatureTypesList.isEmpty()) { - return subTypeSet.size(); - } - // Not sure what to do here - return 0; - } - - private static int typeCount(Permanent permanent, Game game) { - return partyTypes.stream().mapToInt(subType -> permanent.hasSubtype(subType, game) ? 1 : 0).sum(); - } - - private static void addType(Permanent permanent, Set subTypeSet, Game game) { for (SubType subType : partyTypes) { if (permanent.hasSubtype(subType, game)) { subTypeSet.add(subType); } } + return subTypeSet; + } + + @Override + public int calculate(Game game, Ability sourceAbility, Effect effect) { + Map> creatureTypesMap = new HashMap<>(); + game.getBattlefield() + .getActivePermanents( + filter, sourceAbility.getControllerId(), sourceAbility.getSourceId(), game + ).stream() + .forEach(permanent -> creatureTypesMap.put(permanent.getId(), makeSet(permanent, game))); + if (creatureTypesMap.size() < 2) { + return creatureTypesMap.size(); + } + Set availableTypes = creatureTypesMap + .values() + .stream() + .flatMap(Collection::stream) + .collect(Collectors.toSet()); + if (creatureTypesMap.size() == 2) { + return Math.min(2, availableTypes.size()); + } + Map subTypeUUIDMap = new HashMap<>(); + for (Map.Entry> entry : creatureTypesMap.entrySet()) { + for (SubType subType : entry.getValue()) { + if (!subTypeUUIDMap.containsKey(subType)) { + subTypeUUIDMap.put(subType, entry.getKey()); + break; + } + } + if (subTypeUUIDMap.size() >= availableTypes.size()) { + return subTypeUUIDMap.size(); + } else { + for (SubType subType : entry.getValue()) { + attemptRearrange(subType, entry.getKey(), entry.getValue(), subTypeUUIDMap, creatureTypesMap); + } + } + } + return subTypeUUIDMap.keySet().size(); } @Override From fa40ffe837d6739c29858b343f78f36456342f7d Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Thu, 3 Sep 2020 16:34:25 -0400 Subject: [PATCH 3/8] added party count test --- .../cards/dynamicvalue/PartyCountTest.java | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/PartyCountTest.java diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/PartyCountTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/PartyCountTest.java new file mode 100644 index 0000000000..7d3ba6b14a --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/PartyCountTest.java @@ -0,0 +1,127 @@ +package org.mage.test.cards.dynamicvalue; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author TheElk801 + */ +public class PartyCountTest extends CardTestPlayerBase { + + private static final String shpd = "Shepherd of Heroes"; + private static final String skrmshr = "Aven Skirmisher"; + private static final String dncstr = "Dromoka Dunecaster"; + private static final String ddgr = "Goldmeadow Dodger"; + private static final String mstfrm = "Mistform Sliver"; + private static final String glrdr = "Galerider Sliver"; + private static final String mtllc = "Metallic Sliver"; + private static final String pltd = "Plated Sliver"; + private static final String cleric = "Cleric"; + private static final String rogue = "Rogue"; + private static final String warrior = "Warrior"; + private static final String wizard = "Wizard"; + + private void setType(String creature, String type) { + setChoice(playerA, type); + activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerA, creature); + waitStackResolved(2, PhaseStep.POSTCOMBAT_MAIN); + } + + @Test + public void testSingleMember() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.HAND, playerA, shpd); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shpd); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 22); + } + + @Test + public void testTwoMembers() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 6); + addCard(Zone.HAND, playerA, skrmshr); + addCard(Zone.HAND, playerA, shpd); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, skrmshr); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shpd); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 24); + } + + @Test + public void testTwoMembers2() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + addCard(Zone.HAND, playerA, skrmshr, 2); + addCard(Zone.HAND, playerA, shpd); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, skrmshr); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, skrmshr); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shpd); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 24); + } + + @Test + public void testThreeMembers() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + addCard(Zone.HAND, playerA, skrmshr); + addCard(Zone.HAND, playerA, dncstr); + addCard(Zone.HAND, playerA, shpd); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, skrmshr); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, dncstr); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shpd); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 26); + } + + @Test + public void testOddCombos() { + addCard(Zone.BATTLEFIELD, playerA, "Tundra", 19); + addCard(Zone.HAND, playerA, mstfrm); + addCard(Zone.HAND, playerA, glrdr); + addCard(Zone.HAND, playerA, mtllc); + addCard(Zone.HAND, playerA, pltd); + addCard(Zone.HAND, playerA, shpd); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, mstfrm); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, glrdr); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, mtllc); + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, pltd); + + setType(mstfrm, rogue); + setType(mstfrm, wizard); + setType(mstfrm, warrior); + + setType(glrdr, rogue); + setType(glrdr, cleric); + + setType(mtllc, cleric); + setType(mtllc, wizard); + + setType(pltd, warrior); + setType(pltd, wizard); + + castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shpd); + + setStopAt(2, PhaseStep.END_TURN); + execute(); + + assertLife(playerA, 28); + } +} From f0df4267a3d5d20c120b7e3b36a62d61a2697e6d Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Thu, 3 Sep 2020 18:12:43 -0400 Subject: [PATCH 4/8] fixed tests, updated test framework --- .../cards/dynamicvalue/PartyCountTest.java | 88 ++++++++----------- .../serverside/base/MageTestPlayerBase.java | 28 ++++++ 2 files changed, 64 insertions(+), 52 deletions(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/PartyCountTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/PartyCountTest.java index 7d3ba6b14a..83d435d956 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/PartyCountTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/PartyCountTest.java @@ -1,6 +1,8 @@ package org.mage.test.cards.dynamicvalue; +import mage.constants.CardType; import mage.constants.PhaseStep; +import mage.constants.SubType; import mage.constants.Zone; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -14,30 +16,18 @@ public class PartyCountTest extends CardTestPlayerBase { private static final String skrmshr = "Aven Skirmisher"; private static final String dncstr = "Dromoka Dunecaster"; private static final String ddgr = "Goldmeadow Dodger"; - private static final String mstfrm = "Mistform Sliver"; - private static final String glrdr = "Galerider Sliver"; - private static final String mtllc = "Metallic Sliver"; - private static final String pltd = "Plated Sliver"; - private static final String cleric = "Cleric"; - private static final String rogue = "Rogue"; - private static final String warrior = "Warrior"; - private static final String wizard = "Wizard"; - - private void setType(String creature, String type) { - setChoice(playerA, type); - activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerA, creature); - waitStackResolved(2, PhaseStep.POSTCOMBAT_MAIN); - } @Test public void testSingleMember() { addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); addCard(Zone.HAND, playerA, shpd); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shpd); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, shpd); - setStopAt(2, PhaseStep.END_TURN); + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); execute(); + assertAllCommandsUsed(); assertLife(playerA, 22); } @@ -48,11 +38,13 @@ public class PartyCountTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, skrmshr); addCard(Zone.HAND, playerA, shpd); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, skrmshr); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shpd); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, skrmshr); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, shpd); - setStopAt(2, PhaseStep.END_TURN); + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); execute(); + assertAllCommandsUsed(); assertLife(playerA, 24); } @@ -63,12 +55,14 @@ public class PartyCountTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, skrmshr, 2); addCard(Zone.HAND, playerA, shpd); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, skrmshr); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, skrmshr); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shpd); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, skrmshr); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, skrmshr); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, shpd); - setStopAt(2, PhaseStep.END_TURN); + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); execute(); + assertAllCommandsUsed(); assertLife(playerA, 24); } @@ -80,47 +74,37 @@ public class PartyCountTest extends CardTestPlayerBase { addCard(Zone.HAND, playerA, dncstr); addCard(Zone.HAND, playerA, shpd); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, skrmshr); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, dncstr); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shpd); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, skrmshr); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, dncstr); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, shpd); - setStopAt(2, PhaseStep.END_TURN); + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); execute(); + assertAllCommandsUsed(); assertLife(playerA, 26); } + private void makeCreature(String name, SubType... subTypes) { + addCustomCardWithAbility("dude", playerA, null, null, CardType.CREATURE, "{1}", Zone.BATTLEFIELD, subTypes); + } + @Test public void testOddCombos() { - addCard(Zone.BATTLEFIELD, playerA, "Tundra", 19); - addCard(Zone.HAND, playerA, mstfrm); - addCard(Zone.HAND, playerA, glrdr); - addCard(Zone.HAND, playerA, mtllc); - addCard(Zone.HAND, playerA, pltd); + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); addCard(Zone.HAND, playerA, shpd); + makeCreature("crtA", SubType.ROGUE, SubType.WIZARD, SubType.WARRIOR); + makeCreature("crtB", SubType.ROGUE, SubType.CLERIC); + makeCreature("crtC", SubType.CLERIC, SubType.WIZARD); + makeCreature("crtD", SubType.WARRIOR, SubType.WIZARD); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, mstfrm); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, glrdr); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, mtllc); - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, pltd); + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, shpd); - setType(mstfrm, rogue); - setType(mstfrm, wizard); - setType(mstfrm, warrior); - - setType(glrdr, rogue); - setType(glrdr, cleric); - - setType(mtllc, cleric); - setType(mtllc, wizard); - - setType(pltd, warrior); - setType(pltd, wizard); - - castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, shpd); - - setStopAt(2, PhaseStep.END_TURN); + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); execute(); + assertAllCommandsUsed(); assertLife(playerA, 28); } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java index 4889220447..694498f91c 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java @@ -1,5 +1,6 @@ package org.mage.test.serverside.base; +import mage.MageInt; import mage.abilities.Abilities; import mage.abilities.AbilitiesImpl; import mage.abilities.Ability; @@ -381,8 +382,15 @@ public abstract class MageTestPlayerBase { protected void addCustomCardWithAbility(String customName, TestPlayer controllerPlayer, Ability ability, SpellAbility spellAbility, CardType cardType, String spellCost, Zone putAtZone) { + addCustomCardWithAbility(customName, controllerPlayer, ability, spellAbility, cardType, spellCost, putAtZone, null); + } + + protected void addCustomCardWithAbility(String customName, TestPlayer controllerPlayer, Ability ability, SpellAbility spellAbility, + CardType cardType, String spellCost, Zone putAtZone, SubType... additionalSubTypes) { CustomTestCard.clearCustomAbilities(customName); CustomTestCard.addCustomAbility(customName, spellAbility, ability); + CustomTestCard.clearAdditionalSubtypes(customName); + CustomTestCard.addAdditionalSubtypes(customName, additionalSubTypes); CardSetInfo testSet = new CardSetInfo(customName, "custom", "123", Rarity.COMMON); PermanentCard card = new PermanentCard(new CustomTestCard(controllerPlayer.getId(), testSet, cardType, spellCost), controllerPlayer.getId(), currentGame); @@ -414,6 +422,7 @@ class CustomTestCard extends CardImpl { static private final Map> abilitiesList = new HashMap<>(); // card name -> abilities static private final Map spellAbilitiesList = new HashMap<>(); // card name -> spell ability + static private final Map> subTypesList = new HashMap<>(); // card name -> additional subtypes static void addCustomAbility(String cardName, SpellAbility spellAbility, Ability ability) { if (!abilitiesList.containsKey(cardName)) { @@ -430,6 +439,14 @@ class CustomTestCard extends CardImpl { spellAbilitiesList.remove(cardName); } + static void addAdditionalSubtypes(String cardName, SubType... subtypes) { + subTypesList.computeIfAbsent(cardName, s -> new HashSet<>()).addAll(Arrays.asList(subtypes.clone())); + } + + static void clearAdditionalSubtypes(String cardName) { + subTypesList.remove(cardName); + } + CustomTestCard(UUID ownerId, CardSetInfo setInfo, CardType cardType, String spellCost) { super(ownerId, setInfo, new CardType[]{cardType}, spellCost); @@ -443,6 +460,17 @@ class CustomTestCard extends CardImpl { this.addAbility(ability.copy()); } } + + Set subTypeSet = subTypesList.get(setInfo.getName()); + if (subTypeSet != null) { + for (SubType subType : subTypeSet) { + this.subtype.add(subType); + } + } + if (cardType == CardType.CREATURE) { + this.power = new MageInt(1); + this.toughness = new MageInt(1); + } } private CustomTestCard(final CustomTestCard card) { From 86446f49775991bcbbd3c096d35735af1974aeb7 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Thu, 3 Sep 2020 18:25:39 -0400 Subject: [PATCH 5/8] added an additional test --- .../test/cards/dynamicvalue/PartyCountTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/PartyCountTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/PartyCountTest.java index 83d435d956..5a41094ac6 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/PartyCountTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/PartyCountTest.java @@ -108,4 +108,20 @@ public class PartyCountTest extends CardTestPlayerBase { assertLife(playerA, 28); } + + @Test + public void testChangelings() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerA, "Impostor of the Sixth Pride", 4); + addCard(Zone.HAND, playerA, shpd); + + castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, shpd); + + setStopAt(1, PhaseStep.END_TURN); + setStrictChooseMode(true); + execute(); + assertAllCommandsUsed(); + + assertLife(playerA, 28); + } } From 84609b74e07392a309ff303525f86640fddc23d6 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Thu, 3 Sep 2020 18:25:59 -0400 Subject: [PATCH 6/8] fixed some errors in party count computation, should be good now --- .../dynamicvalue/common/PartyCount.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Mage/src/main/java/mage/abilities/dynamicvalue/common/PartyCount.java b/Mage/src/main/java/mage/abilities/dynamicvalue/common/PartyCount.java index a0607f9dd4..c6214dea56 100644 --- a/Mage/src/main/java/mage/abilities/dynamicvalue/common/PartyCount.java +++ b/Mage/src/main/java/mage/abilities/dynamicvalue/common/PartyCount.java @@ -36,10 +36,10 @@ public enum PartyCount implements DynamicValue { SubType.WIZARD ); - private void attemptRearrange(SubType subType, UUID uuid, Set creatureTypes, Map subTypeUUIDMap, Map> creatureTypesMap) { + private static boolean attemptRearrange(SubType subType, UUID uuid, Set creatureTypes, Map subTypeUUIDMap, Map> creatureTypesMap) { UUID uuid1 = subTypeUUIDMap.get(subType); if (uuid1 == null) { - return; + return false; } Set creatureTypes1 = creatureTypesMap.get(uuid1); for (SubType subType1 : creatureTypes1) { @@ -49,13 +49,14 @@ public enum PartyCount implements DynamicValue { if (!subTypeUUIDMap.containsKey(subType1)) { subTypeUUIDMap.put(subType, uuid); subTypeUUIDMap.put(subType1, uuid1); - continue; + return true; } - attemptRearrange(subType1, uuid1, creatureTypes, subTypeUUIDMap, creatureTypesMap); + return attemptRearrange(subType1, uuid1, creatureTypes, subTypeUUIDMap, creatureTypesMap); } + return false; } - private Set makeSet(Permanent permanent, Game game) { + private static Set makeSet(Permanent permanent, Game game) { Set subTypeSet = new HashSet<>(); for (SubType subType : partyTypes) { if (permanent.hasSubtype(subType, game)) { @@ -94,9 +95,13 @@ public enum PartyCount implements DynamicValue { } if (subTypeUUIDMap.size() >= availableTypes.size()) { return subTypeUUIDMap.size(); + } else if (subTypeUUIDMap.containsValue(entry.getKey())) { + continue; } else { for (SubType subType : entry.getValue()) { - attemptRearrange(subType, entry.getKey(), entry.getValue(), subTypeUUIDMap, creatureTypesMap); + if (attemptRearrange(subType, entry.getKey(), entry.getValue(), subTypeUUIDMap, creatureTypesMap)) { + break; + } } } } From 003fd098b0edae40aa2bdc21bcb0a02eff024b57 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Thu, 3 Sep 2020 18:36:27 -0400 Subject: [PATCH 7/8] fixed a small error with test generation --- .../java/org/mage/test/cards/dynamicvalue/PartyCountTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/PartyCountTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/PartyCountTest.java index 5a41094ac6..1a18557d7d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/PartyCountTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/dynamicvalue/PartyCountTest.java @@ -87,7 +87,7 @@ public class PartyCountTest extends CardTestPlayerBase { } private void makeCreature(String name, SubType... subTypes) { - addCustomCardWithAbility("dude", playerA, null, null, CardType.CREATURE, "{1}", Zone.BATTLEFIELD, subTypes); + addCustomCardWithAbility(name, playerA, null, null, CardType.CREATURE, "{1}", Zone.BATTLEFIELD, subTypes); } @Test From 0d12f566e51062b75856c072734c388ab4883c2b Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Thu, 3 Sep 2020 18:39:05 -0400 Subject: [PATCH 8/8] fixed an NPE issue --- .../org/mage/test/serverside/base/MageTestPlayerBase.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java index 694498f91c..55ddf6c13d 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestPlayerBase.java @@ -440,7 +440,9 @@ class CustomTestCard extends CardImpl { } static void addAdditionalSubtypes(String cardName, SubType... subtypes) { - subTypesList.computeIfAbsent(cardName, s -> new HashSet<>()).addAll(Arrays.asList(subtypes.clone())); + if(subtypes!=null) { + subTypesList.computeIfAbsent(cardName, s -> new HashSet<>()).addAll(Arrays.asList(subtypes.clone())); + } } static void clearAdditionalSubtypes(String cardName) {