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