/** * Once any non-basic lands are added, add basic lands until the deck is filled. * * @param landsNeeded how many remaining lands are needed. * @param percentage the percentage needed for each color in the final deck. * @param count how many of each color can be produced by non-basic lands. * @param basicLands list of information about basic lands from the database. */ private static void addBasicLands( int landsNeeded, Map<String, Double> percentage, Map<String, Integer> count, Map<String, List<CardInfo>> basicLands) { int colorTotal = 0; ColoredManaSymbol colorToAdd = null; // Add up the totals for all colors, to keep track of the percentage a color is. for (Map.Entry<String, Integer> c : count.entrySet()) { colorTotal += c.getValue(); } // Keep adding basic lands until we fill the deck while (landsNeeded > 0) { double minPercentage = Integer.MIN_VALUE; for (ColoredManaSymbol color : ColoredManaSymbol.values()) { // What percentage of this color is requested double neededPercentage = percentage.get(color.toString()); // If there is a 0% need for basic lands of this color, skip it if (neededPercentage <= 0) { continue; } int currentCount = count.get(color.toString()); double thisPercentage = 0.0; // Calculate the percentage of lands so far that produce this color if (currentCount > 0) thisPercentage = (currentCount / (double) colorTotal) * 100.0; // Check if the color is the most "needed" (highest percentage) we have seen so far if (neededPercentage - thisPercentage > minPercentage) { // Put this color land forward to be added colorToAdd = color; minPercentage = (neededPercentage - thisPercentage); } } if (colorToAdd != null) { genPool.addCard(getBasicLand(colorToAdd, basicLands)); count.put(colorToAdd.toString(), count.get(colorToAdd.toString()) + 1); colorTotal++; landsNeeded--; } } }
/** * 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<String, List<CardInfo>> basicLands) { int tries = 0; int countNonBasic = 0; // Store the nonbasic lands (if any) we'll add List<Card> deckLands = new ArrayList<>(); // Calculates the percentage of colored mana symbols over all spells in the deck Map<String, Double> 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<Card> landCards = genPool.filterLands(CardRepository.instance.findCards(criteria)); int allCount = landCards.size(); Random random = new Random(); if (allCount > 0) { while (countNonBasic < landsCount / 2) { Card card = landCards.get(random.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<String, Integer> 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); }
/** * 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<CardInfo> cardPool = CardRepository.instance.findCards(criteria); int retrievedCount = cardPool.size(); List<DeckGeneratorCMC> deckCMCs = genPool.getCMCsForSpellCount(spellCount); Random random = new Random(); int count = 0; int reservesAdded = 0; if (retrievedCount > 0 && retrievedCount >= spellCount) { int tries = 0; while (count < spellCount) { Card card = cardPool.get(random.nextInt(retrievedCount)).getMockCard(); if (genPool.isValidSpellCard(card)) { int cardCMC = card.getManaCost().convertedManaCost(); for (DeckGeneratorCMC 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) { genPool.tryAddReserve(card, cardCMC); 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."); } }