diff --git a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java index 1e062a2396d..59fcb99ce2b 100644 --- a/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java +++ b/Mage.Client/src/main/java/mage/client/remote/CallbackClientImpl.java @@ -318,7 +318,6 @@ public class CallbackClientImpl implements CallbackClient { // uses for client side only (example: update after scrollbars support) GamePanel panel = MageFrame.getGame(callback.getObjectId()); if (panel != null) { - logger.info("redraw"); panel.updateGame(); } break; diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java index 9b13c4809ae..a7dc1d138f2 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/src/mage/player/ai/ComputerPlayer6.java @@ -391,6 +391,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ { protected void resolve(SimulationNode2 node, int depth, Game game) { StackObject stackObject = game.getStack().getFirst(); if (stackObject instanceof StackAbility) { + // AI hint for search effects (calc all possible cards for best score) SearchEffect effect = getSearchEffect((StackAbility) stackObject); if (effect != null && stackObject.getControllerId().equals(playerId)) { diff --git a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java index 59d2553e7d0..d7deb819b41 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.AI/src/main/java/mage/player/ai/ComputerPlayer.java @@ -113,7 +113,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { if (hand.size() < 6 || isTestsMode() // ignore mulligan in tests || game.getClass().getName().contains("Momir") // ignore mulligan in Momir games - ) { + ) { return false; } Set lands = hand.getCards(new FilterLandCard(), game); @@ -2711,7 +2711,7 @@ public class ComputerPlayer extends PlayerImpl implements Player { } protected void findBestPermanentTargets(Outcome outcome, UUID abilityControllerId, UUID sourceId, FilterPermanent filter, Game game, Target target, - List goodList, List badList, List allList) { + List goodList, List badList, List allList) { // searching for most valuable/powerfull permanents goodList.clear(); badList.clear(); @@ -2893,27 +2893,38 @@ public class ComputerPlayer extends PlayerImpl implements Player { } /** - * Sets a possible target player + * Sets a possible target player. Depends on bad/good outcome + * + * @param source null on choose and non-null on chooseTarget */ private boolean setTargetPlayer(Outcome outcome, Target target, Ability source, UUID sourceId, UUID abilityControllerId, UUID randomOpponentId, Game game, boolean required) { + Outcome affectedOutcome; + if (abilityControllerId == this.playerId) { + // selects for itself + affectedOutcome = outcome; + } else { + // selects for another player + affectedOutcome = Outcome.inverse(outcome); + } + if (target.getOriginalTarget() instanceof TargetOpponent) { if (source == null) { if (target.canTarget(randomOpponentId, game)) { target.add(randomOpponentId, game); return true; } - } else if (target.canTarget(randomOpponentId, source, game)) { - target.add(randomOpponentId, game); + } else if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) { + target.addTarget(randomOpponentId, source, game); return true; } - for (UUID currentId : game.getOpponents(abilityControllerId)) { + for (UUID possibleOpponentId : game.getOpponents(abilityControllerId)) { if (source == null) { - if (target.canTarget(currentId, game)) { - target.add(currentId, game); + if (target.canTarget(possibleOpponentId, game)) { + target.add(possibleOpponentId, game); return true; } - } else if (target.canTarget(currentId, source, game)) { - target.add(currentId, game); + } else if (target.canTarget(abilityControllerId, possibleOpponentId, source, game)) { + target.addTarget(possibleOpponentId, source, game); return true; } } @@ -2921,8 +2932,9 @@ public class ComputerPlayer extends PlayerImpl implements Player { } if (target.getOriginalTarget() instanceof TargetPlayer) { - if (outcome.isGood()) { + if (affectedOutcome.isGood()) { if (source == null) { + // good if (target.canTarget(abilityControllerId, game)) { target.add(abilityControllerId, game); return true; @@ -2934,19 +2946,20 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } } else { + // good if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) { - target.addTarget(playerId, source, game); + target.addTarget(abilityControllerId, source, game); return true; } if (target.isRequired(sourceId, game)) { - if (target.canTarget(randomOpponentId, game)) { - target.add(randomOpponentId, game); + if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) { + target.addTarget(randomOpponentId, source, game); return true; } } } - } else if (source == null) { + // bad if (target.canTarget(randomOpponentId, game)) { target.add(randomOpponentId, game); return true; @@ -2958,13 +2971,14 @@ public class ComputerPlayer extends PlayerImpl implements Player { } } } else { - if (target.canTarget(randomOpponentId, game)) { - target.add(randomOpponentId, game); + // bad + if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) { + target.addTarget(randomOpponentId, source, game); return true; } if (required) { - if (target.canTarget(abilityControllerId, game)) { - target.add(abilityControllerId, game); + if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) { + target.addTarget(abilityControllerId, source, game); return true; } } diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java index 6feb0d0eeca..097c08ef3b7 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/src/mage/player/ai/ComputerPlayer2.java @@ -351,6 +351,7 @@ public class ComputerPlayer2 extends ComputerPlayer implements Player { } protected int simulatePriority(SimulationNode node, Game game, int alpha, int beta) { + // NOT USED in real AI, see ComputerPlayer6 if (Thread.interrupted()) { Thread.currentThread().interrupt(); logger.debug(indent(node.depth) + "interrupted"); diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java index 2f78ce079f1..00c6bf24775 100644 --- a/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/cards/abilities/keywords/HexproofTest.java @@ -3,6 +3,7 @@ package org.mage.test.cards.abilities.keywords; import mage.abilities.keyword.HexproofAbility; import mage.constants.PhaseStep; import mage.constants.Zone; +import mage.counters.CounterType; import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps; @@ -127,6 +128,7 @@ public class HexproofTest extends CardTestPlayerBaseWithAIHelps { assertAllCommandsUsed(); assertGraveyardCount(playerA, "Swamp", 1); + assertCounterCount(playerA, "Liliana Vess", CounterType.LOYALTY, 5 + 1); } @Test @@ -158,7 +160,7 @@ public class HexproofTest extends CardTestPlayerBaseWithAIHelps { } @Test - public void test_AI_MustTargetOnlyValid() { + public void test_AI_MustTargetOnlyValid_1() { // +1: Target player discards a card. addCard(Zone.BATTLEFIELD, playerA, "Liliana Vess", 1); addCard(Zone.HAND, playerA, "Balduvian Bears", 1); @@ -180,6 +182,54 @@ public class HexproofTest extends CardTestPlayerBaseWithAIHelps { // no discarded cards assertGraveyardCount(playerA, 0); assertGraveyardCount(playerB, 0); + // no activated abilities + assertCounterCount(playerA, "Liliana Vess", CounterType.LOYALTY, 5 - 2); // search library for -2 + } + + @Test + public void test_AI_MustTargetOnlyValid_2() { + // +1: Target player discards a card. + addCard(Zone.BATTLEFIELD, playerA, "Liliana Vess", 1); + addCard(Zone.HAND, playerA, "Balduvian Bears", 1); + addCard(Zone.HAND, playerA, "Swamp", 1); + addCard(Zone.HAND, playerB, "Matter Reshaper", 1); + addCard(Zone.HAND, playerB, "Mountain", 1); + // + // You and permanents you control gain hexproof from blue and from black until end of turn. + addCard(Zone.HAND, playerB, "Veil of Summer", 1); // instant {G} + addCard(Zone.BATTLEFIELD, playerB, "Forest", 1); + + // prepare hexproof + castSpell(1, PhaseStep.UPKEEP, playerB, "Veil of Summer"); + + // ai must not use +1 on itself (due bad score) and must not use on opponent (due hexproof) + aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + // no discarded cards + assertGraveyardCount(playerA, 0); + assertGraveyardCount(playerB, 1); // Veil of Summer + // no activated abilities + assertCounterCount(playerA, "Liliana Vess", CounterType.LOYALTY, 5 - 2); // search library for -2 + } + + @Test + public void test_AI_Logs() { + addCard(Zone.HAND, playerA, "Lightning Bolt", 3); + addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3); + + aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA); + + setStrictChooseMode(true); + setStopAt(1, PhaseStep.END_TURN); + execute(); + assertAllCommandsUsed(); + + assertLife(playerB, 20 - 3 * 3); } @Test