localdex 0.2.47__py3-none-any.whl → 0.2.48__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,699 @@
1
+ """
2
+ Stat calculation functionality for LocalDex.
3
+
4
+ This module contains all the stat calculation functions for Pokemon,
5
+ including HP, Attack, Defense, Special Attack, Special Defense, and Speed calculations.
6
+ """
7
+
8
+ import math
9
+ from typing import Dict, List, Tuple, Union
10
+
11
+ from .models import BaseStats
12
+
13
+
14
+ class StatCalculator:
15
+ """
16
+ A class containing all Pokemon stat calculation functions.
17
+
18
+ This class provides methods to calculate Pokemon stats from base stats,
19
+ IVs, EVs, and levels, as well as reverse calculations to determine
20
+ required EVs for target stat values.
21
+ """
22
+
23
+ def __init__(self, localdex):
24
+ """
25
+ Initialize the StatCalculator with a LocalDex instance.
26
+
27
+ Args:
28
+ localdex: LocalDex instance for accessing Pokemon data
29
+ """
30
+ self.localdex = localdex
31
+
32
+ def get_base_stats_from_species(self, species: str) -> BaseStats:
33
+ """Get base stats from species name"""
34
+ return self.localdex.get_pokemon(name_or_id=species).base_stats
35
+
36
+ def get_hp_stat_from_species(self, species: str, iv: int, ev: int, level: int = 100) -> int:
37
+ """Calculate HP stat for a species with given IVs, EVs, and level"""
38
+ base_stats = self.get_base_stats_from_species(species)
39
+ return self.calculate_hp(base_stats.hp, iv, ev, level)
40
+
41
+ def get_attack_stat_from_species(self, species: str, iv: int, ev: int, level: int = 100, nature_modifier: float = 1.0) -> int:
42
+ """Calculate Attack stat for a species with given IVs, EVs, and level"""
43
+ base_stats = self.get_base_stats_from_species(species)
44
+ return self.calculate_other_stat(base_stats.attack, iv, ev, level, nature_modifier)
45
+
46
+ def get_defense_stat_from_species(self, species: str, iv: int, ev: int, level: int = 100, nature_modifier: float = 1.0) -> int:
47
+ """Calculate Defense stat for a species with given IVs, EVs, and level"""
48
+ base_stats = self.get_base_stats_from_species(species)
49
+ return self.calculate_other_stat(base_stats.defense, iv, ev, level, nature_modifier)
50
+
51
+ def get_special_attack_stat_from_species(self, species: str, iv: int, ev: int, level: int = 100, nature_modifier: float = 1.0) -> int:
52
+ """Calculate Special Attack stat for a species with given IVs, EVs, and level"""
53
+ base_stats = self.get_base_stats_from_species(species)
54
+ return self.calculate_other_stat(base_stats.special_attack, iv, ev, level, nature_modifier)
55
+
56
+ def get_special_defense_stat_from_species(self, species: str, iv: int, ev: int, level: int = 100, nature_modifier: float = 1.0) -> int:
57
+ """Calculate Special Defense stat for a species with given IVs, EVs, and level"""
58
+ base_stats = self.get_base_stats_from_species(species)
59
+ return self.calculate_other_stat(base_stats.special_defense, iv, ev, level, nature_modifier)
60
+
61
+ def get_speed_stat_from_species(self, species: str, iv: int, ev: int, level: int = 100, nature_modifier: float = 1.0) -> int:
62
+ """Calculate Speed stat for a species with given IVs, EVs, and level"""
63
+ base_stats = self.get_base_stats_from_species(species)
64
+ return self.calculate_other_stat(base_stats.speed, iv, ev, level, nature_modifier)
65
+
66
+ def get_substitute_health_from_species(self, species: str, iv: int, ev: int, level: int = 100) -> int:
67
+ """Calculate substitute health for a species (1/4 of max HP)"""
68
+ max_hp = self.get_hp_stat_from_species(species, iv, ev, level)
69
+ return int(max_hp / 4)
70
+
71
+ def calculate_hp(self, base: int, iv: int, ev: int, level: int = 100) -> int:
72
+ """
73
+ Calculate HP using the Pokemon HP formula.
74
+
75
+ Args:
76
+ base (int): Base HP stat
77
+ iv (int): Individual Value (0-31)
78
+ ev (int): Effort Value (0-252)
79
+ level (int): Pokemon level (1-100)
80
+
81
+ Returns:
82
+ int: Calculated HP value
83
+ """
84
+ hp = math.floor(((2 * base + iv + math.floor(ev / 4)) * level) / 100) + level + 10
85
+ return hp
86
+
87
+ def calculate_other_stat(self, base: int, iv: int, ev: int, level: int = 100, nature_modifier: float = 1.0) -> int:
88
+ """
89
+ Calculate other stats (Attack, Defense, Sp. Attack, Sp. Defense, Speed) using the Pokemon stat formula.
90
+
91
+ Args:
92
+ base (int): Base stat value
93
+ iv (int): Individual Value (0-31)
94
+ ev (int): Effort Value (0-252)
95
+ level (int): Pokemon level (1-100)
96
+ nature_modifier (float): Nature modifier (0.9 for hindering, 1.0 for neutral, 1.1 for beneficial)
97
+
98
+ Returns:
99
+ int: Calculated stat value
100
+ """
101
+ stat = math.floor((math.floor(((2 * base + iv + math.floor(ev / 4)) * level) / 100) + 5) * nature_modifier)
102
+ return stat
103
+
104
+ def calculate_hp_ev(self, total_hp: int, base_hp: int, iv: int, level: int = 100) -> int:
105
+ """
106
+ Calculate HP EV from total HP stat using the reverse of the Pokemon HP formula.
107
+
108
+ If the target HP is impossible to achieve with any EV value, returns the EV that
109
+ produces the closest possible HP value. For impossibly high HP values, returns 252 EVs.
110
+ For impossibly low HP values, returns 0 EVs.
111
+
112
+ Args:
113
+ total_hp (int): Total HP stat value
114
+ base_hp (int): Base HP stat
115
+ iv (int): Individual Value (0-31)
116
+ level (int): Pokemon level (1-100)
117
+
118
+ Returns:
119
+ int: Required EV value (0-252) - closest possible value if target is impossible
120
+ """
121
+ if level <= 0:
122
+ raise ValueError("Level must be greater than 0")
123
+
124
+ # Calculate the minimum and maximum possible HP values
125
+ min_hp = self.calculate_hp(base_hp, iv, 0, level)
126
+ max_hp = self.calculate_hp(base_hp, iv, 252, level)
127
+
128
+ # If target HP is impossible, return the closest boundary
129
+ if total_hp <= min_hp:
130
+ return 0 # Return 0 EVs for impossibly low HP
131
+ elif total_hp >= max_hp:
132
+ return 252 # Return 252 EVs for impossibly high HP
133
+
134
+ # Find the EV that gives us the closest HP value
135
+ best_ev = 0
136
+ best_diff = float('inf')
137
+
138
+ for test_ev in range(0, 253, 4): # EVs are always multiples of 4
139
+ test_hp = self.calculate_hp(base_hp, iv, test_ev, level)
140
+ diff = abs(test_hp - total_hp)
141
+
142
+ if diff < best_diff:
143
+ best_diff = diff
144
+ best_ev = test_ev
145
+ elif diff == best_diff and test_ev < best_ev:
146
+ # If we have the same difference, prefer the lower EV
147
+ best_ev = test_ev
148
+
149
+ return best_ev
150
+
151
+ def calculate_other_stat_ev(self, total_stat: int, base_stat: int, iv: int, level: int = 100, nature_modifier: float = 1.0) -> int:
152
+ """
153
+ Calculate EV for other stats (Attack, Defense, Sp. Attack, Sp. Defense, Speed) using the reverse of the Pokemon stat formula.
154
+
155
+ If the target stat is impossible to achieve with any EV value, returns the EV that
156
+ produces the closest possible stat value. For impossibly high stat values, returns 252 EVs.
157
+ For impossibly low stat values, returns 0 EVs.
158
+
159
+ Args:
160
+ total_stat (int): Total stat value
161
+ base_stat (int): Base stat value
162
+ iv (int): Individual Value (0-31)
163
+ level (int): Pokemon level (1-100)
164
+ nature_modifier (float): Nature modifier (0.9 for hindering, 1.0 for neutral, 1.1 for beneficial)
165
+
166
+ Returns:
167
+ int: Required EV value (0-252) - closest possible value if target is impossible
168
+ """
169
+ if level <= 0:
170
+ raise ValueError("Level must be greater than 0")
171
+ if nature_modifier <= 0:
172
+ raise ValueError("Nature modifier must be greater than 0")
173
+
174
+ # Calculate the minimum and maximum possible stat values
175
+ min_stat = self.calculate_other_stat(base_stat, iv, 0, level, nature_modifier)
176
+ max_stat = self.calculate_other_stat(base_stat, iv, 252, level, nature_modifier)
177
+
178
+ # If target stat is impossible, return the closest boundary
179
+ if total_stat <= min_stat:
180
+ return 0 # Return 0 EVs for impossibly low stat
181
+ elif total_stat >= max_stat:
182
+ return 252 # Return 252 EVs for impossibly high stat
183
+
184
+ # Find the EV that gives us the closest stat value
185
+ best_ev = 0
186
+ best_diff = float('inf')
187
+
188
+ for test_ev in range(0, 253, 4): # EVs are always multiples of 4
189
+ test_stat = self.calculate_other_stat(base_stat, iv, test_ev, level, nature_modifier)
190
+ diff = abs(test_stat - total_stat)
191
+
192
+ if diff < best_diff:
193
+ best_diff = diff
194
+ best_ev = test_ev
195
+ elif diff == best_diff and test_ev < best_ev:
196
+ # If we have the same difference, prefer the lower EV
197
+ best_ev = test_ev
198
+
199
+ return best_ev
200
+
201
+ def calculate_attack_ev(self, total_attack: int, base_attack: int, iv: int, level: int = 100, nature_modifier: float = 1.0) -> int:
202
+ """
203
+ Calculate Attack EV from total Attack stat.
204
+
205
+ If the target Attack is impossible to achieve with any EV value, returns the EV that
206
+ produces the closest possible Attack value. For impossibly high Attack values, returns 252 EVs.
207
+ For impossibly low Attack values, returns 0 EVs.
208
+
209
+ Args:
210
+ total_attack (int): Total Attack stat value
211
+ base_attack (int): Base Attack stat
212
+ iv (int): Individual Value (0-31)
213
+ level (int): Pokemon level (1-100)
214
+ nature_modifier (float): Nature modifier (0.9 for hindering, 1.0 for neutral, 1.1 for beneficial)
215
+
216
+ Returns:
217
+ int: Required EV value (0-252) - closest possible value if target is impossible
218
+ """
219
+ return self.calculate_other_stat_ev(total_attack, base_attack, iv, level, nature_modifier)
220
+
221
+ def calculate_defense_ev(self, total_defense: int, base_defense: int, iv: int, level: int = 100, nature_modifier: float = 1.0) -> int:
222
+ """
223
+ Calculate Defense EV from total Defense stat.
224
+
225
+ If the target Defense is impossible to achieve with any EV value, returns the EV that
226
+ produces the closest possible Defense value. For impossibly high Defense values, returns 252 EVs.
227
+ For impossibly low Defense values, returns 0 EVs.
228
+
229
+ Args:
230
+ total_defense (int): Total Defense stat value
231
+ base_defense (int): Base Defense stat
232
+ iv (int): Individual Value (0-31)
233
+ level (int): Pokemon level (1-100)
234
+ nature_modifier (float): Nature modifier (0.9 for hindering, 1.0 for neutral, 1.1 for beneficial)
235
+
236
+ Returns:
237
+ int: Required EV value (0-252) - closest possible value if target is impossible
238
+ """
239
+ return self.calculate_other_stat_ev(total_defense, base_defense, iv, level, nature_modifier)
240
+
241
+ def calculate_special_attack_ev(self, total_special_attack: int, base_special_attack: int, iv: int, level: int = 100, nature_modifier: float = 1.0) -> int:
242
+ """
243
+ Calculate Special Attack EV from total Special Attack stat.
244
+
245
+ If the target Special Attack is impossible to achieve with any EV value, returns the EV that
246
+ produces the closest possible Special Attack value. For impossibly high Special Attack values, returns 252 EVs.
247
+ For impossibly low Special Attack values, returns 0 EVs.
248
+
249
+ Args:
250
+ total_special_attack (int): Total Special Attack stat value
251
+ base_special_attack (int): Base Special Attack stat
252
+ iv (int): Individual Value (0-31)
253
+ level (int): Pokemon level (1-100)
254
+ nature_modifier (float): Nature modifier (0.9 for hindering, 1.0 for neutral, 1.1 for beneficial)
255
+
256
+ Returns:
257
+ int: Required EV value (0-252) - closest possible value if target is impossible
258
+ """
259
+ return self.calculate_other_stat_ev(total_special_attack, base_special_attack, iv, level, nature_modifier)
260
+
261
+ def calculate_special_defense_ev(self, total_special_defense: int, base_special_defense: int, iv: int, level: int = 100, nature_modifier: float = 1.0) -> int:
262
+ """
263
+ Calculate Special Defense EV from total Special Defense stat.
264
+
265
+ If the target Special Defense is impossible to achieve with any EV value, returns the EV that
266
+ produces the closest possible Special Defense value. For impossibly high Special Defense values, returns 252 EVs.
267
+ For impossibly low Special Defense values, returns 0 EVs.
268
+
269
+ Args:
270
+ total_special_defense (int): Total Special Defense stat value
271
+ base_special_defense (int): Base Special Defense stat
272
+ iv (int): Individual Value (0-31)
273
+ level (int): Pokemon level (1-100)
274
+ nature_modifier (float): Nature modifier (0.9 for hindering, 1.0 for neutral, 1.1 for beneficial)
275
+
276
+ Returns:
277
+ int: Required EV value (0-252) - closest possible value if target is impossible
278
+ """
279
+ return self.calculate_other_stat_ev(total_special_defense, base_special_defense, iv, level, nature_modifier)
280
+
281
+ def calculate_speed_ev(self, total_speed: int, base_speed: int, iv: int, level: int = 100, nature_modifier: float = 1.0) -> int:
282
+ """
283
+ Calculate Speed EV from total Speed stat.
284
+
285
+ If the target Speed is impossible to achieve with any EV value, returns the EV that
286
+ produces the closest possible Speed value. For impossibly high Speed values, returns 252 EVs.
287
+ For impossibly low Speed values, returns 0 EVs.
288
+
289
+ Args:
290
+ total_speed (int): Total Speed stat value
291
+ base_speed (int): Base Speed stat
292
+ iv (int): Individual Value (0-31)
293
+ level (int): Pokemon level (1-100)
294
+ nature_modifier (float): Nature modifier (0.9 for hindering, 1.0 for neutral, 1.1 for beneficial)
295
+
296
+ Returns:
297
+ int: Required EV value (0-252) - closest possible value if target is impossible
298
+ """
299
+ return self.calculate_other_stat_ev(total_speed, base_speed, iv, level, nature_modifier)
300
+
301
+ def calculate_hp_ev_from_species(self, species: str, total_hp: int, iv: int, level: int = 100) -> int:
302
+ """
303
+ Calculate HP EV from total HP stat using species name.
304
+
305
+ If the target HP is impossible to achieve with any EV value, returns the EV that
306
+ produces the closest possible HP value. For impossibly high HP values, returns 252 EVs.
307
+ For impossibly low HP values, returns 0 EVs.
308
+
309
+ Args:
310
+ species (str): Pokemon species name
311
+ total_hp (int): Total HP stat value
312
+ iv (int): Individual Value (0-31)
313
+ level (int): Pokemon level (1-100)
314
+
315
+ Returns:
316
+ int: Required EV value (0-252) - closest possible value if target is impossible
317
+ """
318
+ base_stats = self.get_base_stats_from_species(species)
319
+ return self.calculate_hp_ev(total_hp, base_stats.hp, iv, level)
320
+
321
+ def calculate_attack_ev_from_species(self, species: str, total_attack: int, iv: int, level: int = 100, nature_modifier: float = 1.0) -> int:
322
+ """
323
+ Calculate Attack EV from total Attack stat using species name.
324
+
325
+ If the target Attack is impossible to achieve with any EV value, returns the EV that
326
+ produces the closest possible Attack value. For impossibly high Attack values, returns 252 EVs.
327
+ For impossibly low Attack values, returns 0 EVs.
328
+
329
+ Args:
330
+ species (str): Pokemon species name
331
+ total_attack (int): Total Attack stat value
332
+ iv (int): Individual Value (0-31)
333
+ level (int): Pokemon level (1-100)
334
+ nature_modifier (float): Nature modifier (0.9 for hindering, 1.0 for neutral, 1.1 for beneficial)
335
+
336
+ Returns:
337
+ int: Required EV value (0-252) - closest possible value if target is impossible
338
+ """
339
+ base_stats = self.get_base_stats_from_species(species)
340
+ return self.calculate_attack_ev(total_attack, base_stats.attack, iv, level, nature_modifier)
341
+
342
+ def calculate_defense_ev_from_species(self, species: str, total_defense: int, iv: int, level: int = 100, nature_modifier: float = 1.0) -> int:
343
+ """
344
+ Calculate Defense EV from total Defense stat using species name.
345
+
346
+ If the target Defense is impossible to achieve with any EV value, returns the EV that
347
+ produces the closest possible Defense value. For impossibly high Defense values, returns 252 EVs.
348
+ For impossibly low Defense values, returns 0 EVs.
349
+
350
+ Args:
351
+ species (str): Pokemon species name
352
+ total_defense (int): Total Defense stat value
353
+ iv (int): Individual Value (0-31)
354
+ level (int): Pokemon level (1-100)
355
+ nature_modifier (float): Nature modifier (0.9 for hindering, 1.0 for neutral, 1.1 for beneficial)
356
+
357
+ Returns:
358
+ int: Required EV value (0-252) - closest possible value if target is impossible
359
+ """
360
+ base_stats = self.get_base_stats_from_species(species)
361
+ return self.calculate_defense_ev(total_defense, base_stats.defense, iv, level, nature_modifier)
362
+
363
+ def calculate_special_attack_ev_from_species(self, species: str, total_special_attack: int, iv: int, level: int = 100, nature_modifier: float = 1.0) -> int:
364
+ """
365
+ Calculate Special Attack EV from total Special Attack stat using species name.
366
+
367
+ If the target Special Attack is impossible to achieve with any EV value, returns the EV that
368
+ produces the closest possible Special Attack value. For impossibly high Special Attack values, returns 252 EVs.
369
+ For impossibly low Special Attack values, returns 0 EVs.
370
+
371
+ Args:
372
+ species (str): Pokemon species name
373
+ total_special_attack (int): Total Special Attack stat value
374
+ iv (int): Individual Value (0-31)
375
+ level (int): Pokemon level (1-100)
376
+ nature_modifier (float): Nature modifier (0.9 for hindering, 1.0 for neutral, 1.1 for beneficial)
377
+
378
+ Returns:
379
+ int: Required EV value (0-252) - closest possible value if target is impossible
380
+ """
381
+ base_stats = self.get_base_stats_from_species(species)
382
+ return self.calculate_special_attack_ev(total_special_attack, base_stats.special_attack, iv, level, nature_modifier)
383
+
384
+ def calculate_special_defense_ev_from_species(self, species: str, total_special_defense: int, iv: int, level: int = 100, nature_modifier: float = 1.0) -> int:
385
+ """
386
+ Calculate Special Defense EV from total Special Defense stat using species name.
387
+
388
+ If the target Special Defense is impossible to achieve with any EV value, returns the EV that
389
+ produces the closest possible Special Defense value. For impossibly high Special Defense values, returns 252 EVs.
390
+ For impossibly low Special Defense values, returns 0 EVs.
391
+
392
+ Args:
393
+ species (str): Pokemon species name
394
+ total_special_defense (int): Total Special Defense stat value
395
+ iv (int): Individual Value (0-31)
396
+ level (int): Pokemon level (1-100)
397
+ nature_modifier (float): Nature modifier (0.9 for hindering, 1.0 for neutral, 1.1 for beneficial)
398
+
399
+ Returns:
400
+ int: Required EV value (0-252) - closest possible value if target is impossible
401
+ """
402
+ base_stats = self.get_base_stats_from_species(species)
403
+ return self.calculate_special_defense_ev(total_special_defense, base_stats.special_defense, iv, level, nature_modifier)
404
+
405
+ def calculate_speed_ev_from_species(self, species: str, total_speed: int, iv: int, level: int = 100, nature_modifier: float = 1.0) -> int:
406
+ """
407
+ Calculate Speed EV from total Speed stat using species name.
408
+
409
+ If the target Speed is impossible to achieve with any EV value, returns the EV that
410
+ produces the closest possible Speed value. For impossibly high Speed values, returns 252 EVs.
411
+ For impossibly low Speed values, returns 0 EVs.
412
+
413
+ Args:
414
+ species (str): Pokemon species name
415
+ total_speed (int): Total Speed stat value
416
+ iv (int): Individual Value (0-31)
417
+ level (int): Pokemon level (1-100)
418
+ nature_modifier (float): Nature modifier (0.9 for hindering, 1.0 for neutral, 1.1 for beneficial)
419
+
420
+ Returns:
421
+ int: Required EV value (0-252) - closest possible value if target is impossible
422
+ """
423
+ base_stats = self.get_base_stats_from_species(species)
424
+ return self.calculate_speed_ev(total_speed, base_stats.speed, iv, level, nature_modifier)
425
+
426
+ def calculate_all_evs_from_species(self, species: str, stats: Dict[str, int], ivs: Dict[str, int], level: int = 100, nature_modifier: float = 1.0) -> Dict[str, int]:
427
+ """
428
+ Calculate all EV values for a Pokemon using species name and target stats.
429
+
430
+ If any target stat is impossible to achieve with any EV value, returns the EV that
431
+ produces the closest possible stat value. For impossibly high stat values, returns 252 EVs.
432
+ For impossibly low stat values, returns 0 EVs.
433
+
434
+ Args:
435
+ species (str): Pokemon species name
436
+ stats (Dict[str, int]): Dictionary of target stat values with keys: 'hp', 'attack', 'defense', 'special_attack', 'special_defense', 'speed'
437
+ ivs (Dict[str, int]): Dictionary of IV values with keys: 'hp', 'attack', 'defense', 'special_attack', 'special_defense', 'speed'
438
+ level (int): Pokemon level (1-100)
439
+ nature_modifier (float): Nature modifier (0.9 for hindering, 1.0 for neutral, 1.1 for beneficial)
440
+
441
+ Returns:
442
+ Dict[str, int]: Dictionary of required EV values for each stat - closest possible values if targets are impossible
443
+ """
444
+ base_stats = self.get_base_stats_from_species(species)
445
+
446
+ evs = {}
447
+
448
+ # Calculate HP EV (no nature modifier)
449
+ if 'hp' in stats:
450
+ evs['hp'] = self.calculate_hp_ev(stats['hp'], base_stats.hp, ivs.get('hp', 31), level)
451
+
452
+ # Calculate other stat EVs (with nature modifier)
453
+ if 'attack' in stats:
454
+ evs['attack'] = self.calculate_attack_ev(stats['attack'], base_stats.attack, ivs.get('attack', 31), level, nature_modifier)
455
+
456
+ if 'defense' in stats:
457
+ evs['defense'] = self.calculate_defense_ev(stats['defense'], base_stats.defense, ivs.get('defense', 31), level, nature_modifier)
458
+
459
+ if 'special_attack' in stats:
460
+ evs['special_attack'] = self.calculate_special_attack_ev(stats['special_attack'], base_stats.special_attack, ivs.get('special_attack', 31), level, nature_modifier)
461
+
462
+ if 'special_defense' in stats:
463
+ evs['special_defense'] = self.calculate_special_defense_ev(stats['special_defense'], base_stats.special_defense, ivs.get('special_defense', 31), level, nature_modifier)
464
+
465
+ if 'speed' in stats:
466
+ evs['speed'] = self.calculate_speed_ev(stats['speed'], base_stats.speed, ivs.get('speed', 31), level, nature_modifier)
467
+
468
+ return evs
469
+
470
+ def calculate_hp_ev_and_nature_combinations(self, total_hp: int, base_hp: int, iv: int, level: int = 100) -> List[Tuple[int, float]]:
471
+ """
472
+ Calculate all possible EV and nature modifier combinations for HP.
473
+ Note: HP is not affected by nature, so this returns only the EV value with nature_modifier=1.0.
474
+
475
+ Args:
476
+ total_hp (int): Total HP stat value
477
+ base_hp (int): Base HP stat
478
+ iv (int): Individual Value (0-31)
479
+ level (int): Pokemon level (1-100)
480
+
481
+ Returns:
482
+ List[Tuple[int, float]]: List of (EV, nature_modifier) combinations
483
+ """
484
+ # HP is not affected by nature, so there's only one combination
485
+ ev = self.calculate_hp_ev(total_hp, base_hp, iv, level)
486
+ return [(ev, 1.0)]
487
+
488
+ def calculate_other_stat_ev_and_nature_combinations(self, total_stat: int, base_stat: int, iv: int, level: int = 100) -> List[Tuple[int, float]]:
489
+ """
490
+ Calculate all possible EV and nature modifier combinations for other stats.
491
+
492
+ Args:
493
+ total_stat (int): Total stat value
494
+ base_stat (int): Base stat value
495
+ iv (int): Individual Value (0-31)
496
+ level (int): Pokemon level (1-100)
497
+
498
+ Returns:
499
+ List[Tuple[int, float]]: List of (EV, nature_modifier) combinations
500
+ """
501
+ if level <= 0:
502
+ raise ValueError("Level must be greater than 0")
503
+
504
+ combinations = []
505
+
506
+ # Try each nature modifier: hindering (0.9), neutral (1.0), beneficial (1.1)
507
+ nature_modifiers = [0.9, 1.0, 1.1]
508
+
509
+ for nature_modifier in nature_modifiers:
510
+ try:
511
+ ev = self.calculate_other_stat_ev(total_stat, base_stat, iv, level, nature_modifier)
512
+
513
+ # Verify this combination actually produces the target stat (or closest possible)
514
+ calculated_stat = self.calculate_other_stat(base_stat, iv, ev, level, nature_modifier)
515
+
516
+ # Accept the combination if it produces the target stat or the closest possible value
517
+ if calculated_stat == total_stat or abs(calculated_stat - total_stat) <= 1:
518
+ combinations.append((ev, nature_modifier))
519
+
520
+ except (ValueError, ZeroDivisionError):
521
+ # Skip invalid combinations
522
+ continue
523
+
524
+ # Remove duplicates and sort by EV (ascending)
525
+ unique_combinations = list(set(combinations))
526
+ unique_combinations.sort(key=lambda x: x[0])
527
+
528
+ return unique_combinations
529
+
530
+ def calculate_attack_ev_and_nature_combinations(self, total_attack: int, base_attack: int, iv: int, level: int = 100) -> List[Tuple[int, float]]:
531
+ """
532
+ Calculate all possible Attack EV and nature modifier combinations.
533
+
534
+ Args:
535
+ total_attack (int): Total Attack stat value
536
+ base_attack (int): Base Attack stat
537
+ iv (int): Individual Value (0-31)
538
+ level (int): Pokemon level (1-100)
539
+
540
+ Returns:
541
+ List[Tuple[int, float]]: List of (EV, nature_modifier) combinations
542
+ """
543
+ return self.calculate_other_stat_ev_and_nature_combinations(total_attack, base_attack, iv, level)
544
+
545
+ def calculate_defense_ev_and_nature_combinations(self, total_defense: int, base_defense: int, iv: int, level: int = 100) -> List[Tuple[int, float]]:
546
+ """
547
+ Calculate all possible Defense EV and nature modifier combinations.
548
+
549
+ Args:
550
+ total_defense (int): Total Defense stat value
551
+ base_defense (int): Base Defense stat
552
+ iv (int): Individual Value (0-31)
553
+ level (int): Pokemon level (1-100)
554
+
555
+ Returns:
556
+ List[Tuple[int, float]]: List of (EV, nature_modifier) combinations
557
+ """
558
+ return self.calculate_other_stat_ev_and_nature_combinations(total_defense, base_defense, iv, level)
559
+
560
+ def calculate_special_attack_ev_and_nature_combinations(self, total_special_attack: int, base_special_attack: int, iv: int, level: int = 100) -> List[Tuple[int, float]]:
561
+ """
562
+ Calculate all possible Special Attack EV and nature modifier combinations.
563
+
564
+ Args:
565
+ total_special_attack (int): Total Special Attack stat value
566
+ base_special_attack (int): Base Special Attack stat
567
+ iv (int): Individual Value (0-31)
568
+ level (int): Pokemon level (1-100)
569
+
570
+ Returns:
571
+ List[Tuple[int, float]]: List of (EV, nature_modifier) combinations
572
+ """
573
+ return self.calculate_other_stat_ev_and_nature_combinations(total_special_attack, base_special_attack, iv, level)
574
+
575
+ def calculate_special_defense_ev_and_nature_combinations(self, total_special_defense: int, base_special_defense: int, iv: int, level: int = 100) -> List[Tuple[int, float]]:
576
+ """
577
+ Calculate all possible Special Defense EV and nature modifier combinations.
578
+
579
+ Args:
580
+ total_special_defense (int): Total Special Defense stat value
581
+ base_special_defense (int): Base Special Defense stat
582
+ iv (int): Individual Value (0-31)
583
+ level (int): Pokemon level (1-100)
584
+
585
+ Returns:
586
+ List[Tuple[int, float]]: List of (EV, nature_modifier) combinations
587
+ """
588
+ return self.calculate_other_stat_ev_and_nature_combinations(total_special_defense, base_special_defense, iv, level)
589
+
590
+ def calculate_speed_ev_and_nature_combinations(self, total_speed: int, base_speed: int, iv: int, level: int = 100) -> List[Tuple[int, float]]:
591
+ """
592
+ Calculate all possible Speed EV and nature modifier combinations.
593
+
594
+ Args:
595
+ total_speed (int): Total Speed stat value
596
+ base_speed (int): Base Speed stat
597
+ iv (int): Individual Value (0-31)
598
+ level (int): Pokemon level (1-100)
599
+
600
+ Returns:
601
+ List[Tuple[int, float]]: List of (EV, nature_modifier) combinations
602
+ """
603
+ return self.calculate_other_stat_ev_and_nature_combinations(total_speed, base_speed, iv, level)
604
+
605
+ def calculate_hp_ev_and_nature_combinations_from_species(self, species: str, total_hp: int, iv: int, level: int = 100) -> List[Tuple[int, float]]:
606
+ """
607
+ Calculate all possible HP EV and nature modifier combinations using species name.
608
+
609
+ Args:
610
+ species (str): Pokemon species name
611
+ total_hp (int): Total HP stat value
612
+ iv (int): Individual Value (0-31)
613
+ level (int): Pokemon level (1-100)
614
+
615
+ Returns:
616
+ List[Tuple[int, float]]: List of (EV, nature_modifier) combinations
617
+ """
618
+ base_stats = self.get_base_stats_from_species(species)
619
+ return self.calculate_hp_ev_and_nature_combinations(total_hp, base_stats.hp, iv, level)
620
+
621
+ def calculate_attack_ev_and_nature_combinations_from_species(self, species: str, total_attack: int, iv: int, level: int = 100) -> List[Tuple[int, float]]:
622
+ """
623
+ Calculate all possible Attack EV and nature modifier combinations using species name.
624
+
625
+ Args:
626
+ species (str): Pokemon species name
627
+ total_attack (int): Total Attack stat value
628
+ iv (int): Individual Value (0-31)
629
+ level (int): Pokemon level (1-100)
630
+
631
+ Returns:
632
+ List[Tuple[int, float]]: List of (EV, nature_modifier) combinations
633
+ """
634
+ base_stats = self.get_base_stats_from_species(species)
635
+ return self.calculate_attack_ev_and_nature_combinations(total_attack, base_stats.attack, iv, level)
636
+
637
+ def calculate_defense_ev_and_nature_combinations_from_species(self, species: str, total_defense: int, iv: int, level: int = 100) -> List[Tuple[int, float]]:
638
+ """
639
+ Calculate all possible Defense EV and nature modifier combinations using species name.
640
+
641
+ Args:
642
+ species (str): Pokemon species name
643
+ total_defense (int): Total Defense stat value
644
+ iv (int): Individual Value (0-31)
645
+ level (int): Pokemon level (1-100)
646
+
647
+ Returns:
648
+ List[Tuple[int, float]]: List of (EV, nature_modifier) combinations
649
+ """
650
+ base_stats = self.get_base_stats_from_species(species)
651
+ return self.calculate_defense_ev_and_nature_combinations(total_defense, base_stats.defense, iv, level)
652
+
653
+ def calculate_special_attack_ev_and_nature_combinations_from_species(self, species: str, total_special_attack: int, iv: int, level: int = 100) -> List[Tuple[int, float]]:
654
+ """
655
+ Calculate all possible Special Attack EV and nature modifier combinations using species name.
656
+
657
+ Args:
658
+ species (str): Pokemon species name
659
+ total_special_attack (int): Total Special Attack stat value
660
+ iv (int): Individual Value (0-31)
661
+ level (int): Pokemon level (1-100)
662
+
663
+ Returns:
664
+ List[Tuple[int, float]]: List of (EV, nature_modifier) combinations
665
+ """
666
+ base_stats = self.get_base_stats_from_species(species)
667
+ return self.calculate_special_attack_ev_and_nature_combinations(total_special_attack, base_stats.special_attack, iv, level)
668
+
669
+ def calculate_special_defense_ev_and_nature_combinations_from_species(self, species: str, total_special_defense: int, iv: int, level: int = 100) -> List[Tuple[int, float]]:
670
+ """
671
+ Calculate all possible Special Defense EV and nature modifier combinations using species name.
672
+
673
+ Args:
674
+ species (str): Pokemon species name
675
+ total_special_defense (int): Total Special Defense stat value
676
+ iv (int): Individual Value (0-31)
677
+ level (int): Pokemon level (1-100)
678
+
679
+ Returns:
680
+ List[Tuple[int, float]]: List of (EV, nature_modifier) combinations
681
+ """
682
+ base_stats = self.get_base_stats_from_species(species)
683
+ return self.calculate_special_defense_ev_and_nature_combinations(total_special_defense, base_stats.special_defense, iv, level)
684
+
685
+ def calculate_speed_ev_and_nature_combinations_from_species(self, species: str, total_speed: int, iv: int, level: int = 100) -> List[Tuple[int, float]]:
686
+ """
687
+ Calculate all possible Speed EV and nature modifier combinations using species name.
688
+
689
+ Args:
690
+ species (str): Pokemon species name
691
+ total_speed (int): Total Speed stat value
692
+ iv (int): Individual Value (0-31)
693
+ level (int): Pokemon level (1-100)
694
+
695
+ Returns:
696
+ List[Tuple[int, float]]: List of (EV, nature_modifier) combinations
697
+ """
698
+ base_stats = self.get_base_stats_from_species(species)
699
+ return self.calculate_speed_ev_and_nature_combinations(total_speed, base_stats.speed, iv, level)