package forge.deck.generate; import forge.*; import forge.error.ErrorViewer; import forge.properties.ForgeProps; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Random; /** * <p>Generate3ColorDeck class.</p> * * @author Forge * @version $Id: $ */ public class Generate3ColorDeck { private String color1 = ""; private String color2 = ""; private String color3 = ""; private Random r = null; private Map<String, String> ClrMap = null; private ArrayList<String> notColors = null; private ArrayList<DLnd> DualLands = null; private ArrayList<String> DL = null; private Map<String, Integer> CardCounts = null; /** * <p>Constructor for Generate3ColorDeck.</p> * * @param Clr1 a {@link java.lang.String} object. * @param Clr2 a {@link java.lang.String} object. * @param Clr3 a {@link java.lang.String} object. */ public Generate3ColorDeck(String Clr1, String Clr2, String Clr3) { r = MyRandom.random; CardCounts = new HashMap<String, Integer>(); ClrMap = new HashMap<String, String>(); ClrMap.put("white", "W"); ClrMap.put("blue", "U"); ClrMap.put("black", "B"); ClrMap.put("red", "R"); ClrMap.put("green", "G"); notColors = new ArrayList<String>(); notColors.add("white"); notColors.add("blue"); notColors.add("black"); notColors.add("red"); notColors.add("green"); DualLands = new ArrayList<DLnd>(); DualLands.add(new DLnd("Tundra", "WU")); DualLands.add(new DLnd("Hallowed Fountain", "WU")); DualLands.add(new DLnd("Underground Sea", "UB")); DualLands.add(new DLnd("Watery Grave", "UB")); DualLands.add(new DLnd("Badlands", "BR")); DualLands.add(new DLnd("Blood Crypt", "BR")); DualLands.add(new DLnd("Taiga", "RG")); DualLands.add(new DLnd("Stomping Ground", "RG")); DualLands.add(new DLnd("Savannah", "GW")); DualLands.add(new DLnd("Temple Garden", "GW")); DualLands.add(new DLnd("Scrubland", "WB")); DualLands.add(new DLnd("Godless Shrine", "WB")); DualLands.add(new DLnd("Volcanic Island", "UR")); DualLands.add(new DLnd("Steam Vents", "UR")); DualLands.add(new DLnd("Bayou", "BG")); DualLands.add(new DLnd("Overgrown Tomb", "BG")); DualLands.add(new DLnd("Plateau", "RW")); DualLands.add(new DLnd("Sacred Foundry", "RW")); DualLands.add(new DLnd("Tropical Island", "GU")); DualLands.add(new DLnd("Breeding Pool", "GU")); if (Clr1.equals("AI")) { // choose first color color1 = notColors.get(r.nextInt(5)); // choose second color String c2 = notColors.get(r.nextInt(5)); while (c2.equals(color1)) c2 = notColors.get(r.nextInt(5)); color2 = c2; String c3 = notColors.get(r.nextInt(5)); while (c3.equals(color1) || c3.equals(color2)) c3 = notColors.get(r.nextInt(5)); color3 = c3; } else { color1 = Clr1; color2 = Clr2; color3 = Clr3; } notColors.remove(color1); notColors.remove(color2); notColors.remove(color3); DL = new ArrayList<String>(); for (int i = 0; i < DualLands.size(); i++) { DLnd d = DualLands.get(i); if ((d.Mana.contains(ClrMap.get(color1)) && d.Mana.contains(ClrMap.get(color2))) || (d.Mana.contains(ClrMap.get(color1)) && d.Mana.contains(ClrMap.get(color3))) || (d.Mana.contains(ClrMap.get(color2)) && d.Mana.contains(ClrMap.get(color3)))) { DL.add(d.Name); CardCounts.put(d.Name, 0); } } } /** * <p>get3ColorDeck.</p> * * @param Size a int. * @return a {@link forge.CardList} object. */ public CardList get3ColorDeck(int Size) { int lc = 0; // loop counter to prevent infinite card selection loops String tmpDeck = ""; CardList tDeck = new CardList(); int LandsPercentage = 44; int CreatPercentage = 34; int SpellPercentage = 22; // start with all cards // remove cards that generated decks don't like CardList AllCards = CardFilter.filter(AllZone.getCardFactory(), new CardListFilter() { public boolean addCard(Card c) { return !(c.getSVar("RemAIDeck").equals("True") || c.getSVar("RemRandomDeck").equals("True")); } }); // reduce to cards that match the colors CardList CL1 = AllCards.getColor(color1); CardList CL2 = AllCards.getColor(color2); CardList CL3 = AllCards.getColor(color3); // remove multicolor cards that don't match the colors CardListFilter clrF = new CardListFilter() { public boolean addCard(Card c) { for (int i = 0; i < notColors.size(); i++) { if (c.getManaCost().contains(ClrMap.get(notColors.get(i)))) return false; } return true; } }; CL1 = CL1.filter(clrF); CL2 = CL2.filter(clrF); CL3 = CL3.filter(clrF); // build subsets based on type CardList Cr1 = CL1.getType("Creature"); CardList Cr2 = CL2.getType("Creature"); CardList Cr3 = CL3.getType("Creature"); String ISE[] = {"Instant", "Sorcery", "Enchantment", "Planeswalker"}; CardList Sp1 = CL1.getValidCards(ISE, null, null); CardList Sp2 = CL2.getValidCards(ISE, null, null); CardList Sp3 = CL3.getValidCards(ISE, null, null); // final card pools CardList Cr123 = new CardList(); CardList Sp123 = new CardList(); // used for mana curve in the card pool final int MinCMC[] = {1}, MaxCMC[] = {3}; CardListFilter cmcF = new CardListFilter() { public boolean addCard(Card c) { int cCMC = c.getCMC(); return (cCMC >= MinCMC[0]) && (cCMC <= MaxCMC[0]); } }; // select cards to build card pools using a mana curve for (int i = 3; i > 0; i--) { CardList Cr1CMC = Cr1.filter(cmcF); CardList Cr2CMC = Cr2.filter(cmcF); CardList Cr3CMC = Cr3.filter(cmcF); CardList Sp1CMC = Sp1.filter(cmcF); CardList Sp2CMC = Sp2.filter(cmcF); CardList Sp3CMC = Sp3.filter(cmcF); for (int j = 0; j < i; j++) { Card c = Cr1CMC.get(r.nextInt(Cr1CMC.size())); Cr123.add(c); CardCounts.put(c.getName(), 0); c = Cr2CMC.get(r.nextInt(Cr2CMC.size())); Cr123.add(c); CardCounts.put(c.getName(), 0); c = Cr3CMC.get(r.nextInt(Cr3CMC.size())); Cr123.add(c); CardCounts.put(c.getName(), 0); c = Sp1CMC.get(r.nextInt(Sp1CMC.size())); Sp123.add(c); CardCounts.put(c.getName(), 0); c = Sp2CMC.get(r.nextInt(Sp2CMC.size())); Sp123.add(c); CardCounts.put(c.getName(), 0); c = Sp3CMC.get(r.nextInt(Sp3CMC.size())); Sp123.add(c); CardCounts.put(c.getName(), 0); } MinCMC[0] += 2; MaxCMC[0] += 2; // resulting mana curve of the card pool //18x 1 - 3 //12x 3 - 5 //6x 5 - 7 //=36x - card pool could support up to a 257 card deck (all 4-ofs plus basic lands) } // shuffle card pools Cr123.shuffle(); Sp123.shuffle(); // calculate card counts float p = (float) ((float) CreatPercentage * .01); int CreatCnt = (int) (p * (float) Size); tmpDeck += "Creature Count:" + CreatCnt + "\n"; p = (float) ((float) SpellPercentage * .01); int SpellCnt = (int) (p * (float) Size); tmpDeck += "Spell Count:" + SpellCnt + "\n"; // build deck from the card pools for (int i = 0; i < CreatCnt; i++) { Card c = Cr123.get(r.nextInt(Cr123.size())); lc = 0; while (CardCounts.get(c.getName()) > 3 || lc > 100) { c = Cr123.get(r.nextInt(Cr123.size())); lc++; } if (lc > 100) throw new RuntimeException("Generate3ColorDeck : get3ColorDeck -- looped too much -- Cr123"); tDeck.add(AllZone.getCardFactory().getCard(c.getName(), AllZone.getComputerPlayer())); int n = CardCounts.get(c.getName()); CardCounts.put(c.getName(), n + 1); tmpDeck += c.getName() + " " + c.getManaCost() + "\n"; } for (int i = 0; i < SpellCnt; i++) { Card c = Sp123.get(r.nextInt(Sp123.size())); lc = 0; while (CardCounts.get(c.getName()) > 3 || lc > 100) { c = Sp123.get(r.nextInt(Sp123.size())); lc++; } if (lc > 100) throw new RuntimeException("Generate3ColorDeck : get3ColorDeck -- looped too much -- Sp123"); tDeck.add(AllZone.getCardFactory().getCard(c.getName(), AllZone.getComputerPlayer())); int n = CardCounts.get(c.getName()); CardCounts.put(c.getName(), n + 1); tmpDeck += c.getName() + " " + c.getManaCost() + "\n"; } // Add lands int numLands = 0; if (LandsPercentage > 0) { p = (float) ((float) LandsPercentage * .01); numLands = (int) (p * (float) Size); } else // otherwise, just fill in the rest of the deck with basic lands numLands = Size - tDeck.size(); tmpDeck += "numLands:" + numLands + "\n"; int nDLands = (numLands / 4); for (int i = 0; i < nDLands; i++) { String s = DL.get(r.nextInt(DL.size())); lc = 0; while (CardCounts.get(s) > 3 || lc > 20) { s = DL.get(r.nextInt(DL.size())); lc++; } if (lc > 20) throw new RuntimeException("Generate3ColorDeck : get3ColorDeck -- looped too much -- DL"); tDeck.add(AllZone.getCardFactory().getCard(s, AllZone.getHumanPlayer())); int n = CardCounts.get(s); CardCounts.put(s, n + 1); tmpDeck += s + "\n"; } numLands -= nDLands; if (numLands > 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 (int i = 0; i < tDeck.size(); i++) { String mc = tDeck.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 (int i = 0; i < 5; i++) { totalColor += ClrCnts[i].Count; tmpDeck += ClrCnts[i].Color + ":" + ClrCnts[i].Count + "\n"; } tmpDeck += "totalColor:" + totalColor + "\n"; for (int i = 0; i < 5; i++) { if (ClrCnts[i].Count > 0) { // calculate number of lands for each color p = (float) ClrCnts[i].Count / (float) totalColor; int nLand = (int) ((float) numLands * p); tmpDeck += "nLand-" + ClrCnts[i].Color + ":" + nLand + "\n"; // 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++) tDeck.add(AllZone.getCardFactory().getCard(ClrCnts[i].Color, AllZone.getComputerPlayer())); } } } tmpDeck += "DeckSize:" + tDeck.size() + "\n"; // fix under-sized or over-sized decks, due to integer arithmetic if (tDeck.size() < Size) { int diff = Size - tDeck.size(); for (int i = 0; i < diff; i++) { Card c = tDeck.get(r.nextInt(tDeck.size())); lc = 0; while (CardCounts.get(c.getName()) > 3 || lc > Size) { c = tDeck.get(r.nextInt(tDeck.size())); lc++; } if (lc > Size) throw new RuntimeException("Generate3ColorDeck : get3ColorDeck -- looped too much -- undersize"); int n = CardCounts.get(c.getName()); tDeck.add(AllZone.getCardFactory().getCard(c.getName(), AllZone.getComputerPlayer())); CardCounts.put(c.getName(), n + 1); tmpDeck += "Added:" + c.getName() + "\n"; } } else if (tDeck.size() > Size) { int diff = tDeck.size() - Size; for (int i = 0; i < diff; i++) { Card c = tDeck.get(r.nextInt(tDeck.size())); while (c.isBasicLand()) // don't remove basic lands c = tDeck.get(r.nextInt(tDeck.size())); tDeck.remove(c); tmpDeck += "Removed:" + c.getName() + "\n"; } } tmpDeck += "DeckSize:" + tDeck.size() + "\n"; if (ForgeProps.getProperty("showdeck/3color", "false").equals("true")) ErrorViewer.showError(tmpDeck); return tDeck; } private class CCnt { public String Color; public int Count; public CCnt(String clr, int cnt) { Color = clr; Count = cnt; } } private class DLnd { public String Name; public String Mana; public DLnd(String nm, String mn) { Name = nm; Mana = mn; } } }