Fix Switch AI Bug: AI never switching out when it could be OHKO'd (#5089)

* Fix switch AI bug

* Forgot to save a file lol

* Fix infinite loop test compatibility
This commit is contained in:
Pawkkie 2024-08-07 09:58:39 -04:00 committed by GitHub
parent ce74c85e4f
commit 9f845a79e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 25 additions and 6 deletions

View file

@ -197,6 +197,7 @@ enum RandomTag
RNG_TRACE, RNG_TRACE,
RNG_FICKLE_BEAM, RNG_FICKLE_BEAM,
RNG_AI_ABILITY, RNG_AI_ABILITY,
RNG_AI_HASBADODDS,
RNG_SHELL_SIDE_ARM, RNG_SHELL_SIDE_ARM,
}; };

View file

@ -148,8 +148,8 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult)
playerMove = gBattleMons[opposingBattler].moves[i]; playerMove = gBattleMons[opposingBattler].moves[i];
if (playerMove != MOVE_NONE && gMovesInfo[playerMove].power != 0) if (playerMove != MOVE_NONE && gMovesInfo[playerMove].power != 0)
{ {
struct SimulatedDamage dmg = AI_CalcDamage(playerMove, opposingBattler, battler, &effectiveness, FALSE, weather, DMG_ROLL_HIGHEST); damageTaken = AI_CalcDamage(playerMove, opposingBattler, battler, &effectiveness, FALSE, weather, DMG_ROLL_HIGHEST).expected;
if (dmg.expected > maxDamageTaken) if (damageTaken > maxDamageTaken)
maxDamageTaken = damageTaken; maxDamageTaken = damageTaken;
} }
} }
@ -172,7 +172,7 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult)
return FALSE; return FALSE;
// Start assessing whether or not mon has bad odds // Start assessing whether or not mon has bad odds
// Jump straight to swtiching out in cases where mon gets OHKO'd // Jump straight to switching out in cases where mon gets OHKO'd
if (((getsOneShot && gBattleMons[opposingBattler].speed > gBattleMons[battler].speed) // If the player OHKOs and outspeeds OR OHKOs, doesn't outspeed but isn't 2HKO'd if (((getsOneShot && gBattleMons[opposingBattler].speed > gBattleMons[battler].speed) // If the player OHKOs and outspeeds OR OHKOs, doesn't outspeed but isn't 2HKO'd
|| (getsOneShot && gBattleMons[opposingBattler].speed <= gBattleMons[battler].speed && maxDamageDealt < gBattleMons[opposingBattler].hp / 2)) || (getsOneShot && gBattleMons[opposingBattler].speed <= gBattleMons[battler].speed && maxDamageDealt < gBattleMons[opposingBattler].hp / 2))
&& (gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 2 // And the current mon has at least 1/2 their HP, or 1/4 HP and Regenerator && (gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 2 // And the current mon has at least 1/2 their HP, or 1/4 HP and Regenerator
@ -180,7 +180,7 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult)
&& gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 4))) && gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 4)))
{ {
// 50% chance to stay in regardless // 50% chance to stay in regardless
if (Random() % 2 == 0) if (!RandomPercentage(RNG_AI_HASBADODDS, 50))
return FALSE; return FALSE;
// Switch mon out // Switch mon out
@ -203,7 +203,7 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult)
return FALSE; return FALSE;
// 50% chance to stay in regardless // 50% chance to stay in regardless
if (Random() % 2 == 0) if (!RandomPercentage(RNG_AI_HASBADODDS, 50))
return FALSE; return FALSE;
// Switch mon out // Switch mon out

View file

@ -121,7 +121,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Avoid infinite loop if damage
{ {
GIVEN { GIVEN {
ASSUME(gItemsInfo[ITEM_LEFTOVERS].holdEffect == HOLD_EFFECT_LEFTOVERS); ASSUME(gItemsInfo[ITEM_LEFTOVERS].holdEffect == HOLD_EFFECT_LEFTOVERS);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_SMART_MON_CHOICES);
PLAYER(SPECIES_MEOWTH_GALARIAN) { Level(100); Moves(MOVE_GROWL, MOVE_FAKE_OUT, MOVE_HONE_CLAWS); } PLAYER(SPECIES_MEOWTH_GALARIAN) { Level(100); Moves(MOVE_GROWL, MOVE_FAKE_OUT, MOVE_HONE_CLAWS); }
// Scenario courtesy of Duke, who triggered the bug in the first place // Scenario courtesy of Duke, who triggered the bug in the first place
OPPONENT(SPECIES_MEOWTH_GALARIAN) { Level(5); Moves(MOVE_GROWL, MOVE_FAKE_OUT, MOVE_HONE_CLAWS); } OPPONENT(SPECIES_MEOWTH_GALARIAN) { Level(5); Moves(MOVE_GROWL, MOVE_FAKE_OUT, MOVE_HONE_CLAWS); }
@ -357,3 +357,21 @@ AI_SINGLE_BATTLE_TEST("AI won't use trapping behaviour if player only has 1 mon
TURN{ MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_SELF_DESTRUCT); EXPECT_SEND_OUT(opponent, 2); } TURN{ MOVE(player, MOVE_PROTECT); EXPECT_MOVE(opponent, MOVE_SELF_DESTRUCT); EXPECT_SEND_OUT(opponent, 2); }
} }
} }
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if mon would be OKHO'd and they have a good switchin 50% of the time")
{
PASSES_RANDOMLY(50, 100, RNG_AI_HASBADODDS);
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_RHYDON].types[0] == TYPE_GROUND);
ASSUME(gSpeciesInfo[SPECIES_PELIPPER].types[0] == TYPE_WATER);
ASSUME(gSpeciesInfo[SPECIES_PELIPPER].types[1] == TYPE_FLYING);
ASSUME(gMovesInfo[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC);
ASSUME(gMovesInfo[MOVE_EARTHQUAKE].type == TYPE_GROUND);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES);
PLAYER(SPECIES_ELECTRODE) { Moves(MOVE_THUNDERBOLT, MOVE_THUNDER_WAVE, MOVE_THUNDER_SHOCK); }
OPPONENT(SPECIES_PELIPPER) { Moves(MOVE_EARTHQUAKE); };
OPPONENT(SPECIES_RHYDON) { Moves(MOVE_EARTHQUAKE); Ability(ABILITY_ROCK_HEAD); }
} WHEN {
TURN { MOVE(player, MOVE_THUNDERBOLT) ; EXPECT_SWITCH(opponent, 1); }
}
}