mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
Use card ranker in BoosterDraftAI so pack cards are compared with existing cards in deck
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user