diff --git a/forge-ai/src/main/java/forge/ai/AiController.java b/forge-ai/src/main/java/forge/ai/AiController.java index 3dee39ead59..177bd09324d 100644 --- a/forge-ai/src/main/java/forge/ai/AiController.java +++ b/forge-ai/src/main/java/forge/ai/AiController.java @@ -1594,7 +1594,8 @@ public class AiController { String assertex = ComparatorUtil.verifyTransitivity(ComputerUtilAbility.saEvaluator, all); Sentry.captureMessage(ex.getMessage() + "\nAssertionError [verifyTransitivity]: " + assertex); } - + //avoid ComputerUtil.aiLifeInDanger in loops as it slows down a lot.. call this outside loops will generally be fast... + boolean isLifeInDanger = player.isLivingEnd() && ComputerUtil.aiLifeInDanger(player, true, 0); for (final SpellAbility sa : ComputerUtilAbility.getOriginalAndAltCostAbilities(all, player)) { // Don't add Counterspells to the "normal" playcard lookups if (skipCounter && sa.getApi() == ApiType.Counter) { @@ -1609,6 +1610,29 @@ public class AiController { continue; } } + //living end AI decks + AiPlayDecision aiPlayDecision = AiPlayDecision.CantPlaySa; + if (player.isLivingEnd()) { + if (CardLists.filter(player.getZone(ZoneType.Library).getCards(), c-> "Living End".equalsIgnoreCase(c.getName())).size() < 1) { + player.setHasLivingEnd(false); // stop forced playing since we don't have any choice + continue; + } + if (sa.isCycling() && sa.canCastTiming(player)) { + if (ComputerUtilCost.canPayCost(sa, player, sa.isTrigger())) + aiPlayDecision = AiPlayDecision.WillPlay; + } else if (sa.getHostCard().hasKeyword(Keyword.CASCADE)) { + if (isLifeInDanger) { //needs more tune up for certain conditions + aiPlayDecision = player.getCreaturesInPlay().size() >= 4 ? AiPlayDecision.CantPlaySa : AiPlayDecision.WillPlay; + } else if (CardLists.filter(player.getZone(ZoneType.Graveyard).getCards(), CardPredicates.Presets.CREATURES).size() > 4) { + if (player.getCreaturesInPlay().size() >= 4) // it's good minimum + continue; + else if (!sa.getHostCard().isPermanent() && sa.canCastTiming(player) && ComputerUtilCost.canPayCost(sa, player, sa.isTrigger())) + aiPlayDecision = AiPlayDecision.WillPlay;// needs tuneup for bad matchups like reanimator and other things to check on opponent graveyard + } else { + continue; + } + } + } sa.setActivatingPlayer(player, true); SpellAbility root = sa.getRootAbility(); @@ -1617,8 +1641,8 @@ public class AiController { sa.setLastStateBattlefield(game.getLastStateBattlefield()); sa.setLastStateGraveyard(game.getLastStateGraveyard()); } - - AiPlayDecision opinion = canPlayAndPayFor(sa); + //override decision for living end player + AiPlayDecision opinion = player.isLivingEnd() && AiPlayDecision.WillPlay.equals(aiPlayDecision) ? aiPlayDecision : canPlayAndPayFor(sa); // reset LastStateBattlefield sa.clearLastState(); diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index c766958d429..95a2fd331bc 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -2146,6 +2146,11 @@ public class ComputerUtil { final int handSize = handList.size(); final int landSize = lands.size(); int score = handList.size(); + if (ai.isLivingEnd()) { + final CardCollectionView livingEnd = CardLists.filter(handList, c -> "Living End".equalsIgnoreCase(c.getName())); + if (livingEnd.size() > 0) + score = -(livingEnd.size() * 10); + } if (handSize/2 == landSize || handSize/2 == landSize +1) { score += 10; diff --git a/forge-game/src/main/java/forge/game/Game.java b/forge-game/src/main/java/forge/game/Game.java index 822952de6d4..709f1ae9bd5 100644 --- a/forge-game/src/main/java/forge/game/Game.java +++ b/forge-game/src/main/java/forge/game/Game.java @@ -265,6 +265,7 @@ public class Game { for (RegisteredPlayer psc : players0) { IGameEntitiesFactory factory = (IGameEntitiesFactory)psc.getPlayer(); Player pl = factory.createIngamePlayer(this, plId++); + pl.setHasLivingEnd(psc.getDeck().getMain().countByName("Living End", true) > 0); allPlayers.add(pl); ingamePlayers.add(pl); diff --git a/forge-game/src/main/java/forge/game/player/Player.java b/forge-game/src/main/java/forge/game/player/Player.java index ebcba8996e7..fd7f0f0ed9c 100644 --- a/forge-game/src/main/java/forge/game/player/Player.java +++ b/forge-game/src/main/java/forge/game/player/Player.java @@ -168,6 +168,7 @@ public class Player extends GameEntity implements Comparable { private int maxHandSize = 7; private int startingHandSize = 7; private boolean unlimitedHandSize = false; + private boolean hasLivingEnd = false; private Card lastDrawnCard; private Card ringBearer, theRing; private String namedCard = ""; @@ -455,6 +456,13 @@ public class Player extends GameEntity implements Comparable { return isOpponentOf(otherPlayer); } + public boolean isLivingEnd() { + return hasLivingEnd; + } + public void setHasLivingEnd(boolean value) { + hasLivingEnd = value; + } + public final boolean setLife(final int newLife, final SpellAbility sa) { boolean change = false; // rule 119.5