Пример #1
0
 public List <float> PredictOutcome(List <ICollection <Account> > teams)
 {
     return(teams.Select(t =>
                         SetupGame(t.Select(x => x.AccountID).ToList(),
                                   teams.Where(t2 => !t2.Equals(t)).SelectMany(t2 => t2.Select(x => x.AccountID)).ToList(),
                                   true,
                                   RatingSystems.ConvertDateToDays(DateTime.Now),
                                   -1
                                   ).getBlackWinProbability() * 2 / teams.Count).ToList());
 }
Пример #2
0
 public List <float> PredictOutcome(IEnumerable <IEnumerable <Account> > teams, DateTime time)
 {
     return(teams.Select(t =>
                         SetupGame(t.Select(x => RatingSystems.GetRatingId(x.AccountID)).ToList(),
                                   teams.Where(t2 => !t2.Equals(t)).SelectMany(t2 => t2.Select(x => RatingSystems.GetRatingId(x.AccountID))).ToList(),
                                   true,
                                   RatingSystems.ConvertDateToDays(time),
                                   -1
                                   ).getBlackWinProbability() * 2 / teams.Count()).ToList());
 }
Пример #3
0
        public void ProcessBattle(SpringBattle battle, bool removeBattle = false)
        {
            ICollection <int> winners = battle.SpringBattlePlayers.Where(p => p.IsInVictoryTeam && !p.IsSpectator).Select(p => RatingSystems.GetRatingId(p.AccountID)).ToList();
            ICollection <int> losers  = battle.SpringBattlePlayers.Where(p => !p.IsInVictoryTeam && !p.IsSpectator).Select(p => RatingSystems.GetRatingId(p.AccountID)).ToList();
            int date = RatingSystems.ConvertDateToDays(battle.StartTime);

            if (removeBattle)
            {
                if (ProcessedBattles.Contains(battle.SpringBattleID) && RatingSystems.Initialized)
                {
                    Trace.TraceInformation("WHR " + category + " removing battle " + battle.SpringBattleID + " from " + battle.StartTime);
                    var game = SetupGame(losers, winners, false, date, battle.SpringBattleID);
                    losers.Union(winners).Select(x => getPlayerById(x)).ForEach(x => x.RemoveGame(game));
                    ProcessedBattles.Remove(battle.SpringBattleID);
                    battlesRegistered--;
                    latestBattle = battle;
                    Trace.TraceInformation(battlesRegistered + " battles registered for WHR " + category + ", latest Battle: " + battle.SpringBattleID);
                    UpdateRatings();
                }
                return;
            }

            if (winners.Count > 0 && losers.Count > 0 && winners.Intersect(losers).Count() == 0)
            {
                if (ProcessedBattles.Contains(battle.SpringBattleID))
                {
                    return;
                }

                battlesRegistered++;
                ProcessedBattles.Add(battle.SpringBattleID);

                if (firstBattle == null)
                {
                    firstBattle = battle;
                }
                latestBattle = battle;
                if (date > RatingSystems.ConvertDateToDays(DateTime.UtcNow))
                {
                    Trace.TraceWarning("WHR " + category + ": Tried to register battle " + battle.SpringBattleID + " which is from the future " + (date) + " > " + RatingSystems.ConvertDateToDays(DateTime.UtcNow));
                }
                else
                {
                    createGame(losers, winners, false, date, battle.SpringBattleID);
                    if (RatingSystems.Initialized)
                    {
                        lastBattleRanked = true;
                        Trace.TraceInformation(battlesRegistered + " battles registered for WHR " + category + ", latest Battle: " + battle.SpringBattleID);
                        UpdateRatings();
                    }
                }
            }
        }
Пример #4
0
        public List <float> PredictOutcome(IEnumerable <IEnumerable <Account> > teams, DateTime time)
        {
            var predictions = teams.Select(t =>
                                           SetupGame(t.Select(x => (x.AccountID)).Distinct().ToList(),
                                                     teams.Where(t2 => !t2.Equals(t)).Select(t2 => (ICollection <int>)t2.Select(x => (x.AccountID)).Distinct().ToList()).ToList(),
                                                     RatingSystems.ConvertDateToDays(time),
                                                     -1,
                                                     true
                                                     ).GetWinProbability()).ToList();

            return(predictions);
        }
Пример #5
0
        public void ProcessBattle(SpringBattle battle)
        {
            latestBattle = battle;
            ICollection <int> winners = battle.SpringBattlePlayers.Where(p => p.IsInVictoryTeam && !p.IsSpectator).Select(p => p.AccountID).ToList();
            ICollection <int> losers  = battle.SpringBattlePlayers.Where(p => !p.IsInVictoryTeam && !p.IsSpectator).Select(p => p.AccountID).ToList();

            if (winners.Count > 0 && losers.Count > 0)
            {
                battlesRegistered++;
                createGame(losers, winners, false, RatingSystems.ConvertDateToDays(battle.StartTime), battle.SpringBattleID);
                if (RatingSystems.Initialized)
                {
                    Trace.TraceInformation(battlesRegistered + " battles registered for WHR");
                    UpdateRatings();
                }
            }
        }
