mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 20:58:03 +00:00
Improve AI logic for Connive / Change of Plans (#2367)
* - Update the MTGDecksNet conversion and AI playability test toolchain to the latest version (Python 3 compatible). * - Make the input/output folders generic * - Make the input/output folders generic, part 2. * - Add the Scryfall all-prices.txt generator script. * - Minor code cleanup. * - Improve ConniveAi for Change of Plans. * - Check if the AI can draw cards for ConniveAI
This commit is contained in:
@@ -12,16 +12,67 @@ import forge.game.zone.ZoneType;
|
||||
|
||||
public class ConniveAi extends SpellAbilityAi {
|
||||
@Override
|
||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||
final Card source = sa.getHostCard();
|
||||
boolean preferred = true;
|
||||
protected boolean canPlayAI(Player ai, SpellAbility sa) {
|
||||
if (!ai.canDraw()) {
|
||||
return false; // can't draw anything
|
||||
}
|
||||
|
||||
CardCollection list;
|
||||
list = CardLists.getTargetableCards(new CardCollection(ai.getCardsIn(ZoneType.Battlefield)), sa);
|
||||
|
||||
// Filter AI-specific targets if provided
|
||||
list = ComputerUtil.filterAITgts(sa, ai, list, false);
|
||||
|
||||
int totalTargets = list.size();
|
||||
int maxTargets = sa.getMaxTargets();
|
||||
if ("X".equals(sa.getParam("TargetMax")) && "Count$xPaid".equals(sa.getSVar("X"))) {
|
||||
// TODO: consider making the library margin (currently hardcoded to 5) a configurable AI parameter
|
||||
maxTargets = Math.min(list.size(), Math.max(0, ai.getCardsIn(ZoneType.Library).size() - 5));
|
||||
sa.setXManaCostPaid(maxTargets);
|
||||
}
|
||||
|
||||
sa.resetTargets();
|
||||
while (sa.canAddMoreTarget()) {
|
||||
if ((list.isEmpty() && sa.isTargetNumberValid() && !sa.getTargets().isEmpty())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (list.isEmpty()) {
|
||||
// Still an empty list, but we have to choose something (mandatory); expand targeting to
|
||||
// include AI's own cards to see if there's anything targetable (e.g. Plague Belcher).
|
||||
list = CardLists.getTargetableCards(ai.getCardsIn(ZoneType.Battlefield), sa);
|
||||
}
|
||||
|
||||
if (list.isEmpty()) {
|
||||
// Not mandatory, or the the list was regenerated and is still empty,
|
||||
// so return whether or not we found enough targets
|
||||
return sa.isTargetNumberValid();
|
||||
}
|
||||
|
||||
Card choice = ComputerUtilCard.getBestCreatureAI(list);
|
||||
|
||||
if (choice != null) {
|
||||
sa.getTargets().add(choice);
|
||||
list.remove(choice);
|
||||
} else {
|
||||
// Didn't want to choose anything?
|
||||
list.clear();
|
||||
}
|
||||
}
|
||||
return !sa.getTargets().isEmpty() && sa.isTargetNumberValid();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean doTriggerAINoCost(Player ai, SpellAbility sa, boolean mandatory) {
|
||||
if (!ai.canDraw() && !mandatory) {
|
||||
return false; // can't draw anything
|
||||
}
|
||||
|
||||
boolean preferred = true;
|
||||
CardCollection list;
|
||||
list = CardLists.getTargetableCards(new CardCollection(ai.getCardsIn(ZoneType.Battlefield)), sa);
|
||||
|
||||
// Filter AI-specific targets if provided
|
||||
list = ComputerUtil.filterAITgts(sa, ai, list, false);
|
||||
|
||||
sa.resetTargets();
|
||||
while (sa.canAddMoreTarget()) {
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
package forge.ai.ability;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.base.Predicates;
|
||||
|
||||
import forge.ai.ComputerUtil;
|
||||
import forge.ai.ComputerUtilCard;
|
||||
import forge.ai.SpellAbilityAi;
|
||||
import forge.game.Game;
|
||||
import forge.game.GameEntity;
|
||||
import forge.game.ability.AbilityUtils;
|
||||
import forge.game.card.Card;
|
||||
import forge.game.card.CardCollectionView;
|
||||
@@ -19,6 +17,10 @@ import forge.game.spellability.TargetRestrictions;
|
||||
import forge.game.zone.ZoneType;
|
||||
import forge.util.MyRandom;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class PhasesAi extends SpellAbilityAi {
|
||||
@Override
|
||||
protected boolean canPlayAI(Player aiPlayer, SpellAbility sa) {
|
||||
@@ -126,4 +128,14 @@ public class PhasesAi extends SpellAbilityAi {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends GameEntity> T chooseSingleEntity(Player ai, SpellAbility sa, Collection<T> options, boolean isOptional, Player targetedPlayer, Map<String, Object> params) {
|
||||
// TODO: improve the selection logic, e.g. for cards like Change of Plans. Currently will
|
||||
// confirm everything unless AILogic is "DontPhaseOut", in which case it'll confirm nothing.
|
||||
if ("DontPhaseOut".equals(sa.getParam("AILogic"))) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return super.chooseSingleEntity(ai, sa, options, isOptional, targetedPlayer, params);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ Name:Change of Plans
|
||||
ManaCost:X 1 U
|
||||
Types:Instant
|
||||
A:SP$ Connive | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select X target creatures you control | TargetMin$ X | TargetMax$ X | SubAbility$ DBPhase | SpellDescription$ Each of X target creatures you control connive.
|
||||
SVar:DBPhase:DB$ Phases | Defined$ Targeted | AnyNumber$ True | StackDescription$ {p:You} may have any number of them phase out. | SpellDescription$ You may have any number of them phase out. (To have a creature connive, draw a card, then discard a card. If you discarded a nonland card, put a +1/+1 counter on that creature. Treat phased out permanents and anything attached to them as though they don't exist until their controller's next turn.)
|
||||
SVar:DBPhase:DB$ Phases | Defined$ Targeted | AnyNumber$ True | StackDescription$ {p:You} may have any number of them phase out. | AILogic$ DontPhaseOut | SpellDescription$ You may have any number of them phase out. (To have a creature connive, draw a card, then discard a card. If you discarded a nonland card, put a +1/+1 counter on that creature. Treat phased out permanents and anything attached to them as though they don't exist until their controller's next turn.)
|
||||
SVar:X:Count$xPaid
|
||||
DeckHas:Ability$Discard|Counters
|
||||
Oracle:Each of X target creatures you control connive. You may have any number of them phase out. (To have a creature connive, draw a card, then discard a card. If you discarded a nonland card, put a +1/+1 counter on that creature. Treat phased out permanents and anything attached to them as though they don't exist until their controller's next turn.)
|
||||
|
||||
Reference in New Issue
Block a user