trueskill/src/Rating.php

75 lines
3.0 KiB
PHP
Raw Normal View History

2022-07-05 13:55:47 +00:00
<?php
namespace DNW\Skills;
// Container for a player's rating.
2022-07-05 13:33:34 +00:00
use DNW\Skills\Numerics\GaussianDistribution;
2022-07-05 14:21:06 +00:00
class Rating implements \Stringable
{
private const CONSERVATIVE_STANDARD_DEVIATION_MULTIPLIER = 3;
/**
* Constructs a rating.
2022-07-05 13:55:47 +00:00
*
* @param float $mean The statistical mean value of the rating (also known as mu).
* @param float $standardDeviation The standard deviation of the rating (also known as s).
* @param float|int $conservativeStandardDeviationMultiplier optional The number of standardDeviations to subtract from the mean to achieve a conservative rating.
*/
public function __construct(private float $mean, private float $standardDeviation, private float|int $conservativeStandardDeviationMultiplier = self::CONSERVATIVE_STANDARD_DEVIATION_MULTIPLIER)
{
}
/**
2023-08-02 09:04:56 +00:00
* The statistical mean value of the rating (also known as mu).
*/
2023-08-02 09:04:56 +00:00
public function getMean(): float
{
return $this->mean;
}
/**
* The standard deviation (the spread) of the rating. This is also known as s.
*/
2023-08-02 09:04:56 +00:00
public function getStandardDeviation(): float
{
return $this->standardDeviation;
}
/**
* A conservative estimate of skill based on the mean and standard deviation.
*/
2023-08-02 09:04:56 +00:00
public function getConservativeRating(): float
{
return $this->mean - $this->conservativeStandardDeviationMultiplier * $this->standardDeviation;
}
2023-08-02 09:36:44 +00:00
public function getPartialUpdate(Rating $prior, Rating $fullPosterior, float $updatePercentage): Rating
{
$priorGaussian = new GaussianDistribution($prior->getMean(), $prior->getStandardDeviation());
2023-08-02 09:36:44 +00:00
$posteriorGaussian = new GaussianDistribution($fullPosterior->getMean(), $fullPosterior->getStandardDeviation());
// From a clarification email from Ralf Herbrich:
2022-07-05 13:33:34 +00:00
// "the idea is to compute a linear interpolation between the prior and posterior skills of each player
// ... in the canonical space of parameters"
$precisionDifference = $posteriorGaussian->getPrecision() - $priorGaussian->getPrecision();
$partialPrecisionDifference = $updatePercentage * $precisionDifference;
2023-08-02 09:04:56 +00:00
$precisionMeanDifference = $posteriorGaussian->getPrecisionMean() - $priorGaussian->getPrecisionMean();
$partialPrecisionMeanDifference = $updatePercentage * $precisionMeanDifference;
$partialPosteriorGaussion = GaussianDistribution::fromPrecisionMean(
$priorGaussian->getPrecisionMean() + $partialPrecisionMeanDifference,
$priorGaussian->getPrecision() + $partialPrecisionDifference
);
return new Rating($partialPosteriorGaussion->getMean(), $partialPosteriorGaussion->getStandardDeviation(), $prior->conservativeStandardDeviationMultiplier);
}
2022-07-05 14:21:06 +00:00
public function __toString(): string
{
return sprintf('mean=%.4f, standardDeviation=%.4f', $this->mean, $this->standardDeviation);
}
2022-07-05 13:55:47 +00:00
}