mirror of
https://github.com/correl/mage.git
synced 2024-12-26 03:00:11 +00:00
AI improves:
* AI: fixed that AI was able to targeting an invalid player targets in some use cases (#5630); * AI: fixed that AI was able to ignore targeted triggers in some use cases; * AI: improved player targeting for not own chooses (if it's make a choice for another player);
This commit is contained in:
parent
f3b8f0a44a
commit
42325c7c2e
5 changed files with 86 additions and 21 deletions
|
@ -318,7 +318,6 @@ public class CallbackClientImpl implements CallbackClient {
|
||||||
// uses for client side only (example: update after scrollbars support)
|
// uses for client side only (example: update after scrollbars support)
|
||||||
GamePanel panel = MageFrame.getGame(callback.getObjectId());
|
GamePanel panel = MageFrame.getGame(callback.getObjectId());
|
||||||
if (panel != null) {
|
if (panel != null) {
|
||||||
logger.info("redraw");
|
|
||||||
panel.updateGame();
|
panel.updateGame();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -391,6 +391,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
||||||
protected void resolve(SimulationNode2 node, int depth, Game game) {
|
protected void resolve(SimulationNode2 node, int depth, Game game) {
|
||||||
StackObject stackObject = game.getStack().getFirst();
|
StackObject stackObject = game.getStack().getFirst();
|
||||||
if (stackObject instanceof StackAbility) {
|
if (stackObject instanceof StackAbility) {
|
||||||
|
// AI hint for search effects (calc all possible cards for best score)
|
||||||
SearchEffect effect = getSearchEffect((StackAbility) stackObject);
|
SearchEffect effect = getSearchEffect((StackAbility) stackObject);
|
||||||
if (effect != null
|
if (effect != null
|
||||||
&& stackObject.getControllerId().equals(playerId)) {
|
&& stackObject.getControllerId().equals(playerId)) {
|
||||||
|
|
|
@ -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) {
|
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 (target.getOriginalTarget() instanceof TargetOpponent) {
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
if (target.canTarget(randomOpponentId, game)) {
|
if (target.canTarget(randomOpponentId, game)) {
|
||||||
target.add(randomOpponentId, game);
|
target.add(randomOpponentId, game);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (target.canTarget(randomOpponentId, source, game)) {
|
} else if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) {
|
||||||
target.add(randomOpponentId, game);
|
target.addTarget(randomOpponentId, source, game);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (UUID currentId : game.getOpponents(abilityControllerId)) {
|
for (UUID possibleOpponentId : game.getOpponents(abilityControllerId)) {
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
if (target.canTarget(currentId, game)) {
|
if (target.canTarget(possibleOpponentId, game)) {
|
||||||
target.add(currentId, game);
|
target.add(possibleOpponentId, game);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (target.canTarget(currentId, source, game)) {
|
} else if (target.canTarget(abilityControllerId, possibleOpponentId, source, game)) {
|
||||||
target.add(currentId, game);
|
target.addTarget(possibleOpponentId, source, game);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2921,8 +2932,9 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target.getOriginalTarget() instanceof TargetPlayer) {
|
if (target.getOriginalTarget() instanceof TargetPlayer) {
|
||||||
if (outcome.isGood()) {
|
if (affectedOutcome.isGood()) {
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
|
// good
|
||||||
if (target.canTarget(abilityControllerId, game)) {
|
if (target.canTarget(abilityControllerId, game)) {
|
||||||
target.add(abilityControllerId, game);
|
target.add(abilityControllerId, game);
|
||||||
return true;
|
return true;
|
||||||
|
@ -2934,19 +2946,20 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// good
|
||||||
if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) {
|
if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) {
|
||||||
target.addTarget(playerId, source, game);
|
target.addTarget(abilityControllerId, source, game);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (target.isRequired(sourceId, game)) {
|
if (target.isRequired(sourceId, game)) {
|
||||||
if (target.canTarget(randomOpponentId, game)) {
|
if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) {
|
||||||
target.add(randomOpponentId, game);
|
target.addTarget(randomOpponentId, source, game);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (source == null) {
|
} else if (source == null) {
|
||||||
|
// bad
|
||||||
if (target.canTarget(randomOpponentId, game)) {
|
if (target.canTarget(randomOpponentId, game)) {
|
||||||
target.add(randomOpponentId, game);
|
target.add(randomOpponentId, game);
|
||||||
return true;
|
return true;
|
||||||
|
@ -2958,13 +2971,14 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (target.canTarget(randomOpponentId, game)) {
|
// bad
|
||||||
target.add(randomOpponentId, game);
|
if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) {
|
||||||
|
target.addTarget(randomOpponentId, source, game);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (required) {
|
if (required) {
|
||||||
if (target.canTarget(abilityControllerId, game)) {
|
if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) {
|
||||||
target.add(abilityControllerId, game);
|
target.addTarget(abilityControllerId, source, game);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -351,6 +351,7 @@ public class ComputerPlayer2 extends ComputerPlayer implements Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int simulatePriority(SimulationNode node, Game game, int alpha, int beta) {
|
protected int simulatePriority(SimulationNode node, Game game, int alpha, int beta) {
|
||||||
|
// NOT USED in real AI, see ComputerPlayer6
|
||||||
if (Thread.interrupted()) {
|
if (Thread.interrupted()) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
logger.debug(indent(node.depth) + "interrupted");
|
logger.debug(indent(node.depth) + "interrupted");
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.abilities.keywords;
|
||||||
import mage.abilities.keyword.HexproofAbility;
|
import mage.abilities.keyword.HexproofAbility;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
|
import mage.counters.CounterType;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||||
|
@ -127,6 +128,7 @@ public class HexproofTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
assertAllCommandsUsed();
|
assertAllCommandsUsed();
|
||||||
|
|
||||||
assertGraveyardCount(playerA, "Swamp", 1);
|
assertGraveyardCount(playerA, "Swamp", 1);
|
||||||
|
assertCounterCount(playerA, "Liliana Vess", CounterType.LOYALTY, 5 + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -158,7 +160,7 @@ public class HexproofTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test_AI_MustTargetOnlyValid() {
|
public void test_AI_MustTargetOnlyValid_1() {
|
||||||
// +1: Target player discards a card.
|
// +1: Target player discards a card.
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Liliana Vess", 1);
|
addCard(Zone.BATTLEFIELD, playerA, "Liliana Vess", 1);
|
||||||
addCard(Zone.HAND, playerA, "Balduvian Bears", 1);
|
addCard(Zone.HAND, playerA, "Balduvian Bears", 1);
|
||||||
|
@ -180,6 +182,54 @@ public class HexproofTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
// no discarded cards
|
// no discarded cards
|
||||||
assertGraveyardCount(playerA, 0);
|
assertGraveyardCount(playerA, 0);
|
||||||
assertGraveyardCount(playerB, 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
|
@Test
|
||||||
|
|
Loading…
Reference in a new issue