Пример #6
0
        public float GetAverageRecentWinChance(int AccountID)
        {
            if (!players.ContainsKey((AccountID)))
            {
                return(0.4f);
            }
            var recentGames = players[AccountID].days
                              .Where(day => day.day >= RatingSystems.ConvertDateToDays(DateTime.UtcNow.AddDays(-1)))
                              .SelectMany(day => day.games.SelectMany(g => g))
                              .OrderByDescending(g => g.id)
                              .Take(5);
            float recentWinChance = 0.4f;

            if (recentGames.Count() > 0)
            {
                recentWinChance = recentGames.Select(x => x.winnerPlayers.Contains(players[AccountID]) ? x.GetWinProbability() : (1 - x.GetWinProbability())).Average();
            }
            //Trace.TraceInformation($"The {recentGames.Count()} most recent games for {AccountID} are {string.Join(", ", recentGames.Select(x => x.id))}, the average win chance is {recentWinChance}.");
            return(recentWinChance);
        }
Пример #7
0
        private void UpdateLadderRatings()
        {
            float avgElo    = 0;
            float weightSum = 0;
            float varSum    = 0;
            int   minDay    = RatingSystems.ConvertDateToDays(DateTime.UtcNow) - GlobalConst.LadderActivityDays;

            for (int i = 0; i < days.Count; i++)
            {
                if (days[i].day >= minDay)
                {
                    weightSum += days[i].weight;
                    varSum    += days[i].weight * days[i].GetEloStdev() * days[i].GetEloStdev();
                    avgElo    += days[i].GetElo() * days[i].weight;
                }
            }
            avgElo        /= weightSum;
            varSum        /= weightSum;
            this.avgEloVar = varSum;
            this.avgElo    = avgElo;
        }
Пример #8
0
 public PlayerRating(int Rank, float Percentile, float Elo, float Uncertainty) : this(Rank, Percentile, Elo, Uncertainty, RatingSystems.ConvertDateToDays(DateTime.Now))
 {
 }
