Completed
Push — master ( 9a3191...caa5cd )
by Jérémy
02:07
created

Calculator::calculate()   B

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
            case 1:
329
                // 0-22
330
                return 22;
331
            case 2:
332
                // 23-29
333
                return 29;
334
            case 3:
335
                // 30-36
336
                return 36;
337
        }
338
        // 37-45
339
        return 45;
340
    }
341
342
    /**
343
     * Get attack_range
344
     *
345
     * @return array
346
     */
347
    public function getAttackRange(): array
348
    {
349
        return $this->attack_range;
350
    }
351
352
    /**
353
     * Get defense_range
354
     *
355
     * @return array
356
     */
357
    public function getDefenseRange(): array
358
    {
359
        return $this->defense_range;
360
    }
361
362
    /**
363
     * Get stamina_range
364
     *
365
     * @return array
366
     */
367
    public function getStaminaRange(): array
368
    {
369
        return $this->stamina_range;
370
    }
371
372
    /**
373
     * Get potentialLevels
374
     *
375
     * @return Collection
376
     */
377
    public function getPotentialLevels(): Collection
378
    {
379
        return $this->potentialLevels;
380
    }
381
382
    /**
383
     * Get potentialStamina
384
     *
385
     * @return Collection
386
     */
387
    public function getPotentialStamina(): Collection
388
    {
389
        return $this->potentialStamina;
390
    }
391
392
    /**
393
     * Get potentialCombinaisons
394
     *
395
     * @return Collection
396
     */
397
    public function getPotentialCombinaisons(): Collection
398
    {
399
        return $this->potentialCombinaisons;
400
    }
401
}
402