public void SetPlayerCards(Deck deck, List <Card> revealedCards) { PlayerCards.Clear(); foreach (var c in revealedCards) { var card = PlayerCards.FirstOrDefault(x => x.Id == c.Id); if (card != null) { card.Count++; } else { PlayerCards.Add(new TrackedCard(c.Id, c.Count)); } } if (deck != null) { foreach (var c in deck.Cards) { var e = PlayerCards.FirstOrDefault(x => x.Id == c.Id); if (e == null) { PlayerCards.Add(new TrackedCard(c.Id, c.Count, c.Count)); } else if (c.Count > e.Count) { e.Unconfirmed = c.Count - e.Count; e.Count = c.Count; } } } }
/// <summary> /// Import the deck from the tracked decklist /// </summary> /// <param name="selectedDeck">The deck that is currently selected</param> /// <returns>An instance of a deck that can be serialized</returns> private static Deck FromTracker(TrackerDeck selectedDeck) { Deck result = new Deck(); // detect if an instance was provided if (selectedDeck == null) { throw new ArgumentException("No deck was provided"); } try { // convert cards in selected deck to (dbfid, count) format foreach (var card in selectedDeck.Cards) { result.CardDbfIds.Add(HearthDb.Cards.All[card.Id].DbfId, card.Count); } } catch (Exception exc) { Log.Error(exc); throw new Exception("Exception while parsing the currently played deck"); } // Extract the hero try { // Extract hero from mirror result.HeroDbfId = MapClassNameToHero(selectedDeck.Class); } catch (Exception e) { Log.Error(e); throw new Exception("Could not parse the used hero"); } // Extract deck name try { // Extract hero from mirror result.Name = selectedDeck.Name; } catch (Exception e) { Log.Error(e); throw new Exception("Could not parse the name of the deck"); } try { result.Format = selectedDeck.IsWildDeck ? HearthDb.Enums.FormatType.FT_WILD : HearthDb.Enums.FormatType.FT_STANDARD; } catch (Exception e) { Log.Error(e); throw new Exception("Could not parse deck format"); } return(result); }
private Deck CreateDeck(PlayerClass klass, IEnumerable <TrackedCard> cards) { Common.Log.Debug($"Tracker: CreatingDeck {klass} {cards.Count()}"); var deck = new Deck() { Class = klass }; // add the cards to the deck // create a temp HDT deck too, to check if its standard var hdtDeck = new HDTDeck(); foreach (var card in cards) { var c = DB.GetCardFromId(card.Id); c.Count = card.Count; hdtDeck.Cards.Add(c); if (c != null && c != DB.UnknownCard) { deck.Cards.Add( new Card(c.Id, c.LocalizedName, c.Count, c.Background.Clone())); Common.Log.Debug($"Tracker: Card {c.Id} x{c.Count}"); } else { Common.Log.Debug($"Tracker: Card {card.Id} not found"); } } deck.IsStandard = hdtDeck.StandardViable; return(deck); }
/// <summary> /// Add a deck to the Decklist. /// </summary> /// <param name="name">Name of the deck</param> /// <param name="deck">The new deck to add</param> /// <param name="archive">Flag, if the new deck should be archieved</param> /// <param name="tags">Tags to be added to the new deck</param> public void AddDeck(string name, HDTDeck deck, bool archive, params string[] tags) { deck.Name = name; if (tags.Any()) { var reloadTags = false; foreach (var t in tags) { if (!DeckList.Instance.AllTags.Contains(t)) { DeckList.Instance.AllTags.Add(t); reloadTags = true; } deck.Tags.Add(t); } if (reloadTags) { DeckList.Save(); Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Normal, new Action(() => { Core.MainWindow.ReloadTags(); //TODO: Refresh tags in all tag lists in the UI. })); } } // Add and save deck deck.Archived = archive; DeckList.Instance.Decks.Add(deck); }
public void AddDeck(Deck deck) { if (deck == null) { Common.Log.Debug("Tracker: Attempting to add a null deck"); return; } HDTDeck d = new HDTDeck() { Name = deck.Name, Class = deck.Class.ToString(), Cards = new ObservableCollection <HDTCard>(deck.Cards.Select(c => DB.GetCardFromId(c.Id))) }; Common.Log.Debug($"Tracker: Adding deck '{d.Name}' {d.Cards} ({d.Cards.Count})"); DeckList.Instance.Decks.Add(d); }
public void OpenDeckEditor(IEnumerable <Card> cards, params string[] tags) { if (cards == null || cards.Count() <= 0) { Common.Log.Debug("Tracker: OpenDeckEditor cards are empty"); return; } var deck = new Hearthstone_Deck_Tracker.Hearthstone.Deck(); string klass = null; foreach (var c in cards) { var card = Hearthstone_Deck_Tracker.Hearthstone.Database.GetCardFromId(c.Id); card.Count = c.Count; deck.Cards.Add(card); // get class from any class cards var cardKlass = card.PlayerClass; if (klass == null && cardKlass != null && card.PlayerClass.ToLowerInvariant() != "neutral") { klass = card.PlayerClass; Common.Log.Debug($"Tracker: Class card {klass} found"); } } if (!string.IsNullOrWhiteSpace(klass)) { deck.Class = klass; } else { Common.Log.Info($"NewDeck: Class not found"); return; } if (tags.Length > 0) { deck.Tags.AddRange(tags); } API.Core.MainWindow.ShowDeckEditorFlyout(deck, true); }
public static System.Windows.Media.Imaging.BitmapSource Generate(HDT.Hearthstone.Deck deck, bool cardsOnly) { const int CardHeight = 35; const int InfoHeight = 124; const int ScreenshotWidth = 219; const int Dpi = 96; var height = CardHeight * deck.GetSelectedDeckVersion().Cards.Count; if (!cardsOnly) { height += InfoHeight; } var control = new HDT.Controls.DeckView(deck, cardsOnly); control.Measure(new System.Windows.Size(ScreenshotWidth, height)); control.Arrange(new System.Windows.Rect(new System.Windows.Size(ScreenshotWidth, height))); control.UpdateLayout(); //Log.Debug($"Screenshot: {control.ActualWidth} x {control.ActualHeight}"); var bmp = new System.Windows.Media.Imaging.RenderTargetBitmap(ScreenshotWidth, height, Dpi, Dpi, System.Windows.Media.PixelFormats.Pbgra32); bmp.Render(control); return(bmp); }
public bool BelongsToDeckVerion(Deck deck) => PlayerDeckVersion == deck.Version || (HasHearthStatsDeckVersionId && HearthStatsDeckVersionId == deck.HearthStatsDeckVersionId) || (!HasHearthStatsDeckVersionId && HasHearthStatsDeckId && HearthStatsDeckId == deck.HearthStatsId) || !IsAssociatedWithDeckVersion && deck.Version == new SerializableVersion(1, 0);
public void SetPlayerCards(Deck deck, List <Card> revealedCards) => SetPlayerCards(deck?.Cards, revealedCards);
public bool BelongsToDeckVerion(Deck deck) => PlayerDeckVersion == deck.Version || !IsAssociatedWithDeckVersion && deck.Version == new SerializableVersion(1, 0);
/// <summary> /// Determine similarity between all archetype decks and all cards played by the opponent yet. /// Then update the cardlist displayed in the overlay with the highest matching archetype deck after removing all opponent cards. /// </summary> internal async void UpdateCardList() { // Small delay to guarantee opponents cards list is up to date (should be 300+ ms in debug mode or with attached debugger, otherwise restarting HDT could lead to an incomplete opponent decklist!) #if DEBUG await Task.Delay(1000); #else await Task.Delay(100); #endif // Only continue if in valid game mode or game format if (!IsValidGameMode || !IsValidGameFormat) { return; } // Get opponent's cards list (all yet revealed cards) //var opponentCardlist = Core.Game.Opponent.RevealedCards; IList <Card> opponentCardlist = Core.Game.Opponent.OpponentCardList.Where(x => !x.IsCreated).ToList(); // If opponent's class is unknown yet or we have no imported archetype decks in the database, return empty card list //if (!opponentCardlist.Any() || !ArchetypeDecks.Any()) if (CoreAPI.Game.Opponent.Class == "" || !ArchetypeDecks.Any()) { currentArchetypeDeckGuid = Guid.Empty; _advisorOverlay.Update(new List <Card>(), true); } else { // Create archetype dictionary IDictionary <Deck, float> dict = new Dictionary <Deck, float>(); // Calculate similarities between all opponent's class archetype decks and all yet known opponent cards. Exclude wild decks in standard format using NAND expression. OR expression should also work: (!d.IsWildDeck || CoreAPI.Game.CurrentFormat == Format.Wild) foreach (var archetypeDeck in ArchetypeDecks.Where(d => d.Class == CoreAPI.Game.Opponent.Class && !(d.IsWildDeck && CoreAPI.Game.CurrentFormat == Format.Standard))) { // Insert deck with calculated value into dictionary and prevent exception by inserting duplicate decks if (!dict.ContainsKey(archetypeDeck)) { dict.Add(archetypeDeck, archetypeDeck.Similarity(opponentCardlist)); } } // Get highest similarity value var maxSim = dict.Values.DefaultIfEmpty(0).Max(); // Some unreproducable bug threw an exception here. System.InvalidOperationException: Sequence contains no elements @ IEnumerable.Max() => should be fixed by DefaultIfEmpty() now! // If any archetype deck matches more than MinimumSimilarity (as percentage) show the deck with the highest similarity (important: we need at least 1 deck in dict, otherwise we can't show any results.) if (dict.Count > 0 && maxSim >= Settings.Default.MinimumSimilarity * 0.01) { // Select top decks with highest similarity value var topSimDecks = (from d in dict where Math.Abs(d.Value - maxSim) < 0.001 select d).ToList(); // Select top decks with most played games var maxGames = topSimDecks.Max(x => x.Key.GetPlayedGames()); // If class was something like "Groddo the Bogwarden" in monster hunt, we got an InvalidOperationException at Max() because dict was empty. Now we check for dict > 0 above to prevent this. var topGamesDecks = (from t in topSimDecks where t.Key.GetPlayedGames() == maxGames select t).ToList(); // Select best matched deck with both highest similarity value and most played games var matchedDeck = topGamesDecks.First(); // Show matched deck name and similarity value or number of matching cards and number of all played cards if (Settings.Default.ShowAbsoluteSimilarity) { // Count how many cards from opponent deck are in matched deck int matchingCards = matchedDeck.Key.CountMatchingCards(opponentCardlist); _advisorOverlay.LblArchetype.Text = String.Format("{0} ({1}/{2})", matchedDeck.Key.Name, matchingCards, opponentCardlist.Sum(x => x.Count)); } else { _advisorOverlay.LblArchetype.Text = String.Format("{0} ({1}%)", matchedDeck.Key.Name, Math.Round(matchedDeck.Value * 100, 2)); } _advisorOverlay.LblStats.Text = String.Format("{0}", matchedDeck.Key.Note); Deck deck = DeckList.Instance.Decks.Where(d => d.TagList.ToLowerInvariant().Contains("archetype")).First(d => d.Name == matchedDeck.Key.Name); if (deck != null) { var predictedCards = ((Deck)deck.Clone()).Cards.ToList(); // Remove already played opponent cards from predicted archetype deck. But don't remove revealed jousted cards, because they were only seen and not played yet. foreach (var card in opponentCardlist.Where(x => !x.Jousted)) { if (predictedCards.Contains(card)) { var item = predictedCards.Find(x => x.Id == card.Id); item.Count -= card.Count; } } //var sortedPredictedCards = predictedCards.OrderBy(x => x.Cost).ThenBy(y => y.Name).ToList(); bool isNewArchetypeDeck = currentArchetypeDeckGuid != matchedDeck.Key.DeckId; // Update overlay cards _advisorOverlay.Update(predictedCards, isNewArchetypeDeck); // Remember current archetype deck guid with highest similarity to opponent's played cards currentArchetypeDeckGuid = matchedDeck.Key.DeckId; } } else { // If no archetype deck matches more than MinimumSimilarity clear the list and show the best match percentage _advisorOverlay.LblArchetype.Text = String.Format("Best match: {0}%", Math.Round(maxSim * 100, 2)); _advisorOverlay.LblStats.Text = ""; _advisorOverlay.Update(new List <Card>(), currentArchetypeDeckGuid != Guid.Empty); currentArchetypeDeckGuid = Guid.Empty; } } }
public void SetPlayerCards(Deck deck, List<Card> revealedCards) { PlayerCards.Clear(); foreach(var c in revealedCards) { var card = PlayerCards.FirstOrDefault(x => x.Id == c.Id); if(card != null) card.Count++; else PlayerCards.Add(new TrackedCard(c.Id, c.Count)); } if(deck != null) { foreach(var c in deck.Cards) { var e = PlayerCards.FirstOrDefault(x => x.Id == c.Id); if(e == null) PlayerCards.Add(new TrackedCard(c.Id, c.Count, c.Count)); else if(c.Count > e.Count) { e.Unconfirmed = c.Count - e.Count; e.Count = c.Count; } } } }
public bool BelongsToDeckVerion(Deck deck) => PlayerDeckVersion == deck.Version || (HasHearthStatsDeckVersionId && HearthStatsDeckVersionId == deck.HearthStatsDeckVersionId) || (!HasHearthStatsDeckVersionId && HasHearthStatsDeckId && HearthStatsDeckId == deck.HearthStatsId) || !IsAssociatedWithDeckVersion && deck.Version == new SerializableVersion(1, 0);
/// <summary> /// Determine similarity between all archetype decks and all cards played by the opponent yet. /// Then update the cardlist displayed in the overlay with the highest matching archetype deck after removing all opponent cards. /// </summary> internal async void UpdateCardList() { // Only continue if in valid game mode or game format if (!IsValidGameMode || !IsValidGameFormat) { return; } // Small delay to guarantee opponents cards list is up to date (should be 300+ ms in debug mode or with attached debugger, otherwise restarting HDT could lead to an incomplete opponent decklist!) #if DEBUG await Task.Delay(1000); #else await Task.Delay(100); #endif // Get opponent's cards list (all yet revealed cards) //var opponentCardlist = Core.Game.Opponent.RevealedCards; IList <Card> opponentCardlist = Core.Game.Opponent.OpponentCardList.Where(x => !x.IsCreated).ToList(); // If no opponent's cards were revealed yet or we have no imported archetype decks in the database, return empty card list if (!opponentCardlist.Any() || !ArchetypeDecks.Any()) { currentArchetypeDeckGuid = Guid.Empty; _advisorOverlay.Update(new List <Card>(), true); } else { // Create archetype dictionary IDictionary <Deck, float> dict = new Dictionary <Deck, float>(); // Calculate similarities between all opponent's class archetype decks and all yet known opponent cards. Exclude wild decks in standard format using NAND expression. foreach (var archetypeDeck in ArchetypeDecks.Where(d => d.Class == CoreAPI.Game.Opponent.Class && !(d.IsWildDeck && CoreAPI.Game.CurrentFormat == Format.Standard))) { // Insert deck with calculated value into dictionary and prevent exception by inserting duplicate decks if (!dict.ContainsKey(archetypeDeck)) { dict.Add(archetypeDeck, archetypeDeck.Similarity(opponentCardlist)); } } // Get highest similarity value var maxSim = dict.Values.Max(); // TODO: Some unreproducable bug threw an exception here. System.InvalidOperationException: Sequence contains no elements @ IEnumerable.Max() // If any archetype deck matches more than MinimumSimilarity show the deck with the highest similarity if (maxSim >= Settings.Default.MinimumSimilarity) { // Select top decks with highest similarity value var topSimDecks = (from d in dict where Math.Abs(d.Value - maxSim) < 0.001 select d).ToList(); // Select top decks with most played games var maxGames = topSimDecks.Max(x => x.Key.GetPlayedGames()); var topGamesDecks = (from t in topSimDecks where t.Key.GetPlayedGames() == maxGames select t).ToList(); // Select best matched deck with both highest similarity value and most played games var matchedDeck = topGamesDecks.First(); // Show matched deck name and similarity value or number of matching cards and number of all played cards if (Settings.Default.ShowAbsoluteSimilarity) { // Count how many cards from opponent deck are in matched deck int matchingCards = matchedDeck.Key.CountMatchingCards(opponentCardlist); _advisorOverlay.LblArchetype.Text = String.Format("{0} ({1}/{2})", matchedDeck.Key.Name, matchingCards, matchedDeck.Key.CountUnion(opponentCardlist)); } else { _advisorOverlay.LblArchetype.Text = String.Format("{0} ({1}%)", matchedDeck.Key.Name, Math.Round(matchedDeck.Value * 100, 2)); } _advisorOverlay.LblStats.Text = String.Format("{0}", matchedDeck.Key.Note); Deck deck = DeckList.Instance.Decks.Where(d => d.TagList.ToLowerInvariant().Contains("archetype")).First(d => d.Name == matchedDeck.Key.Name); if (deck != null) { var predictedCards = ((Deck)deck.Clone()).Cards.ToList(); foreach (var card in opponentCardlist) { // Remove already played opponent cards from predicted archetype deck. But don't remove revealed jousted cards, because they were only seen and not played yet. if (predictedCards.Contains(card)) { var item = predictedCards.Find(x => x.Id == card.Id); if (!card.Jousted) { item.Count -= card.Count; } // highlight jousted cards in green // also highlight when deck has 2 of a card and we have matched one if (item.Count > 0) { item.HighlightInHand = true; item.InHandCount += card.Count; } else { item.HighlightInHand = false; item.InHandCount = 0; } } if (Settings.Default.ShowNonMatchingCards) { // Show known cards that don't match the archetype deck, in red if (!predictedCards.Contains(card)) { var item = (Card)card.Clone(); item.HighlightInHand = false; item.WasDiscarded = true; if (!item.Jousted) { item.Count = 0; } predictedCards.Add(item); } } } //var sortedPredictedCards = predictedCards.OrderBy(x => x.Cost).ThenBy(y => y.Name).ToList(); bool isNewArchetypeDeck = currentArchetypeDeckGuid != matchedDeck.Key.DeckId; // Update overlay cards _advisorOverlay.Update(predictedCards.ToSortedCardList(), isNewArchetypeDeck); // Remember current archetype deck guid with highest similarity to opponent's played cards currentArchetypeDeckGuid = matchedDeck.Key.DeckId; } } else { _advisorOverlay.LblArchetype.Text = String.Format("Best match: {0}%", Math.Round(maxSim * 100, 2)); _advisorOverlay.LblStats.Text = ""; _advisorOverlay.Update(Settings.Default.ShowNonMatchingCards ? opponentCardlist.ToList() : new List <Card>(), currentArchetypeDeckGuid != Guid.Empty); currentArchetypeDeckGuid = Guid.Empty; } } }