Compare commits

...

14 Commits

Author SHA1 Message Date
770c4dbcc6 Documentation warnings from pandoc resolved 2025-11-26 13:59:49 +00:00
d7a60176bb Library type. Requirement update 2025-11-20 13:35:45 +00:00
60102910e3 Regular update 2025-09-02 09:41:15 +00:00
dd4ecff60a Update packages 2025-08-27 08:26:23 +00:00
03a9a3987a Less modern PHP to make the older linting tools work on the examples code 2025-07-14 11:59:21 +00:00
ec2e637315 Warning removal 2025-07-08 09:41:38 +00:00
1d06fa0b0a Requirement update 2025-06-24 12:55:00 +00:00
a097fa7f71 Few more comments 2025-06-18 11:49:40 +00:00
ad9d7f94de Upodate PHPUnit to latest version 2025-06-06 14:24:46 +00:00
1725979b1c Bumping versions 2025-06-02 08:49:46 +00:00
ac7e3f5c2d CodeStandard as HTML / MarkDown 2025-05-14 09:28:51 +00:00
adc587ac0d More coding standards.
Diagrams in API documentation
2025-05-13 12:46:11 +00:00
d729caac1f Simplifying some casts 2025-05-12 09:40:44 +00:00
bd9fccb87b Regular updates and fixing static analysis findings 2025-05-12 09:19:17 +00:00
28 changed files with 311 additions and 231 deletions

View File

@@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive"> <phive xmlns="https://phar.io/phive">
<phar name="phpmd" version="^2.15.0" installed="2.15.0" location="./tools/phpmd" copy="false"/> <phar name="phpmd" version="^2.15.0" installed="2.15.0" location="./tools/phpmd" copy="false"/>
<phar name="overtrue/phplint" version="^9.6.2" installed="9.6.2" location="./tools/phplint" copy="false"/> <phar name="phpstan" version="^2.1.12" installed="2.1.32" location="./tools/phpstan" copy="false"/>
<phar name="phpstan" version="^2.1.12" installed="2.1.12" location="./tools/phpstan" copy="false"/> <phar name="psalm" version="^7.0.0-beta6" installed="7.0.0-beta11" location="./tools/psalm" copy="false"/>
<phar name="psalm" version="^7.0.0-beta6" installed="7.0.0-beta6" location="./tools/psalm" copy="false"/> <phar name="phpcs" version="^3.12.2" installed="3.13.5" location="./tools/phpcs" copy="false"/>
<phar name="phpcs" version="^3.12.2" installed="3.12.2" location="./tools/phpcs" copy="false"/> <phar name="phpcbf" version="^3.12.2" installed="3.13.5" location="./tools/phpcbf" copy="false"/>
<phar name="phpcbf" version="^3.12.2" installed="3.12.2" location="./tools/phpcbf" copy="false"/> <phar name="phpdocumentor" version="^3.7.1" installed="3.9.1" location="./tools/phpdocumentor" copy="false"/>
<phar name="phpdocumentor" version="^3.7.1" installed="3.7.1" location="./tools/phpdocumentor" copy="false"/> <phar name="phpbench" version="^1.4.1" installed="1.4.3" location="./tools/phpbench" 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.4.4" location="./tools/phpunit" copy="false"/>
</phive> </phive>

View File

@@ -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>

View File

@@ -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
View File

@@ -0,0 +1,3 @@
{
"php.version": "8.4"
}

View File

@@ -1,5 +1,6 @@
--- ---
title: README title: README
lang: en
... ...
# PHP TrueSkill Implementation # PHP TrueSkill Implementation
This is a PHP port of the Moserware.Skills project that's available at This is a PHP port of the Moserware.Skills project that's available at

View File

@@ -1,7 +1,13 @@
{ {
"name": "dnw/php-trueskill", "name": "dnw/php-trueskill",
"description": "Trueskill implementation by Moserware updated for PHP 8.4", "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.4" "php": "^8.4"
}, },
@@ -22,34 +28,36 @@
} }
}, },
"scripts": { "scripts": {
"test": "phpunit", "test": "tools/phpunit",
"document": "tools/phpDocumentor", "document": "tools/phpdocumentor",
"benchmark": "tools/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": [
"tools/phplint", "tools/phpcbf",
"tools/phpcs", "tools/phpcs",
"tools/phpcbf src/ tests/ benchmark/ examples/", "tools/phpmd src/,tests/,benchmark/,examples/ text phpmd.ruleset.xml"
"tools/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":"tools/phpstan analyze --error-format=raw", "analyze-phpstan": "tools/phpstan analyze --error-format=raw",
"analyze-psalm": "tools/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 --verbose -s README.md -o output/README.html",
"pandoc -s docs/index.rst -o output/index.html" "pandoc --verbose -f rst -s docs/index.rst -o output/index.html --metadata=lang:en",
"tools/phpcs --generator=MarkDown | pandoc --verbose -f markdown -s -o output/CodeStandard.html --metadata title='Code Standard' --metadata=lang:en"
], ],
"all": [ "all": [
"@test", "@test",
"@lint", "@lint",
"@analyze", "@analyze",
"@document",
"@metrics", "@metrics",
"@html" "@html"
] ]
} },
"type": "library"
} }

