From 84a9972034d87215d7465c5174e8b5634c93a86e Mon Sep 17 00:00:00 2001 From: Bug Hunter Date: Fri, 14 Jan 2022 04:43:13 +0000 Subject: [PATCH] Improve detection of blocking requirements --- .../main/java/forge/ai/ComputerUtilCard.java | 4 +- .../java/forge/game/combat/CombatUtil.java | 58 ++++++++++++------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index d0578669b07..7202c3410ad 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -1050,10 +1050,10 @@ public class ComputerUtilCard { public static boolean useRemovalNow(final SpellAbility sa, final Card c, final int dmg, ZoneType destination) { final Player ai = sa.getActivatingPlayer(); final AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); - final Player opp = ai.getWeakestOpponent(); final Game game = ai.getGame(); final PhaseHandler ph = game.getPhaseHandler(); final PhaseType phaseType = ph.getPhase(); + final Player opp = ph.getPlayerTurn().isOpponentOf(ai) ? ph.getPlayerTurn() : ai.getStrongestOpponent(); final int costRemoval = sa.getHostCard().getCMC(); final int costTarget = c.getCMC(); @@ -1184,7 +1184,7 @@ public class ComputerUtilCard { threat += (-1 + 1.0f * evaluateCreature(c) / 100) / costRemoval; if (ai.getLife() > 0 && ComputerUtilCombat.canAttackNextTurn(c)) { 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) } if (ph.isPlayerTurn(ai) && phaseType.isAfter(PhaseType.COMBAT_DECLARE_BLOCKERS)) { diff --git a/forge-game/src/main/java/forge/game/combat/CombatUtil.java b/forge-game/src/main/java/forge/game/combat/CombatUtil.java index 35e90c30d9c..9a964753c03 100644 --- a/forge-game/src/main/java/forge/game/combat/CombatUtil.java +++ b/forge-game/src/main/java/forge/game/combat/CombatUtil.java @@ -340,8 +340,12 @@ public class CombatUtil { public static boolean payRequiredBlockCosts(Game game, Card blocker, Card attacker) { Cost blockCost = CombatUtil.getBlockCost(game, blocker, attacker); + if (blockCost == null) { + return true; + } + 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) { @@ -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)) { - 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 - if (!blockers.contains(blocker) && mustBlockAnAttacker(blocker, combat, freeBlockers)) { - return TextUtil.concatWithSpace(blocker.toString(),"must block an attacker, but has not been assigned to block any."); + if (mustBlockAnAttacker(blocker, combat, freeBlockers)) { + 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." @@ -804,14 +809,14 @@ public class CombatUtil { if (canBlock(attacker, blocker, combat)) { boolean must = true; if (getMinNumBlockersForAttacker(attacker, defending) > 1) { - final List possibleBlockers = Lists.newArrayList(defendersArmy); - possibleBlockers.remove(blocker); + final List possibleBlockers = Lists.newArrayList(freeBlockers); + possibleBlockers.addAll(combat.getBlockers(attacker)); if (!canBeBlocked(attacker, possibleBlockers, combat)) { must = false; } } if (must) { - return TextUtil.concatWithSpace(blocker.toString(),"must block each combat but was not assigned to block any attacker now."); + return TextUtil.concatWithSpace(blocker.toString(), "must block each combat but was not assigned to block any attacker now."); } } } @@ -822,9 +827,9 @@ public class CombatUtil { for (final Card blocker : blockers) { boolean cantBlockAlone = blocker.hasKeyword("CARDNAME can't attack or block alone.") || blocker.hasKeyword("CARDNAME can't block alone."); if (blockers.size() < 2 && cantBlockAlone) { - return TextUtil.concatWithSpace(blocker.toString(),"can't block alone."); + return TextUtil.concatWithSpace(blocker.toString(), "can't block alone."); } else if (blockers.size() < 3 && blocker.hasKeyword("CARDNAME can't block unless at least two other creatures block.")) { - return TextUtil.concatWithSpace(blocker.toString(),"can't block unless at least two other creatures block."); + return TextUtil.concatWithSpace(blocker.toString(), "can't block unless at least two other creatures block."); } else if (blocker.hasKeyword("CARDNAME can't block unless a creature with greater power also blocks.")) { boolean found = false; int power = blocker.getNetPower(); @@ -836,7 +841,7 @@ public class CombatUtil { } } if (!found) { - return TextUtil.concatWithSpace(blocker.toString(),"can't block unless a creature with greater power also blocks."); + return TextUtil.concatWithSpace(blocker.toString(), "can't block unless a creature with greater power also blocks."); } } } @@ -845,7 +850,7 @@ public class CombatUtil { int cntBlockers = combat.getBlockers(attacker).size(); // don't accept blocker amount for attackers with keyword defining valid blockers amount if (cntBlockers > 0 && !canAttackerBeBlockedWithAmount(attacker, cntBlockers, combat)) - return TextUtil.concatWithSpace(attacker.toString(),"cannot be blocked with", String.valueOf(cntBlockers), "creatures you've assigned"); + return TextUtil.concatWithSpace(attacker.toString(), "cannot be blocked with", String.valueOf(cntBlockers), "creatures you've assigned"); } return null; @@ -868,10 +873,6 @@ public class CombatUtil { return false; } - if (!canBlock(blocker, combat)) { - return false; - } - final CardCollectionView attackers = combat.getAttackers(); final CardCollection attackersWithLure = new CardCollection(); for (final Card attacker : attackers) { @@ -912,6 +913,7 @@ public class CombatUtil { } } + final CardCollection requirementCards = new CardCollection(); final Player defender = blocker.getController(); for (final Card attacker : attackersWithLure) { if (canBeBlocked(attacker, combat, defender) && canBlock(attacker, blocker)) { @@ -926,7 +928,7 @@ public class CombatUtil { } } if (canBe) { - return true; + requirementCards.add(attacker); } } } @@ -949,12 +951,26 @@ public class CombatUtil { } } if (canBe) { - return true; + requirementCards.add(attacker); } } } - return false; + if (requirementCards.isEmpty()) { + 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? @@ -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 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) - && blocker.getMustBlockCards().contains(attacker) + && !blocker.getMustBlockCards().contains(attacker) && !mustBeBlockedBy && mustBlockAnAttacker(blocker, combat, null)) { return false;