mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
AI: improved combat support:
* added support to attack battle permanents (#10246); * fixed that AI was able to attack multiple targets by single creature (#7434); * added docs; * added todos with another bugs or possible problems with AI combat;
This commit is contained in:
parent
6858d43547
commit
e4157fefb8
16 changed files with 239 additions and 89 deletions
|
|
@ -43,7 +43,7 @@ import java.util.stream.Collectors;
|
|||
/**
|
||||
* AI: server side bot with game simulations (mad bot, part of implementation)
|
||||
*
|
||||
* @author nantuko
|
||||
* @author nantuko, JayDi85
|
||||
*/
|
||||
public class ComputerPlayer6 extends ComputerPlayer {
|
||||
|
||||
|
|
@ -887,7 +887,7 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
|||
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, activePlayerId, activePlayerId))) {
|
||||
Player attackingPlayer = game.getPlayer(activePlayerId);
|
||||
|
||||
// check alpha strike first (all in attack to kill)
|
||||
// check alpha strike first (all in attack to kill a player)
|
||||
for (UUID defenderId : game.getOpponents(playerId)) {
|
||||
Player defender = game.getPlayer(defenderId);
|
||||
if (!defender.isInGame()) {
|
||||
|
|
@ -908,7 +908,9 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
|||
}
|
||||
}
|
||||
|
||||
// check all other actions
|
||||
// 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)) {
|
||||
Player defender = game.getPlayer(defenderId);
|
||||
if (!defender.isInGame()) {
|
||||
|
|
@ -983,37 +985,64 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
|||
}
|
||||
}
|
||||
|
||||
// now we have a list of all attackers that can safely attack:
|
||||
// first check to see if any Planeswalkers can be killed
|
||||
// find possible target for attack (priority: planeswalker -> battle -> player)
|
||||
int totalPowerOfAttackers = 0;
|
||||
int loyaltyCounters = 0;
|
||||
for (Permanent planeswalker : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, defender.getId(), game)) {
|
||||
if (planeswalker != null) {
|
||||
loyaltyCounters = planeswalker.getCounters(game).getCount(CounterType.LOYALTY);
|
||||
// verify the attackers can kill the planeswalker, otherwise attack the player
|
||||
for (Permanent attacker : attackersToCheck) {
|
||||
totalPowerOfAttackers += attacker.getPower().getValue();
|
||||
int usedPowerOfAttackers = 0;
|
||||
for (Permanent attacker : attackersToCheck) {
|
||||
totalPowerOfAttackers += attacker.getPower().getValue();
|
||||
}
|
||||
|
||||
// TRY ATTACK PLANESWALKER + BATTLE
|
||||
List<Permanent> possiblePermanentDefenders = new ArrayList<>();
|
||||
// planeswalker first priority
|
||||
game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_PLANESWALKER, activePlayerId, game)
|
||||
.stream()
|
||||
.filter(p -> p.canBeAttacked(null, defenderId, game))
|
||||
.forEach(possiblePermanentDefenders::add);
|
||||
// battle second priority
|
||||
game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_BATTLE, activePlayerId, game)
|
||||
.stream()
|
||||
.filter(p -> p.canBeAttacked(null, defenderId, game))
|
||||
.forEach(possiblePermanentDefenders::add);
|
||||
|
||||
for (Permanent permanentDefender : possiblePermanentDefenders) {
|
||||
if (usedPowerOfAttackers >= totalPowerOfAttackers) {
|
||||
break;
|
||||
}
|
||||
int currentCounters;
|
||||
if (permanentDefender.isPlaneswalker(game)) {
|
||||
currentCounters = permanentDefender.getCounters(game).getCount(CounterType.LOYALTY);
|
||||
} else if (permanentDefender.isBattle(game)) {
|
||||
currentCounters = permanentDefender.getCounters(game).getCount(CounterType.DEFENSE);
|
||||
} else {
|
||||
// impossible error (SBA must remove all planeswalkers/battles with 0 counters before declare attackers)
|
||||
throw new IllegalStateException("AI: can't find counters for defending permanent " + permanentDefender.getName(), new Throwable());
|
||||
}
|
||||
|
||||
// attack anyway (for kill or damage)
|
||||
// TODO: add attackers optimization here (1 powerfull + min number of additional permanents,
|
||||
// current code uses random/etb order)
|
||||
for (Permanent attackingPermanent : attackersToCheck) {
|
||||
if (attackingPermanent.isAttacking()) {
|
||||
// already used for another target
|
||||
continue;
|
||||
}
|
||||
if (totalPowerOfAttackers < loyaltyCounters) {
|
||||
attackingPlayer.declareAttacker(attackingPermanent.getId(), permanentDefender.getId(), game, true);
|
||||
currentCounters -= attackingPermanent.getPower().getValue();
|
||||
usedPowerOfAttackers += attackingPermanent.getPower().getValue();
|
||||
if (currentCounters <= 0) {
|
||||
break;
|
||||
}
|
||||
// kill the Planeswalker
|
||||
for (Permanent attacker : attackersToCheck) {
|
||||
loyaltyCounters -= attacker.getPower().getValue();
|
||||
attackingPlayer.declareAttacker(attacker.getId(), planeswalker.getId(), game, true);
|
||||
if (loyaltyCounters <= 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TRY ATTACK PLAYER
|
||||
// any remaining attackers go for the player
|
||||
for (Permanent attackingPermanent : attackersToCheck) {
|
||||
// if not already attacking a Planeswalker...
|
||||
if (!attackingPermanent.isAttacking()) {
|
||||
attackingPlayer.declareAttacker(attackingPermanent.getId(), defenderId, game, true);
|
||||
if (attackingPermanent.isAttacking()) {
|
||||
continue;
|
||||
}
|
||||
attackingPlayer.declareAttacker(attackingPermanent.getId(), defenderId, game, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
|
|||
private boolean priorityPlay(Game game) {
|
||||
if (lastLoggedTurn != game.getTurnNum()) {
|
||||
lastLoggedTurn = game.getTurnNum();
|
||||
logger.info("======================= Turn: " + game.getTurnNum() + " [" + game.getPlayer(game.getActivePlayerId()).getName() + "] =========================================");
|
||||
logger.info("======================= Turn: " + game.getState().toString() + " [" + game.getPlayer(game.getActivePlayerId()).getName() + "] =========================================");
|
||||
}
|
||||
logState(game);
|
||||
logger.debug("Priority -- Step: " + (game.getTurnStepType() + " ").substring(0, 25) + " ActivePlayer-" + game.getPlayer(game.getActivePlayerId()).getName() + " PriorityPlayer-" + name);
|
||||
|
|
|
|||
|
|
@ -198,9 +198,13 @@ public final class CombatUtil {
|
|||
return blockers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated TODO: unused, can be deleted?
|
||||
*/
|
||||
public static SurviveInfo willItSurvive(Game game, UUID attackingPlayerId, UUID defendingPlayerId, Permanent attacker, Permanent blocker) {
|
||||
Game sim = game.copy();
|
||||
|
||||
// TODO: bugged, miss combat.clear code (possible bugs - wrong blocker declare by AI on multiple options?)
|
||||
Combat combat = sim.getCombat();
|
||||
combat.setAttacker(attackingPlayerId);
|
||||
combat.setDefenders(sim);
|
||||
|
|
@ -232,40 +236,6 @@ public final class CombatUtil {
|
|||
return new SurviveInfo(!sim.getBattlefield().containsPermanent(attacker.getId()), !sim.getBattlefield().containsPermanent(blocker.getId()));
|
||||
}
|
||||
|
||||
public static SurviveInfo getCombatInfo(Game game, UUID attackingPlayerId, UUID defendingPlayerId, Permanent attacker) {
|
||||
Game sim = game.copy();
|
||||
|
||||
Combat combat = sim.getCombat();
|
||||
combat.setAttacker(attackingPlayerId);
|
||||
combat.setDefenders(sim);
|
||||
|
||||
UUID defenderId = sim.getCombat().getDefenders().iterator().next();
|
||||
boolean triggered = false;
|
||||
|
||||
sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_BLOCKERS, defendingPlayerId, defendingPlayerId));
|
||||
|
||||
sim.checkStateAndTriggered();
|
||||
while (!sim.getStack().isEmpty()) {
|
||||
triggered = true;
|
||||
sim.getStack().resolve(sim);
|
||||
sim.applyEffects();
|
||||
}
|
||||
sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_POST, sim.getActivePlayerId(), sim.getActivePlayerId()));
|
||||
|
||||
simulateStep(sim, new CombatDamageStep(true));
|
||||
simulateStep(sim, new CombatDamageStep(false));
|
||||
simulateStep(sim, new EndOfCombatStep());
|
||||
// The following commented out call produces random freezes.
|
||||
//sim.checkStateAndTriggered();
|
||||
while (!sim.getStack().isEmpty()) {
|
||||
triggered = true;
|
||||
sim.getStack().resolve(sim);
|
||||
sim.applyEffects();
|
||||
}
|
||||
|
||||
return new SurviveInfo(!sim.getBattlefield().containsPermanent(attacker.getId()), false, sim.getPlayer(defenderId), triggered);
|
||||
}
|
||||
|
||||
protected static void simulateStep(Game game, Step step) {
|
||||
game.getPhase().setStep(step);
|
||||
if (!step.skipStep(game, game.getActivePlayerId())) {
|
||||
|
|
@ -339,6 +309,7 @@ public final class CombatUtil {
|
|||
|
||||
Game sim = game.copy();
|
||||
|
||||
// TODO: bugged, miss combat.clear code (possible bugs - wrong blocker declare by AI on multiple options?)
|
||||
Combat combat = sim.getCombat();
|
||||
combat.setAttacker(attackingPlayerId);
|
||||
combat.setDefenders(sim);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue