From d6c3ef33f07ea074493f665ef7220f33eab6f9a5 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Sun, 25 Apr 2021 22:19:00 -0400 Subject: [PATCH] [STX] added booster collation --- .../mage/sets/StrixhavenSchoolOfMages.java | 186 +++++++++++++++++- .../mage/test/sets/BoosterGenerationTest.java | 62 ++++++ .../main/java/mage/cards/ExpansionSet.java | 16 +- 3 files changed, 253 insertions(+), 11 deletions(-) diff --git a/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java b/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java index b3408f3450c..57813151db6 100644 --- a/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java +++ b/Mage.Sets/src/mage/sets/StrixhavenSchoolOfMages.java @@ -2,12 +2,19 @@ package mage.sets; import mage.cards.Card; import mage.cards.ExpansionSet; +import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; +import mage.collation.BoosterCollator; +import mage.collation.BoosterStructure; +import mage.collation.CardRun; +import mage.collation.RarityConfiguration; import mage.constants.Rarity; import mage.constants.SetType; import mage.constants.SubType; import mage.util.RandomUtil; +import java.util.ArrayList; import java.util.List; /** @@ -22,7 +29,7 @@ public final class StrixhavenSchoolOfMages extends ExpansionSet { } private StrixhavenSchoolOfMages() { - super("Strixhaven: School of Mages", "STX", ExpansionSet.buildDate(2021, 4, 23), SetType.EXPANSION); + super("Strixhaven: School of Mages", "STX", ExpansionSet.buildDate(2021, 4, 23), SetType.EXPANSION, new StrixhavenSchoolOfMagesCollator()); this.blockName = "Strixhaven: School of Mages"; this.hasBoosters = true; this.hasBasicLands = true; @@ -441,10 +448,10 @@ public final class StrixhavenSchoolOfMages extends ExpansionSet { private void addLesson(List booster) { // Boosters have one Lesson card final Rarity rarity; - int i = RandomUtil.nextInt(15); - if (i == 14) { // TODO: change this when correct ratio is known + int i = RandomUtil.nextInt(121); + if (i < 2) { rarity = Rarity.MYTHIC; - } else if (i >= 10) { + } else if (i < 22) { rarity = Rarity.RARE; } else { rarity = Rarity.COMMON; @@ -462,4 +469,175 @@ public final class StrixhavenSchoolOfMages extends ExpansionSet { } return cards; } + + @Override + protected void generateBoosterMap() { + super.generateBoosterMap(); + CardRepository + .instance + .findCards(new CardCriteria().setCodes("STA")) + .stream() + .forEach(cardInfo -> inBoosterMap.put("STA_" + cardInfo.getCardNumber(), cardInfo)); + } +} + +// Booster collation info from https://www.lethe.xyz/mtg/collation/stx.html +// Using Belgian collation plus other info inferred from various sources +class StrixhavenSchoolOfMagesCollator implements BoosterCollator { + + private static class StrixhavenSchoolOfMagesRun extends CardRun { + private static final StrixhavenSchoolOfMagesRun commonA = new StrixhavenSchoolOfMagesRun(true, "249", "206", "182", "226", "237", "255", "210", "209", "239", "226", "251", "185", "219", "243", "206", "164", "215", "238", "256", "184", "239", "208", "254", "237", "185", "223", "251", "206", "166", "182", "164", "238", "204", "233", "184", "210", "182", "252", "243", "254", "208", "194", "255", "243", "249", "201", "204", "194", "235", "239", "166", "251", "223", "252", "237", "208", "233", "241", "219", "255", "201", "194", "164", "252", "170", "223", "241", "215", "166", "249", "237", "238", "184", "210", "243", "209", "235", "252", "204", "210", "185", "233", "249", "256", "239", "235", "209", "170", "201", "226", "241", "215", "206", "256", "208", "254", "185", "251", "226", "170", "184", "256", "235", "233", "209", "238", "254", "219", "201", "241", "182", "164", "194", "223", "170", "204", "166", "219", "215", "255"); + private static final StrixhavenSchoolOfMagesRun commonB = new StrixhavenSchoolOfMagesRun(true, "79", "9", "111", "136", "52", "85", "12", "118", "131", "40", "90", "34", "117", "141", "60", "69", "30", "93", "140", "36", "74", "16", "97", "143", "40", "87", "11", "99", "121", "39", "75", "30", "106", "122", "38", "79", "22", "97", "131", "51", "90", "23", "112", "145", "50", "87", "18", "103", "124", "43", "68", "34", "102", "142", "49", "77", "16", "93", "140", "52", "73", "32", "116", "144", "60", "63", "22", "109", "124", "55", "69", "9", "106", "143", "39", "73", "23", "116", "142", "61", "84", "8", "99", "122", "43", "74", "32", "117", "145", "36", "75", "19", "112", "141", "61", "77", "8", "109", "144", "50", "84", "18", "118", "137", "55", "68", "12", "111", "121", "38", "63", "19", "103", "136", "51", "85", "11", "102", "137", "49"); + private static final StrixhavenSchoolOfMagesRun commonC = new StrixhavenSchoolOfMagesRun(true, "263", "268", "270", "271", "273", "275"); + private static final StrixhavenSchoolOfMagesRun uncommonA = new StrixhavenSchoolOfMagesRun(true, "257", "65", "56", "132", "92", "125", "72", "62", "104", "89", "123", "54", "10", "135", "114", "53", "105", "188", "45", "115", "47", "70", "100", "129", "88", "260", "46", "76", "257", "110", "65", "13", "139", "78", "92", "26", "72", "15", "56", "89", "134", "28", "70", "91", "132", "105", "31", "123", "88", "104", "135", "54", "115", "25", "76", "13", "62", "125", "35", "188", "65", "260", "10", "114", "129", "47", "100", "15", "257", "53", "26", "110", "139", "28", "134", "56", "35", "45", "91", "72", "10", "31", "132", "54", "89", "15", "78", "46", "123", "25", "92", "135", "104", "70", "125", "105", "260", "62", "188", "129", "114", "88", "13", "53", "26", "115", "47", "76", "28", "139", "100", "35", "91", "45", "78", "134", "31", "46", "25", "110"); + private static final StrixhavenSchoolOfMagesRun uncommonB = new StrixhavenSchoolOfMagesRun(true, "229", "218", "216", "222", "212", "198", "71", "177", "202", "138", "258", "162", "81", "175", "213", "224", "220", "176", "227", "107", "247", "186", "169", "216", "261", "197", "242", "126", "262", "198", "24", "225", "207", "229", "190", "202", "81", "178", "200", "193", "41", "162", "71", "171", "59", "212", "218", "222", "227", "138", "231", "220", "258", "175", "169", "186", "107", "193", "250", "197", "176", "171", "126", "261", "247", "227", "213", "177", "262", "207", "190", "224", "225", "24", "216", "200", "242", "71", "178", "41", "212", "59", "198", "81", "231", "220", "229", "218", "202", "169", "175", "222", "193", "197", "250", "213", "186", "126", "177", "190", "258", "138", "162", "107", "261", "224", "200", "176", "178", "24", "247", "262", "207", "171", "225", "59", "231", "242", "41", "250"); + private static final StrixhavenSchoolOfMagesRun rareA = new StrixhavenSchoolOfMagesRun(false, "6", "14", "17", "20", "27", "29", "33", "37", "42", "44", "48", "58", "64", "66", "80", "82", "86", "94", "96", "98", "101", "113", "119", "127", "130", "133", "146", "147", "150", "152", "154", "155", "157", "158", "159", "160", "161", "165", "172", "173", "174", "179", "180", "181", "199", "205", "214", "217", "221", "228", "232", "234", "244", "246", "248", "253", "259", "264", "265", "266", "267", "269", "272", "274", "6", "14", "17", "20", "27", "29", "33", "37", "42", "44", "48", "58", "64", "66", "80", "82", "86", "94", "96", "98", "101", "113", "119", "127", "130", "133", "146", "147", "150", "152", "154", "155", "157", "158", "159", "160", "161", "165", "172", "173", "174", "179", "180", "181", "199", "205", "214", "217", "221", "228", "232", "234", "244", "246", "248", "253", "259", "264", "265", "266", "267", "269", "272", "274", "21", "83", "95", "128", "148", "149", "151", "153", "156", "163", "167", "168", "189", "191", "192", "196", "203", "230", "240", "245"); + private static final StrixhavenSchoolOfMagesRun commonLesson = new StrixhavenSchoolOfMagesRun(false, "1", "2", "3", "4", "183", "187", "195", "211", "236"); + private static final StrixhavenSchoolOfMagesRun rareLesson = new StrixhavenSchoolOfMagesRun(false, "5", "7", "57", "67", "108", "120", "7", "57", "67", "108", "120"); + private static final StrixhavenSchoolOfMagesRun uncommonArchive = new StrixhavenSchoolOfMagesRun(false, "3", "4", "9", "18", "19", "20", "23", "24", "29", "30", "35", "37", "41", "44", "46", "49", "51", "57"); + private static final StrixhavenSchoolOfMagesRun rareArchive = new StrixhavenSchoolOfMagesRun(false, "5", "6", "7", "8", "10", "13", "14", "15", "16", "21", "26", "28", "31", "32", "34", "38", "39", "42", "45", "47", "48", "52", "53", "56", "58", "59", "60", "61", "62", "63"); + private static final StrixhavenSchoolOfMagesRun mythicArchive = new StrixhavenSchoolOfMagesRun(false, "1", "2", "11", "12", "17", "22", "25", "27", "33", "36", "40", "43", "50", "54", "55"); + + private StrixhavenSchoolOfMagesRun(boolean keepOrder, String... numbers) { + super(keepOrder, numbers); + } + } + + private static class StrixhavenSchoolOfMagesStructure extends BoosterStructure { + private static final StrixhavenSchoolOfMagesStructure C1 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonC, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB + ); + private static final StrixhavenSchoolOfMagesStructure C2 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB + ); + private static final StrixhavenSchoolOfMagesStructure C3 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonC, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB + ); + private static final StrixhavenSchoolOfMagesStructure C4 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonA, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB, + StrixhavenSchoolOfMagesRun.commonB + ); + private static final StrixhavenSchoolOfMagesStructure U1 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.uncommonA, + StrixhavenSchoolOfMagesRun.uncommonB, + StrixhavenSchoolOfMagesRun.uncommonB + ); + private static final StrixhavenSchoolOfMagesStructure U2 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.uncommonA, + StrixhavenSchoolOfMagesRun.uncommonA, + StrixhavenSchoolOfMagesRun.uncommonB + ); + private static final StrixhavenSchoolOfMagesStructure R1 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.rareA + ); + private static final StrixhavenSchoolOfMagesStructure L1 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.commonLesson + ); + private static final StrixhavenSchoolOfMagesStructure L2 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.rareLesson + ); + private static final StrixhavenSchoolOfMagesStructure A1 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.uncommonArchive + ); + private static final StrixhavenSchoolOfMagesStructure A2 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.rareArchive + ); + private static final StrixhavenSchoolOfMagesStructure A3 = new StrixhavenSchoolOfMagesStructure( + StrixhavenSchoolOfMagesRun.mythicArchive + ); + + private StrixhavenSchoolOfMagesStructure(CardRun... runs) { + super(runs); + } + } + + private final RarityConfiguration commonRuns = new RarityConfiguration( + false, + StrixhavenSchoolOfMagesStructure.C1, + StrixhavenSchoolOfMagesStructure.C2, + StrixhavenSchoolOfMagesStructure.C3, + StrixhavenSchoolOfMagesStructure.C4 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + StrixhavenSchoolOfMagesStructure.U1, + StrixhavenSchoolOfMagesStructure.U2 + ); + private final RarityConfiguration rareRuns = new RarityConfiguration( + StrixhavenSchoolOfMagesStructure.R1 + ); + private final RarityConfiguration lessonRuns = new RarityConfiguration( + false, + StrixhavenSchoolOfMagesStructure.L1, StrixhavenSchoolOfMagesStructure.L1, + StrixhavenSchoolOfMagesStructure.L1, StrixhavenSchoolOfMagesStructure.L1, + StrixhavenSchoolOfMagesStructure.L1, StrixhavenSchoolOfMagesStructure.L1, + StrixhavenSchoolOfMagesStructure.L1, StrixhavenSchoolOfMagesStructure.L1, + StrixhavenSchoolOfMagesStructure.L1, StrixhavenSchoolOfMagesStructure.L1, + StrixhavenSchoolOfMagesStructure.L1, + + StrixhavenSchoolOfMagesStructure.L2, StrixhavenSchoolOfMagesStructure.L2 + ); + private final RarityConfiguration archiveRuns = new RarityConfiguration( + false, + StrixhavenSchoolOfMagesStructure.A1, StrixhavenSchoolOfMagesStructure.A1, + StrixhavenSchoolOfMagesStructure.A1, StrixhavenSchoolOfMagesStructure.A1, + StrixhavenSchoolOfMagesStructure.A1, StrixhavenSchoolOfMagesStructure.A1, + StrixhavenSchoolOfMagesStructure.A1, StrixhavenSchoolOfMagesStructure.A1, + StrixhavenSchoolOfMagesStructure.A1, StrixhavenSchoolOfMagesStructure.A1, + + StrixhavenSchoolOfMagesStructure.A2, StrixhavenSchoolOfMagesStructure.A2, + StrixhavenSchoolOfMagesStructure.A2, StrixhavenSchoolOfMagesStructure.A2, + + StrixhavenSchoolOfMagesStructure.A3 + ); + + @Override + public void shuffle() { + commonRuns.shuffle(); + uncommonRuns.shuffle(); + rareRuns.shuffle(); + lessonRuns.shuffle(); + archiveRuns.shuffle(); + } + + @Override + public List makeBooster() { + List booster = new ArrayList<>(); + booster.addAll(commonRuns.getNext().makeRun()); + booster.addAll(uncommonRuns.getNext().makeRun()); + booster.addAll(rareRuns.getNext().makeRun()); + booster.addAll(lessonRuns.getNext().makeRun()); + archiveRuns.getNext().makeRun().stream().map(s -> "STA_" + s).forEach(booster::add); + return booster; + } } diff --git a/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java b/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java index 4a77a8a6ffe..6960f9a01b9 100644 --- a/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/sets/BoosterGenerationTest.java @@ -9,6 +9,7 @@ import mage.cards.repository.CardInfo; import mage.cards.repository.CardScanner; import mage.constants.CardType; import mage.constants.Rarity; +import mage.constants.SubType; import mage.sets.*; import org.junit.Assert; import org.junit.Before; @@ -444,4 +445,65 @@ public class BoosterGenerationTest extends MageTestBase { ); } } + + @Test + public void testStrixhavenSchoolOfMages_LessonsAndArchive() { + boolean foundUncommonLesson = false; + boolean foundNoUncommonLesson = false; + + for (int i = 1; i <= 100; i++) { + List booster = StrixhavenSchoolOfMages.getInstance().createBooster(); + List nonLessons = booster + .stream() + .filter(c -> "STX".equals(c.getExpansionSetCode())) + .filter(c -> !c.hasSubtype(SubType.LESSON, null)) + .collect(Collectors.toList()); + List lessons = booster + .stream() + .filter(c -> "STX".equals(c.getExpansionSetCode())) + .filter(c -> c.hasSubtype(SubType.LESSON, null)) + .collect(Collectors.toList()); + + assertEquals("Booster should have 15 cards", 15, booster.size()); + + assertFalse( + "Booster should have no basic lands:" + str(booster), + contains(booster, basics, null) + ); + + assertEquals( + "Booster should have 9 non-Lesson commons", 9, + nonLessons.stream().map(Card::getRarity).filter(Rarity.COMMON::equals).count() + ); + assertEquals( + "Booster should have 3 uncommons", 3, + booster.stream().filter(c -> "STX".equals(c.getExpansionSetCode())).map(Card::getRarity).filter(Rarity.UNCOMMON::equals).count() + ); + assertEquals( + "Booster should have 1 non-Lesson rare/mythic", 1, + nonLessons.stream().map(Card::getRarity).filter(r -> r == Rarity.RARE || r == Rarity.MYTHIC).count() + ); + + assertEquals( + "Booster should have 1 Mystical Archive card", 1, + booster.stream().map(Card::getExpansionSetCode).filter("STA"::equals).count() + ); + + assertTrue("Booster should have no more than 2 total Lessons", lessons.size() <= 2); + assertEquals( + "Booster should have 1 non-uncommon Lesson", 1, + lessons.stream().filter(c -> c.getRarity() != Rarity.UNCOMMON).count() + ); + long uncommonLessonCount = lessons.stream().filter(c -> c.getRarity() == Rarity.UNCOMMON).count(); + assertTrue("Booster should have no more than 1 uncommon Lesson", uncommonLessonCount <= 1); + + foundUncommonLesson |= uncommonLessonCount > 0; + foundNoUncommonLesson |= uncommonLessonCount == 0; + if (foundUncommonLesson && foundNoUncommonLesson && i > 20) { + break; + } + } + assertTrue("No booster contained an uncommon Lesson", foundUncommonLesson); + assertTrue("Every booster contained an uncommon Lesson", foundNoUncommonLesson); + } } diff --git a/Mage/src/main/java/mage/cards/ExpansionSet.java b/Mage/src/main/java/mage/cards/ExpansionSet.java index 6e3102fac73..2803abf7b8d 100644 --- a/Mage/src/main/java/mage/cards/ExpansionSet.java +++ b/Mage/src/main/java/mage/cards/ExpansionSet.java @@ -284,13 +284,7 @@ public abstract class ExpansionSet implements Serializable { private List createBoosterUsingCollator() { if (inBoosterMap.isEmpty()) { - CardCriteria criteria = new CardCriteria(); - criteria.setCodes(code); - CardRepository - .instance - .findCards(criteria) - .stream() - .forEach(cardInfo -> inBoosterMap.put(cardInfo.getCardNumber(), cardInfo)); + generateBoosterMap(); } return boosterCollator .makeBooster() @@ -300,6 +294,14 @@ public abstract class ExpansionSet implements Serializable { .collect(Collectors.toList()); } + protected void generateBoosterMap() { + CardRepository + .instance + .findCards(new CardCriteria().setCodes(code)) + .stream() + .forEach(cardInfo -> inBoosterMap.put(cardInfo.getCardNumber(), cardInfo)); + } + protected boolean boosterIsValid(List booster) { if (validateBoosterColors) { if (!validateColors(booster)) {