PHPMD reintroduced.

This commit is contained in:
Jens True 2024-07-04 09:38:03 +00:00
parent 5dc0c7058d
commit cf588f3fc2
28 changed files with 56692 additions and 165 deletions

@ -26,12 +26,13 @@
}, },
"scripts": { "scripts": {
"test": "phpunit", "test": "phpunit",
"document": "phpDocumentor", "document": "phpDocumentor",
"benchmark": "phpbench run --report=default --output=build-artifact", "benchmark": "phpbench run --report=default --output=build-artifact",
"metrics": "vendor/bin/phpmetrics --config=phpmetrics.json", "metrics": "vendor/bin/phpmetrics --config=phpmetrics.json",
"lint": [ "lint": [
"phplint", "phplint",
"phpcs" "phpcs",
"phpmd src/,tests/,benchmark/,examples/ text phpmd.ruleset.xml"
], ],
"analyze": [ "analyze": [
"@analyze-phpstan", "@analyze-phpstan",
@ -47,10 +48,9 @@
], ],
"all": [ "all": [
"@test", "@test",
"@document",
"@benchmark",
"@lint", "@lint",
"@analyze", "@analyze",
"@document",
"@metrics", "@metrics",
"@html" "@html"
] ]

34
composer.lock generated

@ -1377,16 +1377,16 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "1.11.5", "version": "1.11.6",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/phpstan/phpstan.git", "url": "https://github.com/phpstan/phpstan.git",
"reference": "490f0ae1c92b082f154681d7849aee776a7c1443" "reference": "6ac78f1165346c83b4a753f7e4186d969c6ad0ee"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/490f0ae1c92b082f154681d7849aee776a7c1443", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6ac78f1165346c83b4a753f7e4186d969c6ad0ee",
"reference": "490f0ae1c92b082f154681d7849aee776a7c1443", "reference": "6ac78f1165346c83b4a753f7e4186d969c6ad0ee",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1431,7 +1431,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-06-17T15:10:54+00:00" "time": "2024-07-01T15:33:06+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
@ -1756,16 +1756,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "10.5.24", "version": "10.5.25",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "5f124e3e3e561006047b532fd0431bf5bb6b9015" "reference": "831bf82312be6037e811833ddbea0b8de60ea314"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5f124e3e3e561006047b532fd0431bf5bb6b9015", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/831bf82312be6037e811833ddbea0b8de60ea314",
"reference": "5f124e3e3e561006047b532fd0431bf5bb6b9015", "reference": "831bf82312be6037e811833ddbea0b8de60ea314",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -1837,7 +1837,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues", "issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy", "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.24" "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.25"
}, },
"funding": [ "funding": [
{ {
@ -1853,7 +1853,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2024-06-20T13:09:54+00:00" "time": "2024-07-03T05:49:17+00:00"
}, },
{ {
"name": "psalm/plugin-phpunit", "name": "psalm/plugin-phpunit",
@ -2020,16 +2020,16 @@
}, },
{ {
"name": "rector/rector", "name": "rector/rector",
"version": "1.1.1", "version": "1.2.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/rectorphp/rector.git", "url": "https://github.com/rectorphp/rector.git",
"reference": "c930cdb21294f10955ddfc31b720971e8333943d" "reference": "2fa387553db22b6f9bcccf5ff16f2c2c18a52a65"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/rectorphp/rector/zipball/c930cdb21294f10955ddfc31b720971e8333943d", "url": "https://api.github.com/repos/rectorphp/rector/zipball/2fa387553db22b6f9bcccf5ff16f2c2c18a52a65",
"reference": "c930cdb21294f10955ddfc31b720971e8333943d", "reference": "2fa387553db22b6f9bcccf5ff16f2c2c18a52a65",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -2067,7 +2067,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/rectorphp/rector/issues", "issues": "https://github.com/rectorphp/rector/issues",
"source": "https://github.com/rectorphp/rector/tree/1.1.1" "source": "https://github.com/rectorphp/rector/tree/1.2.0"
}, },
"funding": [ "funding": [
{ {
@ -2075,7 +2075,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-06-21T07:51:17+00:00" "time": "2024-07-01T14:24:45+00:00"
}, },
{ {
"name": "sebastian/cli-parser", "name": "sebastian/cli-parser",

97
examples/motogp/goat.php Normal file

@ -0,0 +1,97 @@
<?php
require __DIR__ . "/../../vendor/autoload.php";
use League\Csv\Reader;
use League\Csv\Statement;
use DNW\Skills\TrueSkill\FactorGraphTrueSkillCalculator;
use DNW\Skills\GameInfo;
use DNW\Skills\Player;
use DNW\Skills\Team;
use DNW\Skills\Teams;
//load the CSV document from a stream
$stream = fopen('motogp.csv', 'r');
$csv = Reader::createFromStream($stream);
$csv->setDelimiter(',');
$csv->setHeaderOffset(0);
//build a statement
$stmt = Statement::create()->where(static fn (array $record): bool => $record['category']=="MotoGP" || $record['category']=="500cc");
//$stmt = Statement::create();
/** @var $riders Player[] */
$riders = [];
//query your records from the document
$records = $stmt->process($csv);
$gameInfo = new GameInfo();
$calculator = new FactorGraphTrueSkillCalculator();
$first_record = $records->first();
$year_race = $first_record['year'].'_'.$first_record['sequence'].'_'.$first_record['category'];
$race_rate = [];
foreach($records as $record)
{
if ($year_race !== $record['year'].'_'.$record['sequence'].'_'.$record['category'])
{
//Calculate the old race
$newRatings = $calculator->calculateNewRatings($gameInfo, $teams, $pos);
//update ratings
$highest_rate = 0;
$highest_rider = "";
foreach($riders as $rider) {
//echo $rider['P']->getId().": ". $newRatings->getRating($rider['P'])->getConservativeRating() . PHP_EOL;
$rider['T']->setRating($rider['P'], $newRatings->getRating($rider['P']));
if($newRatings->getRating($rider['P'])->getConservativeRating() > $highest_rate)
{
$highest_rate = $newRatings->getRating($rider['P'])->getConservativeRating();
$highest_rider = $rider['P']->getId();
}
}
echo sprintf('Highest rider: %s => %s', $highest_rider, $highest_rate).PHP_EOL;
foreach($global_riders as $r)
{
$rate = $r['T']->getRating($r['P'])->getConservativeRating();
$race_rate[$year_race][$r['P']->getId()] = $rate;
if (!isset($top_rating[$r['P']->getId()]) || $top_rating[$r['P']->getId()] < $rate)
{
$top_rating[$r['P']->getId()] = $rate;
}
}
//prepare for next race
$year_race = $record['year'].'_'.$record['sequence'].'_'.$record['category'];
$races[] =['year' => $record['year'], 'race'=> $record['sequence'], 'circuit'=> $record['circuit_name']];
echo "New Race: ".$year_race. ' => '. $record['circuit_name'].PHP_EOL;
$riders = [];
$teams = [];
$pos = [];
}
//Is it a new rider?
if(!isset($global_riders[$record['rider']]))
{
$global_riders[$record['rider']]['P'] = new Player($record['rider_name']);
$global_riders[$record['rider']]['T'] = new Team($global_riders[$record['rider']]['P'], $gameInfo->getDefaultRating());
//echo "New Rider: ". $record['rider'] . " => ".$global_riders[$record['rider']]['P']->getId().PHP_EOL;
}
$riders[] = $global_riders[$record['rider']];
$teams[] = $global_riders[$record['rider']]['T'];
//Position or DNF?
$pos[] = $record['position'] >= 1 ? $record['position'] : end($pos);
}
echo "All time top score".PHP_EOL;
asort($top_rating);
foreach($top_rating as $n=>$r)
{
echo sprintf('%s => %s', $n, $r).PHP_EOL;
}

56397
examples/motogp/motogp.csv Normal file

File diff suppressed because it is too large Load Diff

9
phpmd.baseline.xml Normal file

@ -0,0 +1,9 @@
<?xml version="1.0"?>
<phpmd-baseline>
<violation rule="PHPMD\Rule\Design\TooManyPublicMethods" file="src/Numerics/GaussianDistribution.php"/>
<violation rule="PHPMD\Rule\UnusedPrivateMethod" file="src/Numerics/GaussianDistribution.php" method="errorFunctionCumulativeTo"/>
<violation rule="PHPMD\Rule\UnusedPrivateMethod" file="src/Numerics/GaussianDistribution.php" method="inverseErrorFunctionCumulativeTo"/>
<violation rule="PHPMD\Rule\Design\WeightedMethodCount" file="src/Numerics/Matrix.php"/>
<violation rule="PHPMD\Rule\Design\CouplingBetweenObjects" file="src/TrueSkill/FactorGraphTrueSkillCalculator.php"/>
<violation rule="PHPMD\Rule\Design\CouplingBetweenObjects" file="src/TrueSkill/TrueSkillFactorGraph.php"/>
</phpmd-baseline>

49
phpmd.ruleset.xml Normal file

@ -0,0 +1,49 @@
<?xml version="1.0"?>
<ruleset name="TrueSkill custom PHPMD rules"
xmlns="http://pmd.sf.net/ruleset/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0
http://pmd.sf.net/ruleset_xml_schema.xsd"
xsi:noNamespaceSchemaLocation="
http://pmd.sf.net/ruleset_xml_schema.xsd">
<description>
TrueSkill custom PHPMD rules
</description>
<!-- Import the entire unused code rule set -->
<rule ref="rulesets/cleancode.xml">
<exclude name="StaticAccess" />
<exclude name="ElseExpression" />
</rule>
<rule ref="rulesets/codesize.xml" >
<exclude name="TooManyMethods" />
<exclude name="TooManyPublicMethods" />
</rule>
<rule ref="rulesets/codesize.xml/TooManyMethods">
<priority>1</priority>
<properties>
<property name="ignorepattern" value="#^(set|get|test)|test$#i" />
</properties>
</rule>
<rule ref="rulesets/codesize.xml/TooManyPublicMethods">
<priority>1</priority>
<properties>
<property name="ignorepattern" value="#^(set|get|test)|test$#i" />
</properties>
</rule>
<!--rule ref="rulesets/controversial.xml" /-->
<rule ref="rulesets/design.xml" />
<rule ref="rulesets/naming.xml" >
<exclude name="LongClassName" />
<exclude name="ShortClassName" />
<exclude name="ShortVariable" />
<exclude name="LongVariable" />
<exclude name="ShortMethodName" />
</rule>
<rule ref="rulesets/unusedcode.xml" />
<!-- Import entire naming rule set and exclude rules -->
</ruleset>

@ -15,7 +15,7 @@ abstract class Factor
*/ */
private array $messages = []; private array $messages = [];
private readonly HashMap $messageToVariableBinding; private readonly HashMap $msgToVariableBinding;
/** /**
* @var Variable[] $variables * @var Variable[] $variables
@ -24,7 +24,7 @@ abstract class Factor
protected function __construct() protected function __construct()
{ {
$this->messageToVariableBinding = new HashMap(); $this->msgToVariableBinding = new HashMap();
} }
/** /**
@ -70,7 +70,7 @@ abstract class Factor
{ {
Guard::argumentIsValidIndex($messageIndex, count($this->messages), 'messageIndex'); Guard::argumentIsValidIndex($messageIndex, count($this->messages), 'messageIndex');
$message = $this->messages[$messageIndex]; $message = $this->messages[$messageIndex];
$variable = $this->messageToVariableBinding->getValue($message); $variable = $this->msgToVariableBinding->getValue($message);
return $this->updateMessageVariable($message, $variable); return $this->updateMessageVariable($message, $variable);
} }
@ -85,7 +85,7 @@ abstract class Factor
*/ */
public function resetMarginals(): void public function resetMarginals(): void
{ {
$allValues = $this->messageToVariableBinding->getAllValues(); $allValues = $this->msgToVariableBinding->getAllValues();
foreach ($allValues as $currentVariable) { foreach ($allValues as $currentVariable) {
$currentVariable->resetToPrior(); $currentVariable->resetToPrior();
} }
@ -101,7 +101,7 @@ abstract class Factor
Guard::argumentIsValidIndex($messageIndex, count($this->messages), 'messageIndex'); Guard::argumentIsValidIndex($messageIndex, count($this->messages), 'messageIndex');
$message = $this->messages[$messageIndex]; $message = $this->messages[$messageIndex];
$variable = $this->messageToVariableBinding->getValue($message); $variable = $this->msgToVariableBinding->getValue($message);
return $this->sendMessageVariable($message, $variable); return $this->sendMessageVariable($message, $variable);
} }
@ -112,7 +112,7 @@ abstract class Factor
protected function createVariableToMessageBindingWithMessage(Variable $variable, Message $message): Message protected function createVariableToMessageBindingWithMessage(Variable $variable, Message $message): Message
{ {
$this->messageToVariableBinding->setValue($message, $variable); $this->msgToVariableBinding->setValue($message, $variable);
$this->messages[] = $message; $this->messages[] = $message;
$this->variables[] = $variable; $this->variables[] = $variable;

@ -17,7 +17,7 @@ abstract class FactorGraphLayer
/** /**
* @var array<int,array<int,Variable>> * @var array<int,array<int,Variable>>
*/ */
private array $outputVariablesGroups = []; private array $outputVarGroups = [];
/** /**
* @var array<int,array<int,Variable>> * @var array<int,array<int,Variable>>
@ -48,7 +48,7 @@ abstract class FactorGraphLayer
*/ */
public function &getOutputVariablesGroups(): array public function &getOutputVariablesGroups(): array
{ {
return $this->outputVariablesGroups; return $this->outputVarGroups;
} }
/** /**

@ -26,12 +26,12 @@ class FactorList
$listCount = count($this->list); $listCount = count($this->list);
for ($i = 0; $i < $listCount; ++$i) { for ($i = 0; $i < $listCount; ++$i) {
$f = $this->list[$i]; $factor = $this->list[$i];
$numberOfMessages = $f->getNumberOfMessages(); $numberOfMessages = $factor->getNumberOfMessages();
for ($j = 0; $j < $numberOfMessages; ++$j) { for ($j = 0; $j < $numberOfMessages; ++$j) {
$sumLogZ += $f->sendMessageIndex($j); $sumLogZ += $factor->sendMessageIndex($j);
} }
} }

@ -6,20 +6,20 @@ namespace DNW\Skills\FactorGraphs;
class VariableFactory class VariableFactory
{ {
public function __construct(private \Closure $variablePriorInitializer) public function __construct(private \Closure $varPriorInitializer)
{ {
} }
public function createBasicVariable(): Variable public function createBasicVariable(): Variable
{ {
$initializer = $this->variablePriorInitializer; $initializer = $this->varPriorInitializer;
return new Variable($initializer()); return new Variable($initializer());
} }
public function createKeyedVariable(mixed $key): KeyedVariable public function createKeyedVariable(mixed $key): KeyedVariable
{ {
$initializer = $this->variablePriorInitializer; $initializer = $this->varPriorInitializer;
return new KeyedVariable($key, $initializer()); return new KeyedVariable($key, $initializer());
} }

@ -27,7 +27,7 @@ class GameInfo
public function __construct( public function __construct(
private readonly float $initialMean = self::DEFAULT_INITIAL_MEAN, private readonly float $initialMean = self::DEFAULT_INITIAL_MEAN,
private readonly float $initialStandardDeviation = self::DEFAULT_INITIAL_STANDARD_DEVIATION, private readonly float $initialStdDev = self::DEFAULT_INITIAL_STANDARD_DEVIATION,
private readonly float $beta = self::DEFAULT_BETA, private readonly float $beta = self::DEFAULT_BETA,
private readonly float $dynamicsFactor = self::DEFAULT_DYNAMICS_FACTOR, private readonly float $dynamicsFactor = self::DEFAULT_DYNAMICS_FACTOR,
private readonly float $drawProbability = self::DEFAULT_DRAW_PROBABILITY private readonly float $drawProbability = self::DEFAULT_DRAW_PROBABILITY
@ -42,7 +42,7 @@ class GameInfo
public function getInitialStandardDeviation(): float public function getInitialStandardDeviation(): float
{ {
return $this->initialStandardDeviation; return $this->initialStdDev;
} }
public function getBeta(): float public function getBeta(): float
@ -62,6 +62,6 @@ class GameInfo
public function getDefaultRating(): Rating public function getDefaultRating(): Rating
{ {
return new Rating($this->initialMean, $this->initialStandardDeviation); return new Rating($this->initialMean, $this->initialStdDev);
} }
} }

@ -13,15 +13,15 @@ namespace DNW\Skills\Numerics;
class BasicMath class BasicMath
{ {
/** /**
* Squares the input (x^2 = x * x) * Squares the input (input^2 = input * input)
* *
* @param $x Value to square (x) * @param $input Value to square (input)
* *
* @return float The squared value (x^2) * @return float The squared value (input^2)
*/ */
public static function square(float $x): float public static function square(float $input): float
{ {
return $x * $x; return $input * $input;
} }
/** /**

@ -55,7 +55,6 @@ class GaussianDistribution
$this->precisionMean = $this->precision * $this->mean; $this->precisionMean = $this->precision * $this->mean;
} else { } else {
$this->precision = \INF; $this->precision = \INF;
$this->precisionMean = $this->mean == 0 ? 0 : \INF; $this->precisionMean = $this->mean == 0 ? 0 : \INF;
} }
} }
@ -175,7 +174,7 @@ class GaussianDistribution
BasicMath::square($meanDifference) / (2 * $varianceDifference); BasicMath::square($meanDifference) / (2 * $varianceDifference);
} }
public static function at(float $x, float $mean = 0.0, float $standardDeviation = 1.0): float public static function at(float $var, float $mean = 0.0, float $standardDeviation = 1.0): float
{ {
// See http://mathworld.wolfram.com/NormalDistribution.html // See http://mathworld.wolfram.com/NormalDistribution.html
// 1 -(x-mean)^2 / (2*stdDev^2) // 1 -(x-mean)^2 / (2*stdDev^2)
@ -183,22 +182,22 @@ class GaussianDistribution
// stdDev * sqrt(2*pi) // stdDev * sqrt(2*pi)
$multiplier = 1.0 / ($standardDeviation * self::M_SQRT_2_PI); $multiplier = 1.0 / ($standardDeviation * self::M_SQRT_2_PI);
$expPart = exp((-1.0 * BasicMath::square($x - $mean)) / (2 * BasicMath::square($standardDeviation))); $expPart = exp((-1.0 * BasicMath::square($var - $mean)) / (2 * BasicMath::square($standardDeviation)));
return $multiplier * $expPart; return $multiplier * $expPart;
} }
public static function cumulativeTo(float $x): float public static function cumulativeTo(float $var): float
{ {
$result = GaussianDistribution::errorFunctionCumulativeTo(-M_SQRT1_2 * $x); $result = GaussianDistribution::errorFunctionCumulativeTo(-M_SQRT1_2 * $var);
return 0.5 * $result; return 0.5 * $result;
} }
private static function errorFunctionCumulativeTo(float $x): float private static function errorFunctionCumulativeTo(float $var): float
{ {
// Derived from page 265 of Numerical Recipes 3rd Edition // Derived from page 265 of Numerical Recipes 3rd Edition
$z = abs($x); $z = abs($var);
$t = 2.0 / (2.0 + $z); $t = 2.0 / (2.0 + $z);
$ty = 4 * $t - 2; $ty = 4 * $t - 2;
@ -246,7 +245,7 @@ class GaussianDistribution
$ans = $t * exp(-$z * $z + 0.5 * ($coefficients[0] + $ty * $d) - $dd); $ans = $t * exp(-$z * $z + 0.5 * ($coefficients[0] + $ty * $d) - $dd);
return ($x >= 0.0) ? $ans : (2.0 - $ans); return ($var >= 0.0) ? $ans : (2.0 - $ans);
} }
private static function inverseErrorFunctionCumulativeTo(float $p): float private static function inverseErrorFunctionCumulativeTo(float $p): float
@ -272,9 +271,9 @@ class GaussianDistribution
return ($p < 1.0) ? $x : -$x; return ($p < 1.0) ? $x : -$x;
} }
public static function inverseCumulativeTo(float $x, float $mean = 0.0, float $standardDeviation = 1.0): float public static function inverseCumulativeTo(float $var, float $mean = 0.0, float $standardDeviation = 1.0): float
{ {
// From numerical recipes, page 320 // From numerical recipes, page 320
return $mean - M_SQRT2 * $standardDeviation * GaussianDistribution::inverseErrorFunctionCumulativeTo(2 * $x); return $mean - M_SQRT2 * $standardDeviation * GaussianDistribution::inverseErrorFunctionCumulativeTo(2 * $var);
} }
} }

@ -76,10 +76,10 @@ class Matrix
$transposeMatrix = []; $transposeMatrix = [];
$rowMatrixData = $this->matrixRowData; $rowMatrixData = $this->matrixRowData;
for ($currentRowTransposeMatrix = 0; $currentRowTransposeMatrix < $this->columnCount; ++$currentRowTransposeMatrix) { for ($curRowTransposeMx = 0; $curRowTransposeMx < $this->columnCount; ++$curRowTransposeMx) {
for ($currentColumnTransposeMatrix = 0; $currentColumnTransposeMatrix < $this->rowCount; ++$currentColumnTransposeMatrix) { for ($curColTransposeMx = 0; $curColTransposeMx < $this->rowCount; ++$curColTransposeMx) {
$transposeMatrix[$currentRowTransposeMatrix][$currentColumnTransposeMatrix] = $transposeMatrix[$curRowTransposeMx][$curColTransposeMx] =
$rowMatrixData[$currentColumnTransposeMatrix][$currentRowTransposeMatrix]; $rowMatrixData[$curColTransposeMx][$curRowTransposeMx];
} }
} }

@ -8,14 +8,14 @@ class PartialPlay
{ {
public static function getPartialPlayPercentage(Player $player): float public static function getPartialPlayPercentage(Player $player): float
{ {
$partialPlayPercentage = $player->getPartialPlayPercentage(); $partialPlayPct = $player->getPartialPlayPercentage();
// HACK to get around bug near 0 // HACK to get around bug near 0
$smallestPercentage = 0.0001; $smallestPct = 0.0001;
if ($partialPlayPercentage < $smallestPercentage) { if ($partialPlayPct < $smallestPct) {
return $smallestPercentage; return $smallestPct;
} }
return $partialPlayPercentage; return $partialPlayPct;
} }
} }

@ -13,27 +13,27 @@ class Player implements ISupportPartialPlay, ISupportPartialUpdate
private const DEFAULT_PARTIAL_UPDATE_PERCENTAGE = 1.0; private const DEFAULT_PARTIAL_UPDATE_PERCENTAGE = 1.0;
private readonly float $PartialPlayPercentage; private readonly float $PartialPlayPct;
private readonly float $PartialUpdatePercentage; private readonly float $PartialUpdatePct;
/** /**
* Constructs a player. * Constructs a player.
* *
* @param string|int $Id The identifier for the player, such as a name. * @param string|int $Id The identifier for the player, such as a name.
* @param float $partialPlayPercentage The weight percentage to give this player when calculating a new rank. * @param float $partialPlayPct The weight percentage to give this player when calculating a new rank.
* @param float $partialUpdatePercentage Indicated how much of a skill update a player should receive where 0 represents no update and 1.0 represents 100% of the update. * @param float $partialUpdatePct Indicated how much of a skill update a player should receive where 0 represents no update and 1.0 represents 100% of the update.
*/ */
public function __construct( public function __construct(
private readonly mixed $Id, private readonly mixed $Id,
float $partialPlayPercentage = self::DEFAULT_PARTIAL_PLAY_PERCENTAGE, float $partialPlayPct = self::DEFAULT_PARTIAL_PLAY_PERCENTAGE,
float $partialUpdatePercentage = self::DEFAULT_PARTIAL_UPDATE_PERCENTAGE float $partialUpdatePct = self::DEFAULT_PARTIAL_UPDATE_PERCENTAGE
) )
{ {
Guard::argumentInRangeInclusive($partialPlayPercentage, 0.0, 1.0, 'partialPlayPercentage'); Guard::argumentInRangeInclusive($partialPlayPct, 0.0, 1.0, 'partialPlayPercentage');
Guard::argumentInRangeInclusive($partialUpdatePercentage, 0, 1.0, 'partialUpdatePercentage'); Guard::argumentInRangeInclusive($partialUpdatePct, 0, 1.0, 'partialUpdatePercentage');
$this->PartialPlayPercentage = $partialPlayPercentage; $this->PartialPlayPct = $partialPlayPct;
$this->PartialUpdatePercentage = $partialUpdatePercentage; $this->PartialUpdatePct = $partialUpdatePct;
} }
/** /**
@ -49,7 +49,7 @@ class Player implements ISupportPartialPlay, ISupportPartialUpdate
*/ */
public function getPartialPlayPercentage(): float public function getPartialPlayPercentage(): float
{ {
return $this->PartialPlayPercentage; return $this->PartialPlayPct;
} }
/** /**
@ -57,6 +57,6 @@ class Player implements ISupportPartialPlay, ISupportPartialUpdate
*/ */
public function getPartialUpdatePercentage(): float public function getPartialUpdatePercentage(): float
{ {
return $this->PartialUpdatePercentage; return $this->PartialUpdatePct;
} }
} }

@ -171,7 +171,8 @@ class FactorGraphTrueSkillCalculator extends SkillCalculator
$currentColumn = 0; $currentColumn = 0;
for ($i = 0; $i < count($teamAssignmentsList) - 1; ++$i) { $teamCnt = count($teamAssignmentsList);
for ($i = 0; $i < $teamCnt - 1; ++$i) {
$currentTeam = $teamAssignmentsList[$i]; $currentTeam = $teamAssignmentsList[$i];
// Need to add in 0's for all the previous players, since they're not // Need to add in 0's for all the previous players, since they're not

@ -60,9 +60,9 @@ class GaussianLikelihoodFactor extends GaussianFactor
$a * ($marginal2->getPrecision() - $message2Value->getPrecision()) $a * ($marginal2->getPrecision() - $message2Value->getPrecision())
); );
$oldMarginalWithoutMessage = GaussianDistribution::divide($marginal1, $message1Value); $oldMarginalWithoutMsg = GaussianDistribution::divide($marginal1, $message1Value);
$newMarginal = GaussianDistribution::multiply($oldMarginalWithoutMessage, $newMessage); $newMarginal = GaussianDistribution::multiply($oldMarginalWithoutMsg, $newMessage);
// Update the message and marginal // Update the message and marginal

@ -19,9 +19,9 @@ use DNW\Skills\Numerics\GaussianDistribution;
class GaussianWeightedSumFactor extends GaussianFactor class GaussianWeightedSumFactor extends GaussianFactor
{ {
/** /**
* @var array<int[]> $variableIndexOrdersForWeights * @var array<int[]> $varIndexOrdersForWeights
*/ */
private array $variableIndexOrdersForWeights = []; private array $varIndexOrdersForWeights = [];
/** /**
* This following is used for convenience, for example, the first entry is [0, 1, 2] * This following is used for convenience, for example, the first entry is [0, 1, 2]
@ -58,9 +58,9 @@ class GaussianWeightedSumFactor extends GaussianFactor
$variablesToSumLength = count($variablesToSum); $variablesToSumLength = count($variablesToSum);
// 0..n-1 // 0..n-1
$this->variableIndexOrdersForWeights[0] = []; $this->varIndexOrdersForWeights[0] = [];
for ($i = 0; $i < ($variablesToSumLength + 1); ++$i) { for ($i = 0; $i < ($variablesToSumLength + 1); ++$i) {
$this->variableIndexOrdersForWeights[0][] = $i; $this->varIndexOrdersForWeights[0][] = $i;
} }
$variableWeightsLength = count($variableWeights); $variableWeightsLength = count($variableWeights);
@ -113,7 +113,7 @@ class GaussianWeightedSumFactor extends GaussianFactor
$currentWeights[$currentDestinationWeightIndex] = $finalWeight; $currentWeights[$currentDestinationWeightIndex] = $finalWeight;
$currentWeightsSquared[$currentDestinationWeightIndex] = BasicMath::square($finalWeight); $currentWeightsSquared[$currentDestinationWeightIndex] = BasicMath::square($finalWeight);
$variableIndices[count($variableWeights)] = 0; $variableIndices[count($variableWeights)] = 0;
$this->variableIndexOrdersForWeights[] = $variableIndices; $this->varIndexOrdersForWeights[] = $variableIndices;
$this->weights[$weightsIndex] = $currentWeights; $this->weights[$weightsIndex] = $currentWeights;
$this->weightsSquared[$weightsIndex] = $currentWeightsSquared; $this->weightsSquared[$weightsIndex] = $currentWeightsSquared;
@ -160,9 +160,7 @@ class GaussianWeightedSumFactor extends GaussianFactor
// The math works out so that 1/newPrecision = sum of a_i^2 /marginalsWithoutMessages[i] // The math works out so that 1/newPrecision = sum of a_i^2 /marginalsWithoutMessages[i]
$inverseOfNewPrecisionSum = 0.0; $inverseOfNewPrecisionSum = 0.0;
$anotherInverseOfNewPrecisionSum = 0.0;
$weightedMeanSum = 0.0; $weightedMeanSum = 0.0;
$anotherWeightedMeanSum = 0.0;
$weightsSquaredLength = count($weightsSquared); $weightsSquaredLength = count($weightsSquared);
@ -172,16 +170,11 @@ class GaussianWeightedSumFactor extends GaussianFactor
$inverseOfNewPrecisionSum += $weightsSquared[$i] / $inverseOfNewPrecisionSum += $weightsSquared[$i] /
($variables[$i + 1]->getValue()->getPrecision() - $messages[$i + 1]->getValue()->getPrecision()); ($variables[$i + 1]->getValue()->getPrecision() - $messages[$i + 1]->getValue()->getPrecision());
$diff = GaussianDistribution::divide($variables[$i + 1]->getValue(), $messages[$i + 1]->getValue());
$anotherInverseOfNewPrecisionSum += $weightsSquared[$i] / $diff->getPrecision();
$weightedMeanSum += $weights[$i] $weightedMeanSum += $weights[$i]
* *
($variables[$i + 1]->getValue()->getPrecisionMean() - $messages[$i + 1]->getValue()->getPrecisionMean()) ($variables[$i + 1]->getValue()->getPrecisionMean() - $messages[$i + 1]->getValue()->getPrecisionMean())
/ /
($variables[$i + 1]->getValue()->getPrecision() - $messages[$i + 1]->getValue()->getPrecision()); ($variables[$i + 1]->getValue()->getPrecision() - $messages[$i + 1]->getValue()->getPrecision());
$anotherWeightedMeanSum += $weights[$i] * $diff->getPrecisionMean() / $diff->getPrecision();
} }
$newPrecision = 1.0 / $inverseOfNewPrecisionSum; $newPrecision = 1.0 / $inverseOfNewPrecisionSum;
@ -214,7 +207,7 @@ class GaussianWeightedSumFactor extends GaussianFactor
$updatedMessages = []; $updatedMessages = [];
$updatedVariables = []; $updatedVariables = [];
$indicesToUse = $this->variableIndexOrdersForWeights[$messageIndex]; $indicesToUse = $this->varIndexOrdersForWeights[$messageIndex];
// The tricky part here is that we have to put the messages and variables in the same // The tricky part here is that we have to put the messages and variables in the same
// order as the weights. Thankfully, the weights and messages share the same index numbers, // order as the weights. Thankfully, the weights and messages share the same index numbers,
// so we just need to make sure they're consistent // so we just need to make sure they're consistent

@ -15,8 +15,8 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
{ {
public function __construct( public function __construct(
TrueSkillFactorGraph $parentGraph, TrueSkillFactorGraph $parentGraph,
private readonly TeamPerformancesToTeamPerformanceDifferencesLayer $TeamPerformancesToTeamPerformanceDifferencesLayer, private readonly TeamPerformancesToTeamPerformanceDifferencesLayer $teamPerformancesToTeamPerformanceDifferencesLayer,
private readonly TeamDifferencesComparisonLayer $TeamDifferencesComparisonLayer private readonly TeamDifferencesComparisonLayer $teamDifferencesComparisonLayer
) )
{ {
parent::__construct($parentGraph); parent::__construct($parentGraph);
@ -25,20 +25,20 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
public function getLocalFactors(): array public function getLocalFactors(): array
{ {
return array_merge( return array_merge(
$this->TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors(), $this->teamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors(),
$this->TeamDifferencesComparisonLayer->getLocalFactors() $this->teamDifferencesComparisonLayer->getLocalFactors()
); );
} }
public function buildLayer(): void public function buildLayer(): void
{ {
$inputVariablesGroups = $this->getInputVariablesGroups(); $inputVariablesGroups = $this->getInputVariablesGroups();
$this->TeamPerformancesToTeamPerformanceDifferencesLayer->setInputVariablesGroups($inputVariablesGroups); $this->teamPerformancesToTeamPerformanceDifferencesLayer->setInputVariablesGroups($inputVariablesGroups);
$this->TeamPerformancesToTeamPerformanceDifferencesLayer->buildLayer(); $this->teamPerformancesToTeamPerformanceDifferencesLayer->buildLayer();
$teamDifferencesOutputVariablesGroups = $this->TeamPerformancesToTeamPerformanceDifferencesLayer->getOutputVariablesGroups(); $teamDifferencesOutputVariablesGroups = $this->teamPerformancesToTeamPerformanceDifferencesLayer->getOutputVariablesGroups();
$this->TeamDifferencesComparisonLayer->setInputVariablesGroups($teamDifferencesOutputVariablesGroups); $this->teamDifferencesComparisonLayer->setInputVariablesGroups($teamDifferencesOutputVariablesGroups);
$this->TeamDifferencesComparisonLayer->buildLayer(); $this->teamDifferencesComparisonLayer->buildLayer();
} }
public function createPriorSchedule(): ?ScheduleSequence public function createPriorSchedule(): ?ScheduleSequence
@ -56,9 +56,9 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
} }
// When dealing with differences, there are always (n-1) differences, so add in the 1 // When dealing with differences, there are always (n-1) differences, so add in the 1
$totalTeamDifferences = count($this->TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors()); $totalTeamDifferences = count($this->teamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors());
$localFactors = $this->TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors(); $localFactors = $this->teamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors();
$firstDifferencesFactor = $localFactors[0]; $firstDifferencesFactor = $localFactors[0];
$lastDifferencesFactor = $localFactors[$totalTeamDifferences - 1]; $lastDifferencesFactor = $localFactors[$totalTeamDifferences - 1];
@ -83,8 +83,8 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
private function createTwoTeamInnerPriorLoopSchedule(): ScheduleSequence private function createTwoTeamInnerPriorLoopSchedule(): ScheduleSequence
{ {
$teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = $this->TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors(); $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = $this->teamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors();
$teamDifferencesComparisonLayerLocalFactors = $this->TeamDifferencesComparisonLayer->getLocalFactors(); $teamDifferencesComparisonLayerLocalFactors = $this->teamDifferencesComparisonLayer->getLocalFactors();
$firstPerfToTeamDiff = $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[0]; $firstPerfToTeamDiff = $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[0];
$firstTeamDiffComparison = $teamDifferencesComparisonLayerLocalFactors[0]; $firstTeamDiffComparison = $teamDifferencesComparisonLayerLocalFactors[0];
@ -109,13 +109,13 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
private function createMultipleTeamInnerPriorLoopSchedule(): ScheduleLoop private function createMultipleTeamInnerPriorLoopSchedule(): ScheduleLoop
{ {
$totalTeamDifferences = count($this->TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors()); $totalTeamDifferences = count($this->teamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors());
$forwardScheduleList = []; $forwardScheduleList = [];
for ($i = 0; $i < $totalTeamDifferences - 1; ++$i) { for ($i = 0; $i < $totalTeamDifferences - 1; ++$i) {
$teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = $this->TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors(); $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = $this->teamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors();
$teamDifferencesComparisonLayerLocalFactors = $this->TeamDifferencesComparisonLayer->getLocalFactors(); $teamDifferencesComparisonLayerLocalFactors = $this->teamDifferencesComparisonLayer->getLocalFactors();
$currentTeamPerfToTeamPerfDiff = $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[$i]; $currentTeamPerfToTeamPerfDiff = $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[$i];
$currentTeamDiffComparison = $teamDifferencesComparisonLayerLocalFactors[$i]; $currentTeamDiffComparison = $teamDifferencesComparisonLayerLocalFactors[$i];
@ -151,8 +151,8 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
$backwardScheduleList = []; $backwardScheduleList = [];
for ($i = 0; $i < $totalTeamDifferences - 1; ++$i) { for ($i = 0; $i < $totalTeamDifferences - 1; ++$i) {
$teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = $this->TeamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors(); $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors = $this->teamPerformancesToTeamPerformanceDifferencesLayer->getLocalFactors();
$teamDifferencesComparisonLayerLocalFactors = $this->TeamDifferencesComparisonLayer->getLocalFactors(); $teamDifferencesComparisonLayerLocalFactors = $this->teamDifferencesComparisonLayer->getLocalFactors();
$differencesFactor = $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[$totalTeamDifferences - 1 - $i]; $differencesFactor = $teamPerformancesToTeamPerformanceDifferencesLayerLocalFactors[$totalTeamDifferences - 1 - $i];
$comparisonFactor = $teamDifferencesComparisonLayerLocalFactors[$totalTeamDifferences - 1 - $i]; $comparisonFactor = $teamDifferencesComparisonLayerLocalFactors[$totalTeamDifferences - 1 - $i];

@ -27,8 +27,8 @@ class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLaye
$this->addLayerFactor($newSumFactor); $this->addLayerFactor($newSumFactor);
// REVIEW: Does it make sense to have groups of one? // REVIEW: Does it make sense to have groups of one?
$outputVariablesGroups = &$this->getOutputVariablesGroups(); $outputVarGroups = &$this->getOutputVariablesGroups();
$outputVariablesGroups[] = [$teamPerformance]; $outputVarGroups[] = [$teamPerformance];
} }
} }

@ -34,12 +34,12 @@ class PlayerPriorValuesToSkillsLayer extends TrueSkillFactorGraphLayer
$localCurrentTeam = $currentTeam; $localCurrentTeam = $currentTeam;
$currentTeamSkills = []; $currentTeamSkills = [];
$currentTeamAllPlayers = $localCurrentTeam->getAllPlayers(); $curTeamAllPlayers = $localCurrentTeam->getAllPlayers();
foreach ($currentTeamAllPlayers as $currentTeamPlayer) { foreach ($curTeamAllPlayers as $curTeamPlayer) {
$localCurrentTeamPlayer = $currentTeamPlayer; $localCurTeamPlayer = $curTeamPlayer;
$currentTeamPlayerRating = $currentTeam->getRating($localCurrentTeamPlayer); $curTeamPlayerRating = $currentTeam->getRating($localCurTeamPlayer);
$playerSkill = $this->createSkillOutputVariable($localCurrentTeamPlayer); $playerSkill = $this->createSkillOutputVariable($localCurTeamPlayer);
$priorFactor = $this->createPriorFactor($currentTeamPlayerRating, $playerSkill); $priorFactor = $this->createPriorFactor($curTeamPlayerRating, $playerSkill);
$this->addLayerFactor($priorFactor); $this->addLayerFactor($priorFactor);
$currentTeamSkills[] = $playerSkill; $currentTeamSkills[] = $playerSkill;
} }

@ -15,25 +15,25 @@ class PlayerSkillsToPerformancesLayer extends TrueSkillFactorGraphLayer
{ {
public function buildLayer(): void public function buildLayer(): void
{ {
$inputVariablesGroups = $this->getInputVariablesGroups(); $inputVarGroups = $this->getInputVariablesGroups();
$outputVariablesGroups = &$this->getOutputVariablesGroups(); $outputVarGroups = &$this->getOutputVariablesGroups();
foreach ($inputVariablesGroups as $currentTeam) { foreach ($inputVarGroups as $currentTeam) {
$currentTeamPlayerPerformances = []; $currentTeamPlayerPerformances = [];
/** /**
* @var Variable $playerSkillVariable * @var Variable $playerSkillVar
*/ */
foreach ($currentTeam as $playerSkillVariable) { foreach ($currentTeam as $playerSkillVar) {
$localPlayerSkillVariable = $playerSkillVariable; $localPlayerSkillVar = $playerSkillVar;
$currentPlayer = ($localPlayerSkillVariable instanceof KeyedVariable) ? $localPlayerSkillVariable->getKey() : ""; $currentPlayer = ($localPlayerSkillVar instanceof KeyedVariable) ? $localPlayerSkillVar->getKey() : "";
$playerPerformance = $this->createOutputVariable($currentPlayer); $playerPerformance = $this->createOutputVariable($currentPlayer);
$newLikelihoodFactor = $this->createLikelihood($localPlayerSkillVariable, $playerPerformance); $newLikelihoodFactor = $this->createLikelihood($localPlayerSkillVar, $playerPerformance);
$this->addLayerFactor($newLikelihoodFactor); $this->addLayerFactor($newLikelihoodFactor);
$currentTeamPlayerPerformances[] = $playerPerformance; $currentTeamPlayerPerformances[] = $playerPerformance;
} }
$outputVariablesGroups[] = $currentTeamPlayerPerformances; $outputVarGroups[] = $currentTeamPlayerPerformances;
} }
} }

@ -118,9 +118,9 @@ class TrueSkillFactorGraph extends FactorGraph
$allLayersReverse = array_reverse($this->layers); $allLayersReverse = array_reverse($this->layers);
foreach ($allLayersReverse as $currentLayer) { foreach ($allLayersReverse as $currentLayer) {
$currentPosteriorSchedule = $currentLayer->createPosteriorSchedule(); $curPosteriorSchedule = $currentLayer->createPosteriorSchedule();
if ($currentPosteriorSchedule != NULL) { if ($curPosteriorSchedule != NULL) {
$fullSchedule[] = $currentPosteriorSchedule; $fullSchedule[] = $curPosteriorSchedule;
} }
} }

@ -102,33 +102,33 @@ 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(float $teamPerformanceDifference, float $drawMargin, float $c): float public static function wWithinMarginScaled(float $teamPerformanceDiff, float $drawMargin, float $c): float
{ {
return self::wWithinMargin($teamPerformanceDifference / $c, $drawMargin / $c); return self::wWithinMargin($teamPerformanceDiff / $c, $drawMargin / $c);
} }
// From F#: // From F#:
public static function wWithinMargin(float $teamPerformanceDifference, float $drawMargin): float public static function wWithinMargin(float $teamPerformanceDiff, float $drawMargin): float
{ {
$teamPerformanceDifferenceAbsoluteValue = abs($teamPerformanceDifference); $teamPerformanceDiffAbsValue = abs($teamPerformanceDiff);
$denominator = GaussianDistribution::cumulativeTo($drawMargin - $teamPerformanceDifferenceAbsoluteValue) $denominator = GaussianDistribution::cumulativeTo($drawMargin - $teamPerformanceDiffAbsValue)
- -
GaussianDistribution::cumulativeTo(-$drawMargin - $teamPerformanceDifferenceAbsoluteValue); GaussianDistribution::cumulativeTo(-$drawMargin - $teamPerformanceDiffAbsValue);
if ($denominator < 2.222758749e-162) { if ($denominator < 2.222758749e-162) {
return 1.0; return 1.0;
} }
$vt = self::vWithinMargin($teamPerformanceDifferenceAbsoluteValue, $drawMargin); $vt = self::vWithinMargin($teamPerformanceDiffAbsValue, $drawMargin);
return $vt * $vt + return $vt * $vt +
(($drawMargin - $teamPerformanceDifferenceAbsoluteValue) (($drawMargin - $teamPerformanceDiffAbsValue)
* *
GaussianDistribution::at( GaussianDistribution::at(
$drawMargin - $teamPerformanceDifferenceAbsoluteValue $drawMargin - $teamPerformanceDiffAbsValue
) )
- (-$drawMargin - $teamPerformanceDifferenceAbsoluteValue) - (-$drawMargin - $teamPerformanceDiffAbsValue)
* *
GaussianDistribution::at(-$drawMargin - $teamPerformanceDifferenceAbsoluteValue)) / $denominator; GaussianDistribution::at(-$drawMargin - $teamPerformanceDiffAbsValue)) / $denominator;
} }
} }

@ -125,9 +125,9 @@ class TwoTeamTrueSkillCalculator extends SkillCalculator
} }
$selfTeamAllPlayers = $selfTeam->getAllPlayers(); $selfTeamAllPlayers = $selfTeam->getAllPlayers();
foreach ($selfTeamAllPlayers as $selfTeamCurrentPlayer) { foreach ($selfTeamAllPlayers as $selfTeamCurPlayer) {
$localSelfTeamCurrentPlayer = $selfTeamCurrentPlayer; $localSelfTeamCurPlayer = $selfTeamCurPlayer;
$previousPlayerRating = $selfTeam->getRating($localSelfTeamCurrentPlayer); $previousPlayerRating = $selfTeam->getRating($localSelfTeamCurPlayer);
$meanMultiplier = (BasicMath::square($previousPlayerRating->getStandardDeviation()) + $tauSquared) / $c; $meanMultiplier = (BasicMath::square($previousPlayerRating->getStandardDeviation()) + $tauSquared) / $c;
$stdDevMultiplier = (BasicMath::square($previousPlayerRating->getStandardDeviation()) + $tauSquared) / BasicMath::square($c); $stdDevMultiplier = (BasicMath::square($previousPlayerRating->getStandardDeviation()) + $tauSquared) / BasicMath::square($c);
@ -139,7 +139,7 @@ class TwoTeamTrueSkillCalculator extends SkillCalculator
(BasicMath::square($previousPlayerRating->getStandardDeviation()) + $tauSquared) * (1 - $w * $stdDevMultiplier) (BasicMath::square($previousPlayerRating->getStandardDeviation()) + $tauSquared) * (1 - $w * $stdDevMultiplier)
); );
$newPlayerRatings->setRating($localSelfTeamCurrentPlayer, new Rating($newMean, $newStdDev)); $newPlayerRatings->setRating($localSelfTeamCurPlayer, new Rating($newMean, $newStdDev));
} }
} }

