private static function calculateNewRating(GameInfo $gameInfo, Rating $selfRating, Rating $opponentRating, $comparison) { $drawMargin = DrawMargin::getDrawMarginFromDrawProbability($gameInfo->getDrawProbability(), $gameInfo->getBeta()); $c = sqrt(square($selfRating->getStandardDeviation()) + square($opponentRating->getStandardDeviation()) + 2 * square($gameInfo->getBeta())); $winningMean = $selfRating->getMean(); $losingMean = $opponentRating->getMean(); switch ($comparison) { case PairwiseComparison::WIN: case PairwiseComparison::DRAW: // NOP break; case PairwiseComparison::LOSE: $winningMean = $opponentRating->getMean(); $losingMean = $selfRating->getMean(); break; } $meanDelta = $winningMean - $losingMean; if ($comparison != PairwiseComparison::DRAW) { // non-draw case $v = TruncatedGaussianCorrectionFunctions::vExceedsMarginScaled($meanDelta, $drawMargin, $c); $w = TruncatedGaussianCorrectionFunctions::wExceedsMarginScaled($meanDelta, $drawMargin, $c); $rankMultiplier = (int) $comparison; } else { $v = TruncatedGaussianCorrectionFunctions::vWithinMarginScaled($meanDelta, $drawMargin, $c); $w = TruncatedGaussianCorrectionFunctions::wWithinMarginScaled($meanDelta, $drawMargin, $c); $rankMultiplier = 1; } $meanMultiplier = (square($selfRating->getStandardDeviation()) + square($gameInfo->getDynamicsFactor())) / $c; $varianceWithDynamics = square($selfRating->getStandardDeviation()) + square($gameInfo->getDynamicsFactor()); $stdDevMultiplier = $varianceWithDynamics / square($c); $newMean = $selfRating->getMean() + $rankMultiplier * $meanMultiplier * $v; $newStdDev = sqrt($varianceWithDynamics * (1 - $w * $stdDevMultiplier)); return new Rating($newMean, $newStdDev); }
private static function updatePlayerRatings(GameInfo $gameInfo, RatingContainer &$newPlayerRatings, Team $selfTeam, Team $otherTeam, $selfToOtherTeamComparison) { $drawMargin = DrawMargin::getDrawMarginFromDrawProbability($gameInfo->getDrawProbability(), $gameInfo->getBeta()); $betaSquared = square($gameInfo->getBeta()); $tauSquared = square($gameInfo->getDynamicsFactor()); $totalPlayers = $selfTeam->count() + $otherTeam->count(); $meanGetter = function ($currentRating) { return $currentRating->getMean(); }; $selfMeanSum = sum($selfTeam->getAllRatings(), $meanGetter); $otherTeamMeanSum = sum($otherTeam->getAllRatings(), $meanGetter); $varianceGetter = function ($currentRating) { return square($currentRating->getStandardDeviation()); }; $c = sqrt(sum($selfTeam->getAllRatings(), $varianceGetter) + sum($otherTeam->getAllRatings(), $varianceGetter) + $totalPlayers * $betaSquared); $winningMean = $selfMeanSum; $losingMean = $otherTeamMeanSum; switch ($selfToOtherTeamComparison) { case PairwiseComparison::WIN: case PairwiseComparison::DRAW: // NOP break; case PairwiseComparison::LOSE: $winningMean = $otherTeamMeanSum; $losingMean = $selfMeanSum; break; } $meanDelta = $winningMean - $losingMean; if ($selfToOtherTeamComparison != PairwiseComparison::DRAW) { // non-draw case $v = TruncatedGaussianCorrectionFunctions::vExceedsMarginScaled($meanDelta, $drawMargin, $c); $w = TruncatedGaussianCorrectionFunctions::wExceedsMarginScaled($meanDelta, $drawMargin, $c); $rankMultiplier = (int) $selfToOtherTeamComparison; } else { // assume draw $v = TruncatedGaussianCorrectionFunctions::vWithinMarginScaled($meanDelta, $drawMargin, $c); $w = TruncatedGaussianCorrectionFunctions::wWithinMarginScaled($meanDelta, $drawMargin, $c); $rankMultiplier = 1; } $selfTeamAllPlayers =& $selfTeam->getAllPlayers(); foreach ($selfTeamAllPlayers as &$selfTeamCurrentPlayer) { $localSelfTeamCurrentPlayer =& $selfTeamCurrentPlayer; $previousPlayerRating = $selfTeam->getRating($localSelfTeamCurrentPlayer); $meanMultiplier = (square($previousPlayerRating->getStandardDeviation()) + $tauSquared) / $c; $stdDevMultiplier = (square($previousPlayerRating->getStandardDeviation()) + $tauSquared) / square($c); $playerMeanDelta = $rankMultiplier * $meanMultiplier * $v; $newMean = $previousPlayerRating->getMean() + $playerMeanDelta; $newStdDev = sqrt((square($previousPlayerRating->getStandardDeviation()) + $tauSquared) * (1 - $w * $stdDevMultiplier)); $newPlayerRatings->setRating($localSelfTeamCurrentPlayer, new Rating($newMean, $newStdDev)); } }
public function calculateMatchQuality(GameInfo $gameInfo, array $teams) { // We need to create the A matrix which is the player team assigments. $teamAssignmentsList = $teams; $skillsMatrix = $this->getPlayerCovarianceMatrix($teamAssignmentsList); $meanVector = $this->getPlayerMeansVector($teamAssignmentsList); $meanVectorTranspose = $meanVector->getTranspose(); $playerTeamAssignmentsMatrix = $this->createPlayerTeamAssignmentMatrix($teamAssignmentsList, $meanVector->getRowCount()); $playerTeamAssignmentsMatrixTranspose = $playerTeamAssignmentsMatrix->getTranspose(); $betaSquared = square($gameInfo->getBeta()); $start = Matrix::multiply($meanVectorTranspose, $playerTeamAssignmentsMatrix); $aTa = Matrix::multiply(Matrix::scalarMultiply($betaSquared, $playerTeamAssignmentsMatrixTranspose), $playerTeamAssignmentsMatrix); $aTSA = Matrix::multiply(Matrix::multiply($playerTeamAssignmentsMatrixTranspose, $skillsMatrix), $playerTeamAssignmentsMatrix); $middle = Matrix::add($aTa, $aTSA); $middleInverse = $middle->getInverse(); $end = Matrix::multiply($playerTeamAssignmentsMatrixTranspose, $meanVector); $expPartMatrix = Matrix::scalarMultiply(-0.5, Matrix::multiply(Matrix::multiply($start, $middleInverse), $end)); $expPart = $expPartMatrix->getDeterminant(); $sqrtPartNumerator = $aTa->getDeterminant(); $sqrtPartDenominator = $middle->getDeterminant(); $sqrtPart = $sqrtPartNumerator / $sqrtPartDenominator; $result = exp($expPart) * sqrt($sqrtPart); return $result; }
function loadRatings() { $stats = file_get_contents('games.stats'); $stats = explode("\n", $stats); $ratings = array(); foreach ($stats as $stat) { if (empty($stat)) { continue; } $stat = explode("\t", $stat); $playerId = (int) $stat[0]; $mean = (double) $stat[1]; $stddev = (double) $stat[2]; $ratings[$playerId] = new Rating($mean, $stddev); } $gameInfo = new GameInfo(); $players = loadPlayers(); foreach ($players as $playerId => $playerName) { if (!isset($ratings[$playerId])) { $ratings[$playerId] = $gameInfo->getDefaultRating(); } } return $ratings; }
public function getPlayerWinProbability(GameInfo $gameInfo, $playerRating, $opponentRating) { $ratingDifference = $playerRating - $opponentRating; // See equation 1.1 in the TrueSkill paper return GaussianDistribution::cumulativeTo($ratingDifference / (sqrt(2) * $gameInfo->getBeta())); }
private static function oneOnTwoBalancedPartialPlay($testClass, SkillCalculator $calculator) { $gameInfo = new GameInfo(); $p1 = new Player(1); $team1 = new Team($p1, $gameInfo->getDefaultRating()); $p2 = new Player(2, 0.0); $p3 = new Player(3, 1.0); $team2 = new Team(); $team2->addPlayer($p2, $gameInfo->getDefaultRating()); $team2->addPlayer($p3, $gameInfo->getDefaultRating()); $teams = Teams::concat($team1, $team2); $newRatings = $calculator->calculateNewRatings($gameInfo, $teams, array(1, 2)); $p1NewRating = $newRatings->getRating($p1); $p2NewRating = $newRatings->getRating($p2); $p3NewRating = $newRatings->getRating($p3); // This should be roughly the same as a 1 v 1 self::assertRating($testClass, 29.39648040436841, 7.1713980703143205, $p1NewRating); self::assertRating($testClass, 24.999560351959563, 8.333749978770932, $p2NewRating); self::assertRating($testClass, 20.603519595631585, 7.1713980703143205, $p3NewRating); $matchQuality = $calculator->calculateMatchQuality($gameInfo, $teams); self::assertMatchQuality($testClass, 0.44721358745011336, $matchQuality); }