コード例 #1
0
        public string DebugPlayer(Account player)
        {
            if (!RatingSystems.Initialized)
            {
                return("");
            }
            if (!players.ContainsKey(RatingSystems.GetRatingId(player.AccountID)))
            {
                return("Unknown player");
            }
            string debugString = "";

            foreach (PlayerDay d in players[RatingSystems.GetRatingId(player.AccountID)].days)
            {
                debugString +=
                    d.day + ";" +
                    d.getElo() + ";" +
                    d.uncertainty * 100 + ";" +
                    d.wonGames.Select(g =>
                                      g.whitePlayers.Select(p => p.id.ToString()).Aggregate("", (x, y) => x + "," + y) + "/" +
                                      g.blackPlayers.Select(p => p.id.ToString()).Aggregate("", (x, y) => x + "," + y) + "/" +
                                      (g.blackWins ? "Second" : "First") + "/" +
                                      g.id
                                      ).Aggregate("", (x, y) => x + "|" + y) + "\r\n";
            }
            return(debugString);
        }
コード例 #2
0
        //optimized selector method for high player counts
        public List <Account> GetTopPlayersIn(int count, Dictionary <int, Account> accounts)
        {
            lock (updateLockInternal)
            {
                int            counter = 0;
                List <Account> retval  = new List <Account>();

                Account acc;
                foreach (var pair in sortedPlayers)
                {
                    if (!accounts.ContainsKey(pair.Value))
                    {
                        continue;
                    }
                    acc = accounts[pair.Value];
                    if (playerRatings[RatingSystems.GetRatingId(acc.AccountID)].Rank < int.MaxValue)
                    {
                        if (counter++ >= count)
                        {
                            break;
                        }
                        retval.Add(acc);
                    }
                }
                return(retval);
            }
        }
コード例 #3
0
        public static float GetRankProgress(Account acc)
        {
            float bestProgress = 0;
            bool  isActive     = false;

            foreach (var ratingSystem in RatingSystems.GetRatingSystems())
            {
                if (ratingSystem.GetActivePlayers() < 50)
                {
                    continue;
                }
                var rating = ratingSystem.GetPlayerRating(acc.AccountID);
                if (rating.Rank == int.MaxValue)
                {
                    continue;
                }
                isActive = true;
                var stdev     = Math.Min(10000, rating.Uncertainty);
                var bracket   = ratingSystem.GetPercentileBracket(acc.Rank);
                var rankCeil  = bracket.UpperEloLimit + stdev;
                var rankFloor = bracket.LowerEloLimit - stdev;
                bestProgress = Math.Max(bestProgress, Math.Min(1, (rating.RealElo - rankFloor) / (rankCeil - rankFloor)));
                //Trace.TraceInformation(acc.Name + ": bracket(" + bracket.LowerEloLimit + ", " + bracket.UpperEloLimit + ") requirements (" + rankFloor + ", " + rankCeil + ") current: " + rating.RealElo + " -> progress: " + bestProgress);
            }
            if (!isActive)
            {
                return(0.001f);
            }
            return(bestProgress);
        }
コード例 #4
0
        public List <Account> GetTopPlayers(int count, Func <Account, bool> selector)
        {
            lock (updateLockInternal)
            {
                int            counter = 0;
                List <Account> retval  = new List <Account>();

                using (ZkDataContext db = new ZkDataContext())
                {
                    foreach (var pair in sortedPlayers)
                    {
                        Account acc = db.Accounts
                                      .Where(a => (a.AccountID) == pair.Value)
                                      .Include(a => a.Clan)
                                      .Include(a => a.Faction)
                                      .FirstOrDefault();
                        if (playerRatings[RatingSystems.GetRatingId(acc.AccountID)].Rank < int.MaxValue && selector.Invoke(acc))
                        {
                            if (counter++ >= count)
                            {
                                break;
                            }
                            retval.Add(acc);
                        }
                    }
                }
                return(retval);
            }
        }
コード例 #5
0
 public Dictionary <DateTime, float> GetPlayerRatingHistory(int AccountID)
 {
     if (!players.ContainsKey(RatingSystems.GetRatingId(AccountID)))
     {
         return(new Dictionary <DateTime, float>());
     }
     return(players[RatingSystems.GetRatingId(AccountID)].days.ToDictionary(day => RatingSystems.ConvertDaysToDate(day.day), day => day.getElo() + RatingOffset));
 }
