mirror of
https://github.com/furyfire/trueskill.git
synced 2025-07-04 03:32:47 +02:00
Compare commits
15 Commits
2212d45f61
...
master
Author | SHA1 | Date | |
---|---|---|---|
1d06fa0b0a | |||
a097fa7f71 | |||
ad9d7f94de | |||
1725979b1c | |||
ac7e3f5c2d | |||
adc587ac0d | |||
d729caac1f | |||
bd9fccb87b | |||
55656b7889 | |||
38f332b223 | |||
b38a9656eb | |||
3c617e9869 | |||
5a414b8307 | |||
e2620fde53 | |||
22002891c5 |
7
.gitignore
vendored
7
.gitignore
vendored
@ -1,6 +1,5 @@
|
|||||||
.*/
|
vendor/
|
||||||
vendor
|
|
||||||
.*.cache/
|
.*.cache/
|
||||||
*.phar
|
|
||||||
.phpdoc/
|
.phpdoc/
|
||||||
output/
|
output/
|
||||||
|
tools/
|
12
.phive/phars.xml
Normal file
12
.phive/phars.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<phive xmlns="https://phar.io/phive">
|
||||||
|
<phar name="phpmd" version="^2.15.0" installed="2.15.0" location="./tools/phpmd" copy="false"/>
|
||||||
|
<phar name="phpstan" version="^2.1.12" installed="2.1.17" location="./tools/phpstan" copy="false"/>
|
||||||
|
<phar name="psalm" version="^7.0.0-beta6" installed="7.0.0-beta9" location="./tools/psalm" copy="false"/>
|
||||||
|
<phar name="phpcs" version="^3.12.2" installed="3.13.2" location="./tools/phpcs" copy="false"/>
|
||||||
|
<phar name="phpcbf" version="^3.12.2" installed="3.13.2" location="./tools/phpcbf" copy="false"/>
|
||||||
|
<phar name="phpdocumentor" version="^3.7.1" installed="3.8.0" location="./tools/phpdocumentor" copy="false"/>
|
||||||
|
<phar name="phpbench" version="^1.4.1" installed="1.4.1" location="./tools/phpbench" copy="false"/>
|
||||||
|
<phar name="infection" version="^0.29.14" installed="0.29.14" location="./tools/infection" copy="false"/>
|
||||||
|
<phar name="phpunit" version="^12.1.3" installed="12.2.3" location="./tools/phpunit" copy="false"/>
|
||||||
|
</phive>
|
32
.phpcs.xml
32
.phpcs.xml
@ -6,6 +6,11 @@
|
|||||||
<file>tests/</file>
|
<file>tests/</file>
|
||||||
<file>benchmark/</file>
|
<file>benchmark/</file>
|
||||||
|
|
||||||
|
<arg name="basepath" value="."/>
|
||||||
|
<arg name="colors"/>
|
||||||
|
<arg name="parallel" value="8"/>
|
||||||
|
<arg name="report" value="emacs"/>
|
||||||
|
<arg value="p"/>
|
||||||
|
|
||||||
<rule ref="PSR1">
|
<rule ref="PSR1">
|
||||||
<exclude name="Generic.Files.LineLength"/>
|
<exclude name="Generic.Files.LineLength"/>
|
||||||
@ -18,23 +23,19 @@
|
|||||||
<exclude name="Generic.Files.LowercasedFilename.NotFound"/>
|
<exclude name="Generic.Files.LowercasedFilename.NotFound"/>
|
||||||
<exclude name="Generic.PHP.ClosingPHPTag.NotFound"/>
|
<exclude name="Generic.PHP.ClosingPHPTag.NotFound"/>
|
||||||
<exclude name="Generic.Files.EndFileNoNewline.Found"/>
|
<exclude name="Generic.Files.EndFileNoNewline.Found"/>
|
||||||
<exclude name="Generic.Files.EndFileNoNewline.Found"/>
|
|
||||||
<exclude name="Generic.Arrays.DisallowShortArraySyntax.Found"/>
|
<exclude name="Generic.Arrays.DisallowShortArraySyntax.Found"/>
|
||||||
<exclude name="Generic.Functions.OpeningFunctionBraceKernighanRitchie.BraceOnNewLine"/>
|
<exclude name="Generic.Functions.OpeningFunctionBraceKernighanRitchie.BraceOnNewLine"/>
|
||||||
<exclude name="Generic.Classes.OpeningBraceSameLine.BraceOnNewLine"/>
|
<exclude name="Generic.Classes.OpeningBraceSameLine.BraceOnNewLine"/>
|
||||||
<exclude name="Generic.PHP.LowerCaseConstant.Found"/>
|
<exclude name="Generic.PHP.LowerCaseConstant.Found"/>
|
||||||
<exclude name="Generic.Formatting.SpaceAfterCast"/>
|
|
||||||
<exclude name="Generic.Formatting.MultipleStatementAlignment.NotSameWarning"/>
|
<exclude name="Generic.Formatting.MultipleStatementAlignment.NotSameWarning"/>
|
||||||
<exclude name="Generic.Commenting.DocComment.MissingShort"/>
|
<exclude name="Generic.Commenting.DocComment.MissingShort"/>
|
||||||
<exclude name="Generic.NamingConventions.AbstractClassNamePrefix.Missing"/>
|
<exclude name="Generic.NamingConventions.AbstractClassNamePrefix.Missing"/>
|
||||||
<exclude name="Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed"/>
|
<exclude name="Generic.CodeAnalysis.ForLoopWithTestFunctionCall.NotAllowed"/>
|
||||||
<exclude name="Generic.NamingConventions.InterfaceNameSuffix.Missing"/>
|
<exclude name="Generic.NamingConventions.InterfaceNameSuffix.Missing"/>
|
||||||
<exclude name="Generic.Commenting.Todo.TaskFound"/>
|
|
||||||
<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUse"/>
|
<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUse"/>
|
||||||
<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed"/>
|
<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInExtendedClassAfterLastUsed"/>
|
||||||
<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed"/>
|
<exclude name="Generic.CodeAnalysis.UnusedFunctionParameter.FoundInImplementedInterfaceAfterLastUsed"/>
|
||||||
<exclude name="Generic.Formatting.SpaceBeforeCast.NoSpace"/>
|
|
||||||
<exclude name="Generic.CodeAnalysis.UselessOverridingMethod.Found"/>
|
|
||||||
<exclude name="Squiz.Functions.MultiLineFunctionDeclaration.NewlineBeforeOpenBrace"/>
|
<exclude name="Squiz.Functions.MultiLineFunctionDeclaration.NewlineBeforeOpenBrace"/>
|
||||||
</rule>
|
</rule>
|
||||||
|
|
||||||
@ -50,4 +51,25 @@
|
|||||||
</property>
|
</property>
|
||||||
</properties>
|
</properties>
|
||||||
</rule>
|
</rule>
|
||||||
|
|
||||||
|
<rule ref="Generic.Formatting.SpaceAfterCast">
|
||||||
|
<properties>
|
||||||
|
<property name="spacing" value="0"/>
|
||||||
|
</properties>
|
||||||
|
</rule>
|
||||||
|
|
||||||
|
<!-- Do not allow unreachable code. -->
|
||||||
|
<rule ref="Squiz.PHP.NonExecutableCode"/>
|
||||||
|
|
||||||
|
<!-- Do not allow ambiguous conditions. -->
|
||||||
|
<rule ref="Generic.CodeAnalysis.RequireExplicitBooleanOperatorPrecedence"/>
|
||||||
|
|
||||||
|
<!-- The testing bootstrap file uses string concats to stop IDEs seeing the class aliases -->
|
||||||
|
<rule ref="Generic.Strings.UnnecessaryStringConcat" />
|
||||||
|
|
||||||
|
<!-- This test file specifically *needs* Windows line endings for testing purposes. -->
|
||||||
|
<rule ref="Generic.Files.LineEndings.InvalidEOLChar" />
|
||||||
|
|
||||||
|
<!-- Avoid false positive with this sniff detecting itself -->
|
||||||
|
<rule ref="Generic.Commenting.Todo"/>
|
||||||
</ruleset>
|
</ruleset>
|
12
.phplint.yml
12
.phplint.yml
@ -1,12 +0,0 @@
|
|||||||
path:
|
|
||||||
- src/
|
|
||||||
- tests/
|
|
||||||
- benchmark/
|
|
||||||
jobs: 10
|
|
||||||
extensions:
|
|
||||||
- php
|
|
||||||
exclude:
|
|
||||||
- vendor
|
|
||||||
warning: true
|
|
||||||
memory-limit: -1
|
|
||||||
log-junit: "output/lint.xml"
|
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"php.version": "8.4"
|
||||||
|
}
|
4317
CodeStandard.md
Normal file
4317
CodeStandard.md
Normal file
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,7 @@ use DNW\Skills\Team;
|
|||||||
/**
|
/**
|
||||||
* Basic Benchmarks.
|
* Basic Benchmarks.
|
||||||
*/
|
*/
|
||||||
class BasicBench
|
final class BasicBench
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* To benchmark performance when using TwoPlayerTrueSkillCalculator
|
* To benchmark performance when using TwoPlayerTrueSkillCalculator
|
||||||
@ -41,9 +41,11 @@ class BasicBench
|
|||||||
|
|
||||||
$team1 = new Team($p1, $newRatings->getRating($p1));
|
$team1 = new Team($p1, $newRatings->getRating($p1));
|
||||||
$team2 = new Team($p2, $newRatings->getRating($p2));
|
$team2 = new Team($p2, $newRatings->getRating($p2));
|
||||||
|
|
||||||
$newRatings->getRating($p1)->getConservativeRating();
|
ob_start();
|
||||||
$newRatings->getRating($p2)->getConservativeRating();
|
echo $newRatings->getRating($p1)->getConservativeRating();
|
||||||
|
echo $newRatings->getRating($p2)->getConservativeRating();
|
||||||
|
ob_clean();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,8 +75,10 @@ class BasicBench
|
|||||||
$team1 = new Team($p1, $newRatings->getRating($p1));
|
$team1 = new Team($p1, $newRatings->getRating($p1));
|
||||||
$team2 = new Team($p2, $newRatings->getRating($p2));
|
$team2 = new Team($p2, $newRatings->getRating($p2));
|
||||||
|
|
||||||
$newRatings->getRating($p1)->getConservativeRating();
|
ob_start();
|
||||||
$newRatings->getRating($p2)->getConservativeRating();
|
echo $newRatings->getRating($p1)->getConservativeRating();
|
||||||
|
echo $newRatings->getRating($p2)->getConservativeRating();
|
||||||
|
ob_clean();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,8 +108,10 @@ class BasicBench
|
|||||||
$team1 = new Team($p1, $newRatings->getRating($p1));
|
$team1 = new Team($p1, $newRatings->getRating($p1));
|
||||||
$team2 = new Team($p2, $newRatings->getRating($p2));
|
$team2 = new Team($p2, $newRatings->getRating($p2));
|
||||||
|
|
||||||
$newRatings->getRating($p1)->getConservativeRating();
|
ob_start();
|
||||||
$newRatings->getRating($p2)->getConservativeRating();
|
echo $newRatings->getRating($p1)->getConservativeRating();
|
||||||
|
echo $newRatings->getRating($p2)->getConservativeRating();
|
||||||
|
ob_clean();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,9 +144,11 @@ class BasicBench
|
|||||||
$team2 = new Team($p2, $newRatings->getRating($p2));
|
$team2 = new Team($p2, $newRatings->getRating($p2));
|
||||||
$team3 = new Team($p3, $newRatings->getRating($p3));
|
$team3 = new Team($p3, $newRatings->getRating($p3));
|
||||||
|
|
||||||
$newRatings->getRating($p1)->getConservativeRating();
|
ob_start();
|
||||||
$newRatings->getRating($p2)->getConservativeRating();
|
echo $newRatings->getRating($p1)->getConservativeRating();
|
||||||
$newRatings->getRating($p3)->getConservativeRating();
|
echo $newRatings->getRating($p2)->getConservativeRating();
|
||||||
|
echo $newRatings->getRating($p3)->getConservativeRating();
|
||||||
|
ob_clean();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,19 @@
|
|||||||
{
|
{
|
||||||
"name": "dnw/php-trueskill",
|
"name": "dnw/php-trueskill",
|
||||||
"description": "Trueskill implementation by Moserware updated for PHP 8.2",
|
"description": "Trueskill implementation by Moserware updated for PHP 8.4",
|
||||||
"keywords": ["trueskill", "matchmaking", "ranking", "skill", "elo"],
|
"keywords": [
|
||||||
|
"trueskill",
|
||||||
|
"matchmaking",
|
||||||
|
"ranking",
|
||||||
|
"skill",
|
||||||
|
"elo"
|
||||||
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^8.2"
|
"php": "^8.4"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpstan/phpstan": "^1.0",
|
|
||||||
"vimeo/psalm": "dev-master",
|
|
||||||
"phpmetrics/phpmetrics": "^3.0-dev",
|
"phpmetrics/phpmetrics": "^3.0-dev",
|
||||||
"phpunit/phpunit": "^11.2",
|
"phpunit/phpunit": "^11.2",
|
||||||
"psalm/plugin-phpunit": "^0.18.4",
|
|
||||||
"rector/rector": "^1.0",
|
"rector/rector": "^1.0",
|
||||||
"league/csv": "^9.0"
|
"league/csv": "^9.0"
|
||||||
},
|
},
|
||||||
@ -25,27 +28,27 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "phpunit",
|
"test": "tools/phpunit",
|
||||||
"document": "phpDocumentor",
|
"document": "tools/phpdocumentor",
|
||||||
"benchmark": "phpbench run --report=default --output=build-artifact",
|
"benchmark": "tools/phpbench run --report=default --output=build-artifact",
|
||||||
"metrics": "vendor/bin/phpmetrics --config=phpmetrics.yml",
|
"metrics": "phpmetrics --config=phpmetrics.yml",
|
||||||
"lint": [
|
"lint": [
|
||||||
"phplint",
|
"tools/phpcbf",
|
||||||
"phpcs",
|
"tools/phpcs",
|
||||||
"phpcbf src/ tests/ benchmark/ examples/",
|
"tools/phpmd src/,tests/,benchmark/,examples/ text phpmd.ruleset.xml"
|
||||||
"phpmd src/,tests/,benchmark/,examples/ text phpmd.ruleset.xml"
|
|
||||||
],
|
],
|
||||||
"analyze": [
|
"analyze": [
|
||||||
"@analyze-phpstan",
|
"@analyze-phpstan",
|
||||||
"@analyze-psalm",
|
"@analyze-psalm",
|
||||||
"@analyze-rector"
|
"@analyze-rector"
|
||||||
],
|
],
|
||||||
"analyze-phpstan":"vendor/bin/phpstan analyze --error-format=raw",
|
"analyze-phpstan": "tools/phpstan analyze --error-format=raw",
|
||||||
"analyze-psalm": "vendor/bin/psalm --no-cache --show-info=true",
|
"analyze-psalm": "tools/psalm --show-info=true",
|
||||||
"analyze-rector": "vendor/bin/rector --dry-run",
|
"analyze-rector": "rector --dry-run",
|
||||||
"html": [
|
"html": [
|
||||||
"pandoc -s README.md -o output/README.html",
|
"pandoc -s README.md -o output/README.html",
|
||||||
"pandoc -s docs/index.rst -o output/index.html"
|
"pandoc -s docs/index.rst -o output/index.html",
|
||||||
|
"tools/phpcs --generator=MarkDown | pandoc -o output/CodeStandard.html --metadata title=\"Code Standard\""
|
||||||
],
|
],
|
||||||
"all": [
|
"all": [
|
||||||
"@test",
|
"@test",
|
||||||
@ -56,4 +59,4 @@
|
|||||||
"@html"
|
"@html"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
2690
composer.lock
generated
2690
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -33,6 +33,7 @@ Links
|
|||||||
* `Test report <test/index.html>`_
|
* `Test report <test/index.html>`_
|
||||||
* `Mutation testing <mutation/infection.html>`_
|
* `Mutation testing <mutation/infection.html>`_
|
||||||
* `Code metrics <metrics/index.html>`_
|
* `Code metrics <metrics/index.html>`_
|
||||||
|
* `Code Standard <CodeStandard.html>`_
|
||||||
|
|
||||||
|
|
||||||
Standard Tools
|
Standard Tools
|
||||||
|
@ -16,5 +16,5 @@
|
|||||||
</source>
|
</source>
|
||||||
</api>
|
</api>
|
||||||
</version>
|
</version>
|
||||||
<!--setting name="graphs.enabled" value="true"/-->
|
<setting name="graphs.enabled" value="true"/>
|
||||||
</phpdocumentor>
|
</phpdocumentor>
|
@ -12,11 +12,5 @@
|
|||||||
<directory name="src"/>
|
<directory name="src"/>
|
||||||
<directory name="tests"/>
|
<directory name="tests"/>
|
||||||
<directory name="benchmark"/>
|
<directory name="benchmark"/>
|
||||||
<ignoreFiles>
|
|
||||||
<directory name="vendor"/>
|
|
||||||
</ignoreFiles>
|
|
||||||
</projectFiles>
|
</projectFiles>
|
||||||
<plugins>
|
|
||||||
<pluginClass class="Psalm\PhpUnitPlugin\Plugin"/>
|
|
||||||
</plugins>
|
|
||||||
</psalm>
|
</psalm>
|
||||||
|
@ -24,7 +24,7 @@ abstract class FactorGraphLayer
|
|||||||
*/
|
*/
|
||||||
private array $inputVariablesGroups = [];
|
private array $inputVariablesGroups = [];
|
||||||
|
|
||||||
protected function __construct(private readonly TrueSkillFactorGraph $parentFactorGraph)
|
public function __construct(private readonly TrueSkillFactorGraph $parentFactorGraph)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ namespace DNW\Skills\FactorGraphs;
|
|||||||
/**
|
/**
|
||||||
* Helper class for computing the factor graph's normalization constant.
|
* Helper class for computing the factor graph's normalization constant.
|
||||||
*/
|
*/
|
||||||
class FactorList
|
final class FactorList
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var Factor[] $list
|
* @var Factor[] $list
|
||||||
@ -31,11 +31,11 @@ class FactorList
|
|||||||
$numberOfMessages = $factor->getNumberOfMessages();
|
$numberOfMessages = $factor->getNumberOfMessages();
|
||||||
|
|
||||||
for ($j = 0; $j < $numberOfMessages; ++$j) {
|
for ($j = 0; $j < $numberOfMessages; ++$j) {
|
||||||
$sumLogZ += $factor->sendMessageIndex($j);
|
$sumLogZ += (float)$factor->sendMessageIndex($j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$sumLogS = 0;
|
$sumLogS = 0.0;
|
||||||
|
|
||||||
foreach ($list as &$currentFactor) {
|
foreach ($list as &$currentFactor) {
|
||||||
$sumLogS += $currentFactor->getLogNormalization();
|
$sumLogS += $currentFactor->getLogNormalization();
|
||||||
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace DNW\Skills\FactorGraphs;
|
namespace DNW\Skills\FactorGraphs;
|
||||||
|
|
||||||
class KeyedVariable extends Variable
|
final class KeyedVariable extends Variable
|
||||||
{
|
{
|
||||||
public function __construct(private readonly mixed $key, mixed $prior)
|
public function __construct(private readonly mixed $key, mixed $prior)
|
||||||
{
|
{
|
||||||
|
@ -6,7 +6,7 @@ namespace DNW\Skills\FactorGraphs;
|
|||||||
|
|
||||||
use DNW\Skills\Numerics\GaussianDistribution;
|
use DNW\Skills\Numerics\GaussianDistribution;
|
||||||
|
|
||||||
class Message
|
final class Message
|
||||||
{
|
{
|
||||||
public function __construct(private GaussianDistribution $value)
|
public function __construct(private GaussianDistribution $value)
|
||||||
{
|
{
|
||||||
|
@ -4,12 +4,13 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace DNW\Skills\FactorGraphs;
|
namespace DNW\Skills\FactorGraphs;
|
||||||
|
|
||||||
class ScheduleLoop extends Schedule
|
final class ScheduleLoop extends Schedule
|
||||||
{
|
{
|
||||||
public function __construct(private readonly Schedule $scheduleToLoop, private readonly float $maxDelta)
|
public function __construct(private readonly Schedule $scheduleToLoop, private readonly float $maxDelta)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
public function visit(int $depth = -1, int $maxDepth = 0): float
|
public function visit(int $depth = -1, int $maxDepth = 0): float
|
||||||
{
|
{
|
||||||
$delta = $this->scheduleToLoop->visit($depth + 1, $maxDepth);
|
$delta = $this->scheduleToLoop->visit($depth + 1, $maxDepth);
|
||||||
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace DNW\Skills\FactorGraphs;
|
namespace DNW\Skills\FactorGraphs;
|
||||||
|
|
||||||
class ScheduleSequence extends Schedule
|
final class ScheduleSequence extends Schedule
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param Schedule[] $schedules
|
* @param Schedule[] $schedules
|
||||||
@ -13,6 +13,7 @@ class ScheduleSequence extends Schedule
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
public function visit(int $depth = -1, int $maxDepth = 0): float
|
public function visit(int $depth = -1, int $maxDepth = 0): float
|
||||||
{
|
{
|
||||||
$maxDelta = 0;
|
$maxDelta = 0;
|
||||||
|
@ -4,12 +4,13 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace DNW\Skills\FactorGraphs;
|
namespace DNW\Skills\FactorGraphs;
|
||||||
|
|
||||||
class ScheduleStep extends Schedule
|
final class ScheduleStep extends Schedule
|
||||||
{
|
{
|
||||||
public function __construct(private readonly Factor $factor, private readonly int $index)
|
public function __construct(private readonly Factor $factor, private readonly int $index)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
public function visit(int $depth = -1, int $maxDepth = 0): float
|
public function visit(int $depth = -1, int $maxDepth = 0): float
|
||||||
{
|
{
|
||||||
return $this->factor->updateMessageIndex($this->index);
|
return $this->factor->updateMessageIndex($this->index);
|
||||||
|
@ -4,9 +4,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace DNW\Skills\FactorGraphs;
|
namespace DNW\Skills\FactorGraphs;
|
||||||
|
|
||||||
class VariableFactory
|
final readonly class VariableFactory
|
||||||
{
|
{
|
||||||
public function __construct(private readonly \Closure $varPriorInitializer)
|
public function __construct(private \Closure $varPriorInitializer)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,30 +7,30 @@ namespace DNW\Skills;
|
|||||||
/**
|
/**
|
||||||
* Parameters about the game for calculating the TrueSkill.
|
* Parameters about the game for calculating the TrueSkill.
|
||||||
*/
|
*/
|
||||||
class GameInfo
|
final readonly class GameInfo
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Default initial mean / 6
|
* Default initial mean / 6
|
||||||
*/
|
*/
|
||||||
private const DEFAULT_BETA = 4.1666666666666666666666666666667;
|
private const float DEFAULT_BETA = 4.1666666666666666666666666666667;
|
||||||
|
|
||||||
private const DEFAULT_DRAW_PROBABILITY = 0.10;
|
private const float DEFAULT_DRAW_PROBABILITY = 0.10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default initial mean / 300
|
* Default initial mean / 300
|
||||||
*/
|
*/
|
||||||
private const DEFAULT_DYNAMICS_FACTOR = 0.083333333333333333333333333333333;
|
private const float DEFAULT_DYNAMICS_FACTOR = 0.083333333333333333333333333333333;
|
||||||
|
|
||||||
private const DEFAULT_INITIAL_MEAN = 25.0;
|
private const float DEFAULT_INITIAL_MEAN = 25.0;
|
||||||
|
|
||||||
private const DEFAULT_INITIAL_STANDARD_DEVIATION = 8.3333333333333333333333333333333;
|
private const float DEFAULT_INITIAL_STANDARD_DEVIATION = 8.3333333333333333333333333333333;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly float $initialMean = self::DEFAULT_INITIAL_MEAN,
|
private float $initialMean = self::DEFAULT_INITIAL_MEAN,
|
||||||
private readonly float $initialStdDev = self::DEFAULT_INITIAL_STANDARD_DEVIATION,
|
private float $initialStdDev = self::DEFAULT_INITIAL_STANDARD_DEVIATION,
|
||||||
private readonly float $beta = self::DEFAULT_BETA,
|
private float $beta = self::DEFAULT_BETA,
|
||||||
private readonly float $dynamicsFactor = self::DEFAULT_DYNAMICS_FACTOR,
|
private float $dynamicsFactor = self::DEFAULT_DYNAMICS_FACTOR,
|
||||||
private readonly float $drawProbability = self::DEFAULT_DRAW_PROBABILITY
|
private float $drawProbability = self::DEFAULT_DRAW_PROBABILITY
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ use Exception;
|
|||||||
*
|
*
|
||||||
* @see http://www.moserware.com/2008/01/borrowing-ideas-from-3-interesting.html
|
* @see http://www.moserware.com/2008/01/borrowing-ideas-from-3-interesting.html
|
||||||
*/
|
*/
|
||||||
class Guard
|
final class Guard
|
||||||
{
|
{
|
||||||
public static function argumentIsValidIndex(int $index, int $count, string $parameterName): void
|
public static function argumentIsValidIndex(int $index, int $count, string $parameterName): void
|
||||||
{
|
{
|
||||||
@ -23,7 +23,7 @@ class Guard
|
|||||||
public static function argumentInRangeInclusive(float $value, float $min, float $max, string $parameterName): void
|
public static function argumentInRangeInclusive(float $value, float $min, float $max, string $parameterName): void
|
||||||
{
|
{
|
||||||
if (($value < $min) || ($value > $max)) {
|
if (($value < $min) || ($value > $max)) {
|
||||||
throw new Exception($parameterName . ' is not in the valid range [' . $min . ', ' . $max . ']');
|
throw new Exception($parameterName . ' is not in the valid range [' . (int)$min . ', ' . (int)$max . ']');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,15 +7,15 @@ namespace DNW\Skills;
|
|||||||
/**
|
/**
|
||||||
* Basic hashmap that supports object keys.
|
* Basic hashmap that supports object keys.
|
||||||
*/
|
*/
|
||||||
class HashMap
|
final class HashMap
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var mixed[] $hashToValue
|
* @var mixed[] $hashToValue Store the hash to value mapping.
|
||||||
*/
|
*/
|
||||||
private array $hashToValue = [];
|
private array $hashToValue = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var mixed[] $hashToKey
|
* @var mixed[] $hashToKey Store the hash to original key mapping.
|
||||||
*/
|
*/
|
||||||
private array $hashToKey = [];
|
private array $hashToKey = [];
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ namespace DNW\Skills\Numerics;
|
|||||||
* @author Jeff Moser <jeff@moserware.com>
|
* @author Jeff Moser <jeff@moserware.com>
|
||||||
* @copyright 2010 Jeff Moser
|
* @copyright 2010 Jeff Moser
|
||||||
*/
|
*/
|
||||||
class BasicMath
|
final class BasicMath
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Squares the input (input^2 = input * input)
|
* Squares the input (input^2 = input * input)
|
||||||
@ -27,7 +27,7 @@ class BasicMath
|
|||||||
/**
|
/**
|
||||||
* Sums the items in $itemsToSum
|
* Sums the items in $itemsToSum
|
||||||
*
|
*
|
||||||
* @param mixed[] $itemsToSum The items to sum,
|
* @param mixed[] $itemsToSum The items to sum.
|
||||||
* @param \Closure $callback The function to apply to each array element before summing.
|
* @param \Closure $callback The function to apply to each array element before summing.
|
||||||
*
|
*
|
||||||
* @return float The sum.
|
* @return float The sum.
|
||||||
|
@ -10,11 +10,11 @@ namespace DNW\Skills\Numerics;
|
|||||||
* @author Jeff Moser <jeff@moserware.com>
|
* @author Jeff Moser <jeff@moserware.com>
|
||||||
* @copyright 2010 Jeff Moser
|
* @copyright 2010 Jeff Moser
|
||||||
*/
|
*/
|
||||||
class GaussianDistribution
|
final class GaussianDistribution
|
||||||
{
|
{
|
||||||
private const DEFAULT_STANDARD_DEVIATION = 1.0;
|
private const float DEFAULT_STANDARD_DEVIATION = 1.0;
|
||||||
|
|
||||||
private const DEFAULT_MEAN = 0.0;
|
private const float DEFAULT_MEAN = 0.0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Square Root 2π.
|
* Square Root 2π.
|
||||||
@ -23,7 +23,7 @@ class GaussianDistribution
|
|||||||
*
|
*
|
||||||
* @link https://www.wolframalpha.com/input?i=sqrt%282*pi%29 Source of value
|
* @link https://www.wolframalpha.com/input?i=sqrt%282*pi%29 Source of value
|
||||||
*/
|
*/
|
||||||
private const M_SQRT_2_PI = 2.5066282746310005024157652848110452530069867406099383166299235763;
|
private const float M_SQRT_2_PI = 2.5066282746310005024157652848110452530069867406099383166299235763;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Log of Square Root 2π.
|
* Log of Square Root 2π.
|
||||||
@ -32,10 +32,11 @@ class GaussianDistribution
|
|||||||
*
|
*
|
||||||
* @link https://www.wolframalpha.com/input?i=log%28sqrt%282*pi%29%29 Source of value
|
* @link https://www.wolframalpha.com/input?i=log%28sqrt%282*pi%29%29 Source of value
|
||||||
*/
|
*/
|
||||||
private const M_LOG_SQRT_2_PI = 0.9189385332046727417803297364056176398613974736377834128171515404;
|
private const float M_LOG_SQRT_2_PI = 0.9189385332046727417803297364056176398613974736377834128171515404;
|
||||||
|
|
||||||
// precision and precisionMean are used because they make multiplying and dividing simpler
|
/**
|
||||||
// (see the accompanying math paper for more details)
|
* Precision and precisionMean are used because they make multiplying and dividing simpler.
|
||||||
|
*/
|
||||||
private float $precision = 1.0;
|
private float $precision = 1.0;
|
||||||
|
|
||||||
private float $precisionMean = 0.0;
|
private float $precisionMean = 0.0;
|
||||||
@ -171,7 +172,7 @@ class GaussianDistribution
|
|||||||
$meanDifference = $numerator->mean - $denominator->mean;
|
$meanDifference = $numerator->mean - $denominator->mean;
|
||||||
|
|
||||||
return log($denominator->variance) + self::M_LOG_SQRT_2_PI - log($varianceDifference) / 2.0 +
|
return log($denominator->variance) + self::M_LOG_SQRT_2_PI - log($varianceDifference) / 2.0 +
|
||||||
BasicMath::square($meanDifference) / (2 * $varianceDifference);
|
BasicMath::square($meanDifference) / (2.0 * $varianceDifference);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function at(float $var, float $mean = 0.0, float $standardDeviation = 1.0): float
|
public static function at(float $var, float $mean = 0.0, float $standardDeviation = 1.0): float
|
||||||
@ -182,7 +183,7 @@ 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($var - $mean)) / (2 * BasicMath::square($standardDeviation)));
|
$expPart = exp((-1.0 * BasicMath::square($var - $mean)) / (2.0 * BasicMath::square($standardDeviation)));
|
||||||
|
|
||||||
return $multiplier * $expPart;
|
return $multiplier * $expPart;
|
||||||
}
|
}
|
||||||
@ -200,7 +201,7 @@ class GaussianDistribution
|
|||||||
$z = abs($var);
|
$z = abs($var);
|
||||||
|
|
||||||
$t = 2.0 / (2.0 + $z);
|
$t = 2.0 / (2.0 + $z);
|
||||||
$ty = 4 * $t - 2;
|
$ty = 4.0 * $t - 2.0;
|
||||||
|
|
||||||
$coefficients = [
|
$coefficients = [
|
||||||
-1.3026537197817094,
|
-1.3026537197817094,
|
||||||
@ -259,8 +260,8 @@ class GaussianDistribution
|
|||||||
return 100;
|
return 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
$pp = ($p < 1.0) ? $p : 2 - $p;
|
$pp = ($p < 1.0) ? $p : 2.0 - $p;
|
||||||
$t = sqrt(-2 * log($pp / 2.0)); // Initial guess
|
$t = sqrt(-2.0 * log($pp / 2.0)); // Initial guess
|
||||||
$x = -M_SQRT1_2 * ((2.30753 + $t * 0.27061) / (1.0 + $t * (0.99229 + $t * 0.04481)) - $t);
|
$x = -M_SQRT1_2 * ((2.30753 + $t * 0.27061) / (1.0 + $t * (0.99229 + $t * 0.04481)) - $t);
|
||||||
|
|
||||||
for ($j = 0; $j < 2; ++$j) {
|
for ($j = 0; $j < 2; ++$j) {
|
||||||
@ -274,6 +275,6 @@ class GaussianDistribution
|
|||||||
public static function inverseCumulativeTo(float $var, 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 * $var);
|
return $mean - M_SQRT2 * $standardDeviation * GaussianDistribution::inverseErrorFunctionCumulativeTo(2.0 * $var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace DNW\Skills\Numerics;
|
namespace DNW\Skills\Numerics;
|
||||||
|
|
||||||
class IdentityMatrix extends DiagonalMatrix
|
final class IdentityMatrix extends DiagonalMatrix
|
||||||
{
|
{
|
||||||
public function __construct(int $rows)
|
public function __construct(int $rows)
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@ use Exception;
|
|||||||
|
|
||||||
class Matrix
|
class Matrix
|
||||||
{
|
{
|
||||||
public const ERROR_TOLERANCE = 0.0000000001;
|
public const float ERROR_TOLERANCE = 0.0000000001;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<int,array<int,float>> $matrixRowData
|
* @param array<int,array<int,float>> $matrixRowData
|
||||||
@ -79,7 +79,7 @@ class Matrix
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getValue(int $row, int $col): float|int
|
public function getValue(int $row, int $col): float
|
||||||
{
|
{
|
||||||
$this->checkRowCol($row, $col);
|
$this->checkRowCol($row, $col);
|
||||||
return $this->matrixRowData[$row][$col];
|
return $this->matrixRowData[$row][$col];
|
||||||
@ -212,7 +212,7 @@ class Matrix
|
|||||||
return self::scalarMultiply($determinantInverse, $adjugate);
|
return self::scalarMultiply($determinantInverse, $adjugate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function scalarMultiply(float|int $scalarValue, Matrix $matrix): Matrix
|
public static function scalarMultiply(float $scalarValue, Matrix $matrix): Matrix
|
||||||
{
|
{
|
||||||
$rows = $matrix->getRowCount();
|
$rows = $matrix->getRowCount();
|
||||||
$columns = $matrix->getColumnCount();
|
$columns = $matrix->getColumnCount();
|
||||||
@ -265,7 +265,7 @@ class Matrix
|
|||||||
|
|
||||||
for ($currentRow = 0; $currentRow < $resultRows; ++$currentRow) {
|
for ($currentRow = 0; $currentRow < $resultRows; ++$currentRow) {
|
||||||
for ($currentColumn = 0; $currentColumn < $resultColumns; ++$currentColumn) {
|
for ($currentColumn = 0; $currentColumn < $resultColumns; ++$currentColumn) {
|
||||||
$productValue = 0;
|
$productValue = 0.0;
|
||||||
|
|
||||||
for ($vectorIndex = 0; $vectorIndex < $left->getColumnCount(); ++$vectorIndex) {
|
for ($vectorIndex = 0; $vectorIndex < $left->getColumnCount(); ++$vectorIndex) {
|
||||||
$leftValue = $left->getValue($currentRow, $vectorIndex);
|
$leftValue = $left->getValue($currentRow, $vectorIndex);
|
||||||
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace DNW\Skills\Numerics;
|
namespace DNW\Skills\Numerics;
|
||||||
|
|
||||||
class SquareMatrix extends Matrix
|
final class SquareMatrix extends Matrix
|
||||||
{
|
{
|
||||||
public function __construct(float|int ...$allValues)
|
public function __construct(float|int ...$allValues)
|
||||||
{
|
{
|
||||||
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace DNW\Skills\Numerics;
|
namespace DNW\Skills\Numerics;
|
||||||
|
|
||||||
class Vector extends Matrix
|
final class Vector extends Matrix
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param float[] $vectorValues
|
* @param float[] $vectorValues
|
||||||
|
@ -4,7 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace DNW\Skills;
|
namespace DNW\Skills;
|
||||||
|
|
||||||
class PartialPlay
|
final class PartialPlay
|
||||||
{
|
{
|
||||||
public static function getPartialPlayPercentage(Player $player): float
|
public static function getPartialPlayPercentage(Player $player): float
|
||||||
{
|
{
|
||||||
|
@ -7,25 +7,31 @@ namespace DNW\Skills;
|
|||||||
/**
|
/**
|
||||||
* Represents a player who has a Rating.
|
* Represents a player who has a Rating.
|
||||||
*/
|
*/
|
||||||
class Player implements ISupportPartialPlay, ISupportPartialUpdate
|
final readonly class Player implements ISupportPartialPlay, ISupportPartialUpdate
|
||||||
{
|
{
|
||||||
private const DEFAULT_PARTIAL_PLAY_PERCENTAGE = 1.0; // = 100% play time
|
private const float DEFAULT_PARTIAL_PLAY_PERCENTAGE = 1.0; // = 100% play time
|
||||||
|
|
||||||
private const DEFAULT_PARTIAL_UPDATE_PERCENTAGE = 1.0;
|
private const float DEFAULT_PARTIAL_UPDATE_PERCENTAGE = 1.0;
|
||||||
|
|
||||||
private readonly float $PartialPlayPct;
|
/**
|
||||||
|
* @var float The weight percentage to give this player when calculating a new rank.
|
||||||
|
*/
|
||||||
|
private float $PartialPlayPct;
|
||||||
|
|
||||||
private readonly float $PartialUpdatePct;
|
/**
|
||||||
|
* @var float Indicated how much of a skill update a player should receive where 0 represents no update and 1.0 represents 100% of the update.
|
||||||
|
*/
|
||||||
|
private 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 $partialPlayPct 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 $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.
|
* @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 mixed $Id,
|
||||||
float $partialPlayPct = self::DEFAULT_PARTIAL_PLAY_PERCENTAGE,
|
float $partialPlayPct = self::DEFAULT_PARTIAL_PLAY_PERCENTAGE,
|
||||||
float $partialUpdatePct = self::DEFAULT_PARTIAL_UPDATE_PERCENTAGE
|
float $partialUpdatePct = self::DEFAULT_PARTIAL_UPDATE_PERCENTAGE
|
||||||
)
|
)
|
||||||
@ -47,6 +53,7 @@ class Player implements ISupportPartialPlay, ISupportPartialUpdate
|
|||||||
/**
|
/**
|
||||||
* Indicates the percent of the time the player should be weighted where 0.0 indicates the player didn't play and 1.0 indicates the player played 100% of the time.
|
* Indicates the percent of the time the player should be weighted where 0.0 indicates the player didn't play and 1.0 indicates the player played 100% of the time.
|
||||||
*/
|
*/
|
||||||
|
#[\Override]
|
||||||
public function getPartialPlayPercentage(): float
|
public function getPartialPlayPercentage(): float
|
||||||
{
|
{
|
||||||
return $this->PartialPlayPct;
|
return $this->PartialPlayPct;
|
||||||
@ -55,6 +62,7 @@ class Player implements ISupportPartialPlay, ISupportPartialUpdate
|
|||||||
/**
|
/**
|
||||||
* Indicated how much of a skill update a player should receive where 0.0 represents no update and 1.0 represents 100% of the update.
|
* Indicated how much of a skill update a player should receive where 0.0 represents no update and 1.0 represents 100% of the update.
|
||||||
*/
|
*/
|
||||||
|
#[\Override]
|
||||||
public function getPartialUpdatePercentage(): float
|
public function getPartialUpdatePercentage(): float
|
||||||
{
|
{
|
||||||
return $this->PartialUpdatePct;
|
return $this->PartialUpdatePct;
|
||||||
|
@ -6,6 +6,6 @@ namespace DNW\Skills;
|
|||||||
|
|
||||||
use DNW\Skills\Numerics\Range;
|
use DNW\Skills\Numerics\Range;
|
||||||
|
|
||||||
class PlayersRange extends Range
|
final class PlayersRange extends Range
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ namespace DNW\Skills;
|
|||||||
/**
|
/**
|
||||||
* Helper class to sort ranks in non-decreasing order.
|
* Helper class to sort ranks in non-decreasing order.
|
||||||
*/
|
*/
|
||||||
class RankSorter
|
final class RankSorter
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Performs an in-place sort of the items in according to the ranks in non-decreasing order.
|
* Performs an in-place sort of the items in according to the ranks in non-decreasing order.
|
||||||
|
@ -9,18 +9,18 @@ use DNW\Skills\Numerics\GaussianDistribution;
|
|||||||
/**
|
/**
|
||||||
* Container for a player's rating.
|
* Container for a player's rating.
|
||||||
*/
|
*/
|
||||||
class Rating
|
final readonly class Rating
|
||||||
{
|
{
|
||||||
private const CONSERVATIVE_STANDARD_DEVIATION_MULTIPLIER = 3;
|
private const float CONSERVATIVE_STANDARD_DEVIATION_MULTIPLIER = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a rating.
|
* Constructs a rating.
|
||||||
*
|
*
|
||||||
* @param float $mean The statistical mean value of the rating (also known as mu).
|
* @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 $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.
|
* @param float $conservativeStandardDeviationMultiplier optional The number of standardDeviations to subtract from the mean to achieve a conservative rating.
|
||||||
*/
|
*/
|
||||||
public function __construct(private readonly float $mean, private readonly float $standardDeviation, private readonly float|int $conservativeStandardDeviationMultiplier = self::CONSERVATIVE_STANDARD_DEVIATION_MULTIPLIER)
|
public function __construct(private float $mean, private float $standardDeviation, private float $conservativeStandardDeviationMultiplier = self::CONSERVATIVE_STANDARD_DEVIATION_MULTIPLIER)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,9 @@ namespace DNW\Skills;
|
|||||||
|
|
||||||
class RatingContainer
|
class RatingContainer
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* Link Player to a Rating using a hash map.
|
||||||
|
*/
|
||||||
private readonly HashMap $playerToRating;
|
private readonly HashMap $playerToRating;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
|
@ -11,11 +11,11 @@ use Exception;
|
|||||||
*/
|
*/
|
||||||
abstract class SkillCalculator
|
abstract class SkillCalculator
|
||||||
{
|
{
|
||||||
public const NONE = 0x00;
|
public const int NONE = 0x00;
|
||||||
|
|
||||||
public const PARTIAL_PLAY = 0x01;
|
public const int PARTIAL_PLAY = 0x01;
|
||||||
|
|
||||||
public const PARTIAL_UPDATE = 0x02;
|
public const int PARTIAL_UPDATE = 0x02;
|
||||||
|
|
||||||
protected function __construct(
|
protected function __construct(
|
||||||
private readonly int $supportedOptions,
|
private readonly int $supportedOptions,
|
||||||
|
@ -4,9 +4,9 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace DNW\Skills;
|
namespace DNW\Skills;
|
||||||
|
|
||||||
class Team extends RatingContainer
|
final class Team extends RatingContainer
|
||||||
{
|
{
|
||||||
public function __construct(Player $player = NULL, Rating $rating = NULL)
|
public function __construct(?Player $player = NULL, ?Rating $rating = NULL)
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
if (! $player instanceof Player) {
|
if (! $player instanceof Player) {
|
||||||
|
@ -6,6 +6,6 @@ namespace DNW\Skills;
|
|||||||
|
|
||||||
use DNW\Skills\Numerics\Range;
|
use DNW\Skills\Numerics\Range;
|
||||||
|
|
||||||
class TeamsRange extends Range
|
final class TeamsRange extends Range
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,6 @@ final class DrawMargin
|
|||||||
//
|
//
|
||||||
// margin = inversecdf((draw probability + 1)/2) * sqrt(n1+n2) * beta
|
// margin = inversecdf((draw probability + 1)/2) * sqrt(n1+n2) * beta
|
||||||
// n1 and n2 are the number of players on each team
|
// n1 and n2 are the number of players on each team
|
||||||
return GaussianDistribution::inverseCumulativeTo(.5 * ($drawProbability + 1), 0, 1) * M_SQRT2 * $beta;
|
return GaussianDistribution::inverseCumulativeTo(0.5 * ($drawProbability + 1.0), 0.0, 1.0) * M_SQRT2 * $beta;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ use DNW\Skills\Rating;
|
|||||||
/**
|
/**
|
||||||
* Calculates TrueSkill using a full factor graph.
|
* Calculates TrueSkill using a full factor graph.
|
||||||
*/
|
*/
|
||||||
class FactorGraphTrueSkillCalculator extends SkillCalculator
|
final class FactorGraphTrueSkillCalculator extends SkillCalculator
|
||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
@ -32,6 +32,7 @@ class FactorGraphTrueSkillCalculator extends SkillCalculator
|
|||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
#[\Override]
|
||||||
public function calculateNewRatings(
|
public function calculateNewRatings(
|
||||||
GameInfo $gameInfo,
|
GameInfo $gameInfo,
|
||||||
array $teams,
|
array $teams,
|
||||||
@ -54,6 +55,7 @@ class FactorGraphTrueSkillCalculator extends SkillCalculator
|
|||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
#[\Override]
|
||||||
public function calculateMatchQuality(GameInfo $gameInfo, array $teams): float
|
public function calculateMatchQuality(GameInfo $gameInfo, array $teams): float
|
||||||
{
|
{
|
||||||
// We need to create the A matrix which is the player team assigments.
|
// We need to create the A matrix which is the player team assigments.
|
||||||
@ -190,7 +192,7 @@ class FactorGraphTrueSkillCalculator extends SkillCalculator
|
|||||||
$nextTeam = $teamAssignmentsList[$i + 1];
|
$nextTeam = $teamAssignmentsList[$i + 1];
|
||||||
foreach ($nextTeam->getAllPlayers() as $nextTeamPlayer) {
|
foreach ($nextTeam->getAllPlayers() as $nextTeamPlayer) {
|
||||||
// Add a -1 * playing time to represent the difference
|
// Add a -1 * playing time to represent the difference
|
||||||
$playerAssignments[$currentColumn][] = -1 * PartialPlay::getPartialPlayPercentage($nextTeamPlayer);
|
$playerAssignments[$currentColumn][] = -1.0 * PartialPlay::getPartialPlayPercentage($nextTeamPlayer);
|
||||||
--$rowsRemaining;
|
--$rowsRemaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ abstract class GaussianFactor extends Factor
|
|||||||
/**
|
/**
|
||||||
* Sends the factor-graph message with and returns the log-normalization constant.
|
* Sends the factor-graph message with and returns the log-normalization constant.
|
||||||
*/
|
*/
|
||||||
|
#[\Override]
|
||||||
protected function sendMessageVariable(Message $message, Variable $variable): float|int
|
protected function sendMessageVariable(Message $message, Variable $variable): float|int
|
||||||
{
|
{
|
||||||
$marginal = $variable->getValue();
|
$marginal = $variable->getValue();
|
||||||
@ -24,6 +25,7 @@ abstract class GaussianFactor extends Factor
|
|||||||
return $logZ;
|
return $logZ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
public function createVariableToMessageBinding(Variable $variable): Message
|
public function createVariableToMessageBinding(Variable $variable): Message
|
||||||
{
|
{
|
||||||
$newDistribution = GaussianDistribution::fromPrecisionMean(0, 0);
|
$newDistribution = GaussianDistribution::fromPrecisionMean(0, 0);
|
||||||
|
@ -14,7 +14,7 @@ use DNW\Skills\TrueSkill\TruncatedGaussianCorrectionFunctions;
|
|||||||
*
|
*
|
||||||
* See the accompanying math paper for more details.
|
* See the accompanying math paper for more details.
|
||||||
*/
|
*/
|
||||||
class GaussianGreaterThanFactor extends GaussianFactor
|
final class GaussianGreaterThanFactor extends GaussianFactor
|
||||||
{
|
{
|
||||||
public function __construct(private readonly float $epsilon, Variable $variable)
|
public function __construct(private readonly float $epsilon, Variable $variable)
|
||||||
{
|
{
|
||||||
@ -22,6 +22,7 @@ class GaussianGreaterThanFactor extends GaussianFactor
|
|||||||
$this->createVariableToMessageBinding($variable);
|
$this->createVariableToMessageBinding($variable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
public function getLogNormalization(): float
|
public function getLogNormalization(): float
|
||||||
{
|
{
|
||||||
$vars = $this->getVariables();
|
$vars = $this->getVariables();
|
||||||
@ -42,6 +43,7 @@ class GaussianGreaterThanFactor extends GaussianFactor
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
protected function updateMessageVariable(Message $message, Variable $variable): float
|
protected function updateMessageVariable(Message $message, Variable $variable): float
|
||||||
{
|
{
|
||||||
$oldMarginal = clone $variable->getValue();
|
$oldMarginal = clone $variable->getValue();
|
||||||
|
@ -15,7 +15,7 @@ use Exception;
|
|||||||
*
|
*
|
||||||
* See the accompanying math paper for more details.
|
* See the accompanying math paper for more details.
|
||||||
*/
|
*/
|
||||||
class GaussianLikelihoodFactor extends GaussianFactor
|
final class GaussianLikelihoodFactor extends GaussianFactor
|
||||||
{
|
{
|
||||||
private readonly float $precision;
|
private readonly float $precision;
|
||||||
|
|
||||||
@ -28,10 +28,11 @@ class GaussianLikelihoodFactor extends GaussianFactor
|
|||||||
$this->createVariableToMessageBinding($variable2);
|
$this->createVariableToMessageBinding($variable2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
public function getLogNormalization(): float
|
public function getLogNormalization(): float
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var KeyedVariable[]|mixed $vars
|
* @var KeyedVariable[] $vars
|
||||||
*/
|
*/
|
||||||
$vars = $this->getVariables();
|
$vars = $this->getVariables();
|
||||||
/**
|
/**
|
||||||
@ -73,6 +74,7 @@ class GaussianLikelihoodFactor extends GaussianFactor
|
|||||||
return GaussianDistribution::subtract($newMarginal, $marginal1);
|
return GaussianDistribution::subtract($newMarginal, $marginal1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
public function updateMessageIndex(int $messageIndex): float
|
public function updateMessageIndex(int $messageIndex): float
|
||||||
{
|
{
|
||||||
$messages = $this->getMessages();
|
$messages = $this->getMessages();
|
||||||
|
@ -13,7 +13,7 @@ use DNW\Skills\Numerics\GaussianDistribution;
|
|||||||
*
|
*
|
||||||
* See the accompanying math paper for more details.
|
* See the accompanying math paper for more details.
|
||||||
*/
|
*/
|
||||||
class GaussianPriorFactor extends GaussianFactor
|
final class GaussianPriorFactor extends GaussianFactor
|
||||||
{
|
{
|
||||||
private readonly GaussianDistribution $newMessage;
|
private readonly GaussianDistribution $newMessage;
|
||||||
|
|
||||||
@ -29,6 +29,7 @@ class GaussianPriorFactor extends GaussianFactor
|
|||||||
$this->createVariableToMessageBindingWithMessage($variable, $newMessage);
|
$this->createVariableToMessageBindingWithMessage($variable, $newMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
protected function updateMessageVariable(Message $message, Variable $variable): float
|
protected function updateMessageVariable(Message $message, Variable $variable): float
|
||||||
{
|
{
|
||||||
$oldMarginal = clone $variable->getValue();
|
$oldMarginal = clone $variable->getValue();
|
||||||
|
@ -16,7 +16,7 @@ use DNW\Skills\Numerics\GaussianDistribution;
|
|||||||
*
|
*
|
||||||
* See the accompanying math paper for more details.
|
* See the accompanying math paper for more details.
|
||||||
*/
|
*/
|
||||||
class GaussianWeightedSumFactor extends GaussianFactor
|
final class GaussianWeightedSumFactor extends GaussianFactor
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var array<int[]> $varIndexOrdersForWeights
|
* @var array<int[]> $varIndexOrdersForWeights
|
||||||
@ -72,12 +72,12 @@ class GaussianWeightedSumFactor extends GaussianFactor
|
|||||||
|
|
||||||
$weightsLength = $variableWeightsLength + 1;
|
$weightsLength = $variableWeightsLength + 1;
|
||||||
for ($weightsIndex = 1; $weightsIndex < $weightsLength; ++$weightsIndex) {
|
for ($weightsIndex = 1; $weightsIndex < $weightsLength; ++$weightsIndex) {
|
||||||
$currentWeights = \array_fill(0, $variableWeightsLength, 0);
|
$currentWeights = \array_fill(0, $variableWeightsLength, 0.0);
|
||||||
|
|
||||||
$variableIndices = \array_fill(0, $variableWeightsLength + 1, 0);
|
$variableIndices = \array_fill(0, $variableWeightsLength + 1, 0.0);
|
||||||
$variableIndices[0] = $weightsIndex;
|
$variableIndices[0] = $weightsIndex;
|
||||||
|
|
||||||
$currentWeightsSquared = \array_fill(0, $variableWeightsLength, 0);
|
$currentWeightsSquared = \array_fill(0, $variableWeightsLength, 0.0);
|
||||||
|
|
||||||
// keep a single variable to keep track of where we are in the array.
|
// keep a single variable to keep track of where we are in the array.
|
||||||
// This is helpful since we skip over one of the spots
|
// This is helpful since we skip over one of the spots
|
||||||
@ -90,9 +90,9 @@ class GaussianWeightedSumFactor extends GaussianFactor
|
|||||||
|
|
||||||
$currentWeight = (-$variableWeights[$currentWeightSourceIndex] / $variableWeights[$weightsIndex - 1]);
|
$currentWeight = (-$variableWeights[$currentWeightSourceIndex] / $variableWeights[$weightsIndex - 1]);
|
||||||
|
|
||||||
if ($variableWeights[$weightsIndex - 1] == 0) {
|
if ($variableWeights[$weightsIndex - 1] == 0.0) {
|
||||||
// HACK: Getting around division by zero
|
// HACK: Getting around division by zero
|
||||||
$currentWeight = 0;
|
$currentWeight = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$currentWeights[$currentDestinationWeightIndex] = $currentWeight;
|
$currentWeights[$currentDestinationWeightIndex] = $currentWeight;
|
||||||
@ -112,7 +112,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.0;
|
||||||
$this->varIndexOrdersForWeights[] = $variableIndices;
|
$this->varIndexOrdersForWeights[] = $variableIndices;
|
||||||
|
|
||||||
$this->weights[$weightsIndex] = $currentWeights;
|
$this->weights[$weightsIndex] = $currentWeights;
|
||||||
@ -127,6 +127,7 @@ class GaussianWeightedSumFactor extends GaussianFactor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
public function getLogNormalization(): float
|
public function getLogNormalization(): float
|
||||||
{
|
{
|
||||||
$vars = $this->getVariables();
|
$vars = $this->getVariables();
|
||||||
@ -197,6 +198,7 @@ class GaussianWeightedSumFactor extends GaussianFactor
|
|||||||
return $finalDiff;
|
return $finalDiff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
public function updateMessageIndex(int $messageIndex): float
|
public function updateMessageIndex(int $messageIndex): float
|
||||||
{
|
{
|
||||||
$allMessages = $this->getMessages();
|
$allMessages = $this->getMessages();
|
||||||
|
@ -14,7 +14,7 @@ use DNW\Skills\TrueSkill\TruncatedGaussianCorrectionFunctions;
|
|||||||
*
|
*
|
||||||
* See the accompanying math paper for more details.
|
* See the accompanying math paper for more details.
|
||||||
*/
|
*/
|
||||||
class GaussianWithinFactor extends GaussianFactor
|
final class GaussianWithinFactor extends GaussianFactor
|
||||||
{
|
{
|
||||||
public function __construct(private readonly float $epsilon, Variable $variable)
|
public function __construct(private readonly float $epsilon, Variable $variable)
|
||||||
{
|
{
|
||||||
@ -23,6 +23,7 @@ class GaussianWithinFactor extends GaussianFactor
|
|||||||
$this->createVariableToMessageBinding($variable);
|
$this->createVariableToMessageBinding($variable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
public function getLogNormalization(): float
|
public function getLogNormalization(): float
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -46,6 +47,7 @@ class GaussianWithinFactor extends GaussianFactor
|
|||||||
return -GaussianDistribution::logProductNormalization($messageFromVariable, $message) + log($z);
|
return -GaussianDistribution::logProductNormalization($messageFromVariable, $message) + log($z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
protected function updateMessageVariable(Message $message, Variable $variable): float
|
protected function updateMessageVariable(Message $message, Variable $variable): float
|
||||||
{
|
{
|
||||||
$oldMarginal = clone $variable->getValue();
|
$oldMarginal = clone $variable->getValue();
|
||||||
|
@ -11,7 +11,7 @@ use DNW\Skills\TrueSkill\TrueSkillFactorGraph;
|
|||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
// The whole purpose of this is to do a loop on the bottom
|
// The whole purpose of this is to do a loop on the bottom
|
||||||
class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
|
final class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
|
||||||
{
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
TrueSkillFactorGraph $parentGraph,
|
TrueSkillFactorGraph $parentGraph,
|
||||||
@ -22,6 +22,7 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
|
|||||||
parent::__construct($parentGraph);
|
parent::__construct($parentGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
public function getLocalFactors(): array
|
public function getLocalFactors(): array
|
||||||
{
|
{
|
||||||
return array_merge(
|
return array_merge(
|
||||||
@ -30,6 +31,7 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
public function buildLayer(): void
|
public function buildLayer(): void
|
||||||
{
|
{
|
||||||
$inputVariablesGroups = $this->getInputVariablesGroups();
|
$inputVariablesGroups = $this->getInputVariablesGroups();
|
||||||
@ -41,7 +43,8 @@ class IteratedTeamDifferencesInnerLayer extends TrueSkillFactorGraphLayer
|
|||||||
$this->teamDifferencesComparisonLayer->buildLayer();
|
$this->teamDifferencesComparisonLayer->buildLayer();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createPriorSchedule(): ?ScheduleSequence
|
#[\Override]
|
||||||
|
public function createPriorSchedule(): ScheduleSequence
|
||||||
{
|
{
|
||||||
switch (count($this->getInputVariablesGroups())) {
|
switch (count($this->getInputVariablesGroups())) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -11,8 +11,9 @@ use DNW\Skills\TrueSkill\Factors\GaussianWeightedSumFactor;
|
|||||||
use DNW\Skills\FactorGraphs\Variable;
|
use DNW\Skills\FactorGraphs\Variable;
|
||||||
use DNW\Skills\FactorGraphs\KeyedVariable;
|
use DNW\Skills\FactorGraphs\KeyedVariable;
|
||||||
|
|
||||||
class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLayer
|
final class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLayer
|
||||||
{
|
{
|
||||||
|
#[\Override]
|
||||||
public function buildLayer(): void
|
public function buildLayer(): void
|
||||||
{
|
{
|
||||||
$inputVariablesGroups = $this->getInputVariablesGroups();
|
$inputVariablesGroups = $this->getInputVariablesGroups();
|
||||||
@ -32,7 +33,8 @@ class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLaye
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createPriorSchedule(): ?ScheduleSequence
|
#[\Override]
|
||||||
|
public function createPriorSchedule(): ScheduleSequence
|
||||||
{
|
{
|
||||||
$localFactors = $this->getLocalFactors();
|
$localFactors = $this->getLocalFactors();
|
||||||
|
|
||||||
@ -49,7 +51,7 @@ class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLaye
|
|||||||
/**
|
/**
|
||||||
* @param KeyedVariable[] $teamMembers
|
* @param KeyedVariable[] $teamMembers
|
||||||
*/
|
*/
|
||||||
protected function createPlayerToTeamSumFactor(array $teamMembers, Variable $sumVariable): GaussianWeightedSumFactor
|
private function createPlayerToTeamSumFactor(array $teamMembers, Variable $sumVariable): GaussianWeightedSumFactor
|
||||||
{
|
{
|
||||||
$weights = array_map(
|
$weights = array_map(
|
||||||
static function ($v): float {
|
static function ($v): float {
|
||||||
@ -66,7 +68,8 @@ class PlayerPerformancesToTeamPerformancesLayer extends TrueSkillFactorGraphLaye
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createPosteriorSchedule(): ?ScheduleSequence
|
#[\Override]
|
||||||
|
public function createPosteriorSchedule(): ScheduleSequence
|
||||||
{
|
{
|
||||||
$allFactors = [];
|
$allFactors = [];
|
||||||
$localFactors = $this->getLocalFactors();
|
$localFactors = $this->getLocalFactors();
|
||||||
|
@ -17,7 +17,7 @@ use DNW\Skills\FactorGraphs\ScheduleSequence;
|
|||||||
|
|
||||||
// We intentionally have no Posterior schedule since the only purpose here is to
|
// We intentionally have no Posterior schedule since the only purpose here is to
|
||||||
// start the process.
|
// start the process.
|
||||||
class PlayerPriorValuesToSkillsLayer extends TrueSkillFactorGraphLayer
|
final class PlayerPriorValuesToSkillsLayer extends TrueSkillFactorGraphLayer
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @param Team[] $teams
|
* @param Team[] $teams
|
||||||
@ -27,6 +27,7 @@ class PlayerPriorValuesToSkillsLayer extends TrueSkillFactorGraphLayer
|
|||||||
parent::__construct($parentGraph);
|
parent::__construct($parentGraph);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
public function buildLayer(): void
|
public function buildLayer(): void
|
||||||
{
|
{
|
||||||
$teams = $this->teams;
|
$teams = $this->teams;
|
||||||
@ -49,7 +50,8 @@ class PlayerPriorValuesToSkillsLayer extends TrueSkillFactorGraphLayer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createPriorSchedule(): ?ScheduleSequence
|
#[\Override]
|
||||||
|
public function createPriorSchedule(): ScheduleSequence
|
||||||
{
|
{
|
||||||
$localFactors = $this->getLocalFactors();
|
$localFactors = $this->getLocalFactors();
|
||||||
|
|
||||||
|
@ -11,8 +11,9 @@ use DNW\Skills\Numerics\BasicMath;
|
|||||||
use DNW\Skills\TrueSkill\Factors\GaussianLikelihoodFactor;
|
use DNW\Skills\TrueSkill\Factors\GaussianLikelihoodFactor;
|
||||||
use DNW\Skills\FactorGraphs\ScheduleSequence;
|
use DNW\Skills\FactorGraphs\ScheduleSequence;
|
||||||
|
|
||||||
class PlayerSkillsToPerformancesLayer extends TrueSkillFactorGraphLayer
|
final class PlayerSkillsToPerformancesLayer extends TrueSkillFactorGraphLayer
|
||||||
{
|
{
|
||||||
|
#[\Override]
|
||||||
public function buildLayer(): void
|
public function buildLayer(): void
|
||||||
{
|
{
|
||||||
$inputVarGroups = $this->getInputVariablesGroups();
|
$inputVarGroups = $this->getInputVariablesGroups();
|
||||||
@ -51,7 +52,8 @@ class PlayerSkillsToPerformancesLayer extends TrueSkillFactorGraphLayer
|
|||||||
return $this->getParentFactorGraph()->getVariableFactory()->createKeyedVariable($key);
|
return $this->getParentFactorGraph()->getVariableFactory()->createKeyedVariable($key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createPriorSchedule(): ?ScheduleSequence
|
#[\Override]
|
||||||
|
public function createPriorSchedule(): ScheduleSequence
|
||||||
{
|
{
|
||||||
$localFactors = $this->getLocalFactors();
|
$localFactors = $this->getLocalFactors();
|
||||||
|
|
||||||
@ -65,7 +67,8 @@ class PlayerSkillsToPerformancesLayer extends TrueSkillFactorGraphLayer
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createPosteriorSchedule(): ?ScheduleSequence
|
#[\Override]
|
||||||
|
public function createPosteriorSchedule(): ScheduleSequence
|
||||||
{
|
{
|
||||||
$localFactors = $this->getLocalFactors();
|
$localFactors = $this->getLocalFactors();
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ use DNW\Skills\TrueSkill\Factors\GaussianGreaterThanFactor;
|
|||||||
use DNW\Skills\TrueSkill\Factors\GaussianWithinFactor;
|
use DNW\Skills\TrueSkill\Factors\GaussianWithinFactor;
|
||||||
use DNW\Skills\TrueSkill\TrueSkillFactorGraph;
|
use DNW\Skills\TrueSkill\TrueSkillFactorGraph;
|
||||||
|
|
||||||
class TeamDifferencesComparisonLayer extends TrueSkillFactorGraphLayer
|
final class TeamDifferencesComparisonLayer extends TrueSkillFactorGraphLayer
|
||||||
{
|
{
|
||||||
private readonly float $epsilon;
|
private readonly float $epsilon;
|
||||||
|
|
||||||
@ -23,6 +23,7 @@ class TeamDifferencesComparisonLayer extends TrueSkillFactorGraphLayer
|
|||||||
$this->epsilon = DrawMargin::getDrawMarginFromDrawProbability($gameInfo->getDrawProbability(), $gameInfo->getBeta());
|
$this->epsilon = DrawMargin::getDrawMarginFromDrawProbability($gameInfo->getDrawProbability(), $gameInfo->getBeta());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[\Override]
|
||||||
public function buildLayer(): void
|
public function buildLayer(): void
|
||||||
{
|
{
|
||||||
$inputVarGroups = $this->getInputVariablesGroups();
|
$inputVarGroups = $this->getInputVariablesGroups();
|
||||||
|
@ -7,8 +7,9 @@ namespace DNW\Skills\TrueSkill\Layers;
|
|||||||
use DNW\Skills\FactorGraphs\Variable;
|
use DNW\Skills\FactorGraphs\Variable;
|
||||||
use DNW\Skills\TrueSkill\Factors\GaussianWeightedSumFactor;
|
use DNW\Skills\TrueSkill\Factors\GaussianWeightedSumFactor;
|
||||||
|
|
||||||
class TeamPerformancesToTeamPerformanceDifferencesLayer extends TrueSkillFactorGraphLayer
|
final class TeamPerformancesToTeamPerformanceDifferencesLayer extends TrueSkillFactorGraphLayer
|
||||||
{
|
{
|
||||||
|
#[\Override]
|
||||||
public function buildLayer(): void
|
public function buildLayer(): void
|
||||||
{
|
{
|
||||||
$inputVariablesGroups = $this->getInputVariablesGroups();
|
$inputVariablesGroups = $this->getInputVariablesGroups();
|
||||||
@ -28,11 +29,7 @@ class TeamPerformancesToTeamPerformanceDifferencesLayer extends TrueSkillFactorG
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createTeamPerformanceToDifferenceFactor(
|
private function createTeamPerformanceToDifferenceFactor(Variable $strongerTeam, Variable $weakerTeam, Variable $output): GaussianWeightedSumFactor
|
||||||
Variable $strongerTeam,
|
|
||||||
Variable $weakerTeam,
|
|
||||||
Variable $output
|
|
||||||
): GaussianWeightedSumFactor
|
|
||||||
{
|
{
|
||||||
$teams = [$strongerTeam, $weakerTeam];
|
$teams = [$strongerTeam, $weakerTeam];
|
||||||
$weights = [1.0, -1.0];
|
$weights = [1.0, -1.0];
|
||||||
|
@ -9,8 +9,4 @@ use DNW\Skills\TrueSkill\TrueSkillFactorGraph;
|
|||||||
|
|
||||||
abstract class TrueSkillFactorGraphLayer extends FactorGraphLayer
|
abstract class TrueSkillFactorGraphLayer extends FactorGraphLayer
|
||||||
{
|
{
|
||||||
public function __construct(TrueSkillFactorGraph $parentGraph)
|
|
||||||
{
|
|
||||||
parent::__construct($parentGraph);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ use DNW\Skills\TrueSkill\Layers\PlayerSkillsToPerformancesLayer;
|
|||||||
use DNW\Skills\TrueSkill\Layers\TeamDifferencesComparisonLayer;
|
use DNW\Skills\TrueSkill\Layers\TeamDifferencesComparisonLayer;
|
||||||
use DNW\Skills\TrueSkill\Layers\TeamPerformancesToTeamPerformanceDifferencesLayer;
|
use DNW\Skills\TrueSkill\Layers\TeamPerformancesToTeamPerformanceDifferencesLayer;
|
||||||
|
|
||||||
class TrueSkillFactorGraph extends FactorGraph
|
final class TrueSkillFactorGraph extends FactorGraph
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var FactorGraphLayer[] $layers
|
* @var FactorGraphLayer[] $layers
|
||||||
@ -33,6 +33,8 @@ class TrueSkillFactorGraph extends FactorGraph
|
|||||||
private readonly PlayerPriorValuesToSkillsLayer $priorLayer;
|
private readonly PlayerPriorValuesToSkillsLayer $priorLayer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Constructor
|
||||||
|
*
|
||||||
* @param GameInfo $gameInfo Parameters for the game.
|
* @param GameInfo $gameInfo Parameters for the game.
|
||||||
* @param Team[] $teams A mapping of team players and their ratings.
|
* @param Team[] $teams A mapping of team players and their ratings.
|
||||||
* @param int[] $teamRanks The ranks of the teams where 1 is first place. For a tie, repeat the number (e.g. 1, 2, 2).
|
* @param int[] $teamRanks The ranks of the teams where 1 is first place. For a tie, repeat the number (e.g. 1, 2, 2).
|
||||||
|
@ -6,7 +6,7 @@ namespace DNW\Skills\TrueSkill;
|
|||||||
|
|
||||||
use DNW\Skills\Numerics\GaussianDistribution;
|
use DNW\Skills\Numerics\GaussianDistribution;
|
||||||
|
|
||||||
class TruncatedGaussianCorrectionFunctions
|
final class TruncatedGaussianCorrectionFunctions
|
||||||
{
|
{
|
||||||
// These functions from the bottom of page 4 of the TrueSkill paper.
|
// These functions from the bottom of page 4 of the TrueSkill paper.
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ use DNW\Skills\TeamsRange;
|
|||||||
* When you only have two players, a lot of the math simplifies. The main purpose of this class
|
* When you only have two players, a lot of the math simplifies. The main purpose of this class
|
||||||
* is to show the bare minimum of what a TrueSkill implementation should have.
|
* is to show the bare minimum of what a TrueSkill implementation should have.
|
||||||
*/
|
*/
|
||||||
class TwoPlayerTrueSkillCalculator extends SkillCalculator
|
final class TwoPlayerTrueSkillCalculator extends SkillCalculator
|
||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
@ -31,11 +31,8 @@ class TwoPlayerTrueSkillCalculator extends SkillCalculator
|
|||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
public function calculateNewRatings(
|
#[\Override]
|
||||||
GameInfo $gameInfo,
|
public function calculateNewRatings(GameInfo $gameInfo, array $teams, array $teamRanks): RatingContainer
|
||||||
array $teams,
|
|
||||||
array $teamRanks
|
|
||||||
): RatingContainer
|
|
||||||
{
|
{
|
||||||
// Basic argument checking
|
// Basic argument checking
|
||||||
$this->validateTeamCountAndPlayersCountPerTeam($teams);
|
$this->validateTeamCountAndPlayersCountPerTeam($teams);
|
||||||
@ -94,7 +91,7 @@ class TwoPlayerTrueSkillCalculator extends SkillCalculator
|
|||||||
+
|
+
|
||||||
BasicMath::square($opponentRating->getStandardDeviation())
|
BasicMath::square($opponentRating->getStandardDeviation())
|
||||||
+
|
+
|
||||||
2 * BasicMath::square($gameInfo->getBeta())
|
2.0 * BasicMath::square($gameInfo->getBeta())
|
||||||
);
|
);
|
||||||
|
|
||||||
$winningMean = $selfRating->getMean();
|
$winningMean = $selfRating->getMean();
|
||||||
@ -117,11 +114,11 @@ class TwoPlayerTrueSkillCalculator extends SkillCalculator
|
|||||||
// non-draw case
|
// non-draw case
|
||||||
$v = TruncatedGaussianCorrectionFunctions::vExceedsMarginScaled($meanDelta, $drawMargin, $c);
|
$v = TruncatedGaussianCorrectionFunctions::vExceedsMarginScaled($meanDelta, $drawMargin, $c);
|
||||||
$w = TruncatedGaussianCorrectionFunctions::wExceedsMarginScaled($meanDelta, $drawMargin, $c);
|
$w = TruncatedGaussianCorrectionFunctions::wExceedsMarginScaled($meanDelta, $drawMargin, $c);
|
||||||
$rankMultiplier = $comparison->value;
|
$rankMultiplier = (float)$comparison->value;
|
||||||
} else {
|
} else {
|
||||||
$v = TruncatedGaussianCorrectionFunctions::vWithinMarginScaled($meanDelta, $drawMargin, $c);
|
$v = TruncatedGaussianCorrectionFunctions::vWithinMarginScaled($meanDelta, $drawMargin, $c);
|
||||||
$w = TruncatedGaussianCorrectionFunctions::wWithinMarginScaled($meanDelta, $drawMargin, $c);
|
$w = TruncatedGaussianCorrectionFunctions::wWithinMarginScaled($meanDelta, $drawMargin, $c);
|
||||||
$rankMultiplier = 1;
|
$rankMultiplier = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$meanMultiplier = (BasicMath::square($selfRating->getStandardDeviation()) + BasicMath::square($gameInfo->getDynamicsFactor())) / $c;
|
$meanMultiplier = (BasicMath::square($selfRating->getStandardDeviation()) + BasicMath::square($gameInfo->getDynamicsFactor())) / $c;
|
||||||
@ -130,7 +127,7 @@ class TwoPlayerTrueSkillCalculator extends SkillCalculator
|
|||||||
$stdDevMultiplier = $varianceWithDynamics / BasicMath::square($c);
|
$stdDevMultiplier = $varianceWithDynamics / BasicMath::square($c);
|
||||||
|
|
||||||
$newMean = $selfRating->getMean() + ($rankMultiplier * $meanMultiplier * $v);
|
$newMean = $selfRating->getMean() + ($rankMultiplier * $meanMultiplier * $v);
|
||||||
$newStdDev = sqrt($varianceWithDynamics * (1 - $w * $stdDevMultiplier));
|
$newStdDev = sqrt($varianceWithDynamics * (1.0 - $w * $stdDevMultiplier));
|
||||||
|
|
||||||
return new Rating($newMean, $newStdDev);
|
return new Rating($newMean, $newStdDev);
|
||||||
}
|
}
|
||||||
@ -138,6 +135,7 @@ class TwoPlayerTrueSkillCalculator extends SkillCalculator
|
|||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
#[\Override]
|
||||||
public function calculateMatchQuality(GameInfo $gameInfo, array $teams): float
|
public function calculateMatchQuality(GameInfo $gameInfo, array $teams): float
|
||||||
{
|
{
|
||||||
$this->validateTeamCountAndPlayersCountPerTeam($teams);
|
$this->validateTeamCountAndPlayersCountPerTeam($teams);
|
||||||
@ -158,16 +156,16 @@ class TwoPlayerTrueSkillCalculator extends SkillCalculator
|
|||||||
|
|
||||||
// This is the square root part of the equation:
|
// This is the square root part of the equation:
|
||||||
$sqrtPart = sqrt(
|
$sqrtPart = sqrt(
|
||||||
(2 * $betaSquared)
|
(2.0 * $betaSquared)
|
||||||
/
|
/
|
||||||
(2 * $betaSquared + $player1SigmaSquared + $player2SigmaSquared)
|
(2.0 * $betaSquared + $player1SigmaSquared + $player2SigmaSquared)
|
||||||
);
|
);
|
||||||
|
|
||||||
// This is the exponent part of the equation:
|
// This is the exponent part of the equation:
|
||||||
$expPart = exp(
|
$expPart = exp(
|
||||||
(-1 * BasicMath::square($player1Rating->getMean() - $player2Rating->getMean()))
|
(-1.0 * BasicMath::square($player1Rating->getMean() - $player2Rating->getMean()))
|
||||||
/
|
/
|
||||||
(2 * (2 * $betaSquared + $player1SigmaSquared + $player2SigmaSquared))
|
(2.0 * (2.0 * $betaSquared + $player1SigmaSquared + $player2SigmaSquared))
|
||||||
);
|
);
|
||||||
|
|
||||||
return $sqrtPart * $expPart;
|
return $sqrtPart * $expPart;
|
||||||
|
@ -21,7 +21,7 @@ use DNW\Skills\TeamsRange;
|
|||||||
*
|
*
|
||||||
* When you only have two teams, the math is still simple: no factor graphs are used yet.
|
* When you only have two teams, the math is still simple: no factor graphs are used yet.
|
||||||
*/
|
*/
|
||||||
class TwoTeamTrueSkillCalculator extends SkillCalculator
|
final class TwoTeamTrueSkillCalculator extends SkillCalculator
|
||||||
{
|
{
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
@ -31,6 +31,7 @@ class TwoTeamTrueSkillCalculator extends SkillCalculator
|
|||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
#[\Override]
|
||||||
public function calculateNewRatings(GameInfo $gameInfo, array $teams, array $teamRanks): RatingContainer
|
public function calculateNewRatings(GameInfo $gameInfo, array $teams, array $teamRanks): RatingContainer
|
||||||
{
|
{
|
||||||
$this->validateTeamCountAndPlayersCountPerTeam($teams);
|
$this->validateTeamCountAndPlayersCountPerTeam($teams);
|
||||||
@ -63,13 +64,7 @@ class TwoTeamTrueSkillCalculator extends SkillCalculator
|
|||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function updatePlayerRatings(
|
private static function updatePlayerRatings(GameInfo $gameInfo, RatingContainer $newPlayerRatings, Team $selfTeam, Team $otherTeam, PairwiseComparison $selfToOtherTeamComparison): void
|
||||||
GameInfo $gameInfo,
|
|
||||||
RatingContainer $newPlayerRatings,
|
|
||||||
Team $selfTeam,
|
|
||||||
Team $otherTeam,
|
|
||||||
PairwiseComparison $selfToOtherTeamComparison
|
|
||||||
): void
|
|
||||||
{
|
{
|
||||||
$drawMargin = DrawMargin::getDrawMarginFromDrawProbability(
|
$drawMargin = DrawMargin::getDrawMarginFromDrawProbability(
|
||||||
$gameInfo->getDrawProbability(),
|
$gameInfo->getDrawProbability(),
|
||||||
@ -93,7 +88,7 @@ class TwoTeamTrueSkillCalculator extends SkillCalculator
|
|||||||
+
|
+
|
||||||
BasicMath::sum($otherTeam->getAllRatings(), $varianceGetter)
|
BasicMath::sum($otherTeam->getAllRatings(), $varianceGetter)
|
||||||
+
|
+
|
||||||
$totalPlayers * $betaSquared
|
(float)$totalPlayers * $betaSquared
|
||||||
);
|
);
|
||||||
|
|
||||||
$winningMean = $selfMeanSum;
|
$winningMean = $selfMeanSum;
|
||||||
@ -116,12 +111,12 @@ class TwoTeamTrueSkillCalculator extends SkillCalculator
|
|||||||
// non-draw case
|
// non-draw case
|
||||||
$v = TruncatedGaussianCorrectionFunctions::vExceedsMarginScaled($meanDelta, $drawMargin, $c);
|
$v = TruncatedGaussianCorrectionFunctions::vExceedsMarginScaled($meanDelta, $drawMargin, $c);
|
||||||
$w = TruncatedGaussianCorrectionFunctions::wExceedsMarginScaled($meanDelta, $drawMargin, $c);
|
$w = TruncatedGaussianCorrectionFunctions::wExceedsMarginScaled($meanDelta, $drawMargin, $c);
|
||||||
$rankMultiplier = $selfToOtherTeamComparison->value;
|
$rankMultiplier = (float)$selfToOtherTeamComparison->value;
|
||||||
} else {
|
} else {
|
||||||
// assume draw
|
// assume draw
|
||||||
$v = TruncatedGaussianCorrectionFunctions::vWithinMarginScaled($meanDelta, $drawMargin, $c);
|
$v = TruncatedGaussianCorrectionFunctions::vWithinMarginScaled($meanDelta, $drawMargin, $c);
|
||||||
$w = TruncatedGaussianCorrectionFunctions::wWithinMarginScaled($meanDelta, $drawMargin, $c);
|
$w = TruncatedGaussianCorrectionFunctions::wWithinMarginScaled($meanDelta, $drawMargin, $c);
|
||||||
$rankMultiplier = 1;
|
$rankMultiplier = 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
$selfTeamAllPlayers = $selfTeam->getAllPlayers();
|
$selfTeamAllPlayers = $selfTeam->getAllPlayers();
|
||||||
@ -136,7 +131,7 @@ class TwoTeamTrueSkillCalculator extends SkillCalculator
|
|||||||
$newMean = $previousPlayerRating->getMean() + $playerMeanDelta;
|
$newMean = $previousPlayerRating->getMean() + $playerMeanDelta;
|
||||||
|
|
||||||
$newStdDev = sqrt(
|
$newStdDev = sqrt(
|
||||||
(BasicMath::square($previousPlayerRating->getStandardDeviation()) + $tauSquared) * (1 - $w * $stdDevMultiplier)
|
(BasicMath::square($previousPlayerRating->getStandardDeviation()) + $tauSquared) * (1.0 - $w * $stdDevMultiplier)
|
||||||
);
|
);
|
||||||
|
|
||||||
$newPlayerRatings->setRating($localSelfTeamCurPlayer, new Rating($newMean, $newStdDev));
|
$newPlayerRatings->setRating($localSelfTeamCurPlayer, new Rating($newMean, $newStdDev));
|
||||||
@ -146,6 +141,7 @@ class TwoTeamTrueSkillCalculator extends SkillCalculator
|
|||||||
/**
|
/**
|
||||||
* {@inheritdoc}
|
* {@inheritdoc}
|
||||||
*/
|
*/
|
||||||
|
#[\Override]
|
||||||
public function calculateMatchQuality(GameInfo $gameInfo, array $teams): float
|
public function calculateMatchQuality(GameInfo $gameInfo, array $teams): float
|
||||||
{
|
{
|
||||||
$this->validateTeamCountAndPlayersCountPerTeam($teams);
|
$this->validateTeamCountAndPlayersCountPerTeam($teams);
|
||||||
@ -157,7 +153,7 @@ class TwoTeamTrueSkillCalculator extends SkillCalculator
|
|||||||
$team2Ratings = $teams[1]->getAllRatings();
|
$team2Ratings = $teams[1]->getAllRatings();
|
||||||
$team2Count = count($team2Ratings);
|
$team2Count = count($team2Ratings);
|
||||||
|
|
||||||
$totalPlayers = $team1Count + $team2Count;
|
$totalPlayers = (float)($team1Count + $team2Count);
|
||||||
|
|
||||||
$betaSquared = BasicMath::square($gameInfo->getBeta());
|
$betaSquared = BasicMath::square($gameInfo->getBeta());
|
||||||
|
|
||||||
@ -182,9 +178,9 @@ class TwoTeamTrueSkillCalculator extends SkillCalculator
|
|||||||
);
|
);
|
||||||
|
|
||||||
$expPart = exp(
|
$expPart = exp(
|
||||||
(-1 * BasicMath::square($team1MeanSum - $team2MeanSum))
|
(-1.0 * BasicMath::square($team1MeanSum - $team2MeanSum))
|
||||||
/
|
/
|
||||||
(2 * ($totalPlayers * $betaSquared + $team1StdDevSquared + $team2StdDevSquared))
|
(2.0 * ($totalPlayers * $betaSquared + $team1StdDevSquared + $team2StdDevSquared))
|
||||||
);
|
);
|
||||||
|
|
||||||
return $expPart * $sqrtPart;
|
return $expPart * $sqrtPart;
|
||||||
|
@ -12,7 +12,7 @@ use PHPUnit\Framework\Attributes\UsesClass;
|
|||||||
|
|
||||||
#[CoversClass(Variable::class)]
|
#[CoversClass(Variable::class)]
|
||||||
#[UsesClass(GaussianDistribution::class)]
|
#[UsesClass(GaussianDistribution::class)]
|
||||||
class VariableTest extends TestCase
|
final class VariableTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testGetterSetter(): void
|
public function testGetterSetter(): void
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,7 @@ use PHPUnit\Framework\Attributes\UsesClass;
|
|||||||
|
|
||||||
#[CoversClass(GameInfo::class)]
|
#[CoversClass(GameInfo::class)]
|
||||||
#[UsesClass(Rating::class)]
|
#[UsesClass(Rating::class)]
|
||||||
class GameInfoTest extends TestCase
|
final class GameInfoTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testMembers(): void
|
public function testMembers(): void
|
||||||
{
|
{
|
||||||
|
@ -10,7 +10,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
use PHPUnit\Framework\Attributes\CoversClass;
|
use PHPUnit\Framework\Attributes\CoversClass;
|
||||||
|
|
||||||
#[CoversClass(Guard::class)]
|
#[CoversClass(Guard::class)]
|
||||||
class GuardTest extends TestCase
|
final class GuardTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testargumentIsValidIndexArgumentAbove(): void
|
public function testargumentIsValidIndexArgumentAbove(): void
|
||||||
{
|
{
|
||||||
|
@ -10,7 +10,7 @@ use PHPUnit\Framework\Attributes\CoversClass;
|
|||||||
use stdClass;
|
use stdClass;
|
||||||
|
|
||||||
#[CoversClass(HashMap::class)]
|
#[CoversClass(HashMap::class)]
|
||||||
class HashMapTest extends TestCase
|
final class HashMapTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testHashmap(): void
|
public function testHashmap(): void
|
||||||
{
|
{
|
||||||
|
@ -9,7 +9,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
use PHPUnit\Framework\Attributes\CoversClass;
|
use PHPUnit\Framework\Attributes\CoversClass;
|
||||||
|
|
||||||
#[CoversClass(BasicMath::class)]
|
#[CoversClass(BasicMath::class)]
|
||||||
class BasicMathTest extends TestCase
|
final class BasicMathTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testSquare(): void
|
public function testSquare(): void
|
||||||
{
|
{
|
||||||
@ -23,7 +23,7 @@ class BasicMathTest extends TestCase
|
|||||||
$arr = [1, 1, 1, 1];
|
$arr = [1, 1, 1, 1];
|
||||||
|
|
||||||
$func_return = static fn(float $f): float => $f;
|
$func_return = static fn(float $f): float => $f;
|
||||||
$func_double = static fn(float $f): float => $f * 2;
|
$func_double = static fn(float $f): float => $f * 2.0;
|
||||||
$this->assertEquals(4, BasicMath::sum($arr, $func_return));
|
$this->assertEquals(4, BasicMath::sum($arr, $func_return));
|
||||||
$this->assertEquals(8, BasicMath::sum($arr, $func_double));
|
$this->assertEquals(8, BasicMath::sum($arr, $func_double));
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,9 @@ use PHPUnit\Framework\Attributes\UsesClass;
|
|||||||
|
|
||||||
#[CoversClass(GaussianDistribution::class)]
|
#[CoversClass(GaussianDistribution::class)]
|
||||||
#[UsesClass(BasicMath::class)]
|
#[UsesClass(BasicMath::class)]
|
||||||
class GaussianDistributionTest extends TestCase
|
final class GaussianDistributionTest extends TestCase
|
||||||
{
|
{
|
||||||
private const ERROR_TOLERANCE = 0.000001;
|
private const float ERROR_TOLERANCE = 0.000001;
|
||||||
|
|
||||||
public function testGetters(): void
|
public function testGetters(): void
|
||||||
{
|
{
|
||||||
@ -24,7 +24,7 @@ class GaussianDistributionTest extends TestCase
|
|||||||
$this->assertEquals(9, $gd->getVariance());
|
$this->assertEquals(9, $gd->getVariance());
|
||||||
$this->assertEquals(3, $gd->getStandardDeviation());
|
$this->assertEquals(3, $gd->getStandardDeviation());
|
||||||
$this->assertEquals(1 / 9, $gd->getPrecision());
|
$this->assertEquals(1 / 9, $gd->getPrecision());
|
||||||
$this->assertEquals(1 / 9 * 10, $gd->getPrecisionMean());
|
$this->assertEquals(1.0 / 9.0 * 10.0, $gd->getPrecisionMean());
|
||||||
$this->assertEqualsWithDelta(0.13298076013, $gd->getNormalizationConstant(), GaussianDistributionTest::ERROR_TOLERANCE);
|
$this->assertEqualsWithDelta(0.13298076013, $gd->getNormalizationConstant(), GaussianDistributionTest::ERROR_TOLERANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ class GaussianDistributionTest extends TestCase
|
|||||||
|
|
||||||
$product2 = GaussianDistribution::multiply($m4s5, $m6s7);
|
$product2 = GaussianDistribution::multiply($m4s5, $m6s7);
|
||||||
|
|
||||||
$expectedMean = (4 * BasicMath::square(7) + 6 * BasicMath::square(5)) / (BasicMath::square(5) + BasicMath::square(7));
|
$expectedMean = (4.0 * BasicMath::square(7) + 6.0 * BasicMath::square(5)) / (BasicMath::square(5) + BasicMath::square(7));
|
||||||
$this->assertEqualsWithDelta($expectedMean, $product2->getMean(), GaussianDistributionTest::ERROR_TOLERANCE);
|
$this->assertEqualsWithDelta($expectedMean, $product2->getMean(), GaussianDistributionTest::ERROR_TOLERANCE);
|
||||||
|
|
||||||
$expectedSigma = sqrt(((BasicMath::square(5) * BasicMath::square(7)) / (BasicMath::square(5) + BasicMath::square(7))));
|
$expectedSigma = sqrt(((BasicMath::square(5) * BasicMath::square(7)) / (BasicMath::square(5) + BasicMath::square(7))));
|
||||||
@ -74,7 +74,7 @@ class GaussianDistributionTest extends TestCase
|
|||||||
$this->assertEqualsWithDelta(2.0, $productDividedByStandardNormal->getMean(), GaussianDistributionTest::ERROR_TOLERANCE);
|
$this->assertEqualsWithDelta(2.0, $productDividedByStandardNormal->getMean(), GaussianDistributionTest::ERROR_TOLERANCE);
|
||||||
$this->assertEqualsWithDelta(3.0, $productDividedByStandardNormal->getStandardDeviation(), GaussianDistributionTest::ERROR_TOLERANCE);
|
$this->assertEqualsWithDelta(3.0, $productDividedByStandardNormal->getStandardDeviation(), GaussianDistributionTest::ERROR_TOLERANCE);
|
||||||
|
|
||||||
$product2 = new GaussianDistribution((4 * BasicMath::square(7) + 6 * BasicMath::square(5)) / (BasicMath::square(5) + BasicMath::square(7)), sqrt(((BasicMath::square(5) * BasicMath::square(7)) / (BasicMath::square(5) + BasicMath::square(7)))));
|
$product2 = new GaussianDistribution((4.0 * BasicMath::square(7) + 6.0 * BasicMath::square(5)) / (BasicMath::square(5) + BasicMath::square(7)), sqrt(((BasicMath::square(5) * BasicMath::square(7)) / (BasicMath::square(5) + BasicMath::square(7)))));
|
||||||
$m4s5 = new GaussianDistribution(4, 5);
|
$m4s5 = new GaussianDistribution(4, 5);
|
||||||
$product2DividedByM4S5 = GaussianDistribution::divide($product2, $m4s5);
|
$product2DividedByM4S5 = GaussianDistribution::divide($product2, $m4s5);
|
||||||
$this->assertEqualsWithDelta(6.0, $product2DividedByM4S5->getMean(), GaussianDistributionTest::ERROR_TOLERANCE);
|
$this->assertEqualsWithDelta(6.0, $product2DividedByM4S5->getMean(), GaussianDistributionTest::ERROR_TOLERANCE);
|
||||||
|
@ -20,7 +20,7 @@ use Exception;
|
|||||||
#[CoversClass(DiagonalMatrix::class)]
|
#[CoversClass(DiagonalMatrix::class)]
|
||||||
#[CoversClass(Vector::class)]
|
#[CoversClass(Vector::class)]
|
||||||
// phpcs:disable PSR2.Methods.FunctionCallSignature,Generic.Functions.FunctionCallArgumentSpacing.TooMuchSpaceAfterComma
|
// phpcs:disable PSR2.Methods.FunctionCallSignature,Generic.Functions.FunctionCallArgumentSpacing.TooMuchSpaceAfterComma
|
||||||
class MatrixTest extends TestCase
|
final class MatrixTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testEmptyMatrix(): void
|
public function testEmptyMatrix(): void
|
||||||
{
|
{
|
||||||
@ -295,7 +295,7 @@ class MatrixTest extends TestCase
|
|||||||
1, 0, 6);
|
1, 0, 6);
|
||||||
|
|
||||||
$cInverse = $c->getInverse();
|
$cInverse = $c->getInverse();
|
||||||
$d = Matrix::scalarMultiply((1.0 / 22), new SquareMatrix(24, -12, -2,
|
$d = Matrix::scalarMultiply((1.0 / 22.0), new SquareMatrix(24, -12, -2,
|
||||||
5, 3, -5,
|
5, 3, -5,
|
||||||
-4, 2, 4));
|
-4, 2, 4));
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ use PHPUnit\Framework\Attributes\CoversClass;
|
|||||||
use Exception;
|
use Exception;
|
||||||
|
|
||||||
#[CoversClass(Range::class)]
|
#[CoversClass(Range::class)]
|
||||||
class RangeTest extends TestCase
|
final class RangeTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testConstructInvalidParam(): void
|
public function testConstructInvalidParam(): void
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@ use PHPUnit\Framework\Attributes\UsesClass;
|
|||||||
#[CoversClass(PartialPlay::class)]
|
#[CoversClass(PartialPlay::class)]
|
||||||
#[UsesClass(Player::class)]
|
#[UsesClass(Player::class)]
|
||||||
#[UsesClass(Guard::class)]
|
#[UsesClass(Guard::class)]
|
||||||
class PartialPlayTest extends TestCase
|
final class PartialPlayTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testgetPartialPlayPercentage(): void
|
public function testgetPartialPlayPercentage(): void
|
||||||
{
|
{
|
||||||
|
@ -12,7 +12,7 @@ use PHPUnit\Framework\Attributes\UsesClass;
|
|||||||
|
|
||||||
#[CoversClass(Player::class)]
|
#[CoversClass(Player::class)]
|
||||||
#[UsesClass(Guard::class)]
|
#[UsesClass(Guard::class)]
|
||||||
class PlayerTest extends TestCase
|
final class PlayerTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testPlayerObjectGetterSetter(): void
|
public function testPlayerObjectGetterSetter(): void
|
||||||
{
|
{
|
||||||
|
@ -9,7 +9,7 @@ use PHPUnit\Framework\TestCase;
|
|||||||
use PHPUnit\Framework\Attributes\CoversClass;
|
use PHPUnit\Framework\Attributes\CoversClass;
|
||||||
|
|
||||||
#[CoversClass(RankSorter::class)]
|
#[CoversClass(RankSorter::class)]
|
||||||
class RankSorterTest extends TestCase
|
final class RankSorterTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testSort(): void
|
public function testSort(): void
|
||||||
{
|
{
|
||||||
|
@ -18,7 +18,7 @@ use PHPUnit\Framework\Attributes\UsesClass;
|
|||||||
#[UsesClass(Player::class)]
|
#[UsesClass(Player::class)]
|
||||||
#[UsesClass(Rating::class)]
|
#[UsesClass(Rating::class)]
|
||||||
#[UsesClass(Guard::class)]
|
#[UsesClass(Guard::class)]
|
||||||
class RatingContainerTest extends TestCase
|
final class RatingContainerTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testRatingContainer(): void
|
public function testRatingContainer(): void
|
||||||
{
|
{
|
||||||
|
@ -14,7 +14,7 @@ use PHPUnit\Framework\Attributes\UsesClass;
|
|||||||
#[CoversClass(Rating::class)]
|
#[CoversClass(Rating::class)]
|
||||||
#[UsesClass(BasicMath::class)]
|
#[UsesClass(BasicMath::class)]
|
||||||
#[UsesClass(GaussianDistribution::class)]
|
#[UsesClass(GaussianDistribution::class)]
|
||||||
class RatingTest extends TestCase
|
final class RatingTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testGetRatingParameters(): void
|
public function testGetRatingParameters(): void
|
||||||
{
|
{
|
||||||
|
@ -17,7 +17,7 @@ use PHPUnit\Framework\Attributes\RequiresPhpunit;
|
|||||||
#[UsesClass(PlayersRange::class)]
|
#[UsesClass(PlayersRange::class)]
|
||||||
#[UsesClass(TeamsRange::class)]
|
#[UsesClass(TeamsRange::class)]
|
||||||
#[RequiresPhpunit('<12.0')]
|
#[RequiresPhpunit('<12.0')]
|
||||||
class SkillCalculatorTest extends TestCase
|
final class SkillCalculatorTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testisSupported(): void
|
public function testisSupported(): void
|
||||||
{
|
{
|
||||||
|
@ -20,7 +20,7 @@ use PHPUnit\Framework\Attributes\UsesClass;
|
|||||||
#[UsesClass(Player::class)]
|
#[UsesClass(Player::class)]
|
||||||
#[UsesClass(Rating::class)]
|
#[UsesClass(Rating::class)]
|
||||||
#[UsesClass(Guard::class)]
|
#[UsesClass(Guard::class)]
|
||||||
class TeamTest extends TestCase
|
final class TeamTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testTeam(): void
|
public function testTeam(): void
|
||||||
{
|
{
|
||||||
|
@ -14,9 +14,9 @@ use PHPUnit\Framework\Attributes\UsesClass;
|
|||||||
#[CoversClass(DrawMargin::class)]
|
#[CoversClass(DrawMargin::class)]
|
||||||
#[UsesClass(BasicMath::class)]
|
#[UsesClass(BasicMath::class)]
|
||||||
#[UsesClass(GaussianDistribution::class)]
|
#[UsesClass(GaussianDistribution::class)]
|
||||||
class DrawMarginTest extends TestCase
|
final class DrawMarginTest extends TestCase
|
||||||
{
|
{
|
||||||
private const ERROR_TOLERANCE = 0.000001;
|
private const float ERROR_TOLERANCE = 0.000001;
|
||||||
|
|
||||||
public function testGetDrawMarginFromDrawProbability(): void
|
public function testGetDrawMarginFromDrawProbability(): void
|
||||||
{
|
{
|
||||||
|
@ -10,16 +10,9 @@ use DNW\Skills\Player;
|
|||||||
use DNW\Skills\Team;
|
use DNW\Skills\Team;
|
||||||
use DNW\Skills\TrueSkill\FactorGraphTrueSkillCalculator;
|
use DNW\Skills\TrueSkill\FactorGraphTrueSkillCalculator;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use PHPUnit\Framework\Attributes\CoversClass;
|
|
||||||
use PHPUnit\Framework\Attributes\CoversNothing;
|
use PHPUnit\Framework\Attributes\CoversNothing;
|
||||||
use PHPUnit\Framework\Attributes\UsesClass;
|
|
||||||
|
|
||||||
#[CoversClass(FactorGraphTrueSkillCalculator::class)]
|
final class FactorGraphTrueSkillCalculatorTest extends TestCase
|
||||||
#[UsesClass(\DNW\Skills\Numerics\Range::class)]
|
|
||||||
#[UsesClass(\DNW\Skills\PlayersRange::class)]
|
|
||||||
#[UsesClass(\DNW\Skills\SkillCalculator::class)]
|
|
||||||
#[UsesClass(\DNW\Skills\TeamsRange::class)]
|
|
||||||
class FactorGraphTrueSkillCalculatorTest extends TestCase
|
|
||||||
{
|
{
|
||||||
#[CoversNothing]
|
#[CoversNothing]
|
||||||
public function testMicrosoftResearchExample(): void
|
public function testMicrosoftResearchExample(): void
|
||||||
@ -75,6 +68,7 @@ class FactorGraphTrueSkillCalculatorTest extends TestCase
|
|||||||
TrueSkillCalculatorTests::testPartialPlayScenarios($this, $calculator);
|
TrueSkillCalculatorTests::testPartialPlayScenarios($this, $calculator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[CoversNothing]
|
||||||
public function testMethodisSupported(): void
|
public function testMethodisSupported(): void
|
||||||
{
|
{
|
||||||
$calculator = new FactorGraphTrueSkillCalculator();
|
$calculator = new FactorGraphTrueSkillCalculator();
|
||||||
|
@ -11,11 +11,11 @@ use DNW\Skills\SkillCalculator;
|
|||||||
use DNW\Skills\Team;
|
use DNW\Skills\Team;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
class TrueSkillCalculatorTests
|
final class TrueSkillCalculatorTests
|
||||||
{
|
{
|
||||||
private const ERROR_TOLERANCE_TRUESKILL = 0.085;
|
private const float ERROR_TOLERANCE_TRUESKILL = 0.085;
|
||||||
|
|
||||||
private const ERROR_TOLERANCE_MATCH_QUALITY = 0.0005;
|
private const float ERROR_TOLERANCE_MATCH_QUALITY = 0.0005;
|
||||||
|
|
||||||
// These are the roll-up ones
|
// These are the roll-up ones
|
||||||
|
|
||||||
@ -811,15 +811,15 @@ class TrueSkillCalculatorTests
|
|||||||
|
|
||||||
private static function sixteenTeamsOfOneNotDrawn(TestCase $testClass, SkillCalculator $calculator): void
|
private static function sixteenTeamsOfOneNotDrawn(TestCase $testClass, SkillCalculator $calculator): void
|
||||||
{
|
{
|
||||||
$player1 = new Player(1);
|
$player1 = new Player(1);
|
||||||
$player2 = new Player(2);
|
$player2 = new Player(2);
|
||||||
$player3 = new Player(3);
|
$player3 = new Player(3);
|
||||||
$player4 = new Player(4);
|
$player4 = new Player(4);
|
||||||
$player5 = new Player(5);
|
$player5 = new Player(5);
|
||||||
$player6 = new Player(6);
|
$player6 = new Player(6);
|
||||||
$player7 = new Player(7);
|
$player7 = new Player(7);
|
||||||
$player8 = new Player(8);
|
$player8 = new Player(8);
|
||||||
$player9 = new Player(9);
|
$player9 = new Player(9);
|
||||||
$player10 = new Player(10);
|
$player10 = new Player(10);
|
||||||
$player11 = new Player(11);
|
$player11 = new Player(11);
|
||||||
$player12 = new Player(12);
|
$player12 = new Player(12);
|
||||||
@ -830,15 +830,15 @@ class TrueSkillCalculatorTests
|
|||||||
|
|
||||||
$gameInfo = new GameInfo();
|
$gameInfo = new GameInfo();
|
||||||
|
|
||||||
$team1 = new Team($player1, $gameInfo->getDefaultRating());
|
$team1 = new Team($player1, $gameInfo->getDefaultRating());
|
||||||
$team2 = new Team($player2, $gameInfo->getDefaultRating());
|
$team2 = new Team($player2, $gameInfo->getDefaultRating());
|
||||||
$team3 = new Team($player3, $gameInfo->getDefaultRating());
|
$team3 = new Team($player3, $gameInfo->getDefaultRating());
|
||||||
$team4 = new Team($player4, $gameInfo->getDefaultRating());
|
$team4 = new Team($player4, $gameInfo->getDefaultRating());
|
||||||
$team5 = new Team($player5, $gameInfo->getDefaultRating());
|
$team5 = new Team($player5, $gameInfo->getDefaultRating());
|
||||||
$team6 = new Team($player6, $gameInfo->getDefaultRating());
|
$team6 = new Team($player6, $gameInfo->getDefaultRating());
|
||||||
$team7 = new Team($player7, $gameInfo->getDefaultRating());
|
$team7 = new Team($player7, $gameInfo->getDefaultRating());
|
||||||
$team8 = new Team($player8, $gameInfo->getDefaultRating());
|
$team8 = new Team($player8, $gameInfo->getDefaultRating());
|
||||||
$team9 = new Team($player9, $gameInfo->getDefaultRating());
|
$team9 = new Team($player9, $gameInfo->getDefaultRating());
|
||||||
$team10 = new Team($player10, $gameInfo->getDefaultRating());
|
$team10 = new Team($player10, $gameInfo->getDefaultRating());
|
||||||
$team11 = new Team($player11, $gameInfo->getDefaultRating());
|
$team11 = new Team($player11, $gameInfo->getDefaultRating());
|
||||||
$team12 = new Team($player12, $gameInfo->getDefaultRating());
|
$team12 = new Team($player12, $gameInfo->getDefaultRating());
|
||||||
|
@ -14,7 +14,7 @@ use PHPUnit\Framework\Attributes\UsesClass;
|
|||||||
#[CoversClass(TruncatedGaussianCorrectionFunctions::class)]
|
#[CoversClass(TruncatedGaussianCorrectionFunctions::class)]
|
||||||
#[UsesClass(BasicMath::class)]
|
#[UsesClass(BasicMath::class)]
|
||||||
#[UsesClass(GaussianDistribution::class)]
|
#[UsesClass(GaussianDistribution::class)]
|
||||||
class TruncatedGaussianCorrectionFunctionsTest extends TestCase
|
final class TruncatedGaussianCorrectionFunctionsTest extends TestCase
|
||||||
{
|
{
|
||||||
public function testvGreaterThan(): void
|
public function testvGreaterThan(): void
|
||||||
{
|
{
|
||||||
|
@ -6,11 +6,9 @@ namespace DNW\Skills\Tests\TrueSkill;
|
|||||||
|
|
||||||
use DNW\Skills\TrueSkill\TwoPlayerTrueSkillCalculator;
|
use DNW\Skills\TrueSkill\TwoPlayerTrueSkillCalculator;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use PHPUnit\Framework\Attributes\CoversClass;
|
|
||||||
use PHPUnit\Framework\Attributes\CoversNothing;
|
use PHPUnit\Framework\Attributes\CoversNothing;
|
||||||
|
|
||||||
#[CoversClass(TwoPlayerTrueSkillCalculator::class)]
|
final class TwoPlayerTrueSkillCalculatorTest extends TestCase
|
||||||
class TwoPlayerTrueSkillCalculatorTest extends TestCase
|
|
||||||
{
|
{
|
||||||
#[CoversNothing]
|
#[CoversNothing]
|
||||||
public function testTwoPlayerTrueSkillCalculator(): void
|
public function testTwoPlayerTrueSkillCalculator(): void
|
||||||
|
@ -6,11 +6,9 @@ namespace DNW\Skills\Tests\TrueSkill;
|
|||||||
|
|
||||||
use DNW\Skills\TrueSkill\TwoTeamTrueSkillCalculator;
|
use DNW\Skills\TrueSkill\TwoTeamTrueSkillCalculator;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use PHPUnit\Framework\Attributes\CoversClass;
|
|
||||||
use PHPUnit\Framework\Attributes\CoversNothing;
|
use PHPUnit\Framework\Attributes\CoversNothing;
|
||||||
|
|
||||||
#[CoversClass(TwoTeamTrueSkillCalculator::class)]
|
final class TwoTeamTrueSkillCalculatorTest extends TestCase
|
||||||
class TwoTeamTrueSkillCalculatorTest extends TestCase
|
|
||||||
{
|
{
|
||||||
#[CoversNothing]
|
#[CoversNothing]
|
||||||
public function testTwoTeamTrueSkillCalculator(): void
|
public function testTwoTeamTrueSkillCalculator(): void
|
||||||
|
Reference in New Issue
Block a user