Skip to content

Commit eb7874d

Browse files
liamiak1claude
andcommitted
Test that the AI casts both cards, and still takes Meathook's trigger
One test per card being unflagged - Toxic Deluge for the -X/-X and PayLife path, Flowstone Slide for the +X/-X one, where survivors get power out of it. The trigger case passes either way by design: it guards The Meathook Massacre against a future change re-announcing an X that was already paid on casting. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 07e1f54 commit eb7874d

1 file changed

Lines changed: 107 additions & 0 deletions

File tree

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package forge.ai.ability;
2+
3+
import forge.ai.AITest;
4+
import forge.ai.SpellApiToAi;
5+
import forge.game.Game;
6+
import forge.game.ability.ApiType;
7+
import forge.game.card.Card;
8+
import forge.game.phase.PhaseType;
9+
import forge.game.player.Player;
10+
import forge.game.spellability.SpellAbility;
11+
import forge.game.trigger.Trigger;
12+
import forge.game.zone.ZoneType;
13+
import org.testng.AssertJUnit;
14+
import org.testng.annotations.Test;
15+
16+
public class PumpAllAiTest extends AITest {
17+
18+
@Test
19+
public void testToxicDelugeSweepsOpponentBoard() {
20+
Game game = initAndCreateGame();
21+
Player ai = game.getPlayers().get(1);
22+
Player opponent = game.getPlayers().get(0);
23+
24+
addCards("Swamp", 3, ai);
25+
Card deluge = addCardToZone("Toxic Deluge", ai, ZoneType.Hand);
26+
addCards("Grizzly Bears", 3, opponent);
27+
28+
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, ai);
29+
game.getAction().checkStateEffects(true);
30+
31+
SpellAbility sa = findPumpAllAbility(deluge);
32+
AssertJUnit.assertNotNull("Toxic Deluge should have a PumpAll spell ability", sa);
33+
sa.setActivatingPlayer(ai);
34+
35+
AssertJUnit.assertTrue("AI should cast Toxic Deluge to sweep an opposing board it does not share",
36+
SpellApiToAi.Converter.get(sa).canPlayWithSubs(ai, sa).willingToPlay());
37+
AssertJUnit.assertEquals("AI should pay exactly enough life to kill 2/2s",
38+
Integer.valueOf(2), sa.getXManaCostPaid());
39+
}
40+
41+
@Test
42+
public void testFlowstoneSlideSweepsForItsManaX() {
43+
Game game = initAndCreateGame();
44+
Player ai = game.getPlayers().get(1);
45+
Player opponent = game.getPlayers().get(0);
46+
47+
addCards("Mountain", 6, ai);
48+
Card slide = addCardToZone("Flowstone Slide", ai, ZoneType.Hand);
49+
addCards("Grizzly Bears", 3, opponent);
50+
51+
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, ai);
52+
game.getAction().checkStateEffects(true);
53+
54+
SpellAbility sa = findPumpAllAbility(slide);
55+
AssertJUnit.assertNotNull("Flowstone Slide should have a PumpAll spell ability", sa);
56+
sa.setActivatingPlayer(ai);
57+
58+
// +X/-X, so unlike Toxic Deluge the survivors get power out of it - which only counts
59+
// against the AI on a turn the opponent still has a combat left.
60+
AssertJUnit.assertTrue("AI should cast Flowstone Slide to sweep an opposing board",
61+
SpellApiToAi.Converter.get(sa).canPlayWithSubs(ai, sa).willingToPlay());
62+
AssertJUnit.assertEquals("AI should pay the cheapest X that kills 2/2s",
63+
Integer.valueOf(2), sa.getXManaCostPaid());
64+
}
65+
66+
@Test
67+
public void testAlreadyPaidXOnATriggerIsNotReannounced() {
68+
Game game = initAndCreateGame();
69+
Player ai = game.getPlayers().get(1);
70+
Player opponent = game.getPlayers().get(0);
71+
72+
addCards("Grizzly Bears", 3, opponent);
73+
Card meathook = addCard("The Meathook Massacre", ai);
74+
75+
game.getPhaseHandler().devModeSet(PhaseType.MAIN1, ai);
76+
game.getAction().checkStateEffects(true);
77+
78+
SpellAbility sa = null;
79+
for (Trigger t : meathook.getTriggers()) {
80+
SpellAbility overriding = t.getOverridingAbility();
81+
if (overriding != null && overriding.getApi() == ApiType.PumpAll) {
82+
sa = overriding;
83+
break;
84+
}
85+
}
86+
AssertJUnit.assertNotNull("The Meathook Massacre should have a PumpAll trigger", sa);
87+
88+
// X here was paid when the enchantment was cast. Trying to re-announce it finds no X in the
89+
// trigger's own cost and yields nothing to pay, which would make the AI refuse the trigger.
90+
sa.setActivatingPlayer(ai);
91+
sa.setXManaCostPaid(3);
92+
93+
AssertJUnit.assertTrue("AI should take a trigger that wipes the opposing board",
94+
SpellApiToAi.Converter.get(sa).canPlayWithSubs(ai, sa).willingToPlay());
95+
AssertJUnit.assertEquals("X paid on casting must survive the AI's evaluation of the trigger",
96+
Integer.valueOf(3), sa.getXManaCostPaid());
97+
}
98+
99+
private SpellAbility findPumpAllAbility(Card card) {
100+
for (SpellAbility sa : card.getSpellAbilities()) {
101+
if (sa.getApi() == ApiType.PumpAll) {
102+
return sa;
103+
}
104+
}
105+
return null;
106+
}
107+
}

0 commit comments

Comments
 (0)