AI Tests + accompanying bugfixes for Skill Swap, Worry Seed, weather setting in double battles, and Discharging into an ally's lightningrod (#7297)

This commit is contained in:
surskitty 2025-07-10 16:08:14 -04:00 committed by GitHub
parent 4e558af76e
commit 424c127b8b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 111 additions and 23 deletions

View File

@ -39,9 +39,10 @@ enum AIScore
DECENT_EFFECT = 2,
GOOD_EFFECT = 3,
BEST_EFFECT = 4,
PERFECT_EFFECT = 10,
BAD_EFFECT = -1,
AWFUL_EFFECT = -3,
WORST_EFFECT = -5
WORST_EFFECT = -10
};
// AI_TryToFaint

View File

@ -5279,7 +5279,7 @@ s32 BattlerBenefitsFromAbilityScore(u32 battler, u32 ability, struct AiLogicData
case ABILITY_POWER_SPOT:
case ABILITY_VICTORY_STAR:
if (IsDoubleBattle() && IsBattlerAlive(BATTLE_PARTNER(battler)) && aiData->abilities[BATTLE_PARTNER(battler)] != ability)
return GOOD_EFFECT;
return BEST_EFFECT;
break;
case ABILITY_GUTS:
if (HasMoveWithCategory(battler, DAMAGE_CATEGORY_PHYSICAL) && gBattleMons[battler].status1 & (STATUS1_CAN_MOVE))
@ -5295,7 +5295,7 @@ s32 BattlerBenefitsFromAbilityScore(u32 battler, u32 ability, struct AiLogicData
case ABILITY_VITAL_SPIRIT:
if (HasMoveWithEffect(battler, EFFECT_REST))
return WORST_EFFECT;
return NO_INCREASE;
break;
case ABILITY_INTIMIDATE:
{
u32 abilityDef = aiData->abilities[FOE(battler)];
@ -5335,6 +5335,8 @@ s32 BattlerBenefitsFromAbilityScore(u32 battler, u32 ability, struct AiLogicData
return WEAK_EFFECT;
if (gBattleMons[battler].status1 & (STATUS1_TOXIC_POISON))
return BEST_EFFECT;
if (gBattleMons[battler].status1 & STATUS1_ANY)
return NO_INCREASE;
break;
// Also used to Simple Beam SIMPLE_BEAM.
case ABILITY_SIMPLE:
@ -5354,11 +5356,13 @@ s32 BattlerBenefitsFromAbilityScore(u32 battler, u32 ability, struct AiLogicData
return NO_INCREASE;
}
return GOOD_EFFECT;
case ABILITY_NONE:
return NO_INCREASE;
default:
break;
}
return NO_INCREASE;
return WEAK_EFFECT;
}
u32 GetThinkingBattler(u32 battler)

View File

@ -281,12 +281,22 @@ AI_SINGLE_BATTLE_TEST("AI uses Wide Guard against Earthquake when opponent would
AI_SINGLE_BATTLE_TEST("AI uses Worry Seed against Rest")
{
u32 move;
u64 aiFlags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT;
PARAMETRIZE { move = MOVE_REST; }
PARAMETRIZE { move = MOVE_EXTREME_SPEED; }
PARAMETRIZE { move = MOVE_REST; aiFlags |= AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION; }
PARAMETRIZE { move = MOVE_EXTREME_SPEED; aiFlags |= AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION; }
GIVEN {
PLAYER(SPECIES_ZUBAT) { Moves(MOVE_REST, MOVE_SLEEP_TALK, MOVE_AIR_CUTTER); }
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT | AI_FLAG_PREDICT_MOVE);
OPPONENT(SPECIES_BUDEW) { Moves(MOVE_WORRY_SEED, MOVE_SLUDGE_BOMB); }
AI_FLAGS(aiFlags);
PLAYER(SPECIES_ZIGZAGOON) { Moves(move, MOVE_SLEEP_TALK, MOVE_HEADBUTT); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_WORRY_SEED, MOVE_HEADBUTT); }
} WHEN {
TURN { MOVE(player, MOVE_AIR_CUTTER); EXPECT_MOVE(opponent, MOVE_WORRY_SEED); }
if (move == MOVE_REST)
TURN { MOVE(player, MOVE_HEADBUTT); EXPECT_MOVE(opponent, MOVE_WORRY_SEED); }
else
TURN { MOVE(player, MOVE_HEADBUTT); NOT_EXPECT_MOVE(opponent, MOVE_WORRY_SEED); }
}
}
@ -309,4 +319,23 @@ AI_SINGLE_BATTLE_TEST("AI uses Simple Beam against Contrary Leaf Storm")
}
}
AI_SINGLE_BATTLE_TEST("AI uses Skill Swap against Poison Heal")
{
u8 status;
u64 aiFlags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT;
PARAMETRIZE { status = STATUS1_POISON; }
PARAMETRIZE { status = STATUS1_POISON; aiFlags |= AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; }
PARAMETRIZE { status = STATUS1_TOXIC_POISON; aiFlags |= AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION; }
GIVEN {
PLAYER(SPECIES_SHROOMISH) { Ability(ABILITY_POISON_HEAL); Status1(status); }
AI_FLAGS(aiFlags);
OPPONENT(SPECIES_SPINDA) { Moves(MOVE_SKILL_SWAP, MOVE_HEADBUTT); }
} WHEN {
if (status == STATUS1_TOXIC_POISON)
TURN { EXPECT_MOVE(opponent, MOVE_SKILL_SWAP); }
else
TURN { NOT_EXPECT_MOVE(opponent, MOVE_SKILL_SWAP); }
}
}

