public TriadDeck(IEnumerable <int> knownCardIds, IEnumerable <int> unknownCardlIds) { TriadCardDB cardDB = TriadCardDB.Get(); knownCards = new List <TriadCard>(); foreach (int id in knownCardIds) { TriadCard card = cardDB.cards[id]; if (card != null && card.IsValid()) { knownCards.Add(card); } } unknownCardPool = new List <TriadCard>(); foreach (int id in unknownCardlIds) { TriadCard card = cardDB.cards[id]; if (card != null && card.IsValid()) { unknownCardPool.Add(card); } } UpdateDeckId(); }
public bool Load() { List <TriadCard> loadedCards = new List <TriadCard>(); int maxLoadedId = 0; try { XmlDocument xdoc = new XmlDocument(); Stream dataStream = AssetManager.Get().GetAsset(DBPath); xdoc.Load(dataStream); foreach (XmlNode cardNode in xdoc.DocumentElement.ChildNodes) { XmlElement cardElem = (XmlElement)cardNode; if (cardElem != null && cardElem.Name == "card") { try { ETriadCardRarity cardRarity = ETriadCardRarity.Common; ETriadCardType cardType = ETriadCardType.None; bool bHasRarity = TryParseCardRarity(cardElem.GetAttribute("rarity"), out cardRarity); bool bHasType = TryParseCardType(cardElem.GetAttribute("type"), out cardType); if (bHasType && bHasRarity) { TriadCard newCard = new TriadCard( int.Parse(cardElem.GetAttribute("id")), WebUtility.HtmlDecode(cardElem.GetAttribute("name")), cardElem.GetAttribute("icon"), cardRarity, cardType, ParseCardSideNum(cardElem.GetAttribute("up")), ParseCardSideNum(cardElem.GetAttribute("dn")), ParseCardSideNum(cardElem.GetAttribute("lt")), ParseCardSideNum(cardElem.GetAttribute("rt")), int.Parse(cardElem.GetAttribute("sort"))); if (newCard.IsValid()) { loadedCards.Add(newCard); maxLoadedId = Math.Max(maxLoadedId, newCard.Id); } else { Logger.WriteLine("Loading failed! ob[" + newCard + "], xml:" + cardElem.OuterXml); } } else { Logger.WriteLine("Loading failed! xml:" + cardElem.OuterXml); } } catch (Exception ex) { Logger.WriteLine("Loading failed! Exception:" + ex); } } } } catch (Exception ex) { Logger.WriteLine("Loading failed! Exception:" + ex); } if (loadedCards.Count > 0) { while (cards.Count < (maxLoadedId + 1)) { cards.Add(null); } foreach (TriadCard card in loadedCards) { cards[card.Id] = card; } } sameNumberMap.Clear(); int sameNumberId = 0; for (int Idx1 = 0; Idx1 < cards.Count; Idx1++) { TriadCard card1 = cards[Idx1]; if (card1 != null && card1.SameNumberId < 0) { bool bHasSameNumberCards = false; for (int Idx2 = (Idx1 + 1); Idx2 < cards.Count; Idx2++) { TriadCard card2 = cards[Idx2]; if (card2 != null && card2.SameNumberId < 0) { bool bHasSameNumbers = (card1.Sides[0] == card2.Sides[0]) && (card1.Sides[1] == card2.Sides[1]) && (card1.Sides[2] == card2.Sides[2]) && (card1.Sides[3] == card2.Sides[3]); bHasSameNumberCards = bHasSameNumberCards || bHasSameNumbers; if (bHasSameNumbers) { if (!sameNumberMap.ContainsKey(sameNumberId)) { sameNumberMap.Add(sameNumberId, new List <TriadCard>()); sameNumberMap[sameNumberId].Add(card1); card1.SameNumberId = sameNumberId; } sameNumberMap[sameNumberId].Add(card2); card2.SameNumberId = sameNumberId; } } } if (bHasSameNumberCards) { sameNumberId++; } } } Logger.WriteLine("Loaded cards: " + loadedCards.Count + ", same sides: " + sameNumberMap.Count); return(loadedCards.Count > 0); }
private bool FindCardPool(List <TriadCard> allCards, List <TriadGameModifier> modifiers, List <TriadCard> lockedCards) { currentPool = new CardPool(); int maxRarityNum = Enum.GetValues(typeof(ETriadCardRarity)).Length; int priRarityNum = (int)commonRarity + 1; int[] mapAvailRarity = new int[maxRarityNum]; // special case: don't include any rare slots with reverse rule if there's enough cards in common list bool hasReverseMod = false; bool hasAscensionMod = false; foreach (TriadGameModifier mod in modifiers) { if (mod.GetType() == typeof(TriadGameModifierReverse)) { hasReverseMod = true; } else if (mod.GetType() == typeof(TriadGameModifierAscention)) { hasAscensionMod = true; } } // find number of priority lists based on unique rarity limits List <ETriadCardRarity> priRarityThr = new List <ETriadCardRarity>(); for (int idxR = priRarityNum; idxR < maxRarityNum; idxR++) { ETriadCardRarity testRarity = (ETriadCardRarity)idxR; if (!hasReverseMod && maxSlotsPerRarity.ContainsKey(testRarity) && maxSlotsPerRarity[testRarity] > 0) { mapAvailRarity[idxR] = maxSlotsPerRarity[testRarity]; mapAvailRarity[idxR - 1] -= maxSlotsPerRarity[testRarity]; priRarityThr.Add(testRarity); } } if (debugMode) { Logger.WriteLine("FindCardPool> priRarityThr:{0}, maxAvail:[{1},{2},{3},{4},{5}], reverse:{6}, ascention:{7}", priRarityThr.Count, mapAvailRarity[0], mapAvailRarity[1], mapAvailRarity[2], mapAvailRarity[3], mapAvailRarity[4], hasReverseMod, hasAscensionMod); } // check rarity of locked cards, eliminate pri list when threshold is matched // when multiple pri rarities are locked, start eliminating from pool above // e.g. 2x 4 star locked => 4 star out, 5 star out currentPool.deckSlotTypes = new int[lockedCards.Count]; int numLockedCards = 0; for (int idx = 0; idx < lockedCards.Count; idx++) { TriadCard card = lockedCards[idx]; if (card != null) { if (card.Rarity > commonRarity) { for (int testR = (int)card.Rarity; testR <= maxRarityNum; testR++) { if (mapAvailRarity[testR] > 0) { mapAvailRarity[testR]--; break; } } } currentPool.deckSlotTypes[idx] = DeckSlotLocked; numLockedCards++; } else { currentPool.deckSlotTypes[idx] = DeckSlotCommon; } } if (debugMode) { Logger.WriteLine(">> adjusted for locking, numLocked:{0}, maxAvail:[{1},{2},{3},{4},{5}]", numLockedCards, mapAvailRarity[0], mapAvailRarity[1], mapAvailRarity[2], mapAvailRarity[3], mapAvailRarity[4]); } if (numLockedCards == lockedCards.Count) { return(false); } List <CardScoreData> commonScoredList = new List <CardScoreData>(); List <List <CardScoreData> > priScoredList = new List <List <CardScoreData> >(); for (int idxP = 0; idxP < priRarityThr.Count; idxP++) { priScoredList.Add(new List <CardScoreData>()); } // reverse priority thresholds, idx:0 becomes strongest card priRarityThr.Reverse(); // assign each owned card to scored lists foreach (TriadCard card in allCards) { if (card == null || !card.IsValid()) { continue; } // try to guess how good card will perform // - avg of sides // - std of sides // - max of sides // - rarity (should be reflected by sides already) // - corners with same number // - max corner number int numberMax = Math.Max(Math.Max(card.Sides[0], card.Sides[1]), Math.Max(card.Sides[2], card.Sides[3])); int numberSum = card.Sides[0] + card.Sides[1] + card.Sides[2] + card.Sides[3]; float numberAvg = numberSum / 4.0f; float numberMeanSqDiff = ((card.Sides[0] - numberAvg) * (card.Sides[0] - numberAvg)) + ((card.Sides[1] - numberAvg) * (card.Sides[1] - numberAvg)) + ((card.Sides[2] - numberAvg) * (card.Sides[2] - numberAvg)) + ((card.Sides[3] - numberAvg) * (card.Sides[3] - numberAvg)); float numberStd = (float)Math.Sqrt(numberMeanSqDiff / 4); int cornerNum = 0; int numCorners = 0; if (card.Sides[0] == card.Sides[1]) { numCorners++; cornerNum = Math.Max(cornerNum, card.Sides[0]); } if (card.Sides[1] == card.Sides[2]) { numCorners++; cornerNum = Math.Max(cornerNum, card.Sides[1]); } if (card.Sides[2] == card.Sides[3]) { numCorners++; cornerNum = Math.Max(cornerNum, card.Sides[2]); } if (card.Sides[3] == card.Sides[0]) { numCorners++; cornerNum = Math.Max(cornerNum, card.Sides[3]); } CardScoreData scoredCard = new CardScoreData() { card = card }; scoredCard.score = (numberAvg * scoreAvgSides) + (numberStd * scoreStdSides) + (numberMax * scoreMaxSides) + (numCorners * scoreSameCorners) + (cornerNum * scoreMaxCorner) + ((int)card.Rarity * scoreRarity); foreach (TriadGameModifier mod in modifiers) { mod.OnScoreCard(card, ref scoredCard.score); } for (int idxP = 0; idxP < priRarityThr.Count; idxP++) { if (card.Rarity <= priRarityThr[idxP]) { priScoredList[idxP].Add(scoredCard); } } if (card.Rarity <= commonRarity) { commonScoredList.Add(scoredCard); } } if (debugMode) { Logger.WriteLine(">> card lists sorted, common:{0}", commonScoredList.Count); } bool isPoolValid = (commonScoredList.Count > 0); if (isPoolValid) { int numPriLists = 0; int deckSlotIdx = isOrderImportant ? 1 : 0; for (int idx = 0; idx < priScoredList.Count; idx++) { int numAvail = mapAvailRarity[(int)priRarityThr[idx]]; if (debugMode) { Logger.WriteLine(" pri list[{0}]:{1}, rarity:{2}, avail:{3}", idx, priScoredList[idx].Count, priRarityThr[idx], numAvail); } if ((numAvail > 0) && (priScoredList[idx].Count > 0)) { // initial deckSlotIdx should be already past only available spot (e.g. all slots but [0] are locked), make sure to wrap around // find fist available Common slot to overwrite with priority list, repeat numAvail times for (int idxAvail = 0; idxAvail < numAvail; idxAvail++) { for (int idxD = 0; idxD < currentPool.deckSlotTypes.Length; idxD++) { if (currentPool.deckSlotTypes[deckSlotIdx] == DeckSlotCommon) { break; } deckSlotIdx++; } currentPool.deckSlotTypes[deckSlotIdx] = numPriLists; } numPriLists++; } else { priScoredList[idx].Clear(); } } // ascension modifier special case: same type across all pools is best // aply after priority lists were trimmed if (hasAscensionMod) { ApplyAscentionFilter(commonScoredList, priScoredList); } if (numPriLists > 0) { currentPool.priorityLists = new TriadCard[numPriLists][]; if (debugMode) { Logger.WriteLine(">> num priority lists:{0}", numPriLists); } int idxP = 0; for (int idxL = 0; idxL < priScoredList.Count; idxL++) { int maxPriorityToUse = Math.Min(numPriorityToBuild, priScoredList[idxL].Count); if (maxPriorityToUse > 0) { currentPool.priorityLists[idxP] = new TriadCard[maxPriorityToUse]; priScoredList[idxL].Sort(); for (int idxC = 0; idxC < maxPriorityToUse; idxC++) { currentPool.priorityLists[idxP][idxC] = priScoredList[idxL][idxC].card; } idxP++; } } } // adjust pool of common cards based on avail common slots // - all common: use requested size // - scale down 20% per every priority list slot int numPriSlots = 0; for (int idx = 0; idx < currentPool.deckSlotTypes.Length; idx++) { numPriSlots += (currentPool.deckSlotTypes[idx] >= 0) ? 1 : 0; } int maxCommonToUse = Math.Min(numCommonToBuild - (numCommonToBuild * numPriSlots * numCommonPctToDropPerPriSlot / 100), commonScoredList.Count); if (debugMode) { Logger.WriteLine(">> adjusting common pool based on priSlots:{0} and drop:{1}% => {2}", numPriSlots, numCommonPctToDropPerPriSlot, maxCommonToUse); } currentPool.commonList = new TriadCard[maxCommonToUse]; commonScoredList.Sort(); for (int idx = 0; idx < currentPool.commonList.Length; idx++) { currentPool.commonList[idx] = commonScoredList[idx].card; } } if (debugMode) { Logger.WriteLine(">> deck slot types:[{0}, {1}, {2}, {3}, {4}]", currentPool.deckSlotTypes[0], currentPool.deckSlotTypes[1], currentPool.deckSlotTypes[2], currentPool.deckSlotTypes[3], currentPool.deckSlotTypes[4]); } return(isPoolValid); }
private Dictionary <int, TriadCard> ParseCards(string folderPath) { Dictionary <int, ETriadCardType> typeMap = ParseCardTypes(folderPath); Dictionary <int, string> nameMap = ParseCardNames(folderPath); Dictionary <string, ETriadCardType> typeNameMap = CreateCardTypeNameMap(); string patternRarity = "TripleTriadCardRarity#"; Dictionary <int, TriadCard> loadedCards = new Dictionary <int, TriadCard>(); List <string[]> cardData = ParseCSVFile(folderPath + "TripleTriadCardResident.csv"); if (cardData.Count > 0 && cardData[0].Length == 10) { for (int Idx = 0; Idx < cardData.Count; Idx++) { int keyIdx = int.Parse(cardData[Idx][0]); int rarityIdx = 0; if (cardData[Idx][6].StartsWith(patternRarity)) { rarityIdx = int.Parse(cardData[Idx][6].Substring(patternRarity.Length)); } else { rarityIdx = int.Parse(cardData[Idx][6]); } ETriadCardType cardType = ETriadCardType.None; string typeDef = cardData[Idx][7]; if (typeDef.Length == 1) { int typeDefInt = int.Parse(typeDef); cardType = typeMap[typeDefInt]; } else if (typeDef.Length > 0) { cardType = typeNameMap[typeDef]; } int iconId = 82500 + keyIdx; string iconPath = iconId.ToString("000000") + ".png"; int numUp = int.Parse(cardData[Idx][2]); int numDown = int.Parse(cardData[Idx][3]); int numRight = int.Parse(cardData[Idx][4]); int numLeft = int.Parse(cardData[Idx][5]); if (numUp > 0 && numDown > 0 && numRight > 0 && numLeft > 0) { TriadCard cardOb = new TriadCard(0, nameMap[keyIdx], iconPath, (ETriadCardRarity)(rarityIdx - 1), cardType, numUp, numDown, numLeft, numRight, int.Parse(cardData[Idx][9])); if (cardOb.IsValid()) { loadedCards.Add(keyIdx, cardOb); } } } } else { throw new Exception("Unable to parse cards from csv!"); } return(loadedCards); }