public RatingInfoDoubles.WeightingFactors CalculateMatchWeight(TeamInfo playerTeam, TeamInfo opponentTeam, Result r, int numPartnerResults)
        {
            /*
             * Match Weight is A x B x C x D * E * F
             * Where:
             * A = Opponent's Player Rating Reliability
             * B = Parter Frequency
             * C = Match Competitiveness Factor
             * D = Opponent Benchmark Reliability Factor
             * E = Interpool Reliability Factor
             * F = Team Rating Reliability
             */

            var a = (_ratingRule.EnableMatchFormatReliability) ? CalculateMatchFormatReliabilityFactor(r) : 10.0f;
            var b = _ratingRule.EnablePartnerFrequencyReliability ? CalculatePartnerFrequency(numPartnerResults) : 1.0f;

            var c           = (_ratingRule.EnableMatchCompetitivenessCoeffecient) ? MatchCompetivenessCalculator.CalculateMatchCompetivenessCoeffecient(playerTeam, opponentTeam, r, _ratingRule) : 1.0f;
            var d           = 1.0f; // not yet used
            var e           = (_ratingRule.EnableInterpoolCoeffecient) ? CalculateInterpoolCoefficient(playerTeam, opponentTeam) : 1.0f;
            var f           = CalculateOpponentRatingReliabilityFactor(opponentTeam);
            var matchWeight = a * b * c * d * e * f;

            return(new RatingInfoDoubles.WeightingFactors
            {
                MatchFormatReliability = a,
                MatchFrequencyReliability = b,
                MatchCompetitivenessCoeffecient = c,
                BenchmarkMatchCoeffecient = d,
                OpponentRatingReliability = f,
                InterpoolCoeffecient = e,
                MatchWeight = Math.Truncate(100000 * matchWeight) / 100000
            });
        }
        private float CalculateInterpoolCoefficient(TeamInfo playerTeam, TeamInfo opponentTeam)
        {
            // any diff in country ids
            var isForeignMatch = playerTeam.Player1CountryId != opponentTeam.Player1CountryId ||
                                 playerTeam.Player1CountryId != opponentTeam.Player2CountryId ||
                                 playerTeam.Player2CountryId != opponentTeam.Player2CountryId;

            if (playerTeam.HasCollegePlayer ^ opponentTeam.HasCollegePlayer)
            {
                return(_ratingRule.interpoolCoefficientCollege);
            }

            return(isForeignMatch ? _ratingRule.interpoolCoefficientCountry : 1.0f);
        }
        public Result.MatchGender GetGenderType(TeamInfo team1, TeamInfo team2)
        {
            var genders = new List <string>()
            {
                team1.Player1Gender, team1.Player2Gender, team2.Player1Gender, team2.Player2Gender
            };

            if (genders.Contains("M") && genders.Contains("F"))
            {
                return(Result.MatchGender.Coed);
            }
            if (genders.Contains("M"))
            {
                return(Result.MatchGender.Male);
            }
            if (genders.Contains("F"))
            {
                return(Result.MatchGender.Female);
            }
            throw new RatingException("Unable to determine result gender type");
        }
Пример #4
0
        // same thing but with team ratings
        public static float CalculateUnderdogMatch(TeamInfo playerTeam, TeamInfo opponentTeam, Result matchInfo, RatingRule rule)
        {
            TeamInfo winner, loser;

            if (matchInfo.Winner1Id == playerTeam.Player1Id || matchInfo.Winner1Id == playerTeam.Player2Id)
            {
                winner = playerTeam;
                loser  = opponentTeam;
            }
            else
            {
                winner = opponentTeam;
                loser  = playerTeam;
            }
            if (winner.TeamRating > loser.TeamRating && !CompetiveThresholdReached(matchInfo)) //If the expected team won and the loser didn't reach competitive threshold, it's expected.
            {
                return(rule.UnderDogMatchReliability);
            }
            else //Underdog was competitive
            {
                return(rule.CompetitiveUnderDogMatchReliability);
            }
        }
