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); }
//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); } }
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); } }
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)); }
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)); }
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()); }
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); }
//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); } }
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; } }
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; } }
//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); } }
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); } }
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 }