Пример #9
0
        //private


        //Runs in O(N log(N)) for all players
        private void UpdateRankings(IEnumerable <Player> players)
        {
            try
            {
                int currentDay = RatingSystems.ConvertDateToDays(DateTime.UtcNow);
                foreach (var p in players)
                {
                    if (p.days.Count == 0)
                    {
                        Trace.TraceError("WHR " + category + " has invalid player " + p.id + " with no days(games)");
                        continue;
                    }
                    float elo             = p.days.Last().getElo() + RatingOffset;
                    float lastUncertainty = p.days.Last().uncertainty * 100;
                    int   lastDay         = p.days.Last().day;
                    playerRatings[p.id] = new PlayerRating(int.MaxValue, 1, elo, lastUncertainty, lastDay, currentDay);
                    float rating = -playerRatings[p.id].Elo + 0.001f * (float)rand.NextDouble();
                    if (playerKeys.ContainsKey(p.id))
                    {
                        sortedPlayers.Remove(playerKeys[p.id]);
                    }
                    playerKeys[p.id]      = rating;
                    sortedPlayers[rating] = p.id;
                }
                float[] playerUncertainties = new float[playerRatings.Count];
                int     index = 0;
                float   DynamicMaxUncertainty = GlobalConst.MinimumDynamicMaxLadderUncertainty;
                int     maxAge = GlobalConst.LadderActivityDays;
                foreach (var pair in playerRatings)
                {
                    if (currentDay - pair.Value.LastGameDate > maxAge)
                    {
                        playerUncertainties[index++] = 9999 + index; //don't use infinity because i'm doing shady floating point things
                    }
                    else
                    {
                        playerUncertainties[index++] = (float)pair.Value.Uncertainty;
                    }
                }
                Array.Sort(playerUncertainties);
                DynamicMaxUncertainty = Math.Max(DynamicMaxUncertainty, playerUncertainties[Math.Min(playerUncertainties.Length, GlobalConst.LadderSize) - 1] + 0.01f);
                int          activePlayers         = Math.Max(1, ~Array.BinarySearch(playerUncertainties, DynamicMaxUncertainty));
                int          rank                  = 0;
                List <int>   newTopPlayers         = new List <int>();
                int          matched               = 0;
                List <float> newPercentileBrackets = new List <float>();
                newPercentileBrackets.Add(3000);
                float   percentile;
                float[] percentilesRev = Ranks.Percentiles.Reverse().ToArray();
                foreach (var pair in sortedPlayers)
                {
                    if (playerRatings[pair.Value].Uncertainty <= DynamicMaxUncertainty && currentDay - playerRatings[pair.Value].LastGameDate <= maxAge)
                    {
                        newTopPlayers.Add(pair.Value);
                        if (rank == matched && rank < topPlayers.Count && topPlayers[rank] == pair.Value)
                        {
                            matched++;
                        }
                        rank++;
                        percentile = (float)rank / activePlayers;
                        if (newPercentileBrackets.Count <= Ranks.Percentiles.Length && percentile > percentilesRev[newPercentileBrackets.Count - 1])
                        {
                            newPercentileBrackets.Add(playerRatings[pair.Value].Elo);
                        }
                        playerRatings[pair.Value].ApplyLadderUpdate(rank, percentile, currentDay);
                    }
                    else if (playerRatings[pair.Value].Rank < int.MaxValue)
                    {
                        playerRatings[pair.Value].ApplyLadderUpdate(int.MaxValue, 1, currentDay);
                    }
                }
                this.activePlayers = rank;
                newPercentileBrackets.Add(0);
                PercentileBrackets = newPercentileBrackets.Select(x => x).Reverse().ToArray();
                topPlayers         = newTopPlayers;
                laddersCache       = new List <Account>();
                Trace.TraceInformation("WHR " + category + " Ladders updated with " + topPlayers.Count + "/" + this.players.Count + " entries, max uncertainty selected: " + DynamicMaxUncertainty + " brackets are now: " + string.Join(", ", PercentileBrackets));

                var playerIds = players.Select(x => x.id).ToList();
                if (playerIds.Count() < 100)
                {
                    SaveToDB(playerIds);
                }
                else
                {
                    SaveToDB();
                }

                //check for rank updates

                List <int> playersWithRatingChange = new List <int>();
                using (var db = new ZkDataContext())
                {
                    var lastBattlePlayers = db.SpringBattlePlayers.Where(p => p.SpringBattleID == latestBattle.SpringBattleID && !p.IsSpectator).Include(x => x.Account).ToList();
                    if (latestBattle.GetRatingCategory() == category && lastBattleRanked)
                    {
                        lastBattleRanked = false;
                        lastBattlePlayers.Where(p => playerOldRatings.ContainsKey(RatingSystems.GetRatingId(p.AccountID)) && !p.EloChange.HasValue).ForEach(p =>
                        {
                            p.EloChange = playerRatings[RatingSystems.GetRatingId(p.AccountID)].RealElo - playerOldRatings[RatingSystems.GetRatingId(p.AccountID)].RealElo;
                        });
                        var updatedRanks = lastBattlePlayers.Where(p => Ranks.UpdateRank(p.Account, p.IsInVictoryTeam, !p.IsInVictoryTeam, db)).Select(x => x.Account).ToList();
                        updatedRanks.ForEach(p => db.Entry(p).State = EntityState.Modified);
                        playersWithRatingChange = lastBattlePlayers.Select(x => x.AccountID).ToList();
                    }
                    db.SpringBattlePlayers.Where(p => p.SpringBattleID == latestBattle.SpringBattleID && !p.IsSpectator).ToList().ForEach(x => playerOldRatings[RatingSystems.GetRatingId(x.AccountID)] = playerRatings[RatingSystems.GetRatingId(x.AccountID)]);
                    db.SaveChanges();
                }

                if (latestBattle.GetRatingCategory() == category && lastBattleRanked)
                {
                    //Publish new results only after saving new stats to db.
                    RatingsUpdated(this, new RatingUpdate()
                    {
                        affectedPlayers = playersWithRatingChange
                    });
                }


                //check for topX updates
                GetTopPlayers(GlobalConst.LadderSize);
                foreach (var listener in topPlayersUpdateListeners)
                {
                    if (matched < listener.Value)
                    {
                        listener.Key.TopPlayersUpdated(GetTopPlayers(listener.Value));
                    }
                }
            }
            catch (Exception ex)
            {
                string dbg = "WHR " + category + ": Failed to update rankings " + ex + "\nPlayers: ";
                foreach (var p in players)
                {
                    dbg += p.id + " (" + p.days.Count + " days), ";
                }
                Trace.TraceError(dbg);
            }
        }
        //private


        //Runs in O(N log(N)) for all players
        private void UpdateRankings(IEnumerable <Player> players)
        {
            try
            {
                int currentDay = RatingSystems.ConvertDateToDays(DateTime.UtcNow);
                foreach (var p in players)
                {
                    if (p.days.Count == 0)
                    {
                        Trace.TraceError("WHR " + category + " has invalid player " + p.id + " with no days(games)");
                        continue;
                    }
                    float elo             = p.days.Last().getElo() + RatingOffset;
                    float lastUncertainty = p.days.Last().uncertainty * 100;
                    int   lastDay         = p.days.Last().day;
                    playerRatings[p.id] = new PlayerRating(int.MaxValue, 1, elo, lastUncertainty, lastDay, currentDay);
                    float rating = -playerRatings[p.id].Elo + 0.001f * (float)rand.NextDouble();
                    if (playerKeys.ContainsKey(p.id))
                    {
                        sortedPlayers.Remove(playerKeys[p.id]);
                    }
                    playerKeys[p.id]      = rating;
                    sortedPlayers[rating] = p.id;
                }
                float[] playerUncertainties = new float[playerRatings.Count];
                int     index = 0;
                float   DynamicMaxUncertainty = GlobalConst.MinimumDynamicMaxLadderUncertainty;
                int     maxAge = GlobalConst.LadderActivityDays;
                foreach (var pair in playerRatings)
                {
                    if (currentDay - pair.Value.LastGameDate > maxAge)
                    {
                        playerUncertainties[index++] = 9999 + index; //don't use infinity because i'm doing shady floating point things
                    }
                    else
                    {
                        playerUncertainties[index++] = (float)pair.Value.Uncertainty;
                    }
                }
                Array.Sort(playerUncertainties);
                DynamicMaxUncertainty = Math.Max(DynamicMaxUncertainty, playerUncertainties[Math.Min(playerUncertainties.Length, GlobalConst.LadderSize) - 1] + 0.01f);
                int          activePlayers         = Math.Max(1, ~Array.BinarySearch(playerUncertainties, DynamicMaxUncertainty));
                int          rank                  = 0;
                List <int>   newTopPlayers         = new List <int>();
                int          matched               = 0;
                List <float> newPercentileBrackets = new List <float>();
                newPercentileBrackets.Add(3000);
                float   percentile;
                float[] percentilesRev = Ranks.Percentiles.Reverse().ToArray();
                foreach (var pair in sortedPlayers)
                {
                    if (playerRatings[pair.Value].Uncertainty <= DynamicMaxUncertainty && currentDay - playerRatings[pair.Value].LastGameDate <= maxAge)
                    {
                        newTopPlayers.Add(pair.Value);
                        if (rank == matched && rank < topPlayers.Count && topPlayers[rank] == pair.Value)
                        {
                            matched++;
                        }
                        rank++;
                        percentile = (float)rank / activePlayers;
                        if (newPercentileBrackets.Count <= Ranks.Percentiles.Length && percentile > percentilesRev[newPercentileBrackets.Count - 1])
                        {
                            newPercentileBrackets.Add(playerRatings[pair.Value].RealElo);
                        }
                        playerRatings[pair.Value].ApplyLadderUpdate(rank, percentile, currentDay);
                    }
                    else if (playerRatings[pair.Value].Rank < int.MaxValue)
                    {
                        playerRatings[pair.Value].ApplyLadderUpdate(int.MaxValue, 1, currentDay);
                    }
                }
                this.activePlayers = rank;
                newPercentileBrackets.Add(0);
                PercentileBrackets = newPercentileBrackets.Select(x => x).Reverse().ToArray();
                topPlayers         = newTopPlayers;
                laddersCache       = new List <Account>();
                Trace.TraceInformation("WHR " + category + " Ladders updated with " + topPlayers.Count + "/" + this.players.Count + " entries, max uncertainty selected: " + DynamicMaxUncertainty);

                var playerIds = players.Select(x => x.id).ToList();
                if (playerIds.Count() < 100)
                {
                    SaveToDB(playerIds);
                }
                else
                {
                    SaveToDB();
                }

                //check for topX updates
                GetTopPlayers(GlobalConst.LadderSize);
                foreach (var listener in topPlayersUpdateListeners)
                {
                    if (matched < listener.Value)
                    {
                        listener.Key.TopPlayersUpdated(GetTopPlayers(listener.Value));
                    }
                }
                RatingsUpdated(this, new RatingUpdate()
                {
                    affectedPlayers = players.Select(x => x.id)
                });
            }
            catch (Exception ex)
            {
                string dbg = "WHR " + category + ": Failed to update rankings " + ex + "\nPlayers: ";
                foreach (var p in players)
                {
                    dbg += p.id + " (" + p.days.Count + " days), ";
                }
                Trace.TraceError(dbg);
            }
        }
