From afaed6fea5146e76b8e4cc36ae5bc7dac5924d33 Mon Sep 17 00:00:00 2001 From: leriomaggio Date: Fri, 13 Aug 2021 11:14:31 +0100 Subject: [PATCH] RENEWED and Refined implementation of getUnique for cards, also taking into account current Card Art Preference getUnique previously returned random results, based on the internal order in which entries were found. Now, filtering by Unique Cards in Catalog is aware of the current Cart Art Preference Also, the algorithm takes into account the case in which card image is missing. In that case, the method tries to fill in the gaps with those alternatives having images. If not possible, the original entries are returned. --- .../java/forge/itemmanager/CardManager.java | 111 ++++++++++++++++-- 1 file changed, 99 insertions(+), 12 deletions(-) diff --git a/forge-gui-desktop/src/main/java/forge/itemmanager/CardManager.java b/forge-gui-desktop/src/main/java/forge/itemmanager/CardManager.java index 8f69d994b4e..2e22066c5f5 100644 --- a/forge-gui-desktop/src/main/java/forge/itemmanager/CardManager.java +++ b/forge-gui-desktop/src/main/java/forge/itemmanager/CardManager.java @@ -1,10 +1,12 @@ package forge.itemmanager; -import java.util.*; -import java.util.Map.Entry; - -import javax.swing.JMenu; - +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.ListMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Multimaps; +import forge.StaticData; +import forge.card.CardEdition; import forge.game.GameFormat; import forge.gamemodes.quest.QuestWorld; import forge.gamemodes.quest.data.QuestPreferences; @@ -16,8 +18,13 @@ import forge.model.FModel; import forge.screens.home.quest.DialogChooseFormats; import forge.screens.home.quest.DialogChooseSets; import forge.screens.match.controllers.CDetailPicture; +import forge.util.CollectionSuppliers; import forge.util.Localizer; +import javax.swing.*; +import java.util.*; +import java.util.Map.Entry; + /** * ItemManager for cards * @@ -49,16 +56,96 @@ public class CardManager extends ItemManager { @Override protected Iterable> getUnique(Iterable> items) { - //use special technique for getting unique cards so that cards without art aren't shown - HashMap> map = new HashMap<>(); + ListMultimap> entriesByName = Multimaps.newListMultimap(new TreeMap<>(String.CASE_INSENSITIVE_ORDER), CollectionSuppliers.arrayLists()); for (Entry item : items) { - final String key = item.getKey().getName(); - final Entry oldValue = map.get(key); - if (oldValue == null || !oldValue.getKey().hasImage()) { //only replace in map if old value doesn't have image - map.put(key, item); + final String cardName = item.getKey().getName(); + entriesByName.put(cardName, item); + } + + // Now we're ready to go on with retrieving cards to be returned + Map cardsMap = new HashMap<>(); + for (String cardName : entriesByName.keySet()) { + List> entries = entriesByName.get(cardName); + + ListMultimap> entriesByEdition = Multimaps.newListMultimap(new HashMap<>(), CollectionSuppliers.arrayLists()); + for (Entry entry : entries) { + CardEdition ed = StaticData.instance().getCardEdition(entry.getKey().getEdition()); + if (ed != null && StaticData.instance().getCardArtPreference().accept(ed)) + entriesByEdition.put(ed, entry); + } + if (entriesByEdition.size() == 0) + continue; // skip card + // Try to retain only those editions accepted by the current Card Art Preference Policy + List acceptedEditions = Lists.newArrayList(Iterables.filter(entriesByEdition.keySet(), new Predicate() { + @Override + public boolean apply(CardEdition ed) { + return StaticData.instance().getCardArtPreference().accept(ed); + } + })); + // If policy too strict, fall back to getting all editions. + if (acceptedEditions.size() == 0) + // Policy is too strict for current PaperCard in Entry. Remove any filter + acceptedEditions.addAll(entriesByEdition.keySet()); + List> entriesToAdd = getEntriesToAdd(entriesByEdition, acceptedEditions); + for (Entry entry : entriesToAdd) + cardsMap.put(entry.getKey(), entry.getValue()); + } + return cardsMap.entrySet(); + } + + /* + Get all the Entries to Add, also accounting for missing images. + If in the end, not all entries found have image, the original ones are used to fill in the total number + of entries to return. + */ + private List> getEntriesToAdd(ListMultimap> entriesByEdition, + List acceptedEditions) { + // Use standard sort + index, for better performance! + Collections.sort(acceptedEditions); + int selectionIndex = 0; + if (StaticData.instance().cardArtPreferenceIsLatest()) + selectionIndex = acceptedEditions.size()-1; + CardEdition uniqueEdition = acceptedEditions.get(selectionIndex); + + // These are now the entries to add to Cards Map + List> uniqueEntries = entriesByEdition.get(uniqueEdition); + + // The last bit to check is whether all of them have a corresponding image. Otherwise, try to escalate + // others. + int entriesToReturn = uniqueEntries.size(); + List> entriesToAdd = new ArrayList<>(); + for (Entry entry : uniqueEntries) { + if (!entry.getKey().hasImage()) + continue; // Skip entries with no image + entriesToAdd.add(entry); + } + + if (entriesToAdd.size() < entriesToReturn) { + // some are missing, keep exploring other editions + int start = 1; + int end = acceptedEditions.size(); + if (selectionIndex != 0) { // latest art case: the last was selected + start = 0; + end = acceptedEditions.size() - 1; + } + for (int editionIndex = start; editionIndex < end; editionIndex++) { + CardEdition edition = acceptedEditions.get(editionIndex); + for (Entry entry : entriesByEdition.get(edition)) { + if (!entry.getKey().hasImage()) + continue; // Skip entries with no image + entriesToAdd.add(entry); + if (entriesToAdd.size() == entriesToReturn) + break; + } + } + // if at this stage, there are still entries to add, fill missing ones from the original list + for (Entry entry : uniqueEntries) { + entriesToAdd.add(entry); + if (entriesToAdd.size() == entriesToReturn) + break; } } - return map.values(); + return entriesToAdd; } /* Static overrides shared with SpellShopManager*/