2019-02-01 15:53:39 +01:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* Created by PhpStorm.
|
|
|
|
* User: jeroen
|
|
|
|
* Date: 1/02/19
|
|
|
|
* Time: 11:18
|
|
|
|
*/
|
|
|
|
|
|
|
|
namespace JeroenED\Libpairtwo;
|
|
|
|
|
2019-05-27 21:45:19 +02:00
|
|
|
use JeroenED\Libpairtwo\Enums\Tiebreak;
|
2019-02-11 22:41:44 +01:00
|
|
|
use JeroenED\Libpairtwo\Enums\Color;
|
2019-02-01 15:53:39 +01:00
|
|
|
|
2019-05-28 17:04:53 +02:00
|
|
|
class Tournament extends Tiebreaks
|
2019-02-01 15:53:39 +01:00
|
|
|
{
|
|
|
|
/**
|
2019-03-20 17:33:09 +01:00
|
|
|
* Gets a player by its ID
|
|
|
|
*
|
2019-02-11 17:54:50 +01:00
|
|
|
* @param integer $id
|
2019-02-01 15:53:39 +01:00
|
|
|
* @return Player
|
|
|
|
*/
|
2019-02-11 17:54:50 +01:00
|
|
|
public function getPlayerById(int $id)
|
2019-02-01 15:53:39 +01:00
|
|
|
{
|
|
|
|
return $this->GetPlayers()[$id];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-03-20 17:33:09 +01:00
|
|
|
* Adds a player
|
|
|
|
*
|
2019-02-01 15:53:39 +01:00
|
|
|
* @param Player $Player
|
|
|
|
*/
|
|
|
|
public function addPlayer(Player $Player)
|
|
|
|
{
|
|
|
|
$newArray = $this->GetPlayers();
|
|
|
|
$newArray[] = $Player;
|
|
|
|
$this->setPlayers($newArray);
|
|
|
|
}
|
2019-02-01 17:02:33 +01:00
|
|
|
|
|
|
|
/**
|
2019-03-20 17:33:09 +01:00
|
|
|
* Updates player on id to the given Player object
|
|
|
|
*
|
2019-02-11 17:54:50 +01:00
|
|
|
* @param int $id
|
2019-02-01 17:02:33 +01:00
|
|
|
* @param Player $player
|
|
|
|
*/
|
2019-02-11 17:54:50 +01:00
|
|
|
public function updatePlayer(int $id, Player $player)
|
2019-02-01 17:02:33 +01:00
|
|
|
{
|
|
|
|
$newArray = $this->GetPlayers();
|
|
|
|
$newArray[$id] = $player;
|
|
|
|
$this->setPlayers($newArray);
|
|
|
|
}
|
2019-02-06 17:23:37 +01:00
|
|
|
|
2019-05-27 21:45:19 +02:00
|
|
|
/**
|
|
|
|
* Adds a Tiebreak
|
|
|
|
*
|
|
|
|
* @param Tiebreak $tiebreak
|
|
|
|
*/
|
|
|
|
public function addTiebreak(Tiebreak $tiebreak)
|
|
|
|
{
|
|
|
|
$newArray = $this->getTiebreaks();
|
|
|
|
$newArray[] = $tiebreak;
|
|
|
|
$this->setTiebreaks($newArray);
|
|
|
|
}
|
|
|
|
|
2019-02-06 17:23:37 +01:00
|
|
|
/**
|
2019-03-20 17:33:09 +01:00
|
|
|
* Adds a round with given Round object
|
|
|
|
*
|
2019-02-06 17:23:37 +01:00
|
|
|
* @param Round $round
|
|
|
|
*/
|
|
|
|
public function addRound(Round $round)
|
|
|
|
{
|
2019-02-11 22:41:44 +01:00
|
|
|
$newArray = $this->getRounds();
|
|
|
|
$newArray[$round->getRoundNo()] = $round;
|
2019-02-06 17:23:37 +01:00
|
|
|
$this->setRounds($newArray);
|
|
|
|
}
|
|
|
|
|
2019-02-11 22:41:44 +01:00
|
|
|
/**
|
2019-03-20 17:33:09 +01:00
|
|
|
* Gets a round by its number.
|
|
|
|
*
|
2019-02-11 22:41:44 +01:00
|
|
|
* @param int $roundNo
|
|
|
|
* @return Round
|
|
|
|
*/
|
|
|
|
public function getRoundByNo(int $roundNo): Round
|
|
|
|
{
|
|
|
|
return $this->getRounds()[$roundNo];
|
|
|
|
}
|
|
|
|
|
2019-02-11 16:43:36 +01:00
|
|
|
/**
|
2019-03-20 17:33:09 +01:00
|
|
|
* Adds a pairing to the tournament
|
|
|
|
*
|
2019-02-11 16:43:36 +01:00
|
|
|
* @param Pairing $pairing
|
|
|
|
*/
|
|
|
|
public function addPairing(Pairing $pairing)
|
|
|
|
{
|
|
|
|
$newArray = $this->GetPairings();
|
|
|
|
$newArray[] = $pairing;
|
|
|
|
$this->setPairings($newArray);
|
|
|
|
}
|
|
|
|
|
2019-02-11 22:41:44 +01:00
|
|
|
/**
|
2019-03-20 17:33:09 +01:00
|
|
|
* Converts pairings into games with a black and white player
|
2019-02-11 22:41:44 +01:00
|
|
|
*/
|
|
|
|
public function pairingsToRounds(): void
|
|
|
|
{
|
2019-04-20 16:55:39 +02:00
|
|
|
/** @var Pairing[] $pairings */
|
2019-02-11 22:41:44 +01:00
|
|
|
$pairings = $this->getPairings();
|
2019-03-20 12:46:46 +01:00
|
|
|
|
|
|
|
/** @var Pairing[] */
|
|
|
|
$cache = array();
|
|
|
|
|
2019-02-11 22:41:44 +01:00
|
|
|
foreach ($pairings as $pairing) {
|
2019-04-20 16:55:39 +02:00
|
|
|
// Add pairing to player
|
|
|
|
$pairing->getPlayer()->addPairing($pairing);
|
2019-02-11 22:41:44 +01:00
|
|
|
$round = $pairing->getRound();
|
|
|
|
$color = $pairing->getColor();
|
2019-05-01 15:49:12 +02:00
|
|
|
|
|
|
|
$this->getRoundByNo($round)->addPairing($pairing);
|
2019-03-20 12:46:46 +01:00
|
|
|
$opponent = null;
|
2019-03-20 17:33:09 +01:00
|
|
|
foreach ($cache as $key=>$cached) {
|
2019-03-20 12:46:46 +01:00
|
|
|
if (!is_null($cached)) {
|
2019-04-20 16:55:39 +02:00
|
|
|
if (($cached->getOpponent() == $pairing->getPlayer()) && ($cached->getRound() == $pairing->getRound())) {
|
2019-03-20 12:46:46 +01:00
|
|
|
$opponent = $cached;
|
2019-04-20 16:55:39 +02:00
|
|
|
$cache[$key] = null;
|
2019-03-20 12:46:46 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-02-11 22:41:44 +01:00
|
|
|
$game = new Game();
|
2019-03-19 15:58:41 +01:00
|
|
|
if ($color->getValue() == Color::white) {
|
2019-03-20 12:46:46 +01:00
|
|
|
$game->setWhite($pairing);
|
|
|
|
$game->setBlack($opponent);
|
2019-03-19 15:58:41 +01:00
|
|
|
} elseif ($color->getValue() == Color::black) {
|
2019-03-20 12:46:46 +01:00
|
|
|
$game->setWhite($opponent);
|
|
|
|
$game->setBlack($pairing);
|
2019-02-11 22:41:44 +01:00
|
|
|
}
|
|
|
|
|
2019-03-20 12:46:46 +01:00
|
|
|
if (is_null($game->getWhite()) || is_null($game->getBlack())) {
|
|
|
|
$cache[] = $pairing;
|
|
|
|
} else {
|
|
|
|
// Check if game already exists
|
|
|
|
if (!$this->GameExists($game, $round)) {
|
|
|
|
$this->AddGame($game, $round);
|
|
|
|
}
|
2019-02-11 22:41:44 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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->getNoOfRounds(); $i++) {
|
|
|
|
$search[] = $i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($search as $round) {
|
|
|
|
if (!isset($this->getRounds()[$round])) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
$games = $this->getRounds()[$round]->getGames();
|
2019-03-20 12:46:46 +01:00
|
|
|
if (is_null($games)) {
|
|
|
|
return false;
|
|
|
|
}
|
2019-02-11 22:41:44 +01:00
|
|
|
foreach ($games as $roundgame) {
|
|
|
|
if ($roundgame->getWhite() == $game->getWhite() &&
|
|
|
|
$roundgame->getBlack() == $game->getBlack() &&
|
|
|
|
$roundgame->getResult() == $game->getResult()
|
|
|
|
) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2019-03-20 17:33:09 +01:00
|
|
|
* Adds a game to the tournament
|
|
|
|
*
|
2019-02-11 22:41:44 +01:00
|
|
|
* @param Game $game
|
|
|
|
* @param int $round
|
|
|
|
*/
|
|
|
|
public function addGame(Game $game, int $round)
|
|
|
|
{
|
|
|
|
if (!isset($this->getRounds()[$round])) {
|
|
|
|
$roundObj = new Round();
|
|
|
|
$roundObj->setRoundNo($round);
|
|
|
|
$this->addRound($roundObj);
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->getRoundByNo($round)->addGame($game);
|
|
|
|
}
|
|
|
|
|
2019-02-06 17:24:10 +01:00
|
|
|
/**
|
2019-03-20 17:33:09 +01:00
|
|
|
* Gets the ranking of the tournament
|
|
|
|
*
|
2019-02-11 17:54:50 +01:00
|
|
|
* @return Player[]
|
2019-02-06 17:24:10 +01:00
|
|
|
*/
|
2019-05-28 16:26:03 +02:00
|
|
|
public function getRanking()
|
2019-02-06 17:24:10 +01:00
|
|
|
{
|
|
|
|
$players = $this->getPlayers();
|
2019-05-29 15:48:23 +02:00
|
|
|
foreach ($this->getTiebreaks() as $tbkey=>$tiebreak) {
|
|
|
|
foreach ($players as $pkey => $player) {
|
|
|
|
$break = $this->calculateTiebreak($tiebreak, $player, $tbkey);
|
|
|
|
$tiebreaks = $player->getTiebreaks();
|
|
|
|
$tiebreaks[$tbkey] = $break;
|
|
|
|
$player->setTiebreaks($tiebreaks);
|
|
|
|
$this->updatePlayer($pkey, $player);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$sortedplayers[0] = $players;
|
|
|
|
foreach ($this->getTiebreaks() 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->getTiebreaks()[$tbkey])) {
|
|
|
|
if ($playerkey != 0) {
|
|
|
|
$newgroupkey++;
|
|
|
|
if ($player->getTiebreaks()[$tbkey] == $tosortplayers[$groupkey][$playerkey - 1]->getTiebreaks()[$tbkey]) {
|
|
|
|
$newgroupkey--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$sortedplayers[$newgroupkey][] = $player;
|
|
|
|
}
|
|
|
|
$newgroupkey++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$finalarray = [];
|
|
|
|
foreach ($sortedplayers as $sort1) {
|
|
|
|
foreach ($sort1 as $player) {
|
|
|
|
$finalarray[] = $player;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return $finalarray;
|
2019-02-06 18:22:25 +01:00
|
|
|
}
|
|
|
|
|
2019-02-06 20:10:52 +01:00
|
|
|
/**
|
2019-02-11 17:54:50 +01:00
|
|
|
* @param Player $a
|
|
|
|
* @param Player $b
|
2019-05-29 15:48:23 +02:00
|
|
|
* @return \Closure
|
2019-02-06 20:10:52 +01:00
|
|
|
*/
|
2019-05-29 15:48:23 +02:00
|
|
|
|
|
|
|
private function sortTiebreak(int $key)
|
2019-02-06 18:22:25 +01:00
|
|
|
{
|
2019-05-29 15:48:23 +02:00
|
|
|
return function (Player $a, Player $b) use ($key) {
|
|
|
|
if (($b->getTiebreaks()[$key] == $a->getTiebreaks()[$key]) || ($a->getTiebreaks()[$key] === false) || ($b->getTiebreaks()[$key] === false)) {
|
|
|
|
return 0;
|
2019-05-28 18:07:43 +02:00
|
|
|
}
|
2019-05-29 15:48:23 +02:00
|
|
|
return ($b->getTiebreaks()[$key] > $a->getTiebreaks()[$key]) ? +1 : -1;
|
|
|
|
};
|
2019-02-06 17:24:10 +01:00
|
|
|
}
|
2019-02-06 20:10:52 +01:00
|
|
|
|
2019-05-28 16:26:03 +02:00
|
|
|
|
2019-02-06 20:10:52 +01:00
|
|
|
/**
|
2019-05-30 08:33:16 +02:00
|
|
|
* @return float|null
|
2019-05-28 16:26:03 +02:00
|
|
|
*/
|
2019-05-29 15:48:23 +02:00
|
|
|
private function calculateTiebreak(Tiebreak $tiebreak, Player $player, int $tbkey = 0): ?float
|
2019-05-28 16:26:03 +02:00
|
|
|
{
|
2019-05-28 18:07:43 +02:00
|
|
|
switch ($tiebreak) {
|
|
|
|
case Tiebreak::Keizer:
|
2019-05-29 15:48:23 +02:00
|
|
|
return $this->calculateKeizer($player);
|
2019-05-28 18:07:43 +02:00
|
|
|
break;
|
|
|
|
case Tiebreak::American:
|
2019-05-29 15:48:23 +02:00
|
|
|
return $this->calculateAmerican($player);
|
2019-05-28 18:07:43 +02:00
|
|
|
break;
|
|
|
|
case Tiebreak::Points:
|
2019-05-29 15:48:23 +02:00
|
|
|
return $this->calculatePoints($player);
|
2019-05-28 18:07:43 +02:00
|
|
|
break;
|
|
|
|
case Tiebreak::Baumbach:
|
2019-05-29 15:48:23 +02:00
|
|
|
return $this->calculateBaumbach($player);
|
2019-05-28 18:07:43 +02:00
|
|
|
break;
|
|
|
|
case Tiebreak::BlackPlayed:
|
2019-05-29 15:48:23 +02:00
|
|
|
return $this->calculateBlackPlayed($player);
|
2019-05-28 18:07:43 +02:00
|
|
|
break;
|
|
|
|
case Tiebreak::BlackWin:
|
2019-05-29 15:48:23 +02:00
|
|
|
return $this->calculateBlackWin($player);
|
|
|
|
break;
|
|
|
|
case Tiebreak::Between:
|
|
|
|
return $this->calculateMutualResult($player, $this->getPlayers(), $tbkey);
|
2019-05-28 18:07:43 +02:00
|
|
|
break;
|
2019-05-29 17:56:30 +02:00
|
|
|
case Tiebreak::Aro:
|
2019-05-31 22:45:26 +02:00
|
|
|
return $this->calculateAverageRating($player, $this->getPriorityElo());
|
2019-05-29 17:56:30 +02:00
|
|
|
break;
|
2019-05-30 08:33:52 +02:00
|
|
|
case Tiebreak::AroCut:
|
2019-05-31 22:45:26 +02:00
|
|
|
return $this->calculateAverageRating($player, $this->getPriorityElo(), 1);
|
2019-05-30 08:33:52 +02:00
|
|
|
break;
|
2019-05-30 21:07:05 +02:00
|
|
|
case Tiebreak::Koya:
|
|
|
|
return $this->calculateKoya($player);
|
|
|
|
break;
|
2019-05-30 21:13:14 +02:00
|
|
|
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;
|
2019-05-30 21:16:12 +02:00
|
|
|
case Tiebreak::Sonneborn:
|
|
|
|
return $this->calculateSonneborn($player);
|
|
|
|
break;
|
2019-05-30 21:20:14 +02:00
|
|
|
case Tiebreak::Kashdan:
|
|
|
|
return $this->calculateKashdan($player);
|
|
|
|
break;
|
2019-05-30 21:25:37 +02:00
|
|
|
case Tiebreak::Cumulative:
|
|
|
|
return $this->calculateCumulative($player);
|
|
|
|
break;
|
2019-05-31 22:50:07 +02:00
|
|
|
case Tiebreak::AveragePerformance:
|
2019-05-31 22:45:26 +02:00
|
|
|
return $this->calculateAveragePerformance($player, $this->getPriorityElo());
|
2019-05-30 21:30:28 +02:00
|
|
|
break;
|
2019-05-31 22:50:07 +02:00
|
|
|
case Tiebreak::Performance:
|
|
|
|
return $player->getPerformance($this->getPriorityElo());
|
|
|
|
break;
|
2019-05-29 15:48:23 +02:00
|
|
|
default:
|
|
|
|
return null;
|
2019-05-28 16:26:03 +02:00
|
|
|
}
|
|
|
|
}
|
2019-05-29 17:57:42 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the average rating for tournament
|
|
|
|
*
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
public function getAverageElo(): int
|
|
|
|
{
|
|
|
|
$totalrating = 0;
|
|
|
|
$players = 0;
|
|
|
|
foreach ($this->getPlayers() as $player) {
|
2019-05-31 11:26:23 +02:00
|
|
|
$toadd = $player->getElo($this->getPriorityElo());
|
2019-05-29 17:57:42 +02:00
|
|
|
if ($toadd == 0) {
|
|
|
|
$toadd = $this->getNonRatedElo();
|
|
|
|
}
|
|
|
|
|
|
|
|
$totalrating += $toadd;
|
|
|
|
$players++;
|
|
|
|
}
|
|
|
|
return intdiv($totalrating, $players);
|
|
|
|
}
|
2019-02-01 15:53:39 +01:00
|
|
|
}
|