|
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 $groupsByRound; |
|
12
|
|
|
public $hasPrelimimary; |
|
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
|
|
|
|
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.