mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-16 10:48:00 +00:00
Only return subset of potential attackers based on current declaration
This commit is contained in:
@@ -357,35 +357,41 @@ public class AiAttackController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// this checks to make sure that the computer player doesn't lose when the human player attacks
|
// this checks to make sure that the computer player doesn't lose when the human player attacks
|
||||||
public final List<Card> notNeededAsBlockers(final List<Card> attackers) {
|
public final List<Card> notNeededAsBlockers(final List<Card> currentAttackers, final List<Card> potentialAttackers) {
|
||||||
//check for time walks
|
//check for time walks
|
||||||
if (ai.getGame().getPhaseHandler().getNextTurn().equals(ai)) {
|
if (ai.getGame().getPhaseHandler().getNextTurn().equals(ai)) {
|
||||||
return attackers;
|
return potentialAttackers;
|
||||||
}
|
}
|
||||||
// no need to block (already holding mana to cast fog next turn)
|
// no need to block (already holding mana to cast fog next turn)
|
||||||
if (!AiCardMemory.isMemorySetEmpty(ai, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT)) {
|
if (!AiCardMemory.isMemorySetEmpty(ai, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT)) {
|
||||||
// Don't send the card that'll do the fog effect to attack, it's unsafe!
|
// Don't send the card that'll do the fog effect to attack, it's unsafe!
|
||||||
|
|
||||||
List<Card> toRemove = Lists.newArrayList();
|
List<Card> toRemove = Lists.newArrayList();
|
||||||
for (Card c : attackers) {
|
for (Card c : potentialAttackers) {
|
||||||
if (AiCardMemory.isRememberedCard(ai, c, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT)) {
|
if (AiCardMemory.isRememberedCard(ai, c, AiCardMemory.MemorySet.CHOSEN_FOG_EFFECT)) {
|
||||||
toRemove.add(c);
|
toRemove.add(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
attackers.removeAll(toRemove);
|
potentialAttackers.removeAll(toRemove);
|
||||||
return attackers;
|
return potentialAttackers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ai.isCardInPlay("Masako the Humorless")) {
|
||||||
|
// "Tapped creatures you control can block as though they were untapped."
|
||||||
|
return potentialAttackers;
|
||||||
|
}
|
||||||
|
|
||||||
|
final CardCollection notNeededAsBlockers = new CardCollection(potentialAttackers);
|
||||||
|
|
||||||
final List<Card> vigilantes = new ArrayList<>();
|
final List<Card> vigilantes = new ArrayList<>();
|
||||||
for (final Card c : myList) {
|
for (final Card c : Iterables.concat(currentAttackers, potentialAttackers)) {
|
||||||
if (c.getName().equals("Masako the Humorless")) {
|
|
||||||
// "Tapped creatures you control can block as though they were untapped."
|
|
||||||
return attackers;
|
|
||||||
}
|
|
||||||
// no need to block if an effect is in play which untaps all creatures
|
// no need to block if an effect is in play which untaps all creatures
|
||||||
// (pseudo-Vigilance akin to Awakening or Prophet of Kruphix)
|
// (pseudo-Vigilance akin to Awakening or Prophet of Kruphix)
|
||||||
if (c.hasKeyword(Keyword.VIGILANCE) || ComputerUtilCard.willUntap(ai, c)) {
|
if (c.hasKeyword(Keyword.VIGILANCE) || ComputerUtilCard.willUntap(ai, c)) {
|
||||||
vigilantes.add(c);
|
vigilantes.add(c);
|
||||||
|
} else if (currentAttackers.contains(c)) {
|
||||||
|
// already attacking so can't block
|
||||||
|
notNeededAsBlockers.add(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// reduce the search space
|
// reduce the search space
|
||||||
@@ -399,10 +405,8 @@ public class AiAttackController {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
final CardCollection notNeededAsBlockers = new CardCollection(attackers);
|
|
||||||
|
|
||||||
// don't hold back creatures that can't block any of the human creatures
|
// don't hold back creatures that can't block any of the human creatures
|
||||||
final List<Card> blockers = getPossibleBlockers(attackers, opponentsAttackers, true);
|
final List<Card> blockers = getPossibleBlockers(potentialAttackers, opponentsAttackers, true);
|
||||||
|
|
||||||
if (!blockers.isEmpty()) {
|
if (!blockers.isEmpty()) {
|
||||||
notNeededAsBlockers.removeAll(blockers);
|
notNeededAsBlockers.removeAll(blockers);
|
||||||
@@ -473,12 +477,15 @@ public class AiAttackController {
|
|||||||
// these creatures will be available to block anyway
|
// these creatures will be available to block anyway
|
||||||
notNeededAsBlockers.addAll(vigilantes);
|
notNeededAsBlockers.addAll(vigilantes);
|
||||||
|
|
||||||
|
// remove those that were only included to ensure a full picture for the baseline
|
||||||
|
notNeededAsBlockers.removeAll(currentAttackers);
|
||||||
|
|
||||||
// Increase the total number of blockers needed by 1 if Finest Hour in play
|
// Increase the total number of blockers needed by 1 if Finest Hour in play
|
||||||
// (human will get an extra first attack with a creature that untaps)
|
// (human will get an extra first attack with a creature that untaps)
|
||||||
// In addition, if the computer guesses it needs no blockers, make sure
|
// In addition, if the computer guesses it needs no blockers, make sure
|
||||||
// that it won't be surprised by Exalted
|
// that it won't be surprised by Exalted
|
||||||
final int humanExaltedBonus = defendingOpponent.countExaltedBonus();
|
final int humanExaltedBonus = defendingOpponent.countExaltedBonus();
|
||||||
int blockersNeeded = attackers.size() - notNeededAsBlockers.size();
|
int blockersNeeded = potentialAttackers.size() - notNeededAsBlockers.size();
|
||||||
|
|
||||||
if (humanExaltedBonus > 0) {
|
if (humanExaltedBonus > 0) {
|
||||||
final boolean finestHour = defendingOpponent.isCardInPlay("Finest Hour");
|
final boolean finestHour = defendingOpponent.isCardInPlay("Finest Hour");
|
||||||
@@ -511,24 +518,28 @@ public class AiAttackController {
|
|||||||
public void reinforceWithBanding(final Player player, final Combat combat) {
|
public void reinforceWithBanding(final Player player, final Combat combat) {
|
||||||
reinforceWithBanding(player, combat, null);
|
reinforceWithBanding(player, combat, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reinforceWithBanding(final Player player, final Combat combat, final Card test) {
|
public void reinforceWithBanding(final Player player, final Combat combat, final Card test) {
|
||||||
|
CardCollection attackers = combat.getAttackers();
|
||||||
|
if (attackers.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
List<String> bandsWithString = Arrays.asList("Bands with Other Legendary Creatures",
|
List<String> bandsWithString = Arrays.asList("Bands with Other Legendary Creatures",
|
||||||
"Bands with Other Creatures named Wolves of the Hunt",
|
"Bands with Other Creatures named Wolves of the Hunt",
|
||||||
"Bands with Other Dinosaurs");
|
"Bands with Other Dinosaurs");
|
||||||
|
|
||||||
List<Card> bandingCreatures = new CardCollection();
|
List<Card> bandingCreatures = null;
|
||||||
|
|
||||||
if (test == null) {
|
if (test == null) {
|
||||||
bandingCreatures.addAll(notNeededAsBlockers(player.getCreaturesInPlay()));
|
bandingCreatures = CardLists.filter(myList, card -> card.hasKeyword(Keyword.BANDING) || card.hasAnyKeyword(bandsWithString));
|
||||||
bandingCreatures = CardLists.filter(bandingCreatures, card -> card.hasKeyword(Keyword.BANDING) || card.hasAnyKeyword(bandsWithString));
|
|
||||||
|
|
||||||
// filter out anything that can't legally attack or is already declared as an attacker
|
// filter out anything that can't legally attack or is already declared as an attacker
|
||||||
bandingCreatures = CardLists.filter(bandingCreatures, card -> !combat.isAttacking(card) && CombatUtil.canAttack(card));
|
bandingCreatures = CardLists.filter(bandingCreatures, card -> !combat.isAttacking(card) && CombatUtil.canAttack(card));
|
||||||
|
|
||||||
|
bandingCreatures = notNeededAsBlockers(attackers, bandingCreatures);
|
||||||
} else {
|
} else {
|
||||||
// Test a specific creature for Banding
|
// Test a specific creature for Banding
|
||||||
if (test.hasKeyword(Keyword.BANDING) || test.hasAnyKeyword(bandsWithString)) {
|
if (test.hasKeyword(Keyword.BANDING) || test.hasAnyKeyword(bandsWithString)) {
|
||||||
bandingCreatures.add(test);
|
bandingCreatures = new CardCollection(test);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -539,12 +550,11 @@ public class AiAttackController {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!bandingCreatures.isEmpty()) {
|
if (bandingCreatures != null) {
|
||||||
List<String> evasionKeywords = Arrays.asList("Flying", "Horsemanship", "Shadow", "Plainswalk", "Islandwalk",
|
List<String> evasionKeywords = Arrays.asList("Flying", "Horsemanship", "Shadow", "Plainswalk", "Islandwalk",
|
||||||
"Forestwalk", "Mountainwalk", "Swampwalk");
|
"Forestwalk", "Mountainwalk", "Swampwalk");
|
||||||
|
|
||||||
// TODO: Assign to band with the best attacker for now, but needs better logic.
|
// TODO: Assign to band with the best attacker for now, but needs better logic.
|
||||||
CardCollection attackers = combat.getAttackers();
|
|
||||||
for (Card c : bandingCreatures) {
|
for (Card c : bandingCreatures) {
|
||||||
Card bestBand;
|
Card bestBand;
|
||||||
|
|
||||||
@@ -1275,7 +1285,7 @@ public class AiAttackController {
|
|||||||
if ( LOG_AI_ATTACKS )
|
if ( LOG_AI_ATTACKS )
|
||||||
System.out.println("Normal attack");
|
System.out.println("Normal attack");
|
||||||
|
|
||||||
attackersLeft = notNeededAsBlockers(attackersLeft);
|
attackersLeft = notNeededAsBlockers(combat.getAttackers(), attackersLeft);
|
||||||
attackersLeft = sortAttackers(attackersLeft);
|
attackersLeft = sortAttackers(attackersLeft);
|
||||||
|
|
||||||
if ( LOG_AI_ATTACKS )
|
if ( LOG_AI_ATTACKS )
|
||||||
|
|||||||
Reference in New Issue
Block a user