AI: improved stability and bug fixes (related to #13290):

- bug's reason: wrong usage of canTarget, add/addTarget, getOpponents, etc;
- fixed that it can target dead players in some use cases (close #13507);
- fixed that it wrongly choose targets in bad/good effects in some use cases;
- fixed that it can't find valid targets in some use cases;
- fixed game freezes and errors with some cards;
This commit is contained in:
Oleg Agafonov 2025-04-19 07:04:55 +04:00
parent b915c6590b
commit 3dc606501d
10 changed files with 219 additions and 204 deletions

View file

@ -976,7 +976,7 @@ public class ComputerPlayer6 extends ComputerPlayer {
Player attackingPlayer = game.getPlayer(activePlayerId);
// check alpha strike first (all in attack to kill a player)
for (UUID defenderId : game.getOpponents(playerId)) {
for (UUID defenderId : game.getOpponents(playerId, true)) {
Player defender = game.getPlayer(defenderId);
if (!defender.isInGame()) {
continue;
@ -999,7 +999,7 @@ public class ComputerPlayer6 extends ComputerPlayer {
// TODO: add game simulations here to find best attackers/blockers combination
// find safe attackers (can't be killed by blockers)
for (UUID defenderId : game.getOpponents(playerId)) {
for (UUID defenderId : game.getOpponents(playerId, true)) {
Player defender = game.getPlayer(defenderId);
if (!defender.isInGame()) {
continue;

View file

@ -32,7 +32,8 @@ public final class GameStateEvaluator2 {
public static PlayerEvaluateScore evaluate(UUID playerId, Game game, boolean useCombatPermanentScore) {
// TODO: add multi opponents support, so AI can take better actions
Player player = game.getPlayer(playerId);
Player opponent = game.getPlayer(game.getOpponents(playerId).stream().findFirst().orElse(null));
// must find all leaved opponents too
Player opponent = game.getPlayer(game.getOpponents(playerId, false).stream().findFirst().orElse(null));
if (opponent == null) {
return new PlayerEvaluateScore(playerId, WIN_GAME_SCORE);
}

View file

@ -200,7 +200,7 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
Ability ability1 = iterator.next();
if (ability1.getTargets().size() == 1 && ability1.getTargets().get(0).getTargets().size() == 1) {
Permanent permanent = game.getPermanent(ability1.getFirstTarget());
if (permanent != null && !game.getOpponents(playerId).contains(permanent.getControllerId())) {
if (permanent != null && !game.getOpponents(playerId, true).contains(permanent.getControllerId())) {
iterator.remove();
continue;
}
@ -216,11 +216,11 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
Ability ability1 = iterator.next();
if (ability1.getTargets().size() == 1 && ability1.getTargets().get(0).getTargets().size() == 1) {
Permanent permanent = game.getPermanent(ability1.getFirstTarget());
if (permanent != null && game.getOpponents(playerId).contains(permanent.getControllerId())) {
if (permanent != null && game.getOpponents(playerId, true).contains(permanent.getControllerId())) {
iterator.remove();
continue;
}
if (game.getOpponents(playerId).contains(ability1.getFirstTarget())) {
if (game.getOpponents(playerId, true).contains(ability1.getFirstTarget())) {
iterator.remove();
}
}
@ -233,7 +233,7 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
public List<Combat> addAttackers(Game game) {
Map<Integer, Combat> engagements = new HashMap<>();
//useful only for two player games - will only attack first opponent
UUID defenderId = game.getOpponents(playerId).iterator().next();
UUID defenderId = game.getOpponents(playerId, true).iterator().next();
List<Permanent> attackersList = super.getAvailableAttackers(defenderId, game);
//use binary digits to calculate powerset of attackers
int powerElements = (int) Math.pow(2, attackersList.size());