Initial attempt, doesn't see move effectiveness / damage

This commit is contained in:
Pawkkie 2025-04-08 00:15:28 -04:00
parent d7a6e0bea8
commit 11d26af7a8
7 changed files with 114 additions and 14 deletions

View File

@ -352,6 +352,7 @@ struct AiLogicData
u8 shouldSwitch; // Stores result of ShouldSwitch, which decides whether a mon should be switched out
u8 aiCalcInProgress:1;
u8 battlerDoingPrediction; // Stores which battler is currently running its prediction calcs
u16 predictedMove[MAX_BATTLERS_COUNT];
};
struct AI_ThinkingStruct

View File

@ -122,5 +122,6 @@ void Ai_UpdateSwitchInData(u32 battler);
void Ai_UpdateFaintData(u32 battler);
void SetAiLogicDataForTurn(struct AiLogicData *aiData);
void ResetDynamicAiFunc(void);
u32 BattleAI_PredictMove(u32 battler, u32 opposingBattler);
#endif // GUARD_BATTLE_AI_MAIN_H

View File

@ -6,7 +6,7 @@
// still has them in the ROM. This is because the developers forgot
// to define NDEBUG before release, however this has been changed as
// Ruby's actual debug build does not use the AGBPrint features.
#define NDEBUG
// #define NDEBUG
// To enable printf debugging, comment out "#define NDEBUG". This allows
// the various AGBPrint functions to be used. (See include/gba/isagbprint.h).

View File

