This commit is contained in:
Pawkkie 2026-03-21 23:38:09 +01:00 committed by GitHub
commit ceb507cab6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 154 additions and 12 deletions

View File

@ -1,6 +1,14 @@
#ifndef GUARD_CONFIG_AI_H
#define GUARD_CONFIG_AI_H
// Frame count references; tests inflate by ~5 frames, actual frame counts are lower
#define AI_FRAME_CEILING_SINGLES_NO_FLAGS 8
#define AI_FRAME_CEILING_SINGLES_SMART_TRAINER 18
#define AI_FRAME_CEILING_DOUBLES_NO_FLAGS 44
#define AI_FRAME_CEILING_DOUBLES_SMART_TRAINER 77
#define AI_FRAME_CEILING_STEVEN_MULTI 37
#define AI_FRAME_CEILING_STEVEN_MULTI_SMART_TRAINER 46
// For the details on what specific factors the switching functions are considering, go read the corresponding function inside ShouldSwitch in src/battle_ai_switch_items.c
// These configuration options control how likely the AI is to switch if it determines that a switch meets all of its criteria
// Think of them almost like success rates; if the AI has determined that it needs to switch out to hit Wonder Guard, how often do you want it to actually take that course of action? Etc.

View File

@ -144,15 +144,19 @@ static s32 (*const sBattleAiFuncTable[])(enum BattlerId, enum BattlerId, enum Mo
void AIDebugTimerStart()
{
// Set delay timer to count how long it takes for AI to choose action/move
gBattleStruct->aiDelayTimer = gMain.vblankCounter1;
CycleCountStart();
if (TESTING || DEBUG_AI_DELAY_TIMER)
gBattleStruct->aiDelayTimer = gMain.vblankCounter1;
if (!TESTING)
CycleCountStart();
}
void AIDebugTimerEnd()
{
// We add to existing to compound multiple calls
gBattleStruct->aiDelayFrames += gMain.vblankCounter1 - gBattleStruct->aiDelayTimer;
gBattleStruct->aiDelayCycles += CycleCountEnd();
if (TESTING || DEBUG_AI_DELAY_TIMER)
gBattleStruct->aiDelayFrames += gMain.vblankCounter1 - gBattleStruct->aiDelayTimer;
if (!TESTING)
gBattleStruct->aiDelayCycles += CycleCountEnd();
}
void BattleAI_SetupItems(void)
@ -384,8 +388,7 @@ void ComputeBattlerDecisions(enum BattlerId battler)
gAiLogicData->aiCalcInProgress = TRUE;
if (DEBUG_AI_DELAY_TIMER)
AIDebugTimerStart();
AIDebugTimerStart();
// Setup battler and prediction data
BattleAI_SetupAIData(0xF, battler);
@ -406,8 +409,7 @@ void ComputeBattlerDecisions(enum BattlerId battler)
BattlerChooseNonMoveAction();
ModifySwitchAfterMoveScoring(battler);
if (DEBUG_AI_DELAY_TIMER)
AIDebugTimerEnd();
AIDebugTimerEnd();
gAiLogicData->aiCalcInProgress = FALSE;
}
@ -729,8 +731,7 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData)
gAiLogicData->aiCalcInProgress = TRUE;
if (DEBUG_AI_DELAY_TIMER)
AIDebugTimerStart();
AIDebugTimerStart();
aiData->weatherHasEffect = HasWeatherEffect();
weather = AI_GetWeather();
@ -767,8 +768,7 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData)
aiData->predictingMove = RandomPercentage(RNG_AI_PREDICT_MOVE, PREDICT_MOVE_CHANCE);
}
if (DEBUG_AI_DELAY_TIMER)
AIDebugTimerEnd();
AIDebugTimerEnd();
gAiLogicData->aiCalcInProgress = FALSE;
}

View File

