diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 855272a..0000000 --- a/.gitattributes +++ /dev/null @@ -1,7 +0,0 @@ -# *.php diff=php - -#/.gitattributes export-ignore -#/.gitignore export-ignore -#/.php_cs export-ignore -#/.travis.ini export-ignore -#/.travis.yml export-ignore diff --git a/.gitignore b/.gitignore index 8394ebf..4395ff4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +/bin/ +!/bin/.gitkeep /build/ /vendor/ composer.lock @@ -5,7 +7,8 @@ composer.lock /.php_cs.cache /nbproject/ /res/protected/ -/.idea +/.idea/*.* +!/.idea/codeStyles/* /doc/api/* /libpairtwo-*-dist.zip @@ -13,3 +16,5 @@ composer.lock /dist/vendor/ /dist/composer.json /dist/doc/ + +/.phpdoc diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..35629c3 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,134 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..79ee123 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..cd4ed5a --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/.php_cs b/.php_cs deleted file mode 100644 index 3357476..0000000 --- a/.php_cs +++ /dev/null @@ -1,12 +0,0 @@ -exclude(['build', 'vendor']) - ->in(__DIR__); - -return PhpCsFixer\Config::create() - ->setFinder($finder) - ->setUsingCache(true) - ->setRules([ - '@PSR2' => true, - ]); diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 47e576e..0000000 --- a/.travis.yml +++ /dev/null @@ -1,18 +0,0 @@ -language: php - -matrix: - fast_finish: true - include: - - php: 7.2 - - php: 7.2 - env: 'COMPOSER_FLAGS="--prefer-stable --prefer-lowest"' - -cache: - directories: - - $HOME/.composer/cache - -install: - - composer update ${COMPOSER_FLAGS} --no-interaction - -script: - - make tests diff --git a/Makefile b/Makefile index bcc9b8f..59761c7 100644 --- a/Makefile +++ b/Makefile @@ -6,14 +6,54 @@ VERSION := $(if $(TAG),$(TAG),dev-$(BRANCH)) help: @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-12s\033[0m %s\n", $$1, $$2}' -tests: ## Executes the test suite - vendor/bin/phpunit +install-dev: ## Installs the required common devtools + @echo "Downloading phpdoc" + @wget https://phpdoc.org/phpDocumentor.phar -O bin/phpdoc 2> /dev/null + @echo "Downloading phpcs" + @wget https://squizlabs.github.io/PHP_CodeSniffer/phpcs.phar -O bin/phpcs 2> /dev/null + @wget https://squizlabs.github.io/PHP_CodeSniffer/phpcbf.phar -O bin/phpcbf 2> /dev/null + @echo "Adding execution rights on the binaries" + @chmod +x bin/phpcs bin/phpcbf bin/phpdoc + @echo "Installation of devtools finished" + @echo "Please add $(shell echo $(PWD))/bin to your PATH" -coverage: ## Executes the test suite and creates code coverage reports - vendor/bin/phpunit --coverage-html build/coverage +docs: ## Generates api-docs + phpdoc -d ./src -t ./doc/api -view-coverage: ## Shows the code coverage report - open build/coverage/index.html +dist: ## Generates distribution + cp dist/composer* res/ + mv dist/composer-dist.json dist/composer.json + sed -i -e "s%//VERSION//%$(VERSION)%g" dist/composer.json + cd dist && composer install + rm dist/composer.json + rm dist/composer.lock + mv dist/composer-dist-installed.json dist/composer.json + make api + mkdir -p dist/doc + cp -r doc/api dist/doc + cd dist && zip -r ../libpairtwo-$(VERSION)-dist.zip * + git reset --hard HEAD + mv res/composer* dist/ + +clean: clean-dist clean-dev clean-repo ## Cleans all assets + +clean-dev: ## Cleans dev assets + rm -rf doc/api + rm -rf .idea + rm -rf .libpairtwo-distro + rm -rf vendor + rm -rf composer.lock + +clean-dist: ## Cleans distribution assets + rm -rf dist/doc + rm -rf dist/vendor + rm -rf dist/composer.json + rm -rf libpairtwo-*-dist.zip + +clean-repo: ## Cleans the git repository + git fsck + git prune + git gc api: ## Generates api-docs phpdoc -d ./src -t ./doc/api @@ -54,7 +94,7 @@ clean-repo: ## Cleans the git repository git gc cs: ## Fixes coding standard problems - vendor/bin/php-cs-fixer fix || true + php bin/phpcs || true tag: ## Creates a new signed git tag $(if $(TAG),,$(error TAG is not defined. Pass via "make tag TAG=X.X.X")) diff --git a/README.md b/README.md index aa48978..5b254e2 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # libpairtwo -![logo](https://raw.githubusercontent.com/JeroenED/libpairtwo/master/res/logo-lightback-250px.png "Logo libpairtwo") +![logo](https://raw.githubusercontent.com/JeroenED/libpairtwo/develop/res/logo-lightback-250px.png "Logo libpairtwo") Copyright 2018- (c) Jeroen De Meerleer diff --git a/bin/.gitkeep b/bin/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/composer.json b/composer.json index adde7bd..5b8dbf7 100644 --- a/composer.json +++ b/composer.json @@ -2,7 +2,10 @@ "name": "jeroened/libpairtwo", "type": "library", "description": "Export chess tournaments directly on the web", - "keywords": ["chess", "pairing"], + "keywords": [ + "chess", + "pairing" + ], "license": "AGPL-3.0-or-later", "authors": [ { @@ -18,7 +21,6 @@ "ext-iconv": "*" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^2.16" }, "autoload": { "psr-4": { diff --git a/dist/composer-dist.json b/dist/composer-dist.json index 7de20d9..7de9466 100644 --- a/dist/composer-dist.json +++ b/dist/composer-dist.json @@ -5,8 +5,8 @@ "url": "../" } ], - "require": { + "require": { "jeroened/libpairtwo": "//VERSION//" }, "minimum-stability": "dev" -} \ No newline at end of file +} diff --git a/dist/template.php b/dist/template.php index 9cd3e19..986bc3b 100644 --- a/dist/template.php +++ b/dist/template.php @@ -27,7 +27,10 @@ use JeroenED\Libpairtwo\IOFactory; require_once 'vendor/autoload.php'; if (!file_exists($pairingfile)) { - trigger_error('Your file is not set or doesn\'t exist! Edit the file: ' . __FILE__ . ' and try again', E_USER_ERROR); + trigger_error( + 'Your file is not set or doesn\'t exist! Edit the file: ' . __FILE__ . ' and try again', + E_USER_ERROR + ); } $reader = IOFactory::createReader($fileformat); @@ -49,8 +52,18 @@ foreach ($reader->Tournament->Rounds as $round) { foreach ($round->GamesByBoard as $game) { echo '' . PHP_EOL; echo '' . ($game->Board + 1) . '' . PHP_EOL; - echo '' . $game->White->Player->Name . '' . PHP_EOL; - echo '' . $game->Black->Player->Name . '' . PHP_EOL; + echo '' . + $game->White->Player->Name . + ' (' . + $game->White->Player->getElo($reader->Tournament->PriorityElo) . + ')' . + PHP_EOL; + echo '' . + $game->Black->Player->Name . + ' (' . + $game->Black->Player->getElo($reader->Tournament->PriorityElo) . + ')' . + PHP_EOL; echo '' . $game->Result->getValue() . '' . PHP_EOL; echo '' . PHP_EOL; } @@ -73,11 +86,11 @@ foreach ($reader->Tournament->Rounds as $round) { echo '

' . PHP_EOL; } -echo '

Rankings

' . PHP_EOL; +echo '

Global Rankings

