mirror of
https://github.com/furyfire/trueskill.git
synced 2025-01-16 01:47:39 +00: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;
|
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
|
// the multiplicative correction of a double-sided truncated Gaussian with unit variance
|
||||||
public static function wWithinMarginScaled($teamPerformanceDifference, $drawMargin, $c)
|
public static function wWithinMarginScaled($teamPerformanceDifference, $drawMargin, $c)
|
||||||
{
|
{
|
||||||
return self::wWithinMargin(teamPerformanceDifference/c, drawMargin/c);
|
return self::wWithinMargin($teamPerformanceDifference/$c, $drawMargin/$c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// From F#:
|
// From F#:
|
||||||
@ -119,7 +119,7 @@ class TruncatedGaussianCorrectionFunctions
|
|||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$vt = vWithinMargin($teamPerformanceDifferenceAbsoluteValue, $drawMargin);
|
$vt = self::vWithinMargin($teamPerformanceDifferenceAbsoluteValue, $drawMargin);
|
||||||
|
|
||||||
return $vt*$vt +
|
return $vt*$vt +
|
||||||
(
|
(
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace Moserware\Skills\TrueSkill;
|
namespace Moserware\Skills\TrueSkill;
|
||||||
|
|
||||||
|
require_once(dirname(__FILE__) . "/../Guard.php");
|
||||||
require_once(dirname(__FILE__) . "/../PairwiseComparison.php");
|
require_once(dirname(__FILE__) . "/../PairwiseComparison.php");
|
||||||
require_once(dirname(__FILE__) . "/../RankSorter.php");
|
require_once(dirname(__FILE__) . "/../RankSorter.php");
|
||||||
require_once(dirname(__FILE__) . "/../Rating.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__) . "/DrawMargin.php");
|
||||||
require_once(dirname(__FILE__) . "/TruncatedGaussianCorrectionFunctions.php");
|
require_once(dirname(__FILE__) . "/TruncatedGaussianCorrectionFunctions.php");
|
||||||
|
|
||||||
|
use Moserware\Skills\Guard;
|
||||||
use Moserware\Skills\PairwiseComparison;
|
use Moserware\Skills\PairwiseComparison;
|
||||||
use Moserware\Skills\RankSorter;
|
use Moserware\Skills\RankSorter;
|
||||||
use Moserware\Skills\Rating;
|
use Moserware\Skills\Rating;
|
||||||
@ -44,7 +46,8 @@ class TwoPlayerTrueSkillCalculator extends SkillCalculator
|
|||||||
array $teams,
|
array $teams,
|
||||||
array $teamRanks)
|
array $teamRanks)
|
||||||
{
|
{
|
||||||
// Basic argument checking
|
// Basic argument checking
|
||||||
|
Guard::argumentNotNull($gameInfo, "gameInfo");
|
||||||
$this->validateTeamCountAndPlayersCountPerTeam($teams);
|
$this->validateTeamCountAndPlayersCountPerTeam($teams);
|
||||||
|
|
||||||
// Make sure things are in order
|
// Make sure things are in order
|
||||||
@ -136,7 +139,8 @@ class TwoPlayerTrueSkillCalculator extends SkillCalculator
|
|||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public function calculateMatchQuality($gameInfo, array $teams)
|
public function calculateMatchQuality($gameInfo, array $teams)
|
||||||
{
|
{
|
||||||
|
Guard::argumentNotNull($gameInfo, "gameInfo");
|
||||||
$this->validateTeamCountAndPlayersCountPerTeam($teams);
|
$this->validateTeamCountAndPlayersCountPerTeam($teams);
|
||||||
|
|
||||||
$team1 = $teams[0];
|
$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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
@ -1,12 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once(dirname(__FILE__) . "/../../PHPSkills/GameInfo.php");
|
require_once(dirname(__FILE__) . "/../../PHPSkills/GameInfo.php");
|
||||||
require_once(dirname(__FILE__) . "/../../PHPSkills/Player.php");
|
require_once(dirname(__FILE__) . "/../../PHPSkills/Player.php");
|
||||||
|
require_once(dirname(__FILE__) . "/../../PHPSkills/Rating.php");
|
||||||
require_once(dirname(__FILE__) . "/../../PHPSkills/Team.php");
|
require_once(dirname(__FILE__) . "/../../PHPSkills/Team.php");
|
||||||
require_once(dirname(__FILE__) . "/../../PHPSkills/Teams.php");
|
require_once(dirname(__FILE__) . "/../../PHPSkills/Teams.php");
|
||||||
require_once(dirname(__FILE__) . "/../../PHPSkills/SkillCalculator.php");
|
require_once(dirname(__FILE__) . "/../../PHPSkills/SkillCalculator.php");
|
||||||
|
|
||||||
use Moserware\Skills\GameInfo;
|
use Moserware\Skills\GameInfo;
|
||||||
use Moserware\Skills\Player;
|
use Moserware\Skills\Player;
|
||||||
|
use Moserware\Skills\Rating;
|
||||||
use Moserware\Skills\Team;
|
use Moserware\Skills\Team;
|
||||||
use Moserware\Skills\Teams;
|
use Moserware\Skills\Teams;
|
||||||
use Moserware\Skills\SkillCalculator;
|
use Moserware\Skills\SkillCalculator;
|
||||||
@ -21,10 +23,31 @@ class TrueSkillCalculatorTests
|
|||||||
public static function testAllTwoPlayerScenarios($testClass, SkillCalculator $calculator)
|
public static function testAllTwoPlayerScenarios($testClass, SkillCalculator $calculator)
|
||||||
{
|
{
|
||||||
self::twoPlayerTestNotDrawn($testClass, $calculator);
|
self::twoPlayerTestNotDrawn($testClass, $calculator);
|
||||||
//self::twoPlayerTestDrawn($testClass, $calculator);
|
self::twoPlayerTestDrawn($testClass, $calculator);
|
||||||
//self::oneOnOneMassiveUpsetDrawTest($testClass, $calculator);
|
self::twoPlayerChessTestNotDrawn($testClass, $calculator);
|
||||||
//self::twoPlayerChessTestNotDrawn($testClass, $calculator);
|
self::oneOnOneMassiveUpsetDrawTest($testClass, $calculator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function testAllTwoTeamScenarios($testClass, SkillCalculator $calculator)
|
||||||
|
{
|
||||||
|
//OneOnTwoSimpleTest(calculator);
|
||||||
|
//OneOnTwoDrawTest(calculator);
|
||||||
|
//OneOnTwoSomewhatBalanced(calculator);
|
||||||
|
//OneOnThreeDrawTest(calculator);
|
||||||
|
//OneOnThreeSimpleTest(calculator);
|
||||||
|
//OneOnSevenSimpleTest(calculator);
|
||||||
|
|
||||||
|
self::twoOnTwoSimpleTest($testClass, $calculator);
|
||||||
|
//TwoOnTwoUnbalancedDrawTest(calculator);
|
||||||
|
//TwoOnTwoDrawTest(calculator);
|
||||||
|
//TwoOnTwoUpsetTest(calculator);
|
||||||
|
|
||||||
|
//ThreeOnTwoTests(calculator);
|
||||||
|
|
||||||
|
//FourOnFourSimpleTest(calculator);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//------------------- Actual Tests ---------------------------
|
//------------------- Actual Tests ---------------------------
|
||||||
// If you see more than 3 digits of precision in the decimal point, then the expected values calculated from
|
// If you see more than 3 digits of precision in the decimal point, then the expected values calculated from
|
||||||
@ -61,6 +84,110 @@ class TrueSkillCalculatorTests
|
|||||||
self::assertMatchQuality($testClass, 0.447, $calculator->calculateMatchQuality($gameInfo, $teams));
|
self::assertMatchQuality($testClass, 0.447, $calculator->calculateMatchQuality($gameInfo, $teams));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function twoPlayerTestDrawn($testClass, SkillCalculator $calculator)
|
||||||
|
{
|
||||||
|
$player1 = new Player(1);
|
||||||
|
$player2 = new Player(2);
|
||||||
|
|
||||||
|
$gameInfo = new GameInfo();
|
||||||
|
|
||||||
|
$team1 = new Team($player1, $gameInfo->getDefaultRating());
|
||||||
|
$team2 = new Team($player2, $gameInfo->getDefaultRating());
|
||||||
|
|
||||||
|
$teams = Teams::concat($team1, $team2);
|
||||||
|
$newRatings = $calculator->calculateNewRatings($gameInfo, $teams, array(1, 1));
|
||||||
|
|
||||||
|
$player1NewRating = $newRatings->getRating($player1);
|
||||||
|
self::assertRating($testClass, 25.0, 6.4575196623173081, $player1NewRating);
|
||||||
|
|
||||||
|
$player2NewRating = $newRatings->getRating($player2);
|
||||||
|
self::assertRating($testClass, 25.0, 6.4575196623173081, $player2NewRating);
|
||||||
|
|
||||||
|
self::assertMatchQuality($testClass, 0.447, $calculator->calculateMatchQuality($gameInfo, $teams));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function twoPlayerChessTestNotDrawn($testClass, SkillCalculator $calculator)
|
||||||
|
{
|
||||||
|
// Inspired by a real bug :-)
|
||||||
|
$player1 = new Player(1);
|
||||||
|
$player2 = new Player(2);
|
||||||
|
$gameInfo = new GameInfo(1200.0, 1200.0 / 3.0, 200.0, 1200.0 / 300.0, 0.03);
|
||||||
|
|
||||||
|
$team1 = new Team($player1, new Rating(1301.0007, 42.9232));
|
||||||
|
$team2 = new Team($player2, new Rating(1188.7560, 42.5570));
|
||||||
|
|
||||||
|
$newRatings = $calculator->calculateNewRatings($gameInfo, Teams::concat($team1, $team2), array(1, 2));
|
||||||
|
|
||||||
|
$player1NewRating = $newRatings->getRating($player1);
|
||||||
|
self::assertRating($testClass, 1304.7820836053318, 42.843513887848658, $player1NewRating);
|
||||||
|
|
||||||
|
$player2NewRating = $newRatings->getRating($player2);
|
||||||
|
self::assertRating($testClass, 1185.0383099003536, 42.485604606897752, $player2NewRating);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function oneOnOneMassiveUpsetDrawTest($testClass, SkillCalculator $calculator)
|
||||||
|
{
|
||||||
|
$player1 = new Player(1);
|
||||||
|
|
||||||
|
$gameInfo = new GameInfo();
|
||||||
|
|
||||||
|
$team1 = new Team($player1, $gameInfo->getDefaultRating());
|
||||||
|
|
||||||
|
$player2 = new Player(2);
|
||||||
|
|
||||||
|
$team2 = new Team($player2, new Rating(50, 12.5));
|
||||||
|
|
||||||
|
$teams = Teams::concat($team1, $team2);
|
||||||
|
|
||||||
|
$newRatingsWinLose = $calculator->calculateNewRatings($gameInfo, $teams, array(1, 1));
|
||||||
|
|
||||||
|
// Winners
|
||||||
|
self::assertRating($testClass, 31.662, 7.137, $newRatingsWinLose->getRating($player1));
|
||||||
|
|
||||||
|
// Losers
|
||||||
|
self::assertRating($testClass, 35.010, 7.910, $newRatingsWinLose->getRating($player2));
|
||||||
|
|
||||||
|
self::assertMatchQuality($testClass, 0.110, $calculator->calculateMatchQuality($gameInfo, $teams));
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Two Team Tests
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private static function twoOnTwoSimpleTest($testClass, SkillCalculator $calculator)
|
||||||
|
{
|
||||||
|
$player1 = new Player(1);
|
||||||
|
$player2 = new Player(2);
|
||||||
|
|
||||||
|
$gameInfo = new GameInfo();
|
||||||
|
|
||||||
|
$team1 = new Team();
|
||||||
|
$team1->addPlayer($player1, $gameInfo->getDefaultRating());
|
||||||
|
$team1->addPlayer($player2, $gameInfo->getDefaultRating());
|
||||||
|
|
||||||
|
$player3 = new Player(3);
|
||||||
|
$player4 = new Player(4);
|
||||||
|
|
||||||
|
$team2 = new Team();
|
||||||
|
$team2->addPlayer($player3, $gameInfo->getDefaultRating());
|
||||||
|
$team2->addPlayer($player4, $gameInfo->getDefaultRating());
|
||||||
|
|
||||||
|
$teams = Teams::concat($team1, $team2);
|
||||||
|
$newRatingsWinLose = $calculator->calculateNewRatings($gameInfo, $teams, array(1, 2));
|
||||||
|
|
||||||
|
// Winners
|
||||||
|
self::assertRating($testClass, 28.108, 7.774, $newRatingsWinLose->getRating($player1));
|
||||||
|
self::assertRating($testClass, 28.108, 7.774, $newRatingsWinLose->getRating($player2));
|
||||||
|
|
||||||
|
// Losers
|
||||||
|
self::assertRating($testClass, 21.892, 7.774, $newRatingsWinLose->getRating($player3));
|
||||||
|
self::assertRating($testClass, 21.892, 7.774, $newRatingsWinLose->getRating($player4));
|
||||||
|
|
||||||
|
self::assertMatchQuality($testClass, 0.447, $calculator->calculateMatchQuality($gameInfo, $teams));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static function assertRating($testClass, $expectedMean, $expectedStandardDeviation, $actual)
|
private static function assertRating($testClass, $expectedMean, $expectedStandardDeviation, $actual)
|
||||||
{
|
{
|
||||||
$testClass->assertEquals($expectedMean, $actual->getMean(), '', self::ERROR_TOLERANCE_TRUESKILL);
|
$testClass->assertEquals($expectedMean, $actual->getMean(), '', self::ERROR_TOLERANCE_TRUESKILL);
|
||||||
|
27
UnitTests/TrueSkill/TwoTeamTrueSkillCalculatorTest.php
Normal file
27
UnitTests/TrueSkill/TwoTeamTrueSkillCalculatorTest.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
require_once 'PHPUnit/Framework.php';
|
||||||
|
require_once 'PHPUnit/TextUI/TestRunner.php';
|
||||||
|
|
||||||
|
require_once(dirname(__FILE__) . '/../../PHPSkills/TrueSkill/TwoTeamTrueSkillCalculator.php');
|
||||||
|
require_once(dirname(__FILE__) . '/TrueSkillCalculatorTests.php');
|
||||||
|
|
||||||
|
use \PHPUnit_Framework_TestCase;
|
||||||
|
use Moserware\Skills\TrueSkill\TwoTeamTrueSkillCalculator;
|
||||||
|
|
||||||
|
class TwoTeamTrueSkillCalculatorTest extends PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
public function testTwoTeamTrueSkillCalculator()
|
||||||
|
{
|
||||||
|
$calculator = new TwoTeamTrueSkillCalculator();
|
||||||
|
|
||||||
|
// We only support two players
|
||||||
|
TrueSkillCalculatorTests::testAllTwoPlayerScenarios($this, $calculator);
|
||||||
|
TrueSkillCalculatorTests::testAllTwoTeamScenarios($this, $calculator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$testSuite = new \PHPUnit_Framework_TestSuite();
|
||||||
|
$testSuite->addTest( new TwoTeamTrueSkillCalculatorTest("testTwoTeamTrueSkillCalculator"));
|
||||||
|
|
||||||
|
\PHPUnit_TextUI_TestRunner::run($testSuite);
|
||||||
|
?>
|
Reference in New Issue
Block a user