// Calculates the match quality as the likelihood of all teams drawing (0% = bad, 100% = well matched). func (calc *TwoTeamCalc) CalcMatchQual(gi *skills.GameInfo, teams []skills.Team) float64 { // Basic argument checking validateTeamCount(teams, twoTeamTeamRange) validatePlayersPerTeam(teams, twoTeamPlayerRange) // We've verified that there's just two teams team1 := teams[0] team1Count := team1.PlayerCount() team2 := teams[1] team2Count := team2.PlayerCount() totalPlayers := team1Count + team2Count betaSqr := numerics.Sqr(gi.Beta) team1MeanSum := team1.Accum(skills.MeanSum) team1VarSum := team1.Accum(skills.VarianceSum) team2MeanSum := team2.Accum(skills.MeanSum) team2VarSum := team2.Accum(skills.VarianceSum) // This comes from equation 4.1 in the TrueSkill paper on page 8 // The equation was broken up into the part under the square root sign and // the exponential part to make the code easier to read. betaSqrPlayers := betaSqr * float64(totalPlayers) sqrtPart := math.Sqrt(betaSqrPlayers / (betaSqrPlayers + team1VarSum + team2VarSum)) expPart := math.Exp(-.5 * numerics.Sqr(team1MeanSum-team2MeanSum) / (betaSqrPlayers + team1VarSum + team2VarSum)) return expPart * sqrtPart }
// Calculates the match quality as the likelihood of all teams drawing (0% = bad, 100% = well matched). func (calc *TwoPlayerCalc) CalcMatchQual(gi *skills.GameInfo, teams []skills.Team) float64 { validateTeamCount(teams, twoPlayerTeamRange) validatePlayersPerTeam(teams, twoPlayerPlayerRange) team1 := teams[0] p1 := team1.Players()[0] p1Rating := team1.PlayerRating(p1) team2 := teams[1] p2 := team2.Players()[0] p2Rating := team2.PlayerRating(p2) // We just use equation 4.1 found on page 8 of the TrueSkill 2006 paper: betaSqr := numerics.Sqr(gi.Beta) p1var := p1Rating.Variance() p2var := p2Rating.Variance() // This is the square root part of the equation: sqrtPart := math.Sqrt(2 * betaSqr / (2*betaSqr + p1var + p2var)) // This is the exponent part of the equation: numerator := -numerics.Sqr(p1Rating.Mean() - p2Rating.Mean()) denominator := 2 * (2*betaSqr + p1var + p2var) expPart := math.Exp(numerator / denominator) return sqrtPart * expPart }
func twoTeamUpdateRatings(gi *skills.GameInfo, newSkills skills.PlayerRatings, selfTeam, otherTeam skills.Team, comparison int) { drawMargin := drawMarginFromDrawProbability(gi.DrawProbability, gi.Beta) betaSqr := numerics.Sqr(gi.Beta) tauSqr := numerics.Sqr(gi.DynamicsFactor) totalPlayers := selfTeam.PlayerCount() + otherTeam.PlayerCount() selfMeanSum := selfTeam.Accum(skills.MeanSum) otherMeanSum := otherTeam.Accum(skills.MeanSum) c := math.Sqrt(selfTeam.Accum(skills.VarianceSum) + otherTeam.Accum(skills.VarianceSum) + float64(totalPlayers)*betaSqr) winningMean := selfMeanSum losingMean := otherMeanSum if comparison == skills.Lose { winningMean, losingMean = losingMean, winningMean } meanDelta := winningMean - losingMean var v, w, rankMultiplier float64 if comparison != skills.Draw { v = vExceedsMarginC(meanDelta, drawMargin, c) w = wExceedsMarginC(meanDelta, drawMargin, c) rankMultiplier = float64(comparison) } else { v = vWithinMarginC(meanDelta, drawMargin, c) w = wWithinMarginC(meanDelta, drawMargin, c) rankMultiplier = 1 } for p, r := range selfTeam.PlayerRatings { prevPlayerRating := r meanMultiplier := (prevPlayerRating.Variance() + tauSqr) / c stdDevMultiplier := (prevPlayerRating.Variance() + tauSqr) / numerics.Sqr(c) playerMeanDelta := rankMultiplier * meanMultiplier * v newMean := prevPlayerRating.Mean() + playerMeanDelta newStdDev := math.Sqrt((prevPlayerRating.Variance() + tauSqr) * (1 - w*stdDevMultiplier)) newSkills[p] = skills.NewRating(newMean, newStdDev) } }
func twoPlayerCalcNewRating(gi *skills.GameInfo, selfRating, oppRating skills.Rating, comparison int) skills.Rating { drawMargin := drawMarginFromDrawProbability(gi.DrawProbability, gi.Beta) c := math.Sqrt(numerics.Sqr(selfRating.Stddev()) + numerics.Sqr(oppRating.Stddev()) + 2*numerics.Sqr(gi.Beta)) winningMean := selfRating.Mean() losingMean := oppRating.Mean() if comparison == skills.Lose { winningMean = oppRating.Mean() losingMean = selfRating.Mean() } meanDelta := winningMean - losingMean var v, w, rankMultiplier float64 if comparison != skills.Draw { v = vExceedsMarginC(meanDelta, drawMargin, c) w = wExceedsMarginC(meanDelta, drawMargin, c) rankMultiplier = float64(comparison) } else { v = vWithinMarginC(meanDelta, drawMargin, c) w = wWithinMarginC(meanDelta, drawMargin, c) rankMultiplier = 1 } meanMultiplier := (numerics.Sqr(selfRating.Stddev()) + numerics.Sqr(gi.DynamicsFactor)) / c varianceWithDynamics := numerics.Sqr(selfRating.Stddev()) + numerics.Sqr(gi.DynamicsFactor) stdDevMultiplier := varianceWithDynamics / numerics.Sqr(c) newMean := selfRating.Mean() + (rankMultiplier * meanMultiplier * v) newStdDev := math.Sqrt(varianceWithDynamics * (1 - w*stdDevMultiplier)) return skills.NewRating(newMean, newStdDev) }
func (r Rating) Variance() float64 { return numerics.Sqr(r.stddev) }