@ -2,6 +2,140 @@
#include "test/battle.h"
#include "battle_ai_util.h"
AI_SINGLE_BATTLE_TEST("AI thinking time doesn't explode (singles, no flags)")
{
GIVEN {
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_CELEBRATE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_CELEBRATE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
} WHEN {
TURN { }
} THEN {
EXPECT_LT(gBattleStruct->aiDelayFrames, AI_FRAME_CEILING_SINGLES_NO_FLAGS);
}
}
AI_SINGLE_BATTLE_TEST("AI thinking time doesn't explode (singles, smart)")
{
GIVEN {
AI_FLAGS(AI_FLAG_SMART_TRAINER);
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_CELEBRATE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_CELEBRATE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
} WHEN {
TURN { }
} THEN {
EXPECT_LT(gBattleStruct->aiDelayFrames, AI_FRAME_CEILING_SINGLES_SMART_TRAINER);
}
}
AI_DOUBLE_BATTLE_TEST("AI thinking time doesn't explode (doubles, no flags)")
{
GIVEN {
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_CELEBRATE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_CELEBRATE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_CELEBRATE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_CELEBRATE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
} WHEN {
TURN { }
} THEN {
EXPECT_LT(gBattleStruct->aiDelayFrames, AI_FRAME_CEILING_DOUBLES_NO_FLAGS);
}
}
AI_DOUBLE_BATTLE_TEST("AI thinking time doesn't explode (doubles, smart)")
{
GIVEN {
AI_FLAGS(AI_FLAG_SMART_TRAINER);
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_CELEBRATE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_CELEBRATE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_CELEBRATE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_CELEBRATE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_FLING); }
} WHEN {
TURN { }
} THEN {
EXPECT_LT(gBattleStruct->aiDelayFrames, AI_FRAME_CEILING_DOUBLES_SMART_TRAINER);
}
}
AI_MULTI_BATTLE_TEST("AI thinking time doesn't explode (Steven multi)")
{
GIVEN {
AI_FLAGS(AI_FLAG_BASIC_TRAINER);
BATTLER_AI_FLAGS(B_BATTLER_1, AI_FLAG_BASIC_TRAINER);
BATTLER_AI_FLAGS(B_BATTLER_3, AI_FLAG_CHECK_BAD_MOVE);
MULTI_PLAYER(SPECIES_ZIGZAGOON) { Level(100); Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_CELEBRATE); }
MULTI_PARTNER(SPECIES_METANG) { Level(42); Moves(MOVE_CELEBRATE, MOVE_PSYCHIC, MOVE_REFLECT, MOVE_METAL_CLAW); }
MULTI_PARTNER(SPECIES_SKARMORY) { Level(43); Moves(MOVE_TOXIC, MOVE_AERIAL_ACE, MOVE_PROTECT, MOVE_STEEL_WING); }
MULTI_PARTNER(SPECIES_AGGRON) { Level(44); Moves(MOVE_THUNDER, MOVE_PROTECT, MOVE_SOLARBEAM, MOVE_DRAGON_CLAW); }
MULTI_OPPONENT_A(SPECIES_MIGHTYENA) { Level(42); Moves(MOVE_CELEBRATE, MOVE_SCARY_FACE, MOVE_ASSURANCE, MOVE_SWAGGER); }
MULTI_OPPONENT_A(SPECIES_CROBAT) { Level(43); Moves(MOVE_HAZE, MOVE_BITE, MOVE_AIR_CUTTER, MOVE_QUICK_GUARD); }
MULTI_OPPONENT_A(SPECIES_CAMERUPT) { Level(44); Moves(MOVE_YAWN, MOVE_TAKE_DOWN, MOVE_CURSE, MOVE_EARTH_POWER); }
MULTI_OPPONENT_B(SPECIES_CAMERUPT) { Level(36); Moves(MOVE_TAKE_DOWN, MOVE_CELEBRATE, MOVE_EARTH_POWER, MOVE_LAVA_PLUME); }
MULTI_OPPONENT_B(SPECIES_MIGHTYENA) { Level(38); Moves(MOVE_TAUNT, MOVE_SCARY_FACE, MOVE_ASSURANCE, MOVE_SWAGGER); }
MULTI_OPPONENT_B(SPECIES_GOLBAT) { Level(40); Moves(MOVE_BITE, MOVE_AIR_CUTTER, MOVE_QUICK_GUARD, MOVE_POISON_FANG); }
} WHEN {
TURN { }
} THEN {
EXPECT_LT(gBattleStruct->aiDelayFrames, AI_FRAME_CEILING_STEVEN_MULTI);
}
}
AI_MULTI_BATTLE_TEST("AI thinking time doesn't explode (Steven multi, smart)")
{
GIVEN {
AI_FLAGS(AI_FLAG_SMART_TRAINER);
MULTI_PLAYER(SPECIES_ZIGZAGOON) { Level(100); Moves(MOVE_DOUBLE_EDGE, MOVE_BELLY_DRUM, MOVE_FLAIL, MOVE_CELEBRATE); }
MULTI_PARTNER(SPECIES_METANG) { Level(42); Moves(MOVE_CELEBRATE, MOVE_PSYCHIC, MOVE_REFLECT, MOVE_METAL_CLAW); }
MULTI_PARTNER(SPECIES_SKARMORY) { Level(43); Moves(MOVE_TOXIC, MOVE_AERIAL_ACE, MOVE_PROTECT, MOVE_STEEL_WING); }
MULTI_PARTNER(SPECIES_AGGRON) { Level(44); Moves(MOVE_THUNDER, MOVE_PROTECT, MOVE_SOLARBEAM, MOVE_DRAGON_CLAW); }
MULTI_OPPONENT_A(SPECIES_MIGHTYENA) { Level(42); Moves(MOVE_CELEBRATE, MOVE_SCARY_FACE, MOVE_ASSURANCE, MOVE_SWAGGER); }
MULTI_OPPONENT_A(SPECIES_CROBAT) { Level(43); Moves(MOVE_HAZE, MOVE_BITE, MOVE_AIR_CUTTER, MOVE_QUICK_GUARD); }
MULTI_OPPONENT_A(SPECIES_CAMERUPT) { Level(44); Moves(MOVE_YAWN, MOVE_TAKE_DOWN, MOVE_CURSE, MOVE_EARTH_POWER); }
MULTI_OPPONENT_B(SPECIES_CAMERUPT) { Level(36); Moves(MOVE_TAKE_DOWN, MOVE_CELEBRATE, MOVE_EARTH_POWER, MOVE_LAVA_PLUME); }
MULTI_OPPONENT_B(SPECIES_MIGHTYENA) { Level(38); Moves(MOVE_TAUNT, MOVE_SCARY_FACE, MOVE_ASSURANCE, MOVE_SWAGGER); }
MULTI_OPPONENT_B(SPECIES_GOLBAT) { Level(40); Moves(MOVE_BITE, MOVE_AIR_CUTTER, MOVE_QUICK_GUARD, MOVE_POISON_FANG); }
} WHEN {
TURN { }
} THEN {
EXPECT_LT(gBattleStruct->aiDelayFrames, AI_FRAME_CEILING_STEVEN_MULTI_SMART_TRAINER);
}
}
AI_SINGLE_BATTLE_TEST("AI prefers Bubble over Water Gun if it's slower")
{
u32 speedPlayer, speedAi;