diff --git a/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckChooser.java b/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckChooser.java index dd437d42fb3..42fa4a971c7 100644 --- a/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckChooser.java +++ b/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckChooser.java @@ -153,7 +153,7 @@ public class FDeckChooser extends JPanel implements IDecksComboBoxListener { private void updateMatrix(GameFormat format) { lstDecks.setAllowMultipleSelections(false); - lstDecks.setPool(CardThemedDeckGenerator.getMatrixDecks(format)); + lstDecks.setPool(CardThemedDeckGenerator.getMatrixDecks(format, isAi)); lstDecks.setup(ItemManagerConfig.STRING_ONLY); btnRandom.setText("Random"); diff --git a/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckViewer.java b/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckViewer.java index aee20e3f376..0634aa16b1c 100644 --- a/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckViewer.java +++ b/forge-gui-desktop/src/main/java/forge/deckchooser/FDeckViewer.java @@ -118,8 +118,8 @@ public class FDeckViewer extends FDialog { } }); - final int width = 800; - final int height = 600; + final int width = 1920; + final int height = 1080; this.setPreferredSize(new Dimension(width, height)); this.setSize(width, height); diff --git a/forge-gui-mobile/src/forge/deck/FDeckChooser.java b/forge-gui-mobile/src/forge/deck/FDeckChooser.java index a3940a23585..2982e633998 100644 --- a/forge-gui-mobile/src/forge/deck/FDeckChooser.java +++ b/forge-gui-mobile/src/forge/deck/FDeckChooser.java @@ -601,12 +601,12 @@ public class FDeckChooser extends FScreen { break; case STANDARD_CARDGEN_DECK: maxSelections = 1; - pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getStandard()); + pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getStandard(), isAi); config = ItemManagerConfig.STRING_ONLY; break; case MODERN_CARDGEN_DECK: maxSelections = 1; - pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getModern()); + pool = CardThemedDeckGenerator.getMatrixDecks(FModel.getFormats().getModern(), isAi); config = ItemManagerConfig.STRING_ONLY; break; case MODERN_COLOR_DECK: diff --git a/forge-gui/res/deckgendecks/Modern.dat b/forge-gui/res/deckgendecks/Modern.dat index cae30372c67..28f8c3527ff 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 11db018d6d1..cd5d20d9b66 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 c86a76a6f1c..0895791a842 100644 --- a/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java +++ b/forge-gui/src/main/java/forge/deck/CardRelationMatrixGenerator.java @@ -22,12 +22,12 @@ import java.util.*; */ public final class CardRelationMatrixGenerator { - public static HashMap>> cardPools = new HashMap<>(); + public static HashMap>>> cardPools = new HashMap<>(); public static void initialize(){ - HashMap> standardMap = CardThemedMatrixIO.loadMatrix(FModel.getFormats().getStandard()); - HashMap> modernMap = CardThemedMatrixIO.loadMatrix(FModel.getFormats().getModern()); + HashMap>> standardMap = CardThemedMatrixIO.loadMatrix(FModel.getFormats().getStandard()); + HashMap>> modernMap = CardThemedMatrixIO.loadMatrix(FModel.getFormats().getModern()); if(standardMap==null || modernMap==null){ reInitialize(); return; @@ -40,12 +40,12 @@ public final class CardRelationMatrixGenerator { cardPools.put(FModel.getFormats().getStandard(),initializeFormat(FModel.getFormats().getStandard())); cardPools.put(FModel.getFormats().getModern(),initializeFormat(FModel.getFormats().getModern())); for(GameFormat format:cardPools.keySet()){ - HashMap> map = cardPools.get(format); + HashMap>> map = cardPools.get(format); CardThemedMatrixIO.saveMatrix(format,map); } } - public static HashMap> initializeFormat(GameFormat format){ + public static HashMap>> initializeFormat(GameFormat format){ IStorage decks = new StorageImmediatelySerialized("Generator", new DeckStorage(new File(ForgeConstants.DECK_GEN_DIR+ForgeConstants.PATH_SEPARATOR+format.getName()), ForgeConstants.DECK_GEN_DIR, false), @@ -83,7 +83,7 @@ public final class CardRelationMatrixGenerator { } } } - HashMap> cardPools = new HashMap<>(); + HashMap>> cardPools = new HashMap<>(); for (PaperCard card:cardList){ int col=cardIntegerMap.get(card.getName()); int[] distances = matrix[col]; @@ -92,21 +92,21 @@ public final class CardRelationMatrixGenerator { ArrayIndexComparator comparator = new ArrayIndexComparator(ArrayUtils.toObject(distances)); Integer[] indices = comparator.createIndexArray(); Arrays.sort(indices, comparator); - List deckPool=new ArrayList<>(); + List> deckPool=new ArrayList<>(); int k=0; - boolean isZeroDistance=false; + boolean excludeThisCard=false;//if there are too few cards with at least one connection for (int j=0;j<20;++k){ if(distances[indices[cardList.size()-1-k]]==0){ - isZeroDistance=true; + excludeThisCard = true; break; } PaperCard cardToAdd=integerCardMap.get(indices[cardList.size()-1-k]); - if(!cardToAdd.getRules().getMainPart().getType().isLand()){//need 15 non-land cards + if(!cardToAdd.getRules().getMainPart().getType().isLand()){//need x non-land cards ++j; } - deckPool.add(cardToAdd); + deckPool.add(new AbstractMap.SimpleEntry(cardToAdd,distances[indices[cardList.size()-1-k]])); }; - if(isZeroDistance){ + if(excludeThisCard){ continue; } cardPools.put(card.getName(),deckPool); diff --git a/forge-gui/src/main/java/forge/deck/CardThemedDeckGenerator.java b/forge-gui/src/main/java/forge/deck/CardThemedDeckGenerator.java index 90b30e3620c..3a9dec31ba8 100644 --- a/forge-gui/src/main/java/forge/deck/CardThemedDeckGenerator.java +++ b/forge-gui/src/main/java/forge/deck/CardThemedDeckGenerator.java @@ -10,23 +10,25 @@ import java.util.List; * Created by maustin on 09/05/2017. */ public class CardThemedDeckGenerator extends DeckProxy implements Comparable { - public static List getMatrixDecks(GameFormat format){ + public static List getMatrixDecks(GameFormat format, boolean isForAi){ final List decks = new ArrayList(); for(String card: CardRelationMatrixGenerator.cardPools.get(format).keySet()) { - decks.add(new CardThemedDeckGenerator(card, format)); + decks.add(new CardThemedDeckGenerator(card, format, isForAi)); } return decks; } private final String name; private final int index; private final GameFormat format; + private final boolean isForAi; - private CardThemedDeckGenerator(String cardName, GameFormat format0) { + private CardThemedDeckGenerator(String cardName, GameFormat format0, boolean isForAi0) { super(); name = cardName; index = 0; format=format0; + isForAi=isForAi0; } public CardEdition getEdition() { @@ -52,7 +54,7 @@ public class CardThemedDeckGenerator extends DeckProxy implements Comparable keys = new ArrayList<>(CardRelationMatrixGenerator.cardPools.get(format).keySet()); String randomKey = keys.get( random.nextInt(keys.size()) ); Predicate cardFilter = Predicates.and(format.getFilterPrinted(),PaperCard.Predicates.name(randomKey)); PaperCard keyCard = FModel.getMagicDb().getCommonCards().getAllCards(cardFilter).get(0); try { - return buildCardGenDeck(keyCard,format); + return buildCardGenDeck(keyCard,format,isForAI); }catch (Exception e){ e.printStackTrace(); - return buildCardGenDeck(format); + return buildCardGenDeck(format,isForAI); } } - public static Deck buildCardGenDeck(String cardName, GameFormat format){ + public static Deck buildCardGenDeck(String cardName, GameFormat format, boolean isForAI){ try { Predicate cardFilter = Predicates.and(format.getFilterPrinted(),PaperCard.Predicates.name(cardName)); - return buildCardGenDeck(FModel.getMagicDb().getCommonCards().getAllCards(cardFilter).get(0),format); + return buildCardGenDeck(FModel.getMagicDb().getCommonCards().getAllCards(cardFilter).get(0),format,isForAI); }catch (Exception e){ e.printStackTrace(); - return buildCardGenDeck(format); + return buildCardGenDeck(format,isForAI); } } - public static Deck buildCardGenDeck(PaperCard card, GameFormat format){ - List selectedCards = new ArrayList<>(); - selectedCards.addAll(CardRelationMatrixGenerator.cardPools.get(format).get(card.getName())); - List toRemove = new ArrayList<>(); + /** + * Take two lists of cards with counts of each and combine the second into the first by adding a mean normalized fraction + * of the count in the second list to the first list. + * @param cards1 + * @param cards2 + */ + public static void combineDistances(List> cards1,List> cards2){ + Integer maxDistance=0; + for (Map.Entry pair1:cards1){ + maxDistance=maxDistance+pair1.getValue(); + } + maxDistance=maxDistance/cards1.size(); + Integer maxDistance2=0; + for (Map.Entry pair2:cards2){ + maxDistance2=maxDistance2+pair2.getValue(); + } + maxDistance2=maxDistance2/cards2.size(); + for (Map.Entry pair2:cards2){ + boolean isCardPresent=false; + for (Map.Entry pair1:cards1){ + if (pair1.getKey().equals(pair2.getKey())){ + pair1.setValue(pair1.getValue()+new Float((pair2.getValue()*0.4*maxDistance/maxDistance2)).intValue()); + isCardPresent=true; + break; + } + } + if(!isCardPresent){ + Map.Entry newEntry=new AbstractMap.SimpleEntry(pair2.getKey(),new Float((pair2.getValue()*0.4*maxDistance/maxDistance2)).intValue()); + cards1.add(pair2); + } + } + } + + public static class CardDistanceComparator implements Comparator> + { + @Override + public int compare(Map.Entry index1, Map.Entry index2) + { + // Autounbox from Integer to int to use as array indexes + return index1.getValue().compareTo(index2.getValue()); + } + } + + /** + * Build a deck based on the chosen card. + * + * @param card + * @param format + * @param isForAI + * @return + */ + public static Deck buildCardGenDeck(PaperCard card, GameFormat format, boolean isForAI){ + List> potentialCards = new ArrayList<>(); + potentialCards.addAll(CardRelationMatrixGenerator.cardPools.get(format).get(card.getName())); + Collections.sort(potentialCards,new CardDistanceComparator()); + Collections.reverse(potentialCards); + //get second keycard Random r = new Random(); + List preSelectedCards = new ArrayList<>(); + for(Map.Entry pair:potentialCards){ + preSelectedCards.add(pair.getKey()); + } + //filter out land cards and if for AI non-playable cards as potential second key cards + Iterable preSelectedNonLandCards; + if(isForAI){ + preSelectedNonLandCards=Iterables.filter(preSelectedCards,Predicates.and( + Predicates.compose(CardRulesPredicates.IS_KEPT_IN_AI_DECKS, PaperCard.FN_GET_RULES), + Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES))); + }else{ + preSelectedNonLandCards=Iterables.filter(preSelectedCards, + Predicates.compose(CardRulesPredicates.Presets.IS_NON_LAND, PaperCard.FN_GET_RULES)); + } + preSelectedCards= Lists.newArrayList(preSelectedNonLandCards); + + //choose a second card randomly from the top 8 cards if possible + int randMax=8; + if(preSelectedCards.size()> potentialSecondCards = CardRelationMatrixGenerator.cardPools.get(format).get(secondKeycard.getName()); + + //combine card distances from second key card and re-sort + if(potentialSecondCards !=null && potentialSecondCards.size()>0) { + combineDistances(potentialCards, potentialSecondCards); + Collections.sort(potentialCards, new CardDistanceComparator()); + Collections.reverse(potentialCards); + } + + List selectedCards = new ArrayList<>(); + for(Map.Entry pair:potentialCards){ + selectedCards.add(pair.getKey()); + } + List toRemove = new ArrayList<>(); + //randomly remove cards int removeCount=0; int i=0; @@ -78,9 +166,16 @@ public class DeckgenUtil { toRemove.add(c); removeCount++; } + if(c.getName().equals(card.getName())){//may have been added in secondary list + toRemove.add(c); + } + if(c.getName().equals(secondKeycard.getName())){//remove so we can add correct amount + toRemove.add(c); + } ++i; } selectedCards.removeAll(toRemove); + //Add keycard List playsetList = new ArrayList<>(); int keyCardCount=4; if(card.getRules().getMainPart().getManaCost().getCMC()>7){ @@ -91,6 +186,16 @@ public class DeckgenUtil { for(int j=0;j7){ + keyCard2Count=1+r.nextInt(4); + }else if(card.getRules().getMainPart().getManaCost().getCMC()>5){ + keyCard2Count=2+r.nextInt(3); + } + for(int j=0;j27){ System.out.println("Too many lands "+deck.getMain().countAll(Predicates.compose(CardRulesPredicates.Presets.IS_LAND, PaperCard.FN_GET_RULES))); - deck=buildCardGenDeck(format); + deck=buildCardGenDeck(format,isForAI); } while(deck.get(DeckSection.Sideboard).countAll()>15){ deck.get(DeckSection.Sideboard).remove(deck.get(DeckSection.Sideboard).get(0)); diff --git a/forge-gui/src/main/java/forge/deck/RandomDeckGenerator.java b/forge-gui/src/main/java/forge/deck/RandomDeckGenerator.java index 8f6f7dd2d8b..01c5dde105f 100644 --- a/forge-gui/src/main/java/forge/deck/RandomDeckGenerator.java +++ b/forge-gui/src/main/java/forge/deck/RandomDeckGenerator.java @@ -99,9 +99,9 @@ public class RandomDeckGenerator extends DeckProxy implements Comparable(); count = Aggregates.randomInt(1, 3); diff --git a/forge-gui/src/main/java/forge/deck/io/CardThemedMatrixIO.java b/forge-gui/src/main/java/forge/deck/io/CardThemedMatrixIO.java index c24acbcc50f..a768edb15c8 100644 --- a/forge-gui/src/main/java/forge/deck/io/CardThemedMatrixIO.java +++ b/forge-gui/src/main/java/forge/deck/io/CardThemedMatrixIO.java @@ -30,7 +30,7 @@ public class CardThemedMatrixIO { /** suffix for all gauntlet data files */ public static final String SUFFIX_DATA = ".dat"; - public static void saveMatrix(GameFormat format, HashMap> map){ + public static void saveMatrix(GameFormat format, HashMap>> map){ File file = getMatrixFile(format); ObjectOutputStream s = null; try { @@ -51,11 +51,11 @@ public class CardThemedMatrixIO { } } - public static HashMap> loadMatrix(GameFormat format){ + public static HashMap>> loadMatrix(GameFormat format){ try { FileInputStream fin = new FileInputStream(getMatrixFile(format)); ObjectInputStream s = new ObjectInputStream(fin); - HashMap> matrix = (HashMap>) s.readObject(); + HashMap>> matrix = (HashMap>>) s.readObject(); s.close(); return matrix; }catch (Exception e){ diff --git a/forge-gui/src/main/java/forge/gauntlet/GauntletUtil.java b/forge-gui/src/main/java/forge/gauntlet/GauntletUtil.java index 8bdc9c61764..81f709e5df5 100644 --- a/forge-gui/src/main/java/forge/gauntlet/GauntletUtil.java +++ b/forge-gui/src/main/java/forge/gauntlet/GauntletUtil.java @@ -33,10 +33,10 @@ public class GauntletUtil { deck = DeckgenUtil.getRandomColorDeck(FModel.getFormats().getStandard().getFilterPrinted(),true); break; case STANDARD_CARDGEN_DECK: - deck = DeckgenUtil.buildCardGenDeck(FModel.getFormats().getStandard()); + deck = DeckgenUtil.buildCardGenDeck(FModel.getFormats().getStandard(),true); break; case MODERN_CARDGEN_DECK: - deck = DeckgenUtil.buildCardGenDeck(FModel.getFormats().getModern()); + deck = DeckgenUtil.buildCardGenDeck(FModel.getFormats().getModern(),true); break; case MODERN_COLOR_DECK: deck = DeckgenUtil.getRandomColorDeck(FModel.getFormats().getModern().getFilterPrinted(),true); diff --git a/forge-gui/src/main/java/forge/limited/CardThemedDeckBuilder.java b/forge-gui/src/main/java/forge/limited/CardThemedDeckBuilder.java index 828243406c8..c8f28ddabd6 100644 --- a/forge-gui/src/main/java/forge/limited/CardThemedDeckBuilder.java +++ b/forge-gui/src/main/java/forge/limited/CardThemedDeckBuilder.java @@ -46,6 +46,7 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { protected int landsNeeded = 25; protected final PaperCard keyCard; + protected final PaperCard secondKeyCard; protected DeckColors deckColors; protected Predicate hasColor; @@ -71,14 +72,19 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { * @param dList * Cards to build the deck from. */ - public CardThemedDeckBuilder(PaperCard keyCard0, final List dList, GameFormat format) { + public CardThemedDeckBuilder(PaperCard keyCard0,PaperCard secondKeyCard0, final List dList, GameFormat format, boolean isForAI) { super(FModel.getMagicDb().getCommonCards(), DeckFormat.Limited, format.getFilterPrinted()); this.availableList = dList; keyCard=keyCard0; + secondKeyCard=secondKeyCard0; // remove Unplayables - final Iterable playables = Iterables.filter(availableList, - Predicates.compose(CardRulesPredicates.IS_KEPT_IN_AI_DECKS, PaperCard.FN_GET_RULES)); - this.aiPlayables = Lists.newArrayList(playables); + if(isForAI) { + final Iterable playables = Iterables.filter(availableList, + Predicates.compose(CardRulesPredicates.IS_KEPT_IN_AI_DECKS, PaperCard.FN_GET_RULES)); + this.aiPlayables = Lists.newArrayList(playables); + }else{ + this.aiPlayables = Lists.newArrayList(availableList); + } this.availableList.removeAll(aiPlayables); deckColors = new DeckColors(); for(PaperCard c:getAiPlayables()){ @@ -94,6 +100,9 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { if(!colors.hasAllColors(keyCard.getRules().getColorIdentity().getColor())){ colors = ColorSet.fromMask(colors.getColor() | keyCard.getRules().getColorIdentity().getColor()); } + if(!colors.hasAllColors(secondKeyCard.getRules().getColorIdentity().getColor())){ + colors = ColorSet.fromMask(colors.getColor() | secondKeyCard.getRules().getColorIdentity().getColor()); + } if (logColorsToConsole) { System.out.println(keyCard.getName()); System.out.println("Pre Colors: " + colors.toEnumSet().toString()); @@ -136,6 +145,14 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { 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 @@ -302,7 +319,7 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { * @return name */ private String generateName() { - return keyCard.getName() + " based deck"; + return keyCard.getName() + " / " + secondKeyCard.getName() +" based deck"; } /** @@ -754,11 +771,11 @@ public class CardThemedDeckBuilder extends DeckGeneratorBase { rankedColorList.removeAll(keyCardList); } final Map targetCMCs = new HashMap<>(); - targetCMCs.put(1,r.nextInt(5));//2 - targetCMCs.put(2,r.nextInt(5)+4);//6 - targetCMCs.put(3,r.nextInt(5)+5);//7 - targetCMCs.put(4,r.nextInt(5)+2);//4 - targetCMCs.put(5,r.nextInt(5)+2);//3 + 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 diff --git a/forge-gui/src/main/java/forge/tournament/TournamentUtil.java b/forge-gui/src/main/java/forge/tournament/TournamentUtil.java index f9aa7e561c4..a020f41a54b 100644 --- a/forge-gui/src/main/java/forge/tournament/TournamentUtil.java +++ b/forge-gui/src/main/java/forge/tournament/TournamentUtil.java @@ -33,10 +33,10 @@ public class TournamentUtil { deck = DeckgenUtil.getRandomColorDeck(FModel.getFormats().getStandard().getFilterPrinted(),true); break; case STANDARD_CARDGEN_DECK: - deck = DeckgenUtil.buildCardGenDeck(FModel.getFormats().getStandard()); + deck = DeckgenUtil.buildCardGenDeck(FModel.getFormats().getStandard(),true); break; case MODERN_CARDGEN_DECK: - deck = DeckgenUtil.buildCardGenDeck(FModel.getFormats().getModern()); + deck = DeckgenUtil.buildCardGenDeck(FModel.getFormats().getModern(),true); break; case MODERN_COLOR_DECK: deck = DeckgenUtil.getRandomColorDeck(FModel.getFormats().getModern().getFilterPrinted(),true);