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
|