Test Failed
Branch master (1006b0)
by Julien
02:54
created

DirectEliminationTreeGen   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 242
Duplicated Lines 2.48 %

Coupling/Cohesion

Components 1
Dependencies 0

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 29
lcom 1
cbo 0
dl 6
loc 242
rs 10
c 1
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
B run() 0 36 4
B assignPositions() 0 48 4
B printBrackets() 0 35 4
B printRoundTitles() 0 46 6
B getPlayerList() 6 20 7
A orderTeamsInSeededOrder() 0 19 3

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
namespace Xoco70\KendoTournaments\TreeGen;
4
5
use Xoco70\KendoTournaments\Contracts\TreeGenerable;
6
7
class DirectEliminationTreeGen implements TreeGenerable
8
{
9
    private $names;
10
    private $brackets = array();
11
    private $noTeams;
12
    private $noRounds;
13
    private $playerWrapperHeight = 30;
14
    private $matchWrapperWidth = 150;
15
    private $roundSpacing = 40;
16
    private $matchSpacing = 42;
17
    private $borderWidth = 3;
18
19
    public function __construct($names)
20
    {
21
22
        $this->names = $names;
23
24
        $this->run();
25
26
    }
27
28
    public function run()
29
    {
30
        $teams = [];
31
        //If no names have been entered, then use numbers
32
33
        if ($this->names != '') {
34
            $teams = $this->names;
35
            $this->noTeams = count($teams);
36
        }
37
38
39
        //Calculate the size of the first full round - for example if you have 5 teams, then the first full round will consist of 4 teams
40
        $minimumFirstRoundSize = pow(2, ceil(log($this->noTeams) / log(2)));
41
        $this->noRounds = log($minimumFirstRoundSize, 2);
42
43
44
        //Order the teams in a seeded order - this is required regardless of whether it is a seeded tournament or not, as it prevents BYEs playing eachother
45
        $teams = $this->orderTeamsInSeededOrder($teams);
46
47
        $roundNumber = 1;
48
49
        //Group 2 teams into a match
50
        $matches = array_chunk($teams, 2);
51
52
53
        //If there's already a match in the match array, then that means the next round is round 2, so increase the round number
54
        if (count($this->brackets)) $roundNumber++;
55
        $countMatches = count($matches);
56
        //Create the first full round of teams, 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
        $this->assignPositions();
62
63
    }
64
65
    private function assignPositions()
66
    {
67
68
        //Variables required for figuring outing the height of the vertical connectors
69
70
        $matchSpacingMultiplier = 0.5;
71
        $playerWrapperHeightMultiplier = 1;
72
73
        foreach ($this->brackets as $roundNumber => &$round) {
74
75
            foreach ($round as $matchNumber => &$match) {
76
77
                //Give teams a nicer index
78
79
                $match['playerA'] = $match[0];
80
                $match['playerB'] = $match[1];
81
82
                unset($match[0]);
83
                unset($match[1]);
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'] = ($matchSpacingMultiplier * $this->matchSpacing) + ($playerWrapperHeightMultiplier * $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
105
            //Update the spacing variables
106
107
            $matchSpacingMultiplier *= 2;
108
            $playerWrapperHeightMultiplier *= 2;
109
110
        }
111
112
    }
113
114
    public function printBrackets()
115
    {
116
117
        $this->printRoundTitles();
118
119
        echo '<div id="brackets-wrapper">';
120
121
        foreach ($this->brackets as $roundNumber => $round) {
122
123
            foreach ($round as $matchNumber => $match) {
124
125
                echo '<div class="match-wrapper" style="top: ' . $match['matchWrapperTop'] . 'px; left: ' . $match['matchWrapperLeft'] . 'px; width: ' . $this->matchWrapperWidth . 'px;">
126
                        <input type="text" class="score">'
127
                    . $this->getPlayerList($match['playerA']) .
128
                    '<div class="match-divider">
129
                        </div>
130
                        <input type="text" class="score">'
131
                    . $this->getPlayerList($match['playerB']) .
132
                    '</div>';
133
134
                if ($roundNumber != $this->noRounds) {
135
136
                    echo '<div class="vertical-connector" style="top: ' . $match['vConnectorTop'] . 'px; left: ' . $match['vConnectorLeft'] . 'px; height: ' . $match['vConnectorHeight'] . 'px;"></div>
137
                          <div class="horizontal-connector" style="top: ' . $match['hConnectorTop'] . 'px; left: ' . $match['hConnectorLeft'] . 'px;"></div>
138
                          <div class="horizontal-connector" style="top: ' . $match['hConnector2Top'] . 'px; left: ' . $match['hConnector2Left'] . 'px;"></div>';
139
140
                }
141
142
            }
143
144
        }
145
146
        echo '</div>';
147
148
    }
149
150
    /**
151
     * Print Round Titles
152
     */
153
    private function printRoundTitles()
154
    {
155
156
        if ($this->noTeams == 2) {
157
158
            $roundTitles = array('Final');
159
160
        } elseif ($this->noTeams <= 4) {
161
162
            $roundTitles = array('Semi-Finals', 'Final');
163
164
        } elseif ($this->noTeams <= 8) {
165
166
            $roundTitles = array('Quarter-Finals', 'Semi-Finals', 'Final');
167
168
        } else {
169
170
            $roundTitles = array('Quarter-Finals', 'Semi-Finals', 'Final');
171
            $noRounds = ceil(log($this->noTeams, 2));
172
            $noTeamsInFirstRound = pow(2, ceil(log($this->noTeams) / log(2)));
173
            $tempRounds = array();
174
175
            //The minus 3 is to ignore the final, semi final and quarter final rounds
176
177
            for ($i = 0; $i < $noRounds - 3; $i++) {
178
                $tempRounds[] = 'Last ' . $noTeamsInFirstRound;
179
                $noTeamsInFirstRound /= 2;
180
            }
181
182
            $roundTitles = array_merge($tempRounds, $roundTitles);
183
184
        }
185
186
        echo '<div id="round-titles-wrapper">';
187
188
        foreach ($roundTitles as $key => $roundTitle) {
189
190
            $left = $key * ($this->matchWrapperWidth + $this->roundSpacing - 1);
191
192
            echo '<div class="round-title" style="left: ' . $left . 'px;">' . $roundTitle . '</div>';
193
194
        }
195
196
        echo '</div>';
197
198
    }
199
200
    /**
201
     * @param $selected
202
     * @return string
203
     */
204
    private function getPlayerList($selected)
205
    {
206
207
        $html = '<select>
208
                <option' . ($selected == '' ? ' selected' : '') . '></option>';
209
        foreach (array_merge($this->brackets[1]) as $bracket) { // Bug Fix 24-02-2017 , $this->brackets[2]
0 ignored issues
show
Unused Code Comprehensibility introduced by
48% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
210 View Code Duplication
            if ($bracket['playerA'] != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
211
                $html .= '<option' . ($selected == $bracket['playerA'] ? ' selected' : '') . '>' . $bracket['playerA'] . '</option>';
212
            }
213
214 View Code Duplication
            if ($bracket['playerB'] != '') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
215
                $html .= '<option' . ($selected == $bracket['playerB'] ? ' selected' : '') . '>' . $bracket['playerB'] . '</option>';
216
            }
217
        }
218
219
        $html .= '</select>';
220
221
        return $html;
222
223
    }
224
225
    /**
226
     * @param $teams
227
     * @return array
228
     */
229
    private function orderTeamsInSeededOrder($teams): array
230
    {
231
        $logNoTeam = log($this->noTeams / 2, 2);
232
233
        for ($i = 0; $i < $logNoTeam; $i++) {
234
235
            $out = [];
236
237
            foreach ($teams as $player) {
238
                $splice = pow(2, $i);
239
                $out = array_merge($out, array_splice($teams, 0, $splice));
240
                $out = array_merge($out, array_splice($teams, -$splice));
241
            }
242
243
            $teams = $out;
244
245
        }
246
        return $teams;
247
    }
248
}
249