From 8a16eda062588754e1ed993922fc32069f8fd519 Mon Sep 17 00:00:00 2001 From: Evan Kranzler Date: Fri, 12 Feb 2021 17:35:28 -0500 Subject: [PATCH] Implement new way to generate boosters using box mapping info (WIP) (#7529) * [THB] added initial common/uncommon collation mechanism * [THB] added rare/mythic and lands to pack generation * fixed some card names * broke out collation into its own separate classes * built collation into ExpansionSet * added note about collation information * [KHM] added collation info * updated collation to use collector number rather than name * added shuffle to set constructor * added some notes on collation methods --- Mage.Sets/src/mage/sets/Kaldheim.java | 161 ++++++++++++++++- .../src/mage/sets/TherosBeyondDeath.java | 164 +++++++++++++++++- .../mage/test/sets/BoosterGenerationTest.java | 1 + .../main/java/mage/cards/ExpansionSet.java | 40 ++++- .../java/mage/collation/BoosterCollator.java | 13 ++ .../java/mage/collation/BoosterStructure.java | 36 ++++ .../src/main/java/mage/collation/CardRun.java | 11 ++ .../mage/collation/RarityConfiguration.java | 27 +++ .../src/main/java/mage/collation/Rotater.java | 50 ++++++ 9 files changed, 500 insertions(+), 3 deletions(-) create mode 100644 Mage/src/main/java/mage/collation/BoosterCollator.java create mode 100644 Mage/src/main/java/mage/collation/BoosterStructure.java create mode 100644 Mage/src/main/java/mage/collation/CardRun.java create mode 100644 Mage/src/main/java/mage/collation/RarityConfiguration.java create mode 100644 Mage/src/main/java/mage/collation/Rotater.java diff --git a/Mage.Sets/src/mage/sets/Kaldheim.java b/Mage.Sets/src/mage/sets/Kaldheim.java index 60a48378723..f3e38e53abd 100644 --- a/Mage.Sets/src/mage/sets/Kaldheim.java +++ b/Mage.Sets/src/mage/sets/Kaldheim.java @@ -4,6 +4,10 @@ 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.CardType; import mage.constants.Rarity; import mage.constants.SetType; @@ -26,7 +30,7 @@ public final class Kaldheim extends ExpansionSet { private final List savedSpecialLand = new ArrayList<>(); private Kaldheim() { - super("Kaldheim", "KHM", ExpansionSet.buildDate(2021, 2, 5), SetType.EXPANSION); + super("Kaldheim", "KHM", ExpansionSet.buildDate(2021, 2, 5), SetType.EXPANSION, new KaldheimCollator()); this.blockName = "Kaldheim"; this.hasBasicLands = true; this.hasBoosters = true; @@ -489,3 +493,158 @@ public final class Kaldheim extends ExpansionSet { return new ArrayList<>(savedSpecialLand); } } + +// Booster collation info from https://www.lethe.xyz/mtg/collation/khm.html +// Using USA collation for common/uncommon and JP for rare/mythic +class KaldheimCollator implements BoosterCollator { + + private static class KaldheimRun extends CardRun { + private static final KaldheimRun commonA = new KaldheimRun(true, 34, 77, 136, 13, 78, 149, 3, 47, 127, 14, 67, 140, 19, 54, 124, 38, 49, 147, 39, 55, 157, 1, 53, 141, 37, 66, 126, 10, 71, 155, 4, 65, 121, 13, 77, 136, 34, 78, 127, 3, 47, 149, 14, 54, 124, 38, 67, 140, 19, 55, 147, 39, 49, 157, 37, 53, 141, 10, 65, 155, 1, 71, 121, 4, 66, 126); + private static final KaldheimRun commonB = new KaldheimRun(true, 102, 176, 87, 183, 93, 184, 104, 178, 117, 174, 111, 171, 96, 194, 84, 176, 119, 180, 83, 164, 89, 172, 87, 175, 102, 183, 104, 178, 93, 174, 117, 184, 111, 171, 84, 194, 119, 164, 96, 180, 89, 176, 83, 172, 102, 175, 87, 178, 104, 174, 93, 183, 117, 171, 119, 184, 84, 164, 111, 194, 89, 180, 96, 172, 83, 175); + private static final KaldheimRun commonC1 = new KaldheimRun(true, 187, 152, 242, 46, 173, 23, 101, 246, 48, 190, 32, 151, 99, 68, 267, 31, 91, 192, 143, 57, 100, 243, 105, 16, 134, 42, 196, 238, 187, 46, 23, 242, 152, 173, 48, 32, 246, 190, 151, 101, 31, 68, 99, 267, 91, 134, 105, 57, 16, 192, 100, 143, 243, 196, 42); + private static final KaldheimRun commonC2 = new KaldheimRun(true, 11, 193, 95, 158, 17, 239, 44, 159, 129, 7, 118, 85, 138, 74, 165, 11, 129, 193, 150, 72, 5, 95, 159, 74, 158, 17, 85, 239, 118, 138, 44, 7, 238, 193, 150, 165, 5, 72, 158, 95, 11, 44, 159, 239, 129, 17, 85, 74, 7, 118, 5, 150, 165, 138, 72); + private static final KaldheimRun uncommonA = new KaldheimRun(true, 215, 236, 212, 208, 195, 224, 332, 6, 232, 18, 106, 268, 209, 162, 8, 76, 122, 88, 182, 206, 202, 62, 110, 132, 200, 325, 271, 211, 144, 103, 215, 236, 258, 56, 163, 113, 28, 226, 2, 58, 263, 148, 232, 162, 224, 208, 195, 323, 268, 18, 106, 6, 233, 8, 76, 122, 209, 88, 182, 206, 202, 62, 110, 132, 321, 220, 271, 211, 144, 258, 2, 28, 263, 113, 226, 103, 236, 163, 56, 215, 148, 58, 329, 195, 6, 232, 233, 18, 212, 162, 268, 106, 208, 103, 322, 76, 122, 88, 182, 206, 202, 62, 110, 132, 200, 220, 271, 211, 144, 8, 58, 28, 258, 113, 56, 148, 2, 263, 226, 163); + private static final KaldheimRun uncommonB = new KaldheimRun(true, 30, 166, 75, 201, 265, 222, 45, 135, 256, 191, 231, 235, 36, 250, 316, 128, 25, 247, 264, 35, 97, 186, 223, 59, 60, 130, 216, 80, 244, 259, 217, 133, 64, 245, 108, 189, 331, 137, 116, 253, 30, 166, 75, 201, 265, 327, 45, 128, 256, 247, 235, 36, 191, 25, 170, 250, 135, 231, 186, 35, 60, 324, 97, 130, 59, 264, 244, 80, 328, 259, 133, 217, 64, 245, 108, 189, 230, 137, 116, 253, 30, 166, 75, 201, 265, 222, 45, 256, 191, 235, 170, 135, 36, 128, 25, 247, 250, 231, 35, 223, 60, 130, 97, 264, 216, 186, 59, 244, 80, 259, 217, 133, 304, 245, 108, 189, 230, 137, 116, 253); + private static final KaldheimRun rareA = new KaldheimRun(false, 9, 20, 21, 24, 26, 29, 43, 50, 51, 52, 61, 63, 69, 73, 82, 86, 90, 92, 107, 109, 112, 115, 120, 123, 125, 131, 142, 146, 161, 169, 179, 181, 185, 188, 197, 203, 204, 205, 207, 210, 213, 219, 227, 228, 229, 234, 237, 240, 241, 251, 252, 254, 255, 260, 272, 275, 9, 20, 21, 24, 26, 29, 43, 50, 51, 52, 61, 63, 69, 73, 82, 86, 90, 92, 107, 109, 112, 115, 120, 123, 125, 131, 142, 146, 161, 169, 179, 181, 185, 188, 197, 203, 204, 205, 207, 210, 213, 219, 227, 228, 229, 234, 237, 240, 241, 251, 252, 254, 255, 260, 272, 275, 15, 22, 33, 40, 41, 70, 94, 98, 114, 145, 154, 160, 168, 198, 218, 221, 225, 320); + private static final KaldheimRun rareB = new KaldheimRun(false, 9, 20, 300, 24, 26, 301, 43, 303, 51, 52, 61, 63, 69, 73, 82, 86, 90, 306, 107, 109, 307, 309, 310, 311, 125, 131, 312, 146, 161, 315, 317, 318, 185, 188, 319, 203, 204, 205, 207, 210, 213, 219, 227, 330, 229, 234, 237, 240, 241, 290, 291, 292, 255, 293, 272, 275, 9, 20, 300, 24, 26, 301, 43, 303, 51, 52, 61, 63, 69, 73, 82, 86, 90, 306, 107, 109, 307, 309, 310, 311, 125, 131, 312, 146, 161, 315, 317, 318, 185, 188, 319, 203, 204, 205, 207, 210, 213, 219, 227, 330, 229, 234, 237, 240, 241, 290, 291, 292, 255, 293, 272, 275, 299, 22, 294, 302, 295, 305, 94, 296, 308, 297, 313, 298, 314, 287, 288, 326, 289, 320); + private static final KaldheimRun land = new KaldheimRun(true, 276, 277, 283, 249, 278, 285, 248, 276, 285, 279, 261, 269, 257, 249, 248, 283, 270, 285, 277, 282, 284, 270, 278, 248, 279, 269, 281, 274, 280, 279, 257, 281, 284, 277, 257, 274, 273, 279, 276, 262, 266, 284, 281, 273, 282, 278, 262, 280, 279, 274, 262, 282, 283, 278, 262, 279, 261, 285, 273, 266, 283, 261, 280, 284, 266, 278, 270, 285, 282, 280, 276, 277, 273, 278, 269, 273, 249, 261, 274); + + private KaldheimRun(boolean keepOrder, Integer... names) { + super(keepOrder, names); + } + } + + private static class KaldheimStructure extends BoosterStructure { + private static final KaldheimStructure C1 = new KaldheimStructure( + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonB, + KaldheimRun.commonB, + KaldheimRun.commonC1, + KaldheimRun.commonC1, + KaldheimRun.commonC1, + KaldheimRun.commonC1, + KaldheimRun.commonC1, + KaldheimRun.commonC1 + ); + private static final KaldheimStructure C2 = new KaldheimStructure( + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonB, + KaldheimRun.commonB, + KaldheimRun.commonC1, + KaldheimRun.commonC1, + KaldheimRun.commonC1, + KaldheimRun.commonC1, + KaldheimRun.commonC1 + ); + private static final KaldheimStructure C3 = new KaldheimStructure( + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonB, + KaldheimRun.commonB, + KaldheimRun.commonB, + KaldheimRun.commonC2, + KaldheimRun.commonC2, + KaldheimRun.commonC2, + KaldheimRun.commonC2 + ); + private static final KaldheimStructure C4 = new KaldheimStructure( + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonB, + KaldheimRun.commonB, + KaldheimRun.commonB, + KaldheimRun.commonC2, + KaldheimRun.commonC2, + KaldheimRun.commonC2 + ); + private static final KaldheimStructure C5 = new KaldheimStructure( + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonA, + KaldheimRun.commonB, + KaldheimRun.commonB, + KaldheimRun.commonC2, + KaldheimRun.commonC2, + KaldheimRun.commonC2, + KaldheimRun.commonC2 + ); + private static final KaldheimStructure U1 = new KaldheimStructure( + KaldheimRun.uncommonA, + KaldheimRun.uncommonA, + KaldheimRun.uncommonA + ); + private static final KaldheimStructure U2 = new KaldheimStructure( + KaldheimRun.uncommonB, + KaldheimRun.uncommonB, + KaldheimRun.uncommonB + ); + private static final KaldheimStructure R1 = new KaldheimStructure( + KaldheimRun.rareA + ); + private static final KaldheimStructure R2 = new KaldheimStructure( + KaldheimRun.rareB + ); + private static final KaldheimStructure L1 = new KaldheimStructure( + KaldheimRun.land + ); + + private KaldheimStructure(CardRun... runs) { + super(runs); + } + } + + private final RarityConfiguration commonRuns = new RarityConfiguration( + false, + KaldheimStructure.C1, + KaldheimStructure.C2, + KaldheimStructure.C3, + KaldheimStructure.C4, + KaldheimStructure.C5, + KaldheimStructure.C1, + KaldheimStructure.C2, + KaldheimStructure.C3, + KaldheimStructure.C4, + KaldheimStructure.C5, + KaldheimStructure.C4, + KaldheimStructure.C5 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + KaldheimStructure.U1, + KaldheimStructure.U2 + ); + private final RarityConfiguration rareRuns = new RarityConfiguration( + false, + KaldheimStructure.R1, + KaldheimStructure.R1, + KaldheimStructure.R2 + ); + private final RarityConfiguration landRuns = new RarityConfiguration( + KaldheimStructure.L1 + ); + + + @Override + public void shuffle() { + commonRuns.shuffle(); + uncommonRuns.shuffle(); + rareRuns.shuffle(); + landRuns.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(landRuns.getNext().makeRun()); + return booster; + } +} diff --git a/Mage.Sets/src/mage/sets/TherosBeyondDeath.java b/Mage.Sets/src/mage/sets/TherosBeyondDeath.java index d9408a02c60..19d2f103a84 100644 --- a/Mage.Sets/src/mage/sets/TherosBeyondDeath.java +++ b/Mage.Sets/src/mage/sets/TherosBeyondDeath.java @@ -1,9 +1,16 @@ package mage.sets; import mage.cards.ExpansionSet; +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 java.util.ArrayList; +import java.util.List; + /** * @author TheElk801 */ @@ -16,7 +23,7 @@ public final class TherosBeyondDeath extends ExpansionSet { } private TherosBeyondDeath() { - super("Theros Beyond Death", "THB", ExpansionSet.buildDate(2020, 1, 24), SetType.EXPANSION); + super("Theros Beyond Death", "THB", ExpansionSet.buildDate(2020, 1, 24), SetType.EXPANSION, new TherosBeyondDeathCollator()); this.blockName = "Theros Beyond Death"; this.hasBoosters = true; this.numBoosterLands = 1; @@ -385,3 +392,158 @@ public final class TherosBeyondDeath extends ExpansionSet { cards.add(new SetCardInfo("Wrap in Flames", 164, Rarity.COMMON, mage.cards.w.WrapInFlames.class)); } } + +// Booster collation info from https://www.lethe.xyz/mtg/collation/thb.html +// Using USA collation for common/uncommon, rare collation inferred from other sets +class TherosBeyondDeathCollator implements BoosterCollator { + + private static class TherosBeyondDeathRun extends CardRun { + private static final TherosBeyondDeathRun commonA = new TherosBeyondDeathRun(true, 155, 29, 79, 127, 38, 57, 159, 41, 66, 140, 30, 78, 163, 28, 56, 137, 25, 68, 144, 20, 67, 146, 26, 49, 134, 40, 61, 159, 29, 51, 164, 17, 57, 149, 38, 66, 127, 30, 47, 144, 36, 79, 155, 41, 67, 137, 28, 78, 140, 25, 56, 163, 20, 49, 146, 40, 68, 134, 17, 51, 149, 26, 47, 164, 36, 61); + private static final TherosBeyondDeathRun commonB = new TherosBeyondDeathRun(true, 186, 85, 191, 116, 201, 103, 202, 115, 184, 120, 194, 110, 192, 88, 177, 113, 171, 86, 195, 109, 179, 114, 202, 85, 201, 103, 184, 116, 186, 115, 192, 110, 191, 114, 177, 120, 194, 88, 171, 113, 179, 86, 195, 109, 201, 85, 184, 116, 202, 110, 186, 103, 191, 115, 192, 114, 179, 113, 194, 109, 195, 86, 177, 88, 171, 120); + private static final TherosBeyondDeathRun commonC1 = new TherosBeyondDeathRun(true, 203, 154, 106, 77, 10, 174, 58, 16, 141, 238, 122, 46, 173, 152, 22, 240, 100, 74, 200, 142, 97, 11, 48, 203, 241, 154, 106, 35, 82, 174, 77, 10, 240, 141, 100, 58, 238, 122, 232, 152, 22, 46, 111, 173, 241, 16, 74, 97, 48, 11, 200, 142, 111, 82, 35); + private static final TherosBeyondDeathRun commonC2 = new TherosBeyondDeathRun(true, 44, 96, 197, 145, 232, 34, 126, 204, 249, 54, 135, 231, 187, 175, 44, 143, 95, 96, 197, 135, 107, 6, 32, 204, 126, 34, 54, 249, 145, 231, 187, 96, 6, 143, 44, 107, 34, 175, 135, 249, 95, 197, 54, 204, 126, 32, 6, 175, 95, 231, 145, 107, 187, 32, 143); + private static final TherosBeyondDeathRun uncommonA = new TherosBeyondDeathRun(true, 223, 65, 153, 8, 112, 227, 99, 167, 33, 138, 4, 189, 228, 45, 59, 180, 105, 1, 136, 196, 206, 139, 83, 89, 233, 31, 131, 91, 219, 193, 27, 133, 64, 199, 213, 264, 42, 153, 205, 8, 136, 4, 189, 33, 223, 2, 138, 112, 27, 233, 260, 180, 31, 59, 99, 131, 105, 267, 81, 139, 228, 167, 133, 219, 65, 1, 83, 125, 206, 193, 42, 91, 227, 89, 199, 153, 8, 81, 213, 64, 112, 223, 4, 136, 205, 105, 139, 99, 65, 2, 180, 228, 59, 1, 233, 45, 189, 227, 33, 196, 83, 138, 206, 42, 219, 167, 131, 31, 89, 193, 91, 125, 213, 199, 81, 27, 2, 64, 133, 205); + private static final TherosBeyondDeathRun uncommonB = new TherosBeyondDeathRun(true, 226, 101, 128, 183, 21, 234, 87, 50, 242, 176, 239, 132, 9, 216, 62, 119, 172, 160, 104, 69, 168, 225, 130, 237, 63, 15, 102, 166, 5, 129, 121, 53, 239, 70, 182, 128, 21, 234, 92, 69, 101, 160, 23, 230, 75, 130, 104, 172, 50, 7, 162, 87, 183, 226, 62, 216, 258, 132, 176, 237, 263, 15, 242, 63, 5, 225, 168, 129, 121, 53, 230, 21, 70, 102, 166, 128, 92, 234, 23, 183, 160, 104, 75, 226, 162, 7, 239, 182, 9, 132, 101, 69, 172, 216, 242, 50, 176, 87, 225, 62, 15, 168, 119, 237, 130, 5, 70, 102, 166, 63, 23, 129, 121, 53, 182, 7, 162, 230, 92, 75); + private static final TherosBeyondDeathRun rareA = new TherosBeyondDeathRun(false, 207, 84, 165, 3, 43, 209, 210, 212, 214, 169, 90, 12, 13, 215, 94, 217, 98, 218, 19, 24, 222, 243, 178, 55, 181, 108, 188, 235, 148, 60, 151, 198, 236, 37, 156, 157, 39, 158, 244, 245, 246, 247, 248, 72, 73, 124, 170, 76, 117, 118, 161, 80, 123, 207, 84, 165, 3, 43, 209, 210, 212, 214, 169, 90, 12, 13, 215, 94, 217, 98, 218, 19, 24, 222, 243, 178, 55, 181, 108, 188, 235, 148, 60, 151, 198, 236, 37, 156, 157, 39, 158, 244, 245, 246, 247, 248, 72, 73, 124, 170, 76, 117, 118, 161, 80, 123, 14, 18, 52, 71, 93, 147, 150, 185, 190, 208, 211, 220, 221, 224, 229); + private static final TherosBeyondDeathRun rareB = new TherosBeyondDeathRun(false, 207, 84, 165, 3, 43, 209, 210, 212, 214, 169, 90, 12, 13, 215, 94, 217, 98, 218, 19, 24, 222, 243, 178, 55, 181, 108, 188, 235, 148, 60, 151, 198, 236, 37, 156, 157, 39, 158, 244, 245, 246, 247, 248, 72, 73, 124, 170, 76, 117, 118, 161, 80, 123, 207, 84, 165, 3, 43, 209, 210, 212, 214, 169, 90, 12, 13, 215, 94, 217, 98, 218, 19, 24, 222, 243, 178, 55, 181, 108, 188, 235, 148, 60, 151, 198, 236, 37, 156, 157, 39, 158, 244, 245, 246, 247, 248, 72, 73, 124, 170, 76, 117, 118, 161, 80, 123, 255, 259, 52, 261, 262, 147, 265, 266, 190, 256, 257, 268, 221, 224, 229); + private static final TherosBeyondDeathRun land = new TherosBeyondDeathRun(false, 250, 251, 252, 253, 254); + + private TherosBeyondDeathRun(boolean keepOrder, Integer... names) { + super(keepOrder, names); + } + } + + private static class TherosBeyondDeathStructure extends BoosterStructure { + private static final TherosBeyondDeathStructure C1 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1 + ); + private static final TherosBeyondDeathStructure C2 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1, + TherosBeyondDeathRun.commonC1 + ); + private static final TherosBeyondDeathStructure C3 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonC2, + TherosBeyondDeathRun.commonC2, + TherosBeyondDeathRun.commonC2, + TherosBeyondDeathRun.commonC2 + ); + private static final TherosBeyondDeathStructure C4 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonC2, + TherosBeyondDeathRun.commonC2, + TherosBeyondDeathRun.commonC2 + ); + private static final TherosBeyondDeathStructure C5 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonA, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonB, + TherosBeyondDeathRun.commonC2, + TherosBeyondDeathRun.commonC2 + ); + private static final TherosBeyondDeathStructure U1 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.uncommonA, + TherosBeyondDeathRun.uncommonB, + TherosBeyondDeathRun.uncommonB + ); + private static final TherosBeyondDeathStructure U2 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.uncommonA, + TherosBeyondDeathRun.uncommonA, + TherosBeyondDeathRun.uncommonB + ); + private static final TherosBeyondDeathStructure R1 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.rareA + ); + private static final TherosBeyondDeathStructure R2 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.rareB + ); + private static final TherosBeyondDeathStructure L1 = new TherosBeyondDeathStructure( + TherosBeyondDeathRun.land + ); + + private TherosBeyondDeathStructure(CardRun... runs) { + super(runs); + } + } + + private final RarityConfiguration commonRuns = new RarityConfiguration( + false, + TherosBeyondDeathStructure.C1, + TherosBeyondDeathStructure.C2, + TherosBeyondDeathStructure.C3, + TherosBeyondDeathStructure.C4, + TherosBeyondDeathStructure.C5, + TherosBeyondDeathStructure.C1, + TherosBeyondDeathStructure.C2, + TherosBeyondDeathStructure.C3, + TherosBeyondDeathStructure.C4, + TherosBeyondDeathStructure.C5, + TherosBeyondDeathStructure.C4, + TherosBeyondDeathStructure.C5 + ); + private final RarityConfiguration uncommonRuns = new RarityConfiguration( + TherosBeyondDeathStructure.U1, + TherosBeyondDeathStructure.U2 + ); + private final RarityConfiguration rareRuns = new RarityConfiguration( + false, + TherosBeyondDeathStructure.R1, + TherosBeyondDeathStructure.R1, + TherosBeyondDeathStructure.R2 + ); + private final RarityConfiguration landRuns = new RarityConfiguration( + TherosBeyondDeathStructure.L1 + ); + + + @Override + public void shuffle() { + commonRuns.shuffle(); + uncommonRuns.shuffle(); + rareRuns.shuffle(); + landRuns.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(landRuns.getNext().makeRun()); + 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 fc562171a80..990a39d92ce 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 @@ -335,6 +335,7 @@ public class BoosterGenerationTest extends MageTestBase { boolean foundVale = false; boolean foundMDFC = false; boolean foundNoMDFC = false; + for (int i = 1; i <= 100; i++) { List booster = Kaldheim.getInstance().createBooster(); diff --git a/Mage/src/main/java/mage/cards/ExpansionSet.java b/Mage/src/main/java/mage/cards/ExpansionSet.java index 50d6a311f96..43bf0ab5f68 100644 --- a/Mage/src/main/java/mage/cards/ExpansionSet.java +++ b/Mage/src/main/java/mage/cards/ExpansionSet.java @@ -6,6 +6,7 @@ import mage.abilities.keyword.PartnerWithAbility; import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; +import mage.collation.BoosterCollator; import mage.constants.Rarity; import mage.constants.SetType; import mage.util.CardUtil; @@ -26,7 +27,7 @@ public abstract class ExpansionSet implements Serializable { public static final CardGraphicInfo FULL_ART_BFZ_VARIOUS = new CardGraphicInfo(FrameStyle.BFZ_FULL_ART_BASIC, true); public static final CardGraphicInfo FULL_ART_ZEN_VARIOUS = new CardGraphicInfo(FrameStyle.ZEN_FULL_ART_BASIC, true); - public class SetCardInfo implements Serializable { + public static class SetCardInfo implements Serializable { private final String name; private final String cardNumber; @@ -92,6 +93,7 @@ public abstract class ExpansionSet implements Serializable { protected Date releaseDate; protected ExpansionSet parentSet; protected SetType setType; + protected BoosterCollator boosterCollator; // TODO: 03.10.2018, hasBasicLands can be removed someday -- it's uses to optimize lands search in deck generation and lands adding (search all available lands from sets) protected boolean hasBasicLands = true; @@ -120,14 +122,23 @@ public abstract class ExpansionSet implements Serializable { protected int maxCardNumberInBooster; // used to omit cards with collector numbers beyond the regular cards in a set for boosters protected final EnumMap> savedCards; + protected final Map inBoosterMap = new HashMap<>(); public ExpansionSet(String name, String code, Date releaseDate, SetType setType) { + this(name, code, releaseDate, setType, null); + } + + public ExpansionSet(String name, String code, Date releaseDate, SetType setType, BoosterCollator boosterCollator) { this.name = name; this.code = code; this.releaseDate = releaseDate; this.setType = setType; this.maxCardNumberInBooster = Integer.MAX_VALUE; savedCards = new EnumMap<>(Rarity.class); + this.boosterCollator = boosterCollator; + if (this.boosterCollator != null) { + this.boosterCollator.shuffle(); + } } public String getName() { @@ -240,6 +251,9 @@ public abstract class ExpansionSet implements Serializable { } public List createBooster() { + if (boosterCollator != null) { + return createBoosterUsingCollator(); + } for (int i = 0; i < 100; i++) {//don't want to somehow loop forever @@ -262,6 +276,30 @@ public abstract class ExpansionSet implements Serializable { return tryBooster(); } + public void shuffleCollator() { + if (boosterCollator != null) { + boosterCollator.shuffle(); + } + } + + private List createBoosterUsingCollator() { + if (inBoosterMap.isEmpty()) { + CardCriteria criteria = new CardCriteria(); + criteria.setCodes(code); + CardRepository + .instance + .findCards(criteria) + .stream() + .forEach(cardInfo -> inBoosterMap.put(cardInfo.getCardNumberAsInt(), cardInfo)); + } + return boosterCollator + .makeBooster() + .stream() + .map(inBoosterMap::get) + .map(CardInfo::getCard) + .collect(Collectors.toList()); + } + protected boolean boosterIsValid(List booster) { if (validateBoosterColors) { if (!validateColors(booster)) { diff --git a/Mage/src/main/java/mage/collation/BoosterCollator.java b/Mage/src/main/java/mage/collation/BoosterCollator.java new file mode 100644 index 00000000000..6e4d0c1d298 --- /dev/null +++ b/Mage/src/main/java/mage/collation/BoosterCollator.java @@ -0,0 +1,13 @@ +package mage.collation; + +import java.util.List; + +/** + * @author TheElk801 + */ +public interface BoosterCollator { + + public void shuffle(); + + public List makeBooster(); +} diff --git a/Mage/src/main/java/mage/collation/BoosterStructure.java b/Mage/src/main/java/mage/collation/BoosterStructure.java new file mode 100644 index 00000000000..84a8c9d477c --- /dev/null +++ b/Mage/src/main/java/mage/collation/BoosterStructure.java @@ -0,0 +1,36 @@ +package mage.collation; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Current implementation is built only for sequential collation + * Boosters are collated through a variety of different methods + * Striped collation not supported yet + * For more information: https://www.lethe.xyz/mtg/collation/ + * + * @author TheElk801 + */ +public abstract class BoosterStructure { + + private final List slots; + + protected BoosterStructure(CardRun... runs) { + this.slots = Arrays.asList(runs); + } + + public List makeRun() { + List cards = new ArrayList<>(); + for (CardRun run : this.slots) { + cards.add(run.getNext()); + } + return cards; + } + + public void shuffle() { + for (CardRun run : this.slots) { + run.shuffle(); + } + } +} diff --git a/Mage/src/main/java/mage/collation/CardRun.java b/Mage/src/main/java/mage/collation/CardRun.java new file mode 100644 index 00000000000..845d1150684 --- /dev/null +++ b/Mage/src/main/java/mage/collation/CardRun.java @@ -0,0 +1,11 @@ +package mage.collation; + +/** + * @author TheElk801 + */ +public abstract class CardRun extends Rotater { + + public CardRun(boolean keepOrder, Integer... names) { + super(keepOrder, names); + } +} diff --git a/Mage/src/main/java/mage/collation/RarityConfiguration.java b/Mage/src/main/java/mage/collation/RarityConfiguration.java new file mode 100644 index 00000000000..3ba6807b860 --- /dev/null +++ b/Mage/src/main/java/mage/collation/RarityConfiguration.java @@ -0,0 +1,27 @@ +package mage.collation; + +/** + * @author TheElk801 + */ +public class RarityConfiguration extends Rotater { + + public RarityConfiguration(BoosterStructure item) { + super(item); + } + + public RarityConfiguration(BoosterStructure item1, BoosterStructure item2) { + super(item1, item2); + } + + public RarityConfiguration(boolean keepOrder, BoosterStructure... items) { + super(keepOrder, items); + } + + @Override + public void shuffle() { + for (BoosterStructure structure : this.items) { + structure.shuffle(); + } + super.shuffle(); + } +} diff --git a/Mage/src/main/java/mage/collation/Rotater.java b/Mage/src/main/java/mage/collation/Rotater.java new file mode 100644 index 00000000000..6558106e50e --- /dev/null +++ b/Mage/src/main/java/mage/collation/Rotater.java @@ -0,0 +1,50 @@ +package mage.collation; + +import mage.util.RandomUtil; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * A class for shuffling a list by choosing a random starting point and looping through it + * + * @author TheElk801 + */ +public class Rotater { + + protected final List items; + private final boolean keepOrder; + private int position = 0; + + public Rotater(T item) { + this(true, item); + } + + public Rotater(T item1, T item2) { + this(true, item1, item2); + } + + public Rotater(boolean keepOrder, T... items) { + this.items = Arrays.asList(items); + this.keepOrder = keepOrder; + } + + public int iterate() { + int i = position; + position++; + position %= items.size(); + return i; + } + + public T getNext() { + return items.get(iterate()); + } + + public void shuffle() { + position = RandomUtil.nextInt(items.size()); + if (!keepOrder) { + Collections.shuffle(items, RandomUtil.getRandom()); + } + } +}