Skip to content

Commit ada8c73

Browse files
limited expansion of strict testing to early gens
1 parent 4318f77 commit ada8c73

File tree

4 files changed

+82
-32
lines changed

4 files changed

+82
-32
lines changed

src/poke_env/battle/abstract_battle.py

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -496,14 +496,21 @@ def parse_message(self, split_message: List[str]):
496496
while event[-1].startswith("[spread]"):
497497
event = event[:-1]
498498

499-
if event[-1] in {"[from] lockedmove", "[from]lockedmove"}:
499+
if event[-1] in {
500+
"[from] lockedmove",
501+
"[from]lockedmove",
502+
"[from] Sky Attack",
503+
}:
500504
use = False
501505
reveal = False
502506
event = event[:-1]
503507

504508
if event[-1] in {"[from] Pursuit", "[from]Pursuit", "[zeffect]"}:
505509
event = event[:-1]
506510

511+
if event[-1] == "[from] Sleep Talk":
512+
event[-1] = "[from] move: Sleep Talk"
513+
507514
if event[-1].startswith("[anim]"):
508515
event = event[:-1]
509516

@@ -555,8 +562,10 @@ def parse_message(self, split_message: List[str]):
555562
self.battle_tag,
556563
self.turn,
557564
)
558-
if event[-1] == "[from] Magic Coat":
559-
return
565+
if event[-1] == "[from] Magic Coat" or event[-1] == "[from] Mirror Move":
566+
use = False
567+
reveal = False
568+
event = event[:-1]
560569

561570
while event[-1] == "[still]":
562571
event = event[:-1]
@@ -614,8 +623,8 @@ def parse_message(self, split_message: List[str]):
614623
# We're setting use=False in the one (with the override) in order to prevent two pps from being used
615624
# incorrectly.
616625
mon.moved(move, failed=failed, use=False, reveal=reveal)
617-
overriden = mon.moves[Move.retrieve_id(overridden_move)]
618-
overriden.use(pressure)
626+
overridden = mon.moves[Move.retrieve_id(overridden_move)]
627+
overridden.use(pressure)
619628
else:
620629
if not failed and move in {
621630
"Sleep Talk",
@@ -742,6 +751,10 @@ def parse_message(self, split_message: List[str]):
742751
types = event[4]
743752
mon.start_effect(effect, details=types)
744753
else:
754+
if effect == "Mimic":
755+
mon._mimic_move = Move(
756+
Move.retrieve_id(event[4]), gen=self._data.gen, from_mimic=True
757+
)
745758
mon.start_effect(effect)
746759

747760
if mon.is_dynamaxed:
@@ -778,6 +791,11 @@ def parse_message(self, split_message: List[str]):
778791
mv = mon.moves[to_id_str(event[4])]
779792
# Don't let current pp exceed max pp
780793
mv._current_pp = min(mv._current_pp + 10, mv.max_pp)
794+
if effect == "move: Mimic":
795+
mon = self.get_pokemon(target)
796+
mon._mimic_move = Move(
797+
Move.retrieve_id(event[4]), gen=self._data.gen, from_mimic=True
798+
)
781799
elif target != "": # ['', '-activate', '', 'move: Splash']
782800
self.get_pokemon(target).start_effect(effect)
783801
elif event[1] == "-status":

src/poke_env/battle/move.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,23 @@ class Move:
7777
"_base_power_override",
7878
"_current_pp",
7979
"_dynamaxed_move",
80+
"_from_mimic",
8081
"_gen",
8182
"_moves_dict",
8283
"_request_target",
8384
)
8485

85-
def __init__(self, move_id: str, gen: int, raw_id: Optional[str] = None):
86+
def __init__(
87+
self,
88+
move_id: str,
89+
gen: int,
90+
raw_id: Optional[str] = None,
91+
from_mimic: bool = False,
92+
):
8693
self._id = move_id
8794
self._base_power_override = None
8895
self._gen = gen
96+
self._from_mimic = from_mimic
8997
self._moves_dict = GenData.from_gen(gen).moves
9098

