Calculator::calculate()   B
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 30
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 30
rs 8.8571
c 0
b 0
f 0
cc 1
eloc 22
nc 1
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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
                    $combinaison->getTotal() > $this->getMaxGlobalEvaluation($global-1)) {
178
                    return true;
179
                }
180
                return false;
181
            })
182
            // Eliminate impossible combinaisons with best stats
183
            ->filter(function ($combinaison) use ($bestStats) {
184
                $nonBestStats = array_diff(self::AVAILABLE_OPTIONS, $bestStats);
185
                foreach ($nonBestStats as $nonBestStat) {
186
                    if ($combinaison->getAbbreviated($nonBestStat) >= $combinaison->getMaximalIv()) {
187
                        return false;
188
                    }
189
                }
190
                return true;
191
            })
192
            // Eliminate impossible combinaisons with equals best stats
193
            ->filter(function ($combinaison) use ($bestStats) {
194
                foreach ($bestStats as $bestStat) {
195
                    if ($combinaison->getAbbreviated($bestStat) != $combinaison->getMaximalIv()) {
196
                        return false;
197
                    }
198
                }
199
                return true;
200
            });
201
202
        return $this;
203
    }
204
205
    /**
206
     * Remove unavailable options give by user
207
     * @param $bestStats
208
     * @return array
209
     */
210
    private function cleanBestStats($bestStats): array
211
    {
212
        return array_intersect(self::AVAILABLE_OPTIONS, $bestStats);
213
    }
214
215
    /**
216
     * Remove impossible values for differents IV with coach indications
217
     * @param array $bestStats
218
     * @param int   $maxStat
219
     */
220
    private function setRanges(array $bestStats, int $maxStat)
221
    {
222
        $this->setRange($bestStats, $maxStat, 'atk', $this->attack_range);
223
        $this->setRange($bestStats, $maxStat, 'def', $this->defense_range);
224
        $this->setRange($bestStats, $maxStat, 'hp', $this->stamina_range);
225
    }
226
227
    private function setRange($bestStats, $maxStat, $option, &$property)
228
    {
229
        if (in_array($option, $bestStats)) {
230
            $property = $this->getMaxRange($maxStat);
231
        } else {
232
            $property = $this->getLowerRange($maxStat);
233
        }
234
    }
235
236
    /**
237
     * Calculate if a combinaison of value match the given CP
238
     * @param  Pokemon $pokemon
239
     * @param  Level   $level
240
     * @param  int     $cp
241
     * @param  int     $attackIV
242
     * @param  int     $defenseIV
243
     * @param  int     $staminaIV
244
     * @return bool
245
     */
246
    private function testCP(
247
        Pokemon $pokemon,
248
        Level $level,
249
        int $cp,
250
        int $attackIV,
251
        int $defenseIV,
252
        int $staminaIV
253
    ) {
254
        $attackFactor = $pokemon->getBaseAttack() + $attackIV;
255
        $defenseFactor = pow($pokemon->getBaseDefense() + $defenseIV, 0.5);
256
        $staminaFactor = pow($pokemon->getBaseStamina() + $staminaIV, 0.5);
257
        $scalarFactor = pow($level->getCpScalar(), 2);
258
259
        return $cp == floor($attackFactor * $defenseFactor * $staminaFactor * $scalarFactor / 10);
260
    }
261
262
    /**
263
     * Calculate if a combinaison of value match the given HP
264
     * @param  Pokemon $pokemon
265
     * @param  Level   $level
266
     * @param  int     $hp
267
     * @param  int     $staminaIV
268
     * @return bool
269
     */
270
    private function testHP(
271
        Pokemon $pokemon,
272
        Level $level,
273
        int $hp,
274
        int $staminaIV
275
    ) {
276
        return $hp == (int) floor(($pokemon->getBaseStamina() + $staminaIV) * $level->getCpScalar());
277
    }
278
279
    /**
280
     * Return the range of stats given by the coach
281
     * @param  int   $maxStat
282
     * @return array
283
     */
284
    private function getMaxRange(int $maxStat)
285
    {
286
        switch ($maxStat) {
287
            case 1:
288
                return range(0, 7);
289
            case 2:
290
                return range(8, 12);
291
            case 3:
292
                return range(13, 14);
293
            case 4:
294
                return [15];
295
        };
296
        return range(0, 15);
297
    }
298
299
    /**
300
     * Return the range of stats non cited by the coach
301
     * @param  int   $maxStat
302
     * @return array
303
     */
304
    private function getLowerRange(int $maxStat)
305
    {
306
        switch ($maxStat) {
307
            case 1:
308
                return range(0, 6);
309
            case 2:
310
                return range(0, 11);
311
            case 3:
312
                return range(0, 13);
313
            case 4:
314
                return range(0, 14);
315
        };
316
        return range(0, 15);
317
    }
318
319
    /**
320
     * Get the threshold of the global evaluation given by the coach
321
     * @param $global
322
     * @return int
323
     */
324
    private function getMaxGlobalEvaluation($global)
325
    {
326
        switch ($global) {
327
            case 0:
328
                return -1;
329
            case 1:
330
                // 0-22
331
                return 22;
332
            case 2:
333
                // 23-29
334
                return 29;
335
            case 3:
336
                // 30-36
337
                return 36;
338
        }
339
        // 37-45
340
        return 45;
341
    }
342
343
    /**
344
     * Get attack_range
345
     *
346
     * @return array
347
     */
348
    public function getAttackRange(): array
349
    {
350
        return $this->attack_range;
351
    }
352
353
    /**
354
     * Get defense_range
355
     *
356
     * @return array
357
     */
358
    public function getDefenseRange(): array
359
    {
360
        return $this->defense_range;
361
    }
362
363
    /**
364
     * Get stamina_range
365
     *
366
     * @return array
367
     */
368
    public function getStaminaRange(): array
369
    {
370
        return $this->stamina_range;
371
    }
372
373
    /**
374
     * Get potentialLevels
375
     *
376
     * @return Collection
377
     */
378
    public function getPotentialLevels(): Collection
379
    {
380
        return $this->potentialLevels;
381
    }
382
383
    /**
384
     * Get potentialStamina
385
     *
386
     * @return Collection
387
     */
388
    public function getPotentialStamina(): Collection
389
    {
390
        return $this->potentialStamina;
391
    }
392
393
    /**
394
     * Get potentialCombinaisons
395
     *
396
     * @return Collection
397
     */
398
    public function getPotentialCombinaisons(): Collection
399
    {
400
        return $this->potentialCombinaisons;
401
    }
402
}
403