TEMP implementation of Smart Card Art Selection method

This commit is contained in:
leriomaggio
2021-07-24 11:33:37 +01:00
parent 56080cdf3f
commit d9620a5556

View File

@@ -21,8 +21,10 @@ import com.google.common.base.Function;
import com.google.common.collect.Lists;
import forge.StaticData;
import forge.card.CardDb;
import forge.card.CardEdition;
import forge.item.IPaperCard;
import forge.item.PaperCard;
import org.apache.commons.lang3.tuple.Pair;
import java.util.*;
import java.util.Map.Entry;
@@ -220,71 +222,195 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
return;
}
boolean hasExplicitlySpecifiedSet = false;
boolean smartCardArtSelection = StaticData.instance().smartCardArtSelectionIsEnabled();
boolean foundCardsWithUnspecifiedEditions = false;
Map<DeckSection, ArrayList<String>> cardsWithNoEdition = null;
if (smartCardArtSelection)
cardsWithNoEdition = new EnumMap<>(DeckSection.class);
for (Entry<String, List<String>> s : deferredSections.entrySet()) {
DeckSection sec = DeckSection.smartValueOf(s.getKey());
if (sec == null) {
if (sec == null)
continue;
}
final List<String> cardsInSection = s.getValue();
for (String k : cardsInSection)
if (k.indexOf(CardDb.NameSetSeparator) > 0)
hasExplicitlySpecifiedSet = true;
if (smartCardArtSelection){
List<Pair<String, Integer>> cardRequests = CardPool.processCardList(cardsInSection);
ArrayList<String> cardNamesWithNoEdition = new ArrayList<>();
for (Pair<String, Integer> pair : cardRequests) {
String requestString = pair.getLeft();
CardDb.CardRequest request = CardDb.CardRequest.fromString(requestString);
if (request.edition == null) {
foundCardsWithUnspecifiedEditions = true;
cardNamesWithNoEdition.add(request.cardName);
}
}
if (cardNamesWithNoEdition.size() > 0)
cardsWithNoEdition.put(sec, cardNamesWithNoEdition);
}
CardPool pool = CardPool.fromCardList(cardsInSection);
// NOTE: @Leriomaggio -
// this will need improvements with a validation schema for each section to avoid
// accidental additions and/or sanitise the content of each section.
// I used to store planes and schemes under sideboard header, so this will assign them to a correct section
IPaperCard sample = pool.get(0);
if (sample != null && ( sample.getRules().getType().isPlane() || sample.getRules().getType().isPhenomenon())) {
if (sample != null && ( sample.getRules().getType().isPlane() || sample.getRules().getType().isPhenomenon()))
sec = DeckSection.Planes;
}
if (sample != null && sample.getRules().getType().isScheme()) {
if (sample != null && sample.getRules().getType().isScheme())
sec = DeckSection.Schemes;
}
putSection(sec, pool);
}
deferredSections = null;
if (!hasExplicitlySpecifiedSet) {
convertByXitaxMethod();
// deferredSections = null;
if (smartCardArtSelection && foundCardsWithUnspecifiedEditions){
/* Logic Explained:
foundCardsWithUnspecifiedEditions = true:
some cards have been retrieved according to Preferred Card Art Option
that is, some sections to have a list of cards to match
smartCardArtSelection = true
Optimising Preferred Card Art Option is wanted
*/
updateCardArtSelectionInPools(cardsWithNoEdition);
}
}
private void convertByXitaxMethod() {
Date dateWithAllCards = StaticData.instance().getEditions().getEarliestDateWithAllCards(getAllCardsInASinglePool());
for(Entry<DeckSection, CardPool> p : parts.entrySet()) {
if( p.getKey() == DeckSection.Planes || p.getKey() == DeckSection.Schemes || p.getKey() == DeckSection.Avatar)
private void updateCardArtSelectionInPools(Map<DeckSection, ArrayList<String>> cardsWithNoEdition) {
StaticData data = StaticData.instance();
for(Entry<DeckSection, CardPool> part : parts.entrySet()) {
DeckSection deckSection = part.getKey();
if(deckSection == DeckSection.Planes || deckSection == DeckSection.Schemes || deckSection == DeckSection.Avatar)
continue;
CardPool newPool = new CardPool();
// == 0. First Off, check if there is anything at all to do for the current section
ArrayList<String> cardNamesWithNoEditionInSection = cardsWithNoEdition.getOrDefault(deckSection, null);
if (cardNamesWithNoEditionInSection == null || cardNamesWithNoEditionInSection.size() == 0)
continue; // nothing to do here
for(Entry<PaperCard, Integer> cp : p.getValue()){
// Otherwise, start by gathering existing pool
CardPool pool = part.getValue();
// == 1. Gather Statistics per Edition
Map<CardEdition, Integer> editionStatistics = new HashMap<>();
for(Entry<PaperCard, Integer> cp : pool) {
PaperCard card = cp.getKey();
// Exclude basicLand from stats
if (card.isVeryBasicLand())
continue;
int count = cp.getValue();
CardEdition edition = data.getCardEdition(card.getEdition());
int currentCount = editionStatistics.getOrDefault(edition, 0);
currentCount += count;
editionStatistics.put(edition, currentCount);
}
/* == 2. Get the REFERENCE EDITION:
Note: This is the first, and the most crucial part of the procedure as future choices on card
art will be made depending on this REFERENCE EDITION.
The Reference Edition is the Edition wrt. other arts will be retrieved, using
a selection policy that complies with the CardArt Preference Specified in the Settings/DB.
In more details, the REFERENCE edition is the most recent edition among those having the highest
number of cards in the pool//deck section - NOT including Basic Lands!
*/
CardEdition referenceEdition = null;
int maxCardOccurrence = 0;
for(Entry<CardEdition, Integer> entry : editionStatistics.entrySet()) {
Integer cardCount = entry.getValue();
CardEdition ed = entry.getKey();
if (referenceEdition == null || cardCount > maxCardOccurrence){
maxCardOccurrence = cardCount;
referenceEdition = ed;
}
else if (cardCount == maxCardOccurrence) {
// Same Card Count, update referenceEdition is release date is more recent than current reference
if (ed.getDate().compareTo(referenceEdition.getDate()) > 0)
referenceEdition = ed;
}
}
// == 3. Last step, update the pool!
// Get Reference release date as two days AFTER the actual release date
// of the reference date so that in case, the reference date will be counted in too!
Calendar cal = Calendar.getInstance();
cal.setTime(referenceEdition.getDate());
cal.add(Calendar.DATE, 2);
Date releaseDateReferenceEdition = cal.getTime();
CardPool newPool = new CardPool();
for(Entry<PaperCard, Integer> cp : pool) {
PaperCard card = cp.getKey();
int count = cp.getValue();
PaperCard replacementCard;
replacementCard = StaticData.instance().getAlternativeCardPrint(card, dateWithAllCards);
// Note @leriomaggio: The following logic is very obscure to me
// Why looking for a replacement Card and then not using adding it to the pool?
// Also, what does "having the same art index" say about the two cards?
if (replacementCard.getArtIndex() == card.getArtIndex()) {
if (card.hasImage())
if (!cardNamesWithNoEditionInSection.contains(card.getName()))
newPool.add(card, count); // Nothing to do
else if (card.getEdition().equals(referenceEdition.getCode()))
newPool.add(card, count); // Nothing to do either
else {
PaperCard alternativeArtCard = data.getAlternativeCardPrint(card, releaseDateReferenceEdition);
if (alternativeArtCard == null) // no alternative found
newPool.add(card, count);
else{
if (!alternativeArtCard.isVeryBasicLand()){
if (!data.cardArtPreferenceIsLatest()) // if it's not Latest Art, No worries at all
newPool.add(alternativeArtCard, count);
// Is Latest
CardEdition alternateCardEd = data.getCardEdition(alternativeArtCard.getEdition());
if (referenceEdition.isModern() && alternateCardEd.isModern())
newPool.add(alternativeArtCard, count); // keep the substitution
else
newPool.add(replacementCard, count);
} else {
if (card.hasImage())
newPool.add(card.getName(), card.getEdition(), count); // this is to randomize art
else
newPool.add(replacementCard, count);
newPool.add(card, count); // keep the original selection based on card art preference
} else
newPool.add(alternativeArtCard, count); // BasicLand - any sub will be great!
}
}
}
parts.put(p.getKey(), newPool);
parts.put(deckSection, newPool);
}
}
// private void convertByXitaxMethod() {
// Date dateWithAllCards = StaticData.instance().getEditions().getEarliestDateWithAllCards(getAllCardsInASinglePool());
// for(Entry<DeckSection, CardPool> p : parts.entrySet()) {
// if( p.getKey() == DeckSection.Planes || p.getKey() == DeckSection.Schemes || p.getKey() == DeckSection.Avatar)
// continue;
//
// CardPool newPool = new CardPool();
//
// for(Entry<PaperCard, Integer> cp : p.getValue()) {
// PaperCard card = cp.getKey();
// int count = cp.getValue();
//
// PaperCard replacementCard;
// replacementCard = StaticData.instance().getAlternativeCardPrint(card, dateWithAllCards);
//
// // Note @leriomaggio: The following logic is very obscure to me
// // Why looking for a replacement Card and then not using adding it to the pool?
// // Also, what does "having the same art index" say about the two cards?
// if (replacementCard == null) {
// newPool.add(card, count);
// }
// else {
// if (replacementCard.getArtIndex() == card.getArtIndex()) {
// if (card.hasImage())
// newPool.add(card, count);
// else
// newPool.add(replacementCard, count);
// } else {
// if (card.hasImage())
// newPool.add(card.getName(), card.getEdition(), count); // this is to randomize art
// else
// newPool.add(replacementCard, count);
// }
// }
// }
//
// parts.put(p.getKey(), newPool);
// }
// }
public static final Function<Deck, String> FN_NAME_SELECTOR = new Function<Deck, String>() {
@Override
public String apply(Deck arg1) {