9199
if move_id.startswith("hiddenpower") and raw_id is not None:
@@ -440,7 +448,10 @@ def max_pp(self) -> int:
440448
:return: The move's max pp.
441449
:rtype: int
442450
"""
443-
return self.entry["pp"] * 8 // 5
451+
max_pp = self.entry["pp"] * 8 // 5
452+
if self._gen < 3 and not self._from_mimic:
453+
max_pp = min(max_pp, 61)
454+
return max_pp
444455

445456
@property
446457
def n_hit(self) -> Tuple[int, int]:

src/poke_env/battle/pokemon.py

Lines changed: 44 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class Pokemon:
3333
"_last_request",
3434
"_level",
3535
"_max_hp",
36+
"_mimic_move",
3637
"_forme_change_ability",
3738
"_moves",
3839
"_must_recharge",
@@ -125,6 +126,7 @@ def __init__(
125126
self._temporary_ability: Optional[str] = None
126127
self._forme_change_ability: Optional[str] = None
127128
self._temporary_types: List[PokemonType] = []
129+
self._mimic_move: Optional[Move] = None
128130

129131
if request_pokemon:
130132
self.update_from_request(request_pokemon)
@@ -155,6 +157,8 @@ def __str__(self) -> str:
155157
def _add_move(self, move_id: str) -> Optional[Move]:
156158
"""Store the move if applicable."""
157159
id_ = Move.retrieve_id(move_id)
160+
if id_ in self.moves:
161+
return self.moves[id_]
158162
if not Move.should_be_stored(id_, self._data.gen):
159163
return None
160164
if id_ not in self._moves:
@@ -210,6 +214,8 @@ def check_consistency(self, pkmn_request: Dict[str, Any], player_role: str):
210214
pkmn_request["active"] == self.active
211215
), f"{pkmn_request['active']} != {self.active}\nrequest: {pkmn_request}"
212216
if self.item == "unknown_item":
217+
# needed for ability initialization in start of game,
218+
# done anyway in update_from_request()
213219
self._item = pkmn_request["item"]
214220
if self._data.gen > 4:
215221
assert pkmn_request["item"] == (
@@ -222,11 +228,18 @@ def check_consistency(self, pkmn_request: Dict[str, Any], player_role: str):
222228
), f"{pkmn_request['condition']} != {self.hp_status}\nrequest: {pkmn_request}"
223229
if self.base_species == "mew":
224230
return
225-
for move_request, move in zip(pkmn_request["moves"], self.moves.values()):
226-
assert Move.retrieve_id(move_request) == Move.retrieve_id(
227-
move.id
228-
), f"{Move.retrieve_id(move_request)} != {Move.retrieve_id(move.id)}\nrequest: {pkmn_request}"
231+
if not (
232+
self._mimic_move is not None
233+
and self._mimic_move.id in [m.id for m in self._moves.values()]
234+
):
235+
# Ensures no duplicate move due to mimic copying an already-known move
236+
for move_request, move in zip(pkmn_request["moves"], self.moves.values()):
237+
assert Move.retrieve_id(move_request) == Move.retrieve_id(
238+
move.id
239+
), f"{Move.retrieve_id(move_request)} != {Move.retrieve_id(move.id)}\nrequest: {pkmn_request}"
229240
if self.ability is None:
241+
# needed for ability initialization in start of game,
242+
# done anyway in update_from_request()
230243
self.ability = pkmn_request["baseAbility"]
231244
assert pkmn_request["baseAbility"] == (
232245
self.base_ability or ""
@@ -237,10 +250,7 @@ def check_consistency(self, pkmn_request: Dict[str, Any], player_role: str):
237250
), f"{pkmn_request['ability']} != {self.ability or ''}"
238251

239252
def check_move_consistency(self, active_request: Dict[str, Any]):
240-
if (
241-
self.base_species in ["ditto", "mew"]
242-
or "copycat" in [m.id for m in self.moves.values()]
243-
):
253+
if self.base_species in ["ditto", "mew"]:
244254
return
245255
for move_request in active_request["moves"]:
246256
matches = [
@@ -252,7 +262,8 @@ def check_move_consistency(self, active_request: Dict[str, Any]):
252262
if not matches:
253263
continue
254264
move = matches[0]
255-
if "pp" in move_request and self._data.gen not in [7, 8]:
265+
if "pp" in move_request and self._data.gen not in [1, 2, 3, 7, 8]:
266+
# exclude early gens because of unreliable Showdown event messages
256267
# exclude gen 7 and 8 because of Z-move and Max Move PP untrackability
257268
assert (
258269
move_request["pp"] == move.current_pp
@@ -368,6 +379,7 @@ def faint(self):
368379
self._current_hp = 0
369380
self._status = Status.FNT
370381
self.temporary_ability = None
382+
self._mimic_move = None
371383
self._clear_effects()
372384

373385
def forme_change(self, species: str):
@@ -563,6 +575,7 @@ def switch_out(self, fields: Dict[Field, int]):
563575
self._protect_counter = 0
564576
self.temporary_ability = None
565577
self._temporary_types = []
578+
self._mimic_move = None
566579

567580
if self._status == Status.TOX:
568581
self._status_counter = 0
@@ -603,7 +616,7 @@ def _update_from_pokedex(self, species: str, store_species: bool = True):
603616
self._possible_abilities = [
604617
to_id_str(ability) for ability in dex_entry["abilities"].values()
605618
]
606-
if len(self._possible_abilities) == 1:
619+
if len(self._possible_abilities) == 1 and self._data.gen >= 3:
607620
self.ability = self._possible_abilities[0]
608621
else:
609622
self.forme_change_ability = None
@@ -955,6 +968,18 @@ def first_turn(self) -> bool:
955968
"""
956969
return self._first_turn
957970

