diff --git a/Mage.Client/src/main/java/mage/client/components/BracketLegalityLabel.java b/Mage.Client/src/main/java/mage/client/components/BracketLegalityLabel.java index aa163daaa4a..c45fb575270 100644 --- a/Mage.Client/src/main/java/mage/client/components/BracketLegalityLabel.java +++ b/Mage.Client/src/main/java/mage/client/components/BracketLegalityLabel.java @@ -4,8 +4,12 @@ import mage.MageObject; import mage.cards.Card; import mage.cards.decks.Deck; import mage.client.util.GUISizeHelper; +import org.apache.log4j.Logger; import java.awt.*; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; import java.util.List; import java.util.*; import java.util.stream.Stream; @@ -16,22 +20,73 @@ import java.util.stream.Stream; *

* Support: * - [x] game changers - * - [ ] infinite combos + * - [x] infinite combos * - [x] mass land destruction * - [x] extra turns * - [x] tutors + * Features: + * - [x] find possible bracket level of the deck + * - [x] find affected cards by checking group + * - [x] can auto-generate infinite combos list, see verify test downloadAndPrepareCommanderBracketsData + * - [ ] TODO: tests + * - [ ] TODO: table - players brackets level disclose settings + * - [ ] TODO: generate - convert card name to xmage format and assert on bad names (ascii only) * * @author JayDi85 */ public class BracketLegalityLabel extends LegalityLabel { + private static final Logger logger = Logger.getLogger(BracketLegalityLabel.class); + private static final String GROUP_GAME_CHANGES = "Game Changers"; - private static final String GROUP_INFINITE_COMBOS = "Infinite Combos (unsupported)"; + private static final String GROUP_INFINITE_COMBOS = "Infinite Combos"; private static final String GROUP_MASS_LAND_DESTRUCTION = "Mass Land Destruction"; private static final String GROUP_EXTRA_TURN = "Extra Turns"; private static final String GROUP_TUTORS = "Tutors"; - private final BracketLevel level; + private static final Map> MAX_GROUP_LIMITS = new LinkedHashMap<>(); + + static { + // 1 + // No cards from the Game Changer list. + // No intentional two-card infinite combos. + // No mass land destruction. + // No extra turn cards. + // Tutors should be sparse. + // 2 + // No cards from the Game Changer list. + // No intentional two-card infinite combos. + // No mass land destruction. + // Extra turn cards should only appear in low quantities and should not be chained in succession or looped. + // Tutors should be sparse. + // 3 + // Up to three (3) cards from the Game Changer list. + // No intentional early game two-card infinite combos. + // No mass land destruction. + // Extra turn cards should only appear in low quantities and should not be chained in succession or looped. + // 4 + // 5 + // allow any cards + + // cards limits per brackets level, it's ok to use 99 as max + // group - levels 0, 1, 2, 3, 4, 5 + MAX_GROUP_LIMITS.put(GROUP_GAME_CHANGES, + Arrays.asList(0, 0, 0, 3, 99, 99)); + MAX_GROUP_LIMITS.put(GROUP_INFINITE_COMBOS, + Arrays.asList(0, 0, 0, 0, 99, 99)); + MAX_GROUP_LIMITS.put(GROUP_MASS_LAND_DESTRUCTION, + Arrays.asList(0, 0, 0, 0, 99, 99)); + MAX_GROUP_LIMITS.put(GROUP_EXTRA_TURN, + Arrays.asList(0, 0, 0, 3, 99, 99)); + MAX_GROUP_LIMITS.put(GROUP_TUTORS, + Arrays.asList(0, 3, 3, 99, 99, 99)); + } + + private static final String RESOURCE_INFINITE_COMBOS = "brackets/infinite-combos.txt"; + + private final String fullName; + private final String shortName; + private final int maxLevel; private final List foundGameChangers = new ArrayList<>(); private final List foundInfiniteCombos = new ArrayList<>(); @@ -41,28 +96,14 @@ public class BracketLegalityLabel extends LegalityLabel { private final List badCards = new ArrayList<>(); private final List fullGameChanges = new ArrayList<>(); + private final Set fullInfiniteCombos = new HashSet<>(); // card1@card2, sorted by names, name must be xmage compatible - public enum BracketLevel { - BRACKET_1("Bracket 1"), - BRACKET_2_3("Bracket 2-3"), - BRACKET_4_5("Bracket 4-5"); - - private final String name; - - BracketLevel(String name) { - this.name = name; - } - - @Override - public String toString() { - return this.name; - } - } - - public BracketLegalityLabel(BracketLevel level) { - super(level.toString(), null); - this.level = level; - setPreferredSize(DIM_PREFERRED); + public BracketLegalityLabel(String fullName, String shortName, int maxLevel) { + super(shortName, null); + this.fullName = fullName; + this.shortName = shortName; + this.maxLevel = maxLevel; + setPreferredSize(DIM_PREFERRED_1_OF_5); } @Override @@ -72,49 +113,26 @@ public class BracketLegalityLabel extends LegalityLabel { private void validateBracketLevel() { this.badCards.clear(); - switch (this.level) { - case BRACKET_1: - // No cards from the Game Changer list. - // No intentional two-card infinite combos. - // No mass land destruction. - // No extra turn cards. - // Tutors should be sparse. - this.badCards.addAll(this.foundGameChangers); - this.badCards.addAll(this.foundInfiniteCombos); - this.badCards.addAll(this.foundMassLandDestruction); - this.badCards.addAll(this.foundExtraTurn); - if (this.foundTutors.size() > 3) { - this.badCards.addAll(this.foundTutors); - } - break; - case BRACKET_2_3: - // 2 - // No cards from the Game Changer list. - // No intentional two-card infinite combos. - // No mass land destruction. - // Extra turn cards should only appear in low quantities and should not be chained in succession or looped. - // Tutors should be sparse. - // 3 - // Up to three (3) cards from the Game Changer list. - // No intentional early game two-card infinite combos. - // No mass land destruction. - // Extra turn cards should only appear in low quantities and should not be chained in succession or looped. - if (this.foundGameChangers.size() > 3) { - this.badCards.addAll(this.foundGameChangers); - } - this.badCards.addAll(this.foundInfiniteCombos); - this.badCards.addAll(this.foundMassLandDestruction); - if (this.foundExtraTurn.size() > 3) { - this.badCards.addAll(this.foundExtraTurn); - } - // this.badCards.addAll(this.foundTutors); // allow any amount - break; - case BRACKET_4_5: - // allow any cards - break; - default: - throw new IllegalArgumentException("Unsupported level: " + this.level); + + if (this.foundGameChangers.size() > getMaxCardsLimit(GROUP_GAME_CHANGES)) { + this.badCards.addAll(this.foundGameChangers); } + if (this.foundInfiniteCombos.size() > getMaxCardsLimit(GROUP_INFINITE_COMBOS)) { + this.badCards.addAll(this.foundInfiniteCombos); + } + if (this.foundMassLandDestruction.size() > getMaxCardsLimit(GROUP_MASS_LAND_DESTRUCTION)) { + this.badCards.addAll(this.foundMassLandDestruction); + } + if (this.foundExtraTurn.size() > getMaxCardsLimit(GROUP_EXTRA_TURN)) { + this.badCards.addAll(this.foundExtraTurn); + } + if (this.foundTutors.size() > getMaxCardsLimit(GROUP_TUTORS)) { + this.badCards.addAll(this.foundTutors); + } + } + + private Integer getMaxCardsLimit(String groupName) { + return MAX_GROUP_LIMITS.get(groupName).get(this.maxLevel); } @Override @@ -122,31 +140,41 @@ public class BracketLegalityLabel extends LegalityLabel { collectAll(deck); validateBracketLevel(); - int infoFontSize = Math.round(GUISizeHelper.cardTooltipFont.getSize() * 0.6f); + int infoFontHeaderSize = Math.round(GUISizeHelper.cardTooltipFont.getSize() * 1.0f); + int infoFontTextSize = Math.round(GUISizeHelper.cardTooltipFont.getSize() * 0.6f); // show all found cards in any use cases Color showColor = this.badCards.isEmpty() ? COLOR_LEGAL : COLOR_NOT_LEGAL; List showInfo = new ArrayList<>(); if (this.badCards.isEmpty()) { - showInfo.add("

Deck is GOOD for " + this.level + "

"); + showInfo.add(String.format("

Deck is GOOD for %s

", + infoFontHeaderSize, + this.fullName + )); } else { - showInfo.add("

Deck is BAD for " + this.level + "

"); + showInfo.add(String.format("

Deck is BAD for %s

", + infoFontHeaderSize, + this.fullName + )); showInfo.add("

(click here to select all bad cards)

"); } Map> groups = new LinkedHashMap<>(); - groups.put(GROUP_GAME_CHANGES, this.foundGameChangers); - groups.put(GROUP_INFINITE_COMBOS, this.foundInfiniteCombos); - groups.put(GROUP_MASS_LAND_DESTRUCTION, this.foundMassLandDestruction); - groups.put(GROUP_EXTRA_TURN, this.foundExtraTurn); - groups.put(GROUP_TUTORS, this.foundTutors); + groups.put(GROUP_GAME_CHANGES + getStats(GROUP_GAME_CHANGES), this.foundGameChangers); + groups.put(GROUP_INFINITE_COMBOS + getStats(GROUP_INFINITE_COMBOS), this.foundInfiniteCombos); + groups.put(GROUP_MASS_LAND_DESTRUCTION + getStats(GROUP_MASS_LAND_DESTRUCTION), this.foundMassLandDestruction); + groups.put(GROUP_EXTRA_TURN + getStats(GROUP_EXTRA_TURN), this.foundExtraTurn); + groups.put(GROUP_TUTORS + getStats(GROUP_TUTORS), this.foundTutors); groups.forEach((group, cards) -> { showInfo.add("
"); - showInfo.add("
"); - showInfo.add("" + group + ": " + cards.size() + ""); - if (!cards.isEmpty()) { - showInfo.add("