Passed
Branch master (7949ab)
by Tomáš
02:07
created

DoubleElimination   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 138
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 90
dl 0
loc 138
ccs 91
cts 91
cp 1
rs 10
c 0
b 0
f 0
wmc 19

4 Methods

Rating   Name   Duplication   Size   Complexity  
A generate() 0 61 4
A calcByes() 0 8 2
B generateLosingSide() 0 49 11
A generateWinSide() 0 12 2
1
<?php
2
3
namespace TournamentGenerator\Preset;
4
5
/**
6
 *
7
 */
8
class DoubleElimination extends \TournamentGenerator\Tournament
9
{
10
11 2
	public function generate() {
12 2
		$this->allowSkip();
13
14 2
		$countTeams = count($this->getTeams());
15
16 2
		if ($countTeams < 3) throw new \Exception('Double elimination is possible for minimum of 3 teams - '.$countTeams.' teams given.');
17
18
19
		// CALCULATE BYES
20 2
		$nextPow = 0;
21 2
		$byes = $this->calcByes($countTeams, $nextPow);
22
23 2
		$startRound = $this->round('Start round');
24
25 2
		$roundsNum = log($nextPow, 2)*2;
26
27 2
		$startGroups = ($countTeams+$byes)/2;
28
29 2
		$previousGroups = [];
30 2
		$previousLosingGroups = [];
31 2
		$groupIds = [];
32 2
		$allGroups = [];
33
34 2
		for ($i=1; $i <= $startGroups; $i++) {
35 2
			$g = $startRound->group('Start group - '.$i)->setInGame(2)->setType(\TournamentGenerator\Constants::ROUND_TWO);
36 2
			$allGroups[] = $g;
37 2
			$groupIds[] = $g->getId();
38 2
			$previousGroups[] = $g;
39
		}
40
41
		// SPLIT TEAMS EVENLY
42 2
		$this->splitTeams();
43
44 2
		for ($r=2; $r <= $roundsNum-1; $r++) {
45 2
			$groups = [];
46 2
			$losingGroups = [];
47 2
			$round = $this->round('Round '.$r);
48
49
			// GENERATE GROUPS AND PROGRESSIONS
50
51
			// GENERATING LOSING AND WINNING SIDE
52 2
			$lastLosingGroup = $this->generateLosingSide($r, $round, $allGroups, $previousLosingGroups, $previousGroups, $losingGroups);
53 2
			$this->generateWinSide($r, $byes, $countTeams, $round, $allGroups, $groups, $lastWinningGroup, $previousGroups);
54
55 2
			$previousGroups = $groups;
56 2
			$previousLosingGroups = $losingGroups;
57
		}
58
59
		// LAST ROUND
60 2
		$round = $this->round('Round '.$roundsNum.' - Finale');
61 2
		$groupFinal = $round->group('Round '.$r.' - finale')->setInGame(2)->setType(\TournamentGenerator\Constants::ROUND_TWO)->setOrder(1);
62 2
		$allGroups[] = $groupFinal;
63 2
		$lastLosingGroup->progression($groupFinal, 0, 1);
64 2
		$lastWinningGroup->progression($groupFinal, 0, 1);
65
66
		// REPEAT GROUP IF LOSING TEAM WON
67 2
		$group = $round->group('Round '.$r.' - finale (2)')->setInGame(2)->setType(\TournamentGenerator\Constants::ROUND_TWO)->setOrder(1);
68 2
		$twoLoss = new \TournamentGenerator\TeamFilter('losses', '=', 1, $allGroups);
69 2
		$groupFinal->progression($group, 0, 2)->addFilter($twoLoss);
70
71 2
		return $this;
72
73
	}
74
75 2
	private function calcByes(int $countTeams, int &$nextPow) {
76 2
		$byes = 0;
77 2
		$nextPow = $countTeams;
78 2
		if ( !\TournamentGenerator\isPowerOf2($countTeams) ) {
79 1
			$nextPow = bindec(str_pad(1, strlen(decbin($countTeams))+1, 0, STR_PAD_RIGHT));
80 1
			$byes = $nextPow-$countTeams;
81
		}
82 2
		return $byes;
83
	}
84 2
	private function generateWinSide(int &$r, int &$byes, int &$countTeams, \TournamentGenerator\Round &$round, array &$allGroups, array &$groups, \TournamentGenerator\Group &$lastWinningGroup = null, array &$previousGroups = []) {
85 2
		$order = 1;
86 2
		for ($g=1; $g <= (($countTeams+$byes)/pow(2, $r)); $g++) {
87 2
			$group = $round->group('Round '.$r.' - win '.$g)->setInGame(2)->setType(\TournamentGenerator\Constants::ROUND_TWO)->setOrder($order);
88 2
			$allGroups[] = $group;
89 2
			$order += 2;
90 2
			$groups[] = $group;
91 2
			$lastWinningGroup = $group; // KEEP THE LAST GROUP FOR FINALE
92 2
			$previousGroups[2*($g-1)]->progression($group, 0, 1); // PROGRESS FROM GROUP BEFORE
93 2
			$previousGroups[(2*($g-1))+1]->progression($group, 0, 1); // PROGREESS FROM GROUP BEFORE
94
		}
95 2
		return $this;
96
	}
97 2
	private function generateLosingSide(int &$r, \TournamentGenerator\Round &$round, array &$allGroups, array &$previousLosingGroups = [], array &$previousGroups = [], array &$losingGroups = []) {
98 2
		$losingGroupTeamsCount = count($previousLosingGroups)+count($previousGroups);
99 2
		$order = 2;
100 2
		if (\TournamentGenerator\isPowerOf2($losingGroupTeamsCount)) { // IF THE NUMBER OF TEAMS IS A POWER OF 2, GENERATE GROUPS WITHOUT BYES
101 2
			for ($g=1; $g <= $losingGroupTeamsCount/2; $g++) {
102 2
				$group = $round->group('Round '.$r.' - loss '.$g)->setInGame(2)->setType(\TournamentGenerator\Constants::ROUND_TWO)->setOrder($order);
103 2
				$allGroups[] = $group;
104 2
				$order += 2;
105 2
				$losingGroups[] = $group;
106 2
				$lastLosingGroup = $group; // KEEP THE LAST GROUP FOR FINALE
107 2
				if ($r === 2) { // FIRST LOSING ROUND
108 2
					$previousGroups[2*($g-1)]->progression($group, 1, 1); // PROGRESS FROM STARTING GROUP
109 2
					$previousGroups[(2*($g-1))+1]->progression($group, 1, 1); // PROGREESS FROM STARTING GROUP
110
				}
111 2
				elseif ($losingGroupTeamsCount >= 2) {
112 2
					$previousLosingGroups[$g-1]->progression($group, 0, 1); // PROGRESS FROM LOSING GROUP BEFORE
113 2
					if (isset(array_reverse($previousGroups)[$g-1])) array_reverse($previousGroups)[$g-1]->progression($group, 1, 1); // PROGREESS FROM WINNING GROUP BEFORE
114 2
					else $previousLosingGroups[$g]->progression($group, 0, 1); // PROGRESS OTHER TEAM FROM LOSING GROUP BEEFORE
115
				}
116
			}
117
		}
118
		else { // IF THE NUMBER OF TEAMS IS NOT A POWER OF 2, GENERATE GROUPS WITH BYES
119
			// LOOK FOR THE CLOSEST LOWER POWER OF 2
120 2
			$losingByes = $losingGroupTeamsCount-bindec(str_pad(1, strlen(decbin($losingGroupTeamsCount)), 0, STR_PAD_RIGHT));
121 2
			$n = (floor(count($previousLosingGroups)/2)+$losingByes);
122 2
			$byesGroupsNums = [];
123 2
			$byesProgressed = 0;
124 2
			for ($i=0; $i < $losingByes; $i++) {
125 2
				$byesGroupsNums[] = $n-($i*2);
126
			}
127 2
			$lastGroup = 0;
128 2
			for ($g=1; $g <= ((count($previousLosingGroups)/2)+$losingByes); $g++) {
129 2
				$group = $round->group('Round '.$r.' - loss '.$g)->setInGame(2)->setType(\TournamentGenerator\Constants::ROUND_TWO)->setOrder($order);
130 2
				$allGroups[] = $group;
131 2
				$order += 2;
132 2
				$losingGroups[] = $group;
133 2
				$lastLosingGroup = $group; // KEEP THE LAST GROUP FOR FINALE
134 2
				if (in_array($g, $byesGroupsNums) && isset($previousGroups[$byesProgressed])) { // EMPTY GROUP FROM BYE
135 2
					$previousGroups[$byesProgressed]->progression($group, 1, 1); // PROGRESS FROM WINNING GROUP BEFORE
136 2
					$byesProgressed++;
137
				}
138
				else {
139 2
					$previousLosingGroups[$lastGroup]->progression($group, 0, 1); // PROGRESS FROM LOSING GROUP BEFORE
140 2
					if (isset($previousLosingGroups[$lastGroup + 1])) $previousLosingGroups[$lastGroup + 1]->progression($group, 0, 1); // PROGREESS FROM LOSING GROUP BEFORE
141 2
					$lastGroup += 2;
142
				}
143
			}
144
		}
145 2
		return $lastLosingGroup;
146
	}
147
148
}
149