Completed
Push — master ( a1765c...501088 )
by Jérémy
06:26
created

Calculator   B

Complexity

Total Complexity 41

Size/Duplication

Total Lines 371
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Importance

Changes 0
Metric Value
wmc 41
lcom 1
cbo 7
dl 0
loc 371
rs 8.2769
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 1
B calculate() 0 30 1
B findPotentialStamina() 0 23 4
B findPotentialCombinaisons() 0 28 5
B cleanImpossibleCombinaisons() 0 22 4
A cleanBestStats() 0 4 1
A setRanges() 0 6 1
A setRange() 0 8 2
A testCP() 0 15 1
A testHP() 0 8 1
B getMaxRange() 0 14 5
B getLowerRange() 0 14 5
A getMaxGlobalEvaluation() 0 16 4
A getAttackRange() 0 4 1
A getDefenseRange() 0 4 1
A getStaminaRange() 0 4 1
A getPotentialLevels() 0 4 1
A getPotentialStamina() 0 4 1
A getPotentialCombinaisons() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Calculator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Calculator, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * (c) Jérémy Marodon <[email protected]>
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
namespace Th3Mouk\PokemonGoIVCalculator\Calculator;
10
11
use Illuminate\Support\Collection;
12
use Th3Mouk\PokemonGoIVCalculator\Entities\IvCombinaison;
13
use Th3Mouk\PokemonGoIVCalculator\Entities\Level;
14
use Th3Mouk\PokemonGoIVCalculator\Entities\Pokemon;
15
use Th3Mouk\PokemonGoIVCalculator\Entities\StaminaLevelCombinaison;
16
use Th3Mouk\PokemonGoIVCalculator\Extractors\LevelExtractor;
17
use Th3Mouk\PokemonGoIVCalculator\Extractors\Pokedex;
18
19
class Calculator
20
{
21
    private const AVAILABLE_OPTIONS = ['atk', 'def', 'hp'];
22
23
    private $attack_range;
24
    private $defense_range;
25
    private $stamina_range;
26
27
    /**
28
     * @var Collection
29
     */
30
    private $potentialLevels;
31
32
    /**
33
     * @var Collection
34
     */
35
    private $potentialStamina;
36
37
    /**
38
     * @var Collection
39
     */
40
    private $potentialCombinaisons;
41
42
    /**
43
     * Calculator constructor.
44
     */
45
    public function __construct()
46
    {
47
        $this->attack_range = range(0, 15);
48
        $this->defense_range = range(0, 15);
49
        $this->stamina_range = range(0, 15);
50
51
        $this->potentialLevels = new Collection();
52
        $this->potentialStamina = new Collection();
53
        $this->potentialCombinaisons = new Collection();
54
    }
55
56
    /**
57
     * Process all operations to retrieve differents IV combinaisons
58
     * @param  string  $pokemonName
59
     * @param  int     $cp
60
     * @param  int     $hp
61
     * @param  int     $dusts
62
     * @param  int     $global
63
     * @param  int     $maxStat
64
     * @param  array   $bestStats
65
     * @param  bool    $upgraded
66
     * @return Pokemon
67
     */
68
    public function calculate(
69
        string $pokemonName,
70
        int $cp,
71
        int $hp,
72
        int $dusts,
73
        int $global,
74
        int $maxStat,
75
        array $bestStats,
76
        bool $upgraded = false
77
    ): Pokemon {
78
        $pokemon = (new Pokedex())->get($pokemonName);
79
80
        $pokemon->setCp($cp);
81
        $pokemon->setHp($hp);
82
83
        $bestStats = $this->cleanBestStats($bestStats);
84
85
        $this->setRanges($bestStats, $maxStat);
86
87
        $this->potentialLevels = (new LevelExtractor())->getDustFiltered($dusts);
88
89
        $this
90
            ->findPotentialStamina($pokemon, $hp, $upgraded)
91
            ->findPotentialCombinaisons($pokemon, $cp)
92
            ->cleanImpossibleCombinaisons($bestStats, $global);
93
94
        $pokemon->setIvCombinaisons($this->potentialCombinaisons);
95
96
        return $pokemon;
97
    }
98
99
    /**
100
     * Retrieve the possible level and stamina IV to match HP
101
     * @param  Pokemon $pokemon
102
     * @param  int     $hp
103
     * @param  bool    $upgraded
104
     * @return $this
105
     */
106
    private function findPotentialStamina(
107
        Pokemon $pokemon,
108
        int $hp,
109
        bool $upgraded
110
    ) {
111
        foreach ($this->potentialLevels as $data) {
112
            $level = new Level(
113
                $data->level, $data->dust, $data->cpScalar, $upgraded
114
            );
115
116
            foreach ($this->stamina_range as $staminaIV) {
117
                if ($this->testHP($pokemon, $level, $hp, $staminaIV)) {
118
                    $combinaison = new StaminaLevelCombinaison(
119
                        $level,
120
                        $staminaIV
121
                    );
122
                    $this->potentialStamina->push($combinaison);
123
                }
124
            }
125
        }
126
127
        return $this;
128
    }
129
130
    /**
131
     * Test remaining combinaisons which match CP
132
     * @param  Pokemon $pokemon
133
     * @param  int     $cp
134
     * @return $this
135
     */
136
    private function findPotentialCombinaisons(Pokemon $pokemon, int $cp)
137
    {
138
        foreach ($this->potentialStamina as $staminaCombinaison) {
139
            foreach ($this->attack_range as $attackIV) {
140
                foreach ($this->defense_range as $defenseIV) {
141
                    if ($this->testCP(
142
                        $pokemon,
143
                        $staminaCombinaison->getLevel(),
144
                        $cp,
145
                        $attackIV,
146
                        $defenseIV,
147
                        $staminaCombinaison->getStamina())
148
                    ) {
149
                        $combinaison = new IvCombinaison(
150
                            $staminaCombinaison->getLevel(),
151
                            $attackIV,
152
                            $defenseIV,
153
                            $staminaCombinaison->getStamina()
154
                        );
155
156
                        $this->potentialCombinaisons->push($combinaison);
157
                    }
158
                }
159
            }
160
        }
161
162
        return $this;
163
    }
164
165
    /**
166
     * Remove impossible combinaisons with coach indications
167
     * @param  array $bestStats
168
     * @param  int   $global
169
     * @return $this
170
     */
171
    private function cleanImpossibleCombinaisons(array $bestStats, int $global)
172
    {
173
        $this->potentialCombinaisons = $this->potentialCombinaisons
174
            // Eliminate impossible combinaison for global combinaison
175
            ->filter(function ($combinaison) use ($global) {
176
                if ($combinaison->getTotal() < $this->getMaxGlobalEvaluation($global)) {
177
                    return true;
178
                }
179
                return false;
180
            })
181
            // Eliminate impossible combinaisons with best stats
182
            ->filter(function ($combinaison) use ($bestStats) {
183
                $nonBestStats = array_diff(self::AVAILABLE_OPTIONS, $bestStats);
184
                foreach ($nonBestStats as $nonBestStat) {
185
                    if ($combinaison->getAbbreviated($nonBestStat) < $combinaison->getMaximalIv()) {
186
                        return true;
187
                    }
188
                    return false;
189
                }
190
            });
191
        return $this;
192
    }
193
194
    /**
195
     * Remove unavailable options give by user
196
     * @param $bestStats
197
     * @return array
198
     */
199
    private function cleanBestStats($bestStats): array
200
    {
201
        return array_intersect(self::AVAILABLE_OPTIONS, $bestStats);
202
    }
203
204
    /**
205
     * Remove impossible values for differents IV with coach indications
206
     * @param array $bestStats
207
     * @param int   $maxStat
208
     */
209
    private function setRanges(array $bestStats, int $maxStat)
210
    {
211
        $this->setRange($bestStats, $maxStat, 'atk', $this->attack_range);
212
        $this->setRange($bestStats, $maxStat, 'def', $this->defense_range);
213
        $this->setRange($bestStats, $maxStat, 'hp', $this->stamina_range);
214
    }
215
216
    private function setRange($bestStats, $maxStat, $option, &$property)
217
    {
218
        if (in_array($option, $bestStats)) {
219
            $property = $this->getMaxRange($maxStat);
220
        } else {
221
            $property = $this->getLowerRange($maxStat);
222
        }
223
    }
224
225
    /**
226
     * Calculate if a combinaison of value match the given CP
227
     * @param  Pokemon $pokemon
228
     * @param  Level   $level
229
     * @param  int     $cp
230
     * @param  int     $attackIV
231
     * @param  int     $defenseIV
232
     * @param  int     $staminaIV
233
     * @return bool
234
     */
235
    private function testCP(
236
        Pokemon $pokemon,
237
        Level $level,
238
        int $cp,
239
        int $attackIV,
240
        int $defenseIV,
241
        int $staminaIV
242
    ) {
243
        $attackFactor = $pokemon->getBaseAttack() + $attackIV;
244
        $defenseFactor = pow($pokemon->getBaseDefense() + $defenseIV, 0.5);
245
        $staminaFactor = pow($pokemon->getBaseStamina() + $staminaIV, 0.5);
246
        $scalarFactor = pow($level->getCpScalar(), 2);
247
248
        return $cp == floor($attackFactor * $defenseFactor * $staminaFactor * $scalarFactor / 10);
249
    }
250
251
    /**
252
     * Calculate if a combinaison of value match the given HP
253
     * @param  Pokemon $pokemon
254
     * @param  Level   $level
255
     * @param  int     $hp
256
     * @param  int     $staminaIV
257
     * @return bool
258
     */
259
    private function testHP(
260
        Pokemon $pokemon,
261
        Level $level,
262
        int $hp,
263
        int $staminaIV
264
    ) {
265
        return $hp == (int) floor(($pokemon->getBaseStamina() + $staminaIV) * $level->getCpScalar());
266
    }
267
268
    /**
269
     * Return the range of stats given by the coach
270
     * @param  int   $maxStat
271
     * @return array
272
     */
273
    private function getMaxRange(int $maxStat)
274
    {
275
        switch ($maxStat) {
276
            case 1:
277
                return range(0, 7);
278
            case 2:
279
                return range(8, 12);
280
            case 3:
281
                return range(13, 14);
282
            case 4:
283
                return [15];
284
        };
285
        return range(0, 15);
286
    }
287
288
    /**
289
     * Return the range of stats non cited by the coach
290
     * @param  int   $maxStat
291
     * @return array
292
     */
293
    private function getLowerRange(int $maxStat)
294
    {
295
        switch ($maxStat) {
296
            case 1:
297
                return range(0, 6);
298
            case 2:
299
                return range(0, 11);
300
            case 3:
301
                return range(0, 13);
302
            case 4:
303
                return range(0, 14);
304
        };
305
        return range(0, 15);
306
    }
307
308
    /**
309
     * Get the threshold of the global evaluation given by the coach
310
     * @param $global
311
     * @return int
312
     */
313
    private function getMaxGlobalEvaluation($global)
314
    {
315
        switch ($global) {
316
            case 1:
317
                // 0-22
318
                return 22;
319
            case 2:
320
                // 23-29
321
                return 29;
322
            case 3:
323
                // 30-36
324
                return 36;
325
        }
326
        // 37-45
327
        return 45;
328
    }
329
330
    /**
331
     * Get attack_range
332
     *
333
     * @return array
334
     */
335
    public function getAttackRange(): array
336
    {
337
        return $this->attack_range;
338
    }
339
340
    /**
341
     * Get defense_range
342
     *
343
     * @return array
344
     */
345
    public function getDefenseRange(): array
346
    {
347
        return $this->defense_range;
348
    }
349
350
    /**
351
     * Get stamina_range
352
     *
353
     * @return array
354
     */
355
    public function getStaminaRange(): array
356
    {
357
        return $this->stamina_range;
358
    }
359
360
    /**
361
     * Get potentialLevels
362
     *
363
     * @return Collection
364
     */
365
    public function getPotentialLevels(): Collection
366
    {
367
        return $this->potentialLevels;
368
    }
369
370
    /**
371
     * Get potentialStamina
372
     *
373
     * @return Collection
374
     */
375
    public function getPotentialStamina(): Collection
376
    {
377
        return $this->potentialStamina;
378
    }
379
380
    /**
381
     * Get potentialCombinaisons
382
     *
383
     * @return Collection
384
     */
385
    public function getPotentialCombinaisons(): Collection
386
    {
387
        return $this->potentialCombinaisons;
388
    }
389
}
390