コード例 #6
0
 public Dictionary <DateTime, float> GetPlayerLadderRatingHistory(int AccountID)
 {
     if (!players.ContainsKey(RatingSystems.GetRatingId(AccountID)))
     {
         return(new Dictionary <DateTime, float>());
     }
     return(players[RatingSystems.GetRatingId(AccountID)].days.ToDictionary(day => RatingSystems.ConvertDaysToDate(day.day), day => day.GetElo() + RatingOffset - day.GetEloStdev() * GlobalConst.RatingConfidenceSigma));
 }
コード例 #7
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());
 }
コード例 #8
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());
 }
コード例 #9
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();
                    }
                }
            }
        }
コード例 #10
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);
        }
コード例 #11
0
 private void runIterations(int count)
 {
     for (int i = 0; i < count; i++)
     {
         runSingleIteration();
     }
     foreach (Player p in players.Values)
     {
         p.updateUncertainty();
     }
     RatingSystems.BackupToDB(this);
 }
コード例 #12
0
        public PlayerRating GetPlayerRating(int accountID)
        {
            if (!completelyInitialized)
            {
                return(cachedDbRatings.GetOrAdd(accountID,
                                                id =>
                {
                    using (var db = new ZkDataContext())
                        return db.AccountRatings.FirstOrDefault(x => x.AccountID == id && x.RatingCategory == category)
                        ?.ToPlayerRating() ?? DefaultRating;
                }));
            }

            return(playerRatings.ContainsKey(RatingSystems.GetRatingId(accountID)) ? playerRatings[RatingSystems.GetRatingId(accountID)] : DefaultRating);
        }
コード例 #13
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();
                }
            }
        }
コード例 #14
0
        public static float GetRankProgress(Account acc)
        {
            float bestProgress = 0;
            bool  isActive     = false;

            foreach (var ratingSystem in RatingSystems.GetRatingSystems())
            {
                var progress = GetRankProgress(acc, ratingSystem);
                if (progress != null)
                {
                    isActive     = true;
                    bestProgress = Math.Max(bestProgress, progress.ProgressRatio);
                }
            }
            if (!isActive)
            {
                return(0.001f);
            }
            return(bestProgress);
        }
コード例 #15
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);
        }
コード例 #16
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;
        }
コード例 #17
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);
                }
            }
        }
コード例 #18
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));
        }
コード例 #19
0
 public PlayerRating GetPlayerRating(int accountID)
 {
     return(playerRatings.ContainsKey(RatingSystems.GetRatingId(accountID)) ? playerRatings[RatingSystems.GetRatingId(accountID)] : DefaultRating);
 }
コード例 #20
0
 public PlayerDay GetInternalRating(int accountID, DateTime time)
 {
     return(players[accountID].days.Find(x => x.day == RatingSystems.ConvertDateToDays(time)));
 }
コード例 #21
0
 public void UpdateRatings()
 {
     if (!RatingSystems.Initialized)
     {
         return;
     }
     if (battlesRegistered == 0)
     {
         Trace.TraceWarning("No battles registered for WHR " + category);
         return;
     }
     lock (updateLock)
     {
         Action updateAction = null;
         if (!completelyInitialized)
         {
             updateAction = (() =>
             {
                 Trace.TraceInformation("Initializing WHR " + category + " ratings for " + battlesRegistered + " battles, this will take some time..");
                 runIterations(75);
                 UpdateRankings(players.Values);
                 playerOldRatings = new Dictionary <int, PlayerRating>(playerRatings);
                 completelyInitialized = true;
                 cachedDbRatings.Clear();
             });
         }
         else if (DateTime.UtcNow.Subtract(lastUpdateTime).TotalHours >= GlobalConst.LadderUpdatePeriod)
         {
             updateAction = (() =>
             {
                 Trace.TraceInformation("Updating all WHR " + category + " ratings");
                 runIterations(1);
                 UpdateRankings(players.Values);
             });
             lastUpdateTime = DateTime.UtcNow;
         }
         else
         {
             updateAction = (() =>
             {
                 Trace.TraceInformation("Updating WHR " + category + " ratings for pending battles: " + pendingDebriefings.Keys.Select(x => "B" + x).StringJoin());
                 IEnumerable <Player> players = pendingDebriefings.Values.SelectMany(x => x.battle.SpringBattlePlayers).Where(p => !p.IsSpectator).Select(p => getPlayerById(RatingSystems.GetRatingId(p.AccountID)));
                 players.ForEach(p => p.RunOneNewtonIteration(true));
                 UpdateRankings(players);
             });
         }
         Task.Factory.StartNew(() =>
         {
             try
             {
                 lock (updateLockInternal)
                 {
                     DateTime start = DateTime.Now;
                     updateAction.Invoke();
                     Trace.TraceInformation("WHR " + category + " Ratings updated in " + DateTime.Now.Subtract(start).TotalSeconds + " seconds, " + (GC.GetTotalMemory(false) / (1 << 20)) + "MiB total memory allocated");
                 }
             }
             catch (Exception ex)
             {
                 Trace.TraceError("Thread error while updating WHR " + category + " " + ex);
             }
         }, CancellationToken.None, TaskCreationOptions.None, PriorityScheduler.BelowNormal);
     }
 }
