Use card ranker in BoosterDraftAI so pack cards are compared with existing cards in deck

This commit is contained in:
mcrawford620
2016-07-11 05:12:49 +00:00
parent 8b5e00f93b
commit 83d93c4436
5 changed files with 108 additions and 138 deletions

View File

@@ -2,17 +2,14 @@ package forge;
import forge.card.CardRarity;
import forge.card.CardRules;
import forge.card.DeckHints;
import forge.item.PaperCard;
import forge.limited.CardRanker;
import forge.properties.ForgeConstants;
import forge.util.FileUtil;
import org.apache.commons.lang3.tuple.Pair;
import org.junit.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import java.awt.print.Paper;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
@@ -32,12 +29,16 @@ public class CardRankerTest {
Assert.assertEquals("Hero of Goma Fada", cp.getName());
List<PaperCard> list = new ArrayList<PaperCard>();
PaperCard c0 = readCard("hero_of_goma_fada.txt");
PaperCard c0 = readCard("makindi_patrol.txt");
list.add(c0);
PaperCard c1 = readCard("makindi_patrol.txt");
PaperCard c1 = readCard("hero_of_goma_fada.txt");
list.add(c1);
PaperCard c2 = readCard("altars_reap.txt");
list.add(c2);
PaperCard c3 = readCard("plains.txt");
list.add(c3);
List<PaperCard> ranked = ranker.rankCards(list);
List<PaperCard> ranked = ranker.rankCardsInDeck(list);
}
/**

View File

@@ -49,14 +49,9 @@ public class BoosterDraftAI {
protected static final int N_DECKS = 7;
// holds all the cards for each of the computer's decks
protected final List<List<PaperCard>> deck = new ArrayList<List<PaperCard>>();
protected final List<List<PaperCard>> decks = new ArrayList<List<PaperCard>>();
protected final List<DeckColors> playerColors = new ArrayList<DeckColors>();
// roughly equivalent to 25 ranks in a core set, or 15 ranks in a small set
private static final double TAKE_BEST_THRESHOLD = 0.1;
// rank worse than any other card available to draft
private static final double RANK_UNPICKABLE = 999.0;
protected CardRanker ranker = new CardRanker();
/**
* <p>
@@ -74,99 +69,26 @@ public class BoosterDraftAI {
System.out.println("Player[" + player + "] pack: " + chooseFrom.toString());
}
final List<PaperCard> deck = decks.get(player);
final DeckColors deckCols = this.playerColors.get(player);
final ColorSet currentChoice = deckCols.getChosenColors();
final ColorSet chosenColors = deckCols.getChosenColors();
final boolean canAddMoreColors = deckCols.canChoseMoreColors();
final List<Pair<PaperCard, Double>> rankedCards = rankCards(chooseFrom, IBoosterDraft.CUSTOM_RANKINGS_FILE[0]);
for (final Pair<PaperCard, Double> p : rankedCards) {
double valueBoost = 0;
// If a card is not ai playable, somewhat decrease its rating
if( p.getKey().getRules().getAiHints().getRemAIDecks() ) {
valueBoost = TAKE_BEST_THRESHOLD;
}
// if I cannot choose more colors, and the card cannot be played with chosen colors, decrease its rating.
if( !canAddMoreColors && !p.getKey().getRules().getManaCost().canBePaidWithAvaliable(currentChoice.getColor())) {
valueBoost = TAKE_BEST_THRESHOLD * 3;
}
if (valueBoost > 0) {
p.setValue(p.getValue() + valueBoost);
//System.out.println(p.getKey() + " is now " + p.getValue());
}
}
double bestRanking = Double.MAX_VALUE;
PaperCard bestPick = null;
final List<PaperCard> possiblePick = new ArrayList<PaperCard>();
for (final Pair<PaperCard, Double> p : rankedCards) {
final double rating = p.getValue();
if(rating <= bestRanking + .01) {
if (rating < bestRanking) {
// found a better card start a new list
possiblePick.clear();
bestRanking = rating;
}
possiblePick.add(p.getKey());
}
}
bestPick = Aggregates.random(possiblePick);
List<PaperCard> rankedCards = ranker.rankCardsInPack(chooseFrom, deck, chosenColors, canAddMoreColors);
PaperCard bestPick = rankedCards.get(0);
if (canAddMoreColors) {
deckCols.addColorsOf(bestPick);
}
if (ForgePreferences.DEV_MODE) {
System.out.println("Player[" + player + "] picked: " + bestPick + " ranking of " + bestRanking);
System.out.println("Player[" + player + "] picked: " + bestPick);
}
this.deck.get(player).add(bestPick);
this.decks.get(player).add(bestPick);
return bestPick;
}
/**
* Sort cards by rank. Note that if pack has cards from different editions,
* they could have the same rank. Basic lands and unrecognised cards are
* rated worse than all other possible picks.
*
* @param chooseFrom
* List of cards
* @return map of rankings
*/
private static List<Pair<PaperCard, Double>> rankCards(final Iterable<PaperCard> chooseFrom, String customRankings) {
final List<Pair<PaperCard, Double>> rankedCards = new ArrayList<Pair<PaperCard,Double>>();
for (final PaperCard card : chooseFrom) {
Double rank;
if (MagicColor.Constant.BASIC_LANDS.contains(card.getName())) {
rank = RANK_UNPICKABLE;
} else {
if (customRankings != null) {
rank = DraftRankCache.getCustomRanking(customRankings, card.getName());
if (rank == null) {
// try the default draft rankings if there's no entry in the custom rankings file
rank = DraftRankCache.getRanking(card.getName(), card.getEdition());
}
} else {
rank = DraftRankCache.getRanking(card.getName(), card.getEdition());
}
if (rank == null) {
if (ForgePreferences.DEV_MODE) {
System.out.println("Draft Rankings - Card Not Found: " + card.getName());
}
rank = RANK_UNPICKABLE;
}
}
rankedCards.add(MutablePair.of(card, rank));
}
return rankedCards;
}
/**
* <p>
* getDecks.
@@ -175,14 +97,14 @@ public class BoosterDraftAI {
* @return an array of {@link forge.deck.Deck} objects.
*/
public Deck[] getDecks() {
final Deck[] out = new Deck[this.deck.size()];
final Deck[] out = new Deck[this.decks.size()];
for (int i = 0; i < this.deck.size(); i++) {
for (int i = 0; i < this.decks.size(); i++) {
if (ForgePreferences.DEV_MODE) {
System.out.println("Deck[" + i + "]");
}
out[i] = new BoosterDeckBuilder(this.deck.get(i), this.playerColors.get(i)).buildDeck();
out[i] = new BoosterDeckBuilder(this.decks.get(i), this.playerColors.get(i)).buildDeck();
}
return out;
} // getDecks()
@@ -195,7 +117,7 @@ public class BoosterDraftAI {
public BoosterDraftAI() {
// Initialize deck array and playerColors list
for (int i = 0; i < N_DECKS; i++) {
this.deck.add(new ArrayList<PaperCard>());
this.decks.add(new ArrayList<PaperCard>());
this.playerColors.add(new DeckColors());
}
} // BoosterDraftAI()

View File

@@ -2,8 +2,7 @@ package forge.limited;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import forge.card.CardRulesPredicates;
import forge.card.DeckHints;
import forge.card.*;
import forge.item.PaperCard;
import org.apache.commons.lang3.tuple.Pair;
@@ -14,24 +13,29 @@ import java.util.Map;
public class CardRanker {
private static final double SCORE_UNPICKABLE = -100.0;
/**
* Rank cards.
*
* @param cards PaperCards to rank
* @return List of beans with card rankings
*/
public List<PaperCard> rankCards(final Iterable<PaperCard> cards) {
public List<PaperCard> rankCardsInDeck(final Iterable<PaperCard> cards) {
List<Pair<Double, PaperCard>> cardScores = getScores(cards);
Collections.sort(cardScores, Collections.reverseOrder(new CardRankingComparator()));
return sortAndCreateList(cardScores);
}
List<PaperCard> rankedCards = new ArrayList<>(cardScores.size());
for (Pair<Double, PaperCard> pair : cardScores) {
System.out.println(pair.getKey().toString() + " " + pair.getValue().getName());
rankedCards.add(pair.getValue());
}
public List<PaperCard> rankCardsInPack(
final Iterable<PaperCard> cardsInPack,
final List<PaperCard> deck,
ColorSet chosenColors,
boolean canAddMoreColors
) {
List<Pair<Double, PaperCard>> cardScores = getScoresForPack(cardsInPack, deck, chosenColors, canAddMoreColors);
return rankedCards;
return sortAndCreateList(cardScores);
}
private List<Pair<Double, PaperCard>> getScores(Iterable<PaperCard> cards) {
@@ -42,14 +46,15 @@ public class CardRanker {
cache.add(card);
}
String customRankings = IBoosterDraft.CUSTOM_RANKINGS_FILE[0];
for (int i = 0; i < cache.size(); i++) {
final PaperCard card = cache.get(i);
double score = getRawScore(card, customRankings);
List<PaperCard> otherCards = getCardsExcept(cache, i);
double score = getRawScore(card);
if (card.getRules().getAiHints().getRemAIDecks()) {
score -= 20.0;
}
List<PaperCard> otherCards = getCardsExceptOne(cache, i);
score += calculateSynergies(card, otherCards);
cardScores.add(Pair.of(score, card));
@@ -58,28 +63,58 @@ public class CardRanker {
return cardScores;
}
private double getRawScore(PaperCard card, String customRankings) {
Double rkg;
if (customRankings != null) {
rkg = DraftRankCache.getCustomRanking(customRankings, card.getName());
if (rkg == null) {
// try the default rankings if custom rankings contain no entry
rkg = DraftRankCache.getRanking(card.getName(), card.getEdition());
private List<Pair<Double, PaperCard>> getScoresForPack(
Iterable<PaperCard> cardsInPack,
List<PaperCard> deck,
ColorSet chosenColors,
boolean canAddMoreColors
) {
List<Pair<Double, PaperCard>> cardScores = new ArrayList<>();
for (PaperCard card : cardsInPack) {
double score = getRawScore(card);
if (card.getRules().getAiHints().getRemAIDecks()) {
score -= 20.0;
}
} else {
rkg = DraftRankCache.getRanking(card.getName(), card.getEdition());
if( !canAddMoreColors && !card.getRules().getManaCost().canBePaidWithAvaliable(chosenColors.getColor())) {
score -= 50.0;
}
score += calculateSynergies(card, deck);
cardScores.add(Pair.of(score, card));
}
double rawScore;
if (rkg != null) {
rawScore = 100 - (100 * rkg);
return cardScores;
}
private double getRawScore(PaperCard card) {
Double rawScore;
if (MagicColor.Constant.BASIC_LANDS.contains(card.getName())) {
rawScore = SCORE_UNPICKABLE;
} else {
rawScore = 0.0;
Double rkg;
String customRankings = IBoosterDraft.CUSTOM_RANKINGS_FILE[0];
if (customRankings != null) {
rkg = DraftRankCache.getCustomRanking(customRankings, card.getName());
if (rkg == null) {
// try the default rankings if custom rankings contain no entry
rkg = DraftRankCache.getRanking(card.getName(), card.getEdition());
}
} else {
rkg = DraftRankCache.getRanking(card.getName(), card.getEdition());
}
if (rkg != null) {
rawScore = 100 - (100 * rkg);
} else {
rawScore = SCORE_UNPICKABLE;
}
}
return rawScore;
}
private List<PaperCard> getCardsExcept(List<PaperCard> cache, int i) {
private List<PaperCard> getCardsExceptOne(List<PaperCard> cache, int i) {
List<PaperCard> otherCards = new ArrayList<>();
otherCards.addAll(cache.subList(0, i));
if (i + 1 < cache.size()) {
@@ -102,7 +137,9 @@ public class CardRanker {
final DeckHints hints = card.getRules().getAiHints().getDeckHints();
if (hints != null && hints.getType() != DeckHints.Type.NONE) {
final List<PaperCard> comboCards = hints.filter(otherCards);
score = comboCards.size() * 10;
if (comboCards.size() > 0) {
score = comboCards.size() * 10;
}
}
return score;
}
@@ -115,9 +152,22 @@ public class CardRanker {
String buff = var.getValue();
final Iterable<PaperCard> buffers = Iterables.filter(otherCards,
Predicates.compose(CardRulesPredicates.subType(buff), PaperCard.FN_GET_RULES));
matchBuffScore = Iterables.size(buffers) * 3;
if (Iterables.size(buffers) > 0) {
matchBuffScore = Iterables.size(buffers) * 3;
}
}
}
return matchBuffScore;
}
private List<PaperCard> sortAndCreateList(List<Pair<Double, PaperCard>> cardScores) {
Collections.sort(cardScores, Collections.reverseOrder(new CardRankingComparator()));
List<PaperCard> rankedCards = new ArrayList<>(cardScores.size());
for (Pair<Double, PaperCard> pair : cardScores) {
rankedCards.add(pair.getValue());
}
return rankedCards;
}
}

View File

@@ -1,8 +1,6 @@
package forge.limited;
import java.awt.print.Paper;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -10,8 +8,6 @@ import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
@@ -62,13 +58,14 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
private final List<PaperCard> aiPlayables;
private final List<PaperCard> deckList = new ArrayList<PaperCard>();
private final List<String> setsWithBasicLands = new ArrayList<String>();
// Views for aiPlayable
protected CardRanker ranker = new CardRanker();
private CardRanker ranker = new CardRanker();
// Views for aiPlayable
private Iterable<PaperCard> onColorCreatures;
private Iterable<PaperCard> onColorNonCreatures;
private static final boolean logToConsole = true;
private static final boolean logToConsole = false;
/**
*
@@ -88,7 +85,7 @@ public class LimitedDeckBuilder extends DeckGeneratorBase {
// remove Unplayables
final Iterable<PaperCard> playables = Iterables.filter(availableList,
Predicates.compose(CardRulesPredicates.IS_KEPT_IN_AI_DECKS, PaperCard.FN_GET_RULES));
this.aiPlayables = this.ranker.rankCards(playables);
this.aiPlayables = this.ranker.rankCardsInDeck(playables);
this.availableList.removeAll(aiPlayables);
findBasicLandSets();

View File

@@ -22,10 +22,10 @@ public class WinstonDraftAI extends BoosterDraftAI{
}
public WinstonDraftAI() {
this.deck.clear();
this.decks.clear();
this.playerColors.clear();
for (int i = 0; i < N_DECKS; i++) {
this.deck.add(new ArrayList<PaperCard>());
this.decks.add(new ArrayList<PaperCard>());
this.playerColors.add(new DeckColors());
}
}
@@ -61,7 +61,7 @@ public class WinstonDraftAI extends BoosterDraftAI{
}
}
if (acquire != null) {
this.deck.get(0).addAll(acquire.toFlatList());
this.decks.get(0).addAll(acquire.toFlatList());
//tallyDeckColors();
}
}
@@ -104,6 +104,6 @@ public class WinstonDraftAI extends BoosterDraftAI{
}*/
public int getAIDraftSize() {
return this.deck.get(0).size();
return this.decks.get(0).size();
}
}