Add AI_FLAG_WEIGH_ABILITY_PREDICTION (#5636)
This commit is contained in:
parent
b15f7ad4f1
commit
c8b0da2493
5 changed files with 63 additions and 24 deletions
|
@ -165,3 +165,6 @@ AI always assumes it will roll the lowest possible result when comparing damage
|
|||
|
||||
## `AI_FLAG_SEQUENCE_SWITCHING`
|
||||
AI will always switch out after a KO in exactly party order as defined in the trainer data (ie. slot 1, then 2, then 3, etc.). The AI will never switch out mid-battle unless forced to (Roar etc.). If the AI uses a move that requires a switch where it makes a decision about what to send in (U-Turn etc.), it will always switch out into the lowest available party index.
|
||||
|
||||
## `AI_FLAG_WEIGH_ABILITY_PREDICTION`
|
||||
AI will predict the player's ability based to its aiRating. Without this flag the AI randomly assumes an ability with an even distribution between all possible abilities until one is confirmed. With this flag, it instead guesses proportionally to each ability's aiRating, making it far more likely to guess an ability like Water Absorb than Damp if both are options.
|
||||
|
|
|
@ -49,12 +49,13 @@
|
|||
#define AI_FLAG_CONSERVATIVE (1 << 18) // AI assumes all moves will low roll damage.
|
||||
#define AI_FLAG_SEQUENCE_SWITCHING (1 << 19) // AI switches in mons in exactly party order, and never switches mid-battle.
|
||||
#define AI_FLAG_DOUBLE_ACE_POKEMON (1 << 20) // AI has *two* Ace Pokémon. The last two Pokémons in the party won't be used unless they're the last ones remaining. Goes well in battles where the trainer ID equals to twins, couples, etc.
|
||||
#define AI_FLAG_WEIGH_ABILITY_PREDICTION (1 << 21) // AI will predict player's ability based on aiRating
|
||||
|
||||
#define AI_FLAG_COUNT 21
|
||||
#define AI_FLAG_COUNT 22
|
||||
|
||||
// The following options are enough to have a basic/smart trainer. Any other addtion could make the trainer worse/better depending on the flag
|
||||
#define AI_FLAG_BASIC_TRAINER (AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY)
|
||||
#define AI_FLAG_SMART_TRAINER (AI_FLAG_BASIC_TRAINER | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES)
|
||||
#define AI_FLAG_SMART_TRAINER (AI_FLAG_BASIC_TRAINER | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_WEIGH_ABILITY_PREDICTION)
|
||||
|
||||
// 'other' ai logic flags
|
||||
#define AI_FLAG_DYNAMIC_FUNC (1 << 28) // Create custom AI functions for specific battles via "setdynamicaifunc" cmd
|
||||
|
|
|
@ -174,6 +174,7 @@ enum RandomTag
|
|||
RNG_AI_SWITCH_SE_DEFENSIVE,
|
||||
RNG_SHELL_SIDE_ARM,
|
||||
RNG_RANDOM_TARGET,
|
||||
RNG_AI_PREDICT_ABILITY,
|
||||
RNG_HEALER,
|
||||
};
|
||||
|
||||
|
|
|
@ -102,6 +102,15 @@ bool32 IsAiBattlerAware(u32 battlerId)
|
|||
return BattlerHasAi(battlerId);
|
||||
}
|
||||
|
||||
bool32 IsAiBattlerPredictingAbility(u32 battlerId)
|
||||
{
|
||||
if (AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT] & AI_FLAG_WEIGH_ABILITY_PREDICTION
|
||||
|| AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] & AI_FLAG_WEIGH_ABILITY_PREDICTION)
|
||||
return TRUE;
|
||||
|
||||
return BattlerHasAi(battlerId);
|
||||
}
|
||||
|
||||
void ClearBattlerMoveHistory(u32 battlerId)
|
||||
{
|
||||
memset(BATTLE_HISTORY->usedMoves[battlerId], 0, sizeof(BATTLE_HISTORY->usedMoves[battlerId]));
|
||||
|
@ -1333,6 +1342,8 @@ s32 AI_DecideKnownAbilityForTurn(u32 battlerId)
|
|||
u32 validAbilities[NUM_ABILITY_SLOTS];
|
||||
u8 i, numValidAbilities = 0;
|
||||
u32 knownAbility = AI_GetBattlerAbility(battlerId);
|
||||
u32 indexAbility;
|
||||
u32 abilityAiRatings[NUM_ABILITY_SLOTS] = {0};
|
||||
|
||||
// We've had ability overwritten by e.g. Worry Seed. It is not part of AI_PARTY in case of switching
|
||||
if (gBattleStruct->overwrittenAbilities[battlerId])
|
||||
|
@ -1355,9 +1366,16 @@ s32 AI_DecideKnownAbilityForTurn(u32 battlerId)
|
|||
|
||||
for (i = 0; i < NUM_ABILITY_SLOTS; i++)
|
||||
{
|
||||
if (gSpeciesInfo[gBattleMons[battlerId].species].abilities[i] != ABILITY_NONE)
|
||||
validAbilities[numValidAbilities++] = gSpeciesInfo[gBattleMons[battlerId].species].abilities[i];
|
||||
indexAbility = gSpeciesInfo[gBattleMons[battlerId].species].abilities[i];
|
||||
if (indexAbility != ABILITY_NONE)
|
||||
{
|
||||
abilityAiRatings[numValidAbilities] = gAbilitiesInfo[indexAbility].aiRating;
|
||||
validAbilities[numValidAbilities++] = indexAbility;
|
||||
}
|
||||
}
|
||||
|
||||
if (numValidAbilities > 0 && IsAiBattlerPredictingAbility(battlerId))
|
||||
return validAbilities[RandomWeighted(RNG_AI_PREDICT_ABILITY, abilityAiRatings[0], abilityAiRatings[1], abilityAiRatings[2])];
|
||||
|
||||
if (numValidAbilities > 0)
|
||||
return validAbilities[RandomUniform(RNG_AI_ABILITY, 0, numValidAbilities - 1)];
|
||||
|
|
16
test/battle/ai/ai_flag_predict_ability.c
Normal file
16
test/battle/ai/ai_flag_predict_ability.c
Normal file
|
@ -0,0 +1,16 @@
|
|||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
#include "battle_ai_util.h"
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_WEIGH_ABILITY_PREDICTION: AI will predict opposing ability based on its aiRating")
|
||||
{
|
||||
PASSES_RANDOMLY(7, 14, RNG_AI_PREDICT_ABILITY);
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_WEIGH_ABILITY_PREDICTION);
|
||||
PLAYER(SPECIES_LANTURN) { Ability(ABILITY_VOLT_ABSORB); };
|
||||
OPPONENT(SPECIES_LANTURN) { Moves(MOVE_THUNDERBOLT, MOVE_ICE_BEAM, MOVE_SURF); }
|
||||
} WHEN {
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_THUNDERBOLT); }
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue