diff --git a/.gitattributes b/.gitattributes
index 879be5bbaa4..b60dd68aac0 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -12037,11 +12037,13 @@ src/main/java/forge/game/GamePlayerRating.java -text
src/main/java/forge/game/GameState.java -text
src/main/java/forge/game/GameSummary.java svneol=native#text/plain
src/main/java/forge/game/GameType.java -text
+src/main/java/forge/game/limited/BoosterDeck.java -text
src/main/java/forge/game/limited/BoosterDraft.java svneol=native#text/plain
src/main/java/forge/game/limited/BoosterDraftAI.java svneol=native#text/plain
src/main/java/forge/game/limited/CCnt.java svneol=native#text/plain
src/main/java/forge/game/limited/CardPoolLimitation.java -text
src/main/java/forge/game/limited/CardRatings.java -text
+src/main/java/forge/game/limited/CreatureComparator.java -text
src/main/java/forge/game/limited/CustomLimited.java svneol=native#text/plain
src/main/java/forge/game/limited/DeckColors.java svneol=native#text/plain
src/main/java/forge/game/limited/IBoosterDraft.java svneol=native#text/plain
diff --git a/.project b/.project
index 5af90efe210..30f1a3c91f4 100644
--- a/.project
+++ b/.project
@@ -20,10 +20,21 @@
+ * buildDeck. + *
+ * + * @param draftedList + * a {@link forge.CardList} object. + * @param colors + * a {@link forge.game.limited.DeckColors} object. + * @return a {@link forge.deck.Deck} object. + */ + private void buildDeck() { + + aiPlayables = draftedList.filter(new CardListFilter() { + @Override + public boolean addCard(final Card c) { + return !(c.getSVar("RemAIDeck").equals("True") || c.getSVar("RemRandomDeck").equals("True")); + } + }); + for (int i = 0; i < aiPlayables.size(); i++) { + draftedList.remove(aiPlayables.get(i)); + } + + + // 1. Add best 15 on-color creatures + addBestCreatures(15); + + // 2.Try to fill up to 22 with on-color non-creature cards + addNonCreatures(cardsNeeded - deckList.size()); + + // 3.Try to fill up to 22 with on-color creatures cards (if more than 15 + // are present) + addBestCreatures(cardsNeeded - deckList.size()); + + CardList nonLands = aiPlayables.getNotType("Land").getOnly2Colors(colors.getColor1(), colors.getColor2()); + + // 4. If there are still on-color cards and the average cmc is low add a + // 23rd card. + if (cardsNeeded == 0 && CardListUtil.getAverageCMC(deckList) < 3 && !nonLands.isEmpty()) { + Card c = nonLands.get(0); + deckList.add(c); + aiPlayables.remove(0); + landsNeeded--; + } + + // 5. If there are still less than 22 non-land cards add off-color + // cards. + addRandomCards(cardsNeeded - deckList.size()); + + // 6. If it's not a mono color deck, add non-basic lands that were drafted. + addNonBasicLands(); + + final CCnt[] clrCnts = calculateLandNeeds(); + + if (landsNeeded > 0) { + addLands(clrCnts); + } + + while (deckList.size() > 40) { + final Card c = deckList.get(MyRandom.getRandom().nextInt(deckList.size() - 1)); + deckList.remove(c); + aiPlayables.add(c); + } + + while (deckList.size() < 40) { + if (aiPlayables.size() > 1) { + final Card c = aiPlayables.get(MyRandom.getRandom().nextInt(aiPlayables.size() - 1)); + deckList.add(c); + aiPlayables.remove(c); + } else if (aiPlayables.size() == 1) { + final Card c = aiPlayables.get(0); + deckList.add(c); + aiPlayables.remove(c); + } else { + // if no playable cards remain fill up with basic lands + for (int i = 0; i < 5; i++) { + if (clrCnts[i].getCount() > 0) { + final Card c = AllZone.getCardFactory().getCard(clrCnts[i].getColor(), + AllZone.getComputerPlayer()); + c.setCurSetCode(IBoosterDraft.LAND_SET_CODE[0]); + deckList.add(c); + break; + } + } + } + } + if (deckList.size() == 40) { + this.getMain().add(deckList); + this.getSideboard().add(aiPlayables); + this.getSideboard().add(draftedList); + } else { + throw new RuntimeException("BoosterDraftAI : buildDeck() error, decksize not 40"); + } + } + + /** + * Add lands to fulfill the given color counts. + * @param clrCnts + */ + private void addLands(final CCnt[] clrCnts) { + + // total of all ClrCnts + int totalColor = 0; + for (int i = 0; i < 5; i++) { + totalColor += clrCnts[i].getCount(); + // tmpDeck += ClrCnts[i].Color + ":" + ClrCnts[i].Count + "\n"; + } + + // tmpDeck += "totalColor:" + totalColor + "\n"; + + for (int i = 0; i < 5; i++) { + if (clrCnts[i].getCount() > 0) { // calculate number of lands + // for + // each color + final float p = (float) clrCnts[i].getCount() / (float) totalColor; + final int nLand = (int) (landsNeeded * p) + 1; + // tmpDeck += "nLand-" + ClrCnts[i].Color + ":" + nLand + + // "\n"; + if (Constant.Runtime.DEV_MODE[0]) { + System.out.println("Basics[" + clrCnts[i].getColor() + "]:" + nLand); + } + + // just to prevent a null exception by the deck size fixing + // code + // CardCounts.put(ClrCnts[i].Color, nLand); + + for (int j = 0; j <= nLand; j++) { + final Card c = AllZone.getCardFactory().getCard(clrCnts[i].getColor(), + AllZone.getComputerPlayer()); + c.setCurSetCode(IBoosterDraft.LAND_SET_CODE[0]); + deckList.add(c); + landsNeeded--; + } + } + } + int n = 0; + while (landsNeeded > 0) { + if (clrCnts[n].getCount() > 0) { + final Card c = AllZone.getCardFactory().getCard(clrCnts[n].getColor(), AllZone.getComputerPlayer()); + c.setCurSetCode(IBoosterDraft.LAND_SET_CODE[0]); + deckList.add(c); + landsNeeded--; + + if (Constant.Runtime.DEV_MODE[0]) { + System.out.println("AddBasics: " + c.getName()); + } + } + if (++n > 4) { + n = 0; + } + } + } + + /** + * attempt to optimize basic land counts according to color representation + * @return CCnt + */ + private CCnt[] calculateLandNeeds() { + final CCnt[] clrCnts = { new CCnt("Plains", 0), new CCnt("Island", 0), new CCnt("Swamp", 0), + new CCnt("Mountain", 0), new CCnt("Forest", 0) }; + + // count each card color using mana costs + // TODO: count hybrid mana differently? + for (int i = 0; i < deckList.size(); i++) { + final CardManaCost mc = deckList.get(i).getManaCost(); + + // count each mana symbol in the mana cost + for (ManaCostShard shard : mc.getShards()) { + byte mask = shard.getColorMask(); + + if ((mask & CardColor.WHITE) > 0) { + clrCnts[0].setCount(clrCnts[0].getCount() + 1); + } + if ((mask & CardColor.BLUE) > 0) { + clrCnts[1].setCount(clrCnts[1].getCount() + 1); + } + if ((mask & CardColor.BLACK) > 0) { + clrCnts[2].setCount(clrCnts[2].getCount() + 1); + } + if ((mask & CardColor.RED) > 0) { + clrCnts[3].setCount(clrCnts[3].getCount() + 1); + } + if ((mask & CardColor.GREEN) > 0) { + clrCnts[4].setCount(clrCnts[4].getCount() + 1); + } + } + } + return clrCnts; + } + + /** + * Add non-basic lands to the deck. + */ + private void addNonBasicLands() { + CardList lands = aiPlayables.getType("Land"); + while (!colors.getColor1().equals(colors.getColor2()) && landsNeeded > 0 && lands.size() > 0) { + final Card c = lands.get(0); + + deckList.add(c); + landsNeeded--; + aiPlayables.remove(c); + + lands = aiPlayables.getType("Land"); + + if (Constant.Runtime.DEV_MODE[0]) { + System.out.println("Land:" + c.getName()); + } + } + } + + /** + * Add random cards to the deck. + * @param nCards + */ + private void addRandomCards(int nCards) { + CardList z = aiPlayables.getNotType("Land"); + int ii = 0; + while ((nCards > 0) && (z.size() > 1)) { + + final Card c = z.get(MyRandom.getRandom().nextInt(z.size() - 1)); + + deckList.add(c); + cardsNeeded--; + nCards--; + aiPlayables.remove(c); + z.remove(c); + + if (Constant.Runtime.DEV_MODE[0]) { + System.out.println("NonLands[" + ii++ + "]:" + c.getName() + "(" + c.getManaCost() + ")"); + } + } + } + + /** + * Add non creatures to the deck. + * @param nCards + */ + private void addNonCreatures(int nCards) { + CardList others = aiPlayables.getNotType("Creature").getNotType("Land") + .getOnly2Colors(colors.getColor1(), colors.getColor2()); + + int ii = 0; + while (nCards > 0 && others.size() > 0) { + int index = 0; + if (others.size() > 1) { + index = MyRandom.getRandom().nextInt(others.size() - 1); + } + final Card c = others.get(index); + + deckList.add(c); + cardsNeeded--; + nCards--; + aiPlayables.remove(c); + others.remove(c); + + if (Constant.Runtime.DEV_MODE[0]) { + System.out.println("Others[" + ii++ + "]:" + c.getName() + " (" + c.getManaCost() + ")"); + } + } + } + + /** + * Add the best creatures to the deck. + * + * @param nCreatures + */ + private void addBestCreatures(int nCreatures) { + CardList creatures = aiPlayables.getType("Creature").getOnly2Colors(colors.getColor1(), colors.getColor2()); + creatures.sort(new CreatureComparator()); + + int i = 0; + while (nCreatures > 0 && creatures.size() > 0) { + final Card c = creatures.get(0); + + deckList.add(c); + cardsNeeded--; + nCreatures--; + aiPlayables.remove(c); + creatures.remove(c); + + if (Constant.Runtime.DEV_MODE[0]) { + System.out.println("Creature[" + i + "]:" + c.getName() + " (" + c.getManaCost() + ")"); + } + + i++; + } + } + +} diff --git a/src/main/java/forge/game/limited/BoosterDraftAI.java b/src/main/java/forge/game/limited/BoosterDraftAI.java index cf874215eb7..1ab8fdfe9a9 100644 --- a/src/main/java/forge/game/limited/BoosterDraftAI.java +++ b/src/main/java/forge/game/limited/BoosterDraftAI.java @@ -18,23 +18,18 @@ package forge.game.limited; import java.util.ArrayList; -import java.util.Comparator; import java.util.Map; import java.util.Random; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; -import forge.AllZone; import forge.Card; import forge.CardList; import forge.CardListFilter; import forge.CardListUtil; import forge.Constant; -import forge.card.CardColor; -import forge.card.CardManaCost; import forge.card.cardfactory.CardFactoryUtil; -import forge.card.mana.ManaCostShard; import forge.card.spellability.AbilityMana; import forge.deck.Deck; import forge.util.MyRandom; @@ -116,7 +111,7 @@ public class BoosterDraftAI { if (!hasPicked) { final CardList creatures = aiPlayables.getType("Creature"); - creatures.sort(this.bestCreature); + creatures.sort(new CreatureComparator()); debugCreatures(creatures); if (creatures.size() > 0) { @@ -156,7 +151,7 @@ public class BoosterDraftAI { if (!hasPicked) { final CardList creatures = aiPlayables.getType("Creature").getMonoColored(true); - creatures.sort(this.bestCreature); + creatures.sort(new CreatureComparator()); debugCreatures(creatures); if (creatures.size() > 0) { @@ -186,7 +181,7 @@ public class BoosterDraftAI { // list, and 1 non-creature. typeList = colorList.getType("Creature"); if (typeList.size() > 0) { - typeList.sort(this.bestCreature); + typeList.sort(new CreatureComparator()); wouldPick.add(typeList.get(0)); if (typeList.size() > 1) { wouldPick.add(typeList.get(1)); @@ -353,328 +348,11 @@ public class BoosterDraftAI { System.out.println("Deck[" + i + "]"); } - out[i] = this.buildDeck(this.deck[i], this.playerColors.get(i)); + out[i] = new BoosterDeck(this.deck[i], this.playerColors.get(i)); } return out; } // getDecks() - - /** - *- * buildDeck. - *
- * - * @param dList - * a {@link forge.CardList} object. - * @param pClrs - * a {@link forge.game.limited.DeckColors} object. - * @return a {@link forge.deck.Deck} object. - */ - private Deck buildDeck(final CardList dList, final DeckColors pClrs) { - - final CardList outList = new CardList(); - int cardsNeeded = 22; - int landsNeeded = 18; - - final CardList aiPlayables = dList.filter(new CardListFilter() { - @Override - public boolean addCard(final Card c) { - return !(c.getSVar("RemAIDeck").equals("True") || c.getSVar("RemRandomDeck").equals("True")); - } - }); - for (int i = 0; i < aiPlayables.size(); i++) { - dList.remove(aiPlayables.get(i)); - } - - final CardList creatures = aiPlayables.getType("Creature").getOnly2Colors(pClrs.getColor1(), pClrs.getColor2()); - - int nCreatures = 15; - - creatures.sort(this.bestCreature); - - // 1.Add up to 15 on-color creatures - int i = 0; - while (nCreatures > 0 && creatures.size() > 0) { - final Card c = creatures.get(0); - - outList.add(c); - cardsNeeded--; - nCreatures--; - aiPlayables.remove(c); - creatures.remove(c); - - if (Constant.Runtime.DEV_MODE[0]) { - System.out.println("Creature[" + i + "]:" + c.getName() + " (" + c.getManaCost() + ")"); - } - - i++; - } - - /* - * CardList otherCreatures = aiPlayables.getType("Creature"); while - * ((nCreatures > 1) && (otherCreatures.size() > 1)) { final Card c = - * otherCreatures.get(MyRandom.getRandom().nextInt(otherCreatures.size() - * - 1)); outList.add(c); cardsNeeded--; nCreatures--; - * aiPlayables.remove(c); - * - * otherCreatures = aiPlayables.getType("Creature"); - * - * if (Constant.Runtime.DEV_MODE[0]) { - * System.out.println("AddCreature: " + c.getName() + " (" + - * c.getManaCost() + ")"); } } - */ - - CardList others = aiPlayables.getNotType("Creature").getNotType("Land") - .getOnly2Colors(pClrs.getColor1(), pClrs.getColor2()); - - // 2.Try to fill up to 22 with on-color non-creature cards - int ii = 0; - while (cardsNeeded > 0 && others.size() > 0) { - int index = 0; - if (others.size() > 1) { - index = MyRandom.getRandom().nextInt(others.size() - 1); - } - final Card c = others.get(index); - - // out.addMain(c.getName()); - outList.add(c); - cardsNeeded--; - aiPlayables.remove(c); - - others = aiPlayables.getNotType("Creature").getNotType("Land") - .getOnly2Colors(pClrs.getColor1(), pClrs.getColor2()); - - if (Constant.Runtime.DEV_MODE[0]) { - System.out.println("Others[" + ii++ + "]:" + c.getName() + " (" + c.getManaCost() + ")"); - } - } - - i = 0; - // 3.Try to fill up to 22 with on-color creatures cards (if more than 15 - // are present) - while (cardsNeeded > 0 && (0 < creatures.size())) { - final Card c = creatures.get(0); - - outList.add(c); - cardsNeeded--; - aiPlayables.remove(c); - creatures.remove(c); - - if (Constant.Runtime.DEV_MODE[0]) { - System.out.println("Creature[" + i + "]:" + c.getName() + " (" + c.getManaCost() + ")"); - } - - i++; - } - - CardList nonLands = aiPlayables.getNotType("Land").getOnly2Colors(pClrs.getColor1(), pClrs.getColor2()); - - // 4. If there are still on-color cards and the average cmc is low add a - // 23rd card. - if (cardsNeeded == 0 && CardListUtil.getAverageCMC(outList) < 3 && !nonLands.isEmpty()) { - Card c = nonLands.get(0); - outList.add(c); - aiPlayables.remove(0); - landsNeeded--; - } - - // 5. If there are still less than 22 non-land cards add off-color - // cards. - ii = 0; - CardList z = aiPlayables.getNotType("Land"); - while ((cardsNeeded > 0) && (z.size() > 1)) { - - // if (z.size() < 1) - // throw new - // RuntimeException("BoosterDraftAI : buildDeck() error, deck does not have enough non-lands"); - final Card c = z.get(MyRandom.getRandom().nextInt(z.size() - 1)); - - // out.addMain(c.getName()); - outList.add(c); - cardsNeeded--; - aiPlayables.remove(c); - - z = aiPlayables.getNotType("Land"); - - if (Constant.Runtime.DEV_MODE[0]) { - System.out.println("NonLands[" + ii++ + "]:" + c.getName() + "(" + c.getManaCost() + ")"); - } - } - - // 6. If it's not a mono color deck, add non-basic lands. - CardList lands = aiPlayables.getType("Land"); - while (!pClrs.getColor1().equals(pClrs.getColor2()) && landsNeeded > 0 && lands.size() > 0) { - final Card c = lands.get(0); - - outList.add(c); - landsNeeded--; - aiPlayables.remove(c); - - lands = aiPlayables.getType("Land"); - - if (Constant.Runtime.DEV_MODE[0]) { - System.out.println("Land:" + c.getName()); - } - } - - // attempt to optimize basic land counts according - // to color representation - - final CCnt[] clrCnts = { new CCnt("Plains", 0), new CCnt("Island", 0), new CCnt("Swamp", 0), - new CCnt("Mountain", 0), new CCnt("Forest", 0) }; - - // count each card color using mana costs - // TODO: count hybrid mana differently? - for (i = 0; i < outList.size(); i++) { - final CardManaCost mc = outList.get(i).getManaCost(); - - // count each mana symbol in the mana cost - for (ManaCostShard shard : mc.getShards()) { - byte mask = shard.getColorMask(); - - if ((mask & CardColor.WHITE) > 0) { - clrCnts[0].setCount(clrCnts[0].getCount() + 1); - } - if ((mask & CardColor.BLUE) > 0) { - clrCnts[1].setCount(clrCnts[1].getCount() + 1); - } - if ((mask & CardColor.BLACK) > 0) { - clrCnts[2].setCount(clrCnts[2].getCount() + 1); - } - if ((mask & CardColor.RED) > 0) { - clrCnts[3].setCount(clrCnts[3].getCount() + 1); - } - if ((mask & CardColor.GREEN) > 0) { - clrCnts[4].setCount(clrCnts[4].getCount() + 1); - } - } - } - - if (landsNeeded > 0) { - // total of all ClrCnts - int totalColor = 0; - for (i = 0; i < 5; i++) { - totalColor += clrCnts[i].getCount(); - // tmpDeck += ClrCnts[i].Color + ":" + ClrCnts[i].Count + "\n"; - } - - // tmpDeck += "totalColor:" + totalColor + "\n"; - - for (i = 0; i < 5; i++) { - if (clrCnts[i].getCount() > 0) { // calculate number of lands - // for - // each color - final float p = (float) clrCnts[i].getCount() / (float) totalColor; - final int nLand = (int) (landsNeeded * p) + 1; - // tmpDeck += "nLand-" + ClrCnts[i].Color + ":" + nLand + - // "\n"; - if (Constant.Runtime.DEV_MODE[0]) { - System.out.println("Basics[" + clrCnts[i].getColor() + "]:" + nLand); - } - - // just to prevent a null exception by the deck size fixing - // code - // CardCounts.put(ClrCnts[i].Color, nLand); - - for (int j = 0; j <= nLand; j++) { - final Card c = AllZone.getCardFactory().getCard(clrCnts[i].getColor(), - AllZone.getComputerPlayer()); - c.setCurSetCode(IBoosterDraft.LAND_SET_CODE[0]); - outList.add(c); - landsNeeded--; - } - } - } - int n = 0; - while (landsNeeded > 0) { - if (clrCnts[n].getCount() > 0) { - final Card c = AllZone.getCardFactory().getCard(clrCnts[n].getColor(), AllZone.getComputerPlayer()); - c.setCurSetCode(IBoosterDraft.LAND_SET_CODE[0]); - outList.add(c); - landsNeeded--; - - if (Constant.Runtime.DEV_MODE[0]) { - System.out.println("AddBasics: " + c.getName()); - } - } - if (++n > 4) { - n = 0; - } - } - } - - while (outList.size() > 40) { - final Card c = outList.get(MyRandom.getRandom().nextInt(outList.size() - 1)); - outList.remove(c); - aiPlayables.add(c); - } - - while (outList.size() < 40) { - if (aiPlayables.size() > 1) { - final Card c = aiPlayables.get(MyRandom.getRandom().nextInt(aiPlayables.size() - 1)); - outList.add(c); - aiPlayables.remove(c); - } else if (aiPlayables.size() == 1) { - final Card c = aiPlayables.get(0); - outList.add(c); - aiPlayables.remove(c); - } else { - // if no playable cards remain fill up with basic lands - for (i = 0; i < 5; i++) { - if (clrCnts[i].getCount() > 0) { - final Card c = AllZone.getCardFactory().getCard(clrCnts[i].getColor(), - AllZone.getComputerPlayer()); - c.setCurSetCode(IBoosterDraft.LAND_SET_CODE[0]); - outList.add(c); - break; - } - } - } - } - if (outList.size() == 40) { - final Deck out = new Deck(); - out.getMain().add(outList); - out.getSideboard().add(aiPlayables); - out.getSideboard().add(dList); - return out; - } - throw new RuntimeException("BoosterDraftAI : buildDeck() error, decksize not 40"); - } - - /* - * private Deck getDeck(CardList list) { Deck out = new - * Deck(GameType.Draft); for(int i = 0; i < list.size(); i++) - * out.addMain(list.get(i).getName()); - * - * return out; }//getDeck() - * - * //add Land to list argument private void addLand(CardList list, String[] - * color) { Card land; for(int i = 0; i < 9; i++) { land = - * AllZone.getCardFactory().getCard(colorToLand.get(color[0]).toString(), - * AllZone.getComputerPlayer()); - * - * land.setCurSetCode(land.getMostRecentSet()); - * land.setImageFilename(CardUtil.buildFilename(land)); - * - * list.add(land); - * - * land = - * AllZone.getCardFactory().getCard(colorToLand.get(color[1]).toString(), - * AllZone.getComputerPlayer()); - * - * land.setCurSetCode(land.getMostRecentSet()); - * land.setImageFilename(CardUtil.buildFilename(land)); - * - * list.add(land); } - * - * //if(list.getType("Land").size() != 18) //throw new RuntimeException( - * "BoosterDraftAI : addLand() error, deck does not have 18 lands - " - * +list.getType("Land").size()); - * - * //if(list.size() != 40) //throw new - * RuntimeException("BoosterDraftAI : addLand() error, deck is not 40 cards - " - * +list.size()); }//addLand() - */ - + // returns 7 different ints, within the range of 0-9 /** @@ -758,48 +436,6 @@ public class BoosterDraftAI { { Constant.Color.RED, Constant.Color.WHITE } }; - private final Comparator