From 8ad3238109751c75e63abf91f73b196941aa06bb Mon Sep 17 00:00:00 2001 From: Tirth Bharatiya <41485330+tirth1@users.noreply.github.com> Date: Tue, 28 Nov 2023 12:44:53 +0530 Subject: [PATCH] refactor: improved code usage in DeckGeneratorPool (#11479) * moved generateSpells and generateLands method to DeckGeneratorPool. * moved generateBasicLands method to DeckGeneratorPool. --- .../client/deck/generator/DeckGenerator.java | 149 +----------------- .../deck/generator/DeckGeneratorPool.java | 141 +++++++++++++++++ 2 files changed, 148 insertions(+), 142 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java index c5a4aa15756..a9f99c1daa8 100644 --- a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java +++ b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGenerator.java @@ -33,9 +33,9 @@ public final class DeckGenerator { } - private static final int MAX_TRIES = 8196; + protected static final int MAX_TRIES = 8196; private static DeckGeneratorDialog genDialog; - private static DeckGeneratorPool genPool; + protected static DeckGeneratorPool genPool; /** * Builds a deck out of the selected block/set/format. @@ -157,151 +157,16 @@ public final class DeckGenerator { nonBasicLandCriteria.notSupertypes(SuperType.BASIC); // Generate basic land cards - Map> basicLands = generateBasicLands(setsToUse); + Map> basicLands = DeckGeneratorPool.generateBasicLands(setsToUse); - generateSpells(creatureCriteria, genPool.getCreatureCount()); - generateSpells(nonCreatureCriteria, genPool.getNonCreatureCount()); - generateLands(nonBasicLandCriteria, genPool.getLandCount(), basicLands); + DeckGeneratorPool.generateSpells(creatureCriteria, genPool.getCreatureCount()); + DeckGeneratorPool.generateSpells(nonCreatureCriteria, genPool.getNonCreatureCount()); + DeckGeneratorPool.generateLands(genDialog.useNonBasicLand(), nonBasicLandCriteria, basicLands); // Reconstructs the final deck and adjusts for Math rounding and/or missing cards return genPool.getDeck(); } - /** - * Generates all spells for the deck. Each card is retrieved from the - * database and checked against the converted mana cost (CMC) needed for the - * current card pool. If a card's CMC matches the CMC range required by the - * pool, it is added to the deck. This ensures that the majority of cards - * fit a fixed mana curve for the deck, and it is playable. Creatures and - * non-creatures are retrieved separately to ensure the deck contains a - * reasonable mix of both. - * - * @param criteria the criteria to search for in the database. - * @param spellCount the number of spells that match the criteria needed in - * the deck. - */ - private static void generateSpells(CardCriteria criteria, int spellCount) { - List cardPool = CardRepository.instance.findCards(criteria); - int retrievedCount = cardPool.size(); - List deckCMCs = genPool.getCMCsForSpellCount(spellCount); - int count = 0; - int reservesAdded = 0; - boolean added; - if (retrievedCount > 0 && retrievedCount >= spellCount) { - int tries = 0; - while (count < spellCount) { - Card card = cardPool.get(RandomUtil.nextInt(retrievedCount)).getMockCard(); - if (genPool.isValidSpellCard(card)) { - int cardCMC = card.getManaValue(); - for (DeckGeneratorCMC.CMC deckCMC : deckCMCs) { - if (cardCMC >= deckCMC.min && cardCMC <= deckCMC.max) { - int currentAmount = deckCMC.getAmount(); - if (currentAmount > 0) { - deckCMC.setAmount(currentAmount - 1); - genPool.addCard(card.copy()); - count++; - } - } else if (reservesAdded < (genPool.getDeckSize() / 2)) { - added = genPool.tryAddReserve(card, cardCMC); - if (added) { - reservesAdded++; - } - } - } - } - tries++; - if (tries > MAX_TRIES) { - // Break here, we'll fill in random missing ones later - break; - } - } - } else { - throw new IllegalStateException("Not enough cards to generate deck."); - } - - } - - /** - * Generates all the lands for the deck. Generates non-basic if selected by - * the user and if the deck isn't monocolored. Will fetch non-basic lands if - * required and then fill up the remaining space with basic lands. Basic - * lands are adjusted according to the mana symbols seen in the cards used - * in this deck. Usually the lands will be well balanced relative to the - * color of cards. - * - * @param criteria the criteria of the lands to search for in the database. - * @param landsCount the amount of lands required for this deck. - * @param basicLands information about the basic lands from the sets used. - */ - private static void generateLands(CardCriteria criteria, int landsCount, Map> basicLands) { - - int tries = 0; - int countNonBasic = 0; - // Store the nonbasic lands (if any) we'll add - List deckLands = new ArrayList<>(); - - // Calculates the percentage of colored mana symbols over all spells in the deck - Map percentage = genPool.calculateSpellColorPercentages(); - - // Only dual/tri color lands are generated for now, and not non-basic lands that only produce colorless mana. - if (!genPool.isMonoColoredDeck() && genDialog.useNonBasicLand()) { - List landCards = genPool.filterLands(CardRepository.instance.findCards(criteria)); - int allCount = landCards.size(); - if (allCount > 0) { - while (countNonBasic < landsCount / 2) { - Card card = landCards.get(RandomUtil.nextInt(allCount)); - if (genPool.isValidLandCard(card)) { - Card addedCard = card.copy(); - deckLands.add(addedCard); - genPool.addCard(addedCard); - countNonBasic++; - } - tries++; - // to avoid infinite loop - if (tries > MAX_TRIES) { - // Not a problem, just use what we have - break; - } - } - } - } - // Calculate the amount of colored mana already can be produced by the non-basic lands - Map count = genPool.countManaProduced(deckLands); - // Fill up the rest of the land quota with basic lands adjusted to fit the deck's mana costs - addBasicLands(landsCount - countNonBasic, percentage, count, basicLands); - } - - /** - * Returns a map of colored mana symbol to basic land cards of that color. - * - * @param setsToUse which sets to retrieve basic lands from. - * @return a map of color to basic lands. - */ - private static Map> generateBasicLands(List setsToUse) { - - Set landSets = TournamentUtil.getLandSetCodeForDeckSets(setsToUse); - - CardCriteria criteria = new CardCriteria(); - if (!landSets.isEmpty()) { - criteria.setCodes(landSets.toArray(new String[landSets.size()])); - } - criteria.ignoreSetsWithSnowLands(); - - Map> basicLandMap = new HashMap<>(); - - for (ColoredManaSymbol c : ColoredManaSymbol.values()) { - String landName = DeckGeneratorPool.getBasicLandName(c.toString()); - criteria.rarities(Rarity.LAND).name(landName); - List cards = CardRepository.instance.findCards(criteria); - if (cards.isEmpty()) { // Workaround to get basic lands if lands are not available for the given sets - criteria.setCodes("M15"); - cards = CardRepository.instance.findCards(criteria); - } - basicLandMap.put(landName, cards); - } - return basicLandMap; - } - /** * Once any non-basic lands are added, add basic lands until the deck is * filled. @@ -312,7 +177,7 @@ public final class DeckGenerator { * @param basicLands list of information about basic lands from the * database. */ - private static void addBasicLands(int landsNeeded, Map percentage, Map count, Map> basicLands) { + static void addBasicLands(int landsNeeded, Map percentage, Map count, Map> basicLands) { int colorTotal = 0; ColoredManaSymbol colorToAdd = ColoredManaSymbol.U; diff --git a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorPool.java b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorPool.java index 2ba561b1ce9..e2b9d5cf6c1 100644 --- a/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorPool.java +++ b/Mage.Client/src/main/java/mage/client/deck/generator/DeckGeneratorPool.java @@ -4,9 +4,13 @@ package mage.client.deck.generator; import mage.abilities.Ability; import mage.cards.Card; import mage.cards.decks.Deck; +import mage.cards.repository.CardCriteria; import mage.cards.repository.CardInfo; +import mage.cards.repository.CardRepository; import mage.constants.ColoredManaSymbol; +import mage.constants.Rarity; import mage.util.RandomUtil; +import mage.util.TournamentUtil; import java.util.*; @@ -475,4 +479,141 @@ public class DeckGeneratorPool cardCounts.put(cardName, 0); return cardCounts.get((cardName)); } + + + /** + * Generates all the lands for the deck. Generates non-basic if selected by + * the user and if the deck isn't monocolored. Will fetch non-basic lands if + * required and then fill up the remaining space with basic lands. Basic + * lands are adjusted according to the mana symbols seen in the cards used + * in this deck. Usually the lands will be well balanced relative to the + * color of cards. + * + * @param useNonBasicLand + * @param criteria the criteria of the lands to search for in the database. + * @param basicLands information about the basic lands from the sets used. + */ + protected static void generateLands(boolean useNonBasicLand, CardCriteria criteria, Map> basicLands) { + DeckGeneratorPool genPool = DeckGenerator.genPool; + int tries = 0; + int countNonBasic = 0; + int landsCount = genPool.getLandCount(); + // Store the nonbasic lands (if any) we'll add + List deckLands = new ArrayList<>(); + + // Calculates the percentage of colored mana symbols over all spells in the deck + Map percentage = genPool.calculateSpellColorPercentages(); + + // Only dual/tri color lands are generated for now, and not non-basic lands that only produce colorless mana. + if (!genPool.isMonoColoredDeck() && useNonBasicLand) { + List landCards = genPool.filterLands(CardRepository.instance.findCards(criteria)); + int allCount = landCards.size(); + if (allCount > 0) { + while (countNonBasic < landsCount / 2) { + Card card = landCards.get(RandomUtil.nextInt(allCount)); + if (genPool.isValidLandCard(card)) { + Card addedCard = card.copy(); + deckLands.add(addedCard); + genPool.addCard(addedCard); + countNonBasic++; + } + tries++; + // to avoid infinite loop + if (tries > DeckGenerator.MAX_TRIES) { + // Not a problem, just use what we have + break; + } + } + } + } + // Calculate the amount of colored mana already can be produced by the non-basic lands + Map count = genPool.countManaProduced(deckLands); + // Fill up the rest of the land quota with basic lands adjusted to fit the deck's mana costs + DeckGenerator.addBasicLands(landsCount - countNonBasic, percentage, count, basicLands); + } + + /** + * Generates all spells for the deck. Each card is retrieved from the + * database and checked against the converted mana cost (CMC) needed for the + * current card pool. If a card's CMC matches the CMC range required by the + * pool, it is added to the deck. This ensures that the majority of cards + * fit a fixed mana curve for the deck, and it is playable. Creatures and + * non-creatures are retrieved separately to ensure the deck contains a + * reasonable mix of both. + * + * @param criteria the criteria to search for in the database. + * @param spellCount the number of spells that match the criteria needed in + * the deck. + */ + protected static void generateSpells(CardCriteria criteria, int spellCount) { + DeckGeneratorPool genPool = DeckGenerator.genPool; + List cardPool = CardRepository.instance.findCards(criteria); + int retrievedCount = cardPool.size(); + List deckCMCs = genPool.getCMCsForSpellCount(spellCount); + int count = 0; + int reservesAdded = 0; + boolean added; + if (retrievedCount > 0 && retrievedCount >= spellCount) { + int tries = 0; + while (count < spellCount) { + Card card = cardPool.get(RandomUtil.nextInt(retrievedCount)).getMockCard(); + if (genPool.isValidSpellCard(card)) { + int cardCMC = card.getManaValue(); + for (DeckGeneratorCMC.CMC deckCMC : deckCMCs) { + if (cardCMC >= deckCMC.min && cardCMC <= deckCMC.max) { + int currentAmount = deckCMC.getAmount(); + if (currentAmount > 0) { + deckCMC.setAmount(currentAmount - 1); + genPool.addCard(card.copy()); + count++; + } + } else if (reservesAdded < (genPool.getDeckSize() / 2)) { + added = genPool.tryAddReserve(card, cardCMC); + if (added) { + reservesAdded++; + } + } + } + } + tries++; + if (tries > DeckGenerator.MAX_TRIES) { + // Break here, we'll fill in random missing ones later + break; + } + } + } else { + throw new IllegalStateException("Not enough cards to generate deck."); + } + } + + /** + * Returns a map of colored mana symbol to basic land cards of that color. + * + * @param setsToUse which sets to retrieve basic lands from. + * @return a map of color to basic lands. + */ + protected static Map> generateBasicLands(List setsToUse) { + + Set landSets = TournamentUtil.getLandSetCodeForDeckSets(setsToUse); + + CardCriteria criteria = new CardCriteria(); + if (!landSets.isEmpty()) { + criteria.setCodes(landSets.toArray(new String[landSets.size()])); + } + criteria.ignoreSetsWithSnowLands(); + + Map> basicLandMap = new HashMap<>(); + + for (ColoredManaSymbol c : ColoredManaSymbol.values()) { + String landName = DeckGeneratorPool.getBasicLandName(c.toString()); + criteria.rarities(Rarity.LAND).name(landName); + List cards = CardRepository.instance.findCards(criteria); + if (cards.isEmpty()) { // Workaround to get basic lands if lands are not available for the given sets + criteria.setCodes("M15"); + cards = CardRepository.instance.findCards(criteria); + } + basicLandMap.put(landName, cards); + } + return basicLandMap; + } }