mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
AiHints upgrade
This commit is contained in:
@@ -945,6 +945,39 @@ public class AiBlockController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void makeRequiredBlocks(Combat combat) {
|
||||||
|
// assign blockers that have to block
|
||||||
|
final CardCollection chumpBlockers = CardLists.getKeyword(blockersLeft, "CARDNAME blocks each combat if able.");
|
||||||
|
// if an attacker with lure attacks - all that can block
|
||||||
|
for (final Card blocker : blockersLeft) {
|
||||||
|
if (CombatUtil.mustBlockAnAttacker(blocker, combat, null)) {
|
||||||
|
chumpBlockers.add(blocker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!chumpBlockers.isEmpty()) {
|
||||||
|
for (final Card attacker : attackers) {
|
||||||
|
List<Card> blockers = getPossibleBlockers(combat, attacker, chumpBlockers, false);
|
||||||
|
for (final Card blocker : blockers) {
|
||||||
|
if (CombatUtil.canBlock(attacker, blocker, combat) && blockersLeft.contains(blocker)
|
||||||
|
&& (CombatUtil.mustBlockAnAttacker(blocker, combat, null)
|
||||||
|
|| blocker.hasKeyword("CARDNAME blocks each combat if able."))) {
|
||||||
|
combat.addBlocker(attacker, blocker);
|
||||||
|
if (!blocker.getMustBlockCards().isEmpty()) {
|
||||||
|
int mustBlockAmt = blocker.getMustBlockCards().size();
|
||||||
|
final CardCollectionView blockedSoFar = combat.getAttackersBlockedBy(blocker);
|
||||||
|
boolean canBlockAnother = CombatUtil.canBlockMoreCreatures(blocker, blockedSoFar);
|
||||||
|
if (!canBlockAnother || mustBlockAmt == blockedSoFar.size()) {
|
||||||
|
blockersLeft.remove(blocker);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
blockersLeft.remove(blocker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void clearBlockers(final Combat combat, final List<Card> possibleBlockers) {
|
private void clearBlockers(final Combat combat, final List<Card> possibleBlockers) {
|
||||||
for (final Card blocker : CardLists.filterControlledBy(combat.getAllBlockers(), ai)) {
|
for (final Card blocker : CardLists.filterControlledBy(combat.getAllBlockers(), ai)) {
|
||||||
// don't touch other player's blockers
|
// don't touch other player's blockers
|
||||||
@@ -1008,9 +1041,6 @@ public class AiBlockController {
|
|||||||
|
|
||||||
clearBlockers(combat, possibleBlockers);
|
clearBlockers(combat, possibleBlockers);
|
||||||
|
|
||||||
List<Card> blockers;
|
|
||||||
List<Card> chumpBlockers;
|
|
||||||
|
|
||||||
diff = (ai.getLife() * 2) - 5; // This is the minimal gain for an unnecessary trade
|
diff = (ai.getLife() * 2) - 5; // This is the minimal gain for an unnecessary trade
|
||||||
if (ai.getController().isAI() && diff > 0 && ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.PLAY_AGGRO)) {
|
if (ai.getController().isAI() && diff > 0 && ((PlayerControllerAi) ai.getController()).getAi().getBooleanProperty(AiProps.PLAY_AGGRO)) {
|
||||||
diff = 0;
|
diff = 0;
|
||||||
@@ -1106,37 +1136,9 @@ public class AiBlockController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// assign blockers that have to block
|
// block requirements
|
||||||
chumpBlockers = CardLists.getKeyword(blockersLeft, "CARDNAME blocks each combat if able.");
|
// TODO because this isn't done earlier, sometimes a good block will enforce a restriction that prevents another for the requirement
|
||||||
// if an attacker with lure attacks - all that can block
|
makeRequiredBlocks(combat);
|
||||||
for (final Card blocker : blockersLeft) {
|
|
||||||
if (CombatUtil.mustBlockAnAttacker(blocker, combat, null)) {
|
|
||||||
chumpBlockers.add(blocker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!chumpBlockers.isEmpty()) {
|
|
||||||
CardLists.shuffle(attackers);
|
|
||||||
for (final Card attacker : attackers) {
|
|
||||||
blockers = getPossibleBlockers(combat, attacker, chumpBlockers, false);
|
|
||||||
for (final Card blocker : blockers) {
|
|
||||||
if (CombatUtil.canBlock(attacker, blocker, combat) && blockersLeft.contains(blocker)
|
|
||||||
&& (CombatUtil.mustBlockAnAttacker(blocker, combat, null)
|
|
||||||
|| blocker.hasKeyword("CARDNAME blocks each combat if able."))) {
|
|
||||||
combat.addBlocker(attacker, blocker);
|
|
||||||
if (!blocker.getMustBlockCards().isEmpty()) {
|
|
||||||
int mustBlockAmt = blocker.getMustBlockCards().size();
|
|
||||||
final CardCollectionView blockedSoFar = combat.getAttackersBlockedBy(blocker);
|
|
||||||
boolean canBlockAnother = CombatUtil.canBlockMoreCreatures(blocker, blockedSoFar);
|
|
||||||
if (!canBlockAnother || mustBlockAmt == blockedSoFar.size()) {
|
|
||||||
blockersLeft.remove(blocker);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
blockersLeft.remove(blocker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check to see if it's possible to defend a Planeswalker under attack with a chump block,
|
// check to see if it's possible to defend a Planeswalker under attack with a chump block,
|
||||||
// unless life is low enough to be more worried about saving preserving the life total
|
// unless life is low enough to be more worried about saving preserving the life total
|
||||||
@@ -1186,8 +1188,7 @@ public class AiBlockController {
|
|||||||
* Orders a blocker that put onto the battlefield blocking. Depends heavily
|
* Orders a blocker that put onto the battlefield blocking. Depends heavily
|
||||||
* on the implementation of orderBlockers().
|
* on the implementation of orderBlockers().
|
||||||
*/
|
*/
|
||||||
public static CardCollection orderBlocker(final Card attacker, final Card blocker,
|
public static CardCollection orderBlocker(final Card attacker, final Card blocker, final CardCollection oldBlockers) {
|
||||||
final CardCollection oldBlockers) {
|
|
||||||
// add blocker to existing ordering
|
// add blocker to existing ordering
|
||||||
// sort by evaluate, then insert it appropriately
|
// sort by evaluate, then insert it appropriately
|
||||||
// relies on current implementation of orderBlockers()
|
// relies on current implementation of orderBlockers()
|
||||||
|
|||||||
@@ -209,6 +209,16 @@ public final class CardRulesPredicates {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Predicate<CardRules> deckHasExactly(final DeckHints.Type type, final String has[]) {
|
||||||
|
return new Predicate<CardRules>() {
|
||||||
|
@Override
|
||||||
|
public boolean apply(final CardRules card) {
|
||||||
|
DeckHints deckHas = card.getAiHints().getDeckHas();
|
||||||
|
return deckHas != null && deckHas.isValid() && deckHas.is(type, has);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Core type.
|
* Core type.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import com.google.common.collect.Iterables;
|
|||||||
|
|
||||||
import forge.item.PaperCard;
|
import forge.item.PaperCard;
|
||||||
import forge.util.PredicateString.StringOp;
|
import forge.util.PredicateString.StringOp;
|
||||||
|
import forge.util.collect.FCollection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DeckHints provides the ability for a Card to "want" another Card or type of
|
* DeckHints provides the ability for a Card to "want" another Card or type of
|
||||||
@@ -73,12 +74,26 @@ public class DeckHints {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (Pair<Type, String> filter : filters) {
|
for (Pair<Type, String> filter : filters) {
|
||||||
if (filter.getLeft() == type && filter.getRight().equals(hint)) {
|
if (filter.getLeft() == type && filter.getRight().contains(hint)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
public boolean is(Type type, String hints[]) {
|
||||||
|
if (filters == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (String hint : hints) {
|
||||||
|
for (Pair<Type, String> filter : filters) {
|
||||||
|
if (filter.getLeft() == type && filter.getRight().equals(hint)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a Map of Cards by Type from the given Iterable<PaperCard> that match this
|
* Returns a Map of Cards by Type from the given Iterable<PaperCard> that match this
|
||||||
@@ -95,6 +110,10 @@ public class DeckHints {
|
|||||||
String param = pair.getRight();
|
String param = pair.getRight();
|
||||||
Iterable<PaperCard> cards = getCardsForFilter(cardList, type, param);
|
Iterable<PaperCard> cards = getCardsForFilter(cardList, type, param);
|
||||||
if (cards != null) {
|
if (cards != null) {
|
||||||
|
// if a type is used more than once intersect respective matches
|
||||||
|
if (ret.get(type) != null) {
|
||||||
|
Iterables.retainAll(cards, new FCollection<>(ret.get(type)));
|
||||||
|
}
|
||||||
ret.put(type, cards);
|
ret.put(type, cards);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,13 +162,16 @@ public class DeckHints {
|
|||||||
|
|
||||||
private Iterable<PaperCard> getCardsForFilter(Iterable<PaperCard> cardList, Type type, String param) {
|
private Iterable<PaperCard> getCardsForFilter(Iterable<PaperCard> cardList, Type type, String param) {
|
||||||
List<PaperCard> cards = new ArrayList<>();
|
List<PaperCard> cards = new ArrayList<>();
|
||||||
|
|
||||||
|
// this is case ABILITY, but other types can also use this when the implicit parsing would miss
|
||||||
|
String[] abilities = param.split("\\|");
|
||||||
|
for (String ability : abilities) {
|
||||||
|
Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.deckHas(type, ability), PaperCard.FN_GET_RULES));
|
||||||
|
}
|
||||||
|
// bonus if a DeckHas can satisfy the type with multiple ones
|
||||||
|
Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.deckHasExactly(type, abilities), PaperCard.FN_GET_RULES));
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case ABILITY:
|
|
||||||
String[] abilities = param.split("\\|");
|
|
||||||
for (String ability : abilities) {
|
|
||||||
Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.deckHas(Type.ABILITY, ability), PaperCard.FN_GET_RULES));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case COLOR:
|
case COLOR:
|
||||||
String[] colors = param.split("\\|");
|
String[] colors = param.split("\\|");
|
||||||
for (String color : colors) {
|
for (String color : colors) {
|
||||||
@@ -187,6 +209,7 @@ public class DeckHints {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case NONE:
|
case NONE:
|
||||||
|
case ABILITY: // already done above
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return cards;
|
return cards;
|
||||||
|
|||||||
Reference in New Issue
Block a user