diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java b/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java index e63389c2ef7..0cbfbfc6590 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/VLobby.java @@ -257,6 +257,7 @@ public class VLobby implements ILobbyView { final LobbySlot slot = lobby.getSlot(i); final FDeckChooser deckChooser = getDeckChooser(i); final FDeckChooser commanderDeckChooser = getCommanderDeckChooser(i); + final FDeckChooser tinyLeaderDeckChooser = getTinyLeaderDeckChooser(i); final PlayerPanel panel; final boolean isNewPanel; if (hasPanel) { @@ -272,6 +273,7 @@ public class VLobby implements ILobbyView { playersScroll.add(panel, constraints); deckChooser.restoreSavedState(); commanderDeckChooser.restoreSavedState(); + tinyLeaderDeckChooser.restoreSavedState(); if (i == 0) { changePlayerFocus(0); } @@ -291,8 +293,12 @@ public class VLobby implements ILobbyView { panel.setMayRemove(lobby.mayRemove(i)); panel.update(); - deckChooser.setIsAi(slot.getType() == LobbySlotType.AI); - if (fullUpdate && (type == LobbySlotType.LOCAL || type == LobbySlotType.AI)) { + final boolean isSlotAI = slot.getType() == LobbySlotType.AI; + + deckChooser.setIsAi(isSlotAI); + commanderDeckChooser.setIsAi(isSlotAI); + tinyLeaderDeckChooser.setIsAi(isSlotAI); + if (fullUpdate && (type == LobbySlotType.LOCAL || isSlotAI)) { selectDeck(i); } if (isNewPanel) { @@ -363,7 +369,7 @@ public class VLobby implements ILobbyView { @SuppressWarnings("serial") private void buildDeckPanels(final int playerIndex) { // Main deck - final FDeckChooser mainChooser = new FDeckChooser(null, false, GameType.Constructed, false); + final FDeckChooser mainChooser = new FDeckChooser(null, isPlayerAI(playerIndex), GameType.Constructed, false); mainChooser.getLstDecks().setSelectCommand(new UiCommand() { @Override public final void run() { selectMainDeck(playerIndex); @@ -385,7 +391,7 @@ public class VLobby implements ILobbyView { selectCommanderDeck(playerIndex); } });*/ - final FDeckChooser commanderChooser = new FDeckChooser(null, false, GameType.Commander, true); + final FDeckChooser commanderChooser = new FDeckChooser(null, isPlayerAI(playerIndex), GameType.Commander, true); commanderChooser.getLstDecks().setSelectCommand(new UiCommand() { @Override public final void run() { selectCommanderDeck(playerIndex); @@ -394,7 +400,7 @@ public class VLobby implements ILobbyView { commanderChooser.initialize(); commanderDeckChoosers.add(commanderChooser); - final FDeckChooser tinyLeaderChooser = new FDeckChooser(null, false, GameType.TinyLeaders, true); + final FDeckChooser tinyLeaderChooser = new FDeckChooser(null, isPlayerAI(playerIndex), GameType.TinyLeaders, true); tinyLeaderChooser.getLstDecks().setSelectCommand(new UiCommand() { @Override public final void run() { selectTinyLeadersDeck(playerIndex); diff --git a/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java b/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java index be176d263d7..5b901753a8f 100644 --- a/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java +++ b/forge-gui-desktop/src/main/java/forge/view/SimulateMatch.java @@ -161,10 +161,10 @@ public class SimulateMatch { private static void simulateSingleMatch(Match mc, int iGame, boolean outputGamelog) { - StopWatch sw = new StopWatch(); + final StopWatch sw = new StopWatch(); sw.start(); - Game g1 = mc.createGame(); + final Game g1 = mc.createGame(); // will run match in the same thread long startTime = System.currentTimeMillis(); diff --git a/forge-gui/res/deckgendecks/Commander.dat b/forge-gui/res/deckgendecks/Commander.dat index 32142b05d93..8a7434b8b67 100644 Binary files a/forge-gui/res/deckgendecks/Commander.dat and b/forge-gui/res/deckgendecks/Commander.dat differ diff --git a/forge-gui/res/deckgendecks/Modern.dat b/forge-gui/res/deckgendecks/Modern.dat index 6f7201b2c5f..13beb19934d 100644 Binary files a/forge-gui/res/deckgendecks/Modern.dat and b/forge-gui/res/deckgendecks/Modern.dat differ diff --git a/forge-gui/res/deckgendecks/Standard.dat b/forge-gui/res/deckgendecks/Standard.dat index 4deb78fbe16..b89787b14ca 100644 Binary files a/forge-gui/res/deckgendecks/Standard.dat and b/forge-gui/res/deckgendecks/Standard.dat differ diff --git a/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java b/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java index a17d1219e0a..c59e554d563 100644 --- a/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java +++ b/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java @@ -26,6 +26,11 @@ import java.util.*; public final class CardRelationMatrixGenerator { public static HashMap>>> cardPools = new HashMap<>(); + /** + To ensure that only cards with at least 14 connections (as 14*4+4=60) are included in the card based deck + generation pools + **/ + public static final int MIN_REQUIRED_CONNECTIONS = 14; public static boolean initialize(){ List formatStrings = new ArrayList<>(); @@ -112,7 +117,7 @@ public final class CardRelationMatrixGenerator { List> deckPool=new ArrayList<>(); int k=0; boolean excludeThisCard=false;//if there are too few cards with at least one connection - for (int j=0;j<20;++k){ + for (int j=0;j getMatrixDecks(GameFormat format, boolean isForAi){ final List decks = new ArrayList(); for(String card: CardRelationMatrixGenerator.cardPools.get(format.getName()).keySet()) { + //exclude non AI playables as keycards for AI decks + if(isForAi&&FModel.getMagicDb().getCommonCards().getUniqueByName(card).getRules().getAiHints().getRemAIDecks()){ + continue; + } decks.add(new CardThemedDeckGenerator(card, format, isForAi)); } return decks; diff --git a/forge-gui/src/main/java/forge/deck/DeckgenUtil.java b/forge-gui/src/main/java/forge/deck/DeckgenUtil.java index 1b48fc05bf7..e663cf86c8d 100644 --- a/forge-gui/src/main/java/forge/deck/DeckgenUtil.java +++ b/forge-gui/src/main/java/forge/deck/DeckgenUtil.java @@ -586,26 +586,14 @@ public class DeckgenUtil { gen = new CardThemedCommanderDeckBuilder(commander, selectedPartner,preSelectedCards,forAi,format); }else{ cardDb = FModel.getMagicDb().getCommonCards(); - ColorSet colorID; - colorID = commander.getRules().getColorIdentity(); - List comColors = new ArrayList(2); - if (colorID.hasWhite()) { comColors.add("White"); } - if (colorID.hasBlue()) { comColors.add("Blue"); } - if (colorID.hasBlack()) { comColors.add("Black"); } - if (colorID.hasRed()) { comColors.add("Red"); } - if (colorID.hasGreen()) { comColors.add("Green"); } - - if(comColors.size()==1){ - gen = new DeckGeneratorMonoColor(cardDb, format, comColors.get(0)); - }else if(comColors.size()==2){ - gen = new DeckGenerator2Color(cardDb, format, comColors.get(0), comColors.get(1)); - }else if(comColors.size()==3){ - gen = new DeckGenerator3Color(cardDb, format, comColors.get(0), comColors.get(1), comColors.get(2)); - }else if(comColors.size()==4){ - gen = new DeckGenerator4Color(cardDb, format, comColors.get(0), comColors.get(1), comColors.get(2), comColors.get(3)); - }else if(comColors.size()==5){ - gen = new DeckGenerator5Color(cardDb, format); - } + //shuffle first 400 random cards + Iterable colorList = Iterables.filter(format.getCardPool(cardDb).getAllCards(), + Predicates.compose(Predicates.or(new CardThemedDeckBuilder.MatchColorIdentity(commander.getRules().getColorIdentity()), + DeckGeneratorBase.COLORLESS_CARDS), PaperCard.FN_GET_RULES)); + List cardList = Lists.newArrayList(colorList); + Collections.shuffle(cardList, new Random()); + List shortList = cardList.subList(1, 400); + gen = new CardThemedCommanderDeckBuilder(commander, selectedPartner,shortList,forAi,format); } diff --git a/forge-gui/src/main/java/forge/limited/CardThemedCommanderDeckBuilder.java b/forge-gui/src/main/java/forge/limited/CardThemedCommanderDeckBuilder.java index ab2887b2e25..a08992016a5 100644 --- a/forge-gui/src/main/java/forge/limited/CardThemedCommanderDeckBuilder.java +++ b/forge-gui/src/main/java/forge/limited/CardThemedCommanderDeckBuilder.java @@ -1,77 +1,26 @@ package forge.limited; -import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; -import forge.card.*; -import forge.card.mana.ManaCost; -import forge.card.mana.ManaCostShard; -import forge.deck.CardPool; -import forge.deck.Deck; +import forge.card.CardRulesPredicates; +import forge.card.ColorSet; import forge.deck.DeckFormat; -import forge.deck.DeckSection; -import forge.deck.generation.DeckGenPool; -import forge.deck.generation.DeckGeneratorBase; -import forge.item.IPaperCard; import forge.item.PaperCard; import forge.model.FModel; -import forge.util.MyRandom; -import java.util.*; +import java.util.List; /** - * Limited format deck. + * Created by maustin on 28/02/2018. */ -public class CardThemedCommanderDeckBuilder extends DeckGeneratorBase { - @Override - protected final float getLandPercentage() { - return 0.44f; - } - @Override - protected final float getCreaturePercentage() { - return 0.33f; - } - @Override - protected final float getSpellPercentage() { - return 0.23f; - } +public class CardThemedCommanderDeckBuilder extends CardThemedDeckBuilder { - protected int targetSize; - protected int numSpellsNeeded; - protected int numCreaturesToStart; - protected int landsNeeded; - - protected final PaperCard commanderCard; - protected final PaperCard partnerCard; - - protected Predicate hasColor; - protected final List availableList; - protected final List aiPlayables; - protected final List deckList = new ArrayList<>(); - protected final List setsWithBasicLands = new ArrayList<>(); - protected List rankedColorList; - - // Views for aiPlayable - protected Iterable onColorCreatures; - protected Iterable onColorNonCreatures; - - protected static final boolean logToConsole = false; - protected static final boolean logColorsToConsole = false; - - - /** - * - * Constructor. - * - * @param dList - * Cards to build the deck from. - */ - public CardThemedCommanderDeckBuilder(PaperCard commanderCard0,PaperCard partner0, final List dList, boolean isForAI, DeckFormat format) { + public CardThemedCommanderDeckBuilder(PaperCard commanderCard0, PaperCard partner0, final List dList, boolean isForAI, DeckFormat format) { super(FModel.getMagicDb().getCommonCards(), format); this.availableList = dList; - commanderCard = commanderCard0; - partnerCard = partner0; + keyCard = commanderCard0; + secondKeyCard = partner0; // remove Unplayables if(isForAI) { final Iterable playables = Iterables.filter(availableList, @@ -82,700 +31,49 @@ public class CardThemedCommanderDeckBuilder extends DeckGeneratorBase { } this.availableList.removeAll(aiPlayables); targetSize=format.getMainRange().getMinimum(); - colors = commanderCard.getRules().getColorIdentity(); - colors = ColorSet.fromMask(colors.getColor() | commanderCard.getRules().getColorIdentity().getColor()); - if(partnerCard!=null) { - colors = ColorSet.fromMask(colors.getColor() | partnerCard.getRules().getColorIdentity().getColor()); + colors = keyCard.getRules().getColorIdentity(); + colors = ColorSet.fromMask(colors.getColor() | keyCard.getRules().getColorIdentity().getColor()); + if(secondKeyCard!=null) { + colors = ColorSet.fromMask(colors.getColor() | secondKeyCard.getRules().getColorIdentity().getColor()); targetSize--; } numSpellsNeeded = ((Double)Math.floor(targetSize*(getCreaturePercentage()+getSpellPercentage()))).intValue(); numCreaturesToStart = ((Double)Math.ceil(targetSize*(getCreaturePercentage()))).intValue(); landsNeeded = ((Double)Math.ceil(targetSize*(getLandPercentage()))).intValue();; if (logColorsToConsole) { - System.out.println(commanderCard.getName()); + System.out.println(keyCard.getName()); System.out.println("Pre Colors: " + colors.toEnumSet().toString()); } findBasicLandSets(); } - + @Override + protected void addKeyCards(){ + //do nothing as keycards are commander/partner and are added by the DeckGenUtils + } @Override - public CardPool getDeck(final int size, final boolean forAi) { - return buildDeck().getMain(); + protected void addLandKeyCards(){ + //do nothing as keycards are commander/partner and are added by the DeckGenUtils } - /** - *

- * buildDeck. - *

- * - * @return the new Deck. - */ - @SuppressWarnings("unused") - public Deck buildDeck() { - // 1. Prepare - hasColor = Predicates.or(new MatchColorIdentity(colors), COLORLESS_CARDS); - if (logColorsToConsole) { - System.out.println(commanderCard.getName()); - System.out.println("Colors: " + colors.toEnumSet().toString()); - } - Iterable colorList = Iterables.filter(aiPlayables, - Predicates.compose(hasColor, PaperCard.FN_GET_RULES)); - rankedColorList = Lists.newArrayList(colorList); - onColorCreatures = Iterables.filter(rankedColorList, - Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard.FN_GET_RULES)); - onColorNonCreatures = Iterables.filter(rankedColorList, - Predicates.compose(CardRulesPredicates.Presets.IS_NON_CREATURE_SPELL, PaperCard.FN_GET_RULES)); - // Guava iterables do not copy the collection contents, instead they act - // as filters and iterate over _source_ collection each time. So even if - // aiPlayable has changed, there is no need to create a new iterable. - - // 2. Add any planeswalkers - removed - treat as non-creature - - // 3. Add creatures, trying to follow mana curve - - addManaCurveCards(onColorCreatures, numCreaturesToStart, "Creatures"); - if (logToConsole) { - System.out.println("Post Creatures : " + deckList.size()); - } - - // 4.Try to fill up to num needed with on-color non-creature cards - addManaCurveCards(onColorNonCreatures, numSpellsNeeded - deckList.size(), "Spells"); - if (logToConsole) { - System.out.println("Post Spells : " + deckList.size()); - } - - // 5.If we couldn't get enough, try to fill up with on-color cards - addCards(rankedColorList, numSpellsNeeded - deckList.size()); - if (logToConsole) { - System.out.println("Post more creatures : " + deckList.size()); - } - - // 6. If there are still on-color cards, and the average cmc is low, add - // extras. - double avCMC=getAverageCMC(deckList); - int maxCMC=getMaxCMC(deckList); - if (deckList.size() == numSpellsNeeded && avCMC < 4) { - addLowCMCCard(); - if(targetSize>60){ - addLowCMCCard(); - } - } - if (deckList.size() >= numSpellsNeeded && avCMC < 3 && maxCMC<6) { - addLowCMCCard(); - } - if (deckList.size() >= numSpellsNeeded && avCMC < 2.5 && maxCMC<5) { - addLowCMCCard(); - if(targetSize>60){ - addLowCMCCard(); - } - } - if (logToConsole) { - System.out.println("Post lowcoc : " + deckList.size()); - } - -/* // 7. If not enough cards yet, try to add a third color, - // to try and avoid adding purely random cards. - addThirdColorCards(numSpellsNeeded - deckList.size()); - if (logColorsToConsole) { - System.out.println("Post 3rd colour : " + deckList.size()); - System.out.println("Colors: " + colors.toEnumSet().toString()); - }*/ - - // 8. Check for DeckNeeds cards. - //checkRemRandomDeckCards(); - no need - - // 9. If there are still less than 22 non-land cards add off-color - // cards. This should be avoided. - addRandomCards(numSpellsNeeded - deckList.size()); - if (logToConsole) { - System.out.println("Post Randoms : " + deckList.size()); - } - - List duals = getDualLandList(); - addNonBasicLands(); - if (logToConsole) { - System.out.println("Post Nonbasic lands : " + deckList.size()); - } - - checkEvolvingWilds(); - - // 11. Fill up with basic lands. - final int[] clrCnts = calculateLandNeeds(); - - // Add dual lands - if (clrCnts.length>1) { - - for (String s : duals) { - this.cardCounts.put(s, 0); - } - } - - - if (landsNeeded > 0) { - addLands(clrCnts); - } - if (logToConsole) { - System.out.println("Post Lands : " + deckList.size()); - } - if (commanderCard.getRules().getColorIdentity().isColorless()&&landsNeeded>0){ - // 10. Add non-basic lands that were drafted. - addWastesIfRequired(); - } - fixDeckSize(); - if (logToConsole) { - System.out.println("Post Size fix : " + deckList.size()); - } - - //Create Deck - final Deck result = new Deck(generateName()); - result.getMain().add(deckList); - - //Add remaining non-land colour matching cards to sideboard - final CardPool cp = result.getOrCreate(DeckSection.Sideboard); - Iterable potentialSideboard = Iterables.filter(aiPlayables, - Predicates.and(Predicates.compose(hasColor, PaperCard.FN_GET_RULES), - Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES))); - int i=0; - while(i<15 && potentialSideboard.iterator().hasNext()){ - PaperCard sbCard = potentialSideboard.iterator().next(); - cp.add(sbCard); - aiPlayables.remove(sbCard); - rankedColorList.remove(sbCard); - - - ++i; - } - if (logToConsole) { - debugFinalDeck(); - } - return result; - + @Override + protected void addThirdColorCards(int num) { + //do nothing as we cannot add extra colours beyond commanders } - /** - * If evolving wilds is in the deck and there are fewer than 4 spaces for basic lands - remove evolving wilds - */ - protected void checkEvolvingWilds(){ - List evolvingWilds = Lists.newArrayList(Iterables.filter(deckList,PaperCard.Predicates.name("Evolving Wilds"))); - if((evolvingWilds.size()>0 && landsNeeded<4 ) || colors.countColors()<2){ - deckList.removeAll(evolvingWilds); - landsNeeded=landsNeeded+evolvingWilds.size(); - aiPlayables.addAll(evolvingWilds); - } + @Override + protected void updateColors(){ + //do nothing as we cannot deviate from commander colours } - - - protected void addLowCMCCard(){ - final Iterable nonLands = Iterables.filter(aiPlayables, - Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES)); - final PaperCard card = Iterables.getFirst(nonLands, null); - if (card != null) { - deckList.add(card); - aiPlayables.remove(card); - rankedColorList.remove(card); - - landsNeeded--; - if (logToConsole) { - System.out.println("Low CMC: " + card.getName()); - } - } - } - - /** - * Set the basic land pool - * @param edition - * @return - */ - protected boolean setBasicLandPool(String edition){ - Predicate isSetBasicLand; - if (edition !=null){ - isSetBasicLand = Predicates.and(IPaperCard.Predicates.printedInSet(edition), - Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES)); - }else{ - isSetBasicLand = Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES); - } - - landPool = new DeckGenPool(format.getCardPool(fullCardDB).getAllCards(isSetBasicLand)); - return landPool.contains("Plains"); - } - /** * Generate a descriptive name. * * @return name */ - private String generateName() { - return commanderCard.getName() +" based commander deck"; - } - - /** - * Print out listing of all cards for debugging. - */ - private void debugFinalDeck() { - int i = 0; - System.out.println("DECK"); - for (final PaperCard c : deckList) { - i++; - System.out.println(i + ". " + c.toString() + ": " + c.getRules().getManaCost().toString()); - } - i = 0; - System.out.println("NOT PLAYABLE"); - for (final PaperCard c : availableList) { - i++; - System.out.println(i + ". " + c.toString() + ": " + c.getRules().getManaCost().toString()); - } - i = 0; - System.out.println("NOT PICKED"); - for (final PaperCard c : aiPlayables) { - i++; - System.out.println(i + ". " + c.toString() + ": " + c.getRules().getManaCost().toString()); - } - } - - /** - * If the deck does not have 40 cards, fix it. This method should not be - * called if the stuff above it is working correctly. - * - */ - private void fixDeckSize() { - while (deckList.size() > targetSize) { - if (logToConsole) { - System.out.println("WARNING: Fixing deck size, currently " + deckList.size() + " cards."); - } - final PaperCard c = deckList.get(MyRandom.getRandom().nextInt(deckList.size() - 1)); - deckList.remove(c); - aiPlayables.add(c); - if (logToConsole) { - System.out.println(" - Removed " + c.getName() + " randomly."); - } - } - if(deckList.size()==targetSize){ - return; - } - - Predicate possibleFromFullPool = new Predicate() { - @Override - public boolean apply(PaperCard card) { - return format.isLegalCard(card) - &&!card.getRules().getManaCost().isPureGeneric() - && colors.containsAllColorsFrom(card.getRules().getColorIdentity().getColor()) - && !deckList.contains(card) - &&!card.getRules().getAiHints().getRemAIDecks() - &&!card.getRules().getAiHints().getRemRandomDecks() - &&!card.getRules().getMainPart().getType().isLand(); - } - }; - List randomPool = Lists.newArrayList(pool.getAllCards(possibleFromFullPool)); - Collections.shuffle(randomPool,new Random()); - Iterator iRandomPool=randomPool.iterator(); - while (deckList.size() < targetSize) { - if (logToConsole) { - System.out.println("WARNING: Fixing deck size, currently " + deckList.size() + " cards."); - } - PaperCard randomCard = iRandomPool.next(); - deckList.add(randomCard); - if (logToConsole) { - System.out.println(" - Added " + randomCard.getName() + " randomly."); - } - } - } - - /** - * Find the sets that have basic lands for the available cards. - */ - private void findBasicLandSets() { - final Set sets = new HashSet<>(); - for (final PaperCard cp : aiPlayables) { - final CardEdition ee = FModel.getMagicDb().getEditions().get(cp.getEdition()); - if( !sets.contains(cp.getEdition()) && CardEdition.Predicates.hasBasicLands.apply(ee)) { - sets.add(cp.getEdition()); - } - } - setsWithBasicLands.addAll(sets); - if (setsWithBasicLands.isEmpty()) { - setsWithBasicLands.add("BFZ"); - } - } - - /** - * Add lands to fulfill the given color counts. - * - * @param clrCnts - * counts of lands needed, by color - */ - private void addLands(final int[] clrCnts) { - // basic lands that are available in the deck - final Iterable basicLands = Iterables.filter(aiPlayables, Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES)); - final Set snowLands = new HashSet(); - - // total of all ClrCnts - int totalColor = 0; - int numColors = 0; - for (int i = 0; i < 5; i++) { - totalColor += clrCnts[i]; - if (clrCnts[i] > 0) { - numColors++; - } - } - /*if (totalColor == 0) { - for (int j = 0; j < nLand; j++) { - deckList.add(getBasicLand(i)); - } - }*/ - - // do not update landsNeeded until after the loop, because the - // calculation involves landsNeeded - for (int i = 0; i < 5; i++) { - if (clrCnts[i] > 0) { - // calculate number of lands for each color - float p = (float) clrCnts[i] / (float) totalColor; - if (numColors == 2) { - // In the normal two-color case, constrain to within 40% and 60% so that the AI - // doesn't put too few lands of the lesser color, risking getting screwed on that color. - // Don't do this for the odd case where a third color had to be added to the deck. - p = Math.min(Math.max(p, 0.4f), 0.6f); - } - int nLand = Math.round(landsNeeded * p); // desired truncation to int - if (logToConsole) { - System.out.printf("Basics[%s]: %d/%d = %f%% = %d cards%n", MagicColor.Constant.BASIC_LANDS.get(i), clrCnts[i], totalColor, 100*p, nLand); - } - - // if appropriate snow-covered lands are available, add them - for (final PaperCard cp : basicLands) { - if (cp.getName().equals(MagicColor.Constant.SNOW_LANDS.get(i))) { - snowLands.add(cp); - nLand--; - } - } - - for (int j = 0; j < nLand; j++) { - deckList.add(getBasicLand(i)); - } - } - } - - // A common problem at this point is that p in the above loop was exactly 1/2, - // and nLand rounded up for both colors, so that one too many lands was added. - // So if the deck size is > 60, remove the last land added. - // Otherwise, the fixDeckSize() method would remove random cards. - while (deckList.size() > targetSize) { - deckList.remove(deckList.size() - 1); - } - - deckList.addAll(snowLands); - aiPlayables.removeAll(snowLands); - rankedColorList.remove(snowLands); - } - - /** - * Get basic land. - * - * @param basicLand - * the set to take basic lands from (pass 'null' for random). - * @return card - */ - private PaperCard getBasicLand(final int basicLand) { - String set; - if (setsWithBasicLands.size() > 1) { - set = setsWithBasicLands.get(MyRandom.getRandom().nextInt(setsWithBasicLands.size() - 1)); - } else { - set = setsWithBasicLands.get(0); - } - return FModel.getMagicDb().getCommonCards().getCard(MagicColor.Constant.BASIC_LANDS.get(basicLand), set); - } - - /** - * Only adds wastes if present in the card pool but if present adds them all - */ - private void addWastesIfRequired(){ - Iterable wastes = Iterables.filter(aiPlayables,PaperCard.Predicates.name("Wastes")); - if(wastes.iterator().hasNext()){ - PaperCard waste = wastes.iterator().next(); - while(landsNeeded>0) { - deckList.add(waste); - landsNeeded--; - } - aiPlayables.remove(waste); - rankedColorList.remove(waste); - } - } - - /** - * Attempt to optimize basic land counts according to color representation. - * Only consider colors that are supposed to be in the deck. It's not worth - * putting one land in for that random off-color card we had to stick in at - * the end... - * - * @return CCnt - */ - private int[] calculateLandNeeds() { - final int[] clrCnts = { 0,0,0,0,0 }; - // count each card color using mana costs - for (final PaperCard cp : deckList) { - final ManaCost mc = cp.getRules().getManaCost(); - - // count each mana symbol in the mana cost - for (final ManaCostShard shard : mc) { - for ( int i = 0 ; i < MagicColor.WUBRG.length; i++ ) { - final byte c = MagicColor.WUBRG[i]; - - if ( shard.canBePaidWithManaOfColor(c) && colors.hasAnyColor(c)) { - clrCnts[i]++; - } - } - } - } - return clrCnts; - } - - /** - * Add non-basic lands to the deck. - */ - private void addNonBasicLands() { - final Iterable lands = Iterables.filter(aiPlayables, - Predicates.compose(CardRulesPredicates.Presets.IS_NONBASIC_LAND, PaperCard.FN_GET_RULES)); - List landsToAdd = new ArrayList<>(); - int minBasics;//Keep a minimum number of basics to ensure playable decks - if(colors.isMonoColor()){ - minBasics=Math.round((r.nextInt(15)+6)*targetSize/60); - }else{ - minBasics=Math.round((r.nextInt(8)+6)*targetSize/60); - } - - - for (final PaperCard card : lands) { - if (landsNeeded > minBasics) { - // Throw out any dual-lands for the wrong colors. Assume - // everything else is either - // (a) dual-land of the correct two colors, or - // (b) a land that generates colorless mana and has some other - // beneficial effect. - if (!card.getRules().getColorIdentity().isColorless() && card.getRules().getColorIdentity().getSharedColors(colors).countColors()==0){ - //skip as does not match colours - if (logToConsole) { - System.out.println("Excluding NonBasicLand: " + card.getName()); - } - continue; - } - if (!inverseDLands.contains(card.getName())&&!dLands.contains(card.getName())&&r.nextInt(100)<90) { - landsToAdd.add(card); - landsNeeded--; - if (logToConsole) { - System.out.println("NonBasicLand[" + landsNeeded + "]:" + card.getName()); - } - } - } - } - deckList.addAll(landsToAdd); - aiPlayables.removeAll(landsToAdd); - rankedColorList.removeAll(landsToAdd); - } - - - /** - * Add random cards to the deck. - * - * @param num - * number to add - */ - private void addRandomCards(int num) { - Predicate possibleFromFullPool = new Predicate() { - @Override - public boolean apply(PaperCard card) { - return format.isLegalCard(card) - &&!card.getRules().getManaCost().isPureGeneric() - && colors.containsAllColorsFrom(card.getRules().getColorIdentity().getColor()) - && !deckList.contains(card) - &&!card.getRules().getAiHints().getRemAIDecks() - &&!card.getRules().getAiHints().getRemRandomDecks() - &&!card.getRules().getMainPart().getType().isLand(); - } - }; - List randomPool = Lists.newArrayList(pool.getAllCards(possibleFromFullPool)); - Collections.shuffle(randomPool,new Random()); - Iterator iRandomPool=randomPool.iterator(); - for(int i=0;i cards, int num) { - List cardsToAdd = new ArrayList<>(); - for (final PaperCard card : cards) { - if(card.getRules().getMainPart().getType().isLand()){ - continue; - } - if (num +1 > 0) { - cardsToAdd.add(card); - if (logToConsole) { - System.out.println("Extra needed[" + num + "]:" + card.getName() + " (" + card.getRules().getManaCost() + ")"); - } - num--; - } else { - break; - } - } - deckList.addAll(cardsToAdd); - aiPlayables.removeAll(cardsToAdd); - rankedColorList.removeAll(cardsToAdd); - } - - /** - * Add cards to the deck, trying to follow some mana curve. Trying to - * have generous limits at each cost, but perhaps still too strict. But - * we're trying to prevent the AI from adding everything at a single cost. - * - * @param creatures - * cards to choose from - * @param num - * number to add - */ - private void addManaCurveCards(final Iterable creatures, int num, String nameForLog) { -/* // Add the deck card - if(commanderCard.getRules().getMainPart().getType().isCreature()) { - keyCards = Iterables.filter(aiPlayables,PaperCard.Predicates.name(commanderCard.getName())); - final List keyCardList = Lists.newArrayList(keyCards); - deckList.addAll(keyCardList); - aiPlayables.removeAll(keyCardList); - rankedColorList.removeAll(keyCardList); - }*/ - final Map targetCMCs = new HashMap<>(); - targetCMCs.put(1,Math.round((r.nextInt(4)+2)*targetSize/60));//2 - targetCMCs.put(2,Math.round((r.nextInt(5)+5)*targetSize/60));//6 - targetCMCs.put(3,Math.round((r.nextInt(5)+6)*targetSize/60));//7 - targetCMCs.put(4,Math.round((r.nextInt(3)+3)*targetSize/60));//4 - targetCMCs.put(5,Math.round((r.nextInt(3)+3)*targetSize/60));//3 - targetCMCs.put(6,Math.round((r.nextInt(3)+1)*targetSize/60));//2 - - - final Map creatureCosts = new HashMap(); - for (int i = 1; i < 7; i++) { - creatureCosts.put(i, 0); - } - final Predicate filter = Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, - PaperCard.FN_GET_RULES); - for (final IPaperCard creature : Iterables.filter(deckList, filter)) { - int cmc = creature.getRules().getManaCost().getCMC(); - if (cmc < 1) { - cmc = 1; - } else if (cmc > 6) { - cmc = 6; - } - creatureCosts.put(cmc, creatureCosts.get(cmc) + 1); - } - - List creaturesToAdd = new ArrayList<>(); - for (final PaperCard card : creatures) { - int cmc = card.getRules().getManaCost().getCMC(); - if (cmc < 1) { - cmc = 1; - } else if (cmc > 6) { - cmc = 6; - } - final Integer currentAtCmc = creatureCosts.get(cmc); - boolean willAddCreature = false; - if (cmc <= 1 && currentAtCmc < targetCMCs.get(1)) { - willAddCreature = true; - } else if (cmc == 2 && currentAtCmc < targetCMCs.get(2)) { - willAddCreature = true; - } else if (cmc == 3 && currentAtCmc < targetCMCs.get(3)) { - willAddCreature = true; - } else if (cmc == 4 && currentAtCmc < targetCMCs.get(4)) { - willAddCreature = true; - } else if (cmc == 5 && currentAtCmc < targetCMCs.get(5)) { - willAddCreature = true; - } else if (cmc >= 6 && currentAtCmc < targetCMCs.get(6)) { - willAddCreature = true; - } - - if (willAddCreature) { - creaturesToAdd.add(card); - num--; - creatureCosts.put(cmc, creatureCosts.get(cmc) + 1); - if (logToConsole) { - System.out.println(nameForLog+"[" + num + "]:" + card.getName() + " (" + card.getRules().getManaCost() + ")"); - } - } else { - if (logToConsole) { - System.out.println(card.getName() + " not added because CMC " + card.getRules().getManaCost().getCMC() - + " has " + currentAtCmc + " already."); - } - } - if (num <= 0) { - break; - } - } - deckList.addAll(creaturesToAdd); - aiPlayables.removeAll(creaturesToAdd); - rankedColorList.removeAll(creaturesToAdd); - } - - /** - * Calculate average CMC. - * - * @param cards - * cards to choose from - * @return the average - */ - private static double getAverageCMC(final List cards) { - double sum = 0.0; - for (final IPaperCard cardPrinted : cards) { - sum += cardPrinted.getRules().getManaCost().getCMC(); - } - return sum / cards.size(); - } - - /** - * Calculate max CMC. - * - * @param cards - * cards to choose from - * @return the average - */ - private static int getMaxCMC(final List cards) { - int max = 0; - for (final IPaperCard cardPrinted : cards) { - if(cardPrinted.getRules().getManaCost().getCMC()>max) { - max = cardPrinted.getRules().getManaCost().getCMC(); - } - } - return max; - } - - /** - * @return the colors - */ - public ColorSet getColors() { - return colors; - } - - /** - * @param colors0 - * the colors to set - */ - public void setColors(final ColorSet colors0) { - colors = colors0; - } - - /** - * @return the aiPlayables - */ - public List getAiPlayables() { - return aiPlayables; + @Override + protected String generateName() { + return keyCard.getName() +" based commander deck"; } } diff --git a/forge-gui/src/main/java/forge/limited/CardThemedDeckBuilder.java b/forge-gui/src/main/java/forge/limited/CardThemedDeckBuilder.java index 4c6ca201730..bcbbebf6e65 100644 --- a/forge-gui/src/main/java/forge/limited/CardThemedDeckBuilder.java +++ b/forge-gui/src/main/java/forge/limited/CardThemedDeckBuilder.java @@ -1,22 +1,10 @@ package forge.limited; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; - -import forge.card.CardEdition; -import forge.card.CardRules; -import forge.card.CardRulesPredicates; -import forge.card.ColorSet; -import forge.card.MagicColor; +import forge.card.*; import forge.card.mana.ManaCost; import forge.card.mana.ManaCostShard; import forge.deck.CardPool; @@ -25,39 +13,43 @@ import forge.deck.DeckFormat; import forge.deck.DeckSection; import forge.deck.generation.DeckGenPool; import forge.deck.generation.DeckGeneratorBase; +import forge.deck.generation.IDeckGenPool; import forge.game.GameFormat; import forge.item.IPaperCard; import forge.item.PaperCard; import forge.model.FModel; import forge.util.MyRandom; +import java.util.*; + /** * Limited format deck. */ public class CardThemedDeckBuilder extends DeckGeneratorBase { @Override protected final float getLandPercentage() { - return 0.44f; + return 0.41f; } @Override protected final float getCreaturePercentage() { - return 0.33f; + return 0.34f; } @Override protected final float getSpellPercentage() { - return 0.23f; + return 0.25f; } - protected int numSpellsNeeded = 35; - protected int landsNeeded = 25; + protected int targetSize; + protected int numSpellsNeeded; + protected int numCreaturesToStart; + protected int landsNeeded; - protected final PaperCard keyCard; - protected final PaperCard secondKeyCard; + protected PaperCard keyCard; + protected PaperCard secondKeyCard; - protected DeckColors deckColors; protected Predicate hasColor; - protected final List availableList; - protected final List aiPlayables; + protected List availableList; + protected List aiPlayables; protected final List deckList = new ArrayList<>(); protected final List setsWithBasicLands = new ArrayList<>(); protected List rankedColorList; @@ -71,6 +63,10 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { protected static final boolean logColorsToConsole = false; + public CardThemedDeckBuilder(IDeckGenPool pool, DeckFormat format){ + super(pool,format); + } + /** * * Constructor. @@ -78,8 +74,8 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { * @param dList * Cards to build the deck from. */ - public CardThemedDeckBuilder(PaperCard keyCard0,PaperCard secondKeyCard0, final List dList, GameFormat format, boolean isForAI) { - super(FModel.getMagicDb().getCommonCards(), DeckFormat.Limited, format.getFilterPrinted()); + public CardThemedDeckBuilder(PaperCard keyCard0, PaperCard secondKeyCard0, final List dList, GameFormat format, boolean isForAI) { + super(FModel.getMagicDb().getCommonCards(), DeckFormat.Constructed, format.getFilterPrinted()); this.availableList = dList; keyCard=keyCard0; secondKeyCard=secondKeyCard0; @@ -92,13 +88,21 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { this.aiPlayables = Lists.newArrayList(availableList); } this.availableList.removeAll(aiPlayables); - deckColors = new DeckColors(); + targetSize=DeckFormat.Constructed.getMainRange().getMinimum(); + FullDeckColors deckColors = new FullDeckColors(); + int cardCount=0; + //get colours for first 20 cards for(PaperCard c:getAiPlayables()){ if(deckColors.canChoseMoreColors()){ deckColors.addColorsOf(c); + cardCount++; + } + if(cardCount>20){ + break; } } colors = deckColors.getChosenColors(); + if (logColorsToConsole) { System.out.println(keyCard.getName()); System.out.println("Pre Colors: " + colors.toEnumSet().toString()); @@ -109,6 +113,9 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { if(!colors.hasAllColors(secondKeyCard.getRules().getColorIdentity().getColor())){ colors = ColorSet.fromMask(colors.getColor() | secondKeyCard.getRules().getColorIdentity().getColor()); } + numSpellsNeeded = ((Double)Math.floor(targetSize*(getCreaturePercentage()+getSpellPercentage()))).intValue(); + numCreaturesToStart = ((Double)Math.ceil(targetSize*(getCreaturePercentage()))).intValue(); + landsNeeded = ((Double)Math.ceil(targetSize*(getLandPercentage()))).intValue();; if (logColorsToConsole) { System.out.println(keyCard.getName()); System.out.println("Pre Colors: " + colors.toEnumSet().toString()); @@ -117,12 +124,24 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { } - @Override public CardPool getDeck(final int size, final boolean forAi) { return buildDeck().getMain(); } + protected void updateColors(){ + //update colors + FullDeckColors finalDeckColors = new FullDeckColors(); + for(PaperCard c:deckList){ + if(finalDeckColors.canChoseMoreColors()){ + finalDeckColors.addColorsOf(c); + } + } + colors = finalDeckColors.getChosenColors(); + if (logColorsToConsole) { + System.out.println("Final Colors: " + colors.toEnumSet().toString()); + } + } /** *

* buildDeck. @@ -143,45 +162,31 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { rankedColorList = Lists.newArrayList(colorList); onColorCreatures = Iterables.filter(rankedColorList, Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard.FN_GET_RULES)); - // Add the deck card - if(!keyCard.getRules().getMainPart().getType().isLand()&&!keyCard.getRules().getMainPart().getType().isCreature()) { - keyCards = Iterables.filter(aiPlayables,PaperCard.Predicates.name(keyCard.getName())); - final List keyCardList = Lists.newArrayList(keyCards); - deckList.addAll(keyCardList); - aiPlayables.removeAll(keyCardList); - rankedColorList.removeAll(keyCardList); - } - // Add the deck card - if(!secondKeyCard.getRules().getMainPart().getType().isLand()&&!keyCard.getRules().getMainPart().getType().isCreature()) { - Iterable secondKeyCards = Iterables.filter(aiPlayables,PaperCard.Predicates.name(secondKeyCard.getName())); - final List keyCardList = Lists.newArrayList(secondKeyCards); - deckList.addAll(keyCardList); - aiPlayables.removeAll(keyCardList); - rankedColorList.removeAll(keyCardList); - } onColorNonCreatures = Iterables.filter(rankedColorList, Predicates.compose(CardRulesPredicates.Presets.IS_NON_CREATURE_SPELL, PaperCard.FN_GET_RULES)); // Guava iterables do not copy the collection contents, instead they act // as filters and iterate over _source_ collection each time. So even if // aiPlayable has changed, there is no need to create a new iterable. - // 2. Add any planeswalkers - removed - treat as non-creature + // 2. Add keycards + + addKeyCards(); // 3. Add creatures, trying to follow mana curve - addManaCurveCreatures(onColorCreatures, 20); + addManaCurveCards(onColorCreatures, numCreaturesToStart, "Creatures"); if (logToConsole) { System.out.println("Post Creatures : " + deckList.size()); } // 4.Try to fill up to num needed with on-color non-creature cards - addNonCreatures(onColorNonCreatures, numSpellsNeeded - deckList.size()); + addManaCurveCards(onColorNonCreatures, numSpellsNeeded - deckList.size(), "Spells"); if (logToConsole) { System.out.println("Post Spells : " + deckList.size()); } - // 5.If we couldn't get enough, try to fill up with on-color creature cards - addCreatures(onColorCreatures, numSpellsNeeded - deckList.size()); + // 5.If we couldn't get enough, try to fill up with on-color cards + addCards(rankedColorList, numSpellsNeeded - deckList.size()); if (logToConsole) { System.out.println("Post more creatures : " + deckList.size()); } @@ -190,14 +195,20 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { // extras. double avCMC=getAverageCMC(deckList); int maxCMC=getMaxCMC(deckList); - if (deckList.size() >= numSpellsNeeded && avCMC < 4) { + if (deckList.size() == numSpellsNeeded && avCMC < 4) { addLowCMCCard(); + if(targetSize>60){ + addLowCMCCard(); + } } if (deckList.size() >= numSpellsNeeded && avCMC < 3 && maxCMC<6) { addLowCMCCard(); } if (deckList.size() >= numSpellsNeeded && avCMC < 2.5 && maxCMC<5) { addLowCMCCard(); + if(targetSize>60){ + addLowCMCCard(); + } } if (logToConsole) { System.out.println("Post lowcoc : " + deckList.size()); @@ -220,19 +231,12 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { if (logToConsole) { System.out.println("Post Randoms : " + deckList.size()); } - //update colors - FullDeckColors finalDeckColors = new FullDeckColors(); - for(PaperCard c:deckList){ - if(finalDeckColors.canChoseMoreColors()){ - finalDeckColors.addColorsOf(c); - } - } - colors = finalDeckColors.getChosenColors(); - if (logColorsToConsole) { - System.out.println("Final Colors: " + colors.toEnumSet().toString()); - } - // 10. Add non-basic lands that were drafted. - addWastesIfRequired(); + + updateColors(); + + addLandKeyCards(); + + // 10. Add non-basic lands List duals = getDualLandList(); addNonBasicLands(); if (logToConsole) { @@ -257,9 +261,13 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { addLands(clrCnts); } if (logToConsole) { + System.out.println("Lands needed : " + landsNeeded); System.out.println("Post Lands : " + deckList.size()); } - fixDeckSize(clrCnts); + if (keyCard.getRules().getColorIdentity().isColorless()&&landsNeeded>0){ + addWastesIfRequired(); + } + fixDeckSize(); if (logToConsole) { System.out.println("Post Size fix : " + deckList.size()); } @@ -278,6 +286,8 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { PaperCard sbCard = potentialSideboard.iterator().next(); cp.add(sbCard); aiPlayables.remove(sbCard); + rankedColorList.remove(sbCard); + ++i; } @@ -288,6 +298,61 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { } + protected void addKeyCards(){ + // Add the first keycard if not land + if(!keyCard.getRules().getMainPart().getType().isLand()) { + keyCards = Iterables.filter(aiPlayables,PaperCard.Predicates.name(keyCard.getName())); + final List keyCardList = Lists.newArrayList(keyCards); + deckList.addAll(keyCardList); + aiPlayables.removeAll(keyCardList); + rankedColorList.removeAll(keyCardList); + } + // Add the second keycard if not land + if(!secondKeyCard.getRules().getMainPart().getType().isLand()) { + Iterable secondKeyCards = Iterables.filter(aiPlayables,PaperCard.Predicates.name(secondKeyCard.getName())); + final List keyCardList = Lists.newArrayList(secondKeyCards); + deckList.addAll(keyCardList); + aiPlayables.removeAll(keyCardList); + rankedColorList.removeAll(keyCardList); + } + } + + protected void addLandKeyCards(){ + // Add the deck card + if(keyCard.getRules().getMainPart().getType().isLand()) { + keyCards = Iterables.filter(aiPlayables,PaperCard.Predicates.name(keyCard.getName())); + final List keyCardList = Lists.newArrayList(keyCards); + deckList.addAll(keyCardList); + aiPlayables.removeAll(keyCardList); + rankedColorList.removeAll(keyCardList); + landsNeeded--; + } + // Add the deck card + if(secondKeyCard.getRules().getMainPart().getType().isLand()) { + Iterable secondKeyCards = Iterables.filter(aiPlayables,PaperCard.Predicates.name(secondKeyCard.getName())); + final List keyCardList = Lists.newArrayList(secondKeyCards); + deckList.addAll(keyCardList); + aiPlayables.removeAll(keyCardList); + rankedColorList.removeAll(keyCardList); + landsNeeded--; + } + } + + public static class MatchColorIdentity implements Predicate { + private final ColorSet allowedColor; + + public MatchColorIdentity(ColorSet color) { + allowedColor = color; + } + + @Override + public boolean apply(CardRules subject) { + ManaCost mc = subject.getManaCost(); + boolean generic = mc.isPureGeneric(); + return ((allowedColor.containsAllColorsFrom(subject.getColorIdentity().getColor()))); + } + } + /** * If evolving wilds is in the deck and there are fewer than 4 spaces for basic lands - remove evolving wilds */ @@ -300,308 +365,13 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { } } - - protected void addLowCMCCard(){ - final Iterable nonLands = Iterables.filter(rankedColorList, - Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES)); - final PaperCard card = Iterables.getFirst(nonLands, null); - if (card != null) { - deckList.add(card); - aiPlayables.remove(card); - landsNeeded--; - if (logToConsole) { - System.out.println("Low CMC: " + card.getName()); - } - } - } - - /** - * Set the basic land pool - * @param edition - * @return - */ - protected boolean setBasicLandPool(String edition){ - Predicate isSetBasicLand; - if (edition !=null){ - isSetBasicLand = Predicates.and(IPaperCard.Predicates.printedInSet(edition), - Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES)); - }else{ - isSetBasicLand = Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES); - } - - landPool = new DeckGenPool(format.getCardPool(fullCardDB).getAllCards(isSetBasicLand)); - return landPool.contains("Plains"); - } - - /** - * Generate a descriptive name. - * - * @return name - */ - private String generateName() { - return keyCard.getName() + " - " + secondKeyCard.getName() +" based deck"; - } - - /** - * Print out listing of all cards for debugging. - */ - private void debugFinalDeck() { - int i = 0; - System.out.println("DECK"); - for (final PaperCard c : deckList) { - i++; - System.out.println(i + ". " + c.toString() + ": " + c.getRules().getManaCost().toString()); - } - i = 0; - System.out.println("NOT PLAYABLE"); - for (final PaperCard c : availableList) { - i++; - System.out.println(i + ". " + c.toString() + ": " + c.getRules().getManaCost().toString()); - } - i = 0; - System.out.println("NOT PICKED"); - for (final PaperCard c : aiPlayables) { - i++; - System.out.println(i + ". " + c.toString() + ": " + c.getRules().getManaCost().toString()); - } - } - - /** - * If the deck does not have 40 cards, fix it. This method should not be - * called if the stuff above it is working correctly. - * - * @param clrCnts - * color counts needed - */ - private void fixDeckSize(final int[] clrCnts) { - while (deckList.size() > 60) { - if (logToConsole) { - System.out.println("WARNING: Fixing deck size, currently " + deckList.size() + " cards."); - } - final PaperCard c = deckList.get(MyRandom.getRandom().nextInt(deckList.size() - 1)); - deckList.remove(c); - aiPlayables.add(c); - if (logToConsole) { - System.out.println(" - Removed " + c.getName() + " randomly."); - } - } - - while (deckList.size() < 60) { - if (logToConsole) { - System.out.println("WARNING: Fixing deck size, currently " + deckList.size() + " cards."); - } - if (aiPlayables.size() > 1) { - final PaperCard c = aiPlayables.get(MyRandom.getRandom().nextInt(aiPlayables.size() - 1)); - deckList.add(c); - aiPlayables.remove(c); - if (logToConsole) { - System.out.println(" - Added " + c.getName() + " randomly."); - } - } else if (aiPlayables.size() == 1) { - final PaperCard c = aiPlayables.get(0); - deckList.add(c); - aiPlayables.remove(c); - if (logToConsole) { - System.out.println(" - Added " + c.getName() + " randomly."); - } - } else { - // if no playable cards remain fill up with basic lands - for (int i = 0; i < 5; i++) { - if (clrCnts[i] > 0) { - final PaperCard cp = getBasicLand(i); - deckList.add(cp); - if (logToConsole) { - System.out.println(" - Added " + cp.getName() + " as last resort."); - } - break; - } - } - } - } - } - - /** - * Find the sets that have basic lands for the available cards. - */ - private void findBasicLandSets() { - final Set sets = new HashSet<>(); - for (final PaperCard cp : aiPlayables) { - final CardEdition ee = FModel.getMagicDb().getEditions().get(cp.getEdition()); - if( !sets.contains(cp.getEdition()) && CardEdition.Predicates.hasBasicLands.apply(ee)) { - sets.add(cp.getEdition()); - } - } - setsWithBasicLands.addAll(sets); - if (setsWithBasicLands.isEmpty()) { - setsWithBasicLands.add("BFZ"); - } - } - - /** - * Add lands to fulfill the given color counts. - * - * @param clrCnts - * counts of lands needed, by color - */ - private void addLands(final int[] clrCnts) { - // basic lands that are available in the deck - final Iterable basicLands = Iterables.filter(aiPlayables, Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES)); - final Set snowLands = new HashSet(); - - // total of all ClrCnts - int totalColor = 0; - int numColors = 0; - for (int i = 0; i < 5; i++) { - totalColor += clrCnts[i]; - if (clrCnts[i] > 0) { - numColors++; - } - } - if (totalColor == 0) { - throw new RuntimeException("Add Lands to empty deck list!"); - } - - // do not update landsNeeded until after the loop, because the - // calculation involves landsNeeded - for (int i = 0; i < 5; i++) { - if (clrCnts[i] > 0) { - // calculate number of lands for each color - float p = (float) clrCnts[i] / (float) totalColor; - if (numColors == 2) { - // In the normal two-color case, constrain to within 40% and 60% so that the AI - // doesn't put too few lands of the lesser color, risking getting screwed on that color. - // Don't do this for the odd case where a third color had to be added to the deck. - p = Math.min(Math.max(p, 0.4f), 0.6f); - } - int nLand = Math.round(landsNeeded * p); // desired truncation to int - if (logToConsole) { - System.out.printf("Basics[%s]: %d/%d = %f%% = %d cards%n", MagicColor.Constant.BASIC_LANDS.get(i), clrCnts[i], totalColor, 100*p, nLand); - } - - // if appropriate snow-covered lands are available, add them - for (final PaperCard cp : basicLands) { - if (cp.getName().equals(MagicColor.Constant.SNOW_LANDS.get(i))) { - snowLands.add(cp); - nLand--; - } - } - - for (int j = 0; j < nLand; j++) { - deckList.add(getBasicLand(i)); - } - } - } - - // A common problem at this point is that p in the above loop was exactly 1/2, - // and nLand rounded up for both colors, so that one too many lands was added. - // So if the deck size is > 60, remove the last land added. - // Otherwise, the fixDeckSize() method would remove random cards. - while (deckList.size() > 60) { - deckList.remove(deckList.size() - 1); - } - - deckList.addAll(snowLands); - aiPlayables.removeAll(snowLands); - } - - /** - * Get basic land. - * - * @param basicLand - * the set to take basic lands from (pass 'null' for random). - * @return card - */ - private PaperCard getBasicLand(final int basicLand) { - String set; - if (setsWithBasicLands.size() > 1) { - set = setsWithBasicLands.get(MyRandom.getRandom().nextInt(setsWithBasicLands.size() - 1)); - } else { - set = setsWithBasicLands.get(0); - } - return FModel.getMagicDb().getCommonCards().getCard(MagicColor.Constant.BASIC_LANDS.get(basicLand), set); - } - - /** - * Only adds wastes if present in the card pool but if present adds them all - */ - private void addWastesIfRequired(){ - List toAdd = Lists.newArrayList(Iterables.filter(aiPlayables,PaperCard.Predicates.name("Wastes"))); - deckList.addAll(toAdd); - aiPlayables.removeAll(toAdd); - rankedColorList.removeAll(toAdd); - landsNeeded = landsNeeded - toAdd.size(); - } - - /** - * Attempt to optimize basic land counts according to color representation. - * Only consider colors that are supposed to be in the deck. It's not worth - * putting one land in for that random off-color card we had to stick in at - * the end... - * - * @return CCnt - */ - private int[] calculateLandNeeds() { - final int[] clrCnts = { 0,0,0,0,0 }; - // count each card color using mana costs - for (final PaperCard cp : deckList) { - final ManaCost mc = cp.getRules().getManaCost(); - - // count each mana symbol in the mana cost - for (final ManaCostShard shard : mc) { - for ( int i = 0 ; i < MagicColor.WUBRG.length; i++ ) { - final byte c = MagicColor.WUBRG[i]; - - if ( shard.canBePaidWithManaOfColor(c) && colors.hasAnyColor(c)) { - clrCnts[i]++; - } - } - } - } - return clrCnts; - } - - /** - * Add non-basic lands to the deck. - */ - private void addNonBasicLands() { - final Iterable lands = Iterables.filter(aiPlayables, - Predicates.compose(CardRulesPredicates.Presets.IS_NONBASIC_LAND, PaperCard.FN_GET_RULES)); - List landsToAdd = new ArrayList<>(); - int minBasics=r.nextInt(6)+3;//Keep a minimum number of basics to ensure playable decks - for (final PaperCard card : lands) { - if (landsNeeded > minBasics) { - // Throw out any dual-lands for the wrong colors. Assume - // everything else is either - // (a) dual-land of the correct two colors, or - // (b) a land that generates colorless mana and has some other - // beneficial effect. - if (!card.getRules().getColorIdentity().isColorless() && card.getRules().getColorIdentity().getSharedColors(colors).countColors()==0){ - //skip as does not match colours - if (logToConsole) { - System.out.println("Excluding NonBasicLand: " + card.getName()); - } - continue; - } - if (!inverseDLands.contains(card.getName())&&!dLands.contains(card.getName())&&r.nextInt(100)<90) { - landsToAdd.add(card); - landsNeeded--; - if (logToConsole) { - System.out.println("NonBasicLand[" + landsNeeded + "]:" + card.getName()); - } - } - } - } - deckList.addAll(landsToAdd); - aiPlayables.removeAll(landsToAdd); - } - /** * Add a third color to the deck. * * @param num * number to add */ - private void addThirdColorCards(int num) { + protected void addThirdColorCards(int num) { if (num > 0) { final Iterable others = Iterables.filter(aiPlayables, Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES)); @@ -639,6 +409,316 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { } } + + + protected void addLowCMCCard(){ + final Iterable nonLands = Iterables.filter(rankedColorList, + Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES)); + final PaperCard card = Iterables.getFirst(nonLands, null); + if (card != null) { + deckList.add(card); + aiPlayables.remove(card); + rankedColorList.remove(card); + + landsNeeded--; + if (logToConsole) { + System.out.println("Low CMC: " + card.getName()); + } + } + } + + /** + * Set the basic land pool + * @param edition + * @return + */ + protected boolean setBasicLandPool(String edition){ + Predicate isSetBasicLand; + if (edition !=null){ + isSetBasicLand = Predicates.and(IPaperCard.Predicates.printedInSet(edition), + Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES)); + }else{ + isSetBasicLand = Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES); + } + + landPool = new DeckGenPool(format.getCardPool(fullCardDB).getAllCards(isSetBasicLand)); + return landPool.contains("Plains"); + } + + /** + * Generate a descriptive name. + * + * @return name + */ + protected String generateName() { + return keyCard.getName() + " - " + secondKeyCard.getName() +" based deck"; + } + + /** + * Print out listing of all cards for debugging. + */ + private void debugFinalDeck() { + int i = 0; + System.out.println("DECK"); + for (final PaperCard c : deckList) { + i++; + System.out.println(i + ". " + c.toString() + ": " + c.getRules().getManaCost().toString()); + } + i = 0; + System.out.println("NOT PLAYABLE"); + for (final PaperCard c : availableList) { + i++; + System.out.println(i + ". " + c.toString() + ": " + c.getRules().getManaCost().toString()); + } + i = 0; + System.out.println("NOT PICKED"); + for (final PaperCard c : aiPlayables) { + i++; + System.out.println(i + ". " + c.toString() + ": " + c.getRules().getManaCost().toString()); + } + } + + /** + * If the deck does not have 40 cards, fix it. This method should not be + * called if the stuff above it is working correctly. + * + */ + private void fixDeckSize() { + while (deckList.size() > targetSize) { + if (logToConsole) { + System.out.println("WARNING: Fixing deck size, currently " + deckList.size() + " cards."); + } + final PaperCard c = deckList.get(MyRandom.getRandom().nextInt(deckList.size() - 1)); + deckList.remove(c); + aiPlayables.add(c); + if (logToConsole) { + System.out.println(" - Removed " + c.getName() + " randomly."); + } + } + if(deckList.size()==targetSize){ + return; + } + + Predicate possibleFromFullPool = new Predicate() { + @Override + public boolean apply(PaperCard card) { + return format.isLegalCard(card) + &&!card.getRules().getManaCost().isPureGeneric() + && colors.containsAllColorsFrom(card.getRules().getColorIdentity().getColor()) + && !deckList.contains(card) + &&!card.getRules().getAiHints().getRemAIDecks() + &&!card.getRules().getAiHints().getRemRandomDecks() + &&!card.getRules().getMainPart().getType().isLand(); + } + }; + List randomPool = Lists.newArrayList(pool.getAllCards(possibleFromFullPool)); + Collections.shuffle(randomPool,new Random()); + Iterator iRandomPool=randomPool.iterator(); + while (deckList.size() < targetSize) { + if (logToConsole) { + System.out.println("WARNING: Fixing deck size, currently " + deckList.size() + " cards."); + } + PaperCard randomCard = iRandomPool.next(); + deckList.add(randomCard); + if (logToConsole) { + System.out.println(" - Added " + randomCard.getName() + " randomly."); + } + } + } + + /** + * Find the sets that have basic lands for the available cards. + */ + protected void findBasicLandSets() { + final Set sets = new HashSet<>(); + for (final PaperCard cp : aiPlayables) { + final CardEdition ee = FModel.getMagicDb().getEditions().get(cp.getEdition()); + if( !sets.contains(cp.getEdition()) && CardEdition.Predicates.hasBasicLands.apply(ee)) { + sets.add(cp.getEdition()); + } + } + setsWithBasicLands.addAll(sets); + if (setsWithBasicLands.isEmpty()) { + setsWithBasicLands.add("BFZ"); + } + } + + /** + * Add lands to fulfill the given color counts. + * + * @param clrCnts + * counts of lands needed, by color + */ + private void addLands(final int[] clrCnts) { + // basic lands that are available in the deck + final Iterable basicLands = Iterables.filter(aiPlayables, Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES)); + final Set snowLands = new HashSet(); + + // total of all ClrCnts + int totalColor = 0; + int numColors = 0; + for (int i = 0; i < 5; i++) { + totalColor += clrCnts[i]; + if (clrCnts[i] > 0) { + numColors++; + } + } + + // do not update landsNeeded until after the loop, because the + // calculation involves landsNeeded + for (int i = 0; i < 5; i++) { + if (clrCnts[i] > 0) { + // calculate number of lands for each color + float p = (float) clrCnts[i] / (float) totalColor; + int nLand = Math.round(landsNeeded * p); // desired truncation to int + if (logToConsole) { + System.out.printf("Basics[%s]: %d/%d = %f%% = %d cards%n", MagicColor.Constant.BASIC_LANDS.get(i), clrCnts[i], totalColor, 100*p, nLand); + } + + // if appropriate snow-covered lands are available, add them + for (final PaperCard cp : basicLands) { + if (cp.getName().equals(MagicColor.Constant.SNOW_LANDS.get(i))) { + snowLands.add(cp); + nLand--; + } + } + + for (int j = 0; j < nLand; j++) { + deckList.add(getBasicLand(i)); + } + } + } + + // A common problem at this point is that p in the above loop was exactly 1/2, + // and nLand rounded up for both colors, so that one too many lands was added. + // So if the deck size is > 60, remove the last land added. + // Otherwise, the fixDeckSize() method would remove random cards. + while (deckList.size() > targetSize) { + deckList.remove(deckList.size() - 1); + } + + deckList.addAll(snowLands); + aiPlayables.removeAll(snowLands); + rankedColorList.remove(snowLands); + } + + /** + * Get basic land. + * + * @param basicLand + * the set to take basic lands from (pass 'null' for random). + * @return card + */ + private PaperCard getBasicLand(final int basicLand) { + String set; + if (setsWithBasicLands.size() > 1) { + set = setsWithBasicLands.get(MyRandom.getRandom().nextInt(setsWithBasicLands.size() - 1)); + } else { + set = setsWithBasicLands.get(0); + } + return FModel.getMagicDb().getCommonCards().getCard(MagicColor.Constant.BASIC_LANDS.get(basicLand), set); + } + + /** + * Only adds wastes if present in the card pool but if present adds them all + */ + private void addWastesIfRequired(){ + if(colors.isColorless()) { + PaperCard waste = FModel.getMagicDb().getCommonCards().getUniqueByName("Wastes"); + while (landsNeeded > 0) { + deckList.add(waste); + landsNeeded--; + } + aiPlayables.remove(waste); + rankedColorList.remove(waste); + } + } + + /** + * Attempt to optimize basic land counts according to color representation. + * Only consider colors that are supposed to be in the deck. It's not worth + * putting one land in for that random off-color card we had to stick in at + * the end... + * + * @return CCnt + */ + private int[] calculateLandNeeds() { + final int[] clrCnts = { 0,0,0,0,0 }; + // count each card color using mana costs + for (final PaperCard cp : deckList) { + final ManaCost mc = cp.getRules().getManaCost(); + + // count each mana symbol in the mana cost + for (final ManaCostShard shard : mc) { + for ( int i = 0 ; i < MagicColor.WUBRG.length; i++ ) { + final byte c = MagicColor.WUBRG[i]; + + if ( shard.canBePaidWithManaOfColor(c) && colors.hasAnyColor(c)) { + clrCnts[i]++; + } + } + } + } + //check all colors have at least one count for each color in colors + for ( int i = 0 ; i < MagicColor.WUBRG.length; i++ ) { + final byte c = MagicColor.WUBRG[i]; + + if ( colors.hasAnyColor(c)) { + if(clrCnts[i] == 0 ) { + clrCnts[i]++; + } + } + } + return clrCnts; + } + + /** + * Add non-basic lands to the deck. + */ + private void addNonBasicLands() { + final Iterable lands = Iterables.filter(aiPlayables, + Predicates.compose(CardRulesPredicates.Presets.IS_NONBASIC_LAND, PaperCard.FN_GET_RULES)); + List landsToAdd = new ArrayList<>(); + int minBasics;//Keep a minimum number of basics to ensure playable decks + if(colors.isColorless()){ + minBasics=0; + }else if(colors.isMonoColor()){ + minBasics=Math.round((r.nextInt(15)+9)*((float) targetSize) / 60); + }else{ + minBasics=Math.round((r.nextInt(8)+6)*((float) targetSize) / 60); + } + + + for (final PaperCard card : lands) { + if (landsNeeded > minBasics) { + // Throw out any dual-lands for the wrong colors. Assume + // everything else is either + // (a) dual-land of the correct two colors, or + // (b) a land that generates colorless mana and has some other + // beneficial effect. + if (!card.getRules().getColorIdentity().isColorless() && card.getRules().getColorIdentity().getSharedColors(colors).countColors()==0 + || card.getRules().getColorIdentity().isMulticolor()&&colors.isMonoColor()){//remove dual lands from mono coloured decks + //skip as does not match colours + if (logToConsole) { + System.out.println("Excluding NonBasicLand: " + card.getName()); + } + continue; + } + if (!inverseDLands.contains(card.getName())&&!dLands.contains(card.getName())&&r.nextInt(100)<90) { + landsToAdd.add(card); + landsNeeded--; + if (logToConsole) { + System.out.println("NonBasicLand[" + landsNeeded + "]:" + card.getName()); + } + } + } + } + deckList.addAll(landsToAdd); + aiPlayables.removeAll(landsToAdd); + rankedColorList.removeAll(landsToAdd); + } + + /** * Add random cards to the deck. * @@ -646,81 +726,61 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { * number to add */ private void addRandomCards(int num) { - final Iterable others = Iterables.filter(aiPlayables, - Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES)); - List toAdd = new ArrayList<>(); - for (final PaperCard card : others) { - if (num > 0) { - toAdd.add(card); - num--; - if (logToConsole) { - System.out.println("Random[" + num + "]:" + card.getName() + "(" - + card.getRules().getManaCost() + ")"); - } - } else { - break; + Predicate possibleFromFullPool = new Predicate() { + @Override + public boolean apply(PaperCard card) { + return format.isLegalCard(card) + &&!card.getRules().getManaCost().isPureGeneric() + && colors.containsAllColorsFrom(card.getRules().getColorIdentity().getColor()) + && !deckList.contains(card) + &&!card.getRules().getAiHints().getRemAIDecks() + &&!card.getRules().getAiHints().getRemRandomDecks() + &&!card.getRules().getMainPart().getType().isLand(); + } + }; + List randomPool = Lists.newArrayList(pool.getAllCards(possibleFromFullPool)); + Collections.shuffle(randomPool,new Random()); + Iterator iRandomPool=randomPool.iterator(); + for(int i=0;i nonCreatures, int num) { - List toAdd = new ArrayList<>(); - for (final PaperCard card : nonCreatures) { - if (num > 0) { - toAdd.add(card); - num--; - if (logToConsole) { - System.out.println("Others[" + num + "]:" + card.getName() + " (" - + card.getRules().getManaCost() + ")"); - } - } else { - break; - } - } - deckList.addAll(toAdd); - aiPlayables.removeAll(toAdd); - rankedColorList.removeAll(toAdd); } /** * Add creatures to the deck. * - * @param creatures + * @param cards * cards to choose from * @param num * number to add */ - private void addCreatures(final Iterable creatures, int num) { - List creaturesToAdd = new ArrayList<>(); - for (final PaperCard card : creatures) { - if (num > 0) { - creaturesToAdd.add(card); - num--; + private void addCards(final Iterable cards, int num) { + List cardsToAdd = new ArrayList<>(); + for (final PaperCard card : cards) { + if(card.getRules().getMainPart().getType().isLand()){ + continue; + } + if (num +1 > 0) { + cardsToAdd.add(card); if (logToConsole) { - System.out.println("Creature[" + num + "]:" + card.getName() + " (" + card.getRules().getManaCost() + ")"); + System.out.println("Extra needed[" + num + "]:" + card.getName() + " (" + card.getRules().getManaCost() + ")"); } + num--; } else { break; } } - deckList.addAll(creaturesToAdd); - aiPlayables.removeAll(creaturesToAdd); - rankedColorList.removeAll(creaturesToAdd); + deckList.addAll(cardsToAdd); + aiPlayables.removeAll(cardsToAdd); + rankedColorList.removeAll(cardsToAdd); } /** - * Add creatures to the deck, trying to follow some mana curve. Trying to + * Add cards to the deck, trying to follow some mana curve. Trying to * have generous limits at each cost, but perhaps still too strict. But * we're trying to prevent the AI from adding everything at a single cost. * @@ -729,22 +789,22 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { * @param num * number to add */ - private void addManaCurveCreatures(final Iterable creatures, int num) { - // Add the deck card - if(keyCard.getRules().getMainPart().getType().isCreature()) { - keyCards = Iterables.filter(aiPlayables,PaperCard.Predicates.name(keyCard.getName())); + private void addManaCurveCards(final Iterable creatures, int num, String nameForLog) { +/* // Add the deck card + if(commanderCard.getRules().getMainPart().getType().isCreature()) { + keyCards = Iterables.filter(aiPlayables,PaperCard.Predicates.name(commanderCard.getName())); final List keyCardList = Lists.newArrayList(keyCards); deckList.addAll(keyCardList); aiPlayables.removeAll(keyCardList); rankedColorList.removeAll(keyCardList); - } + }*/ final Map targetCMCs = new HashMap<>(); - targetCMCs.put(1,r.nextInt(4)+2);//2 - targetCMCs.put(2,r.nextInt(5)+5);//6 - targetCMCs.put(3,r.nextInt(5)+6);//7 - targetCMCs.put(4,r.nextInt(3)+3);//4 - targetCMCs.put(5,r.nextInt(3)+3);//3 - targetCMCs.put(6,r.nextInt(3)+1);//2 + targetCMCs.put(1,Math.round((r.nextInt(4)+2)*targetSize/60));//2 + targetCMCs.put(2,Math.round((r.nextInt(5)+5)*targetSize/60));//6 + targetCMCs.put(3,Math.round((r.nextInt(5)+6)*targetSize/60));//7 + targetCMCs.put(4,Math.round((r.nextInt(3)+3)*targetSize/60));//4 + targetCMCs.put(5,Math.round((r.nextInt(3)+3)*targetSize/60));//3 + targetCMCs.put(6,Math.round((r.nextInt(3)+1)*targetSize/60));//2 final Map creatureCosts = new HashMap(); @@ -792,7 +852,7 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { num--; creatureCosts.put(cmc, creatureCosts.get(cmc) + 1); if (logToConsole) { - System.out.println("Creature[" + num + "]:" + card.getName() + " (" + card.getRules().getManaCost() + ")"); + System.out.println(nameForLog+"[" + num + "]:" + card.getName() + " (" + card.getRules().getManaCost() + ")"); } } else { if (logToConsole) { diff --git a/forge-gui/src/main/java/forge/limited/DeckColors.java b/forge-gui/src/main/java/forge/limited/DeckColors.java index b93a20f06a6..99964e2de39 100644 --- a/forge-gui/src/main/java/forge/limited/DeckColors.java +++ b/forge-gui/src/main/java/forge/limited/DeckColors.java @@ -25,8 +25,8 @@ import forge.item.IPaperCard; public class DeckColors { - private ColorSet chosen; - private int colorMask; + protected ColorSet chosen; + protected int colorMask; public int MAX_COLORS = 2; diff --git a/forge-gui/src/main/java/forge/limited/FullDeckColors.java b/forge-gui/src/main/java/forge/limited/FullDeckColors.java index 770a5114e3b..62b87ae3262 100644 --- a/forge-gui/src/main/java/forge/limited/FullDeckColors.java +++ b/forge-gui/src/main/java/forge/limited/FullDeckColors.java @@ -1,5 +1,9 @@ package forge.limited; +import forge.card.ColorSet; +import forge.card.MagicColor; +import forge.item.IPaperCard; + /** * Created by maustin on 11/05/2017. */ @@ -7,4 +11,26 @@ public class FullDeckColors extends DeckColors { public FullDeckColors(){ MAX_COLORS = 5; } + + public void addColorsOf(final IPaperCard pickedCard) { + final ColorSet colorsCanAdd = chosen.inverse(); + final ColorSet toAdd = colorsCanAdd.getSharedColors(pickedCard.getRules().getColorIdentity()); + + int cntColorsAssigned = getChosenColors().countColors(); + final boolean haveSpace = cntColorsAssigned < MAX_COLORS; + if (!haveSpace || toAdd.isColorless()) { + return; + } + + for (final byte color : MagicColor.WUBRG) { + if (toAdd.hasAnyColor(color)) { + colorMask |= color; + chosen = null; // invalidate color set + cntColorsAssigned++; + } + if (cntColorsAssigned >= MAX_COLORS) { + break; + } + } + } }