コード例 #22
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);
            }
        }
コード例 #23
0
 public void UpdateRatings()
 {
     if (!RatingSystems.Initialized)
     {
         return;
     }
     if (latestBattle == null)
     {
         //Trace.TraceInformation("WHR " + category +": No battles to evaluate");
         return;
     }
     lock (updateLock)
     {
         Action updateAction = null;
         if (lastUpdate == null)
         {
             updateAction = (() =>
             {
                 Trace.TraceInformation("Initializing WHR " + category + " ratings for " + battlesRegistered + " battles, this will take some time.. From B" + firstBattle?.SpringBattleID + " to B" + latestBattle?.SpringBattleID);
                 runIterations(75);
                 UpdateRankings(players.Values);
                 playerOldRatings = new Dictionary <int, PlayerRating>(playerRatings);
                 completelyInitialized = true;
                 cachedDbRatings.Clear();
             });
         }
         else if (DateTime.UtcNow.Subtract(lastUpdateTime).TotalHours >= GlobalConst.LadderUpdatePeriod)
         {
             updateAction = (() =>
             {
                 Trace.TraceInformation("Updating all WHR " + category + " ratings");
                 runIterations(1);
                 UpdateRankings(players.Values);
             });
             lastUpdateTime = DateTime.UtcNow;
         }
         else if (!latestBattle.Equals(lastUpdate))
         {
             updateAction = (() =>
             {
                 Trace.TraceInformation("Updating WHR " + category + " ratings for last Battle: " + latestBattle.SpringBattleID);
                 IEnumerable <Player> players = latestBattle.SpringBattlePlayers.Where(p => !p.IsSpectator).Select(p => getPlayerById(RatingSystems.GetRatingId(p.AccountID)));
                 players.ForEach(p => p.runOneNewtonIteration());
                 players.ForEach(p => p.updateUncertainty());
                 UpdateRankings(players);
             });
         }
         else
         {
             //Trace.TraceInformation("No WHR " + category +" ratings to update");
             return;
         }
         var lastUpdateEx = lastUpdate;
         Task.Factory.StartNew(() =>
         {
             try
             {
                 lock (updateLockInternal)
                 {
                     DateTime start = DateTime.Now;
                     updateAction.Invoke();
                     Trace.TraceInformation("WHR " + category + " Ratings updated in " + DateTime.Now.Subtract(start).TotalSeconds + " seconds, " + (GC.GetTotalMemory(false) / (1 << 20)) + "MiB total memory allocated");
                 }
             }
             catch (Exception ex)
             {
                 Trace.TraceError("Thread error while updating WHR " + category + " " + ex);
             }
         }, CancellationToken.None, TaskCreationOptions.None, PriorityScheduler.BelowNormal);
         lastUpdate = latestBattle;
     }
 }
