- Rewriting Booster Draft to be a bit more capable of handling Draft Matters cards

This commit is contained in:
Sol
2016-08-18 20:22:10 +00:00
parent 26166ef1dd
commit 9da706cbad
7 changed files with 304 additions and 200 deletions

2
.gitattributes vendored
View File

@@ -19750,6 +19750,8 @@ forge-gui/src/main/java/forge/limited/DraftRankCache.java -text
forge-gui/src/main/java/forge/limited/GauntletMini.java -text forge-gui/src/main/java/forge/limited/GauntletMini.java -text
forge-gui/src/main/java/forge/limited/IBoosterDraft.java svneol=native#text/plain forge-gui/src/main/java/forge/limited/IBoosterDraft.java svneol=native#text/plain
forge-gui/src/main/java/forge/limited/LimitedDeckBuilder.java -text forge-gui/src/main/java/forge/limited/LimitedDeckBuilder.java -text
forge-gui/src/main/java/forge/limited/LimitedPlayer.java -text
forge-gui/src/main/java/forge/limited/LimitedPlayerAI.java -text
forge-gui/src/main/java/forge/limited/LimitedPoolType.java -text forge-gui/src/main/java/forge/limited/LimitedPoolType.java -text
forge-gui/src/main/java/forge/limited/LimitedWinLoseController.java -text forge-gui/src/main/java/forge/limited/LimitedWinLoseController.java -text
forge-gui/src/main/java/forge/limited/ReadDraftRankings.java -text forge-gui/src/main/java/forge/limited/ReadDraftRankings.java -text

View File

