197 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			197 lines
		
	
	
		
			7.1 KiB
		
	
	
	
		
			Python
		
	
	
	
"""
 | 
						|
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:
 | 
						|
        possible_moves = [i for i in range(data.constants["MOVES_COUNT"]) if i not in expanded_blacklist]
 | 
						|
    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
 |