Completed
Push — master ( 26fe90...eb810e )
by Jérémy
03:39
created

Calculator::getAttackRange()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
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
    public function calculate(
57
        string $pokemonName,
58
        int $cp,
59
        int $hp,
60
        int $dusts,
61
        int $global,
62
        int $maxStat,
63
        array $bestStats,
64
        bool $upgraded = false
65
    ): Pokemon {
66
        $pokemon = (new Pokedex())->get($pokemonName);
67
68
        $pokemon->setCp($cp);
69
        $pokemon->setHp($hp);
70
71
        $this->setRanges($bestStats, $maxStat);
72
73
        $this->potentialLevels = (new LevelExtractor())->getDustFiltered($dusts);
74
75
        $this
76
            ->findPotentialStamina($pokemon, $hp, $upgraded)
77
            ->findPotentialCombinaisons($pokemon, $cp)
78
            ->cleanImpossibleCombinaisons($bestStats, $global);
79
80
        $pokemon->setIvCombinaisons($this->potentialCombinaisons);
81
82
        return $pokemon;
83
    }
84
85
    /**
86
     * @param Pokemon $pokemon
87
     * @param int $hp
88
     *
89
     * @param bool $upgraded
90
     * @return $this
91
     */
92
    private function findPotentialStamina(
93
        Pokemon $pokemon,
94
        int $hp,
95
        bool $upgraded
96
    ) {
97
        foreach ($this->potentialLevels as $data) {
98
            $level = new Level(
99
                $data->level, $data->dust, $data->cpScalar, $upgraded
100
            );
101
102
            foreach ($this->stamina_range as $staminaIV) {
103
                if ($this->testHP($pokemon, $level, $hp, $staminaIV)) {
104
                    $combinaison = new StaminaLevelCombinaison(
105
                        $level,
106
                        $staminaIV
107
                    );
108
                    $this->potentialStamina->push($combinaison);
109
                }
110
            }
111
        }
112
113
        return $this;
114
    }
115
116
    /**
117
     * @param Pokemon $pokemon
118
     * @param int $cp
119
     * @return $this
120
     */
121
    private function findPotentialCombinaisons(Pokemon $pokemon, int $cp)
122
    {
123
        foreach ($this->potentialStamina as $staminaCombinaison) {
124
            foreach ($this->attack_range as $attackIV) {
125
                foreach ($this->defense_range as $defenseIV) {
126
                    if ($this->testCP(
127
                        $pokemon,
128
                        $staminaCombinaison->getLevel(),
129
                        $cp,
130
                        $attackIV,
131
                        $defenseIV,
132
                        $staminaCombinaison->getStamina())
133
                    ) {
134
                        $combinaison = new IvCombinaison(
135
                            $staminaCombinaison->getLevel(),
136
                            $attackIV,
137
                            $defenseIV,
138
                            $staminaCombinaison->getStamina()
139
                        );
140
141
                        $this->potentialCombinaisons->push($combinaison);
142
                    }
143
                }
144
            }
145
        }
146
147
        return $this;
148
    }
149
150
    private function cleanImpossibleCombinaisons(array $bestStats, int $global)
151
    {
152
        $this->potentialCombinaisons = $this->potentialCombinaisons
153
            // Eliminate impossible combinaison for global combinaison
154
            ->filter(function ($combinaison) use ($global) {
155
                if ($combinaison->getTotal() < $this->getMaxGlobalEvaluation($global)) {
156
                    return true;
157
                }
158
                return false;
159
            })
160
            // Eliminate impossible combinaisons with best stats
161
            ->filter(function ($combinaison) use ($bestStats) {
162
                $nonBestStats = array_diff(self::AVAILABLE_OPTIONS, $bestStats);
163
                foreach ($nonBestStats as $nonBestStat) {
164
                    if ($combinaison->getAbbreviated($nonBestStat) < $combinaison->getMaximalIv()) {
165
                        return true;
166
                    }
167
                    return false;
168
                }
169
            });
170
        return $this;
171
    }
172
173
    private function setRanges(array $bestStats, int $maxStat)
174
    {
175
        $this->setRange($bestStats, $maxStat, 'atk', $this->attack_range);
176
        $this->setRange($bestStats, $maxStat, 'def', $this->defense_range);
177
        $this->setRange($bestStats, $maxStat, 'hp', $this->stamina_range);
178
    }
