From 6c1f4c7ffe364f74fc99ff6fd2fc337087e1affb Mon Sep 17 00:00:00 2001 From: Justin C <73019026+neoFuzz@users.noreply.github.com> Date: Sat, 7 Dec 2024 01:13:22 +1100 Subject: [PATCH] Fix for noted types for Volo's journal (#6665) --- .../src/main/java/forge/ai/ComputerUtil.java | 20 ++--- .../main/java/forge/ai/ComputerUtilCard.java | 8 +- .../ability/effects/ChooseTypeEffect.java | 8 +- .../ai/simulation/GameSimulationTest.java | 85 +++++++++++++++++-- 4 files changed, 98 insertions(+), 23 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 57abdca4936..0470dc324cc 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -2544,35 +2544,33 @@ public static String chooseSomeType(Player ai, String kindOfType, SpellAbility s if (logic.equals("MostProminentOnBattlefield")) { chosen = ComputerUtilCard.getMostProminentType(game.getCardsIn(ZoneType.Battlefield), valid); - } - else if (logic.equals("MostProminentComputerControls")) { + } else if (logic.equals("MostProminentComputerControls")) { chosen = ComputerUtilCard.getMostProminentType(ai.getCardsIn(ZoneType.Battlefield), valid); - } - else if (logic.equals("MostProminentComputerControlsOrOwns")) { + } else if (logic.equals("MostProminentComputerControlsOrOwns")) { CardCollectionView list = ai.getCardsIn(Arrays.asList(ZoneType.Battlefield, ZoneType.Hand)); if (list.isEmpty()) { list = ai.getCardsIn(Arrays.asList(ZoneType.Library)); } chosen = ComputerUtilCard.getMostProminentType(list, valid); - } - else if (logic.equals("MostProminentOppControls")) { + } else if (logic.equals("MostProminentOppControls")) { CardCollection list = ai.getOpponents().getCardsIn(ZoneType.Battlefield); chosen = ComputerUtilCard.getMostProminentType(list, valid); if (!CardType.isACreatureType(chosen) || invalidTypes.contains(chosen)) { list = CardLists.filterControlledBy(game.getCardsInGame(), ai.getOpponents()); chosen = ComputerUtilCard.getMostProminentType(list, valid); } - } - else if (logic.startsWith("MostProminentInComputerDeck")) { + } else if (logic.startsWith("MostProminentInComputerDeck")) { boolean includeTokens = !logic.endsWith("NonToken"); chosen = ComputerUtilCard.getMostProminentType(ai.getAllCards(), valid, includeTokens); - } - else if (logic.equals("MostProminentInComputerGraveyard")) { + } else if (logic.equals("MostProminentInComputerGraveyard")) { chosen = ComputerUtilCard.getMostProminentType(ai.getCardsIn(ZoneType.Graveyard), valid); } } + if (!CardType.isACreatureType(chosen) || invalidTypes.contains(chosen)) { - chosen = "Sliver"; + chosen = validTypes.size() == 1 ? (String) validTypes.toArray()[0] : + ComputerUtilCard.getMostProminentType(ai.getAllCards(), validTypes, false); + //chosen = "Sliver"; } } else if (kindOfType.equals("Basic Land")) { diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index 0029486c916..222a89daabe 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -875,12 +875,14 @@ public static String getMostProminentType(final CardCollectionView list, final C int max = 0; String maxType = ""; + // Iterate through typesInDeck and consider only valid types for (final Entry entry : typesInDeck.entrySet()) { final String type = entry.getKey(); - if (max < entry.getValue()) { - max = entry.getValue(); - maxType = type; + // consider the types that are in the valid list + if ((valid.isEmpty() || valid.contains(type)) && max < entry.getValue()) { + max = entry.getValue(); + maxType = type; } } diff --git a/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java b/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java index 30851113bf8..4add251484b 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/ChooseTypeEffect.java @@ -38,7 +38,7 @@ protected String getStackDescription(SpellAbility sa) { public void resolve(SpellAbility sa) { final Card card = sa.getHostCard(); final String type = sa.getParam("Type"); - final List invalidTypes = sa.hasParam("InvalidTypes") ? Arrays.asList(sa.getParam("InvalidTypes").split(",")) : new ArrayList<>(); + final List invalidTypes = getInvalidTypes(sa); final List validTypes = new ArrayList<>(); final List tgtPlayers = getTargetPlayers(sa); final boolean secret = sa.hasParam("Secretly"); @@ -154,4 +154,10 @@ public void resolve(SpellAbility sa) { throw new InvalidParameterException(sa.getHostCard() + "'s ability resulted in no types to choose from"); } } + + private static List getInvalidTypes(SpellAbility sa) { + return sa.hasParam("InvalidTypes") ? + Arrays.asList(sa.getParam("InvalidTypes").split(",")) : + new ArrayList<>(); + } } diff --git a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulationTest.java b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulationTest.java index 791a9d056fb..5a64aad7389 100644 --- a/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulationTest.java +++ b/forge-gui-desktop/src/test/java/forge/ai/simulation/GameSimulationTest.java @@ -1,12 +1,6 @@ package forge.ai.simulation; -import java.util.List; - -import org.testng.AssertJUnit; -import org.testng.annotations.Test; - import com.google.common.collect.Lists; - import forge.ai.ComputerUtilAbility; import forge.card.CardStateName; import forge.card.MagicColor; @@ -14,12 +8,19 @@ import forge.game.ability.ApiType; import forge.game.card.Card; import forge.game.card.CardCollection; +import forge.game.card.CardCollectionView; import forge.game.card.CounterEnumType; import forge.game.keyword.Keyword; import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.game.spellability.SpellAbility; import forge.game.zone.ZoneType; +import org.testng.AssertJUnit; +import org.testng.annotations.Test; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; public class GameSimulationTest extends SimulationTest { @@ -154,7 +155,7 @@ public void testEnchantedAbilities() { @Test public void testEtbTriggers() { Game game = initAndCreateGame(); - Player p0 = game.getPlayers().get(0); + Player p0 = game.getPlayers().get(0); Player p = game.getPlayers().get(1); addCard("Black Knight", p); addCards("Swamp", 5, p); @@ -1391,7 +1392,7 @@ public void testDeathsShadow() { p.setLife(-1, null); game.getAction().checkStateEffects(true); assert (deathsShadow.getNetPower() == 13); // on negative life, should - // always be 13/13 + // always be 13/13 } @Test @@ -2548,4 +2549,72 @@ public void testBasicSpellFizzling() { // spell should fizzle so no card was drawn AssertJUnit.assertEquals(0, game.getPlayers().get(0).getCardsIn(ZoneType.Hand).size()); } + + /** + * blah + */ + @Test + public void testVoloJournal() { + Game game = initAndCreateGame(); + Player p0 = game.getPlayers().get(0); + Player p = game.getPlayers().get(1); + + addCards("Island", 7, p); + addCards("Mountain", 2, p); + addCards("Forest", 2, p); + + Card c = addCardToZone("Volo, Itinerant Scholar", p, ZoneType.Hand); + Card[] cards = { + addCardToZone("Cathartic Adept", p, ZoneType.Hand), + addCardToZone("Cathartic Adept", p, ZoneType.Hand), + addCardToZone("Drowner Initiate", p, ZoneType.Hand), + addCardToZone("Atog", p, ZoneType.Hand), + addCardToZone("Atog", p, ZoneType.Hand) + }; + + game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p); + game.getAction().checkStateEffects(true); + + SpellAbility playVoloSA = c.getFirstSpellAbility(); + playVoloSA.setActivatingPlayer(p); + + GameSimulator sim = createSimulator(game, p); + sim.simulateSpellAbility(playVoloSA); + Game simGame = sim.getSimulatedGameState(); + + for (Card card : cards) { + SpellAbility a1 = card.getSpellAbilities().get(0); + a1.setActivatingPlayer(p); + sim.simulateSpellAbility(a1); + } + + Player simP = simGame.getPlayer(p.getId()); + CardCollectionView btlf = simP.getCardsIn(ZoneType.Battlefield); + List words = List.of(new String[]{"Human", "Wizard", "Atog", "Merfolk"}); + + for (Card card : btlf) { + if (card.getName().equals("Volo's Journal")) { + // All words are present in the iterable + AssertJUnit.assertTrue(areWordsInIterable(words, card.getNotedTypes())); + } + } + } + + protected boolean areWordsInIterable(List words, Iterable iterable) { + // Create a frequency map for the words in the iterable + Map frequencyMap = new HashMap<>(); + for (String item : iterable) { + frequencyMap.put(item, frequencyMap.getOrDefault(item, 0) + 1); + } + + // Check if each word in the list appears exactly once + for (String word : words) { + if (frequencyMap.getOrDefault(word, 0) != 1) { + return false; // If the word doesn't appear exactly once, return false + } + } + + return true; // All words appear exactly once + } + }