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

* Further optimizes AI_CompareDamagingMoves

* viableMove name change + 2 tests

* some issues

---------

Co-authored-by: DizzyEggg <jajkodizzy@wp.pl>
This commit is contained in:
Alex 2023-10-30 10:04:59 +01:00 committed by GitHub
parent 1467ffa3d8
commit f461bc3256
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 102 additions and 7 deletions

View File

@ -3149,6 +3149,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;
@ -3164,11 +3166,13 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId)
{
leastHits = noOfHits[i];
}
viableMoveScores[i] = AI_SCORE_DEFAULT;
isPowerfulIgnoredEffect[i] = IsInIgnoredPowerfulMoveEffects(gBattleMoves[moves[i]].effect);
}
else
{
noOfHits[i] = -1;
viableMoveScores[i] = 0;
isPowerfulIgnoredEffect[i] = FALSE;
}
/*
@ -3194,19 +3198,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;

View File

@ -2063,7 +2063,6 @@ bool32 HasMoveWithSplit(u32 battler, u32 split)
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && GetBattleMoveSplit(moves[i]) == split)
return TRUE;
}
return FALSE;
}

View File

@ -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);
@ -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)
@ -191,6 +197,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;