package forge; import forge.card.spellability.Ability_Mana; import forge.deck.Deck; import java.util.*; /** * <p>BoosterDraftAI class.</p> * * @author Forge * @version $Id: $ */ public class BoosterDraftAI { public BoosterDraft bd = null; //once a deck has this number of creatures the computer randomly //picks a card, so the final computer deck has 12-20 creatures //minimum of creatures per deck //private static final int nCreatures = 16; /** * Constant <code>nDecks=7</code> */ private static final int nDecks = 7; //holds all the cards for each of the computer's decks private CardList[] deck = new CardList[nDecks]; private String[][] deckColor = new String[nDecks][]; /** * Constant <code>colorToLand</code> */ private static Map<String, String> colorToLand = new TreeMap<String, String>(); //picks one Card from in_choose, removes that card, and returns the list //returns the cards not picked /** * <p>choose.</p> * * @param in_choose a {@link forge.CardList} object. * @param player a int. * @return a {@link forge.CardList} object. */ public CardList choose(final CardList in_choose, int player) { //in_choose should ONLY be on the RIGHT side of any equal sign //only 1 card should be removed from in_choose if (Constant.Runtime.DevMode[0]) System.out.println("Player[" + player + "] pack: " + in_choose.toString()); CardList list = new CardList(); boolean hasPicked = false; Card pickedCard = new Card(); CardList AIPlayables = in_choose.filter(new CardListFilter() { public boolean addCard(Card c) { if (c.getSVar("RemAIDeck").equals("True") || c.getSVar("RemRandomDeck").equals("True")) return false; return true; } }); if (playerColors.get(player).Color1.equals("none") && playerColors.get(player).Color2.equals("none")) { // CardList creatures = AIPlayables.getType("Creature").getColored(); creatures.sort(bestCreature); //for (int i=0; i<creatures.size(); i++) //System.out.println("creature[" + i + "]: " + creatures.get(i).getName()); if (creatures.size() > 0) { pickedCard = creatures.get(creatures.size() - 1); playerColors.get(player).Color1 = pickedCard.getColor().get(0).toStringArray().get(0); if (Constant.Runtime.DevMode[0]) System.out.println("Player[" + player + "] Color1: " + playerColors.get(player).Color1); playerColors.get(player).Mana1 = playerColors.get(player).ColorToMana(playerColors.get(player).Color1); //if the first pick has more than one color add the second as second color to draft if (pickedCard.getColor().get(0).toStringArray().size() > 1) { playerColors.get(player).Color2 = pickedCard.getColor().get(0).toStringArray().get(1); if (Constant.Runtime.DevMode[0]) System.out.println("Player[" + player + "] Color2: " + playerColors.get(player).Color2); playerColors.get(player).Mana2 = playerColors.get(player).ColorToMana(playerColors.get(player).Color2); } hasPicked = true; } } else if (!playerColors.get(player).Color1.equals("none") && playerColors.get(player).Color2.equals("none")) { CardList creatures = AIPlayables.getType("Creature").getColored(); creatures.sort(bestCreature); //for (int i=0; i<creatures.size(); i++) //System.out.println("creature[" + i + "]: " + creatures.get(i).getName()); if (creatures.size() > 0) { pickedCard = creatures.get(creatures.size() - 1); playerColors.get(player).Color2 = pickedCard.getColor().get(0).toStringArray().get(0); if (Constant.Runtime.DevMode[0]) System.out.println("Player[" + player + "] Color2: " + playerColors.get(player).Color2); playerColors.get(player).Mana2 = playerColors.get(player).ColorToMana(playerColors.get(player).Color2); hasPicked = true; } } else { CardList typeList; CardList colorList; colorList = AIPlayables.getOnly2Colors(playerColors.get(player).Color1, playerColors.get(player).Color2); if (colorList.size() > 0) { typeList = colorList.getType("Creature"); if (typeList.size() > 0) { typeList.sort(bestCreature); typeList.reverse(); list.add(typeList.get(0)); if (typeList.size() > 1) list.add(typeList.get(1)); } typeList = colorList.getType("Instant"); typeList.addAll(colorList.getType("Sorcery")); if (typeList.size() > 0) { CardListUtil.sortCMC(typeList); list.add(typeList.get(typeList.size() / 2)); } typeList = colorList.getType("Enchantment"); if (typeList.size() > 0) { CardListUtil.sortCMC(typeList); list.add(typeList.get(0)); } typeList = colorList.getType("Planeswalker"); if (typeList.size() > 0) list.add(typeList.get(0)); typeList = colorList.getType("Artifact"); if (typeList.size() > 0) { CardListUtil.sortCMC(typeList); list.add(typeList.get(0)); } } else { /* if (!playerColors.get(player).Splash.equals("none")) { // pick randomly from splash color colorList = AIPlayables.getColor(playerColors.get(player).Splash); if (colorList.size() > 0) { Random r = new Random(); list.add(colorList.get(r.nextInt(colorList.size()))); } } else { // pick splash color ArrayList<String> otherColors = new ArrayList<String>(); for (int i=0; i<5; i++) otherColors.add(Constant.Color.onlyColors[i]); otherColors.remove(playerColors.get(player).Color1); otherColors.remove(playerColors.get(player).Color2); colorList = new CardList(); for (int i=0; i<otherColors.size(); i++) colorList.add(in_choose.getColor(otherColors.get(i))); if (colorList.size() > 0) { Random r = new Random(); pickedCard = colorList.get(r.nextInt(colorList.size())); playerColors.get(player).Splash = pickedCard.getColor().get(0).toStringArray().get(0); System.out.println("Player["+player+"] Splash: "+playerColors.get(player).Splash); playerColors.get(player).ManaS = playerColors.get(player).ColorToMana(playerColors.get(player).Splash); hasPicked = true; } } */ typeList = AIPlayables.getType("Land"); if (typeList.size() > 0) { for (int i = 0; i < typeList.size(); i++) { ArrayList<Ability_Mana> maList = typeList.get(i).getManaAbility(); for (int j = 0; j < maList.size(); j++) { if (maList.get(j).canProduce(playerColors.get(player).Mana1) || maList.get(j).canProduce(playerColors.get(player).Mana2)) //|| maList.get(j).canProduce(playerColors.get(player).ManaS)) list.add(typeList.get(i)); } } } } } if (!hasPicked) { Random r = new Random(); if (list.size() > 0) { list.shuffle(); pickedCard = list.get(r.nextInt(list.size())); hasPicked = true; } else { in_choose.shuffle(); pickedCard = in_choose.get(r.nextInt(in_choose.size())); hasPicked = true; } } if (hasPicked) { in_choose.remove(pickedCard); deck[player].add(pickedCard); if (Constant.Runtime.DevMode[0]) System.out.println("Player[" + player + "] picked " + pickedCard.getName() + " (" + pickedCard.getManaCost() + ") " + pickedCard.getType().toString() + "\n"); } return in_choose; }//choose() /* I get some wierd error when I have this method, I don't know whats wrong private void checkDeckList(CardList[] deck) { if(deck.length != nDecks) throw new RuntimeException("BoosterDraftAI : checkDeckList() error, deck list size is not 7 - " +deck.length); for(int i = 0; i < nDecks; i++) { if(deck[i].size() != 22) { throw new RuntimeException("BoosterDraftAI : checkDeckList() error, deck list size is not 22 - " +deck[i].size() +" - " +deck.toString()); } if(countCreatures(deck[i]) < nCreatures) throw new RuntimeException("BoosterDraftAI : checkDeckList() error, deck needs more creatures - " +countCreatures(deck[i])); for(int inner = 0; inner < 22; inner++) if(! CardUtil.getColors(deck[i].getCard(inner)).contains(deckColor[i][0]) && ! CardUtil.getColors(deck[i].getCard(inner)).contains(deckColor[i][1])) throw new RuntimeException("BoosterDraftAI : checkDeckList() error, deck has different card colors"); }//for }//checkDeckList() */ //private int countCreatures(CardList list) {return list.getType("Creature").size();} /** * <p>testColors.</p> * * @param n an array of int. */ private void testColors(int[] n) { if (n.length != nDecks) throw new RuntimeException("BoosterDraftAI : testColors error, numbers array length does not equal 7"); Set<Integer> set = new TreeSet<Integer>(); for (int i = 0; i < nDecks; i++) set.add(Integer.valueOf(n[i])); if (set.size() != nDecks) throw new RuntimeException("BoosterDraftAI : testColors error, numbers not unique"); for (int i = 0; i < nDecks; i++) if (n[i] < 0 || deckColorChoices.length <= n[i]) throw new RuntimeException("BoosterDraftAI : testColors error, index out of range - " + n[i]); }//testColors() /** * <p>getDecks.</p> * * @return an array of {@link forge.deck.Deck} objects. */ public Deck[] getDecks() { //check CardList[] deck for errors //checkDeckList(deck); Deck[] out = new Deck[deck.length]; for (int i = 0; i < deck.length; i++) { //addLand(deck[i], deckColor[i]); //out[i] = getDeck(deck[i]); if (Constant.Runtime.DevMode[0]) System.out.println("Deck[" + i + "]"); out[i] = buildDeck(deck[i], playerColors.get(i)); } return out; }//getDecks() /** * <p>buildDeck.</p> * * @param dList a {@link forge.CardList} object. * @param pClrs a {@link forge.DeckColors} object. * @return a {@link forge.deck.Deck} object. */ private Deck buildDeck(CardList dList, DeckColors pClrs) { Deck out = new Deck(Constant.GameType.Draft); CardList outList = new CardList(); int cardsNeeded = 22; int landsNeeded = 18; CardList AIPlayables = dList.filter(new CardListFilter() { public boolean addCard(Card c) { return !(c.getSVar("RemAIDeck").equals("True")); } }); for (int i = 0; i < AIPlayables.size(); i++) dList.remove(AIPlayables.get(i)); CardList creatures = AIPlayables.getType("Creature").getOnly2Colors(pClrs.Color1, pClrs.Color2); int nCreatures = 15; creatures.sort(bestCreature); creatures.reverse(); int i = 0; while (nCreatures > 0 && i < creatures.size()) { Card c = creatures.get(i); outList.add(c); cardsNeeded--; nCreatures--; AIPlayables.remove(c); if (Constant.Runtime.DevMode[0]) System.out.println("Creature[" + i + "]:" + c.getName() + " (" + c.getManaCost() + ")"); i++; } CardList otherCreatures = AIPlayables.getType("Creature"); while (nCreatures > 1 && otherCreatures.size() > 1) { Card c = otherCreatures.get(MyRandom.random.nextInt(otherCreatures.size() - 1)); outList.add(c); cardsNeeded--; nCreatures--; AIPlayables.remove(c); otherCreatures = AIPlayables.getType("Creature"); if (Constant.Runtime.DevMode[0]) System.out.println("AddCreature: " + c.getName() + " (" + c.getManaCost() + ")"); } CardList others = AIPlayables.getNotType("Creature").getNotType("Land").getOnly2Colors(pClrs.Color1, pClrs.Color2); int ii = 0; while (cardsNeeded > 0 && others.size() > 1) { Card c = others.get(MyRandom.random.nextInt(others.size() - 1)); //out.addMain(c.getName()); outList.add(c); cardsNeeded--; AIPlayables.remove(c); others = AIPlayables.getNotType("Creature").getNotType("Land").getOnly2Colors(pClrs.Color1, pClrs.Color2); if (Constant.Runtime.DevMode[0]) System.out.println("Others[" + ii++ + "]:" + c.getName() + " (" + c.getManaCost() + ")"); } 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"); Card c = z.get(MyRandom.random.nextInt(z.size() - 1)); //out.addMain(c.getName()); outList.add(c); cardsNeeded--; AIPlayables.remove(c); z = AIPlayables.getNotType("Land"); if (Constant.Runtime.DevMode[0]) System.out.println("NonLands[" + ii++ + "]:" + c.getName() + "(" + c.getManaCost() + ")"); } CardList lands = AIPlayables.getType("Land"); while (landsNeeded > 0 && lands.size() > 0) { Card c = lands.get(0); outList.add(c); landsNeeded--; AIPlayables.remove(c); lands = AIPlayables.getType("Land"); if (Constant.Runtime.DevMode[0]) System.out.println("Land:" + c.getName()); } if (landsNeeded > 0) // attempt to optimize basic land counts according to color representation { 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++) { String mc = outList.get(i).getManaCost(); // count each mana symbol in the mana cost for (int j = 0; j < mc.length(); j++) { char c = mc.charAt(j); if (c == 'W') ClrCnts[0].Count++; else if (c == 'U') ClrCnts[1].Count++; else if (c == 'B') ClrCnts[2].Count++; else if (c == 'R') ClrCnts[3].Count++; else if (c == 'G') ClrCnts[4].Count++; } } // total of all ClrCnts int totalColor = 0; for (i = 0; i < 5; i++) { totalColor += ClrCnts[i].Count; //tmpDeck += ClrCnts[i].Color + ":" + ClrCnts[i].Count + "\n"; } //tmpDeck += "totalColor:" + totalColor + "\n"; for (i = 0; i < 5; i++) { if (ClrCnts[i].Count > 0) { // calculate number of lands for each color float p = (float) ClrCnts[i].Count / (float) totalColor; int nLand = (int) ((float) landsNeeded * p) + 1; //tmpDeck += "nLand-" + ClrCnts[i].Color + ":" + nLand + "\n"; if (Constant.Runtime.DevMode[0]) System.out.println("Basics[" + ClrCnts[i].Color + "]:" + 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++) { Card c = AllZone.getCardFactory().getCard(ClrCnts[i].Color, AllZone.getComputerPlayer()); c.setCurSetCode(BoosterDraft.LandSetCode[0]); outList.add(c); landsNeeded--; } } } int n = 0; while (landsNeeded > 0) { if (ClrCnts[n].Count > 0) { Card c = AllZone.getCardFactory().getCard(ClrCnts[n].Color, AllZone.getComputerPlayer()); c.setCurSetCode(BoosterDraft.LandSetCode[0]); outList.add(c); landsNeeded--; if (Constant.Runtime.DevMode[0]) System.out.println("AddBasics: " + c.getName()); } if (++n > 4) n = 0; } } while (outList.size() > 40) { Card c = outList.get(MyRandom.random.nextInt(outList.size() - 1)); outList.remove(c); AIPlayables.add(c); } while (outList.size() < 40) { Card c = AIPlayables.get(MyRandom.random.nextInt(AIPlayables.size() - 1)); outList.add(c); AIPlayables.remove(c); } if (outList.size() == 40) { for (i = 0; i < outList.size(); i++) out.addMain(outList.get(i).getName() + "|" + outList.get(i).getCurSetCode()); for (i = 0; i < AIPlayables.size(); i++) out.addSideboard(AIPlayables.get(i).getName() + "|" + AIPlayables.get(i).getCurSetCode()); for (i = 0; i < dList.size(); i++) out.addSideboard(dList.get(i).getName() + "|" + dList.get(i).getCurSetCode()); } else throw new RuntimeException("BoosterDraftAI : buildDeck() error, decksize not 40"); return out; } /* private Deck getDeck(CardList list) { Deck out = new Deck(Constant.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 /** * <p>getDeckColors.</p> * * @return an array of int. */ private int[] getDeckColors() { int[] out = new int[nDecks]; int start = MyRandom.random.nextInt(10); for (int i = 0; i < out.length; i++) { //% to get an index between 0 and deckColorChoices.length out[i] = start % deckColorChoices.length; start++; } testColors(out); return out; }//getDeckColors() /** * <p>Constructor for BoosterDraftAI.</p> */ public BoosterDraftAI() { //choose colors for decks int[] n = getDeckColors(); for (int i = 0; i < n.length; i++) deckColor[i] = deckColorChoices[n[i]]; //initilize color map colorToLand.put(Constant.Color.Black, "Swamp"); colorToLand.put(Constant.Color.Blue, "Island"); colorToLand.put(Constant.Color.Green, "Forest"); colorToLand.put(Constant.Color.Red, "Mountain"); colorToLand.put(Constant.Color.White, "Plains"); //initilize deck array and playerColors list for (int i = 0; i < deck.length; i++) { deck[i] = new CardList(); playerColors.add(new DeckColors()); } }//BoosterDraftAI() private ArrayList<DeckColors> playerColors = new ArrayList<DeckColors>(); //all 10 two color combinations private String[][] deckColorChoices = { {Constant.Color.Black, Constant.Color.Blue}, {Constant.Color.Black, Constant.Color.Green}, {Constant.Color.Black, Constant.Color.Red}, {Constant.Color.Black, Constant.Color.White}, {Constant.Color.Blue, Constant.Color.Green}, {Constant.Color.Blue, Constant.Color.Red}, {Constant.Color.Blue, Constant.Color.White}, {Constant.Color.Green, Constant.Color.Red}, {Constant.Color.Green, Constant.Color.White}, {Constant.Color.Red, Constant.Color.White} }; private Comparator<Card> bestCreature = new Comparator<Card>() { public int compare(Card a, Card b) { int cmcA = a.getCMC(); if (cmcA == 0) cmcA = 1; cmcA *= 10; int cmcB = b.getCMC(); if (cmcB == 0) cmcB = 1; cmcB *= 10; int attA = a.getBaseAttack() * 10; int attB = b.getBaseAttack() * 10; int defA = a.getBaseDefense() * 10; int defB = b.getBaseDefense() * 10; int keyA = a.getKeyword().size() * 10; int keyB = b.getKeyword().size() * 10; int abA = a.getSpellAbility().length * 10; int abB = b.getSpellAbility().length * 10; int trgA = a.getTriggers().size() * 10; int trgB = b.getTriggers().size() * 10; int rarA = 0; int rarB = 0; if (a.getCurSetRarity().equals("Common")) rarA = 1; else if (a.getCurSetRarity().equals("Uncommon")) rarA = 2; else if (a.getCurSetRarity().equals("Rare")) rarA = 4; else if (a.getCurSetRarity().equals("Mythic")) rarA = 8; if (b.getCurSetRarity().equals("Common")) rarB = 1; else if (b.getCurSetRarity().equals("Uncommon")) rarB = 2; else if (b.getCurSetRarity().equals("Rare")) rarB = 4; else if (b.getCurSetRarity().equals("Mythic")) rarB = 8; /** * <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. */ int scoreA = ((attA + defA) / cmcA) + keyA + abA + trgA + rarA; int scoreB = ((attB + defB) / cmcB) + keyB + abB + trgB + rarB; if (scoreA == scoreB) return 0; /** * <p>Constructor for deckColors.</p> */ else if (scoreA > scoreB) return 1; else if (scoreB > scoreA) return -1; /** * <p>ColorToMana.</p> * * @param color a {@link java.lang.String} object. * @return a {@link java.lang.String} object. */ return 0; } }; }//BoosterDraftAI()