@ -7,6 +7,7 @@ namespace DNW\Skills\Tests;
use DNW\Skills\HashMap; use DNW\Skills\HashMap;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\CoversClass;
use stdClass;
#[CoversClass(HashMap::class)] #[CoversClass(HashMap::class)]
class HashMapTest extends TestCase class HashMapTest extends TestCase
@ -19,8 +20,8 @@ class HashMapTest extends TestCase
$this->assertEquals([], $h->getAllValues()); $this->assertEquals([], $h->getAllValues());
$o1 = new \stdClass(); $o1 = new stdClass();
$o2 = new \stdClass(); $o2 = new stdClass();
$h->setValue($o1, 1); $h->setValue($o1, 1);
$h->setvalue($o2, 2); $h->setvalue($o2, 2);

@ -9,7 +9,6 @@ use DNW\Skills\Player;
use DNW\Skills\Rating; use DNW\Skills\Rating;
use DNW\Skills\SkillCalculator; use DNW\Skills\SkillCalculator;
use DNW\Skills\Team; use DNW\Skills\Team;
use DNW\Skills\Teams;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
class TrueSkillCalculatorTests class TrueSkillCalculatorTests
@ -70,7 +69,6 @@ class TrueSkillCalculatorTests
// online calculator at http://atom.research.microsoft.com/trueskill/rankcalculator.aspx // online calculator at http://atom.research.microsoft.com/trueskill/rankcalculator.aspx
// //
// All match quality expected values came from the online calculator // All match quality expected values came from the online calculator
// In both cases, there may be some discrepancy after the first decimal point. I think this is due to my implementation // In both cases, there may be some discrepancy after the first decimal point. I think this is due to my implementation
// using slightly higher precision in GaussianDistribution. // using slightly higher precision in GaussianDistribution.
@ -849,24 +847,7 @@ class TrueSkillCalculatorTests
$team15 = new Team($player15, $gameInfo->getDefaultRating()); $team15 = new Team($player15, $gameInfo->getDefaultRating());
$team16 = new Team($player16, $gameInfo->getDefaultRating()); $team16 = new Team($player16, $gameInfo->getDefaultRating());
$teams = [ $teams = [$team1, $team2, $team3, $team4, $team5, $team6, $team7, $team8, $team9, $team10, $team11, $team12, $team13, $team14, $team15, $team16];
$team1,
$team2,
$team3,
$team4,
$team5,
$team6,
$team7,
$team8,
$team9,
$team10,
$team11,
$team12,
$team13,
$team14,
$team15,
$team16
];
$newRatings = $calculator->calculateNewRatings( $newRatings = $calculator->calculateNewRatings(
$gameInfo, $gameInfo,