From 9da706cbade65936d82cdeaf80a8a61f42479801 Mon Sep 17 00:00:00 2001 From: Sol Date: Thu, 18 Aug 2016 20:22:10 +0000 Subject: [PATCH] - Rewriting Booster Draft to be a bit more capable of handling Draft Matters cards --- .gitattributes | 2 + .../src/test/java/forge/BoosterDraftTest.java | 37 +--- .../main/java/forge/limited/BoosterDraft.java | 193 +++++++++--------- .../java/forge/limited/BoosterDraftAI.java | 30 +-- .../java/forge/limited/IBoosterDraft.java | 36 +--- .../java/forge/limited/LimitedPlayer.java | 148 ++++++++++++++ .../java/forge/limited/LimitedPlayerAI.java | 58 ++++++ 7 files changed, 304 insertions(+), 200 deletions(-) create mode 100644 forge-gui/src/main/java/forge/limited/LimitedPlayer.java create mode 100644 forge-gui/src/main/java/forge/limited/LimitedPlayerAI.java diff --git a/.gitattributes b/.gitattributes index ba0eb7ed176..e1b47fd6356 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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/IBoosterDraft.java svneol=native#text/plain 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/LimitedWinLoseController.java -text forge-gui/src/main/java/forge/limited/ReadDraftRankings.java -text diff --git a/forge-gui-desktop/src/test/java/forge/BoosterDraftTest.java b/forge-gui-desktop/src/test/java/forge/BoosterDraftTest.java index 47ea68649e5..441e7fbdf87 100644 --- a/forge-gui-desktop/src/test/java/forge/BoosterDraftTest.java +++ b/forge-gui-desktop/src/test/java/forge/BoosterDraftTest.java @@ -7,6 +7,7 @@ import forge.game.card.Card; import forge.item.PaperCard; import forge.item.SealedProduct; import forge.limited.IBoosterDraft; +import forge.limited.LimitedPlayer; import forge.model.FModel; import org.testng.annotations.Test; @@ -24,29 +25,14 @@ import java.util.List; @Test(groups = { "UnitTest" }, timeOut = 1000, enabled = false) public class BoosterDraftTest implements IBoosterDraft { - /** The n. */ private int n = 3; - /** - *

- * getDecks. - *

