Completed
Push — master ( 26d986...9b7bb9 )
by Julien
09:29
created

CreateSingleEliminationTree::printRoundTitles()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 34
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

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