261
composer.lock generated
View File

@@ -9,16 +9,16 @@
"packages-dev": [ "packages-dev": [
{ {
"name": "league/csv", "name": "league/csv",
"version": "9.23.0", "version": "9.27.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/thephpleague/csv.git", "url": "https://github.com/thephpleague/csv.git",
"reference": "774008ad8a634448e4f8e288905e070e8b317ff3" "reference": "26de738b8fccf785397d05ee2fc07b6cd8749797"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/thephpleague/csv/zipball/774008ad8a634448e4f8e288905e070e8b317ff3", "url": "https://api.github.com/repos/thephpleague/csv/zipball/26de738b8fccf785397d05ee2fc07b6cd8749797",
"reference": "774008ad8a634448e4f8e288905e070e8b317ff3", "reference": "26de738b8fccf785397d05ee2fc07b6cd8749797",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -28,14 +28,14 @@
"require-dev": { "require-dev": {
"ext-dom": "*", "ext-dom": "*",
"ext-xdebug": "*", "ext-xdebug": "*",
"friendsofphp/php-cs-fixer": "^3.69.0", "friendsofphp/php-cs-fixer": "^3.75.0",
"phpbench/phpbench": "^1.4.0", "phpbench/phpbench": "^1.4.1",
"phpstan/phpstan": "^1.12.18", "phpstan/phpstan": "^1.12.27",
"phpstan/phpstan-deprecation-rules": "^1.2.1", "phpstan/phpstan-deprecation-rules": "^1.2.1",
"phpstan/phpstan-phpunit": "^1.4.2", "phpstan/phpstan-phpunit": "^1.4.2",
"phpstan/phpstan-strict-rules": "^1.6.2", "phpstan/phpstan-strict-rules": "^1.6.2",
"phpunit/phpunit": "^10.5.16 || ^11.5.7", "phpunit/phpunit": "^10.5.16 || ^11.5.22 || ^12.3.6",
"symfony/var-dumper": "^6.4.8 || ^7.2.3" "symfony/var-dumper": "^6.4.8 || ^7.3.0"
}, },
"suggest": { "suggest": {
"ext-dom": "Required to use the XMLConverter and the HTMLConverter classes", "ext-dom": "Required to use the XMLConverter and the HTMLConverter classes",
@@ -96,20 +96,20 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-03-28T06:52:04+00:00" "time": "2025-10-25T08:35:20+00:00"
}, },
{ {
"name": "myclabs/deep-copy", "name": "myclabs/deep-copy",
"version": "1.13.1", "version": "1.13.4",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/myclabs/DeepCopy.git", "url": "https://github.com/myclabs/DeepCopy.git",
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -148,7 +148,7 @@
], ],
"support": { "support": {
"issues": "https://github.com/myclabs/DeepCopy/issues", "issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
}, },
"funding": [ "funding": [
{ {
@@ -156,20 +156,20 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-04-29T12:36:36+00:00" "time": "2025-08-01T08:46:24+00:00"
}, },
{ {
"name": "nikic/php-parser", "name": "nikic/php-parser",
"version": "v5.4.0", "version": "v5.6.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/nikic/PHP-Parser.git", "url": "https://github.com/nikic/PHP-Parser.git",
"reference": "447a020a1f875a434d62f2a401f53b82a396e494" "reference": "3a454ca033b9e06b63282ce19562e892747449bb"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb",
"reference": "447a020a1f875a434d62f2a401f53b82a396e494", "reference": "3a454ca033b9e06b63282ce19562e892747449bb",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -188,7 +188,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "5.0-dev" "dev-master": "5.x-dev"
} }
}, },
"autoload": { "autoload": {
@@ -212,26 +212,26 @@
], ],
"support": { "support": {
"issues": "https://github.com/nikic/PHP-Parser/issues", "issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2"
}, },
"time": "2024-12-30T11:07:19+00:00" "time": "2025-10-21T19:32:17+00:00"
}, },
{ {
"name": "openmetrics-php/exposition-text", "name": "openmetrics-php/exposition-text",
"version": "v0.4.1", "version": "v0.4.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/openmetrics-php/exposition-text.git", "url": "https://github.com/openmetrics-php/exposition-text.git",
"reference": "fcfca38f693e168f4520bbd359d91528bbb9a8e8" "reference": "4f65004f93e53eac4b9668879b92d3c200a851ba"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/openmetrics-php/exposition-text/zipball/fcfca38f693e168f4520bbd359d91528bbb9a8e8", "url": "https://api.github.com/repos/openmetrics-php/exposition-text/zipball/4f65004f93e53eac4b9668879b92d3c200a851ba",
"reference": "fcfca38f693e168f4520bbd359d91528bbb9a8e8", "reference": "4f65004f93e53eac4b9668879b92d3c200a851ba",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": "^7.2 || ^8.0" "php": "^7.2 || ^8.0 || ^8.4"
}, },
"require-dev": { "require-dev": {
"ext-xdebug": "*" "ext-xdebug": "*"
@@ -255,9 +255,9 @@
"description": "Implementation of the text exposition format of OpenMetrics", "description": "Implementation of the text exposition format of OpenMetrics",
"support": { "support": {
"issues": "https://github.com/openmetrics-php/exposition-text/issues", "issues": "https://github.com/openmetrics-php/exposition-text/issues",
"source": "https://github.com/openmetrics-php/exposition-text/tree/v0.4.1" "source": "https://github.com/openmetrics-php/exposition-text/tree/v0.4.2"
}, },
"time": "2024-07-16T12:47:30+00:00" "time": "2025-06-04T14:59:47+00:00"
}, },
{ {
"name": "phar-io/manifest", "name": "phar-io/manifest",
@@ -456,16 +456,11 @@
}, },
{ {
"name": "phpstan/phpstan", "name": "phpstan/phpstan",
"version": "1.12.25", "version": "1.12.32",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "e310849a19e02b8bfcbb63147f495d8f872dd96f"
},
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/e310849a19e02b8bfcbb63147f495d8f872dd96f", "url": "https://api.github.com/repos/phpstan/phpstan/zipball/2770dcdf5078d0b0d53f94317e06affe88419aa8",
"reference": "e310849a19e02b8bfcbb63147f495d8f872dd96f", "reference": "2770dcdf5078d0b0d53f94317e06affe88419aa8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -510,20 +505,20 @@
"type": "github" "type": "github"
} }
], ],
"time": "2025-04-27T12:20:45+00:00" "time": "2025-09-30T10:16:31+00:00"
}, },
{ {
"name": "phpunit/php-code-coverage", "name": "phpunit/php-code-coverage",
"version": "11.0.9", "version": "11.0.11",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "14d63fbcca18457e49c6f8bebaa91a87e8e188d7" "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/14d63fbcca18457e49c6f8bebaa91a87e8e188d7", "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4",
"reference": "14d63fbcca18457e49c6f8bebaa91a87e8e188d7", "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -580,15 +575,27 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.9" "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.11"
}, },
"funding": [ "funding": [
{ {
"url": "https://github.com/sebastianbergmann", "url": "https://github.com/sebastianbergmann",
"type": "github" "type": "github"
},
{
"url": "https://liberapay.com/sebastianbergmann",
"type": "liberapay"
},
{
"url": "https://thanks.dev/u/gh/sebastianbergmann",
"type": "thanks_dev"
},
{
"url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage",
"type": "tidelift"
} }
], ],
"time": "2025-02-25T13:26:39+00:00" "time": "2025-08-27T14:37:49+00:00"
}, },
{ {
"name": "phpunit/php-file-iterator", "name": "phpunit/php-file-iterator",
@@ -837,16 +844,16 @@
}, },
{ {
"name": "phpunit/phpunit", "name": "phpunit/phpunit",
"version": "11.5.18", "version": "11.5.44",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git", "url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "fc3e887c7f3f9917e1bf61e523413d753db00a17" "reference": "c346885c95423eda3f65d85a194aaa24873cda82"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fc3e887c7f3f9917e1bf61e523413d753db00a17", "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/c346885c95423eda3f65d85a194aaa24873cda82",
"reference": "fc3e887c7f3f9917e1bf61e523413d753db00a17", "reference": "c346885c95423eda3f65d85a194aaa24873cda82",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -856,24 +863,24 @@
"ext-mbstring": "*", "ext-mbstring": "*",
"ext-xml": "*", "ext-xml": "*",
"ext-xmlwriter": "*", "ext-xmlwriter": "*",
"myclabs/deep-copy": "^1.13.0", "myclabs/deep-copy": "^1.13.4",
"phar-io/manifest": "^2.0.4", "phar-io/manifest": "^2.0.4",
"phar-io/version": "^3.2.1", "phar-io/version": "^3.2.1",
"php": ">=8.2", "php": ">=8.2",
"phpunit/php-code-coverage": "^11.0.9", "phpunit/php-code-coverage": "^11.0.11",
"phpunit/php-file-iterator": "^5.1.0", "phpunit/php-file-iterator": "^5.1.0",
"phpunit/php-invoker": "^5.0.1", "phpunit/php-invoker": "^5.0.1",
"phpunit/php-text-template": "^4.0.1", "phpunit/php-text-template": "^4.0.1",
"phpunit/php-timer": "^7.0.1", "phpunit/php-timer": "^7.0.1",
"sebastian/cli-parser": "^3.0.2", "sebastian/cli-parser": "^3.0.2",
"sebastian/code-unit": "^3.0.3", "sebastian/code-unit": "^3.0.3",
"sebastian/comparator": "^6.3.1", "sebastian/comparator": "^6.3.2",
"sebastian/diff": "^6.0.2", "sebastian/diff": "^6.0.2",
"sebastian/environment": "^7.2.0", "sebastian/environment": "^7.2.1",
"sebastian/exporter": "^6.3.0", "sebastian/exporter": "^6.3.2",
"sebastian/global-state": "^7.0.2", "sebastian/global-state": "^7.0.2",
"sebastian/object-enumerator": "^6.0.1", "sebastian/object-enumerator": "^6.0.1",
"sebastian/type": "^5.1.2", "sebastian/type": "^5.1.3",
"sebastian/version": "^5.0.2", "sebastian/version": "^5.0.2",
"staabm/side-effects-detector": "^1.0.5" "staabm/side-effects-detector": "^1.0.5"
}, },
@@ -918,7 +925,7 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues", "issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy", "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
"source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.18" "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.44"
}, },
"funding": [ "funding": [
{ {
@@ -942,7 +949,7 @@
"type": "tidelift" "type": "tidelift"
} }
], ],
"time": "2025-04-22T06:09:49+00:00" "time": "2025-11-13T07:17:35+00:00"
}, },
{ {
"name": "rector/rector", "name": "rector/rector",
@@ -1175,16 +1182,16 @@
}, },
{ {
"name": "sebastian/comparator", "name": "sebastian/comparator",
"version": "6.3.1", "version": "6.3.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/comparator.git", "url": "https://github.com/sebastianbergmann/comparator.git",
"reference": "24b8fbc2c8e201bb1308e7b05148d6ab393b6959" "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/24b8fbc2c8e201bb1308e7b05148d6ab393b6959", "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8",
"reference": "24b8fbc2c8e201bb1308e7b05148d6ab393b6959", "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1243,15 +1250,27 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/comparator/issues", "issues": "https://github.com/sebastianbergmann/comparator/issues",
"security": "https://github.com/sebastianbergmann/comparator/security/policy", "security": "https://github.com/sebastianbergmann/comparator/security/policy",
"source": "https://github.com/sebastianbergmann/comparator/tree/6.3.1" "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2"
}, },
"funding": [ "funding": [
{ {
"url": "https://github.com/sebastianbergmann", "url": "https://github.com/sebastianbergmann",
"type": "github" "type": "github"
},
{
"url": "https://liberapay.com/sebastianbergmann",
"type": "liberapay"
},
{
"url": "https://thanks.dev/u/gh/sebastianbergmann",
"type": "thanks_dev"
},
{
"url": "https://tidelift.com/funding/github/packagist/sebastian/comparator",
"type": "tidelift"
} }
], ],
"time": "2025-03-07T06:57:01+00:00" "time": "2025-08-10T08:07:46+00:00"
}, },
{ {
"name": "sebastian/complexity", "name": "sebastian/complexity",
@@ -1380,23 +1399,23 @@
}, },
{ {
"name": "sebastian/environment", "name": "sebastian/environment",
"version": "7.2.0", "version": "7.2.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/environment.git", "url": "https://github.com/sebastianbergmann/environment.git",
"reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5" "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4",
"reference": "855f3ae0ab316bbafe1ba4e16e9f3c078d24a0c5", "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=8.2" "php": ">=8.2"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^11.0" "phpunit/phpunit": "^11.3"
}, },
"suggest": { "suggest": {
"ext-posix": "*" "ext-posix": "*"
@@ -1432,28 +1451,40 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/environment/issues", "issues": "https://github.com/sebastianbergmann/environment/issues",
"security": "https://github.com/sebastianbergmann/environment/security/policy", "security": "https://github.com/sebastianbergmann/environment/security/policy",
"source": "https://github.com/sebastianbergmann/environment/tree/7.2.0" "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1"
}, },
"funding": [ "funding": [
{ {
"url": "https://github.com/sebastianbergmann", "url": "https://github.com/sebastianbergmann",
"type": "github" "type": "github"
},
{
"url": "https://liberapay.com/sebastianbergmann",
"type": "liberapay"
},
{
"url": "https://thanks.dev/u/gh/sebastianbergmann",
"type": "thanks_dev"
},
{
"url": "https://tidelift.com/funding/github/packagist/sebastian/environment",
"type": "tidelift"
} }
], ],
"time": "2024-07-03T04:54:44+00:00" "time": "2025-05-21T11:55:47+00:00"
}, },
{ {
"name": "sebastian/exporter", "name": "sebastian/exporter",
"version": "6.3.0", "version": "6.3.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/exporter.git", "url": "https://github.com/sebastianbergmann/exporter.git",
"reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3" "reference": "70a298763b40b213ec087c51c739efcaa90bcd74"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/3473f61172093b2da7de1fb5782e1f24cc036dc3", "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74",
"reference": "3473f61172093b2da7de1fb5782e1f24cc036dc3", "reference": "70a298763b40b213ec087c51c739efcaa90bcd74",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1467,7 +1498,7 @@
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-main": "6.1-dev" "dev-main": "6.3-dev"
} }
}, },
"autoload": { "autoload": {
@@ -1510,15 +1541,27 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/exporter/issues", "issues": "https://github.com/sebastianbergmann/exporter/issues",
"security": "https://github.com/sebastianbergmann/exporter/security/policy", "security": "https://github.com/sebastianbergmann/exporter/security/policy",
"source": "https://github.com/sebastianbergmann/exporter/tree/6.3.0" "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2"
}, },
"funding": [ "funding": [
{ {
"url": "https://github.com/sebastianbergmann", "url": "https://github.com/sebastianbergmann",
"type": "github" "type": "github"
},
{
"url": "https://liberapay.com/sebastianbergmann",
"type": "liberapay"
},
{
"url": "https://thanks.dev/u/gh/sebastianbergmann",
"type": "thanks_dev"
},
{
"url": "https://tidelift.com/funding/github/packagist/sebastian/exporter",
"type": "tidelift"
} }
], ],
"time": "2024-12-05T09:17:50+00:00" "time": "2025-09-24T06:12:51+00:00"
}, },
{ {
"name": "sebastian/global-state", "name": "sebastian/global-state",
@@ -1756,23 +1799,23 @@
}, },
{ {
"name": "sebastian/recursion-context", "name": "sebastian/recursion-context",
"version": "6.0.2", "version": "6.0.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/recursion-context.git", "url": "https://github.com/sebastianbergmann/recursion-context.git",
"reference": "694d156164372abbd149a4b85ccda2e4670c0e16" "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/694d156164372abbd149a4b85ccda2e4670c0e16", "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc",
"reference": "694d156164372abbd149a4b85ccda2e4670c0e16", "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
"php": ">=8.2" "php": ">=8.2"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^11.0" "phpunit/phpunit": "^11.3"
}, },
"type": "library", "type": "library",
"extra": { "extra": {
@@ -1808,28 +1851,40 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/recursion-context/issues", "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
"security": "https://github.com/sebastianbergmann/recursion-context/security/policy", "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
"source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.2" "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3"
}, },
"funding": [ "funding": [
{ {
"url": "https://github.com/sebastianbergmann", "url": "https://github.com/sebastianbergmann",
"type": "github" "type": "github"
},
{
"url": "https://liberapay.com/sebastianbergmann",
"type": "liberapay"
},
{
"url": "https://thanks.dev/u/gh/sebastianbergmann",
"type": "thanks_dev"
},
{
"url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context",
"type": "tidelift"
} }
], ],
"time": "2024-07-03T05:10:34+00:00" "time": "2025-08-13T04:42:22+00:00"
}, },
{ {
"name": "sebastian/type", "name": "sebastian/type",
"version": "5.1.2", "version": "5.1.3",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/sebastianbergmann/type.git", "url": "https://github.com/sebastianbergmann/type.git",
"reference": "a8a7e30534b0eb0c77cd9d07e82de1a114389f5e" "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/a8a7e30534b0eb0c77cd9d07e82de1a114389f5e", "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449",
"reference": "a8a7e30534b0eb0c77cd9d07e82de1a114389f5e", "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -1865,15 +1920,27 @@
"support": { "support": {
"issues": "https://github.com/sebastianbergmann/type/issues", "issues": "https://github.com/sebastianbergmann/type/issues",
"security": "https://github.com/sebastianbergmann/type/security/policy", "security": "https://github.com/sebastianbergmann/type/security/policy",
"source": "https://github.com/sebastianbergmann/type/tree/5.1.2" "source": "https://github.com/sebastianbergmann/type/tree/5.1.3"
}, },
"funding": [ "funding": [
{ {
"url": "https://github.com/sebastianbergmann", "url": "https://github.com/sebastianbergmann",
"type": "github" "type": "github"
},
{
"url": "https://liberapay.com/sebastianbergmann",
"type": "liberapay"
},
{
"url": "https://thanks.dev/u/gh/sebastianbergmann",
"type": "thanks_dev"
},
{
"url": "https://tidelift.com/funding/github/packagist/sebastian/type",
"type": "tidelift"
} }
], ],
"time": "2025-03-18T13:35:50+00:00" "time": "2025-08-09T06:55:48+00:00"
}, },
{ {
"name": "sebastian/version", "name": "sebastian/version",
@@ -1983,16 +2050,16 @@
}, },
{ {
"name": "theseer/tokenizer", "name": "theseer/tokenizer",
"version": "1.2.3", "version": "1.3.1",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/theseer/tokenizer.git", "url": "https://github.com/theseer/tokenizer.git",
"reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" "reference": "b7489ce515e168639d17feec34b8847c326b0b3c"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c",
"reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", "reference": "b7489ce515e168639d17feec34b8847c326b0b3c",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2021,7 +2088,7 @@
"description": "A small library for converting tokenized PHP source code into XML and potentially other formats", "description": "A small library for converting tokenized PHP source code into XML and potentially other formats",
"support": { "support": {
"issues": "https://github.com/theseer/tokenizer/issues", "issues": "https://github.com/theseer/tokenizer/issues",
"source": "https://github.com/theseer/tokenizer/tree/1.2.3" "source": "https://github.com/theseer/tokenizer/tree/1.3.1"
}, },
"funding": [ "funding": [
{ {
@@ -2029,7 +2096,7 @@
"type": "github" "type": "github"
} }
], ],
"time": "2024-03-03T12:36:25+00:00" "time": "2025-11-17T20:03:58+00:00"
} }
], ],
"aliases": [], "aliases": [],
@@ -2043,5 +2110,5 @@
"php": "^8.4" "php": "^8.4"
}, },
"platform-dev": {}, "platform-dev": {},
"plugin-api-version": "2.6.0" "plugin-api-version": "2.9.0"
} }

