mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 10:18:01 +00:00
- Experimental: attempting to improve the AI choice for all-in assault for battlefield situations where the defender will have several, but not enough, defenders with evasion (e.g. Flying).
- Currently only enabled for the Experimental AI profile for the testing period.
This commit is contained in:
@@ -424,10 +424,21 @@ public class AiAttackController {
|
|||||||
|
|
||||||
final Player opp = this.defendingOpponent;
|
final Player opp = this.defendingOpponent;
|
||||||
|
|
||||||
|
CardCollection accountedBlockers = new CardCollection(this.blockers);
|
||||||
for (Card attacker : attackers) {
|
for (Card attacker : attackers) {
|
||||||
if (!CombatUtil.canBeBlocked(attacker, this.blockers, null)
|
if (!CombatUtil.canBeBlocked(attacker, accountedBlockers, null)
|
||||||
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
|
|| attacker.hasKeyword("You may have CARDNAME assign its combat damage as though it weren't blocked.")) {
|
||||||
unblockedAttackers.add(attacker);
|
unblockedAttackers.add(attacker);
|
||||||
|
} else {
|
||||||
|
if (ai.getController().isAI()) {
|
||||||
|
AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
|
||||||
|
if (aic.getBooleanProperty(AiProps.COMBAT_ASSAULT_ATTACK_EVASION_PREDICTION)) {
|
||||||
|
// Attempt to identify which blockers will already be taken (blocking other things),
|
||||||
|
// such that Flying, Shadow, Reach, and other mechanics can be properly accounted for.
|
||||||
|
List<Card> potentialBestBlockers = CombatUtil.getPotentialBestBlockers(attacker, accountedBlockers, null);
|
||||||
|
accountedBlockers.removeAll(potentialBestBlockers);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,7 +53,8 @@ public enum AiProps { /** */
|
|||||||
STRIPMINE_HIGH_PRIORITY_ON_SKIPPED_LANDDROP ("false"),
|
STRIPMINE_HIGH_PRIORITY_ON_SKIPPED_LANDDROP ("false"),
|
||||||
TOKEN_GENERATION_ABILITY_CHANCE ("100"), /** */
|
TOKEN_GENERATION_ABILITY_CHANCE ("100"), /** */
|
||||||
TOKEN_GENERATION_ALWAYS_IF_FROM_PLANESWALKER ("true"), /** */
|
TOKEN_GENERATION_ALWAYS_IF_FROM_PLANESWALKER ("true"), /** */
|
||||||
TOKEN_GENERATION_ALWAYS_IF_OPP_ATTACKS ("true"); /** */
|
TOKEN_GENERATION_ALWAYS_IF_OPP_ATTACKS ("true"),
|
||||||
|
COMBAT_ASSAULT_ATTACK_EVASION_PREDICTION ("false"); /** */
|
||||||
|
|
||||||
private final String strDefaultVal;
|
private final String strDefaultVal;
|
||||||
|
|
||||||
|
|||||||
@@ -624,6 +624,30 @@ public class CombatUtil {
|
|||||||
return canAttackerBeBlockedWithAmount(attacker, blocks, combat);
|
return canAttackerBeBlockedWithAmount(attacker, blocks, combat);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<Card> getPotentialBestBlockers(final Card attacker, final List<Card> blockers, final Combat combat) {
|
||||||
|
List<Card> potentialBlockers = Lists.newArrayList();
|
||||||
|
if (blockers.isEmpty() || attacker == null) {
|
||||||
|
return potentialBlockers;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (final Card blocker : blockers) {
|
||||||
|
if (CombatUtil.canBeBlocked(attacker, blocker.getController()) && CombatUtil.canBlock(attacker, blocker)) {
|
||||||
|
potentialBlockers.add(blocker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int minBlockers = getMinNumBlockersForAttacker(attacker, blockers.get(0).getController());
|
||||||
|
|
||||||
|
CardLists.sortByPowerDesc(potentialBlockers);
|
||||||
|
|
||||||
|
List<Card> minBlockerList = Lists.newArrayList();
|
||||||
|
for (int i = 0; i < minBlockers && i < potentialBlockers.size(); i++) {
|
||||||
|
minBlockerList.add(potentialBlockers.get(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return minBlockerList;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* needsMoreBlockers.
|
* needsMoreBlockers.
|
||||||
@@ -1077,7 +1101,7 @@ public class CombatUtil {
|
|||||||
if (defender != null) {
|
if (defender != null) {
|
||||||
return amount >= defender.getCreaturesInPlay().size();
|
return amount >= defender.getCreaturesInPlay().size();
|
||||||
}
|
}
|
||||||
System.out.println("Warning: it was impossible to deduce the defending player in CombatUtil::canAttackerBeBlockedWithAmount, returning 'true' (safest default).");
|
System.out.println("Warning: it was impossible to deduce the defending player in CombatUtil#canAttackerBeBlockedWithAmount, returning 'true' (safest default).");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (amount < defender.getCreaturesInPlay().size()) {
|
if (amount < defender.getCreaturesInPlay().size()) {
|
||||||
@@ -1088,4 +1112,36 @@ public class CombatUtil {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getMinNumBlockersForAttacker(Card attacker, Player defender) {
|
||||||
|
List<String> restrictions = Lists.newArrayList();
|
||||||
|
for (String kw : attacker.getKeywords()) {
|
||||||
|
if (kw.startsWith("CantBeBlockedByAmount")) {
|
||||||
|
restrictions.add(TextUtil.split(kw, ' ', 2)[1]);
|
||||||
|
}
|
||||||
|
if (kw.equals("Menace")) {
|
||||||
|
restrictions.add("LT2");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int minBlockers = 1;
|
||||||
|
for ( String res : restrictions ) {
|
||||||
|
int operand = Integer.parseInt(res.substring(2));
|
||||||
|
String operator = res.substring(0, 2);
|
||||||
|
if (operator.equals("LT") || operator.equals("GE")) {
|
||||||
|
if (minBlockers < operand) {
|
||||||
|
minBlockers = operand;
|
||||||
|
}
|
||||||
|
} else if (operator.equals("LE") || operator.equals("GT") || operator.equals("EQ")) {
|
||||||
|
if (minBlockers < operand + 1) {
|
||||||
|
minBlockers = operand + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (attacker.hasKeyword("CARDNAME can't be blocked unless all creatures defending player controls block it.")) {
|
||||||
|
if (defender != null) {
|
||||||
|
minBlockers = defender.getCreaturesInPlay().size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return minBlockers;
|
||||||
|
}
|
||||||
} // end class CombatUtil
|
} // end class CombatUtil
|
||||||
|
|||||||
@@ -51,3 +51,7 @@ TOKEN_GENERATION_ABILITY_CHANCE=80
|
|||||||
# Situations where the AI should always use the token-generation abilities if possible
|
# Situations where the AI should always use the token-generation abilities if possible
|
||||||
TOKEN_GENERATION_ALWAYS_IF_FROM_PLANESWALKER=true
|
TOKEN_GENERATION_ALWAYS_IF_FROM_PLANESWALKER=true
|
||||||
TOKEN_GENERATION_ALWAYS_IF_OPP_ATTACKS=true
|
TOKEN_GENERATION_ALWAYS_IF_OPP_ATTACKS=true
|
||||||
|
|
||||||
|
# Attempt to predict the number of potential blockers with various forms of evasion when
|
||||||
|
# deciding to do an all-in assault attack (Experimental!)
|
||||||
|
COMBAT_ASSAULT_EVASION_PREDICTION=false
|
||||||
|
|||||||
@@ -52,3 +52,6 @@ TOKEN_GENERATION_ABILITY_CHANCE=80
|
|||||||
TOKEN_GENERATION_ALWAYS_IF_FROM_PLANESWALKER=true
|
TOKEN_GENERATION_ALWAYS_IF_FROM_PLANESWALKER=true
|
||||||
TOKEN_GENERATION_ALWAYS_IF_OPP_ATTACKS=true
|
TOKEN_GENERATION_ALWAYS_IF_OPP_ATTACKS=true
|
||||||
|
|
||||||
|
# Attempt to predict the number of potential blockers with various forms of evasion when
|
||||||
|
# deciding to do an all-in assault attack (Experimental!)
|
||||||
|
COMBAT_ASSAULT_EVASION_PREDICTION=false
|
||||||
|
|||||||
@@ -51,3 +51,7 @@ TOKEN_GENERATION_ABILITY_CHANCE=100
|
|||||||
# Situations where the AI should always use the token-generation abilities if possible
|
# Situations where the AI should always use the token-generation abilities if possible
|
||||||
TOKEN_GENERATION_ALWAYS_IF_FROM_PLANESWALKER=true
|
TOKEN_GENERATION_ALWAYS_IF_FROM_PLANESWALKER=true
|
||||||
TOKEN_GENERATION_ALWAYS_IF_OPP_ATTACKS=true
|
TOKEN_GENERATION_ALWAYS_IF_OPP_ATTACKS=true
|
||||||
|
|
||||||
|
# Attempt to predict the number of potential blockers with various forms of evasion when
|
||||||
|
# deciding to do an all-in assault attack (Experimental!)
|
||||||
|
COMBAT_ASSAULT_EVASION_PREDICTION=true
|
||||||
|
|||||||
@@ -51,3 +51,7 @@ TOKEN_GENERATION_ABILITY_CHANCE=80
|
|||||||
# Situations where the AI should always use the token-generation abilities if possible
|
# Situations where the AI should always use the token-generation abilities if possible
|
||||||
TOKEN_GENERATION_ALWAYS_IF_FROM_PLANESWALKER=true
|
TOKEN_GENERATION_ALWAYS_IF_FROM_PLANESWALKER=true
|
||||||
TOKEN_GENERATION_ALWAYS_IF_OPP_ATTACKS=true
|
TOKEN_GENERATION_ALWAYS_IF_OPP_ATTACKS=true
|
||||||
|
|
||||||
|
# Attempt to predict the number of potential blockers with various forms of evasion when
|
||||||
|
# deciding to do an all-in assault attack (Experimental!)
|
||||||
|
COMBAT_ASSAULT_EVASION_PREDICTION=false
|
||||||
|
|||||||
Reference in New Issue
Block a user