mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 20:28:00 +00:00
TEMP implementation of Smart Card Art Selection method
This commit is contained in:
@@ -21,8 +21,10 @@ import com.google.common.base.Function;
|
|||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import forge.StaticData;
|
import forge.StaticData;
|
||||||
import forge.card.CardDb;
|
import forge.card.CardDb;
|
||||||
|
import forge.card.CardEdition;
|
||||||
import forge.item.IPaperCard;
|
import forge.item.IPaperCard;
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
|
import org.apache.commons.lang3.tuple.Pair;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
@@ -220,71 +222,195 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
|
|||||||
return;
|
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()) {
|
for (Entry<String, List<String>> s : deferredSections.entrySet()) {
|
||||||
DeckSection sec = DeckSection.smartValueOf(s.getKey());
|
DeckSection sec = DeckSection.smartValueOf(s.getKey());
|
||||||
if (sec == null) {
|
if (sec == null)
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
final List<String> cardsInSection = s.getValue();
|
final List<String> cardsInSection = s.getValue();
|
||||||
for (String k : cardsInSection)
|
|
||||||
if (k.indexOf(CardDb.NameSetSeparator) > 0)
|
if (smartCardArtSelection){
|
||||||
hasExplicitlySpecifiedSet = true;
|
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);
|
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
|
// I used to store planes and schemes under sideboard header, so this will assign them to a correct section
|
||||||
IPaperCard sample = pool.get(0);
|
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;
|
sec = DeckSection.Planes;
|
||||||
}
|
if (sample != null && sample.getRules().getType().isScheme())
|
||||||
if (sample != null && sample.getRules().getType().isScheme()) {
|
|
||||||
sec = DeckSection.Schemes;
|
sec = DeckSection.Schemes;
|
||||||
}
|
|
||||||
putSection(sec, pool);
|
putSection(sec, pool);
|
||||||
}
|
}
|
||||||
|
// deferredSections = null;
|
||||||
deferredSections = null;
|
if (smartCardArtSelection && foundCardsWithUnspecifiedEditions){
|
||||||
if (!hasExplicitlySpecifiedSet) {
|
/* Logic Explained:
|
||||||
convertByXitaxMethod();
|
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() {
|
private void updateCardArtSelectionInPools(Map<DeckSection, ArrayList<String>> cardsWithNoEdition) {
|
||||||
Date dateWithAllCards = StaticData.instance().getEditions().getEarliestDateWithAllCards(getAllCardsInASinglePool());
|
StaticData data = StaticData.instance();
|
||||||
for(Entry<DeckSection, CardPool> p : parts.entrySet()) {
|
for(Entry<DeckSection, CardPool> part : parts.entrySet()) {
|
||||||
if( p.getKey() == DeckSection.Planes || p.getKey() == DeckSection.Schemes || p.getKey() == DeckSection.Avatar)
|
DeckSection deckSection = part.getKey();
|
||||||
|
if(deckSection == DeckSection.Planes || deckSection == DeckSection.Schemes || deckSection == DeckSection.Avatar)
|
||||||
continue;
|
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();
|
PaperCard card = cp.getKey();
|
||||||
int count = cp.getValue();
|
int count = cp.getValue();
|
||||||
|
if (!cardNamesWithNoEditionInSection.contains(card.getName()))
|
||||||
PaperCard replacementCard;
|
newPool.add(card, count); // Nothing to do
|
||||||
replacementCard = StaticData.instance().getAlternativeCardPrint(card, dateWithAllCards);
|
else if (card.getEdition().equals(referenceEdition.getCode()))
|
||||||
|
newPool.add(card, count); // Nothing to do either
|
||||||
// Note @leriomaggio: The following logic is very obscure to me
|
else {
|
||||||
// Why looking for a replacement Card and then not using adding it to the pool?
|
PaperCard alternativeArtCard = data.getAlternativeCardPrint(card, releaseDateReferenceEdition);
|
||||||
// Also, what does "having the same art index" say about the two cards?
|
if (alternativeArtCard == null) // no alternative found
|
||||||
if (replacementCard.getArtIndex() == card.getArtIndex()) {
|
|
||||||
if (card.hasImage())
|
|
||||||
newPool.add(card, count);
|
newPool.add(card, count);
|
||||||
else
|
else{
|
||||||
newPool.add(replacementCard, count);
|
if (!alternativeArtCard.isVeryBasicLand()){
|
||||||
} else {
|
if (!data.cardArtPreferenceIsLatest()) // if it's not Latest Art, No worries at all
|
||||||
if (card.hasImage())
|
newPool.add(alternativeArtCard, count);
|
||||||
newPool.add(card.getName(), card.getEdition(), count); // this is to randomize art
|
// Is Latest
|
||||||
else
|
CardEdition alternateCardEd = data.getCardEdition(alternativeArtCard.getEdition());
|
||||||
newPool.add(replacementCard, count);
|
if (referenceEdition.isModern() && alternateCardEd.isModern())
|
||||||
|
newPool.add(alternativeArtCard, count); // keep the substitution
|
||||||
|
else
|
||||||
|
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>() {
|
public static final Function<Deck, String> FN_NAME_SELECTOR = new Function<Deck, String>() {
|
||||||
@Override
|
@Override
|
||||||
public String apply(Deck arg1) {
|
public String apply(Deck arg1) {
|
||||||
|
|||||||
Reference in New Issue
Block a user