Пример #11
0
        //private


        //Runs in O(N log(N)) for all players
        private void UpdateRankings(IEnumerable <Player> players)
        {
            try
            {
                Dictionary <int, float> oldRatings = new Dictionary <int, float>();

                //check for ladder elo updates
                using (var db = new ZkDataContext())
                {
                    var battleIDs         = pendingDebriefings.Keys.ToList();
                    var lastBattlePlayers = db.SpringBattlePlayers.Where(p => battleIDs.Contains(p.SpringBattleID) && !p.IsSpectator).Include(x => x.Account).ToList();
                    oldRatings = lastBattlePlayers.ToDictionary(p => p.AccountID, p => playerRatings[p.AccountID].LadderElo);
                    lastBattlePlayers.ForEach(p => playerRatings[p.AccountID].LadderElo = Ranks.UpdateLadderRating(p.Account, category, this.players[p.AccountID].avgElo + RatingOffset, p.IsInVictoryTeam, !p.IsInVictoryTeam, db));
                }

                //update ladders
                int currentDay  = RatingSystems.ConvertDateToDays(DateTime.UtcNow);
                int playerCount = 0;
                using (var db = new ZkDataContext())
                {
                    foreach (var p in players)
                    {
                        if (p.days.Count == 0)
                        {
                            Trace.TraceError("WHR " + category + " has invalid player " + p.id + " with no days(games)");
                            continue;
                        }
                        float elo = p.days.Last().GetElo() + RatingOffset;
                        float lastNaturalRatingVar = p.avgEloVar * GlobalConst.EloToNaturalRatingMultiplierSquared;
                        var   lastDay = p.days.Last();
                        float ladderElo;
                        if (playerRatings.ContainsKey(p.id))
                        {
                            ladderElo = playerRatings[p.id].LadderElo;
                        }
                        else
                        {
                            ladderElo = (float?)db.AccountRatings.Where(x => x.AccountID == p.id && x.RatingCategory == category).FirstOrDefault()?.LadderElo ?? DefaultRating.LadderElo;
                        }
                        playerRatings[p.id] = new PlayerRating(int.MaxValue, 1, elo, lastNaturalRatingVar, GlobalConst.NaturalRatingVariancePerDay(lastDay.totalWeight), lastDay.day, currentDay, ladderElo, !float.IsNaN(p.avgElo));
                        float rating = -playerRatings[p.id].LadderElo + 0.001f * (float)rand.NextDouble();
                        if (playerKeys.ContainsKey(p.id))
                        {
                            sortedPlayers.Remove(playerKeys[p.id]);
                        }
                        playerKeys[p.id]      = rating;
                        sortedPlayers[rating] = p.id;
                        if (playerRatings[p.id].Ranked)
                        {
                            playerCount++;
                        }
                    }
                }
                this.activePlayers = playerCount;
                int          rank                  = 0;
                List <int>   newTopPlayers         = new List <int>();
                int          matched               = 0;
                List <float> newPercentileBrackets = new List <float>();
                newPercentileBrackets.Add(playerRatings[sortedPlayers.First().Value].LadderElo + 420);
                float   percentile;
                float[] percentilesRev = Ranks.Percentiles.Reverse().ToArray();
                foreach (var pair in sortedPlayers)
                {
                    if (playerRatings[pair.Value].Ranked)
                    {
                        newTopPlayers.Add(pair.Value);
                        if (rank == matched && rank < topPlayers.Count && topPlayers[rank] == pair.Value)
                        {
                            matched++;
                        }
                        rank++;
                        percentile = (float)rank / activePlayers;
                        if (newPercentileBrackets.Count <= Ranks.Percentiles.Length && percentile > percentilesRev[newPercentileBrackets.Count - 1])
                        {
                            newPercentileBrackets.Add(playerRatings[pair.Value].LadderElo);
                        }
                        playerRatings[pair.Value].ApplyLadderUpdate(rank, percentile, currentDay, true);
                    }
                    else if (playerRatings[pair.Value].Rank < int.MaxValue)
                    {
                        playerRatings[pair.Value].ApplyLadderUpdate(int.MaxValue, 1, currentDay, false);
                    }
                }
                newPercentileBrackets.Add(newPercentileBrackets.Last() - 420);
                PercentileBrackets = newPercentileBrackets.Select(x => x).Reverse().ToArray();
                topPlayers         = newTopPlayers;
                laddersCache       = new List <Account>();
                Trace.TraceInformation("WHR " + category + " Ladders updated with " + topPlayers.Count + "/" + this.players.Count + " entries. Brackets are now: " + string.Join(", ", PercentileBrackets));

                var playerIds = players.Select(x => x.id).ToList();
                if (playerIds.Count() < 100)
                {
                    SaveToDB(playerIds);
                }
                else
                {
                    SaveToDB();
                }

                //check for rank updates

                if (pendingDebriefings.Any())
                {
                    List <int>                playersWithRatingChange = new List <int>();
                    Dictionary <int, int>     oldRanks         = new Dictionary <int, int>();
                    Dictionary <int, Account> updatedRanks     = new Dictionary <int, Account>();
                    Dictionary <int, Account> involvedAccounts = new Dictionary <int, Account>();
                    Trace.TraceInformation("WHR Filling in Debriefings for Battles: " + pendingDebriefings.Keys.Select(x => "B" + x).StringJoin());
                    using (var db = new ZkDataContext())
                    {
                        var battleIDs         = pendingDebriefings.Keys.ToList();
                        var lastBattlePlayers = db.SpringBattlePlayers.Where(p => battleIDs.Contains(p.SpringBattleID) && !p.IsSpectator).Include(x => x.Account).ToList();
                        involvedAccounts = lastBattlePlayers.ToDictionary(p => p.AccountID, p => p.Account);
                        Trace.TraceInformation("WHR Debriefing players: " + involvedAccounts.Values.Select(x => x.Name).StringJoin());
                        oldRanks     = lastBattlePlayers.ToDictionary(p => p.AccountID, p => p.Account.Rank);
                        updatedRanks = lastBattlePlayers.Where(p => Ranks.UpdateRank(p.Account, p.IsInVictoryTeam, !p.IsInVictoryTeam, db)).Select(x => x.Account).ToDictionary(p => p.AccountID, p => p);
                        updatedRanks.Values.ForEach(p => db.Entry(p).State = EntityState.Modified);
                        playersWithRatingChange = lastBattlePlayers.Select(x => x.AccountID).ToList();

                        lastBattlePlayers.Where(p => playerOldRatings.ContainsKey(RatingSystems.GetRatingId(p.AccountID)) && !p.EloChange.HasValue).ForEach(p =>
                        {
                            //p.EloChange = playerRatings[RatingSystems.GetRatingId(p.AccountID)].RealElo - playerOldRatings[RatingSystems.GetRatingId(p.AccountID)].RealElo;
                            p.EloChange = playerRatings[p.AccountID].LadderElo - oldRatings[p.AccountID];
                        });

                        db.SpringBattlePlayers.Where(p => battleIDs.Contains(p.SpringBattleID) && !p.IsSpectator).ToList().ForEach(x => playerOldRatings[RatingSystems.GetRatingId(x.AccountID)] = playerRatings[RatingSystems.GetRatingId(x.AccountID)]);
                        db.SaveChanges();
                    }
                    //Publish new results only after saving new stats to db.
                    pendingDebriefings.ForEach(pair =>
                    {
                        pair.Value.partialDebriefing.DebriefingUsers.Values.ForEach(user => {
                            try
                            {
                                user.EloChange  = playerRatings[user.AccountID].LadderElo - oldRatings[user.AccountID];
                                user.IsRankup   = updatedRanks.ContainsKey(user.AccountID) && oldRanks[user.AccountID] < updatedRanks[user.AccountID].Rank;
                                user.IsRankdown = updatedRanks.ContainsKey(user.AccountID) && oldRanks[user.AccountID] > updatedRanks[user.AccountID].Rank;
                                var prog        = Ranks.GetRankProgress(involvedAccounts[user.AccountID], this);
                                if (prog == null)
                                {
                                    Trace.TraceWarning("User " + user.AccountID + " is wrongfully unranked");
                                }
                                user.NextRankElo = prog.RankCeilElo;
                                user.PrevRankElo = prog.RankFloorElo;
                                user.NewElo      = prog.CurrentElo;
                            }
                            catch (Exception ex)
                            {
                                Trace.TraceError("Unable to complete debriefing for user " + user.AccountID + ": " + ex);
                            }
                        });
                        pair.Value.partialDebriefing.RatingCategory = category.ToString();
                        pair.Value.debriefingConsumer.Invoke(pair.Value.partialDebriefing);
                    });
                    RatingsUpdated(this, new RatingUpdate()
                    {
                        affectedPlayers = playersWithRatingChange
                    });
                    pendingDebriefings.Clear();
                }


                //check for topX updates
                GetTopPlayers(GlobalConst.LadderSize);
                foreach (var listener in topPlayersUpdateListeners)
                {
                    if (matched < listener.Value)
                    {
                        listener.Key.TopPlayersUpdated(GetTopPlayers(listener.Value));
                    }
                }
            }
            catch (Exception ex)
            {
                string dbg = "WHR " + category + ": Failed to update rankings " + ex + "\nPlayers: ";
                foreach (var p in players)
                {
                    dbg += p.id + " (" + p.days.Count + " days), ";
                }
                Trace.TraceError(dbg);
            }
        }
