using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Runtime.Serialization; using System.Xml; using DominionBase.Cards; using DominionBase.Piles; namespace Dominion.NET_WPF { [DataContract] public class Statistics { [DataMember] public Dictionary GlobalStatistics { get; internal set; } = new Dictionary(); public void Add(DominionBase.IGame game) { Add(game, null); } public void Add(DominionBase.IGame game, DominionBase.Players.Player player) { // Make sure it exists if (!GlobalStatistics.ContainsKey(game.Players.Count)) GlobalStatistics[game.Players.Count] = new NPlayersStatistics(game.Players.Count); // Add the game to the correct Statistics section GlobalStatistics[game.Players.Count].Add(game, player); } private static string Filename => Path.Combine(DominionBase.Utilities.Application.ApplicationPath, "stats.xml"); public void Save() { try { // TODO : This is not to be used for this release -- still some pondering to do. return; var serializer = new DataContractSerializer(typeof(Statistics), new Type[] { }); using (var swStatistics = new StreamWriter(Filename)) { using (var writer = new XmlTextWriter(swStatistics)) { writer.Formatting = Formatting.Indented; serializer.WriteObject(writer, this); writer.Flush(); } } //XmlSerializer xsStatistics = new XmlSerializer(typeof(Statistics), new Type[] { }); //StreamWriter swStatistics = new StreamWriter(Statistics.Filename); //xsStatistics.Serialize(swStatistics, this); //swStatistics.Close(); } catch (IOException) { } } public static Statistics Load() { Statistics statistics = null; try { // TODO : This is not to be used for this release -- still some pondering to do. return new Statistics(); var serializer = new DataContractSerializer(typeof(Statistics), new Type[] { }); using (var fs = new FileStream(Filename, FileMode.Open)) { using (var reader = XmlDictionaryReader.CreateTextReader(fs, new XmlDictionaryReaderQuotas())) { while (reader.Read()) { switch (reader.NodeType) { case XmlNodeType.Element: if (serializer.IsStartObject(reader)) { statistics = (Statistics)serializer.ReadObject(reader); } break; } } } } //using (StreamWriter swStatistics = new StreamWriter(Statistics.Filename)) //{ // using (XmlTextWriter writer = new XmlTextWriter(swStatistics)) // { // writer.Formatting = Formatting.Indented; // serializer.WriteObject(writer, this); // writer.Flush(); // } //} //XmlSerializer mySerializer = new XmlSerializer(typeof(Statistics), new Type[] { }); //FileStream myFileStream = new FileStream(Statistics.Filename, FileMode.Open); //statistics = (Statistics)mySerializer.Deserialize(myFileStream); } catch { statistics = new Statistics(); } return statistics; } } [DataContract] public class NPlayersStatistics { public NPlayersStatistics() { } public NPlayersStatistics(int numberOfPlayers) { NumberOfPlayers = numberOfPlayers; } [DataMember] public int NumberOfPlayers { get; internal set; } = 1; [DataMember] public int GamesPlayed { get; internal set; } [DataMember] public int GamesWon { get; internal set; } [DataMember] public int WinStreak { get; internal set; } [DataMember] public int LossStreak { get; internal set; } [DataMember] public int LongestWinStreak { get; internal set; } [DataMember] public int LongestLossStreak { get; internal set; } [DataMember] public int LowestScore { get; internal set; } = int.MaxValue; [DataMember] public int HighestScore { get; internal set; } = int.MinValue; [DataMember] private long TotalScore { get; set; } public double AverageScore { get { if (GamesPlayed == 0) return 0d; return (double)TotalScore / GamesPlayed; } } [DataMember] public Dictionary WinningCardCounts { get; set; } = new Dictionary(); [DataMember] public Dictionary WinningHumanCardCounts { get; set; } = new Dictionary(); public void Add(DominionBase.IGame game, DominionBase.Players.Player player) { if (game.Players.Count != NumberOfPlayers) throw new Exception("Incorrect number of players in game!"); if (player != null) { if (!game.Players.Contains(player)) throw new Exception("Player not found in game!"); // One more game played! GamesPlayed++; // If the game actually finished properly, instead of being aborted if (game.State == DominionBase.GameState.Ended) { // Player won (or at least tied in winning) if (game.Winners.Contains(player)) GamesWon++; if (player.VictoryPoints < LowestScore) LowestScore = player.VictoryPoints; if (player.VictoryPoints > HighestScore) HighestScore = player.VictoryPoints; TotalScore += player.VictoryPoints; } } if (game.State == DominionBase.GameState.Ended) { foreach (var winner in game.Winners) { // Since properly-used cards like Feast, Mining Village, & Embargo never end up in a player's hand, // we need to count how many of the cards were played during the game and add them to the player's // total as well as the total number of cards in the player's hand var trashedCards = game.TurnsTaken.Where(t => t.Player == winner). SelectMany( t => t.CardsPlayed.Where(c => c is DominionBase.Cards.Base.Feast || c is DominionBase.Cards.Seaside.Embargo || c is DominionBase.Cards.Seaside2ndEdition.Embargo || c is DominionBase.Cards.Seaside2019Errata.Embargo || (c is DominionBase.Cards.Intrigue.MiningVillage && t.CardsTrashed.Contains(c)) || (c is DominionBase.Cards.Intrigue2ndEdition.MiningVillage && t.CardsTrashed.Contains(c)) || (c is DominionBase.Cards.Cornucopia.HornOfPlenty && t.CardsTrashed.Contains(c)) || (c is DominionBase.Cards.Cornucopia2ndEdition.HornOfPlenty && t.CardsTrashed.Contains(c)) ) ); var trashedCardsList = trashedCards as IList ?? trashedCards.ToList(); var handCount = winner.Hand.Count + trashedCardsList.Count; foreach (var tableable in game.Table.TableEntities.Values) { var supply = (Supply) tableable; var type = supply.Type; var typeKey = type.AssemblyQualifiedName; var typeCount = winner.Hand.Count(c => c.Type == type) + trashedCardsList.Count(c => c.Type == type); if (!WinningCardCounts.ContainsKey(typeKey)) WinningCardCounts[typeKey] = new CardStatistics(type); WinningCardCounts[typeKey].Add(typeCount, handCount); if (winner.PlayerType == DominionBase.Players.PlayerType.Human) { if (!WinningHumanCardCounts.ContainsKey(typeKey)) WinningHumanCardCounts[typeKey] = new CardStatistics(type); WinningHumanCardCounts[typeKey].Add(typeCount, handCount); } } } } } } [DataContract] public class CardStatistics { public CardStatistics() { } public CardStatistics(Type type) { Type = type; } public CardStatistics(Type type, int count, int outOfCount) : this(type) { Add(count, outOfCount); } [IgnoreDataMember] public Type Type { get; internal set; } [DataMember] public string AssemblyQualifiedName { get { return Type == null ? string.Empty : Type.AssemblyQualifiedName; } internal set { Type = Type.GetType(value); } } [DataMember] public int Count { get; internal set; } [DataMember] public int OutOfCount { get; internal set; } [DataMember] public int GameCount { get; internal set; } public double CardFrequency => (double)Count / OutOfCount; public void Add(int count, int outOfCount) { Count += count; OutOfCount += outOfCount; GameCount++; } public override string ToString() { return $"Card: {Type.Name} :: {Count}/{OutOfCount} ({CardFrequency:0.0%}) in {GameCount} games"; } } }