Пример #5
0
        // same thing but with team ratings
        public static float CalculateMatchCompetivenessCoeffecient(TeamInfo playerTeam, TeamInfo opponentTeam, Result matchInfo, RatingRule rule)
        {
            float UTRDelta = (float)Math.Abs(playerTeam.TeamRating - opponentTeam.TeamRating), coeffecient = 1;

            if (UTRDelta <= rule.NormalMatchMaxUTRDelta && UTRDelta >= rule.CloseMatchMaxUTRDelta) //Normal match
            {
                coeffecient = DoCoeffcientCalculation(UTRDelta, rule);
            }
            else if (UTRDelta < rule.CloseMatchMaxUTRDelta)                                                                               // Close Match
            {
                coeffecient = IsMatchLopsided(matchInfo, rule) ? rule.LopsidedMatchReliability : DoCoeffcientCalculation(UTRDelta, rule); //If a close match is lopsided, we assume one player must have been having an off game, reduced credit
            }
            else if (UTRDelta > rule.NormalMatchMaxUTRDelta)                                                                              //Lopsided Match
            {
                coeffecient = CalculateUnderdogMatch(playerTeam, opponentTeam, matchInfo, rule);                                          //Underdog matches, where player's have a large skill gap, calculate differently
            }
            else
            {
                throw new RatingException("Unable to determine competitiveness coeficcient for result id: " + matchInfo.Id);
                //coeffecient = 0.0f; //Fail safe, every match should be caught above, may want to throw exception here
            }
            return(coeffecient);
        }
        public int GetPartnerResults(TeamInfo team, List <Result> results)
        {
            var count = 0;

            foreach (var r in results)
            {
                var team1Ids = new List <int> {
                    r.Winner1Id, r.Winner2Id ?? 0
                };
                var team2Ids = new List <int> {
                    r.Loser1Id, r.Loser2Id ?? 0
                };
                if (team1Ids.Contains(team.Player1Id) && team1Ids.Contains(team.Player2Id))
                {
                    count++;
                }
                if (team2Ids.Contains(team.Player1Id) && team2Ids.Contains(team.Player2Id))
                {
                    count++;
                }
            }
            return(count > 0 ? count : 1);
        }
        public double CalculateDynamicRating(int playerId, TeamInfo playerTeam, TeamInfo opponentTeam, Result result)
        {
            // Effect of Partner-UTR-Spread on Team Adjustment Factor
            var matchPartnersDelta = playerTeam.RatingDiff - opponentTeam.RatingDiff;
            var baseline           = CalculateMatchBaseline(playerTeam.TeamRating, opponentTeam.TeamRating);
            var teamAdjustment     = CalculateTeamAdjustmentFactor(playerId, result, matchPartnersDelta, baseline, GetGenderType(playerTeam, opponentTeam));
            var teamDynamicRating  = CalculateTeamDynamicRating(baseline, teamAdjustment);

            // determine which position player is in
            if (playerId == playerTeam.Player1Id)
            {
                // calc dynamic rating as if partner rating was the same and cap there
                var playerTeamDynamicRating = CalculateTeamDynamicRating(CalculateMatchBaseline(playerTeam.Player1Rating, opponentTeam.TeamRating), teamAdjustment);
                var dynamicCap = (playerTeamDynamicRating);
                var dynamic    = (teamDynamicRating - playerTeam.TeamRating) + playerTeam.Player1Rating;
                dynamic = Clamp(dynamic, 1, dynamicCap);
                if (_ratingRule.EnableDynamicRatingCap)
                {
                    return(playerTeam.Player1Gender.ToLower() == "m" ? Math.Min(16.5f, dynamic) : Math.Min(13.5f, dynamic));
                }
                // cap dynamic
                return(dynamic);
            }
            if (playerId == playerTeam.Player2Id)
            {
                var playerTeamDynamicRating = CalculateTeamDynamicRating(CalculateMatchBaseline(playerTeam.Player2Rating, opponentTeam.TeamRating), teamAdjustment);
                var dynamicCap = (playerTeamDynamicRating);
                var dynamic    = (teamDynamicRating - playerTeam.TeamRating) + playerTeam.Player2Rating;
                dynamic = Clamp(dynamic, 1, dynamicCap);
                if (_ratingRule.EnableDynamicRatingCap)
                {
                    return(playerTeam.Player2Gender.ToLower() == "m" ? Math.Min(16.5f, dynamic) : Math.Min(13.5f, dynamic));
                }
                return(dynamic);
            }
            throw new RatingException("Could not map player to team");
        }
 private static double CalculateOpponentRatingReliabilityFactor(TeamInfo opponentTeam)
 {
     // mean of opponent reliabilities
     return(opponentTeam.TeamReliability);
 }
        public RatingInfoDoubles CalculateDoublesUtr(List <Result> results, Dictionary <int, Player> players, Player player)
        {
            var    ratingInfo                 = new RatingInfoDoubles();
            double sumWeight                  = 0;
            double sumDynamicXWeight          = 0;
            double sumPlayerReliabilityWeight = 0;
            int    resultCount                = 0;
            var    eligibleResultsList        = new List <int>();

            foreach (var result in results)
            {
                var winner1 = players[result.Winner1Id];
                var winner2 = players[result.Winner2Id ?? 0];
                var loser1  = players[result.Loser1Id];
                var loser2  = players[result.Loser2Id ?? 0];

                if (winner1 == null || winner2 == null || loser1 == null || loser2 == null)
                {
                    continue;
                }

                double w1Reliability;
                double w2Reliability;
                double l1Reliability;
                double l2Reliability;

                // this value should be pre-calculated
                var isCompetitiveResult =
                    result.Competitiveness.Equals("Competitive", StringComparison.OrdinalIgnoreCase);

                winner1.Stats.AssignedRating = AssignPlayerRatingForDoubles(winner1, winner2, loser1, loser2, isCompetitiveResult, out w1Reliability);
                winner2.Stats.AssignedRating = AssignPlayerRatingForDoubles(winner2, winner1, loser1, loser2, isCompetitiveResult, out w2Reliability);
                loser1.Stats.AssignedRating  = AssignPlayerRatingForDoubles(loser1, loser2, winner1, winner2, isCompetitiveResult, out l1Reliability);
                loser2.Stats.AssignedRating  = AssignPlayerRatingForDoubles(loser2, loser1, winner1, winner2, isCompetitiveResult, out l2Reliability);

                // skip result if someone cannot be assigned
                if (winner1.Stats.AssignedRating == null ||
                    winner2.Stats.AssignedRating == null ||
                    loser1.Stats.AssignedRating == null ||
                    loser2.Stats.AssignedRating == null)
                {
                    continue;
                }

                winner1.Stats.AssignedReliability = w1Reliability;
                winner2.Stats.AssignedReliability = w2Reliability;
                loser1.Stats.AssignedReliability  = l1Reliability;
                loser2.Stats.AssignedReliability  = l2Reliability;

                TeamInfo playerTeam;
                TeamInfo opponentTeam;

                // identify player's team
                if (player.Id == winner1.Id || player.Id == winner2.Id)
                {
                    playerTeam   = new TeamInfo(winner1, winner2, false);
                    opponentTeam = new TeamInfo(loser1, loser2, true);
                }
                else
                {
                    playerTeam   = new TeamInfo(loser1, loser2, false);
                    opponentTeam = new TeamInfo(winner1, winner2, true);
                }

                if (opponentTeam.TeamReliability <= 0)
                {
                    continue;
                }

                var dynamic = CalculateDynamicRating(player.Id, playerTeam, opponentTeam, result);
                dynamic = Math.Truncate(1000 * dynamic) / 1000;
                var numPartnerResults = GetPartnerResults(playerTeam, results);
                var matchWeights      = CalculateMatchWeight(playerTeam, opponentTeam, result, numPartnerResults);

                if (matchWeights.MatchWeight >= _ratingRule.eligibleResultsWeightThreshold)
                {
                    eligibleResultsList.Add(result.Id);
                }

                sumDynamicXWeight += (dynamic * matchWeights.MatchWeight);
                sumWeight         += matchWeights.MatchWeight;
                // divide by interpool coefficient so it doesn't skew player reliability
                sumPlayerReliabilityWeight += (matchWeights.MatchWeight / matchWeights.InterpoolCoeffecient);

                resultCount++;
            }
            // store active doubles results
            player.Stats.ActiveDoublesResults = JsonConvert.SerializeObject(eligibleResultsList.ToArray());

            var n = resultCount;

            // if no valid results, don't calculate
            if (n <= 0)
            {
                ratingInfo.Rating      = 0;
                ratingInfo.Reliability = 0;
                return(ratingInfo);
            }

            var doublesUtr                  = (sumWeight > 0) ? (sumDynamicXWeight / sumWeight) : 1d;
            var doublesReliability          = players[player.Id].Stats.AssignedReliability;
            var singlesReliabilityAsPercent = ((player.Stats.RatingReliability ?? 0) / 10);
            var doublesReliabilityAsPercent = (doublesReliability / 10);

            // Determination of Player’s (Singles) UTR and corresponding Player’s (singles) UTR Reliability. Factor in if available
            var singlesWeight = player.Stats.RatingReliability >= _ratingRule.singlesWeightReliabilityThreshold
                ? _ratingRule.singlesWeightOnDoubles
                : 0;

            if (player.Stats.FinalRating > 0 && player.Stats.RatingReliability > 0)
            {
                ratingInfo.Rating = ((n * (doublesUtr * doublesReliabilityAsPercent)) + (singlesWeight * ((player.Stats.FinalRating ?? 0) * singlesReliabilityAsPercent))) / ((n * doublesReliabilityAsPercent) + (singlesWeight * singlesReliabilityAsPercent));
            }
            else
            {
                ratingInfo.Rating = (n * (doublesUtr * doublesReliabilityAsPercent)) / (n * doublesReliabilityAsPercent);
            }

            ratingInfo.Reliability = CalculatePlayerReliability(sumPlayerReliabilityWeight);

            return(ratingInfo);
        }