Completed
Push — master ( a743c4...8e8354 )
by Tomáš
02:35
created

Group::getProgressions()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 2
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace TournamentGenerator;
4
5
use Exception;
6
use TournamentGenerator\Containers\GameContainer;
7
use TournamentGenerator\Containers\HierarchyContainer;
8
use TournamentGenerator\Containers\TeamContainer;
9
use TournamentGenerator\Interfaces\WithGames;
10
use TournamentGenerator\Interfaces\WithGeneratorSetters;
11
use TournamentGenerator\Interfaces\WithSkipSetters;
12
use TournamentGenerator\Interfaces\WithTeams;
13
use TournamentGenerator\Traits\WithGames as WithGamesTrait;
14
use TournamentGenerator\Traits\WithTeams as WithTeamsTrait;
15
16
/**
17
 * Tournament group
18
 *
19
 * Group is a collection of teams that play against each other. It defaults to Round-robin group where one team plays against every other team in a group.
20
 * Group can also be setup in such a way that teams play only one game against one other team (randomly selected). Teams from groups can be progressed (moved) to other groups.
21
 *
22
 * @package TournamentGenerator
23
 * @author  Tomáš Vojík <[email protected]>
24
 * @since   0.1
25
 */
26
class Group extends HierarchyBase implements WithGeneratorSetters, WithSkipSetters, WithTeams, WithGames
27
{
28
	use WithTeamsTrait;
29
	use WithGamesTrait;
30
31
	/** @var Helpers\Generator Generator class to generate games of this group */
32
	protected Helpers\Generator $generator;
33
	/** @var string[]|int[] List of already progressed teams' id */
34
	protected array $progressed = [];
35
	/** @var string Ordering parameter */
36
	protected string $ordering = Constants::POINTS;
37
	/** @var Progression[] List of progressions from this group */
38
	protected array $progressions = [];
39
	/** @var int Points acquired for winning */
40
	protected int $winPoints = 3;
41
	/** @var int Points acquired from draw */
42
	protected int $drawPoints = 1;
43
	/** @var int Points acquired from loss */
44
	protected int $lostPoints = 0;
45
	/** @var int Points acquired from placing second (only for 3 or 4 teams in one game) */
46
	protected int $secondPoints = 2;
47
	/** @var int Points acquired from placing third (only for 4 teams in one game) */
48
	protected int $thirdPoints = 1;
49
	/**
50
	 * @var int Points acquired from progressing to the next round
51
	 * @details This can be useful when getting the total team order in a tournament. Ex: If you consider teams that progressed to the next round to be higher on the scoreboard even if they got less points in total than some other teams that did not progressed.
52
	 */
53
	protected int $progressPoints = 50;
54
	/** @var int Group order in a round */
55
	protected int $order = 0;
56
57
	/**
58
	 * Group constructor.
59
	 *
60
	 * @param string          $name Group name
61
	 * @param string|int|null $id   Group id - if omitted -> it is generated automatically as unique string
62
	 */
63 216
	public function __construct(string $name, $id = null) {
64 216
		$this->setName($name);
65 216
		$this->generator = new Helpers\Generator($this);
66 216
		$this->setId($id ?? uniqid('', false));
67 216
		$this->games = new GameContainer($this->id);
68 216
		$this->teams = new TeamContainer($this->id);
69 216
		$this->container = new HierarchyContainer($this->id);
70 216
	}
71
72
	/**
73
	 * Add one or more teams into the object.
74
	 *
75
	 * @param Team ...$teams Team objects
76
	 *
77
	 * @return $this
78
	 * @throws Exception
79
	 */
80 150
	public function addTeam(Team ...$teams) : Group {
81 150
		foreach ($teams as $team) {
82 150
			$this->teams->insert($team);
83 150
			$team->addGroupResults($this);
84
		}
85 150
		return $this;
86
	}
87
88
	/**
89
	 * Create a new team and add it into the object
90
	 *
91
	 * @param string          $name Name of the new team
92
	 * @param string|int|null $id   Id of the new team - if omitted -> it is generated automatically as unique string
93
	 *
94
	 * @return Team Newly created team
95
	 * @throws Exception
96
	 */
97 86
	public function team(string $name = '', $id = null) : Team {
98 86
		$t = new Team($name, $id);
99 86
		$this->teams->insert($t);
100 86
		$t->addGroupResults($this);
101 86
		return $t;
102
	}
103
104
	/**
105
	 * Allows round skipping
106
	 *
107
	 * @return $this
108
	 */
109 1
	public function allowSkip() : Group {
110 1
		$this->generator->allowSkip();
111 1
		return $this;
112
	}
113
114
	/**
115
	 * Set round skipping
116
	 *
117
	 * @param bool $skip
118
	 *
119
	 * @return $this
120
	 */
121 1
	public function disallowSkip() : Group {
122 1
		$this->generator->disallowSkip();
123 1
		return $this;
124
	}
125
126
	/**
127
	 * Set round skipping
128
	 *
129
	 * @param bool $skip
130
	 *
131
	 * @return $this
132
	 */
133 129
	public function setSkip(bool $skip) : Group {
134 129
		$this->generator->setSkip($skip);
135 129
		return $this;
136
	}
137
138
	/**
139
	 * Getter for round skipping
140
	 *
141
	 * @return bool
142
	 */
143 6
	public function getSkip() : bool {
144 6
		return $this->generator->getSkip();
145
	}
146
147
	/**
148
	 * Get points for winning
149
	 *
150
	 * @return int
151
	 */
152 83
	public function getWinPoints() : int {
153 83
		return $this->winPoints;
154
	}
155
156
	/**
157
	 * Set points for winning
158
	 *
159
	 * @param int $points
160
	 *
161
	 * @return $this
162
	 */
163 2
	public function setWinPoints(int $points) : Group {
164 2
		$this->winPoints = $points;
165 2
		return $this;
166
	}
167
168
	/**
169
	 * Get points for draw
170
	 *
171
	 * @return int
172
	 */
173 21
	public function getDrawPoints() : int {
174 21
		return $this->drawPoints;
175
	}
176
177
	/**
178
	 * Set points for draw
179
	 *
180
	 * @param int $points
181
	 *
182
	 * @return $this
183
	 */
184 2
	public function setDrawPoints(int $points) : Group {
185 2
		$this->drawPoints = $points;
186 2
		return $this;
187
	}
188
189
	/**
190
	 * Get points for losing
191
	 *
192
	 * @return int
193
	 */
194 83
	public function getLostPoints() : int {
195 83
		return $this->lostPoints;
196
	}
197
198
	/**
199
	 * Set points for losing
200
	 *
201
	 * @param int $points
202
	 *
203
	 * @return $this
204
	 */
205 2
	public function setLostPoints(int $points) : Group {
206 2
		$this->lostPoints = $points;
207 2
		return $this;
208
	}
209
210
	/**
211
	 * Get points for being second
212
	 *
213
	 * @return int
214
	 */
215 17
	public function getSecondPoints() : int {
216 17
		return $this->secondPoints;
217
	}
218
219
	/**
220
	 * Set points for being second
221
	 *
222
	 * @param int $points
223
	 *
224
	 * @return $this
225
	 */
226 2
	public function setSecondPoints(int $points) : Group {
227 2
		$this->secondPoints = $points;
228 2
		return $this;
229
	}
230
231
	/**
232
	 * Get points for being third
233
	 *
234
	 * @return int
235
	 */
236 16
	public function getThirdPoints() : int {
237 16
		return $this->thirdPoints;
238
	}
239
240
	/**
241
	 * Set points for being third
242
	 *
243
	 * @param int $points
244
	 *
245
	 * @return $this
246
	 */
247 2
	public function setThirdPoints(int $points) : Group {
248 2
		$this->thirdPoints = $points;
249 2
		return $this;
250
	}
251
252
	/**
253
	 * Get points for progression
254
	 *
255
	 * @return int
256
	 */
257 21
	public function getProgressPoints() : int {
258 21
		return $this->progressPoints;
259
	}
260
261
	/**
262
	 * Set points for progression
263
	 *
264
	 * @param int $points
265
	 *
266
	 * @return Group
267
	 */
268 2
	public function setProgressPoints(int $points) : Group {
269 2
		$this->progressPoints = $points;
270 2
		return $this;
271
	}
272
273
	/**
274
	 * Set maximum group size
275
	 *
276
	 * Does not disallow adding teams to this round!
277
	 * This can be used to split teams in a group into "subgroups" if you need to limit the maximum number of games.
278
	 *
279
	 * @param int $size
280
	 *
281
	 * @return $this
282
	 * @throws Exception
283
	 */
284 8
	public function setMaxSize(int $size) : Group {
285 8
		$this->generator->setMaxSize($size);
286 7
		return $this;
287
	}
288
289
	/**
290
	 * Get the maximum group size
291
	 *
292
	 * Does not disallow adding teams to this round!
293
	 * This can be used to split teams in a group into "subgroups" if you need to limit the maximum number of games.
294
	 *
295
	 * @return int
296
	 */
297 5
	public function getMaxSize() : int {
298 5
		return $this->generator->getMaxSize();
299
	}
300
301
	/**
302
	 * Set group type
303
	 *
304
	 * @param string $type
305
	 *
306
	 * @return $this
307
	 * @throws Exception
308
	 * @see Constants::GroupTypes
309
	 *
310
	 */
311 21
	public function setType(string $type = Constants::ROUND_ROBIN) : Group {
312 21
		$this->generator->setType($type);
313 21
		return $this;
314
	}
315
316
	/**
317
	 * Get group type
318
	 *
319
	 * @return string
320
	 * @see Constants::GroupTypes
321
	 *
322
	 */
323 5
	public function getType() : string {
324 5
		return $this->generator->getType();
325
	}
326
327
	/**
328
	 * Get group order
329
	 *
330
	 * @return int
331
	 */
332 3
	public function getOrder() : int {
333 3
		return $this->order;
334
	}
335
336
	/**
337
	 * Set group order
338
	 *
339
	 * @param int $order
340
	 *
341
	 * @return $this
342
	 */
343 4
	public function setOrder(int $order) : Group {
344 4
		$this->order = $order;
345 4
		return $this;
346
	}
347
348
	/**
349
	 * Get parameter to order the teams by
350
	 *
351
	 * @return string
352
	 */
353 1
	public function getOrdering() : string {
354 1
		return $this->ordering;
355
	}
356
357
	/**
358
	 * Set parameter to order the teams by
359
	 *
360
	 * @param string $ordering
361
	 *
362
	 * @return $this
363
	 * @throws Exception
364
	 * @see Constants::OrderingTypes
365
	 *
366
	 */
367 1
	public function setOrdering(string $ordering = Constants::POINTS) : Group {
368 1
		if (!in_array($ordering, Constants::OrderingTypes, true)) {
369 1
			throw new Exception('Unknown group ordering: '.$ordering);
370
		}
371 1
		$this->ordering = $ordering;
372 1
		return $this;
373
	}
374
375
	/**
376
	 * Set how many teams play in one game
377
	 *
378
	 * @param int $inGame 2 / 3 / 4
379
	 *
380
	 * @return $this
381
	 * @throws Exception
382
	 */
383 54
	public function setInGame(int $inGame) : Group {
384 54
		$this->generator->setInGame($inGame);
385 54
		return $this;
386
	}
387
388
	/**
389
	 * Get how many teams play in one game
390
	 *
391
	 * @return int
392
	 */
393 82
	public function getInGame() : int {
394 82
		return $this->generator->getInGame();
395
	}
396
397
	/**
398
	 * Add a progression to this group
399
	 *
400
	 * @param Progression $progression
401
	 *
402
	 * @return $this
403
	 */
404 1
	public function addProgression(Progression $progression) : Group {
405 1
		$this->progressions[] = $progression;
406 1
		return $this;
407
	}
408
409
	/**
410
	 * Creates a new progression from this group
411
	 *
412
	 * Progression uses a similar syntax to php's array_slice() function.
413
	 *
414
	 * @param Group    $to     Which group to progress to
415
	 * @param int      $offset First index
416
	 * @param int|null $len    Maximum number of teams to progress
417
	 *
418
	 * @return Progression
419
	 *
420
	 * @see https://www.php.net/manual/en/function.array-slice.php
421
	 */
422 34
	public function progression(Group $to, int $offset = 0, int $len = null) : Progression {
423 34
		$p = new Progression($this, $to, $offset, $len);
424 34
		$this->progressions[] = $p;
425 34
		return $p;
426
	}
427
428
	/**
429
	 * Progress all teams using already setup progression
430
	 *
431
	 * @pre  All progressions are setup
432
	 * @post All teams have been moved into their next groups
433
	 *
434
	 * @param bool $blank If true - create dummy teams instead of progressing the real objects
435
	 *
436
	 * @return $this
437
	 * @throws Exception
438
	 */
439 33
	public function progress(bool $blank = false) : Group {
440 33
		foreach ($this->progressions as $progression) {
441 33
			$progression->progress($blank);
442
		}
443 33
		return $this;
444
	}
445
446
	/**
447
	 * Add teams to the `progressed` list
448
	 *
449
	 * @param Team[] $teams
450
	 *
451
	 * @return $this
452
	 */
453 34
	public function addProgressed(Team ...$teams) : Group {
454 34
		$this->progressed = array_merge($this->progressed, array_map(static function($a) {
455 34
			return $a->getId();
456 34
		}, $teams));
457 34
		return $this;
458
	}
459
460
	/**
461
	 * Check if a given team is progressed from this group
462
	 *
463
	 * @param Team $team
464
	 *
465
	 * @return bool
466
	 */
467 7
	public function isProgressed(Team $team) : bool {
468 7
		return in_array($team->getId(), $this->progressed, true);
469
	}
470
471
	/**
472
	 * Generate all games
473
	 *
474
	 * @return array
475
	 * @throws Exception
476
	 */
477 44
	public function genGames() : array {
478 44
		return $this->generator->genGames();
479
	}
480
481
	/**
482
	 * Create a new game and add it to the group
483
	 *
484
	 * @param Team[] $teams Teams that are playing
485
	 *
486
	 * @post The game's id is set to the current auto-incremented value
487
	 *
488
	 * @return Game
489
	 * @throws Exception
490
	 */
491 141
	public function game(array $teams = []) : Game {
492 141
		$g = new Game($teams, $this);
493 141
		$g->setId($this->games->getAutoIncrement());
494 141
		$this->games->incrementId();
495 141
		$this->games->insert($g);
496 141
		return $g;
497
	}
498
499
	/**
500
	 * Add games to this group
501
	 *
502
	 * @param Game[] $games
503
	 *
504
	 * @post The games' id is set to the current auto-incremented value
505
	 *
506
	 * @return $this
507
	 * @throws Exception
508
	 */
509 38
	public function addGame(Game ...$games) : Group {
510 38
		$this->games->insert(...$games);
511
		// Set the game id's
512 38
		foreach ($games as $game) {
513 38
			$game->setId($this->games->getAutoIncrement());
514
			/** @noinspection DisconnectedForeachInstructionInspection */
515 38
			$this->games->incrementId();
516
		}
517 38
		return $this;
518
	}
519
520
	/**
521
	 * Order generated games to minimize teams playing multiple games after one other.
522
	 *
523
	 * @post The game ids are reset according to their new order
524
	 *
525
	 * @return Game[]
526
	 * @throws Exception
527
	 */
528 5
	public function orderGames() : array {
529 5
		if (count($this->games) < 5) {
530 1
			return $this->games->get();
531
		}
532 4
		$this->games->resetAutoIncrement();
533 4
		return $this->generator->orderGames();
534
	}
535
536
	/**
537
	 * Simulate all games in this group as they would be played for real
538
	 *
539
	 * @param TeamFilter[]|TeamFilter[][] $filters Filters to teams returned from the group
540
	 * @param bool                        $reset   If true - the scores will be reset after simulation
541
	 *
542
	 * @return Team[]
543
	 * @throws Exception
544
	 */
545 31
	public function simulate(array $filters = [], bool $reset = true) : array {
546 31
		return Helpers\Simulator::simulateGroup($this, $filters, $reset);
547
	}
548
549
	/**
550
	 * Reset all game results as if they were not played
551
	 *
552
	 * @post All games in this group are marked as "not played"
553
	 * @post All scores in this group are deleted
554
	 *
555
	 * @return $this
556
	 * @throws Exception
557
	 */
558 15
	public function resetGames() : Group {
559 15
		foreach ($this->getGames() as $game) {
560 15
			$game->resetResults();
561
		}
562 15
		return $this;
563
	}
564
565
	/**
566
	 * Check if all games in this group has been played
567
	 *
568
	 * @return bool
569
	 */
570 35
	public function isPlayed() : bool {
571 35
		if (count($this->games) === 0) {
572 10
			return false;
573
		}
574 30
		return count(array_filter($this->getGames(), static function($a) {
575 30
				return $a->isPlayed();
576 30
			})) !== 0;
577
	}
578
579
	/**
580
	 * Get all progressions
581
	 *
582
	 * @return Progression[]
583
	 */
584 4
	public function getProgressions() : array {
585 4
		return $this->progressions;
586
	}
587
588
}
589