179
180
    private function setRange($bestStats, $maxStat, $option, &$property)
181
    {
182
        if (in_array($option, $bestStats)) {
183
            $property = $this->getMaxRange($maxStat);
184
        } else {
185
            $property = $this->getLowerRange($maxStat);
186
        }
187
    }
188
189
    /**
190
     * @param Pokemon $pokemon
191
     * @param Level $level
192
     * @param int $cp
193
     * @param int $attackIV
194
     * @param int $defenseIV
195
     * @param int $staminaIV
196
     * @return bool
197
     */
198
    private function testCP(
199
        Pokemon $pokemon,
200
        Level $level,
201
        int $cp,
202
        int $attackIV,
203
        int $defenseIV,
204
        int $staminaIV
205
    ) {
206
        $attackFactor = $pokemon->getBaseAttack() + $attackIV;
207
        $defenseFactor = pow($pokemon->getBaseDefense() + $defenseIV, 0.5);
208
        $staminaFactor = pow($pokemon->getBaseStamina() + $staminaIV, 0.5);
209
        $scalarFactor = pow($level->getCpScalar(), 2);
210
211
        return $cp == floor($attackFactor * $defenseFactor * $staminaFactor * $scalarFactor / 10);
212
    }
213
214
    /**
215
     * @param Pokemon $pokemon
216
     * @param Level $level
217
     * @param int $hp
218
     * @param int $staminaIV
219
     * @return bool
220
     */
221
    private function testHP(
222
        Pokemon $pokemon,
223
        Level $level,
224
        int $hp,
225
        int $staminaIV
226
    ) {
227
        return $hp == (int) floor(($pokemon->getBaseStamina() + $staminaIV) * $level->getCpScalar());
228
    }
229
230
    /**
231
     * Return the range of stats given by the coach
232
     * @param int $maxStat
233
     * @return array
234
     */
235
    private function getMaxRange(int $maxStat)
236
    {
237
        switch ($maxStat) {
238
            case 1:
239
                return range(0, 7);
240
            case 2:
241
                return range(8, 12);
242
            case 3:
243
                return range(13, 14);
244
            case 4:
245
                return [15];
246
        };
247
        return range(0, 15);
248
    }
249
250
    /**
251
     * Return the range of stats non cited by the coach
252
     * @param int $maxStat
253
     * @return array
254
     */
255
    private function getLowerRange(int $maxStat)
256
    {
257
        switch ($maxStat) {
258
            case 1:
259
                return range(0, 6);
260
            case 2:
261
                return range(0, 11);
262
            case 3:
263
                return range(0, 13);
264
            case 4:
265
                return range(0, 14);
266
        };
267
        return range(0, 15);
268
    }
269
270
    /**
271
     * Get the threshold of the global evaluation given by the coach
272
     * @param $global
273
     * @return int
274
     */
275
    private function getMaxGlobalEvaluation($global)
276
    {
277
        switch ($global) {
278
            case 1:
279
                // 0-22
280
                return 22;
281
            case 2:
282
                // 23-29
283
                return 29;
284
            case 3:
285
                // 30-36
286
                return 36;
287
        }
288
        // 37-45
289
        return 45;
290
    }
291
292
    /**
293
     * Get attack_range
294
     *
295
     * @return array
296
     */
297
    public function getAttackRange(): array
298
    {
299
        return $this->attack_range;
300
    }
301
302
    /**
303
     * Get defense_range
304
     *
305
     * @return array
306
     */
307
    public function getDefenseRange(): array
308
    {
309
        return $this->defense_range;
310
    }
311
312
    /**
313
     * Get stamina_range
314
     *
315
     * @return array
316
     */
317
    public function getStaminaRange(): array
318
    {
319
        return $this->stamina_range;
320
    }
321
322
    /**
323
     * Get potentialLevels
324
     *
325
     * @return Collection
326
     */
327
    public function getPotentialLevels(): Collection
328
    {
329
        return $this->potentialLevels;
330
    }
331
332
    /**
333
     * Get potentialStamina
334
     *
335
     * @return Collection
336
     */
337
    public function getPotentialStamina(): Collection
338
    {
339
        return $this->potentialStamina;
340
    }
341
342
    /**
343
     * Get potentialCombinaisons
344
     *
345
     * @return Collection
346
     */
347
    public function getPotentialCombinaisons(): Collection
348
    {
349
        return $this->potentialCombinaisons;
350
    }
351
}
352