AI: improved combat support:

* added support to attack battle permanents (#10246);
* fixed that AI was able to attack multiple targets by single creature (#7434);
* added docs;
* added todos with another bugs or possible problems with AI combat;
This commit is contained in:
Oleg Agafonov 2024-01-19 23:37:35 +04:00
parent 6858d43547
commit e4157fefb8
16 changed files with 239 additions and 89 deletions

View file

@ -3,20 +3,22 @@ package org.mage.test.cards.battle;
import mage.game.permanent.Permanent;
import mage.players.Player;
import org.junit.jupiter.api.Assertions;
import org.mage.test.serverside.base.CardTestPlayerBase;
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
/**
* @author TheElk801
*/
public class BattleBaseTest extends CardTestPlayerBase {
public class BattleBaseTest extends CardTestPlayerBaseWithAIHelps {
protected static final String belenon = "Invasion of Belenon";
protected static final String warAnthem = "Belenon War Anthem";
protected static final String kaladesh = "Invasion of Kaladesh";
protected static final String bear = "Grizzly Bears";
protected static final String bearWithFlyingAndVigilance = "Abbey Griffin";
protected static final String confiscate = "Confiscate";
protected static final String impact = "Explosive Impact";
protected static final String stifle = "Stifle";
protected static final String fayden = "Dack Fayden";
protected void assertBattle(Player controller, Player protector, String name) {
assertPermanentCount(controller, name, 1);

View file

@ -6,7 +6,7 @@ import mage.counters.CounterType;
import org.junit.Test;
/**
* @author TheElk801
* @author TheElk801, JayDi85
*/
public class BattleDuelTest extends BattleBaseTest {
@ -155,6 +155,7 @@ public class BattleDuelTest extends BattleBaseTest {
assertGraveyardCount(playerA, kaladesh, 1);
assertPermanentCount(playerA, "Thopter Token", 1);
}
@Test
public void testSpellCardTypeTrigger() {
addCard(Zone.BATTLEFIELD, playerA, "Plateau", 3 + 6);
@ -174,6 +175,93 @@ public class BattleDuelTest extends BattleBaseTest {
assertPermanentCount(playerA, "Serra Faithkeeper", 1);
assertPermanentCount(playerA, "Warrior Token", 1);
assertPowerToughness(playerA, "Deeproot Champion",3,3);
assertPowerToughness(playerA, "Deeproot Champion", 3, 3);
}
@Test
public void test_AI_CastBattle() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
addCard(Zone.HAND, playerA, belenon);
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertBattle(playerA, playerB, belenon);
assertPermanentCount(playerA, "Knight Token", 1);
}
@Test
public void test_AI_AttackPriority_TargetBattleInsteadPlayer() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
addCard(Zone.BATTLEFIELD, playerA, bear);
addCard(Zone.HAND, playerA, belenon);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, belenon);
// ai must attack planeswalker instead player
aiPlayStep(1, PhaseStep.DECLARE_ATTACKERS, playerA);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertBattle(playerA, playerB, belenon);
assertPermanentCount(playerA, "Knight Token", 1);
assertTapped(bear, true);
assertLife(playerB, 20);
assertCounterCount(belenon, CounterType.DEFENSE, 5 - 2);
}
@Test
public void test_AI_AttackPriority_TargetPlaneswalkerInsteadBattle() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
addCard(Zone.BATTLEFIELD, playerA, bear);
addCard(Zone.BATTLEFIELD, playerB, fayden); // 3
addCard(Zone.HAND, playerA, belenon);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, belenon);
// ai must attack planeswalker instead battle/player
aiPlayStep(1, PhaseStep.DECLARE_ATTACKERS, playerA);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertBattle(playerA, playerB, belenon);
assertPermanentCount(playerA, "Knight Token", 1);
assertTapped(bear, true);
assertLife(playerB, 20);
assertCounterCount(belenon, CounterType.DEFENSE, 5);
assertCounterCount(fayden, CounterType.LOYALTY, 3 - 2);
}
@Test
public void test_AI_MustNotAttackMultipleTargets() {
// bug with multiple targets for single attacker: https://github.com/magefree/mage/issues/7434
addCard(Zone.BATTLEFIELD, playerA, "Plains", 3);
addCard(Zone.BATTLEFIELD, playerA, bearWithFlyingAndVigilance);
addCard(Zone.BATTLEFIELD, playerB, fayden); // 3
addCard(Zone.HAND, playerA, belenon);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, belenon);
// ai must attack planeswalker instead battle/player
// ai must attack only single target
aiPlayStep(1, PhaseStep.DECLARE_ATTACKERS, playerA);
setStrictChooseMode(true);
setStopAt(1, PhaseStep.END_TURN);
execute();
assertBattle(playerA, playerB, belenon);
assertPermanentCount(playerA, "Knight Token", 1);
assertTapped(bearWithFlyingAndVigilance, false); // vigilance
assertLife(playerB, 20);
assertCounterCount(belenon, CounterType.DEFENSE, 5);
assertCounterCount(fayden, CounterType.LOYALTY, 3 - 2);
}
}

View file

@ -14,7 +14,7 @@ public abstract class CardTestCommander4PlayersWithAIHelps extends CardTestComma
@Override
protected TestPlayer createPlayer(String name, RangeOfInfluence rangeOfInfluence) {
// use same RangeOfInfluence.ALL as CardTestCommander4Players do
TestPlayer testPlayer = new TestPlayer(new TestComputerPlayer7(name, RangeOfInfluence.ALL, 6));
TestPlayer testPlayer = new TestPlayer(new TestComputerPlayer7(name, rangeOfInfluence, 6));
testPlayer.setAIPlayer(false); // AI can't play it by itself, use AI commands
return testPlayer;
}

View file

@ -15,7 +15,7 @@ public abstract class CardTestPlayerBaseWithAIHelps extends CardTestPlayerBase {
@Override
protected TestPlayer createPlayer(String name, RangeOfInfluence rangeOfInfluence) {
TestPlayer testPlayer = new TestPlayer(new TestComputerPlayer7(name, RangeOfInfluence.ONE, 6));
TestPlayer testPlayer = new TestPlayer(new TestComputerPlayer7(name, rangeOfInfluence, 6));
testPlayer.setAIPlayer(false); // AI can't play it by itself, use AI commands
return testPlayer;
}

View file

@ -1681,6 +1681,8 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
* AI play STEP to the end with multi game simulations (calcs and play best
* actions until step ends, can be called in the middle of the step) All
* choices must be made by AI (e.g. strict mode possible)
* <p>
* Can be used for AI's declare of attackers/blockers
*/
public void aiPlayStep(int turnNum, PhaseStep step, TestPlayer player) {
assertAiPlayAndGameCompatible(player);