Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AI_CompareDamagingMoves changes to increase the score by 1 for best moves #3382

Merged
merged 4 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 36 additions & 7 deletions src/battle_ai_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -3169,8 +3169,9 @@ static u32 GetAIMostDamagingMoveId(u32 battlerAtk, u32 battlerDef)

static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId)
{
u32 i;
u32 i, bestViableMoveSc0re;
AlexOn1ine marked this conversation as resolved.
Show resolved Hide resolved
bool32 multipleBestMoves = FALSE;
u32 viableMoveScores[MAX_MON_MOVES];
s32 noOfHits[MAX_MON_MOVES];
s32 score = 0;
s32 leastHits = 1000, leastHitsId = 0;
Expand All @@ -3187,11 +3188,13 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId)
leastHits = noOfHits[i];
leastHitsId = i;
}
viableMoveScores[i] = 100;
AlexOn1ine marked this conversation as resolved.
Show resolved Hide resolved
isPowerfulIgnoredEffect[i] = IsInIgnoredPowerfulMoveEffects(gBattleMoves[moves[i]].effect);
}
else
{
noOfHits[i] = -1;
viableMoveScores[i] = 0;
isPowerfulIgnoredEffect[i] = FALSE;
}
/*
Expand All @@ -3217,19 +3220,45 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId)
multipleBestMoves = TRUE;
// We need to make sure it's the current move which is objectively better.
if (isPowerfulIgnoredEffect[i] && !isPowerfulIgnoredEffect[currId])
ADJUST_SCORE(3);
else if (CompareMoveAccuracies(battlerAtk, battlerDef, currId, i) == 0)
ADJUST_SCORE(2);
else if (AI_WhichMoveBetter(moves[currId], moves[i], battlerAtk, battlerDef, noOfHits[currId]) == 0)
viableMoveScores[i] -= 3;
else if (!isPowerfulIgnoredEffect[i] && isPowerfulIgnoredEffect[currId])
viableMoveScores[currId] -= 3;

switch (CompareMoveAccuracies(battlerAtk, battlerDef, currId, i))
{
// MgbaPrintf_("%S better than %S", gMoveNames[moves[currId]], gMoveNames[moves[i]]);
ADJUST_SCORE(1);
case 0:
viableMoveScores[i] -= 2;
break;
case 1:
viableMoveScores[currId] -= 2;
break;
}
switch (AI_WhichMoveBetter(moves[currId], moves[i], battlerAtk, battlerDef, noOfHits[currId]))
{
case 0:
viableMoveScores[i] -= 1;
break;
case 1:
viableMoveScores[currId] -= 1;
break;
}
}
}
// Turns out the current move deals the most dmg compared to the other 3.
if (!multipleBestMoves)
ADJUST_SCORE(1);
else
{
bestViableMoveSc0re = 0;
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (viableMoveScores[i] > bestViableMoveSc0re)
bestViableMoveSc0re = viableMoveScores[i];
}
// Unless a better move was found increase score of current move
if (viableMoveScores[currId] == bestViableMoveSc0re)
ADJUST_SCORE(1);
}
}

return score;
Expand Down
65 changes: 65 additions & 0 deletions test/battle/ai.c
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ AI_SINGLE_BATTLE_TEST("AI prefers moves with better accuracy, but only if they b
PARAMETRIZE { move1 = MOVE_MEGA_KICK; move2 = MOVE_SLAM; move3 = MOVE_TACKLE; move4 = MOVE_GUST; hp = 5; expectedMove = MOVE_GUST; expectedMove2 = MOVE_TACKLE; turns = 1; }
// All moves hit with No guard ability
PARAMETRIZE { move1 = MOVE_MEGA_KICK; move2 = MOVE_GUST; hp = 5; expectedMove = MOVE_MEGA_KICK; expectedMove2 = MOVE_GUST; turns = 1; }
// Tests to compare move that always hits and a beneficial effect. A move with higher acc should be chosen in this case.
PARAMETRIZE { move1 = MOVE_SHOCK_WAVE; move2 = MOVE_ICY_WIND; hp = 5; expectedMove = MOVE_SHOCK_WAVE; turns = 1; }
PARAMETRIZE { move1 = MOVE_SHOCK_WAVE; move2 = MOVE_ICY_WIND; move3 = MOVE_THUNDERBOLT; hp = 5; expectedMove = MOVE_SHOCK_WAVE; expectedMove2 = MOVE_THUNDERBOLT; turns = 1; }

GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
Expand All @@ -105,6 +108,9 @@ AI_SINGLE_BATTLE_TEST("AI prefers moves with better accuracy, but only if they b
ASSUME(gBattleMoves[MOVE_MEGA_KICK].accuracy < gBattleMoves[MOVE_STRENGTH].accuracy);
ASSUME(gBattleMoves[MOVE_TACKLE].accuracy == 100);
ASSUME(gBattleMoves[MOVE_GUST].accuracy == 100);
ASSUME(gBattleMoves[MOVE_SHOCK_WAVE].accuracy == 0);
ASSUME(gBattleMoves[MOVE_THUNDERBOLT].accuracy == 100);
ASSUME(gBattleMoves[MOVE_ICY_WIND].accuracy == 95);
AlexOn1ine marked this conversation as resolved.
Show resolved Hide resolved
OPPONENT(SPECIES_EXPLOUD) { Moves(move1, move2, move3, move4); Ability(abilityAtk); SpAttack(50); } // Low Sp.Atk, so Swift deals less damage than Strength.
} WHEN {
switch (turns)
Expand Down Expand Up @@ -192,6 +198,65 @@ AI_SINGLE_BATTLE_TEST("AI prefers Earthquake over Drill Run if both require the
}
}

AI_SINGLE_BATTLE_TEST("AI prefers a weaker move over a one with a downside effect if both require the same number of hits to ko")
{
u16 move1 = MOVE_NONE, move2 = MOVE_NONE, move3 = MOVE_NONE, move4 = MOVE_NONE;
u16 hp, expectedMove, turns;

// Both moves require the same number of turns but Flamethrower will be chosen over Overheat (powerful effect)
PARAMETRIZE { move1 = MOVE_OVERHEAT; move2 = MOVE_FLAMETHROWER; hp = 300; expectedMove = MOVE_FLAMETHROWER; turns = 2; }
// Overheat kill in least amount of turns
PARAMETRIZE { move1 = MOVE_OVERHEAT; move2 = MOVE_FLAMETHROWER; hp = 250; expectedMove = MOVE_OVERHEAT; turns = 1; }

GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { HP(hp); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_TYPHLOSION) { Moves(move1, move2); }
} WHEN {
switch (turns)
{
case 1:
TURN { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); }
break;
case 2:
TURN { EXPECT_MOVE(opponent, expectedMove); }
TURN { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); }
break;
}
}
SCENE {
MESSAGE("Wobbuffet fainted!");
}
}

AI_SINGLE_BATTLE_TEST("AI prefers moves with the best possible score, chosen randomly if tied")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { HP(5); };
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_THUNDERBOLT, MOVE_SLUDGE_BOMB, MOVE_TAKE_DOWN); }
} WHEN {
TURN { EXPECT_MOVES(opponent, MOVE_THUNDERBOLT, MOVE_SLUDGE_BOMB); SEND_OUT(player, 1); }
}
SCENE {
MESSAGE("Wobbuffet fainted!");
}
}

AI_SINGLE_BATTLE_TEST("AI can choose a stat boosting move randomly")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_THUNDERBOLT, MOVE_SLUDGE_BOMB, MOVE_SWORDS_DANCE); }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, in this case it shouldn't ever choose Swords Dance, because both Tbold and Sludge Bomb are special moves.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. Seems like a Sword Dance issue. I will add the check.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the test to highlight Sword Dance a little bit more.

} WHEN {
TURN { EXPECT_MOVES(opponent, MOVE_THUNDERBOLT, MOVE_SLUDGE_BOMB, MOVE_SWORDS_DANCE); }
}
}

AI_SINGLE_BATTLE_TEST("AI chooses the safest option to faint the target, taking into account accuracy and move effect")
{
u16 move1 = MOVE_NONE, move2 = MOVE_NONE, move3 = MOVE_NONE, move4 = MOVE_NONE;
Expand Down