Example #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);
        }
Example #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);
            }
        }
Example #3
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);
            }
        }
Example #4
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));
 }
Example #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));
 }
Example #6
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());
 }
Example #7
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);
        }
Example #8
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);
            }
        }
Example #9
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;
     }
 }
Example #10
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();
                    }
                }
            }
        }
 public PlayerRating GetPlayerRating(int accountID)
 {
     return(playerRatings.ContainsKey(RatingSystems.GetRatingId(accountID)) ? playerRatings[RatingSystems.GetRatingId(accountID)] : DefaultRating);
 }
        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;
            }
        }
Example #13
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);
            }
        }
Example #14
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);
     }
 }
Example #15
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);
                }
            }
        }
        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
        }