Passed
Branch develop (f993a2)
by Julien
06:23
created

SingleEliminationTreeGen::chunkAndShuffle()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 15
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 8
nc 3
nop 1
1
<?php
2
3
namespace Xoco70\LaravelTournaments\TreeGen;
4
5
use Illuminate\Support\Collection;
6
use Xoco70\LaravelTournaments\Exceptions\TreeGenerationException;
7
use Xoco70\LaravelTournaments\Models\ChampionshipSettings;
8
use Xoco70\LaravelTournaments\Models\Fight;
9
use Xoco70\LaravelTournaments\Models\FightersGroup;
10
use Xoco70\LaravelTournaments\Models\SingleEliminationFight;
11
use Xoco70\LaravelTournaments\Models\PreliminaryFight;
12
13
14
15
abstract class SingleEliminationTreeGen extends TreeGen
16
{
17
    abstract protected function updateGroupFighters(FightersGroup $group, Fight $fight, $oldFighters);
18
    /**
19
     * Calculate the Byes need to fill the Championship Tree.
20
     *
21
     * @param $fighters
22
     *
23
     * @return Collection
24
     */
25 View Code Duplication
    protected function getByeGroup($fighters)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
26
    {
27
        $fighterCount = $fighters->count();
28
        $firstRoundGroupSize = $this->firstRoundGroupSize();
29
        $treeSize = $this->getTreeSize($fighterCount, $firstRoundGroupSize);
30
        $byeCount = $treeSize - $fighterCount;
31
        return $this->createByeGroup($byeCount);
32
    }
33
34
    /**
35
     * Save Groups with their parent info.
36
     *
37
     * @param int $numRounds
38
     * @param int $numFighters
39
     */
40 View Code Duplication
    protected function pushGroups($numRounds, $numFighters)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
41
    {
42
        // TODO Here is where you should change when enable several winners for preliminary
43
        for ($roundNumber = 2; $roundNumber <= $numRounds + 1; $roundNumber++) {
44
            // From last match to first match
45
            $maxMatches = ($numFighters / pow(2, $roundNumber));
46
47
            for ($matchNumber = 1; $matchNumber <= $maxMatches; $matchNumber++) {
48
                $fighters = $this->createByeGroup(2);
49
                $group = $this->saveGroup($matchNumber, $roundNumber, null);
50
                $this->syncGroup($group, $fighters);
51
            }
52
        }
53
    }
54
55
    /**
56
     * Create empty groups for direct Elimination Tree.
57
     *
58
     * @param $numFighters
59
     */
60
    protected function pushEmptyGroupsToTree($numFighters)
61
    {
62
        if ($this->championship->hasPreliminary()) {
63
            $numFightersElim = $numFighters / $this->championship->getSettings()->preliminaryGroupSize * 2;
64
            // We calculate how much rounds we will have
65
            $numRounds = intval(log($numFightersElim, 2)); // 3 rounds, but begining from round 2 ( ie => 4)
66
            return $this->pushGroups($numRounds, $numFightersElim);
67
        }
68
        // We calculate how much rounds we will have
69
        $numRounds = $this->getNumRounds($numFighters);
70
71
        return $this->pushGroups($numRounds, $numFighters);
72
    }
73
74
75
76
    /**
77
     * Generate First Round Fights.
78
     */
79
    protected function generateFights()
80
    {
81
        //  First Round Fights
82
        $settings = $this->championship->getSettings();
83
        $initialRound = 1;
84
85
        // Very specific case to common case : Preliminary with 3 fighters
86
        if ($this->championship->hasPreliminary() && $settings->preliminaryGroupSize == 3) {
87
            // First we make all first fights of all groups
88
            // Then we make all second fights of all groups
89
            // Then we make all third fights of all groups
90
            $groups = $this->championship->groupsByRound(1)->get();
91
            for ($numFight = 1; $numFight <= $settings->preliminaryGroupSize; $numFight++) {
92
                $fight = new PreliminaryFight();
93
                $fight->saveFights($groups, $numFight);
94
            }
95
            $initialRound++;
96
        }
97
        // Save Next rounds
98
        $fight = new SingleEliminationFight();
99
        $fight->saveFights($this->championship, $initialRound);
100
    }
101
102
    /**
103
     * Return number of rounds for the tree based on fighter count.
104
     *
105
     * @param $numFighters
106
     *
107
     * @return int
108
     */
109
    protected function getNumRounds($numFighters)
110
    {
111
        return intval(log($numFighters / $this->firstRoundGroupSize() * 2, 2));
112
    }
113
114
    private function firstRoundGroupSize()
115
    {
116
        return $this->championship->hasPreliminary()
117
            ? $this->championship->getSettings()->preliminaryGroupSize
118
            : 2;
119
    }
120
121
    protected function generateAllTrees()
122
    {
123
        $this->minFightersCheck();
124
        $usersByArea = $this->getFightersByArea();
125
        $numFighters = count($usersByArea->collapse());
126
        $this->generateGroupsForRound($usersByArea, 1);
127
        $this->pushEmptyGroupsToTree($numFighters); // Abstract
128
        $this->addParentToChildren($numFighters);
129
130
    }
131
132
    /**
133
     * @param Collection $usersByArea
134
     * @param $round
135
     */
136
    public function generateGroupsForRound(Collection $usersByArea, $round)
137
    {
138
        $order = 1;
139
        foreach ($usersByArea as $fightersByEntity) {
140
            // Chunking to make small round robin groups
141
            $chunkedFighters = $this->chunkAndShuffle($fightersByEntity);
142
            foreach ($chunkedFighters as $fighters) {
143
                $fighters = $fighters->pluck('id');
144
                if (!app()->runningUnitTests()) {
145
                    $fighters = $fighters->shuffle();
146
                }
147
                $group = $this->saveGroup($order, $round, null);
148
                $this->syncGroup($group, $fighters);
149
                $order++;
150
            }
151
        }
152
    }
153
154
    /**
155
     * Check if there is enough fighters, throw exception otherwise
156
     * @throws TreeGenerationException
157
     */
158
    private function minFightersCheck()
159
    {
160
        $fighters = $this->getFighters();
161
        $areas = $this->settings->fightingAreas;
162
        $fighterType = $this->settings->isTeam
163
            ? trans_choice('.team', 2)
164
            : trans_choice('laravel-tournaments::core.competitor', 2);
165
166
        $minFighterCount = $fighters->count() / $areas;
167
168
169
        if ($this->settings->hasPreliminary && $fighters->count() / ($this->settings->preliminaryGroupSize * $areas) < 1) {
170
            throw new TreeGenerationException(trans('laravel-tournaments::core.min_competitor_required', [
171
                'number' => $this->settings->preliminaryGroupSize * $areas,
172
                'fighter_type' => $fighterType
173
            ]));
174
        }
175
176
        if ($minFighterCount < ChampionshipSettings::MIN_COMPETITORS_BY_AREA) {
177
            throw new TreeGenerationException(trans('laravel-tournaments::core.min_competitor_required', [
178
                'number' => ChampionshipSettings::MIN_COMPETITORS_BY_AREA,
179
                'fighter_type' => $fighterType
180
            ]));
181
        }
182
    }
183
}
184