Completed
Push — master ( a1765c...501088 )
by Jérémy
06:26
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
                    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