Пример #12
0
        public void ProcessBattle(SpringBattle battle)
        {
            ICollection <int> winners = battle.SpringBattlePlayers.Where(p => p.IsInVictoryTeam && !p.IsSpectator).Select(p => RatingSystems.GetRatingId(p.AccountID)).Distinct().ToList();
            ICollection <int> losers  = battle.SpringBattlePlayers.Where(p => !p.IsInVictoryTeam && !p.IsSpectator).Select(p => RatingSystems.GetRatingId(p.AccountID)).Distinct().ToList();

            int date = RatingSystems.ConvertDateToDays(battle.StartTime);

            if (RatingSystems.Initialized)
            {
                if (winners.Intersect(losers).Any())
                {
                    Trace.TraceWarning("WHR B" + battle.SpringBattleID + " has winner loser intersection");
                }
                if (ProcessedBattles.Contains(battle.SpringBattleID))
                {
                    Trace.TraceWarning("WHR B" + battle.SpringBattleID + " has already been processed");
                }
                if (winners.Count == 0)
                {
                    Trace.TraceWarning("WHR B" + battle.SpringBattleID + " has no winner");
                }
                if (losers.Count == 0)
                {
                    Trace.TraceWarning("WHR B" + battle.SpringBattleID + " has no loser");
                }
            }

            if (!winners.Intersect(losers).Any() && !ProcessedBattles.Contains(battle.SpringBattleID) && winners.Count > 0 && losers.Count > 0)
            {
                battlesRegistered++;
                ProcessedBattles.Add(battle.SpringBattleID);

                if (date > RatingSystems.ConvertDateToDays(DateTime.UtcNow))
                {
                    Trace.TraceWarning("WHR " + category + ": Tried to register battle " + battle.SpringBattleID + " which is from the future " + (date) + " > " + RatingSystems.ConvertDateToDays(DateTime.UtcNow));
                }
                else
                {
                    CreateGame(losers, winners, false, date, battle.SpringBattleID);
                    futureDebriefings.ForEach(u => pendingDebriefings.TryAdd(u.Key, u.Value));
                    futureDebriefings.Clear();

                    if (RatingSystems.Initialized)
                    {
                        Trace.TraceInformation(battlesRegistered + " battles registered for WHR " + category + ", latest Battle: " + battle.SpringBattleID);
                        UpdateRatings();
                    }
                }
            }
            else
            {
                PendingDebriefing debriefing;
                futureDebriefings.TryGetValue(battle.SpringBattleID, out debriefing);
                if (debriefing == null)
                {
                    pendingDebriefings.TryGetValue(battle.SpringBattleID, out debriefing);
                }
                if (debriefing != null)
                {
                    Trace.TraceWarning("Battle " + battle.SpringBattleID + " was processed before attaching pending report");
                    debriefing.debriefingConsumer.Invoke(debriefing.partialDebriefing);
                }
            }
        }
