diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 6bb0eb3e8c..6573e02f11 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -1,5 +1,6 @@ package org.mage.test.player; +import mage.MageItem; import mage.MageObject; import mage.MageObjectReference; import mage.ObjectColor; @@ -74,6 +75,7 @@ public class TestPlayer implements Player { private final List actions = new ArrayList<>(); private final List choices = new ArrayList<>(); // choices stack for choice private final List targets = new ArrayList<>(); // targets stack for choose (it's uses on empty direct target by cast command) + private final Map aliases = new HashMap<>(); // aliases for game objects/players (use it for cards with same name to save and use) private final List modesSet = new ArrayList<>(); private final ComputerPlayer computerPlayer; @@ -102,6 +104,7 @@ public class TestPlayer implements Player { this.actions.addAll(testPlayer.actions); this.choices.addAll(testPlayer.choices); this.targets.addAll(testPlayer.targets); + this.aliases.putAll(testPlayer.aliases); this.modesSet.addAll(testPlayer.modesSet); this.computerPlayer = testPlayer.computerPlayer.copy(); if (testPlayer.groupsForTargetHandling != null) { @@ -121,6 +124,18 @@ public class TestPlayer implements Player { return this.targets; } + public Map getAliases() { + return this.aliases; + } + + public UUID getAliasByName(String searchName) { + if (searchName.startsWith("@")) { + return this.aliases.getOrDefault(searchName.substring(1), null); + } else { + return this.aliases.getOrDefault(searchName, null); + } + } + public void addModeChoice(String mode) { modesSet.add(mode); } @@ -129,6 +144,10 @@ public class TestPlayer implements Player { targets.add(target); } + public void addAlias(String name, UUID Id) { + aliases.put(name, Id); + } + public ManaOptions getAvailableManaTest(Game game) { return computerPlayer.getManaAvailable(game); } @@ -314,6 +333,30 @@ public class TestPlayer implements Player { return result; } + public String generateAliasName(String baseAlias, boolean useMiltiNames, int iteration) { + if (useMiltiNames) { + return baseAlias + "." + iteration; + } else { + return baseAlias; + } + } + + private boolean isObjectHaveTargetNameOrAliase(MageObject object, String nameOrAliase) { + if (object == null || nameOrAliase == null) { + return false; + } + + if (nameOrAliase.startsWith("@") && object.getId().equals(getAliasByName(nameOrAliase))) { + return true; + } + + if (nameOrAliase.isEmpty() && object.getName().isEmpty()) { + return true; + } + + return object.getName().startsWith(nameOrAliase); + } + private boolean handleNonPlayerTargetTarget(String target, Ability ability, Game game) { boolean result = true; if (target == null) { @@ -375,28 +418,46 @@ public class TestPlayer implements Player { for (UUID id : currentTarget.possibleTargets(ability.getSourceId(), ability.getControllerId(), game)) { if (!currentTarget.getTargets().contains(id)) { MageObject object = game.getObject(id); - if (object != null - && ((object.isCopy() && !originOnly) || (!object.isCopy() && !copyOnly)) - && ((!targetName.isEmpty() && object.getName().startsWith(targetName)) || (targetName.isEmpty() && object.getName().isEmpty()))) { - if (currentTarget.getNumberOfTargets() == 1) { - currentTarget.clearChosen(); - } - if (currentTarget instanceof TargetCreaturePermanentAmount) { - // supports only to set the complete amount to one target - TargetCreaturePermanentAmount targetAmount = (TargetCreaturePermanentAmount) currentTarget; - targetAmount.setAmount(ability, game); - int amount = targetAmount.getAmountRemaining(); - targetAmount.addTarget(id, amount, ability, game); - targetsSet++; - } else { - currentTarget.addTarget(id, ability, game); - targetsSet++; - } - if (currentTarget.getTargets().size() == currentTarget.getMaxNumberOfTargets()) { - index++; - } - break; + + if (object == null) { + continue; } + + // only origin + if (originOnly && object.isCopy()) { + continue; + } + + // only copy + if (copyOnly && !object.isCopy()) { + continue; + } + + // need by alias or by name + if (!isObjectHaveTargetNameOrAliase(object, targetName)) { + continue; + } + + // founded, can use as target + + if (currentTarget.getNumberOfTargets() == 1) { + currentTarget.clearChosen(); + } + if (currentTarget instanceof TargetCreaturePermanentAmount) { + // supports only to set the complete amount to one target + TargetCreaturePermanentAmount targetAmount = (TargetCreaturePermanentAmount) currentTarget; + targetAmount.setAmount(ability, game); + int amount = targetAmount.getAmountRemaining(); + targetAmount.addTarget(id, amount, ability, game); + targetsSet++; + } else { + currentTarget.addTarget(id, ability, game); + targetsSet++; + } + if (currentTarget.getTargets().size() == currentTarget.getMaxNumberOfTargets()) { + index++; + } + break; } } } @@ -607,6 +668,13 @@ public class TestPlayer implements Player { actions.remove(action); wasProccessed = true; } + + // check aliase at zone: alias name, zone, must have (only for TestPlayer) + if (params[0].equals(CHECK_COMMAND_ALIAS_ZONE) && params.length == 4) { + assertAliasZone(action, game, this, params[1], Zone.valueOf(params[2]), Boolean.parseBoolean(params[3])); + actions.remove(action); + wasProccessed = true; + } } if (!wasProccessed) { Assert.fail("Unknow check command or params: " + command); @@ -674,6 +742,15 @@ public class TestPlayer implements Player { actions.remove(action); wasProccessed = true; } + + // show aliases + if (params[0].equals(SHOW_COMMAND_ALIASES) && params.length == 1) { + printStart(action.getActionName()); + printAliases(game, this); + printEnd(); + actions.remove(action); + wasProccessed = true; + } } if (!wasProccessed) { @@ -777,6 +854,38 @@ public class TestPlayer implements Player { } } + + private String getAliasInfo(Game game, TestPlayer player, String aliasName) { + MageItem item = findAliasObject(game, player, aliasName); + if (item == null) { + return aliasName + " [not exists]"; + } + + if (item instanceof MageObject) { + Zone zone = game.getState().getZone(item.getId()); + return aliasName + " - " + ((MageObject) item).getIdName() + " - " + (zone != null ? zone.toString() : "null"); + } + + if (item instanceof Player) { + return aliasName + " - " + ((Player) item).getName(); + } + + return aliasName + " [unknown object " + item.getId() + "]"; + } + + private void printAliases(Game game, TestPlayer player) { + System.out.println("Total aliases: " + player.getAliases().size()); + + List data = player.getAliases().entrySet().stream() + .map(entry -> (getAliasInfo(game, player, entry.getKey()))) + .sorted() + .collect(Collectors.toList()); + + for (String s : data) { + System.out.println(s); + } + } + private void assertPT(PlayerAction action, Game game, Player player, String permanentName, int Power, int Toughness) { Permanent perm = findPermanentWithAssert(action, game, player, permanentName); @@ -891,6 +1000,36 @@ public class TestPlayer implements Player { } } + private MageItem findAliasObject(Game game, TestPlayer player, String aliasName) { + UUID objectId = player.getAliasByName(aliasName); + if (objectId == null) { + return null; + } + + MageObject itemObject = game.getObject(objectId); + if (itemObject != null) { + return itemObject; + } + + Player itemPlayer = game.getPlayer(objectId); + if (itemPlayer != null) { + return itemPlayer; + } + + return null; + } + + private void assertAliasZone(PlayerAction action, Game game, TestPlayer player, String aliasName, Zone needZone, boolean mustHave) { + MageItem item = findAliasObject(game, player, aliasName); + Zone currentZone = (item == null ? null : game.getState().getZone(item.getId())); + + if (mustHave) { + Assert.assertEquals(action.getActionName() + " - alias " + aliasName + " must have zone " + needZone.toString(), needZone, currentZone); + } else { + Assert.assertNotEquals(action.getActionName() + " - alias " + aliasName + " must have not zone " + needZone.toString(), needZone, currentZone); + } + } + private void assertManaPoolInner(PlayerAction action, Player player, ManaType manaType, Integer amount) { Integer current = player.getManaPool().get(manaType); Assert.assertEquals(action.getActionName() + " - mana pool must contain [" + amount.toString() + " " + manaType.toString() + "], but found [" + current.toString() + "]", amount, current); @@ -1785,6 +1924,8 @@ public class TestPlayer implements Player { this.choices.addAll(((TestPlayer) player).choices); this.targets.clear(); this.targets.addAll(((TestPlayer) player).targets); + this.aliases.clear(); + this.aliases.putAll(((TestPlayer) player).aliases); computerPlayer.restore(player); } @@ -2880,7 +3021,7 @@ public class TestPlayer implements Player { boolean founded = false; String foundedRecord = ""; CheckTargets: - for(String targetRecord : targets) { + for (String targetRecord : targets) { String[] choiceSettings = targetRecord.split("\\^"); if (choiceSettings.length == 2 && choiceSettings[1].startsWith("X=")) { // can choice @@ -2890,7 +3031,7 @@ public class TestPlayer implements Player { Assert.assertNotEquals("choice amount must be not zero", 0, choiceAmount); Assert.assertTrue("choice amount " + choiceAmount + "must be <= remaining " + target.getAmountRemaining(), choiceAmount <= target.getAmountRemaining()); - for(UUID possibleTarget : target.possibleTargets(source.getSourceId(), source.getControllerId(), game)) { + for (UUID possibleTarget : target.possibleTargets(source.getSourceId(), source.getControllerId(), game)) { MageObject objectPermanent = game.getObject(possibleTarget); Player objectPlayer = game.getPlayer(possibleTarget); String objectName = objectPermanent != null ? objectPermanent.getName() : objectPlayer.getName(); diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java index e86e9ee26c..03f6a38ce1 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/impl/CardTestPlayerAPIImpl.java @@ -60,6 +60,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public static final String CHECK_COMMAND_COLOR = "COLOR"; public static final String CHECK_COMMAND_SUBTYPE = "SUBTYPE"; public static final String CHECK_COMMAND_MANA_POOL = "MANA_POOL"; + public static final String CHECK_COMMAND_ALIAS_ZONE = "ALIAS_ZONE"; // TODO: add target player param to commands public static final String SHOW_COMMAND_LIBRARY = "LIBRARY"; @@ -68,6 +69,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement public static final String SHOW_COMMAND_GRAVEYEARD = "GRAVEYARD"; public static final String SHOW_COMMAND_EXILE = "EXILE"; public static final String SHOW_COMMAND_AVAILABLE_ABILITIES = "AVAILABLE_ABILITIES"; + public static final String SHOW_COMMAND_ALIASES = "ALIASES"; + + // TODO: add target player param to commands + public static final String ALIAS_COMMAND_ADD = "ADD"; protected GameOptions gameOptions; @@ -301,6 +306,14 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement check(checkName, turnNum, step, player, CHECK_COMMAND_MANA_POOL, colors, amount.toString()); } + public void checkAliasZone(String checkName, int turnNum, PhaseStep step, TestPlayer player, String alias, Zone zone) { + checkAliasZone(checkName, turnNum, step, player, alias, zone, true); + } + + public void checkAliasZone(String checkName, int turnNum, PhaseStep step, TestPlayer player, String alias, Zone zone, Boolean mustHave) { + check(checkName, turnNum, step, player, CHECK_COMMAND_ALIAS_ZONE, alias, zone.toString(), mustHave.toString()); + } + // show commands private void show(String showName, int turnNum, PhaseStep step, TestPlayer player, String command, String... params) { @@ -335,6 +348,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement show(showName, turnNum, step, player, SHOW_COMMAND_AVAILABLE_ABILITIES); } + public void showAliases(String showName, int turnNum, PhaseStep step, TestPlayer player) { + show(showName, turnNum, step, player, SHOW_COMMAND_ALIASES); + } + /** * Removes all cards from player's library from the game. Usually this * should be used once before initialization to form the library in certain @@ -399,6 +416,19 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement @Override public void addCard(Zone gameZone, TestPlayer player, String cardName, int count, boolean tapped) { + // aliases for mage objects + String aliasName = ""; + boolean useAliasMultiNames = (count != 1); + if (cardName.contains("@")) { + aliasName = cardName.substring(cardName.indexOf("@") + 1); + cardName = cardName.substring(0, cardName.indexOf("@")); + } + + // one card = one aliase, massive adds can use auto-name + if (!useAliasMultiNames && !aliasName.isEmpty() && player.getAliasByName(aliasName) != null) { + Assert.fail("Can't add card " + cardName + " - alias " + aliasName + " already exists for " + player.getName()); + } + if (gameZone == Zone.BATTLEFIELD) { for (int i = 0; i < count; i++) { CardInfo cardInfo = CardRepository.instance.findCard(cardName); @@ -409,6 +439,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement PermanentCard p = new PermanentCard(card.copy(), player.getId(), currentGame); p.setTapped(tapped); getBattlefieldCards(player).add(p); + + if (!aliasName.isEmpty()) { + player.addAlias(player.generateAliasName(aliasName, useAliasMultiNames, i + 1), p.getId()); + } } } else { if (tapped) { @@ -422,6 +456,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement throw new AssertionError("Couldn't find a card: " + cardName); } cards.add(card); + + if (!aliasName.isEmpty()) { + player.addAlias(player.generateAliasName(aliasName, useAliasMultiNames, i + 1), card.getId()); + } } } } @@ -1246,7 +1284,6 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement } public enum StackClause { - WHILE_ON_STACK, WHILE_COPY_ON_STACK, WHILE_NOT_ON_STACK diff --git a/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java b/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java new file mode 100644 index 0000000000..0085722167 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/testapi/TestAliases.java @@ -0,0 +1,92 @@ +package org.mage.test.testapi; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * @author JayDi85 + */ + +public class TestAliases extends CardTestPlayerBase { + + @Test + public void test_DifferentZones() { + addCard(Zone.LIBRARY, playerA, "Swamp@lib", 1); + addCard(Zone.HAND, playerA, "Swamp@hand", 1); + addCard(Zone.BATTLEFIELD, playerA, "Swamp@battle", 1); + addCard(Zone.GRAVEYARD, playerA, "Swamp@grave", 1); + + showAliases("A aliases", 1, PhaseStep.UPKEEP, playerA); + checkAliasZone("lib", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "lib", Zone.LIBRARY); + checkAliasZone("hand", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "hand", Zone.HAND); + checkAliasZone("battle", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "battle", Zone.BATTLEFIELD); + checkAliasZone("grave", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "grave", Zone.GRAVEYARD); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_MultipleNames() { + addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5); + addCard(Zone.BATTLEFIELD, playerA, "Island@isl", 5); + addCard(Zone.BATTLEFIELD, playerB, "Plains", 5); + addCard(Zone.BATTLEFIELD, playerB, "Mountain@mnt", 5); + + checkPermanentCount("Swamp must exists", 1, PhaseStep.UPKEEP, playerA, "Swamp", 5); + checkPermanentCount("Island must exists", 1, PhaseStep.UPKEEP, playerA, "Island", 5); + checkPermanentCount("Plains must exists", 1, PhaseStep.UPKEEP, playerB, "Plains", 5); + checkPermanentCount("Mountain must exists", 1, PhaseStep.UPKEEP, playerB, "Mountain", 5); + // + showAliases("A aliases", 1, PhaseStep.UPKEEP, playerA); + showAliases("B aliases", 1, PhaseStep.UPKEEP, playerB); + // A + checkAliasZone("Swamp must not", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Swamp", Zone.BATTLEFIELD, false); + checkAliasZone("Swamp.1 must not", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Swamp.1", Zone.BATTLEFIELD, false); + checkAliasZone("Island must not", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Island", Zone.BATTLEFIELD, false); + checkAliasZone("isl must not", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "isl", Zone.BATTLEFIELD, false); + checkAliasZone("isl.1 must", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "isl.1", Zone.BATTLEFIELD, true); + checkAliasZone("isl.2 must", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "isl.2", Zone.BATTLEFIELD, true); + checkAliasZone("isl.5 must", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "isl.5", Zone.BATTLEFIELD, true); + // B + checkAliasZone("Plains must not", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Plains", Zone.BATTLEFIELD, false); + checkAliasZone("Plains.1 must not", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Plains.1", Zone.BATTLEFIELD, false); + checkAliasZone("Plains must not", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Plains", Zone.BATTLEFIELD, false); + checkAliasZone("mnt must not", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "mnt", Zone.BATTLEFIELD, false); + checkAliasZone("mnt.1 must", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "mnt.1", Zone.BATTLEFIELD, true); + checkAliasZone("mnt.2 must", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "mnt.2", Zone.BATTLEFIELD, true); + checkAliasZone("mnt.5 must", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "mnt.5", Zone.BATTLEFIELD, true); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + } + + @Test + public void test_CastTarget() { + addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion@lion", 5); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5); + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "@lion.1"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "@lion.3"); + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt", "@lion.5"); + + showAliases("A aliases", 1, PhaseStep.POSTCOMBAT_MAIN, playerA); + checkAliasZone("1", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "lion.1", Zone.BATTLEFIELD, false); + checkAliasZone("2", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "lion.2", Zone.BATTLEFIELD, true); + checkAliasZone("3", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "lion.3", Zone.BATTLEFIELD, false); + checkAliasZone("4", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "lion.4", Zone.BATTLEFIELD, true); + checkAliasZone("5", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "lion.5", Zone.BATTLEFIELD, false); + + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertGraveyardCount(playerA, "Lightning Bolt", 3); + assertGraveyardCount(playerA, "Silvercoat Lion", 3); + } +} \ No newline at end of file