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) {
|
||||
for (final Card blocker : CardLists.filterControlledBy(combat.getAllBlockers(), ai)) {
|
||||
// don't touch other player's blockers
|
||||
@@ -1008,9 +1041,6 @@ public class AiBlockController {
|
||||
|
||||
clearBlockers(combat, possibleBlockers);
|
||||
|
||||
List<Card> blockers;
|
||||
List<Card> chumpBlockers;
|
||||
|
||||
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)) {
|
||||
diff = 0;
|
||||
@@ -1106,37 +1136,9 @@ public class AiBlockController {
|
||||
}
|
||||
}
|
||||
|
||||
// assign blockers that have to block
|
||||
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()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// block requirements
|
||||
// TODO because this isn't done earlier, sometimes a good block will enforce a restriction that prevents another for the requirement
|
||||
makeRequiredBlocks(combat);
|
||||
|
||||
// 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
|
||||
@@ -1186,8 +1188,7 @@ public class AiBlockController {
|
||||
* Orders a blocker that put onto the battlefield blocking. Depends heavily
|
||||
* on the implementation of orderBlockers().
|
||||
*/
|
||||
public static CardCollection orderBlocker(final Card attacker, final Card blocker,
|
||||
final CardCollection oldBlockers) {
|
||||
public static CardCollection orderBlocker(final Card attacker, final Card blocker, final CardCollection oldBlockers) {
|
||||
// add blocker to existing ordering
|
||||
// sort by evaluate, then insert it appropriately
|
||||
// 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.
|
||||
*
|
||||
|
||||
@@ -14,6 +14,7 @@ import com.google.common.collect.Iterables;
|
||||
|
||||
import forge.item.PaperCard;
|
||||
import forge.util.PredicateString.StringOp;
|
||||
import forge.util.collect.FCollection;
|
||||
|
||||
/**
|
||||
* DeckHints provides the ability for a Card to "want" another Card or type of
|
||||
@@ -73,12 +74,26 @@ public class DeckHints {
|
||||
return false;
|
||||
}
|
||||
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 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
|
||||
@@ -95,6 +110,10 @@ public class DeckHints {
|
||||
String param = pair.getRight();
|
||||
Iterable<PaperCard> cards = getCardsForFilter(cardList, type, param);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -143,13 +162,16 @@ public class DeckHints {
|
||||
|
||||
private Iterable<PaperCard> getCardsForFilter(Iterable<PaperCard> cardList, Type type, String param) {
|
||||
List<PaperCard> cards = new ArrayList<>();
|
||||
switch (type) {
|
||||
case ABILITY:
|
||||
|
||||
// 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, ability), PaperCard.FN_GET_RULES));
|
||||
Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.deckHas(type, ability), PaperCard.FN_GET_RULES));
|
||||
}
|
||||
break;
|
||||
// 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) {
|
||||
case COLOR:
|
||||
String[] colors = param.split("\\|");
|
||||
for (String color : colors) {
|
||||
@@ -187,6 +209,7 @@ public class DeckHints {
|
||||
}
|
||||
break;
|
||||
case NONE:
|
||||
case ABILITY: // already done above
|
||||
break;
|
||||
}
|
||||
return cards;
|
||||
|
||||
Reference in New Issue
Block a user