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

Calculator::cleanImpossibleCombinaisons()   C

Complexity

Conditions 7
Paths 1

Size

Total Lines 33
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 33
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 19
nc 1
nop 2
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