- * - * @return an array of {@link forge.deck.Deck} objects. - */ @Override @Test(timeOut = 1000) public Deck[] getDecks() { return null; } - /** - *

- * nextChoice. - *

- * - * @return a {@link forge.CardList} object. - */ @Override public CardPool nextChoice() { this.n--; @@ -62,13 +48,6 @@ public class BoosterDraftTest implements IBoosterDraft { System.out.println(c.getName()); } - /** - *

- * hasNextChoice. - *

- * - * @return a boolean. - */ @Override public boolean hasNextChoice() { return this.n > 0; @@ -79,24 +58,10 @@ public class BoosterDraftTest implements IBoosterDraft { return hasNextChoice(); } - /** - *

- * getChosenCards. - *

- * - * @return a {@link forge.CardList} object. - */ public List getChosenCards() { return null; } - /** - *

- * getUnchosenCards. - *

- * - * @return a {@link forge.CardList} object. - */ public List getUnchosenCards() { return null; } diff --git a/forge-gui/src/main/java/forge/limited/BoosterDraft.java b/forge-gui/src/main/java/forge/limited/BoosterDraft.java index df77335a1bd..44b83b228e4 100644 --- a/forge-gui/src/main/java/forge/limited/BoosterDraft.java +++ b/forge-gui/src/main/java/forge/limited/BoosterDraft.java @@ -43,18 +43,16 @@ import java.util.*; * Booster Draft Format. */ public class BoosterDraft implements IBoosterDraft { - private final BoosterDraftAI draftAI = new BoosterDraftAI(); private static final int N_PLAYERS = 8; public static final String FILE_EXT = ".draft"; + private final List players = new ArrayList<>(); + private LimitedPlayer localPlayer; protected int nextBoosterGroup = 0; private int currentBoosterSize = 0; private int currentBoosterPick = 0; - private int[] draftingBooster; + private int packsInDraft; - private List> pack; // size 8 - - /** The draft picks. */ private final Map draftPicks = new TreeMap<>(); protected LimitedPoolType draftFormat; @@ -63,6 +61,7 @@ public class BoosterDraft implements IBoosterDraft { public static BoosterDraft createDraft(final LimitedPoolType draftType) { final BoosterDraft draft = new BoosterDraft(draftType); if (!draft.generateProduct()) { return null; } + draft.initializeBoosters(); return draft; } @@ -115,7 +114,7 @@ public class BoosterDraft implements IBoosterDraft { if (sets.size() > 1) { Object p; - if (nPacks == 3) { + if (nPacks == 3 && sets.size() < 4) { p = SGuiChoose.oneOrNone("Choose Set Combination", getSetCombos(sets)); } else { 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!"); } - this.pack = this.get8BoosterPack(); return true; } @@ -172,16 +170,26 @@ public class BoosterDraft implements IBoosterDraft { IBoosterDraft.LAND_SET_CODE[0] = block.getLandSet(); IBoosterDraft.CUSTOM_RANKINGS_FILE[0] = null; - draft.pack = draft.get8BoosterPack(); + draft.initializeBoosters(); return draft; } protected BoosterDraft() { - this.draftFormat = LimitedPoolType.Full; + this(LimitedPoolType.Full); } protected BoosterDraft(final LimitedPoolType draftType) { - this.draftAI.setBd(this); 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) { @@ -228,19 +236,12 @@ public class BoosterDraft implements IBoosterDraft { return customs; } - /** - *

- * nextChoice. - *

- * - * @return a {@link forge.deck.CardPool} object. - */ @Override public CardPool nextChoice() { + // Primary draft loop - Computer Chooses from their packs, you choose form your packs if (this.isRoundOver()) { - // If all packs are depleted crack 8 new packs - this.pack = this.get8BoosterPack(); - if (this.pack == null) { + // If this round is over, try to start the next round + if (!startRound()) { return null; } } @@ -248,7 +249,7 @@ public class BoosterDraft implements IBoosterDraft { this.computerChoose(); final CardPool result = new CardPool(); - result.addAllFlat(this.pack.get(this.getCurrentBoosterIndex())); + result.addAllFlat(localPlayer.nextChoice()); if (result.isEmpty()) { // Can't set a card, since none are available. Just pass "empty" packs. @@ -260,129 +261,95 @@ public class BoosterDraft implements IBoosterDraft { return result; } - /** - *

- * get8BoosterPack. - *

- * - * @return an array of {@link forge.deck.CardPool} objects. - */ - public List> get8BoosterPack() { - if (this.nextBoosterGroup >= this.product.size()) { - return null; - } - - final List> list = new ArrayList<>(); - for (int i = 0; i < 8; i++) { - list.add(this.product.get(this.nextBoosterGroup).get()); + public void initializeBoosters() { + for(Supplier> boosterRound : this.product) { + for (int i = 0; i < N_PLAYERS; i++) { + this.players.get(i).receiveUnopenedPack(boosterRound.get()); + } } + startRound(); + } + public boolean startRound() { this.nextBoosterGroup++; - this.currentBoosterSize = list.get(0).size(); this.currentBoosterPick = 0; - draftingBooster = new int[]{0, 1, 2, 3, 4, 5, 6, 7}; - return list; + packsInDraft = this.players.size(); + 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 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() { // Alternate direction of pack passing int adjust = this.nextBoosterGroup % 2 == 1 ? 1 : -1; for(int i = 0; i < N_PLAYERS; i++) { - draftingBooster[i] = (draftingBooster[i] + adjust + pack.size()) % pack.size(); + List 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() { // Loop through players 1-7 to draft their current pack for (int i = 1; i < N_PLAYERS; i++) { - final List booster = this.pack.get(this.draftingBooster[i]); - - // Empty boosters can happen in a Conspiracy draft - if (!booster.isEmpty()) { - booster.remove(this.draftAI.choose(booster, i-1)); - } + LimitedPlayer pl = this.players.get(i); + pl.draftCard(pl.chooseCard()); } - } // computerChoose() + } - /** - * - * Get the current booster index for the Human - * @return int - */ public int getCurrentBoosterIndex() { - return this.draftingBooster[0]; + return localPlayer.currentPack; } @Override public boolean isRoundOver() { - for(List singlePack : this.pack) { - if (!singlePack.isEmpty()) { - return false; - } - } - - return true; + return packsInDraft == 0; } - @Override public boolean hasNextChoice() { - return this.nextBoosterGroup < this.product.size() || !this.isRoundOver(); + return !this.isRoundOver() || !this.localPlayer.unopenedPacks.isEmpty(); } /** {@inheritDoc} */ @Override public void setChoice(final PaperCard c) { - final List thisBooster = this.pack.get(this.getCurrentBoosterIndex()); + final List thisBooster = this.localPlayer.nextChoice(); if (!thisBooster.contains(c)) { throw new RuntimeException("BoosterDraft : setChoice() error - card not found - " + c + " - booster pack = " + thisBooster); } - 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(); + recordDraftPick(thisBooster, c); - 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); - } - } - } - - thisBooster.remove(c); + this.localPlayer.draftCard(c); this.currentBoosterPick++; this.passPacks(); - } // setChoice() - - @Override - public boolean isPileDraft() { - return false; } @@ -403,7 +370,6 @@ public class BoosterDraft implements IBoosterDraft { return sb.toString(); } - private static List getSetCombos(final List setz) { final String[] sets = setz.toArray(ArrayUtils.EMPTY_STRING_ARRAY); final List setCombos = new ArrayList<>(); @@ -466,4 +432,31 @@ public class BoosterDraft implements IBoosterDraft { } return setCombos; } + + private void recordDraftPick(final List 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); + } + } + } + } } diff --git a/forge-gui/src/main/java/forge/limited/BoosterDraftAI.java b/forge-gui/src/main/java/forge/limited/BoosterDraftAI.java index 59ad4d2aad0..3ac539038de 100644 --- a/forge-gui/src/main/java/forge/limited/BoosterDraftAI.java +++ b/forge-gui/src/main/java/forge/limited/BoosterDraftAI.java @@ -35,12 +35,8 @@ import forge.properties.ForgePreferences; */ public class BoosterDraftAI { - /** The bd. */ + // TODO When WinstonDraft gets related changes that BoosterDraft gets, this can be deleted private IBoosterDraft bd = null; - - /** - * Constant nDecks=7. - */ protected static final int N_DECKS = 7; // holds all the cards for each of the computer's decks @@ -83,13 +79,6 @@ public class BoosterDraftAI { return bestPick; } - /** - *

- * getDecks. - *

- * - * @return an array of {@link forge.deck.Deck} objects. - */ public Deck[] getDecks() { final Deck[] out = new Deck[this.decks.size()]; @@ -103,11 +92,6 @@ public class BoosterDraftAI { return out; } // getDecks() - /** - *

- * Constructor for BoosterDraftAI. - *

- */ public BoosterDraftAI() { // Initialize deck array and playerColors list for (int i = 0; i < N_DECKS; i++) { @@ -116,21 +100,9 @@ public class BoosterDraftAI { } } // BoosterDraftAI() - /** - * Gets the bd. - * - * @return the bd - */ public IBoosterDraft getBd() { return this.bd; } - - /** - * Sets the bd. - * - * @param bd0 - * the bd to set - */ public void setBd(final IBoosterDraft bd0) { this.bd = bd0; } diff --git a/forge-gui/src/main/java/forge/limited/IBoosterDraft.java b/forge-gui/src/main/java/forge/limited/IBoosterDraft.java index 103d2afa965..3797aea5c73 100644 --- a/forge-gui/src/main/java/forge/limited/IBoosterDraft.java +++ b/forge-gui/src/main/java/forge/limited/IBoosterDraft.java @@ -31,49 +31,15 @@ import forge.item.PaperCard; * @version $Id$ */ public interface IBoosterDraft { - /** - *

- * nextChoice. - *

- * - * @return a {@link CardPool} object. - */ + CardPool nextChoice(); - - /** - *

- * setChoice. - *

- * - * @param c - * a {@link forge.game.card.Card} object. - */ void setChoice(PaperCard c); - - /** - *

- * hasNextChoice. - *

- * - * @return a boolean. - */ boolean hasNextChoice(); boolean isRoundOver(); - - /** - *

- * getDecks. - *

- * - * @return an array of {@link forge.deck.Deck} objects. - */ Deck[] getDecks(); // size 7, all the computers decks - /** Constant LandSetCode="{}". */ CardEdition[] LAND_SET_CODE = { null }; - String[] CUSTOM_RANKINGS_FILE = { null }; - boolean isPileDraft(); } diff --git a/forge-gui/src/main/java/forge/limited/LimitedPlayer.java b/forge-gui/src/main/java/forge/limited/LimitedPlayer.java new file mode 100644 index 00000000000..3f3cd6f9ec6 --- /dev/null +++ b/forge-gui/src/main/java/forge/limited/LimitedPlayer.java @@ -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> packQueue; + protected Queue> 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 revealed = Lists.newArrayList(); + private Map> noted = new HashMap<>(); + private Map 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 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 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 passPack() { + return packQueue.poll(); + } + + public void receiveUnopenedPack(List pack) { + unopenedPacks.add(pack); + } + + public void receiveOpenedPack(List 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; + } + */ +} + diff --git a/forge-gui/src/main/java/forge/limited/LimitedPlayerAI.java b/forge-gui/src/main/java/forge/limited/LimitedPlayerAI.java new file mode 100644 index 00000000000..cb8927345f7 --- /dev/null +++ b/forge-gui/src/main/java/forge/limited/LimitedPlayerAI.java @@ -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 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 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(); + } +}