mirror of
				https://github.com/furyfire/trueskill.git
				synced 2025-11-04 10:12:28 +01:00 
			
		
		
		
	First time I got the two team TrueSkill calculator up and running
This commit is contained in:
		@@ -14,4 +14,11 @@ function square($x)
 | 
			
		||||
{
 | 
			
		||||
    return $x * $x;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function sum($itemsToSum, $funcName )
 | 
			
		||||
{
 | 
			
		||||
    $mappedItems = array_map($funcName, $itemsToSum);
 | 
			
		||||
    return array_sum($mappedItems);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
?>
 | 
			
		||||
@@ -103,7 +103,7 @@ class TruncatedGaussianCorrectionFunctions
 | 
			
		||||
    // the multiplicative correction of a double-sided truncated Gaussian with unit variance
 | 
			
		||||
    public static function wWithinMarginScaled($teamPerformanceDifference, $drawMargin, $c)
 | 
			
		||||
    {
 | 
			
		||||
        return self::wWithinMargin(teamPerformanceDifference/c, drawMargin/c);
 | 
			
		||||
        return self::wWithinMargin($teamPerformanceDifference/$c, $drawMargin/$c);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // From F#:
 | 
			
		||||
@@ -119,7 +119,7 @@ class TruncatedGaussianCorrectionFunctions
 | 
			
		||||
            return 1.0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $vt = vWithinMargin($teamPerformanceDifferenceAbsoluteValue, $drawMargin);
 | 
			
		||||
        $vt = self::vWithinMargin($teamPerformanceDifferenceAbsoluteValue, $drawMargin);
 | 
			
		||||
 | 
			
		||||
        return $vt*$vt +
 | 
			
		||||
               (
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
 | 
			
		||||
namespace Moserware\Skills\TrueSkill;
 | 
			
		||||
 | 
			
		||||
require_once(dirname(__FILE__) . "/../Guard.php");
 | 
			
		||||
require_once(dirname(__FILE__) . "/../PairwiseComparison.php");
 | 
			
		||||
require_once(dirname(__FILE__) . "/../RankSorter.php");
 | 
			
		||||
require_once(dirname(__FILE__) . "/../Rating.php");
 | 
			
		||||
@@ -16,6 +17,7 @@ require_once(dirname(__FILE__) . "/../Numerics/BasicMath.php");
 | 
			
		||||
require_once(dirname(__FILE__) . "/DrawMargin.php");
 | 
			
		||||
require_once(dirname(__FILE__) . "/TruncatedGaussianCorrectionFunctions.php");
 | 
			
		||||
 | 
			
		||||
use Moserware\Skills\Guard;
 | 
			
		||||
use Moserware\Skills\PairwiseComparison;
 | 
			
		||||
use Moserware\Skills\RankSorter;
 | 
			
		||||
use Moserware\Skills\Rating;
 | 
			
		||||
@@ -44,7 +46,8 @@ class TwoPlayerTrueSkillCalculator extends SkillCalculator
 | 
			
		||||
                                        array $teams,
 | 
			
		||||
                                        array $teamRanks)
 | 
			
		||||
    {
 | 
			
		||||
        // Basic argument checking        
 | 
			
		||||
        // Basic argument checking
 | 
			
		||||
        Guard::argumentNotNull($gameInfo, "gameInfo");
 | 
			
		||||
        $this->validateTeamCountAndPlayersCountPerTeam($teams);
 | 
			
		||||
 | 
			
		||||
        // Make sure things are in order
 | 
			
		||||
@@ -136,7 +139,8 @@ class TwoPlayerTrueSkillCalculator extends SkillCalculator
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public function calculateMatchQuality($gameInfo, array $teams)
 | 
			
		||||
    {        
 | 
			
		||||
    {
 | 
			
		||||
        Guard::argumentNotNull($gameInfo, "gameInfo");
 | 
			
		||||
        $this->validateTeamCountAndPlayersCountPerTeam($teams);
 | 
			
		||||
 | 
			
		||||
        $team1 = $teams[0];
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										222
									
								
								PHPSkills/TrueSkill/TwoTeamTrueSkillCalculator.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										222
									
								
								PHPSkills/TrueSkill/TwoTeamTrueSkillCalculator.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,222 @@
 | 
			
		||||
<?php
 | 
			
		||||
 | 
			
		||||
namespace Moserware\Skills\TrueSkill;
 | 
			
		||||
 | 
			
		||||
require_once(dirname(__FILE__) . "/../GameInfo.php");
 | 
			
		||||
require_once(dirname(__FILE__) . "/../Guard.php");
 | 
			
		||||
require_once(dirname(__FILE__) . "/../PairwiseComparison.php");
 | 
			
		||||
require_once(dirname(__FILE__) . "/../RankSorter.php");
 | 
			
		||||
require_once(dirname(__FILE__) . "/../Rating.php");
 | 
			
		||||
require_once(dirname(__FILE__) . "/../RatingContainer.php");
 | 
			
		||||
require_once(dirname(__FILE__) . "/../SkillCalculator.php");
 | 
			
		||||
 | 
			
		||||
require_once(dirname(__FILE__) . "/../Team.php");
 | 
			
		||||
 | 
			
		||||
require_once(dirname(__FILE__) . "/../PlayersRange.php");
 | 
			
		||||
require_once(dirname(__FILE__) . "/../TeamsRange.php");
 | 
			
		||||
 | 
			
		||||
require_once(dirname(__FILE__) . "/../Numerics/BasicMath.php");
 | 
			
		||||
 | 
			
		||||
require_once(dirname(__FILE__) . "/DrawMargin.php");
 | 
			
		||||
require_once(dirname(__FILE__) . "/TruncatedGaussianCorrectionFunctions.php");
 | 
			
		||||
 | 
			
		||||
use Moserware\Skills\GameInfo;
 | 
			
		||||
use Moserware\Skills\Guard;
 | 
			
		||||
use Moserware\Skills\PairwiseComparison;
 | 
			
		||||
use Moserware\Skills\RankSorter;
 | 
			
		||||
use Moserware\Skills\Rating;
 | 
			
		||||
use Moserware\Skills\RatingContainer;
 | 
			
		||||
use Moserware\Skills\SkillCalculator;
 | 
			
		||||
use Moserware\Skills\SkillCalculatorSupportedOptions;
 | 
			
		||||
 | 
			
		||||
use Moserware\Skills\PlayersRange;
 | 
			
		||||
use Moserware\Skills\TeamsRange;
 | 
			
		||||
 | 
			
		||||
use Moserware\Skills\Team;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Calculates new ratings for only two teams where each team has 1 or more players.
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <remarks>
 | 
			
		||||
/// When you only have two teams, the math is still simple: no factor graphs are used yet.
 | 
			
		||||
/// </remarks>
 | 
			
		||||
class TwoTeamTrueSkillCalculator extends SkillCalculator
 | 
			
		||||
{
 | 
			
		||||
    public function __construct()
 | 
			
		||||
    {
 | 
			
		||||
        parent::__construct(SkillCalculatorSupportedOptions::NONE, TeamsRange::exactly(2), PlayersRange::atLeast(1));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public function calculateNewRatings($gameInfo,
 | 
			
		||||
                                        array $teams,
 | 
			
		||||
                                        array $teamRanks)
 | 
			
		||||
    {
 | 
			
		||||
        Guard::argumentNotNull($gameInfo, "gameInfo");
 | 
			
		||||
        $this->validateTeamCountAndPlayersCountPerTeam($teams);
 | 
			
		||||
 | 
			
		||||
        RankSorter::sort($teams, $teamRanks);
 | 
			
		||||
 | 
			
		||||
        $team1 = $teams[0];
 | 
			
		||||
        $team2 = $teams[1];
 | 
			
		||||
 | 
			
		||||
        $wasDraw = ($teamRanks[0] == $teamRanks[1]);
 | 
			
		||||
 | 
			
		||||
        $results = new RatingContainer();
 | 
			
		||||
 | 
			
		||||
        self::updatePlayerRatings($gameInfo,
 | 
			
		||||
                                  $results,
 | 
			
		||||
                                  $team1,
 | 
			
		||||
                                  $team2,
 | 
			
		||||
                                  $wasDraw ? PairwiseComparison::DRAW : PairwiseComparison::WIN);
 | 
			
		||||
 | 
			
		||||
        self::updatePlayerRatings($gameInfo,
 | 
			
		||||
                                  $results,
 | 
			
		||||
                                  $team2,
 | 
			
		||||
                                  $team1,
 | 
			
		||||
                                  $wasDraw ? PairwiseComparison::DRAW : PairwiseComparison::LOSE);
 | 
			
		||||
 | 
			
		||||
        return $results;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach ($selfTeam->getAllPlayers() as $selfTeamCurrentPlayer)
 | 
			
		||||
        {
 | 
			
		||||
            $previousPlayerRating = $selfTeam->getRating($selfTeamCurrentPlayer);
 | 
			
		||||
 | 
			
		||||
            $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($selfTeamCurrentPlayer, new Rating($newMean, $newStdDev));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public function calculateMatchQuality($gameInfo, array $teams)
 | 
			
		||||
    {
 | 
			
		||||
        Guard::argumentNotNull($gameInfo, "gameInfo");
 | 
			
		||||
        $this->validateTeamCountAndPlayersCountPerTeam($teams);
 | 
			
		||||
 | 
			
		||||
        // We've verified that there's just two teams
 | 
			
		||||
        $team1Ratings = $teams[0]->getAllRatings();
 | 
			
		||||
        $team1Count = count($team1Ratings);
 | 
			
		||||
 | 
			
		||||
        $team2Ratings = $teams[1]->getAllRatings();
 | 
			
		||||
        $team2Count = count($team2Ratings);
 | 
			
		||||
 | 
			
		||||
        $totalPlayers = $team1Count + $team2Count;
 | 
			
		||||
 | 
			
		||||
        $betaSquared = square($gameInfo->getBeta());
 | 
			
		||||
 | 
			
		||||
        $meanGetter =
 | 
			
		||||
            function($currentRating)
 | 
			
		||||
            {
 | 
			
		||||
                return $currentRating->getMean();
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        $varianceGetter =
 | 
			
		||||
            function($currentRating)
 | 
			
		||||
            {
 | 
			
		||||
                return square($currentRating->getStandardDeviation());
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        $team1MeanSum = sum($team1Ratings, $meanGetter);
 | 
			
		||||
        $team1StdDevSquared = sum($team1Ratings, $varianceGetter);
 | 
			
		||||
 | 
			
		||||
        $team2MeanSum = sum($team2Ratings, $meanGetter);
 | 
			
		||||
        $team2SigmaSquared = sum($team2Ratings, $varianceGetter);
 | 
			
		||||
 | 
			
		||||
        // 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.
 | 
			
		||||
 | 
			
		||||
        $sqrtPart
 | 
			
		||||
            = sqrt(
 | 
			
		||||
                ($totalPlayers*$betaSquared)
 | 
			
		||||
                /
 | 
			
		||||
                ($totalPlayers*$betaSquared + $team1StdDevSquared + $team2SigmaSquared)
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
        $expPart
 | 
			
		||||
            = exp(
 | 
			
		||||
                (-1*square($team1MeanSum - $team2MeanSum))
 | 
			
		||||
                /
 | 
			
		||||
                (2*($totalPlayers*$betaSquared + $team1StdDevSquared + $team2SigmaSquared))
 | 
			
		||||
                );
 | 
			
		||||
 | 
			
		||||
        return $expPart*$sqrtPart;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
?>
 | 
			
		||||
		Reference in New Issue
	
	Block a user