Improve detection of blocking requirements

This commit is contained in:
Bug Hunter
2022-01-14 04:43:13 +00:00
committed by Michael Kamensky
parent 37e8c41b7e
commit 84a9972034
2 changed files with 39 additions and 23 deletions

View File

@@ -1050,10 +1050,10 @@ public class ComputerUtilCard {
public static boolean useRemovalNow(final SpellAbility sa, final Card c, final int dmg, ZoneType destination) { public static boolean useRemovalNow(final SpellAbility sa, final Card c, final int dmg, ZoneType destination) {
final Player ai = sa.getActivatingPlayer(); final Player ai = sa.getActivatingPlayer();
final AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); final AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
final Player opp = ai.getWeakestOpponent();
final Game game = ai.getGame(); final Game game = ai.getGame();
final PhaseHandler ph = game.getPhaseHandler(); final PhaseHandler ph = game.getPhaseHandler();
final PhaseType phaseType = ph.getPhase(); final PhaseType phaseType = ph.getPhase();
final Player opp = ph.getPlayerTurn().isOpponentOf(ai) ? ph.getPlayerTurn() : ai.getStrongestOpponent();
final int costRemoval = sa.getHostCard().getCMC(); final int costRemoval = sa.getHostCard().getCMC();
final int costTarget = c.getCMC(); final int costTarget = c.getCMC();
@@ -1184,7 +1184,7 @@ public class ComputerUtilCard {
threat += (-1 + 1.0f * evaluateCreature(c) / 100) / costRemoval; threat += (-1 + 1.0f * evaluateCreature(c) / 100) / costRemoval;
if (ai.getLife() > 0 && ComputerUtilCombat.canAttackNextTurn(c)) { if (ai.getLife() > 0 && ComputerUtilCombat.canAttackNextTurn(c)) {
Combat combat = game.getCombat(); Combat combat = game.getCombat();
threat += 1.0f * ComputerUtilCombat.damageIfUnblocked(c, opp, combat, true) / ai.getLife(); threat += 1.0f * ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) / ai.getLife();
//TODO:add threat from triggers and other abilities (ie. Master of Cruelties) //TODO:add threat from triggers and other abilities (ie. Master of Cruelties)
} }
if (ph.isPlayerTurn(ai) && phaseType.isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)) { if (ph.isPlayerTurn(ai) && phaseType.isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)) {

View File

@@ -340,8 +340,12 @@ public class CombatUtil {
public static boolean payRequiredBlockCosts(Game game, Card blocker, Card attacker) { public static boolean payRequiredBlockCosts(Game game, Card blocker, Card attacker) {
Cost blockCost = CombatUtil.getBlockCost(game, blocker, attacker); Cost blockCost = CombatUtil.getBlockCost(game, blocker, attacker);
if (blockCost == null) {
return true;
}
SpellAbility fakeSA = new SpellAbility.EmptySa(blocker, blocker.getController()); SpellAbility fakeSA = new SpellAbility.EmptySa(blocker, blocker.getController());
return blockCost == null || blocker.getController().getController().payManaOptional(blocker, blockCost, fakeSA, "Pay cost to declare " + blocker + " a blocker. ", ManaPaymentPurpose.DeclareBlocker); return blocker.getController().getController().payManaOptional(blocker, blockCost, fakeSA, "Pay cost to declare " + blocker + " a blocker. ", ManaPaymentPurpose.DeclareBlocker);
} }
static Cost getBlockCost(Game game, Card blocker, Card attacker) { static Cost getBlockCost(Game game, Card blocker, Card attacker) {
@@ -784,15 +788,16 @@ public class CombatUtil {
} }
} }
} }
if (potentialBlockers >= additionalBlockers && !blockedSoFar.contains(cardToBeBlocked) && canBlockMoreCreatures(blocker, blockedSoFar) if (potentialBlockers >= additionalBlockers && !blockedSoFar.contains(cardToBeBlocked)
&& (canBlockMoreCreatures(blocker, blockedSoFar) || freeBlockers.contains(blocker))
&& combat.isAttacking(cardToBeBlocked) && canBlock(cardToBeBlocked, blocker)) { && combat.isAttacking(cardToBeBlocked) && canBlock(cardToBeBlocked, blocker)) {
return TextUtil.concatWithSpace(blocker.toString(), "must still block", TextUtil.addSuffix(cardToBeBlocked.toString(),".")); return TextUtil.concatWithSpace(blocker.toString(), "must still block", TextUtil.addSuffix(cardToBeBlocked.toString(),"."));
} }
} }
} }
// lure effects // lure effects
if (!blockers.contains(blocker) && mustBlockAnAttacker(blocker, combat, freeBlockers)) { if (mustBlockAnAttacker(blocker, combat, freeBlockers)) {
return TextUtil.concatWithSpace(blocker.toString(),"must block an attacker, but has not been assigned to block any."); return TextUtil.concatWithSpace(blocker.toString(), "must block an attacker, but has not been assigned to block", blockers.contains(blocker) ? "the right ones." : "any.");
} }
// "CARDNAME blocks each turn/combat if able." // "CARDNAME blocks each turn/combat if able."
@@ -804,8 +809,8 @@ public class CombatUtil {
if (canBlock(attacker, blocker, combat)) { if (canBlock(attacker, blocker, combat)) {
boolean must = true; boolean must = true;
if (getMinNumBlockersForAttacker(attacker, defending) > 1) { if (getMinNumBlockersForAttacker(attacker, defending) > 1) {
final List<Card> possibleBlockers = Lists.newArrayList(defendersArmy); final List<Card> possibleBlockers = Lists.newArrayList(freeBlockers);
possibleBlockers.remove(blocker); possibleBlockers.addAll(combat.getBlockers(attacker));
if (!canBeBlocked(attacker, possibleBlockers, combat)) { if (!canBeBlocked(attacker, possibleBlockers, combat)) {
must = false; must = false;
} }
@@ -868,10 +873,6 @@ public class CombatUtil {
return false; return false;
} }
if (!canBlock(blocker, combat)) {
return false;
}
final CardCollectionView attackers = combat.getAttackers(); final CardCollectionView attackers = combat.getAttackers();
final CardCollection attackersWithLure = new CardCollection(); final CardCollection attackersWithLure = new CardCollection();
for (final Card attacker : attackers) { for (final Card attacker : attackers) {
@@ -912,6 +913,7 @@ public class CombatUtil {
} }
} }
final CardCollection requirementCards = new CardCollection();
final Player defender = blocker.getController(); final Player defender = blocker.getController();
for (final Card attacker : attackersWithLure) { for (final Card attacker : attackersWithLure) {
if (canBeBlocked(attacker, combat, defender) && canBlock(attacker, blocker)) { if (canBeBlocked(attacker, combat, defender) && canBlock(attacker, blocker)) {
@@ -926,7 +928,7 @@ public class CombatUtil {
} }
} }
if (canBe) { if (canBe) {
return true; requirementCards.add(attacker);
} }
} }
} }
@@ -949,14 +951,28 @@ public class CombatUtil {
} }
} }
if (canBe) { if (canBe) {
return true; requirementCards.add(attacker);
} }
} }
} }
if (requirementCards.isEmpty()) {
return false; return false;
} }
boolean mustBlock = true;
if (!canBlock(blocker, combat)) {
// the blocker can't block more but is he even part of another requirement?
for (Card req : requirementCards) {
if (combat.getAttackersBlockedBy(blocker).contains(req)) {
mustBlock = false;
break;
}
}
}
return mustBlock;
}
// can a player block with one or more creatures at the moment? // can a player block with one or more creatures at the moment?
/** /**
* <p> * <p>
@@ -1044,7 +1060,7 @@ public class CombatUtil {
&& !(attacker.hasKeyword("CARDNAME must be blocked if able.") && combat.getBlockers(attacker).isEmpty()) && !(attacker.hasKeyword("CARDNAME must be blocked if able.") && combat.getBlockers(attacker).isEmpty())
&& !(attacker.hasKeyword("CARDNAME must be blocked by exactly one creature if able.") && combat.getBlockers(attacker).size() != 1) && !(attacker.hasKeyword("CARDNAME must be blocked by exactly one creature if able.") && combat.getBlockers(attacker).size() != 1)
&& !(attacker.hasKeyword("CARDNAME must be blocked by two or more creatures if able.") && combat.getBlockers(attacker).size() < 2) && !(attacker.hasKeyword("CARDNAME must be blocked by two or more creatures if able.") && combat.getBlockers(attacker).size() < 2)
&& blocker.getMustBlockCards().contains(attacker) && !blocker.getMustBlockCards().contains(attacker)
&& !mustBeBlockedBy && !mustBeBlockedBy
&& mustBlockAnAttacker(blocker, combat, null)) { && mustBlockAnAttacker(blocker, combat, null)) {
return false; return false;