| 
									
										
										
										
											2023-11-12 13:39:34 -08:00
										 |  |  | """
 | 
					
						
							|  |  |  | Functions related to pokemon species and moves | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | import time | 
					
						
							|  |  |  | from typing import TYPE_CHECKING, Dict, List, Set, Optional, Tuple | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from .data import SpeciesData, data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if TYPE_CHECKING: | 
					
						
							|  |  |  |     from random import Random | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _damaging_moves = frozenset({ | 
					
						
							|  |  |  |       1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  13, | 
					
						
							|  |  |  |      16,  17,  20,  21,  22,  23,  24,  25,  26,  27,  29,  30, | 
					
						
							|  |  |  |      31,  33,  34,  35,  36,  37,  38,  40,  41,  42,  44,  51, | 
					
						
							|  |  |  |      52,  53,  55,  56,  58,  59,  60,  61,  62,  63,  64,  65, | 
					
						
							|  |  |  |      66,  67,  69,  71,  72,  75,  76,  80,  82,  83,  84,  85, | 
					
						
							|  |  |  |      87,  88,  89,  91,  93,  94,  98,  99, 101, 121, 122, 123, | 
					
						
							|  |  |  |     124, 125, 126, 128, 129, 130, 131, 132, 136, 140, 141, 143, | 
					
						
							|  |  |  |     145, 146, 149, 152, 154, 155, 157, 158, 161, 162, 163, 167, | 
					
						
							|  |  |  |     168, 172, 175, 177, 179, 181, 183, 185, 188, 189, 190, 192, | 
					
						
							|  |  |  |     196, 198, 200, 202, 205, 209, 210, 211, 216, 217, 218, 221, | 
					
						
							|  |  |  |     222, 223, 224, 225, 228, 229, 231, 232, 233, 237, 238, 239, | 
					
						
							|  |  |  |     242, 245, 246, 247, 248, 250, 251, 253, 257, 263, 265, 267, | 
					
						
							|  |  |  |     276, 279, 280, 282, 284, 290, 292, 295, 296, 299, 301, 302, | 
					
						
							|  |  |  |     304, 305, 306, 307, 308, 309, 310, 311, 314, 315, 317, 318, | 
					
						
							|  |  |  |     323, 324, 325, 326, 327, 328, 330, 331, 332, 333, 337, 338, | 
					
						
							|  |  |  |     340, 341, 342, 343, 344, 345, 348, 350, 351, 352, 353, 354 | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _move_types = [ | 
					
						
							|  |  |  |      0,  0,  1,  0,  0,  0,  0, 10, 15, 13,  0,  0,  0,  0,  0, | 
					
						
							|  |  |  |      0,  2,  2,  0,  2,  0,  0, 12,  0,  1,  0,  1,  1,  4,  0, | 
					
						
							|  |  |  |      0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  3,  6,  6,  0, 17, | 
					
						
							|  |  |  |      0,  0,  0,  0,  0,  0,  3, 10, 10, 15, 11, 11, 11, 15, 15, | 
					
						
							|  |  |  |     14, 11, 15,  0,  2,  2,  1,  1,  1,  1,  0, 12, 12, 12,  0, | 
					
						
							|  |  |  |     12, 12,  3, 12, 12, 12,  6, 16, 10, 13, 13, 13, 13,  5,  4, | 
					
						
							|  |  |  |      4,  4,  3, 14, 14, 14, 14, 14,  0,  0, 14,  7,  0,  0,  0, | 
					
						
							|  |  |  |      0,  0,  0,  0,  7, 11,  0, 14, 14, 15, 14,  0,  0,  0,  2, | 
					
						
							|  |  |  |      0,  0,  7,  3,  3,  4, 10, 11, 11,  0,  0,  0,  0, 14, 14, | 
					
						
							|  |  |  |      0,  1,  0, 14,  3,  0,  6,  0,  2,  0, 11,  0, 12,  0, 14, | 
					
						
							|  |  |  |      0,  3, 11,  0,  0,  4, 14,  5,  0,  0,  0,  0,  0,  0,  0, | 
					
						
							|  |  |  |      0,  0,  1, 17,  6,  0,  7, 10,  0,  9,  0,  0,  2, 12,  1, | 
					
						
							|  |  |  |      7, 15,  0,  1,  0, 17,  0,  0,  3,  4, 11,  4, 13,  0,  7, | 
					
						
							|  |  |  |      0, 15,  1,  4,  0, 16,  5, 12,  0,  0,  5,  0,  0,  0, 13, | 
					
						
							|  |  |  |      6,  8,  0,  0,  0,  0,  0,  0,  0,  0,  0, 10,  4,  1,  6, | 
					
						
							|  |  |  |     16,  0,  0, 17,  0,  0,  8,  8,  1,  0, 12,  0,  0,  1, 16, | 
					
						
							|  |  |  |     11, 10, 17, 14,  0,  0,  5,  7, 14,  1, 11, 17,  0,  0,  0, | 
					
						
							|  |  |  |      0,  0, 10, 15, 17, 17, 10, 17,  0,  1,  0,  0,  0, 13, 17, | 
					
						
							|  |  |  |      0, 14, 14,  0,  0, 12,  1, 14,  0,  1,  1,  0, 17,  0, 10, | 
					
						
							|  |  |  |     14, 14,  0,  7, 17,  0, 11,  1,  0,  6, 14, 14,  2,  0, 10, | 
					
						
							|  |  |  |      4, 15, 12,  0,  0,  3,  0, 10, 11,  8,  7,  0, 12, 17,  2, | 
					
						
							|  |  |  |     10,  0,  5,  6,  8, 12,  0, 14, 11,  6,  7, 14,  1,  4, 15, | 
					
						
							|  |  |  |     11, 12,  2, 15,  8,  0,  0, 16, 12,  1,  2,  4,  3,  0, 13, | 
					
						
							|  |  |  |     12, 11, 14, 12, 16,  5, 13, 11,  8, 14 | 
					
						
							|  |  |  | ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _moves_by_type: Dict[int, List[int]] = {} | 
					
						
							|  |  |  | for move, type in enumerate(_move_types): | 
					
						
							|  |  |  |     _moves_by_type.setdefault(type, []).append(move) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _move_blacklist = frozenset({ | 
					
						
							|  |  |  |     0,    # MOVE_NONE | 
					
						
							|  |  |  |     165,  # Struggle | 
					
						
							|  |  |  |     15,   # Cut | 
					
						
							|  |  |  |     148,  # Flash | 
					
						
							|  |  |  |     249,  # Rock Smash | 
					
						
							|  |  |  |     70,   # Strength | 
					
						
							|  |  |  |     57,   # Surf | 
					
						
							|  |  |  |     19,   # Fly | 
					
						
							|  |  |  |     291,  # Dive | 
					
						
							|  |  |  |     127   # Waterfall | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _legendary_pokemon = frozenset({ | 
					
						
							|  |  |  |     'Mew', | 
					
						
							|  |  |  |     'Mewtwo', | 
					
						
							|  |  |  |     'Articuno', | 
					
						
							|  |  |  |     'Zapdos', | 
					
						
							|  |  |  |     'Moltres', | 
					
						
							|  |  |  |     'Lugia', | 
					
						
							|  |  |  |     'Ho-oh', | 
					
						
							|  |  |  |     'Raikou', | 
					
						
							|  |  |  |     'Suicune', | 
					
						
							|  |  |  |     'Entei', | 
					
						
							|  |  |  |     'Celebi', | 
					
						
							|  |  |  |     'Groudon', | 
					
						
							|  |  |  |     'Kyogre', | 
					
						
							|  |  |  |     'Rayquaza', | 
					
						
							|  |  |  |     'Latios', | 
					
						
							|  |  |  |     'Latias', | 
					
						
							|  |  |  |     'Registeel', | 
					
						
							|  |  |  |     'Regirock', | 
					
						
							|  |  |  |     'Regice', | 
					
						
							|  |  |  |     'Jirachi', | 
					
						
							|  |  |  |     'Deoxys' | 
					
						
							|  |  |  | }) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_random_species( | 
					
						
							|  |  |  |         random: "Random", | 
					
						
							|  |  |  |         candidates: List[Optional[SpeciesData]], | 
					
						
							|  |  |  |         nearby_bst: Optional[int] = None, | 
					
						
							|  |  |  |         species_type: Optional[int] = None, | 
					
						
							|  |  |  |         allow_legendaries: bool = True) -> SpeciesData: | 
					
						
							|  |  |  |     candidates: List[SpeciesData] = [species for species in candidates if species is not None] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if species_type is not None: | 
					
						
							|  |  |  |         candidates = [species for species in candidates if species_type in species.types] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if not allow_legendaries: | 
					
						
							|  |  |  |         candidates = [species for species in candidates if species.label not in _legendary_pokemon] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if nearby_bst is not None: | 
					
						
							|  |  |  |         def has_nearby_bst(species: SpeciesData, max_percent_different: int) -> bool: | 
					
						
							|  |  |  |             return abs(sum(species.base_stats) - nearby_bst) < nearby_bst * (max_percent_different / 100) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         max_percent_different = 10 | 
					
						
							|  |  |  |         bst_filtered_candidates = [species for species in candidates if has_nearby_bst(species, max_percent_different)] | 
					
						
							|  |  |  |         while len(bst_filtered_candidates) == 0: | 
					
						
							|  |  |  |             max_percent_different += 10 | 
					
						
							|  |  |  |             bst_filtered_candidates = [ | 
					
						
							|  |  |  |                 species | 
					
						
							|  |  |  |                 for species in candidates | 
					
						
							|  |  |  |                 if has_nearby_bst(species, max_percent_different) | 
					
						
							|  |  |  |             ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         candidates = bst_filtered_candidates | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return random.choice(candidates) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_random_type(random: "Random") -> int: | 
					
						
							|  |  |  |     picked_type = random.randrange(0, 18) | 
					
						
							|  |  |  |     while picked_type == 9:  # Don't pick the ??? type | 
					
						
							|  |  |  |         picked_type = random.randrange(0, 18) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return picked_type | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_random_move( | 
					
						
							|  |  |  |         random: "Random", | 
					
						
							|  |  |  |         blacklist: Optional[Set[int]] = None, | 
					
						
							|  |  |  |         type_bias: int = 0, | 
					
						
							|  |  |  |         normal_bias: int = 0, | 
					
						
							|  |  |  |         type_target: Optional[Tuple[int, int]] = None) -> int: | 
					
						
							|  |  |  |     expanded_blacklist = _move_blacklist | (blacklist if blacklist is not None else set()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     bias = random.random() * 100 | 
					
						
							|  |  |  |     if bias < type_bias: | 
					
						
							|  |  |  |         pass  # Keep type_target unchanged | 
					
						
							|  |  |  |     elif bias < type_bias + ((100 - type_bias) * (normal_bias / 100)): | 
					
						
							|  |  |  |         type_target = (0, 0) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         type_target = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     chosen_move = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # The blacklist is relatively small, so if we don't need to restrict | 
					
						
							|  |  |  |     # ourselves to any particular types, it's usually much faster to pick | 
					
						
							|  |  |  |     # a random number and hope it works. Limit this to 5 tries in case the | 
					
						
							|  |  |  |     # blacklist is actually significant enough to make this unlikely to work. | 
					
						
							|  |  |  |     if type_target is None: | 
					
						
							|  |  |  |         remaining_attempts = 5 | 
					
						
							|  |  |  |         while remaining_attempts > 0: | 
					
						
							|  |  |  |             remaining_attempts -= 1 | 
					
						
							|  |  |  |             chosen_move = random.randrange(0, data.constants["MOVES_COUNT"]) | 
					
						
							|  |  |  |             if chosen_move not in expanded_blacklist: | 
					
						
							|  |  |  |                 return chosen_move | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             chosen_move = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # We're either matching types or failed to pick a move above | 
					
						
							|  |  |  |     if type_target is None: | 
					
						
							| 
									
										
										
										
											2023-11-22 10:21:15 -08:00
										 |  |  |         possible_moves = [i for i in range(data.constants["MOVES_COUNT"]) if i not in expanded_blacklist] | 
					
						
							| 
									
										
										
										
											2023-11-12 13:39:34 -08:00
										 |  |  |     else: | 
					
						
							|  |  |  |         possible_moves = [move for move in _moves_by_type[type_target[0]] if move not in expanded_blacklist] + \ | 
					
						
							|  |  |  |                             [move for move in _moves_by_type[type_target[1]] if move not in expanded_blacklist] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if len(possible_moves) == 0: | 
					
						
							|  |  |  |         return get_random_move(random, None, type_bias, normal_bias, type_target) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return random.choice(possible_moves) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def get_random_damaging_move(random: "Random", blacklist: Optional[Set[int]] = None) -> int: | 
					
						
							|  |  |  |     expanded_blacklist = _move_blacklist | (blacklist if blacklist is not None else set()) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     move_options = list(_damaging_moves) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     move = random.choice(move_options) | 
					
						
							|  |  |  |     while move in expanded_blacklist: | 
					
						
							|  |  |  |         move = random.choice(move_options) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return move |