mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 18:28:00 +00:00
Greatly simplified BoosterDraftAI to pick cards based on calculated card rating.
This commit is contained in:
@@ -58,7 +58,7 @@ public final class ColorSet implements Comparable<ColorSet> {
|
||||
}
|
||||
|
||||
public static ColorSet fromMask(final int mask) {
|
||||
int mask32 = (mask & 0x3E) >> 1;
|
||||
int mask32 = (mask & MagicColor.ALL_COLORS) >> 1;
|
||||
if (allColors[mask32] == null) {
|
||||
allColors[mask32] = new ColorSet((byte) mask);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ public class MagicColor {
|
||||
public static final byte RED = 1 << 4;
|
||||
public static final byte WHITE = 1 << 1;
|
||||
|
||||
|
||||
public static final byte ALL_COLORS = BLACK | BLUE | WHITE | RED | GREEN;
|
||||
public static final int NUMBER_OR_COLORS = 5;
|
||||
|
||||
|
||||
public static byte fromName(String s) {
|
||||
if (s.equalsIgnoreCase(Constant.Color.WHITE) || s.equalsIgnoreCase("w")) {
|
||||
|
||||
@@ -115,13 +115,11 @@ public class AbilityManaPart implements java.io.Serializable {
|
||||
for (final String c : produced.split(" ")) {
|
||||
try {
|
||||
int colorlessAmount = Integer.parseInt(c);
|
||||
final String color = InputPayManaCostUtil.getLongColorString(c);
|
||||
for (int i = 0; i < colorlessAmount; i++) {
|
||||
this.lastProduced.add(new Mana(color, source, this));
|
||||
this.lastProduced.add(new Mana(c, source, this));
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
final String color = InputPayManaCostUtil.getLongColorString(c);
|
||||
this.lastProduced.add(new Mana(color, source, this));
|
||||
this.lastProduced.add(new Mana(c, source, this));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,23 +19,13 @@ package forge.game.limited;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.TreeMap;
|
||||
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;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import forge.Constant;
|
||||
import forge.Constant.Preferences;
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.CardRulesPredicates;
|
||||
import forge.card.CardRules;
|
||||
import forge.deck.Deck;
|
||||
import forge.deck.generate.GenerateDeckUtil;
|
||||
import forge.item.CardPrinted;
|
||||
import forge.util.Aggregates;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -79,141 +69,50 @@ public class BoosterDraftAI {
|
||||
System.out.println("Player[" + player + "] pack: " + chooseFrom.toString());
|
||||
}
|
||||
|
||||
CardPrinted pickedCard = null;
|
||||
DeckColors deckCols = this.playerColors.get(player);
|
||||
ColorSet currentChoice = deckCols.getChosenColors();
|
||||
boolean canAddMoreColors = deckCols.canChoseMoreColors();
|
||||
|
||||
List<Pair<CardPrinted, Double>> rankedCards = rankCards(chooseFrom);
|
||||
|
||||
for(Pair<CardPrinted, Double> p : rankedCards) {
|
||||
// If a card is not ai playable, somewhat decreare its rating
|
||||
if( p.getKey().getCard().getRemAIDecks() )
|
||||
p.setValue(p.getValue() - TAKE_BEST_THRESHOLD);
|
||||
|
||||
Predicate<CardPrinted> pred = Predicates.compose(CardRulesPredicates.IS_KEPT_IN_AI_DECKS, CardPrinted.FN_GET_RULES);
|
||||
Iterable<CardPrinted> aiPlayablesView = Iterables.filter(chooseFrom, pred);
|
||||
List<CardPrinted> aiPlayables = Lists.newArrayList(aiPlayablesView);
|
||||
|
||||
TreeMap<Double, CardPrinted> rankedCards = rankCards(chooseFrom);
|
||||
|
||||
if (this.playerColors.get(player).getColor1().equals("none")
|
||||
&& this.playerColors.get(player).getColor2().equals("none")) {
|
||||
// Generally the first pick of the draft, no colors selected yet.
|
||||
|
||||
// Sort playable cards by rank
|
||||
TreeMap<Double, CardPrinted> rankedPlayableCards = rankCards(aiPlayables);
|
||||
|
||||
pickedCard = pickCard(rankedCards, rankedPlayableCards);
|
||||
|
||||
if (!pickedCard.getCard().getColor().isColorless() && aiPlayables.contains(pickedCard)) {
|
||||
ColorSet color = pickedCard.getCard().getColor();
|
||||
if (color.isMonoColor()) {
|
||||
this.playerColors.get(player).setColor1(color.toString());
|
||||
} else {
|
||||
// Arbitrary ordering here...
|
||||
if (color.hasWhite()) {
|
||||
this.playerColors.get(player).setColor1(Constant.Color.WHITE);
|
||||
}
|
||||
if (color.hasBlue()) {
|
||||
if (this.playerColors.get(player).getColor1().equals("none")) {
|
||||
this.playerColors.get(player).setColor1(Constant.Color.BLUE);
|
||||
} else {
|
||||
this.playerColors.get(player).setColor2(Constant.Color.BLUE);
|
||||
}
|
||||
}
|
||||
if (color.hasBlack()) {
|
||||
if (this.playerColors.get(player).getColor1().equals("none")) {
|
||||
this.playerColors.get(player).setColor1(Constant.Color.BLACK);
|
||||
} else {
|
||||
this.playerColors.get(player).setColor2(Constant.Color.BLACK);
|
||||
}
|
||||
}
|
||||
if (color.hasRed()) {
|
||||
if (this.playerColors.get(player).getColor1().equals("none")) {
|
||||
this.playerColors.get(player).setColor1(Constant.Color.RED);
|
||||
} else {
|
||||
this.playerColors.get(player).setColor2(Constant.Color.RED);
|
||||
}
|
||||
}
|
||||
if (color.hasGreen()) {
|
||||
if (this.playerColors.get(player).getColor1().equals("none")) {
|
||||
this.playerColors.get(player).setColor1(Constant.Color.GREEN);
|
||||
} else {
|
||||
this.playerColors.get(player).setColor2(Constant.Color.GREEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Preferences.DEV_MODE) {
|
||||
System.out.println("Player[" + player + "] Color1: " + this.playerColors.get(player).getColor1());
|
||||
if (!this.playerColors.get(player).getColor2().equals("none")) {
|
||||
System.out.println("Player[" + player + "] Color2: "
|
||||
+ this.playerColors.get(player).getColor2());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!this.playerColors.get(player).getColor1().equals("none")
|
||||
&& this.playerColors.get(player).getColor2().equals("none")) {
|
||||
// Has already picked one color, but not the second.
|
||||
|
||||
// Sort playable, on-color, or mono-colored, or colorless cards
|
||||
TreeMap<Double, CardPrinted> rankedPlayableCards = new TreeMap<Double, CardPrinted>();
|
||||
for (CardPrinted card : aiPlayables) {
|
||||
ColorSet currentColor1 = ColorSet.fromNames(this.playerColors.get(player).getColor1());
|
||||
ColorSet color = card.getCard().getColor();
|
||||
if (color.isColorless() || color.sharesColorWith(currentColor1) || color.isMonoColor()) {
|
||||
Double rkg = draftRankings.getRanking(card.getName(), card.getEdition());
|
||||
if (rkg != null) {
|
||||
rankedPlayableCards.put(rkg, card);
|
||||
} else {
|
||||
rankedPlayableCards.put(0.0, card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pickedCard = pickCard(rankedCards, rankedPlayableCards);
|
||||
|
||||
ColorSet color = pickedCard.getCard().getColor();
|
||||
if (!color.isColorless() && aiPlayables.contains(pickedCard)) {
|
||||
ColorSet currentColor1 = ColorSet.fromNames(this.playerColors.get(player).getColor1());
|
||||
if (color.isMonoColor()) {
|
||||
if (!color.sharesColorWith(currentColor1)) {
|
||||
this.playerColors.get(player).setColor2(color.toString());
|
||||
}
|
||||
} else {
|
||||
// Arbitrary ordering...
|
||||
if (color.hasWhite() && !currentColor1.isWhite()) {
|
||||
this.playerColors.get(player).setColor2(Constant.Color.WHITE);
|
||||
} else if (color.hasBlue() && !currentColor1.isBlue()) {
|
||||
this.playerColors.get(player).setColor2(Constant.Color.BLUE);
|
||||
} else if (color.hasBlack() && !currentColor1.isBlack()) {
|
||||
this.playerColors.get(player).setColor2(Constant.Color.BLACK);
|
||||
} else if (color.hasRed() && !currentColor1.isRed()) {
|
||||
this.playerColors.get(player).setColor2(Constant.Color.RED);
|
||||
} else if (color.hasGreen() && !currentColor1.isGreen()) {
|
||||
this.playerColors.get(player).setColor2(Constant.Color.GREEN);
|
||||
}
|
||||
}
|
||||
if (Preferences.DEV_MODE) {
|
||||
System.out.println("Player[" + player + "] Color2: " + this.playerColors.get(player).getColor2());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Has already picked both colors.
|
||||
DeckColors dckColors = this.playerColors.get(player);
|
||||
ColorSet colors = ColorSet.fromNames(dckColors.getColor1(), dckColors.getColor2());
|
||||
Predicate<CardRules> hasColor = Predicates.or(new GenerateDeckUtil.CanBePaidWithColors(colors),
|
||||
GenerateDeckUtil.COLORLESS_CARDS);
|
||||
|
||||
Iterable<CardPrinted> colorList = Iterables.filter(aiPlayables, Predicates.compose(hasColor, CardPrinted.FN_GET_RULES));
|
||||
|
||||
// Sort playable, on-color cards by rank
|
||||
TreeMap<Double, CardPrinted> rankedPlayableCards = rankCards(colorList);
|
||||
|
||||
pickedCard = pickCard(rankedCards, rankedPlayableCards);
|
||||
// if I cannot choose more colors, and the card cannot be played with chosen colors, decrease its rating.
|
||||
if( !canAddMoreColors && !p.getKey().getCard().getManaCost().canBePaidWithAvaliable(currentChoice))
|
||||
p.setValue(p.getValue() - 10);
|
||||
}
|
||||
|
||||
if (pickedCard == null) {
|
||||
final Random r = new Random();
|
||||
pickedCard = chooseFrom.get(r.nextInt(chooseFrom.size()));
|
||||
int cntBestCards = 0;
|
||||
double bestRating = Double.NEGATIVE_INFINITY;
|
||||
CardPrinted bestPick = null;
|
||||
for(Pair<CardPrinted, Double> p : rankedCards) {
|
||||
double rating = p.getValue();
|
||||
if( rating > bestRating )
|
||||
{
|
||||
bestRating = rating;
|
||||
bestPick = p.getKey();
|
||||
cntBestCards = 1;
|
||||
} else if ( rating == bestRating ) {
|
||||
cntBestCards++;
|
||||
}
|
||||
}
|
||||
|
||||
if (pickedCard != null) {
|
||||
chooseFrom.remove(pickedCard);
|
||||
this.deck.get(player).add(pickedCard);
|
||||
if (cntBestCards > 1) {
|
||||
final List<CardPrinted> possiblePick = new ArrayList<CardPrinted>();
|
||||
for(Pair<CardPrinted, Double> p : rankedCards) {
|
||||
if ( p.getValue() == bestRating )
|
||||
possiblePick.add(p.getKey());
|
||||
}
|
||||
bestPick = Aggregates.random(possiblePick);
|
||||
}
|
||||
|
||||
return pickedCard;
|
||||
if (canAddMoreColors)
|
||||
deckCols.addColorsOf(bestPick);
|
||||
|
||||
return bestPick;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,69 +124,20 @@ public class BoosterDraftAI {
|
||||
* List of cards
|
||||
* @return map of rankings
|
||||
*/
|
||||
private TreeMap<Double, CardPrinted> rankCards(final Iterable<CardPrinted> chooseFrom) {
|
||||
TreeMap<Double, CardPrinted> rankedCards = new TreeMap<Double, CardPrinted>();
|
||||
private List<Pair<CardPrinted, Double>> rankCards(final Iterable<CardPrinted> chooseFrom) {
|
||||
List<Pair<CardPrinted, Double>> rankedCards = new ArrayList<Pair<CardPrinted,Double>>();
|
||||
for (CardPrinted card : chooseFrom) {
|
||||
Double rkg = draftRankings.getRanking(card.getName(), card.getEdition());
|
||||
if (rkg != null) {
|
||||
rankedCards.put(rkg, card);
|
||||
rankedCards.add(Pair.of(card, rkg));
|
||||
} else {
|
||||
System.out.println("Draft Rankings - Card Not Found: " + card.getName());
|
||||
rankedCards.put(0.0, card);
|
||||
rankedCards.add(Pair.of(card, 0.0));
|
||||
}
|
||||
}
|
||||
return rankedCards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a card.
|
||||
*
|
||||
* @param rankedCards
|
||||
* @param rankedPlayableCards
|
||||
* @return CardPrinted
|
||||
*/
|
||||
private CardPrinted pickCard(TreeMap<Double, CardPrinted> rankedCards,
|
||||
TreeMap<Double, CardPrinted> rankedPlayableCards) {
|
||||
CardPrinted pickedCard = null;
|
||||
Map.Entry<Double, CardPrinted> best = rankedCards.firstEntry();
|
||||
if (best != null) {
|
||||
if (rankedPlayableCards.containsValue(best.getValue())) {
|
||||
// If best card is playable, pick it.
|
||||
pickedCard = best.getValue();
|
||||
System.out.println("Choose Best: " + "[" + best.getKey() + "] " + pickedCard.getName() + " ("
|
||||
+ pickedCard.getCard().getManaCost() + ") " + pickedCard.getCard().getType().toString());
|
||||
} else {
|
||||
// If not, find the best card that is playable.
|
||||
Map.Entry<Double, CardPrinted> bestPlayable = rankedPlayableCards.firstEntry();
|
||||
if (bestPlayable == null) {
|
||||
// Nothing is playable, so just take the best card.
|
||||
pickedCard = best.getValue();
|
||||
System.out.println("Nothing playable, choose Best: " + "[" + best.getKey() + "] "
|
||||
+ pickedCard.getName() + " (" + pickedCard.getCard().getManaCost() + ") "
|
||||
+ pickedCard.getCard().getType().toString());
|
||||
} else {
|
||||
// If the best card is far better than the best playable,
|
||||
// take the best. Otherwise, take the one that is playable.
|
||||
if (best.getKey() + TAKE_BEST_THRESHOLD < bestPlayable.getKey()) {
|
||||
pickedCard = best.getValue();
|
||||
System.out.println("Best is much better than playable; chose Best: " + "[" + best.getKey()
|
||||
+ "] " + pickedCard.getName() + " (" + pickedCard.getCard().getManaCost() + ") "
|
||||
+ pickedCard.getCard().getType().toString());
|
||||
System.out.println("Playable was: " + "[" + bestPlayable.getKey() + "] "
|
||||
+ bestPlayable.getValue().getName());
|
||||
} else {
|
||||
pickedCard = bestPlayable.getValue();
|
||||
System.out.println("Chosen Playable: " + "[" + bestPlayable.getKey() + "] "
|
||||
+ pickedCard.getName() + " (" + pickedCard.getCard().getManaCost() + ") "
|
||||
+ pickedCard.getCard().getType().toString());
|
||||
System.out.println("Best was: " + "[" + best.getKey() + "] " + best.getValue().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return pickedCard;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* getDecks.
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
*/
|
||||
package forge.game.limited;
|
||||
|
||||
import forge.card.CardManaCost;
|
||||
import forge.card.ColorSet;
|
||||
import forge.card.MagicColor;
|
||||
import forge.item.CardPrinted;
|
||||
|
||||
/**
|
||||
* Created by IntelliJ IDEA. User: dhudson Date: 6/24/11 Time: 8:42 PM To change
|
||||
@@ -25,96 +28,45 @@ import forge.card.ColorSet;
|
||||
*/
|
||||
class DeckColors {
|
||||
|
||||
/** The Color1. */
|
||||
private String color1 = "none";
|
||||
|
||||
/** The Color2. */
|
||||
private String color2 = "none";
|
||||
|
||||
private ColorSet chosen;
|
||||
private int colorMask;
|
||||
public final static int MAX_COLORS = 2;
|
||||
// public String Splash = "none";
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructor for deckColors.
|
||||
* </p>
|
||||
*
|
||||
* @param c1
|
||||
* a {@link java.lang.String} object.
|
||||
* @param c2
|
||||
* a {@link java.lang.String} object.
|
||||
* @param sp
|
||||
* a {@link java.lang.String} object.
|
||||
*/
|
||||
public DeckColors(final String c1, final String c2, final String sp) {
|
||||
this.setColor1(c1);
|
||||
this.setColor2(c2);
|
||||
// Splash = sp;
|
||||
public ColorSet getChosenColors() {
|
||||
if ( null == chosen )
|
||||
chosen = ColorSet.fromMask(colorMask);
|
||||
return chosen;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Constructor for DeckColors.
|
||||
* </p>
|
||||
* TODO: Write javadoc for this method.
|
||||
* @param pickedCard
|
||||
*/
|
||||
public DeckColors() {
|
||||
public void addColorsOf(CardPrinted pickedCard) {
|
||||
|
||||
CardManaCost colorsInCard = pickedCard.getCard().getManaCost();
|
||||
|
||||
int colorsCanAdd = MagicColor.ALL_COLORS & ~getChosenColors().getColor();
|
||||
int colorsWantAdd = colorsInCard.getColorProfile() & colorsCanAdd;
|
||||
ColorSet toAdd = ColorSet.fromMask(colorsWantAdd);
|
||||
|
||||
int cntColorsAssigned = getChosenColors().countColors();
|
||||
boolean haveSpace = cntColorsAssigned < MAX_COLORS;
|
||||
if( !haveSpace || toAdd.isColorless() )
|
||||
return;
|
||||
|
||||
for(int i = 0; i < MagicColor.NUMBER_OR_COLORS && cntColorsAssigned < MAX_COLORS; i++ )
|
||||
if (( colorsWantAdd & MagicColor.WHITE << i ) > 0) {
|
||||
colorMask |= MagicColor.WHITE << i;
|
||||
chosen = null; // invalidate color set
|
||||
cntColorsAssigned++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color1.
|
||||
*
|
||||
* @return the color1
|
||||
*/
|
||||
public String getColor1() {
|
||||
return this.color1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color1.
|
||||
*
|
||||
* @param color1in
|
||||
* the color1 to set
|
||||
*/
|
||||
public void setColor1(final String color1in) {
|
||||
this.color1 = color1in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color2.
|
||||
*
|
||||
* @return the color2
|
||||
*/
|
||||
public String getColor2() {
|
||||
return this.color2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color2.
|
||||
*
|
||||
* @param color2in
|
||||
* the color2 to set
|
||||
*/
|
||||
public void setColor2(final String color2in) {
|
||||
this.color2 = color2in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this to CardColor.
|
||||
*
|
||||
* @return equivalent CardColor
|
||||
*/
|
||||
public ColorSet getCardColors() {
|
||||
return ColorSet.fromNames(color1, color2);
|
||||
}
|
||||
|
||||
/**
|
||||
* toString.
|
||||
*
|
||||
* @return description.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return color1 + '/' + color2;
|
||||
public boolean canChoseMoreColors() {
|
||||
return getChosenColors().countColors() < MAX_COLORS;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ public class LimitedDeck {
|
||||
public LimitedDeck(List<CardPrinted> dList, DeckColors pClrs) {
|
||||
this.availableList = dList;
|
||||
this.deckColors = pClrs;
|
||||
this.colors = pClrs.getCardColors();
|
||||
this.colors = pClrs.getChosenColors();
|
||||
|
||||
// removeUnplayables();
|
||||
Iterable<CardPrinted> playables = Iterables.filter(availableList,
|
||||
|
||||
Reference in New Issue
Block a user