View File

@@ -1,5 +1,7 @@
=============
Documentation Documentation
============= =============
This is a PHP port of the Moserware.Skills project that's available at This is a PHP port of the Moserware.Skills project that's available at
http://github.com/moserware/Skills http://github.com/moserware/Skills
@@ -17,9 +19,9 @@ https://www.moserware.com/2010/03/computing-your-skill.html
From Microsoft From Microsoft
-------------- --------------
The TrueSkill ranking system is a skill based ranking system for Xbox Live(opens in new tab) developed at Microsoft Research(opens in new tab). The purpose of a ranking system is to both identify and track the skills of gamers in a game (mode) in order to be able to match them into competitive matches. TrueSkill has been used to rank and match players in many different games, from Halo 3 to Forza Motorsport 7(opens in new tab). The TrueSkill ranking system is a skill based ranking system for Xbox Live developed at Microsoft Research. The purpose of a ranking system is to both identify and track the skills of gamers in a game (mode) in order to be able to match them into competitive matches. TrueSkill has been used to rank and match players in many different games, from Halo 3 to Forza Motorsport 7.
An improved version of the TrueSkill ranking system, named TrueSkill 2(opens in new tab), launched with Gears of War 4(opens in new tab) and was later incorporated into Halo 5(opens in new tab). An improved version of the TrueSkill ranking system, named TrueSkill 2, launched with Gears of War 4 and was later incorporated into Halo 5.
The classic TrueSkill ranking system only uses the final standings of all teams in a match in order to update the skill estimates (ranks) of all players in the match. The TrueSkill 2 ranking system also uses the individual scores of players in order to weight the contribution of each player to each team. As a result, TrueSkill 2 is much faster at figuring out the skill of a new player. The classic TrueSkill ranking system only uses the final standings of all teams in a match in order to update the skill estimates (ranks) of all players in the match. The TrueSkill 2 ranking system also uses the individual scores of players in order to weight the contribution of each player to each team. As a result, TrueSkill 2 is much faster at figuring out the skill of a new player.
@@ -33,11 +35,16 @@ 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>`_
* `Benchmark <benchmark.html>`_
Standard Tools Standard Tools
-------------- --------------
* PHP8.3 * PHP8.4
Development Tools
-------------------
* PlantUML * PlantUML
* GraphViz * GraphViz
* Pandoc * Pandoc
@@ -47,7 +54,5 @@ PHP Tools
--------- ---------
For development Composer and the following packages are used (Recommended as Phars installed via Phive) For development Composer and the following packages are used (Recommended as Phars installed via Phive)
* sudo phive install -g composer phpdocumentor infection phpcs phpcbf phploc phpbench
* sudo phive install -g overtrue/phplint --force-accept-unsigned
* composer install * composer install
* composer all * composer all

View File

@@ -11,13 +11,14 @@ use DNW\Skills\Team;
use DNW\Skills\Teams; use DNW\Skills\Teams;
//load the CSV document from a stream //load the CSV document from a stream
$stream = fopen('motogp.csv', 'r'); $csv = Reader::createFromPath('motogp.csv', 'r');
$csv = Reader::createFromStream($stream);
$csv->setDelimiter(','); $csv->setDelimiter(',');
$csv->setHeaderOffset(0); $csv->setHeaderOffset(0);
$csv->setEscape('');
//build a statement //build a statement
$stmt = Statement::create()->where(static fn (array $record): bool => $record['category'] == "MotoGP" || $record['category'] == "500cc"); $statement = new Statement();
$stmt = $statement->where(static fn (array $record): bool => $record['category'] == "MotoGP" || $record['category'] == "500cc");
/** /**
* @var $riders Player[] * @var $riders Player[]

View File

@@ -16,5 +16,5 @@
</source> </source>
</api> </api>
</version> </version>
<!--setting name="graphs.enabled" value="true"/--> <setting name="graphs.enabled" value="true"/>
</phpdocumentor> </phpdocumentor>

View File

@@ -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)
{ {
} }

View File

@@ -10,12 +10,12 @@ namespace DNW\Skills;
final 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 = [];

View File

@@ -27,7 +27,7 @@ final 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.

View File

@@ -34,8 +34,9 @@ final class GaussianDistribution
*/ */
private const float 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;

View File

@@ -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];
@@ -130,10 +130,10 @@ class Matrix
// | a b | // | a b |
// | c d | // | c d |
// The determinant is ad - bc // The determinant is ad - bc
$a = (float)$this->getValue(0, 0); $a = $this->getValue(0, 0);
$b = (float)$this->getValue(0, 1); $b = $this->getValue(0, 1);
$c = (float)$this->getValue(1, 0); $c = $this->getValue(1, 0);
$d = (float)$this->getValue(1, 1); $d = $this->getValue(1, 1);
return $a * $d - $b * $c; return $a * $d - $b * $c;
} }
@@ -148,7 +148,7 @@ class Matrix
// I expand along the first row // I expand along the first row
for ($currentColumn = 0; $currentColumn < $this->columnCount; ++$currentColumn) { for ($currentColumn = 0; $currentColumn < $this->columnCount; ++$currentColumn) {
$firstRowColValue = (float)$this->getValue(0, $currentColumn); $firstRowColValue = $this->getValue(0, $currentColumn);
$cofactor = $this->getCofactor(0, $currentColumn); $cofactor = $this->getCofactor(0, $currentColumn);
$itemToAdd = $firstRowColValue * $cofactor; $itemToAdd = $firstRowColValue * $cofactor;
$result += $itemToAdd; $result += $itemToAdd;
@@ -201,7 +201,7 @@ class Matrix
public function getInverse(): Matrix|SquareMatrix public function getInverse(): Matrix|SquareMatrix
{ {
if (($this->rowCount == 1) && ($this->columnCount == 1)) { if (($this->rowCount == 1) && ($this->columnCount == 1)) {
return new SquareMatrix(1.0 / (float)$this->getValue(0, 0)); return new SquareMatrix(1.0 / $this->getValue(0, 0));
} }
// Take the simple approach: // Take the simple approach:
@@ -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();
@@ -240,9 +240,9 @@ class Matrix
for ($currentRow = 0; $currentRow < $left->getRowCount(); ++$currentRow) { for ($currentRow = 0; $currentRow < $left->getRowCount(); ++$currentRow) {
for ($currentColumn = 0; $currentColumn < $right->getColumnCount(); ++$currentColumn) { for ($currentColumn = 0; $currentColumn < $right->getColumnCount(); ++$currentColumn) {
$resultMatrix[$currentRow][$currentColumn] = $resultMatrix[$currentRow][$currentColumn] =
(float)$left->getValue($currentRow, $currentColumn) $left->getValue($currentRow, $currentColumn)
+ +
(float)$right->getValue($currentRow, $currentColumn); $right->getValue($currentRow, $currentColumn);
} }
} }
@@ -265,11 +265,11 @@ 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 = (float)$left->getValue($currentRow, $vectorIndex); $leftValue = $left->getValue($currentRow, $vectorIndex);
$rightValue = (float)$right->getValue($vectorIndex, $currentColumn); $rightValue = $right->getValue($vectorIndex, $currentColumn);
$vectorIndexProduct = $leftValue * $rightValue; $vectorIndexProduct = $leftValue * $rightValue;
$productValue += $vectorIndexProduct; $productValue += $vectorIndexProduct;
} }
@@ -339,8 +339,8 @@ class Matrix
for ($currentColumn = 0; $currentColumn < $this->columnCount; ++$currentColumn) { for ($currentColumn = 0; $currentColumn < $this->columnCount; ++$currentColumn) {
$delta = $delta =
abs( abs(
(float)$this->getValue($currentRow, $currentColumn) - $this->getValue($currentRow, $currentColumn) -
(float)$otherMatrix->getValue($currentRow, $currentColumn) $otherMatrix->getValue($currentRow, $currentColumn)
); );
if ($delta > self::ERROR_TOLERANCE) { if ($delta > self::ERROR_TOLERANCE) {

View File

@@ -13,14 +13,20 @@ final readonly class Player implements ISupportPartialPlay, ISupportPartialUpdat
private const float DEFAULT_PARTIAL_UPDATE_PERCENTAGE = 1.0; private const float DEFAULT_PARTIAL_UPDATE_PERCENTAGE = 1.0;
/**
* @var float The weight percentage to give this player when calculating a new rank.
*/
private float $PartialPlayPct; private float $PartialPlayPct;
/**
* @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; 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.
*/ */

View File

@@ -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()

View File

@@ -192,7 +192,7 @@ final 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;
} }

View File

@@ -72,12 +72,12 @@ final 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 @@ final 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 @@ final 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;

View File

@@ -29,11 +29,7 @@ final class TeamPerformancesToTeamPerformanceDifferencesLayer extends TrueSkillF
} }
} }
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];

View File

@@ -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);
}
} }

View File

@@ -33,6 +33,8 @@ final 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).

View File

@@ -32,11 +32,7 @@ final class TwoPlayerTrueSkillCalculator extends SkillCalculator
* {@inheritdoc} * {@inheritdoc}
*/ */
#[\Override] #[\Override]
public function calculateNewRatings( public function calculateNewRatings(GameInfo $gameInfo, array $teams, array $teamRanks): RatingContainer
GameInfo $gameInfo,
array $teams,
array $teamRanks
): RatingContainer
{ {
// Basic argument checking // Basic argument checking
$this->validateTeamCountAndPlayersCountPerTeam($teams); $this->validateTeamCountAndPlayersCountPerTeam($teams);
@@ -118,11 +114,11 @@ final 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;

View File

@@ -64,13 +64,7 @@ final 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(),
@@ -94,7 +88,7 @@ final class TwoTeamTrueSkillCalculator extends SkillCalculator
+ +
BasicMath::sum($otherTeam->getAllRatings(), $varianceGetter) BasicMath::sum($otherTeam->getAllRatings(), $varianceGetter)
+ +
$totalPlayers * $betaSquared (float)$totalPlayers * $betaSquared
); );
$winningMean = $selfMeanSum; $winningMean = $selfMeanSum;
@@ -117,7 +111,7 @@ final 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);

View File

@@ -10,15 +10,8 @@ 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)]
#[UsesClass(\DNW\Skills\Numerics\Range::class)]
#[UsesClass(\DNW\Skills\PlayersRange::class)]
#[UsesClass(\DNW\Skills\SkillCalculator::class)]
#[UsesClass(\DNW\Skills\TeamsRange::class)]
final class FactorGraphTrueSkillCalculatorTest extends TestCase final class FactorGraphTrueSkillCalculatorTest extends TestCase
{ {
#[CoversNothing] #[CoversNothing]
@@ -75,6 +68,7 @@ final 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();

View File

@@ -811,15 +811,15 @@ final 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 @@ final 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());

View File

@@ -6,10 +6,8 @@ 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 final class TwoPlayerTrueSkillCalculatorTest extends TestCase
{ {
#[CoversNothing] #[CoversNothing]

View File

@@ -6,10 +6,8 @@ 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 final class TwoTeamTrueSkillCalculatorTest extends TestCase
{ {
#[CoversNothing] #[CoversNothing]