Skip to content

Commit

Permalink
AI_CompareDamagingMoves changes to increase the score by 1 for best m…
Browse files Browse the repository at this point in the history
…oves (rh-hideout#3382)

* Further optimizes AI_CompareDamagingMoves

* viableMove name change + 2 tests

* some issues

---------

Co-authored-by: DizzyEggg <jajkodizzy@wp.pl>
  • Loading branch information
2 people authored and wiz1989 committed Jan 29, 2024
1 parent 72296fa commit 857da90
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 7 deletions.
42 changes: 36 additions & 6 deletions src/battle_ai_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -3170,6 +3170,8 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId)
{
u32 i;
bool32 multipleBestMoves = FALSE;
s32 viableMoveScores[MAX_MON_MOVES];
s32 bestViableMoveScore;
s32 noOfHits[MAX_MON_MOVES];
s32 score = 0;
s32 leastHits = 1000, leastHitsId = 0;
Expand All @@ -3186,11 +3188,13 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId)
leastHits = noOfHits[i];
leastHitsId = i;
}
viableMoveScores[i] = AI_SCORE_DEFAULT;
isPowerfulIgnoredEffect[i] = IsInIgnoredPowerfulMoveEffects(gBattleMoves[moves[i]].effect);
}
else
{
noOfHits[i] = -1;
viableMoveScores[i] = 0;
isPowerfulIgnoredEffect[i] = FALSE;
}
/*
Expand All @@ -3216,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
{
bestViableMoveScore = 0;
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (viableMoveScores[i] > bestViableMoveScore)
bestViableMoveScore = viableMoveScores[i];
}
// Unless a better move was found increase score of current move
if (viableMoveScores[currId] == bestViableMoveScore)
ADJUST_SCORE(1);
}
}

return score;
Expand Down
1 change: 0 additions & 1 deletion src/battle_ai_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -2083,7 +2083,6 @@ bool32 HasMoveWithSplit(u32 battler, u32 split)
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && GetBattleMoveSplit(moves[i]) == split)
return TRUE;
}

return FALSE;
}

Expand Down
66 changes: 66 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 != 100);
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,66 @@ 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 status move that boosts the attack by two")
{
GIVEN {
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_WOBBUFFET) { HP(250); };
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_KANGASKHAN) { Moves(MOVE_STRENGTH, MOVE_HORN_ATTACK, MOVE_SWORDS_DANCE); }
} WHEN {
TURN { EXPECT_MOVES(opponent, MOVE_STRENGTH, MOVE_SWORDS_DANCE); }
TURN { EXPECT_MOVE(opponent, MOVE_STRENGTH); SEND_OUT(player, 1); }
}
}

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

0 comments on commit 857da90

Please sign in to comment.