* Added a simple check for infinite loops so that if players confirm the game ends in a draw (#3329).

This commit is contained in:
LevelX2 2017-05-16 14:45:30 +02:00
parent 7b61ad7455
commit 5ce813ad87
2 changed files with 75 additions and 0 deletions

View file

@ -7,6 +7,7 @@ package org.mage.test.game.ends;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.game.permanent.Permanent;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
@ -77,4 +78,45 @@ public class GameIsADrawTest extends CardTestPlayerBase {
}
/**
* So here I made a simple infinite loop with Stuffy Doll and Pariah's
* Shield, which should make the game a draw. But instead, it just keeps
* going...
*/
@Test
public void GameDrawByInfiniteLoop() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
// All damage that would be dealt to you is dealt to equipped creature instead.
// Equip {3}
addCard(Zone.BATTLEFIELD, playerA, "Pariah's Shield", 1); // Artifact Equipment {5}
// As Stuffy Doll enters the battlefield, choose a player.
// Stuffy Doll is indestructible.
// Whenever Stuffy Doll is dealt damage, it deals that much damage to the chosen player.
// {T}: Stuffy Doll deals 1 damage to itself.
addCard(Zone.HAND, playerA, "Stuffy Doll", 1); // Artifact Creature {5} 0/1
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stuffy Doll");
setChoice(playerA, "PlayerA");
setChoice(playerA, "PlayerA");
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", "Stuffy Doll");
activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}");
setStopAt(3, PhaseStep.END_TURN);
execute();
assertPermanentCount(playerA, "Stuffy Doll", 1);
Permanent shield = getPermanent("Pariah's Shield");
Assert.assertTrue("Pariah's Shield is attached", shield.getAttachedTo() != null);
Assert.assertFalse("Player A has not won.", playerA.hasWon());
Assert.assertFalse("Player B has not won.", playerB.hasWon());
Assert.assertTrue("Game has ended.", currentGame.hasEnded());
Assert.assertTrue("Inifinite loop detected, game has be de a draw.", currentGame.isADraw());
}
}

View file

@ -1233,6 +1233,7 @@ public abstract class GameImpl implements Game, Serializable {
@Override
public void playPriority(UUID activePlayerId, boolean resuming) {
int errorContinueCounter = 0;
int infiniteLoopCounter = 0;
int bookmark = 0;
clearAllBookmarks();
try {
@ -1286,6 +1287,13 @@ public abstract class GameImpl implements Game, Serializable {
}
if (allPassed()) {
if (!state.getStack().isEmpty()) {
if (getStack().size() < 4) {
infiniteLoopCounter++;
if (infiniteLoopCounter > 15) {
isInfiniteLoop();
infiniteLoopCounter = 0;
}
}
//20091005 - 115.4
resolve();
applyEffects();
@ -1294,6 +1302,7 @@ public abstract class GameImpl implements Game, Serializable {
resetShortLivingLKI();
break;
} else {
infiniteLoopCounter = 0;
resetLKI();
return;
}
@ -1350,6 +1359,30 @@ public abstract class GameImpl implements Game, Serializable {
}
}
protected void isInfiniteLoop() {
StackObject stackObject = getStack().getFirst();
if (stackObject != null) {
Player controller = getPlayer(stackObject.getControllerId());
if (controller != null) {
for (UUID playerId : getState().getPlayersInRange(controller.getId(), this)) {
Player player = getPlayer(playerId);
if (!player.chooseUse(Outcome.Detriment, "Draw game because of infinite looping?", null, this)) {
informPlayers(controller.getLogName() + " has NOT confirmed that the game is a draw because of infinite looping.");
return;
}
informPlayers(controller.getLogName() + " has confirmed that the game is a draw because of infinite looping.");
}
for (UUID playerId : getState().getPlayersInRange(controller.getId(), this)) {
Player player = getPlayer(playerId);
if (player != null) {
player.drew(this);
}
}
}
}
}
protected boolean allPassed() {
for (Player player : state.getPlayers().values()) {
if (!player.isPassed() && player.canRespond()) {