mirror of
https://github.com/furyfire/trueskill.git
synced 2025-03-29 04:13:22 +00:00
Start of factor graph port. Things don't work yet, but a lot of syntax updates towards PHP
This commit is contained in:
168
PHPSkills/TrueSkill/FactorGraphTrueSkillCalculator.php
Normal file
168
PHPSkills/TrueSkill/FactorGraphTrueSkillCalculator.php
Normal file
@ -0,0 +1,168 @@
|
||||
<?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;
|
||||
}
|
||||
}
|
||||
|
||||
?>
|
Reference in New Issue
Block a user