Group::setOrdering()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

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