Improve AI logic for cavern of souls. (#1787)

* Improve AI logic for cavern of souls.

Improve AI logic for cavern of souls.

- Don't count token creature types when looking for most prominent creature type, since cavern of souls doesn't help with them.
- Weigh cards in hand and commanders double, as they are more likely to be cast.

I tested with a Tiny Leaders deck running Geist of Saint Traft that didn't have any creature type synergy and confirmed that the changes make the AI choose Spirit for Geist, rather than Human as before.

* Apply this to a few more cards.

* Remove a print statement.

* Address review comments.
This commit is contained in:
asvitkine
2022-11-03 01:03:37 -04:00
committed by GitHub
parent 05963bb68f
commit 2804206776
14 changed files with 60 additions and 71 deletions

View File

@@ -2378,8 +2378,9 @@ public class ComputerUtil {
chosen = ComputerUtilCard.getMostProminentType(list, valid);
}
}
else if (logic.equals("MostProminentInComputerDeck")) {
chosen = ComputerUtilCard.getMostProminentType(ai.getAllCards(), valid);
else if (logic.startsWith("MostProminentInComputerDeck")) {
boolean includeTokens = !logic.endsWith("NonToken");
chosen = ComputerUtilCard.getMostProminentType(ai.getAllCards(), valid, includeTokens);
}
else if (logic.equals("MostProminentInComputerGraveyard")) {
chosen = ComputerUtilCard.getMostProminentType(ai.getCardsIn(ZoneType.Graveyard), valid);

View File

@@ -762,30 +762,21 @@ public class ComputerUtilCard {
return maxName;
}
public static String getMostProminentBasicLandType(final CardCollectionView list) {
return getMostProminentType(list, CardType.getBasicTypes());
public static String getMostProminentType(final CardCollectionView list, final Collection<String> valid) {
return getMostProminentType(list, valid, true);
}
/**
* <p>
* getMostProminentCreatureType.
* </p>
*
* @param list
* @return a {@link java.lang.String} object.
*/
public static String getMostProminentCreatureType(final CardCollectionView list) {
return getMostProminentType(list, CardType.getAllCreatureTypes());
}
public static String getMostProminentType(final CardCollectionView list, final Collection<String> valid) {
public static String getMostProminentType(final CardCollectionView list, final Collection<String> valid, boolean includeTokens) {
if (list.size() == 0) {
return "";
}
final Map<String, Integer> typesInDeck = Maps.newHashMap();
// TODO JAVA 8 use getOrDefault
for (final Card c : list) {
if (!includeTokens && c.isToken()) {
continue;
}
// Changeling are all creature types, they are not interesting for
// counting creature types
if (c.hasStartOfKeyword(Keyword.CHANGELING.toString())) {
@@ -803,15 +794,20 @@ public class ComputerUtilCard {
continue;
}
// Cards in hand and commanders are worth double, as they are more likely to be played.
int weight = 1;
if (c.isInZone(ZoneType.Hand) || c.isRealCommander()) {
weight = 2;
}
Set<String> cardCreatureTypes = c.getType().getCreatureTypes();
for (String type : cardCreatureTypes) {
Integer count = typesInDeck.get(type);
if (count == null) {
count = 0;
}
typesInDeck.put(type, count + 1);
Integer count = typesInDeck.getOrDefault(type, 0);
typesInDeck.put(type, count + weight);
}
//also take into account abilities that generate tokens
if (includeTokens) {
for (SpellAbility sa : c.getAllSpellAbilities()) {
if (sa.getApi() != ApiType.Token) {
continue;
@@ -821,16 +817,13 @@ public class ComputerUtilCard {
if (!CardType.isACreatureType(var)) {
continue;
}
Integer count = typesInDeck.get(var);
if (count == null) {
count = 0;
}
typesInDeck.put(var, count + 1);
Integer count = typesInDeck.getOrDefault(var, 0);
typesInDeck.put(var, count + weight);
}
}
}
// same for Trigger that does make Tokens
for (Trigger t :c .getTriggers()) {
for (Trigger t : c.getTriggers()) {
SpellAbility sa = t.ensureAbility();
if (sa != null) {
if (sa.getApi() != ApiType.Token || !sa.hasParam("TokenTypes")) {
@@ -840,21 +833,16 @@ public class ComputerUtilCard {
if (!CardType.isACreatureType(var)) {
continue;
}
Integer count = typesInDeck.get(var);
if (count == null) {
count = 0;
}
typesInDeck.put(var, count + 1);
Integer count = typesInDeck.getOrDefault(var, 0);
typesInDeck.put(var, count + weight);
}
}
}
// special rule for Fabricate and Servo
if (c.hasStartOfKeyword(Keyword.FABRICATE.toString())) {
Integer count = typesInDeck.get("Servo");
if (count == null) {
count = 0;
Integer count = typesInDeck.getOrDefault("Servo", 0);
typesInDeck.put("Servo", count + weight);
}
typesInDeck.put("Servo", count + 1);
}
} // for

View File

@@ -2,6 +2,6 @@ Name:Belbe's Portal
ManaCost:5
Types:Artifact
K:ETBReplacement:Other:ChooseCT
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type. | AILogic$ MostProminentInComputerDeck
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type. | AILogic$ MostProminentInComputerDeckNonToken
A:AB$ ChangeZone | Cost$ 3 T | Origin$ Hand | Destination$ Battlefield | ChangeType$ Creature.ChosenType | ChangeNum$ 1 | SpellDescription$ You may put a creature card of the chosen type from your hand onto the battlefield.
Oracle:As Belbe's Portal enters the battlefield, choose a creature type.\n{3}, {T}: You may put a creature card of the chosen type from your hand onto the battlefield.

View File

@@ -2,7 +2,7 @@ Name:Bloodline Shaman
ManaCost:1 G
Types:Creature Elf Wizard Shaman
PT:1/1
A:AB$ ChooseType | Cost$ T | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeck | SubAbility$ ShamanDig | SpellDescription$ Choose a creature type. Reveal the top card of your library. If that card is a creature card of the chosen type, put it into your hand. Otherwise, put it into your graveyard.
A:AB$ ChooseType | Cost$ T | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeckNonToken | SubAbility$ ShamanDig | SpellDescription$ Choose a creature type. Reveal the top card of your library. If that card is a creature card of the chosen type, put it into your hand. Otherwise, put it into your graveyard.
SVar:ShamanDig:DB$ Dig | DigNum$ 1 | Reveal$ True | ChangeNum$ All | ChangeValid$ Creature.ChosenType | DestinationZone$ Hand | DestinationZone2$ Graveyard
AI:RemoveDeck:Random
Oracle:{T}: Choose a creature type. Reveal the top card of your library. If that card is a creature card of the chosen type, put it into your hand. Otherwise, put it into your graveyard.

View File

@@ -4,7 +4,7 @@ Types:Artifact Creature Golem
PT:2/2
S:Mode$ Continuous | Affected$ Creature.ChosenType | AddPower$ 1 | AddToughness$ 1 | Description$ Creatures of the chosen type get +1/+1.
K:ETBReplacement:Other:ChooseCT
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type. | AILogic$ MostProminentInComputerDeck
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type. | AILogic$ MostProminentInComputerDeckNonToken
T:Mode$ ChangesZone | ValidCard$ Card.Self | Origin$ Any | Destination$ Battlefield | Execute$ TrigDig | TriggerDescription$ When CARDNAME enters the battlefield, reveal the top four cards of your library. Put all creature cards of the chosen type revealed this way into your hand and the rest on the bottom of your library in any order.
SVar:TrigDig:DB$ Dig | DigNum$ 4 | Reveal$ True | ChangeNum$ All | ChangeValid$ Creature.ChosenType
SVar:PlayMain1:TRUE

View File

@@ -2,7 +2,7 @@ Name:Cavern of Souls
ManaCost:no cost
Types:Land
K:ETBReplacement:Other:ChooseCT
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type. | AILogic$ MostProminentInComputerDeck
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type. | AILogic$ MostProminentInComputerDeckNonToken
A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}.
A:AB$ Mana | Cost$ T | Produced$ Any | RestrictValid$ Spell.Creature+ChosenType | AddsNoCounter$ True | SpellDescription$ Add one mana of any color. Spend this mana only to cast a creature spell of the chosen type, and that spell can't be countered.
Oracle:As Cavern of Souls enters the battlefield, choose a creature type.\n{T}: Add {C}.\n{T}: Add one mana of any color. Spend this mana only to cast a creature spell of the chosen type, and that spell can't be countered.

View File

@@ -2,7 +2,7 @@ Name:Herald's Horn
ManaCost:3
Types:Artifact
K:ETBReplacement:Other:ChooseCT
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type. | AILogic$ MostProminentInComputerDeck
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type. | AILogic$ MostProminentInComputerDeckNonToken
S:Mode$ ReduceCost | ValidCard$ Creature.ChosenType | Type$ Spell | Activator$ You | Amount$ 1 | Description$ Creature spells you cast of the chosen type cost {1} less to cast.
T:Mode$ Phase | Phase$ Upkeep | ValidPlayer$ You | Execute$ TrigPeek | TriggerZones$ Battlefield | TriggerDescription$ At the beginning of your upkeep, look at the top card of your library. If it's a creature card of the chosen type, you may reveal it and put it into your hand.
SVar:TrigPeek:DB$ PeekAndReveal | PeekAmount$ 1 | RevealValid$ Creature.ChosenType | RevealOptional$ True | RememberRevealed$ True | SubAbility$ DBChangeZone

View File

@@ -13,6 +13,6 @@ Name:The Ringhart Crest
ManaCost:1 G
Types:Legendary Artifact
K:ETBReplacement:Other:ChooseCT
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeck | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type.
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeckNonToken | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type.
A:AB$ Mana | Cost$ T | Produced$ G | RestrictValid$ Spell.Creature+ChosenType,Spell.Creature+Legendary | SpellDescription$ Add {G}. Spend this mana only to cast a creature spell of the chosen type or a legendary creature spell.
Oracle:As The Ringhart Crest enters the battlefield, choose a creature type.\n{T}: Add {G}. Spend this mana only to cast a creature spell of the chosen type or a legendary creature spell.

View File

@@ -2,7 +2,7 @@ Name:Molten Echoes
ManaCost:2 R R
Types:Enchantment
K:ETBReplacement:Other:ChooseCT
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeck | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type.
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeckNonToken | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type.
T:Mode$ ChangesZone | ValidCard$ Creature.nonToken+ChosenType+YouCtrl | Origin$ Any | Destination$ Battlefield | Execute$ TrigCopyPermanent | TriggerZones$ Battlefield | TriggerDescription$ Whenever a nontoken creature of the chosen type enters the battlefield under your control, create a token that's a copy of that creature. That token gains haste. Exile it at the beginning of the next end step.
SVar:TrigCopyPermanent:DB$ CopyPermanent | Defined$ TriggeredCard | NumCopies$ 1 | PumpKeywords$ Haste | AtEOT$ Exile
Oracle:As Molten Echoes enters the battlefield, choose a creature type.\nWhenever a nontoken creature of the chosen type enters the battlefield under your control, create a token that's a copy of that creature. That token gains haste. Exile it at the beginning of the next end step.

View File

@@ -4,7 +4,7 @@ Types:Creature Shapeshifter
PT:2/3
K:Changeling
K:ETBReplacement:Other:ChooseCT
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeck | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type.
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeckNonToken | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type.
S:Mode$ Continuous | Affected$ Card.TopLibrary+YouCtrl | AffectedZone$ Library | MayLookAt$ You | Description$ You may look at the top card of your library any time.
S:Mode$ Continuous | Affected$ Creature.ChosenType+TopLibrary+YouCtrl+nonLand | AffectedZone$ Library | MayPlay$ True | Description$ You may cast creature spells of the chosen type from the top of your library.
Oracle:Changeling (This card is every creature type.)\nAs Realmwalker enters the battlefield, choose a creature type.\nYou may look at the top card of your library any time.\nYou may cast creature spells of the chosen type from the top of your library.

View File

@@ -2,7 +2,7 @@ Name:Reflections of Littjara
ManaCost:4 U
Types:Enchantment
K:ETBReplacement:Other:ChooseCT
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeck | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type. | StackDescription$ SpellDescription
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeckNonToken | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type. | StackDescription$ SpellDescription
T:Mode$ SpellCast | ValidCard$ Card.ChosenType | ValidActivatingPlayer$ You | TriggerZones$ Battlefield | Execute$ TrigCopy | TriggerDescription$ Whenever you cast a spell of the chosen type, copy that spell. (A copy of a permanent spell becomes a token.)
SVar:TrigCopy:DB$ CopySpellAbility | Defined$ TriggeredSpellAbility | AILogic$ Always | MayChooseTarget$ True
DeckHas:Ability$Token

View File

@@ -2,7 +2,7 @@ Name:Unclaimed Territory
ManaCost:no cost
Types:Land
K:ETBReplacement:Other:ChooseCT
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type. | AILogic$ MostProminentInComputerDeck
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type. | AILogic$ MostProminentInComputerDeckNonToken
A:AB$ Mana | Cost$ T | Produced$ C | SpellDescription$ Add {C}.
A:AB$ Mana | Cost$ T | Produced$ Any | RestrictValid$ Spell.Creature+ChosenType | SpellDescription$ Add one mana of any color. Spend this mana only to cast a creature spell of the chosen type.
Oracle:As Unclaimed Territory enters the battlefield, choose a creature type.\n{T}: Add {C}.\n{T}: Add one mana of any color. Spend this mana only to cast a creature spell of the chosen type.

View File

@@ -2,7 +2,7 @@ Name:Urza's Incubator
ManaCost:3
Types:Artifact
K:ETBReplacement:Other:ChooseCT
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeck | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type.
SVar:ChooseCT:DB$ ChooseType | Defined$ You | Type$ Creature | AILogic$ MostProminentInComputerDeckNonToken | SpellDescription$ As CARDNAME enters the battlefield, choose a creature type.
S:Mode$ ReduceCost | ValidCard$ Creature.ChosenType | Type$ Spell | Amount$ 2 | Description$ Creature spells of the chosen type cost {2} less to cast.
AI:RemoveDeck:Random
Oracle:As Urza's Incubator enters the battlefield, choose a creature type.\nCreature spells of the chosen type cost {2} less to cast.

View File

@@ -1,7 +1,7 @@
Name:Vexing Arcanix
ManaCost:4
Types:Artifact
A:AB$ NameCard | Cost$ 3 T | ValidTgts$ Player | TgtPrompt$ Select target player | SubAbility$ DBDig | AILogic$ MostProminentInComputerDeck | SpellDescription$ Target player chooses a card name, then reveals the top card of their library. If that card has the chosen name, the player puts it into their hand. Otherwise, the player puts it into their graveyard and CARDNAME deals 2 damage to them.
A:AB$ NameCard | Cost$ 3 T | ValidTgts$ Player | TgtPrompt$ Select target player | SubAbility$ DBDig | AILogic$ MostProminentInComputerDeckNonToken | SpellDescription$ Target player chooses a card name, then reveals the top card of their library. If that card has the chosen name, the player puts it into their hand. Otherwise, the player puts it into their graveyard and CARDNAME deals 2 damage to them.
SVar:DBDig:DB$ Dig | DigNum$ 1 | Defined$ Targeted | ChangeNum$ All | ChangeValid$ Card.NamedCard | DestinationZone2$ Graveyard | Reveal$ True | RememberChanged$ True | SubAbility$ DBDamage
SVar:DBDamage:DB$ DealDamage | NumDmg$ 2 | Defined$ Targeted | ConditionDefined$ Remembered | ConditionPresent$ Card.NamedCard | ConditionCompare$ EQ0 | SubAbility$ DBCleanup
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True