' . PHP_EOL; echo '' . PHP_EOL; echo '' . PHP_EOL; -echo '' . PHP_EOL; -foreach ($reader->Tournament->TieBreaks as $tiebreak) { +echo '' . PHP_EOL; +foreach ($reader->Tournament->Tiebreaks as $tiebreak) { echo '' . PHP_EOL; } echo '' . PHP_EOL; @@ -88,7 +101,8 @@ $rank = 1; foreach ($reader->Tournament->Ranking as $player) { echo '' . PHP_EOL; echo '' . PHP_EOL; - echo '' . PHP_EOL; + echo '' . PHP_EOL; + echo '' . PHP_EOL; echo '' . PHP_EOL; echo '' . PHP_EOL; $rank++; @@ -96,6 +110,35 @@ foreach ($reader->Tournament->Ranking as $player) { echo '' . PHP_EOL; echo '
Name (elo)
Name (elo)Category' . $tiebreak->getValue() . '
' . $rank . '' . $player->Name . '(' . $player->getElo($reader->Tournament->PriorityElo) . ')' . $player->Name . ' (' . $player->getElo($reader->Tournament->PriorityElo) . ')' . $player->Category . '' . implode('', $player->Tiebreaks) . '
' . PHP_EOL; ?> - + +Rankings per Category' . PHP_EOL; + +foreach ($reader->Tournament->Categories as $category) { + echo '' . PHP_EOL; + echo ''; + echo '' . PHP_EOL; + echo '' . PHP_EOL; + foreach ($reader->Tournament->Tiebreaks as $tiebreak) { + echo '' . PHP_EOL; + } + echo '' . PHP_EOL; + echo '' . PHP_EOL; + echo '' . PHP_EOL; + + $rank = 1; + foreach ($reader->Tournament->RankingForCategory($category) as $player) { + echo '' . PHP_EOL; + echo '' . PHP_EOL; + echo '' . PHP_EOL; + echo '' . PHP_EOL; + echo '' . PHP_EOL; + $rank++; + } + echo '' . PHP_EOL; + echo '
' . $category . '
Name (elo)' . $tiebreak->getValue() . '
' . $rank . '' . $player->Name . ' (' . $player->getElo($reader->Tournament->PriorityElo) . ')' . implode('', $player->Tiebreaks) . '
' . PHP_EOL; +} +?> + diff --git a/phpunit.xml.dist b/phpunit.xml.dist deleted file mode 100644 index 8ef43a4..0000000 --- a/phpunit.xml.dist +++ /dev/null @@ -1,21 +0,0 @@ - - - - - ./tests - - - - - - ./src - - ./tests - ./vendor - - - - diff --git a/res/categories_test.sws b/res/categories_test.sws new file mode 100644 index 0000000..0303e98 Binary files /dev/null and b/res/categories_test.sws differ diff --git a/res/logo-darkback.svg b/res/logo-darkback.svg index 0120264..e40b337 100644 --- a/res/logo-darkback.svg +++ b/res/logo-darkback.svg @@ -2,194 +2,193 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - + xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns="http://www.w3.org/2000/svg" + width="134.01421mm" + height="89.507103mm" + viewBox="0 0 134.01421 89.507103" + version="1.1" + id="svg8" + inkscape:version="0.92.3 (2405546, 2018-03-11)" + sodipodi:docname="logo-darkback.svg" + inkscape:export-filename="/mnt/Git-Projects/JeroenED/libpairtwo/res/logo-lightback.svg.png" + inkscape:export-xdpi="95" + inkscape:export-ydpi="95"> + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/res/logo-lightback.svg b/res/logo-lightback.svg index 190bac0..fb5eb83 100644 --- a/res/logo-lightback.svg +++ b/res/logo-lightback.svg @@ -2,194 +2,193 @@ - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - + xmlns:osb="http://www.openswatchbook.org/uri/2009/osb" + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns="http://www.w3.org/2000/svg" + width="134.01421mm" + height="89.507103mm" + viewBox="0 0 134.01421 89.507103" + version="1.1" + id="svg8" + inkscape:version="0.92.3 (2405546, 2018-03-11)" + sodipodi:docname="logo-lightback.svg" + inkscape:export-filename="/mnt/Git-Projects/JeroenED/libpairtwo/res/logo-lightback.svg.png" + inkscape:export-xdpi="95" + inkscape:export-ydpi="95"> + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/res/testsws.html b/res/testsws.html index 69407ae..e1e93b1 100644 --- a/res/testsws.html +++ b/res/testsws.html @@ -1,215 +1,340 @@ - - - - -Libpairtwo Tournament - - - - - - - - - - - - - -

Libpairtwo Tournament

- - - -
- - - - - -
313 - - - - - - -
(©) FRBE 2005PairTwo 6.13 (2015/08/10)
Org. : Libpairtwo Organiser ,Libpairtwo Place ,2019
Arbit. : Libpairtwo Organiser
Tempo : 40c/2h + 1h QPF
Tie BreakPts, Mutual Result, Number of Victories, Number of game Black played, Bucholtz Cut 1, SonneBorn-Berger
-
F.R.B.E.
- - - -
- - - - - - -
R1R2R3
American Ranking
-
-
- - - - - - - - - - - - -
Ranking after round 3
-   Tit Name                    Rati   F/M  Club                        Cou    Pts  Res   Vic   #Bla  BC1   S B  
-  1 FM Libpairtwo Player 2    (1802)  M    Liga Vlaams-Brabant (VSF)  BEL   3.0/3    0.0     0     0   0.0  0.00 
-  2  * Libpairtwo Player 1    (1801)  M    Liga Antwerpen (VSF)       BEL   1.5/3    0.0     0     0   0.0  0.00 
-  3 IM Libpairtwo Player 3    (1803)  F    Liga West Vlaanderen (VSF) BEL   1.0/2    0.0     0     0   0.0  0.00 
-  4 GM Libpairtwo Player 4    (1804)  F    Liga Oost Vlaanderen (VSF) BEL   0.5/2    0.0     0     0   0.0  0.00 
-Top of Page
-
Round 1 (01-01-2019)
-  1 Libpairtwo Player 2   ( 3)  1-0  Libpairtwo Player 4   ( 1) 
-  2 Libpairtwo Player 3   ( 2)  0-1  Libpairtwo Player 1   ( 4) 
-
- -(Go to Top) -
Round 2 (06-01-2019)
-  1 Libpairtwo Player 1   ( 4)  0-1  Libpairtwo Player 2   ( 3) 
-  2 Libpairtwo Player 3   ( 2)  Bye
-    Libpairtwo Player 4   (  1) Absent      
-
- -(Go to Top) -
Round 3 (11-01-2019)
-  1 Libpairtwo Player 4   ( 1)  ½-½  Libpairtwo Player 1   ( 4) 
-  2 Libpairtwo Player 2   ( 3)  Bye
-    Libpairtwo Player 3   (  2) Absent      
-
- -(Go to Top) -
- - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-American Ranking -
 NameRating PtsPerf R1R2R3Res Vic #BlaBC1 S B
1Libpairtwo Player 2 (FM)18023.02602 4W + 2B +bye 0.000 0.0 0.00
2Libpairtwo Player 1 (*)18011.51810 3B + 1W - 4B = 0.000 0.0 0.00
3Libpairtwo Player 3 (IM)18031.01331 2W -bye... 0.000 0.0 0.00
4Libpairtwo Player 4 (GM)18040.51626 1B -... 2W = 0.000 0.0 0.00
- -

Top of Page - - - - -

- - -
-(©) FRBE 2005 -PairTwo 6.13 (2015/08/10)

- + + + + + Libpairtwo Tournament + + + + + + + + + + + + + + + + +

Libpairtwo Tournament

+ + + +
+ + + + + + +
313 + + + + + + + + + + + + + + + + + + + + + +
(©) FRBE 2005PairTwo 6.13 + (2015/08/10)
Org. : Libpairtwo Organiser ,Libpairtwo Place ,2019
Arbit. : Libpairtwo Organiser
Tempo : 40c/2h + 1h QPF
Tie BreakPts, Mutual Result, Number of Victories, Number of game Black played, Bucholtz + Cut 1, SonneBorn-Berger +
+
F.R.B.E.
+ + + +
+ + + + + + + + + +
R1R2R3
American Ranking
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Ranking after round 3
+   Tit Name                    Rati   F/M  Club                        Cou    Pts  Res   Vic   #Bla  BC1   S B  
+  1 FM Libpairtwo Player 2    (1802)  M    Liga Vlaams-Brabant (VSF)  BEL   3.0/3    0.0     0     0   0.0  0.00
+  2  * Libpairtwo Player 1    (1801)  M    Liga Antwerpen (VSF)       BEL   1.5/3    0.0     0     0   0.0  0.00
+  3 IM Libpairtwo Player 3    (1803)  F    Liga West Vlaanderen (VSF) BEL   1.0/2    0.0     0     0   0.0  0.00
+  4 GM Libpairtwo Player 4    (1804)  F    Liga Oost Vlaanderen (VSF) BEL   0.5/2    0.0     0     0   0.0  0.00
+Top of Page
+
+
Round 1 (01-01-2019)
+  1 Libpairtwo Player 2   ( 3)  1-0  Libpairtwo Player 4   ( 1)
+  2 Libpairtwo Player 3   ( 2)  0-1  Libpairtwo Player 1   ( 4)
+
+ + (Go to Top) +
Round 2 (06-01-2019)
+  1 Libpairtwo Player 1   ( 4)  0-1  Libpairtwo Player 2   ( 3)
+  2 Libpairtwo Player 3   ( 2)  Bye
+    Libpairtwo Player 4   (  1) Absent      
+
+ + (Go to Top) +
Round 3 (11-01-2019)
+  1 Libpairtwo Player 4   ( 1)  �-�  Libpairtwo Player 1   ( 4)
+  2 Libpairtwo Player 2   ( 3)  Bye
+    Libpairtwo Player 3   (  2) Absent      
+
+ + (Go to Top) +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ American Ranking +
 NameRating PtsPerf R1R2R3Res Vic #BlaBC1 S B
1Libpairtwo Player 2 (FM)18023.02602 4W + 2B +bye 0.000 0.0 0.00
2Libpairtwo Player 1 (*)18011.51810 3B + 1W - 4B = 0.000 0.0 0.00
3Libpairtwo Player 3 (IM)18031.01331 2W -bye... 0.000 0.0 0.00
4Libpairtwo Player 4 (GM)18040.51626 1B -... 2W = 0.000 0.0 0.00
+
+ +

Top of Page + + + + +

+ + + + + +
+ (©) FRBE 2005 + PairTwo 6.13 (2015/08/10)
+

+ + diff --git a/src/Constants.php b/src/Constants.php index 68ec994..6992f7c 100644 --- a/src/Constants.php +++ b/src/Constants.php @@ -1,13 +1,14 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo; @@ -20,18 +21,35 @@ use JeroenED\Libpairtwo\Enums\Result; * * Static class for constants * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ class Constants { - const Won = [ Result::Won, Result::WonForfait, Result::WonBye, Result::WonAdjourned ]; - const Draw = [ Result::Draw, Result::DrawAdjourned ]; - const Lost = [ Result::Absent, Result::Bye, Result::Lost, Result::Adjourned ]; - const NotPlayed = [ Result::Bye, Result::WonBye, Result::Absent ]; - const Played = [ Result::Won, Result::WonForfait, Result::WonBye, Result::WonAdjourned, Result::Draw, Result::DrawAdjourned, Result::Absent, Result::Bye, Result::Lost, Result::Adjourned ]; - const Black = [ Color::Black ]; - const White = [ Color::White ]; + public const BLACK = [Color::BLACK]; + + public const DRAW = [Result::DRAW, Result::DRAW_ADJOURNED]; + + public const LOST = [Result::ABSENT, Result::BYE, Result::LOST, Result::ADJOURNED]; + + public const NOTPLAYED = [Result::BYE, Result::WON_BYE, Result::ABSENT]; + + public const PLAYED = [ + Result::WON, + Result::WON_FORFAIT, + Result::WON_BYE, + Result::WON_ADJOURNED, + Result::DRAW, + Result::DRAW_ADJOURNED, + Result::ABSENT, + Result::BYE, + Result::LOST, + Result::ADJOURNED + ]; + + public const WHITE = [Color::WHITE]; + + public const WON = [Result::WON, Result::WON_FORFAIT, Result::WON_BYE, Result::WON_ADJOURNED]; } diff --git a/src/Enums/Color.php b/src/Enums/Color.php index 32d4827..fb2631e 100644 --- a/src/Enums/Color.php +++ b/src/Enums/Color.php @@ -1,13 +1,14 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo\Enums; @@ -19,14 +20,16 @@ use MyCLabs\Enum\Enum; * * List of all compatible colors * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ class Color extends Enum { - const Black = 'B'; - const White = 'W'; - const None = '*'; + public const BLACK = 'B'; + + public const NONE = '*'; + + public const WHITE = 'W'; } diff --git a/src/Enums/Gameresult.php b/src/Enums/Gameresult.php index bf0b6f9..450d6e3 100644 --- a/src/Enums/Gameresult.php +++ b/src/Enums/Gameresult.php @@ -1,13 +1,14 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo\Enums; @@ -19,24 +20,36 @@ use MyCLabs\Enum\Enum; * * List of all compatible gameresults * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ class Gameresult extends Enum { - const None = '-'; - const WhiteWins = '1-0'; - const Draw = '0.5-0.5'; - const BlackWins = '0-1'; - const WhiteWinsForfait = '1-0 FF'; - const BlackWinsForfait = '0-1 FF'; - const BothLoseForfait = '0-0 FF'; - const BothWinAdjourned = '1-1 A'; - const WhiteWinsBlackDrawsAdjourned = '1-0.5 A'; - const WhiteDrawsBlackWinsAdjourned = '0.5-1 A'; - const DrawAdjourned = '0.5-0.5 A'; - const WhiteLoseBlackDrawsAdjourned = '0-0.5'; - const WhiteDrawsBlackLoseAdjourned = '0.5-0'; + public const BLACK_WINS = '0-1'; + + public const BLACK_WINS_FORFAIT = '0-1 FF'; + + public const BOTH_LOSE_FORFAIT = '0-0 FF'; + + public const BOTH_WIN_ADJOURNED = '1-1 A'; + + public const DRAW = '0.5-0.5'; + + public const DRAW_ADJOURNED = '0.5-0.5 A'; + + public const NONE = '-'; + + public const WHITE_DRAWS_BLACK_LOSE_ADJOURNED = '0.5-0'; + + public const WHITE_DRAWS_BLACK_WINS_ADJOURNED = '0.5-1 A'; + + public const WHITE_LOST_BLACK_DRAWS_ADJOURNED = '0-0.5'; + + public const WHITE_WINS = '1-0'; + + public const WHITE_WINS_BLACK_DRAWS_ADJOURNED = '1-0.5 A'; + + public const WHITE_WINS_FORFAIT = '1-0 FF'; } diff --git a/src/Enums/Gender.php b/src/Enums/Gender.php index dae4b30..4a50021 100644 --- a/src/Enums/Gender.php +++ b/src/Enums/Gender.php @@ -1,13 +1,14 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo\Enums; @@ -19,14 +20,16 @@ use MyCLabs\Enum\Enum; * * List of all compatible genders * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ class Gender extends Enum { - const Neutral = 'X'; // Unforturnately, Incompatible with Pairtwo (Dinos) - const Male = 'M'; - const Female = 'F'; + public const FEMALE = 'F'; + + public const MALE = 'M'; + + public const NEUTRAL = 'X'; // Unfortunately, Incompatible with Pairtwo (Dinos) } diff --git a/src/Enums/Result.php b/src/Enums/Result.php index 15922a2..37b11be 100644 --- a/src/Enums/Result.php +++ b/src/Enums/Result.php @@ -1,13 +1,14 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo\Enums; @@ -19,22 +20,32 @@ use MyCLabs\Enum\Enum; * * List of all compatible results * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ class Result extends Enum { - const None = '*'; - const Lost = '0'; - const Draw = '0.5'; - const Won = '1'; - const Absent = '0 FF'; - const WonForfait = '1 FF'; - const Adjourned = '0 A'; - const DrawAdjourned = '0.5 A'; - const WonAdjourned = '1 A'; - const Bye = '0 Bye'; - const WonBye = '1 Bye'; + public const ABSENT = '0 FF'; + + public const ADJOURNED = '0 A'; + + public const BYE = '0 Bye'; + + public const DRAW = '0.5'; + + public const DRAW_ADJOURNED = '0.5 A'; + + public const LOST = '0'; + + public const NONE = '*'; + + public const WON = '1'; + + public const WON_ADJOURNED = '1 A'; + + public const WON_BYE = '1 Bye'; + + public const WON_FORFAIT = '1 FF'; } diff --git a/src/Enums/Tiebreak.php b/src/Enums/Tiebreak.php index 3f9745b..b799bf7 100644 --- a/src/Enums/Tiebreak.php +++ b/src/Enums/Tiebreak.php @@ -1,13 +1,14 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo\Enums; @@ -19,34 +20,56 @@ use MyCLabs\Enum\Enum; * * List of all compatible tiebreaks * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ class Tiebreak extends Enum { - const None = ""; - const Keizer = "Keizer"; - const Points = "Points"; - const Buchholz = "Buchholz"; - const BuchholzMed = "Buchholz Median"; - const BuchholzCut = "Buchholz Cut"; - const BuchholzMed2 = "Buchholz Median 2"; - const BuchholzCut2 = "Buchholz Cut 2"; - const Sonneborn = "Sonneborn-Berger"; - const Kashdan = "Kashdan"; - const SoccerKashdan = "Soccer Kashdan"; - const Cumulative = "Cumulative"; - const Between = "Mutual Result"; - const Koya = "Koya"; - const Baumbach = "Most wins"; // Ref: https://en.wikipedia.org/wiki/Tie-breaking_in_Swiss-system_tournaments#Most_wins_(Baumbach) Please tell me why? - const AveragePerformance = "Average performance"; - const Performance = "Performance"; - const Aro = "Average Rating"; - const AroCut = "Average Rating Cut"; - const BlackPlayed = "Black played"; - const BlackWin = "Black Winned"; - const Testmatch = "Testmatch"; - const Drawing = "Drawing of lot"; + public const ARO = "Average Rating"; + + public const AROCUT = "Average Rating Cut"; + + public const AVERAGE_PERFORMANCE = "Average performance"; + + public const BAUMBACH = "Most wins"; // Ref: https://en.wikipedia.org/wiki/Tie-breaking_in_Swiss-system_tournaments#Most_wins_(Baumbach) Please tell me why? + + public const BETWEEN = "Mutual Result"; + + public const BLACK_PLAYED = "Black played"; + + public const BLACK_WIN = "Black Winned"; + + public const BUCHHOLZ = "Buchholz"; + + public const BUCHHOLZ_CUT = "Buchholz Cut"; + + public const BUCHHOLZ_CUT_2 = "Buchholz Cut 2"; + + public const BUCHHOLZ_MED = "Buchholz Median"; + + public const BUCHHOLZ_MED_2 = "Buchholz Median 2"; + + public const CUMULATIVE = "Cumulative"; + + public const DRAWING_OF_LOT = "Drawing of lot"; + + public const KASHDAN = "Kashdan"; + + public const KEIZER = "Keizer"; + + public const KOYA = "Koya"; + + public const NONE = ""; + + public const PERFORMANCE = "Performance"; + + public const POINTS = "Points"; + + public const SOCCER_KASHDAN = "Soccer Kashdan"; + + public const SONNEBORN = "Sonneborn-Berger"; + + public const TESTMATCH = "Testmatch"; } diff --git a/src/Enums/Title.php b/src/Enums/Title.php index e27cf94..9cc8f86 100644 --- a/src/Enums/Title.php +++ b/src/Enums/Title.php @@ -1,13 +1,14 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo\Enums; @@ -19,24 +20,36 @@ use MyCLabs\Enum\Enum; * * List of all compatible titles * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ class Title extends Enum { - const NONE = '*'; - const ELO = 'Elo'; - const NM = 'National Master'; - const WCM = 'Woman Candidate Master'; - const WFM = 'Woman Fide Master'; - const CM = 'Candidate Master'; - const WIM = 'Woman International Master'; - const FM = 'Fide Master'; - const WGM = 'Woman Grand Master'; - const HM = 'Honorary International Master'; - const IM = 'International Master'; - const HG = 'Honorary Grand Master'; - const GM = 'Grand Master'; + public const CM = 'Candidate Master'; + + public const ELO = 'Elo'; + + public const FM = 'Fide Master'; + + public const GM = 'Grand Master'; + + public const HG = 'Honorary Grand Master'; + + public const HM = 'Honorary International Master'; + + public const IM = 'International Master'; + + public const NM = 'National Master'; + + public const NONE = '*'; + + public const WCM = 'Woman Candidate Master'; + + public const WFM = 'Woman Fide Master'; + + public const WGM = 'Woman Grand Master'; + + public const WIM = 'Woman International Master'; } diff --git a/src/Enums/TournamentSystem.php b/src/Enums/TournamentSystem.php index 2109555..965fb89 100644 --- a/src/Enums/TournamentSystem.php +++ b/src/Enums/TournamentSystem.php @@ -1,13 +1,14 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo\Enums; @@ -19,15 +20,18 @@ use MyCLabs\Enum\Enum; * * List of all compatible tournament systems * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ class TournamentSystem extends Enum { - const Swiss = 'Swiss'; - const Closed = 'Closed'; - const American = 'American'; - const Keizer = 'Keizer'; + public const AMERICAN = 'American'; + + public const CLOSED = 'Closed'; + + public const KEIZER = 'Keizer'; + + public const SWISS = 'Swiss'; } diff --git a/src/Exceptions/IncompatibleReaderException.php b/src/Exceptions/IncompatibleReaderException.php index 5a4cd41..1f10e4c 100644 --- a/src/Exceptions/IncompatibleReaderException.php +++ b/src/Exceptions/IncompatibleReaderException.php @@ -1,13 +1,14 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo\Exceptions; @@ -17,10 +18,10 @@ namespace JeroenED\Libpairtwo\Exceptions; * * Exception to be raised when a reader is used that is incompatible * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ class IncompatibleReaderException extends LibpairtwoException { diff --git a/src/Exceptions/LibpairtwoException.php b/src/Exceptions/LibpairtwoException.php index 147b783..1e08948 100644 --- a/src/Exceptions/LibpairtwoException.php +++ b/src/Exceptions/LibpairtwoException.php @@ -1,27 +1,30 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo\Exceptions; +use Exception; + /** * Exception LibpairtwoException * * General exception when something goes wrong with libpairtwo * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ -class LibpairtwoException extends \Exception +class LibpairtwoException extends Exception { } diff --git a/src/Game.php b/src/Game.php index 00d379e..4597043 100644 --- a/src/Game.php +++ b/src/Game.php @@ -1,54 +1,39 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo; use JeroenED\Libpairtwo\Enums\Gameresult; -use JeroenED\Libpairtwo\Pairing; -use DateTime; /** * Class Games * * Class for a game of the tournament * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ class Game { /** - * The pairing for this games as seen from white's side - * - * @var Pairing | null - */ - public $White; - - /** - * The pairing for this games as seen from blacks's side + * The pairing for this games as seen from black's side * * @var Pairing | null */ public $Black; - /** - * The calculated game result - * - * @var GameResult | null - */ - private $CalculatedResult; - /** * The board where this game is held * @@ -56,10 +41,26 @@ class Game */ public $Board; + /** + * The calculated game result + * + * @var GameResult | null + */ + private $CalculatedResult; + + /** + * The pairing for this games as seen from white's side + * + * @var Pairing | null + */ + public $White; + /** * Returns fields that were not directly assigned. * Class Game contains the special field Result containing the result of the game + * * @param string $key + * * @return Gameresult */ public function __get(string $key) @@ -67,6 +68,7 @@ class Game if ($key == 'Result') { return $this->calculateResult(); } + return null; } @@ -88,20 +90,20 @@ class Game $whitesplit = explode(" ", $whiteResult); $blacksplit = explode(" ", $blackResult); - $special=''; - if (isset($whitesplit[1]) && $whitesplit[1] != 'Bye') { - $special = ' ' . $whitesplit[1]; + $special = ''; + if (isset($whitesplit[ 1 ]) && $whitesplit[ 1 ] != 'Bye') { + $special = ' ' . $whitesplit[ 1 ]; } - if (isset($blacksplit[1]) && $blacksplit[1] != 'Bye') { - $special = ' ' . $blacksplit[1]; + if (isset($blacksplit[ 1 ]) && $blacksplit[ 1 ] != 'Bye') { + $special = ' ' . $blacksplit[ 1 ]; } - if ($whitesplit[0] == '*') { - $whitesplit[0] = ''; + if ($whitesplit[ 0 ] == '*') { + $whitesplit[ 0 ] = ''; } - if ($blacksplit[0] == '*') { - $blacksplit[0] = ''; + if ($blacksplit[ 0 ] == '*') { + $blacksplit[ 0 ] = ''; } - $result = new Gameresult($whitesplit[0] . '-' . $blacksplit[0] . $special); + $result = new Gameresult($whitesplit[ 0 ] . '-' . $blacksplit[ 0 ] . $special); $this->CalculatedResult = $result; return $result; @@ -111,6 +113,7 @@ class Game * Checks if 2 games are equal * * @param Game $game + * * @return bool */ public function equals(Game $game): bool @@ -118,6 +121,6 @@ class Game return ( $this->White->Player === $game->White->Player && $this->Black->Player === $game->Black->Player && - $this->Result == $game->Result); + $this->Result->getKey() == $game->Result->getKey()); } } diff --git a/src/IOFactory.php b/src/IOFactory.php index 9b3e70e..393ece8 100644 --- a/src/IOFactory.php +++ b/src/IOFactory.php @@ -1,30 +1,30 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo; use JeroenED\LibPairtwo\Exceptions\LibpairtwoException; use JeroenED\Libpairtwo\Interfaces\ReaderInterface; -use JeroenED\Libpairtwo\Readers\Pairtwo6; /** * Class IOFactory * * Class for creating readers for pairing files * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ abstract class IOFactory { @@ -34,31 +34,30 @@ abstract class IOFactory * @var array */ private static $Readers = [ - 'Swar-4' => Readers\Swar4::class, + 'Swar-4' => Readers\Swar4::class, 'Pairtwo-6' => Readers\Pairtwo6::class, 'Pairtwo-5' => Readers\Pairtwo6::class // File structure identical ]; - /** * Creates a reader for $type * * Compatible types are Swar-4, Pairtwo-5, Pairtwo-6 * * @param string $type + * * @return ReaderInterface * @throws LibpairtwoException */ public static function createReader(string $type): ReaderInterface { - if (!isset(self::$Readers[$type])) { + if (!isset(self::$Readers[ $type ])) { throw new LibpairtwoException("Cannot read type $type"); } // create reader class - $readerClass = self::$Readers[$type]; - $reader = new $readerClass; + $readerClass = self::$Readers[ $type ]; - return $reader; + return new $readerClass(); } } diff --git a/src/Interfaces/ReaderInterface.php b/src/Interfaces/ReaderInterface.php index 2cd6bcd..b84ed32 100644 --- a/src/Interfaces/ReaderInterface.php +++ b/src/Interfaces/ReaderInterface.php @@ -1,36 +1,36 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo\Interfaces; -use JeroenED\Libpairtwo\Tournament; - /** * Interface ReaderInterface * * Sets the methods a reader needs to implement * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ interface ReaderInterface { /** * Reads out $filename * - * @param $filename - * @return ReaderInterface + * @param $filename + * + * @return void */ public function read(string $filename): void; } diff --git a/src/Pairing.php b/src/Pairing.php index 33dd05d..25730f1 100644 --- a/src/Pairing.php +++ b/src/Pairing.php @@ -1,13 +1,14 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo; @@ -20,26 +21,19 @@ use JeroenED\Libpairtwo\Enums\Result; * * Class for a pairing of the tournament * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ class Pairing { /** - * The player of the pairing. Please note this means the pairing was seen from the point of view of this player + * The number of the board where the game was held * - * @var Player | null + * @var int */ - public $Player; - - /** - * The opponent of player - * - * @var Player | null - */ - public $Opponent; + public $Board; /** * The color of the player. @@ -49,8 +43,23 @@ class Pairing */ public $Color; + /** + * The opponent of player + * + * @var Player | null + */ + public $Opponent; + + /** + * The player of the pairing. Please note this means the pairing was seen from the point of view of this player + * + * @var Player | null + */ + public $Player; + /** * The result of the Game. Possible values contain Won, Lost, Draw, Forfait, Bye, etc. + * * @var Result */ public $Result; @@ -61,11 +70,4 @@ class Pairing * @var int */ public $Round; - - /** - * The number of the board where the game was held - * - * @var int - */ - public $Board; } diff --git a/src/Player.php b/src/Player.php index 3184228..23a2f53 100644 --- a/src/Player.php +++ b/src/Player.php @@ -1,53 +1,48 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo; +use DateTime; use JeroenED\Libpairtwo\Enums\Gender; use JeroenED\Libpairtwo\Enums\Title; -use DateTime; /** * Class Player * * Class for a player of the tournament * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ class Player { /** - * Name of the player + * Binary data that was read out of the pairing file + * + * @var bool|DateTime|int|string[] + */ + + private $BinaryData; + + /** + * The category the player belongs to * * @var string */ - public $Name; - - /** - * The player ids for the player. Possible keys are, but not limited to nation and fide - * - * @var int[] - */ - public $Ids; - - /** - * The Elos for the player. Possible keys are, but not limited to nation and fide - * - * @var int[] - */ - public $Elos; + public $Category; /** * Birthday of the player @@ -57,33 +52,11 @@ class Player public $DateOfBirth; /** - * Tiebreak points of the player. These values are calculated when Tournament->Ranking is called + * The Elos for the player. Possible keys are, but not limited to nation and fide * - * @var float[] + * @var int[] */ - public $Tiebreaks = []; - - /** - * The nation the player belongs to. Be noted this does not actually mean this is his main nationality. A player can be signed USCF but may be Italian - * - * @var string - */ - public $Nation; - - // TODO: Implement categories - /** - * The category the player belongs to - * - * @var string - */ - public $Category; - - /** - * The title of the player. Possible values can be GM, IM, IA, etc. - * - * @var Title - */ - public $Title; + public $Elos; /** * The gender of the player. Possible values contain Male, Female and Neutral @@ -92,6 +65,30 @@ class Player */ public $Gender; + /** + * The player ids for the player. Possible keys are, but not limited to nation and fide + * + * @var int[] + */ + public $Ids; + + // TODO: Implement categories + + /** + * Name of the player + * + * @var string + */ + public $Name; + + /** + * The nation the player belongs to. Be noted this does not actually mean this is his main nationality. A player + * can be signed USCF but may be Italian + * + * @var string + */ + public $Nation; + /** * The pairings of the player * @@ -100,12 +97,112 @@ class Player public $Pairings = []; /** - * Binary data that was read out of the pairing file + * Tiebreak points of the player. These values are calculated when Tournament->Ranking is called * - * @var bool|DateTime|int|string[] + * @var float[] */ + public $Tiebreaks = []; - private $BinaryData; + /** + * The title of the player. Possible values can be GM, IM, IA, etc. + * + * @var Title + */ + public $Title; + + /** + * Returns the performance rating of the player + * + * WARNING: Calculation currently incorrect. Uses the rule of 400 as temporary solution + * + * @param $type + * @param $unratedElo + * + * @return float + */ + public function Performance(string $type, int $unratedElo): float + { + $total = 0; + $opponents = 0; + foreach ($this->Pairings as $pairing) { + if (array_search($pairing->Result, Constants::NOTPLAYED) === false) { + $opponentElo = $pairing->Opponent->getElo($type); + $opponentElo = $opponentElo != 0 ? $opponentElo : $unratedElo; + if (array_search($pairing->Result, Constants::WON) !== false) { + $total += $opponentElo + 400; + } elseif (array_search($pairing->Result, Constants::LOST) !== false) { + $total += $opponentElo - 400; + } elseif (array_search($pairing->Result, Constants::DRAW) !== false) { + $total += $opponentElo; + } + $opponents++; + } + } + + return round($total / $opponents); + } + + /** + * Returns an array of Player objects where name matches $search + * + * @param string $search + * @param Tournament $tournament + * + * @return Player[] + */ + public static function PlayersByName(string $search, Tournament $tournament): array + { + /** + * @var Player[] + */ + $players = $tournament->Players; + + /** + * @var Player[] + */ + $return = []; + + foreach ($players as $player) { + if (fnmatch($search, $player->Name)) { + $return[] = $player; + } + } + + return $return; + } + + /** + * Magic method to read out several fields. If field was not found it is being searched in the binary data fields + * + * @param string $key + * + * @return bool|DateTime|int|string|null + */ + public function __get(string $key) + { + if ($key == 'PlayedGames') { + return $this->playedGames(); + } elseif ($key == 'NoOfWins') { + return $this->noOfWins(); + } elseif ($key == 'Opponents') { + return $this->opponents(); + } elseif (isset($this->BinaryData[ $key ])) { + return $this->BinaryData[ $key ]; + } + + return null; + } + + /** + * Sets binary data that is read out the pairing file but is not needed immediately + * + * @param string $key + * @param bool|int|DateTime|string $value + */ + public function __set(string $key, $value): void + { + $this->BinaryData[ $key ] = $value; + } /** * Adds a pairing to the tournament @@ -119,97 +216,6 @@ class Player $this->Pairings = $newArray; } - /** - * Returns an array of Player objects where name matches $search - * - * @param string $search - * @param Tournament $tournament - * @return Player[] - */ - public static function PlayersByName(string $search, Tournament $tournament): array - { - /** @var Player[] */ - $players = $tournament->Players; - - /** @var Player[] */ - $return = []; - - foreach ($players as $player) { - if (fnmatch($search, $player->Name)) { - $return[] = $player; - } - } - - return $return; - } - - /** - * Returns the elo of elotype for the player - * @param string $type - * @return int - */ - public function getElo(string $type): int - { - return $this->Elos[$type]; - } - - /** - * Sets the elo of elotype for the player - * - * @param string $type - * @param int $value - */ - public function setElo(string $type, int $value): void - { - $currentElos = $this->Elos; - $currentElos[$type] = $value; - $this->Elos = $currentElos; - } - - /** - * Returns the identifier of type for the player - * - * Common possible values are Fide or National - * - * @param string $type - * @return string - */ - public function getId(string $type): string - { - return $this->Ids[$type]; - } - - /** - * Sets the identifier of type for the player - * - * Common possible values are Fide or National - * - * @param string $type - * @param string $value - */ - public function setId(string $type, string $value): void - { - $currentIds = $this->Ids; - $currentIds[$type] = $value; - $this->Ids = $currentIds; - } - - /** - * Returns the number of won matches for the player - * - * @return int - */ - private function noOfWins(): int - { - $wins = 0; - foreach ($this->Pairings as $pairing) { - if (array_search($pairing->Result, Constants::Won) !== false) { - $wins++; - } - } - return $wins; - } - /** * Returns the points of the player after round $round * @@ -218,39 +224,25 @@ class Player * 0 points are awarded for loss * * @param int $round + * * @return float */ public function calculatePoints(int $round = -1): float { $points = 0; - foreach ($this->Pairings as $key=>$pairing) { + foreach ($this->Pairings as $key => $pairing) { if ($key < $round || $round == -1) { - if (array_search($pairing->Result, Constants::Won) !== false) { + if (array_search($pairing->Result, Constants::WON) !== false) { $points = $points + 1; - } elseif (array_search($pairing->Result, Constants::Draw) !== false) { + } elseif (array_search($pairing->Result, Constants::DRAW) !== false) { $points = $points + 0.5; } } } + return $points; } - /** - * Returns the points of a virtual player as described in the Fide Handbook C.02 chapter 13.15.2. - * - * Return the same score for all rounds until $byeround and added with a half point for each subsequent round - * - * @param int $byeround - * @return float - */ - public function calculatePointsForVirtualPlayer(int $byeround): float - { - $points = $this->calculatePoints($byeround); - foreach (array_slice($this->Pairings, $byeround +1) as $key=>$pairing) { - $points += 0.5; - } - return $points; - } /** * Returns the points of the player that should be used for tiebreaking systems. * @@ -264,74 +256,75 @@ class Player { $points = 0; foreach ($this->Pairings as $pairing) { - if (array_search($pairing->Result, Constants::NotPlayed) !== false) { + if (array_search($pairing->Result, Constants::NOTPLAYED) !== false) { $points = $points + 0.5; - } elseif (array_search($pairing->Result, Constants::Won) !== false) { + } elseif (array_search($pairing->Result, Constants::WON) !== false) { $points = $points + 1; - } elseif (array_search($pairing->Result, Constants::Draw) !== false) { + } elseif (array_search($pairing->Result, Constants::DRAW) !== false) { $points = $points + 0.5; } } + return $points; } + /** - * Returns the performance rating of the player + * Returns the points of a virtual player as described in the Fide Handbook C.02 chapter 13.15.2. * - * WARNING: Calculation currently incorrect. Uses the rule of 400 as temporary solution + * Return the same score for all rounds until $byeround and added with a half point for each subsequent round * - * @param $type - * @param $unratedElo - * @return int + * @param int $byeround + * + * @return float */ - public function Performance(string $type, int $unratedElo): float + public function calculatePointsForVirtualPlayer(int $byeround): float { - $total = 0; - $opponents = 0; - foreach ($this->Pairings as $pairing) { - if (array_search($pairing->Result, Constants::NotPlayed) === false) { - $opponentElo = $pairing->Opponent->getElo($type); - $opponentElo = $opponentElo != 0 ? $opponentElo : $unratedElo; - if (array_search($pairing->Result, Constants::Won) !== false) { - $total += $opponentElo + 400; - } elseif (array_search($pairing->Result, Constants::Lost) !== false) { - $total += $opponentElo - 400; - } elseif (array_search($pairing->Result, Constants::Draw) !== false) { - $total += $opponentElo; - } - $opponents++; - } + $points = $this->calculatePoints($byeround); + foreach (array_slice($this->Pairings, $byeround + 1) as $key => $pairing) { + $points += 0.5; } - return round($total / $opponents); + + return $points; } - /** - * Returns the number of played games of the player + * Returns the elo of elotype for the player + * + * @param string $type * * @return int */ - private function playedGames(): int + public function getElo(string $type): int { - $total = 0; - foreach ($this->Pairings as $pairing) { - if (array_search($pairing->Result, Constants::Played) !== false) { - $total++; - } - } - return $total; + return $this->Elos[ $type ]; + } + + /** + * Returns the identifier of type for the player + * + * Common possible values are Fide or National + * + * @param string $type + * + * @return string + */ + public function getId(string $type): string + { + return $this->Ids[ $type ]; } /** * Returns if player has played against all players of the array * * @param Player[] $players + * * @return bool */ public function hasPlayedAllPlayersOfArray(array $players): bool { $ownkey = array_search($this, $players); if ($ownkey !== false) { - unset($players[$ownkey]); + unset($players[ $ownkey ]); } $total = 0; foreach ($players as $player) { @@ -343,6 +336,23 @@ class Player return $total == count($players); } + /** + * Returns the number of won matches for the player + * + * @return int + */ + private function noOfWins(): int + { + $wins = 0; + foreach ($this->Pairings as $pairing) { + if (array_search($pairing->Result, Constants::WON) !== false) { + $wins++; + } + } + + return $wins; + } + /** * Returns all opponents of $this * @@ -350,43 +360,58 @@ class Player */ private function opponents() { - $return = array(); + $return = []; foreach ($this->Pairings as $pairing) { if (!empty($pairing->Opponent)) { $return[] = $pairing->Opponent; } } + return $return; } /** - * Magic method to read out several fields. If field was not found it is being searched in the binary data fields + * Returns the number of played games of the player * - * @param string $key - * @return bool|DateTime|int|string|null + * @return int */ - public function __get(string $key) + private function playedGames(): int { - if ($key == 'PlayedGames') { - return $this->playedGames(); - } elseif ($key == 'NoOfWins') { - return $this->noOfWins(); - } elseif ($key == 'Opponents') { - return $this->opponents(); - } elseif (isset($this->BinaryData[$key])) { - return $this->BinaryData[$key]; + $total = 0; + foreach ($this->Pairings as $pairing) { + if (array_search($pairing->Result, Constants::PLAYED) !== false) { + $total++; + } } - return null; + + return $total; } /** - * Sets binary data that is read out the pairing file but is not needed immediately + * Sets the elo of elotype for the player * - * @param string $key - * @param bool|int|DateTime|string $value + * @param string $type + * @param int $value */ - public function __set(string $key, $value): void + public function setElo(string $type, int $value): void { - $this->BinaryData[$key] = $value; + $currentElos = $this->Elos; + $currentElos[ $type ] = $value; + $this->Elos = $currentElos; + } + + /** + * Sets the identifier of type for the player + * + * Common possible values are Fide or National + * + * @param string $type + * @param string $value + */ + public function setId(string $type, string $value): void + { + $currentIds = $this->Ids; + $currentIds[ $type ] = $value; + $this->Ids = $currentIds; } } diff --git a/src/Readers/Pairtwo6.php b/src/Readers/Pairtwo6.php index cce7ae1..9a94d2b 100644 --- a/src/Readers/Pairtwo6.php +++ b/src/Readers/Pairtwo6.php @@ -1,18 +1,20 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ - namespace JeroenED\Libpairtwo\Readers; +use DateTime; +use InvalidArgumentException; use JeroenED\Libpairtwo\Enums\Color; use JeroenED\Libpairtwo\Enums\Gender; use JeroenED\Libpairtwo\Enums\Result; @@ -25,25 +27,35 @@ use JeroenED\Libpairtwo\Pairing; use JeroenED\Libpairtwo\Player; use JeroenED\Libpairtwo\Round; use JeroenED\Libpairtwo\Tournament; -use DateTime; /** * Reader Pairtwo6 * * Reads out Pairtwo-6 files * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ class Pairtwo6 implements ReaderInterface { - const PT_DAYFACTOR = 32; - const PT_MONTHFACTOR = 16; - const PT_YEARFACTOR = 512; - const PT_PASTOFFSET = 117; - const CompatibleVersions = ['6.', '5.']; + public const COMPATIBLE_VERSIONS = ['6.', '5.']; + + public const PT_DAYFACTOR = 32; + + public const PT_MONTHFACTOR = 16; + + public const PT_PASTOFFSET = 117; + + public const PT_YEARFACTOR = 512; + + /** + * Binary data that was read out of the pairing file + * + * @var bool|DateTime|int|string[] + */ + private $BinaryData; /** * Version of Pairtwo this file was created with @@ -59,41 +71,91 @@ class Pairtwo6 implements ReaderInterface */ public $Tournament; - /** - * Binary data that was read out of the pairing file - * @var bool|DateTime|int|string[] - */ - private $BinaryData; - /** * Returns binary data that was read out the pairtwo file but was not needed immediately * * @param string $key + * * @return bool|DateTime|int|string|null */ public function __get(string $key) { - if (isset($this->BinaryData[$key])) { - return $this->BinaryData[$key]; + if (isset($this->BinaryData[ $key ])) { + return $this->BinaryData[ $key ]; } + return null; } /** * Sets binary data that is read out the pairtwo file but is not needed immediately * - * @param string $key + * @param string $key * @param bool|int|DateTime|string $value */ public function __set(string $key, $value): void { - $this->BinaryData[$key] = $value; + $this->BinaryData[ $key ] = $value; + } + + /** + * Adds the first tiebreak to the tournament + */ + private function addTiebreaks(): void + { + switch ($this->Tournament->System) { + case TournamentSystem::KEIZER: + $firstElement = new Tiebreak(Tiebreak::KEIZER); + break; + case TournamentSystem::AMERICAN: + case TournamentSystem::CLOSED: + case TournamentSystem::SWISS: + $firstElement = new Tiebreak(Tiebreak::POINTS); + break; + } + $tiebreaks = $this->Tournament->Tiebreaks; + array_unshift($tiebreaks, $firstElement); + $this->Tournament->Tiebreaks = $tiebreaks; + } + + /** + * Converts integer value to a date representation + * + * @param int $date + * + * @return bool|DateTime + */ + private function convertUIntToTimestamp(int $date) + { + $curyear = date('Y'); + $yearoffset = $curyear - self::PT_PASTOFFSET; + + // Day + $day = $date % self::PT_DAYFACTOR; + if ($day < 1) { + $day = 1; + } + + // Month + $month = ($date / self::PT_DAYFACTOR) % self::PT_MONTHFACTOR; + if ($month < 1) { + $month = 1; + } + + // Year + $year = ($date / self::PT_YEARFACTOR) + $yearoffset; + + $concat = $month . '/' . $day . '/' . intval($year); + $format = 'm/d/Y'; + + return DateTime::createFromFormat($format, $concat); } /** * Actually reads the Swar-File * * @param string $filename + * * @throws IncompatibleReaderException */ public function read(string $filename): void @@ -104,12 +166,11 @@ class Pairtwo6 implements ReaderInterface $offset = 0; - $length = 4; $this->Release = $this->readData('String', substr($swscontents, $offset, $length)); $offset += $length; - if (array_search(substr($this->Release, 0, 2), self::CompatibleVersions) === false) { + if (array_search(substr($this->Release, 0, 2), self::COMPATIBLE_VERSIONS) === false) { throw new IncompatibleReaderException("This file was not created with Pairtwo 5 or higher"); } @@ -261,53 +322,53 @@ class Pairtwo6 implements ReaderInterface $length = 4; switch ($this->readData('Int', substr($swscontents, $offset, $length))) { case 1: - $tiebreak = Tiebreak::Buchholz; + $tiebreak = Tiebreak::BUCHHOLZ; break; case 2: - $tiebreak = Tiebreak::BuchholzMed; + $tiebreak = Tiebreak::BUCHHOLZ_MED; break; case 3: - $tiebreak = Tiebreak::BuchholzCut; + $tiebreak = Tiebreak::BUCHHOLZ_CUT; break; case 4: - $tiebreak = Tiebreak::Sonneborn; + $tiebreak = Tiebreak::SONNEBORN; break; case 5: - $tiebreak = Tiebreak::Kashdan; + $tiebreak = Tiebreak::KASHDAN; break; case 6: - $tiebreak = Tiebreak::Cumulative; + $tiebreak = Tiebreak::CUMULATIVE; break; case 7: - $tiebreak = Tiebreak::Between; + $tiebreak = Tiebreak::BETWEEN; break; case 8: - $tiebreak = Tiebreak::Koya; + $tiebreak = Tiebreak::KOYA; break; case 9: - $tiebreak = Tiebreak::Baumbach; + $tiebreak = Tiebreak::BAUMBACH; break; case 10: - $tiebreak = Tiebreak::Performance; + $tiebreak = Tiebreak::PERFORMANCE; break; case 11: - $tiebreak = Tiebreak::Aro; + $tiebreak = Tiebreak::ARO; break; case 12: - $tiebreak = Tiebreak::AroCut; + $tiebreak = Tiebreak::AROCUT; break; case 13: - $tiebreak = Tiebreak::BlackPlayed; + $tiebreak = Tiebreak::BLACK_PLAYED; break; case 14: - $tiebreak = Tiebreak::Testmatch; + $tiebreak = Tiebreak::TESTMATCH; break; case 15: - $tiebreak = Tiebreak::Drawing; + $tiebreak = Tiebreak::DRAWING_OF_LOT; break; case 0: default: - $tiebreak = Tiebreak::None; + $tiebreak = Tiebreak::NONE; break; } $this->Tournament->addTieBreak(new Tiebreak($tiebreak)); @@ -315,9 +376,14 @@ class Pairtwo6 implements ReaderInterface } // Categorie - $length = 4 * 10; - $this->Categorie = $this->readData('Int', substr($swscontents, $offset, $length)); - $offset += $length; + for ($i = 0; $i < 10; $i++) { + $length = 4; + $category = $this->readData('Int', substr($swscontents, $offset, $length)); + if ($category != 0) { + $this->Tournament->addCategory('+' . $category); + } + $offset += $length; + } // ExtraPoints $length = 4 * 20; @@ -395,7 +461,8 @@ class Pairtwo6 implements ReaderInterface $offset += $length; $length = 1; - $player->Category = $this->readData('String', substr($swscontents, $offset, $length)); + $player->Category = + $this->Tournament->Categories[ $this->readData('Int', substr($swscontents, $offset, $length)) - 1 ]; $offset += $length; $length = 1; @@ -447,13 +514,13 @@ class Pairtwo6 implements ReaderInterface $length = 1; switch ($this->readData('Int', substr($swscontents, $offset, $length))) { case 1: - $gender = Gender::Male; + $gender = Gender::MALE; break; case 2: - $gender = Gender::Female; + $gender = Gender::FEMALE; break; default: - $gender = Gender::Neutral; + $gender = Gender::NEUTRAL; break; } $player->Gender = new Gender($gender); @@ -498,12 +565,12 @@ class Pairtwo6 implements ReaderInterface $this->Tournament->addPlayer($player); } // PlayerNames - $length = (Integer)$this->NewNamePos + 0; + $length = (int)$this->NewNamePos + 0; $this->PlayerNames = substr($swscontents, $offset, $length); $offset += $length; for ($i = 0; $i < $this->NewPlayer; $i++) { - $player = $this->Tournament->PlayerById($i); + $player = $this->Tournament->playerById($i); $namelength = $player->NameLength; $nameoffset = $player->NamePos; $player->Name = $this->readData("String", substr($this->PlayerNames, $nameoffset, $namelength)); @@ -585,17 +652,17 @@ class Pairtwo6 implements ReaderInterface $length = 4; switch ($this->readData('Int', substr($swscontents, $offset, $length))) { case 2: - $system = TournamentSystem::Closed; + $system = TournamentSystem::CLOSED; break; case 4: - $system = TournamentSystem::American; + $system = TournamentSystem::AMERICAN; break; case 6: - $system = TournamentSystem::Keizer; + $system = TournamentSystem::KEIZER; break; case 0: default: - $system = TournamentSystem::Swiss; + $system = TournamentSystem::SWISS; break; } $this->Tournament->System = new TournamentSystem($system); @@ -651,12 +718,12 @@ class Pairtwo6 implements ReaderInterface for ($x = 0; $x < $this->CreatedRounds; $x++) { $pairing = new Pairing(); - $pairing->Player = $this->Tournament->PlayerById($i); + $pairing->Player = $this->Tournament->playerById($i); $length = 4; $opponent = $this->readData('Int', substr($swscontents, $offset, $length)); if ($opponent != 4294967295) { - $pairing->Opponent = $this->Tournament->PlayerById($opponent); + $pairing->Opponent = $this->Tournament->playerById($opponent); } $offset += $length; @@ -665,15 +732,15 @@ class Pairtwo6 implements ReaderInterface switch ($this->readData('Int', substr($swscontents, $offset, $length))) { case 255: case 253: - $color = Color::Black; + $color = Color::BLACK; break; case 1: case 3: - $color = Color::White; - break; + $color = Color::WHITE; + break; case 0: default: - $color = Color::None; + $color = Color::NONE; break; } $pairing->Color = new Color($color); @@ -682,38 +749,38 @@ class Pairtwo6 implements ReaderInterface $length = 1; switch ($this->readData('Int', substr($swscontents, $offset, $length))) { case 1: - $result = Result::Lost; + $result = Result::LOST; break; case 2: - $result = Result::Absent; + $result = Result::ABSENT; break; case 3: - $result = Result::Adjourned; + $result = Result::ADJOURNED; break; case 4: - $result = Result::Bye; + $result = Result::BYE; break; case 6: - $result = Result::Draw; + $result = Result::DRAW; break; case 8: - $result = Result::DrawAdjourned; + $result = Result::DRAW_ADJOURNED; break; case 11: - $result = Result::Won; + $result = Result::WON; break; case 12: - $result = Result::WonForfait; + $result = Result::WON_FORFAIT; break; case 13: - $result = Result::WonAdjourned; + $result = Result::WON_ADJOURNED; break; case 14: - $result = Result::WonBye; + $result = Result::WON_BYE; break; case 0: default: - $result = Result::None; + $result = Result::NONE; break; } $pairing->Result = new Result($result); @@ -747,7 +814,8 @@ class Pairtwo6 implements ReaderInterface * * @param string $type * @param string $data - * @param mixed $default + * @param mixed $default + * * @return bool|DateTime|int|string */ private function readData(string $type, string $data, $default = null) @@ -758,8 +826,8 @@ class Pairtwo6 implements ReaderInterface if ($data == '') { return (is_null($default)) ? '' : $default; } + return iconv('windows-1252', 'utf-8', $data); - break; case 'Hex': case 'Int': case 'Bool': @@ -769,7 +837,7 @@ class Pairtwo6 implements ReaderInterface foreach ($hex as $key => $item) { if ($item == "00") { - $hex[$key] = ""; + $hex[ $key ] = ""; } else { break; } @@ -781,80 +849,28 @@ class Pairtwo6 implements ReaderInterface if ($hex == '00') { return (is_null($default)) ? '00' : $default; } + return $hex; } elseif ($type == 'Int') { if ($hex == '00') { return (is_null($default)) ? 0 : $default; } + return hexdec($hex); } elseif ($type == 'Date') { if ($hex == '00') { return (is_null($default)) ? $this->convertUIntToTimestamp(0) : $default; } + return $this->convertUIntToTimestamp(hexdec($hex)); } elseif ($type == 'Bool') { - return ($hex == "01") ? true : false; + return ($hex == "01"); } break; default: - throw new \InvalidArgumentException("Datatype not known"); - break; + throw new InvalidArgumentException("Datatype not known"); } return false; } - - /** - * Converts integer value to a date representation - * - * @param int $date - * @return bool|DateTime - */ - private function convertUIntToTimestamp(int $date) - { - $curyear = date('Y'); - $yearoffset = $curyear - self::PT_PASTOFFSET; - - // Day - $day = $date % self::PT_DAYFACTOR; - if ($day < 1) { - $day = 1; - } - - // Month - $month = ($date / self::PT_DAYFACTOR) % self::PT_MONTHFACTOR; - if ($month < 1) { - $month = 1; - } - - // Year - $year = ($date / self::PT_YEARFACTOR) + $yearoffset; - - $concat = $month . '/' . $day . '/' . intval($year); - $format = 'm/d/Y'; - - - return DateTime::createFromFormat($format, $concat); - } - - - /** - * Adds the first tiebreak to the tournament - */ - private function addTiebreaks(): void - { - switch ($this->Tournament->System) { - case TournamentSystem::Keizer: - $firstElement = new Tiebreak(Tiebreak::Keizer); - break; - case TournamentSystem::American: - case TournamentSystem::Closed: - case TournamentSystem::Swiss: - $firstElement = new Tiebreak(Tiebreak::Points); - break; - } - $tiebreaks = $this->Tournament->Tiebreaks; - array_unshift($tiebreaks, $firstElement); - $this->Tournament->Tiebreaks = $tiebreaks; - } } diff --git a/src/Readers/Swar4.php b/src/Readers/Swar4.php index c5436fa..21bf722 100644 --- a/src/Readers/Swar4.php +++ b/src/Readers/Swar4.php @@ -1,16 +1,20 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ + namespace JeroenED\Libpairtwo\Readers; +use DateTime; +use InvalidArgumentException; use JeroenED\Libpairtwo\Enums\Color; use JeroenED\Libpairtwo\Enums\Gender; use JeroenED\Libpairtwo\Enums\Result; @@ -23,45 +27,25 @@ use JeroenED\Libpairtwo\Pairing; use JeroenED\Libpairtwo\Player; use JeroenED\Libpairtwo\Round; use JeroenED\Libpairtwo\Tournament; -use DateTime; /** * Reader Swar4 * * Reads out Swar-4 files * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ class Swar4 implements ReaderInterface { /** - * Version of Pairtwo this file was created with - * - * @var string + * @var array */ - public $Release; + public const COMPATIBLE_VERSIONS = ['v4.']; - /** - * The tournament - * - * @var Tournament - */ - public $Tournament; - - /** - * Binary data that was read out of the pairing file - * - * @var bool|DateTime|int|string[] - */ - private $BinaryData; - - /** @var array */ - const CompatibleVersions = ['v4.']; - - const Tempos = [ + public const TEMPOS = [ [ '105 min/40 moves + 15 min. QPF', '120 min/40 moves + 15 min. with incr. 30" starting from 40th move', @@ -78,7 +62,8 @@ class Swar4 implements ReaderInterface '90 min with incr. 30"', '50 min with incr. 10"', 'other' - ],[ + ], + [ '10 min. with incr. 10"', '10 min. with incr. 15"', '10 min. with incr.5"', @@ -102,7 +87,8 @@ class Swar4 implements ReaderInterface '45 min. QPF', '8 min. with incr.4"', 'other' - ],[ + ], + [ '3 min. with incr. 2"', '3 min. with incr. 3"', '4 min. with incr. 2"', @@ -120,10 +106,97 @@ class Swar4 implements ReaderInterface ] ]; + /** + * Binary data that was read out of the pairing file + * + * @var bool|DateTime|int|string[] + */ + private $BinaryData; + + /** + * Version of Pairtwo this file was created with + * + * @var string + */ + public $Release; + + /** + * The tournament + * + * @var Tournament + */ + public $Tournament; + + /** + * Returns binary data that was read out the swar file but was not needed immediately + * + * @param string $key + * + * @return bool|DateTime|int|string|null + */ + public function __get(string $key) + { + if (isset($this->BinaryData[ $key ])) { + return $this->BinaryData[ $key ]; + } + + return null; + } + + /** + * Sets binary data that is read out the swar file but is not needed immediately + * + * @param string $key + * @param bool|int|DateTime|string $value + */ + public function __set(string $key, $value): void + { + $this->BinaryData[ $key ] = $value; + } + + /** + * Adds the first tiebreak to the tournament + */ + private function addTiebreaks(): void + { + switch ($this->Tournament->System) { + case TournamentSystem::AMERICAN: + case TournamentSystem::CLOSED: + case TournamentSystem::SWISS: + default: + $firstElement = new Tiebreak(Tiebreak::POINTS); + } + $tiebreaks = $this->Tournament->Tiebreaks; + array_unshift($tiebreaks, $firstElement); + $this->Tournament->Tiebreaks = $tiebreaks; + } + + /** + * Converts a swar-4 string to a \DateTime object + * + * @param string $string + * + * @return DateTime + */ + public function convertStringToDate(string $string): DateTime + { + if (strlen($string) == 10) { + return DateTime::createFromFormat('d/m/Y', $string); + } elseif (strlen($string) == 8) { + return DateTime::createFromFormat('Ymd', $string); + } else { + $default = new DateTime(); + $default->setTimestamp(0); + + return $default; + } + } + /** * Actually reads the Swar-File * * @param string $filename + * * @throws IncompatibleReaderException */ public function read(string $filename): void @@ -131,7 +204,7 @@ class Swar4 implements ReaderInterface $swshandle = fopen($filename, 'rb'); $this->Release = $this->readData('String', $swshandle); - if (array_search(substr($this->Release, 0, 3), self::CompatibleVersions) === false) { + if (array_search(substr($this->Release, 0, 3), self::COMPATIBLE_VERSIONS) === false) { throw new IncompatibleReaderException("This file was not created with Swar 4"); } @@ -181,16 +254,16 @@ class Swar4 implements ReaderInterface case 3: case 4: default: - $system = TournamentSystem::Swiss; + $system = TournamentSystem::SWISS; break; case 5: case 6: case 7: - $system = TournamentSystem::Closed; + $system = TournamentSystem::CLOSED; break; case 8: case 9: - $system = TournamentSystem::American; + $system = TournamentSystem::AMERICAN; break; } $this->Tournament->System = new TournamentSystem($system); @@ -244,7 +317,7 @@ class Swar4 implements ReaderInterface // [DATES] $this->readData('String', $swshandle); - $this->Tournament->Tempo = Self::Tempos[$this->Tournament->TournoiStd][$this->Tournament->TempoIndex]; + $this->Tournament->Tempo = self::TEMPOS[ $this->Tournament->TournoiStd ][ $this->Tournament->TempoIndex ]; for ($i = 0; $i < $this->Tournament->NoOfRounds; $i++) { $round = new Round(); @@ -261,52 +334,52 @@ class Swar4 implements ReaderInterface switch ($this->readData('Int', $swshandle)) { case 0: default: - $tiebreak = Tiebreak::None; + $tiebreak = Tiebreak::NONE; break; case 1: - $tiebreak = Tiebreak::Buchholz; + $tiebreak = Tiebreak::BUCHHOLZ; break; case 2: - $tiebreak = Tiebreak::BuchholzMed; + $tiebreak = Tiebreak::BUCHHOLZ_MED; break; case 3: - $tiebreak = Tiebreak::BuchholzMed2; + $tiebreak = Tiebreak::BUCHHOLZ_MED_2; break; case 4: - $tiebreak = Tiebreak::BuchholzCut; + $tiebreak = Tiebreak::BUCHHOLZ_CUT; break; case 5: - $tiebreak = Tiebreak::BuchholzCut2; + $tiebreak = Tiebreak::BUCHHOLZ_CUT_2; break; case 6: - $tiebreak = Tiebreak::Sonneborn; + $tiebreak = Tiebreak::SONNEBORN; break; case 7: - $tiebreak = Tiebreak::Cumulative; + $tiebreak = Tiebreak::CUMULATIVE; break; case 8: - $tiebreak = Tiebreak::Between; + $tiebreak = Tiebreak::BETWEEN; break; case 9: - $tiebreak = Tiebreak::Koya; + $tiebreak = Tiebreak::KOYA; break; case 10: - $tiebreak = Tiebreak::Baumbach; + $tiebreak = Tiebreak::BAUMBACH; break; case 11: - $tiebreak = Tiebreak::AveragePerformance; + $tiebreak = Tiebreak::AVERAGE_PERFORMANCE; break; case 12: - $tiebreak = Tiebreak::Aro; + $tiebreak = Tiebreak::ARO; break; case 13: - $tiebreak = Tiebreak::AroCut; + $tiebreak = Tiebreak::AROCUT; break; case 14: - $tiebreak = Tiebreak::BlackPlayed; + $tiebreak = Tiebreak::BLACK_PLAYED; break; case 15: - $tiebreak = Tiebreak::BlackWin; + $tiebreak = Tiebreak::BLACK_WIN; break; } $tiebreaks[] = new Tiebreak($tiebreak); @@ -323,19 +396,19 @@ class Swar4 implements ReaderInterface $this->Tournament->Catogory_type = $this->readData('Int', $swshandle); for ($i = 0; $i <= 12; $i++) { - $category[$i]['Cat1'] =$this->readData('String', $swshandle); + $category[ $i ][ 'Cat1' ] = $this->readData('String', $swshandle); } for ($i = 0; $i <= 12; $i++) { - $category[$i]['Cat2'] =$this->readData('String', $swshandle); + $category[ $i ][ 'Cat2' ] = $this->readData('String', $swshandle); } $this->Tournament->Category = $category; // [XTRA_POINTS] $this->readData('String', $swshandle); for ($i = 0; $i < 4; $i++) { - $extrapoints[$i]['pts'] = $this->readData('Int', $swshandle); - $extrapoints[$i]['elo'] = $this->readData('Int', $swshandle); + $extrapoints[ $i ][ 'pts' ] = $this->readData('Int', $swshandle); + $extrapoints[ $i ][ 'elo' ] = $this->readData('Int', $swshandle); } $this->Tournament->Extrapoints = $extrapoints; @@ -351,19 +424,19 @@ class Swar4 implements ReaderInterface $player = new Player(); $player->Classement = $this->readData('Int', $swshandle); $player->Name = $this->readData('String', $swshandle); - $inscriptionNos[$this->readData('Int', $swshandle)] = $i; + $inscriptionNos[ $this->readData('Int', $swshandle) ] = $i; $player->Rank = $this->readData('Int', $swshandle); $player->CatIndex = $this->readData('Int', $swshandle); $player->DateOfBirth = $this->readData('Date', $swshandle); switch ($this->readData('Int', $swshandle)) { case 1: - $gender = Gender::Male; + $gender = Gender::MALE; break; case 2: - $gender = Gender::Female; + $gender = Gender::FEMALE; break; default: - $gender = Gender::Neutral; + $gender = Gender::NEUTRAL; break; } $player->Gender = new Gender($gender); @@ -415,10 +488,10 @@ class Swar4 implements ReaderInterface $player->setId('Club', $this->readData('Int', $swshandle)); $player->ClubName = $this->readData('String', $swshandle); $player->NoOfMatchesNoBye = $this->readData('Int', $swshandle); - $player->Points = $this->readData('Int', $swshandle); // To Calculate by libpairtwo + $player->Points = $this->readData('Int', $swshandle); // To Calculate by libpairtwo $player->AmericanPoints = $this->readData('Int', $swshandle); // To Calculate by libpairtwo for ($t = 0; $t < 5; $t++) { - $tiebreaks[$t] = $this->readData('Int', $swshandle); // To Calculate by libpairtwo + $tiebreaks[ $t ] = $this->readData('Int', $swshandle); // To Calculate by libpairtwo } $player->Tiebreak = $tiebreaks; $player->Performance = $this->readData('Int', $swshandle); // To Calculate by libpairtwo @@ -432,14 +505,14 @@ class Swar4 implements ReaderInterface if ($player->AllocatedRounds != 0) { for ($j = 0; $j < $player->AllocatedRounds; $j++) { - $pairing[$pt]['player'] = $i; - $pairing[$pt]['round'] = $this->readData('Int', $swshandle) - 1; - $pairing[$pt]['table'] = $this->readData('Int', $swshandle) - 1; - $pairing[$pt]['opponent'] = $this->readData('Int', $swshandle); - $pairing[$pt]['result'] = $this->readData('Hex', $swshandle); - $pairing[$pt]['color'] = $this->readData('Int', $swshandle); - $pairing[$pt]['float'] = $this->readData('Int', $swshandle); - $pairing[$pt]['extrapoints'] = $this->readData('Int', $swshandle); + $pairing[ $pt ][ 'player' ] = $i; + $pairing[ $pt ][ 'round' ] = $this->readData('Int', $swshandle) - 1; + $pairing[ $pt ][ 'table' ] = $this->readData('Int', $swshandle) - 1; + $pairing[ $pt ][ 'opponent' ] = $this->readData('Int', $swshandle); + $pairing[ $pt ][ 'result' ] = $this->readData('Hex', $swshandle); + $pairing[ $pt ][ 'color' ] = $this->readData('Int', $swshandle); + $pairing[ $pt ][ 'float' ] = $this->readData('Int', $swshandle); + $pairing[ $pt ][ 'extrapoints' ] = $this->readData('Int', $swshandle); $pt++; } @@ -450,61 +523,62 @@ class Swar4 implements ReaderInterface } $ptn = 0; - while (isset($this->Tournament->Pairing[$ptn]['round'])) { + while (isset($this->Tournament->Pairing[ $ptn ][ 'round' ])) { $pairing = new Pairing(); - $pairing->Player = $this->Tournament->PlayerById($this->Tournament->Pairing[$ptn]['player']); - $pairing->Round = $this->Tournament->Pairing[$ptn]['round']; - if ($this->Tournament->Pairing[$ptn]['opponent'] != 4294967295) { - $pairing->Opponent = $this->Tournament->PlayerById($inscriptionNos[$this->Tournament->Pairing[$ptn]['opponent']]); + $pairing->Player = $this->Tournament->playerById($this->Tournament->Pairing[ $ptn ][ 'player' ]); + $pairing->Round = $this->Tournament->Pairing[ $ptn ][ 'round' ]; + if ($this->Tournament->Pairing[ $ptn ][ 'opponent' ] != 4294967295) { + $pairing->Opponent = + $this->Tournament->playerById($inscriptionNos[ $this->Tournament->Pairing[ $ptn ][ 'opponent' ] ]); } - switch ($this->Tournament->Pairing[$ptn]['result']) { + switch ($this->Tournament->Pairing[ $ptn ][ 'result' ]) { case '1000': - $result = Result::Lost; + $result = Result::LOST; break; case '01': - $result = Result::Absent; + $result = Result::ABSENT; break; case '0010': - $result = Result::Bye; + $result = Result::BYE; break; case '2000': - $result = Result::Draw; + $result = Result::DRAW; break; case '4000': - $result = Result::Won; + $result = Result::WON; break; case '04': - $result = Result::WonForfait; + $result = Result::WON_FORFAIT; break; case '40': - $result = Result::WonBye; + $result = Result::WON_BYE; break; case '00': default: - $result = Result::None; + $result = Result::NONE; break; } - if (array_search($this->Tournament->Pairing[$ptn]['table'], [ 16383, 8191 ]) !== false) { - $result = Result::Absent; + if (array_search($this->Tournament->Pairing[ $ptn ][ 'table' ], [16383, 8191]) !== false) { + $result = Result::ABSENT; } $pairing->Result = new Result($result); - switch ($this->Tournament->Pairing[$ptn]['color']) { + switch ($this->Tournament->Pairing[ $ptn ][ 'color' ]) { case 4294967295: - $color = Color::Black; + $color = Color::BLACK; break; case 1: - $color = Color::White; + $color = Color::WHITE; break; case 0: default: - $color = Color::None; + $color = Color::NONE; break; } $pairing->Color = new Color($color); - $pairing->Board = $this->Tournament->Pairing[$ptn]['table']; + $pairing->Board = $this->Tournament->Pairing[ $ptn ][ 'table' ]; $ptn++; $this->Tournament->addPairing($pairing); } @@ -524,8 +598,9 @@ class Swar4 implements ReaderInterface * * Date (Date representation of $data. Default: 1902/01/01) * * @param string $type - * @param $handle - * @param null $default + * @param $handle + * @param null $default + * * @return array|bool|DateTime|false|float|int|string|null */ private function readData(string $type, $handle, $default = null) @@ -542,11 +617,13 @@ class Swar4 implements ReaderInterface if ($data == '') { return (is_null($default)) ? '' : $default; } + return iconv('windows-1252', 'utf-8', $data); } elseif ($type == 'Date') { if ($data == '') { return (is_null($default)) ? $this->convertStringToDate('01/01/1900') : $default; } + return $this->convertStringToDate($data); } break; @@ -559,7 +636,7 @@ class Swar4 implements ReaderInterface foreach ($hex as $key => $item) { if ($item == "00") { - $hex[$key] = ""; + $hex[ $key ] = ""; } else { break; } @@ -571,77 +648,22 @@ class Swar4 implements ReaderInterface if ($hex == '00') { return (is_null($default)) ? '00' : $default; } + return $hex; } elseif ($type == 'Int') { if ($hex == '00') { return (is_null($default)) ? 0 : $default; } + return hexdec($hex); } elseif ($type == 'Bool') { - return ($hex == "01") ? true : false; + return ($hex == "01"); } break; default: - throw new \InvalidArgumentException("Datatype not known"); - break; + throw new InvalidArgumentException("Datatype not known"); } return false; } - - /** - * Returns binary data that was read out the swar file but was not needed immediately - * - * @param string $key - * @return bool|DateTime|int|string|null - */ - public function __get(string $key) - { - if (isset($this->BinaryData[$key])) { - return $this->BinaryData[$key]; - } - return null; - } - - /** - * Sets binary data that is read out the swar file but is not needed immediately - * - * @param string $key - * @param bool|int|DateTime|string $value - */ - public function __set(string $key, $value): void - { - $this->BinaryData[$key] = $value; - } - - /** - * Converts a swar-4 string to a \DateTime object - * @param string $string - * @return DateTime - */ - public function convertStringToDate(string $string): DateTime - { - if (strlen($string) == 10) { - return DateTime::createFromFormat('d/m/Y', $string); - } elseif (strlen($string) == 8) { - return DateTime::createFromFormat('Ymd', $string); - } - } - - /** - * Adds the first tiebreak to the tournament - */ - private function addTiebreaks(): void - { - switch ($this->Tournament->System) { - case TournamentSystem::American: - case TournamentSystem::Closed: - case TournamentSystem::Swiss: - default: - $firstElement = new Tiebreak(Tiebreak::Points); - } - $tiebreaks = $this->Tournament->Tiebreaks; - array_unshift($tiebreaks, $firstElement); - $this->Tournament->Tiebreaks = $tiebreaks; - } } diff --git a/src/Round.php b/src/Round.php index 910218e..ae62551 100644 --- a/src/Round.php +++ b/src/Round.php @@ -1,13 +1,14 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo; @@ -20,10 +21,10 @@ use JeroenED\Libpairtwo\Enums\Result; * * Class for a round of the tournament * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ class Round { @@ -41,6 +42,13 @@ class Round */ public $Games = []; + /** + * Array of all pairings for this round + * + * @var Pairing[] + */ + public $Pairings = []; + /** * Number of the round * @@ -49,11 +57,44 @@ class Round public $RoundNo; /** - * Array of all pairings for this round + * Magic method to read out several fields. If field was not found it is being searched in the binary data fields * - * @var Pairing[] + * @param string $key + * + * @return bool|DateTime|int|string|null */ - public $Pairings = []; + public function __get(string $key) + { + if ($key == 'Bye') { + return $this->bye(); + } elseif ($key == 'Absent') { + return $this->absent(); + } elseif ($key == 'GamesByBoard') { + return $this->gamesByBoard(); + } elseif (isset($this->BinaryData[ $key ])) { + return $this->BinaryData[ $key ]; + } + + return null; + } + + /** + * Returns an array of pairings where the player is absent + * + * @return Pairing[] + */ + private function absent(): array + { + $allPairings = $this->Pairings; + $absentPairings = []; + foreach ($allPairings as $pairing) { + if ($pairing->Result == Result::ABSENT) { + $absentPairings[] = $pairing; + } + } + + return $absentPairings; + } /** * Adds a game to the round @@ -89,28 +130,12 @@ class Round $allPairings = $this->Pairings; $byePairings = []; foreach ($allPairings as $pairing) { - if ($pairing->Result == Result::WonBye) { + if ($pairing->Result == Result::WON_BYE) { $byePairings[] = $pairing; } } - return $byePairings; - } - /** - * Returns an array of pairings where the player is absent - * - * @return Pairing[] - */ - private function absent(): array - { - $allPairings = $this->Pairings; - $absentPairings = []; - foreach ($allPairings as $pairing) { - if ($pairing->Result == Result::Absent) { - $absentPairings[] = $pairing; - } - } - return $absentPairings; + return $byePairings; } /** @@ -121,7 +146,8 @@ class Round private function gamesByBoard(): array { $allGames = $this->Games; - usort($allGames, array($this, 'sortByBoard')); + usort($allGames, [$this, 'sortByBoard']); + return $allGames; } @@ -130,6 +156,7 @@ class Round * * @param Game $a * @param Game $b + * * @return int */ private function sortByBoard(Game $a, Game $b): int @@ -137,26 +164,7 @@ class Round if (($a->Board == $b->Board) || ($a->Board === false) || ($b->Board === false)) { return 0; } + return ($a->Board > $b->Board) ? +1 : -1; } - - /** - * Magic method to read out several fields. If field was not found it is being searched in the binary data fields - * - * @param string $key - * @return bool|DateTime|int|string|null - */ - public function __get(string $key) - { - if ($key == 'Bye') { - return $this->bye(); - } elseif ($key == 'Absent') { - return $this->absent(); - } elseif ($key == 'GamesByBoard') { - return $this->gamesByBoard(); - } elseif (isset($this->BinaryData[$key])) { - return $this->BinaryData[$key]; - } - return null; - } } diff --git a/src/Tournament.php b/src/Tournament.php index 5997eb3..f511be1 100644 --- a/src/Tournament.php +++ b/src/Tournament.php @@ -1,88 +1,54 @@ - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ namespace JeroenED\Libpairtwo; +use Closure; +use DateTime; use JeroenED\Libpairtwo\Enums\Color; use JeroenED\Libpairtwo\Enums\Tiebreak; use JeroenED\Libpairtwo\Enums\TournamentSystem; -use Closure; -use DateTime; /** * Class Tournament * * Class for the tournament from the pairing file * - * @author Jeroen De Meerleer - * @category Main - * @package Libpairtwo - * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer + * @author Jeroen De Meerleer + * @category Main + * @package Libpairtwo + * @copyright Copyright (c) 2018-2019 Jeroen De Meerleer */ class Tournament { /** - * Name of the Tournament + * An Array of the assigned arbiters * - * @var string + * @var string[] */ - public $Name; + public $Arbiters; /** - * Organiser of the tournament (eg. Donald J. Trump) + * Binary data that was read out of the pairing file * - * - * @var string + * @var bool|DateTime|int|string[] */ - public $Organiser; + private $BinaryData = []; /** - * Club ID of the tournament organiser (eg. 205001600) + * Round objects of all rounds in the tournament * - * @var string + * @var $Categories [] */ - public $OrganiserClubNo; - - /** - * Club name of the tournament organiser (eg. The White House Chess Club) - * - * @var string - */ - public $OrganiserClub; - - /** - * Place where the Tounament is held (eg. The Oval Office) - * - * @var string - */ - public $OrganiserPlace; - - /** - * The country where the tournament is held - * - * @var string - */ - public $OrganiserCountry; - - /** - * Whether or not the tournament is an official tournament and send to the world chess organisation - * - * @var int - */ - public $FideHomol; - - /** - * Start date (First round or Players meeting) of the tournament - * @var DateTime - */ - public $StartDate; + public $Categories = []; /** * End date (Last round or Award Ceremony) of the tournament @@ -92,45 +58,18 @@ class Tournament public $EndDate; /** - * An Array of the assigned arbiters + * The federation for which this tournament is held * - * @var string[] - */ - public $Arbiters; - - /** - * Number of round the tournament has - * - * @var int - */ - public $NoOfRounds; - - /** - * Round objects of all rounds in the tournament - * - * @var Round[] - */ - public $Rounds = []; - - /** - * The tempo of the tournament (eg. 90 min/40 moves + 30 sec. increment starting from move 1) * @var string */ - public $Tempo; + public $Federation; /** - * The elo a player gets if he does not have an official elo + * Whether or not the tournament is an official tournament and send to the world chess organisation * * @var int */ - public $NonRatedElo; - - /** - * The system the tournament (eg. Swiss, Closed, American) - * - * @var TournamentSystem - */ - public $System; + public $FideHomol; /** * The tempo for the first period of a game in the tournament @@ -140,32 +79,60 @@ class Tournament public $FirstPeriod; /** - * The tempo for the second period of a game in the tournament + * Name of the Tournament * * @var string */ - public $SecondPeriod; + public $Name; /** - * The federation for which this tournament is held - * - * @var string - */ - public $Federation; - - /** - * All players of the tournament - * - * @var Player[] - */ - public $Players = []; - - /** - * The year or season the tournament is held or is count for + * Number of round the tournament has * * @var int */ - public $Year; + public $NoOfRounds; + + /** + * The elo a player gets if he does not have an official elo + * + * @var int + */ + public $NonRatedElo; + + /** + * Organiser of the tournament (eg. Donald J. Trump) + * + * @var string + */ + public $Organiser; + + /** + * Club name of the tournament organiser (eg. The White House Chess Club) + * + * @var string + */ + public $OrganiserClub; + + /** + * Club ID of the tournament organiser (eg. 205001600) + * + * @var string + */ + public $OrganiserClubNo; + + /** + * The country where the tournament is held + * + * @var string + */ + public $OrganiserCountry; + + /** + * Place where the Tounament is held (eg. The Oval Office) + * + * @var string + */ + public $OrganiserPlace; /** * All pairings of the tournament @@ -175,11 +142,11 @@ class Tournament public $Pairings = []; /** - * The tiebreaks for the tournament + * All players of the tournament * - * @var Tiebreak[] + * @var Player[] */ - public $Tiebreaks = []; + public $Players = []; /** * The elo that priority above all others @@ -196,85 +163,149 @@ class Tournament public $PriorityId = 'Nation'; /** - * Binary data that was read out of the pairing file + * Round objects of all rounds in the tournament * - * @var bool|DateTime|int|string[] + * @var Round[] */ - private $BinaryData = []; + public $Rounds = []; /** - * Gets a player by its ID + * The tempo for the second period of a game in the tournament * - * @param integer $id - * @return Player + * @var string */ - public function PlayerById(int $id): Player + public $SecondPeriod; + + /** + * Start date (First round or Players meeting) of the tournament + * + * @var DateTime + */ + public $StartDate; + + /** + * The system the tournament (eg. Swiss, Closed, American) + * + * @var TournamentSystem + */ + public $System; + + /** + * The tempo of the tournament (eg. 90 min/40 moves + 30 sec. increment starting from move 1) + * + * @var string + */ + public $Tempo; + + /** + * The tiebreaks for the tournament + * + * @var Tiebreak[] + */ + public $Tiebreaks = []; + + /** + * The year or season the tournament is held or is count for + * + * @var int + */ + public $Year; + + /** + * Get the ranking for a specific category + * + * @param string $category + * + * @return array + */ + public function RankingForCategory(string $category): array { - return $this->Players[$id]; + $globalRanking = $this->ranking(); + + $return = []; + foreach ($globalRanking as $player) { + if ($player->Category == $category) { + $return[] = $player; + } + } + + return $return; } /** - * Adds a player + * Magic method to read out several fields. If field was not found it is being searched in the binary data fields * - * @param Player $Player + * @param string $key * + * @return bool|DateTime|int|string|null */ - public function addPlayer(Player $Player): void + public function __get(string $key) { - $newArray = $this->Players; - $newArray[] = $Player; - $this->Players = $newArray; + if ($key == 'Participants') { + return $this->participants(); + } elseif ($key == 'AverageElo') { + return $this->averageElo(); + } elseif ($key == 'Ranking') { + return $this->ranking(); + } elseif (isset($this->BinaryData[ $key ])) { + return $this->BinaryData[ $key ]; + } + + return null; } /** - * Updates player on id to the given Player object + * Sets binary data that is read out the pairing file but is not needed immediately * - * @param int $id - * @param Player $player + * @param string $key + * @param bool|int|DateTime|string $value * + * @return void */ - public function updatePlayer(int $id, Player $player): void + public function __set(string $key, $value): void { - $newArray = $this->Players; - $newArray[$id] = $player; - $this->Players = $newArray; + $this->BinaryData[ $key ] = $value; } /** - * Adds a Tiebreak - * - * @param Tiebreak $tiebreak + * Adds an arbiter to the tournament * + * @param string $Arbiter */ - public function addTiebreak(Tiebreak $tiebreak): void + public function addArbiter(string $Arbiter): void { - $newArray = $this->Tiebreaks; - $newArray[] = $tiebreak; - $this->Tiebreaks = $newArray; + $newArray = $this->Arbiters; + $newArray[] = $Arbiter; + $this->Arbiters = $newArray; } /** - * Adds a round with given Round object - * - * @param Round $round + * Adds a category with given name object * + * @param string $name */ - public function addRound(Round $round): void + public function addCategory(string $name): void { - $newArray = $this->Rounds; - $newArray[$round->RoundNo] = $round; - $this->Rounds = $newArray; + $newArray = $this->Categories; + $newArray[] = $name; + $this->Categories = $newArray; } /** - * Gets a round by its number. + * Adds a game to the tournament * - * @param int $roundNo - * @return Round + * @param Game $game + * @param int $round */ - public function RoundByNo(int $roundNo): Round + public function addGame(Game $game, int $round): void { - return $this->Rounds[$roundNo]; + if (!isset($this->Rounds[ $round ])) { + $roundObj = new Round(); + $roundObj->RoundNo = $round; + $this->addRound($roundObj); + } + + $this->roundByNo($round)->addGame($game); } /** @@ -290,282 +321,40 @@ class Tournament $this->Pairings = $newArray; } - /** - * Adds an arbiter to the tournament - * - * @param string $Arbiter + * Adds a player * + * @param Player $Player */ - public function addArbiter(string $Arbiter): void + public function addPlayer(Player $Player): void { - $newArray = $this->Arbiters; - $newArray[] = $Arbiter; - $this->Arbiters = $newArray; + $newArray = $this->Players; + $newArray[] = $Player; + $this->Players = $newArray; } /** - * Converts pairings into games with a black and white player - * + * Adds a round with given Round object * + * @param Round $round */ - public function pairingsToRounds(): void + public function addRound(Round $round): void { - /** @var Pairing[] $pairings */ - $pairings = $this->Pairings; - - /** @var Pairing[] */ - $cache = array(); - - /** @var int[] */ - $lastboards; - - /** @var Pairing $pairing */ - foreach ($pairings as $pairing) { - // Add pairing to player - $pairing->Player->addPairing($pairing); - $round = $pairing->Round; - $color = $pairing->Color; - - $this->RoundByNo($round)->addPairing($pairing); - $opponent = null; - - /** - * @var int $key - * @var Pairing $cached - */ - foreach ($cache as $key=>$cached) { - if (!is_null($cached)) { - if (($cached->Opponent == $pairing->Player) && ($cached->Round == $pairing->Round)) { - $opponent = $cached; - $cache[$key] = null; - break; - } - } - } - $game = new Game(); - if ($color->getValue() == Color::White) { - $game->White = $pairing; - $game->Black = $opponent; - } elseif ($color->getValue() == Color::Black) { - $game->White = $opponent; - $game->Black = $pairing; - } - - if (is_null($game->White) || is_null($game->Black)) { - $cache[] = $pairing; - } else { - // Check if game already exists - if (!$this->gameExists($game, $round)) { - $game->Board = $game->White->Board; - // Add board if inexistent - if ($game->Board == -1) { - if (isset($lastboards[$round])) { - $lastboards[$round] += 1; - } else { - $lastboards[$round] = 0; - } - $game->Board = $lastboards[$round]; - $game->White->Board = $lastboards[$round]; - $game->Black->Board = $lastboards[$round]; - } - $this->AddGame($game, $round); - } - } - } + $newArray = $this->Rounds; + $newArray[ $round->RoundNo ] = $round; + $this->Rounds = $newArray; } /** - * Checks if a game already is already registered - * - * @param Game $game - * @param int $round - * @return bool - */ - public function gameExists(Game $game, int $round = -1): bool - { - $search = [ $round ]; - if ($round == -1) { - $search = []; - for ($i = 0; $i < $this->NoOfRounds; $i++) { - $search[] = $i; - } - } - - foreach ($search as $round) { - if (!isset($this->Rounds[$round])) { - return false; - } - $games = $this->Rounds[$round]->Games; - if (is_null($games)) { - return false; - } - foreach ($games as $roundgame) { - if ($game->equals($roundgame)) { - return true; - } - } - } - - return false; - } - - /** - * Adds a game to the tournament - * - * @param Game $game - * @param int $round - * - */ - public function addGame(Game $game, int $round): void - { - if (!isset($this->Rounds[$round])) { - $roundObj = new Round(); - $roundObj->RoundNo = $round; - $this->addRound($roundObj); - } - - $this->RoundByNo($round)->addGame($game); - } - - /** - * Gets the ranking of the tournament - * - * @return Player[] - */ - private function ranking(): array - { - $players = $this->Players; - foreach ($this->Tiebreaks as $tbkey=>$tiebreak) { - foreach ($players as $pkey => $player) { - $break = $this->calculateTiebreak($tiebreak, $player, $tbkey); - $tiebreaks = $player->Tiebreaks; - $tiebreaks[$tbkey] = $break; - $player->Tiebreaks = $tiebreaks; - $this->updatePlayer($pkey, $player); - } - } - $sortedplayers[0] = $players; - foreach ($this->Tiebreaks as $tbkey=>$tiebreak) { - $newgroupkey = 0; - $tosortplayers = $sortedplayers; - $sortedplayers = []; - foreach ($tosortplayers as $groupkey=>$sortedplayerselem) { - usort($tosortplayers[$groupkey], $this->sortTiebreak($tbkey)); - foreach ($tosortplayers[$groupkey] as $playerkey => $player) { - if (!is_null($player->Tiebreaks[$tbkey])) { - if ($playerkey != 0) { - $newgroupkey++; - if ($player->Tiebreaks[$tbkey] == $tosortplayers[$groupkey][$playerkey - 1]->Tiebreaks[$tbkey]) { - $newgroupkey--; - } - } - } - $sortedplayers[$newgroupkey][] = $player; - } - $newgroupkey++; - } - } - $finalarray = []; - foreach ($sortedplayers as $sort1) { - foreach ($sort1 as $player) { - $finalarray[] = $player; - } - } - return $finalarray; - } - - /** - * Sort by tiebreak - * - * @param int $key - * @return Closure - */ - private function sortTiebreak(int $key): Closure - { - return function (Player $a, Player $b) use ($key) { - if (($b->Tiebreaks[$key] == $a->Tiebreaks[$key]) || ($a->Tiebreaks[$key] === false) || ($b->Tiebreaks[$key] === false)) { - return 0; - } - return ($b->Tiebreaks[$key] > $a->Tiebreaks[$key]) ? +1 : -1; - }; - } - - - /** - * Calculates a specific tiebreak for $player + * Adds a Tiebreak * * @param Tiebreak $tiebreak - * @param Player $player - * @param int $tbkey - * @return float */ - private function calculateTiebreak(Tiebreak $tiebreak, Player $player, int $tbkey = 0): float + public function addTiebreak(Tiebreak $tiebreak): void { - switch ($tiebreak) { - case Tiebreak::Keizer: - return $this->calculateKeizer($player); - break; - case Tiebreak::Points: - return $this->calculatePoints($player); - break; - case Tiebreak::Baumbach: - return $this->calculateBaumbach($player); - break; - case Tiebreak::BlackPlayed: - return $this->calculateBlackPlayed($player); - break; - case Tiebreak::BlackWin: - return $this->calculateBlackWin($player); - break; - case Tiebreak::Between: - return $this->calculateMutualResult($player, $this->Players, $tbkey); - break; - case Tiebreak::Aro: - return $this->calculateAverageRating($player, $this->PriorityElo); - break; - case Tiebreak::AroCut: - return $this->calculateAverageRating($player, $this->PriorityElo, 1); - break; - case Tiebreak::Koya: - return $this->calculateKoya($player); - break; - case Tiebreak::Buchholz: - return $this->calculateBuchholz($player); - break; - case Tiebreak::BuchholzCut: - return $this->calculateBuchholz($player, 1); - break; - case Tiebreak::BuchholzMed: - return $this->calculateBuchholz($player, 1, 1); - break; - case Tiebreak::BuchholzCut2: - return $this->calculateBuchholz($player, 2); - break; - case Tiebreak::BuchholzMed2: - return $this->calculateBuchholz($player, 2, 2); - break; - case Tiebreak::Sonneborn: - return $this->calculateSonneborn($player); - break; - case Tiebreak::Kashdan: - return $this->calculateKashdan($player, ["Won" => 4, "Draw" => 2, "Lost" => 1, "NotPlayed" => 0]); - break; - case Tiebreak::SoccerKashdan: - return $this->calculateKashdan($player, ["Won" => 3, "Draw" => 1, "Lost" => 0, "NotPlayed" => -1]); - break; - case Tiebreak::Cumulative: - return $this->calculateCumulative($player); - break; - case Tiebreak::AveragePerformance: - return $this->calculateAveragePerformance($player, $this->PriorityElo); - break; - case Tiebreak::Performance: - return $player->Performance($this->PriorityElo, $this->NonRatedElo); - break; - default: - return 0; - } + $newArray = $this->Tiebreaks; + $newArray[] = $tiebreak; + $this->Tiebreaks = $newArray; } /** @@ -586,23 +375,238 @@ class Tournament $totalrating += $toadd; $players++; } + return intdiv($totalrating, $players); } /** - * Returns the number of participants + * The average performance of the opponents * - * @return int + * @param Player $player + * @param string $type + * @param int $cut + * + * @return float */ - private function participants(): int + private function calculateAveragePerformance(Player $player, string $type, int $cut = 0): float { - return count($this->Players); + $pairings = $player->Pairings; + $allratings = []; + foreach ($pairings as $pairing) { + if (array_search($pairing->Result, Constants::NOTPLAYED) === false) { + $toadd = $pairing->Opponent->Performance($type, $this->NonRatedElo); + if ($toadd != 0) { + $allratings[] = $toadd; + } + } + } + sort($allratings); + $allratings = array_slice($allratings, $cut); + + return round(array_sum($allratings) / count($allratings)); + } + + /** + * The average rating of the opponents + * + * @param Player $player + * @param string $type + * @param int $cut + * + * @return float + */ + private function calculateAverageRating(Player $player, string $type, int $cut = 0): float + { + $pairings = $player->Pairings; + $allratings = []; + foreach ($pairings as $pairing) { + if (array_search($pairing->Result, Constants::NOTPLAYED) === false) { + $toadd = $pairing->Opponent->getElo($type); + if ($toadd != 0) { + $allratings[] = $toadd; + } + } + } + sort($allratings); + $allratings = array_slice($allratings, $cut); + $tiebreak = 0; + if (count($allratings) > 0) { + $tiebreak = round(array_sum($allratings) / count($allratings)); + } + + return $tiebreak; + } + + /** + * Number of won games + * + * @param Player $player + * + * @return float + */ + private function calculateBaumbach(Player $player): float + { + $totalwins = 0; + foreach ($player->Pairings as $pairing) { + if (array_search($pairing->Result, Constants::NOTPLAYED) === false) { + if (array_search($pairing->Result, Constants::WON) !== false) { + $totalwins++; + } + } + } + + return $totalwins; + } + + /** + * Number of played games with black + * + * @param Player $player + * + * @return float + */ + private function calculateBlackPlayed(Player $player): float + { + $totalwins = 0; + foreach ($player->Pairings as $pairing) { + if (array_search($pairing->Color, Constants::BLACK) !== false) { + $totalwins++; + } + } + + return $totalwins; + } + + /** + * Number of won games with black + * + * @param Player $player + * + * @return float + */ + private function calculateBlackWin(Player $player): float + { + $totalwins = 0; + foreach ($player->Pairings as $pairing) { + if (array_search($pairing->Color, Constants::BLACK) !== false && + array_search($pairing->Result, Constants::WON) !== false) { + $totalwins++; + } + } + + return $totalwins; + } + + /** + * The combined points of the opponents + * + * @param Player $player + * @param int $cutlowest + * @param int $cuthighest + * + * @return float + */ + private function calculateBuchholz(Player $player, int $cutlowest = 0, int $cuthighest = 0): float + { + $tiebreak = 0; + $intpairingsWithBye = $player->Pairings; + + $intpairings = []; + $curpoints = 0; + $curround = 1; + foreach ($intpairingsWithBye as $key => $pairing) { + $roundstoplay = (count($intpairingsWithBye)) - $curround; + if (is_null($pairing->Opponent)) { + $intpairings[] = $player->calculatePointsForVirtualPlayer($key); + } else { + $intpairings[] = $pairing->Opponent->calculatePointsForTiebreaks(); + if (array_search($pairing->Result, Constants::WON) !== false) { + $curpoints += 1; + } elseif (array_search($pairing->Result, Constants::DRAW) !== false) { + $curpoints += 0.5; + } + } + $curround++; + } + + usort( + $intpairings, + function ($a, $b) { + if ($b == $a) { + return 0; + } + + return ($a > $b) ? 1 : -1; + } + ); + + $intpairings = array_slice($intpairings, $cutlowest); + $intpairings = array_slice($intpairings, 0 - $cuthighest); + + return array_sum($intpairings); + } + + /** + * Combined score of $player after each round + * + * @param Player $player + * + * @return float + */ + private function calculateCumulative(Player $player): float + { + $tiebreak = 0; + $score = []; + foreach ($player->Pairings as $pairing) { + $toadd = 0; + if (array_search($pairing->Result, Constants::WON) !== false) { + $toadd = 1; + } elseif (array_search($pairing->Result, Constants::DRAW) !== false) { + $toadd = 0.5; + } + $tiebreak += $toadd; + $score[] = $tiebreak; + } + + return array_sum($score); + } + + /** + * $points["Won"] points for each win, $points["Draw"] for each draw and $points["Lost"] point for losing. + * $points["NotPlayed"] points for not played games + * + * @param Player $player + * @param int[] $points + * + * @return float + */ + private function calculateKashdan(Player $player, array $points): float + { + $tiebreak = 0; + foreach ($player->Pairings as $pairing) { + $toadd = 0; + if (array_search($pairing->Result, Constants::WON) !== false) { + $toadd = $points[ "Won" ]; + } elseif (array_search($pairing->Result, Constants::DRAW) !== false) { + $toadd = $points[ "Draw" ]; + } elseif (array_search($pairing->Result, Constants::LOST) !== false) { + $toadd = $points[ "Lost" ]; + } + + if (array_search($pairing->Result, Constants::NOTPLAYED) !== false) { + $toadd = $points[ "NotPlayed" ]; + } + $tiebreak += $toadd; + } + + return $tiebreak; // - $player->NoOfWins; } /** * Points following keizer system * * @param Player $player + * * @return float */ private function calculateKeizer(Player $player): float @@ -611,78 +615,36 @@ class Tournament } /** - * Number of points + * Points against players who have more than $cut % points * * @param Player $player - * @return float - */ - private function calculatePoints(Player $player): float - { - return $player->calculatePoints(); - } - - - /** - * Number of won games + * @param int $cut * - * @param Player $player * @return float */ - private function calculateBaumbach(Player $player): float + private function calculateKoya(Player $player, int $cut = 50): float { - $totalwins = 0; - foreach ($player->Pairings as $pairing) { - if (array_search($pairing->Result, Constants::NotPlayed) === false) { - if (array_search($pairing->Result, Constants::Won) !== false) { - $totalwins++; + $tiebreak = 0; + foreach ($player->Pairings as $plkey => $plpairing) { + if (($plpairing->Opponent->calculatePoints() / count($plpairing->Opponent->Pairings) * 100) >= $cut) { + if (array_search($plpairing->Result, Constants::WON) !== false) { + $tiebreak += 1; + } elseif (array_search($plpairing->Result, Constants::DRAW) !== false) { + $tiebreak += 0.5; } } } - return $totalwins; + + return $tiebreak; } - - /** - * Number of played games with black - * - * @param Player $player - * @return float - */ - private function calculateBlackPlayed(Player $player): float - { - $totalwins = 0; - foreach ($player->Pairings as $pairing) { - if (array_search($pairing->Color, Constants::Black) !== false) { - $totalwins++; - } - } - return $totalwins; - } - - /** - * Number of won games with black - * - * @param Player $player - * @return float - */ - private function calculateBlackWin(Player $player): float - { - $totalwins = 0; - foreach ($player->Pairings as $pairing) { - if (array_search($pairing->Color, Constants::Black) !== false && array_search($pairing->Result, Constants::Won) !== false) { - $totalwins++; - } - } - return $totalwins; - } - - /** * Result between the tied players * * @param Player $player - * @param array $opponents - * @param int $key + * @param array $opponents + * @param int $key + * * @return float */ private function calculateMutualResult(Player $player, array $opponents, int $key): float @@ -713,9 +675,9 @@ class Tournament $totalmatches = 0; foreach ($player->Pairings as $pairing) { if (array_search($pairing->Opponent, $interestingplayers) !== false) { - if (array_search($pairing->Result, Constants::Won) !== false) { + if (array_search($pairing->Result, Constants::WON) !== false) { $points = $points + 1; - } elseif (array_search($pairing->Result, Constants::Draw) !== false) { + } elseif (array_search($pairing->Result, Constants::DRAW) !== false) { $points = $points + 0.5; } $totalmatches++; @@ -724,137 +686,28 @@ class Tournament if ($totalmatches != count($interestingplayers)) { $points = 0; } + return $points; } - /** - * The average rating of the opponents + * Number of points * * @param Player $player - * @param string $type - * @param int $cut + * * @return float */ - private function calculateAverageRating(Player $player, string $type, int $cut = 0): float + private function calculatePoints(Player $player): float { - $pairings = $player->Pairings; - $allratings = []; - foreach ($pairings as $pairing) { - if (array_search($pairing->Result, Constants::NotPlayed) === false) { - $toadd = $pairing->Opponent->getElo($type); - if ($toadd != 0) { - $allratings[] = $toadd; - } - } - } - sort($allratings); - $allratings = array_slice($allratings, $cut); - $tiebreak = 0; - if (count($allratings) > 0) { - $tiebreak = round(array_sum($allratings) / count($allratings)); - } - return $tiebreak; + return $player->calculatePoints(); } - /** - * The average performance of the opponents + * The points of $player's opponents who $player won against, plus half of the points of $player's opponents who + * $player drew against * * @param Player $player - * @param string $type - * @param int $cut - * @return float - */ - private function calculateAveragePerformance(Player $player, string $type, int $cut = 0): float - { - $pairings = $player->Pairings; - $allratings = []; - foreach ($pairings as $pairing) { - if (array_search($pairing->Result, Constants::NotPlayed) === false) { - $toadd = $pairing->Opponent->Performance($type, $this->NonRatedElo); - if ($toadd != 0) { - $allratings[] = $toadd; - } - } - } - sort($allratings); - $allratings = array_slice($allratings, $cut); - return round(array_sum($allratings) / count($allratings)); - } - - - /** - * Points against players who have more than $cut % points * - * @param Player $player - * @param int $cut - * @return float - */ - private function calculateKoya(Player $player, int $cut = 50): float - { - $tiebreak = 0; - foreach ($player->Pairings as $plkey => $plpairing) { - if (($plpairing->Opponent->calculatePoints() / count($plpairing->Opponent->Pairings) * 100) >= $cut) { - if (array_search($plpairing->Result, Constants::Won) !== false) { - $tiebreak += 1; - } elseif (array_search($plpairing->Result, Constants::Draw) !== false) { - $tiebreak += 0.5; - } - } - } - return $tiebreak; - } - - - /** - * The combined points of the opponents - * @param Player $player - * @param int $cutlowest - * @param int $cuthighest - * @return float - */ - private function calculateBuchholz(Player $player, int $cutlowest = 0, int $cuthighest = 0): float - { - $tiebreak = 0; - $intpairingsWithBye = $player->Pairings; - - $intpairings = []; - $curpoints = 0; - $curround = 1; - foreach ($intpairingsWithBye as $key=>$pairing) { - $roundstoplay = (count($intpairingsWithBye)) - $curround; - if (is_null($pairing->Opponent)) { - $intpairings[] = $player->calculatePointsForVirtualPlayer($key); - } else { - $intpairings[] = $pairing->Opponent->calculatePointsForTiebreaks(); - if (array_search($pairing->Result, Constants::Won) !== false) { - $curpoints += 1; - } elseif (array_search($pairing->Result, Constants::Draw) !== false) { - $curpoints += 0.5; - } - } - $curround++; - } - - usort($intpairings, function ($a, $b) { - if ($b == $a) { - return 0; - } - return ($a > $b) ? 1 : -1; - }); - - $intpairings = array_slice($intpairings, $cutlowest); - $intpairings = array_slice($intpairings, 0 - $cuthighest); - - return array_sum($intpairings); - } - - - /** - * The points of $player's opponents who $player won against, plus half of the points of $player's opponents who $player drew against - * - * @param Player $player * @return float */ private function calculateSonneborn(Player $player): float @@ -862,100 +715,289 @@ class Tournament $tiebreak = 0; foreach ($player->Pairings as $key => $pairing) { if ($pairing->Opponent) { - if (array_search($pairing->Result, Constants::Won) !== false) { + if (array_search($pairing->Result, Constants::WON) !== false) { $tiebreak += $pairing->Opponent->calculatePointsForTiebreaks(); - } elseif (array_search($pairing->Result, Constants::Draw) !== false) { + } elseif (array_search($pairing->Result, Constants::DRAW) !== false) { $tiebreak += $pairing->Opponent->calculatePointsForTiebreaks() / 2; } } - if (array_search($pairing->Result, Constants::NotPlayed) !== false) { + if (array_search($pairing->Result, Constants::NOTPLAYED) !== false) { $tiebreak += $player->calculatePointsForVirtualPlayer($key); } } + return $tiebreak; } - /** - * $points["Won"] points for each win, $points["Draw"] for each draw and $points["Lost"] point for losing. $points["NotPlayed"] points for not played games + * Calculates a specific tiebreak for $player + * + * @param Tiebreak $tiebreak + * @param Player $player + * @param int $tbkey * - * @param Player $player - * @param int[] $points * @return float */ - private function calculateKashdan(Player $player, array $points): float + private function calculateTiebreak(Tiebreak $tiebreak, Player $player, int $tbkey = 0): float { - $tiebreak = 0; - foreach ($player->Pairings as $pairing) { - $toadd = 0; - if (array_search($pairing->Result, Constants::Won) !== false) { - $toadd = $points["Won"]; - } elseif (array_search($pairing->Result, Constants::Draw) !== false) { - $toadd = $points["Draw"]; - } elseif (array_search($pairing->Result, Constants::Lost) !== false) { - $toadd = $points["Lost"]; - } - - if (array_search($pairing->Result, Constants::NotPlayed) !== false) { - $toadd = $points["NotPlayed"]; - } - $tiebreak += $toadd; + switch ($tiebreak) { + case Tiebreak::KEIZER: + return $this->calculateKeizer($player); + case Tiebreak::POINTS: + return $this->calculatePoints($player); + case Tiebreak::BAUMBACH: + return $this->calculateBaumbach($player); + case Tiebreak::BLACK_PLAYED: + return $this->calculateBlackPlayed($player); + case Tiebreak::BLACK_WIN: + return $this->calculateBlackWin($player); + case Tiebreak::BETWEEN: + return $this->calculateMutualResult($player, $this->Players, $tbkey); + case Tiebreak::ARO: + return $this->calculateAverageRating($player, $this->PriorityElo); + case Tiebreak::AROCUT: + return $this->calculateAverageRating($player, $this->PriorityElo, 1); + case Tiebreak::KOYA: + return $this->calculateKoya($player); + case Tiebreak::BUCHHOLZ: + return $this->calculateBuchholz($player); + case Tiebreak::BUCHHOLZ_CUT: + return $this->calculateBuchholz($player, 1); + case Tiebreak::BUCHHOLZ_MED: + return $this->calculateBuchholz($player, 1, 1); + case Tiebreak::BUCHHOLZ_CUT_2: + return $this->calculateBuchholz($player, 2); + case Tiebreak::BUCHHOLZ_MED_2: + return $this->calculateBuchholz($player, 2, 2); + case Tiebreak::SONNEBORN: + return $this->calculateSonneborn($player); + case Tiebreak::KASHDAN: + return $this->calculateKashdan($player, ["Won" => 4, "Draw" => 2, "Lost" => 1, "NotPlayed" => 0]); + case Tiebreak::SOCCER_KASHDAN: + return $this->calculateKashdan($player, ["Won" => 3, "Draw" => 1, "Lost" => 0, "NotPlayed" => -1]); + case Tiebreak::CUMULATIVE: + return $this->calculateCumulative($player); + case Tiebreak::AVERAGE_PERFORMANCE: + return $this->calculateAveragePerformance($player, $this->PriorityElo); + case Tiebreak::PERFORMANCE: + return $player->Performance($this->PriorityElo, $this->NonRatedElo); + default: + return 0; } - return $tiebreak; // - $player->NoOfWins; } /** - * Combined score of $player after each round + * Checks if a game already is already registered * + * @param Game $game + * @param int $round + * + * @return bool + */ + public function gameExists(Game $game, int $round = -1): bool + { + $search = [$round]; + if ($round == -1) { + $search = []; + for ($i = 0; $i < $this->NoOfRounds; $i++) { + $search[] = $i; + } + } + + foreach ($search as $round) { + if (!isset($this->Rounds[ $round ])) { + return false; + } + $games = $this->Rounds[ $round ]->Games; + if (is_null($games)) { + return false; + } + foreach ($games as $roundgame) { + if ($game->equals($roundgame)) { + return true; + } + } + } + + return false; + } + + /** + * Converts pairings into games with a black and white player + */ + public function pairingsToRounds(): void + { + $pairings = $this->Pairings; + + /** + * @var Pairing[] + */ + $cache = []; + + foreach ($pairings as $pairing) { + // Add pairing to player + $pairing->Player->addPairing($pairing); + $round = $pairing->Round; + $color = $pairing->Color; + + $this->roundByNo($round)->addPairing($pairing); + $opponent = null; + + /** + * @var int $key + * @var Pairing $cached + */ + foreach ($cache as $key => $cached) { + if (!is_null($cached)) { + if (($cached->Opponent == $pairing->Player) && ($cached->Round == $pairing->Round)) { + $opponent = $cached; + $cache[ $key ] = null; + break; + } + } + } + $game = new Game(); + if ($color->getValue() == Color::WHITE) { + $game->White = $pairing; + $game->Black = $opponent; + } elseif ($color->getValue() == Color::BLACK) { + $game->White = $opponent; + $game->Black = $pairing; + } + + if (is_null($game->White) || is_null($game->Black)) { + $cache[] = $pairing; + } elseif (!$this->gameExists($game, $round)) { // Check if game already exists + $game->Board = $game->White->Board; + // Add board if inexistent + if ($game->Board == -1) { + if (isset($lastboards[ $round ])) { + $lastboards[ $round ] += 1; + } else { + $lastboards[ $round ] = 0; + } + $game->Board = $lastboards[ $round ]; + $game->White->Board = $lastboards[ $round ]; + $game->Black->Board = $lastboards[ $round ]; + } + $this->AddGame($game, $round); + } + } + } + + /** + * Returns the number of participants + * + * @return int + */ + private function participants(): int + { + return count($this->Players); + } + + /** + * Gets a player by its ID + * + * @param int $id + * + * @return Player + */ + public function playerById(int $id): Player + { + return $this->Players[ $id ]; + } + + /** + * Gets the ranking of the tournament + * + * @return Player[] + */ + private function ranking(): array + { + $players = $this->Players; + foreach ($this->Tiebreaks as $tbkey => $tiebreak) { + foreach ($players as $pkey => $player) { + $break = $this->calculateTiebreak($tiebreak, $player, $tbkey); + $tiebreaks = $player->Tiebreaks; + $tiebreaks[ $tbkey ] = $break; + $player->Tiebreaks = $tiebreaks; + $this->updatePlayer($pkey, $player); + } + } + $sortedplayers[ 0 ] = $players; + foreach ($this->Tiebreaks as $tbkey => $tiebreak) { + $newgroupkey = 0; + $tosortplayers = $sortedplayers; + $sortedplayers = []; + foreach ($tosortplayers as $groupkey => $sortedplayerselem) { + usort($tosortplayers[ $groupkey ], $this->sortTiebreak($tbkey)); + foreach ($tosortplayers[ $groupkey ] as $playerkey => $player) { + if (!is_null($player->Tiebreaks[ $tbkey ])) { + if ($playerkey != 0) { + $newgroupkey++; + if ($player->Tiebreaks[ $tbkey ] == + $tosortplayers[ $groupkey ][ $playerkey - 1 ]->Tiebreaks[ $tbkey ]) { + $newgroupkey--; + } + } + } + $sortedplayers[ $newgroupkey ][] = $player; + } + $newgroupkey++; + } + } + $finalarray = []; + foreach ($sortedplayers as $sort1) { + foreach ($sort1 as $player) { + $finalarray[] = $player; + } + } + + return $finalarray; + } + + /** + * Gets a round by its number. + * + * @param int $roundNo + * + * @return Round + */ + public function roundByNo(int $roundNo): Round + { + return $this->Rounds[ $roundNo ]; + } + + /** + * Sort by tiebreak + * + * @param int $key + * + * @return Closure + */ + private function sortTiebreak(int $key): Closure + { + return function (Player $a, Player $b) use ($key) { + if (($b->Tiebreaks[ $key ] == $a->Tiebreaks[ $key ]) || + ($a->Tiebreaks[ $key ] === false) || + ($b->Tiebreaks[ $key ] === false)) { + return 0; + } + + return ($b->Tiebreaks[ $key ] > $a->Tiebreaks[ $key ]) ? +1 : -1; + }; + } + + /** + * Updates player on id to the given Player object + * + * @param int $id * @param Player $player - * @return float */ - private function calculateCumulative(Player $player): float + public function updatePlayer(int $id, Player $player): void { - $tiebreak = 0; - $score = []; - foreach ($player->Pairings as $pairing) { - $toadd = 0; - if (array_search($pairing->Result, Constants::Won) !== false) { - $toadd = 1; - } elseif (array_search($pairing->Result, Constants::Draw) !== false) { - $toadd = 0.5; - } - $tiebreak += $toadd; - $score[] = $tiebreak; - } - return array_sum($score); - } - - /** - * Magic method to read out several fields. If field was not found it is being searched in the binary data fields - * - * @param string $key - * @return bool|DateTime|int|string|null - */ - public function __get(string $key) - { - if ($key == 'Participants') { - return $this->participants(); - } elseif ($key == 'AverageElo') { - return $this->averageElo(); - } elseif ($key == 'Ranking') { - return $this->ranking(); - } elseif (isset($this->BinaryData[$key])) { - return $this->BinaryData[$key]; - } - return null; - } - - /** - * Sets binary data that is read out the pairing file but is not needed immediately - * - * @param string $key - * @param bool|int|DateTime|string $value - * @return void - */ - public function __set(string $key, $value): void - { - $this->BinaryData[$key] = $value; + $newArray = $this->Players; + $newArray[ $id ] = $player; + $this->Players = $newArray; } } diff --git a/tests/ReadSws_test.php b/tests/ReadSws_test.php index 11583c1..1d77f09 100644 --- a/tests/ReadSws_test.php +++ b/tests/ReadSws_test.php @@ -23,6 +23,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ + use JeroenED\Libpairtwo\IOFactory; require_once '../vendor/autoload.php'; @@ -30,62 +31,67 @@ require_once '../vendor/autoload.php'; $sws = IOFactory::createReader('Swar-4'); $sws->read('../res/testswar.swar'); -echo "Release: " . $sws->Release() . PHP_EOL; -echo "Name: " . $sws->Tournament()->Name . PHP_EOL; -echo "Organiser: " . $sws->Tournament()->Organiser. PHP_EOL; -echo "TempoIndex: " . $sws->Tournament()->TempoIndex . PHP_EOL; -echo "TempoType: " . $sws->Tournament()->TournoiStd . PHP_EOL; -echo "Tempo: " . $sws->Tournament()->Tempo . PHP_EOL; -echo "Place: " . $sws->Tournament()->OrganiserPlace . PHP_EOL; -echo "Arbiter 1: " . $sws->Tournament()->Arbiters[0] . PHP_EOL; -echo "Arbiter 2: " . $sws->Tournament()->Arbiter[1] . PHP_EOL; -echo "Rounds: " . $sws->Tournament()->NoOfRounds . PHP_EOL; -echo "Fidehomol: " . $sws->Tournament()->FideHomol . PHP_EOL; -echo "Start-Date: " . $sws->Tournament()->StartDate->format('d/m/Y') . PHP_EOL; -echo "End-Date: " . $sws->Tournament()->EndDate->format('d/m/Y') . PHP_EOL; -echo "System: " . $sws->Tournament()->System->Key() . PHP_EOL; -echo "Place: " . $sws->Tournament()->OrganiserPlace . PHP_EOL; -echo "Unrated-Elo: " . $sws->Tournament()->NonRatedElo . PHP_EOL; -echo "Federation: " . $sws->Tournament()->Federation . PHP_EOL; -echo "Organiser: " . $sws->Tournament()->OrganiserClubNo . PHP_EOL; -echo "Fide Elo P1: " . $sws->Tournament()->PlayerById(0)->Elo('Fide') . PHP_EOL; -echo "Fide Elo P2: " . $sws->Tournament()->PlayerById(1)->Elo('Fide') . PHP_EOL; -echo "Fide Elo P3: " . $sws->Tournament()->PlayerById(2)->Elo('Fide') . PHP_EOL; -echo "KBSB Elo P1: " . $sws->Tournament()->PlayerById(0)->Elo('Nation') . PHP_EOL; -echo "KBSB Elo P2: " . $sws->Tournament()->PlayerById(1)->Elo('Nation') . PHP_EOL; -echo "KBSB Elo P3: " . $sws->Tournament()->PlayerById(2)->Elo('Nation') . PHP_EOL; -echo "Name P1: " . $sws->Tournament()->PlayerById(0)->Name . PHP_EOL; -echo "Name P2: " . $sws->Tournament()->PlayerById(1)->Name . PHP_EOL; -echo "Name P3: " . $sws->Tournament()->PlayerById(2)->Name . PHP_EOL; -echo "Gender P1: " . $sws->Tournament()->PlayerById(0)->Gender->Key() . PHP_EOL; -echo "Gender P2: " . $sws->Tournament()->PlayerById(1)->Gender->Key() . PHP_EOL; -echo "Gender P3: " . $sws->Tournament()->PlayerById(2)->Gender->Key() . PHP_EOL; -echo "Absent P1: " . $sws->Tournament()->PlayerById(0)->Absent . PHP_EOL; -echo "Absent P2: " . $sws->Tournament()->PlayerById(1)->Absent . PHP_EOL; -echo "Absent P3: " . $sws->Tournament()->PlayerById(2)->Absent . PHP_EOL; -echo "Date Round 1: " . $sws->Tournament()->RoundByNo(0)->Date->format('d/m/Y') . PHP_EOL; -echo "Date Round 2: " . $sws->Tournament()->RoundByNo(1)->Date->format('d/m/Y') . PHP_EOL; -echo "Date Round 3: " . $sws->Tournament()->RoundByNo(2)->Date->format('d/m/Y') . PHP_EOL; -echo "Game Round 1: " . $sws->Tournament()->RoundByNo(0)->Games[0]->Result->getValue() . PHP_EOL; -echo "Game Round 2: " . $sws->Tournament()->RoundByNo(1)->Games[0]->Result->getValue() . PHP_EOL; -echo "Game Round 3: " . $sws->Tournament()->RoundByNo(2)->Games[0]->Result->getValue() . PHP_EOL; -echo "Color Pairing 1: " . $sws->Tournament()->Pairings[1]->Color->getKey() . PHP_EOL; -echo "Color Pairing 2: " . $sws->Tournament()->Pairings[2]->Color->getKey() . PHP_EOL; -echo "Color Pairing 3: " . $sws->Tournament()->Pairings[3]->Color->getKey() . PHP_EOL; -echo "Player Pairing 1: " . $sws->Tournament()->Pairings[0]->Player->Name . PHP_EOL; -echo "Player Pairing 2: " . $sws->Tournament()->Pairings[1]->Player->Name . PHP_EOL; -echo "Player Pairing 3: " . $sws->Tournament()->Pairings[2]->Player->Name . PHP_EOL; -echo "Bye Round 1: " . $sws->Tournament()->RoundByNo(2)->Bye[0]->Player->Name . PHP_EOL; -echo "Absent Round 1: " . $sws->Tournament()->RoundByNo(2)->Absent[0]->Player->Name . PHP_EOL; -echo "Tiebreak 1: " . $sws->Tournament()->Tiebreaks[0]->Value() . PHP_EOL; -echo "Tiebreak 2: " . $sws->Tournament()->Tiebreaks[1]->Value() . PHP_EOL; -echo "Tiebreak 3: " . $sws->Tournament()->Tiebreaks[2]->Value() . PHP_EOL; -echo "Tiebreak 4: " . $sws->Tournament()->Tiebreaks[3]->Value() . PHP_EOL; -echo "Tiebreak 5: " . $sws->Tournament()->Tiebreaks[4]->Value() . PHP_EOL; -echo "Tiebreak 6: " . $sws->Tournament()->Tiebreaks[5]->Value() . PHP_EOL; -echo "Average Elo: " . $sws->Tournament()->AverageElo . PHP_EOL; -foreach ($sws->Tournament()->Ranking as $player) { - echo str_pad($player->Name . '(' . $player->Elo($sws->Tournament()->PriorityElo) . ') ', 35) . implode_pad(' ', $player->Tiebreaks, 5, ' ') . PHP_EOL; +echo "Release: " . $sws->Release . PHP_EOL; +echo "Name: " . $sws->Tournament->Name . PHP_EOL; +echo "Organiser: " . $sws->Tournament->Organiser . PHP_EOL; +echo "TempoIndex: " . $sws->Tournament->TempoIndex . PHP_EOL; +echo "TempoType: " . $sws->Tournament->TournoiStd . PHP_EOL; +echo "Tempo: " . $sws->Tournament->Tempo . PHP_EOL; +echo "Place: " . $sws->Tournament->OrganiserPlace . PHP_EOL; +echo "Arbiter 1: " . $sws->Tournament->Arbiters[ 0 ] . PHP_EOL; +echo "Arbiter 2: " . $sws->Tournament->Arbiters[ 1 ] . PHP_EOL; +echo "Rounds: " . $sws->Tournament->NoOfRounds . PHP_EOL; +echo "Fidehomol: " . $sws->Tournament->FideHomol . PHP_EOL; +echo "Start-Date: " . $sws->Tournament->StartDate->format('d/m/Y') . PHP_EOL; +echo "End-Date: " . $sws->Tournament->EndDate->format('d/m/Y') . PHP_EOL; +echo "System: " . $sws->Tournament->System->getKey() . PHP_EOL; +echo "Place: " . $sws->Tournament->OrganiserPlace . PHP_EOL; +echo "Unrated-Elo: " . $sws->Tournament->NonRatedElo . PHP_EOL; +echo "Federation: " . $sws->Tournament->Federation . PHP_EOL; +echo "Organiser: " . $sws->Tournament->OrganiserClubNo . PHP_EOL; +echo "Fide Elo P1: " . $sws->Tournament->PlayerById(0)->getElo('Fide') . PHP_EOL; +echo "Fide Elo P2: " . $sws->Tournament->PlayerById(1)->getElo('Fide') . PHP_EOL; +echo "Fide Elo P3: " . $sws->Tournament->PlayerById(2)->getElo('Fide') . PHP_EOL; +echo "KBSB Elo P1: " . $sws->Tournament->PlayerById(0)->getElo('Nation') . PHP_EOL; +echo "KBSB Elo P2: " . $sws->Tournament->PlayerById(1)->getElo('Nation') . PHP_EOL; +echo "KBSB Elo P3: " . $sws->Tournament->PlayerById(2)->getElo('Nation') . PHP_EOL; +echo "Name P1: " . $sws->Tournament->PlayerById(0)->Name . PHP_EOL; +echo "Name P2: " . $sws->Tournament->PlayerById(1)->Name . PHP_EOL; +echo "Name P3: " . $sws->Tournament->PlayerById(2)->Name . PHP_EOL; +echo "Gender P1: " . $sws->Tournament->PlayerById(0)->Gender->getKey() . PHP_EOL; +echo "Gender P2: " . $sws->Tournament->PlayerById(1)->Gender->getKey() . PHP_EOL; +echo "Gender P3: " . $sws->Tournament->PlayerById(2)->Gender->getKey() . PHP_EOL; +echo "Absent P1: " . $sws->Tournament->PlayerById(0)->Absent . PHP_EOL; +echo "Absent P2: " . $sws->Tournament->PlayerById(1)->Absent . PHP_EOL; +echo "Absent P3: " . $sws->Tournament->PlayerById(2)->Absent . PHP_EOL; +echo "Category P1: " . $sws->Tournament->PlayerById(0)->Category . PHP_EOL; +echo "Category P2: " . $sws->Tournament->PlayerById(1)->Category . PHP_EOL; +echo "Category P3: " . $sws->Tournament->PlayerById(2)->Category . PHP_EOL; +echo "Date Round 1: " . $sws->Tournament->RoundByNo(0)->Date->format('d/m/Y') . PHP_EOL; +echo "Date Round 2: " . $sws->Tournament->RoundByNo(1)->Date->format('d/m/Y') . PHP_EOL; +echo "Date Round 3: " . $sws->Tournament->RoundByNo(2)->Date->format('d/m/Y') . PHP_EOL; +echo "Game Round 1: " . $sws->Tournament->RoundByNo(0)->Games[ 0 ]->Result->getValue() . PHP_EOL; +echo "Game Round 2: " . $sws->Tournament->RoundByNo(1)->Games[ 0 ]->Result->getValue() . PHP_EOL; +echo "Game Round 3: " . $sws->Tournament->RoundByNo(2)->Games[ 0 ]->Result->getValue() . PHP_EOL; +echo "Color Pairing 1: " . $sws->Tournament->Pairings[ 1 ]->Color->getKey() . PHP_EOL; +echo "Color Pairing 2: " . $sws->Tournament->Pairings[ 2 ]->Color->getKey() . PHP_EOL; +echo "Color Pairing 3: " . $sws->Tournament->Pairings[ 3 ]->Color->getKey() . PHP_EOL; +echo "Player Pairing 1: " . $sws->Tournament->Pairings[ 0 ]->Player->Name . PHP_EOL; +echo "Player Pairing 2: " . $sws->Tournament->Pairings[ 1 ]->Player->Name . PHP_EOL; +echo "Player Pairing 3: " . $sws->Tournament->Pairings[ 2 ]->Player->Name . PHP_EOL; +echo "Bye Round 1: " . $sws->Tournament->RoundByNo(2)->Bye[ 0 ]->Player->Name . PHP_EOL; +echo "Absent Round 1: " . $sws->Tournament->RoundByNo(2)->Absent[ 0 ]->Player->Name . PHP_EOL; +echo "Tiebreak 1: " . $sws->Tournament->Tiebreaks[ 0 ]->getValue() . PHP_EOL; +echo "Tiebreak 2: " . $sws->Tournament->Tiebreaks[ 1 ]->getValue() . PHP_EOL; +echo "Tiebreak 3: " . $sws->Tournament->Tiebreaks[ 2 ]->getValue() . PHP_EOL; +echo "Tiebreak 4: " . $sws->Tournament->Tiebreaks[ 3 ]->getValue() . PHP_EOL; +echo "Tiebreak 5: " . $sws->Tournament->Tiebreaks[ 4 ]->getValue() . PHP_EOL; +echo "Tiebreak 6: " . $sws->Tournament->Tiebreaks[ 5 ]->getValue() . PHP_EOL; +echo "Average Elo: " . $sws->Tournament->AverageElo . PHP_EOL; +foreach ($sws->Tournament->RankingForCategory('+2500') as $player) { + echo str_pad($player->Name . '(' . $player->getElo($sws->Tournament->PriorityElo) . ') ', 35) . + implode_pad(' ', $player->Tiebreaks, 5, ' ') . + PHP_EOL; } function implode_pad($glue, $collection, $padlength, $padstring): string @@ -94,5 +100,6 @@ function implode_pad($glue, $collection, $padlength, $padstring): string foreach ($collection as $elem) { $newarray[] = str_pad($elem, $padlength, $padstring); } + return implode($glue, $newarray); }