tweaks for AI improvement

This commit is contained in:
Northmoc
2023-11-30 11:10:56 -05:00
parent b94a2868d8
commit fa3d1366fa
22 changed files with 68 additions and 42 deletions

View File

@@ -17,13 +17,7 @@
*/
package forge.ai;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;
import forge.game.cost.*;
import org.apache.commons.lang3.StringUtils;
@@ -689,6 +683,27 @@ public class ComputerUtil {
}
CardLists.sortByPowerAsc(typeList);
if (sa.isCraft()) {
// remove anything above 3 CMC so that high tier stuff doesn't get exiled with this
CardCollection toRemove = new CardCollection();
for (Card exileTgt : typeList) {
if (exileTgt.isInPlay() && exileTgt.getCMC() >= 3) toRemove.add(exileTgt);
}
typeList.removeAll(toRemove);
if (typeList.size() < amount) return null;
// FIXME: This is suboptimal, maybe implement a single comparator that'll take care of all of this?
CardLists.sortByCmcDesc(typeList);
Collections.reverse(typeList);
typeList.sort(new Comparator<Card>() {
@Override
public int compare(final Card a, final Card b) {
if (!a.isInPlay() && b.isInPlay()) return -1;
else if (!b.isInPlay() && a.isInPlay()) return 1;
else return 0;
}
}); // something that's not on the battlefield should come first
}
final CardCollection exileList = new CardCollection();
for (int i = 0; i < amount; i++) {

View File

@@ -2903,7 +2903,6 @@ public class CardFactoryUtil {
return;
}
String[] k = keyword.split(":");
String cost = "Exile<1/CARDNAME> Exile " + k[1];
final StringBuilder cd = new StringBuilder();
cd.append(k[0]).append(" with ");
@@ -2928,6 +2927,8 @@ public class CardFactoryUtil {
}
}
String partType = part.getType();
//consume .Other from most partTypes
if (partType.contains(".Other")) partType = partType.replace(".Other", "");
String singNoun = part.getTypeDescription() != null ? part.getTypeDescription() :
CardType.CoreType.isValidEnum(partType) ? partType.toLowerCase() : partType;
if (singNoun.equalsIgnoreCase("Permanent")) break;
@@ -2940,11 +2941,11 @@ public class CardFactoryUtil {
cd.append(kCost.getCostMana() != null ? kCost.getCostMana().toString() : "no mana?");
// Create return transformed ability string
String ab = "AB$ ChangeZone | CostDesc$ " + cd.toString() + " | Cost$ " + cost + " | Origin$ Exile | " +
"Destination$ Battlefield | Transformed$ True | Defined$ CorrectedSelf | Craft$ True | " +
"XAnnounceTitle$ " + Localizer.getInstance().getMessage("lblCraft") + " | SorcerySpeed$ True" +
" | StackDescription$ Return this card transformed under its owner's control. (Craft) | " +
"SpellDescription$ (" + inst.getReminderText() + ")";
String ab = "AB$ ChangeZone | CostDesc$ " + cd.toString() + " | Cost$ Exile<1/CARDNAME> " + k[1] + " | " +
"Origin$ Exile | Destination$ Battlefield | Transformed$ True | Defined$ CorrectedSelf | " +
"Craft$ True | XAnnounceTitle$ " + Localizer.getInstance().getMessage("lblCraft") + " | " +
"SorcerySpeed$ True | StackDescription$ Return this card transformed under its owner's control. " +
"(Craft) | SpellDescription$ (" + inst.getReminderText() + ")";
final SpellAbility newSA = AbilityFactory.getAbility(ab, card);
newSA.setIntrinsic(intrinsic);
inst.addSpellAbility(newSA);

View File

@@ -22,10 +22,7 @@ import forge.card.CardType;
import forge.game.Game;
import forge.game.ability.AbilityUtils;
import forge.game.ability.SpellAbilityEffect;
import forge.game.card.Card;
import forge.game.card.CardCollectionView;
import forge.game.card.CardLists;
import forge.game.card.CardPredicates;
import forge.game.card.*;
import forge.game.player.Player;
import forge.game.spellability.SpellAbility;
import forge.game.zone.ZoneType;
@@ -176,12 +173,8 @@ public class CostExile extends CostPartWithList {
type = TextUtil.fastReplace(type, "FromTopGrave", "");
}
CardCollectionView list;
if (zoneRestriction != 1) {
list = game.getCardsIn(this.from);
} else {
list = payer.getCardsIn(this.from);
}
CardCollection list = new CardCollection(zoneRestriction != 1 ? game.getCardsIn(this.from) :
payer.getCardsIn(this.from));
if (this.payCostFromSource()) {
return list.contains(source);
@@ -230,6 +223,21 @@ public class CostExile extends CostPartWithList {
return CardLists.cmcCanSumTo(i, list);
}
// for Craft: do not count the source card twice (it will be sacrificed)
if (ability.isCraft()) {
CostExile firstExileCost = ability.getPayCosts().getCostPartByType(CostExile.class);
if (firstExileCost != null && firstExileCost.payCostFromSource()) list.remove(ability.getHostCard());
// TODO: UGLY HACK! Ideally there should be no AI-specific code here, but not adding any (and adding it into ComputerUtil.chooseExileFrom)
// makes the game fail to play the SA with "AI failed to play....".
if (payer.isAI()) {
CardCollection toRemove = new CardCollection();
for (Card exileTgt : list) {
if (exileTgt.isInPlay() && exileTgt.getCMC() >= 3) toRemove.add(exileTgt);
}
list.removeAll(toRemove);
}
}
// for cards like Allosaurus Rider, do not count it
if (this.from.size() == 1 && this.from.get(0).equals(ZoneType.Hand) && source.isInZone(ZoneType.Hand)
&& list.contains(source)) {
@@ -269,6 +277,8 @@ public class CostExile extends CostPartWithList {
String amount = this.getAmount();
int amt = StringUtils.isNumeric(amount) ? Integer.parseInt(amount) : 0;
String partType = this.getType();
//consume .Other from most partTypes
if (partType.contains(".Other")) partType = partType.replace(".Other", "");
String singNoun = this.getTypeDescription() != null ? this.getTypeDescription() :
CardType.CoreType.isValidEnum(partType) || partType.equals("Permanent") ? partType.toLowerCase() :
partType;

View File

@@ -5,7 +5,7 @@ T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.S
SVar:TrigDraw:AB$ Draw | Cost$ Sac<1/Creature.nonToken/nontoken creature> | NumCards$ Y | SubAbility$ DBMill
SVar:DBMill:DB$ Mill | NumCards$ Y
SVar:Y:Sacrificed$CardPower
K:Craft:2 B B XMin1 ExileCtrlOrGrave<X/Creature>
K:Craft:2 B B XMin1 ExileCtrlOrGrave<X/Creature.Other>
SVar:X:Count$xPaid
A:AB$ ChangeZone | Cost$ 2 B | Origin$ Graveyard | Destination$ Hand | ActivationZone$ Graveyard | SpellDescription$ Return CARDNAME from your graveyard to your hand.
AlternateMode:DoubleFaced

View File

@@ -5,7 +5,7 @@ K:etbCounter:NET:3
A:AB$ Tap | Cost$ T SubCounter<1/NET> | ValidTgts$ Permanent.Other+nonLand | TgtPrompt$ Select another target nonland permanent | SubAbility$ DBEffect | SpellDescription$ Tap another target nonland permanent.
SVar:DBEffect:DB$ Effect | RememberObjects$ Targeted | StaticAbilities$ CantActivate | ForgetOnMoved$ Battlefield | Duration$ UntilTargetedUntaps | SpellDescription$ Its activated abilities can't be activated for as long as it remains tapped.
SVar:CantActivate:Mode$ CantBeActivated | ValidCard$ Card.IsRemembered | ValidSA$ Activated | Description$ Its activated abilities can't be activated for as long as it remains tapped.
K:Craft:1 U ExileCtrlOrGrave<1/Artifact>
K:Craft:1 U ExileCtrlOrGrave<1/Artifact.Other>
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice & Type$Artifact
DeckHas:Ability$Graveyard

View File

@@ -4,7 +4,7 @@ Types:Artifact
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigChange | TriggerDescription$ When CARDNAME enters the battlefield, search your library for a basic Plains card, reveal it, put it into your hand, then shuffle. You gain 2 life.
SVar:TrigChange:DB$ ChangeZone | Origin$ Library | Destination$ Hand | ChangeType$ Plains.Basic | SubAbility$ DBGainLife
SVar:DBGainLife:DB$ GainLife | LifeAmount$ 2
K:Craft:5 W W ExileCtrlOrGrave<1/Artifact>
K:Craft:5 W W ExileCtrlOrGrave<1/Artifact.Other>
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice
DeckHas:Ability$Graveyard|Token|LifeGain & Type$Gnome|Artifact

View File

@@ -4,7 +4,7 @@ Types:Artifact
K:Flash
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDealDamage | TriggerDescription$ When CARDNAME enters the battlefield, it deals 2 damage to any target.
SVar:TrigDealDamage:DB$ DealDamage | ValidTgts$ Any | NumDmg$ 2
K:Craft:2 R ExileCtrlOrGrave<1/Artifact>
K:Craft:2 R ExileCtrlOrGrave<1/Artifact.Other>
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice
DeckHas:Ability$Graveyard

View File

@@ -4,7 +4,7 @@ Types:Artifact
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigMill | TriggerDescription$ When CARDNAME enters the battlefield, mill a card, then draw a card. (To mill a card, put the top card of your library into your graveyard.)
SVar:TrigMill:DB$ Mill | SubAbility$ DBDraw
SVar:DBDraw:DB$ Draw
K:Craft:4 U U ExileCtrlOrGrave<1/Artifact>
K:Craft:4 U U ExileCtrlOrGrave<1/Artifact.Other>
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice
DeckHas:Ability$Graveyard|Mill

View File

@@ -3,7 +3,7 @@ ManaCost:3 G
Types:Artifact
T:Mode$ ChangesZone | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigCounter | TriggerDescription$ When CARDNAME enters the battlefield, distribute three +1/+1 counters among one, two, or three target creatures you control.
SVar:TrigCounter:DB$ PutCounter | ValidTgts$ Creature.YouCtrl | TgtPrompt$ Select one, two or three target creatures you control | CounterType$ P1P1 | CounterNum$ 3 | TargetMin$ 1 | TargetMax$ 3 | DividedAsYouChoose$ 3
K:Craft:5 G G ExileCtrlOrGrave<1/Creature>
K:Craft:5 G G ExileCtrlOrGrave<1/Creature.Other>
AlternateMode:DoubleFaced
DeckHas:Ability$Counters|LifeGain|Graveyard
DeckHints:Ability$Discard|Mill|Sacrifice

View File

@@ -3,7 +3,7 @@ ManaCost:2 G
Types:Artifact
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigDig | TriggerDescription$ When CARDNAME enters the battlefield, look at the top six cards of your library. You may put a land card from among them onto the battlefield tapped. Put the rest on the bottom in a random order.
SVar:TrigDig:DB$ Dig | DigNum$ 6 | ChangeNum$ 1 | ChangeValid$ Card.Land | Optional$ True | DestinationZone$ Battlefield | DestinationZone2$ Library | LibraryPosition$ -1 | RestRandomOrder$ True | Tapped$ True
K:Craft:5 G ExileCtrlOrGrave<1/Cave>
K:Craft:5 G ExileCtrlOrGrave<1/Cave.Other>
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice
DeckHas:Ability$Graveyard

View File

@@ -5,7 +5,7 @@ K:Flash
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigTap | TriggerDescription$ When CARDNAME enters the battlefield, tap up to one target artifact or creature and put two stun counters on it.
SVar:TrigTap:DB$ Tap | TargetMin$ 0 | TargetMax$ 1 | ValidTgts$ Artifact,Creature | TgtPrompt$ Select up to one target artifact or creature | SubAbility$ DBCounter
SVar:DBCounter:DB$ PutCounter | Defined$ Targeted | CounterType$ Stun | CounterNum$ 2
K:Craft:2 U ExileCtrlOrGrave<1/Artifact>
K:Craft:2 U ExileCtrlOrGrave<1/Artifact.Other>
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice
DeckHas:Ability$Graveyard|Counters

View File

@@ -3,7 +3,7 @@ ManaCost:3 W U
Types:Artifact
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigToken | TriggerDescription$ When CARDNAME enters the battlefield, create a 4/4 white and blue Golem artifact creature token.
SVar:TrigToken:DB$ Token | TokenScript$ wu_4_4_a_golem
K:Craft:4 W W U ExileCtrlOrGrave<1/Artifact>
K:Craft:4 W W U ExileCtrlOrGrave<1/Artifact.Other>
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice
DeckHas:Ability$Graveyard|Token & Type$Golem

View File

@@ -2,7 +2,7 @@ Name:Ore-Rich Stalactite
ManaCost:1 R
Types:Artifact
A:AB$ Mana | Cost$ T | Produced$ R | RestrictValid$ Spell.Instant,Spell.Sorcery | SpellDescription$ Add {R}. Spend this mana only to cast an instant or sorcery spell.
K:Craft:3 R R XMin4 ExileFromGrave<X/Instant.Red;Sorcery.Red/red instant and/or sorcery cards>
K:Craft:3 R R XMin4 ExileFromGrave<X/Instant.Red+Other;Sorcery.Red+Other/red instant and/or sorcery cards>
SVar:X:Count$xPaid
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice

View File

@@ -3,7 +3,7 @@ ManaCost:W
Types:Artifact
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigScry | TriggerDescription$ When CARDNAME enters the battlefield, scry 2.
SVar:TrigScry:DB$ Scry | ScryNum$ 2
K:Craft:2 W ExileCtrlOrGrave<1/Artifact>
K:Craft:2 W ExileCtrlOrGrave<1/Artifact.Other>
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice
DeckHas:Ability$Graveyard

View File

@@ -5,7 +5,7 @@ T:Mode$ Attacks | ValidCard$ Creature.EquippedBy | Execute$ TrigDraw | TriggerDe
SVar:TrigDraw:DB$ Draw | SubAbility$ DBDiscard
SVar:DBDiscard:DB$ Discard | Mode$ TgtChoose
K:Equip:1
K:Craft:5 XMin1 ExileCtrlOrGrave<X/Creature>
K:Craft:5 XMin1 ExileCtrlOrGrave<X/Creature.Other>
SVar:X:Count$xPaid
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice

View File

@@ -3,7 +3,7 @@ ManaCost:1 R
Types:Artifact
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigDiscard | TriggerDescription$ When CARDNAME enters the battlefield, you may discard a card. If you do, draw two cards.
SVar:TrigDiscard:AB$ Draw | Cost$ Discard<1/Card> | NumCards$ 2
K:Craft:4 R XMin1 ExileCtrlOrGrave<X/Dinosaur>
K:Craft:4 R XMin1 ExileCtrlOrGrave<X/Dinosaur.Other>
SVar:X:Count$xPaid
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice

View File

@@ -6,7 +6,7 @@ T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.S
SVar:TrigMill:DB$ Mill | NumCards$ 3 | RememberMilled$ True | SubAbility$ DBChangeZone
SVar:DBChangeZone:DB$ ChangeZone | Hidden$ True | Origin$ Graveyard,Exile | Destination$ Hand | ChangeType$ Artifact.IsRemembered | SelectPrompt$ You may put an artifact card from among them into your hand | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
K:Craft:4 ExileCtrlOrGrave<6/Artifact>
K:Craft:4 ExileCtrlOrGrave<6/Artifact.Other>
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice
DeckHas:Ability$Mill|Graveyard|Token

View File

@@ -2,7 +2,7 @@ Name:Throne of the Grim Captain
ManaCost:2
Types:Legendary Artifact
A:AB$ Mill | Cost$ T | NumCards$ 2 | SpellDescription$ Mill two cards.
K:Craft:4 ExileCtrlOrGrave<1/Dinosaur> ExileCtrlOrGrave<1/Merfolk> ExileCtrlOrGrave<1/Pirate> ExileCtrlOrGrave<1/Vampire>:a Dinosaur, a Merfolk, a Pirate, and a Vampire:the four
K:Craft:4 ExileCtrlOrGrave<1/Dinosaur.Other> ExileCtrlOrGrave<1/Merfolk.Other> ExileCtrlOrGrave<1/Pirate.Other> ExileCtrlOrGrave<1/Vampire.Other>:a Dinosaur, a Merfolk, a Pirate, and a Vampire:the four
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice
DeckHas:Ability$Graveyard|Mill

View File

@@ -3,7 +3,7 @@ ManaCost:1 B
Types:Artifact
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigSac | TriggerDescription$ When CARDNAME enters the battlefield, each opponent sacrifices a creature.
SVar:TrigSac:DB$ Sacrifice | Defined$ Opponent | SacValid$ Creature
K:Craft:4 B ExileCtrlOrGrave<1/Creature>
K:Craft:4 B ExileCtrlOrGrave<1/Creature.Other>
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice
DeckHas:Ability$LifeGain|Graveyard

View File

@@ -6,7 +6,7 @@ SVar:TrigRepeat:DB$ RepeatEach | RepeatPlayers$ Player | RepeatSubAbility$ DBCho
SVar:DBChoose:DB$ ChooseCard | Choices$ Creature.RememberedPlayerCtrl+powerLE2 | Mandatory$ True | ChoiceTitle$ Choose a creature card with power 2 or less | ImprintChosen$ True
SVar:DBDestroyAll:DB$ DestroyAll | ValidCards$ Creature.IsNotImprinted | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearImprinted$ True
K:Craft:3 W W ExileCtrlOrGrave<1/Artifact>
K:Craft:3 W W ExileCtrlOrGrave<1/Artifact.Other>
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice
DeckHas:Ability$Mill|Graveyard|Token

View File

@@ -3,7 +3,7 @@ ManaCost:1 B
Types:Artifact
T:Mode$ ChangesZone | Origin$ Any | Destination$ Battlefield | ValidCard$ Card.Self | Execute$ TrigRemove | TriggerDescription$ When CARDNAME enters the battlefield, target opponent reveals their hand. You choose an artifact or creature card from it. That player discards that card.
SVar:TrigRemove:DB$ Discard | ValidTgts$ Opponent | NumCards$ 1 | Mode$ RevealYouChoose | DiscardValid$ Artifact,Creature
K:Craft:5 B ExileCtrlOrGrave<2/Creature>
K:Craft:5 B ExileCtrlOrGrave<2/Creature.OTher>
SVar:X:Count$xPaid
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice

View File

@@ -2,7 +2,7 @@ Name:Waterlogged Hulk
ManaCost:U
Types:Artifact
A:AB$ Mill | Cost$ T | SpellDescription$ Mill a card. (Put the top card of your library into your graveyard.)
K:Craft:3 U ExileCtrlOrGrave<1/Island>
K:Craft:3 U ExileCtrlOrGrave<1/Island.Other>
AlternateMode:DoubleFaced
DeckHints:Ability$Discard|Mill|Sacrifice
DeckHas:Ability$Graveyard|Mill