This commit is contained in:
Dennis Eichhorn 2023-11-09 00:18:12 +00:00
parent 1675a82410
commit 9018cf5b57
5 changed files with 119 additions and 28 deletions

View File

@ -74,4 +74,37 @@ final class Elo
'elo' => (int) \max($eloNew, $this->MIN_ELO), 'elo' => (int) \max($eloNew, $this->MIN_ELO),
]; ];
} }
/**
* Calculate an approximated win probability based on elo points.
*
* @param int $elo1 Elo of the player we want to calculate the win probability for
* @param int $elo2 Opponent elo
* @param bool $draw Is a draw possible?
*
* @return float
*
* @since 1.0.0
*/
public function winProbability(int $elo1, int $elo2, bool $draw = false) : float
{
return $draw
? -1.0 // @todo: implement
: 1 / (1 + \pow(10, ($elo2 - $elo1) / 400));
}
/**
* Calculate an approximated draw probability based on elo points.
*
* @param int $elo1 Elo of the player we want to calculate the win probability for
* @param int $elo2 Opponent elo
*
* @return float
*
* @since 1.0.0
*/
public function drawProbability(int $elo1, int $elo2) : float
{
return -1.0; // @todo: implement
}
} }

View File

@ -29,18 +29,58 @@ use phpOMS\Math\Stochastic\Distribution\NormalDistribution;
*/ */
class TrueSkill class TrueSkill
{ {
public int $DEFAULT_MU = 25; public const DEFAULT_MU = 25;
public float $DEFAULT_SIGMA = 25 / 3; public const DEFAULT_SIGMA = 25 / 3;
public float $DEFAULT_BETA = 25 / 3 / 2; public const DEFAULT_BETA = 25 / 3 / 2;
public float $DEFAULT_TAU = 25 / 3 / 100; public const DEFAULT_TAU = 25 / 3 / 100;
public float $DEFAULT_DRAW_PROBABILITY = 0.1; public const DEFAULT_DRAW_PROBABILITY = 0.1;
public function __construct() private float $mu = 0.0;
private float $sigma = 0.0;
private float $beta = 0.0;
private float $tau = 0.0;
private float $drawProbability = 0.0;
public function __construct(
float $mu = null,
float $sigma = null,
float $beta = null,
float $tau = null,
float $drawProbability = null)
{ {
$this->mu = $mu ?? self::DEFAULT_MU;
$this->sigma = $sigma ?? self::DEFAULT_SIGMA;
$this->beta = $beta ?? self::DEFAULT_BETA;
$this->tau = $tau ?? self::DEFAULT_TAU;
$this->drawProbability = $drawProbability ?? self::DEFAULT_DRAW_PROBABILITY;
}
public function winProbability(array $team1, array $team2, float $drawMargin = 0.0)
{
$sigmaSum = 0.0;
$mu1 = 0.0;
foreach ($team1 as $player) {
$mu1 += $player->mu;
$sigmaSum += $player->sigma * $player->sigma;
}
$mu2 = 0.0;
foreach ($team2 as $player) {
$mu2 += $player->mu;
$sigmaSum += $player->sigma * $player->sigma;
}
$deltaMu = $mu1 - $mu2;
return NormalDistribution::getCdf(
($deltaMu - $drawMargin) / \sqrt((\count($team1) + \count($team2)) * ($this->beta * $this->beta) + $sigmaSum),
0,
1
);
} }
// Draw margin = epsilon // Draw margin = epsilon

View File

@ -82,9 +82,7 @@ class Matrix implements \ArrayAccess, \Iterator
$this->n = $n; $this->n = $n;
$this->m = $m; $this->m = $m;
for ($i = 0; $i < $m; ++$i) { $this->matrix = \array_fill(0, $m, \array_fill(0, $n, 0));
$this->matrix[$i] = \array_fill(0, $n, 0);
}
} }
/** /**
@ -492,7 +490,7 @@ class Matrix implements \ArrayAccess, \Iterator
$newMatrixArr = $this->matrix; $newMatrixArr = $this->matrix;
foreach ($newMatrixArr as $i => $vector) { foreach ($newMatrixArr as $i => $vector) {
foreach ($vector as $j => $value) { foreach ($vector as $j => $_) {
$newMatrixArr[$i][$j] += $scalar; $newMatrixArr[$i][$j] += $scalar;
} }
} }
@ -542,24 +540,44 @@ class Matrix implements \ArrayAccess, \Iterator
} }
$matrixArr = $matrix->toArray(); $matrixArr = $matrix->toArray();
$newMatrix = new self($this->m, $nDim); $newMatrixArr = \array_fill(0, $this->m, \array_fill(0, $nDim, 0));
$newMatrixArr = $newMatrix->toArray();
for ($i = 0; $i < $this->m; ++$i) { // Row of $this if ($mDim > 10 || $nDim > 10) {
for ($c = 0; $c < $nDim; ++$c) { // Column of $matrix // Standard transposed for iteration over rows -> higher cache hit
$temp = 0; $transposedMatrixArr = array();
for ($k = 0; $k < $mDim; ++$k) {
for ($j = 0; $j < $mDim; ++$j) { // Row of $matrix for ($j = 0; $j < $nDim; ++$j) {
$temp += ($this->matrix[$i][$j] ?? 0) * ($matrixArr[$j][$c] ?? 0); $transposedMatrixArr[$k][$j] = $matrixArr[$j][$k];
} }
}
$newMatrixArr[$i][$c] = $temp; for ($i = 0; $i < $this->m; ++$i) {
for ($j = 0; $j < $this->n; ++$j) {
$temp = 0;
for ($k = 0; $k < $mDim; ++$k) {
$temp += $this->matrix[$i][$k] * $transposedMatrixArr[$i][$k];
}
$newMatrixArr[$i][$j] = $temp;
}
}
} else {
// Standard
for ($i = 0; $i < $this->m; ++$i) {
for ($j = 0; $j < $nDim; ++$j) {
$temp = 0;
for ($k = 0; $k < $mDim; ++$k) {
$temp += $this->matrix[$i][$k] * $matrixArr[$k][$j];
}
$newMatrixArr[$i][$j] = $temp;
}
} }
} }
$newMatrix->setMatrix($newMatrixArr); /* @phpstan-ignore-line */ return self::fromArray($newMatrixArr);
return $newMatrix;
} }
/** /**

View File

@ -53,7 +53,7 @@ final class Vector extends Matrix
*/ */
public function setV(int $m, int | float $value) : void public function setV(int $m, int | float $value) : void
{ {
parent::set($m , 0, $value); $this->matrix[$m][0] = $value;
} }
/** /**
@ -67,7 +67,7 @@ final class Vector extends Matrix
*/ */
public function getV(int $m) : int | float public function getV(int $m) : int | float
{ {
return parent::get($m, 0); return $this->matrix[$m][0];
} }
/** /**
@ -82,7 +82,7 @@ final class Vector extends Matrix
public function setMatrixV(array $vector) : self public function setMatrixV(array $vector) : self
{ {
foreach ($vector as $key => $value) { foreach ($vector as $key => $value) {
$this->setV($key, $value); $this->matrix[$key][0] = $value;
} }
return $this; return $this;
@ -194,9 +194,9 @@ final class Vector extends Matrix
public function cross3(self $vector) : self public function cross3(self $vector) : self
{ {
$crossArray = [ $crossArray = [
$this->getV(1) * $vector->getV(2) - $this->getV(2) * $vector->getV(1), $this->matrix[1][0] * $vector->matrix[2][0] - $this->matrix[2][0] * $vector->matrix[1][0],
$this->getV(2) * $vector->getV(0) - $this->getV(0) * $vector->getV(2), $this->matrix[2][0] * $vector->matrix[0][0] - $this->matrix[0][0] * $vector->matrix[2][0],
$this->getV(0) * $vector->getV(1) - $this->getV(1) * $vector->getV(0), $this->matrix[0][0] * $vector->matrix[1][0] - $this->matrix[1][0] * $vector->matrix[0][0],
]; ];
return self::fromArray($crossArray); return self::fromArray($crossArray);