Passed
Push — master ( 59b1f2...d98836 )
by Julien
33:15
created

CreateDirectEliminationTree::run()   B

Complexity

Conditions 6
Paths 16

Size

Total Lines 43
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

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