Test Failed
Push — master ( b6687a...d5e75a )
by Julien
06:32
created

CreateDirectEliminationTree::build()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 29
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 29
rs 8.8571
c 0
b 0
f 0
cc 3
eloc 13
nc 4
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 CreateDirectEliminationTree
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
            return [$fighter1, $fighter2];
34
        })->flatten()->all();
35
    }
36
37
    public function build()
38
    {
39
40
        $fighters = $this->firstRoundName;
41
        $this->numFighters = count($fighters);
42
43
44
        //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
45
        $this->noRounds = log($this->numFighters, 2);
46
        $roundNumber = 1;
47
48
        //Group 2 fighters into a match
49
        $matches = array_chunk($fighters, 2);
50
51
        //If there's already a match in the match array, then that means the next round is round 2, so increase the round number
52
        if (count($this->brackets)) {
53
            $roundNumber++;
54
        }
55
        $countMatches = count($matches);
56
        //Create the first full round of fighters, some may be blank if waiting on the results of a previous round
57
        for ($i = 0; $i < $countMatches; $i++) {
58
            $this->brackets[$roundNumber][$i + 1] = $matches[$i];
59
        }
60
61
        //Create the result of the empty rows for this tournament
62
        $this->assignFightersToBracket($roundNumber, $this->hasPreliminary);
63
        $this->assignPositions();
64
65
    }
66
67
    private function assignPositions()
68
    {
69
70
        //Variables required for figuring outing the height of the vertical connectors
71
72
        $spaceFactor = 0.5;
73
        $playerHeightFactor = 1;
74
75
        foreach ($this->brackets as $roundNumber => &$round) {
76
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
84
                unset($match[0]);
85
                unset($match[1]);
86
87
                //Figure out the bracket positions
88
89
                $match['matchWrapperTop'] = (((2 * $matchNumber) - 1) * (pow(2, ($roundNumber) - 1)) - 1) * (($this->matchSpacing / 2) + $this->playerWrapperHeight);
90
                $match['matchWrapperLeft'] = ($roundNumber - 1) * ($this->matchWrapperWidth + $this->roundSpacing - 1);
91
                $match['vConnectorLeft'] = floor($match['matchWrapperLeft'] + $this->matchWrapperWidth + ($this->roundSpacing / 2) - ($this->borderWidth / 2));
92
                $match['vConnectorHeight'] = ($spaceFactor * $this->matchSpacing) + ($playerHeightFactor * $this->playerWrapperHeight) + $this->borderWidth;
93
                $match['vConnectorTop'] = $match['hConnectorTop'] = $match['matchWrapperTop'] + $this->playerWrapperHeight;
94
                $match['hConnectorLeft'] = ($match['vConnectorLeft'] - ($this->roundSpacing / 2)) + 2;
95
                $match['hConnector2Left'] = $match['matchWrapperLeft'] + $this->matchWrapperWidth + ($this->roundSpacing / 2);
96
97
                //Adjust the positions depending on the match number
98
99
                if (!($matchNumber % 2)) {
100
                    $match['hConnector2Top'] = $match['vConnectorTop'] -= ($match['vConnectorHeight'] - $this->borderWidth);
101
                } else {
102
                    $match['hConnector2Top'] = $match['vConnectorTop'] + ($match['vConnectorHeight'] - $this->borderWidth);
103
                }
104
105
            }
106
107
            //Update the spacing variables
108
109
            $spaceFactor *= 2;
110
            $playerHeightFactor *= 2;
111
112
        }
113
114
    }
115
116
    /**
117
     * Print Round Titles
118
     */
119
    public function printRoundTitles()
120
    {
121
122
        if ($this->numFighters == 2) {
123
124
            $roundTitles = array('Final');
125
126
        } elseif ($this->numFighters <= 4) {
127
128
            $roundTitles = array('Semi-Finals', 'Final');
129
130
        } elseif ($this->numFighters <= 8) {
131
132
            $roundTitles = array('Quarter-Finals', 'Semi-Finals', 'Final');
133
134
        } else {
135
136
            $roundTitles = array('Quarter-Finals', 'Semi-Finals', 'Final');
137
            $noRounds = ceil(log($this->numFighters, 2));
138
            $noTeamsInFirstRound = pow(2, ceil(log($this->numFighters) / log(2)));
139
            $tempRounds = array();
140
141
            //The minus 3 is to ignore the final, semi final and quarter final rounds
142
143
            for ($i = 0; $i < $noRounds - 3; $i++) {
144
                $tempRounds[] = 'Last ' . $noTeamsInFirstRound;
145
                $noTeamsInFirstRound /= 2;
146
            }
147
148
            $roundTitles = array_merge($tempRounds, $roundTitles);
149
150
        }
151
152
        echo '<div id="round-titles-wrapper">';
153
154
        foreach ($roundTitles as $key => $roundTitle) {
155
156
            $left = $key * ($this->matchWrapperWidth + $this->roundSpacing - 1);
157
158
            echo '<div class="round-title" style="left: ' . $left . 'px;">' . $roundTitle . '</div>';
159
160
        }
161
162
        echo '</div>';
163
164
    }
165
166
    /**
167
     * @param $selected
168
     * @return string
169
     */
170
    public function getPlayerList($selected)
171
    {
172
173
        $html = '<select>
174
                <option' . ($selected == '' ? ' selected' : '') . '></option>';
175
176
        foreach ($this->championship->fighters as $fighter) {
177
            $html = $this->addOptionToSelect($selected, $fighter, $html);
178
        }
179
180
        $html .= '</select>';
181
182
        return $html;
183
184
    }
185
186
    public function getNewFighter()
187
    {
188
        if ($this->championship->category->isTeam()) {
189
            return new Team;
190
        }
191
        return new Competitor;
192
    }
193
194
    /**
195
     * @param $numRound
196
     */
197
    private function assignFightersToBracket($numRound, $hasPreliminary)
198
    {
199
        //TODO When Preliminary, we get a problem : Round 2 to 2, or get rounNumber = 1, and fails
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
205
                if ($this->championship->category->isTeam()) {
206
                    $fighter1 = $fight->team1;
207
                    $fighter2 = $fight->team2;
208
209
                } else {
210
                    $fighter1 = $fight->competitor1;
211
                    $fighter2 = $fight->competitor2;
212
                }
213
                $this->brackets[$roundNumber][$matchNumber] = [$fighter1, $fighter2];
214
            }
215
        }
216
    }
217
218
    /**
219
     * @param $selected
220
     * @param $fighter
221
     * @param $html
222
     * @return string
223
     */
224
    private function addOptionToSelect($selected, $fighter, $html): string
225
    {
226
        if ($fighter != null) {
227
            $select = $selected != null && $selected->id == $fighter->id ? ' selected' : '';
228
            $html .= '<option' . $select
229
                . ' value='
230
                . ($fighter->id ?? '')
231
                . '>'
232
                . $fighter->name
233
                . '</option>';
234
235
        }
236
        return $html;
237
    }
238
}
239