diff --git a/forge-ai/src/main/java/forge/ai/simulation/GameStateEvaluator.java b/forge-ai/src/main/java/forge/ai/simulation/GameStateEvaluator.java index f4fcd71e5ad..585917aead9 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/GameStateEvaluator.java +++ b/forge-ai/src/main/java/forge/ai/simulation/GameStateEvaluator.java @@ -8,6 +8,7 @@ import forge.game.phase.PhaseType; import forge.game.player.Player; import forge.game.spellability.AbilityManaPart; import forge.game.spellability.SpellAbility; +import forge.game.staticability.StaticAbility; import forge.game.zone.ZoneType; import java.util.Arrays; @@ -173,9 +174,9 @@ public class GameStateEvaluator { colors_produced.addAll(Arrays.asList(mp.mana(m).split(" "))); } } - value = 100 * max_produced; + value += 100 * max_produced; int size = max(colors_produced.size(), colors_produced.contains("Any") ? 5 : 0); - value += size * 2; + value += size * 6; // add a value for each activated ability that the land has that's not an activated ability. for (SpellAbility m: c.getNonManaAbilities()) { @@ -184,6 +185,12 @@ public class GameStateEvaluator { value += 6; } + // Add a value for each static ability that the land has + for (StaticAbility s : c.getStaticAbilities()) { + // More than the value of having a card in hand. See comment above + value += 6; + } + return value; } else if (c.isEnchantingCard()) { diff --git a/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java b/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java index f7481bbbcb6..c48232afec4 100644 --- a/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java +++ b/forge-ai/src/main/java/forge/ai/simulation/SpellAbilityPicker.java @@ -64,7 +64,7 @@ public class SpellAbilityPicker { print("---- choose ability (phase = " + phaseStr + ")"); } - private List getCandidateSpellsAndAbilities() { + public List getCandidateSpellsAndAbilities() { CardCollection cards = ComputerUtilAbility.getAvailableCards(game, player); cards = ComputerUtilCard.dedupeCards(cards); List all = ComputerUtilAbility.getSpellAbilities(cards, player); @@ -351,7 +351,7 @@ public class SpellAbilityPicker { return AiPlayDecision.WillPlay; } - private Score evaluateSa(final SimulationController controller, PhaseType phase, List saList, int saIndex) { + public Score evaluateSa(final SimulationController controller, PhaseType phase, List saList, int saIndex) { controller.evaluateSpellAbility(saList, saIndex); SpellAbility sa = saList.get(saIndex); diff --git a/forge-gui-desktop/src/test/java/forge/ai/simulation/SimulationTest.java b/forge-gui-desktop/src/test/java/forge/ai/simulation/SimulationTest.java index 3acb9437f43..113125f86da 100644 --- a/forge-gui-desktop/src/test/java/forge/ai/simulation/SimulationTest.java +++ b/forge-gui-desktop/src/test/java/forge/ai/simulation/SimulationTest.java @@ -33,6 +33,22 @@ import forge.model.FModel; public class SimulationTest { private static boolean initialized = false; + public Game resetGame() { + // need to be done after FModel.initialize, or the Localizer isn't loaded yet + List players = Lists.newArrayList(); + Deck d1 = new Deck(); + players.add(new RegisteredPlayer(d1).setPlayer(new LobbyPlayerAi("p2", null))); + Set options = new HashSet<>(); + options.add(AIOption.USE_SIMULATION); + players.add(new RegisteredPlayer(d1).setPlayer(new LobbyPlayerAi("p1", options))); + GameRules rules = new GameRules(GameType.Constructed); + Match match = new Match(rules, players, "Test"); + Game game = new Game(players, rules, match); + game.setAge(GameStage.Play); + + return game; + } + protected Game initAndCreateGame() { if (!initialized) { GuiBase.setInterface(new GuiDesktop()); @@ -47,19 +63,7 @@ public class SimulationTest { initialized = true; } - // need to be done after FModel.initialize, or the Localizer isn't loaded yet - List players = Lists.newArrayList(); - Deck d1 = new Deck(); - players.add(new RegisteredPlayer(d1).setPlayer(new LobbyPlayerAi("p2", null))); - Set options = new HashSet<>(); - options.add(AIOption.USE_SIMULATION); - players.add(new RegisteredPlayer(d1).setPlayer(new LobbyPlayerAi("p1", options))); - GameRules rules = new GameRules(GameType.Constructed); - Match match = new Match(rules, players, "Test"); - Game game = new Game(players, rules, match); - game.setAge(GameStage.Play); - - return game; + return resetGame(); } protected GameSimulator createSimulator(Game game, Player p) { diff --git a/forge-gui-desktop/src/test/java/forge/ai/simulation/SpellAbilityPickerSimulationTest.java b/forge-gui-desktop/src/test/java/forge/ai/simulation/SpellAbilityPickerSimulationTest.java index 92956d8ea68..05b5e8d7963 100644 --- a/forge-gui-desktop/src/test/java/forge/ai/simulation/SpellAbilityPickerSimulationTest.java +++ b/forge-gui-desktop/src/test/java/forge/ai/simulation/SpellAbilityPickerSimulationTest.java @@ -1,8 +1,12 @@ package forge.ai.simulation; import forge.game.spellability.LandAbility; + +import java.util.ArrayList; import java.util.List; +import forge.item.PaperCard; +import forge.model.FModel; import org.testng.AssertJUnit; import org.testng.annotations.Test; @@ -371,6 +375,84 @@ public class SpellAbilityPickerSimulationTest extends SimulationTest { AssertJUnit.assertEquals(desired, sa.getTargetCard()); } + @Test + public void ensureAllLandsArePlayable() { + initAndCreateGame(); + + System.out.println("Adding lands to hand"); + + // add every land to the player's hand +// SimulationController.MAX_DEPTH = 0; + List funky = new ArrayList<>(); + String previous = ""; + for (PaperCard c : FModel.getMagicDb().getCommonCards().getAllCards()) { + // reset the game + Game game = resetGame(); + Player p = game.getPlayers().get(1); + Player opponent = game.getPlayers().get(0); + opponent.setLife(20, null); + + // add one of each basic to the battlefield so that bouncelands and similar work + addCard("Plains", p); + addCard("Island", p); + addCard("Swamp", p); + addCard("Mountain", p); + addCard("Forest", p); + // Add basics to library to ensure fetches work + addCardToZone("Plains", p, ZoneType.Library); + addCardToZone("Island", p, ZoneType.Library); + addCardToZone("Swamp", p, ZoneType.Library); + addCardToZone("Mountain", p, ZoneType.Library); + addCardToZone("Forest", p, ZoneType.Library); + + game.getPhaseHandler().devModeSet(PhaseType.MAIN1, p); + game.getAction().checkStateEffects(true); + + // Only add one version at a time + if (c.getName().equals(previous)) { + continue; + } + previous = c.getName(); + if (c.getRules().getType().isLand()) { + // Skip glacial chasm, it's really weird. + if (c.getName().equals("Glacial Chasm")) { + System.out.println("Skipping " + c.getName()); + continue; + } + + addCardToZone(c.getName(), p, ZoneType.Hand); + + // Once the card has been added to the hand, test it + + GameStateEvaluator.Score s = new GameStateEvaluator().getScoreForGameState(game, p); + System.out.println("Starting score: " + s); + SpellAbilityPicker picker = new SpellAbilityPicker(game, p); + List candidateSAs = picker.getCandidateSpellsAndAbilities(); + for (int i = 0; i < candidateSAs.size(); i++) { + SpellAbility sa = candidateSAs.get(i); + if (sa.isActivatedAbility()) { + continue; + } + GameStateEvaluator.Score value = picker.evaluateSa(new SimulationController(s), game.getPhaseHandler().getPhase(), candidateSAs, i); + System.out.println("sa: " + sa.getHostCard() + ", value: " + value); + if (!(value.value > s.value)) { + funky.add(sa.getHostCard()); + } + } + } + } + + // ensure that every land play has a higher evaluation than doing nothing + System.out.println(funky); + for (Card c : funky) { + GameStateEvaluator gse = new GameStateEvaluator(); + Game game = resetGame(); + System.out.println(c.getName() + ": " + gse.evalCard(game, game.getStartingPlayer(), c)); + } + AssertJUnit.assertEquals(0, funky.size()); + } + + @Test public void testPlayRememberedCardsLand() { Game game = initAndCreateGame();