/// <inheritdoc/> public override IDictionary <TPlayer, Rating> CalculateNewRatings <TPlayer>(GameInfo gameInfo, IEnumerable <IDictionary <TPlayer, Rating> > teams, params int[] teamRanks) { Guard.ArgumentNotNull(gameInfo, "gameInfo"); ValidateTeamCountAndPlayersCountPerTeam(teams); RankSorter.Sort(ref teams, ref teamRanks); IDictionary <TPlayer, Rating> team1 = teams.First(); IDictionary <TPlayer, Rating> team2 = teams.Last(); bool wasDraw = (teamRanks[0] == teamRanks[1]); var results = new Dictionary <TPlayer, Rating>(); UpdatePlayerRatings(gameInfo, results, team1, team2, wasDraw ? PairwiseComparison.Draw : PairwiseComparison.Win); UpdatePlayerRatings(gameInfo, results, team2, team1, wasDraw ? PairwiseComparison.Draw : PairwiseComparison.Lose); return(results); }
public override IDictionary <TPlayer, Rating> CalculateNewRatings <TPlayer>(GameInfo gameInfo, IEnumerable <IDictionary <TPlayer, Rating> > teams, params int[] teamRanks) { // On page 6 of the TrueSkill paper, the authors write: // "When we had to process a team game or a game with more than two teams we used // the so-called *duelling* heuristic: For each player, compute the Δ's in comparison // to all other players based on the team outcome of the player and every other player and // perform an update with the average of the Δ's." // This implements that algorithm. ValidateTeamCountAndPlayersCountPerTeam(teams); RankSorter.Sort(ref teams, ref teamRanks); var teamsList = teams.ToList(); var deltas = new Dictionary <TPlayer, IDictionary <TPlayer, double> >(); for (int ixCurrentTeam = 0; ixCurrentTeam < teamsList.Count; ixCurrentTeam++) { for (int ixOtherTeam = 0; ixOtherTeam < teamsList.Count; ixOtherTeam++) { if (ixOtherTeam == ixCurrentTeam) { // Shouldn't duel against ourself ;) continue; } var currentTeam = teamsList[ixCurrentTeam]; var otherTeam = teamsList[ixOtherTeam]; // Remember that bigger numbers mean worse rank (e.g. other-current is what we want) var comparison = (PairwiseComparison)Math.Sign(teamRanks[ixOtherTeam] - teamRanks[ixCurrentTeam]); foreach (var currentTeamPlayerRatingPair in currentTeam) { foreach (var otherTeamPlayerRatingPair in otherTeam) { UpdateDuels <TPlayer>(gameInfo, deltas, currentTeamPlayerRatingPair.Key, currentTeamPlayerRatingPair.Value, otherTeamPlayerRatingPair.Key, otherTeamPlayerRatingPair.Value, comparison); } } } } var result = new Dictionary <TPlayer, Rating>(); foreach (var currentTeam in teamsList) { foreach (var currentTeamPlayerPair in currentTeam) { var currentPlayerAverageDuellingDelta = deltas[currentTeamPlayerPair.Key].Values.Average(); result[currentTeamPlayerPair.Key] = new EloRating(currentTeamPlayerPair.Value.Mean + currentPlayerAverageDuellingDelta); } } return(result); }
public void SortUnsortedTest() { IEnumerable <string> people = new[] { "Five", "Two1", "Two2", "One", "Four" }; int[] ranks = new[] { 5, 2, 2, 1, 4 }; RankSorter.Sort(ref people, ref ranks); CollectionAssert.AreEqual(new[] { "One", "Two1", "Two2", "Four", "Five" }, people); CollectionAssert.AreEqual(new[] { 1, 2, 2, 4, 5 }, ranks); }
public void SortAlreadySortedTest() { IEnumerable <string> people = new[] { "One", "Two", "Three" }; int[] ranks = new[] { 1, 2, 3 }; RankSorter.Sort(ref people, ref ranks); CollectionAssert.AreEqual(new[] { "One", "Two", "Three" }, people); CollectionAssert.AreEqual(new[] { 1, 2, 3 }, ranks); }
public override IDictionary <TPlayer, Rating> CalculateNewRatings <TPlayer>(GameInfo gameInfo, IEnumerable <IDictionary <TPlayer, Rating> > teams, params int[] teamRanks) { Guard.ArgumentNotNull(gameInfo, "gameInfo"); ValidateTeamCountAndPlayersCountPerTeam(teams); RankSorter.Sort(ref teams, ref teamRanks); var factorGraph = new TrueSkillFactorGraph <TPlayer>(gameInfo, teams, teamRanks); factorGraph.BuildGraph(); factorGraph.RunSchedule(); double probabilityOfOutcome = factorGraph.GetProbabilityOfRanking(); return(factorGraph.GetUpdatedRatings()); }
public override IDictionary<TPlayer, Rating> CalculateNewRatings<TPlayer>(GameInfo gameInfo, IEnumerable<IDictionary<TPlayer, Rating>> teams, params int[] teamRanks) { ValidateTeamCountAndPlayersCountPerTeam(teams); RankSorter.Sort(ref teams, ref teamRanks); var result = new Dictionary<TPlayer, Rating>(); bool isDraw = (teamRanks[0] == teamRanks[1]); var player1 = teams.First().First(); var player2 = teams.Last().First(); var player1Rating = player1.Value.Mean; var player2Rating = player2.Value.Mean; result[player1.Key] = CalculateNewRating(gameInfo, player1Rating, player2Rating, isDraw ? PairwiseComparison.Draw : PairwiseComparison.Win); result[player2.Key] = CalculateNewRating(gameInfo, player2Rating, player1Rating, isDraw ? PairwiseComparison.Draw : PairwiseComparison.Lose); return result; }
/// <inheritdoc/> public override IDictionary <TPlayer, Rating> CalculateNewRatings <TPlayer>(GameInfo gameInfo, IEnumerable <IDictionary <TPlayer, Rating> > teams, params int[] teamRanks) { // Basic argument checking Guard.ArgumentNotNull(gameInfo, "gameInfo"); ValidateTeamCountAndPlayersCountPerTeam(teams); // Make sure things are in order RankSorter.Sort(ref teams, ref teamRanks); // Get the teams as a list to make it easier to index List <IDictionary <TPlayer, Rating> > teamList = teams.ToList(); // Since we verified that each team has one player, we know the player is the first one IDictionary <TPlayer, Rating> winningTeam = teamList[0]; TPlayer winner = winningTeam.Keys.First(); Rating winnerPreviousRating = winningTeam[winner]; IDictionary <TPlayer, Rating> losingTeam = teamList[1]; TPlayer loser = losingTeam.Keys.First(); Rating loserPreviousRating = losingTeam[loser]; bool wasDraw = (teamRanks[0] == teamRanks[1]); var results = new Dictionary <TPlayer, Rating>(); results[winner] = CalculateNewRating(gameInfo, winnerPreviousRating, loserPreviousRating, wasDraw ? PairwiseComparison.Draw : PairwiseComparison.Win); results[loser] = CalculateNewRating(gameInfo, loserPreviousRating, winnerPreviousRating, wasDraw ? PairwiseComparison.Draw : PairwiseComparison.Lose); // And we're done! return(results); }