971+
@property
972+
def forme_change_ability(self) -> Optional[str]:
973+
"""
974+
:return: The pokemon's ability after changing forme. None if the pokemon hasn't changed forme.
975+
:rtype: str, optional
976+
"""
977+
return self._forme_change_ability
978+
979+
@forme_change_ability.setter
980+
def forme_change_ability(self, ability: Optional[str]):
981+
self._forme_change_ability = to_id_str(ability) if ability is not None else None
982+
958983
@property
959984
def gender(self) -> Optional[PokemonGender]:
960985
"""
@@ -1036,25 +1061,21 @@ def max_hp(self) -> int:
10361061
"""
10371062
return self._max_hp or 0
10381063

1039-
@property
1040-
def forme_change_ability(self) -> Optional[str]:
1041-
"""
1042-
:return: The pokemon's ability after changing forme. None if the pokemon hasn't changed forme.
1043-
:rtype: str, optional
1044-
"""
1045-
return self._forme_change_ability
1046-
1047-
@forme_change_ability.setter
1048-
def forme_change_ability(self, ability: Optional[str]):
1049-
self._forme_change_ability = to_id_str(ability) if ability is not None else None
1050-
10511064
@property
10521065
def moves(self) -> Dict[str, Move]:
10531066
"""
10541067
:return: A dictionary of the pokemon's known moves.
10551068
:rtype: Dict[str, Move]
10561069
"""
1057-
return self._moves
1070+
if self._mimic_move is not None:
1071+
return dict(
1072+
[
1073+
(k, v) if k != "mimic" else (self._mimic_move.id, self._mimic_move)
1074+
for k, v in self._moves.items()
1075+
]
1076+
)
1077+
else:
1078+
return self._moves
10581079

10591080
@property
10601081
def must_recharge(self) -> bool:

strict_integration_tests/test_strict.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ async def simple_cross_evaluation(n_battles, players):
1515

1616
@pytest.mark.asyncio
1717
async def test_random_players():
18-
for gen in range(4, 10):
18+
for gen in range(1, 10):
1919
players = [
2020
RandomPlayer(
2121
battle_format=f"gen{gen}randombattle", strict_battle_tracking=True
@@ -25,5 +25,5 @@ async def test_random_players():
2525
),
2626
]
2727
await asyncio.wait_for(
28-
simple_cross_evaluation(100, players=players), timeout=60
28+
simple_cross_evaluation(100, players=players), timeout=150
2929
)

0 commit comments

Comments
 (0)