@ -35,6 +35,7 @@
static u32 ChooseMoveOrAction_Singles(u32 battlerAi);
static u32 ChooseMoveOrAction_Doubles(u32 battlerAi);
static inline void BattleAI_DoAIProcessing(struct AI_ThinkingStruct *aiThink, u32 battlerAi, u32 battlerDef);
static inline void BattleAI_DoAIProcessing_PredictMove(struct AI_ThinkingStruct *aiThink, u32 battler, u32 opposingBattler);
static inline void BattleAI_DoAIProcessing_PredictedSwitchin(struct AI_ThinkingStruct *aiThink, struct AiLogicData *aiData, u32 battlerAi, u32 battlerDef);
static bool32 IsPinchBerryItemEffect(u32 holdEffect);
@ -239,6 +240,7 @@ void BattleAI_SetupFlags(void)
void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler)
{
u32 moveLimitations, moveLimitationsTarget;
u8 defaultScoreMovesTarget = defaultScoreMoves;
u32 flags[MAX_BATTLERS_COUNT];
u32 moveIndex;
@ -268,16 +270,19 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler)
// Initialize move prediction scores
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_PREDICT_MOVES)
{
moveLimitationsTarget = AI_DATA->moveLimitations[gBattlerTarget];
u32 opposingBattler = GetOppositeBattler(battler);
moveLimitationsTarget = AI_DATA->moveLimitations[opposingBattler];
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
if (moveLimitations & (1u << moveIndex))
SET_PREDICTED_SCORE(gBattlerTarget, moveIndex, 0);
if (defaultScoreMoves & 1)
SET_PREDICTED_SCORE(gBattlerTarget, moveIndex, AI_SCORE_DEFAULT);
if (moveLimitationsTarget & (1u << moveIndex))
SET_PREDICTED_SCORE(opposingBattler, moveIndex, 0);
if (defaultScoreMovesTarget & 1)
SET_PREDICTED_SCORE(opposingBattler, moveIndex, AI_SCORE_DEFAULT);
else
SET_PREDICTED_SCORE(gBattlerTarget, moveIndex, 0);
SET_PREDICTED_SCORE(opposingBattler, moveIndex, 0);
defaultScoreMovesTarget >>= 1;
}
}
}
@ -501,6 +506,56 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData)
AI_DATA->aiCalcInProgress = FALSE;
}
u32 BattleAI_PredictMove(u32 battler, u32 opposingBattler)
{
u8 currentMoveArray[MAX_MON_MOVES];
u8 consideredMoveArray[MAX_MON_MOVES];
u32 numOfBestMoves;
s32 i;
u32 flags = AI_THINKING_STRUCT->aiFlags[battler];
AI_DATA->partnerMove = 0; // no ally
while (flags != 0)
{
if (flags & 1)
{
BattleAI_DoAIProcessing_PredictMove(AI_THINKING_STRUCT, battler, opposingBattler);
}
flags >>= 1;
AI_THINKING_STRUCT->aiLogicId++;
}
for (i = 0; i < MAX_MON_MOVES; i++)
{
gAiBattleData->finalScore[opposingBattler][battler][i] = AI_THINKING_STRUCT->predictedScore[i][opposingBattler];
DebugPrintf("Final score: %d", gAiBattleData->finalScore[opposingBattler][battler][i]);
}
numOfBestMoves = 1;
currentMoveArray[0] = AI_THINKING_STRUCT->predictedScore[0][opposingBattler];
consideredMoveArray[0] = 0;
for (i = 1; i < MAX_MON_MOVES; i++)
{
if (gBattleMons[opposingBattler].moves[i] != MOVE_NONE)
{
// In ruby, the order of these if statements is reversed.
if (currentMoveArray[0] == AI_THINKING_STRUCT->predictedScore[i][opposingBattler])
{
currentMoveArray[numOfBestMoves] = AI_THINKING_STRUCT->predictedScore[i][opposingBattler];
consideredMoveArray[numOfBestMoves++] = i;
}
if (currentMoveArray[0] < AI_THINKING_STRUCT->predictedScore[i][opposingBattler])
{
numOfBestMoves = 1;
currentMoveArray[0] = AI_THINKING_STRUCT->predictedScore[i][opposingBattler];
consideredMoveArray[0] = i;
}
}
}
return consideredMoveArray[Random() % numOfBestMoves];
}
static u32 ChooseMoveOrAction_Singles(u32 battlerAi)
{
u8 currentMoveArray[MAX_MON_MOVES];
@ -692,6 +747,44 @@ static inline bool32 ShouldConsiderMoveForBattler(u32 battlerAi, u32 battlerDef,
return TRUE;
}
static inline void BattleAI_DoAIProcessing_PredictMove(struct AI_ThinkingStruct *aiThink, u32 battler, u32 opposingBattler)
{
do
{
if (gBattleMons[opposingBattler].pp[aiThink->movesetIndex] == 0)
aiThink->moveConsidered = MOVE_NONE;
else
aiThink->moveConsidered = gBattleMons[opposingBattler].moves[aiThink->movesetIndex];
DebugPrintf("Move considered: %d", aiThink->moveConsidered);
// There is no point in calculating scores for all 3 battlers(2 opponents + 1 ally) with certain moves.
if (aiThink->moveConsidered != MOVE_NONE
&& aiThink->score[aiThink->movesetIndex] > 0
&& ShouldConsiderMoveForBattler(opposingBattler, battler, aiThink->moveConsidered))
{
if (aiThink->aiLogicId < ARRAY_COUNT(sBattleAiFuncTable)
&& sBattleAiFuncTable[aiThink->aiLogicId] != NULL)
{
// Call AI function
aiThink->predictedScore[aiThink->movesetIndex][opposingBattler] =
sBattleAiFuncTable[aiThink->aiLogicId](opposingBattler,
battler,
aiThink->moveConsidered,
aiThink->predictedScore[aiThink->movesetIndex][opposingBattler]);
DebugPrintf("Current score: %d", aiThink->predictedScore[aiThink->movesetIndex][opposingBattler]);
}
}
else
{
aiThink->predictedScore[aiThink->movesetIndex][opposingBattler] = 0;
}
aiThink->movesetIndex++;
} while (aiThink->movesetIndex < MAX_MON_MOVES);
aiThink->movesetIndex = 0;
}
static inline void BattleAI_DoAIProcessing(struct AI_ThinkingStruct *aiThink, u32 battlerAi, u32 battlerDef)
{
do

View File

@ -448,7 +448,7 @@ static bool32 FindMonThatAbsorbsOpponentsMove(u32 battler)
struct Pokemon *party;
u16 monAbility, aiMove;
u32 opposingBattler = GetOppositeBattler(battler);
u32 incomingMove = AI_DATA->lastUsedMove[opposingBattler];
u32 incomingMove = (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_PREDICT_MOVES) ? AI_DATA->predictedMove[opposingBattler] : AI_DATA->lastUsedMove[opposingBattler];
u32 incomingType = GetMoveType(incomingMove);
u32 predictedMove = incomingMove; // Update for move prediction
u32 predictedType = GetMoveType(predictedMove);

View File

@ -4142,10 +4142,8 @@ enum
STATE_SELECTION_SCRIPT_MAY_RUN
};
void SetupAISwitchingData(u32 battler, enum SwitchType switchType)
void SetupAISwitchingData(u32 battler, u32 opposingBattler, enum SwitchType switchType)
{
s32 opposingBattler = GetOppositeBattler(battler);
// AI's predicting data
if ((AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_PREDICT_SWITCH))
{
@ -4188,10 +4186,17 @@ static void HandleTurnActionSelectionState(void)
&& (BattlerHasAi(battler) && !(gBattleTypeFlags & BATTLE_TYPE_PALACE)))
{
AI_DATA->aiCalcInProgress = TRUE;
u32 opposingBattler = GetOppositeBattler(battler);
// Setup battler data
BattleAI_SetupAIData(0xF, battler);
SetupAISwitchingData(battler, switchType);
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_PREDICT_MOVES)
{
AI_DATA->predictedMove[opposingBattler] = BattleAI_PredictMove(battler, opposingBattler);
DebugPrintf("Predicted move: %d", AI_DATA->predictedMove[opposingBattler]);
}
SetupAISwitchingData(battler, opposingBattler, switchType);
// Do scoring
gAiBattleData->moveOrAction[battler] = BattleAI_ChooseMoveOrAction(battler);

View File

@ -5,8 +5,8 @@
AI_SINGLE_BATTLE_TEST("AI_FLAG_PREDICT_MOVE: AI will predict player's move")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PREDICT_MOVE);
PLAYER(SPECIES_VAPOREON) { Moves(MOVE_SURF, MOVE_TACKLE); }
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_PREDICT_MOVES);
PLAYER(SPECIES_VAPOREON) { Ability(ABILITY_WATER_ABSORB); Moves(MOVE_SURF, MOVE_TACKLE); }
OPPONENT(SPECIES_NUMEL) { Moves(MOVE_TACKLE); }
OPPONENT(SPECIES_VAPOREON) { Ability(ABILITY_WATER_ABSORB); Moves(MOVE_TACKLE); }
} WHEN {