View File

@ -348,7 +348,7 @@ AI_DOUBLE_BATTLE_TEST("AI sees corresponding absorbing abilities on partners")
}
}
AI_DOUBLE_BATTLE_TEST("AI knows if redirection abilities provide immunity to allies")
AI_DOUBLE_BATTLE_TEST("AI treats an ally's redirection ability appropriately (gen 4)")
{
KNOWN_FAILING;
ASSUME(GetMoveTarget(MOVE_DISCHARGE) == MOVE_TARGET_FOES_AND_ALLY);
@ -356,26 +356,44 @@ AI_DOUBLE_BATTLE_TEST("AI knows if redirection abilities provide immunity to all
ASSUME(GetMoveTarget(MOVE_SURF) == MOVE_TARGET_FOES_AND_ALLY);
ASSUME(GetMoveType(MOVE_SURF) == TYPE_WATER);
u32 ability, move, species, config;
u32 ability, move, species;
PARAMETRIZE { species = SPECIES_SEAKING; ability = ABILITY_LIGHTNING_ROD; move = MOVE_DISCHARGE; config = GEN_4; }
PARAMETRIZE { species = SPECIES_SEAKING; ability = ABILITY_LIGHTNING_ROD; move = MOVE_DISCHARGE; config = GEN_5; }
PARAMETRIZE { species = SPECIES_SHELLOS; ability = ABILITY_STORM_DRAIN; move = MOVE_SURF; config = GEN_4; }
PARAMETRIZE { species = SPECIES_SHELLOS; ability = ABILITY_STORM_DRAIN; move = MOVE_SURF; config = GEN_5; }
PARAMETRIZE { species = SPECIES_SEAKING; ability = ABILITY_LIGHTNING_ROD; move = MOVE_DISCHARGE; }
PARAMETRIZE { species = SPECIES_SHELLOS; ability = ABILITY_STORM_DRAIN; move = MOVE_SURF; }
GIVEN {
ASSUME(GetMoveTarget(MOVE_DISCHARGE) == MOVE_TARGET_FOES_AND_ALLY);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_HP_AWARE);
WITH_CONFIG(B_REDIRECT_ABILITY_IMMUNITY, config);
PLAYER(SPECIES_ZIGZAGOON);
PLAYER(SPECIES_ZIGZAGOON);
OPPONENT(SPECIES_SLAKING) { Moves(move, MOVE_SCRATCH); }
WITH_CONFIG(B_REDIRECT_ABILITY_IMMUNITY, GEN_4);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(move, MOVE_HEADBUTT); }
OPPONENT(species) { HP(1); Ability(ability); Moves(MOVE_ROUND); }
} WHEN {
if (config == GEN_5)
TURN { EXPECT_MOVE(opponentLeft, move); }
else
TURN { EXPECT_MOVE(opponentLeft, MOVE_SCRATCH); }
TURN { EXPECT_MOVE(opponentLeft, MOVE_HEADBUTT); }
}
}
AI_DOUBLE_BATTLE_TEST("AI treats an ally's redirection ability appropriately (gen 5+)")
{
ASSUME(GetMoveTarget(MOVE_DISCHARGE) == MOVE_TARGET_FOES_AND_ALLY);
ASSUME(GetMoveType(MOVE_DISCHARGE) == TYPE_ELECTRIC);
ASSUME(GetMoveTarget(MOVE_SURF) == MOVE_TARGET_FOES_AND_ALLY);
ASSUME(GetMoveType(MOVE_SURF) == TYPE_WATER);
u32 ability, move, species;
PARAMETRIZE { species = SPECIES_SEAKING; ability = ABILITY_LIGHTNING_ROD; move = MOVE_DISCHARGE; }
PARAMETRIZE { species = SPECIES_SHELLOS; ability = ABILITY_STORM_DRAIN; move = MOVE_SURF; }
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_HP_AWARE);
WITH_CONFIG(B_REDIRECT_ABILITY_IMMUNITY, GEN_5);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(move, MOVE_HEADBUTT); }
OPPONENT(species) { HP(1); Ability(ability); Moves(MOVE_ROUND); }
} WHEN {
TURN { EXPECT_MOVE(opponentLeft, move); }
}
}
@ -410,6 +428,41 @@ AI_DOUBLE_BATTLE_TEST("AI prioritizes Skill Swapping Contrary to allied mons tha
}
}
// Sandstorm is omitted on purpose.
// Tornadus is currently not willing to set up Sandstorm for its ally, but the actual purpose of this test is to demonstrate that Tornadus or Whimsicott will perform standard VGC openers.
// Rain Dance, Sunny Day, and Snowscape are the actually important ones; setting up a good Sandstorm test + functionality is less important and will be done in later PRs.
AI_DOUBLE_BATTLE_TEST("AI will set up weather for its ally")
{
u32 goodWeather, badWeather, weatherTrigger;
u64 aiFlags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT;
PARAMETRIZE { goodWeather = MOVE_SUNNY_DAY; badWeather = MOVE_RAIN_DANCE; weatherTrigger = MOVE_SOLAR_BEAM; }
PARAMETRIZE { goodWeather = MOVE_RAIN_DANCE; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_THUNDER; }
PARAMETRIZE { goodWeather = MOVE_HAIL; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_BLIZZARD; }
PARAMETRIZE { goodWeather = MOVE_SNOWSCAPE; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_BLIZZARD; }
// PARAMETRIZE { goodWeather = MOVE_SANDSTORM; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_SHORE_UP; }
PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
goodWeather = MOVE_SUNNY_DAY; badWeather = MOVE_RAIN_DANCE; weatherTrigger = MOVE_SOLAR_BEAM; }
PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
goodWeather = MOVE_RAIN_DANCE; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_THUNDER; }
PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
goodWeather = MOVE_HAIL; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_BLIZZARD; }
PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
goodWeather = MOVE_SNOWSCAPE; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_BLIZZARD; }
// PARAMETRIZE { aiFlags |= AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PP_STALL_PREVENTION;
// goodWeather = MOVE_SANDSTORM; badWeather = MOVE_SUNNY_DAY; weatherTrigger = MOVE_SHORE_UP; }
GIVEN {
AI_FLAGS(aiFlags);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_TORNADUS) { Ability(ABILITY_PRANKSTER); Moves(goodWeather, badWeather, MOVE_RETURN, MOVE_TAUNT); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(weatherTrigger, MOVE_EARTH_POWER); }
} WHEN {
TURN { EXPECT_MOVE(opponentLeft, goodWeather); }
}
}
AI_DOUBLE_BATTLE_TEST("AI uses After You to set up Trick Room")
{
u32 move;
@ -433,6 +486,7 @@ AI_DOUBLE_BATTLE_TEST("AI uses After You to set up Trick Room")
}
}
AI_DOUBLE_BATTLE_TEST("AI uses Guard Split to improve its stats")
{