Beispiel #1
0
        private SkillGaussian CalculateTeamRating(IEnumerable <JamPlayerEffectiveness> side, IEnumerable <SkillGaussian> players)
        {
            if (side.Count() > 5)
            {
                throw new InvalidOperationException("too many players on team!");
            }
            // for each player, we need their mean and their playtime portion
            double summedQuality  = 0;
            double summedVariance = 0;

            foreach (JamPlayerEffectiveness player in side)
            {
                // we scale the jammer to be half of the total importance of the team
                // we're adding in the beta variance here, rather than on the player,
                // to make future updates to the player cleaner
                SkillGaussian playerSkill = players.First(p => p.ID == player.PlayerID);
                if (player.IsJammer)
                {
                    summedQuality  += player.JamPortion * playerSkill.Mean * 4;
                    summedVariance += player.JamPortion * (playerSkill.Variance + _betaSquared) * 4;
                }
                else
                {
                    summedQuality  += player.JamPortion * playerSkill.Mean;
                    summedVariance += player.JamPortion * (playerSkill.Variance + _betaSquared);
                }
            }
            return(new SkillGaussian(0, summedQuality, summedVariance));
        }
Beispiel #2
0
        private void AnalyzeJam(IEnumerable <JamPlayerEffectiveness> players, DateTime dateOfJam)
        {
            var teams      = players.GroupBy(p => p.TeamID);
            var team1      = teams.First().ToList();
            var team2      = teams.Last().ToList();
            var skills1    = team1.Select(p => CreatePlayerSkill(p.PlayerID, p.IsJammer, dateOfJam));
            var skills2    = team2.Select(p => CreatePlayerSkill(p.PlayerID, p.IsJammer, dateOfJam));
            var team1Skill = CalculateTeamRating(team1, skills1);
            var team2Skill = CalculateTeamRating(team2, skills2);

            // the difference in team skills, adjusted by the beta, give us a sense of how we expected this matchup to go
            // that can be compared to the actual result
            double c = Math.Sqrt(team1Skill.Variance + team2Skill.Variance + 2 * _betaSquared);

            // in normal TrueSkill, a Beta lead in rating is interpreted as having about an 80% chance of a win.
            // in our implementation, it represents an 80% result; in the 0-penalty case,
            // this is approximately equal to getting about a 3.5 point delta on a jam

            // determine which team exceeded expectations
            // basically, what in trueskill would be the main value becomes our "draw" value,
            // and the actual result becomes our main value
            double meanDiff             = team1Skill.Mean - team2Skill.Mean;
            double scaledMeanDiff       = meanDiff / c;
            double trueSkillDenominator = SkillGaussian.CumulativeTo(scaledMeanDiff, 0, 1);
            double result = _pointDeltaCumulative[_jamTeamPointDeltaMap[players.First().JamID][team1.First().TeamID]];
            // the denominator is effectively the percent result expected for team 1;
            // if the actual result is greater, team 1 overperformed
            double team1Multiplier = 1;

            if (trueSkillDenominator > result)
            {
                meanDiff             = team2Skill.Mean - team1Skill.Mean;
                scaledMeanDiff       = meanDiff / c;
                trueSkillDenominator = SkillGaussian.CumulativeTo(scaledMeanDiff, 0, 1);
                result          = _pointDeltaCumulative[_jamTeamPointDeltaMap[players.First().JamID][team2.First().TeamID]];
                team1Multiplier = -1;
            }
            double inverseResult  = SkillGaussian.InverseCumulativeTo(result, 0, 1);
            double newDenominator = SkillGaussian.CumulativeTo(scaledMeanDiff - inverseResult, 0, 1);
            double v = (SkillGaussian.At(scaledMeanDiff - inverseResult, 0, 1)) / newDenominator;
            double w = v * (v + scaledMeanDiff - inverseResult);

            foreach (SkillGaussian player in skills1)
            {
                CalculateNewPlayerRating(c, team1Multiplier, v, w, player);
            }
            foreach (SkillGaussian player in skills2)
            {
                CalculateNewPlayerRating(c, -team1Multiplier, v, w, player);
            }
        }
Beispiel #3
0
        // at some point, we'll need to take the current sigma of the player and increase it, probably based on time since last data
        private double CalculateTimeEffect(SkillGaussian player, DateTime newTime)
        {
            // how much do we want to scale the player's std dev?
            // it probably ought to be flat, not proportional
            // one way to think of it: how long would a well-understood player have to disappear to be treated as new again?
            // let's say two years-ish, and see what happens

            TimeSpan timeSpan = newTime - player.LastUpdated;

            if (timeSpan > _baseTimeSpan)
            {
                timeSpan = _baseTimeSpan;
            }
            double ratio = ((double)timeSpan.Ticks) / _baseTimeSpan.Ticks;
            double range = _baseSigma - player.Sigma;

            return(range * ratio);
        }
Beispiel #4
0
        private SkillGaussian CreatePlayerSkill(int playerID, bool isJammer, DateTime dateOfGame)
        {
            // variances, not std dev, are sum-able
            // a^2 + b^2 != (a+b)^2
            var dictionary = isJammer ? _jammerSkillDictionary : _blockerSkillDictionary;

            if (!dictionary.ContainsKey(playerID))
            {
                SkillGaussian newPlayer = new SkillGaussian(playerID, 500, _baseVariance, isJammer, dateOfGame);
                dictionary[playerID] = newPlayer;
                return(newPlayer);
            }
            var    startingPlayer = dictionary[playerID];
            double tau            = CalculateTimeEffect(startingPlayer, dateOfGame);

            startingPlayer.AddVariance(Math.Pow(tau, 2));
            startingPlayer.LastUpdated = dateOfGame;
            return(startingPlayer);
        }
Beispiel #5
0
        private void CalculateNewPlayerRating(double c, double team1Multiplier, double v, double w, SkillGaussian player)
        {
            double meanMultiplier   = player.Variance / c;
            double stdDevMultiplier = meanMultiplier / c;

            double        playerMeanDelta = (team1Multiplier * meanMultiplier * v);
            double        newMean         = player.Mean + playerMeanDelta;
            double        newVariance     = player.Variance * (1 - (w * stdDevMultiplier));
            SkillGaussian newValues       = new SkillGaussian(player.ID, newMean, newVariance, player.IsJammer, player.LastUpdated);

            if (player.IsJammer)
            {
                _jammerSkillDictionary[player.ID] = newValues;
            }
            else
            {
                _blockerSkillDictionary[player.ID] = newValues;
            }
        }