mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
AI: reworked blockers selections:
* fixed game freezes for no-possible block configurations like Menace (#13290); * fixed computer cheating to ignore block requirements like Menace (now AI will choose all required blockers instead 1); * improved computer logic for blockers selection (try to sacrifice a creature instead game loose, simple use cases only); * added freeze protection for bad or unsupported attacker-block configuration; * refactor: deleted outdated AI code;
This commit is contained in:
parent
a99871988d
commit
92b7ed8efc
12 changed files with 495 additions and 202 deletions
|
|
@ -155,7 +155,7 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
|||
+ (p.isTapped() ? ",tapped" : "")
|
||||
+ (p.isAttacking() ? ",attacking" : "")
|
||||
+ (p.getBlocking() > 0 ? ",blocking" : "")
|
||||
+ ":" + GameStateEvaluator2.evaluatePermanent(p, game))
|
||||
+ ":" + GameStateEvaluator2.evaluatePermanent(p, game, true))
|
||||
.collect(Collectors.joining("; "));
|
||||
sb.append("-> Permanents: [").append(ownPermanentsInfo).append("]");
|
||||
logger.info(sb.toString());
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ public final class GameStateEvaluator2 {
|
|||
public static final int HAND_CARD_SCORE = 5;
|
||||
|
||||
public static PlayerEvaluateScore evaluate(UUID playerId, Game game) {
|
||||
return evaluate(playerId, game, true);
|
||||
}
|
||||
|
||||
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));
|
||||
|
|
@ -63,7 +67,7 @@ public final class GameStateEvaluator2 {
|
|||
|
||||
// add values of player
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(playerId)) {
|
||||
int onePermScore = evaluatePermanent(permanent, game);
|
||||
int onePermScore = evaluatePermanent(permanent, game, useCombatPermanentScore);
|
||||
playerPermanentsScore += onePermScore;
|
||||
if (logger.isDebugEnabled()) {
|
||||
sbPlayer.append(permanent.getName()).append('[').append(onePermScore).append("] ");
|
||||
|
|
@ -77,7 +81,7 @@ public final class GameStateEvaluator2 {
|
|||
|
||||
// add values of opponent
|
||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(opponent.getId())) {
|
||||
int onePermScore = evaluatePermanent(permanent, game);
|
||||
int onePermScore = evaluatePermanent(permanent, game, useCombatPermanentScore);
|
||||
opponentPermanentsScore += onePermScore;
|
||||
if (logger.isDebugEnabled()) {
|
||||
sbOpponent.append(permanent.getName()).append('[').append(onePermScore).append("] ");
|
||||
|
|
@ -121,7 +125,7 @@ public final class GameStateEvaluator2 {
|
|||
opponentLifeScore, opponentHandScore, opponentPermanentsScore);
|
||||
}
|
||||
|
||||
public static int evaluatePermanent(Permanent permanent, Game game) {
|
||||
public static int evaluatePermanent(Permanent permanent, Game game, boolean useCombatPermanentScore) {
|
||||
// prevent AI from attaching bad auras to its own permanents ex: Brainwash and Demonic Torment (no immediate penalty on the battlefield)
|
||||
int value = 0;
|
||||
if (!permanent.getAttachments().isEmpty()) {
|
||||
|
|
@ -137,14 +141,11 @@ public final class GameStateEvaluator2 {
|
|||
}
|
||||
}
|
||||
}
|
||||
value += ArtificialScoringSystem.getFixedPermanentScore(game, permanent)
|
||||
+ ArtificialScoringSystem.getVariablePermanentScore(game, permanent);
|
||||
return value;
|
||||
}
|
||||
|
||||
public static int evaluateCreature(Permanent creature, Game game) {
|
||||
int value = ArtificialScoringSystem.getFixedPermanentScore(game, creature)
|
||||
+ ArtificialScoringSystem.getVariablePermanentScore(game, creature);
|
||||
value += ArtificialScoringSystem.getFixedPermanentScore(game, permanent);
|
||||
value += ArtificialScoringSystem.getDynamicPermanentScore(game, permanent);
|
||||
if (useCombatPermanentScore) {
|
||||
value += ArtificialScoringSystem.getCombatPermanentScore(game, permanent);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,14 +63,11 @@ public final class ArtificialScoringSystem {
|
|||
return score;
|
||||
}
|
||||
|
||||
public static int getVariablePermanentScore(final Game game, final Permanent permanent) {
|
||||
public static int getDynamicPermanentScore(final Game game, final Permanent permanent) {
|
||||
|
||||
int score = permanent.getCounters(game).getCount(CounterType.CHARGE) * 30;
|
||||
score += permanent.getCounters(game).getCount(CounterType.LEVEL) * 30;
|
||||
score -= permanent.getDamage() * 2;
|
||||
if (!canTap(game, permanent)) {
|
||||
score += getTappedScore(game, permanent);
|
||||
}
|
||||
if (permanent.getCardType(game).contains(CardType.CREATURE)) {
|
||||
final int power = permanent.getPower().getValue();
|
||||
final int toughness = permanent.getToughness().getValue();
|
||||
|
|
@ -95,11 +92,19 @@ public final class ArtificialScoringSystem {
|
|||
}
|
||||
}
|
||||
score += equipments + enchantments;
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
public static int getCombatPermanentScore(final Game game, final Permanent permanent) {
|
||||
int score = 0;
|
||||
if (!canTap(game, permanent)) {
|
||||
score += getTappedScore(game, permanent);
|
||||
}
|
||||
if (permanent.getCardType(game).contains(CardType.CREATURE)) {
|
||||
if (!permanent.canAttack(null, game)) {
|
||||
score -= 100;
|
||||
}
|
||||
|
||||
if (!permanent.canBlockAny(game)) {
|
||||
score -= 30;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import mage.game.permanent.Permanent;
|
|||
import mage.game.turn.CombatDamageStep;
|
||||
import mage.game.turn.EndOfCombatStep;
|
||||
import mage.game.turn.Step;
|
||||
import mage.player.ai.GameStateEvaluator2;
|
||||
import mage.players.Player;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
|
|
@ -89,12 +90,22 @@ public final class CombatUtil {
|
|||
}
|
||||
}
|
||||
|
||||
public static Permanent getWorstCreature(List<Permanent> creatures) {
|
||||
if (creatures.isEmpty()) {
|
||||
return null;
|
||||
public static Permanent getWorstCreature(List<Permanent>... lists) {
|
||||
for (List<Permanent> list : lists) {
|
||||
if (!list.isEmpty()) {
|
||||
list.sort(Comparator.comparingInt(p -> p.getPower().getValue()));
|
||||
return list.get(0);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void removeWorstCreature(Permanent permanent, List<Permanent>... lists) {
|
||||
for (List<Permanent> list : lists) {
|
||||
if (!list.isEmpty()) {
|
||||
list.remove(permanent);
|
||||
}
|
||||
}
|
||||
creatures.sort(Comparator.comparingInt(p -> p.getPower().getValue()));
|
||||
return creatures.get(0);
|
||||
}
|
||||
|
||||
private static int sumDamage(List<Permanent> attackersThatWontBeBlocked, Player defender) {
|
||||
|
|
@ -144,25 +155,98 @@ public final class CombatUtil {
|
|||
return canBlock;
|
||||
}
|
||||
|
||||
public static CombatInfo blockWithGoodTrade(Game game, List<Permanent> attackers, List<Permanent> blockers) {
|
||||
|
||||
/**
|
||||
* AI related code, find better block combination for attackers
|
||||
*/
|
||||
public static CombatInfo blockWithGoodTrade2(Game game, List<Permanent> attackers, List<Permanent> blockers) {
|
||||
UUID attackerId = game.getCombat().getAttackingPlayerId();
|
||||
UUID defenderId = game.getCombat().getDefenders().iterator().next();
|
||||
if (attackerId == null || defenderId == null) {
|
||||
log.warn("Couldn't find attacker or defender: " + attackerId + ' ' + defenderId);
|
||||
return new CombatInfo();
|
||||
}
|
||||
|
||||
// TODO: implement full game simulations of all possible combinations (e.g. multiblockers support)
|
||||
|
||||
CombatInfo combatInfo = new CombatInfo();
|
||||
for (Permanent attacker : attackers) {
|
||||
//TODO: handle attackers with "can't be blocked except"
|
||||
List<Permanent> possibleBlockers = getPossibleBlockers(game, attacker, blockers);
|
||||
List<Permanent> survivedBlockers = getBlockersThatWillSurvive(game, attackerId, defenderId, attacker, possibleBlockers);
|
||||
if (!survivedBlockers.isEmpty()) {
|
||||
Permanent blocker = getWorstCreature(survivedBlockers);
|
||||
// simple combat simulation (1 vs 1)
|
||||
List<Permanent> allBlockers = getPossibleBlockers(game, attacker, blockers);
|
||||
List<SurviveInfo> blockerStats = getBlockersThatWillSurvive2(game, attackerId, defenderId, attacker, allBlockers);
|
||||
Map<Permanent, Integer> blockingDiffScore = new HashMap<>();
|
||||
Map<Permanent, Integer> nonBlockingDiffScore = new HashMap<>();
|
||||
blockerStats.forEach(s -> {
|
||||
blockingDiffScore.put(s.getBlocker(), s.getDiffBlockingScore());
|
||||
nonBlockingDiffScore.put(s.getBlocker(), s.getDiffNonblockingScore());
|
||||
});
|
||||
|
||||
// split blockers usage priority
|
||||
List<Permanent> survivedAndKillBlocker = new ArrayList<>();
|
||||
List<Permanent> survivedBlockers2 = new ArrayList<>();
|
||||
List<Permanent> diedBlockers = new ArrayList<>();
|
||||
blockerStats.forEach(stats -> {
|
||||
if (stats.isAttackerDied() && !stats.isBlockerDied()) {
|
||||
survivedAndKillBlocker.add(stats.getBlocker());
|
||||
} else if (!stats.isBlockerDied()) {
|
||||
survivedBlockers2.add(stats.getBlocker());
|
||||
} else {
|
||||
diedBlockers.add(stats.getBlocker());
|
||||
}
|
||||
});
|
||||
|
||||
int blockedCount = 0;
|
||||
|
||||
// find good blocker
|
||||
Permanent blocker = getWorstCreature(survivedAndKillBlocker, survivedBlockers2);
|
||||
if (blocker != null) {
|
||||
combatInfo.addPair(attacker, blocker);
|
||||
blockers.remove(blocker);
|
||||
removeWorstCreature(blocker, blockers, survivedAndKillBlocker, survivedBlockers2);
|
||||
blockedCount++;
|
||||
}
|
||||
|
||||
// find good sacrifices
|
||||
// TODO: add chump blocking support here?
|
||||
// TODO: there are many triggers on damage, attack, etc - it can't be processed without real game simulations
|
||||
if (blocker == null) {
|
||||
blocker = getWorstCreature(diedBlockers);
|
||||
int diffBlockingScore = blockingDiffScore.getOrDefault(blocker, 0);
|
||||
int diffNonBlockingScore = nonBlockingDiffScore.getOrDefault(blocker, 0);
|
||||
if (diffBlockingScore >= 0 || diffBlockingScore > diffNonBlockingScore) {
|
||||
// it's good use case - can block and kill
|
||||
combatInfo.addPair(attacker, blocker);
|
||||
removeWorstCreature(blocker, blockers, diedBlockers);
|
||||
blockedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// find blockers for restrictions
|
||||
while (true) {
|
||||
if (blockers.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: add multiple use case support with min/max blockedBy conditional and other
|
||||
// see all possible use cases in checkBlockRestrictions, checkBlockRequirementsAfter and checkBlockRestrictionsAfter
|
||||
|
||||
// effects support: can't be blocked except by xxx or more creatures
|
||||
if (blockedCount > 0 && attacker.getMinBlockedBy() > blockedCount) {
|
||||
// it already has 1 blocker (killer in best use case), so no needs in second killer
|
||||
blocker = getWorstCreature(survivedBlockers2, survivedAndKillBlocker, diedBlockers);
|
||||
if (blocker != null) {
|
||||
combatInfo.addPair(attacker, blocker);
|
||||
removeWorstCreature(blocker, blockers, survivedBlockers2, survivedAndKillBlocker, diedBlockers);
|
||||
blockedCount++;
|
||||
continue; // try to find next required blocker
|
||||
} else {
|
||||
// invalid configuration, must stop
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// no more active restrictions
|
||||
break;
|
||||
}
|
||||
|
||||
// no more blockers to use
|
||||
if (blockers.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
|
|
@ -171,40 +255,43 @@ public final class CombatUtil {
|
|||
return combatInfo;
|
||||
}
|
||||
|
||||
private static List<Permanent> getBlockersThatWillSurvive(Game game, UUID attackerId, UUID defenderId, Permanent attacker, List<Permanent> possibleBlockers) {
|
||||
List<Permanent> blockers = new ArrayList<>();
|
||||
/**
|
||||
* Game simulations to find all survived/killer blocker
|
||||
*/
|
||||
private static List<SurviveInfo> getBlockersThatWillSurvive2(Game game, UUID attackerId, UUID defenderId, Permanent attacker, List<Permanent> possibleBlockers) {
|
||||
List<SurviveInfo> res = new ArrayList<>();
|
||||
for (Permanent blocker : possibleBlockers) {
|
||||
SurviveInfo info = willItSurvive(game, attackerId, defenderId, attacker, blocker);
|
||||
//if (info.isAttackerDied() && !info.isBlockerDied()) {
|
||||
if (info != null) {
|
||||
if (info.isAttackerDied()) {
|
||||
blockers.add(blocker);
|
||||
} else if (!info.isBlockerDied()) {
|
||||
blockers.add(blocker);
|
||||
}
|
||||
// TODO: enable willItSurviveSimulation and check stability
|
||||
SurviveInfo info = willItSurviveSimple(game, attackerId, defenderId, attacker, blocker);
|
||||
if (info == null) {
|
||||
continue;
|
||||
}
|
||||
info.setBlocker(blocker);
|
||||
res.add(info);
|
||||
}
|
||||
return blockers;
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated TODO: unused, can be deleted?
|
||||
*/
|
||||
public static SurviveInfo willItSurvive(Game game, UUID attackingPlayerId, UUID defendingPlayerId, Permanent attacker, Permanent blocker) {
|
||||
Game sim = game.createSimulationForAI();
|
||||
|
||||
// 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);
|
||||
|
||||
public static SurviveInfo willItSurviveSimulation(Game originalGame, UUID attackingPlayerId, UUID defendingPlayerId, Permanent attacker, Permanent blocker) {
|
||||
Game sim = originalGame.createSimulationForAI();
|
||||
if (blocker == null || attacker == null || sim.getPlayer(defendingPlayerId) == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: need code research, possible bugs in miss prepare code due real combat logic
|
||||
// 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);
|
||||
int startScore = GameStateEvaluator2.evaluate(defendingPlayerId, sim).getTotalScore();
|
||||
|
||||
// real game simulation
|
||||
// TODO: need debug and testing, old code from 2012
|
||||
// must have infinite/freeze protection (e.g. limit stack resolves)
|
||||
|
||||
// declare
|
||||
sim.getPlayer(defendingPlayerId).declareBlocker(defendingPlayerId, blocker.getId(), attacker.getId(), sim);
|
||||
sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARED_BLOCKERS, defendingPlayerId, defendingPlayerId));
|
||||
|
||||
sim.checkStateAndTriggered();
|
||||
while (!sim.getStack().isEmpty()) {
|
||||
sim.getStack().resolve(sim);
|
||||
|
|
@ -212,101 +299,45 @@ public final class CombatUtil {
|
|||
}
|
||||
sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_POST, sim.getActivePlayerId(), sim.getActivePlayerId()));
|
||||
|
||||
// combat
|
||||
simulateStep(sim, new CombatDamageStep(true));
|
||||
simulateStep(sim, new CombatDamageStep(false));
|
||||
simulateStep(sim, new EndOfCombatStep());
|
||||
// The following commented out call produces random freezes.
|
||||
//sim.checkStateAndTriggered();
|
||||
|
||||
// after
|
||||
sim.checkStateAndTriggered();
|
||||
while (!sim.getStack().isEmpty()) {
|
||||
sim.getStack().resolve(sim);
|
||||
sim.applyEffects();
|
||||
}
|
||||
|
||||
return new SurviveInfo(!sim.getBattlefield().containsPermanent(attacker.getId()), !sim.getBattlefield().containsPermanent(blocker.getId()));
|
||||
int endBlockingScore = GameStateEvaluator2.evaluate(defendingPlayerId, sim).getTotalScore();
|
||||
int endNonBlockingScore = startScore; // TODO: implement
|
||||
return new SurviveInfo(
|
||||
!sim.getBattlefield().containsPermanent(attacker.getId()),
|
||||
!sim.getBattlefield().containsPermanent(blocker.getId()),
|
||||
endBlockingScore - startScore,
|
||||
endNonBlockingScore - startScore
|
||||
);
|
||||
}
|
||||
|
||||
protected static void simulateStep(Game game, Step step) {
|
||||
game.getPhase().setStep(step);
|
||||
if (!step.skipStep(game, game.getActivePlayerId())) {
|
||||
step.beginStep(game, game.getActivePlayerId());
|
||||
// The following commented out call produces random freezes.
|
||||
//game.checkStateAndTriggered();
|
||||
while (!game.getStack().isEmpty()) {
|
||||
game.getStack().resolve(game);
|
||||
game.applyEffects();
|
||||
}
|
||||
step.endStep(game, game.getActivePlayerId());
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean canBlock(Game game, Permanent blocker) {
|
||||
boolean canBlock = true;
|
||||
if (!blocker.isTapped()) {
|
||||
try {
|
||||
canBlock = blocker.canBlock(null, game);
|
||||
} catch (Exception e) {
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return canBlock;
|
||||
}
|
||||
|
||||
public static CombatInfo blockWithGoodTrade2(Game game, List<Permanent> attackers, List<Permanent> blockers) {
|
||||
|
||||
UUID attackerId = game.getCombat().getAttackingPlayerId();
|
||||
UUID defenderId = game.getCombat().getDefenders().iterator().next();
|
||||
if (attackerId == null || defenderId == null) {
|
||||
log.warn("Couldn't find attacker or defender: " + attackerId + ' ' + defenderId);
|
||||
return new CombatInfo();
|
||||
}
|
||||
|
||||
CombatInfo combatInfo = new CombatInfo();
|
||||
for (Permanent attacker : attackers) {
|
||||
//TODO: handle attackers with "can't be blocked except"
|
||||
List<Permanent> possibleBlockers = getPossibleBlockers(game, attacker, blockers);
|
||||
List<Permanent> survivedBlockers = getBlockersThatWillSurvive2(game, attackerId, defenderId, attacker, possibleBlockers);
|
||||
if (!survivedBlockers.isEmpty()) {
|
||||
Permanent blocker = getWorstCreature(survivedBlockers);
|
||||
combatInfo.addPair(attacker, blocker);
|
||||
blockers.remove(blocker);
|
||||
}
|
||||
if (blockers.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return combatInfo;
|
||||
}
|
||||
|
||||
private static List<Permanent> getBlockersThatWillSurvive2(Game game, UUID attackerId, UUID defenderId, Permanent attacker, List<Permanent> possibleBlockers) {
|
||||
List<Permanent> blockers = new ArrayList<>();
|
||||
for (Permanent blocker : possibleBlockers) {
|
||||
SurviveInfo info = willItSurvive2(game, attackerId, defenderId, attacker, blocker);
|
||||
//if (info.isAttackerDied() && !info.isBlockerDied()) {
|
||||
if (info != null) {
|
||||
if (info.isAttackerDied()) {
|
||||
blockers.add(blocker);
|
||||
} else if (!info.isBlockerDied()) {
|
||||
blockers.add(blocker);
|
||||
}
|
||||
}
|
||||
}
|
||||
return blockers;
|
||||
}
|
||||
|
||||
public static SurviveInfo willItSurvive2(Game game, UUID attackingPlayerId, UUID defendingPlayerId, Permanent attacker, Permanent blocker) {
|
||||
|
||||
Game sim = game.createSimulationForAI();
|
||||
|
||||
// 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);
|
||||
|
||||
public static SurviveInfo willItSurviveSimple(Game originalGame, UUID attackingPlayerId, UUID defendingPlayerId, Permanent attacker, Permanent blocker) {
|
||||
Game sim = originalGame.createSimulationForAI();
|
||||
if (blocker == null || attacker == null || sim.getPlayer(defendingPlayerId) == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Combat combat = sim.getCombat();
|
||||
combat.setAttacker(attackingPlayerId);
|
||||
combat.setDefenders(sim);
|
||||
|
||||
Game simNonBlocking = sim.copy();
|
||||
|
||||
// attacker tapped before attack, it will add additional score to blocker, but it must be ignored
|
||||
// so blocker will block same creature with same score without penalty
|
||||
int startScore = GameStateEvaluator2.evaluate(defendingPlayerId, sim, false).getTotalScore();
|
||||
|
||||
// fake game simulation (manual PT calculation)
|
||||
if (attacker.getPower().getValue() >= blocker.getToughness().getValue()) {
|
||||
sim.getBattlefield().removePermanent(blocker.getId());
|
||||
}
|
||||
|
|
@ -314,7 +345,38 @@ public final class CombatUtil {
|
|||
sim.getBattlefield().removePermanent(attacker.getId());
|
||||
}
|
||||
|
||||
return new SurviveInfo(!sim.getBattlefield().containsPermanent(attacker.getId()), !sim.getBattlefield().containsPermanent(blocker.getId()));
|
||||
// fake non-block simulation
|
||||
simNonBlocking.getPlayer(defendingPlayerId).damage(
|
||||
attacker.getPower().getValue(),
|
||||
attacker.getId(),
|
||||
null,
|
||||
simNonBlocking,
|
||||
true,
|
||||
true
|
||||
);
|
||||
simNonBlocking.processAction();
|
||||
|
||||
int endBlockingScore = GameStateEvaluator2.evaluate(defendingPlayerId, sim, false).getTotalScore();
|
||||
int endNonBlockingScore = GameStateEvaluator2.evaluate(defendingPlayerId, simNonBlocking, false).getTotalScore();
|
||||
return new SurviveInfo(
|
||||
!sim.getBattlefield().containsPermanent(attacker.getId()),
|
||||
!sim.getBattlefield().containsPermanent(blocker.getId()),
|
||||
endBlockingScore - startScore,
|
||||
endNonBlockingScore - startScore
|
||||
);
|
||||
}
|
||||
|
||||
private static void simulateStep(Game sim, Step step) {
|
||||
sim.getPhase().setStep(step);
|
||||
if (!step.skipStep(sim, sim.getActivePlayerId())) {
|
||||
step.beginStep(sim, sim.getActivePlayerId());
|
||||
// The following commented out call produces random freezes.
|
||||
//game.checkStateAndTriggered();
|
||||
while (!sim.getStack().isEmpty()) {
|
||||
sim.getStack().resolve(sim);
|
||||
sim.applyEffects();
|
||||
}
|
||||
step.endStep(sim, sim.getActivePlayerId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,50 +1,48 @@
|
|||
package mage.player.ai.util;
|
||||
|
||||
import mage.players.Player;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
* @author noxx
|
||||
* AI: combat simulation result
|
||||
*
|
||||
* @author noxx, JayDi85
|
||||
*/
|
||||
public class SurviveInfo {
|
||||
private boolean attackerDied;
|
||||
private boolean blockerDied;
|
||||
private final boolean attackerDied;
|
||||
private final boolean blockerDied;
|
||||
private final int diffBlockingScore;
|
||||
private final int diffNonblockingScore;
|
||||
|
||||
private Player defender;
|
||||
private boolean triggered;
|
||||
private Permanent blocker; // for final result
|
||||
|
||||
public SurviveInfo(boolean attackerDied, boolean blockerDied, Player defender, boolean triggered) {
|
||||
public SurviveInfo(boolean attackerDied, boolean blockerDied, int diffBlockingScore, int diffNonblockingScore) {
|
||||
this.attackerDied = attackerDied;
|
||||
this.blockerDied = blockerDied;
|
||||
this.defender = defender;
|
||||
this.triggered = triggered;
|
||||
this.diffBlockingScore = diffBlockingScore;
|
||||
this.diffNonblockingScore = diffNonblockingScore;
|
||||
}
|
||||
|
||||
public SurviveInfo(boolean attackerDied, boolean blockerDied) {
|
||||
this.attackerDied = attackerDied;
|
||||
this.blockerDied = blockerDied;
|
||||
public void setBlocker(Permanent blocker) {
|
||||
this.blocker = blocker;
|
||||
}
|
||||
|
||||
public Permanent getBlocker() {
|
||||
return this.blocker;
|
||||
}
|
||||
|
||||
public int getDiffBlockingScore() {
|
||||
return this.diffBlockingScore;
|
||||
}
|
||||
|
||||
public int getDiffNonblockingScore() {
|
||||
return this.diffNonblockingScore;
|
||||
}
|
||||
|
||||
public boolean isAttackerDied() {
|
||||
return attackerDied;
|
||||
}
|
||||
|
||||
public void setAttackerDied(boolean attackerDied) {
|
||||
this.attackerDied = attackerDied;
|
||||
}
|
||||
|
||||
public boolean isBlockerDied() {
|
||||
return blockerDied;
|
||||
}
|
||||
|
||||
public void setBlockerDied(boolean blockerDied) {
|
||||
this.blockerDied = blockerDied;
|
||||
}
|
||||
|
||||
public Player getDefender() {
|
||||
return defender;
|
||||
}
|
||||
|
||||
public boolean isTriggered() {
|
||||
return triggered;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue