forked from External/mage
tests: added many use cases for must be blocked, must blocking and menace effects (related to #13182)
This commit is contained in:
parent
aef220a19b
commit
b2279a8e9c
2 changed files with 423 additions and 44 deletions
|
|
@ -5,15 +5,17 @@ import mage.constants.Zone;
|
|||
import mage.counters.CounterType;
|
||||
import mage.game.permanent.Permanent;
|
||||
import org.junit.Assert;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* Test restrictions for choosing attackers and blockers.
|
||||
*
|
||||
* @author noxx
|
||||
* @author noxx, JayDi85
|
||||
*/
|
||||
public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
|
||||
|
||||
|
|
@ -337,10 +339,9 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
|
|||
/*
|
||||
* Mogg Flunkies cannot attack alone. Cards like Goblin Assault force all goblins to attack each turn.
|
||||
* Mogg Flunkies should not be able to attack.
|
||||
*/
|
||||
*/
|
||||
@Test
|
||||
public void testMustAttackButCannotAttackAlone()
|
||||
{
|
||||
public void testMustAttackButCannotAttackAlone() {
|
||||
/* Mogg Flunkies {1}{R} 3/3
|
||||
Creature — Goblin
|
||||
Mogg Flunkies can't attack or block alone.
|
||||
|
|
@ -434,11 +435,11 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
|
|||
|
||||
@Test
|
||||
public void underworldCerberusBlockedByOneTest() {
|
||||
/* Underworld Cerberus {3}{B}{3} 6/6
|
||||
* Underworld Cerberus can't be blocked except by three or more creatures.
|
||||
* Cards in graveyards can't be the targets of spells or abilities.
|
||||
* When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
|
||||
*/
|
||||
/* Underworld Cerberus {3}{B}{3} 6/6
|
||||
* Underworld Cerberus can't be blocked except by three or more creatures.
|
||||
* Cards in graveyards can't be the targets of spells or abilities.
|
||||
* When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
|
||||
*/
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Underworld Cerberus");
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite"); // 1/1
|
||||
|
||||
|
|
@ -450,18 +451,18 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
|
|||
try {
|
||||
execute();
|
||||
fail("Expected exception not thrown");
|
||||
} catch(UnsupportedOperationException e) {
|
||||
} catch (UnsupportedOperationException e) {
|
||||
assertEquals("Underworld Cerberus is blocked by 1 creature(s). It has to be blocked by 3 or more.", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void underworldCerberusBlockedByTwoTest() {
|
||||
/* Underworld Cerberus {3}{B}{3} 6/6
|
||||
* Underworld Cerberus can't be blocked except by three or more creatures.
|
||||
* Cards in graveyards can't be the targets of spells or abilities.
|
||||
* When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
|
||||
*/
|
||||
/* Underworld Cerberus {3}{B}{3} 6/6
|
||||
* Underworld Cerberus can't be blocked except by three or more creatures.
|
||||
* Cards in graveyards can't be the targets of spells or abilities.
|
||||
* When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
|
||||
*/
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Underworld Cerberus");
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 2); // 1/1
|
||||
|
||||
|
|
@ -474,7 +475,7 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
|
|||
try {
|
||||
execute();
|
||||
fail("Expected exception not thrown");
|
||||
} catch(UnsupportedOperationException e) {
|
||||
} catch (UnsupportedOperationException e) {
|
||||
assertEquals("Underworld Cerberus is blocked by 2 creature(s). It has to be blocked by 3 or more.", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
|
@ -482,11 +483,11 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
|
|||
@Test
|
||||
public void underworldCerberusBlockedByThreeTest() {
|
||||
|
||||
/* Underworld Cerberus {3}{B}{3} 6/6
|
||||
* Underworld Cerberus can't be blocked except by three or more creatures.
|
||||
* Cards in graveyards can't be the targets of spells or abilities.
|
||||
* When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
|
||||
*/
|
||||
/* Underworld Cerberus {3}{B}{3} 6/6
|
||||
* Underworld Cerberus can't be blocked except by three or more creatures.
|
||||
* Cards in graveyards can't be the targets of spells or abilities.
|
||||
* When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
|
||||
*/
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Underworld Cerberus");
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 3); // 1/1
|
||||
|
||||
|
|
@ -511,17 +512,17 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
|
|||
|
||||
@Test
|
||||
public void underworldCerberusBlockedByTenTest() {
|
||||
/* Underworld Cerberus {3}{B}{3} 6/6
|
||||
* Underworld Cerberus can't be blocked except by three or more creatures.
|
||||
* Cards in graveyards can't be the targets of spells or abilities.
|
||||
* When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
|
||||
*/
|
||||
/* Underworld Cerberus {3}{B}{3} 6/6
|
||||
* Underworld Cerberus can't be blocked except by three or more creatures.
|
||||
* Cards in graveyards can't be the targets of spells or abilities.
|
||||
* When Underworld Cerberus dies, exile it and each player returns all creature cards from their graveyard to their hand.
|
||||
*/
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Underworld Cerberus");
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 10); // 1/1
|
||||
|
||||
// Blocked by 10 creatures - this is acceptable as it's >3
|
||||
attack(3, playerA, "Underworld Cerberus");
|
||||
for(int i = 0; i < 10; i++) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
block(3, playerB, "Memnite:" + i, "Underworld Cerberus");
|
||||
}
|
||||
|
||||
|
|
@ -541,24 +542,24 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
|
|||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void irresistiblePreyMustBeBlockedTest() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Llanowar Elves");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Alpha Myr");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest");
|
||||
addCard(Zone.HAND, playerA, "Irresistible Prey");
|
||||
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Bronze Sable");
|
||||
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Irresistible Prey", "Llanowar Elves"); // must be blocked
|
||||
|
||||
|
||||
attack(1, playerA, "Llanowar Elves");
|
||||
attack(1, playerA, "Alpha Myr");
|
||||
|
||||
|
||||
// attempt to block the creature that doesn't have "must be blocked"
|
||||
block(1, playerB, "Bronze Sable", "Alpha Myr");
|
||||
|
||||
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
|
|
@ -568,4 +569,383 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
|
|||
assertTapped("Alpha Myr", true);
|
||||
assertLife(playerB, 18);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_MustBeBlocked_nothing() {
|
||||
// Fear of Being Hunted must be blocked if able.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Fear of Being Hunted"); // 4/2
|
||||
|
||||
attack(1, playerA, "Fear of Being Hunted");
|
||||
checkAttackers("x1 attacker", 1, playerA, "Fear of Being Hunted");
|
||||
checkBlockers("no blocker", 1, playerB, "");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20 - 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_MustBeBlocked_1_blocker() {
|
||||
// Fear of Being Hunted must be blocked if able.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Fear of Being Hunted"); // 4/2
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Alpha Myr", 1); // 2/1
|
||||
|
||||
attack(1, playerA, "Fear of Being Hunted");
|
||||
checkAttackers("x1 attacker", 1, playerA, "Fear of Being Hunted");
|
||||
checkBlockers("forced x1 blocker", 1, playerB, "Alpha Myr");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20);
|
||||
assertGraveyardCount(playerA, "Fear of Being Hunted", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_MustBeBlocked_many_blockers_good() {
|
||||
// Fear of Being Hunted must be blocked if able.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Fear of Being Hunted"); // 4/2
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Spectral Bears", 10); // 3/3
|
||||
|
||||
// TODO: human logic can't be tested (until isHuman replaced by ~isComputer), so current use case will
|
||||
// take first available blocker
|
||||
attack(1, playerA, "Fear of Being Hunted");
|
||||
checkAttackers("x1 attacker", 1, playerA, "Fear of Being Hunted");
|
||||
checkBlockers("x1 optimal blocker", 1, playerB, "Spectral Bears");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20);
|
||||
assertGraveyardCount(playerA, "Fear of Being Hunted", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_MustBeBlocked_many_blockers_bad() {
|
||||
// Fear of Being Hunted must be blocked if able.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Fear of Being Hunted"); // 4/2
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 10); // 1/1
|
||||
|
||||
// TODO: human logic can't be tested (until isHuman replaced by ~isComputer), so current use case will
|
||||
// take first available blocker
|
||||
attack(1, playerA, "Fear of Being Hunted");
|
||||
checkAttackers("x1 attacker", 1, playerA, "Fear of Being Hunted");
|
||||
checkBlockers("x1 optimal blocker", 1, playerB, "Memnite");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20);
|
||||
assertPermanentCount(playerA, "Fear of Being Hunted", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
// TODO: enable and duplicate for AI -- after implement choose blocker logic and isHuman replace by ~isComputer
|
||||
public void test_MustBeBlocked_many_blockers_optimal() {
|
||||
// Fear of Being Hunted must be blocked if able.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Fear of Being Hunted"); // 4/2
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); // 1/1
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Grizzly Bears", 1); // 2/2
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Spectral Bears", 1); // 3/3
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Deadbridge Goliath", 1); // 5/5
|
||||
|
||||
attack(1, playerA, "Fear of Being Hunted");
|
||||
checkAttackers("x1 attacker", 1, playerA, "Fear of Being Hunted");
|
||||
checkBlockers("x1 optimal blocker", 1, playerB, "Deadbridge Goliath");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20);
|
||||
assertGraveyardCount(playerA, "Fear of Being Hunted", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_MustBlocking_zero_blockers() {
|
||||
// All creatures able to block target creature this turn do so.
|
||||
addCard(Zone.HAND, playerA, "Bloodscent"); // {3}{G}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); // {3}{G}
|
||||
//
|
||||
// Menace
|
||||
// Each creature you control with menace can't be blocked except by three or more creatures.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Sonorous Howlbonder"); // 2/2
|
||||
|
||||
// prepare
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodscent", "Sonorous Howlbonder");
|
||||
|
||||
attack(1, playerA, "Sonorous Howlbonder");
|
||||
checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder");
|
||||
checkBlockers("no blocker", 1, playerB, "");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20 - 2);
|
||||
assertPermanentCount(playerA, "Sonorous Howlbonder", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_MustBlocking_full_blockers() {
|
||||
// All creatures able to block target creature this turn do so.
|
||||
addCard(Zone.HAND, playerA, "Bloodscent"); // {3}{G}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); // {3}{G}
|
||||
//
|
||||
// Menace
|
||||
// Each creature you control with menace can't be blocked except by three or more creatures.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Sonorous Howlbonder"); // 2/2
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 3); // 1/1
|
||||
|
||||
// prepare
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodscent", "Sonorous Howlbonder");
|
||||
|
||||
attack(1, playerA, "Sonorous Howlbonder");
|
||||
setChoiceAmount(playerA, 1); // assign damage to 1 of 3 blocking memnites
|
||||
checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder");
|
||||
checkBlockers("x3 blockers", 1, playerB, "Memnite", "Memnite", "Memnite");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20);
|
||||
assertGraveyardCount(playerA, "Sonorous Howlbonder", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_MustBlocking_many_blockers() {
|
||||
// possible bug: AI's blockers auto-fix assign too many blockers (e.g. x10 instead x3 by required effect)
|
||||
|
||||
// All creatures able to block target creature this turn do so.
|
||||
addCard(Zone.HAND, playerA, "Bloodscent"); // {3}{G}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); // {3}{G}
|
||||
//
|
||||
// Menace
|
||||
// Each creature you control with menace can't be blocked except by three or more creatures.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Sonorous Howlbonder"); // 2/2
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 5); // 1/1
|
||||
|
||||
// prepare
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodscent", "Sonorous Howlbonder");
|
||||
|
||||
attack(1, playerA, "Sonorous Howlbonder");
|
||||
setChoiceAmount(playerA, 1); // assign damage to 1 of 3 blocking memnites
|
||||
checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder");
|
||||
checkBlockers("all blockers", 1, playerB, "Memnite", "Memnite", "Memnite", "Memnite", "Memnite");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20);
|
||||
assertGraveyardCount(playerA, "Sonorous Howlbonder", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
// TODO: need exception fix - java.lang.UnsupportedOperationException: Sonorous Howlbonder is blocked by 1 creature(s). It has to be blocked by 3 or more.
|
||||
// It's auto-fix in block configuration, so exception must be fixed cause AI works with it
|
||||
public void test_MustBlocking_low_blockers() {
|
||||
// possible bug: exception on wrong block configuration
|
||||
// if effect require x3 blockers, but opponent has only 1 then it must use 1 blocker anyway
|
||||
|
||||
// All creatures able to block target creature this turn do so.
|
||||
addCard(Zone.HAND, playerA, "Bloodscent"); // {3}{G}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 4); // {3}{G}
|
||||
//
|
||||
// Menace
|
||||
// Each creature you control with menace can't be blocked except by three or more creatures.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Sonorous Howlbonder"); // 2/2
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); // 1/1
|
||||
|
||||
// prepare
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bloodscent", "Sonorous Howlbonder");
|
||||
|
||||
attack(1, playerA, "Sonorous Howlbonder");
|
||||
setChoiceAmount(playerA, 1); // assign damage to 1 of 3 blocking memnites
|
||||
checkAttackers("x1 attacker", 1, playerA, "Sonorous Howlbonder");
|
||||
checkBlockers("one possible blocker", 1, playerB, "Memnite");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20);
|
||||
assertGraveyardCount(playerA, "Sonorous Howlbonder", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_MustBeBlockedWithMenace_0_blockers() {
|
||||
// At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creature’s
|
||||
// power until end of turn. That creature must be blocked this combat if able.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Neyith of the Dire Hunt"); // 3/3
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
|
||||
//
|
||||
// Menace
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Alley Strangler", 1); // 2/3
|
||||
|
||||
addTarget(playerA, "Alley Strangler"); // boost target
|
||||
setChoice(playerA, true); // boost target
|
||||
attack(1, playerA, "Alley Strangler");
|
||||
checkAttackers("x1 attacker", 1, playerA, "Alley Strangler");
|
||||
checkBlockers("no blocker", 1, playerB, "");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20 - 4);
|
||||
assertGraveyardCount(playerA, "Alley Strangler", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // TODO: need improve of block configuration auto-fix (block by x2 instead x1)
|
||||
public void test_MustBeBlockedWithMenace_all_blockers() {
|
||||
// At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creature’s
|
||||
// power until end of turn. That creature must be blocked this combat if able.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Neyith of the Dire Hunt"); // 3/3
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
|
||||
//
|
||||
// Menace
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Alley Strangler", 1); // 2/3
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 2); // 1/1
|
||||
|
||||
// If the target creature has menace, two creatures must block it if able.
|
||||
// (2020-06-23)
|
||||
|
||||
addTarget(playerA, "Alley Strangler"); // boost target
|
||||
setChoice(playerA, true); // boost target
|
||||
attack(1, playerA, "Alley Strangler");
|
||||
checkAttackers("x1 attacker", 1, playerA, "Alley Strangler");
|
||||
checkBlockers("x2 blockers", 1, playerB, "Memnite", "Memnite");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20);
|
||||
assertGraveyardCount(playerA, "Alley Strangler", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore // TODO: need improve of block configuration auto-fix (block by x2 instead x1)
|
||||
public void test_MustBeBlockedWithMenace_many_blockers() {
|
||||
// At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creature’s
|
||||
// power until end of turn. That creature must be blocked this combat if able.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Neyith of the Dire Hunt"); // 3/3
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
|
||||
//
|
||||
// Menace
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Alley Strangler", 1); // 2/3
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 10); // 1/1
|
||||
|
||||
// If the target creature has menace, two creatures must block it if able.
|
||||
// (2020-06-23)
|
||||
|
||||
addTarget(playerA, "Alley Strangler"); // boost target
|
||||
setChoice(playerA, true); // boost target
|
||||
attack(1, playerA, "Alley Strangler");
|
||||
checkAttackers("x1 attacker", 1, playerA, "Alley Strangler");
|
||||
checkBlockers("x2 blockers", 1, playerB, "Memnite", "Memnite");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20);
|
||||
assertGraveyardCount(playerA, "Alley Strangler", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
// TODO: need auto-fix cause AI use it (it must ignore bad blocker configuration and allow to pass without blockers at all)
|
||||
public void test_MustBeBlockedWithMenace_low_blockers_auto() {
|
||||
// At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creature’s
|
||||
// power until end of turn. That creature must be blocked this combat if able.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Neyith of the Dire Hunt"); // 3/3
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
|
||||
//
|
||||
// Menace
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Alley Strangler", 1); // 2/3
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); // 1/1
|
||||
|
||||
// If the target creature has menace, two creatures must block it if able.
|
||||
// (2020-06-23)
|
||||
//
|
||||
// If a creature is required to block a creature with menace, another creature must also block that creature
|
||||
// if able. If none can, the creature that’s required to block can block another creature or not block at all.
|
||||
// (2020-04-17)
|
||||
|
||||
// auto-fix block config inside
|
||||
|
||||
addTarget(playerA, "Alley Strangler"); // boost target
|
||||
setChoice(playerA, true); // boost target
|
||||
attack(1, playerA, "Alley Strangler");
|
||||
checkAttackers("x1 attacker", 1, playerA, "Alley Strangler");
|
||||
checkBlockers("no blockers", 1, playerB, "");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20 - 4);
|
||||
assertGraveyardCount(playerA, "Alley Strangler", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
// TODO: need exception fix java.lang.UnsupportedOperationException: Alley Strangler is blocked by 1 creature(s). It has to be blocked by 2 or more.
|
||||
// It's ok to have such exception in unit tests from manual setup
|
||||
// If it's impossible to auto-fix, then keep that error and ignore the test
|
||||
public void test_MustBeBlockedWithMenace_low_blockers_manual() {
|
||||
// At the beginning of combat on your turn, you may pay {2}{R/G}. If you do, double target creature’s
|
||||
// power until end of turn. That creature must be blocked this combat if able.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Neyith of the Dire Hunt"); // 3/3
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
|
||||
//
|
||||
// Menace
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Alley Strangler", 1); // 2/3
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Memnite", 1); // 1/1
|
||||
|
||||
// If the target creature has menace, two creatures must block it if able.
|
||||
// (2020-06-23)
|
||||
//
|
||||
// If a creature is required to block a creature with menace, another creature must also block that creature
|
||||
// if able. If none can, the creature that’s required to block can block another creature or not block at all.
|
||||
// (2020-04-17)
|
||||
|
||||
// define blocker manual
|
||||
|
||||
addTarget(playerA, "Alley Strangler"); // boost target
|
||||
setChoice(playerA, true); // boost target
|
||||
attack(1, playerA, "Alley Strangler");
|
||||
block(1, playerB, "Memnite", "Alley Strangler");
|
||||
checkAttackers("x1 attacker", 1, playerA, "Alley Strangler");
|
||||
checkBlockers("no blockers", 1, playerB, "");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 20 - 4);
|
||||
assertGraveyardCount(playerA, "Alley Strangler", 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -893,7 +893,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
Map<UUID, Integer> minNumberOfBlockersMap = new HashMap<>();
|
||||
Map<UUID, Integer> minPossibleBlockersMap = new HashMap<>();
|
||||
|
||||
// check mustBlock requirements of creatures from opponents of attacking player
|
||||
// FIND attackers and potential blockers for "must be blocked" effects
|
||||
for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURES_CONTROLLED, player.getId(), game)) {
|
||||
// creature is controlled by an opponent of the attacker
|
||||
if (opponents.contains(creature.getControllerId())) {
|
||||
|
|
@ -1012,7 +1012,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
if (toBeBlockedCreature != null) {
|
||||
CombatGroup toBeBlockedGroup = findGroup(toBeBlockedCreature);
|
||||
if (toBeBlockedGroup != null && toBeBlockedGroup.getDefendingPlayerId().equals(creature.getControllerId())) {
|
||||
minNumberOfBlockersMap.put(toBeBlockedCreature, effect.getMinNumberOfBlockers());
|
||||
minNumberOfBlockersMap.put(toBeBlockedCreature, effect.getMinNumberOfBlockers()); // TODO: fail on multiple effects 1 + 2 min blockers?
|
||||
Permanent toBeBlockedCreaturePermanent = game.getPermanent(toBeBlockedCreature);
|
||||
if (toBeBlockedCreaturePermanent != null) {
|
||||
minPossibleBlockersMap.put(toBeBlockedCreature, toBeBlockedCreaturePermanent.getMinBlockedBy());
|
||||
|
|
@ -1096,12 +1096,10 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// check if for attacking creatures with mustBeBlockedByAtLeastX requirements are fulfilled
|
||||
// APPLY potential blockers to attackers with "must be blocked" effects
|
||||
for (UUID toBeBlockedCreatureId : mustBeBlockedByAtLeastX.keySet()) {
|
||||
for (CombatGroup combatGroup : game.getCombat().getGroups()) {
|
||||
if (combatGroup.getAttackers().contains(toBeBlockedCreatureId)) {
|
||||
|
|
@ -1124,6 +1122,8 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
if (!requirementFulfilled) {
|
||||
// creature is not blocked but has possible blockers
|
||||
if (controller.isHuman()) {
|
||||
// HUMAN logic - send warning about wrong blocker config and repeat declare
|
||||
// TODO: replace isHuman by !isComputer for working unit tests
|
||||
Permanent toBeBlockedCreature = game.getPermanent(toBeBlockedCreatureId);
|
||||
if (toBeBlockedCreature != null) {
|
||||
// check if all possible blocker block other creatures they are forced to block
|
||||
|
|
@ -1142,9 +1142,8 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
// take the first potential blocker from the set to block for the AI
|
||||
// AI logic - auto-fix wrong blocker config (take the first potential blocker)
|
||||
for (UUID possibleBlockerId : mustBeBlockedByAtLeastX.get(toBeBlockedCreatureId)) {
|
||||
String blockRequiredMessage = isCreatureDoingARequiredBlock(
|
||||
possibleBlockerId, toBeBlockedCreatureId, mustBeBlockedByAtLeastX, game);
|
||||
|
|
@ -1167,8 +1166,8 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// check if creatures are forced to block but do not block at all or block creatures they are not forced to block
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (Map.Entry<UUID, Set<UUID>> entry : creatureMustBlockAttackers.entrySet()) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue