Completed
Push — master ( 5d9f44...4b85e8 )
by Tomáš
03:31
created

DoubleElimination::generateLosingSide()   B

Complexity

Conditions 11
Paths 7

Size

Total Lines 49
Code Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 38
nc 7
nop 6
dl 0
loc 49
rs 7.3166
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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