Completed
Push — master ( da80d6...45fc90 )
by Julien
07:21
created

CreateSingleEliminationTree::printRoundTitles()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 13
rs 9.4285
cc 2
eloc 7
nc 2
nop 0
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
65
    private function assignPositions()
66
    {
67
68
        //Variables required for figuring outing the height of the vertical connectors
69
70
        $spaceFactor = 0.5;
71
        $playerHeightFactor = 1;
72
        foreach ($this->brackets as $roundNumber => &$round) {
73
            foreach ($round as $matchNumber => &$match) {
74
75
                //Give teams a nicer index
76
77
                $match['playerA'] = $match[0];
78
                $match['playerB'] = $match[1];
79
                $match['winner_id'] = $match[2];
80
81
                unset($match[0]);
82
                unset($match[1]);
83
                unset($match[2]);
84
85
                //Figure out the bracket positions
86
87
                $match['matchWrapperTop'] = (((2 * $matchNumber) - 1) * (pow(2, ($roundNumber) - 1)) - 1) * (($this->matchSpacing / 2) + $this->playerWrapperHeight);
88
                $match['matchWrapperLeft'] = ($roundNumber - 1) * ($this->matchWrapperWidth + $this->roundSpacing - 1);
89
                $match['vConnectorLeft'] = floor($match['matchWrapperLeft'] + $this->matchWrapperWidth + ($this->roundSpacing / 2) - ($this->borderWidth / 2));
90
                $match['vConnectorHeight'] = ($spaceFactor * $this->matchSpacing) + ($playerHeightFactor * $this->playerWrapperHeight) + $this->borderWidth;
91
                $match['vConnectorTop'] = $match['hConnectorTop'] = $match['matchWrapperTop'] + $this->playerWrapperHeight;
92
                $match['hConnectorLeft'] = ($match['vConnectorLeft'] - ($this->roundSpacing / 2)) + 2;
93
                $match['hConnector2Left'] = $match['matchWrapperLeft'] + $this->matchWrapperWidth + ($this->roundSpacing / 2);
94
95
                //Adjust the positions depending on the match number
96
97
                if (!($matchNumber % 2)) {
98
                    $match['hConnector2Top'] = $match['vConnectorTop'] -= ($match['vConnectorHeight'] - $this->borderWidth);
99
                } else {
100
                    $match['hConnector2Top'] = $match['vConnectorTop'] + ($match['vConnectorHeight'] - $this->borderWidth);
101
                }
102
            }
103
104
            //Update the spacing variables
105
106
            $spaceFactor *= 2;
107
            $playerHeightFactor *= 2;
108
        }
109
    }
110
111
    /**
112
     * returns titles depending of number of rounds
113
     * @return array
114
     */
115
    public function getRoundTitles()
116
    {
117
        $semiFinalTitles = ['Semi-Finals', 'Final'];
118
        $quarterFinalTitles = ['Quarter-Finals', 'Semi-Finals', 'Final'];
119
        $roundTitle = [
120
            2 => ['Final'],
121
            3 => $semiFinalTitles,
122
            4 => $semiFinalTitles,
123
            5 => $semiFinalTitles,
124
            6 => $quarterFinalTitles,
125
            7 => $quarterFinalTitles,
126
            8 => $quarterFinalTitles,
127
128
        ];
129
        if ($this->numFighters > 8) {
130
            $roundTitles = ['Quarter-Finals', 'Semi-Finals', 'Final'];
131
            $noRounds = ceil(log($this->numFighters, 2));
132
            $noTeamsInFirstRound = pow(2, ceil(log($this->numFighters) / log(2)));
133
            $tempRounds = [];
134
135
            //The minus 3 is to ignore the final, semi final and quarter final rounds
136
137
            for ($i = 0; $i < $noRounds - 3; $i++) {
138
                $tempRounds[] = 'Last ' . $noTeamsInFirstRound;
139
                $noTeamsInFirstRound /= 2;
140
            }
141
            return array_merge($tempRounds, $roundTitles);
142
        }
143
        return $roundTitle[$this->numFighters];
144
    }
145
146
    /**
147
     * Print Round Titles.
148
     */
149
    public function printRoundTitles()
150
    {
151
        $roundTitles = $this->getRoundTitles();
152
153
        echo '<div id="round-titles-wrapper">';
154
155
        foreach ($roundTitles as $key => $roundTitle) {
156
            $left = $key * ($this->matchWrapperWidth + $this->roundSpacing - 1);
157
158
            echo '<div class="round-title" style="left: ' . $left . 'px;">' . $roundTitle . '</div>';
159
        }
160
        echo '</div>';
161
    }
162
163
    /**
164
     * @param $selected
165
     *
166
     * @return string
167
     */
168
    public function getPlayerList($selected)
169
    {
170
        $html = '<select>
171
                <option' . ($selected == '' ? ' selected' : '') . '></option>';
172
173
        foreach ($this->championship->fighters as $fighter) {
174
            $html = $this->addOptionToSelect($selected, $fighter, $html);
175
        }
176
177
        $html .= '</select>';
178
179
        return $html;
180
    }
181
182
    public function getNewFighter()
183
    {
184
        if ($this->championship->category->isTeam()) {
185
            return new Team();
186
        }
187
188
        return new Competitor();
189
    }
190
191
    /**
192
     * @param $numRound
193
     */
194
    private function assignFightersToBracket($numRound, $hasPreliminary)
195
    {
196
        for ($roundNumber = $numRound; $roundNumber <= $this->noRounds; $roundNumber++) {
197
            $groupsByRound = $this->groupsByRound->get($roundNumber + $hasPreliminary);
198
            for ($matchNumber = 1; $matchNumber <= ($this->numFighters / pow(2, $roundNumber)); $matchNumber++) {
199
                $fight = $groupsByRound[$matchNumber - 1]->fights[0];
200
201
                $fighter1 = $fight->competitor1;
202
                $fighter2 = $fight->competitor2;
203
                if ($this->championship->category->isTeam()) {
204
                    $fighter1 = $fight->team1;
205
                    $fighter2 = $fight->team2;
206
                }
207
                $winnerId = $fight->winner_id;
208
                $this->brackets[$roundNumber][$matchNumber] = [$fighter1, $fighter2, $winnerId];
209
            }
210
        }
211
    }
212
213
    /**
214
     * @param $selected
215
     * @param $fighter
216
     * @param $html
217
     *
218
     * @return string
219
     */
220
    private function addOptionToSelect($selected, $fighter, $html): string
221
    {
222
        if ($fighter != null) {
223
            $select = $selected != null && $selected->id == $fighter->id ? ' selected' : '';
224
            $html .= '<option' . $select
225
                . ' value='
226
                . ($fighter->id ?? '')
227
                . '>'
228
                . $fighter->name
229
                . '</option>';
230
        }
231
232
        return $html;
233
    }
234
}
235