mirror of
				https://github.com/furyfire/trueskill.git
				synced 2025-11-04 10:12:28 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			169 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			169 lines
		
	
	
		
			6.8 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
 | 
						|
namespace Moserware\Skills\TrueSkill;
 | 
						|
 | 
						|
/// <summary>
 | 
						|
/// Calculates TrueSkill using a full factor graph.
 | 
						|
/// </summary>
 | 
						|
class FactorGraphTrueSkillCalculator extends SkillCalculator
 | 
						|
{
 | 
						|
    public function __construct()
 | 
						|
    {
 | 
						|
        parent::__construct(SkillCalculatorSupportedOptions::PARTIAL_PLAY | SkillCalculatorSupportedOptions::PARTIAL_UPDATE, TeamsRange::atLeast(2), PlayersRange::atLeast(1));
 | 
						|
    }
 | 
						|
 | 
						|
    public function CalculateNewRatings(GameInfo $gameInfo,
 | 
						|
                                        array $teams,
 | 
						|
                                        array $teamRanks)
 | 
						|
    {
 | 
						|
        Guard::argumentNotNull($gameInfo, "gameInfo");
 | 
						|
        $this->validateTeamCountAndPlayersCountPerTeam($teams);
 | 
						|
 | 
						|
        RankSorter::sort($teams, $teamRanks);
 | 
						|
 | 
						|
        $factorGraph = new TrueSkillFactorGraph($gameInfo, $teams, $teamRanks);
 | 
						|
        $factorGraph->buildGraph();
 | 
						|
        $factorGraph->runSchedule();
 | 
						|
 | 
						|
        $probabilityOfOutcome = $factorGraph->getProbabilityOfRanking();
 | 
						|
 | 
						|
        return $factorGraph->getUpdatedRatings();
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    public function calculateMatchQuality(GameInfo $gameInfo,
 | 
						|
                                          array $teams)
 | 
						|
    {
 | 
						|
        // We need to create the A matrix which is the player team assigments.
 | 
						|
        $teamAssignmentsList = $teams.ToList();
 | 
						|
        $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;
 | 
						|
    }
 | 
						|
 | 
						|
    private static function getPlayerMeansVector(array $teamAssignmentsList)
 | 
						|
    {
 | 
						|
        // A simple vector of all the player means.
 | 
						|
        return new Vector($this->getPlayerRatingValues($teamAssignmentsList,
 | 
						|
                                                       function($rating) 
 | 
						|
                                                       { 
 | 
						|
                                                            return $rating->getMean();
 | 
						|
                                                       }));
 | 
						|
    }
 | 
						|
 | 
						|
    private static function getPlayerCovarianceMatrix(array $teamAssignmentsList)
 | 
						|
    {
 | 
						|
        // This is a square matrix whose diagonal values represent the variance (square of standard deviation) of all
 | 
						|
        // players.
 | 
						|
        return new DiagonalMatrix(
 | 
						|
                        $this->getPlayerRatingValues($teamAssignmentsList,
 | 
						|
                                                     function($rating)
 | 
						|
                                                     {
 | 
						|
                                                        return square($rating->getStandardDeviation());
 | 
						|
                                                     }));
 | 
						|
    }
 | 
						|
 | 
						|
    // Helper function that gets a list of values for all player ratings
 | 
						|
    private static function getPlayerRatingValues(array $teamAssignmentsList,
 | 
						|
                                                  $playerRatingFunction)
 | 
						|
    {
 | 
						|
        $playerRatingValues = array();
 | 
						|
 | 
						|
        foreach ($teamAssignmentsList as $currentTeam)
 | 
						|
        {
 | 
						|
            foreach (currentTeam.Values as $currentRating)
 | 
						|
            {
 | 
						|
                $playerRatingValues[] = $playerRatingFunction($currentRating);
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        return $playerRatingValues;
 | 
						|
    }
 | 
						|
 | 
						|
    private static function createPlayerTeamAssignmentMatrix($teamAssignmentsList, $totalPlayers)
 | 
						|
    {
 | 
						|
        // The team assignment matrix is often referred to as the "A" matrix. It's a matrix whose rows represent the players
 | 
						|
        // and the columns represent teams. At Matrix[row, column] represents that player[row] is on team[col]
 | 
						|
        // Positive values represent an assignment and a negative value means that we subtract the value of the next
 | 
						|
        // team since we're dealing with pairs. This means that this matrix always has teams - 1 columns.
 | 
						|
        // The only other tricky thing is that values represent the play percentage.
 | 
						|
 | 
						|
        // For example, consider a 3 team game where team1 is just player1, team 2 is player 2 and player 3, and
 | 
						|
        // team3 is just player 4. Furthermore, player 2 and player 3 on team 2 played 25% and 75% of the time
 | 
						|
        // (e.g. partial play), the A matrix would be:
 | 
						|
 | 
						|
        // A = this 4x2 matrix:
 | 
						|
        // |  1.00  0.00 |
 | 
						|
        // | -0.25  0.25 |
 | 
						|
        // | -0.75  0.75 |
 | 
						|
        // |  0.00 -1.00 |
 | 
						|
 | 
						|
        $playerAssignments = array();
 | 
						|
        $totalPreviousPlayers = 0;
 | 
						|
 | 
						|
        $teamAssignmentsListCount = count($teamAssignmentsList);
 | 
						|
 | 
						|
        for ($i = 0; $i < $teamAssignmentsListCount - 1; $i++)
 | 
						|
        {
 | 
						|
            $currentTeam = $teamAssignmentsList[$i];
 | 
						|
 | 
						|
            // Need to add in 0's for all the previous players, since they're not
 | 
						|
            // on this team
 | 
						|
            $currentRowValues = array();
 | 
						|
            $playerAssignments[] = &$currentRowValues;
 | 
						|
 | 
						|
            foreach ($currentTeam as $currentRating)
 | 
						|
            {
 | 
						|
                $currentRowValues[] = PartialPlay::getPartialPlayPercentage($currentRating->getKey());
 | 
						|
                // indicates the player is on the team
 | 
						|
                $totalPreviousPlayers++;
 | 
						|
            }
 | 
						|
 | 
						|
            $nextTeam = $teamAssignmentsList[$i + 1];
 | 
						|
            foreach ($nextTeam as $nextTeamPlayerPair)
 | 
						|
            {
 | 
						|
                // Add a -1 * playing time to represent the difference
 | 
						|
                $currentRowValues[] = -1 * PartialPlay::getPartialPlayPercentage($nextTeamPlayerPair->getKey());
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        $playerTeamAssignmentsMatrix = new Matrix($totalPlayers, $teamAssignmentsListCount - 1, $playerAssignments);
 | 
						|
 | 
						|
        return $playerTeamAssignmentsMatrix;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
?>
 |