Example #1
0
 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;
             }
         }
     }
 }
Example #2
0
        /// <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);
        }
Example #4
0
        /// <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);
        }
Example #7
0
        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);
        }
Example #8
0
 public bool BelongsToDeckVerion(Deck deck) => PlayerDeckVersion == deck.Version ||
 (HasHearthStatsDeckVersionId && HearthStatsDeckVersionId == deck.HearthStatsDeckVersionId) ||
 (!HasHearthStatsDeckVersionId && HasHearthStatsDeckId && HearthStatsDeckId == deck.HearthStatsId) ||
 !IsAssociatedWithDeckVersion && deck.Version == new SerializableVersion(1, 0);
Example #9
0
 public void SetPlayerCards(Deck deck, List <Card> revealedCards) => SetPlayerCards(deck?.Cards, revealedCards);
Example #10
0
 public bool BelongsToDeckVerion(Deck deck) => PlayerDeckVersion == deck.Version ||
 !IsAssociatedWithDeckVersion && deck.Version == new SerializableVersion(1, 0);
Example #11
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);
Example #14
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;
                }
            }
        }