Miscallaneous Cleanup Part 2 - Lambdas and Method References (#5737)

* Cleanup - Unnecessary Boxing

* Cleanup - Unnecessary Unboxing

* Cleanup - For-Each Loops

* Cleanup - `indexOf != -1` -> `contains`

* Cleanup - Merge identical catch blocks

* Cleanup - Try-with-resources

* Cleanup - System.lineSeparator

* Cleanup - Reference types to primitives
Some loops over Integers were switched to IntStreams to hopefully cut down on overall boxing.

* Cleanup - Manually filling and copying arrays

* Remove unused imports

* Switch a lambda to a method reference

* Cleanup - CardPredicate Aggregates to method references

* Cleanup - Other static functions to method references

* Cleanup - Ambiguous class reference
Unclear when or how this happened...

* Cleanup - Anonymous -> Method reference

* Cleanup - Anonymous -> Lambda

* Cleanup - Comparator helper methods

* Cleanup - final method in final class

* Cleanup - private final methods

* Remove unused imports

* Convert a couple more lambdas to comparators.

* Simplify creature type list comparison.

---------

Co-authored-by: Jetz <Jetz722@gmail.com>
Co-authored-by: tool4ever <therealtoolkit@hotmail.com>
This commit is contained in:
Jetz72
2024-08-02 01:23:58 -04:00
committed by GitHub
parent 86ac0349ca
commit d62dfe1c8c
490 changed files with 6692 additions and 13091 deletions

View File

@@ -60,12 +60,7 @@ public class ActionEditor extends JComponent{
add(edit,BorderLayout.CENTER); add(edit,BorderLayout.CENTER);
edit.addChangeListener(new ChangeListener() { edit.addChangeListener(e -> emitChanged());
@Override
public void stateChanged(ChangeEvent e) {
emitChanged();
}
});
} }
protected void emitChanged() { protected void emitChanged() {
if (updating) if (updating)

View File

@@ -49,12 +49,7 @@ public class DialogTree extends JPanel {
public void addSelectionListener(){ public void addSelectionListener(){
//subscribe to valueChanged, change to that object in edit pane //subscribe to valueChanged, change to that object in edit pane
dialogTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { dialogTree.getSelectionModel().addTreeSelectionListener(this::emitChanged);
@Override
public void valueChanged(TreeSelectionEvent e) {
emitChanged(e);
}
});
} }

View File

@@ -3,7 +3,6 @@ package forge.adventure.editor;
import com.badlogic.gdx.tools.particleeditor.ParticleEditor; import com.badlogic.gdx.tools.particleeditor.ParticleEditor;
import com.google.common.base.Function;
import forge.localinstance.properties.ForgeConstants; import forge.localinstance.properties.ForgeConstants;
import forge.localinstance.properties.ForgePreferences; import forge.localinstance.properties.ForgePreferences;
import forge.model.FModel; import forge.model.FModel;
@@ -23,12 +22,9 @@ public class EditorMainWindow extends JFrame {
public EditorMainWindow() public EditorMainWindow()
{ {
UIManager.LookAndFeelInfo[] var1 = UIManager.getInstalledLookAndFeels(); UIManager.LookAndFeelInfo[] var1 = UIManager.getInstalledLookAndFeels();
FModel.initialize(null, new Function<ForgePreferences, Void>() { FModel.initialize(null, preferences -> {
@Override preferences.setPref(ForgePreferences.FPref.LOAD_CARD_SCRIPTS_LAZILY, true);
public Void apply(ForgePreferences preferences) { return null;
preferences.setPref(ForgePreferences.FPref.LOAD_CARD_SCRIPTS_LAZILY, true);
return null;
}
}); });
Lang.createInstance(FModel.getPreferences().getPref(ForgePreferences.FPref.UI_LANGUAGE)); Lang.createInstance(FModel.getPreferences().getPref(ForgePreferences.FPref.UI_LANGUAGE));
Localizer.getInstance().initialize(FModel.getPreferences().getPref(ForgePreferences.FPref.UI_LANGUAGE), ForgeConstants.LANG_DIR); Localizer.getInstance().initialize(FModel.getPreferences().getPref(ForgePreferences.FPref.UI_LANGUAGE), ForgeConstants.LANG_DIR);

View File

@@ -4,8 +4,6 @@ import forge.adventure.util.Config;
import javax.swing.*; import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -21,12 +19,7 @@ public class FilePicker extends Box {
super(BoxLayout.X_AXIS); super(BoxLayout.X_AXIS);
this.fileEndings = fileEndings; this.fileEndings = fileEndings;
findButton.addActionListener(new ActionListener() { findButton.addActionListener(e -> FilePicker.this.find());
@Override
public void actionPerformed(ActionEvent e) {
FilePicker.this.find();
}
});
add(edit); add(edit);
add(findButton); add(findButton);

View File

@@ -68,12 +68,7 @@ public class StructureEditor extends JComponent{
add(edit,BorderLayout.CENTER); add(edit,BorderLayout.CENTER);
edit.addChangeListener(new ChangeListener() { edit.addChangeListener(e -> emitChanged());
@Override
public void stateChanged(ChangeEvent e) {
emitChanged();
}
});
} }
protected void emitChanged() { protected void emitChanged() {
ChangeListener[] listeners = listenerList.getListeners(ChangeListener.class); ChangeListener[] listeners = listenerList.getListeners(ChangeListener.class);

View File

@@ -60,12 +60,7 @@ public class TerrainsEditor extends JComponent{
add(edit,BorderLayout.CENTER); add(edit,BorderLayout.CENTER);
edit.addChangeListener(new ChangeListener() { edit.addChangeListener(e -> emitChanged());
@Override
public void stateChanged(ChangeEvent e) {
emitChanged();
}
});
} }
protected void emitChanged() { protected void emitChanged() {
ChangeListener[] listeners = listenerList.getListeners(ChangeListener.class); ChangeListener[] listeners = listenerList.getListeners(ChangeListener.class);

View File

@@ -9,8 +9,6 @@ import forge.adventure.util.Config;
import forge.adventure.util.Paths; import forge.adventure.util.Paths;
import javax.swing.*; import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import java.awt.*; import java.awt.*;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
@@ -81,12 +79,7 @@ public class WorldEditor extends JComponent {
public WorldEditor() { public WorldEditor() {
list.setCellRenderer(new BiomeDataRenderer()); list.setCellRenderer(new BiomeDataRenderer());
list.addListSelectionListener(new ListSelectionListener() { list.addListSelectionListener(e -> WorldEditor.this.updateBiome());
@Override
public void valueChanged(ListSelectionEvent e) {
WorldEditor.this.updateBiome();
}
});
BorderLayout layout = new BorderLayout(); BorderLayout layout = new BorderLayout();
setLayout(layout); setLayout(layout);
add(tabs); add(tabs);

View File

@@ -124,12 +124,7 @@ public class AiAttackController {
List<Card> defenders = defender.getCreaturesInPlay(); List<Card> defenders = defender.getCreaturesInPlay();
int totalMana = ComputerUtilMana.getAvailableManaEstimate(defender, true); int totalMana = ComputerUtilMana.getAvailableManaEstimate(defender, true);
int manaReserved = 0; // for paying the cost to transform int manaReserved = 0; // for paying the cost to transform
Predicate<Card> canAnimate = new Predicate<Card>() { Predicate<Card> canAnimate = c -> !c.isTapped() && !c.isCreature() && !c.isPlaneswalker();
@Override
public boolean apply(Card c) {
return !c.isTapped() && !c.isCreature() && !c.isPlaneswalker();
}
};
CardCollection tappedDefenders = new CardCollection(); CardCollection tappedDefenders = new CardCollection();
for (Card c : CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), canAnimate)) { for (Card c : CardLists.filter(defender.getCardsIn(ZoneType.Battlefield), canAnimate)) {
@@ -331,12 +326,7 @@ public class AiAttackController {
} }
public final static List<Card> getPossibleBlockers(final List<Card> blockers, final List<Card> attackers, final boolean nextTurn) { public final static List<Card> getPossibleBlockers(final List<Card> blockers, final List<Card> attackers, final boolean nextTurn) {
return CardLists.filter(blockers, new Predicate<Card>() { return CardLists.filter(blockers, c -> canBlockAnAttacker(c, attackers, nextTurn));
@Override
public boolean apply(final Card c) {
return canBlockAnAttacker(c, attackers, nextTurn);
}
});
} }
public final static boolean canBlockAnAttacker(final Card c, final List<Card> attackers, final boolean nextTurn) { public final static boolean canBlockAnAttacker(final Card c, final List<Card> attackers, final boolean nextTurn) {
@@ -395,15 +385,10 @@ public class AiAttackController {
} }
} }
// reduce the search space // reduce the search space
final List<Card> opponentsAttackers = CardLists.filter(ai.getOpponents().getCreaturesInPlay(), new Predicate<Card>() { final List<Card> opponentsAttackers = CardLists.filter(ai.getOpponents().getCreaturesInPlay(), c -> !c.hasSVar("EndOfTurnLeavePlay")
@Override && (c.toughnessAssignsDamage() || c.getNetCombatDamage() > 0 // performance shortcuts
public boolean apply(final Card c) { || c.getNetCombatDamage() + ComputerUtilCombat.predictPowerBonusOfAttacker(c, null, null, true) > 0)
return !c.hasSVar("EndOfTurnLeavePlay") && ComputerUtilCombat.canAttackNextTurn(c));
&& (c.toughnessAssignsDamage() || c.getNetCombatDamage() > 0 // performance shortcuts
|| c.getNetCombatDamage() + ComputerUtilCombat.predictPowerBonusOfAttacker(c, null, null, true) > 0)
&& ComputerUtilCombat.canAttackNextTurn(c);
}
});
// 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(potentialAttackers, opponentsAttackers, true); final List<Card> blockers = getPossibleBlockers(potentialAttackers, opponentsAttackers, true);
@@ -770,7 +755,7 @@ public class AiAttackController {
return false; return false;
} }
private final Pair<Integer, Integer> getDamageFromBlockingTramplers(final List<Card> blockedAttackers, final List<Card> blockers, final int myFreeMana) { private Pair<Integer, Integer> getDamageFromBlockingTramplers(final List<Card> blockedAttackers, final List<Card> blockers, final int myFreeMana) {
int currentAttackTax = 0; int currentAttackTax = 0;
int trampleDamage = 0; int trampleDamage = 0;
CardCollection remainingBlockers = new CardCollection(blockers); CardCollection remainingBlockers = new CardCollection(blockers);
@@ -797,7 +782,7 @@ public class AiAttackController {
return Pair.of(trampleDamage, currentAttackTax); return Pair.of(trampleDamage, currentAttackTax);
} }
private final GameEntity chooseDefender(final Combat c, final boolean bAssault) { private GameEntity chooseDefender(final Combat c, final boolean bAssault) {
final FCollectionView<GameEntity> defs = c.getDefenders(); final FCollectionView<GameEntity> defs = c.getDefenders();
if (defs.size() == 1) { if (defs.size() == 1) {
return defs.getFirst(); return defs.getFirst();
@@ -932,31 +917,28 @@ public class AiAttackController {
// check defenders in order of maximum requirements // check defenders in order of maximum requirements
List<Pair<GameEntity, Integer>> reqs = combat.getAttackConstraints().getRequirements().get(attacker).getSortedRequirements(); List<Pair<GameEntity, Integer>> reqs = combat.getAttackConstraints().getRequirements().get(attacker).getSortedRequirements();
final GameEntity def = defender; final GameEntity def = defender;
reqs.sort(new Comparator<Pair<GameEntity, Integer>>() { reqs.sort((r1, r2) -> {
@Override if (r1.getValue() == r2.getValue()) {
public int compare(Pair<GameEntity, Integer> r1, Pair<GameEntity, Integer> r2) { // try to attack the designated defender
if (r1.getValue() == r2.getValue()) { if (r1.getKey().equals(def) && !r2.getKey().equals(def)) {
// try to attack the designated defender return -1;
if (r1.getKey().equals(def) && !r2.getKey().equals(def)) { }
return -1; if (r2.getKey().equals(def) && !r1.getKey().equals(def)) {
} return 1;
if (r2.getKey().equals(def) && !r1.getKey().equals(def)) { }
return 1; // otherwise PW
} if (r1.getKey() instanceof Card && r2.getKey() instanceof Player) {
// otherwise PW return -1;
if (r1.getKey() instanceof Card && r2.getKey() instanceof Player) { }
return -1; if (r2.getKey() instanceof Card && r1.getKey() instanceof Player) {
} return 1;
if (r2.getKey() instanceof Card && r1.getKey() instanceof Player) { }
return 1; // or weakest player
} if (r1.getKey() instanceof Player && r2.getKey() instanceof Player) {
// or weakest player return ((Player) r1.getKey()).getLife() - ((Player) r2.getKey()).getLife();
if (r1.getKey() instanceof Player && r2.getKey() instanceof Player) {
return ((Player) r1.getKey()).getLife() - ((Player) r2.getKey()).getLife();
}
} }
return r2.getValue() - r1.getValue();
} }
return r2.getValue() - r1.getValue();
}); });
for (Pair<GameEntity, Integer> e : reqs) { for (Pair<GameEntity, Integer> e : reqs) {
if (e.getRight() == 0) continue; if (e.getRight() == 0) continue;
@@ -1406,14 +1388,9 @@ public class AiAttackController {
} }
// contains only the defender's blockers that can actually block the attacker // contains only the defender's blockers that can actually block the attacker
CardCollection validBlockers = CardLists.filter(defenders, new Predicate<Card>() { CardCollection validBlockers = CardLists.filter(defenders, defender1 -> CombatUtil.canBlock(attacker, defender1));
@Override
public boolean apply(Card defender) {
return CombatUtil.canBlock(attacker, defender);
}
});
boolean canTrampleOverDefenders = attacker.hasKeyword(Keyword.TRAMPLE) && attacker.getNetCombatDamage() > Aggregates.sum(validBlockers, CardPredicates.Accessors.fnGetNetToughness); boolean canTrampleOverDefenders = attacker.hasKeyword(Keyword.TRAMPLE) && attacker.getNetCombatDamage() > Aggregates.sum(validBlockers, Card::getNetToughness);
// used to check that CanKillAllDangerous check makes sense in context where creatures with dangerous abilities are present // used to check that CanKillAllDangerous check makes sense in context where creatures with dangerous abilities are present
boolean dangerousBlockersPresent = Iterables.any(validBlockers, Predicates.or( boolean dangerousBlockersPresent = Iterables.any(validBlockers, Predicates.or(

View File

@@ -140,23 +140,20 @@ public class AiBlockController {
ComputerUtilCard.sortByEvaluateCreature(attackers); ComputerUtilCard.sortByEvaluateCreature(attackers);
CardLists.sortByPowerDesc(attackers); CardLists.sortByPowerDesc(attackers);
//move cards like Phage the Untouchable to the front //move cards like Phage the Untouchable to the front
attackers.sort(new Comparator<Card>() { attackers.sort((o1, o2) -> {
@Override if (o1.hasSVar("MustBeBlocked") && !o2.hasSVar("MustBeBlocked")) {
public int compare(final Card o1, final Card o2) { return -1;
if (o1.hasSVar("MustBeBlocked") && !o2.hasSVar("MustBeBlocked")) {
return -1;
}
if (!o1.hasSVar("MustBeBlocked") && o2.hasSVar("MustBeBlocked")) {
return 1;
}
if (attackingCmd.contains(o1) && !attackingCmd.contains(o2)) {
return -1;
}
if (!attackingCmd.contains(o1) && attackingCmd.contains(o2)) {
return 1;
}
return 0;
} }
if (!o1.hasSVar("MustBeBlocked") && o2.hasSVar("MustBeBlocked")) {
return 1;
}
if (attackingCmd.contains(o1) && !attackingCmd.contains(o2)) {
return -1;
}
if (!attackingCmd.contains(o1) && attackingCmd.contains(o2)) {
return 1;
}
return 0;
}); });
return attackers; return attackers;
} }
@@ -330,43 +327,35 @@ public class AiBlockController {
} }
private Predicate<Card> rampagesOrNeedsManyToBlock(final Combat combat) { private Predicate<Card> rampagesOrNeedsManyToBlock(final Combat combat) {
return Predicates.or(CardPredicates.hasKeyword(Keyword.RAMPAGE), new Predicate<Card>() { return Predicates.or(CardPredicates.hasKeyword(Keyword.RAMPAGE), input -> {
// select creature that has a max blocker
@Override return StaticAbilityCantAttackBlock.getMinMaxBlocker(input, combat.getDefenderPlayerByAttacker(input)).getRight() < Integer.MAX_VALUE;
public boolean apply(Card input) {
// select creature that has a max blocker
return StaticAbilityCantAttackBlock.getMinMaxBlocker(input, combat.getDefenderPlayerByAttacker(input)).getRight() < Integer.MAX_VALUE;
}
}); });
} }
private Predicate<Card> changesPTWhenBlocked(final boolean onlyForDefVsTrample) { private Predicate<Card> changesPTWhenBlocked(final boolean onlyForDefVsTrample) {
return new Predicate<Card>() { return card -> {
@Override for (final Trigger tr : card.getTriggers()) {
public boolean apply(Card card) { if (tr.getMode() == TriggerType.AttackerBlocked) {
for (final Trigger tr : card.getTriggers()) { SpellAbility ab = tr.getOverridingAbility();
if (tr.getMode() == TriggerType.AttackerBlocked) { if (ab != null) {
SpellAbility ab = tr.getOverridingAbility(); if (ab.getApi() == ApiType.Pump && "Self".equals(ab.getParam("Defined"))) {
if (ab != null) { String rawP = ab.getParam("NumAtt");
if (ab.getApi() == ApiType.Pump && "Self".equals(ab.getParam("Defined"))) { String rawT = ab.getParam("NumDef");
String rawP = ab.getParam("NumAtt"); if ("+X".equals(rawP) && "+X".equals(rawT) && card.getSVar("X").startsWith("Count$Valid Creature.blockingTriggeredAttacker")) {
String rawT = ab.getParam("NumDef"); return true;
if ("+X".equals(rawP) && "+X".equals(rawT) && card.getSVar("X").startsWith("Count$Valid Creature.blockingTriggeredAttacker")) {
return true;
}
// TODO: maybe also predict calculated bonus above certain threshold?
} else if (ab.getApi() == ApiType.PumpAll && ab.hasParam("ValidCards")
&& ab.getParam("ValidCards").startsWith("Creature.blockingSource")) {
int pBonus = AbilityUtils.calculateAmount(card, ab.getParam("NumAtt"), ab);
int tBonus = AbilityUtils.calculateAmount(card, ab.getParam("NumDef"), ab);
return (!onlyForDefVsTrample && pBonus < 0) || tBonus < 0;
} }
// TODO: maybe also predict calculated bonus above certain threshold?
} else if (ab.getApi() == ApiType.PumpAll && ab.hasParam("ValidCards")
&& ab.getParam("ValidCards").startsWith("Creature.blockingSource")) {
int pBonus = AbilityUtils.calculateAmount(card, ab.getParam("NumAtt"), ab);
int tBonus = AbilityUtils.calculateAmount(card, ab.getParam("NumDef"), ab);
return (!onlyForDefVsTrample && pBonus < 0) || tBonus < 0;
} }
} }
} }
return false;
} }
return false;
}; };
} }
@@ -452,15 +441,12 @@ public class AiBlockController {
// Try to add blockers that could be destroyed, but are worth less than the attacker // Try to add blockers that could be destroyed, but are worth less than the attacker
// Don't use blockers without First Strike or Double Strike if attacker has it // Don't use blockers without First Strike or Double Strike if attacker has it
usableBlockers = CardLists.filter(blockers, new Predicate<Card>() { usableBlockers = CardLists.filter(blockers, c -> {
@Override if (ComputerUtilCombat.dealsFirstStrikeDamage(attacker, false, combat)
public boolean apply(final Card c) { && !ComputerUtilCombat.dealsFirstStrikeDamage(c, false, combat)) {
if (ComputerUtilCombat.dealsFirstStrikeDamage(attacker, false, combat) return false;
&& !ComputerUtilCombat.dealsFirstStrikeDamage(c, false, combat)) {
return false;
}
return lifeInDanger || wouldLikeToRandomlyTrade(attacker, c, combat) || ComputerUtilCard.evaluateCreature(c) + diff < ComputerUtilCard.evaluateCreature(attacker);
} }
return lifeInDanger || wouldLikeToRandomlyTrade(attacker, c, combat) || ComputerUtilCard.evaluateCreature(c) + diff < ComputerUtilCard.evaluateCreature(attacker);
}); });
if (usableBlockers.size() < 2) { if (usableBlockers.size() < 2) {
return; return;
@@ -579,13 +565,8 @@ public class AiBlockController {
final List<Card> blockGang = new ArrayList<>(); final List<Card> blockGang = new ArrayList<>();
int absorbedDamage; // The amount of damage needed to kill the first blocker int absorbedDamage; // The amount of damage needed to kill the first blocker
List<Card> usableBlockers = CardLists.filter(blockers, new Predicate<Card>() { List<Card> usableBlockers = CardLists.filter(blockers, c -> c.getNetToughness() > attacker.getNetCombatDamage() // performance shortcut
@Override || c.getNetToughness() + ComputerUtilCombat.predictToughnessBonusOfBlocker(attacker, c, true) > attacker.getNetCombatDamage());
public boolean apply(final Card c) {
return c.getNetToughness() > attacker.getNetCombatDamage() // performance shortcut
|| c.getNetToughness() + ComputerUtilCombat.predictToughnessBonusOfBlocker(attacker, c, true) > attacker.getNetCombatDamage();
}
});
if (usableBlockers.size() < 2) { if (usableBlockers.size() < 2) {
return; return;
} }
@@ -829,12 +810,7 @@ public class AiBlockController {
blockers.removeAll(combat.getBlockers(attacker)); blockers.removeAll(combat.getBlockers(attacker));
// Don't add any blockers that won't kill the attacker because the damage would be prevented by a static effect // Don't add any blockers that won't kill the attacker because the damage would be prevented by a static effect
blockers = CardLists.filter(blockers, new Predicate<Card>() { blockers = CardLists.filter(blockers, blocker -> !ComputerUtilCombat.isCombatDamagePrevented(blocker, attacker, blocker.getNetCombatDamage()));
@Override
public boolean apply(Card blocker) {
return !ComputerUtilCombat.isCombatDamagePrevented(blocker, attacker, blocker.getNetCombatDamage());
}
});
// Try to use safe blockers first // Try to use safe blockers first
if (blockers.size() > 0) { if (blockers.size() > 0) {
@@ -921,13 +897,9 @@ public class AiBlockController {
CardCollection pwsWithChumpBlocks = new CardCollection(); CardCollection pwsWithChumpBlocks = new CardCollection();
CardCollection chosenChumpBlockers = new CardCollection(); CardCollection chosenChumpBlockers = new CardCollection();
CardCollection chumpPWDefenders = CardLists.filter(this.blockersLeft, new Predicate<Card>() { CardCollection chumpPWDefenders = CardLists.filter(this.blockersLeft,
@Override card -> ComputerUtilCard.evaluateCreature(card) <= (card.isToken() ? evalThresholdToken : evalThresholdNonToken)
public boolean apply(Card card) { );
return ComputerUtilCard.evaluateCreature(card) <= (card.isToken() ? evalThresholdToken
: evalThresholdNonToken);
}
});
CardLists.sortByPowerAsc(chumpPWDefenders); CardLists.sortByPowerAsc(chumpPWDefenders);
if (!chumpPWDefenders.isEmpty()) { if (!chumpPWDefenders.isEmpty()) {
for (final Card attacker : attackers) { for (final Card attacker : attackers) {

View File

@@ -37,7 +37,6 @@ import forge.game.ability.AbilityUtils;
import forge.game.ability.ApiType; import forge.game.ability.ApiType;
import forge.game.ability.SpellApiBased; import forge.game.ability.SpellApiBased;
import forge.game.card.*; import forge.game.card.*;
import forge.game.card.CardPredicates.Accessors;
import forge.game.card.CardPredicates.Presets; import forge.game.card.CardPredicates.Presets;
import forge.game.combat.Combat; import forge.game.combat.Combat;
import forge.game.combat.CombatUtil; import forge.game.combat.CombatUtil;
@@ -418,7 +417,7 @@ public class AiController {
CardCollection allCards = new CardCollection(player.getCardsIn(ZoneType.Graveyard)); CardCollection allCards = new CardCollection(player.getCardsIn(ZoneType.Graveyard));
allCards.addAll(player.getCardsIn(ZoneType.Command)); allCards.addAll(player.getCardsIn(ZoneType.Command));
allCards.addAll(cardsInPlay); allCards.addAll(cardsInPlay);
int maxCmcInHand = Aggregates.max(hand, CardPredicates.Accessors.fnGetCmc); int maxCmcInHand = Aggregates.max(hand, Card::getCMC);
int max = Math.max(maxCmcInHand, 6); int max = Math.max(maxCmcInHand, 6);
// consider not playing lands if there are enough already and an ability with a discard cost is present // consider not playing lands if there are enough already and an ability with a discard cost is present
if (landsInPlay.size() + landList.size() > max) { if (landsInPlay.size() + landList.size() > max) {
@@ -437,43 +436,40 @@ public class AiController {
} }
} }
landList = CardLists.filter(landList, new Predicate<Card>() { landList = CardLists.filter(landList, c -> {
@Override CardCollectionView battlefield = player.getCardsIn(ZoneType.Battlefield);
public boolean apply(final Card c) { if (canPlaySpellBasic(c, null) != AiPlayDecision.WillPlay) {
CardCollectionView battlefield = player.getCardsIn(ZoneType.Battlefield); return false;
if (canPlaySpellBasic(c, null) != AiPlayDecision.WillPlay) { }
String name = c.getName();
if (c.getType().isLegendary() && !name.equals("Flagstones of Trokair")) {
if (Iterables.any(battlefield, CardPredicates.nameEquals(name))) {
return false; return false;
} }
String name = c.getName();
if (c.getType().isLegendary() && !name.equals("Flagstones of Trokair")) {
if (Iterables.any(battlefield, CardPredicates.nameEquals(name))) {
return false;
}
}
final CardCollectionView hand = player.getCardsIn(ZoneType.Hand);
CardCollection lands = new CardCollection(battlefield);
lands.addAll(hand);
lands = CardLists.filter(lands, CardPredicates.Presets.LANDS);
int maxCmcInHand = Aggregates.max(hand, CardPredicates.Accessors.fnGetCmc);
if (lands.size() >= Math.max(maxCmcInHand, 6)) {
// don't play MDFC land if other side is spell and enough lands are available
if (!c.isLand() || (c.isModal() && !c.getState(CardStateName.Modal).getType().isLand())) {
return false;
}
// don't play the land if it has cycling and enough lands are available
final FCollectionView<SpellAbility> spellAbilities = c.getSpellAbilities();
for (final SpellAbility sa : spellAbilities) {
if (sa.isCycling()) {
return false;
}
}
}
return player.canPlayLand(c);
} }
final CardCollectionView hand1 = player.getCardsIn(ZoneType.Hand);
CardCollection lands = new CardCollection(battlefield);
lands.addAll(hand1);
lands = CardLists.filter(lands, Presets.LANDS);
int maxCmcInHand = Aggregates.max(hand1, Card::getCMC);
if (lands.size() >= Math.max(maxCmcInHand, 6)) {
// don't play MDFC land if other side is spell and enough lands are available
if (!c.isLand() || (c.isModal() && !c.getState(CardStateName.Modal).getType().isLand())) {
return false;
}
// don't play the land if it has cycling and enough lands are available
final FCollectionView<SpellAbility> spellAbilities = c.getSpellAbilities();
for (final SpellAbility sa : spellAbilities) {
if (sa.isCycling()) {
return false;
}
}
}
return player.canPlayLand(c);
}); });
return landList; return landList;
} }
@@ -1441,8 +1437,8 @@ public class AiController {
} }
} }
int totalCMCInHand = Aggregates.sum(inHand, CardPredicates.Accessors.fnGetCmc); int totalCMCInHand = Aggregates.sum(inHand, Card::getCMC);
int minCMCInHand = Aggregates.min(inHand, CardPredicates.Accessors.fnGetCmc); int minCMCInHand = Aggregates.min(inHand, Card::getCMC);
if (minCMCInHand == Integer.MAX_VALUE) if (minCMCInHand == Integer.MAX_VALUE)
minCMCInHand = 0; minCMCInHand = 0;
int predictedMana = ComputerUtilMana.getAvailableManaEstimate(player, true); int predictedMana = ComputerUtilMana.getAvailableManaEstimate(player, true);
@@ -1450,66 +1446,60 @@ public class AiController {
boolean canCastWithLandDrop = (predictedMana + 1 >= minCMCInHand) && minCMCInHand > 0 && !isTapLand; boolean canCastWithLandDrop = (predictedMana + 1 >= minCMCInHand) && minCMCInHand > 0 && !isTapLand;
boolean cantCastAnythingNow = predictedMana < minCMCInHand; boolean cantCastAnythingNow = predictedMana < minCMCInHand;
boolean hasRelevantAbsOTB = Iterables.any(otb, new Predicate<Card>() { boolean hasRelevantAbsOTB = Iterables.any(otb, card -> {
@Override boolean isTapLand1 = false;
public boolean apply(Card card) { for (ReplacementEffect repl : card.getReplacementEffects()) {
boolean isTapLand = false; // TODO: improve the detection of taplands
for (ReplacementEffect repl : card.getReplacementEffects()) { if (repl.getParamOrDefault("Description", "").equals("CARDNAME enters the battlefield tapped.")) {
// TODO: improve the detection of taplands isTapLand1 = true;
if (repl.getParamOrDefault("Description", "").equals("CARDNAME enters the battlefield tapped.")) {
isTapLand = true;
}
} }
for (SpellAbility sa : card.getSpellAbilities()) {
if (sa.isAbility()
&& sa.getPayCosts().getCostMana() != null
&& sa.getPayCosts().getCostMana().getMana().getCMC() > 0
&& (!sa.getPayCosts().hasTapCost() || !isTapLand)
&& (!sa.hasParam("ActivationZone") || sa.getParam("ActivationZone").contains("Battlefield"))) {
return true;
}
}
return false;
} }
for (SpellAbility sa : card.getSpellAbilities()) {
if (sa.isAbility()
&& sa.getPayCosts().getCostMana() != null
&& sa.getPayCosts().getCostMana().getMana().getCMC() > 0
&& (!sa.getPayCosts().hasTapCost() || !isTapLand1)
&& (!sa.hasParam("ActivationZone") || sa.getParam("ActivationZone").contains("Battlefield"))) {
return true;
}
}
return false;
}); });
boolean hasLandBasedEffect = Iterables.any(otb, new Predicate<Card>() { boolean hasLandBasedEffect = Iterables.any(otb, card -> {
@Override for (Trigger t : card.getTriggers()) {
public boolean apply(Card card) { Map<String, String> params = t.getMapParams();
for (Trigger t : card.getTriggers()) { if ("ChangesZone".equals(params.get("Mode"))
Map<String, String> params = t.getMapParams(); && params.containsKey("ValidCard")
if ("ChangesZone".equals(params.get("Mode")) && (!params.containsKey("AILogic") || !params.get("AILogic").equals("SafeToHold"))
&& params.containsKey("ValidCard") && !params.get("ValidCard").contains("nonLand")
&& (!params.containsKey("AILogic") || !params.get("AILogic").equals("SafeToHold")) && ((params.get("ValidCard").contains("Land")) || (params.get("ValidCard").contains("Permanent")))
&& !params.get("ValidCard").contains("nonLand") && "Battlefield".equals(params.get("Destination"))) {
&& ((params.get("ValidCard").contains("Land")) || (params.get("ValidCard").contains("Permanent"))) // Landfall and other similar triggers
&& "Battlefield".equals(params.get("Destination"))) { return true;
// Landfall and other similar triggers
return true;
}
} }
for (String sv : card.getSVars().keySet()) { }
String varValue = card.getSVar(sv); for (String sv : card.getSVars().keySet()) {
if (varValue.equals("Count$Domain")) { String varValue = card.getSVar(sv);
for (String type : landToPlay.getType().getLandTypes()) { if (varValue.equals("Count$Domain")) {
if (CardType.isABasicLandType(type) && CardLists.getType(otb, type).isEmpty()) { for (String type : landToPlay.getType().getLandTypes()) {
return true; if (CardType.isABasicLandType(type) && CardLists.getType(otb, type).isEmpty()) {
}
}
}
if (varValue.startsWith("Count$Valid") || sv.equals("BuffedBy")) {
if (varValue.contains("Land") || varValue.contains("Plains") || varValue.contains("Forest")
|| varValue.contains("Mountain") || varValue.contains("Island") || varValue.contains("Swamp")
|| varValue.contains("Wastes")) {
// In presence of various cards that get buffs like "equal to the number of lands you control",
// safer for our AI model to just play the land earlier rather than make a blunder
return true; return true;
} }
} }
} }
return false; if (varValue.startsWith("Count$Valid") || sv.equals("BuffedBy")) {
if (varValue.contains("Land") || varValue.contains("Plains") || varValue.contains("Forest")
|| varValue.contains("Mountain") || varValue.contains("Island") || varValue.contains("Swamp")
|| varValue.contains("Wastes")) {
// In presence of various cards that get buffs like "equal to the number of lands you control",
// safer for our AI model to just play the land earlier rather than make a blunder
return true;
}
}
} }
return false;
}); });
// TODO: add prediction for effects that will untap a tapland as it enters the battlefield // TODO: add prediction for effects that will untap a tapland as it enters the battlefield
@@ -1528,7 +1518,7 @@ public class AiController {
return true; return true;
} }
private final SpellAbility getSpellAbilityToPlay() { private SpellAbility getSpellAbilityToPlay() {
CardCollection cards = ComputerUtilAbility.getAvailableCards(game, player); CardCollection cards = ComputerUtilAbility.getAvailableCards(game, player);
cards = ComputerUtilCard.dedupeCards(cards); cards = ComputerUtilCard.dedupeCards(cards);
List<SpellAbility> saList = Lists.newArrayList(); List<SpellAbility> saList = Lists.newArrayList();
@@ -1572,12 +1562,9 @@ public class AiController {
saList = ComputerUtilAbility.getSpellAbilities(cards, player); saList = ComputerUtilAbility.getSpellAbilities(cards, player);
} }
Iterables.removeIf(saList, new Predicate<SpellAbility>() { Iterables.removeIf(saList, spellAbility -> { //don't include removedAI cards if somehow the AI can play the ability or gain control of unsupported card
@Override // TODO allow when experimental profile?
public boolean apply(final SpellAbility spellAbility) { //don't include removedAI cards if somehow the AI can play the ability or gain control of unsupported card return spellAbility instanceof LandAbility || (spellAbility.getHostCard() != null && ComputerUtilCard.isCardRemAIDeck(spellAbility.getHostCard()));
// TODO allow when experimental profile?
return spellAbility instanceof LandAbility || (spellAbility.getHostCard() != null && ComputerUtilCard.isCardRemAIDeck(spellAbility.getHostCard()));
}
}); });
//update LivingEndPlayer //update LivingEndPlayer
useLivingEnd = Iterables.any(player.getZone(ZoneType.Library), CardPredicates.nameEquals("Living End")); useLivingEnd = Iterables.any(player.getZone(ZoneType.Library), CardPredicates.nameEquals("Living End"));
@@ -1997,14 +1984,9 @@ public class AiController {
case FlipOntoBattlefield: case FlipOntoBattlefield:
if ("DamageCreatures".equals(sa.getParam("AILogic"))) { if ("DamageCreatures".equals(sa.getParam("AILogic"))) {
int maxToughness = Integer.parseInt(sa.getSubAbility().getParam("NumDmg")); int maxToughness = Integer.parseInt(sa.getSubAbility().getParam("NumDmg"));
CardCollectionView rightToughness = CardLists.filter(pool, new Predicate<Card>() { CardCollectionView rightToughness = CardLists.filter(pool, card -> card.getController().isOpponentOf(sa.getActivatingPlayer())
@Override && card.getNetToughness() <= maxToughness
public boolean apply(Card card) { && card.canBeDestroyed());
return card.getController().isOpponentOf(sa.getActivatingPlayer())
&& card.getNetToughness() <= maxToughness
&& card.canBeDestroyed();
}
});
Card bestCreature = ComputerUtilCard.getBestCreatureAI(rightToughness.isEmpty() ? pool : rightToughness); Card bestCreature = ComputerUtilCard.getBestCreatureAI(rightToughness.isEmpty() ? pool : rightToughness);
if (bestCreature != null) { if (bestCreature != null) {
result.add(bestCreature); result.add(bestCreature);
@@ -2140,7 +2122,7 @@ public class AiController {
CardCollection all = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.NONLAND_PERMANENTS); CardCollection all = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.NONLAND_PERMANENTS);
CardCollection left = CardLists.filterControlledBy(all, game.getNextPlayerAfter(player, Direction.Left)); CardCollection left = CardLists.filterControlledBy(all, game.getNextPlayerAfter(player, Direction.Left));
CardCollection right = CardLists.filterControlledBy(all, game.getNextPlayerAfter(player, Direction.Right)); CardCollection right = CardLists.filterControlledBy(all, game.getNextPlayerAfter(player, Direction.Right));
return Aggregates.sum(left, Accessors.fnGetCmc) > Aggregates.sum(right, Accessors.fnGetCmc); return Aggregates.sum(left, Card::getCMC) > Aggregates.sum(right, Card::getCMC);
} }
return MyRandom.getRandom().nextBoolean(); return MyRandom.getRandom().nextBoolean();
} }
@@ -2167,8 +2149,8 @@ public class AiController {
} else if (aiLogic.equals("CMCOppControlsByPower")) { } else if (aiLogic.equals("CMCOppControlsByPower")) {
// TODO: improve this to check for how dangerous those creatures actually are relative to host card // TODO: improve this to check for how dangerous those creatures actually are relative to host card
CardCollectionView hand = sa.getActivatingPlayer().getOpponents().getCardsIn(ZoneType.Battlefield); CardCollectionView hand = sa.getActivatingPlayer().getOpponents().getCardsIn(ZoneType.Battlefield);
int powerEven = Aggregates.sum(CardLists.filter(hand, CardPredicates.evenCMC()), Accessors.fnGetNetPower); int powerEven = Aggregates.sum(CardLists.filter(hand, CardPredicates.evenCMC()), Card::getNetPower);
int powerOdd = Aggregates.sum(CardLists.filter(hand, CardPredicates.oddCMC()), Accessors.fnGetNetPower); int powerOdd = Aggregates.sum(CardLists.filter(hand, CardPredicates.oddCMC()), Card::getNetPower);
return powerOdd > powerEven; return powerOdd > powerEven;
} }
return MyRandom.getRandom().nextBoolean(); // outside of any specific logic, choose randomly return MyRandom.getRandom().nextBoolean(); // outside of any specific logic, choose randomly

View File

@@ -1,6 +1,5 @@
package forge.ai; package forge.ai;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.card.CardType; import forge.card.CardType;
@@ -445,16 +444,13 @@ public class AiCostDecision extends CostDecisionMakerBase {
CardCollectionView toExclude = CardCollectionView toExclude =
CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), type.split(";"), CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), type.split(";"),
ability.getActivatingPlayer(), ability.getHostCard(), ability); ability.getActivatingPlayer(), ability.getHostCard(), ability);
toExclude = CardLists.filter(toExclude, new Predicate<Card>() { toExclude = CardLists.filter(toExclude, card -> {
@Override for (final SpellAbility sa : card.getSpellAbilities()) {
public boolean apply(Card card) { if (sa.isManaAbility() && sa.getPayCosts().hasTapCost()) {
for (final SpellAbility sa : card.getSpellAbilities()) { return true;
if (sa.isManaAbility() && sa.getPayCosts().hasTapCost()) {
return true;
}
} }
return false;
} }
return false;
}); });
exclude.addAll(toExclude); exclude.addAll(toExclude);
} }
@@ -642,16 +638,13 @@ public class AiCostDecision extends CostDecisionMakerBase {
// filter for negative counters // filter for negative counters
if (c > toRemove && cost.counter == null) { if (c > toRemove && cost.counter == null) {
List<Card> negatives = CardLists.filter(typeList, new Predicate<Card>() { List<Card> negatives = CardLists.filter(typeList, crd -> {
@Override for (CounterType cType : table.filterToRemove(crd).keySet()) {
public boolean apply(final Card crd) { if (ComputerUtil.isNegativeCounter(cType, crd)) {
for (CounterType cType : table.filterToRemove(crd).keySet()) { return true;
if (ComputerUtil.isNegativeCounter(cType, crd)) {
return true;
}
} }
return false;
} }
return false;
}); });
if (!negatives.isEmpty()) { if (!negatives.isEmpty()) {
@@ -673,16 +666,13 @@ public class AiCostDecision extends CostDecisionMakerBase {
// filter for useless counters // filter for useless counters
// they have no effect on the card, if they are there or removed // they have no effect on the card, if they are there or removed
if (c > toRemove && cost.counter == null) { if (c > toRemove && cost.counter == null) {
List<Card> useless = CardLists.filter(typeList, new Predicate<Card>() { List<Card> useless = CardLists.filter(typeList, crd -> {
@Override for (CounterType ctype : table.filterToRemove(crd).keySet()) {
public boolean apply(final Card crd) { if (ComputerUtil.isUselessCounter(ctype, crd)) {
for (CounterType ctype : table.filterToRemove(crd).keySet()) { return true;
if (ComputerUtil.isUselessCounter(ctype, crd)) {
return true;
}
} }
return false;
} }
return false;
}); });
if (!useless.isEmpty()) { if (!useless.isEmpty()) {
@@ -710,17 +700,14 @@ public class AiCostDecision extends CostDecisionMakerBase {
// try to remove Quest counter on something with enough counters for the // try to remove Quest counter on something with enough counters for the
// effect to continue // effect to continue
if (c > toRemove && (cost.counter == null || cost.counter.is(CounterEnumType.QUEST))) { if (c > toRemove && (cost.counter == null || cost.counter.is(CounterEnumType.QUEST))) {
List<Card> prefs = CardLists.filter(typeList, new Predicate<Card>() { List<Card> prefs = CardLists.filter(typeList, crd -> {
@Override // a Card without MaxQuestEffect doesn't need any Quest
public boolean apply(final Card crd) { // counters
// a Card without MaxQuestEffect doesn't need any Quest int e = 0;
// counters if (crd.hasSVar("MaxQuestEffect")) {
int e = 0; e = Integer.parseInt(crd.getSVar("MaxQuestEffect"));
if (crd.hasSVar("MaxQuestEffect")) {
e = Integer.parseInt(crd.getSVar("MaxQuestEffect"));
}
return crd.getCounters(CounterEnumType.QUEST) > e;
} }
return crd.getCounters(CounterEnumType.QUEST) > e;
}); });
prefs.sort(Collections.reverseOrder(CardPredicates.compareByCounterType(CounterEnumType.QUEST))); prefs.sort(Collections.reverseOrder(CardPredicates.compareByCounterType(CounterEnumType.QUEST)));

View File

@@ -11,28 +11,24 @@ import forge.game.zone.ZoneType;
public final class AiPlayerPredicates { public final class AiPlayerPredicates {
public static final Comparator<Player> compareByZoneValue(final String type, final ZoneType zone, public static Comparator<Player> compareByZoneValue(final String type, final ZoneType zone, final SpellAbility sa) {
final SpellAbility sa) { return (arg0, arg1) -> {
return new Comparator<Player>() { CardCollectionView list0 = AbilityUtils.filterListByType(arg0.getCardsIn(zone), type, sa);
@Override CardCollectionView list1 = AbilityUtils.filterListByType(arg1.getCardsIn(zone), type, sa);
public int compare(Player arg0, Player arg1) {
CardCollectionView list0 = AbilityUtils.filterListByType(arg0.getCardsIn(zone), type, sa);
CardCollectionView list1 = AbilityUtils.filterListByType(arg1.getCardsIn(zone), type, sa);
int v0, v1; int v0, v1;
if ((CardLists.getNotType(list0, "Creature").isEmpty()) if ((CardLists.getNotType(list0, "Creature").isEmpty())
&& (CardLists.getNotType(list1, "Creature").isEmpty())) { && (CardLists.getNotType(list1, "Creature").isEmpty())) {
v0 = ComputerUtilCard.evaluateCreatureList(list0); v0 = ComputerUtilCard.evaluateCreatureList(list0);
v1 = ComputerUtilCard.evaluateCreatureList(list1); v1 = ComputerUtilCard.evaluateCreatureList(list1);
} // otherwise evaluate both lists by CMC and pass only if human } // otherwise evaluate both lists by CMC and pass only if human
// permanents are less valuable // permanents are less valuable
else { else {
v0 = ComputerUtilCard.evaluatePermanentList(list0); v0 = ComputerUtilCard.evaluatePermanentList(list0);
v1 = ComputerUtilCard.evaluatePermanentList(list1); v1 = ComputerUtilCard.evaluatePermanentList(list1);
}
return Integer.compare(v0, v1);
} }
return Integer.compare(v0, v1);
}; };
} }
} }

View File

@@ -93,7 +93,7 @@ public class AiProfileUtil {
* Load a single profile. * Load a single profile.
* @param profileName a profile to load. * @param profileName a profile to load.
*/ */
private static final Map<AiProps, String> loadProfile(final String profileName) { private static Map<AiProps, String> loadProfile(final String profileName) {
Map<AiProps, String> profileMap = new HashMap<>(); Map<AiProps, String> profileMap = new HashMap<>();
List<String> lines = FileUtil.readFile(buildFileName(profileName)); List<String> lines = FileUtil.readFile(buildFileName(profileName));

View File

@@ -408,13 +408,9 @@ public class ComputerUtil {
return ComputerUtilCard.getWorstLand(landsInPlay); return ComputerUtilCard.getWorstLand(landsInPlay);
} }
} }
final CardCollection sacMeList = CardLists.filter(typeList, new Predicate<Card>() { final CardCollection sacMeList = CardLists.filter(typeList, c -> (c.hasSVar("SacMe") && Integer.parseInt(c.getSVar("SacMe")) == priority)
@Override || (priority == 1 && shouldSacrificeThreatenedCard(ai, c, sa))
public boolean apply(final Card c) { );
return (c.hasSVar("SacMe") && Integer.parseInt(c.getSVar("SacMe")) == priority)
|| (priority == 1 && shouldSacrificeThreatenedCard(ai, c, sa));
}
});
if (!sacMeList.isEmpty()) { if (!sacMeList.isEmpty()) {
CardLists.shuffle(sacMeList); CardLists.shuffle(sacMeList);
return sacMeList.getFirst(); return sacMeList.getFirst();
@@ -429,16 +425,13 @@ public class ComputerUtil {
int maxCreatureEval = aic.getIntProperty(AiProps.SACRIFICE_DEFAULT_PREF_MAX_CREATURE_EVAL); int maxCreatureEval = aic.getIntProperty(AiProps.SACRIFICE_DEFAULT_PREF_MAX_CREATURE_EVAL);
boolean allowTokens = aic.getBooleanProperty(AiProps.SACRIFICE_DEFAULT_PREF_ALLOW_TOKENS); boolean allowTokens = aic.getBooleanProperty(AiProps.SACRIFICE_DEFAULT_PREF_ALLOW_TOKENS);
List<String> dontSac = Arrays.asList("Black Lotus", "Mox Pearl", "Mox Jet", "Mox Emerald", "Mox Ruby", "Mox Sapphire", "Lotus Petal"); List<String> dontSac = Arrays.asList("Black Lotus", "Mox Pearl", "Mox Jet", "Mox Emerald", "Mox Ruby", "Mox Sapphire", "Lotus Petal");
CardCollection allowList = CardLists.filter(typeList, new Predicate<Card>() { CardCollection allowList = CardLists.filter(typeList, card -> {
@Override if (card.isCreature() && ComputerUtilCard.evaluateCreature(card) > maxCreatureEval) {
public boolean apply(Card card) { return false;
if (card.isCreature() && ComputerUtilCard.evaluateCreature(card) > maxCreatureEval) {
return false;
}
return (allowTokens && card.isToken())
|| (card.getCMC() >= minCMC && card.getCMC() <= maxCMC && !dontSac.contains(card.getName()));
} }
return (allowTokens && card.isToken())
|| (card.getCMC() >= minCMC && card.getCMC() <= maxCMC && !dontSac.contains(card.getName()));
}); });
if (!allowList.isEmpty()) { if (!allowList.isEmpty()) {
CardLists.sortByCmcDesc(allowList); CardLists.sortByCmcDesc(allowList);
@@ -455,7 +448,7 @@ public class ComputerUtil {
final int landsInHand = Math.min(2, CardLists.getType(ai.getCardsIn(ZoneType.Hand), "Land").size()); final int landsInHand = Math.min(2, CardLists.getType(ai.getCardsIn(ZoneType.Hand), "Land").size());
final CardCollection nonLandsInHand = CardLists.getNotType(ai.getCardsIn(ZoneType.Hand), "Land"); final CardCollection nonLandsInHand = CardLists.getNotType(ai.getCardsIn(ZoneType.Hand), "Land");
nonLandsInHand.addAll(ai.getCardsIn(ZoneType.Library)); nonLandsInHand.addAll(ai.getCardsIn(ZoneType.Library));
final int highestCMC = Math.max(6, Aggregates.max(nonLandsInHand, CardPredicates.Accessors.fnGetCmc)); final int highestCMC = Math.max(6, Aggregates.max(nonLandsInHand, Card::getCMC));
if (landsInPlay.size() + landsInHand >= highestCMC) { if (landsInPlay.size() + landsInHand >= highestCMC) {
// Don't need more land. // Don't need more land.
return ComputerUtilCard.getWorstLand(landsInPlay); return ComputerUtilCard.getWorstLand(landsInPlay);
@@ -539,7 +532,7 @@ public class ComputerUtil {
if (!landsInHand.isEmpty()) { if (!landsInHand.isEmpty()) {
final int numLandsInPlay = CardLists.count(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS_PRODUCING_MANA); final int numLandsInPlay = CardLists.count(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS_PRODUCING_MANA);
final CardCollection nonLandsInHand = CardLists.getNotType(ai.getCardsIn(ZoneType.Hand), "Land"); final CardCollection nonLandsInHand = CardLists.getNotType(ai.getCardsIn(ZoneType.Hand), "Land");
final int highestCMC = Math.max(6, Aggregates.max(nonLandsInHand, CardPredicates.Accessors.fnGetCmc)); final int highestCMC = Math.max(6, Aggregates.max(nonLandsInHand, Card::getCMC));
if (numLandsInPlay >= highestCMC if (numLandsInPlay >= highestCMC
|| (numLandsInPlay + landsInHand.size() > 6 && landsInHand.size() > 1)) { || (numLandsInPlay + landsInHand.size() > 6 && landsInHand.size() > 1)) {
// Don't need more land. // Don't need more land.
@@ -735,13 +728,10 @@ public class ComputerUtil {
// FIXME: This is suboptimal, maybe implement a single comparator that'll take care of all of this? // FIXME: This is suboptimal, maybe implement a single comparator that'll take care of all of this?
CardLists.sortByCmcDesc(typeList); CardLists.sortByCmcDesc(typeList);
Collections.reverse(typeList); Collections.reverse(typeList);
typeList.sort(new Comparator<Card>() { typeList.sort((a, b) -> {
@Override if (!a.isInPlay() && b.isInPlay()) return -1;
public int compare(final Card a, final Card b) { else if (!b.isInPlay() && a.isInPlay()) return 1;
if (!a.isInPlay() && b.isInPlay()) return -1; else return 0;
else if (!b.isInPlay() && a.isInPlay()) return 1;
else return 0;
}
}); // something that's not on the battlefield should come first }); // something that's not on the battlefield should come first
} }
final CardCollection exileList = new CardCollection(); final CardCollection exileList = new CardCollection();
@@ -958,40 +948,37 @@ public class ComputerUtil {
} }
} }
} }
remaining = CardLists.filter(remaining, new Predicate<Card>() { remaining = CardLists.filter(remaining, c -> {
@Override int sacThreshold = 190;
public boolean apply(final Card c) {
int sacThreshold = 190;
String logic = source.getParamOrDefault("AILogic", ""); String logic = source.getParamOrDefault("AILogic", "");
if (logic.startsWith("SacForDamage")) { if (logic.startsWith("SacForDamage")) {
final int damageAmt = logic.contains("cmc") ? c.getManaCost().getCMC() : c.getNetPower(); final int damageAmt = logic.contains("cmc") ? c.getManaCost().getCMC() : c.getNetPower();
if (damageAmt <= 0) { if (damageAmt <= 0) {
return false; return false;
} else if (damageAmt >= ai.getOpponentsSmallestLifeTotal()) { } else if (damageAmt >= ai.getOpponentsSmallestLifeTotal()) {
return true;
} else if (logic.endsWith(".GiantX2") && c.getType().hasCreatureType("Giant")
&& damageAmt * 2 >= ai.getOpponentsSmallestLifeTotal()) {
return true; // TODO: generalize this for any type and actually make the AI prefer giants?
}
}
if ("DesecrationDemon".equals(logic)) {
sacThreshold = SpecialCardAi.DesecrationDemon.getSacThreshold();
} else if (considerSacThreshold != -1) {
sacThreshold = considerSacThreshold;
}
if (c.hasSVar("SacMe") || ComputerUtilCard.evaluateCreature(c) < sacThreshold) {
return true; return true;
} else if (logic.endsWith(".GiantX2") && c.getType().hasCreatureType("Giant")
&& damageAmt * 2 >= ai.getOpponentsSmallestLifeTotal()) {
return true; // TODO: generalize this for any type and actually make the AI prefer giants?
} }
if (ComputerUtilCard.hasActiveUndyingOrPersist(c)) {
return true;
}
return false;
} }
if ("DesecrationDemon".equals(logic)) {
sacThreshold = SpecialCardAi.DesecrationDemon.getSacThreshold();
} else if (considerSacThreshold != -1) {
sacThreshold = considerSacThreshold;
}
if (c.hasSVar("SacMe") || ComputerUtilCard.evaluateCreature(c) < sacThreshold) {
return true;
}
if (ComputerUtilCard.hasActiveUndyingOrPersist(c)) {
return true;
}
return false;
}); });
} }
@@ -1362,7 +1349,7 @@ public class ComputerUtil {
final CardCollection landsInPlay = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS_PRODUCING_MANA); final CardCollection landsInPlay = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS_PRODUCING_MANA);
final CardCollection landsInHand = CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.Presets.LANDS); final CardCollection landsInHand = CardLists.filter(ai.getCardsIn(ZoneType.Hand), CardPredicates.Presets.LANDS);
final CardCollection nonLandsInHand = CardLists.getNotType(ai.getCardsIn(ZoneType.Hand), "Land"); final CardCollection nonLandsInHand = CardLists.getNotType(ai.getCardsIn(ZoneType.Hand), "Land");
final int highestCMC = Math.max(6, Aggregates.max(nonLandsInHand, CardPredicates.Accessors.fnGetCmc)); final int highestCMC = Math.max(6, Aggregates.max(nonLandsInHand, Card::getCMC));
final int discardCMC = discard.getCMC(); final int discardCMC = discard.getCMC();
if (discard.isLand()) { if (discard.isLand()) {
if (landsInPlay.size() >= highestCMC if (landsInPlay.size() >= highestCMC
@@ -2183,12 +2170,7 @@ public class ComputerUtil {
AiController aic = ((PlayerControllerAi)ai.getController()).getAi(); AiController aic = ((PlayerControllerAi)ai.getController()).getAi();
if (aic.getBooleanProperty(AiProps.AVOID_TARGETING_CREATS_THAT_WILL_DIE)) { if (aic.getBooleanProperty(AiProps.AVOID_TARGETING_CREATS_THAT_WILL_DIE)) {
// Try to avoid targeting creatures that are dead on board // Try to avoid targeting creatures that are dead on board
List<Card> willBeKilled = CardLists.filter(list, new Predicate<Card>() { List<Card> willBeKilled = CardLists.filter(list, card -> card.isCreature() && predictCreatureWillDieThisTurn(ai, card, excludeSa));
@Override
public boolean apply(Card card) {
return card.isCreature() && predictCreatureWillDieThisTurn(ai, card, excludeSa);
}
});
list.removeAll(willBeKilled); list.removeAll(willBeKilled);
} }
return list; return list;
@@ -2245,13 +2227,8 @@ public class ComputerUtil {
return finalHandSize; return finalHandSize;
} }
final CardCollectionView lands = CardLists.filter(handList, new Predicate<Card>() { final CardCollectionView lands = CardLists.filter(handList, c -> c.getManaCost().getCMC() <= 0 && !c.hasSVar("NeedsToPlay")
@Override && (c.isLand() || c.isArtifact()));
public boolean apply(final Card c) {
return c.getManaCost().getCMC() <= 0 && !c.hasSVar("NeedsToPlay")
&& (c.isLand() || c.isArtifact());
}
});
final int handSize = handList.size(); final int handSize = handList.size();
final int landSize = lands.size(); final int landSize = lands.size();
@@ -2265,12 +2242,7 @@ public class ComputerUtil {
score += 10; score += 10;
} }
final CardCollectionView castables = CardLists.filter(handList, new Predicate<Card>() { final CardCollectionView castables = CardLists.filter(handList, c -> c.getManaCost().getCMC() <= 0 || c.getManaCost().getCMC() <= landSize);
@Override
public boolean apply(final Card c) {
return c.getManaCost().getCMC() <= 0 || c.getManaCost().getCMC() <= landSize;
}
});
score += castables.size() * 2; score += castables.size() * 2;
@@ -2431,7 +2403,7 @@ public class ComputerUtil {
} else if (c.isCreature()) { } else if (c.isCreature()) {
CardCollection creaturesOTB = CardLists.filter(cardsOTB, CardPredicates.Presets.CREATURES); CardCollection creaturesOTB = CardLists.filter(cardsOTB, CardPredicates.Presets.CREATURES);
int avgCreatureValue = numCards != 0 ? ComputerUtilCard.evaluateCreatureList(allCreatures) / numCards : 0; int avgCreatureValue = numCards != 0 ? ComputerUtilCard.evaluateCreatureList(allCreatures) / numCards : 0;
int maxControlledCMC = Aggregates.max(creaturesOTB, CardPredicates.Accessors.fnGetCmc); int maxControlledCMC = Aggregates.max(creaturesOTB, Card::getCMC);
if (ComputerUtilCard.evaluateCreature(c) < avgCreatureValue) { if (ComputerUtilCard.evaluateCreature(c) < avgCreatureValue) {
if (creaturesOTB.size() > minCreatsToScryCreatsAway) { if (creaturesOTB.size() > minCreatsToScryCreatsAway) {
@@ -2460,12 +2432,7 @@ public class ComputerUtil {
} }
public static CardCollection getCardsToDiscardFromOpponent(Player chooser, Player discarder, SpellAbility sa, CardCollection validCards, int min, int max) { public static CardCollection getCardsToDiscardFromOpponent(Player chooser, Player discarder, SpellAbility sa, CardCollection validCards, int min, int max) {
CardCollection goodChoices = CardLists.filter(validCards, new Predicate<Card>() { CardCollection goodChoices = CardLists.filter(validCards, c -> !c.hasSVar("DiscardMeByOpp") && !c.hasSVar("DiscardMe"));
@Override
public boolean apply(final Card c) {
return !c.hasSVar("DiscardMeByOpp") && !c.hasSVar("DiscardMe");
}
});
if (goodChoices.isEmpty()) { if (goodChoices.isEmpty()) {
goodChoices = validCards; goodChoices = validCards;
} }
@@ -2859,25 +2826,17 @@ public class ComputerUtil {
} }
public static CardCollection getSafeTargets(final Player ai, SpellAbility sa, CardCollectionView validCards) { public static CardCollection getSafeTargets(final Player ai, SpellAbility sa, CardCollectionView validCards) {
CardCollection safeCards = CardLists.filter(validCards, new Predicate<Card>() { CardCollection safeCards = CardLists.filter(validCards, c -> {
@Override if (c.getController() == ai) {
public boolean apply(final Card c) { return !c.getSVar("Targeting").equals("Dies") && !c.getSVar("Targeting").equals("Counter");
if (c.getController() == ai) {
return !c.getSVar("Targeting").equals("Dies") && !c.getSVar("Targeting").equals("Counter");
}
return true;
} }
return true;
}); });
return safeCards; return safeCards;
} }
public static Card getKilledByTargeting(final SpellAbility sa, CardCollectionView validCards) { public static Card getKilledByTargeting(final SpellAbility sa, CardCollectionView validCards) {
CardCollection killables = CardLists.filter(validCards, new Predicate<Card>() { CardCollection killables = CardLists.filter(validCards, c -> c.getController() != sa.getActivatingPlayer() && c.getSVar("Targeting").equals("Dies"));
@Override
public boolean apply(final Card c) {
return c.getController() != sa.getActivatingPlayer() && c.getSVar("Targeting").equals("Dies");
}
});
return ComputerUtilCard.getBestCreatureAI(killables); return ComputerUtilCard.getBestCreatureAI(killables);
} }
@@ -3233,12 +3192,7 @@ public class ComputerUtil {
CardCollectionView inHand = ai.getCardsIn(ZoneType.Hand); CardCollectionView inHand = ai.getCardsIn(ZoneType.Hand);
CardCollectionView inDeck = ai.getCardsIn(ZoneType.Library); CardCollectionView inDeck = ai.getCardsIn(ZoneType.Library);
Predicate<Card> markedAsReanimator = new Predicate<Card>() { Predicate<Card> markedAsReanimator = card -> "true".equalsIgnoreCase(card.getSVar("IsReanimatorCard"));
@Override
public boolean apply(Card card) {
return "true".equalsIgnoreCase(card.getSVar("IsReanimatorCard"));
}
};
int numInHand = CardLists.count(inHand, markedAsReanimator); int numInHand = CardLists.count(inHand, markedAsReanimator);
int numInDeck = CardLists.count(inDeck, markedAsReanimator); int numInDeck = CardLists.count(inDeck, markedAsReanimator);
@@ -3271,12 +3225,7 @@ public class ComputerUtil {
value = ComputerUtilCard.evaluateCreature(source); value = ComputerUtilCard.evaluateCreature(source);
} }
final int totalValue = value; final int totalValue = value;
list = CardLists.filter(srcList, new Predicate<Card>() { list = CardLists.filter(srcList, c -> ComputerUtilCard.evaluateCreature(c) > totalValue + 30);
@Override
public boolean apply(final Card c) {
return ComputerUtilCard.evaluateCreature(c) > totalValue + 30;
}
});
} else { } else {
list = CardLists.getValidCards(srcList, sa.getParam("AITgts"), sa.getActivatingPlayer(), source, sa); list = CardLists.getValidCards(srcList, sa.getParam("AITgts"), sa.getActivatingPlayer(), source, sa);
} }

View File

@@ -4,7 +4,6 @@ import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.card.CardStateName; import forge.card.CardStateName;
@@ -38,18 +37,15 @@ public class ComputerUtilAbility {
CardCollection landList = CardLists.filter(hand, Presets.LANDS); CardCollection landList = CardLists.filter(hand, Presets.LANDS);
//filter out cards that can't be played //filter out cards that can't be played
landList = CardLists.filter(landList, new Predicate<Card>() { landList = CardLists.filter(landList, c -> {
@Override if (!c.getSVar("NeedsToPlay").isEmpty()) {
public boolean apply(final Card c) { final String needsToPlay = c.getSVar("NeedsToPlay");
if (!c.getSVar("NeedsToPlay").isEmpty()) { CardCollection list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), needsToPlay, c.getController(), c, null);
final String needsToPlay = c.getSVar("NeedsToPlay"); if (list.isEmpty()) {
CardCollection list = CardLists.getValidCards(game.getCardsIn(ZoneType.Battlefield), needsToPlay, c.getController(), c, null); return false;
if (list.isEmpty()) {
return false;
}
} }
return player.canPlayLand(c);
} }
return player.canPlayLand(c);
}); });
final CardCollection landsNotInHand = new CardCollection(player.getCardsIn(ZoneType.Graveyard)); final CardCollection landsNotInHand = new CardCollection(player.getCardsIn(ZoneType.Graveyard));

View File

@@ -58,12 +58,7 @@ public class ComputerUtilCard {
public static Card getMostExpensivePermanentAI(final CardCollectionView list, final SpellAbility spell, final boolean targeted) { public static Card getMostExpensivePermanentAI(final CardCollectionView list, final SpellAbility spell, final boolean targeted) {
CardCollectionView all = list; CardCollectionView all = list;
if (targeted) { if (targeted) {
all = CardLists.filter(all, new Predicate<Card>() { all = CardLists.filter(all, c -> c.canBeTargetedBy(spell));
@Override
public boolean apply(final Card c) {
return c.canBeTargetedBy(spell);
}
});
} }
return getMostExpensivePermanentAI(all); return getMostExpensivePermanentAI(all);
} }
@@ -96,7 +91,7 @@ public class ComputerUtilCard {
return null; return null;
} }
// get biggest Artifact // get biggest Artifact
return Aggregates.itemWithMax(all, CardPredicates.Accessors.fnGetCmc); return Aggregates.itemWithMax(all, Card::getCMC);
} }
/** /**
@@ -111,7 +106,7 @@ public class ComputerUtilCard {
return null; return null;
} }
// no AI logic, just return most expensive // no AI logic, just return most expensive
return Aggregates.itemWithMax(all, CardPredicates.Accessors.fnGetCmc); return Aggregates.itemWithMax(all, Card::getCMC);
} }
/** /**
@@ -126,7 +121,7 @@ public class ComputerUtilCard {
return null; return null;
} }
// no AI logic, just return least expensive // no AI logic, just return least expensive
return Aggregates.itemWithMin(all, CardPredicates.Accessors.fnGetCmc); return Aggregates.itemWithMin(all, Card::getCMC);
} }
public static Card getBestPlaneswalkerToDamage(final List<Card> pws) { public static Card getBestPlaneswalkerToDamage(final List<Card> pws) {
@@ -194,16 +189,11 @@ public class ComputerUtilCard {
public static Card getBestEnchantmentAI(final List<Card> list, final SpellAbility spell, final boolean targeted) { public static Card getBestEnchantmentAI(final List<Card> list, final SpellAbility spell, final boolean targeted) {
List<Card> all = CardLists.filter(list, CardPredicates.Presets.ENCHANTMENTS); List<Card> all = CardLists.filter(list, CardPredicates.Presets.ENCHANTMENTS);
if (targeted) { if (targeted) {
all = CardLists.filter(all, new Predicate<Card>() { all = CardLists.filter(all, c -> c.canBeTargetedBy(spell));
@Override
public boolean apply(final Card c) {
return c.canBeTargetedBy(spell);
}
});
} }
// get biggest Enchantment // get biggest Enchantment
return Aggregates.itemWithMax(all, CardPredicates.Accessors.fnGetCmc); return Aggregates.itemWithMax(all, Card::getCMC);
} }
/** /**
@@ -342,12 +332,7 @@ public class ComputerUtilCard {
*/ */
public static Card getCheapestPermanentAI(Iterable<Card> all, final SpellAbility spell, final boolean targeted) { public static Card getCheapestPermanentAI(Iterable<Card> all, final SpellAbility spell, final boolean targeted) {
if (targeted) { if (targeted) {
all = CardLists.filter(all, new Predicate<Card>() { all = CardLists.filter(all, c -> c.canBeTargetedBy(spell));
@Override
public boolean apply(final Card c) {
return c.canBeTargetedBy(spell);
}
});
} }
if (Iterables.isEmpty(all)) { if (Iterables.isEmpty(all)) {
return null; return null;
@@ -524,12 +509,10 @@ public class ComputerUtilCard {
} }
if (hasEnchantmants || hasArtifacts) { if (hasEnchantmants || hasArtifacts) {
final List<Card> ae = CardLists.filter(list, Predicates.and(Predicates.or(CardPredicates.Presets.ARTIFACTS, CardPredicates.Presets.ENCHANTMENTS), new Predicate<Card>() { final List<Card> ae = CardLists.filter(list, Predicates.and(
@Override Predicates.or(CardPredicates.Presets.ARTIFACTS, CardPredicates.Presets.ENCHANTMENTS),
public boolean apply(Card card) { card -> !card.hasSVar("DoNotDiscardIfAble")
return !card.hasSVar("DoNotDiscardIfAble"); ));
}
}));
return getCheapestPermanentAI(ae, null, false); return getCheapestPermanentAI(ae, null, false);
} }
@@ -568,18 +551,10 @@ public class ComputerUtilCard {
return null; return null;
} }
public static final Comparator<Card> EvaluateCreatureComparator = new Comparator<Card>() { public static final Comparator<Card> EvaluateCreatureComparator = (a, b) -> evaluateCreature(b) - evaluateCreature(a);
@Override public static final Comparator<SpellAbility> EvaluateCreatureSpellComparator = (a, b) -> {
public int compare(final Card a, final Card b) { // TODO ideally we could reuse the value from the previous pass with false
return evaluateCreature(b) - evaluateCreature(a); return ComputerUtilAbility.saEvaluator.compareEvaluator(a, b, true);
}
};
public static final Comparator<SpellAbility> EvaluateCreatureSpellComparator = new Comparator<SpellAbility>() {
@Override
public int compare(final SpellAbility a, final SpellAbility b) {
// TODO ideally we could reuse the value from the previous pass with false
return ComputerUtilAbility.saEvaluator.compareEvaluator(a, b, true);
}
}; };
private static final CreatureEvaluator creatureEvaluator = new CreatureEvaluator(); private static final CreatureEvaluator creatureEvaluator = new CreatureEvaluator();
@@ -774,7 +749,7 @@ public class ComputerUtilCard {
// Add all cost of all auras with the same controller // Add all cost of all auras with the same controller
if (card.isEnchanted()) { if (card.isEnchanted()) {
final List<Card> auras = CardLists.filterControlledBy(card.getEnchantedBy(), card.getController()); final List<Card> auras = CardLists.filterControlledBy(card.getEnchantedBy(), card.getController());
curCMC += Aggregates.sum(auras, CardPredicates.Accessors.fnGetCmc) + auras.size(); curCMC += Aggregates.sum(auras, Card::getCMC) + auras.size();
} }
if (curCMC >= bigCMC) { if (curCMC >= bigCMC) {
@@ -990,12 +965,7 @@ public class ComputerUtilCard {
if (color.hasGreen()) map.get(4).setValue(map.get(4).getValue() + 1); if (color.hasGreen()) map.get(4).setValue(map.get(4).getValue() + 1);
} }
Collections.sort(map, new Comparator<Pair<Byte, Integer>>() { Collections.sort(map, Comparator.<Pair<Byte, Integer>>comparingInt(Pair::getValue).reversed());
@Override
public int compare(Pair<Byte, Integer> o1, Pair<Byte, Integer> o2) {
return o2.getValue() - o1.getValue();
}
});
// will this part be once dropped? // will this part be once dropped?
List<String> result = new ArrayList<>(cntColors); List<String> result = new ArrayList<>(cntColors);
@@ -1006,17 +976,14 @@ public class ComputerUtilCard {
return result; return result;
} }
public static final Predicate<Deck> AI_KNOWS_HOW_TO_PLAY_ALL_CARDS = new Predicate<Deck>() { public static final Predicate<Deck> AI_KNOWS_HOW_TO_PLAY_ALL_CARDS = d -> {
@Override for (Entry<DeckSection, CardPool> cp : d) {
public boolean apply(Deck d) { for (Entry<PaperCard, Integer> e : cp.getValue()) {
for (Entry<DeckSection, CardPool> cp : d) { if (e.getKey().getRules().getAiHints().getRemAIDecks())
for (Entry<PaperCard, Integer> e : cp.getValue()) { return false;
if (e.getKey().getRules().getAiHints().getRemAIDecks())
return false;
}
} }
return true;
} }
return true;
}; };
public static List<String> chooseColor(SpellAbility sa, int min, int max, List<String> colorChoices) { public static List<String> chooseColor(SpellAbility sa, int min, int max, List<String> colorChoices) {

View File

@@ -17,7 +17,6 @@
*/ */
package forge.ai; package forge.ai;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@@ -79,11 +78,7 @@ public class ComputerUtilCombat {
*/ */
public static boolean canAttackNextTurn(final Card attacker) { public static boolean canAttackNextTurn(final Card attacker) {
final Iterable<GameEntity> defenders = CombatUtil.getAllPossibleDefenders(attacker.getController()); final Iterable<GameEntity> defenders = CombatUtil.getAllPossibleDefenders(attacker.getController());
return Iterables.any(defenders, new Predicate<GameEntity>() { return Iterables.any(defenders, input -> canAttackNextTurn(attacker, input));
@Override public boolean apply(final GameEntity input) {
return canAttackNextTurn(attacker, input);
}
});
} }
/** /**
@@ -138,12 +133,7 @@ public class ComputerUtilCombat {
*/ */
public static int getTotalFirstStrikeBlockPower(final Card attacker, final Player player) { public static int getTotalFirstStrikeBlockPower(final Card attacker, final Player player) {
List<Card> list = player.getCreaturesInPlay(); List<Card> list = player.getCreaturesInPlay();
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> (c.hasFirstStrike() || c.hasDoubleStrike()) && CombatUtil.canBlock(attacker, c));
@Override
public boolean apply(final Card c) {
return (c.hasFirstStrike() || c.hasDoubleStrike()) && CombatUtil.canBlock(attacker, c);
}
});
return totalFirstStrikeDamageOfBlockers(attacker, list); return totalFirstStrikeDamageOfBlockers(attacker, list);
} }

View File

@@ -1,6 +1,5 @@
package forge.ai; package forge.ai;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -472,12 +471,7 @@ public class ComputerUtilCost {
String totalP = type.split("withTotalPowerGE")[1]; String totalP = type.split("withTotalPowerGE")[1];
type = TextUtil.fastReplace(type, TextUtil.concatNoSpace("+withTotalPowerGE", totalP), ""); type = TextUtil.fastReplace(type, TextUtil.concatNoSpace("+withTotalPowerGE", totalP), "");
CardCollection exclude = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), source.getController(), source, sa); CardCollection exclude = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), source.getController(), source, sa);
exclude = CardLists.filter(exclude, new Predicate<Card>() { exclude = CardLists.filter(exclude, c -> ComputerUtilCard.evaluateCreature(c) >= vehicleValue); // exclude creatures >= vehicle
@Override
public boolean apply(final Card c) {
return ComputerUtilCard.evaluateCreature(c) >= vehicleValue;
}
}); // exclude creatures >= vehicle
exclude.addAll(alreadyTapped); exclude.addAll(alreadyTapped);
CardCollection tappedCrew = ComputerUtil.chooseTapTypeAccumulatePower(ai, type, sa, true, Integer.parseInt(totalP), exclude); CardCollection tappedCrew = ComputerUtil.chooseTapTypeAccumulatePower(ai, type, sa, true, Integer.parseInt(totalP), exclude);
if (tappedCrew != null) { if (tappedCrew != null) {
@@ -616,18 +610,15 @@ public class ComputerUtilCost {
CardCollectionView nonManaSources = CardCollectionView nonManaSources =
CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), part.getType().split(";"), CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), part.getType().split(";"),
sa.getActivatingPlayer(), sa.getHostCard(), sa); sa.getActivatingPlayer(), sa.getHostCard(), sa);
nonManaSources = CardLists.filter(nonManaSources, new Predicate<Card>() { nonManaSources = CardLists.filter(nonManaSources, card -> {
@Override boolean hasManaSa = false;
public boolean apply(Card card) { for (final SpellAbility sa1 : card.getSpellAbilities()) {
boolean hasManaSa = false; if (sa1.isManaAbility() && sa1.getPayCosts().hasTapCost()) {
for (final SpellAbility sa : card.getSpellAbilities()) { hasManaSa = true;
if (sa.isManaAbility() && sa.getPayCosts().hasTapCost()) { break;
hasManaSa = true;
break;
}
} }
return !hasManaSa;
} }
return !hasManaSa;
}); });
if (nonManaSources.size() < part.convertAmount()) { if (nonManaSources.size() < part.convertAmount()) {
return false; return false;

View File

@@ -1,6 +1,5 @@
package forge.ai; package forge.ai;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.*; import com.google.common.collect.*;
import forge.ai.AiCardMemory.MemorySet; import forge.ai.AiCardMemory.MemorySet;
@@ -126,12 +125,7 @@ public class ComputerUtilMana {
} }
} }
} }
orderedCards.sort(new Comparator<Card>() { orderedCards.sort(Comparator.comparingInt(manaCardMap::get));
@Override
public int compare(final Card card1, final Card card2) {
return Integer.compare(manaCardMap.get(card1), manaCardMap.get(card2));
}
});
if (DEBUG_MANA_PAYMENT) { if (DEBUG_MANA_PAYMENT) {
System.out.print("Ordered Cards: " + orderedCards.size()); System.out.print("Ordered Cards: " + orderedCards.size());
@@ -149,29 +143,26 @@ public class ComputerUtilMana {
System.out.println("Unsorted Abilities: " + newAbilities); System.out.println("Unsorted Abilities: " + newAbilities);
} }
newAbilities.sort(new Comparator<SpellAbility>() { newAbilities.sort((ability1, ability2) -> {
@Override int preOrder = orderedCards.indexOf(ability1.getHostCard()) - orderedCards.indexOf(ability2.getHostCard());
public int compare(final SpellAbility ability1, final SpellAbility ability2) {
int preOrder = orderedCards.indexOf(ability1.getHostCard()) - orderedCards.indexOf(ability2.getHostCard());
if (preOrder != 0) { if (preOrder != 0) {
return preOrder; return preOrder;
}
// Mana abilities on the same card
String shardMana = shard.toString().replaceAll("\\{", "").replaceAll("\\}", "");
boolean payWithAb1 = ability1.getManaPart().mana(ability1).contains(shardMana);
boolean payWithAb2 = ability2.getManaPart().mana(ability2).contains(shardMana);
if (payWithAb1 && !payWithAb2) {
return -1;
} else if (payWithAb2 && !payWithAb1) {
return 1;
}
return ability1.compareTo(ability2);
} }
// Mana abilities on the same card
String shardMana = shard.toString().replaceAll("\\{", "").replaceAll("\\}", "");
boolean payWithAb1 = ability1.getManaPart().mana(ability1).contains(shardMana);
boolean payWithAb2 = ability2.getManaPart().mana(ability2).contains(shardMana);
if (payWithAb1 && !payWithAb2) {
return -1;
} else if (payWithAb2 && !payWithAb1) {
return 1;
}
return ability1.compareTo(ability2);
}); });
if (DEBUG_MANA_PAYMENT) { if (DEBUG_MANA_PAYMENT) {
@@ -195,27 +186,21 @@ public class ComputerUtilMana {
final List<SpellAbility> prefSortedAbilities = new ArrayList<>(newAbilities); final List<SpellAbility> prefSortedAbilities = new ArrayList<>(newAbilities);
final List<SpellAbility> otherSortedAbilities = new ArrayList<>(newAbilities); final List<SpellAbility> otherSortedAbilities = new ArrayList<>(newAbilities);
prefSortedAbilities.sort(new Comparator<SpellAbility>() { prefSortedAbilities.sort((ability1, ability2) -> {
@Override if (ability1.getManaPart().mana(ability1).contains(preferredShard))
public int compare(final SpellAbility ability1, final SpellAbility ability2) { return -1;
if (ability1.getManaPart().mana(ability1).contains(preferredShard)) else if (ability2.getManaPart().mana(ability2).contains(preferredShard))
return -1; return 1;
else if (ability2.getManaPart().mana(ability2).contains(preferredShard))
return 1;
return 0; return 0;
}
}); });
otherSortedAbilities.sort(new Comparator<SpellAbility>() { otherSortedAbilities.sort((ability1, ability2) -> {
@Override if (ability1.getManaPart().mana(ability1).contains(preferredShard))
public int compare(final SpellAbility ability1, final SpellAbility ability2) { return 1;
if (ability1.getManaPart().mana(ability1).contains(preferredShard)) else if (ability2.getManaPart().mana(ability2).contains(preferredShard))
return 1; return -1;
else if (ability2.getManaPart().mana(ability2).contains(preferredShard))
return -1;
return 0; return 0;
}
}); });
final List<SpellAbility> finalAbilities = new ArrayList<>(); final List<SpellAbility> finalAbilities = new ArrayList<>();
@@ -251,24 +236,14 @@ public class ComputerUtilMana {
List<SpellAbility> filteredList = Lists.newArrayList(saList); List<SpellAbility> filteredList = Lists.newArrayList(saList);
switch (manaSourceType) { switch (manaSourceType) {
case "Snow": case "Snow":
filteredList.sort(new Comparator<SpellAbility>() { filteredList.sort((ab1, ab2) -> ab1.getHostCard() != null && ab1.getHostCard().isSnow()
@Override && ab2.getHostCard() != null && !ab2.getHostCard().isSnow() ? -1 : 1);
public int compare(SpellAbility ab1, SpellAbility ab2) {
return ab1.getHostCard() != null && ab1.getHostCard().isSnow()
&& ab2.getHostCard() != null && !ab2.getHostCard().isSnow() ? -1 : 1;
}
});
saList = filteredList; saList = filteredList;
break; break;
case "Treasure": case "Treasure":
// Try to spend only one Treasure if possible // Try to spend only one Treasure if possible
filteredList.sort(new Comparator<SpellAbility>() { filteredList.sort((ab1, ab2) -> ab1.getHostCard() != null && ab1.getHostCard().getType().hasSubtype("Treasure")
@Override && ab2.getHostCard() != null && !ab2.getHostCard().getType().hasSubtype("Treasure") ? -1 : 1);
public int compare(SpellAbility ab1, SpellAbility ab2) {
return ab1.getHostCard() != null && ab1.getHostCard().getType().hasSubtype("Treasure")
&& ab2.getHostCard() != null && !ab2.getHostCard().getType().hasSubtype("Treasure") ? -1 : 1;
}
});
SpellAbility first = filteredList.get(0); SpellAbility first = filteredList.get(0);
if (first.getHostCard() != null && first.getHostCard().getType().hasSubtype("Treasure")) { if (first.getHostCard() != null && first.getHostCard().getType().hasSubtype("Treasure")) {
saList.remove(first); saList.remove(first);
@@ -280,22 +255,12 @@ public class ComputerUtilMana {
break; break;
case "TreasureMax": case "TreasureMax":
// Ok to spend as many Treasures as possible // Ok to spend as many Treasures as possible
filteredList.sort(new Comparator<SpellAbility>() { filteredList.sort((ab1, ab2) -> ab1.getHostCard() != null && ab1.getHostCard().getType().hasSubtype("Treasure")
@Override && ab2.getHostCard() != null && !ab2.getHostCard().getType().hasSubtype("Treasure") ? -1 : 1);
public int compare(SpellAbility ab1, SpellAbility ab2) {
return ab1.getHostCard() != null && ab1.getHostCard().getType().hasSubtype("Treasure")
&& ab2.getHostCard() != null && !ab2.getHostCard().getType().hasSubtype("Treasure") ? -1 : 1;
}
});
saList = filteredList; saList = filteredList;
break; break;
case "NotSameCard": case "NotSameCard":
saList = Lists.newArrayList(Iterables.filter(filteredList, new Predicate<SpellAbility>() { saList = Lists.newArrayList(Iterables.filter(filteredList, saPay -> !saPay.getHostCard().getName().equals(sa.getHostCard().getName())));
@Override
public boolean apply(final SpellAbility saPay) {
return !saPay.getHostCard().getName().equals(sa.getHostCard().getName());
}
}));
break; break;
default: default:
break; break;
@@ -1096,12 +1061,7 @@ public class ComputerUtilMana {
private static ManaCostShard getNextShardToPay(ManaCostBeingPaid cost, Multimap<ManaCostShard, SpellAbility> sourcesForShards) { private static ManaCostShard getNextShardToPay(ManaCostBeingPaid cost, Multimap<ManaCostShard, SpellAbility> sourcesForShards) {
List<ManaCostShard> shardsToPay = Lists.newArrayList(cost.getDistinctShards()); List<ManaCostShard> shardsToPay = Lists.newArrayList(cost.getDistinctShards());
// optimize order so that the shards with less available sources are considered first // optimize order so that the shards with less available sources are considered first
shardsToPay.sort(new Comparator<ManaCostShard>() { shardsToPay.sort(Comparator.comparingInt(shard -> sourcesForShards.get(shard).size()));
@Override
public int compare(final ManaCostShard shard1, final ManaCostShard shard2) {
return sourcesForShards.get(shard1).size() - sourcesForShards.get(shard2).size();
}
});
// mind the priorities // mind the priorities
// * Pay mono-colored first // * Pay mono-colored first
// * Pay 2/C with matching colors // * Pay 2/C with matching colors
@@ -1387,12 +1347,7 @@ public class ComputerUtilMana {
public static int getAvailableManaEstimate(final Player p, final boolean checkPlayable) { public static int getAvailableManaEstimate(final Player p, final boolean checkPlayable) {
int availableMana = 0; int availableMana = 0;
final List<Card> srcs = CardLists.filter(p.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() { final List<Card> srcs = CardLists.filter(p.getCardsIn(ZoneType.Battlefield), c -> !c.getManaAbilities().isEmpty());
@Override
public boolean apply(final Card c) {
return !c.getManaAbilities().isEmpty();
}
});
int maxProduced = 0; int maxProduced = 0;
int producedWithCost = 0; int producedWithCost = 0;
@@ -1437,17 +1392,14 @@ public class ComputerUtilMana {
//This method is currently used by AI to estimate available mana //This method is currently used by AI to estimate available mana
public static CardCollection getAvailableManaSources(final Player ai, final boolean checkPlayable) { public static CardCollection getAvailableManaSources(final Player ai, final boolean checkPlayable) {
final CardCollectionView list = CardCollection.combine(ai.getCardsIn(ZoneType.Battlefield), ai.getCardsIn(ZoneType.Hand)); final CardCollectionView list = CardCollection.combine(ai.getCardsIn(ZoneType.Battlefield), ai.getCardsIn(ZoneType.Hand));
final List<Card> manaSources = CardLists.filter(list, new Predicate<Card>() { final List<Card> manaSources = CardLists.filter(list, c -> {
@Override for (final SpellAbility am : getAIPlayableMana(c)) {
public boolean apply(final Card c) { am.setActivatingPlayer(ai, true);
for (final SpellAbility am : getAIPlayableMana(c)) { if (!checkPlayable || (am.canPlay() && am.checkRestrictions(ai))) {
am.setActivatingPlayer(ai, true); return true;
if (!checkPlayable || (am.canPlay() && am.checkRestrictions(ai))) {
return true;
}
} }
return false;
} }
return false;
}); // CardListFilter }); // CardListFilter
final CardCollection sortedManaSources = new CardCollection(); final CardCollection sortedManaSources = new CardCollection();

View File

@@ -570,12 +570,9 @@ public class PlayerControllerAi extends PlayerController {
if (destinationZone == ZoneType.Graveyard) { if (destinationZone == ZoneType.Graveyard) {
// In presence of Volrath's Shapeshifter in deck, try to place the best creature on top of the graveyard // In presence of Volrath's Shapeshifter in deck, try to place the best creature on top of the graveyard
if (Iterables.any(getGame().getCardsInGame(), new Predicate<Card>() { if (Iterables.any(getGame().getCardsInGame(), card -> {
@Override // need a custom predicate here since Volrath's Shapeshifter may have a different name OTB
public boolean apply(Card card) { return card.getOriginalState(CardStateName.Original).getName().equals("Volrath's Shapeshifter");
// need a custom predicate here since Volrath's Shapeshifter may have a different name OTB
return card.getOriginalState(CardStateName.Original).getName().equals("Volrath's Shapeshifter");
}
})) { })) {
int bestValue = 0; int bestValue = 0;
Card bestCreature = null; Card bestCreature = null;
@@ -702,7 +699,7 @@ public class PlayerControllerAi extends PlayerController {
public CardCollectionView chooseCardsToDiscardUnlessType(int num, CardCollectionView hand, String uType, SpellAbility sa) { public CardCollectionView chooseCardsToDiscardUnlessType(int num, CardCollectionView hand, String uType, SpellAbility sa) {
Iterable<Card> cardsOfType = Iterables.filter(hand, CardPredicates.restriction(uType.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa)); Iterable<Card> cardsOfType = Iterables.filter(hand, CardPredicates.restriction(uType.split(","), sa.getActivatingPlayer(), sa.getHostCard(), sa));
if (!Iterables.isEmpty(cardsOfType)) { if (!Iterables.isEmpty(cardsOfType)) {
Card toDiscard = Aggregates.itemWithMin(cardsOfType, CardPredicates.Accessors.fnGetCmc); Card toDiscard = Aggregates.itemWithMin(cardsOfType, Card::getCMC);
return new CardCollection(toDiscard); return new CardCollection(toDiscard);
} }
return getAi().getCardsToDiscard(num, null, sa); return getAi().getCardsToDiscard(num, null, sa);
@@ -1389,21 +1386,12 @@ public class PlayerControllerAi extends PlayerController {
final Player ai = sa.getActivatingPlayer(); final Player ai = sa.getActivatingPlayer();
final PhaseHandler ph = ai.getGame().getPhaseHandler(); final PhaseHandler ph = ai.getGame().getPhaseHandler();
//Filter out mana sources that will interfere with payManaCost() //Filter out mana sources that will interfere with payManaCost()
CardCollection untapped = CardLists.filter(untappedCards, new Predicate<Card>() { CardCollection untapped = CardLists.filter(untappedCards, c -> c.getManaAbilities().isEmpty());
@Override
public boolean apply(final Card c) {
return c.getManaAbilities().isEmpty();
}
});
// Filter out creatures if AI hasn't attacked yet // Filter out creatures if AI hasn't attacked yet
if (ph.isPlayerTurn(ai) && ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) { if (ph.isPlayerTurn(ai) && ph.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
if (improvise) { if (improvise) {
untapped = CardLists.filter(untapped, new Predicate<Card>() { untapped = CardLists.filter(untapped, c -> !c.isCreature());
@Override
public boolean apply(final Card c) {return !c.isCreature();
}
});
} else { } else {
return new HashMap<>(); return new HashMap<>();
} }

View File

@@ -1,6 +1,5 @@
package forge.ai; package forge.ai;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
@@ -91,13 +90,10 @@ public class SpecialAiLogic {
bestOwnCardToUpgrade = Iterables.getFirst(CardLists.getKeyword(listOwn, Keyword.INDESTRUCTIBLE), null); bestOwnCardToUpgrade = Iterables.getFirst(CardLists.getKeyword(listOwn, Keyword.INDESTRUCTIBLE), null);
} }
if (bestOwnCardToUpgrade == null) { if (bestOwnCardToUpgrade == null) {
bestOwnCardToUpgrade = ComputerUtilCard.getWorstCreatureAI(CardLists.filter(listOwn, new Predicate<Card>() { bestOwnCardToUpgrade = ComputerUtilCard.getWorstCreatureAI(CardLists.filter(listOwn, card -> card.isCreature()
@Override && (ComputerUtilCard.isUselessCreature(ai, card)
public boolean apply(Card card) { || ComputerUtilCard.evaluateCreature(token) > 2 * ComputerUtilCard.evaluateCreature(card))
return card.isCreature() && (ComputerUtilCard.isUselessCreature(ai, card) ));
|| ComputerUtilCard.evaluateCreature(token) > 2 * ComputerUtilCard.evaluateCreature(card));
}
}));
} }
if (bestOwnCardToUpgrade != null) { if (bestOwnCardToUpgrade != null) {
if (ComputerUtilCard.isUselessCreature(ai, bestOwnCardToUpgrade) || (ph.getPhase().isAfter(PhaseType.COMBAT_END) || !ph.isPlayerTurn(ai))) { if (ComputerUtilCard.isUselessCreature(ai, bestOwnCardToUpgrade) || (ph.getPhase().isAfter(PhaseType.COMBAT_END) || !ph.isPlayerTurn(ai))) {
@@ -145,14 +141,9 @@ public class SpecialAiLogic {
if (indestructible || (source.getNetToughness() <= dmg && source.getNetToughness() + toughnessBonus * numCreatsToSac > dmg)) { if (indestructible || (source.getNetToughness() <= dmg && source.getNetToughness() + toughnessBonus * numCreatsToSac > dmg)) {
final CardCollection sacFodder = CardLists.filter(ai.getCreaturesInPlay(), final CardCollection sacFodder = CardLists.filter(ai.getCreaturesInPlay(),
new Predicate<Card>() { card -> ComputerUtilCard.isUselessCreature(ai, card)
@Override || card.hasSVar("SacMe")
public boolean apply(Card card) { || ComputerUtilCard.evaluateCreature(card) < selfEval // Maybe around 150 is OK?
return ComputerUtilCard.isUselessCreature(ai, card)
|| card.hasSVar("SacMe")
|| ComputerUtilCard.evaluateCreature(card) < selfEval; // Maybe around 150 is OK?
}
}
); );
return sacFodder.size() >= numCreatsToSac; return sacFodder.size() >= numCreatsToSac;
} }
@@ -196,21 +187,16 @@ public class SpecialAiLogic {
// We have already attacked. Thus, see if we have a creature to sac that is worse to lose // We have already attacked. Thus, see if we have a creature to sac that is worse to lose
// than the card we attacked with. // than the card we attacked with.
final CardCollection sacTgts = CardLists.filter(ai.getCreaturesInPlay(), final CardCollection sacTgts = CardLists.filter(ai.getCreaturesInPlay(),
new Predicate<Card>() { card -> ComputerUtilCard.isUselessCreature(ai, card)
@Override || ComputerUtilCard.evaluateCreature(card) < selfEval
public boolean apply(Card card) {
return ComputerUtilCard.isUselessCreature(ai, card)
|| ComputerUtilCard.evaluateCreature(card) < selfEval;
}
}
); );
if (sacTgts.isEmpty()) { if (sacTgts.isEmpty()) {
return false; return false;
} }
final int minDefT = Aggregates.min(combat.getBlockers(source), CardPredicates.Accessors.fnGetNetToughness); final int minDefT = Aggregates.min(combat.getBlockers(source), Card::getNetToughness);
final int DefP = indestructible ? 0 : Aggregates.sum(combat.getBlockers(source), CardPredicates.Accessors.fnGetNetPower); final int DefP = indestructible ? 0 : Aggregates.sum(combat.getBlockers(source), Card::getNetPower);
// Make sure we don't over-sacrifice, only sac until we can survive and kill a creature // Make sure we don't over-sacrifice, only sac until we can survive and kill a creature
return source.getNetToughness() - source.getDamage() <= DefP || source.getNetCombatDamage() < minDefT; return source.getNetToughness() - source.getDamage() <= DefP || source.getNetCombatDamage() < minDefT;
@@ -218,14 +204,9 @@ public class SpecialAiLogic {
} else { } else {
// We can't deal lethal, check if there's any sac fodder than can be used for other circumstances // We can't deal lethal, check if there's any sac fodder than can be used for other circumstances
final CardCollection sacFodder = CardLists.filter(ai.getCreaturesInPlay(), final CardCollection sacFodder = CardLists.filter(ai.getCreaturesInPlay(),
new Predicate<Card>() { card -> ComputerUtilCard.isUselessCreature(ai, card)
@Override || card.hasSVar("SacMe")
public boolean apply(Card card) { || ComputerUtilCard.evaluateCreature(card) < selfEval // Maybe around 150 is OK?
return ComputerUtilCard.isUselessCreature(ai, card)
|| card.hasSVar("SacMe")
|| ComputerUtilCard.evaluateCreature(card) < selfEval; // Maybe around 150 is OK?
}
}
); );
return !sacFodder.isEmpty(); return !sacFodder.isEmpty();
@@ -302,13 +283,8 @@ public class SpecialAiLogic {
// Check if there's anything that will die anyway that can be eaten to gain a perma-bonus // Check if there's anything that will die anyway that can be eaten to gain a perma-bonus
final CardCollection forcedSacTgts = CardLists.filter(relevantCreats, final CardCollection forcedSacTgts = CardLists.filter(relevantCreats,
new Predicate<Card>() { card -> ComputerUtil.predictThreatenedObjects(ai, null, true).contains(card)
@Override || (combat.isAttacking(card) && combat.isBlocked(card) && ComputerUtilCombat.combatantWouldBeDestroyed(ai, card, combat))
public boolean apply(Card card) {
return ComputerUtil.predictThreatenedObjects(ai, null, true).contains(card)
|| (combat.isAttacking(card) && combat.isBlocked(card) && ComputerUtilCombat.combatantWouldBeDestroyed(ai, card, combat));
}
}
); );
if (!forcedSacTgts.isEmpty()) { if (!forcedSacTgts.isEmpty()) {
return true; return true;
@@ -327,14 +303,9 @@ public class SpecialAiLogic {
// than the card we attacked with. Since we're getting a permanent bonus, consider sacrificing // than the card we attacked with. Since we're getting a permanent bonus, consider sacrificing
// things that are also threatened to be destroyed anyway. // things that are also threatened to be destroyed anyway.
final CardCollection sacTgts = CardLists.filter(relevantCreats, final CardCollection sacTgts = CardLists.filter(relevantCreats,
new Predicate<Card>() { card -> ComputerUtilCard.isUselessCreature(ai, card)
@Override || ComputerUtilCard.evaluateCreature(card) < selfEval
public boolean apply(Card card) { || ComputerUtil.predictThreatenedObjects(ai, null, true).contains(card)
return ComputerUtilCard.isUselessCreature(ai, card)
|| ComputerUtilCard.evaluateCreature(card) < selfEval
|| ComputerUtil.predictThreatenedObjects(ai, null, true).contains(card);
}
}
); );
if (sacTgts.isEmpty()) { if (sacTgts.isEmpty()) {
@@ -342,8 +313,8 @@ public class SpecialAiLogic {
} }
final boolean sourceCantDie = ComputerUtilCombat.combatantCantBeDestroyed(ai, source); final boolean sourceCantDie = ComputerUtilCombat.combatantCantBeDestroyed(ai, source);
final int minDefT = Aggregates.min(combat.getBlockers(source), CardPredicates.Accessors.fnGetNetToughness); final int minDefT = Aggregates.min(combat.getBlockers(source), Card::getNetToughness);
final int DefP = sourceCantDie ? 0 : Aggregates.sum(combat.getBlockers(source), CardPredicates.Accessors.fnGetNetPower); final int DefP = sourceCantDie ? 0 : Aggregates.sum(combat.getBlockers(source), Card::getNetPower);
// Make sure we don't over-sacrifice, only sac until we can survive and kill a creature // Make sure we don't over-sacrifice, only sac until we can survive and kill a creature
return source.getNetToughness() - source.getDamage() <= DefP || source.getNetCombatDamage() < minDefT; return source.getNetToughness() - source.getDamage() <= DefP || source.getNetCombatDamage() < minDefT;
@@ -352,15 +323,10 @@ public class SpecialAiLogic {
// We can't deal lethal, check if there's any sac fodder than can be used for other circumstances // We can't deal lethal, check if there's any sac fodder than can be used for other circumstances
final boolean isBlocking = combat != null && combat.isBlocking(source); final boolean isBlocking = combat != null && combat.isBlocking(source);
final CardCollection sacFodder = CardLists.filter(relevantCreats, final CardCollection sacFodder = CardLists.filter(relevantCreats,
new Predicate<Card>() { card -> ComputerUtilCard.isUselessCreature(ai, card)
@Override || card.hasSVar("SacMe")
public boolean apply(Card card) { || (isBlocking && ComputerUtilCard.evaluateCreature(card) < selfEval)
return ComputerUtilCard.isUselessCreature(ai, card) || ComputerUtil.predictThreatenedObjects(ai, null, true).contains(card)
|| card.hasSVar("SacMe")
|| (isBlocking && ComputerUtilCard.evaluateCreature(card) < selfEval)
|| ComputerUtil.predictThreatenedObjects(ai, null, true).contains(card);
}
}
); );
return !sacFodder.isEmpty(); return !sacFodder.isEmpty();

View File

@@ -17,7 +17,6 @@
*/ */
package forge.ai; package forge.ai;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -478,8 +477,8 @@ public class SpecialCardAi {
boolean cantDie = ComputerUtilCombat.combatantCantBeDestroyed(ai, source); boolean cantDie = ComputerUtilCombat.combatantCantBeDestroyed(ai, source);
CardCollection opposition = isBlocking ? combat.getAttackersBlockedBy(source) : combat.getBlockers(source); CardCollection opposition = isBlocking ? combat.getAttackersBlockedBy(source) : combat.getBlockers(source);
int oppP = Aggregates.sum(opposition, CardPredicates.Accessors.fnGetAttack); int oppP = Aggregates.sum(opposition, Card::getNetCombatDamage);
int oppT = Aggregates.sum(opposition, CardPredicates.Accessors.fnGetNetToughness); int oppT = Aggregates.sum(opposition, Card::getNetToughness);
boolean oppHasFirstStrike = false; boolean oppHasFirstStrike = false;
boolean oppCantDie = true; boolean oppCantDie = true;
@@ -561,7 +560,7 @@ public class SpecialCardAi {
} }
Pair<Integer, Integer> predictedPT = getPumpedPT(ai, source.getNetCombatDamage(), source.getNetToughness()); Pair<Integer, Integer> predictedPT = getPumpedPT(ai, source.getNetCombatDamage(), source.getNetToughness());
int oppT = Aggregates.sum(potentialBlockers, CardPredicates.Accessors.fnGetNetToughness); int oppT = Aggregates.sum(potentialBlockers, Card::getNetToughness);
return potentialBlockers.isEmpty() || (source.hasKeyword(Keyword.TRAMPLE) && predictedPT.getLeft() - oppT >= oppLife); return potentialBlockers.isEmpty() || (source.hasKeyword(Keyword.TRAMPLE) && predictedPT.getLeft() - oppT >= oppLife);
} }
@@ -754,28 +753,23 @@ public class SpecialCardAi {
public static Card getBestAttachTarget(final Player ai, final SpellAbility sa, final List<Card> list) { public static Card getBestAttachTarget(final Player ai, final SpellAbility sa, final List<Card> list) {
Card chosen = null; Card chosen = null;
List<Card> aiStuffies = CardLists.filter(list, new Predicate<Card>() { List<Card> aiStuffies = CardLists.filter(list, c -> {
@Override // Don't enchant creatures that can survive
public boolean apply(final Card c) { if (!c.getController().equals(ai)) {
// Don't enchant creatures that can survive return false;
if (!c.getController().equals(ai)) {
return false;
}
final String name = c.getName();
return name.equals("Stuffy Doll") || name.equals("Boros Reckoner") || name.equals("Spitemare");
} }
final String name = c.getName();
return name.equals("Stuffy Doll") || name.equals("Boros Reckoner") || name.equals("Spitemare");
}); });
if (!aiStuffies.isEmpty()) { if (!aiStuffies.isEmpty()) {
chosen = aiStuffies.get(0); chosen = aiStuffies.get(0);
} else { } else {
List<Card> creatures = CardLists.filterControlledBy(list, ai.getOpponents()); List<Card> creatures = CardLists.filterControlledBy(list, ai.getOpponents());
creatures = CardLists.filter(creatures, new Predicate<Card>() { // Don't enchant creatures that can survive
@Override creatures = CardLists.filter(creatures, c -> c.canBeDestroyed()
public boolean apply(final Card c) { && c.getNetCombatDamage() >= c.getNetToughness()
// Don't enchant creatures that can survive && !c.isEnchantedBy("Guilty Conscience")
return c.canBeDestroyed() && c.getNetCombatDamage() >= c.getNetToughness() && !c.isEnchantedBy("Guilty Conscience"); );
}
});
chosen = ComputerUtilCard.getBestCreatureAI(creatures); chosen = ComputerUtilCard.getBestCreatureAI(creatures);
} }
@@ -1385,12 +1379,7 @@ public class SpecialCardAi {
public static boolean considerMakeDragon(final Player ai, final SpellAbility sa) { public static boolean considerMakeDragon(final Player ai, final SpellAbility sa) {
// TODO: expand this logic to make the AI force the opponent to sacrifice a big threat bigger than a 5/5 flier? // TODO: expand this logic to make the AI force the opponent to sacrifice a big threat bigger than a 5/5 flier?
CardCollection creatures = ai.getCreaturesInPlay(); CardCollection creatures = ai.getCreaturesInPlay();
boolean hasValidTgt = !CardLists.filter(creatures, new Predicate<Card>() { boolean hasValidTgt = !CardLists.filter(creatures, t -> t.getNetPower() < 5 && t.getNetToughness() < 5).isEmpty();
@Override
public boolean apply(Card t) {
return t.getNetPower() < 5 && t.getNetToughness() < 5;
}
}).isEmpty();
if (hasValidTgt) { if (hasValidTgt) {
Card worstCreature = ComputerUtilCard.getWorstCreatureAI(creatures); Card worstCreature = ComputerUtilCard.getWorstCreatureAI(creatures);
sa.getTargets().add(worstCreature); sa.getTargets().add(worstCreature);
@@ -1416,12 +1405,7 @@ public class SpecialCardAi {
public static class SaviorOfOllenbock { public static class SaviorOfOllenbock {
public static boolean consider(final Player ai, final SpellAbility sa) { public static boolean consider(final Player ai, final SpellAbility sa) {
CardCollection oppTargetables = CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), sa); CardCollection oppTargetables = CardLists.getTargetableCards(ai.getOpponents().getCreaturesInPlay(), sa);
CardCollection threats = CardLists.filter(oppTargetables, new Predicate<Card>() { CardCollection threats = CardLists.filter(oppTargetables, card -> !ComputerUtilCard.isUselessCreature(card.getController(), card));
@Override
public boolean apply(Card card) {
return !ComputerUtilCard.isUselessCreature(card.getController(), card);
}
});
CardCollection ownTgts = CardLists.filter(ai.getCardsIn(ZoneType.Graveyard), CardPredicates.Presets.CREATURES); CardCollection ownTgts = CardLists.filter(ai.getCardsIn(ZoneType.Graveyard), CardPredicates.Presets.CREATURES);
// TODO: improve the conditions for when the AI is considered threatened (check the possibility of being attacked?) // TODO: improve the conditions for when the AI is considered threatened (check the possibility of being attacked?)
@@ -1462,13 +1446,10 @@ public class SpecialCardAi {
public static boolean consider(final Player ai, final SpellAbility sa) { public static boolean consider(final Player ai, final SpellAbility sa) {
int loyalty = sa.getHostCard().getCounters(CounterEnumType.LOYALTY); int loyalty = sa.getHostCard().getCounters(CounterEnumType.LOYALTY);
CardCollection creaturesToGet = CardLists.filter(ai.getCardsIn(ZoneType.Graveyard), CardCollection creaturesToGet = CardLists.filter(ai.getCardsIn(ZoneType.Graveyard),
Predicates.and(CardPredicates.Presets.CREATURES, CardPredicates.lessCMC(loyalty - 1), new Predicate<Card>() { Predicates.and(CardPredicates.Presets.CREATURES, CardPredicates.lessCMC(loyalty - 1), card -> {
@Override final Card copy = CardCopyService.getLKICopy(card);
public boolean apply(Card card) { ComputerUtilCard.applyStaticContPT(ai.getGame(), copy, null);
final Card copy = CardCopyService.getLKICopy(card); return copy.getNetToughness() > 0;
ComputerUtilCard.applyStaticContPT(ai.getGame(), copy, null);
return copy.getNetToughness() > 0;
}
})); }));
CardLists.sortByCmcDesc(creaturesToGet); CardLists.sortByCmcDesc(creaturesToGet);
@@ -1515,12 +1496,9 @@ public class SpecialCardAi {
// Cards in library that are either below/at (preferred) or above the max CMC affordable by the AI // Cards in library that are either below/at (preferred) or above the max CMC affordable by the AI
// (the latter might happen if we're playing a Reanimator deck with lots of fatties) // (the latter might happen if we're playing a Reanimator deck with lots of fatties)
CardCollection atTargetCMCInLib = CardLists.filter(creatsInLib, new Predicate<Card>() { CardCollection atTargetCMCInLib = CardLists.filter(creatsInLib,
@Override card -> ComputerUtilMana.hasEnoughManaSourcesToCast(card.getSpellPermanent(), ai)
public boolean apply(Card card) { );
return ComputerUtilMana.hasEnoughManaSourcesToCast(card.getSpellPermanent(), ai);
}
});
if (atTargetCMCInLib.isEmpty()) { if (atTargetCMCInLib.isEmpty()) {
atTargetCMCInLib = CardLists.filter(creatsInLib, CardPredicates.greaterCMC(numManaSrcs)); atTargetCMCInLib = CardLists.filter(creatsInLib, CardPredicates.greaterCMC(numManaSrcs));
} }
@@ -1585,12 +1563,9 @@ public class SpecialCardAi {
int numManaSrcs = ComputerUtilMana.getAvailableManaEstimate(ai, false) int numManaSrcs = ComputerUtilMana.getAvailableManaEstimate(ai, false)
+ Math.min(1, manaSrcsInHand.size()); + Math.min(1, manaSrcsInHand.size());
CardCollection atTargetCMCInLib = CardLists.filter(creatsInLib, new Predicate<Card>() { CardCollection atTargetCMCInLib = CardLists.filter(creatsInLib,
@Override card -> ComputerUtilMana.hasEnoughManaSourcesToCast(card.getSpellPermanent(), ai)
public boolean apply(Card card) { );
return ComputerUtilMana.hasEnoughManaSourcesToCast(card.getSpellPermanent(), ai);
}
});
if (atTargetCMCInLib.isEmpty()) { if (atTargetCMCInLib.isEmpty()) {
atTargetCMCInLib = CardLists.filter(creatsInLib, CardPredicates.greaterCMC(numManaSrcs)); atTargetCMCInLib = CardLists.filter(creatsInLib, CardPredicates.greaterCMC(numManaSrcs));
} }

View File

@@ -5,7 +5,6 @@ import java.util.*;
import forge.game.card.*; import forge.game.card.*;
import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.ObjectUtils;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
@@ -333,26 +332,23 @@ public class AttachAi extends SpellAbilityAi {
list = CardLists.getNotType(list, type); // Filter out Basic Lands that have the same type as the changing type list = CardLists.getNotType(list, type); // Filter out Basic Lands that have the same type as the changing type
// Don't target fetchlands // Don't target fetchlands
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> {
@Override //Check for cards that can be sacrificed in response
public boolean apply(final Card c) { for (final SpellAbility ability : c.getAllSpellAbilities()) {
//Check for cards that can be sacrificed in response if (ability.isActivatedAbility()) {
for (final SpellAbility ability : c.getAllSpellAbilities()) { final Cost cost = ability.getPayCosts();
if (ability.isActivatedAbility()) { for (final CostPart part : cost.getCostParts()) {
final Cost cost = ability.getPayCosts(); if (!(part instanceof CostSacrifice)) {
for (final CostPart part : cost.getCostParts()) { continue;
if (!(part instanceof CostSacrifice)) { }
continue; CostSacrifice sacCost = (CostSacrifice) part;
} if (sacCost.payCostFromSource() && ComputerUtilCost.canPayCost(ability, c.getController(), false)) {
CostSacrifice sacCost = (CostSacrifice) part; return false;
if (sacCost.payCostFromSource() && ComputerUtilCost.canPayCost(ability, c.getController(), false)) {
return false;
}
} }
} }
} }
return true;
} }
return true;
}); });
final Card c = ComputerUtilCard.getBestAI(list); final Card c = ComputerUtilCard.getBestAI(list);
@@ -385,43 +381,40 @@ public class AttachAi extends SpellAbilityAi {
*/ */
private static Card attachAIKeepTappedPreference(final SpellAbility sa, final List<Card> list, final boolean mandatory, final Card attachSource) { private static Card attachAIKeepTappedPreference(final SpellAbility sa, final List<Card> list, final boolean mandatory, final Card attachSource) {
// AI For Cards like Paralyzing Grasp and Glimmerdust Nap // AI For Cards like Paralyzing Grasp and Glimmerdust Nap
final List<Card> prefList = CardLists.filter(list, new Predicate<Card>() { final List<Card> prefList = CardLists.filter(list, c -> {
@Override // Don't do Untapped Vigilance cards
public boolean apply(final Card c) { if (c.isCreature() && c.hasKeyword(Keyword.VIGILANCE) && c.isUntapped()) {
// Don't do Untapped Vigilance cards return false;
if (c.isCreature() && c.hasKeyword(Keyword.VIGILANCE) && c.isUntapped()) { }
if (!mandatory) {
if (!c.isCreature() && !c.getType().hasSubtype("Vehicle") && !c.isTapped()) {
// try to identify if this thing can actually tap
for (SpellAbility ab : c.getAllSpellAbilities()) {
if (ab.getPayCosts().hasTapCost()) {
return true;
}
}
return false; return false;
} }
}
if (!mandatory) { if (!c.isEnchanted()) {
if (!c.isCreature() && !c.getType().hasSubtype("Vehicle") && !c.isTapped()) { return true;
// try to identify if this thing can actually tap }
for (SpellAbility ab : c.getAllSpellAbilities()) {
if (ab.getPayCosts().hasTapCost()) { final Iterable<Card> auras = c.getEnchantedBy();
return true; for (Card aura : auras) {
} SpellAbility auraSA = aura.getSpells().get(0);
} if (auraSA.getApi() == ApiType.Attach) {
if ("KeepTapped".equals(auraSA.getParam("AILogic"))) {
// Don't attach multiple KeepTapped Auras to one card
return false; return false;
} }
} }
if (!c.isEnchanted()) {
return true;
}
final Iterable<Card> auras = c.getEnchantedBy();
for (Card aura : auras) {
SpellAbility auraSA = aura.getSpells().get(0);
if (auraSA.getApi() == ApiType.Attach) {
if ("KeepTapped".equals(auraSA.getParam("AILogic"))) {
// Don't attach multiple KeepTapped Auras to one card
return false;
}
}
}
return true;
} }
return true;
}); });
final Card c = ComputerUtilCard.getBestAI(prefList); final Card c = ComputerUtilCard.getBestAI(prefList);
@@ -506,24 +499,14 @@ public class AttachAi extends SpellAbilityAi {
if (list.isEmpty()) { if (list.isEmpty()) {
return null; return null;
} }
Card c = null; Card card = null;
// AI For choosing a Card to Animate. // AI For choosing a Card to Animate.
List<Card> betterList = CardLists.getNotType(list, "Creature"); List<Card> betterList = CardLists.getNotType(list, "Creature");
if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Animate Artifact")) { if (ComputerUtilAbility.getAbilitySourceName(sa).equals("Animate Artifact")) {
betterList = CardLists.filter(betterList, new Predicate<Card>() { betterList = CardLists.filter(betterList, c -> c.getCMC() > 0);
@Override card = ComputerUtilCard.getMostExpensivePermanentAI(betterList);
public boolean apply(final Card c) {
return c.getCMC() > 0;
}
});
c = ComputerUtilCard.getMostExpensivePermanentAI(betterList);
} else { } else {
List<Card> evenBetterList = CardLists.filter(betterList, new Predicate<Card>() { List<Card> evenBetterList = CardLists.filter(betterList, c -> c.hasKeyword(Keyword.INDESTRUCTIBLE) || c.hasKeyword(Keyword.HEXPROOF));
@Override
public boolean apply(final Card c) {
return c.hasKeyword(Keyword.INDESTRUCTIBLE) || c.hasKeyword(Keyword.HEXPROOF);
}
});
if (!evenBetterList.isEmpty()) { if (!evenBetterList.isEmpty()) {
betterList = evenBetterList; betterList = evenBetterList;
} }
@@ -531,40 +514,32 @@ public class AttachAi extends SpellAbilityAi {
if (!evenBetterList.isEmpty()) { if (!evenBetterList.isEmpty()) {
betterList = evenBetterList; betterList = evenBetterList;
} }
evenBetterList = CardLists.filter(betterList, new Predicate<Card>() { evenBetterList = CardLists.filter(betterList, c -> c.getTurnInZone() != c.getGame().getPhaseHandler().getTurn());
@Override
public boolean apply(final Card c) {
return c.getTurnInZone() != c.getGame().getPhaseHandler().getTurn();
}
});
if (!evenBetterList.isEmpty()) { if (!evenBetterList.isEmpty()) {
betterList = evenBetterList; betterList = evenBetterList;
} }
evenBetterList = CardLists.filter(betterList, new Predicate<Card>() { evenBetterList = CardLists.filter(betterList, c -> {
@Override for (final SpellAbility sa1 : c.getSpellAbilities()) {
public boolean apply(final Card c) { if (sa1.isAbility() && sa1.getPayCosts().hasTapCost()) {
for (final SpellAbility sa : c.getSpellAbilities()) { return false;
if (sa.isAbility() && sa.getPayCosts().hasTapCost()) {
return false;
}
} }
return true;
} }
return true;
}); });
if (!evenBetterList.isEmpty()) { if (!evenBetterList.isEmpty()) {
betterList = evenBetterList; betterList = evenBetterList;
} }
c = ComputerUtilCard.getWorstAI(betterList); card = ComputerUtilCard.getWorstAI(betterList);
} }
// If Mandatory (brought directly into play without casting) gotta // If Mandatory (brought directly into play without casting) gotta
// choose something // choose something
if (c == null && mandatory) { if (card == null && mandatory) {
return chooseLessPreferred(mandatory, list); return chooseLessPreferred(mandatory, list);
} }
return c; return card;
} }
/** /**
@@ -591,29 +566,26 @@ public class AttachAi extends SpellAbilityAi {
//TODO for Reanimate Auras i need the new Attach Spell, in later versions it might be part of the Enchant Keyword //TODO for Reanimate Auras i need the new Attach Spell, in later versions it might be part of the Enchant Keyword
attachSourceLki.addSpellAbility(AbilityFactory.getAbility(attachSourceLki, "NewAttach")); attachSourceLki.addSpellAbility(AbilityFactory.getAbility(attachSourceLki, "NewAttach"));
List<Card> betterList = CardLists.filter(list, new Predicate<Card>() { List<Card> betterList = CardLists.filter(list, c -> {
@Override final Card lki = CardCopyService.getLKICopy(c);
public boolean apply(final Card c) { // need to fake it as if lki would be on the battlefield
final Card lki = CardCopyService.getLKICopy(c); lki.setLastKnownZone(ai.getZone(ZoneType.Battlefield));
// need to fake it as if lki would be on the battlefield
lki.setLastKnownZone(ai.getZone(ZoneType.Battlefield));
// Reanimate Auras use "Enchant creature put onto the battlefield with CARDNAME" with Remembered // Reanimate Auras use "Enchant creature put onto the battlefield with CARDNAME" with Remembered
attachSourceLki.clearRemembered(); attachSourceLki.clearRemembered();
attachSourceLki.addRemembered(lki); attachSourceLki.addRemembered(lki);
// need to check what the cards would be on the battlefield // need to check what the cards would be on the battlefield
// do not attach yet, that would cause Events // do not attach yet, that would cause Events
CardCollection preList = new CardCollection(lki); CardCollection preList = new CardCollection(lki);
preList.add(attachSourceLki); preList.add(attachSourceLki);
c.getGame().getAction().checkStaticAbilities(false, Sets.newHashSet(preList), preList); c.getGame().getAction().checkStaticAbilities(false, Sets.newHashSet(preList), preList);
boolean result = lki.canBeAttached(attachSourceLki, null); boolean result = lki.canBeAttached(attachSourceLki, null);
//reset static abilities //reset static abilities
c.getGame().getAction().checkStaticAbilities(false); c.getGame().getAction().checkStaticAbilities(false);
return result; return result;
}
}); });
final Card c = ComputerUtilCard.getBestCreatureAI(betterList); final Card c = ComputerUtilCard.getBestCreatureAI(betterList);
@@ -864,42 +836,29 @@ public class AttachAi extends SpellAbilityAi {
if (totToughness < 0) { if (totToughness < 0) {
// Kill a creature if we can // Kill a creature if we can
final int tgh = totToughness; final int tgh = totToughness;
prefList = CardLists.filter(list, new Predicate<Card>() { prefList = CardLists.filter(list, c -> {
@Override if (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && (c.getLethalDamage() <= Math.abs(tgh))) {
public boolean apply(final Card c) { return true;
if (!c.hasKeyword(Keyword.INDESTRUCTIBLE) && (c.getLethalDamage() <= Math.abs(tgh))) {
return true;
}
return c.getNetToughness() <= Math.abs(tgh);
} }
return c.getNetToughness() <= Math.abs(tgh);
}); });
} }
Card c = null; Card card = null;
if (prefList == null || prefList.isEmpty()) { if (prefList == null || prefList.isEmpty()) {
prefList = new ArrayList<>(list); prefList = new ArrayList<>(list);
} else { } else {
c = ComputerUtilCard.getBestAI(prefList); card = ComputerUtilCard.getBestAI(prefList);
if (c != null) { if (card != null) {
return c; return card;
} }
} }
if (!keywords.isEmpty()) { if (!keywords.isEmpty()) {
prefList = CardLists.filter(prefList, new Predicate<Card>() { prefList = CardLists.filter(prefList, c -> containsUsefulCurseKeyword(keywords, c, sa));
@Override
public boolean apply(final Card c) {
return containsUsefulCurseKeyword(keywords, c, sa);
}
});
} else if (totPower < 0) { } else if (totPower < 0) {
prefList = CardLists.filter(prefList, new Predicate<Card>() { prefList = CardLists.filter(prefList, c -> c.getNetPower() > 0 && ComputerUtilCombat.canAttackNextTurn(c));
@Override
public boolean apply(final Card c) {
return c.getNetPower() > 0 && ComputerUtilCombat.canAttackNextTurn(c);
}
});
} }
//some auras aren't useful in multiples //some auras aren't useful in multiples
@@ -915,25 +874,22 @@ public class AttachAi extends SpellAbilityAi {
&& sa.getPayCosts() != null && sa.getPayCosts().hasSpecificCostType(CostSacrifice.class)) { && sa.getPayCosts() != null && sa.getPayCosts().hasSpecificCostType(CostSacrifice.class)) {
final int oldEvalRating = ComputerUtilCard.evaluateCreature(sa.getHostCard().getAttachedTo()); final int oldEvalRating = ComputerUtilCard.evaluateCreature(sa.getHostCard().getAttachedTo());
final int threshold = ai.isAI() ? ((PlayerControllerAi)ai.getController()).getAi().getIntProperty(AiProps.SAC_TO_REATTACH_TARGET_EVAL_THRESHOLD) : Integer.MAX_VALUE; final int threshold = ai.isAI() ? ((PlayerControllerAi)ai.getController()).getAi().getIntProperty(AiProps.SAC_TO_REATTACH_TARGET_EVAL_THRESHOLD) : Integer.MAX_VALUE;
prefList = CardLists.filter(prefList, new Predicate<Card>() { prefList = CardLists.filter(prefList, c -> {
@Override if (!c.isCreature()) {
public boolean apply(Card card) { return false;
if (!card.isCreature()) {
return false;
}
return ComputerUtilCard.evaluateCreature(card) >= oldEvalRating + threshold;
} }
return ComputerUtilCard.evaluateCreature(c) >= oldEvalRating + threshold;
}); });
} }
c = ComputerUtilCard.getBestAI(prefList); card = ComputerUtilCard.getBestAI(prefList);
if (c == null) { if (card == null) {
return chooseLessPreferred(mandatory, list); return chooseLessPreferred(mandatory, list);
} }
return acceptableChoice(c, mandatory); return acceptableChoice(card, mandatory);
} }
/** /**
@@ -1029,41 +985,30 @@ public class AttachAi extends SpellAbilityAi {
*/ */
private static Card attachAIPumpPreference(final Player ai, final SpellAbility sa, final List<Card> list, final boolean mandatory, final Card attachSource) { private static Card attachAIPumpPreference(final Player ai, final SpellAbility sa, final List<Card> list, final boolean mandatory, final Card attachSource) {
// AI For choosing a Card to Pump // AI For choosing a Card to Pump
Card c = null; Card card = null;
List<Card> magnetList = null; List<Card> magnetList = null;
String stCheck = null; String stCheck = null;
if (attachSource.isAura() || sa.isBestow()) { if (attachSource.isAura() || sa.isBestow()) {
stCheck = "EnchantedBy"; stCheck = "EnchantedBy";
magnetList = CardLists.filter(list, new Predicate<Card>() { magnetList = CardLists.filter(list, c -> {
@Override if (!c.isCreature()) {
public boolean apply(final Card c) { return false;
if (!c.isCreature()) {
return false;
}
String sVar = c.getSVar("EnchantMe");
return sVar.equals("Multiple") || (sVar.equals("Once") && !c.isEnchanted());
} }
String sVar = c.getSVar("EnchantMe");
return sVar.equals("Multiple") || (sVar.equals("Once") && !c.isEnchanted());
}); });
} else if (attachSource.isEquipment()) { } else if (attachSource.isEquipment()) {
stCheck = "EquippedBy"; stCheck = "EquippedBy";
magnetList = CardLists.filter(list, new Predicate<Card>() { magnetList = CardLists.filter(list, c -> {
@Override if (!c.isCreature()) {
public boolean apply(final Card c) { return false;
if (!c.isCreature()) {
return false;
}
String sVar = c.getSVar("EquipMe");
return sVar.equals("Multiple") || (sVar.equals("Once") && !c.isEquipped());
} }
String sVar = c.getSVar("EquipMe");
return sVar.equals("Multiple") || (sVar.equals("Once") && !c.isEquipped());
}); });
} else if (attachSource.isFortification()) { } else if (attachSource.isFortification()) {
stCheck = "FortifiedBy"; stCheck = "FortifiedBy";
magnetList = CardLists.filter(list, new Predicate<Card>() { magnetList = CardLists.filter(list, c -> c.isCreature() && !c.isFortified());
@Override
public boolean apply(final Card c) {
return c.isCreature() && !c.isFortified();
}
});
} }
// Look for triggers that will damage the creature and remove AI-owned creatures that will die // Look for triggers that will damage the creature and remove AI-owned creatures that will die
@@ -1109,23 +1054,13 @@ public class AttachAi extends SpellAbilityAi {
// Probably want to "weight" the list by amount of Enchantments and // Probably want to "weight" the list by amount of Enchantments and
// choose the "lightest" // choose the "lightest"
List<Card> betterList = CardLists.filter(magnetList, new Predicate<Card>() { List<Card> betterList = CardLists.filter(magnetList, c -> CombatUtil.canAttack(c, ai.getWeakestOpponent()));
@Override
public boolean apply(final Card c) {
return CombatUtil.canAttack(c, ai.getWeakestOpponent());
}
});
if (!betterList.isEmpty()) { if (!betterList.isEmpty()) {
return ComputerUtilCard.getBestAI(betterList); return ComputerUtilCard.getBestAI(betterList);
} }
// Magnet List should not be attached when they are useless // Magnet List should not be attached when they are useless
betterList = CardLists.filter(magnetList, new Predicate<Card>() { betterList = CardLists.filter(magnetList, c -> !ComputerUtilCard.isUselessCreature(ai, c));
@Override
public boolean apply(final Card c) {
return !ComputerUtilCard.isUselessCreature(ai, c);
}
});
if (!betterList.isEmpty()) { if (!betterList.isEmpty()) {
return ComputerUtilCard.getBestAI(betterList); return ComputerUtilCard.getBestAI(betterList);
@@ -1179,38 +1114,30 @@ public class AttachAi extends SpellAbilityAi {
if (totToughness < 0) { if (totToughness < 0) {
// Don't kill my own stuff with Negative toughness Auras // Don't kill my own stuff with Negative toughness Auras
final int tgh = totToughness; final int tgh = totToughness;
prefList = CardLists.filter(prefList, new Predicate<Card>() { prefList = CardLists.filter(prefList, c -> c.getLethalDamage() > Math.abs(tgh));
@Override
public boolean apply(final Card c) {
return c.getLethalDamage() > Math.abs(tgh);
}
});
} }
//only add useful keywords unless P/T bonus is significant //only add useful keywords unless P/T bonus is significant
if (totToughness + totPower < 4 && (!keywords.isEmpty() || grantingExtraBlock)) { if (totToughness + totPower < 4 && (!keywords.isEmpty() || grantingExtraBlock)) {
final int pow = totPower; final int pow = totPower;
final boolean extraBlock = grantingExtraBlock; final boolean extraBlock = grantingExtraBlock;
prefList = CardLists.filter(prefList, new Predicate<Card>() { prefList = CardLists.filter(prefList, c -> {
@Override if (!keywords.isEmpty()) {
public boolean apply(final Card c) { for (final String keyword : keywords) {
if (!keywords.isEmpty()) { if (isUsefulAttachKeyword(keyword, c, sa, pow)) {
for (final String keyword : keywords) { return true;
if (isUsefulAttachKeyword(keyword, c, sa, pow)) {
return true;
}
} }
} }
if (c.hasKeyword(Keyword.INFECT) && pow >= 2) {
// consider +2 power a significant bonus on Infect creatures
return true;
}
if (extraBlock && CombatUtil.canBlock(c, true) && !c.canBlockAny()) {
return true;
}
return false;
} }
if (c.hasKeyword(Keyword.INFECT) && pow >= 2) {
// consider +2 power a significant bonus on Infect creatures
return true;
}
if (extraBlock && CombatUtil.canBlock(c, true) && !c.canBlockAny()) {
return true;
}
return false;
}); });
} }
@@ -1229,21 +1156,11 @@ public class AttachAi extends SpellAbilityAi {
if (!attachSource.getName().equals("Daybreak Coronet")) { if (!attachSource.getName().equals("Daybreak Coronet")) {
// TODO For Auras like Rancor, that aren't as likely to lead to // TODO For Auras like Rancor, that aren't as likely to lead to
// card disadvantage, this check should be skipped // card disadvantage, this check should be skipped
prefList = CardLists.filter(prefList, new Predicate<Card>() { prefList = CardLists.filter(prefList, c -> !c.isEnchanted() || c.hasKeyword(Keyword.HEXPROOF));
@Override
public boolean apply(final Card c) {
return !c.isEnchanted() || c.hasKeyword(Keyword.HEXPROOF);
}
});
} }
// should not attach Auras to creatures that does leave the play // should not attach Auras to creatures that does leave the play
prefList = CardLists.filter(prefList, new Predicate<Card>() { prefList = CardLists.filter(prefList, c -> !c.hasSVar("EndOfTurnLeavePlay"));
@Override
public boolean apply(final Card c) {
return !c.hasSVar("EndOfTurnLeavePlay");
}
});
} }
// Should not attach things to crewed vehicles that will stop being creatures soon // Should not attach things to crewed vehicles that will stop being creatures soon
@@ -1258,12 +1175,7 @@ public class AttachAi extends SpellAbilityAi {
} }
} }
if (canOnlyTargetCreatures && (attachSource.isAura() || attachSource.isEquipment())) { if (canOnlyTargetCreatures && (attachSource.isAura() || attachSource.isEquipment())) {
prefList = CardLists.filter(prefList, new Predicate<Card>() { prefList = CardLists.filter(prefList, c -> c.getTimesCrewedThisTurn() == 0 || (attachSource.isEquipment() && attachSource.getGame().getPhaseHandler().is(PhaseType.MAIN1, ai)));
@Override
public boolean apply(Card card) {
return card.getTimesCrewedThisTurn() == 0 || (attachSource.isEquipment() && attachSource.getGame().getPhaseHandler().is(PhaseType.MAIN1, ai));
}
});
} }
if (!grantingAbilities) { if (!grantingAbilities) {
@@ -1271,17 +1183,14 @@ public class AttachAi extends SpellAbilityAi {
// Filter out creatures that can't Attack or have Defender // Filter out creatures that can't Attack or have Defender
if (keywords.isEmpty()) { if (keywords.isEmpty()) {
final int powerBonus = totPower; final int powerBonus = totPower;
prefList = CardLists.filter(prefList, new Predicate<Card>() { prefList = CardLists.filter(prefList, c -> {
@Override if (!c.isCreature()) {
public boolean apply(final Card c) { return true;
if (!c.isCreature()) {
return true;
}
return powerBonus + c.getNetPower() > 0 && ComputerUtilCombat.canAttackNextTurn(c);
} }
return powerBonus + c.getNetPower() > 0 && ComputerUtilCombat.canAttackNextTurn(c);
}); });
} }
c = ComputerUtilCard.getBestAI(prefList); card = ComputerUtilCard.getBestAI(prefList);
} else { } else {
for (Card pref : prefList) { for (Card pref : prefList) {
if (pref.isLand() && pref.isUntapped()) { if (pref.isLand() && pref.isUntapped()) {
@@ -1290,14 +1199,14 @@ public class AttachAi extends SpellAbilityAi {
} }
// If we grant abilities, we may want to put it on something Weak? // If we grant abilities, we may want to put it on something Weak?
// Possibly more defensive? // Possibly more defensive?
c = ComputerUtilCard.getWorstPermanentAI(prefList, false, false, false, false); card = ComputerUtilCard.getWorstPermanentAI(prefList, false, false, false, false);
} }
if (c == null) { if (card == null) {
return chooseLessPreferred(mandatory, list); return chooseLessPreferred(mandatory, list);
} }
return c; return card;
} }
/** /**
@@ -1338,22 +1247,13 @@ public class AttachAi extends SpellAbilityAi {
// Is a SA that moves target attachment // Is a SA that moves target attachment
if ("MoveTgtAura".equals(sa.getParam("AILogic"))) { if ("MoveTgtAura".equals(sa.getParam("AILogic"))) {
CardCollection list = CardLists.filter(CardUtil.getValidCardsToTarget(sa), Predicates.or(CardPredicates.isControlledByAnyOf(aiPlayer.getOpponents()), new Predicate<Card>() { CardCollection list = CardLists.filter(CardUtil.getValidCardsToTarget(sa), Predicates.or(CardPredicates.isControlledByAnyOf(aiPlayer.getOpponents()),
@Override card -> ComputerUtilCard.isUselessCreature(aiPlayer, card.getAttachedTo())));
public boolean apply(final Card card) {
return ComputerUtilCard.isUselessCreature(aiPlayer, card.getAttachedTo());
}
}));
return !list.isEmpty() ? ComputerUtilCard.getBestAI(list) : null; return !list.isEmpty() ? ComputerUtilCard.getBestAI(list) : null;
} else if ("Unenchanted".equals(sa.getParam("AILogic"))) { } else if ("Unenchanted".equals(sa.getParam("AILogic"))) {
List<Card> list = CardUtil.getValidCardsToTarget(sa); List<Card> list = CardUtil.getValidCardsToTarget(sa);
CardCollection preferred = CardLists.filter(list, new Predicate<Card>() { CardCollection preferred = CardLists.filter(list, card -> !card.hasCardAttachments());
@Override
public boolean apply(final Card card) {
return !card.hasCardAttachments();
}
});
return preferred.isEmpty() ? Aggregates.random(list) : Aggregates.random(preferred); return preferred.isEmpty() ? Aggregates.random(list) : Aggregates.random(preferred);
} }
@@ -1687,25 +1587,19 @@ public class AttachAi extends SpellAbilityAi {
public static Card doPumpOrCurseAILogic(final Player ai, final SpellAbility sa, final List<Card> list, final String type) { public static Card doPumpOrCurseAILogic(final Player ai, final SpellAbility sa, final List<Card> list, final String type) {
Card chosen = null; Card chosen = null;
List<Card> aiType = CardLists.filter(list, new Predicate<Card>() { List<Card> aiType = CardLists.filter(list, c -> {
@Override // Don't buff opponent's creatures of given type
public boolean apply(final Card c) { if (!c.getController().equals(ai)) {
// Don't buff opponent's creatures of given type return false;
if (!c.getController().equals(ai)) {
return false;
}
return c.isValid(type, ai, sa.getHostCard(), sa);
} }
return c.isValid(type, ai, sa.getHostCard(), sa);
}); });
List<Card> oppNonType = CardLists.filter(list, new Predicate<Card>() { List<Card> oppNonType = CardLists.filter(list, c -> {
@Override // Don't debuff AI's own creatures not of given type
public boolean apply(final Card c) { if (c.getController().equals(ai)) {
// Don't debuff AI's own creatures not of given type return false;
if (c.getController().equals(ai)) {
return false;
}
return !c.isValid(type, ai, sa.getHostCard(), sa) && !ComputerUtilCard.isUselessCreature(ai, c);
} }
return !c.isValid(type, ai, sa.getHostCard(), sa) && !ComputerUtilCard.isUselessCreature(ai, c);
}); });
if (!aiType.isEmpty() && !oppNonType.isEmpty()) { if (!aiType.isEmpty() && !oppNonType.isEmpty()) {

View File

@@ -1,7 +1,6 @@
package forge.ai.ability; package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.SpecialAiLogic; import forge.ai.SpecialAiLogic;
import forge.ai.SpecialCardAi; import forge.ai.SpecialCardAi;
@@ -35,12 +34,9 @@ public class BranchAi extends SpellAbilityAi {
} }
final CardCollection attackers = combat.getAttackers(); final CardCollection attackers = combat.getAttackers();
final CardCollection attackingBattle = CardLists.filter(attackers, new Predicate<Card>() { final CardCollection attackingBattle = CardLists.filter(attackers, card -> {
@Override final GameEntity def = combat.getDefenderByAttacker(combat.getBandOfAttacker(card));
public boolean apply(Card card) { return def instanceof Card && ((Card)def).isBattle();
final GameEntity def = combat.getDefenderByAttacker(combat.getBandOfAttacker(card));
return def instanceof Card && ((Card)def).isBattle();
}
}); });
if (!attackingBattle.isEmpty()) { if (!attackingBattle.isEmpty()) {

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -378,14 +377,11 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (type != null && p == ai) { if (type != null && p == ai) {
// AI only "knows" about his information // AI only "knows" about his information
list = CardLists.getValidCards(list, type, source.getController(), source, sa); list = CardLists.getValidCards(list, type, source.getController(), source, sa);
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> {
@Override if (c.getType().isLegendary()) {
public boolean apply(final Card c) { return !ai.isCardInPlay(c.getName());
if (c.getType().isLegendary()) {
return !ai.isCardInPlay(c.getName());
}
return true;
} }
return true;
}); });
} }
// TODO: prevent ai searching its own library when Ob Nixilis, Unshackled is in play // TODO: prevent ai searching its own library when Ob Nixilis, Unshackled is in play
@@ -906,12 +902,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
// Filter AI-specific targets if provided // Filter AI-specific targets if provided
list = ComputerUtil.filterAITgts(sa, ai, list, true); list = ComputerUtil.filterAITgts(sa, ai, list, true);
if (sa.hasParam("AITgtsOnlyBetterThanSelf")) { if (sa.hasParam("AITgtsOnlyBetterThanSelf")) {
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, card -> ComputerUtilCard.evaluateCreature(card) > ComputerUtilCard.evaluateCreature(source) + 30);
@Override
public boolean apply(Card card) {
return ComputerUtilCard.evaluateCreature(card) > ComputerUtilCard.evaluateCreature(source) + 30;
}
});
} }
if (source.isInZone(ZoneType.Hand)) { if (source.isInZone(ZoneType.Hand)) {
@@ -921,29 +912,23 @@ public class ChangeZoneAi extends SpellAbilityAi {
list.remove(source); // spells can't target their own source, because it's actually in the stack zone list.remove(source); // spells can't target their own source, because it's actually in the stack zone
} }
if (sa.hasParam("AttachedTo")) { if (sa.hasParam("AttachedTo")) {
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> {
@Override for (Card card : game.getCardsIn(ZoneType.Battlefield)) {
public boolean apply(final Card c) { if (card.isValid(sa.getParam("AttachedTo"), ai, c, sa)) {
for (Card card : game.getCardsIn(ZoneType.Battlefield)) { return true;
if (card.isValid(sa.getParam("AttachedTo"), ai, c, sa)) {
return true;
}
} }
return false;
} }
return false;
}); });
} }
if (sa.hasParam("AttachAfter")) { if (sa.hasParam("AttachAfter")) {
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> {
@Override for (Card card : game.getCardsIn(ZoneType.Battlefield)) {
public boolean apply(final Card c) { if (card.isValid(sa.getParam("AttachAfter"), ai, c, sa)) {
for (Card card : game.getCardsIn(ZoneType.Battlefield)) { return true;
if (card.isValid(sa.getParam("AttachAfter"), ai, c, sa)) {
return true;
}
} }
return false;
} }
return false;
}); });
} }
@@ -1039,12 +1024,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
} }
// blink logic: get my own permanents back or blink permanents with ETB effects // blink logic: get my own permanents back or blink permanents with ETB effects
if (blink) { if (blink) {
CardCollection blinkTargets = CardLists.filter(list, new Predicate<Card>() { CardCollection blinkTargets = CardLists.filter(list, c -> !c.isToken() && c.getOwner().equals(ai) && (c.getController().isOpponentOf(ai) || c.hasETBTrigger(false)));
@Override
public boolean apply(final Card c) {
return !c.isToken() && c.getOwner().equals(ai) && (c.getController().isOpponentOf(ai) || c.hasETBTrigger(false));
}
});
if (!blinkTargets.isEmpty()) { if (!blinkTargets.isEmpty()) {
CardCollection opponentBlinkTargets = CardLists.filterControlledBy(blinkTargets, ai.getOpponents()); CardCollection opponentBlinkTargets = CardLists.filterControlledBy(blinkTargets, ai.getOpponents());
// prefer post-combat unless targeting opponent's stuff or part of another ability // prefer post-combat unless targeting opponent's stuff or part of another ability
@@ -1070,17 +1050,14 @@ public class ChangeZoneAi extends SpellAbilityAi {
list = CardLists.filterControlledBy(list, ai.getOpponents()); list = CardLists.filterControlledBy(list, ai.getOpponents());
if (!CardLists.getNotType(list, "Land").isEmpty()) { if (!CardLists.getNotType(list, "Land").isEmpty()) {
// When bouncing opponents stuff other than lands, don't bounce cards with CMC 0 // When bouncing opponents stuff other than lands, don't bounce cards with CMC 0
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> {
@Override for (Card aura : c.getEnchantedBy()) {
public boolean apply(final Card c) { return aura.getController().isOpponentOf(ai);
for (Card aura : c.getEnchantedBy()) {
return aura.getController().isOpponentOf(ai);
}
if (blink) {
return c.isToken();
}
return c.isToken() || c.getCMC() > 0;
} }
if (blink) {
return c.isToken();
}
return c.isToken() || c.getCMC() > 0;
}); });
} }
} }
@@ -1102,16 +1079,13 @@ public class ChangeZoneAi extends SpellAbilityAi {
// only retrieve cards from computer graveyard // only retrieve cards from computer graveyard
list = CardLists.filterControlledBy(list, ai); list = CardLists.filterControlledBy(list, ai);
} else if (sa.hasParam("AttachedTo")) { } else if (sa.hasParam("AttachedTo")) {
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> {
@Override for (SpellAbility attach : c.getSpellAbilities()) {
public boolean apply(final Card c) { if ("Pump".equals(attach.getParam("AILogic"))) {
for (SpellAbility attach : c.getSpellAbilities()) { return true; //only use good auras
if ("Pump".equals(attach.getParam("AILogic"))) {
return true; //only use good auras
}
} }
return false;
} }
return false;
}); });
} }
} }
@@ -1135,16 +1109,13 @@ public class ChangeZoneAi extends SpellAbilityAi {
if (!sa.hasParam("AITgtOwnCards")) { if (!sa.hasParam("AITgtOwnCards")) {
list = CardLists.filterControlledBy(list, ai.getOpponents()); list = CardLists.filterControlledBy(list, ai.getOpponents());
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> {
@Override for (Card aura : c.getEnchantedBy()) {
public boolean apply(final Card c) { if (c.getOwner().isOpponentOf(ai) && aura.getController().equals(ai)) {
for (Card aura : c.getEnchantedBy()) { return false;
if (c.getOwner().isOpponentOf(ai) && aura.getController().equals(ai)) {
return false;
}
} }
return true;
} }
return true;
}); });
} }
@@ -1613,22 +1584,14 @@ public class ChangeZoneAi extends SpellAbilityAi {
// Save a card as a default, in case we can't find anything suitable. // Save a card as a default, in case we can't find anything suitable.
Card first = fetchList.get(0); Card first = fetchList.get(0);
if (ZoneType.Battlefield.equals(destination)) { if (ZoneType.Battlefield.equals(destination)) {
fetchList = CardLists.filter(fetchList, new Predicate<Card>() { fetchList = CardLists.filter(fetchList, c1 -> {
@Override if (c1.getType().isLegendary()) {
public boolean apply(final Card c) { return !decider.isCardInPlay(c1.getName());
if (c.getType().isLegendary()) {
return !decider.isCardInPlay(c.getName());
}
return true;
} }
return true;
}); });
if (player.isOpponentOf(decider) && sa.hasParam("GainControl") && activator.equals(decider)) { if (player.isOpponentOf(decider) && sa.hasParam("GainControl") && activator.equals(decider)) {
fetchList = CardLists.filter(fetchList, new Predicate<Card>() { fetchList = CardLists.filter(fetchList, c12 -> !ComputerUtilCard.isCardRemAIDeck(c12) && !ComputerUtilCard.isCardRemRandomDeck(c12));
@Override
public boolean apply(final Card c) {
return !ComputerUtilCard.isCardRemAIDeck(c) && !ComputerUtilCard.isCardRemRandomDeck(c);
}
});
} }
} }
if (ZoneType.Exile.equals(destination) || origin.contains(ZoneType.Battlefield) if (ZoneType.Exile.equals(destination) || origin.contains(ZoneType.Battlefield)
@@ -1715,53 +1678,50 @@ public class ChangeZoneAi extends SpellAbilityAi {
} }
private static CardCollection prefilterOwnListForBounceAnyNum(CardCollection fetchList, Player decider) { private static CardCollection prefilterOwnListForBounceAnyNum(CardCollection fetchList, Player decider) {
fetchList = CardLists.filter(fetchList, new Predicate<Card>() { fetchList = CardLists.filter(fetchList, card -> {
@Override if (card.isToken()) {
public boolean apply(final Card card) { return false;
if (card.isToken()) { }
return false; if (card.isCreature() && ComputerUtilCard.isUselessCreature(decider, card)) {
return true;
}
if (card.isEquipped()) {
return false;
}
if (card.isEnchanted()) {
for (Card enc : card.getEnchantedBy()) {
if (enc.getOwner().isOpponentOf(decider)) {
return true;
}
} }
if (card.isCreature() && ComputerUtilCard.isUselessCreature(decider, card)) { return false;
return true; }
} if (card.hasCounters()) {
if (card.isEquipped()) { if (card.isPlaneswalker()) {
return false; int maxLoyaltyToConsider = 2;
} int loyaltyDiff = 2;
if (card.isEnchanted()) { int chance = 30;
for (Card enc : card.getEnchantedBy()) { if (decider.getController().isAI()) {
if (enc.getOwner().isOpponentOf(decider)) { AiController aic = ((PlayerControllerAi) decider.getController()).getAi();
maxLoyaltyToConsider = aic.getIntProperty(AiProps.BLINK_RELOAD_PLANESWALKER_MAX_LOYALTY);
loyaltyDiff = aic.getIntProperty(AiProps.BLINK_RELOAD_PLANESWALKER_LOYALTY_DIFF);
chance = aic.getIntProperty(AiProps.BLINK_RELOAD_PLANESWALKER_CHANCE);
}
if (MyRandom.percentTrue(chance)) {
int curLoyalty = card.getCounters(CounterEnumType.LOYALTY);
int freshLoyalty = Integer.parseInt(card.getCurrentState().getBaseLoyalty());
if (freshLoyalty - curLoyalty >= loyaltyDiff && curLoyalty <= maxLoyaltyToConsider) {
return true; return true;
} }
} }
return false; } else if (card.isCreature() && card.getCounters(CounterEnumType.M1M1) > 0) {
return true;
} }
if (card.hasCounters()) { return false; // TODO: improve for other counters
if (card.isPlaneswalker()) { } else if (card.isAura()) {
int maxLoyaltyToConsider = 2; return false;
int loyaltyDiff = 2;
int chance = 30;
if (decider.getController().isAI()) {
AiController aic = ((PlayerControllerAi) decider.getController()).getAi();
maxLoyaltyToConsider = aic.getIntProperty(AiProps.BLINK_RELOAD_PLANESWALKER_MAX_LOYALTY);
loyaltyDiff = aic.getIntProperty(AiProps.BLINK_RELOAD_PLANESWALKER_LOYALTY_DIFF);
chance = aic.getIntProperty(AiProps.BLINK_RELOAD_PLANESWALKER_CHANCE);
}
if (MyRandom.percentTrue(chance)) {
int curLoyalty = card.getCounters(CounterEnumType.LOYALTY);
int freshLoyalty = Integer.parseInt(card.getCurrentState().getBaseLoyalty());
if (freshLoyalty - curLoyalty >= loyaltyDiff && curLoyalty <= maxLoyaltyToConsider) {
return true;
}
}
} else if (card.isCreature() && card.getCounters(CounterEnumType.M1M1) > 0) {
return true;
}
return false; // TODO: improve for other counters
} else if (card.isAura()) {
return false;
}
return true;
} }
return true;
}); });
return fetchList; return fetchList;
@@ -1866,14 +1826,11 @@ public class ChangeZoneAi extends SpellAbilityAi {
listGoal = CardLists.getValidCards(listGoal, curGoal + (curGoal.contains(".") ? "+" : ".") + "cmcGE" + goalCMC, source.getController(), source, sa); listGoal = CardLists.getValidCards(listGoal, curGoal + (curGoal.contains(".") ? "+" : ".") + "cmcGE" + goalCMC, source.getController(), source, sa);
} }
listGoal = CardLists.filter(listGoal, new Predicate<Card>() { listGoal = CardLists.filter(listGoal, c -> {
@Override if (c.getType().isLegendary()) {
public boolean apply(final Card c) { return !ai.isCardInPlay(c.getName());
if (c.getType().isLegendary()) {
return !ai.isCardInPlay(c.getName());
}
return true;
} }
return true;
}); });
if (!listGoal.isEmpty()) { if (!listGoal.isEmpty()) {
@@ -2014,17 +1971,14 @@ public class ChangeZoneAi extends SpellAbilityAi {
exiledBy.add(exiled); exiledBy.add(exiled);
} }
} }
scanList = CardLists.filter(scanList, new Predicate<Card>() { scanList = CardLists.filter(scanList, card -> {
@Override if (exiledBy.isEmpty()) {
public boolean apply(Card card) {
if (exiledBy.isEmpty()) {
return true;
}
for (Card c : exiledBy) {
return !c.getType().sharesCardTypeWith(card.getType());
}
return true; return true;
} }
for (Card c : exiledBy) {
return !c.getType().sharesCardTypeWith(card.getType());
}
return true;
}); });
} }
@@ -2061,12 +2015,7 @@ public class ChangeZoneAi extends SpellAbilityAi {
} }
final CardType.CoreType determinedMaxType = maxType; final CardType.CoreType determinedMaxType = maxType;
CardCollection preferredList = CardLists.filter(fetchList, new Predicate<Card>() { CardCollection preferredList = CardLists.filter(fetchList, card -> card.getType().hasType(determinedMaxType));
@Override
public boolean apply(Card card) {
return card.getType().hasType(determinedMaxType);
}
});
CardCollection preferredOppList = CardLists.filter(preferredList, CardPredicates.isControlledByAnyOf(aiPlayer.getOpponents())); CardCollection preferredOppList = CardLists.filter(preferredList, CardPredicates.isControlledByAnyOf(aiPlayer.getOpponents()));
if (!preferredOppList.isEmpty()) { if (!preferredOppList.isEmpty()) {
@@ -2078,22 +2027,19 @@ public class ChangeZoneAi extends SpellAbilityAi {
return Aggregates.random(fetchList); return Aggregates.random(fetchList);
} }
CardCollection preferredList = CardLists.filter(fetchList, new Predicate<Card>() { CardCollection preferredList = CardLists.filter(fetchList, card -> {
@Override boolean playerPref = true;
public boolean apply(Card card) { if (isCurse) {
boolean playerPref = true; playerPref = card.getController().isOpponentOf(aiPlayer);
if (isCurse) { } else if (isOwnOnly) {
playerPref = card.getController().isOpponentOf(aiPlayer); playerPref = card.getController().equals(aiPlayer) || !card.getController().isOpponentOf(aiPlayer);
} else if (isOwnOnly) {
playerPref = card.getController().equals(aiPlayer) || !card.getController().isOpponentOf(aiPlayer);
}
if (!playerPref) {
return false;
}
return card.isValid(valid, aiPlayer, host, sa); // for things like ExilePreference:Land.Basic
} }
if (!playerPref) {
return false;
}
return card.isValid(valid, aiPlayer, host, sa); // for things like ExilePreference:Land.Basic
}); });
if (!preferredList.isEmpty()) { if (!preferredList.isEmpty()) {

View File

@@ -4,7 +4,6 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@@ -85,15 +84,12 @@ public class ChooseCardAi extends SpellAbilityAi {
return false; return false;
} }
final Combat combat = game.getCombat(); final Combat combat = game.getCombat();
choices = CardLists.filter(choices, new Predicate<Card>() { choices = CardLists.filter(choices, c -> {
@Override if (!combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
public boolean apply(final Card c) { return false;
if (!combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
return false;
}
int ref = ComputerUtilAbility.getAbilitySourceName(sa).equals("Forcefield") ? 1 : 0;
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > ref;
} }
int ref = ComputerUtilAbility.getAbilitySourceName(sa).equals("Forcefield") ? 1 : 0;
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > ref;
}); });
return !choices.isEmpty(); return !choices.isEmpty();
} else if (aiLogic.equals("Ashiok")) { } else if (aiLogic.equals("Ashiok")) {
@@ -201,15 +197,12 @@ public class ChooseCardAi extends SpellAbilityAi {
} else if (logic.equals("NeedsPrevention")) { } else if (logic.equals("NeedsPrevention")) {
final Game game = ai.getGame(); final Game game = ai.getGame();
final Combat combat = game.getCombat(); final Combat combat = game.getCombat();
CardCollectionView better = CardLists.filter(options, new Predicate<Card>() { CardCollectionView better = CardLists.filter(options, c -> {
@Override if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
public boolean apply(final Card c) { return false;
if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
return false;
}
int ref = ComputerUtilAbility.getAbilitySourceName(sa).equals("Forcefield") ? 1 : 0;
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > ref;
} }
int ref = ComputerUtilAbility.getAbilitySourceName(sa).equals("Forcefield") ? 1 : 0;
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > ref;
}); });
if (!better.isEmpty()) { if (!better.isEmpty()) {
choice = ComputerUtilCard.getBestAI(better); choice = ComputerUtilCard.getBestAI(better);
@@ -235,26 +228,23 @@ public class ChooseCardAi extends SpellAbilityAi {
choice = creats.get(0); choice = creats.get(0);
} }
} else if ("NegativePowerFirst".equals(logic)) { } else if ("NegativePowerFirst".equals(logic)) {
Card lowest = Aggregates.itemWithMin(options, CardPredicates.Accessors.fnGetNetPower); Card lowest = Aggregates.itemWithMin(options, Card::getNetPower);
if (lowest.getNetPower() <= 0) { if (lowest.getNetPower() <= 0) {
choice = lowest; choice = lowest;
} else { } else {
choice = ComputerUtilCard.getBestCreatureAI(options); choice = ComputerUtilCard.getBestCreatureAI(options);
} }
} else if ("TangleWire".equals(logic)) { } else if ("TangleWire".equals(logic)) {
CardCollectionView betterList = CardLists.filter(options, new Predicate<Card>() { CardCollectionView betterList = CardLists.filter(options, c -> {
@Override if (c.isCreature()) {
public boolean apply(final Card c) { return false;
if (c.isCreature()) { }
for (SpellAbility sa1 : c.getAllSpellAbilities()) {
if (sa1.getPayCosts().hasTapCost()) {
return false; return false;
} }
for (SpellAbility sa : c.getAllSpellAbilities()) {
if (sa.getPayCosts().hasTapCost()) {
return false;
}
}
return true;
} }
return true;
}); });
if (!betterList.isEmpty()) { if (!betterList.isEmpty()) {
choice = betterList.get(0); choice = betterList.get(0);

View File

@@ -3,9 +3,9 @@ package forge.ai.ability;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.Direction; import forge.game.Direction;
import forge.game.Game; import forge.game.Game;
import forge.game.card.Card;
import forge.game.card.CardCollection; import forge.game.card.CardCollection;
import forge.game.card.CardLists; import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.CardPredicates.Presets; import forge.game.card.CardPredicates.Presets;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -28,11 +28,11 @@ public class ChooseDirectionAi extends SpellAbilityAi {
CardCollection all = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.NONLAND_PERMANENTS); CardCollection all = CardLists.filter(game.getCardsIn(ZoneType.Battlefield), Presets.NONLAND_PERMANENTS);
CardCollection aiPermanent = CardLists.filterControlledBy(all, ai); CardCollection aiPermanent = CardLists.filterControlledBy(all, ai);
aiPermanent.remove(sa.getHostCard()); aiPermanent.remove(sa.getHostCard());
int aiValue = Aggregates.sum(aiPermanent, CardPredicates.Accessors.fnGetCmc); int aiValue = Aggregates.sum(aiPermanent, Card::getCMC);
CardCollection left = CardLists.filterControlledBy(all, game.getNextPlayerAfter(ai, Direction.Left)); CardCollection left = CardLists.filterControlledBy(all, game.getNextPlayerAfter(ai, Direction.Left));
CardCollection right = CardLists.filterControlledBy(all, game.getNextPlayerAfter(ai, Direction.Right)); CardCollection right = CardLists.filterControlledBy(all, game.getNextPlayerAfter(ai, Direction.Right));
int leftValue = Aggregates.sum(left, CardPredicates.Accessors.fnGetCmc); int leftValue = Aggregates.sum(left, Card::getCMC);
int rightValue = Aggregates.sum(right, CardPredicates.Accessors.fnGetCmc); int rightValue = Aggregates.sum(right, Card::getCMC);
return aiValue <= leftValue && aiValue <= rightValue; return aiValue <= leftValue && aiValue <= rightValue;
} }
} }

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.ai.*; import forge.ai.*;
@@ -85,12 +84,7 @@ public class ChooseGenericAi extends SpellAbilityAi {
} else if ("Random".equals(logic)) { } else if ("Random".equals(logic)) {
return Aggregates.random(spells); return Aggregates.random(spells);
} else if ("Phasing".equals(logic)) { // Teferi's Realm : keep aggressive } else if ("Phasing".equals(logic)) { // Teferi's Realm : keep aggressive
List<SpellAbility> filtered = Lists.newArrayList(Iterables.filter(spells, new Predicate<SpellAbility>() { List<SpellAbility> filtered = Lists.newArrayList(Iterables.filter(spells, sp -> !sp.getDescription().contains("Creature") && !sp.getDescription().contains("Land")));
@Override
public boolean apply(final SpellAbility sp) {
return !sp.getDescription().contains("Creature") && !sp.getDescription().contains("Land");
}
}));
return Aggregates.random(filtered); return Aggregates.random(filtered);
} else if ("PayUnlessCost".equals(logic)) { } else if ("PayUnlessCost".equals(logic)) {
for (final SpellAbility sp : spells) { for (final SpellAbility sp : spells) {

View File

@@ -3,7 +3,6 @@ package forge.ai.ability;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@@ -94,14 +93,11 @@ public class ChooseSourceAi extends SpellAbilityAi {
choices = CardLists.getValidCards(choices, sa.getParam("Choices"), host.getController(), host, sa); choices = CardLists.getValidCards(choices, sa.getParam("Choices"), host.getController(), host, sa);
} }
final Combat combat = game.getCombat(); final Combat combat = game.getCombat();
choices = CardLists.filter(choices, new Predicate<Card>() { choices = CardLists.filter(choices, c -> {
@Override if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
public boolean apply(final Card c) { return false;
if (combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
return false;
}
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > 0;
} }
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > 0;
}); });
return !choices.isEmpty(); return !choices.isEmpty();
} }
@@ -124,15 +120,12 @@ public class ChooseSourceAi extends SpellAbilityAi {
final Combat combat = game.getCombat(); final Combat combat = game.getCombat();
List<Card> permanentSources = CardLists.filter(options, new Predicate<Card>() { List<Card> permanentSources = CardLists.filter(options, c -> {
@Override if (c == null || c.getZone() == null || c.getZone().getZoneType() != ZoneType.Battlefield
public boolean apply(final Card c) { || combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
if (c == null || c.getZone() == null || c.getZone().getZoneType() != ZoneType.Battlefield return false;
|| combat == null || !combat.isAttacking(c, ai) || !combat.isUnblocked(c)) {
return false;
}
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > 0;
} }
return ComputerUtilCombat.damageIfUnblocked(c, ai, combat, true) > 0;
}); });
// Try to choose the best creature for damage prevention. // Try to choose the best creature for damage prevention.

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
@@ -32,12 +31,7 @@ public class ControlExchangeAi extends SpellAbilityAi {
CardCollection list = CardCollection list =
CardLists.getValidCards(ai.getOpponents().getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa); CardLists.getValidCards(ai.getOpponents().getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa);
// AI won't try to grab cards that are filtered out of AI decks on purpose // AI won't try to grab cards that are filtered out of AI decks on purpose
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> !ComputerUtilCard.isCardRemAIDeck(c) && c.canBeTargetedBy(sa));
@Override
public boolean apply(final Card c) {
return !ComputerUtilCard.isCardRemAIDeck(c) && c.canBeTargetedBy(sa);
}
});
object1 = ComputerUtilCard.getBestAI(list); object1 = ComputerUtilCard.getBestAI(list);
if (sa.hasParam("Defined")) { if (sa.hasParam("Defined")) {
object2 = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa).get(0); object2 = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa).get(0);

View File

@@ -20,7 +20,6 @@ package forge.ai.ability;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -132,46 +131,43 @@ public class ControlGainAi extends SpellAbilityAi {
} }
// AI won't try to grab cards that are filtered out of AI decks on purpose // AI won't try to grab cards that are filtered out of AI decks on purpose
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> {
@Override if (!sa.canTarget(c)) {
public boolean apply(final Card c) { return false;
if (!sa.canTarget(c)) {
return false;
}
if (sa.isTrigger()) {
return true;
}
if (!c.canBeControlledBy(ai)) {
return false;
}
// do not take perm control on something that leaves the play end of turn
if (!lose.contains("EOT") && c.hasSVar("EndOfTurnLeavePlay")) {
return false;
}
if (c.isCreature()) {
if (c.getNetCombatDamage() <= 0) {
return false;
}
// can not attack any opponent
boolean found = false;
for (final Player opp : opponents) {
if (ComputerUtilCombat.canAttackNextTurn(c, opp)) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
// do not take control on something it doesn't know how to use
return !ComputerUtilCard.isCardRemAIDeck(c);
} }
if (sa.isTrigger()) {
return true;
}
if (!c.canBeControlledBy(ai)) {
return false;
}
// do not take perm control on something that leaves the play end of turn
if (!lose.contains("EOT") && c.hasSVar("EndOfTurnLeavePlay")) {
return false;
}
if (c.isCreature()) {
if (c.getNetCombatDamage() <= 0) {
return false;
}
// can not attack any opponent
boolean found = false;
for (final Player opp : opponents) {
if (ComputerUtilCombat.canAttackNextTurn(c, opp)) {
found = true;
break;
}
}
if (!found) {
return false;
}
}
// do not take control on something it doesn't know how to use
return !ComputerUtilCard.isCardRemAIDeck(c);
}); });
if (list.isEmpty()) { if (list.isEmpty()) {

View File

@@ -20,7 +20,6 @@ package forge.ai.ability;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@@ -49,12 +48,7 @@ public class ControlGainVariantAi extends SpellAbilityAi {
String logic = sa.getParam("AILogic"); String logic = sa.getParam("AILogic");
if ("GainControlOwns".equals(logic)) { if ("GainControlOwns".equals(logic)) {
List<Card> list = CardLists.filter(ai.getGame().getCardsIn(ZoneType.Battlefield), new Predicate<Card>() { List<Card> list = CardLists.filter(ai.getGame().getCardsIn(ZoneType.Battlefield), crd -> crd.isCreature() && !crd.getController().equals(crd.getOwner()));
@Override
public boolean apply(final Card crd) {
return crd.isCreature() && !crd.getController().equals(crd.getOwner());
}
});
if (list.isEmpty()) { if (list.isEmpty()) {
return false; return false;
} }

View File

@@ -4,7 +4,6 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
@@ -176,12 +175,7 @@ public class CopyPermanentAi extends SpellAbilityAi {
} }
} }
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> (!c.getType().isLegendary() || canCopyLegendary) || !c.getController().equals(aiPlayer));
@Override
public boolean apply(final Card c) {
return (!c.getType().isLegendary() || canCopyLegendary) || !c.getController().equals(aiPlayer);
}
});
Card choice; Card choice;
if (Iterables.any(list, Presets.CREATURES)) { if (Iterables.any(list, Presets.CREATURES)) {
if (sa.hasParam("TargetingPlayer")) { if (sa.hasParam("TargetingPlayer")) {

View File

@@ -19,7 +19,6 @@ package forge.ai.ability;
import java.util.List; import java.util.List;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
@@ -109,12 +108,7 @@ public abstract class CountersAi extends SpellAbilityAi {
choice = ComputerUtilCard.getBestLandToAnimate(list); choice = ComputerUtilCard.getBestLandToAnimate(list);
} }
} else if (type.equals("DIVINITY")) { } else if (type.equals("DIVINITY")) {
final CardCollection boon = CardLists.filter(list, new Predicate<Card>() { final CardCollection boon = CardLists.filter(list, c -> c.getCounters(CounterEnumType.DIVINITY) == 0);
@Override
public boolean apply(final Card c) {
return c.getCounters(CounterEnumType.DIVINITY) == 0;
}
});
choice = ComputerUtilCard.getMostExpensivePermanentAI(boon); choice = ComputerUtilCard.getMostExpensivePermanentAI(boon);
} else if (CounterType.get(type).isKeywordCounter()) { } else if (CounterType.get(type).isKeywordCounter()) {
choice = ComputerUtilCard.getBestCreatureAI(CardLists.getNotKeyword(list, type)); choice = ComputerUtilCard.getBestCreatureAI(CardLists.getNotKeyword(list, type));

View File

@@ -3,7 +3,6 @@ package forge.ai.ability;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.ai.ComputerUtil; import forge.ai.ComputerUtil;
@@ -261,26 +260,21 @@ public class CountersMoveAi extends SpellAbilityAi {
// prefered logic for this: try to steal counter // prefered logic for this: try to steal counter
List<Card> oppList = CardLists.filterControlledBy(tgtCards, ai.getOpponents()); List<Card> oppList = CardLists.filterControlledBy(tgtCards, ai.getOpponents());
if (!oppList.isEmpty()) { if (!oppList.isEmpty()) {
List<Card> best = CardLists.filter(oppList, new Predicate<Card>() { List<Card> best = CardLists.filter(oppList, card -> {
// do not weak a useless creature if able
@Override if (ComputerUtilCard.isUselessCreature(ai, card)) {
public boolean apply(Card card) { return false;
// do not weak a useless creature if able
if (ComputerUtilCard.isUselessCreature(ai, card)) {
return false;
}
final Card srcCardCpy = CardCopyService.getLKICopy(card);
// cant use substract on Copy
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
// do not steal a P1P1 from Undying if it would die this way
if (cType != null && cType.is(CounterEnumType.P1P1) && srcCardCpy.getNetToughness() <= 0) {
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING) || card.isToken();
}
return true;
} }
final Card srcCardCpy = CardCopyService.getLKICopy(card);
// cant use substract on Copy
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
// do not steal a P1P1 from Undying if it would die this way
if (cType != null && cType.is(CounterEnumType.P1P1) && srcCardCpy.getNetToughness() <= 0) {
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING) || card.isToken();
}
return true;
}); });
// if no Prefered found, try normal list // if no Prefered found, try normal list
@@ -303,33 +297,29 @@ public class CountersMoveAi extends SpellAbilityAi {
List<Card> aiList = CardLists.filterControlledBy(tgtCards, ally); List<Card> aiList = CardLists.filterControlledBy(tgtCards, ally);
if (!aiList.isEmpty()) { if (!aiList.isEmpty()) {
List<Card> best = CardLists.filter(aiList, new Predicate<Card>() { List<Card> best = CardLists.filter(aiList, card -> {
// gain from useless
@Override if (ComputerUtilCard.isUselessCreature(ai, card)) {
public boolean apply(Card card) { return true;
// gain from useless
if (ComputerUtilCard.isUselessCreature(ai, card)) {
return true;
}
// source would leave the game
if (card.hasSVar("EndOfTurnLeavePlay")) {
return true;
}
// try to remove P1P1 from undying or evolve
if (cType != null && cType.is(CounterEnumType.P1P1)) {
if (card.hasKeyword(Keyword.UNDYING) || card.hasKeyword(Keyword.EVOLVE)
|| card.hasKeyword(Keyword.ADAPT)) {
return true;
}
}
if (cType != null && cType.is(CounterEnumType.M1M1) && card.hasKeyword(Keyword.PERSIST)) {
return true;
}
return false;
} }
// source would leave the game
if (card.hasSVar("EndOfTurnLeavePlay")) {
return true;
}
// try to remove P1P1 from undying or evolve
if (cType != null && cType.is(CounterEnumType.P1P1)) {
if (card.hasKeyword(Keyword.UNDYING) || card.hasKeyword(Keyword.EVOLVE)
|| card.hasKeyword(Keyword.ADAPT)) {
return true;
}
}
if (cType != null && cType.is(CounterEnumType.M1M1) && card.hasKeyword(Keyword.PERSIST)) {
return true;
}
return false;
}); });
if (best.isEmpty()) { if (best.isEmpty()) {
@@ -379,34 +369,30 @@ public class CountersMoveAi extends SpellAbilityAi {
if (ComputerUtilCard.evaluateCreature(lkiWithCounters) > ComputerUtilCard.evaluateCreature(lkiWithoutCounters)) { if (ComputerUtilCard.evaluateCreature(lkiWithCounters) > ComputerUtilCard.evaluateCreature(lkiWithoutCounters)) {
List<Card> aiList = CardLists.filterControlledBy(tgtCards, ai); List<Card> aiList = CardLists.filterControlledBy(tgtCards, ai);
if (!aiList.isEmpty()) { if (!aiList.isEmpty()) {
List<Card> best = CardLists.filter(aiList, new Predicate<Card>() { List<Card> best = CardLists.filter(aiList, card -> {
// gain from useless
@Override if (ComputerUtilCard.isUselessCreature(ai, card)) {
public boolean apply(Card card) { return false;
// gain from useless
if (ComputerUtilCard.isUselessCreature(ai, card)) {
return false;
}
// source would leave the game
if (card.hasSVar("EndOfTurnLeavePlay")) {
return false;
}
if (cType != null) {
if (cType.is(CounterEnumType.P1P1) && card.hasKeyword(Keyword.UNDYING)) {
return false;
}
if (cType.is(CounterEnumType.M1M1)) {
return false;
}
if (!card.canReceiveCounters(cType)) {
return false;
}
}
return true;
} }
// source would leave the game
if (card.hasSVar("EndOfTurnLeavePlay")) {
return false;
}
if (cType != null) {
if (cType.is(CounterEnumType.P1P1) && card.hasKeyword(Keyword.UNDYING)) {
return false;
}
if (cType.is(CounterEnumType.M1M1)) {
return false;
}
if (!card.canReceiveCounters(cType)) {
return false;
}
}
return true;
}); });
if (best.isEmpty()) { if (best.isEmpty()) {
@@ -432,22 +418,18 @@ public class CountersMoveAi extends SpellAbilityAi {
// try to move to something useless or something that would leave play // try to move to something useless or something that would leave play
List<Card> oppList = CardLists.filterControlledBy(tgtCards, ai.getOpponents()); List<Card> oppList = CardLists.filterControlledBy(tgtCards, ai.getOpponents());
if (!oppList.isEmpty()) { if (!oppList.isEmpty()) {
List<Card> best = CardLists.filter(oppList, new Predicate<Card>() { List<Card> best = CardLists.filter(oppList, card -> {
// gain from useless
@Override if (!ComputerUtilCard.isUselessCreature(ai, card)) {
public boolean apply(Card card) { return true;
// gain from useless
if (!ComputerUtilCard.isUselessCreature(ai, card)) {
return true;
}
// source would leave the game
if (!card.hasSVar("EndOfTurnLeavePlay")) {
return true;
}
return false;
} }
// source would leave the game
if (!card.hasSVar("EndOfTurnLeavePlay")) {
return true;
}
return false;
}); });
if (best.isEmpty()) { if (best.isEmpty()) {

View File

@@ -4,7 +4,6 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -37,33 +36,28 @@ public class CountersMultiplyAi extends SpellAbilityAi {
// defined are mostly Self or Creatures you control // defined are mostly Self or Creatures you control
CardCollection list = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa); CardCollection list = AbilityUtils.getDefinedCards(sa.getHostCard(), sa.getParam("Defined"), sa);
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> {
if (!c.hasCounters()) {
@Override return false;
public boolean apply(Card c) {
if (!c.hasCounters()) {
return false;
}
if (counterType != null) {
if (c.getCounters(counterType) <= 0) {
return false;
}
if (!c.canReceiveCounters(counterType)) {
return false;
}
} else {
for (Map.Entry<CounterType, Integer> e : c.getCounters().entrySet()) {
// has negative counter it would double
if (ComputerUtil.isNegativeCounter(e.getKey(), c)) {
return false;
}
}
}
return true;
} }
if (counterType != null) {
if (c.getCounters(counterType) <= 0) {
return false;
}
if (!c.canReceiveCounters(counterType)) {
return false;
}
} else {
for (Map.Entry<CounterType, Integer> e : c.getCounters().entrySet()) {
// has negative counter it would double
if (ComputerUtil.isNegativeCounter(e.getKey(), c)) {
return false;
}
}
}
return true;
}); });
if (list.isEmpty()) { if (list.isEmpty()) {
@@ -137,26 +131,21 @@ public class CountersMultiplyAi extends SpellAbilityAi {
CardCollection list = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa); CardCollection list = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
// pre filter targetable cards with counters and can receive one of them // pre filter targetable cards with counters and can receive one of them
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> {
if (!c.hasCounters()) {
@Override return false;
public boolean apply(Card c) {
if (!c.hasCounters()) {
return false;
}
if (counterType != null) {
if (c.getCounters(counterType) <= 0) {
return false;
}
if (!c.canReceiveCounters(counterType)) {
return false;
}
}
return true;
} }
if (counterType != null) {
if (c.getCounters(counterType) <= 0) {
return false;
}
if (!c.canReceiveCounters(counterType)) {
return false;
}
}
return true;
}); });
CardCollection aiList = CardLists.filterControlledBy(list, ai); CardCollection aiList = CardLists.filterControlledBy(list, ai);

View File

@@ -4,7 +4,6 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -31,25 +30,22 @@ public class CountersProliferateAi extends SpellAbilityAi {
if (p.getCounters(CounterEnumType.EXPERIENCE) + p.getCounters(CounterEnumType.ENERGY) >= 1) { if (p.getCounters(CounterEnumType.EXPERIENCE) + p.getCounters(CounterEnumType.ENERGY) >= 1) {
allyExpOrEnergy = true; allyExpOrEnergy = true;
} }
cperms.addAll(CardLists.filter(p.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() { cperms.addAll(CardLists.filter(p.getCardsIn(ZoneType.Battlefield), crd -> {
@Override if (!crd.hasCounters()) {
public boolean apply(final Card crd) {
if (!crd.hasCounters()) {
return false;
}
if (crd.isPlaneswalker()) {
return true;
}
// iterate only over existing counters
for (final Map.Entry<CounterType, Integer> e : crd.getCounters().entrySet()) {
if (e.getValue() >= 1 && !ComputerUtil.isNegativeCounter(e.getKey(), crd)) {
return true;
}
}
return false; return false;
} }
if (crd.isPlaneswalker()) {
return true;
}
// iterate only over existing counters
for (final Map.Entry<CounterType, Integer> e : crd.getCounters().entrySet()) {
if (e.getValue() >= 1 && !ComputerUtil.isNegativeCounter(e.getKey(), crd)) {
return true;
}
}
return false;
})); }));
} }
@@ -58,25 +54,22 @@ public class CountersProliferateAi extends SpellAbilityAi {
for (final Player o : ai.getOpponents()) { for (final Player o : ai.getOpponents()) {
opponentPoison |= o.getPoisonCounters() > 0 && o.canReceiveCounters(CounterEnumType.POISON); opponentPoison |= o.getPoisonCounters() > 0 && o.canReceiveCounters(CounterEnumType.POISON);
hperms.addAll(CardLists.filter(o.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() { hperms.addAll(CardLists.filter(o.getCardsIn(ZoneType.Battlefield), crd -> {
@Override if (!crd.hasCounters()) {
public boolean apply(final Card crd) {
if (!crd.hasCounters()) {
return false;
}
if (crd.isPlaneswalker()) {
return false;
}
// iterate only over existing counters
for (final Map.Entry<CounterType, Integer> e : crd.getCounters().entrySet()) {
if (e.getValue() >= 1 && ComputerUtil.isNegativeCounter(e.getKey(), crd)) {
return true;
}
}
return false; return false;
} }
if (crd.isPlaneswalker()) {
return false;
}
// iterate only over existing counters
for (final Map.Entry<CounterType, Integer> e : crd.getCounters().entrySet()) {
if (e.getValue() >= 1 && ComputerUtil.isNegativeCounter(e.getKey(), crd)) {
return true;
}
}
return false;
})); }));
} }

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -171,13 +170,7 @@ public class CountersPutAi extends CountersAi {
CardCollection oppCreatM1 = CardLists.filter(oppCreat, CardPredicates.hasCounter(CounterEnumType.M1M1)); CardCollection oppCreatM1 = CardLists.filter(oppCreat, CardPredicates.hasCounter(CounterEnumType.M1M1));
oppCreatM1 = CardLists.getNotKeyword(oppCreatM1, Keyword.UNDYING); oppCreatM1 = CardLists.getNotKeyword(oppCreatM1, Keyword.UNDYING);
oppCreatM1 = CardLists.filter(oppCreatM1, new Predicate<Card>() { oppCreatM1 = CardLists.filter(oppCreatM1, input -> input.getNetToughness() <= 1 && input.canReceiveCounters(CounterType.get(CounterEnumType.M1M1)));
@Override
public boolean apply(Card input) {
return input.getNetToughness() <= 1 && input.canReceiveCounters(CounterType.get(CounterEnumType.M1M1));
}
});
Card best = ComputerUtilCard.getBestAI(oppCreatM1); Card best = ComputerUtilCard.getBestAI(oppCreatM1);
if (best != null) { if (best != null) {
@@ -188,17 +181,14 @@ public class CountersPutAi extends CountersAi {
CardCollection aiCreat = CardLists.getTargetableCards(ai.getCreaturesInPlay(), sa); CardCollection aiCreat = CardLists.getTargetableCards(ai.getCreaturesInPlay(), sa);
aiCreat = CardLists.filter(aiCreat, CardPredicates.hasCounters()); aiCreat = CardLists.filter(aiCreat, CardPredicates.hasCounters());
aiCreat = CardLists.filter(aiCreat, new Predicate<Card>() { aiCreat = CardLists.filter(aiCreat, input -> {
@Override for (CounterType counterType : input.getCounters().keySet()) {
public boolean apply(Card input) { if (!ComputerUtil.isNegativeCounter(counterType, input)
for (CounterType counterType : input.getCounters().keySet()) { && input.canReceiveCounters(counterType)) {
if (!ComputerUtil.isNegativeCounter(counterType, input) return true;
&& input.canReceiveCounters(counterType)) {
return true;
}
} }
return false;
} }
return false;
}); });
// TODO check whole state which creature would be the best // TODO check whole state which creature would be the best
@@ -251,8 +241,8 @@ public class CountersPutAi extends CountersAi {
} else if (ai.getGame().getCombat().isBlocking(source)) { } else if (ai.getGame().getCombat().isBlocking(source)) {
// when blocking, consider this if it's possible to save the blocker and/or kill at least one attacker // when blocking, consider this if it's possible to save the blocker and/or kill at least one attacker
CardCollection blocked = ai.getGame().getCombat().getAttackersBlockedBy(source); CardCollection blocked = ai.getGame().getCombat().getAttackersBlockedBy(source);
int totBlkPower = Aggregates.sum(blocked, CardPredicates.Accessors.fnGetNetPower); int totBlkPower = Aggregates.sum(blocked, Card::getNetPower);
int totBlkToughness = Aggregates.min(blocked, CardPredicates.Accessors.fnGetNetToughness); int totBlkToughness = Aggregates.min(blocked, Card::getNetToughness);
int numActivations = ai.getCounters(CounterEnumType.ENERGY) / sa.getPayCosts().getCostEnergy().convertAmount(); int numActivations = ai.getCounters(CounterEnumType.ENERGY) / sa.getPayCosts().getCostEnergy().convertAmount();
if (source.getNetToughness() + numActivations > totBlkPower if (source.getNetToughness() + numActivations > totBlkPower
@@ -327,7 +317,7 @@ public class CountersPutAi extends CountersAi {
if (sa.hasParam("Bolster")) { if (sa.hasParam("Bolster")) {
CardCollection creatsYouCtrl = ai.getCreaturesInPlay(); CardCollection creatsYouCtrl = ai.getCreaturesInPlay();
List<Card> leastToughness = Aggregates.listWithMin(creatsYouCtrl, CardPredicates.Accessors.fnGetNetToughness); List<Card> leastToughness = Aggregates.listWithMin(creatsYouCtrl, Card::getNetToughness);
if (leastToughness.isEmpty()) { if (leastToughness.isEmpty()) {
return false; return false;
} }
@@ -389,7 +379,7 @@ public class CountersPutAi extends CountersAi {
sa.setXManaCostPaid(amount); sa.setXManaCostPaid(amount);
} else if ("ExiledCreatureFromGraveCMC".equals(logic)) { } else if ("ExiledCreatureFromGraveCMC".equals(logic)) {
// e.g. Necropolis // e.g. Necropolis
amount = Aggregates.max(CardLists.filter(ai.getCardsIn(ZoneType.Graveyard), CardPredicates.Presets.CREATURES), CardPredicates.Accessors.fnGetCmc); amount = Aggregates.max(CardLists.filter(ai.getCardsIn(ZoneType.Graveyard), CardPredicates.Presets.CREATURES), Card::getCMC);
if (amount > 0 && ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN)) { if (amount > 0 && ai.getGame().getPhaseHandler().is(PhaseType.END_OF_TURN)) {
return true; return true;
} }
@@ -473,24 +463,21 @@ public class CountersPutAi extends CountersAi {
list = ComputerUtil.getSafeTargets(ai, sa, ai.getCardsIn(ZoneType.Battlefield)); list = ComputerUtil.getSafeTargets(ai, sa, ai.getCardsIn(ZoneType.Battlefield));
} }
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> {
@Override // don't put the counter on the dead creature
public boolean apply(final Card c) { if (sacSelf && c.equals(source)) {
// don't put the counter on the dead creature return false;
if (sacSelf && c.equals(source)) { } else if (hasSacCost && !ComputerUtil.shouldSacrificeThreatenedCard(ai, c, sa)) {
return false; return false;
} else if (hasSacCost && !ComputerUtil.shouldSacrificeThreatenedCard(ai, c, sa)) { }
return false; if ("NoCounterOfType".equals(sa.getParam("AILogic"))) {
} for (String ctrType : types) {
if ("NoCounterOfType".equals(sa.getParam("AILogic"))) { if (c.getCounters(CounterType.getType(ctrType)) > 0) {
for (String ctrType : types) { return false;
if (c.getCounters(CounterType.getType(ctrType)) > 0) {
return false;
}
} }
} }
return sa.canTarget(c) && c.canReceiveCounters(CounterType.getType(type));
} }
return sa.canTarget(c) && c.canReceiveCounters(CounterType.getType(type));
}); });
// Filter AI-specific targets if provided // Filter AI-specific targets if provided
@@ -928,13 +915,9 @@ public class CountersPutAi extends CountersAi {
final int tributeAmount = AbilityUtils.calculateAmount(source, amountStr, sa); final int tributeAmount = AbilityUtils.calculateAmount(source, amountStr, sa);
final boolean isHaste = source.hasKeyword(Keyword.HASTE); final boolean isHaste = source.hasKeyword(Keyword.HASTE);
List<Card> threatening = CardLists.filter(creats, new Predicate<Card>() { List<Card> threatening = CardLists.filter(creats, c -> CombatUtil.canBlock(source, c, !isHaste)
@Override && (c.getNetToughness() > source.getNetPower() + tributeAmount || c.hasKeyword(Keyword.DEATHTOUCH))
public boolean apply(Card c) { );
return CombatUtil.canBlock(source, c, !isHaste)
&& (c.getNetToughness() > source.getNetPower() + tributeAmount || c.hasKeyword(Keyword.DEATHTOUCH));
}
});
if (!threatening.isEmpty()) { if (!threatening.isEmpty()) {
return true; return true;
} }
@@ -946,12 +929,9 @@ public class CountersPutAi extends CountersAi {
return false; return false;
} else if (logic.equals("CanBlockThisTurn")) { } else if (logic.equals("CanBlockThisTurn")) {
// pump haste // pump haste
List<Card> canBlock = CardLists.filter(creats, new Predicate<Card>() { List<Card> canBlock = CardLists.filter(creats, c -> CombatUtil.canBlock(source, c)
@Override && (c.getNetToughness() > source.getNetPower() || c.hasKeyword(Keyword.DEATHTOUCH))
public boolean apply(Card c) { );
return CombatUtil.canBlock(source, c) && (c.getNetToughness() > source.getNetPower() || c.hasKeyword(Keyword.DEATHTOUCH));
}
});
if (!canBlock.isEmpty()) { if (!canBlock.isEmpty()) {
return false; return false;
} }
@@ -1003,22 +983,19 @@ public class CountersPutAi extends CountersAi {
final CardCollection opponents = CardLists.filterControlledBy(options, ai.getOpponents()); final CardCollection opponents = CardLists.filterControlledBy(options, ai.getOpponents());
if (!opponents.isEmpty()) { if (!opponents.isEmpty()) {
final CardCollection negative = CardLists.filter(opponents, new Predicate<Card>() { final CardCollection negative = CardLists.filter(opponents, input -> {
@Override if (input.hasSVar("EndOfTurnLeavePlay"))
public boolean apply(Card input) {
if (input.hasSVar("EndOfTurnLeavePlay"))
return false;
if (ComputerUtilCard.isUselessCreature(ai, input))
return false;
for (CounterType type : types) {
if (type.is(CounterEnumType.M1M1) && amount >= input.getNetToughness())
return true;
if (ComputerUtil.isNegativeCounter(type, input)) {
return true;
}
}
return false; return false;
if (ComputerUtilCard.isUselessCreature(ai, input))
return false;
for (CounterType type : types) {
if (type.is(CounterEnumType.M1M1) && amount >= input.getNetToughness())
return true;
if (ComputerUtil.isNegativeCounter(type, input)) {
return true;
}
} }
return false;
}); });
if (!negative.isEmpty()) { if (!negative.isEmpty()) {
return ComputerUtilCard.getBestAI(negative); return ComputerUtilCard.getBestAI(negative);
@@ -1053,13 +1030,10 @@ public class CountersPutAi extends CountersAi {
if (doNotHaveKeyword.size() > 0) if (doNotHaveKeyword.size() > 0)
filtered = doNotHaveKeyword; filtered = doNotHaveKeyword;
final CardCollection notUseless = CardLists.filter(filtered, new Predicate<Card>() { final CardCollection notUseless = CardLists.filter(filtered, input -> {
@Override if (input.hasSVar("EndOfTurnLeavePlay"))
public boolean apply(Card input) { return false;
if (input.hasSVar("EndOfTurnLeavePlay")) return !ComputerUtilCard.isUselessCreature(ai, input);
return false;
return !ComputerUtilCard.isUselessCreature(ai, input);
}
}); });
if (!notUseless.isEmpty()) { if (!notUseless.isEmpty()) {
@@ -1069,26 +1043,20 @@ public class CountersPutAi extends CountersAi {
// some special logic to reload Persist/Undying // some special logic to reload Persist/Undying
for (CounterType type : types) { for (CounterType type : types) {
if (p1p1.equals(type)) { if (p1p1.equals(type)) {
final CardCollection persist = CardLists.filter(filtered, new Predicate<Card>() { final CardCollection persist = CardLists.filter(filtered, input -> {
@Override if (!input.hasKeyword(Keyword.PERSIST))
public boolean apply(Card input) { return false;
if (!input.hasKeyword(Keyword.PERSIST)) return input.getCounters(m1m1) <= amount;
return false;
return input.getCounters(m1m1) <= amount;
}
}); });
if (!persist.isEmpty()) { if (!persist.isEmpty()) {
filtered = persist; filtered = persist;
} }
} else if (m1m1.equals(type)) { } else if (m1m1.equals(type)) {
final CardCollection undying = CardLists.filter(filtered, new Predicate<Card>() { final CardCollection undying = CardLists.filter(filtered, input -> {
@Override if (!input.hasKeyword(Keyword.UNDYING))
public boolean apply(Card input) { return false;
if (!input.hasKeyword(Keyword.UNDYING)) return input.getCounters(p1p1) <= amount && input.getNetToughness() > amount;
return false;
return input.getCounters(p1p1) <= amount && input.getNetToughness() > amount;
}
}); });
if (!undying.isEmpty()) { if (!undying.isEmpty()) {
@@ -1161,15 +1129,12 @@ public class CountersPutAi extends CountersAi {
CardCollection targets = CardLists.getTargetableCards(ai.getCreaturesInPlay(), sa); CardCollection targets = CardLists.getTargetableCards(ai.getCreaturesInPlay(), sa);
targets.remove(source); targets.remove(source);
targets = CardLists.filter(targets, new Predicate<Card>() { targets = CardLists.filter(targets, card -> {
@Override boolean tgtThreatened = ComputerUtil.predictThreatenedObjects(ai, null, true).contains(card)
public boolean apply(Card card) { || (combat != null && ((combat.isBlocked(card) && ComputerUtilCombat.attackerWouldBeDestroyed(ai, card, combat))
boolean tgtThreatened = ComputerUtil.predictThreatenedObjects(ai, null, true).contains(card) || (combat.isBlocking(card) && ComputerUtilCombat.blockerWouldBeDestroyed(ai, card, combat))));
|| (combat != null && ((combat.isBlocked(card) && ComputerUtilCombat.attackerWouldBeDestroyed(ai, card, combat)) // when threatened, any non-threatened target is good to preserve the counter
|| (combat.isBlocking(card) && ComputerUtilCombat.blockerWouldBeDestroyed(ai, card, combat)))); return !tgtThreatened && (threatened || ComputerUtilCard.evaluateCreature(card, false, false) > ComputerUtilCard.evaluateCreature(source, false, false) + creatDiff);
// when threatened, any non-threatened target is good to preserve the counter
return !tgtThreatened && (threatened || ComputerUtilCard.evaluateCreature(card, false, false) > ComputerUtilCard.evaluateCreature(source, false, false) + creatDiff);
}
}); });
Card bestTgt = ComputerUtilCard.getBestCreatureAI(targets); Card bestTgt = ComputerUtilCard.getBestCreatureAI(targets);
@@ -1194,7 +1159,7 @@ public class CountersPutAi extends CountersAi {
} }
} }
int totBlkPower = Aggregates.sum(combat.getBlockers(source), CardPredicates.Accessors.fnGetNetPower); int totBlkPower = Aggregates.sum(combat.getBlockers(source), Card::getNetPower);
if (source.getNetToughness() <= totBlkPower if (source.getNetToughness() <= totBlkPower
&& source.getNetToughness() + amount > totBlkPower) { && source.getNetToughness() + amount > totBlkPower) {
return true; return true;
@@ -1208,7 +1173,7 @@ public class CountersPutAi extends CountersAi {
} }
} }
int totAtkPower = Aggregates.sum(combat.getAttackersBlockedBy(source), CardPredicates.Accessors.fnGetNetPower); int totAtkPower = Aggregates.sum(combat.getAttackersBlockedBy(source), Card::getNetPower);
if (source.getNetToughness() <= totAtkPower if (source.getNetToughness() <= totAtkPower
&& source.getNetToughness() + amount > totAtkPower) { && source.getNetToughness() + amount > totAtkPower) {
return true; return true;
@@ -1229,7 +1194,7 @@ public class CountersPutAi extends CountersAi {
Card source = sa.getHostCard(); Card source = sa.getHostCard();
CardCollectionView ownLib = CardLists.filter(ai.getCardsIn(ZoneType.Library), CardPredicates.isType("Creature")); CardCollectionView ownLib = CardLists.filter(ai.getCardsIn(ZoneType.Library), CardPredicates.isType("Creature"));
int numCtrs = source.getCounters(CounterEnumType.CHARGE); int numCtrs = source.getCounters(CounterEnumType.CHARGE);
int maxCMC = Aggregates.max(ownLib, CardPredicates.Accessors.fnGetCmc); int maxCMC = Aggregates.max(ownLib, Card::getCMC);
int optimalCMC = 0; int optimalCMC = 0;
int curAmount = 0; int curAmount = 0;
// Assume the AI knows its deck list and realizes what it has left in its library. Could be improved to make this less cheat-y. // Assume the AI knows its deck list and realizes what it has left in its library. Could be improved to make this less cheat-y.
@@ -1247,7 +1212,7 @@ public class CountersPutAi extends CountersAi {
Card source = sa.getHostCard(); Card source = sa.getHostCard();
CardCollectionView oppInPlay = CardLists.filter(ai.getOpponents().getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.NONLAND_PERMANENTS); CardCollectionView oppInPlay = CardLists.filter(ai.getOpponents().getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.NONLAND_PERMANENTS);
int numCtrs = source.getCounters(CounterEnumType.CHARGE); int numCtrs = source.getCounters(CounterEnumType.CHARGE);
int maxCMC = Aggregates.max(oppInPlay, CardPredicates.Accessors.fnGetCmc); int maxCMC = Aggregates.max(oppInPlay, Card::getCMC);
int optimalCMC = 0; int optimalCMC = 0;
int curAmount = 0; int curAmount = 0;
for (int cmc = numCtrs; cmc <= maxCMC; cmc++) { for (int cmc = numCtrs; cmc <= maxCMC; cmc++) {

View File

@@ -3,7 +3,6 @@ package forge.ai.ability;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.ai.ComputerUtilCost; import forge.ai.ComputerUtilCost;
@@ -95,12 +94,7 @@ public class CountersPutAllAi extends SpellAbilityAi {
if (curse) { if (curse) {
if (type.equals("M1M1")) { if (type.equals("M1M1")) {
final List<Card> killable = CardLists.filter(hList, new Predicate<Card>() { final List<Card> killable = CardLists.filter(hList, c -> c.getNetToughness() <= amount);
@Override
public boolean apply(final Card c) {
return c.getNetToughness() <= amount;
}
});
if (!(killable.size() > 2)) { if (!(killable.size() > 2)) {
return false; return false;
} }

View File

@@ -107,12 +107,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
if (!countersList.isEmpty()) { if (!countersList.isEmpty()) {
CardCollectionView marit = ai.getCardsIn(ZoneType.Battlefield, "Marit Lage"); CardCollectionView marit = ai.getCardsIn(ZoneType.Battlefield, "Marit Lage");
boolean maritEmpty = marit.isEmpty() || Iterables.contains(marit, new Predicate<Card>() { boolean maritEmpty = marit.isEmpty() || Iterables.contains(marit, (Predicate<Card>) Card::ignoreLegendRule);
@Override
public boolean apply(Card input) {
return input.ignoreLegendRule();
}
});
if (maritEmpty) { if (maritEmpty) {
CardCollectionView depthsList = CardLists.filter(countersList, CardCollectionView depthsList = CardLists.filter(countersList,
CardPredicates.nameEquals("Dark Depths"), CardPredicates.hasCounter(CounterEnumType.ICE)); CardPredicates.nameEquals("Dark Depths"), CardPredicates.hasCounter(CounterEnumType.ICE));
@@ -237,12 +232,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
// this counters are treat first to be removed // this counters are treat first to be removed
if ("Dark Depths".equals(tgt.getName()) && options.contains(CounterType.get(CounterEnumType.ICE))) { if ("Dark Depths".equals(tgt.getName()) && options.contains(CounterType.get(CounterEnumType.ICE))) {
CardCollectionView marit = ai.getCardsIn(ZoneType.Battlefield, "Marit Lage"); CardCollectionView marit = ai.getCardsIn(ZoneType.Battlefield, "Marit Lage");
boolean maritEmpty = marit.isEmpty() || Iterables.contains(marit, new Predicate<Card>() { boolean maritEmpty = marit.isEmpty() || Iterables.contains(marit, (Predicate<Card>) Card::ignoreLegendRule);
@Override
public boolean apply(Card input) {
return input.ignoreLegendRule();
}
});
if (maritEmpty) { if (maritEmpty) {
return CounterType.get(CounterEnumType.ICE); return CounterType.get(CounterEnumType.ICE);
@@ -288,12 +278,7 @@ public class CountersPutOrRemoveAi extends SpellAbilityAi {
} else { } else {
if (type.is(CounterEnumType.ICE) && "Dark Depths".equals(tgt.getName())) { if (type.is(CounterEnumType.ICE) && "Dark Depths".equals(tgt.getName())) {
CardCollectionView marit = ai.getCardsIn(ZoneType.Battlefield, "Marit Lage"); CardCollectionView marit = ai.getCardsIn(ZoneType.Battlefield, "Marit Lage");
boolean maritEmpty = marit.isEmpty() || Iterables.contains(marit, new Predicate<Card>() { boolean maritEmpty = marit.isEmpty() || Iterables.contains(marit, (Predicate<Card>) Card::ignoreLegendRule);
@Override
public boolean apply(Card input) {
return input.ignoreLegendRule();
}
});
if (maritEmpty) { if (maritEmpty) {
return false; return false;

View File

@@ -118,12 +118,7 @@ public class CountersRemoveAi extends SpellAbilityAi {
list = ComputerUtil.filterAITgts(sa, ai, list, false); list = ComputerUtil.filterAITgts(sa, ai, list, false);
CardCollectionView marit = ai.getCardsIn(ZoneType.Battlefield, "Marit Lage"); CardCollectionView marit = ai.getCardsIn(ZoneType.Battlefield, "Marit Lage");
boolean maritEmpty = marit.isEmpty() || Iterables.contains(marit, new Predicate<Card>() { boolean maritEmpty = marit.isEmpty() || Iterables.contains(marit, (Predicate<Card>) Card::ignoreLegendRule);
@Override
public boolean apply(Card input) {
return input.ignoreLegendRule();
}
});
if (type.matches("All")) { if (type.matches("All")) {
// Logic Part for Vampire Hexmage // Logic Part for Vampire Hexmage

View File

@@ -253,12 +253,7 @@ public class DamageAllAi extends SpellAbilityAi {
CardCollection list = CardCollection list =
CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), validC, source.getController(), source, sa); CardLists.getValidCards(player.getCardsIn(ZoneType.Battlefield), validC, source.getController(), source, sa);
final Predicate<Card> filterKillable = new Predicate<Card>() { final Predicate<Card> filterKillable = c -> ComputerUtilCombat.predictDamageTo(c, dmg, source, false) >= ComputerUtilCombat.getDamageToKill(c, false);
@Override
public boolean apply(final Card c) {
return ComputerUtilCombat.predictDamageTo(c, dmg, source, false) >= ComputerUtilCombat.getDamageToKill(c, false);
}
};
list = CardLists.getNotKeyword(list, Keyword.INDESTRUCTIBLE); list = CardLists.getNotKeyword(list, Keyword.INDESTRUCTIBLE);
list = CardLists.filter(list, filterKillable); list = CardLists.filter(list, filterKillable);

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
@@ -171,7 +170,7 @@ public class DamageDealAi extends DamageAiBase {
} else if ("WildHunt".equals(logic)) { } else if ("WildHunt".equals(logic)) {
// This dummy ability will just deal 0 damage, but holds the logic for the AI for Master of Wild Hunt // This dummy ability will just deal 0 damage, but holds the logic for the AI for Master of Wild Hunt
List<Card> wolves = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), "Creature.Wolf+untapped+YouCtrl+Other", ai, source, sa); List<Card> wolves = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), "Creature.Wolf+untapped+YouCtrl+Other", ai, source, sa);
dmg = Aggregates.sum(wolves, CardPredicates.Accessors.fnGetNetPower); dmg = Aggregates.sum(wolves, Card::getNetPower);
} else if ("Triskelion".equals(logic)) { } else if ("Triskelion".equals(logic)) {
final int n = source.getCounters(CounterEnumType.P1P1); final int n = source.getCounters(CounterEnumType.P1P1);
if (n > 0) { if (n > 0) {
@@ -337,16 +336,11 @@ public class DamageDealAi extends DamageAiBase {
// Filter MustTarget requirements // Filter MustTarget requirements
StaticAbilityMustTarget.filterMustTargetCards(ai, hPlay, sa); StaticAbilityMustTarget.filterMustTargetCards(ai, hPlay, sa);
CardCollection killables = CardLists.filter(hPlay, new Predicate<Card>() { CardCollection killables = CardLists.filter(hPlay, c -> c.getSVar("Targeting").equals("Dies")
@Override || (ComputerUtilCombat.getEnoughDamageToKill(c, d, source, false, noPrevention) <= d)
public boolean apply(final Card c) { && !ComputerUtil.canRegenerate(ai, c)
return c.getSVar("Targeting").equals("Dies") && !c.hasSVar("SacMe")
|| (ComputerUtilCombat.getEnoughDamageToKill(c, d, source, false, noPrevention) <= d) && !ComputerUtilCard.hasActiveUndyingOrPersist(c));
&& !ComputerUtil.canRegenerate(ai, c)
&& !c.hasSVar("SacMe")
&& !ComputerUtilCard.hasActiveUndyingOrPersist(c);
}
});
// Filter AI-specific targets if provided // Filter AI-specific targets if provided
killables = ComputerUtil.filterAITgts(sa, ai, killables, true); killables = ComputerUtil.filterAITgts(sa, ai, killables, true);
@@ -416,15 +410,10 @@ public class DamageDealAi extends DamageAiBase {
final Game game = source.getGame(); final Game game = source.getGame();
List<Card> hPlay = CardLists.filter(getTargetableCards(ai, sa, pl, tgt, activator, source, game), CardPredicates.Presets.PLANESWALKERS); List<Card> hPlay = CardLists.filter(getTargetableCards(ai, sa, pl, tgt, activator, source, game), CardPredicates.Presets.PLANESWALKERS);
CardCollection killables = CardLists.filter(hPlay, new Predicate<Card>() { CardCollection killables = CardLists.filter(hPlay, c -> c.getSVar("Targeting").equals("Dies")
@Override || (ComputerUtilCombat.getEnoughDamageToKill(c, d, source, false, noPrevention) <= d)
public boolean apply(final Card c) { && !ComputerUtil.canRegenerate(ai, c)
return c.getSVar("Targeting").equals("Dies") && !c.hasSVar("SacMe"));
|| (ComputerUtilCombat.getEnoughDamageToKill(c, d, source, false, noPrevention) <= d)
&& !ComputerUtil.canRegenerate(ai, c)
&& !c.hasSVar("SacMe");
}
});
// Filter AI-specific targets if provided // Filter AI-specific targets if provided
killables = ComputerUtil.filterAITgts(sa, ai, killables, true); killables = ComputerUtil.filterAITgts(sa, ai, killables, true);

View File

@@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -69,18 +68,15 @@ public class DebuffAi extends SpellAbilityAi {
List<Card> cards = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa); List<Card> cards = AbilityUtils.getDefinedCards(source, sa.getParam("Defined"), sa);
final Combat combat = game.getCombat(); final Combat combat = game.getCombat();
return Iterables.any(cards, new Predicate<Card>() { return Iterables.any(cards, c -> {
@Override if (c.getController().equals(sa.getActivatingPlayer()) || combat == null)
public boolean apply(final Card c) { return false;
if (c.getController().equals(sa.getActivatingPlayer()) || combat == null)
return false;
if (!combat.isBlocking(c) && !combat.isAttacking(c)) { if (!combat.isBlocking(c) && !combat.isAttacking(c)) {
return false; return false;
}
// don't add duplicate negative keywords
return sa.hasParam("Keywords") && c.hasAnyKeyword(Arrays.asList(sa.getParam("Keywords").split(" & ")));
} }
// don't add duplicate negative keywords
return sa.hasParam("Keywords") && c.hasAnyKeyword(Arrays.asList(sa.getParam("Keywords").split(" & ")));
}); });
} else { } else {
return debuffTgtAI(ai, sa, sa.hasParam("Keywords") ? Arrays.asList(sa.getParam("Keywords").split(" & ")) : null, false); return debuffTgtAI(ai, sa, sa.hasParam("Keywords") ? Arrays.asList(sa.getParam("Keywords").split(" & ")) : null, false);
@@ -172,11 +168,8 @@ public class DebuffAi extends SpellAbilityAi {
final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai); final Player opp = AiAttackController.choosePreferredDefenderPlayer(ai);
CardCollection list = CardLists.getTargetableCards(opp.getCreaturesInPlay(), sa); CardCollection list = CardLists.getTargetableCards(opp.getCreaturesInPlay(), sa);
if (!list.isEmpty()) { if (!list.isEmpty()) {
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> {
@Override return c.hasAnyKeyword(kws); // don't add duplicate negative keywords
public boolean apply(final Card c) {
return c.hasAnyKeyword(kws); // don't add duplicate negative keywords
}
}); });
} }
return list; return list;

View File

@@ -1,10 +1,8 @@
package forge.ai.ability; package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.ai.*; import forge.ai.*;
import forge.card.mana.ManaCost; import forge.card.mana.ManaCost;
import forge.game.ability.ApiType; import forge.game.ability.ApiType;
import forge.game.card.Card;
import forge.game.card.CardCollection; import forge.game.card.CardCollection;
import forge.game.card.CardLists; import forge.game.card.CardLists;
import forge.game.cost.Cost; import forge.game.cost.Cost;
@@ -60,38 +58,35 @@ public class DelayedTriggerAi extends SpellAbilityAi {
// fetch Instant or Sorcery and AI has reason to play this turn // fetch Instant or Sorcery and AI has reason to play this turn
// does not try to get itself // does not try to get itself
final ManaCost costSa = sa.getPayCosts().getTotalMana(); final ManaCost costSa = sa.getPayCosts().getTotalMana();
final int count = CardLists.count(ai.getCardsIn(ZoneType.Hand), new Predicate<Card>() { final int count = CardLists.count(ai.getCardsIn(ZoneType.Hand), c -> {
@Override if (!(c.isInstant() || c.isSorcery()) || c.equals(sa.getHostCard())) {
public boolean apply(final Card c) {
if (!(c.isInstant() || c.isSorcery()) || c.equals(sa.getHostCard())) {
return false;
}
for (SpellAbility ab : c.getSpellAbilities()) {
if (ComputerUtilAbility.getAbilitySourceName(sa).equals(ComputerUtilAbility.getAbilitySourceName(ab))
|| ab.hasParam("AINoRecursiveCheck")) {
// prevent infinitely recursing mana ritual and other abilities with reentry
continue;
} else if ("SpellCopy".equals(ab.getParam("AILogic")) && ab.getApi() == ApiType.DelayedTrigger) {
// don't copy another copy spell, too complex for the AI
continue;
}
if (!ab.canPlay()) {
continue;
}
AiPlayDecision decision = ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(ab);
// see if we can pay both for this spell and for the Effect spell we're considering
if (decision == AiPlayDecision.WillPlay || decision == AiPlayDecision.WaitForMain2) {
ManaCost costAb = ab.getPayCosts().getTotalMana();
ManaCost total = ManaCost.combine(costSa, costAb);
SpellAbility combinedAb = ab.copyWithDefinedCost(new Cost(total, false));
// can we pay both costs?
if (ComputerUtilMana.canPayManaCost(combinedAb, ai, 0, true)) {
return true;
}
}
}
return false; return false;
} }
for (SpellAbility ab : c.getSpellAbilities()) {
if (ComputerUtilAbility.getAbilitySourceName(sa).equals(ComputerUtilAbility.getAbilitySourceName(ab))
|| ab.hasParam("AINoRecursiveCheck")) {
// prevent infinitely recursing mana ritual and other abilities with reentry
continue;
} else if ("SpellCopy".equals(ab.getParam("AILogic")) && ab.getApi() == ApiType.DelayedTrigger) {
// don't copy another copy spell, too complex for the AI
continue;
}
if (!ab.canPlay()) {
continue;
}
AiPlayDecision decision = ((PlayerControllerAi)ai.getController()).getAi().canPlaySa(ab);
// see if we can pay both for this spell and for the Effect spell we're considering
if (decision == AiPlayDecision.WillPlay || decision == AiPlayDecision.WaitForMain2) {
ManaCost costAb = ab.getPayCosts().getTotalMana();
ManaCost total = ManaCost.combine(costSa, costAb);
SpellAbility combinedAb = ab.copyWithDefinedCost(new Cost(total, false));
// can we pay both costs?
if (ComputerUtilMana.canPayManaCost(combinedAb, ai, 0, true)) {
return true;
}
}
}
return false;
}); });
if (count == 0) { if (count == 0) {
@@ -106,30 +101,27 @@ public class DelayedTriggerAi extends SpellAbilityAi {
// fetch Instant or Sorcery without Rebound and AI has reason to play this turn // fetch Instant or Sorcery without Rebound and AI has reason to play this turn
// only need count, not the list // only need count, not the list
final int count = CardLists.count(ai.getCardsIn(ZoneType.Hand), new Predicate<Card>() { final int count = CardLists.count(ai.getCardsIn(ZoneType.Hand), c -> {
@Override if (!(c.isInstant() || c.isSorcery()) || c.hasKeyword(Keyword.REBOUND)) {
public boolean apply(final Card c) {
if (!(c.isInstant() || c.isSorcery()) || c.hasKeyword(Keyword.REBOUND)) {
return false;
}
for (SpellAbility ab : c.getSpellAbilities()) {
if (ComputerUtilAbility.getAbilitySourceName(sa).equals(ComputerUtilAbility.getAbilitySourceName(ab))
|| ab.hasParam("AINoRecursiveCheck")) {
// prevent infinitely recursing mana ritual and other abilities with reentry
continue;
}
if (!ab.canPlay()) {
continue;
}
AiPlayDecision decision = ((PlayerControllerAi) ai.getController()).getAi().canPlaySa(ab);
if (decision == AiPlayDecision.WillPlay || decision == AiPlayDecision.WaitForMain2) {
if (ComputerUtilMana.canPayManaCost(ab, ai, 0, true)) {
return true;
}
}
}
return false; return false;
} }
for (SpellAbility ab : c.getSpellAbilities()) {
if (ComputerUtilAbility.getAbilitySourceName(sa).equals(ComputerUtilAbility.getAbilitySourceName(ab))
|| ab.hasParam("AINoRecursiveCheck")) {
// prevent infinitely recursing mana ritual and other abilities with reentry
continue;
}
if (!ab.canPlay()) {
continue;
}
AiPlayDecision decision = ((PlayerControllerAi) ai.getController()).getAi().canPlaySa(ab);
if (decision == AiPlayDecision.WillPlay || decision == AiPlayDecision.WaitForMain2) {
if (ComputerUtilMana.canPayManaCost(ab, ai, 0, true)) {
return true;
}
}
}
return false;
}); });
if (count == 0) { if (count == 0) {
@@ -140,15 +132,12 @@ public class DelayedTriggerAi extends SpellAbilityAi {
} else if (logic.equals("SaveCreature")) { } else if (logic.equals("SaveCreature")) {
CardCollection ownCreatures = ai.getCreaturesInPlay(); CardCollection ownCreatures = ai.getCreaturesInPlay();
ownCreatures = CardLists.filter(ownCreatures, new Predicate<Card>() { ownCreatures = CardLists.filter(ownCreatures, card -> {
@Override if (ComputerUtilCard.isUselessCreature(ai, card)) {
public boolean apply(final Card card) { return false;
if (ComputerUtilCard.isUselessCreature(ai, card)) {
return false;
}
return ComputerUtil.predictCreatureWillDieThisTurn(ai, card, sa);
} }
return ComputerUtil.predictCreatureWillDieThisTurn(ai, card, sa);
}); });
if (!ownCreatures.isEmpty()) { if (!ownCreatures.isEmpty()) {

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import forge.ai.*; import forge.ai.*;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
@@ -170,30 +169,27 @@ public class DestroyAi extends SpellAbilityAi {
if (!playReusable(ai, sa)) { if (!playReusable(ai, sa)) {
list = CardLists.filter(list, Predicates.not(CardPredicates.hasCounter(CounterEnumType.SHIELD, 1))); list = CardLists.filter(list, Predicates.not(CardPredicates.hasCounter(CounterEnumType.SHIELD, 1)));
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> {
@Override //Check for cards that can be sacrificed in response
public boolean apply(final Card c) { for (final SpellAbility ability : c.getAllSpellAbilities()) {
//Check for cards that can be sacrificed in response if (ability.isActivatedAbility()) {
for (final SpellAbility ability : c.getAllSpellAbilities()) { final Cost cost = ability.getPayCosts();
if (ability.isActivatedAbility()) { for (final CostPart part : cost.getCostParts()) {
final Cost cost = ability.getPayCosts(); if (!(part instanceof CostSacrifice)) {
for (final CostPart part : cost.getCostParts()) { continue;
if (!(part instanceof CostSacrifice)) { }
continue; CostSacrifice sacCost = (CostSacrifice) part;
} if (sacCost.payCostFromSource() && ComputerUtilCost.canPayCost(ability, c.getController(), false)) {
CostSacrifice sacCost = (CostSacrifice) part; return false;
if (sacCost.payCostFromSource() && ComputerUtilCost.canPayCost(ability, c.getController(), false)) {
return false;
}
} }
} }
} }
if (c.hasSVar("SacMe")) {
return false;
}
//Check for undying
return !c.hasKeyword(Keyword.UNDYING) || c.getCounters(CounterEnumType.P1P1) > 0;
} }
if (c.hasSVar("SacMe")) {
return false;
}
//Check for undying
return !c.hasKeyword(Keyword.UNDYING) || c.getCounters(CounterEnumType.P1P1) > 0;
}); });
} }
@@ -201,12 +197,7 @@ public class DestroyAi extends SpellAbilityAi {
// regeneration shield // regeneration shield
if (!noRegen) { if (!noRegen) {
// TODO filter out things that might be tougher? // TODO filter out things that might be tougher?
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> c.getShieldCount() == 0 && !ComputerUtil.canRegenerate(ai, c));
@Override
public boolean apply(final Card c) {
return c.getShieldCount() == 0 && !ComputerUtil.canRegenerate(ai, c);
}
});
} }
// Try to avoid targeting creatures that are dead on board // Try to avoid targeting creatures that are dead on board
@@ -340,12 +331,7 @@ public class DestroyAi extends SpellAbilityAi {
if (!noRegen) { if (!noRegen) {
// TODO filter out things that could regenerate in response? // TODO filter out things that could regenerate in response?
// might be tougher? // might be tougher?
preferred = CardLists.filter(preferred, new Predicate<Card>() { preferred = CardLists.filter(preferred, c -> c.getShieldCount() == 0);
@Override
public boolean apply(final Card c) {
return c.getShieldCount() == 0;
}
});
} }
// Filter AI-specific targets if provided // Filter AI-specific targets if provided

View File

@@ -19,12 +19,7 @@ import forge.game.zone.ZoneType;
public class DestroyAllAi extends SpellAbilityAi { public class DestroyAllAi extends SpellAbilityAi {
private static final Predicate<Card> predicate = new Predicate<Card>() { private static final Predicate<Card> predicate = c -> !(c.hasKeyword(Keyword.INDESTRUCTIBLE) || c.getCounters(CounterEnumType.SHIELD) > 0 || c.hasSVar("SacMe"));
@Override
public boolean apply(final Card c) {
return !(c.hasKeyword(Keyword.INDESTRUCTIBLE) || c.getCounters(CounterEnumType.SHIELD) > 0 || c.hasSVar("SacMe"));
}
};
/* (non-Javadoc) /* (non-Javadoc)
* @see forge.card.abilityfactory.SpellAiLogic#doTriggerAINoCost(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility, boolean) * @see forge.card.abilityfactory.SpellAiLogic#doTriggerAINoCost(forge.game.player.Player, java.util.Map, forge.card.spellability.SpellAbility, boolean)

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.ai.*; import forge.ai.*;
@@ -29,7 +28,6 @@ import forge.game.zone.MagicStack;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.util.MyRandom; import forge.util.MyRandom;
import forge.util.TextUtil; import forge.util.TextUtil;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -173,22 +171,12 @@ public class EffectAi extends SpellAbilityAi {
List<Card> human = opp.getCreaturesInPlay(); List<Card> human = opp.getCreaturesInPlay();
// only count creatures that can attack or block // only count creatures that can attack or block
comp = CardLists.filter(comp, new Predicate<Card>() { comp = CardLists.filter(comp, c -> CombatUtil.canAttack(c, opp));
@Override
public boolean apply(final Card c) {
return CombatUtil.canAttack(c, opp);
}
});
if (comp.size() < 2) { if (comp.size() < 2) {
continue; continue;
} }
final List<Card> attackers = comp; final List<Card> attackers = comp;
human = CardLists.filter(human, new Predicate<Card>() { human = CardLists.filter(human, c -> CombatUtil.canBlockAtLeastOne(c, attackers));
@Override
public boolean apply(final Card c) {
return CombatUtil.canBlockAtLeastOne(c, attackers);
}
});
if (human.isEmpty()) { if (human.isEmpty()) {
continue; continue;
} }
@@ -345,24 +333,20 @@ public class EffectAi extends SpellAbilityAi {
} else if (logic.equals("CantRegenerate")) { } else if (logic.equals("CantRegenerate")) {
if (sa.usesTargeting()) { if (sa.usesTargeting()) {
CardCollection list = CardLists.getTargetableCards(ai.getOpponents().getCardsIn(ZoneType.Battlefield), sa); CardCollection list = CardLists.getTargetableCards(ai.getOpponents().getCardsIn(ZoneType.Battlefield), sa);
list = CardLists.filter(list, CardPredicates.Presets.CAN_BE_DESTROYED, new Predicate<Card>() { list = CardLists.filter(list, CardPredicates.Presets.CAN_BE_DESTROYED, input -> {
Map<AbilityKey, Object> runParams = AbilityKey.mapFromAffected(input);
@Override runParams.put(AbilityKey.Regeneration, true);
public boolean apply(@Nullable Card input) { List<ReplacementEffect> repDestoryList = game.getReplacementHandler().getReplacementList(ReplacementType.Destroy, runParams, ReplacementLayer.Other);
Map<AbilityKey, Object> runParams = AbilityKey.mapFromAffected(input); // no Destroy Replacement, or one non-Regeneration one like Totem-Armor
runParams.put(AbilityKey.Regeneration, true); if (repDestoryList.isEmpty() || Iterables.any(repDestoryList, Predicates.not(CardTraitPredicates.hasParam("Regeneration")))) {
List<ReplacementEffect> repDestoryList = game.getReplacementHandler().getReplacementList(ReplacementType.Destroy, runParams, ReplacementLayer.Other);
// no Destroy Replacement, or one non-Regeneration one like Totem-Armor
if (repDestoryList.isEmpty() || Iterables.any(repDestoryList, Predicates.not(CardTraitPredicates.hasParam("Regeneration")))) {
return false;
}
if (cantRegenerateCheckCombat(input) || cantRegenerateCheckStack(input)) {
return true;
}
return false; return false;
} }
if (cantRegenerateCheckCombat(input) || cantRegenerateCheckStack(input)) {
return true;
}
return false;
}); });
if (list.isEmpty()) { if (list.isEmpty()) {

View File

@@ -20,8 +20,6 @@ package forge.ai.ability;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCombat; import forge.ai.ComputerUtilCombat;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
@@ -91,24 +89,16 @@ public final class EncodeAi extends SpellAbilityAi {
Card choice = null; Card choice = null;
// final String logic = sa.getParam("AILogic"); // final String logic = sa.getParam("AILogic");
// if (logic == null) { // if (logic == null) {
final List<Card> attackers = CardLists.filter(list, new Predicate<Card>() { final List<Card> attackers = CardLists.filter(list, ComputerUtilCombat::canAttackNextTurn);
@Override final List<Card> unblockables = CardLists.filter(attackers, c -> {
public boolean apply(final Card c) { boolean canAttackOpponent = false;
return ComputerUtilCombat.canAttackNextTurn(c); for (Player opp : ai.getOpponents()) {
} if (CombatUtil.canAttack(c, opp) && !CombatUtil.canBeBlocked(c, null, opp)) {
}); canAttackOpponent = true;
final List<Card> unblockables = CardLists.filter(attackers, new Predicate<Card>() { break;
@Override
public boolean apply(final Card c) {
boolean canAttackOpponent = false;
for (Player opp : ai.getOpponents()) {
if (CombatUtil.canAttack(c, opp) && !CombatUtil.canBeBlocked(c, null, opp)) {
canAttackOpponent = true;
break;
}
} }
return canAttackOpponent;
} }
return canAttackOpponent;
}); });
if (!unblockables.isEmpty()) { if (!unblockables.isEmpty()) {
choice = ComputerUtilCard.getBestAI(unblockables); choice = ComputerUtilCard.getBestAI(unblockables);

View File

@@ -2,9 +2,7 @@ package forge.ai.ability;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.card.Card;
import forge.game.card.CardCollectionView; import forge.game.card.CardCollectionView;
import forge.game.card.CardLists; import forge.game.card.CardLists;
import forge.game.phase.PhaseHandler; import forge.game.phase.PhaseHandler;
@@ -26,12 +24,7 @@ public class FlipOntoBattlefieldAi extends SpellAbilityAi {
if ("DamageCreatures".equals(logic)) { if ("DamageCreatures".equals(logic)) {
int maxToughness = Integer.parseInt(sa.getSubAbility().getParam("NumDmg")); int maxToughness = Integer.parseInt(sa.getSubAbility().getParam("NumDmg"));
CardCollectionView rightToughness = CardLists.filter(aiPlayer.getOpponents().getCreaturesInPlay(), new Predicate<Card>() { CardCollectionView rightToughness = CardLists.filter(aiPlayer.getOpponents().getCreaturesInPlay(), card -> card.getNetToughness() <= maxToughness && card.canBeDestroyed());
@Override
public boolean apply(Card card) {
return card.getNetToughness() <= maxToughness && card.canBeDestroyed();
}
});
return !rightToughness.isEmpty(); return !rightToughness.isEmpty();
} }

View File

@@ -2,8 +2,6 @@ package forge.ai.ability;
import java.util.List; import java.util.List;
import com.google.common.base.Predicate;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCombat; import forge.ai.ComputerUtilCombat;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
@@ -32,25 +30,22 @@ public class GoadAi extends SpellAbilityAi {
if (game.getPlayers().size() > 2) { if (game.getPlayers().size() > 2) {
// use this part only in multiplayer // use this part only in multiplayer
CardCollection betterList = CardLists.filter(list, new Predicate<Card>() { CardCollection betterList = CardLists.filter(list, c -> {
@Override // filter only creatures which can attack
public boolean apply(Card c) { if (ComputerUtilCard.isUselessCreature(ai, c)) {
// filter only creatures which can attack
if (ComputerUtilCard.isUselessCreature(ai, c)) {
return false;
}
// useless
if (c.isGoadedBy(ai)) {
return false;
}
// select creatures which can attack an Opponent other than ai
for (Player o : ai.getOpponents()) {
if (ComputerUtilCombat.canAttackNextTurn(c, o)) {
return true;
}
}
return false; return false;
} }
// useless
if (c.isGoadedBy(ai)) {
return false;
}
// select creatures which can attack an Opponent other than ai
for (Player o : ai.getOpponents()) {
if (ComputerUtilCombat.canAttackNextTurn(c, o)) {
return true;
}
}
return false;
}); });
// if better list is not empty, use that one instead // if better list is not empty, use that one instead
@@ -61,20 +56,17 @@ public class GoadAi extends SpellAbilityAi {
} }
} else { } else {
// single Player, goaded creature would attack ai // single Player, goaded creature would attack ai
CardCollection betterList = CardLists.filter(list, new Predicate<Card>() { CardCollection betterList = CardLists.filter(list, c -> {
@Override // filter only creatures which can attack
public boolean apply(Card c) { if (ComputerUtilCard.isUselessCreature(ai, c)) {
// filter only creatures which can attack return false;
if (ComputerUtilCard.isUselessCreature(ai, c)) {
return false;
}
// useless
if (c.isGoadedBy(ai)) {
return false;
}
// select only creatures AI can block
return ComputerUtilCard.canBeBlockedProfitably(ai, c, false);
} }
// useless
if (c.isGoadedBy(ai)) {
return false;
}
// select only creatures AI can block
return ComputerUtilCard.canBeBlockedProfitably(ai, c, false);
}); });
// if better list is not empty, use that one instead // if better list is not empty, use that one instead

View File

@@ -245,7 +245,7 @@ public class ManaAi extends SpellAbilityAi {
if (logic.startsWith("ManaRitualBattery")) { if (logic.startsWith("ManaRitualBattery")) {
// Don't remove more counters than would be needed to cast the more expensive thing we want to cast, // Don't remove more counters than would be needed to cast the more expensive thing we want to cast,
// otherwise the AI grabs too many counters at once. // otherwise the AI grabs too many counters at once.
int maxCtrs = Aggregates.max(castableSpells, CardPredicates.Accessors.fnGetCmc) - manaSurplus; int maxCtrs = Aggregates.max(castableSpells, Card::getCMC) - manaSurplus;
sa.setXManaCostPaid(Math.min(numCounters, maxCtrs)); sa.setXManaCostPaid(Math.min(numCounters, maxCtrs));
} }

View File

@@ -2,7 +2,6 @@ package forge.ai.ability;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.ai.ComputerUtil; import forge.ai.ComputerUtil;
@@ -138,12 +137,7 @@ public abstract class ManifestBaseAi extends SpellAbilityAi {
@Override @Override
protected Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) { protected Card chooseSingleCard(final Player ai, final SpellAbility sa, Iterable<Card> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
if (Iterables.size(options) > 1 || isOptional) { if (Iterables.size(options) > 1 || isOptional) {
CardCollection filtered = CardLists.filter(options, new Predicate<Card>() { CardCollection filtered = CardLists.filter(options, input -> shouldApply(input, ai, sa));
@Override
public boolean apply(Card input) {
return shouldApply(input, ai, sa);
}
});
if (!filtered.isEmpty()) { if (!filtered.isEmpty()) {
return ComputerUtilCard.getBestAI(filtered); return ComputerUtilCard.getBestAI(filtered);
} }

View File

@@ -1,7 +1,6 @@
package forge.ai.ability; package forge.ai.ability;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -159,13 +158,7 @@ public class MillAi extends SpellAbilityAi {
} }
// select Player which would cause the most damage // select Player which would cause the most damage
// JAVA 1.8 use Map.Entry.comparingByValue() Map.Entry<Player, Integer> max = Collections.max(list.entrySet(), Map.Entry.comparingByValue());
Map.Entry<Player, Integer> max = Collections.max(list.entrySet(), new Comparator<Map.Entry<Player,Integer>>(){
@Override
public int compare(Map.Entry<Player, Integer> o1, Map.Entry<Player, Integer> o2) {
return o1.getValue() - o2.getValue();
}
});
sa.getTargets().add(max.getKey()); sa.getTargets().add(max.getKey());
} }

View File

@@ -3,7 +3,6 @@ package forge.ai.ability;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.ai.AiCardMemory; import forge.ai.AiCardMemory;
@@ -122,28 +121,24 @@ public class MustBlockAi extends SpellAbilityAi {
private List<Card> determineBlockerFromList(final Card attacker, final Player ai, Iterable<Card> options, SpellAbility sa, private List<Card> determineBlockerFromList(final Card attacker, final Player ai, Iterable<Card> options, SpellAbility sa,
final boolean onlyLethal, final boolean testTapped) { final boolean onlyLethal, final boolean testTapped) {
List<Card> list = CardLists.filter(options, new Predicate<Card>() { List<Card> list = CardLists.filter(options, c -> {
@Override boolean tapped = c.isTapped();
public boolean apply(final Card c) { if (testTapped) {
boolean tapped = c.isTapped(); c.setTapped(false);
if (testTapped) {
c.setTapped(false);
}
if (!CombatUtil.canBlock(attacker, c)) {
return false;
}
if (ComputerUtilCombat.canDestroyAttacker(ai, attacker, c, null, false)) {
return false;
}
if (onlyLethal && !ComputerUtilCombat.canDestroyBlocker(ai, c, attacker, null, false)) {
return false;
}
if (testTapped) {
c.setTapped(tapped);
}
return true;
} }
if (!CombatUtil.canBlock(attacker, c)) {
return false;
}
if (ComputerUtilCombat.canDestroyAttacker(ai, attacker, c, null, false)) {
return false;
}
if (onlyLethal && !ComputerUtilCombat.canDestroyBlocker(ai, c, attacker, null, false)) {
return false;
}
if (testTapped) {
c.setTapped(tapped);
}
return true;
}); });
return list; return list;

View File

@@ -2,7 +2,6 @@ package forge.ai.ability;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import forge.ai.ComputerUtil; import forge.ai.ComputerUtil;
@@ -29,12 +28,7 @@ public class MutateAi extends SpellAbilityAi {
CardPredicates.hasKeyword(Keyword.DEFENDER), CardPredicates.hasKeyword(Keyword.DEFENDER),
CardPredicates.hasKeyword("CARDNAME can't attack."), CardPredicates.hasKeyword("CARDNAME can't attack."),
CardPredicates.hasKeyword("CARDNAME can't block."), CardPredicates.hasKeyword("CARDNAME can't block."),
new Predicate<Card>() { card -> ComputerUtilCard.isUselessCreature(aiPlayer, card)
@Override
public boolean apply(final Card card) {
return ComputerUtilCard.isUselessCreature(aiPlayer, card);
}
}
))); )));
if (mutateTgts.isEmpty()) { if (mutateTgts.isEmpty()) {

View File

@@ -3,8 +3,6 @@ package forge.ai.ability;
import forge.game.card.CardCopyService; import forge.game.card.CardCopyService;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.google.common.base.Predicate;
import forge.ai.AiController; import forge.ai.AiController;
import forge.ai.AiProps; import forge.ai.AiProps;
import forge.ai.ComputerUtil; import forge.ai.ComputerUtil;
@@ -146,12 +144,9 @@ public class PermanentCreatureAi extends PermanentAi {
if (combat != null && combat.getDefendingPlayers().contains(ai)) { if (combat != null && combat.getDefendingPlayers().contains(ai)) {
// Currently we use a rather simplistic assumption that if we're behind on creature count on board, // Currently we use a rather simplistic assumption that if we're behind on creature count on board,
// a flashed in creature might prove to be good as an additional defender // a flashed in creature might prove to be good as an additional defender
int numUntappedPotentialBlockers = CardLists.filter(ai.getCreaturesInPlay(), new Predicate<Card>() { int numUntappedPotentialBlockers = CardLists.filter(ai.getCreaturesInPlay(),
@Override card1 -> card1.isUntapped() && !ComputerUtilCard.isUselessCreature(ai, card1)
public boolean apply(final Card card) { ).size();
return card.isUntapped() && !ComputerUtilCard.isUselessCreature(ai, card);
}
}).size();
if (combat.getAttackersOf(ai).size() > numUntappedPotentialBlockers) { if (combat.getAttackersOf(ai).size() > numUntappedPotentialBlockers) {
valuableBlocker = true; valuableBlocker = true;

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.ai.*; import forge.ai.*;
import forge.card.CardStateName; import forge.card.CardStateName;
@@ -148,57 +147,54 @@ public class PlayAi extends SpellAbilityAi {
state = CardStateName.Original; state = CardStateName.Original;
} }
List<Card> tgtCards = CardLists.filter(options, new Predicate<Card>() { List<Card> tgtCards = CardLists.filter(options, c -> {
@Override // TODO needs to be aligned for MDFC along with getAbilityToPlay so the knowledge
public boolean apply(final Card c) { // of which spell was the reason for the choice can be used there
// TODO needs to be aligned for MDFC along with getAbilityToPlay so the knowledge for (SpellAbility s : AbilityUtils.getSpellsFromPlayEffect(c, ai, state, false)) {
// of which spell was the reason for the choice can be used there if (!sa.matchesValidParam("ValidSA", s)) {
for (SpellAbility s : AbilityUtils.getSpellsFromPlayEffect(c, ai, state, false)) { continue;
if (!sa.matchesValidParam("ValidSA", s)) { }
continue; if (s instanceof LandAbility) {
} // might want to run some checks here but it's rare anyway
if (s instanceof LandAbility) { return true;
// might want to run some checks here but it's rare anyway }
return true; Spell spell = (Spell) s;
} if (params != null && params.containsKey("CMCLimit")) {
Spell spell = (Spell) s; Integer cmcLimit = (Integer) params.get("CMCLimit");
if (params != null && params.containsKey("CMCLimit")) { if (spell.getPayCosts().getTotalMana().getCMC() > cmcLimit)
Integer cmcLimit = (Integer) params.get("CMCLimit"); continue;
if (spell.getPayCosts().getTotalMana().getCMC() > cmcLimit) }
continue; if (sa.hasParam("WithoutManaCost")) {
} // Try to avoid casting instants and sorceries with X in their cost, since X will be assumed to be 0.
if (sa.hasParam("WithoutManaCost")) { if (!(spell instanceof SpellPermanent)) {
// Try to avoid casting instants and sorceries with X in their cost, since X will be assumed to be 0. if (spell.costHasManaX()) {
if (!(spell instanceof SpellPermanent)) { continue;
if (spell.costHasManaX()) { }
continue; }
}
} spell = (Spell) spell.copyWithNoManaCost();
} else if (sa.hasParam("PlayCost")) {
spell = (Spell) spell.copyWithNoManaCost(); Cost abCost;
} else if (sa.hasParam("PlayCost")) { if ("ManaCost".equals(sa.getParam("PlayCost"))) {
Cost abCost; abCost = new Cost(c.getManaCost(), false);
if ("ManaCost".equals(sa.getParam("PlayCost"))) { } else {
abCost = new Cost(c.getManaCost(), false); abCost = new Cost(sa.getParam("PlayCost"), false);
} else { }
abCost = new Cost(sa.getParam("PlayCost"), false);
} spell = (Spell) spell.copyWithManaCostReplaced(spell.getActivatingPlayer(), abCost);
}
spell = (Spell) spell.copyWithManaCostReplaced(spell.getActivatingPlayer(), abCost); if (AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlayFromEffectAI(spell, !(isOptional || sa.hasParam("Optional")), true)) {
} // Before accepting, see if the spell has a valid number of targets (it should at this point).
if (AiPlayDecision.WillPlay == ((PlayerControllerAi)ai.getController()).getAi().canPlayFromEffectAI(spell, !(isOptional || sa.hasParam("Optional")), true)) { // Proceeding past this point if the spell is not correctly targeted will result
// Before accepting, see if the spell has a valid number of targets (it should at this point). // in "Failed to add to stack" error and the card disappearing from the game completely.
// Proceeding past this point if the spell is not correctly targeted will result if (!spell.isTargetNumberValid() || !ComputerUtilCost.canPayCost(spell, ai, true)) {
// in "Failed to add to stack" error and the card disappearing from the game completely. // if we won't be able to pay the cost, don't choose the card
if (!spell.isTargetNumberValid() || !ComputerUtilCost.canPayCost(spell, ai, true)) { return false;
// if we won't be able to pay the cost, don't choose the card }
return false; return true;
}
return true;
}
} }
return false;
} }
return false;
}); });
if (sa.hasParam("CastTransformed")) { if (sa.hasParam("CastTransformed")) {

View File

@@ -1,7 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import com.google.common.base.Predicate;
import forge.ai.ComputerUtil; import forge.ai.ComputerUtil;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
@@ -86,17 +84,13 @@ public class PoisonAi extends SpellAbilityAi {
PlayerCollection tgts = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa)); PlayerCollection tgts = ai.getOpponents().filter(PlayerPredicates.isTargetableBy(sa));
if (!tgts.isEmpty()) { if (!tgts.isEmpty()) {
// try to select a opponent that can lose through poison counters // try to select a opponent that can lose through poison counters
PlayerCollection betterTgts = tgts.filter(new Predicate<Player>() { PlayerCollection betterTgts = tgts.filter(input -> {
@Override if (input.cantLose()) {
public boolean apply(Player input) { return false;
if (input.cantLose()) { } else if (!input.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
return false; return false;
} else if (!input.canReceiveCounters(CounterType.get(CounterEnumType.POISON))) {
return false;
}
return true;
} }
return true;
}); });
if (!betterTgts.isEmpty()) { if (!betterTgts.isEmpty()) {
@@ -120,15 +114,11 @@ public class PoisonAi extends SpellAbilityAi {
PlayerCollection allies = ai.getAllies().filter(PlayerPredicates.isTargetableBy(sa)); PlayerCollection allies = ai.getAllies().filter(PlayerPredicates.isTargetableBy(sa));
if (!allies.isEmpty()) { if (!allies.isEmpty()) {
// some ally would be unaffected // some ally would be unaffected
PlayerCollection betterAllies = allies.filter(new Predicate<Player>() { PlayerCollection betterAllies = allies.filter(input -> {
@Override if (input.cantLose()) {
public boolean apply(Player input) { return true;
if (input.cantLose()) {
return true;
}
return !input.canReceiveCounters(CounterType.get(CounterEnumType.POISON));
} }
return !input.canReceiveCounters(CounterType.get(CounterEnumType.POISON));
}); });
if (!betterAllies.isEmpty()) { if (!betterAllies.isEmpty()) {
allies = betterAllies; allies = betterAllies;

View File

@@ -2,8 +2,6 @@ package forge.ai.ability;
import java.util.List; import java.util.List;
import com.google.common.base.Predicate;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
import forge.game.ability.AbilityUtils; import forge.game.ability.AbilityUtils;
@@ -30,12 +28,7 @@ public class PowerExchangeAi extends SpellAbilityAi {
List<Card> list = List<Card> list =
CardLists.getValidCards(ai.getGame().getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa); CardLists.getValidCards(ai.getGame().getCardsIn(ZoneType.Battlefield), tgt.getValidTgts(), ai, sa.getHostCard(), sa);
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> c.canBeTargetedBy(sa) && c.getController() != ai);
@Override
public boolean apply(final Card c) {
return c.canBeTargetedBy(sa) && c.getController() != ai;
}
});
CardLists.sortByPowerDesc(list); CardLists.sortByPowerDesc(list);
c1 = list.isEmpty() ? null : list.get(0); c1 = list.isEmpty() ? null : list.get(0);
if (sa.hasParam("Defined")) { if (sa.hasParam("Defined")) {

View File

@@ -3,8 +3,6 @@ package forge.ai.ability;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import com.google.common.base.Predicate;
import forge.ai.AiAttackController; import forge.ai.AiAttackController;
import forge.ai.ComputerUtil; import forge.ai.ComputerUtil;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
@@ -107,54 +105,51 @@ public class ProtectAi extends SpellAbilityAi {
CardCollection list = ai.getCreaturesInPlay(); CardCollection list = ai.getCreaturesInPlay();
final List<GameObject> threatenedObjects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa, true); final List<GameObject> threatenedObjects = ComputerUtil.predictThreatenedObjects(sa.getActivatingPlayer(), sa, true);
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> {
@Override if (!c.canBeTargetedBy(sa)) {
public boolean apply(final Card c) {
if (!c.canBeTargetedBy(sa)) {
return false;
}
// Don't add duplicate protections
if (hasProtectionFromAll(c, gains)) {
return false;
}
if (threatenedObjects.contains(c)) {
return true;
}
if (combat != null) {
//creature is blocking and would be destroyed itself
if (combat.isBlocking(c) && ComputerUtilCombat.blockerWouldBeDestroyed(ai, c, combat)) {
List<Card> threats = combat.getAttackersBlockedBy(c);
return threats != null && !threats.isEmpty() && ProtectAi.toProtectFrom(threats.get(0), sa) != null;
}
//creature is attacking and would be destroyed itself
if (combat.isAttacking(c) && combat.isBlocked(c) && ComputerUtilCombat.attackerWouldBeDestroyed(ai, c, combat)) {
CardCollection threats = combat.getBlockers(c);
if (threats != null && !threats.isEmpty()) {
ComputerUtilCard.sortByEvaluateCreature(threats);
return ProtectAi.toProtectFrom(threats.get(0), sa) != null;
}
}
}
//make unblockable
if (ph.getPlayerTurn() == ai && ph.getPhase() == PhaseType.MAIN1) {
AiAttackController aiAtk = new AiAttackController(ai, c);
String s = aiAtk.toProtectAttacker(sa);
if (s == null) {
return false;
}
Player opponent = ai.getWeakestOpponent();
Combat combat = ai.getGame().getCombat();
int dmg = ComputerUtilCombat.damageIfUnblocked(c, opponent, combat, true);
float ratio = 1.0f * dmg / opponent.getLife();
return MyRandom.getRandom().nextFloat() < ratio;
}
return false; return false;
} }
// Don't add duplicate protections
if (hasProtectionFromAll(c, gains)) {
return false;
}
if (threatenedObjects.contains(c)) {
return true;
}
if (combat != null) {
//creature is blocking and would be destroyed itself
if (combat.isBlocking(c) && ComputerUtilCombat.blockerWouldBeDestroyed(ai, c, combat)) {
List<Card> threats = combat.getAttackersBlockedBy(c);
return threats != null && !threats.isEmpty() && ProtectAi.toProtectFrom(threats.get(0), sa) != null;
}
//creature is attacking and would be destroyed itself
if (combat.isAttacking(c) && combat.isBlocked(c) && ComputerUtilCombat.attackerWouldBeDestroyed(ai, c, combat)) {
CardCollection threats = combat.getBlockers(c);
if (threats != null && !threats.isEmpty()) {
ComputerUtilCard.sortByEvaluateCreature(threats);
return ProtectAi.toProtectFrom(threats.get(0), sa) != null;
}
}
}
//make unblockable
if (ph.getPlayerTurn() == ai && ph.getPhase() == PhaseType.MAIN1) {
AiAttackController aiAtk = new AiAttackController(ai, c);
String s = aiAtk.toProtectAttacker(sa);
if (s == null) {
return false;
}
Player opponent = ai.getWeakestOpponent();
Combat combat1 = ai.getGame().getCombat();
int dmg = ComputerUtilCombat.damageIfUnblocked(c, opponent, combat1, true);
float ratio = 1.0f * dmg / opponent.getLife();
return MyRandom.getRandom().nextFloat() < ratio;
}
return false;
}); });
return list; return list;
} }
@@ -266,19 +261,9 @@ public class ProtectAi extends SpellAbilityAi {
} }
CardCollection pref = CardLists.filterControlledBy(list, ai); CardCollection pref = CardLists.filterControlledBy(list, ai);
pref = CardLists.filter(pref, new Predicate<Card>() { pref = CardLists.filter(pref, c -> !hasProtectionFromAll(c, ProtectEffect.getProtectionList(sa)));
@Override
public boolean apply(final Card c) {
return !hasProtectionFromAll(c, ProtectEffect.getProtectionList(sa));
}
});
final CardCollection pref2 = CardLists.filterControlledBy(list, ai); final CardCollection pref2 = CardLists.filterControlledBy(list, ai);
pref = CardLists.filter(pref, new Predicate<Card>() { pref = CardLists.filter(pref, c -> !hasProtectionFromAny(c, ProtectEffect.getProtectionList(sa)));
@Override
public boolean apply(final Card c) {
return !hasProtectionFromAny(c, ProtectEffect.getProtectionList(sa));
}
});
final List<Card> forced = CardLists.filterControlledBy(list, ai); final List<Card> forced = CardLists.filterControlledBy(list, ai);
while (sa.canAddMoreTarget()) { while (sa.canAddMoreTarget()) {

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -164,9 +163,52 @@ public class PumpAi extends PumpAiBase {
if (attr.isEmpty()) { if (attr.isEmpty()) {
return false; return false;
} }
CardCollection best = CardLists.filter(attr, new Predicate<Card>() { CardCollection best = CardLists.filter(attr, card -> {
@Override int amount = 0;
public boolean apply(Card card) { if (StringUtils.isNumeric(amountStr)) {
amount = AbilityUtils.calculateAmount(source, amountStr, moveSA);
} else if (source.hasSVar(amountStr)) {
if ("Count$ChosenNumber".equals(source.getSVar(amountStr))) {
amount = card.getCounters(cType);
}
}
int i = card.getCounters(cType);
if (i < amount) {
return false;
}
final Card srcCardCpy = CardCopyService.getLKICopy(card);
// cant use substract on Copy
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
if (cType.is(CounterEnumType.P1P1) && srcCardCpy.getNetToughness() <= 0) {
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING)
|| card.isToken();
}
return false;
});
if (best.isEmpty()) {
best = attr;
}
final Card card = ComputerUtilCard.getBestCreatureAI(best);
sa.getTargets().add(card);
return true;
}
} else {
final boolean sameCtrl = moveSA.getTargetRestrictions().isSameController();
List<Card> list = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
if (cType != null) {
list = CardLists.filter(list, CardPredicates.hasCounter(cType));
if (list.isEmpty()) {
return false;
}
List<Card> oppList = CardLists.filterControlledBy(list, ai.getOpponents());
if (!oppList.isEmpty() && !sameCtrl) {
List<Card> best = CardLists.filter(oppList, card -> {
int amount = 0; int amount = 0;
if (StringUtils.isNumeric(amountStr)) { if (StringUtils.isNumeric(amountStr)) {
amount = AbilityUtils.calculateAmount(source, amountStr, moveSA); amount = AbilityUtils.calculateAmount(source, amountStr, moveSA);
@@ -189,57 +231,7 @@ public class PumpAi extends PumpAiBase {
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING) return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING)
|| card.isToken(); || card.isToken();
} }
return false; return true;
}
});
if (best.isEmpty()) {
best = attr;
}
final Card card = ComputerUtilCard.getBestCreatureAI(best);
sa.getTargets().add(card);
return true;
}
} else {
final boolean sameCtrl = moveSA.getTargetRestrictions().isSameController();
List<Card> list = CardLists.getTargetableCards(game.getCardsIn(ZoneType.Battlefield), sa);
if (cType != null) {
list = CardLists.filter(list, CardPredicates.hasCounter(cType));
if (list.isEmpty()) {
return false;
}
List<Card> oppList = CardLists.filterControlledBy(list, ai.getOpponents());
if (!oppList.isEmpty() && !sameCtrl) {
List<Card> best = CardLists.filter(oppList, new Predicate<Card>() {
@Override
public boolean apply(Card card) {
int amount = 0;
if (StringUtils.isNumeric(amountStr)) {
amount = AbilityUtils.calculateAmount(source, amountStr, moveSA);
} else if (source.hasSVar(amountStr)) {
if ("Count$ChosenNumber".equals(source.getSVar(amountStr))) {
amount = card.getCounters(cType);
}
}
int i = card.getCounters(cType);
if (i < amount) {
return false;
}
final Card srcCardCpy = CardCopyService.getLKICopy(card);
// cant use substract on Copy
srcCardCpy.setCounters(cType, srcCardCpy.getCounters(cType) - amount);
if (cType.is(CounterEnumType.P1P1) && srcCardCpy.getNetToughness() <= 0) {
return srcCardCpy.getCounters(cType) > 0 || !card.hasKeyword(Keyword.UNDYING)
|| card.isToken();
}
return true;
}
}); });
if (best.isEmpty()) { if (best.isEmpty()) {
@@ -520,16 +512,13 @@ public class PumpAi extends PumpAiBase {
// Detain target nonland permanent: don't target noncreature permanents that don't have // Detain target nonland permanent: don't target noncreature permanents that don't have
// any activated abilities. // any activated abilities.
if ("DetainNonLand".equals(sa.getParam("AILogic"))) { if ("DetainNonLand".equals(sa.getParam("AILogic"))) {
list = CardLists.filter(list, Predicates.or(CardPredicates.Presets.CREATURES, new Predicate<Card>() { list = CardLists.filter(list, Predicates.or(CardPredicates.Presets.CREATURES, card -> {
@Override for (SpellAbility sa1 : card.getSpellAbilities()) {
public boolean apply(Card card) { if (sa1.isActivatedAbility()) {
for (SpellAbility sa : card.getSpellAbilities()) { return true;
if (sa.isActivatedAbility()) {
return true;
}
} }
return false;
} }
return false;
})); }));
} }
@@ -548,12 +537,7 @@ public class PumpAi extends PumpAiBase {
if ("BetterCreatureThanSource".equals(sa.getParam("AILogic"))) { if ("BetterCreatureThanSource".equals(sa.getParam("AILogic"))) {
// Don't target cards that are not better in value than the targeting card // Don't target cards that are not better in value than the targeting card
final int sourceValue = ComputerUtilCard.evaluateCreature(source); final int sourceValue = ComputerUtilCard.evaluateCreature(source);
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, card -> card.isCreature() && ComputerUtilCard.evaluateCreature(card) > sourceValue + 30);
@Override
public boolean apply(Card card) {
return card.isCreature() && ComputerUtilCard.evaluateCreature(card) > sourceValue + 30;
}
});
} }
if ("ReplaySpell".equals(sa.getParam("AILogic"))) { if ("ReplaySpell".equals(sa.getParam("AILogic"))) {
@@ -819,23 +803,11 @@ public class PumpAi extends PumpAiBase {
} }
values.keySet().removeAll(toRemove); values.keySet().removeAll(toRemove);
// JAVA 1.8 use Map.Entry.comparingByValue() data.put(opp, Collections.max(values.entrySet(), Map.Entry.comparingByValue()));
data.put(opp, Collections.max(values.entrySet(), new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
return o1.getValue() - o2.getValue();
}
}));
} }
if (!data.isEmpty()) { if (!data.isEmpty()) {
// JAVA 1.8 use Map.Entry.comparingByValue() somehow Map.Entry<Player, Map.Entry<String, Integer>> max = Collections.max(data.entrySet(), Comparator.comparingInt(o -> o.getValue().getValue()));
Map.Entry<Player, Map.Entry<String, Integer>> max = Collections.max(data.entrySet(), new Comparator<Map.Entry<Player, Map.Entry<String, Integer>>>() {
@Override
public int compare(Map.Entry<Player, Map.Entry<String, Integer>> o1, Map.Entry<Player, Map.Entry<String, Integer>> o2) {
return o1.getValue().getValue() - o2.getValue().getValue();
}
});
// filter list again by the opponent and a creature of the wanted name that can be targeted // filter list again by the opponent and a creature of the wanted name that can be targeted
list = CardLists.filter(CardLists.filterControlledBy(list, max.getKey()), list = CardLists.filter(CardLists.filterControlledBy(list, max.getKey()),

View File

@@ -122,15 +122,12 @@ public abstract class PumpAiBase extends SpellAbilityAi {
return false; return false;
} }
List<Card> attackers = CardLists.filter(ai.getCreaturesInPlay(), new Predicate<Card>() { List<Card> attackers = CardLists.filter(ai.getCreaturesInPlay(), c -> {
@Override if (c.equals(sa.getHostCard()) && sa.getPayCosts().hasTapCost()
public boolean apply(final Card c) { && (combat == null || !combat.isAttacking(c))) {
if (c.equals(sa.getHostCard()) && sa.getPayCosts().hasTapCost() return false;
&& (combat == null || !combat.isAttacking(c))) {
return false;
}
return (combat != null && combat.isAttacking(c)) || CombatUtil.canAttack(c, card.getController());
} }
return (combat != null && combat.isAttacking(c)) || CombatUtil.canAttack(c, card.getController());
}); });
return CombatUtil.canBlockAtLeastOne(card, attackers); return CombatUtil.canBlockAtLeastOne(card, attackers);
} }
@@ -140,17 +137,14 @@ public abstract class PumpAiBase extends SpellAbilityAi {
return false; return false;
} }
List<Card> attackers = CardLists.filter(ai.getCreaturesInPlay(), new Predicate<Card>() { List<Card> attackers = CardLists.filter(ai.getCreaturesInPlay(), c -> {
@Override if (c.equals(sa.getHostCard()) && sa.getPayCosts().hasTapCost()
public boolean apply(final Card c) { && (combat == null || !combat.isAttacking(c))) {
if (c.equals(sa.getHostCard()) && sa.getPayCosts().hasTapCost() return false;
&& (combat == null || !combat.isAttacking(c))) {
return false;
}
// the cards controller needs to be the one attacked
return (combat != null && combat.isAttacking(c) && card.getController().equals(combat.getDefenderPlayerByAttacker(c))) ||
CombatUtil.canAttack(c, card.getController());
} }
// the cards controller needs to be the one attacked
return (combat != null && combat.isAttacking(c) && card.getController().equals(combat.getDefenderPlayerByAttacker(c))) ||
CombatUtil.canAttack(c, card.getController());
}); });
return CombatUtil.canBlockAtLeastOne(card, attackers); return CombatUtil.canBlockAtLeastOne(card, attackers);
} else if (keyword.endsWith("This card doesn't untap during your next untap step.")) { } else if (keyword.endsWith("This card doesn't untap during your next untap step.")) {
@@ -416,12 +410,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
protected CardCollection getPumpCreatures(final Player ai, final SpellAbility sa, final int defense, final int attack, protected CardCollection getPumpCreatures(final Player ai, final SpellAbility sa, final int defense, final int attack,
final List<String> keywords, final boolean immediately) { final List<String> keywords, final boolean immediately) {
CardCollection list = CardLists.getTargetableCards(ai.getCreaturesInPlay(), sa); CardCollection list = CardLists.getTargetableCards(ai.getCreaturesInPlay(), sa);
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> ComputerUtilCard.shouldPumpCard(ai, sa, c, defense, attack, keywords, immediately));
@Override
public boolean apply(final Card c) {
return ComputerUtilCard.shouldPumpCard(ai, sa, c, defense, attack, keywords, immediately);
}
});
return list; return list;
} }
@@ -449,14 +438,11 @@ public abstract class PumpAiBase extends SpellAbilityAi {
} }
if (defense < 0) { // with spells that give -X/-X, compi will try to destroy a creature if (defense < 0) { // with spells that give -X/-X, compi will try to destroy a creature
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> {
@Override if (c.getSVar("Targeting").equals("Dies") || c.getNetToughness() <= -defense) {
public boolean apply(final Card c) { return true; // can kill indestructible creatures
if (c.getSVar("Targeting").equals("Dies") || c.getNetToughness() <= -defense) {
return true; // can kill indestructible creatures
}
return ComputerUtilCombat.getDamageToKill(c, false) <= -defense && !c.hasKeyword(Keyword.INDESTRUCTIBLE);
} }
return ComputerUtilCombat.getDamageToKill(c, false) <= -defense && !c.hasKeyword(Keyword.INDESTRUCTIBLE);
}); // leaves all creatures that will be destroyed }); // leaves all creatures that will be destroyed
} // -X/-X end } // -X/-X end
else if (attack < 0 && !game.getReplacementHandler().isPreventCombatDamageThisTurn()) { else if (attack < 0 && !game.getReplacementHandler().isPreventCombatDamageThisTurn()) {
@@ -472,18 +458,15 @@ public abstract class PumpAiBase extends SpellAbilityAi {
} else { } else {
// Human active, only curse attacking creatures // Human active, only curse attacking creatures
if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) { if (game.getPhaseHandler().getPhase().isBefore(PhaseType.COMBAT_DECLARE_BLOCKERS)) {
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> {
@Override if (combat == null || !combat.isAttacking(c)) {
public boolean apply(final Card c) { return false;
if (combat == null || !combat.isAttacking(c)) {
return false;
}
if (c.getNetPower() > 0 && ai.getLife() < 5) {
return true;
}
//Don't waste a -7/-0 spell on a 1/1 creature
return c.getNetPower() + attack > -2 || c.getNetPower() > 3;
} }
if (c.getNetPower() > 0 && ai.getLife() < 5) {
return true;
}
//Don't waste a -7/-0 spell on a 1/1 creature
return c.getNetPower() + attack > -2 || c.getNetPower() > 3;
}); });
} else { } else {
list = new CardCollection(); list = new CardCollection();
@@ -501,12 +484,7 @@ public abstract class PumpAiBase extends SpellAbilityAi {
} }
} }
list = CardLists.filter(list, new Predicate<Card>() { list = CardLists.filter(list, c -> containsUsefulKeyword(ai, keywords, c, sa, attack));
@Override
public boolean apply(final Card c) {
return containsUsefulKeyword(ai, keywords, c, sa, attack);
}
});
} else if (sa.hasParam("NumAtt") || sa.hasParam("NumDef")) { } else if (sa.hasParam("NumAtt") || sa.hasParam("NumDef")) {
// X is zero // X is zero
list = new CardCollection(); list = new CardCollection();

View File

@@ -4,7 +4,6 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.ai.ComputerUtil; import forge.ai.ComputerUtil;
@@ -85,23 +84,17 @@ public class PumpAllAi extends PumpAiBase {
if (sa.isCurse()) { if (sa.isCurse()) {
if (defense < 0) { // try to destroy creatures if (defense < 0) { // try to destroy creatures
comp = CardLists.filter(comp, new Predicate<Card>() { comp = CardLists.filter(comp, c -> {
@Override if (c.getNetToughness() <= -defense) {
public boolean apply(final Card c) { return true; // can kill indestructible creatures
if (c.getNetToughness() <= -defense) {
return true; // can kill indestructible creatures
}
return ComputerUtilCombat.getDamageToKill(c, false) <= -defense && !c.hasKeyword(Keyword.INDESTRUCTIBLE);
} }
return ComputerUtilCombat.getDamageToKill(c, false) <= -defense && !c.hasKeyword(Keyword.INDESTRUCTIBLE);
}); // leaves all creatures that will be destroyed }); // leaves all creatures that will be destroyed
human = CardLists.filter(human, new Predicate<Card>() { human = CardLists.filter(human, c -> {
@Override if (c.getNetToughness() <= -defense) {
public boolean apply(final Card c) { return true; // can kill indestructible creatures
if (c.getNetToughness() <= -defense) {
return true; // can kill indestructible creatures
}
return ComputerUtilCombat.getDamageToKill(c, false) <= -defense && !c.hasKeyword(Keyword.INDESTRUCTIBLE);
} }
return ComputerUtilCombat.getDamageToKill(c, false) <= -defense && !c.hasKeyword(Keyword.INDESTRUCTIBLE);
}); // leaves all creatures that will be destroyed }); // leaves all creatures that will be destroyed
} // -X/-X end } // -X/-X end
else if (power < 0) { // -X/-0 else if (power < 0) { // -X/-0

View File

@@ -3,7 +3,6 @@ package forge.ai.ability;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.ai.*; import forge.ai.*;
import forge.game.card.Card; import forge.game.card.Card;
@@ -56,12 +55,7 @@ public class RepeatAi extends SpellAbilityAi {
Card best = null; Card best = null;
Iterable<Card> targetableAi = Iterables.filter(ai.getCreaturesInPlay(), CardPredicates.isTargetableBy(sa)); Iterable<Card> targetableAi = Iterables.filter(ai.getCreaturesInPlay(), CardPredicates.isTargetableBy(sa));
if (!logic.endsWith("IgnoreLegendary")) { if (!logic.endsWith("IgnoreLegendary")) {
best = ComputerUtilCard.getBestAI(Iterables.filter(targetableAi, new Predicate<Card>() { best = ComputerUtilCard.getBestAI(Iterables.filter(targetableAi, Card::ignoreLegendRule));
@Override
public boolean apply(Card card) {
return card.ignoreLegendRule();
}
}));
} else { } else {
best = ComputerUtilCard.getBestAI(targetableAi); best = ComputerUtilCard.getBestAI(targetableAi);
} }

View File

@@ -3,8 +3,6 @@ package forge.ai.ability;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.SpecialCardAi; import forge.ai.SpecialCardAi;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
@@ -97,12 +95,7 @@ public class RepeatEachAi extends SpellAbilityAi {
return hitOpp; return hitOpp;
} else if ("EquipAll".equals(logic)) { } else if ("EquipAll".equals(logic)) {
if (aiPlayer.getGame().getPhaseHandler().is(PhaseType.MAIN1, aiPlayer)) { if (aiPlayer.getGame().getPhaseHandler().is(PhaseType.MAIN1, aiPlayer)) {
final CardCollection unequipped = CardLists.filter(aiPlayer.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() { final CardCollection unequipped = CardLists.filter(aiPlayer.getCardsIn(ZoneType.Battlefield), card -> card.isEquipment() && card.getAttachedTo() != sa.getHostCard());
@Override
public boolean apply(Card card) {
return card.isEquipment() && card.getAttachedTo() != sa.getHostCard();
}
});
return !unequipped.isEmpty(); return !unequipped.isEmpty();
} }

View File

@@ -3,8 +3,6 @@ package forge.ai.ability;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import forge.ai.ComputerUtilCard; import forge.ai.ComputerUtilCard;
import forge.ai.ComputerUtilCost; import forge.ai.ComputerUtilCost;
import forge.ai.SpellAbilityAi; import forge.ai.SpellAbilityAi;
@@ -75,12 +73,7 @@ public class SetStateAi extends SpellAbilityAi {
sa.resetTargets(); sa.resetTargets();
// select only the ones that can transform // select only the ones that can transform
CardCollection list = CardLists.filter(CardUtil.getValidCardsToTarget(sa), CardPredicates.Presets.CREATURES, new Predicate<Card>() { CardCollection list = CardLists.filter(CardUtil.getValidCardsToTarget(sa), CardPredicates.Presets.CREATURES, c -> c.canTransform(sa));
@Override
public boolean apply(Card c) {
return c.canTransform(sa);
}
});
if (list.isEmpty()) { if (list.isEmpty()) {
return false; return false;

View File

@@ -109,39 +109,33 @@ public abstract class TapAiBase extends SpellAbilityAi {
final Game game = ai.getGame(); final Game game = ai.getGame();
CardCollection tapList = CardLists.getTargetableCards(ai.getOpponents().getCardsIn(ZoneType.Battlefield), sa); CardCollection tapList = CardLists.getTargetableCards(ai.getOpponents().getCardsIn(ZoneType.Battlefield), sa);
tapList = CardLists.filter(tapList, Presets.CAN_TAP); tapList = CardLists.filter(tapList, Presets.CAN_TAP);
tapList = CardLists.filter(tapList, new Predicate<Card>() { tapList = CardLists.filter(tapList, c -> {
@Override if (c.isCreature()) {
public boolean apply(final Card c) { return true;
if (c.isCreature()) { }
for (final SpellAbility sa1 : c.getSpellAbilities()) {
if (sa1.isAbility() && sa1.getPayCosts().hasTapCost()) {
return true; return true;
} }
for (final SpellAbility sa : c.getSpellAbilities()) {
if (sa.isAbility() && sa.getPayCosts().hasTapCost()) {
return true;
}
}
return false;
} }
return false;
}); });
//use broader approach when the cost is a positive thing //use broader approach when the cost is a positive thing
if (tapList.isEmpty() && ComputerUtil.activateForCost(sa, ai)) { if (tapList.isEmpty() && ComputerUtil.activateForCost(sa, ai)) {
tapList = CardLists.getTargetableCards(ai.getOpponents().getCardsIn(ZoneType.Battlefield), sa); tapList = CardLists.getTargetableCards(ai.getOpponents().getCardsIn(ZoneType.Battlefield), sa);
tapList = CardLists.filter(tapList, new Predicate<Card>() { tapList = CardLists.filter(tapList, c -> {
@Override if (c.isCreature()) {
public boolean apply(final Card c) { return true;
if (c.isCreature()) { }
for (final SpellAbility sa12 : c.getSpellAbilities()) {
if (sa12.isAbility() && sa12.getPayCosts().hasTapCost()) {
return true; return true;
} }
for (final SpellAbility sa : c.getSpellAbilities()) {
if (sa.isAbility() && sa.getPayCosts().hasTapCost()) {
return true;
}
}
return false;
} }
return false;
}); });
} }
@@ -187,12 +181,7 @@ public abstract class TapAiBase extends SpellAbilityAi {
//Combat has already started //Combat has already started
attackers = game.getCombat().getAttackers(); attackers = game.getCombat().getAttackers();
} else { } else {
attackers = CardLists.filter(ai.getCreaturesInPlay(), new Predicate<Card>() { attackers = CardLists.filter(ai.getCreaturesInPlay(), c -> CombatUtil.canAttack(c, opp));
@Override
public boolean apply(final Card c) {
return CombatUtil.canAttack(c, opp);
}
});
attackers.remove(source); attackers.remove(source);
} }
Predicate<Card> findBlockers = CardPredicates.possibleBlockerForAtLeastOne(attackers); Predicate<Card> findBlockers = CardPredicates.possibleBlockerForAtLeastOne(attackers);
@@ -209,12 +198,7 @@ public abstract class TapAiBase extends SpellAbilityAi {
&& phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) { && phase.getPhase().isBefore(PhaseType.COMBAT_DECLARE_ATTACKERS)) {
// Tap creatures possible blockers before combat during AI's turn. // Tap creatures possible blockers before combat during AI's turn.
if (Iterables.any(tapList, CardPredicates.Presets.CREATURES)) { if (Iterables.any(tapList, CardPredicates.Presets.CREATURES)) {
List<Card> creatureList = CardLists.filter(tapList, new Predicate<Card>() { List<Card> creatureList = CardLists.filter(tapList, c -> c.isCreature() && CombatUtil.canAttack(c, opp));
@Override
public boolean apply(final Card c) {
return c.isCreature() && CombatUtil.canAttack(c, opp);
}
});
choice = ComputerUtilCard.getBestCreatureAI(creatureList); choice = ComputerUtilCard.getBestCreatureAI(creatureList);
} else { // no creatures available } else { // no creatures available
choice = ComputerUtilCard.getMostExpensivePermanentAI(tapList); choice = ComputerUtilCard.getMostExpensivePermanentAI(tapList);

View File

@@ -2,7 +2,6 @@ package forge.ai.ability;
import java.util.List; import java.util.List;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.ai.ComputerUtilCombat; import forge.ai.ComputerUtilCombat;
@@ -75,12 +74,7 @@ public class TapAllAi extends SpellAbilityAi {
// in AI's turn, check if there are possible attackers, before tapping blockers // in AI's turn, check if there are possible attackers, before tapping blockers
if (game.getPhaseHandler().isPlayerTurn(ai)) { if (game.getPhaseHandler().isPlayerTurn(ai)) {
validTappables = ai.getCardsIn(ZoneType.Battlefield); validTappables = ai.getCardsIn(ZoneType.Battlefield);
final boolean any = Iterables.any(validTappables, new Predicate<Card>() { final boolean any = Iterables.any(validTappables, c -> CombatUtil.canAttack(c) && ComputerUtilCombat.canAttackNextTurn(c));
@Override
public boolean apply(final Card c) {
return CombatUtil.canAttack(c) && ComputerUtilCombat.canAttackNextTurn(c);
}
});
return any; return any;
} }
return true; return true;

View File

@@ -3,7 +3,6 @@ package forge.ai.ability;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.ai.AiController; import forge.ai.AiController;
@@ -182,12 +181,7 @@ public class TokenAi extends SpellAbilityAi {
} else { } else {
// Flash Foliage // Flash Foliage
CardCollection list = CardLists.getTargetableCards(ai.getOpponents().getCardsIn(ZoneType.Battlefield), sa); CardCollection list = CardLists.getTargetableCards(ai.getOpponents().getCardsIn(ZoneType.Battlefield), sa);
CardCollection betterList = CardLists.filter(list, new Predicate<Card>() { CardCollection betterList = CardLists.filter(list, c -> c.getLethalDamage() == 1);
@Override
public boolean apply(Card c) {
return c.getLethalDamage() == 1;
}
});
if (!betterList.isEmpty()) { if (!betterList.isEmpty()) {
list = betterList; list = betterList;
} }

View File

@@ -1,6 +1,5 @@
package forge.ai.ability; package forge.ai.ability;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import forge.ai.*; import forge.ai.*;
@@ -408,18 +407,15 @@ public class UntapAi extends SpellAbilityAi {
// (it may actually be possible to enable this for sorceries, but that'll need some canPlay shenanigans) // (it may actually be possible to enable this for sorceries, but that'll need some canPlay shenanigans)
CardCollection playable = CardLists.filter(inHand, Presets.PERMANENTS); CardCollection playable = CardLists.filter(inHand, Presets.PERMANENTS);
CardCollection untappingCards = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), new Predicate<Card>() { CardCollection untappingCards = CardLists.filter(ai.getCardsIn(ZoneType.Battlefield), card -> {
@Override boolean hasUntapLandLogic = false;
public boolean apply(Card card) { for (SpellAbility sa1 : card.getSpellAbilities()) {
boolean hasUntapLandLogic = false; if ("PoolExtraMana".equals(sa1.getParam("AILogic"))) {
for (SpellAbility sa : card.getSpellAbilities()) { hasUntapLandLogic = true;
if ("PoolExtraMana".equals(sa.getParam("AILogic"))) { break;
hasUntapLandLogic = true;
break;
}
} }
return hasUntapLandLogic && card.isUntapped();
} }
return hasUntapLandLogic && card.isUntapped();
}); });
// TODO: currently limited to Main 2, somehow improve to let the AI use this SA at other time? // TODO: currently limited to Main 2, somehow improve to let the AI use this SA at other time?

View File

@@ -197,12 +197,7 @@ public class GameCopier {
// TODO update thisTurnCast // TODO update thisTurnCast
if (advanceToPhase != null) { if (advanceToPhase != null) {
newGame.getPhaseHandler().devAdvanceToPhase(advanceToPhase, new Runnable() { newGame.getPhaseHandler().devAdvanceToPhase(advanceToPhase, () -> GameSimulator.resolveStack(newGame, aiPlayer.getWeakestOpponent()));
@Override
public void run() {
GameSimulator.resolveStack(newGame, aiPlayer.getWeakestOpponent());
}
});
} }
return newGame; return newGame;

View File

@@ -252,32 +252,29 @@ public class GameSimulator {
// TODO: This needs to set an AI controller for all opponents, in case of multiplayer. // TODO: This needs to set an AI controller for all opponents, in case of multiplayer.
PlayerControllerAi sim = new PlayerControllerAi(game, opponent, opponent.getLobbyPlayer()); PlayerControllerAi sim = new PlayerControllerAi(game, opponent, opponent.getLobbyPlayer());
sim.setUseSimulation(true); sim.setUseSimulation(true);
opponent.runWithController(new Runnable() { opponent.runWithController(() -> {
@Override final Set<Card> allAffectedCards = new HashSet<>();
public void run() { game.getAction().checkStateEffects(false, allAffectedCards);
final Set<Card> allAffectedCards = new HashSet<>(); game.getStack().addAllTriggeredAbilitiesToStack();
while (!game.getStack().isEmpty() && !game.isGameOver()) {
debugPrint("Resolving:" + game.getStack().peekAbility());
// Resolve the top effect on the stack.
game.getStack().resolveStack();
// Evaluate state based effects as a result of resolving stack.
// Note: Needs to happen after resolve stack rather than at the
// top of the loop to ensure state effects are evaluated after the
// last resolved effect
game.getAction().checkStateEffects(false, allAffectedCards); game.getAction().checkStateEffects(false, allAffectedCards);
// Add any triggers additional triggers as a result of the above.
// Must be below state effects, since legendary rule is evaluated
// as part of state effects and trigger come afterward. (e.g. to
// correctly handle two Dark Depths - one having no counters).
game.getStack().addAllTriggeredAbilitiesToStack(); game.getStack().addAllTriggeredAbilitiesToStack();
while (!game.getStack().isEmpty() && !game.isGameOver()) {
debugPrint("Resolving:" + game.getStack().peekAbility());
// Resolve the top effect on the stack. // Continue until stack is empty.
game.getStack().resolveStack();
// Evaluate state based effects as a result of resolving stack.
// Note: Needs to happen after resolve stack rather than at the
// top of the loop to ensure state effects are evaluated after the
// last resolved effect
game.getAction().checkStateEffects(false, allAffectedCards);
// Add any triggers additional triggers as a result of the above.
// Must be below state effects, since legendary rule is evaluated
// as part of state effects and trigger come afterward. (e.g. to
// correctly handle two Dark Depths - one having no counters).
game.getStack().addAllTriggeredAbilitiesToStack();
// Continue until stack is empty.
}
} }
}, sim); }, sim);
} }

View File

@@ -59,12 +59,7 @@ public class GameStateEvaluator {
gameCopy = copier.makeCopy(null, aiPlayer); gameCopy = copier.makeCopy(null, aiPlayer);
} }
gameCopy.getPhaseHandler().devAdvanceToPhase(PhaseType.COMBAT_DAMAGE, new Runnable() { gameCopy.getPhaseHandler().devAdvanceToPhase(PhaseType.COMBAT_DAMAGE, () -> GameSimulator.resolveStack(gameCopy, aiPlayer.getWeakestOpponent()));
@Override
public void run() {
GameSimulator.resolveStack(gameCopy, aiPlayer.getWeakestOpponent());
}
});
CombatSimResult result = new CombatSimResult(); CombatSimResult result = new CombatSimResult();
result.copier = copier; result.copier = copier;
result.gameCopy = gameCopy; result.gameCopy = gameCopy;

View File

@@ -227,15 +227,13 @@ public class CardStorageReader {
// Iterate through txt files or zip archive. // Iterate through txt files or zip archive.
// Report relevant numbers to progress monitor model. // Report relevant numbers to progress monitor model.
final Set<CardRules> result = new TreeSet<>(new Comparator<CardRules>() { final Set<CardRules> result;
@Override if (loadingTokens) {
public int compare(final CardRules o1, final CardRules o2) { result = new TreeSet<>(Comparator.comparing(CardRules::getNormalizedName, String.CASE_INSENSITIVE_ORDER));
if (loadingTokens) { }
return String.CASE_INSENSITIVE_ORDER.compare(o1.getNormalizedName(), o2.getNormalizedName()); else {
} result = new TreeSet<>(Comparator.comparing(CardRules::getName, String.CASE_INSENSITIVE_ORDER));
return String.CASE_INSENSITIVE_ORDER.compare(o1.getName(), o2.getName()); }
}
});
if (loadCardsLazily) { if (loadCardsLazily) {
return result; return result;
@@ -321,14 +319,11 @@ public class CardStorageReader {
for (int iPart = 0; iPart < maxParts; iPart++) { for (int iPart = 0; iPart < maxParts; iPart++) {
final int from = iPart * filesPerPart; final int from = iPart * filesPerPart;
final int till = iPart == maxParts - 1 ? totalFiles : from + filesPerPart; final int till = iPart == maxParts - 1 ? totalFiles : from + filesPerPart;
tasks.add(new Callable<List<CardRules>>() { tasks.add(() -> {
@Override final List<CardRules> res = loadCardsInRangeFromZip(entries, from, till);
public List<CardRules> call() throws Exception{ cdl.countDown();
final List<CardRules> res = loadCardsInRangeFromZip(entries, from, till); progressObserver.report(maxParts - (int)cdl.getCount(), maxParts);
cdl.countDown(); return res;
progressObserver.report(maxParts - (int)cdl.getCount(), maxParts);
return res;
}
}); });
} }
return tasks; return tasks;
@@ -342,14 +337,11 @@ public class CardStorageReader {
for (int iPart = 0; iPart < maxParts; iPart++) { for (int iPart = 0; iPart < maxParts; iPart++) {
final int from = iPart * filesPerPart; final int from = iPart * filesPerPart;
final int till = iPart == maxParts - 1 ? totalFiles : from + filesPerPart; final int till = iPart == maxParts - 1 ? totalFiles : from + filesPerPart;
tasks.add(new Callable<List<CardRules>>() { tasks.add(() -> {
@Override final List<CardRules> res = loadCardsInRange(allFiles, from, till);
public List<CardRules> call() throws Exception{ cdl.countDown();
final List<CardRules> res = loadCardsInRange(allFiles, from, till); progressObserver.report(maxParts - (int)cdl.getCount(), maxParts);
cdl.countDown(); return res;
progressObserver.report(maxParts - (int)cdl.getCount(), maxParts);
return res;
}
}); });
} }
return tasks; return tasks;

View File

@@ -633,20 +633,17 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
cardName = cardNameRequest.cardName; cardName = cardNameRequest.cardName;
isFoil = isFoil || cardNameRequest.isFoil; isFoil = isFoil || cardNameRequest.isFoil;
List<PaperCard> candidates = getAllCards(cardName, new Predicate<PaperCard>() { List<PaperCard> candidates = getAllCards(cardName, c -> {
@Override boolean artIndexFilter = true;
public boolean apply(PaperCard c) { boolean collectorNumberFilter = true;
boolean artIndexFilter = true; boolean setFilter = c.getEdition().equalsIgnoreCase(edition.getCode()) ||
boolean collectorNumberFilter = true; c.getEdition().equalsIgnoreCase(edition.getCode2());
boolean setFilter = c.getEdition().equalsIgnoreCase(edition.getCode()) || if (artIndex > 0)
c.getEdition().equalsIgnoreCase(edition.getCode2()); artIndexFilter = (c.getArtIndex() == artIndex);
if (artIndex > 0) if ((collectorNumber != null) && (collectorNumber.length() > 0)
artIndexFilter = (c.getArtIndex() == artIndex); && !(collectorNumber.equals(IPaperCard.NO_COLLECTOR_NUMBER)))
if ((collectorNumber != null) && (collectorNumber.length() > 0) collectorNumberFilter = (c.getCollectorNumber().equals(collectorNumber));
&& !(collectorNumber.equals(IPaperCard.NO_COLLECTOR_NUMBER))) return setFilter && artIndexFilter && collectorNumberFilter;
collectorNumberFilter = (c.getCollectorNumber().equals(collectorNumber));
return setFilter && artIndexFilter && collectorNumberFilter;
}
}); });
if (candidates.isEmpty()) if (candidates.isEmpty())
return null; return null;
@@ -791,26 +788,18 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
Predicate<PaperCard> cardQueryFilter; Predicate<PaperCard> cardQueryFilter;
filter = (filter != null) ? filter : Predicates.alwaysTrue(); filter = (filter != null) ? filter : Predicates.alwaysTrue();
if (releaseDate != null) { if (releaseDate != null) {
cardQueryFilter = new Predicate<PaperCard>() { cardQueryFilter = c -> {
@Override if (c.getArtIndex() != cr.artIndex)
public boolean apply(PaperCard c) { return false; // not interested anyway!
if (c.getArtIndex() != cr.artIndex) CardEdition ed = editions.get(c.getEdition());
return false; // not interested anyway! if (ed == null) return false;
CardEdition ed = editions.get(c.getEdition()); if (releasedBeforeFlag)
if (ed == null) return false; return ed.getDate().before(releaseDate);
if (releasedBeforeFlag) else
return ed.getDate().before(releaseDate); return ed.getDate().after(releaseDate);
else
return ed.getDate().after(releaseDate);
}
}; };
} else // filter candidates based on requested artIndex } else // filter candidates based on requested artIndex
cardQueryFilter = new Predicate<PaperCard>() { cardQueryFilter = card -> card.getArtIndex() == cr.artIndex;
@Override
public boolean apply(PaperCard card) {
return card.getArtIndex() == cr.artIndex;
}
};
cardQueryFilter = Predicates.and(cardQueryFilter, filter); cardQueryFilter = Predicates.and(cardQueryFilter, filter);
cards = getAllCards(cr.cardName, cardQueryFilter); cards = getAllCards(cr.cardName, cardQueryFilter);
// Note: No need to check whether "cards" is empty; the next for loop will validate condition at L699 // Note: No need to check whether "cards" is empty; the next for loop will validate condition at L699
@@ -838,12 +827,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
return null; // nothing to do return null; // nothing to do
// Filter Cards Editions based on set preferences // Filter Cards Editions based on set preferences
List<CardEdition> acceptedEditions = Lists.newArrayList(Iterables.filter(cardEditions, new Predicate<CardEdition>() { List<CardEdition> acceptedEditions = Lists.newArrayList(Iterables.filter(cardEditions, artPref::accept));
@Override
public boolean apply(CardEdition ed) {
return artPref.accept(ed);
}
}));
/* At this point, it may be possible that Art Preference is too-strict for the requested card! /* At this point, it may be possible that Art Preference is too-strict for the requested card!
i.e. acceptedEditions.size() == 0! i.e. acceptedEditions.size() == 0!
@@ -908,13 +892,10 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
} }
public Collection<PaperCard> getUniqueCardsNoAlt() { public Collection<PaperCard> getUniqueCardsNoAlt() {
return Maps.filterEntries(this.uniqueCardsByName, new Predicate<Entry<String, PaperCard>>() { return Maps.filterEntries(this.uniqueCardsByName, e -> {
@Override if (e == null)
public boolean apply(Entry<String, PaperCard> e) { return false;
if (e == null) return e.getKey().equals(e.getValue().getName());
return false;
return e.getKey().equals(e.getValue().getName());
}
}).values(); }).values();
} }
@@ -956,60 +937,46 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
} }
public Collection<PaperCard> getAllCardsNoAlt() { public Collection<PaperCard> getAllCardsNoAlt() {
return Multimaps.filterEntries(allCardsByName, new Predicate<Entry<String, PaperCard>>() { return Multimaps.filterEntries(allCardsByName, entry -> entry.getKey().equals(entry.getValue().getName())).values();
@Override
public boolean apply(Entry<String, PaperCard> entry) {
return entry.getKey().equals(entry.getValue().getName());
}
}).values();
} }
public Collection<PaperCard> getAllNonPromoCards() { public Collection<PaperCard> getAllNonPromoCards() {
return Lists.newArrayList(Iterables.filter(getAllCards(), new Predicate<PaperCard>() { return Lists.newArrayList(Iterables.filter(getAllCards(), paperCard -> {
@Override CardEdition edition = null;
public boolean apply(final PaperCard paperCard) { try {
CardEdition edition = null; edition = editions.getEditionByCodeOrThrow(paperCard.getEdition());
try { } catch (Exception ex) {
edition = editions.getEditionByCodeOrThrow(paperCard.getEdition()); return false;
} catch (Exception ex) {
return false;
}
return edition != null && edition.getType() != Type.PROMO;
} }
return edition != null && edition.getType() != Type.PROMO;
})); }));
} }
public Collection<PaperCard> getUniqueCardsNoAltNoOnline() { public Collection<PaperCard> getUniqueCardsNoAltNoOnline() {
return Lists.newArrayList(Iterables.filter(getUniqueCardsNoAlt(), new Predicate<PaperCard>() { return Lists.newArrayList(Iterables.filter(getUniqueCardsNoAlt(), paperCard -> {
@Override CardEdition edition = null;
public boolean apply(final PaperCard paperCard) { try {
CardEdition edition = null; edition = editions.getEditionByCodeOrThrow(paperCard.getEdition());
try { if (edition.getType() == Type.ONLINE||edition.getType() == Type.FUNNY)
edition = editions.getEditionByCodeOrThrow(paperCard.getEdition());
if (edition.getType() == Type.ONLINE||edition.getType() == Type.FUNNY)
return false;
} catch (Exception ex) {
return false; return false;
} } catch (Exception ex) {
return true; return false;
} }
return true;
})); }));
} }
public Collection<PaperCard> getAllNonPromosNonReprintsNoAlt() { public Collection<PaperCard> getAllNonPromosNonReprintsNoAlt() {
return Lists.newArrayList(Iterables.filter(getAllCardsNoAlt(), new Predicate<PaperCard>() { return Lists.newArrayList(Iterables.filter(getAllCardsNoAlt(), paperCard -> {
@Override CardEdition edition = null;
public boolean apply(final PaperCard paperCard) { try {
CardEdition edition = null; edition = editions.getEditionByCodeOrThrow(paperCard.getEdition());
try { if (edition.getType() == Type.PROMO || edition.getType() == Type.REPRINT || edition.getType()==Type.COLLECTOR_EDITION)
edition = editions.getEditionByCodeOrThrow(paperCard.getEdition());
if (edition.getType() == Type.PROMO || edition.getType() == Type.REPRINT || edition.getType()==Type.COLLECTOR_EDITION)
return false;
} catch (Exception ex) {
return false; return false;
} } catch (Exception ex) {
return true; return false;
} }
return true;
})); }));
} }
@@ -1032,12 +999,7 @@ public final class CardDb implements ICardDatabase, IDeckGenPool {
} }
public List<PaperCard> getAllCardsNoAlt(String cardName) { public List<PaperCard> getAllCardsNoAlt(String cardName) {
return Lists.newArrayList(Multimaps.filterEntries(allCardsByName, new Predicate<Entry<String, PaperCard>>() { return Lists.newArrayList(Multimaps.filterEntries(allCardsByName, entry -> entry.getKey().equals(entry.getValue().getName())).get(getName(cardName)));
@Override
public boolean apply(Entry<String, PaperCard> entry) {
return entry.getKey().equals(entry.getValue().getName());
}
}).get(getName(cardName)));
} }
/** /**

View File

@@ -17,7 +17,6 @@
*/ */
package forge.card; package forge.card;
import com.google.common.base.Function;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.collect.*; import com.google.common.collect.*;
import forge.StaticData; import forge.StaticData;
@@ -423,13 +422,6 @@ public final class CardEdition implements Comparable<CardEdition> {
public Map<String, Integer> getTokens() { return tokenNormalized; } public Map<String, Integer> getTokens() { return tokenNormalized; }
public static final Function<CardEdition, String> FN_GET_CODE = new Function<CardEdition, String>() {
@Override
public String apply(final CardEdition arg1) {
return arg1.getCode();
}
};
@Override @Override
public int compareTo(final CardEdition o) { public int compareTo(final CardEdition o) {
if (o == null) { if (o == null) {
@@ -542,12 +534,12 @@ public final class CardEdition implements Comparable<CardEdition> {
private final boolean isCustomEditions; private final boolean isCustomEditions;
public Reader(File path) { public Reader(File path) {
super(path, CardEdition.FN_GET_CODE); super(path, CardEdition::getCode);
this.isCustomEditions = false; this.isCustomEditions = false;
} }
public Reader(File path, boolean isCustomEditions) { public Reader(File path, boolean isCustomEditions) {
super(path, CardEdition.FN_GET_CODE); super(path, CardEdition::getCode);
this.isCustomEditions = isCustomEditions; this.isCustomEditions = isCustomEditions;
} }
@@ -737,12 +729,7 @@ public final class CardEdition implements Comparable<CardEdition> {
return TXT_FILE_FILTER; return TXT_FILE_FILTER;
} }
public static final FilenameFilter TXT_FILE_FILTER = new FilenameFilter() { public static final FilenameFilter TXT_FILE_FILTER = (dir, name) -> name.endsWith(".txt");
@Override
public boolean accept(final File dir, final String name) {
return name.endsWith(".txt");
}
};
} }
public static class Collection extends StorageBase<CardEdition> { public static class Collection extends StorageBase<CardEdition> {
@@ -798,12 +785,7 @@ public final class CardEdition implements Comparable<CardEdition> {
public Iterable<CardEdition> getPrereleaseEditions() { public Iterable<CardEdition> getPrereleaseEditions() {
List<CardEdition> res = Lists.newArrayList(this); List<CardEdition> res = Lists.newArrayList(this);
return Iterables.filter(res, new Predicate<CardEdition>() { return Iterables.filter(res, edition -> edition.getPrerelease() != null);
@Override
public boolean apply(final CardEdition edition) {
return edition.getPrerelease() != null;
}
});
} }
public CardEdition getEditionByCodeOrThrow(final String code) { public CardEdition getEditionByCodeOrThrow(final String code) {
@@ -822,19 +804,7 @@ public final class CardEdition implements Comparable<CardEdition> {
return set == null ? "" : set.getCode2(); return set == null ? "" : set.getCode2();
} }
public final Function<String, CardEdition> FN_EDITION_BY_CODE = new Function<String, CardEdition>() { public final Comparator<PaperCard> CARD_EDITION_COMPARATOR = Comparator.comparing(c -> Collection.this.get(c.getEdition()));
@Override
public CardEdition apply(String code) {
return Collection.this.get(code);
}
};
public final Comparator<PaperCard> CARD_EDITION_COMPARATOR = new Comparator<PaperCard>() {
@Override
public int compare(PaperCard c1, PaperCard c2) {
return Collection.this.get(c1.getEdition()).compareTo(Collection.this.get(c2.getEdition()));
}
};
public IItemReader<SealedProduct.Template> getBoosterGenerator() { public IItemReader<SealedProduct.Template> getBoosterGenerator() {
return new StorageReaderBase<SealedProduct.Template>(null) { return new StorageReaderBase<SealedProduct.Template>(null) {
@@ -913,12 +883,7 @@ public final class CardEdition implements Comparable<CardEdition> {
CardDb.CardArtPreference artPreference = StaticData.instance().getCardArtPreference(); CardDb.CardArtPreference artPreference = StaticData.instance().getCardArtPreference();
Iterable<CardEdition> editionsWithBasicLands = Iterables.filter( Iterable<CardEdition> editionsWithBasicLands = Iterables.filter(
StaticData.instance().getEditions().getOrderedEditions(), StaticData.instance().getEditions().getOrderedEditions(),
com.google.common.base.Predicates.and(hasBasicLands, new Predicate<CardEdition>() { com.google.common.base.Predicates.and(hasBasicLands, artPreference::accept));
@Override
public boolean apply(CardEdition edition) {
return artPreference.accept(edition);
}
}));
Iterator<CardEdition> editionsIterator = editionsWithBasicLands.iterator(); Iterator<CardEdition> editionsIterator = editionsWithBasicLands.iterator();
List<CardEdition> selectedEditions = new ArrayList<CardEdition>(); List<CardEdition> selectedEditions = new ArrayList<CardEdition>();
while (editionsIterator.hasNext()) while (editionsIterator.hasNext())
@@ -954,19 +919,16 @@ public final class CardEdition implements Comparable<CardEdition> {
} }
} }
public static final Predicate<CardEdition> hasBasicLands = new Predicate<CardEdition>() { public static final Predicate<CardEdition> hasBasicLands = ed -> {
@Override if (ed == null) {
public boolean apply(CardEdition ed) { // Happens for new sets with "???" code
if (ed == null) { return false;
// Happens for new sets with "???" code
return false;
}
for(String landName : MagicColor.Constant.BASIC_LANDS) {
if (null == StaticData.instance().getCommonCards().getCard(landName, ed.getCode(), 0))
return false;
}
return true;
} }
for(String landName : MagicColor.Constant.BASIC_LANDS) {
if (null == StaticData.instance().getCommonCards().getCard(landName, ed.getCode(), 0))
return false;
}
return true;
}; };
} }

View File

@@ -243,7 +243,7 @@ final class CardFace implements ICardFace, Cloneable {
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public final Object clone() { public Object clone() {
try { try {
return super.clone(); return super.clone();
} catch (final Exception ex) { } catch (final Exception ex) {

View File

@@ -67,12 +67,9 @@ public final class CardFacePredicates {
} }
public static Predicate<ICardFace> cmc(final int value) { public static Predicate<ICardFace> cmc(final int value) {
return new Predicate<ICardFace>() { return input -> {
@Override ManaCost cost = input.getManaCost();
public boolean apply(ICardFace input) { return cost != null && cost.getCMC() == value;
ManaCost cost = input.getManaCost();
return cost != null && cost.getCMC() == value;
}
}; };
} }
@@ -138,20 +135,10 @@ public final class CardFacePredicates {
public static class Presets { public static class Presets {
/** The Constant isBasicLand. */ /** The Constant isBasicLand. */
public static final Predicate<ICardFace> IS_BASIC_LAND = new Predicate<ICardFace>() { public static final Predicate<ICardFace> IS_BASIC_LAND = subject -> subject.getType().isBasicLand();
@Override
public boolean apply(final ICardFace subject) {
return subject.getType().isBasicLand();
}
};
/** The Constant isNonBasicLand. */ /** The Constant isNonBasicLand. */
public static final Predicate<ICardFace> IS_NONBASIC_LAND = new Predicate<ICardFace>() { public static final Predicate<ICardFace> IS_NONBASIC_LAND = subject -> subject.getType().isLand() && !subject.getType().isBasicLand();
@Override
public boolean apply(final ICardFace subject) {
return subject.getType().isLand() && !subject.getType().isBasicLand();
}
};
/** The Constant isCreature. */ /** The Constant isCreature. */
public static final Predicate<ICardFace> IS_CREATURE = CardFacePredicates public static final Predicate<ICardFace> IS_CREATURE = CardFacePredicates

View File

@@ -17,8 +17,6 @@
*/ */
package forge.card; package forge.card;
import com.google.common.base.Function;
public enum CardRarity { public enum CardRarity {
BasicLand("L", "Basic Land"), BasicLand("L", "Basic Land"),
Common("C", "Common"), Common("C", "Common"),
@@ -58,10 +56,4 @@ public enum CardRarity {
return Unknown; return Unknown;
} }
public static final Function<CardRarity, String> FN_GET_LONG_NAME = new Function<CardRarity, String>() {
@Override
public String apply(final CardRarity rarity) {
return rarity.longName;
}
};
} }

View File

@@ -1,8 +1,6 @@
package forge.card; package forge.card;
import java.util.ArrayList; import java.util.*;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -21,28 +19,13 @@ import forge.util.PredicateString;
public final class CardRulesPredicates { public final class CardRulesPredicates {
/** The Constant isKeptInAiDecks. */ /** The Constant isKeptInAiDecks. */
public static final Predicate<CardRules> IS_KEPT_IN_AI_DECKS = new Predicate<CardRules>() { public static final Predicate<CardRules> IS_KEPT_IN_AI_DECKS = card -> !card.getAiHints().getRemAIDecks();
@Override
public boolean apply(final CardRules card) {
return !card.getAiHints().getRemAIDecks();
}
};
/** The Constant isKeptInAiLimitedDecks. */ /** The Constant isKeptInAiLimitedDecks. */
public static final Predicate<CardRules> IS_KEPT_IN_AI_LIMITED_DECKS = new Predicate<CardRules>() { public static final Predicate<CardRules> IS_KEPT_IN_AI_LIMITED_DECKS = card -> !card.getAiHints().getRemAIDecks() && !card.getAiHints().getRemNonCommanderDecks();
@Override
public boolean apply(final CardRules card) {
return !card.getAiHints().getRemAIDecks() && !card.getAiHints().getRemNonCommanderDecks();
}
};
/** The Constant isKeptInRandomDecks. */ /** The Constant isKeptInRandomDecks. */
public static final Predicate<CardRules> IS_KEPT_IN_RANDOM_DECKS = new Predicate<CardRules>() { public static final Predicate<CardRules> IS_KEPT_IN_RANDOM_DECKS = card -> !card.getAiHints().getRemRandomDecks();
@Override
public boolean apply(final CardRules card) {
return !card.getAiHints().getRemRandomDecks();
}
};
// Static builder methods - they choose concrete implementation by themselves // Static builder methods - they choose concrete implementation by themselves
/** /**
@@ -160,19 +143,9 @@ public final class CardRulesPredicates {
} }
public static Predicate<CardRules> hasCreatureType(final String... creatureTypes) { public static Predicate<CardRules> hasCreatureType(final String... creatureTypes) {
return new Predicate<CardRules>() { return card -> {
@Override if (!card.getType().isCreature()) { return false; }
public boolean apply(final CardRules card) { return !Collections.disjoint(card.getType().getCreatureTypes(), Arrays.asList(creatureTypes));
if (!card.getType().isCreature()) { return false; }
final Set<String> set = card.getType().getCreatureTypes();
for (final String creatureType : creatureTypes) {
if (set.contains(creatureType)) {
return true;
}
}
return false;
}
}; };
} }
@@ -184,12 +157,7 @@ public final class CardRulesPredicates {
* @return the predicate * @return the predicate
*/ */
public static Predicate<CardRules> hasKeyword(final String keyword) { public static Predicate<CardRules> hasKeyword(final String keyword) {
return new Predicate<CardRules>() { return card -> Iterables.any(card.getAllFaces(), cf -> cf != null && card.hasStartOfKeyword(keyword, cf));
@Override
public boolean apply(final CardRules card) {
return Iterables.any(card.getAllFaces(), cf -> cf != null && card.hasStartOfKeyword(keyword, cf));
}
};
} }
/** /**
@@ -202,22 +170,16 @@ public final class CardRulesPredicates {
* @return the predicate * @return the predicate
*/ */
public static Predicate<CardRules> deckHas(final DeckHints.Type type, final String has) { public static Predicate<CardRules> deckHas(final DeckHints.Type type, final String has) {
return new Predicate<CardRules>() { return card -> {
@Override DeckHints deckHas = card.getAiHints().getDeckHas();
public boolean apply(final CardRules card) { return deckHas != null && deckHas.isValid() && deckHas.contains(type, has);
DeckHints deckHas = card.getAiHints().getDeckHas();
return deckHas != null && deckHas.isValid() && deckHas.contains(type, has);
}
}; };
} }
public static Predicate<CardRules> deckHasExactly(final DeckHints.Type type, final String has[]) { public static Predicate<CardRules> deckHasExactly(final DeckHints.Type type, final String has[]) {
return new Predicate<CardRules>() { return card -> {
@Override DeckHints deckHas = card.getAiHints().getDeckHas();
public boolean apply(final CardRules card) { return deckHas != null && deckHas.isValid() && deckHas.is(type, has);
DeckHints deckHas = card.getAiHints().getDeckHas();
return deckHas != null && deckHas.isValid() && deckHas.is(type, has);
}
}; };
} }
@@ -348,12 +310,7 @@ public final class CardRulesPredicates {
} }
public static Predicate<CardRules> hasColorIdentity(final int colormask) { public static Predicate<CardRules> hasColorIdentity(final int colormask) {
return new Predicate<CardRules>() { return rules -> rules.getColorIdentity().hasNoColorsExcept(colormask);
@Override
public boolean apply(final CardRules rules) {
return rules.getColorIdentity().hasNoColorsExcept(colormask);
}
};
} }
public static Predicate<CardRules> canBePartnerCommanderWith(final CardRules commander) { public static Predicate<CardRules> canBePartnerCommanderWith(final CardRules commander) {
@@ -597,54 +554,19 @@ public final class CardRulesPredicates {
public static final Predicate<CardRules> IS_LAND = CardRulesPredicates.coreType(true, CardType.CoreType.Land); public static final Predicate<CardRules> IS_LAND = CardRulesPredicates.coreType(true, CardType.CoreType.Land);
/** The Constant isBasicLand. */ /** The Constant isBasicLand. */
public static final Predicate<CardRules> IS_BASIC_LAND = new Predicate<CardRules>() { public static final Predicate<CardRules> IS_BASIC_LAND = subject -> subject.getType().isBasicLand();
@Override
public boolean apply(final CardRules subject) {
return subject.getType().isBasicLand();
}
};
/** The Constant isBasicLandNotWastes. */ /** The Constant isBasicLandNotWastes. */
public static final Predicate<CardRules> IS_BASIC_LAND_NOT_WASTES = new Predicate<CardRules>() { public static final Predicate<CardRules> IS_BASIC_LAND_NOT_WASTES = subject -> !subject.getName().equals("Wastes")&&subject.getType().isBasicLand();
@Override
public boolean apply(final CardRules subject) {
return !subject.getName().equals("Wastes")&&subject.getType().isBasicLand();
}
};
/** The Constant isNonBasicLand. */ /** The Constant isNonBasicLand. */
public static final Predicate<CardRules> IS_NONBASIC_LAND = new Predicate<CardRules>() { public static final Predicate<CardRules> IS_NONBASIC_LAND = subject -> subject.getType().isLand() && !subject.getType().isBasicLand();
@Override
public boolean apply(final CardRules subject) {
return subject.getType().isLand() && !subject.getType().isBasicLand();
}
};
public static final Predicate<CardRules> CAN_BE_COMMANDER = new Predicate<CardRules>() { public static final Predicate<CardRules> CAN_BE_COMMANDER = CardRules::canBeCommander;
@Override public static final Predicate<CardRules> CAN_BE_PARTNER_COMMANDER = CardRules::canBePartnerCommander;
public boolean apply(final CardRules subject) {
return subject.canBeCommander();
}
};
public static final Predicate<CardRules> CAN_BE_PARTNER_COMMANDER = new Predicate<CardRules>() {
@Override
public boolean apply(final CardRules subject) {
return subject.canBePartnerCommander();
}
};
public static final Predicate<CardRules> CAN_BE_OATHBREAKER = new Predicate<CardRules>() { public static final Predicate<CardRules> CAN_BE_OATHBREAKER = CardRules::canBeOathbreaker;
@Override public static final Predicate<CardRules> CAN_BE_SIGNATURE_SPELL = CardRules::canBeSignatureSpell;
public boolean apply(final CardRules subject) {
return subject.canBeOathbreaker();
}
};
public static final Predicate<CardRules> CAN_BE_SIGNATURE_SPELL = new Predicate<CardRules>() {
@Override
public boolean apply(final CardRules subject) {
return subject.canBeSignatureSpell();
}
};
public static final Predicate<CardRules> IS_PLANESWALKER = CardRulesPredicates.coreType(true, CardType.CoreType.Planeswalker); public static final Predicate<CardRules> IS_PLANESWALKER = CardRulesPredicates.coreType(true, CardType.CoreType.Planeswalker);
public static final Predicate<CardRules> IS_BATTLE = CardRulesPredicates.coreType(true, CardType.CoreType.Battle); public static final Predicate<CardRules> IS_BATTLE = CardRulesPredicates.coreType(true, CardType.CoreType.Battle);

View File

@@ -493,13 +493,13 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
} }
@Override @Override
public final boolean isAttachment() { return isAura() || isEquipment() || isFortification(); } public boolean isAttachment() { return isAura() || isEquipment() || isFortification(); }
@Override @Override
public final boolean isAura() { return hasSubtype("Aura"); } public boolean isAura() { return hasSubtype("Aura"); }
@Override @Override
public final boolean isEquipment() { return hasSubtype("Equipment"); } public boolean isEquipment() { return hasSubtype("Equipment"); }
@Override @Override
public final boolean isFortification() { return hasSubtype("Fortification"); } public boolean isFortification() { return hasSubtype("Fortification"); }
public boolean isAttraction() { public boolean isAttraction() {
return hasSubtype("Attraction"); return hasSubtype("Attraction");
} }
@@ -858,71 +858,21 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
"Warlock"); "Warlock");
} }
public static class Predicates { public static class Predicates {
public static Predicate<String> IS_LAND_TYPE = new Predicate<String>() { public static Predicate<String> IS_LAND_TYPE = CardType::isALandType;
@Override public static Predicate<String> IS_BASIC_LAND_TYPE = CardType::isABasicLandType;
public boolean apply(String input) { public static Predicate<String> IS_ARTIFACT_TYPE = CardType::isAnArtifactType;
return CardType.isALandType(input);
}
};
public static Predicate<String> IS_BASIC_LAND_TYPE = new Predicate<String>() {
@Override
public boolean apply(String input) {
return CardType.isABasicLandType(input);
}
};
public static Predicate<String> IS_ARTIFACT_TYPE = new Predicate<String>() {
@Override
public boolean apply(String input) {
return CardType.isAnArtifactType(input);
}
};
public static Predicate<String> IS_CREATURE_TYPE = new Predicate<String>() { public static Predicate<String> IS_CREATURE_TYPE = CardType::isACreatureType;
@Override
public boolean apply(String input) {
return CardType.isACreatureType(input);
}
};
public static Predicate<String> IS_ENCHANTMENT_TYPE = new Predicate<String>() { public static Predicate<String> IS_ENCHANTMENT_TYPE = CardType::isAnEnchantmentType;
@Override
public boolean apply(String input) {
return CardType.isAnEnchantmentType(input);
}
};
public static Predicate<String> IS_SPELL_TYPE = new Predicate<String>() { public static Predicate<String> IS_SPELL_TYPE = CardType::isASpellType;
@Override
public boolean apply(String input) {
return CardType.isASpellType(input);
}
};
public static Predicate<String> IS_WALKER_TYPE = new Predicate<String>() { public static Predicate<String> IS_WALKER_TYPE = CardType::isAPlaneswalkerType;
@Override public static Predicate<String> IS_DUNGEON_TYPE = CardType::isADungeonType;
public boolean apply(String input) { public static Predicate<String> IS_BATTLE_TYPE = CardType::isABattleType;
return CardType.isAPlaneswalkerType(input);
}
};
public static Predicate<String> IS_DUNGEON_TYPE = new Predicate<String>() {
@Override
public boolean apply(String input) {
return CardType.isADungeonType(input);
}
};
public static Predicate<String> IS_BATTLE_TYPE = new Predicate<String>() {
@Override
public boolean apply(String input) {
return CardType.isABattleType(input);
}
};
public static Predicate<String> IS_PLANAR_TYPE = new Predicate<String>() { public static Predicate<String> IS_PLANAR_TYPE = CardType::isAPlanarType;
@Override
public boolean apply(String input) {
return CardType.isAPlanarType(input);
}
};
} }
///////// Utility methods ///////// Utility methods
@@ -1036,7 +986,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
* *
* @deprecated * @deprecated
*/ */
public static final String getSingularType(final String type) { public static String getSingularType(final String type) {
if (Constant.singularTypes.containsKey(type)) { if (Constant.singularTypes.containsKey(type)) {
return Constant.singularTypes.get(type); return Constant.singularTypes.get(type);
} }
@@ -1049,7 +999,7 @@ public final class CardType implements Comparable<CardType>, CardTypeView {
* @param type a String. * @param type a String.
* @return the corresponding type. * @return the corresponding type.
*/ */
public static final String getPluralType(final String type) { public static String getPluralType(final String type) {
if (Constant.pluralTypes.containsKey(type)) { if (Constant.pluralTypes.containsKey(type)) {
return Constant.pluralTypes.get(type); return Constant.pluralTypes.get(type);
} }

View File

@@ -172,11 +172,11 @@ public class DeckHints {
// this is case ABILITY, but other types can also use this when the implicit parsing would miss // this is case ABILITY, but other types can also use this when the implicit parsing would miss
String[] params = param.split("\\|"); String[] params = param.split("\\|");
for (String ability : params) { for (String ability : params) {
Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.deckHas(type, ability), PaperCard.FN_GET_RULES)); Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.deckHas(type, ability), PaperCard::getRules));
} }
// bonus if a DeckHas can satisfy the type with multiple ones // bonus if a DeckHas can satisfy the type with multiple ones
if (params.length > 1) { if (params.length > 1) {
Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.deckHasExactly(type, params), PaperCard.FN_GET_RULES)); Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.deckHasExactly(type, params), PaperCard::getRules));
} }
for (String p : params) { for (String p : params) {
@@ -184,19 +184,19 @@ public class DeckHints {
case COLOR: case COLOR:
ColorSet cc = ColorSet.fromNames(p); ColorSet cc = ColorSet.fromNames(p);
if (cc.isColorless()) { if (cc.isColorless()) {
Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.Presets.IS_COLORLESS, PaperCard.FN_GET_RULES)); Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.Presets.IS_COLORLESS, PaperCard::getRules));
} else { } else {
Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.isColor(cc.getColor()), PaperCard.FN_GET_RULES)); Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.isColor(cc.getColor()), PaperCard::getRules));
} }
break; break;
case KEYWORD: case KEYWORD:
Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.hasKeyword(p), PaperCard.FN_GET_RULES)); Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.hasKeyword(p), PaperCard::getRules));
break; break;
case NAME: case NAME:
Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.name(StringOp.EQUALS, p), PaperCard.FN_GET_RULES)); Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.name(StringOp.EQUALS, p), PaperCard::getRules));
break; break;
case TYPE: case TYPE:
Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.joinedType(StringOp.CONTAINS_IC, p), PaperCard.FN_GET_RULES)); Iterables.addAll(cards, getMatchingItems(cardList, CardRulesPredicates.joinedType(StringOp.CONTAINS_IC, p), PaperCard::getRules));
break; break;
case NONE: case NONE:
case ABILITY: // already done above case ABILITY: // already done above
@@ -219,19 +219,16 @@ public class DeckHints {
} else { } else {
tdb = null; tdb = null;
} }
return new Predicate<CardRules>() { return card -> {
@Override if (predicate.apply(card)) {
public boolean apply(final CardRules card) { return true;
if (predicate.apply(card)) { }
for (String tok : card.getTokens()) {
if (tdb != null && tdb.containsRule(tok) && predicate.apply(tdb.getToken(tok).getRules())) {
return true; return true;
} }
for (String tok : card.getTokens()) {
if (tdb != null && tdb.containsRule(tok) && predicate.apply(tdb.getToken(tok).getRules())) {
return true;
}
}
return false;
} }
return false;
}; };
} }

View File

@@ -1,6 +1,5 @@
package forge.card; package forge.card;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
/** /**
@@ -181,10 +180,4 @@ public final class MagicColor {
} }
} }
public static final Function<Color, String> FN_GET_SYMBOL = new Function<Color, String>() {
@Override
public String apply(final Color color) {
return color.symbol;
}
};
} }

View File

@@ -6,8 +6,6 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import com.google.common.base.Function;
import forge.deck.CardPool; import forge.deck.CardPool;
import forge.item.PaperCard; import forge.item.PaperCard;
import forge.util.ItemPool; import forge.util.ItemPool;
@@ -22,9 +20,6 @@ import forge.util.storage.StorageReaderFileSections;
* *
*/ */
public class PrintSheet { public class PrintSheet {
public static final Function<PrintSheet, String> FN_GET_KEY = new Function<PrintSheet, String>() {
@Override public final String apply(PrintSheet sheet) { return sheet.name; }
};
public static final IStorage<PrintSheet> initializePrintSheets(File sheetsFile, CardEdition.Collection editions) { public static final IStorage<PrintSheet> initializePrintSheets(File sheetsFile, CardEdition.Collection editions) {
IStorage<PrintSheet> sheets = new StorageExtendable<>("Special print runs", new PrintSheet.Reader(sheetsFile)); IStorage<PrintSheet> sheets = new StorageExtendable<>("Special print runs", new PrintSheet.Reader(sheetsFile));
@@ -156,7 +151,7 @@ public class PrintSheet {
public static class Reader extends StorageReaderFileSections<PrintSheet> { public static class Reader extends StorageReaderFileSections<PrintSheet> {
public Reader(File file) { public Reader(File file) {
super(file, PrintSheet.FN_GET_KEY); super(file, PrintSheet::getName);
} }
@Override @Override

View File

@@ -318,12 +318,7 @@ public class CardPool extends ItemPool<PaperCard> {
ListMultimap<Integer, CardEdition> editionsStatistics = this.getCardEditionsGroupedByNumberOfCards(false); ListMultimap<Integer, CardEdition> editionsStatistics = this.getCardEditionsGroupedByNumberOfCards(false);
List<Integer> frequencyValues = new ArrayList<>(editionsStatistics.keySet()); List<Integer> frequencyValues = new ArrayList<>(editionsStatistics.keySet());
// Sort in descending order // Sort in descending order
frequencyValues.sort(new Comparator<Integer>() { frequencyValues.sort(Comparator.reverseOrder());
@Override
public int compare(Integer f1, Integer f2) {
return (f1.compareTo(f2)) * -1;
}
});
float weightedMean = 0; float weightedMean = 0;
int sumWeights = 0; int sumWeights = 0;
for (Integer freq : frequencyValues) { for (Integer freq : frequencyValues) {
@@ -351,12 +346,7 @@ public class CardPool extends ItemPool<PaperCard> {
// Now Get editions corresponding to pivot frequency // Now Get editions corresponding to pivot frequency
List<CardEdition> pivotCandidates = new ArrayList<>(editionsStatistics.get(pivotFrequency)); List<CardEdition> pivotCandidates = new ArrayList<>(editionsStatistics.get(pivotFrequency));
// Now Sort candidates chronologically // Now Sort candidates chronologically
pivotCandidates.sort(new Comparator<CardEdition>() { pivotCandidates.sort(CardEdition::compareTo);
@Override
public int compare(CardEdition ed1, CardEdition ed2) {
return ed1.compareTo(ed2);
}
});
boolean searchPolicyAndPoolAreCompliant = isLatestCardArtPreference == this.isModern(); boolean searchPolicyAndPoolAreCompliant = isLatestCardArtPreference == this.isModern();
if (!searchPolicyAndPoolAreCompliant) if (!searchPolicyAndPoolAreCompliant)
Collections.reverse(pivotCandidates); // reverse to have latest-first. Collections.reverse(pivotCandidates); // reverse to have latest-first.

View File

@@ -17,8 +17,6 @@
*/ */
package forge.deck; package forge.deck;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import forge.StaticData; import forge.StaticData;
import forge.card.CardDb; import forge.card.CardDb;
@@ -125,12 +123,7 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
result.add(c.getKey()); result.add(c.getKey());
} }
if (result.size() > 1) { //sort by type so signature spell comes after oathbreaker if (result.size() > 1) { //sort by type so signature spell comes after oathbreaker
Collections.sort(result, new Comparator<PaperCard>() { Collections.sort(result, Comparator.comparing(c -> c.getRules().canBeSignatureSpell()));
@Override
public int compare(final PaperCard c1, final PaperCard c2) {
return Boolean.compare(c1.getRules().canBeSignatureSpell(), c2.getRules().canBeSignatureSpell());
}
});
} }
return result; return result;
} }
@@ -309,12 +302,7 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
continue; // pool empty, no card has been found! continue; // pool empty, no card has been found!
// Filter pool by applying DeckSection Validation schema for Card Types (to avoid inconsistencies) // Filter pool by applying DeckSection Validation schema for Card Types (to avoid inconsistencies)
CardPool filteredPool = pool.getFilteredPoolWithCardsCount(new Predicate<PaperCard>() { CardPool filteredPool = pool.getFilteredPoolWithCardsCount(deckSection::validate);
@Override
public boolean apply(PaperCard input) {
return deckSection.validate(input);
}
});
// Add all the cards from ValidPool anyway! // Add all the cards from ValidPool anyway!
List<String> whiteList = validatedSections.getOrDefault(s.getKey(), null); List<String> whiteList = validatedSections.getOrDefault(s.getKey(), null);
if (whiteList == null) if (whiteList == null)
@@ -326,12 +314,7 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
validatedSections.put(s.getKey(), whiteList); validatedSections.put(s.getKey(), whiteList);
if (filteredPool.countDistinct() != pool.countDistinct()) { if (filteredPool.countDistinct() != pool.countDistinct()) {
CardPool blackList = pool.getFilteredPoolWithCardsCount(new Predicate<PaperCard>() { CardPool blackList = pool.getFilteredPoolWithCardsCount(input -> !(deckSection.validate(input)));
@Override
public boolean apply(PaperCard input) {
return !(deckSection.validate(input));
}
});
for (Entry<PaperCard, Integer> entry : blackList) { for (Entry<PaperCard, Integer> entry : blackList) {
DeckSection cardSection = DeckSection.matchingSection(entry.getKey()); DeckSection cardSection = DeckSection.matchingSection(entry.getKey());
@@ -513,13 +496,6 @@ public class Deck extends DeckBase implements Iterable<Entry<DeckSection, CardPo
return releaseDate.compareTo(referenceReleaseDate) < 0; return releaseDate.compareTo(referenceReleaseDate) < 0;
} }
public static final Function<Deck, String> FN_NAME_SELECTOR = new Function<Deck, String>() {
@Override
public String apply(Deck arg1) {
return arg1.getName();
}
};
/* (non-Javadoc) /* (non-Javadoc)
* @see java.lang.Iterable#iterator() * @see java.lang.Iterable#iterator()
*/ */

View File

@@ -59,25 +59,16 @@ public enum DeckFormat {
return null; return null;
} }
}, },
Commander ( Range.is(99), Range.between(0, 10), 1, null, new Predicate<PaperCard>() { Commander ( Range.is(99), Range.between(0, 10), 1, null,
@Override card -> StaticData.instance().getCommanderPredicate().apply(card)
public boolean apply(PaperCard card) { ),
return StaticData.instance().getCommanderPredicate().apply(card); Oathbreaker ( Range.is(58), Range.between(0, 10), 1, null,
} card -> StaticData.instance().getOathbreakerPredicate().apply(card)
}), ),
Oathbreaker ( Range.is(58), Range.between(0, 10), 1, null, new Predicate<PaperCard>() {
@Override
public boolean apply(PaperCard card) {
return StaticData.instance().getOathbreakerPredicate().apply(card);
}
}),
Pauper ( Range.is(60), Range.between(0, 10), 1), Pauper ( Range.is(60), Range.between(0, 10), 1),
Brawl ( Range.is(59), Range.between(0, 15), 1, null, new Predicate<PaperCard>() { Brawl ( Range.is(59), Range.between(0, 15), 1, null,
@Override card -> StaticData.instance().getBrawlPredicate().apply(card)
public boolean apply(PaperCard card) { ),
return StaticData.instance().getBrawlPredicate().apply(card);
}
}),
TinyLeaders ( Range.is(49), Range.between(0, 10), 1, new Predicate<CardRules>() { TinyLeaders ( Range.is(49), Range.between(0, 10), 1, new Predicate<CardRules>() {
private final Set<String> bannedCards = ImmutableSet.of( private final Set<String> bannedCards = ImmutableSet.of(
"Ancestral Recall", "Balance", "Black Lotus", "Black Vise", "Channel", "Chaos Orb", "Contract From Below", "Counterbalance", "Darkpact", "Demonic Attorney", "Demonic Tutor", "Earthcraft", "Edric, Spymaster of Trest", "Falling Star", "Ancestral Recall", "Balance", "Black Lotus", "Black Vise", "Channel", "Chaos Orb", "Contract From Below", "Counterbalance", "Darkpact", "Demonic Attorney", "Demonic Tutor", "Earthcraft", "Edric, Spymaster of Trest", "Falling Star",
@@ -501,59 +492,41 @@ public enum DeckFormat {
} }
public Predicate<Deck> isLegalDeckPredicate() { public Predicate<Deck> isLegalDeckPredicate() {
return new Predicate<Deck>() { return deck -> getDeckConformanceProblem(deck) == null;
@Override
public boolean apply(Deck deck) {
return getDeckConformanceProblem(deck) == null;
}
};
} }
public Predicate<Deck> hasLegalCardsPredicate(boolean enforceDeckLegality) { public Predicate<Deck> hasLegalCardsPredicate(boolean enforceDeckLegality) {
return new Predicate<Deck>() { return deck -> {
@Override if (!enforceDeckLegality)
public boolean apply(Deck deck) {
if (!enforceDeckLegality)
return true;
if (cardPoolFilter != null) {
for (final Entry<PaperCard, Integer> cp : deck.getAllCardsInASinglePool()) {
if (!cardPoolFilter.apply(cp.getKey().getRules())) {
return false;
}
}
}
if (paperCardPoolFilter != null) {
for (final Entry<PaperCard, Integer> cp : deck.getAllCardsInASinglePool()) {
if (!paperCardPoolFilter.apply(cp.getKey())) {
System.err.println(
"Excluding deck: '" + deck.toString() +
"' Reason: '" + cp.getKey() + "' is not legal."
);
return false;
}
}
}
return true; return true;
if (cardPoolFilter != null) {
for (final Entry<PaperCard, Integer> cp : deck.getAllCardsInASinglePool()) {
if (!cardPoolFilter.apply(cp.getKey().getRules())) {
return false;
}
}
} }
if (paperCardPoolFilter != null) {
for (final Entry<PaperCard, Integer> cp : deck.getAllCardsInASinglePool()) {
if (!paperCardPoolFilter.apply(cp.getKey())) {
System.err.println(
"Excluding deck: '" + deck.toString() +
"' Reason: '" + cp.getKey() + "' is not legal."
);
return false;
}
}
}
return true;
}; };
} }
public Predicate<PaperCard> isLegalCardPredicate() { public Predicate<PaperCard> isLegalCardPredicate() {
return new Predicate<PaperCard>() { return this::isLegalCard;
@Override
public boolean apply(PaperCard card) {
return isLegalCard(card);
}
};
} }
public Predicate<PaperCard> isLegalCommanderPredicate() { public Predicate<PaperCard> isLegalCommanderPredicate() {
return new Predicate<PaperCard>() { return card -> isLegalCommander(card.getRules());
@Override
public boolean apply(PaperCard card) {
return isLegalCommander(card.getRules());
}
};
} }
public Predicate<PaperCard> isLegalCardForCommanderPredicate(List<PaperCard> commanders) { public Predicate<PaperCard> isLegalCardForCommanderPredicate(List<PaperCard> commanders) {
@@ -567,6 +540,6 @@ public enum DeckFormat {
//Notably, no partner ability or combination of partner abilities can ever let a player have more than two commanders. //Notably, no partner ability or combination of partner abilities can ever let a player have more than two commanders.
predicate = Predicates.or(predicate, CardRulesPredicates.canBePartnerCommanderWith(commanders.get(0).getRules())); predicate = Predicates.or(predicate, CardRulesPredicates.canBePartnerCommanderWith(commanders.get(0).getRules()));
} }
return Predicates.compose(predicate, PaperCard.FN_GET_RULES); return Predicates.compose(predicate, PaperCard::getRules);
} }
} }

View File

@@ -23,8 +23,6 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import com.google.common.base.Function;
/** /**
* Related decks usually pertaining to a limited experience like draft or sealed * Related decks usually pertaining to a limited experience like draft or sealed
* This file represents a human player deck and all opposing AI decks * This file represents a human player deck and all opposing AI decks
@@ -32,6 +30,10 @@ import com.google.common.base.Function;
*/ */
public class DeckGroup extends DeckBase { public class DeckGroup extends DeckBase {
public DeckGroup() {
this("");
}
/** /**
* Instantiates a new deck group. * Instantiates a new deck group.
* *
@@ -137,21 +139,6 @@ public class DeckGroup extends DeckBase {
return new DeckGroup(name0); return new DeckGroup(name0);
} }
public static final Function<DeckGroup, String> FN_NAME_SELECTOR = new Function<DeckGroup, String>() {
@Override
public String apply(DeckGroup arg1) {
return arg1.getName();
}
};
public static final Function<DeckGroup, Deck> FN_HUMAN_DECK = new Function<DeckGroup, Deck>() {
@Override
public Deck apply(DeckGroup arg1) {
return arg1.humanDeck;
}
};
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
return humanDeck == null || humanDeck.isEmpty(); return humanDeck == null || humanDeck.isEmpty();

View File

@@ -66,63 +66,42 @@ public enum DeckSection {
} }
private static class Validators { private static class Validators {
static final Function<PaperCard, Boolean> DECK_AND_SIDE_VALIDATOR = new Function<PaperCard, Boolean>() { static final Function<PaperCard, Boolean> DECK_AND_SIDE_VALIDATOR = card -> {
@Override CardType t = card.getRules().getType();
public Boolean apply(PaperCard card) { // NOTE: Same rules applies to both Deck and Side, despite "Conspiracy cards" are allowed
CardType t = card.getRules().getType(); // in the SideBoard (see Rule 313.2)
// NOTE: Same rules applies to both Deck and Side, despite "Conspiracy cards" are allowed // Those will be matched later, in case (see `Deck::validateDeferredSections`)
// in the SideBoard (see Rule 313.2) return !t.isConspiracy() && !t.isDungeon() && !t.isPhenomenon() && !t.isPlane() && !t.isScheme() && !t.isVanguard();
// Those will be matched later, in case (see `Deck::validateDeferredSections`)
return !t.isConspiracy() && !t.isDungeon() && !t.isPhenomenon() && !t.isPlane() && !t.isScheme() && !t.isVanguard();
}
}; };
static final Function<PaperCard, Boolean> COMMANDER_VALIDATOR = new Function<PaperCard, Boolean>() { static final Function<PaperCard, Boolean> COMMANDER_VALIDATOR = card -> {
@Override CardType t = card.getRules().getType();
public Boolean apply(PaperCard card) { return card.getRules().canBeCommander() || t.isPlaneswalker() || card.getRules().canBeOathbreaker() || card.getRules().canBeSignatureSpell();
CardType t = card.getRules().getType();
return card.getRules().canBeCommander() || t.isPlaneswalker() || card.getRules().canBeOathbreaker() || card.getRules().canBeSignatureSpell();
}
}; };
static final Function<PaperCard, Boolean> PLANES_VALIDATOR = new Function<PaperCard, Boolean>() { static final Function<PaperCard, Boolean> PLANES_VALIDATOR = card -> {
@Override CardType t = card.getRules().getType();
public Boolean apply(PaperCard card) { return t.isPlane() || t.isPhenomenon();
CardType t = card.getRules().getType();
return t.isPlane() || t.isPhenomenon();
}
}; };
static final Function<PaperCard, Boolean> DUNGEON_VALIDATOR = new Function<PaperCard, Boolean>() { static final Function<PaperCard, Boolean> DUNGEON_VALIDATOR = card -> {
@Override CardType t = card.getRules().getType();
public Boolean apply(PaperCard card) { return t.isDungeon();
CardType t = card.getRules().getType();
return t.isDungeon();
}
}; };
static final Function<PaperCard, Boolean> SCHEME_VALIDATOR = new Function<PaperCard, Boolean>() { static final Function<PaperCard, Boolean> SCHEME_VALIDATOR = card -> {
@Override CardType t = card.getRules().getType();
public Boolean apply(PaperCard card) { return t.isScheme();
CardType t = card.getRules().getType();
return t.isScheme();
}
}; };
static final Function<PaperCard, Boolean> CONSPIRACY_VALIDATOR = new Function<PaperCard, Boolean>() { static final Function<PaperCard, Boolean> CONSPIRACY_VALIDATOR = card -> {
@Override CardType t = card.getRules().getType();
public Boolean apply(PaperCard card) { return t.isConspiracy();
CardType t = card.getRules().getType();
return t.isConspiracy();
}
}; };
static final Function<PaperCard, Boolean> AVATAR_VALIDATOR = new Function<PaperCard, Boolean>() { static final Function<PaperCard, Boolean> AVATAR_VALIDATOR = card -> {
@Override CardType t = card.getRules().getType();
public Boolean apply(PaperCard card) { return t.isVanguard();
CardType t = card.getRules().getType();
return t.isVanguard();
}
}; };
static final Function<PaperCard, Boolean> ATTRACTION_VALIDATOR = card -> { static final Function<PaperCard, Boolean> ATTRACTION_VALIDATOR = card -> {

View File

@@ -92,12 +92,12 @@ public abstract class DeckGeneratorBase {
final Iterable<PaperCard> cards = selectCardsOfMatchingColorForPlayer(forAi); final Iterable<PaperCard> cards = selectCardsOfMatchingColorForPlayer(forAi);
// build subsets based on type // build subsets based on type
final Iterable<PaperCard> creatures = Iterables.filter(cards, Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard.FN_GET_RULES)); final Iterable<PaperCard> creatures = Iterables.filter(cards, Predicates.compose(CardRulesPredicates.Presets.IS_CREATURE, PaperCard::getRules));
final int creatCnt = (int) Math.ceil(getCreaturePercentage() * size); final int creatCnt = (int) Math.ceil(getCreaturePercentage() * size);
trace.append("Creatures to add:").append(creatCnt).append("\n"); trace.append("Creatures to add:").append(creatCnt).append("\n");
addCmcAdjusted(creatures, creatCnt, cmcLevels); addCmcAdjusted(creatures, creatCnt, cmcLevels);
Predicate<PaperCard> preSpells = Predicates.compose(CardRulesPredicates.Presets.IS_NON_CREATURE_SPELL, PaperCard.FN_GET_RULES); Predicate<PaperCard> preSpells = Predicates.compose(CardRulesPredicates.Presets.IS_NON_CREATURE_SPELL, PaperCard::getRules);
final Iterable<PaperCard> spells = Iterables.filter(cards, preSpells); final Iterable<PaperCard> spells = Iterables.filter(cards, preSpells);
final int spellCnt = (int) Math.ceil(getSpellPercentage() * size); final int spellCnt = (int) Math.ceil(getSpellPercentage() * size);
trace.append("Spells to add:").append(spellCnt).append("\n"); trace.append("Spells to add:").append(spellCnt).append("\n");
@@ -114,9 +114,9 @@ public abstract class DeckGeneratorBase {
Predicate<PaperCard> isSetBasicLand; Predicate<PaperCard> isSetBasicLand;
if (edition !=null){ if (edition !=null){
isSetBasicLand = Predicates.and(IPaperCard.Predicates.printedInSet(edition), isSetBasicLand = Predicates.and(IPaperCard.Predicates.printedInSet(edition),
Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES)); Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard::getRules));
}else{ }else{
isSetBasicLand = Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES); isSetBasicLand = Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard::getRules);
} }
landPool = new DeckGenPool(StaticData.instance().getCommonCards().getAllCards(isSetBasicLand)); landPool = new DeckGenPool(StaticData.instance().getCommonCards().getAllCards(isSetBasicLand));
@@ -239,7 +239,7 @@ public abstract class DeckGeneratorBase {
addSome(targetSize - actualSize, tDeck.toFlatList()); addSome(targetSize - actualSize, tDeck.toFlatList());
} }
else if (actualSize > targetSize) { else if (actualSize > targetSize) {
Predicate<PaperCard> exceptBasicLand = Predicates.not(Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard.FN_GET_RULES)); Predicate<PaperCard> exceptBasicLand = Predicates.not(Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard::getRules));
for (int i = 0; i < 3 && actualSize > targetSize; i++) { for (int i = 0; i < 3 && actualSize > targetSize; i++) {
Iterable<PaperCard> matchingCards = Iterables.filter(tDeck.toFlatList(), exceptBasicLand); Iterable<PaperCard> matchingCards = Iterables.filter(tDeck.toFlatList(), exceptBasicLand);
@@ -266,7 +266,7 @@ public abstract class DeckGeneratorBase {
float requestedOverTotal = (float)cnt / totalWeight; float requestedOverTotal = (float)cnt / totalWeight;
for (ImmutablePair<FilterCMC, Integer> pair : cmcLevels) { for (ImmutablePair<FilterCMC, Integer> pair : cmcLevels) {
Iterable<PaperCard> matchingCards = Iterables.filter(source, Predicates.compose(pair.getLeft(), PaperCard.FN_GET_RULES)); Iterable<PaperCard> matchingCards = Iterables.filter(source, Predicates.compose(pair.getLeft(), PaperCard::getRules));
int cmcCountForPool = (int) Math.ceil(pair.getRight() * desiredOverTotal); int cmcCountForPool = (int) Math.ceil(pair.getRight() * desiredOverTotal);
int addOfThisCmc = Math.round(pair.getRight() * requestedOverTotal); int addOfThisCmc = Math.round(pair.getRight() * requestedOverTotal);
@@ -288,18 +288,15 @@ public abstract class DeckGeneratorBase {
// remove cards that generated decks don't like // remove cards that generated decks don't like
Predicate<CardRules> canPlay = forAi ? AI_CAN_PLAY : CardRulesPredicates.IS_KEPT_IN_RANDOM_DECKS; Predicate<CardRules> canPlay = forAi ? AI_CAN_PLAY : CardRulesPredicates.IS_KEPT_IN_RANDOM_DECKS;
Predicate<CardRules> hasColor = new MatchColorIdentity(colors); Predicate<CardRules> hasColor = new MatchColorIdentity(colors);
Predicate<CardRules> canUseInFormat = new Predicate<CardRules>() { Predicate<CardRules> canUseInFormat = c -> {
@Override // FIXME: should this be limited to AI only (!forAi) or should it be generally applied to all random generated decks?
public boolean apply(CardRules c) { return !c.getAiHints().getRemNonCommanderDecks() || format.hasCommander();
// FIXME: should this be limited to AI only (!forAi) or should it be generally applied to all random generated decks?
return !c.getAiHints().getRemNonCommanderDecks() || format.hasCommander();
}
}; };
if (useArtifacts) { if (useArtifacts) {
hasColor = Predicates.or(hasColor, COLORLESS_CARDS); hasColor = Predicates.or(hasColor, COLORLESS_CARDS);
} }
return Iterables.filter(pool.getAllCards(), Predicates.compose(Predicates.and(canPlay, hasColor, canUseInFormat), PaperCard.FN_GET_RULES)); return Iterables.filter(pool.getAllCards(), Predicates.compose(Predicates.and(canPlay, hasColor, canUseInFormat), PaperCard::getRules));
} }
protected static Map<String, Integer> countLands(ItemPool<PaperCard> outList) { protected static Map<String, Integer> countLands(ItemPool<PaperCard> outList) {
@@ -337,12 +334,9 @@ public abstract class DeckGeneratorBase {
public static final Predicate<CardRules> AI_CAN_PLAY = Predicates.and(CardRulesPredicates.IS_KEPT_IN_AI_DECKS, CardRulesPredicates.IS_KEPT_IN_RANDOM_DECKS); public static final Predicate<CardRules> AI_CAN_PLAY = Predicates.and(CardRulesPredicates.IS_KEPT_IN_AI_DECKS, CardRulesPredicates.IS_KEPT_IN_RANDOM_DECKS);
public static final Predicate<CardRules> COLORLESS_CARDS = new Predicate<CardRules>() { public static final Predicate<CardRules> COLORLESS_CARDS = c -> {
@Override ManaCost mc = c.getManaCost();
public boolean apply(CardRules c) { return c.getColorIdentity().isColorless() && !mc.isNoCost();
ManaCost mc = c.getManaCost();
return c.getColorIdentity().isColorless() && !mc.isNoCost();
}
}; };
public static class MatchColorIdentity implements Predicate<CardRules> { public static class MatchColorIdentity implements Predicate<CardRules> {
@@ -401,7 +395,7 @@ public abstract class DeckGeneratorBase {
Predicate<CardRules> dualLandFilter = CardRulesPredicates.coreType(true, CardType.CoreType.Land); Predicate<CardRules> dualLandFilter = CardRulesPredicates.coreType(true, CardType.CoreType.Land);
Predicate<CardRules> exceptBasicLand = Predicates.not(CardRulesPredicates.Presets.IS_BASIC_LAND); Predicate<CardRules> exceptBasicLand = Predicates.not(CardRulesPredicates.Presets.IS_BASIC_LAND);
Iterable<PaperCard> landCards = pool.getAllCards(Predicates.compose(Predicates.and(dualLandFilter, exceptBasicLand, canPlay), PaperCard.FN_GET_RULES)); Iterable<PaperCard> landCards = pool.getAllCards(Predicates.compose(Predicates.and(dualLandFilter, exceptBasicLand, canPlay), PaperCard::getRules));
Iterable<String> dualLandPatterns = Arrays.asList("Add \\{([WUBRG])\\} or \\{([WUBRG])\\}", Iterable<String> dualLandPatterns = Arrays.asList("Add \\{([WUBRG])\\} or \\{([WUBRG])\\}",
"Add \\{([WUBRG])\\}, \\{([WUBRG])\\}, or \\{([WUBRG])\\}", "Add \\{([WUBRG])\\}, \\{([WUBRG])\\}, or \\{([WUBRG])\\}",
"Add \\{([WUBRG])\\}\\{([WUBRG])\\}", "Add \\{([WUBRG])\\}\\{([WUBRG])\\}",

View File

@@ -21,6 +21,7 @@ import java.io.File;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.util.List; import java.util.List;
import forge.deck.DeckBase;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@@ -45,7 +46,7 @@ public class DeckGroupSerializer extends StorageReaderFolder<DeckGroup> implemen
* @param deckDir0 the deck dir0 * @param deckDir0 the deck dir0
*/ */
public DeckGroupSerializer(final File deckDir0, String rootDir0) { public DeckGroupSerializer(final File deckDir0, String rootDir0) {
super(deckDir0, DeckGroup.FN_NAME_SELECTOR); super(deckDir0, DeckBase::getName);
rootDir = rootDir0; rootDir = rootDir0;
} }
@@ -122,16 +123,12 @@ public class DeckGroupSerializer extends StorageReaderFolder<DeckGroup> implemen
*/ */
@Override @Override
protected FilenameFilter getFileFilter() { protected FilenameFilter getFileFilter() {
return new FilenameFilter() { return (dir, name) -> {
final File testSubject = new File(dir, name);
@Override final boolean isVisibleFolder = testSubject.isDirectory() && !testSubject.isHidden();
public boolean accept(final File dir, final String name) { final boolean hasGoodName = StringUtils.isNotEmpty(name) && !name.startsWith(".");
final File testSubject = new File(dir, name); final File fileHumanDeck = new File(testSubject, DeckGroupSerializer.humanDeckFile);
final boolean isVisibleFolder = testSubject.isDirectory() && !testSubject.isHidden(); return isVisibleFolder && hasGoodName && fileHumanDeck.exists();
final boolean hasGoodName = StringUtils.isNotEmpty(name) && !name.startsWith(".");
final File fileHumanDeck = new File(testSubject, DeckGroupSerializer.humanDeckFile);
return isVisibleFolder && hasGoodName && fileHumanDeck.exists();
}
}; };
} }

View File

@@ -23,6 +23,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import forge.deck.Deck; import forge.deck.Deck;
import forge.deck.DeckBase;
import forge.util.FileSection; import forge.util.FileSection;
import forge.util.FileUtil; import forge.util.FileUtil;
import forge.util.IItemReader; import forge.util.IItemReader;
@@ -39,19 +40,14 @@ public class DeckStorage extends StorageReaderFolder<Deck> implements IItemSeria
private final boolean moveWronglyNamedDecks; private final boolean moveWronglyNamedDecks;
/** Constant <code>DCKFileFilter</code>. */ /** Constant <code>DCKFileFilter</code>. */
public static final FilenameFilter DCK_FILE_FILTER = new FilenameFilter() { public static final FilenameFilter DCK_FILE_FILTER = (dir, name) -> name.endsWith(FILE_EXTENSION);
@Override
public boolean accept(final File dir, final String name) {
return name.endsWith(FILE_EXTENSION);
}
};
public DeckStorage(final File deckDir0, final String rootDir0) { public DeckStorage(final File deckDir0, final String rootDir0) {
this(deckDir0, rootDir0, false); this(deckDir0, rootDir0, false);
} }
public DeckStorage(final File deckDir0, final String rootDir0, boolean moveWrongDecks) { public DeckStorage(final File deckDir0, final String rootDir0, boolean moveWrongDecks) {
super(deckDir0, Deck.FN_NAME_SELECTOR); super(deckDir0, DeckBase::getName);
rootDir = rootDir0; rootDir = rootDir0;
moveWronglyNamedDecks = moveWrongDecks; moveWronglyNamedDecks = moveWrongDecks;
} }

View File

@@ -22,24 +22,19 @@ import java.util.ArrayList;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Function;
import forge.ImageKeys; import forge.ImageKeys;
import forge.StaticData; import forge.StaticData;
import forge.card.CardEdition; import forge.card.CardEdition;
public class BoosterBox extends BoxedProduct { public class BoosterBox extends BoxedProduct {
public static final Function<CardEdition, BoosterBox> FN_FROM_SET = new Function<CardEdition, BoosterBox>() {
@Override public static BoosterBox fromSet(CardEdition edition) {
public BoosterBox apply(final CardEdition arg1) { if (edition.getBoosterBoxCount() <= 0) {
if (arg1.getBoosterBoxCount() <= 0) { return null;
return null;
}
BoosterBox.Template d = new Template(arg1);
if (d == null) { return null; }
return new BoosterBox(arg1.getName(), d, d.cntBoosters);
} }
}; BoosterBox.Template d = new Template(edition);
return new BoosterBox(edition.getName(), d, d.cntBoosters);
}
private final BoosterBox.Template fpData; private final BoosterBox.Template fpData;

View File

@@ -20,7 +20,6 @@ package forge.item;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import forge.ImageKeys; import forge.ImageKeys;
@@ -33,28 +32,22 @@ public class BoosterPack extends SealedProduct {
private final int artIndex; private final int artIndex;
private final int hash; private final int hash;
public static final Function<CardEdition, BoosterPack> FN_FROM_SET = new Function<CardEdition, BoosterPack>() { public static BoosterPack fromSet(CardEdition edition) {
@Override String boosterKind = edition.getRandomBoosterKind();
public BoosterPack apply(final CardEdition edition) { Template d = edition.getBoosterTemplate(boosterKind);
String boosterKind = edition.getRandomBoosterKind(); StringBuilder sb = new StringBuilder(edition.getName());
Template d = edition.getBoosterTemplate(boosterKind); sb.append(" ").append(boosterKind);
StringBuilder sb = new StringBuilder(edition.getName()); return new BoosterPack(sb.toString(), d);
sb.append(" ").append(boosterKind); }
return new BoosterPack(sb.toString(), d);
}
};
public static final Function<String, BoosterPack> FN_FROM_COLOR = new Function<String, BoosterPack>() { public static BoosterPack fromColor(final String color) {
@Override return new BoosterPack(color, new Template("?", ImmutableList.of(
public BoosterPack apply(final String color) { Pair.of(BoosterSlots.COMMON + ":color(\"" + color + "\"):!" + BoosterSlots.LAND, 11),
return new BoosterPack(color, new Template("?", ImmutableList.of( Pair.of(BoosterSlots.UNCOMMON + ":color(\"" + color + "\"):!" + BoosterSlots.LAND, 3),
Pair.of(BoosterSlots.COMMON + ":color(\"" + color + "\"):!" + BoosterSlots.LAND, 11), Pair.of(BoosterSlots.RARE_MYTHIC + ":color(\"" + color + "\"):!" + BoosterSlots.LAND, 1),
Pair.of(BoosterSlots.UNCOMMON + ":color(\"" + color + "\"):!" + BoosterSlots.LAND, 3), Pair.of(BoosterSlots.LAND + ":color(\"" + color + "\")", 1))
Pair.of(BoosterSlots.RARE_MYTHIC + ":color(\"" + color + "\"):!" + BoosterSlots.LAND, 1), ));
Pair.of(BoosterSlots.LAND + ":color(\"" + color + "\")", 1)) }
));
}
};
public BoosterPack(final String name0, final Template boosterData) { public BoosterPack(final String name0, final Template boosterData) {
super(name0, boosterData); super(name0, boosterData);

View File

@@ -22,25 +22,20 @@ import java.util.List;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import com.google.common.base.Function;
import forge.ImageKeys; import forge.ImageKeys;
import forge.StaticData; import forge.StaticData;
import forge.card.CardEdition; import forge.card.CardEdition;
import forge.item.generation.BoosterGenerator; import forge.item.generation.BoosterGenerator;
public class FatPack extends BoxedProduct { public class FatPack extends BoxedProduct {
public static final Function<CardEdition, FatPack> FN_FROM_SET = new Function<CardEdition, FatPack>() { public static FatPack fromSet(final CardEdition edition) {
@Override int boosters = edition.getFatPackCount();
public FatPack apply(final CardEdition edition) { if (boosters <= 0) { return null; }
int boosters = edition.getFatPackCount();
if (boosters <= 0) { return null; }
FatPack.Template d = new Template(edition); FatPack.Template d = new Template(edition);
if (d == null || null == StaticData.instance().getBoosters().get(d.getEdition())) { return null; } if (null == StaticData.instance().getBoosters().get(d.getEdition())) { return null; }
return new FatPack(edition.getName(), d, d.cntBoosters); return new FatPack(edition.getName(), d, d.cntBoosters);
} }
};
private final FatPack.Template fpData; private final FatPack.Template fpData;

View File

@@ -225,19 +225,8 @@ public interface IPaperCard extends InventoryItem, Serializable {
public static final Predicate<PaperCard> IS_WHITE = Predicates.color(true, false, MagicColor.WHITE); public static final Predicate<PaperCard> IS_WHITE = Predicates.color(true, false, MagicColor.WHITE);
public static final Predicate<PaperCard> IS_COLORLESS = Predicates.color(true, true, MagicColor.COLORLESS); public static final Predicate<PaperCard> IS_COLORLESS = Predicates.color(true, true, MagicColor.COLORLESS);
public static final Predicate<PaperCard> IS_UNREBALANCED = new Predicate<PaperCard>() { public static final Predicate<PaperCard> IS_UNREBALANCED = PaperCard::isUnRebalanced;
@Override public static final Predicate<PaperCard> IS_REBALANCED = PaperCard::isRebalanced;
public boolean apply(PaperCard input) {
return input.isUnRebalanced();
}
};
public static final Predicate<PaperCard> IS_REBALANCED = new Predicate<PaperCard>() {
@Override
public boolean apply(PaperCard input) {
return input.isRebalanced();
}
};
} }
} }

View File

@@ -19,26 +19,14 @@ public abstract class ItemPredicate {
* *
* @return the predicate * @return the predicate
*/ */
public static final Predicate<InventoryItem> IsTournamentPack = new Predicate<InventoryItem>() { public static final Predicate<InventoryItem> IsTournamentPack = card -> card instanceof TournamentPack && !((TournamentPack) card).isStarterDeck();
@Override
public boolean apply(final InventoryItem card) {
return card instanceof TournamentPack && !((TournamentPack) card).isStarterDeck();
}
};
/** /**
* Checks that the inventory item is a Starter Deck. * Checks that the inventory item is a Starter Deck.
* *
* @return the predicate * @return the predicate
*/ */
public static final Predicate<InventoryItem> IsStarterDeck = new Predicate<InventoryItem>() { public static final Predicate<InventoryItem> IsStarterDeck = card -> card instanceof TournamentPack && ((TournamentPack) card).isStarterDeck();
@Override
public boolean apply(final InventoryItem card) {
return card instanceof TournamentPack && ((TournamentPack) card).isStarterDeck();
}
};
/** /**
* Checks that the inventory item is a Prebuilt Deck. * Checks that the inventory item is a Prebuilt Deck.

View File

@@ -17,7 +17,6 @@
*/ */
package forge.item; package forge.item;
import com.google.common.base.Function;
import forge.ImageKeys; import forge.ImageKeys;
import forge.StaticData; import forge.StaticData;
import forge.card.*; import forge.card.*;
@@ -160,22 +159,6 @@ public class PaperCard implements Comparable<IPaperCard>, InventoryItemFromSet,
return hasImage; return hasImage;
} }
/**
* Lambda to get rules for selects from list of printed cards.
*/
public static final Function<PaperCard, CardRules> FN_GET_RULES = new Function<PaperCard, CardRules>() {
@Override
public CardRules apply(final PaperCard from) {
return from.rules;
}
};
public static final Function<PaperCard, String> FN_GET_NAME = new Function<PaperCard, String>() {
@Override
public String apply(final PaperCard from) {
return from.getName();
}
};
public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0) { public PaperCard(final CardRules rules0, final String edition0, final CardRarity rarity0) {
this(rules0, edition0, rarity0, IPaperCard.DEFAULT_ART_INDEX, false, this(rules0, edition0, rarity0, IPaperCard.DEFAULT_ART_INDEX, false,
IPaperCard.NO_COLLECTOR_NUMBER, IPaperCard.NO_ARTIST_NAME, IPaperCard.NO_FUNCTIONAL_VARIANT); IPaperCard.NO_COLLECTOR_NUMBER, IPaperCard.NO_ARTIST_NAME, IPaperCard.NO_FUNCTIONAL_VARIANT);

View File

@@ -22,8 +22,6 @@ import java.io.FilenameFilter;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import com.google.common.base.Function;
import forge.ImageKeys; import forge.ImageKeys;
import forge.StaticData; import forge.StaticData;
import forge.deck.Deck; import forge.deck.Deck;
@@ -89,16 +87,9 @@ public class PreconDeck implements InventoryItemFromSet {
return this.description; return this.description;
} }
public static final Function<PreconDeck, String> FN_NAME_SELECTOR = new Function<PreconDeck, String>() {
@Override
public String apply(PreconDeck arg1) {
return arg1.getName();
}
};
public static class Reader extends StorageReaderFolder<PreconDeck> { public static class Reader extends StorageReaderFolder<PreconDeck> {
public Reader(final File deckDir0) { public Reader(final File deckDir0) {
super(deckDir0, PreconDeck.FN_NAME_SELECTOR); super(deckDir0, PreconDeck::getName);
} }
@Override @Override
@@ -123,13 +114,6 @@ public class PreconDeck implements InventoryItemFromSet {
return DeckStorage.DCK_FILE_FILTER; return DeckStorage.DCK_FILE_FILTER;
} }
} }
public static final Function<PreconDeck, Deck> FN_GET_DECK = new Function<PreconDeck, Deck>() {
@Override
public Deck apply(PreconDeck arg1) {
return arg1.getDeck();
}
};
@Override @Override
public String getImageKey(boolean altState) { public String getImageKey(boolean altState) {

Some files were not shown because too many files have changed in this diff Show More