Completed
Push — master ( fdc81c...2e750c )
by Jérémy
02:35
created

Calculator::findPotentialCombinaisons()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 28
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 28
rs 8.439
c 0
b 0
f 0
cc 5
eloc 18
nc 5
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 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