コード例 #24
0
        public void UpdateRatings()
        {
            if (!RatingSystems.Initialized)
            {
                return;
            }
            if (latestBattle == null)
            {
                //Trace.TraceInformation("WHR " + category +": No battles to evaluate");
                return;
            }
            lock (updateLock)
            {
                Action updateAction = null;
                if (lastUpdate == null)
                {
                    updateAction = (() =>
                    {
                        Trace.TraceInformation("Initializing WHR " + category + " ratings for " + battlesRegistered + " battles, this will take some time.. From B" + firstBattle?.SpringBattleID + " to B" + latestBattle?.SpringBattleID);
                        runIterations(75);
                        UpdateRankings(players.Values);
                        playerOldRatings = new Dictionary <int, PlayerRating>(playerRatings);
                    });
                }
                else if (DateTime.UtcNow.Subtract(lastUpdateTime).TotalHours >= GlobalConst.LadderUpdatePeriod)
                {
                    updateAction = (() =>
                    {
                        Trace.TraceInformation("Updating all WHR " + category + " ratings");
                        runIterations(1);
                        UpdateRankings(players.Values);
                    });
                    lastUpdateTime = DateTime.UtcNow;
                }
                else if (!latestBattle.Equals(lastUpdate))
                {
                    updateAction = (() =>
                    {
                        Trace.TraceInformation("Updating WHR " + category + " ratings for last Battle: " + latestBattle.SpringBattleID);
                        IEnumerable <Player> players = latestBattle.SpringBattlePlayers.Where(p => !p.IsSpectator).Select(p => getPlayerById(RatingSystems.GetRatingId(p.AccountID)));
                        players.ForEach(p => p.runOneNewtonIteration());
                        players.ForEach(p => p.updateUncertainty());
                        UpdateRankings(players);
                    });
                }
                else
                {
                    //Trace.TraceInformation("No WHR " + category +" ratings to update");
                    return;
                }
                var lastUpdateEx = lastUpdate;
                Task.Factory.StartNew(() =>
                {
                    try
                    {
                        lock (updateLockInternal)
                        {
                            DateTime start = DateTime.Now;
                            updateAction.Invoke();
                            Trace.TraceInformation("WHR " + category + " Ratings updated in " + DateTime.Now.Subtract(start).TotalSeconds + " seconds, " + (GC.GetTotalMemory(false) / (1 << 20)) + "MiB total memory allocated");

                            IEnumerable <Account> updatedRanks = new List <Account>();
                            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;
                                    });
                                    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);
                                }
                                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();
                            }
                            RatingsUpdated(this, new RatingUpdate()
                            {
                                affectedPlayers = updatedRanks.Select(p => RatingSystems.GetRatingId(p.AccountID))
                            });
                        }
                    }
                    catch (Exception ex)
                    {
                        Trace.TraceError("Thread error while updating WHR " + category + " " + ex);
                    }
                }, CancellationToken.None, TaskCreationOptions.None, PriorityScheduler.BelowNormal);
                lastUpdate = latestBattle;
            }
        }
コード例 #25
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);
            }
        }
コード例 #26
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].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);
            }
        }
コード例 #27
0
 public PlayerRating(int Rank, float Percentile, float Elo, float Uncertainty) : this(Rank, Percentile, Elo, Uncertainty, RatingSystems.ConvertDateToDays(DateTime.Now))
 {
 }
コード例 #28
0
        private static void FillApplicableRatings(SpringBattle battle, SpringBattleContext result)
        {
            battle.ApplicableRatings = 0;
            if (battle.HasBots)
            {
                return;
            }
            if (battle.IsMission)
            {
                return;
            }
            if (battle.SpringBattlePlayers?.Where(x => !x.IsSpectator).Select(x => x.AllyNumber).Distinct().Count() < 2)
            {
                return;
            }
            if (battle.ResourceByMapResourceID?.MapIsSpecial == true)
            {
                return;
            }

            //only count balanced custom matches for elo
            if (battle.Mode == AutohostMode.None && battle.SpringBattlePlayers?.Where(x => !x.IsSpectator).GroupBy(x => x.AllyNumber).Select(x => x.Count()).Distinct().Count() > 1)
            {
                return;
            }
            if (battle.Duration < GlobalConst.MinDurationForElo)
            {
                return;
            }

            //don't mark battles for ratings if they can't be rated
            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();

            if (winners.Count == 0 || losers.Count == 0 || winners.Intersect(losers).Count() != 0)
            {
                return;
            }

            battle.ApplicableRatings |= (RatingCategoryFlags)result.LobbyStartContext.ApplicableRating;
            //Optionally add other flags here, like a casual or overall rating
        }