Completed
Branch master (0e4a87)
by Tomáš
03:29 queued 01:30
created

Generator::getSkip()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
rs 10
c 1
b 0
f 0
1
<?php
2
3
namespace TournamentGenerator;
4
5
/**
6
 *
7
 */
8
class Generator
9
{
10
11
	private $group = null;
12
	private $type = /** @scrutinizer ignore-all */ R_R; // TYPE OF ROUND TO CREATE A LAYOUT
1 ignored issue
show
Bug introduced by
The constant TournamentGenerator\R_R was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
13
	private $inGame = 2; // NUMBER OF TEAMS IN ONE GAME - 2/3/4
14
	private $maxSize = 4; // MAX SIZE OF GROUP BEFORE SPLIT
15
	private $allowSkip = false; // IF IS NUMBER OF TEAMS LESS THAN $this->inGame THEN SKIP PLAYING THIS GROUP
16
	private $games = []; // ARRAY OF GAME OBJECTS
17
18
	function __construct(Group $group) {
19
		$this->group = $group;
20
	}
21
22
	public function allowSkip(){
23
		$this->allowSkip = true;
24
		return $this;
25
	}
26
	public function disallowSkip(){
27
		$this->allowSkip = false;
28
		return $this;
29
	}
30
	public function setSkip(bool $skip) {
31
		$this->allowSkip = $skip;
32
		return $this;
33
	}
34
	public function getSkip() {
35
		return $this->allowSkip;
36
	}
37
38
39
	public function setType(/** @scrutinizer ignore-all */ string $type = R_R) {
1 ignored issue
show
Bug introduced by
The constant TournamentGenerator\R_R was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
40
		if (in_array($type, groupTypes)) $this->type = $type;
41
		else throw new \Exception('Unknown group type: '.$type);
42
		return $this;
43
	}
44
	public function getType() {
45
		return $this->type;
46
	}
47
48
	public function setInGame(int $inGame) {
49
		if ($inGame < 2 ||  $inGame > 4) throw new \Exception('Expected 2,3 or 4 as inGame '.$inGame.' given');
50
		$this->inGame = $inGame;
51
		return $this;
52
	}
53
	public function getInGame() {
54
		return $this->inGame;
55
	}
56
57
	public function setMaxSize(int $maxSize) {
58
		if ($maxSize < 2) throw new \Exception('Max group size has to be at least 2, '.$maxSize.' given');
59
		$this->maxSize = $maxSize;
60
		return $this;
61
	}
62
	public function getMaxSize() {
63
		return $this->maxSite;
0 ignored issues
show
Bug introduced by
The property maxSite does not exist on TournamentGenerator\Generator. Did you mean maxSize?
Loading history...
64
	}
65
66
	public function genGames() {
67
		switch ($this->type) {
68
			case R_R:{
1 ignored issue
show
Bug introduced by
The constant TournamentGenerator\R_R was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
69
					$this->group->addGame($this->r_rGames());
70
				break;}
71
			case TWO_TWO:
1 ignored issue
show
Bug introduced by
The constant TournamentGenerator\TWO_TWO was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
72
				$teams = $this->group->getTeams();
73
				$discard = [];
74
				shuffle($teams);
75
				$count = count($teams);
76
				while (count($teams) % $this->inGame !== 0) { $discard[] = array_shift($teams); }
77
78
				while (count($teams) > 0) {
79
					$tInGame = [];
80
					for ($i=0; $i < $this->inGame; $i++) { $tInGame[] = array_shift($teams); }
81
					$this->group->game($tInGame);
82
				}
83
84
				if (count($discard) > 0 && !$this->allowSkip) throw new \Exception('Couldn\'t make games with all teams. Expected k*'.$this->inGame.' teams '.$count.' teams given - discarting '.count($discard).' teams ('.implode(', ', $discard).') in group '.$this.' - allow skip '.($this->allowSkip ? 'True' : 'False'));
0 ignored issues
show
Bug introduced by
Are you sure $this of type TournamentGenerator\Generator can be used in concatenation? Consider adding a __toString()-method. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

84
				if (count($discard) > 0 && !$this->allowSkip) throw new \Exception('Couldn\'t make games with all teams. Expected k*'.$this->inGame.' teams '.$count.' teams given - discarting '.count($discard).' teams ('.implode(', ', $discard).') in group './** @scrutinizer ignore-type */ $this.' - allow skip '.($this->allowSkip ? 'True' : 'False'));
Loading history...
85
				break;
86
			case COND_SPLIT:
1 ignored issue
show
Bug introduced by
The constant TournamentGenerator\COND_SPLIT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
87
				$games = [];
88
				$teams = $this->group->getTeams();
89
				if (count($teams) > $this->maxSize) {
90
					$groups = array_chunk($teams, /** @scrutinizer ignore-type */ ceil(count($teams)/ceil(count($teams)/$this->maxSize))); // SPLIT TEAMS INTO GROUP OF MAXIMUM SIZE OF $this->maxSize
91
					foreach ($groups as $group) { $games[] = $this->r_rGames($group); }
92
					$g = 0;
93
					foreach ($games as $group) {
94
						$g += count($group);
95
					}
96
					while ($g > 0) {
97
						foreach ($games as $key => $group) {
98
							$this->group->addGame(array_shift($games[$key]));
99
							if (count($games[$key]) === 0) unset($games[$key]);
100
							$g--;
101
						}
102
					}
103
				}
104
				else $this->group->addGame($this->r_rGames());
105
				break;
106
		}
107
		return $this->group->getGames();
108
	}
109
	public function r_rGames(array $teams = []) {
110
		$games = [];
111
		if (count($teams) === 0) $teams = $this->group->getTeams();
112
		switch ($this->inGame) {
113
			case 2:
114
				$games = Generator::circle_genGames2($teams, $this->group);
115
				break;
116
			case 3:{
117
				$teamsB = $teams;
118
				while (count($teamsB) >= 3) {
119
					$lockedTeam = array_shift($teamsB);
120
					$gamesTemp = Generator::circle_genGames2($teamsB, $this->group);
121
					foreach ($gamesTemp as $game) {
122
						$game->addTeam($lockedTeam);
123
					}
124
					$games = array_merge($games, $gamesTemp);
125
				}
126
				break;}
127
			case 4:{
128
				$teamsB = $teams;
129
				$lockedTeam1 = array_shift($teamsB);
130
				while (count($teamsB) >= 4) {
131
					$teamsB2 = $teamsB;
132
					while (count($teamsB2) >= 3) {
133
						$lockedTeam2 = array_shift($teamsB2);
134
						$gamesTemp = Generator::circle_genGames2($teamsB2, $this->group);
135
						foreach ($gamesTemp as $game) {
136
							$game->addTeam($lockedTeam1, $lockedTeam2);
137
						}
138
						$games = array_merge($games, $gamesTemp);
139
					}
140
					$lockedTeam1 = array_shift($teamsB);
141
				}
142
				$games[] = new Game(array_merge([$lockedTeam1], $teamsB), $this->group);
143
				break;}
144
		}
145
		return $games;
146
	}
147
148
	public function orderGames() {
149
150
		$games = $this->group->getGames();
151
152
		if (count($games) <= 4) return $games;
153
154
		$this->games = [];
155
156
		// TEAMS FROM LAST 3 GAMES
157
		// 1 = PLAYED ONLY 3RD GAME FROM END
158
		// 2 = PLAYED ONLY 2ND GAME FROM END
159
		// 3 = PLAYED 3RD AND 2ND GAME FROM END
160
		// 4 = PLAYED ONLY THE LAST GAME
161
		// 5 = PLAYED 3RD AND 1ST GAME FROM END
162
		// 6 = PLAYED 2ND ANS 1ST GAME FROM END
163
		// 7 = PLAYED ALL 3 LAST GAMES
164
		$teams = [];
165
		foreach ($this->group->getTeams() as $team) { $teams[$team->id] = 0; }
166
167
		$this->moveCalculatedGames(array_shift($games), $teams);
168
169
		while (count($games) > 0) {
170
			$found = false;
171
172
			// CYCLE 1
173
			// TEAM WHICH DIDN'T PLAY IN LAST GAME (< 4)
174
			foreach ($games as $key => $game) {
175
				if ($this->orderCheckTeamsVal($game, $teams, [4,5,6,7])) {
176
					$this->moveCalculatedGames($game,$teams);
177
					unset($games[$key]);
178
					$found = true;
179
					break;
180
				}
181
			}
182
			if ($found) continue;
183
184
			// CYCLE 2
185
			// ! TEAM WHICH PLAYED IN LAST TWO GAMES (NOT 6 or 7)
186
			foreach ($games as $key => $game) {
187
				if ($this->orderCheckTeamsVal($game, $teams, [6,7])) {
188
					$this->moveCalculatedGames($game,$teams);
189
					unset($games[$key]);
190
					$found = true;
191
					break;
192
				}
193
			}
194
			if ($found) continue;
195
196
			// CYCLE 3
197
			// NOT TEAM WHICH PLAYED IN LAST THREE GAMES (NOT 7)
198
			// TEAMS THAT DIDN'T PLAY IN LAST GAME WILL PLAY THIS GAME (< 4)
199
			foreach ($games as $key => $game) {
200
				if ($this->orderCheckTeamsVal($game, $teams, [7], [1,2,3])) {
201
					$this->moveCalculatedGames($game,$teams);
202
					unset($games[$key]);
203
					$found = true;
204
					break;
205
				}
206
			}
207
			if ($found) continue;
208
209
			// CYCLE 4
210
			// NOT TEAM WHICH PLAYED IN LAST THREE GAMES (NOT 7)
211
			foreach ($games as $key => $game) {
212
				if ($this->orderCheckTeamsVal($game, $teams, [7])) {
213
					$this->moveCalculatedGames($game,$teams);
214
					unset($games[$key]);
215
					$found = true;
216
					break;
217
				}
218
			}
219
			if ($found) continue;
220
221
			// CYCLE 5
222
			// TEAMS THAT DIDN'T PLAY IN LAST GAME WILL PLAY THIS GAME (< 4)
223
			foreach ($games as $key => $game) {
224
				if ($this->orderCheckTeamsVal($game, $teams, [], [1,2,3])) {
225
					$this->moveCalculatedGames($game,$teams);
226
					unset($games[$key]);
227
					$found = true;
228
					break;
229
				}
230
			}
231
			if ($found) continue;
232
233
			// CYCLE 6
234
			// FIRST AVAILABLE GAME
235
			$this->moveCalculatedGames(array_shift($games),$teams);
236
		}
237
238
		return $this->games;
239
	}
240
	private function moveCalculatedGames(Game $game, array &$teams) {
241
242
		$this->games[] = $game;
243
244
		foreach (end($this->games)->getTeamsIds() as $tid) {
245
			$teams[$tid] += 4;
246
		}
247
248
		if (count($this->games) > 1) {
249
			foreach (prev($this->games)->getTeamsIds() as $tid) {
250
				$teams[$tid] -= 2;
251
			}
252
		}
253
		if (count($this->games) > 2) {
254
			foreach (prev($this->games)->getTeamsIds() as $tid) {
255
				$teams[$tid] -= 1;
256
			}
257
		}
258
		if (count($this->games) > 3) {
259
			foreach (prev($this->games)->getTeamsIds() as $tid) {
260
				$teams[$tid] -= 1;
261
			}
262
		}
263
264
		return $teams;
265
266
	}
267
	private function orderCheckTeamsVal(Game $game, array &$teams, array $checkVals, array $required = []) {
268
269
		$requiredTeams = array_filter($teams, function($a) use ($required) { return in_array($a, $required); });
270
271
		foreach ($game->getTeamsIds() as $tid) {
272
			if (in_array($teams[$tid], $checkVals)) return false;
273
			if (isset($requiredTeams[$tid])) unset($requiredTeams[$tid]);
274
		}
275
276
		if (count($requiredTeams) > 0) return false;
277
278
		return true;
279
280
	}
281
282
	// GENERATES A ROBIN-ROBIN BRACKET
283
	public static function circle_genGames2(array $teams = [], Group $group = null) {
284
		$bracket = []; // ARRAY OF GAMES
285
286
		if (count($teams) % 2 != 0) $teams[] = DUMMY_TEAM; // IF NOT EVEN NUMBER OF TEAMS, ADD DUMMY
287
288
		shuffle($teams); // SHUFFLE TEAMS FOR MORE RANDOMNESS
289
290
		for ($i=0; $i < count($teams)-1; $i++) {
291
			$bracket = array_merge($bracket, Generator::circle_saveBracket($teams, $group)); // SAVE CURRENT ROUND
292
293
			$teams = Generator::circle_rotateBracket($teams); // ROTATE TEAMS IN BRACKET
294
		}
295
296
		return $bracket;
297
298
	}
299
	// CREATE GAMES FROM BRACKET
300
	public static function circle_saveBracket(array $teams, Group $group = null) {
301
302
		$bracket = [];
303
304
		for ($i=0; $i < count($teams)/2; $i++) { // GO THROUGH HALF OF THE TEAMS
305
306
			$home = $teams[$i];
307
			$reverse = array_reverse($teams);
308
			$away = $reverse[$i];
309
310
			if (($home == DUMMY_TEAM || $away == DUMMY_TEAM)) continue; // SKIP WHEN DUMMY_TEAM IS PRESENT
311
312
			$bracket[] = new Game([$home, $away], $group);
0 ignored issues
show
Bug introduced by
It seems like $group can also be of type null; however, parameter $group of TournamentGenerator\Game::__construct() does only seem to accept TournamentGenerator\Group, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

312
			$bracket[] = new Game([$home, $away], /** @scrutinizer ignore-type */ $group);
Loading history...
313
314
		}
315
316
		return $bracket;
317
318
	}
319
	// ROTATE TEAMS IN BRACKET
320
	public static function circle_rotateBracket(array $teams) {
321
322
		$first = array_shift($teams); // THE FIRST TEAM REMAINS FIRST
323
		$last = array_shift($teams); // THE SECOND TEAM MOVES TO LAST PLACE
324
325
		$teams = array_merge([$first], $teams, [$last]); // MERGE BACK TOGETHER
326
327
		return $teams;
328
329
	}
330
331
}
332