@@ -7,6 +7,7 @@ import forge.game.card.Card;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.item.SealedProduct; import forge.item.SealedProduct;
import forge.limited.IBoosterDraft; import forge.limited.IBoosterDraft;
import forge.limited.LimitedPlayer;
import forge.model.FModel; import forge.model.FModel;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@@ -24,29 +25,14 @@ import java.util.List;
@Test(groups = { "UnitTest" }, timeOut = 1000, enabled = false) @Test(groups = { "UnitTest" }, timeOut = 1000, enabled = false)
public class BoosterDraftTest implements IBoosterDraft { public class BoosterDraftTest implements IBoosterDraft {
/** The n. */
private int n = 3; private int n = 3;
/**
* <p>
* getDecks.
* </p>
*
* @return an array of {@link forge.deck.Deck} objects.
*/
@Override @Override
@Test(timeOut = 1000) @Test(timeOut = 1000)
public Deck[] getDecks() { public Deck[] getDecks() {
return null; return null;
} }
/**
* <p>
* nextChoice.
* </p>
*
* @return a {@link forge.CardList} object.
*/
@Override @Override
public CardPool nextChoice() { public CardPool nextChoice() {
this.n--; this.n--;
@@ -62,13 +48,6 @@ public class BoosterDraftTest implements IBoosterDraft {
System.out.println(c.getName()); System.out.println(c.getName());
} }
/**
* <p>
* hasNextChoice.
* </p>
*
* @return a boolean.
*/
@Override @Override
public boolean hasNextChoice() { public boolean hasNextChoice() {
return this.n > 0; return this.n > 0;
@@ -79,24 +58,10 @@ public class BoosterDraftTest implements IBoosterDraft {
return hasNextChoice(); return hasNextChoice();
} }
/**
* <p>
* getChosenCards.
* </p>
*
* @return a {@link forge.CardList} object.
*/
public List<Card> getChosenCards() { public List<Card> getChosenCards() {
return null; return null;
} }
/**
* <p>
* getUnchosenCards.
* </p>
*
* @return a {@link forge.CardList} object.
*/
public List<Card> getUnchosenCards() { public List<Card> getUnchosenCards() {
return null; return null;
} }

View File

@@ -43,18 +43,16 @@ import java.util.*;
* Booster Draft Format. * Booster Draft Format.
*/ */
public class BoosterDraft implements IBoosterDraft { public class BoosterDraft implements IBoosterDraft {
private final BoosterDraftAI draftAI = new BoosterDraftAI();
private static final int N_PLAYERS = 8; private static final int N_PLAYERS = 8;
public static final String FILE_EXT = ".draft"; public static final String FILE_EXT = ".draft";
private final List<LimitedPlayer> players = new ArrayList<>();
private LimitedPlayer localPlayer;
protected int nextBoosterGroup = 0; protected int nextBoosterGroup = 0;
private int currentBoosterSize = 0; private int currentBoosterSize = 0;
private int currentBoosterPick = 0; private int currentBoosterPick = 0;
private int[] draftingBooster; private int packsInDraft;
private List<List<PaperCard>> pack; // size 8
/** The draft picks. */
private final Map<String, Float> draftPicks = new TreeMap<>(); private final Map<String, Float> draftPicks = new TreeMap<>();
protected LimitedPoolType draftFormat; protected LimitedPoolType draftFormat;
@@ -63,6 +61,7 @@ public class BoosterDraft implements IBoosterDraft {
public static BoosterDraft createDraft(final LimitedPoolType draftType) { public static BoosterDraft createDraft(final LimitedPoolType draftType) {
final BoosterDraft draft = new BoosterDraft(draftType); final BoosterDraft draft = new BoosterDraft(draftType);
if (!draft.generateProduct()) { return null; } if (!draft.generateProduct()) { return null; }
draft.initializeBoosters();
return draft; return draft;
} }
@@ -115,7 +114,7 @@ public class BoosterDraft implements IBoosterDraft {
if (sets.size() > 1) { if (sets.size() > 1) {
Object p; Object p;
if (nPacks == 3) { if (nPacks == 3 && sets.size() < 4) {
p = SGuiChoose.oneOrNone("Choose Set Combination", getSetCombos(sets)); p = SGuiChoose.oneOrNone("Choose Set Combination", getSetCombos(sets));
} else { } else {
p = choosePackByPack(sets, nPacks); p = choosePackByPack(sets, nPacks);
@@ -158,7 +157,6 @@ public class BoosterDraft implements IBoosterDraft {
throw new NoSuchElementException("Draft for mode " + this.draftFormat + " has not been set up!"); throw new NoSuchElementException("Draft for mode " + this.draftFormat + " has not been set up!");
} }
this.pack = this.get8BoosterPack();
return true; return true;
} }
@@ -172,16 +170,26 @@ public class BoosterDraft implements IBoosterDraft {
IBoosterDraft.LAND_SET_CODE[0] = block.getLandSet(); IBoosterDraft.LAND_SET_CODE[0] = block.getLandSet();
IBoosterDraft.CUSTOM_RANKINGS_FILE[0] = null; IBoosterDraft.CUSTOM_RANKINGS_FILE[0] = null;
draft.pack = draft.get8BoosterPack(); draft.initializeBoosters();
return draft; return draft;
} }
protected BoosterDraft() { protected BoosterDraft() {
this.draftFormat = LimitedPoolType.Full; this(LimitedPoolType.Full);
} }
protected BoosterDraft(final LimitedPoolType draftType) { protected BoosterDraft(final LimitedPoolType draftType) {
this.draftAI.setBd(this);
this.draftFormat = draftType; this.draftFormat = draftType;
localPlayer = new LimitedPlayer(0);
players.add(localPlayer);
for(int i = 1; i < N_PLAYERS; i++) {
players.add(new LimitedPlayerAI(i));
}
}
@Override
public boolean isPileDraft() {
return false;
} }
private void setupCustomDraft(final CustomLimited draft) { private void setupCustomDraft(final CustomLimited draft) {
@@ -228,19 +236,12 @@ public class BoosterDraft implements IBoosterDraft {
return customs; return customs;
} }
/**
* <p>
* nextChoice.
* </p>
*
* @return a {@link forge.deck.CardPool} object.
*/
@Override @Override
public CardPool nextChoice() { public CardPool nextChoice() {
// Primary draft loop - Computer Chooses from their packs, you choose form your packs
if (this.isRoundOver()) { if (this.isRoundOver()) {
// If all packs are depleted crack 8 new packs // If this round is over, try to start the next round
this.pack = this.get8BoosterPack(); if (!startRound()) {
if (this.pack == null) {
return null; return null;
} }
} }
@@ -248,7 +249,7 @@ public class BoosterDraft implements IBoosterDraft {
this.computerChoose(); this.computerChoose();
final CardPool result = new CardPool(); final CardPool result = new CardPool();
result.addAllFlat(this.pack.get(this.getCurrentBoosterIndex())); result.addAllFlat(localPlayer.nextChoice());
if (result.isEmpty()) { if (result.isEmpty()) {
// Can't set a card, since none are available. Just pass "empty" packs. // Can't set a card, since none are available. Just pass "empty" packs.
@@ -260,129 +261,95 @@ public class BoosterDraft implements IBoosterDraft {
return result; return result;
} }
/** public void initializeBoosters() {
* <p> for(Supplier<List<PaperCard>> boosterRound : this.product) {
* get8BoosterPack. for (int i = 0; i < N_PLAYERS; i++) {
* </p> this.players.get(i).receiveUnopenedPack(boosterRound.get());
* }
* @return an array of {@link forge.deck.CardPool} objects.
*/
public List<List<PaperCard>> get8BoosterPack() {
if (this.nextBoosterGroup >= this.product.size()) {
return null;
}
final List<List<PaperCard>> list = new ArrayList<>();
for (int i = 0; i < 8; i++) {
list.add(this.product.get(this.nextBoosterGroup).get());
} }
startRound();
}
public boolean startRound() {
this.nextBoosterGroup++; this.nextBoosterGroup++;
this.currentBoosterSize = list.get(0).size();
this.currentBoosterPick = 0; this.currentBoosterPick = 0;
draftingBooster = new int[]{0, 1, 2, 3, 4, 5, 6, 7}; packsInDraft = this.players.size();
return list; LimitedPlayer firstPlayer = this.players.get(0);
if (firstPlayer.unopenedPacks.isEmpty()) {
return false;
}
for(LimitedPlayer pl : this.players) {
pl.newPack();
}
this.currentBoosterSize = firstPlayer.packQueue.peek().size();
return true;
} }
public void addSingleBoosterPack(int player, boolean random) {
// TODO Lore Seeker
}
// size 7, all the computers decks
@Override @Override
public Deck[] getDecks() { public Deck[] getDecks() {
return this.draftAI.getDecks(); Deck decks[] = new Deck[7];
for (int i = 1; i < N_PLAYERS; i++) {
decks[i-1] = ((LimitedPlayerAI)this.players.get(i)).buildDeck();
}
return decks;
} }
public void passPacks() { public void passPacks() {
// Alternate direction of pack passing // Alternate direction of pack passing
int adjust = this.nextBoosterGroup % 2 == 1 ? 1 : -1; int adjust = this.nextBoosterGroup % 2 == 1 ? 1 : -1;
for(int i = 0; i < N_PLAYERS; i++) { for(int i = 0; i < N_PLAYERS; i++) {
draftingBooster[i] = (draftingBooster[i] + adjust + pack.size()) % pack.size(); List<PaperCard> passingPack = this.players.get(i).passPack();
if (!passingPack.isEmpty()) {
// TODO Canal Dredger for passing a pack with a single card in it
int passTo = (i + adjust + N_PLAYERS) % N_PLAYERS;
this.players.get(passTo).receiveOpenedPack(passingPack);
this.players.get(passTo).adjustPackNumber(adjust, packsInDraft);
} else {
packsInDraft--;
}
} }
} }
protected void computerChoose() { protected void computerChoose() {
// Loop through players 1-7 to draft their current pack // Loop through players 1-7 to draft their current pack
for (int i = 1; i < N_PLAYERS; i++) { for (int i = 1; i < N_PLAYERS; i++) {
final List<PaperCard> booster = this.pack.get(this.draftingBooster[i]); LimitedPlayer pl = this.players.get(i);
pl.draftCard(pl.chooseCard());
// Empty boosters can happen in a Conspiracy draft
if (!booster.isEmpty()) {
booster.remove(this.draftAI.choose(booster, i-1));
}
} }
} // computerChoose() }
/**
*
* Get the current booster index for the Human
* @return int
*/
public int getCurrentBoosterIndex() { public int getCurrentBoosterIndex() {
return this.draftingBooster[0]; return localPlayer.currentPack;
} }
@Override @Override
public boolean isRoundOver() { public boolean isRoundOver() {
for(List<PaperCard> singlePack : this.pack) { return packsInDraft == 0;
if (!singlePack.isEmpty()) {
return false;
}
}
return true;
} }
@Override @Override
public boolean hasNextChoice() { public boolean hasNextChoice() {
return this.nextBoosterGroup < this.product.size() || !this.isRoundOver(); return !this.isRoundOver() || !this.localPlayer.unopenedPacks.isEmpty();
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public void setChoice(final PaperCard c) { public void setChoice(final PaperCard c) {
final List<PaperCard> thisBooster = this.pack.get(this.getCurrentBoosterIndex()); final List<PaperCard> thisBooster = this.localPlayer.nextChoice();
if (!thisBooster.contains(c)) { if (!thisBooster.contains(c)) {
throw new RuntimeException("BoosterDraft : setChoice() error - card not found - " + c throw new RuntimeException("BoosterDraft : setChoice() error - card not found - " + c
+ " - booster pack = " + thisBooster); + " - booster pack = " + thisBooster);
} }
if (ForgePreferences.UPLOAD_DRAFT) { recordDraftPick(thisBooster, c);
for (int i = 0; i < thisBooster.size(); i++) {
final PaperCard cc = thisBooster.get(i);
final String cnBk = cc.getName() + "|" + cc.getEdition();
float pickValue; this.localPlayer.draftCard(c);
if (cc.equals(c)) {
pickValue = thisBooster.size()
* (1f - (((float) this.currentBoosterPick / this.currentBoosterSize) * 2f));
}
else {
pickValue = 0;
}
if (!this.draftPicks.containsKey(cnBk)) {
this.draftPicks.put(cnBk, pickValue);
}
else {
final float curValue = this.draftPicks.get(cnBk);
final float newValue = (curValue + pickValue) / 2;
this.draftPicks.put(cnBk, newValue);
}
}
}
thisBooster.remove(c);
this.currentBoosterPick++; this.currentBoosterPick++;
this.passPacks(); this.passPacks();
} // setChoice()
@Override
public boolean isPileDraft() {
return false;
} }
@@ -403,7 +370,6 @@ public class BoosterDraft implements IBoosterDraft {
return sb.toString(); return sb.toString();
} }
private static List<String> getSetCombos(final List<String> setz) { private static List<String> getSetCombos(final List<String> setz) {
final String[] sets = setz.toArray(ArrayUtils.EMPTY_STRING_ARRAY); final String[] sets = setz.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
final List<String> setCombos = new ArrayList<>(); final List<String> setCombos = new ArrayList<>();
@@ -466,4 +432,31 @@ public class BoosterDraft implements IBoosterDraft {
} }
return setCombos; return setCombos;
} }
private void recordDraftPick(final List<PaperCard> thisBooster, PaperCard c) {
if (ForgePreferences.UPLOAD_DRAFT) {
for (int i = 0; i < thisBooster.size(); i++) {
final PaperCard cc = thisBooster.get(i);
final String cnBk = cc.getName() + "|" + cc.getEdition();
float pickValue;
if (cc.equals(c)) {
pickValue = thisBooster.size()
* (1f - (((float) this.currentBoosterPick / this.currentBoosterSize) * 2f));
}
else {
pickValue = 0;
}
if (!this.draftPicks.containsKey(cnBk)) {
this.draftPicks.put(cnBk, pickValue);
}
else {
final float curValue = this.draftPicks.get(cnBk);
final float newValue = (curValue + pickValue) / 2;
this.draftPicks.put(cnBk, newValue);
}
}
}
}
} }

View File

@@ -35,12 +35,8 @@ import forge.properties.ForgePreferences;
*/ */
public class BoosterDraftAI { public class BoosterDraftAI {
/** The bd. */ // TODO When WinstonDraft gets related changes that BoosterDraft gets, this can be deleted
private IBoosterDraft bd = null; private IBoosterDraft bd = null;
/**
* Constant <code>nDecks=7.</code>
*/
protected static final int N_DECKS = 7; protected static final int N_DECKS = 7;
// holds all the cards for each of the computer's decks // holds all the cards for each of the computer's decks
@@ -83,13 +79,6 @@ public class BoosterDraftAI {
return bestPick; return bestPick;
} }
/**
* <p>
* getDecks.
* </p>
*
* @return an array of {@link forge.deck.Deck} objects.
*/
public Deck[] getDecks() { public Deck[] getDecks() {
final Deck[] out = new Deck[this.decks.size()]; final Deck[] out = new Deck[this.decks.size()];
@@ -103,11 +92,6 @@ public class BoosterDraftAI {
return out; return out;
} // getDecks() } // getDecks()
/**
* <p>
* Constructor for BoosterDraftAI.
* </p>
*/
public BoosterDraftAI() { public BoosterDraftAI() {
// Initialize deck array and playerColors list // Initialize deck array and playerColors list
for (int i = 0; i < N_DECKS; i++) { for (int i = 0; i < N_DECKS; i++) {
@@ -116,21 +100,9 @@ public class BoosterDraftAI {
} }
} // BoosterDraftAI() } // BoosterDraftAI()
/**
* Gets the bd.
*
* @return the bd
*/
public IBoosterDraft getBd() { public IBoosterDraft getBd() {
return this.bd; return this.bd;
} }
/**
* Sets the bd.
*
* @param bd0
* the bd to set
*/
public void setBd(final IBoosterDraft bd0) { public void setBd(final IBoosterDraft bd0) {
this.bd = bd0; this.bd = bd0;
} }

View File

@@ -31,49 +31,15 @@ import forge.item.PaperCard;
* @version $Id$ * @version $Id$
*/ */
public interface IBoosterDraft { public interface IBoosterDraft {
/**
* <p>
* nextChoice.
* </p>
*
* @return a {@link CardPool} object.
*/
CardPool nextChoice(); CardPool nextChoice();
/**
* <p>
* setChoice.
* </p>
*
* @param c
* a {@link forge.game.card.Card} object.
*/
void setChoice(PaperCard c); void setChoice(PaperCard c);
/**
* <p>
* hasNextChoice.
* </p>
*
* @return a boolean.
*/
boolean hasNextChoice(); boolean hasNextChoice();
boolean isRoundOver(); boolean isRoundOver();
/**
* <p>
* getDecks.
* </p>
*
* @return an array of {@link forge.deck.Deck} objects.
*/
Deck[] getDecks(); // size 7, all the computers decks Deck[] getDecks(); // size 7, all the computers decks
/** Constant <code>LandSetCode="{}"</code>. */
CardEdition[] LAND_SET_CODE = { null }; CardEdition[] LAND_SET_CODE = { null };
String[] CUSTOM_RANKINGS_FILE = { null }; String[] CUSTOM_RANKINGS_FILE = { null };
boolean isPileDraft(); boolean isPileDraft();
} }

View File

@@ -0,0 +1,148 @@
package forge.limited;
import com.google.common.collect.Lists;
import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deck.DeckSection;
import forge.item.PaperCard;
import forge.limited.powers.DraftPower;
import java.util.*;
public class LimitedPlayer {
// A Player class for inside some type of limited environment, like Draft.
final protected int order;
protected int currentPack;
protected int draftedThisRound;
protected Deck deck;
protected Queue<List<PaperCard>> packQueue;
protected Queue<List<PaperCard>> unopenedPacks;
// WIP - Draft Matters cards
/*
private static int CantDraftThisRound = 1,
SpyNextCardDrafted = 1 << 1,
ReceiveLastCard = 1 << 2,
CanRemoveAfterDraft = 1 << 3,
CanTradeAfterDraft = 1 << 4;
private static int MAXFLAGS = CantDraftThisRound | ReceiveLastCard | CanRemoveAfterDraft | SpyNextCardDrafted
| CanTradeAfterDraft;
private int playerFlags = 0;
private List<PaperCard> revealed = Lists.newArrayList();
private Map<String, List<Object>> noted = new HashMap<>();
private Map<DraftPower, Integer> powers = new HashMap<>();
*/
public LimitedPlayer(int seatingOrder) {
order = seatingOrder;
deck = new Deck();
packQueue = new LinkedList<>();
unopenedPacks = new LinkedList<>();
}
public PaperCard chooseCard() {
// A basic LimitedPlayer chooses cards via the UI instead of this function
return null;
}
public boolean draftCard(PaperCard bestPick) {
return draftCard(bestPick, DeckSection.Sideboard);
}
public boolean draftCard(PaperCard bestPick, DeckSection section) {
if (bestPick == null) {
return false;
}
List<PaperCard> chooseFrom = packQueue.peek();
if (chooseFrom == null) {
return false;
}
chooseFrom.remove(bestPick);
CardPool pool = deck.getOrCreate(section);
pool.add(bestPick);
draftedThisRound++;
// TODO Note Lurking Automaton
// TODO Note Paliano, the High City
// TODO Note Aether Searcher
// TODO Note Custodi Peacepeeper
// TODO Note Paliano Vanguard
// TODO Note Garbage Fire
return true;
}
public List<PaperCard> nextChoice() {
return packQueue.peek();
}
public void newPack() {
currentPack = order;
draftedThisRound = 0;
packQueue.add(unopenedPacks.poll());
}
public void adjustPackNumber(int adjust, int numPacks) {
currentPack = (currentPack + adjust + numPacks) % numPacks;
}
public List<PaperCard> passPack() {
return packQueue.poll();
}
public void receiveUnopenedPack(List<PaperCard> pack) {
unopenedPacks.add(pack);
}
public void receiveOpenedPack(List<PaperCard> pack) {
packQueue.add(pack);
}
/*
public void addSingleBoosterPack(boolean random) {
// TODO Lore Seeker
// Generate booster pack then, "receive" that pack
}
public boolean activatePower(DraftPower power) {
if (!powers.containsKey(power)) {
return false;
}
int i = (int)powers.get(power);
if (i == 1) {
powers.remove(power);
} else {
powers.put(power, i-1);
}
power.activate(this);
return true;
}
public boolean noteObject(String cardName, Object notedObj) {
// Returns boolean based on creation of new mapped param
boolean alreadyContained = noted.containsKey(cardName);
if (alreadyContained) {
noted.get(cardName).add(notedObj);
} else {
noted.put(cardName, Lists.newArrayList(notedObj));
}
return !alreadyContained;
}
*/
}

View File

@@ -0,0 +1,58 @@
package forge.limited;
import forge.card.ColorSet;
import forge.deck.CardPool;
import forge.deck.Deck;
import forge.deck.DeckSection;
import forge.item.PaperCard;
import forge.properties.ForgePreferences;
import java.util.List;
public class LimitedPlayerAI extends LimitedPlayer {
protected DeckColors deckCols;
public LimitedPlayerAI(int seatingOrder) {
super(seatingOrder);
deckCols = new DeckColors();
}
@Override
public PaperCard chooseCard() {
if (packQueue.isEmpty()) {
return null;
}
List<PaperCard> chooseFrom = packQueue.peek();
if (chooseFrom.isEmpty()) {
return null;
}
CardPool pool = deck.getOrCreate(DeckSection.Sideboard);
if (ForgePreferences.DEV_MODE) {
System.out.println("Player[" + order + "] pack: " + chooseFrom.toString());
}
final ColorSet chosenColors = deckCols.getChosenColors();
final boolean canAddMoreColors = deckCols.canChoseMoreColors();
List<PaperCard> rankedCards = CardRanker.rankCardsInPack(chooseFrom, pool.toFlatList(), chosenColors, canAddMoreColors);
PaperCard bestPick = rankedCards.get(0);
if (canAddMoreColors) {
deckCols.addColorsOf(bestPick);
}
if (ForgePreferences.DEV_MODE) {
System.out.println("Player[" + order + "] picked: " + bestPick);
}
return bestPick;
}
public Deck buildDeck() {
CardPool section = deck.getOrCreate(DeckSection.Sideboard);
return new BoosterDeckBuilder(section.toFlatList(), deckCols).buildDeck();
}
}