Fix Trianer AI glitch of Sabrina's Alakazam only using Recover against other Psychic Pokemon - pret/pokered GitHub Wiki
Trainer AI mechanism
In Pokémon Red, trainer AI mostly chooses moves at random, but up to three move-choice modification routes can alter the final pick, depending on the trainer.
You can see the modification choices of different trainers in data/trainers/move_choices.asm
move_choices 1, 3 ; SABRINA
move_choices 1, 2 ; GENTLEMAN
move_choices 1, 3 ; RIVAL2
move_choices 1, 3 ; RIVAL3
move_choices 1, 2, 3 ; LORELEI
The example above shows that Sabrina uses modification routes 1 and 3.
Those routes live in engine/battle/trainer_ai.asm. For a full breakdown of how they work, see the Pokémon Red/Blue/Yellow Trainer AI wiki.
How to fix Sabrina AI glitch
The bug is in AIMoveChoiceModification3 in engine/battle/trainer_ai.asm. That route prioritizes moves based on type effectiveness. For each of the AI Pokémon's moves, it applies the following checks:
- If the move's type is effective against the player's Pokémon (whether or not it deals damage), encourage that move.
- If the move is ineffective, scan the other available moves for better alternatives (a damaging move of a different type, or a special move such as one with
SPECIAL_DAMAGE_EFFECT). If a better move exists, discourage the current move. - Only moves with the highest priority remain in contention during the final random choice.
The problem is in step 2: the AI also treats the move currently being evaluated as an alternative. If that move is one of the special cases (for example, a move with SPECIAL_DAMAGE_EFFECT), it can count against itself.
Take Sabrina's Alakazam as an example. Its moveset is:
Psybeam: Psychic Type
Recover: Normal Type
Psywave: Psychic Type
Reflect: Psychic Type
If the player brings a Psychic-type Pokémon, no move is super effective, so step 1 does not apply. Psybeam, Psywave, and Reflect all take the ineffective branch. Psywave is then treated as a "better" move because it has SPECIAL_DAMAGE_EFFECT. Without skipping self-comparison, Psywave discourages itself along with the other Psychic moves, leaving only Recover with a high enough priority—so Alakazam spams Recover.
The fix is straightforward: when running step 2, skip the move currently under evaluation. Store its move number before the loop and continue past it when it comes up again:
.notEffectiveMove ; discourages non-effective moves if better moves are available
push hl
push de
push bc
ld a, [wEnemyMoveType]
ld d, a
+ld a, [wEnemyMoveNum]
+ld e, a
ld hl, wEnemyMonMoves ; enemy moves
ld b, NUM_MOVES + 1
ld c, $0
.loopMoves
dec b
jr z, .done
ld a, [hli]
and a
jr z, .done
call ReadMove
+ld a, [wEnemyMoveNum]
+cp e
+jr z, .loopMoves
ld a, [wEnemyMoveEffect]
Bonus: Lorelei's Dewgong glitch
In Pokémon Red, if you bring a Poison- or Fighting-type Pokémon against Lorelei's Dewgong, Dewgong will endlessly use Rest. This happens because of step 1: Rest is a psychic type move, which is super effective against Poison and Fighting, so Rest gets top priority and becomes the only viable option.
Pokémon Yellow adds a special-case workaround for Lorelei's Dewgong that ignores type effectiveness entirely. A simpler, more general fix is to skip step 1 for moves that deal no damage:
.nextMove
dec b
ret z ; processed all 4 moves
inc hl
ld a, [de]
and a
ret z ; no more moves in move set
inc de
call ReadMove
push hl
push bc
push de
callfar AIGetTypeEffectiveness
pop de
pop bc
pop hl
+ld a, [wEnemyMovePower]
+and a
+jr z, .nextMove
After this fix, Rest is no longer preferred by modifier 3 because it has power = 0, even though its type is Psychic.