From 74c885a3311687d0cde82718c57bb8bedc241870 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Tue, 3 Jun 2025 08:03:14 +0400 Subject: [PATCH] GUI, deck: improved bracket level calculations (added infinite combos support, part of #13341) --- .../components/BracketLegalityLabel.java | 77 ++++++++++++++++++- .../resources/brackets/infinite-combos.txt | 9 +++ 2 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 Mage/src/main/resources/brackets/infinite-combos.txt 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..7f1e39b67d3 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,21 +20,31 @@ 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 + * - [ ] TODO: data download and generate + * - [ ] TODO: tests + * - [ ] TODO: table - players brackets level disclose settings * * @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 static final String RESOURCE_INFINITE_COMBOS = "brackets/infinite-combos.txt"; + private final BracketLevel level; private final List foundGameChangers = new ArrayList<>(); @@ -41,6 +55,7 @@ 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"), @@ -243,8 +258,64 @@ public class BracketLegalityLabel extends LegalityLabel { } private void collectInfiniteCombos(Deck deck) { - // TODO: implement this.foundInfiniteCombos.clear(); + + if (this.fullInfiniteCombos.isEmpty()) { + InputStream in = BracketLegalityLabel.class.getClassLoader().getResourceAsStream(RESOURCE_INFINITE_COMBOS); + if (in == null) { + throw new RuntimeException("Commander brackets: can't load infinite combos list"); + } + try (InputStreamReader input = new InputStreamReader(in); + BufferedReader reader = new BufferedReader(input)) { + String line = reader.readLine(); + while (line != null) { + try { + line = line.trim(); + if (line.startsWith("#")) { + continue; + } + List cards = Arrays.asList(line.split("@")); + if (cards.size() != 2) { + logger.warn("wrong line format in commander brackets file: " + line); + continue; + } + + Collections.sort(cards); + this.fullInfiniteCombos.add(String.join("@", cards)); + } finally { + line = reader.readLine(); + } + } + } catch (Exception e) { + throw new RuntimeException("Tokens brackets: can't load infinite combos list - " + e); + } + } + + // search and check all x2 combinations + List deckCards = new ArrayList<>(); + Set foundCards = new HashSet<>(); + deckCards.addAll(deck.getCards()); + deckCards.addAll(deck.getSideboard()); + for (Card card1 : deckCards) { + for (Card card2 : deckCards) { + if (card1 == card2) { + continue; + } + List names = Arrays.asList(card1.getName(), card2.getName()); + Collections.sort(names); + String deckCombo = String.join("@", names); + if (this.fullInfiniteCombos.contains(deckCombo)) { + foundCards.add(card1); + foundCards.add(card2); + break; + } + } + } + + foundCards.stream() + .map(MageObject::getName) + .sorted() + .forEach(this.foundInfiniteCombos::add); } private void collectMassLandDestruction(Deck deck) { diff --git a/Mage/src/main/resources/brackets/infinite-combos.txt b/Mage/src/main/resources/brackets/infinite-combos.txt new file mode 100644 index 00000000000..4f7fcae51af --- /dev/null +++ b/Mage/src/main/resources/brackets/infinite-combos.txt @@ -0,0 +1,9 @@ +# Infinite x2 cards combos list for commander brackets score system +# Format: card_name_1@card_name_2 - must be sorted by name, xmage compatible without non-ascii chars +# Data source: xxx +# Generated xxx +Bloodchief Ascension@Mindcrank +Zaxara, the Exemplary@Freed from the Real +Brallin, Skyshark Rider@Ophidian Eye +Krosan Restorer@Ashaya, Soul of the Wild +# TODO: generate it \ No newline at end of file