Пример #13
0
 public PlayerDay GetInternalRating(int accountID, DateTime time)
 {
     return(players[accountID].days.Find(x => x.day == RatingSystems.ConvertDateToDays(time)));
 }
Пример #14
0
        //private


        //Runs in O(N log(N)) for all players
        private void UpdateRankings(IEnumerable <Player> players)
        {
            var debriefings = new Dictionary <int, PendingDebriefing>(pendingDebriefings);
            int matched     = 0;

            try
            {
                //check for ladder elo updates
                using (var db = new ZkDataContext())
                {
                    var battleIDs = debriefings.Keys.ToList();
                    foreach (var battleId in debriefings.Keys)
                    {
                        List <SpringBattlePlayer> lastBattlePlayers = db.SpringBattlePlayers.Where(p => p.SpringBattleID == battleId && !p.IsSpectator).Include(x => x.Account).DistinctBy(x => x.AccountID).ToList();
                        Dictionary <int, float>   oldRatings        = lastBattlePlayers.ToDictionary(p => (p.AccountID), p => GetPlayerRating(p.AccountID).LadderElo);
                        lastBattlePlayers.Where(p => !playerRatings.ContainsKey((p.AccountID))).ForEach(p => playerRatings[(p.AccountID)] = new PlayerRating(DefaultRating));
                        Dictionary <int, float> winChances = db.SpringBattles.Where(p => p.SpringBattleID == battleId).First().GetAllyteamWinChances();
                        lastBattlePlayers.ForEach(p => {
                            float eloChange = (p.IsInVictoryTeam ? (1f - winChances[p.AllyNumber]) : (-winChances[p.AllyNumber])) * GlobalConst.LadderEloClassicEloK / lastBattlePlayers.Count(x => x.AllyNumber == p.AllyNumber);
                            playerRatings[p.AccountID].LadderElo = Ranks.UpdateLadderRating(p.Account, category, getPlayerById(p.AccountID).avgElo + RatingOffset, p.IsInVictoryTeam, !p.IsInVictoryTeam, eloChange, db);
                        });
                        lastBattlePlayers.Where(p => !p.EloChange.HasValue).ForEach(p =>
                        {
                            p.EloChange = playerRatings[(p.AccountID)].LadderElo - oldRatings[(p.AccountID)];
                            db.SpringBattlePlayers.Attach(p);
                            db.Entry(p).Property(x => x.EloChange).IsModified = true;
                        });
                        db.SaveChanges();
                    }
                }

                //update ladders
                int currentDay  = RatingSystems.ConvertDateToDays(DateTime.UtcNow);
                int playerCount = 0;
                using (var db = new ZkDataContext())
                {
                    foreach (var p in players)
                    {
                        if (p.days.Count == 0)
                        {
                            Trace.TraceError("WHR " + category + " has invalid player " + p.id + " with no days(games)");
                            continue;
                        }
                        float elo = p.days.Last().GetElo() + RatingOffset;
                        float lastNaturalRatingVar = p.days.Last().naturalRatingVariance;
                        var   lastDay = p.days.Last();
                        float ladderElo;
                        if (playerRatings.ContainsKey(p.id))
                        {
                            ladderElo = playerRatings[p.id].LadderElo;
                        }
                        else
                        {
                            ladderElo = (float?)db.AccountRatings.Where(x => x.AccountID == p.id && x.RatingCategory == category).FirstOrDefault()?.LadderElo ?? DefaultRating.LadderElo;
                        }
                        playerRatings[p.id] = new PlayerRating(int.MaxValue, 1, elo, lastNaturalRatingVar, GlobalConst.NaturalRatingVariancePerDay(lastDay.totalWeight), lastDay.day, currentDay, ladderElo, !float.IsNaN(p.avgElo));
                        float rating = -playerRatings[p.id].LadderElo;
                        if (playerKeys.ContainsKey(p.id))
                        {
                            sortedPlayers.Remove(playerKeys[p.id]);
                        }
                        while (sortedPlayers.ContainsKey(rating))
                        {
                            rating += 0.01f;
                        }
                        playerKeys[p.id]      = rating;
                        sortedPlayers[rating] = p.id;
                        if (playerRatings[p.id].Ranked)
                        {
                            playerCount++;
                        }
                    }
                }
                this.activePlayers = playerCount;
                int          rank                  = 0;
                List <int>   newTopPlayers         = new List <int>();
                List <float> newPercentileBrackets = new List <float>();
                newPercentileBrackets.Add(playerRatings[sortedPlayers.First().Value].LadderElo);
                float   percentile;
                float[] percentilesRev = Ranks.Percentiles.Reverse().ToArray();
                foreach (var pair in sortedPlayers)
                {
                    if (playerRatings[pair.Value].Ranked)
                    {
                        newTopPlayers.Add(pair.Value);
                        if (rank == matched && rank < topPlayers.Count && topPlayers[rank] == pair.Value)
                        {
                            matched++;
                        }
                        rank++;
                        percentile = (float)rank / activePlayers;
                        if (newPercentileBrackets.Count <= Ranks.Percentiles.Length && percentile > percentilesRev[newPercentileBrackets.Count - 1])
                        {
                            newPercentileBrackets.Add(playerRatings[pair.Value].LadderElo);
                        }
                        playerRatings[pair.Value].ApplyLadderUpdate(rank, percentile, currentDay, true);
                    }
                    else if (playerRatings[pair.Value].Rank < int.MaxValue)
                    {
                        playerRatings[pair.Value].ApplyLadderUpdate(int.MaxValue, 1, currentDay, false);
                    }
                }
                if (rank != playerCount)
                {
                    Trace.TraceWarning("WHR has " + playerCount + " active players, but " + rank + " sorted active players");
                }
                while (newPercentileBrackets.Count < Ranks.Percentiles.Length + 1)
                {
                    newPercentileBrackets.Add(playerRatings[sortedPlayers.Last().Value].LadderElo);
                }
                PercentileBrackets = newPercentileBrackets.Select(x => x).Reverse().ToArray();
                topPlayers         = newTopPlayers;
                laddersCache       = new List <Account>();
                Trace.TraceInformation("WHR " + category + " Ladders updated with " + topPlayers.Count + "/" + this.players.Count + " entries. Brackets are now: " + string.Join(", ", PercentileBrackets));

                var playerIds = players.Select(x => x.id).ToList();
                if (playerIds.Count() < 100)
                {
                    SaveToDB(playerIds);
                }
                else
                {
                    SaveToDB();
                }
            }
            catch (Exception ex)
            {
                Trace.TraceError("WHR " + category + ": Failed to update rankings: " + ex);
                PendingDebriefing discard2;
                debriefings.ForEach(x => pendingDebriefings.TryRemove(x.Key, out discard2));
                return;
            }
            try
            {
                //check for rank updates
                if (debriefings.Any())
                {
                    Trace.TraceInformation("WHR Filling in Debriefings for Battles: " + debriefings.Keys.Select(x => "B" + x).StringJoin());
                    using (var db = new ZkDataContext())
                    {
                        foreach (var battleId in debriefings.Keys)
                        {
                            List <SpringBattlePlayer>            lastBattlePlayers = db.SpringBattlePlayers.Where(p => p.SpringBattleID == battleId && !p.IsSpectator).Include(x => x.Account).DistinctBy(x => x.AccountID).ToList();
                            Dictionary <int, SpringBattlePlayer> involvedPlayers   = lastBattlePlayers.ToDictionary(p => p.AccountID, p => p);
                            Trace.TraceInformation("WHR Debriefing players: " + involvedPlayers.Values.Select(x => x.Account.Name).StringJoin());
                            Dictionary <int, int>     oldRanks     = lastBattlePlayers.ToDictionary(p => p.AccountID, p => p.Account.Rank);
                            Dictionary <int, Account> updatedRanks = lastBattlePlayers.Where(p => Ranks.UpdateRank(p.Account, p.IsInVictoryTeam, !p.IsInVictoryTeam, db)).Select(x => x.Account).ToDictionary(p => p.AccountID, p => p);
                            updatedRanks.Values.ForEach(p =>
                            {
                                db.Accounts.Attach(p);
                                db.Entry(p).Property(x => x.Rank).IsModified = true;
                            });
                            List <int> playersWithRatingChange = lastBattlePlayers.Select(x => x.AccountID).ToList();
                            db.SaveChanges();

                            //Publish new results only after saving new stats to db.
                            debriefings[battleId].partialDebriefing.DebriefingUsers.Values.ForEach(user =>
                            {
                                try
                                {
                                    user.EloChange  = involvedPlayers[user.AccountID].EloChange ?? 0;
                                    user.IsRankup   = updatedRanks.ContainsKey(user.AccountID) && oldRanks[user.AccountID] < updatedRanks[user.AccountID].Rank;
                                    user.IsRankdown = updatedRanks.ContainsKey(user.AccountID) && oldRanks[user.AccountID] > updatedRanks[user.AccountID].Rank;
                                    var prog        = Ranks.GetRankProgress(involvedPlayers[user.AccountID].Account, this);
                                    if (prog == null)
                                    {
                                        Trace.TraceWarning("User " + user.AccountID + " is wrongfully unranked");
                                    }
                                    user.NextRankElo = prog.RankCeilElo;
                                    user.PrevRankElo = prog.RankFloorElo;
                                    user.NewElo      = prog.CurrentElo;
                                }
                                catch (Exception ex)
                                {
                                    Trace.TraceError("Unable to complete debriefing for user " + user.AccountID + ": " + ex);
                                }
                            });
                            debriefings[battleId].partialDebriefing.RatingCategory = category.ToString();
                            debriefings[battleId].debriefingConsumer.Invoke(debriefings[battleId].partialDebriefing);
                            RatingsUpdated(this, new RatingUpdate()
                            {
                                affectedPlayers = playersWithRatingChange
                            });
                        }
                    }
                }

                //check for topX updates
                GetTopPlayers(GlobalConst.LadderSize);
                foreach (var listener in topPlayersUpdateListeners)
                {
                    if (matched < listener.Value)
                    {
                        listener.Key.TopPlayersUpdated(GetTopPlayers(listener.Value));
                    }
                }
            }
            catch (Exception ex)
            {
                Trace.TraceError("WHR " + category + ": Failed to process battles for rankings: " + ex);
            }
            PendingDebriefing discard;

            debriefings.ForEach(x => pendingDebriefings.TryRemove(x.Key, out discard));
        }