Skip to content

Commit

Permalink
tests: added tests for auto-lands suggestion in deck editor and tourn…
Browse files Browse the repository at this point in the history
…ey (related to #13127)
  • Loading branch information
JayDi85 committed Dec 16, 2024
1 parent 40e2cf7 commit 2955535
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public void showDialog(Deck deck, DeckEditorMode mode, AddLandCallback callback)
landSetNames.add(expansionInfo.getName());
}
if (landSetNames.isEmpty()) {
throw new IllegalArgumentException("No set with basic land was found");
throw new IllegalArgumentException("No set with basic land was found (possible memory problems, need client restart)");
}
if (landSetNames.size() > 1) {
landSetNames.add("<Random lands>");
Expand Down Expand Up @@ -477,6 +477,7 @@ private void btnSetFastSearchActionPerformed(java.awt.event.ActionEvent evt) {//
}//GEN-LAST:event_btnSetFastSearchActionPerformed

private void autoAddLands() {
// suggest lands amount for deck without lands
int deckSize = ((Number) spnDeckSize.getValue()).intValue();
int[] lands = DeckBuildUtils.landCountSuggestion(deckSize, deck.getMaindeckCards());
spnPlains.setValue(lands[0]);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
package org.mage.test.serverside.deck;

import mage.cards.decks.Deck;
import mage.cards.decks.DeckCardInfo;
import mage.cards.decks.DeckCardLists;
import mage.game.GameException;
import mage.util.DeckBuildUtils;
import mage.util.TournamentUtil;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.MageTestPlayerBase;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
* Testing lands suggestion and adding. It used in:
* - client side: for auto-lands suggest button
* - server side: for AI and invalid deck's timeout
*
* @author JayDi85
*/
public class DeckAutoLandsTest extends MageTestPlayerBase {

@Test
public void test_LandsSuggest_OneColor() {
Deck deck = prepareDeck(Arrays.asList(
new DeckCardInfo("Grizzly Bears", "129", "S99", 40)
));
assertSuggestedLands("one color", deck, 60, 0, 0, 0, 0, 20);
}

@Test
public void test_LandsSuggest_TwoColors() {
Deck deck = prepareDeck(Arrays.asList(
new DeckCardInfo("Arbalest Engineers", "206", "BRO", 40)
));
assertSuggestedLands("two colors", deck, 60, 0, 0, 0, 10, 10);
}

@Test
public void test_LandsSuggest_ThreeColors() {
Deck deck = prepareDeck(Arrays.asList(
new DeckCardInfo("Adun Oakenshield", "216", "LEG", 40)
));
assertSuggestedLands("tree colors", deck, 60, 0, 0, 7, 7, 6);
}

@Test
public void test_LandsSuggest_FiveColors() {
Deck deck = prepareDeck(Arrays.asList(
new DeckCardInfo("Atogatog", "286", "ODY", 40)
));
assertSuggestedLands("five colors", deck, 60, 4, 4, 4, 4, 4);
}

@Test
public void test_LandsSuggest_NoColors() {
Deck deck = prepareDeck(Arrays.asList(
new DeckCardInfo("Abstruse Archaic", "712", "CMM", 40)
));
assertSuggestedLands("no colors", deck, 60, 4, 4, 4, 4, 4);
}

@Test
public void test_LandsSuggest_NoNeedLands() {
Deck deck = prepareDeck(Arrays.asList(
new DeckCardInfo("Grizzly Bears", "129", "S99", 40)
));
assertSuggestedLands("no need lands - 39", deck, 39, 0, 0, 0, 0, 0);
assertSuggestedLands("no need lands - 40", deck, 40, 0, 0, 0, 0, 0);
assertSuggestedLands("no need lands - 41", deck, 41, 0, 0, 0, 0, 1);
}

private Deck prepareDeck(List<DeckCardInfo> cards) {
DeckCardLists source = new DeckCardLists();
source.getCards().addAll(cards);
Deck deck = null;
try {
deck = Deck.load(source, true);
} catch (GameException e) {
Assert.fail("Can't prepare deck: " + cards);
}
return deck;
}

private void assertSuggestedLands(
String info,
Deck deck,
int needTotal,
int needPlains,
int needIslands,
int needSwamps,
int needMountains,
int needForests) {
int[] lands = DeckBuildUtils.landCountSuggestion(needTotal, deck.getMaindeckCards());
List<Integer> current = new ArrayList<>(Arrays.asList(lands[0], lands[1], lands[2], lands[3], lands[4]));
List<Integer> need = new ArrayList<>(Arrays.asList(needPlains, needIslands, needSwamps, needMountains, needForests));
Assert.assertTrue(info + " - wrong deck size", deck.getMaindeckCards().size() + current.stream().mapToInt(x -> x).sum() >= needTotal);
Assert.assertEquals(info + " - wrong lands count (WUBRG)", need, current);
}

@Test
public void test_PossibleSets_OneCompatibleSet() {
Deck deck = prepareDeck(Arrays.asList(
new DeckCardInfo("Grizzly Bears", "129", "S99", 1)
));
assertPossibleSets("one compatible set", deck, Arrays.asList("S99"));
}

@Test
public void test_PossibleSets_MakeSureNoSnowLands() {
Deck deck = prepareDeck(Arrays.asList(
new DeckCardInfo("Grizzly Bears", "129", "S99", 1),
new DeckCardInfo("Abominable Treefolk", "194", "MH1", 1) // MH1 with snow lands
));
assertPossibleSets("no snow lands", deck, Arrays.asList("S99"));
}

@Test
public void test_PossibleSets_MultipleCompatibleSets() {
Deck deck = prepareDeck(Arrays.asList(
new DeckCardInfo("Grizzly Bears", "129", "S99", 1),
new DeckCardInfo("Akki Raider", "92", "BOK", 1), // BOK without lands, but with boosters
new DeckCardInfo("Grizzly Bears", "169", "POR", 1),
new DeckCardInfo("Aggravated Assault", "25", "MP2", 1) // MP2 without lands and boosters
));
assertPossibleSets("multiple compatible sets", deck, Arrays.asList("POR", "S99"));
}

@Test
public void test_PossibleSets_CompatibleBlocks() {
// BOK from Kamigawa block, so it must look at:
// * CHK - Champions of Kamigawa - has lands
// * SOK - Saviors of Kamigawa - no lands
// * BOK - Betrayers of Kamigawa - no lands
Deck deck = prepareDeck(Arrays.asList(
new DeckCardInfo("Akki Raider", "92", "BOK", 1), // BOK without lands, but with boosters
new DeckCardInfo("Aggravated Assault", "25", "MP2", 1) // MP2 without lands and boosters
));
assertPossibleSets("compatible block", deck, Arrays.asList("CHK"));
}

@Test
public void test_PossibleSets_NoCompatibleSetsOrBlocks() {
Deck deck = prepareDeck(Arrays.asList(
new DeckCardInfo("Amulet of Kroog", "36", "ATQ", 1) // ATQ without lands
));
// must find 2 random sets
List<String> possibleSets1 = TournamentUtil.getLandSetCodeForDeckSets(deck.getExpansionSetCodes()).stream().sorted().collect(Collectors.toList());
List<String> possibleSets2 = TournamentUtil.getLandSetCodeForDeckSets(deck.getExpansionSetCodes()).stream().sorted().collect(Collectors.toList());
Assert.assertEquals("must find 1 random set, try 1", 1, possibleSets1.size());
Assert.assertEquals("must find 1 random set, try 2", 1, possibleSets2.size());
Assert.assertNotEquals("must find random sets, try 3", possibleSets1.get(0), possibleSets2.get(0));
}

private void assertPossibleSets(
String info,
Deck deck,
List<String> needSets) {
List<String> possibleSets = TournamentUtil.getLandSetCodeForDeckSets(deck.getExpansionSetCodes()).stream().sorted().collect(Collectors.toList());
Assert.assertEquals(info + " - wrong possible sets", needSets, possibleSets);
}
}
15 changes: 5 additions & 10 deletions Mage/src/main/java/mage/util/DeckBuildUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@

public final class DeckBuildUtils {

/**
* Returns the number of basic lands suggested to complete a deck
* as an array of five ints: plains, islands, swamps, mountains, forests
* Total number of lands always sufficient to reach deckSize
*/
public static int[] landCountSuggestion(int deckSize, Set<Card> deckList) {
/*
Returns the number of basic lands suggested to complete a deck
as an array of five ints: plains, islands, swamps, mountains, forests
Total number of lands always sufficient to reach deckSize
*/
int plains = 0, islands = 0, swamps = 0, mountains = 0, forests = 0;
int landsNeeded = deckSize - deckList.size();
if (landsNeeded > 0) {
Expand Down Expand Up @@ -51,9 +51,4 @@ public static int[] landCountSuggestion(int deckSize, Set<Card> deckList) {
}
return new int[] {plains, islands, swamps, mountains, forests};
}

// Hide constructor - not to be instantiated
private DeckBuildUtils() {
}

}
17 changes: 7 additions & 10 deletions Mage/src/main/java/mage/util/TournamentUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,22 +15,20 @@ public final class TournamentUtil {
/**
* Tries to calculate the most appropriate sets to add basic lands for cards of a deck
*
* @param setCodesDeck
* @return setCode for lands
* @param setCodesDeck all sets in current deck
*/

public static Set<String> getLandSetCodeForDeckSets(Collection<String> setCodesDeck) {

Set<String> landSetCodes = new HashSet<>();
// decide from which sets basic lands are taken from

// from deck's sets
for (String setCode : setCodesDeck) {
ExpansionInfo expansionInfo = ExpansionRepository.instance.getSetByCode(setCode);
if (expansionInfo.hasBasicLands() && !CardRepository.haveSnowLands(setCode)) {
landSetCodes.add(expansionInfo.getCode());
}
}

// if sets have no basic land, take land from block
// from deck's blocks
if (landSetCodes.isEmpty()) {
for (String setCode : setCodesDeck) {
ExpansionInfo expansionInfo = ExpansionRepository.instance.getSetByCode(setCode);
Expand All @@ -42,10 +40,9 @@ public static Set<String> getLandSetCodeForDeckSets(Collection<String> setCodesD
}
}
}
// if still no set with lands found, take one by random

// from random
if (landSetCodes.isEmpty()) {
// if sets have no basic lands and also it has no parent or parent has no lands get last set with lands
// select a set with basic lands by random
List<ExpansionInfo> basicLandSets = ExpansionRepository.instance.getSetsWithBasicLandsByReleaseDate()
.stream()
.filter(exp -> !CardRepository.haveSnowLands(exp.getCode()))
Expand All @@ -56,7 +53,7 @@ public static Set<String> getLandSetCodeForDeckSets(Collection<String> setCodesD
}

if (landSetCodes.isEmpty()) {
throw new IllegalArgumentException("No set with basic land was found");
throw new IllegalArgumentException("No set with basic land was found (possible memory problems, need server restart)");
}
return landSetCodes;
}
Expand Down

0 comments on commit 2955535

Please sign in to comment.