mirror of
https://github.com/correl/mage.git
synced 2025-03-16 17:00:13 -09:00
[ZNR] Implemented party mechanic (#7036)
* added incomplete party count implementation * updated party count implementation * added party count test * fixed tests, updated test framework * added an additional test * fixed some errors in party count computation, should be good now * fixed a small error with test generation * fixed an NPE issue
This commit is contained in:
commit
cc0bb84dad
3 changed files with 245 additions and 3 deletions
|
@ -0,0 +1,127 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSingleMember() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
|
||||||
|
addCard(Zone.HAND, playerA, shpd);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, shpd);
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 22);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTwoMembers() {
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains", 6);
|
||||||
|
addCard(Zone.HAND, playerA, skrmshr);
|
||||||
|
addCard(Zone.HAND, playerA, shpd);
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, skrmshr);
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, shpd);
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
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(1, PhaseStep.PRECOMBAT_MAIN, playerA, skrmshr);
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, skrmshr);
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, shpd);
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
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(1, PhaseStep.PRECOMBAT_MAIN, playerA, skrmshr);
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, dncstr);
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, shpd);
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
assertLife(playerA, 26);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void makeCreature(String name, SubType... subTypes) {
|
||||||
|
addCustomCardWithAbility(name, playerA, null, null, CardType.CREATURE, "{1}", Zone.BATTLEFIELD, subTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOddCombos() {
|
||||||
|
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(1, PhaseStep.POSTCOMBAT_MAIN, playerA, shpd);
|
||||||
|
|
||||||
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
execute();
|
||||||
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package org.mage.test.serverside.base;
|
package org.mage.test.serverside.base;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
import mage.abilities.Abilities;
|
import mage.abilities.Abilities;
|
||||||
import mage.abilities.AbilitiesImpl;
|
import mage.abilities.AbilitiesImpl;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
@ -381,8 +382,15 @@ public abstract class MageTestPlayerBase {
|
||||||
|
|
||||||
protected void addCustomCardWithAbility(String customName, TestPlayer controllerPlayer, Ability ability, SpellAbility spellAbility,
|
protected void addCustomCardWithAbility(String customName, TestPlayer controllerPlayer, Ability ability, SpellAbility spellAbility,
|
||||||
CardType cardType, String spellCost, Zone putAtZone) {
|
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.clearCustomAbilities(customName);
|
||||||
CustomTestCard.addCustomAbility(customName, spellAbility, ability);
|
CustomTestCard.addCustomAbility(customName, spellAbility, ability);
|
||||||
|
CustomTestCard.clearAdditionalSubtypes(customName);
|
||||||
|
CustomTestCard.addAdditionalSubtypes(customName, additionalSubTypes);
|
||||||
|
|
||||||
CardSetInfo testSet = new CardSetInfo(customName, "custom", "123", Rarity.COMMON);
|
CardSetInfo testSet = new CardSetInfo(customName, "custom", "123", Rarity.COMMON);
|
||||||
PermanentCard card = new PermanentCard(new CustomTestCard(controllerPlayer.getId(), testSet, cardType, spellCost), controllerPlayer.getId(), currentGame);
|
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<String, Abilities<Ability>> abilitiesList = new HashMap<>(); // card name -> abilities
|
static private final Map<String, Abilities<Ability>> abilitiesList = new HashMap<>(); // card name -> abilities
|
||||||
static private final Map<String, SpellAbility> spellAbilitiesList = new HashMap<>(); // card name -> spell ability
|
static private final Map<String, SpellAbility> spellAbilitiesList = new HashMap<>(); // card name -> spell ability
|
||||||
|
static private final Map<String, Set<SubType>> subTypesList = new HashMap<>(); // card name -> additional subtypes
|
||||||
|
|
||||||
static void addCustomAbility(String cardName, SpellAbility spellAbility, Ability ability) {
|
static void addCustomAbility(String cardName, SpellAbility spellAbility, Ability ability) {
|
||||||
if (!abilitiesList.containsKey(cardName)) {
|
if (!abilitiesList.containsKey(cardName)) {
|
||||||
|
@ -430,6 +439,16 @@ class CustomTestCard extends CardImpl {
|
||||||
spellAbilitiesList.remove(cardName);
|
spellAbilitiesList.remove(cardName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void addAdditionalSubtypes(String cardName, SubType... subtypes) {
|
||||||
|
if(subtypes!=null) {
|
||||||
|
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) {
|
CustomTestCard(UUID ownerId, CardSetInfo setInfo, CardType cardType, String spellCost) {
|
||||||
super(ownerId, setInfo, new CardType[]{cardType}, spellCost);
|
super(ownerId, setInfo, new CardType[]{cardType}, spellCost);
|
||||||
|
|
||||||
|
@ -443,6 +462,17 @@ class CustomTestCard extends CardImpl {
|
||||||
this.addAbility(ability.copy());
|
this.addAbility(ability.copy());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Set<SubType> 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) {
|
private CustomTestCard(final CustomTestCard card) {
|
||||||
|
|
|
@ -4,7 +4,9 @@ import mage.abilities.Ability;
|
||||||
import mage.abilities.dynamicvalue.DynamicValue;
|
import mage.abilities.dynamicvalue.DynamicValue;
|
||||||
import mage.abilities.effects.Effect;
|
import mage.abilities.effects.Effect;
|
||||||
import mage.constants.SubType;
|
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.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
|
|
||||||
|
@ -16,11 +18,94 @@ import java.util.stream.Collectors;
|
||||||
*/
|
*/
|
||||||
public enum PartyCount implements DynamicValue {
|
public enum PartyCount implements DynamicValue {
|
||||||
instance;
|
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<SubType> partyTypes = Arrays.asList(
|
||||||
|
SubType.CLERIC,
|
||||||
|
SubType.ROGUE,
|
||||||
|
SubType.WARRIOR,
|
||||||
|
SubType.WIZARD
|
||||||
|
);
|
||||||
|
|
||||||
|
private static boolean attemptRearrange(SubType subType, UUID uuid, Set<SubType> creatureTypes, Map<SubType, UUID> subTypeUUIDMap, Map<UUID, Set<SubType>> creatureTypesMap) {
|
||||||
|
UUID uuid1 = subTypeUUIDMap.get(subType);
|
||||||
|
if (uuid1 == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Set<SubType> creatureTypes1 = creatureTypesMap.get(uuid1);
|
||||||
|
for (SubType subType1 : creatureTypes1) {
|
||||||
|
if (subType == subType1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!subTypeUUIDMap.containsKey(subType1)) {
|
||||||
|
subTypeUUIDMap.put(subType, uuid);
|
||||||
|
subTypeUUIDMap.put(subType1, uuid1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return attemptRearrange(subType1, uuid1, creatureTypes, subTypeUUIDMap, creatureTypesMap);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<SubType> makeSet(Permanent permanent, Game game) {
|
||||||
|
Set<SubType> subTypeSet = new HashSet<>();
|
||||||
|
for (SubType subType : partyTypes) {
|
||||||
|
if (permanent.hasSubtype(subType, game)) {
|
||||||
|
subTypeSet.add(subType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subTypeSet;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||||
// TODO: implement this (in separate branch for now)
|
Map<UUID, Set<SubType>> creatureTypesMap = new HashMap<>();
|
||||||
return 0;
|
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<SubType> availableTypes = creatureTypesMap
|
||||||
|
.values()
|
||||||
|
.stream()
|
||||||
|
.flatMap(Collection::stream)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
if (creatureTypesMap.size() == 2) {
|
||||||
|
return Math.min(2, availableTypes.size());
|
||||||
|
}
|
||||||
|
Map<SubType, UUID> subTypeUUIDMap = new HashMap<>();
|
||||||
|
for (Map.Entry<UUID, Set<SubType>> 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 if (subTypeUUIDMap.containsValue(entry.getKey())) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
for (SubType subType : entry.getValue()) {
|
||||||
|
if (attemptRearrange(subType, entry.getKey(), entry.getValue(), subTypeUUIDMap, creatureTypesMap)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return subTypeUUIDMap.keySet().size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Add table
Reference in a new issue