Completed
Push — master ( 886ef9...fa35c6 )
by Julien
06:43
created

assignFightersToBracket()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 26
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 26
rs 8.5806
c 0
b 0
f 0
cc 4
eloc 19
nc 6
nop 2
1
<?php
2
3
namespace Xoco70\LaravelTournaments\TreeGen;
4
5
use Xoco70\LaravelTournaments\Models\Competitor;
6
use Xoco70\LaravelTournaments\Models\Team;
7
8
class CreateSingleEliminationTree
9
{
10
    public $firstRoundName;
11
    public $groupsByRound;
12
    public $hasPreliminary;
13
    public $brackets = [];
14
    public $championship;
15
    public $numFighters;
16
    public $noRounds;
17
    public $playerWrapperHeight = 30;
18
    public $matchWrapperWidth = 150;
19
    public $roundSpacing = 40;
20
    public $matchSpacing = 42;
21
    public $borderWidth = 3;
22
23
    public function __construct($groupsByRound, $championship, $hasPreliminary)
24
    {
25
        $this->championship = $championship;
26
        $this->groupsByRound = $groupsByRound;
27
        $this->hasPreliminary = $hasPreliminary;
28
29
        $this->firstRoundName = $groupsByRound->first()->map(function ($item) use ($championship) {
30
            $fighters = $item->getFightersWithBye();
31
            $fighter1 = $fighters->get(0);
32
            $fighter2 = $fighters->get(1);
33
34
            return [$fighter1, $fighter2];
35
        })->flatten()->all();
36
    }
37
38
    public function build()
39
    {
40
        $fighters = $this->firstRoundName;
41
        $this->numFighters = count($fighters);
42
43
        //Calculate the size of the first full round - for example if you have 5 fighters, then the first full round will consist of 4 fighters
44
        $this->noRounds = log($this->numFighters, 2);
45
        $roundNumber = 1;
46
47
        //Group 2 fighters into a match
48
        $matches = array_chunk($fighters, 2);
49
50
        //If there's already a match in the match array, then that means the next round is round 2, so increase the round number
51
        if (count($this->brackets)) {
52
            $roundNumber++;
53
        }
54
        $countMatches = count($matches);
55
        //Create the first full round of fighters, some may be blank if waiting on the results of a previous round
56
        for ($i = 0; $i < $countMatches; $i++) {
57
            $this->brackets[$roundNumber][$i + 1] = $matches[$i];
58
        }
59
60
        //Create the result of the empty rows for this tournament
61
        $this->assignFightersToBracket($roundNumber, $this->hasPreliminary);
62
        $this->assignPositions();
63
64
        if ($this->numFighters >= $this->championship->getGroupSize() * 2) {
65
            $this->brackets[$this->noRounds][2]['matchWrapperTop'] = $this->brackets[$this->noRounds][1]['matchWrapperTop'] + 100;
66
        }
67
    }
68
69
    private function assignPositions()
70
    {
71
72
        //Variables required for figuring outing the height of the vertical connectors
73
74
        $spaceFactor = 0.5;
75
        $playerHeightFactor = 1;
76
        foreach ($this->brackets as $roundNumber => &$round) {
77
            foreach ($round as $matchNumber => &$match) {
78
79
                //Give teams a nicer index
80
81
                $match['playerA'] = $match[0];
82
                $match['playerB'] = $match[1];
83
                $match['winner_id'] = $match[2];
84
85
                unset($match[0]);
86
                unset($match[1]);
87
                unset($match[2]);
88
89
                //Figure out the bracket positions
90
91
                $match['matchWrapperTop'] = (((2 * $matchNumber) - 1) * (pow(2, ($roundNumber) - 1)) - 1) * (($this->matchSpacing / 2) + $this->playerWrapperHeight);
92
                $match['matchWrapperLeft'] = ($roundNumber - 1) * ($this->matchWrapperWidth + $this->roundSpacing - 1);
93
                $match['vConnectorLeft'] = floor($match['matchWrapperLeft'] + $this->matchWrapperWidth + ($this->roundSpacing / 2) - ($this->borderWidth / 2));
94
                $match['vConnectorHeight'] = ($spaceFactor * $this->matchSpacing) + ($playerHeightFactor * $this->playerWrapperHeight) + $this->borderWidth;
95
                $match['vConnectorTop'] = $match['hConnectorTop'] = $match['matchWrapperTop'] + $this->playerWrapperHeight;
96
                $match['hConnectorLeft'] = ($match['vConnectorLeft'] - ($this->roundSpacing / 2)) + 2;
97
                $match['hConnector2Left'] = $match['matchWrapperLeft'] + $this->matchWrapperWidth + ($this->roundSpacing / 2);
98
99
                //Adjust the positions depending on the match number
100
101
                if (!($matchNumber % 2)) {
102
                    $match['hConnector2Top'] = $match['vConnectorTop'] -= ($match['vConnectorHeight'] - $this->borderWidth);
103
                } else {
104
                    $match['hConnector2Top'] = $match['vConnectorTop'] + ($match['vConnectorHeight'] - $this->borderWidth);
105
                }
106
            }
107
108
            //Update the spacing variables
109
110
            $spaceFactor *= 2;
111
            $playerHeightFactor *= 2;
112
        }
113
    }
114
115
    /**
116
     * returns titles depending of number of rounds
117
     * @return array
118
     */
119
    public function getRoundTitles()
120
    {
121
        $semiFinalTitles = ['Semi-Finals', 'Final'];
122
        $quarterFinalTitles = ['Quarter-Finals', 'Semi-Finals', 'Final'];
123
        $roundTitle = [
124
            2 => ['Final'],
125
            3 => $semiFinalTitles,
126
            4 => $semiFinalTitles,
127
            5 => $semiFinalTitles,
128
            6 => $quarterFinalTitles,
129
            7 => $quarterFinalTitles,
130
            8 => $quarterFinalTitles,
131
132
        ];
133
        if ($this->numFighters > 8) {
134
            $roundTitles = ['Quarter-Finals', 'Semi-Finals', 'Final'];
135
            $noRounds = ceil(log($this->numFighters, 2));
136
            $noTeamsInFirstRound = pow(2, ceil(log($this->numFighters) / log(2)));
137
            $tempRounds = [];
138
139
            //The minus 3 is to ignore the final, semi final and quarter final rounds
140
141
            for ($i = 0; $i < $noRounds - 3; $i++) {
142
                $tempRounds[] = 'Last ' . $noTeamsInFirstRound;
143
                $noTeamsInFirstRound /= 2;
144
            }
145
            return array_merge($tempRounds, $roundTitles);
146
        }
147
        return $roundTitle[$this->numFighters];
148
    }
149
150
    /**
151
     * Print Round Titles.
152
     */
153
    public function printRoundTitles()
154
    {
155
        $roundTitles = $this->getRoundTitles();
156
157
        echo '<div id="round-titles-wrapper">';
158
159
        foreach ($roundTitles as $key => $roundTitle) {
160
            $left = $key * ($this->matchWrapperWidth + $this->roundSpacing - 1);
161
162
            echo '<div class="round-title" style="left: ' . $left . 'px;">' . $roundTitle . '</div>';
163
        }
164
        echo '</div>';
165
    }
166
167
    /**
168
     * @param $selected
169
     *
170
     * @return string
171
     */
172
    public function getPlayerList($selected)
173
    {
174
        $html = '<select>
175
                <option' . ($selected == '' ? ' selected' : '') . '></option>';
176
177
        foreach ($this->championship->fighters as $fighter) {
178
            $html = $this->addOptionToSelect($selected, $fighter, $html);
179
        }
180
181
        $html .= '</select>';
182
183
        return $html;
184
    }
185
186
    public function getNewFighter()
187
    {
188
        if ($this->championship->category->isTeam()) {
189
            return new Team();
190
        }
191
192
        return new Competitor();
193
    }
194
195
    /**
196
     * @param $numRound
197
     */
198
    private function assignFightersToBracket($numRound, $hasPreliminary)
199
    {
200
        for ($roundNumber = $numRound; $roundNumber <= $this->noRounds; $roundNumber++) {
201
            $groupsByRound = $this->groupsByRound->get($roundNumber + $hasPreliminary);
202
            for ($matchNumber = 1; $matchNumber <= ($this->numFighters / pow(2, $roundNumber)); $matchNumber++) {
203
                $fight = $groupsByRound[$matchNumber - 1]->fights[0];
204
                $fighter1 = $fight->fighter1;
205
                $fighter2 = $fight->fighter2;
206
                $winnerId = $fight->winner_id;
207
                $this->brackets[$roundNumber][$matchNumber] = [$fighter1, $fighter2, $winnerId];
208
            }
209
        }
210
        
211
        if ($this->numFighters >= $this->championship->getGroupSize() * 2) {
212
            $lastRound = $this->noRounds;
213
            $lastMatch = $this->numFighters / pow(2, $roundNumber) + 1;
214
            $groupsByRound = $this->groupsByRound->get(intval($this->noRounds));
215
            $group = $groupsByRound[$lastMatch];
216
            $fight = $group->fights[0];
217
            $fighter1 = $fight->fighter1;
218
            $fighter2 = $fight->fighter2;
219
            $winnerId = $fight->winner_id;
220
            $this->brackets[$lastRound][$lastMatch + 1] = [$fighter1, $fighter2, $winnerId];
221
222
        }
223
    }
224
225
    /**
226
     * @param $selected
227
     * @param $fighter
228
     * @param $html
229
     *
230
     * @return string
231
     */
232
    private function addOptionToSelect($selected, $fighter, $html): string
233
    {
234
        if ($fighter != null) {
235
            $select = $selected != null && $selected->id == $fighter->id ? ' selected' : '';
236
            $html .= '<option' . $select
237
                . ' value='
238
                . ($fighter->id ?? '')
239
                . '>'
240
                . $fighter->name
241
                . '</option>';
242
        }
243
244
        return $html;
245
    }
246
}
247