* Choice of Damnations - improved AI support, fixed rollback error in AI games;

* Pain's Reward - improved AI support, fixed rollback error in AI games;
* Volcano Hellion - improved AI support, fixed rollback error in AI games;
This commit is contained in:
Oleg Agafonov 2021-02-06 12:48:53 +04:00
parent ac98a3a31a
commit f692a1f097
5 changed files with 79 additions and 35 deletions

View file

@ -50,10 +50,7 @@ import mage.players.net.UserData;
import mage.players.net.UserGroup;
import mage.target.*;
import mage.target.common.*;
import mage.util.Copier;
import mage.util.RandomUtil;
import mage.util.TournamentUtil;
import mage.util.TreeNode;
import mage.util.*;
import org.apache.log4j.Logger;
import java.io.IOException;
@ -1666,8 +1663,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
@Override
public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variablCost) {
log.debug("announceXCost");
//TODO: improve this
int value = RandomUtil.nextInt(max + 1);
int value = RandomUtil.nextInt(CardUtil.overflowInc(max, 1));
if (value < min) {
value = min;
}
@ -1964,9 +1960,8 @@ public class ComputerPlayer extends PlayerImpl implements Player {
if (message.startsWith("Assign damage to ")) {
return min;
}
//TODO: improve this
if (min < max && min == 0) {
return RandomUtil.nextInt(max + 1);
return RandomUtil.nextInt(CardUtil.overflowInc(max, 1));
}
return min;
}

View file

@ -1,14 +1,12 @@
package mage.cards.c;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledPermanent;
import mage.game.Game;
@ -18,14 +16,15 @@ import mage.target.Target;
import mage.target.common.TargetControlledPermanent;
import mage.target.common.TargetOpponent;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public final class ChoiceOfDamnations extends CardImpl {
public ChoiceOfDamnations(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{5}{B}");
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{B}");
this.subtype.add(SubType.ARCANE);
// Target opponent chooses a number. You may have that player lose that much life. If you don't, that player sacrifices all but that many permanents.
@ -63,13 +62,40 @@ class ChoiceOfDamnationsEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source));
if (targetPlayer != null) {
int amount = targetPlayer.getAmount(0, Integer.MAX_VALUE, "Chooses a number", game);
int numberPermanents = game.getState().getBattlefield().countAll(new FilterPermanent(), targetPlayer.getId(), game);
// AI hint
int amount;
if (!targetPlayer.isHuman() && !targetPlayer.isTestMode()) {
// AI as defender
int safeLifeToLost = Math.max(0, targetPlayer.getLife() / 2);
amount = Math.min(numberPermanents, safeLifeToLost);
} else {
// Human must choose
amount = targetPlayer.getAmount(0, Integer.MAX_VALUE, "Chooses a number", game);
}
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
if (controller.chooseUse(outcome, "Shall " + targetPlayer.getLogName() + " lose " + amount + " life?", source, game)) {
// AI hint
boolean chooseLoseLife;
if (!targetPlayer.isHuman() && !targetPlayer.isTestMode()) {
// AI as attacker
chooseLoseLife = (numberPermanents == 0 || amount <= numberPermanents || targetPlayer.getLife() < amount);
} else {
// Human must choose
chooseLoseLife = controller.chooseUse(outcome, "Shall " + targetPlayer.getLogName() + " lose " + amount + " life?", source, game);
}
if (chooseLoseLife) {
targetPlayer.loseLife(amount, game, source, false);
} else {
int numberPermanents = game.getState().getBattlefield().countAll(new FilterPermanent(), targetPlayer.getId(), game);
// rules:
// If the opponent must sacrifice all but a number of permanents, that opponent chooses that many
// permanents and then sacrifices the rest. If the number chosen is greater than the number of
// permanents the opponent controls, the player sacrifices nothing.
// (2005-06-01)
if (numberPermanents > amount) {
int numberToSacrifice = numberPermanents - amount;
Target target = new TargetControlledPermanent(numberToSacrifice, numberToSacrifice, new FilterControlledPermanent("permanent you control to sacrifice"), false);

View file

@ -1,8 +1,5 @@
package mage.cards.p;
import java.util.Objects;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
@ -13,15 +10,16 @@ import mage.game.Game;
import mage.players.Player;
import mage.players.PlayerList;
import java.util.Objects;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public final class PainsReward extends CardImpl {
public PainsReward(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}");
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}");
// Each player may bid life. You start the bidding with a bid of any number. In turn order, each player may top the high bid. The bidding ends if the high bid stands. The high bidder loses life equal to the high bid and draws four cards.
this.getSpellAbility().addEffect(new PainsRewardEffect());
@ -62,14 +60,19 @@ class PainsRewardEffect extends OneShotEffect {
playerList.setCurrent(controller.getId());
Player winner = game.getPlayer(controller.getId());
int highBid = controller.getAmount(0, Integer.MAX_VALUE, "Choose amount of life to bid", game);
int highBid = chooseLifeAmountToBid(controller, -1, game); // -1 for start with 0 min big
game.informPlayers(winner.getLogName() + " has bet " + highBid + " lifes");
Player currentPlayer = playerList.getNextInRange(controller, game);
while (currentPlayer != null && !Objects.equals(currentPlayer, winner)) {
String text = winner.getLogName() + " has bet " + highBid + " life" + (highBid > 1 ? "s" : "") + ". Top the bid?";
if (currentPlayer.chooseUse(Outcome.Detriment, text, source, game)) {
int newBid = currentPlayer.getAmount(highBid + 1, Integer.MAX_VALUE, "Choose amount of life to bid", game);
// AI hint
int safeLifeToLost = Math.min(6, currentPlayer.getLife() / 2);
Outcome aiOutcome = (highBid + 1 <= safeLifeToLost) ? Outcome.Benefit : Outcome.Detriment;
if (currentPlayer.chooseUse(aiOutcome, text, source, game)) {
int newBid = chooseLifeAmountToBid(currentPlayer, highBid, game);
if (newBid > highBid) {
highBid = newBid;
winner = currentPlayer;
@ -86,4 +89,16 @@ class PainsRewardEffect extends OneShotEffect {
}
return false;
}
private int chooseLifeAmountToBid(Player player, int currentBig, Game game) {
int newBid;
if (!player.isHuman() && !player.isTestMode()) {
// AI choose
newBid = currentBig + 1;
} else {
// Human choose
newBid = player.getAmount(currentBig + 1, Integer.MAX_VALUE, "Choose amount of life to bid", game);
}
return newBid;
}
}

View file

@ -1,7 +1,5 @@
package mage.cards.v;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
@ -11,15 +9,16 @@ import mage.abilities.keyword.EchoAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
*
* @author jeffwadsworth
*/
public final class VolcanoHellion extends CardImpl {
@ -54,7 +53,7 @@ public final class VolcanoHellion extends CardImpl {
class VolcanoHellionEffect extends OneShotEffect {
public VolcanoHellionEffect() {
super(Outcome.AIDontUseIt);
super(Outcome.Damage);
this.staticText = "it deals an amount of damage of your choice to you and target creature. The damage can't be prevented";
}
@ -72,7 +71,20 @@ class VolcanoHellionEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanent(source.getFirstTarget());
if (controller != null) {
int amount = controller.getAmount(0, Integer.MAX_VALUE, "Choose the amount of damage to deliver to you and a target creature. The damage can't be prevented.", game);
int amount;
if (!controller.isHuman() && !controller.isTestMode()) {
// AI hint: have much life and can destroy target permanent
int safeLifeToLost = Math.min(6, controller.getLife() / 2);
if (permanent != null && permanent.getToughness().getValue() <= safeLifeToLost) {
amount = permanent.getToughness().getValue();
} else {
amount = 0;
}
} else {
//Human choose
amount = controller.getAmount(0, Integer.MAX_VALUE, "Choose the amount of damage to deliver to you and a target creature. The damage can't be prevented.", game);
}
if (amount > 0) {
controller.damage(amount, source.getSourceId(), source, game, false, false);
if (permanent != null) {

View file

@ -46,10 +46,6 @@ public class ModalDoubleFacesCardsInCommanderTest extends CardTestCommanderDuelB
addCard(Zone.LIBRARY, playerA, "Forest");
addCard(Zone.LIBRARY, playerA, "Grizzly Bears");
addCard(Zone.LIBRARY, playerA, "Forest");
//
// Exile target artifact or enchantment.
addCard(Zone.HAND, playerB, "Ironwright's Cleansing"); // {2}{W}
addCard(Zone.BATTLEFIELD, playerB, "Plains");
// prepare mdf
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "The Prismatic Bridge");