mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
AI: improved stability:
- fixed game errors with source related filters (related to #13713); - fixed game freeze in hand's cards selection (related to #13290); - fixed game freeze in target amount selection with X=0 (related to #13290);
This commit is contained in:
parent
e8342e1f11
commit
6ad2cdaa78
5 changed files with 38 additions and 55 deletions
|
|
@ -692,7 +692,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
|| target.getOriginalTarget() instanceof TargetCardInHand) {
|
|| target.getOriginalTarget() instanceof TargetCardInHand) {
|
||||||
isAddedSomething = false;
|
isAddedSomething = false;
|
||||||
if (outcome.isGood()) {
|
if (outcome.isGood()) {
|
||||||
// good
|
// good - choose max possible
|
||||||
Cards cards = new CardsImpl(possibleTargets);
|
Cards cards = new CardsImpl(possibleTargets);
|
||||||
List<Card> cardsInHand = new ArrayList<>(cards.getCards(game));
|
List<Card> cardsInHand = new ArrayList<>(cards.getCards(game));
|
||||||
while (!target.isChosen(game)
|
while (!target.isChosen(game)
|
||||||
|
|
@ -703,36 +703,42 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.contains(card.getId())) {
|
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.contains(card.getId())) {
|
||||||
target.addTarget(card.getId(), source, game);
|
target.addTarget(card.getId(), source, game);
|
||||||
isAddedSomething = true;
|
isAddedSomething = true;
|
||||||
cardsInHand.remove(card);
|
|
||||||
if (target.isChoiceCompleted(game)) {
|
if (target.isChoiceCompleted(game)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cardsInHand.remove(card);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// bad
|
// bad - choose the lowest possible
|
||||||
findPlayables(game);
|
findPlayables(game);
|
||||||
for (Card card : unplayable.values()) {
|
for (Card card : unplayable.values()) {
|
||||||
|
if (target.isChosen(game)) {
|
||||||
|
return isAddedSomething;
|
||||||
|
}
|
||||||
if (possibleTargets.contains(card.getId())
|
if (possibleTargets.contains(card.getId())
|
||||||
&& target.canTarget(abilityControllerId, card.getId(), source, game)
|
&& target.canTarget(abilityControllerId, card.getId(), source, game)
|
||||||
&& !target.contains(card.getId())) {
|
&& !target.contains(card.getId())) {
|
||||||
target.addTarget(card.getId(), source, game);
|
target.addTarget(card.getId(), source, game);
|
||||||
isAddedSomething = true;
|
isAddedSomething = true;
|
||||||
if (target.isChoiceCompleted(game)) {
|
if (target.isChosen(game)) {
|
||||||
return true;
|
return isAddedSomething;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!hand.isEmpty()) {
|
if (!hand.isEmpty()) {
|
||||||
for (Card card : hand.getCards(game)) {
|
for (Card card : hand.getCards(game)) {
|
||||||
|
if (target.isChosen(game)) {
|
||||||
|
return isAddedSomething;
|
||||||
|
}
|
||||||
if (possibleTargets.contains(card.getId())
|
if (possibleTargets.contains(card.getId())
|
||||||
&& target.canTarget(abilityControllerId, card.getId(), source, game)
|
&& target.canTarget(abilityControllerId, card.getId(), source, game)
|
||||||
&& !target.contains(card.getId())) {
|
&& !target.contains(card.getId())) {
|
||||||
target.addTarget(card.getId(), source, game);
|
target.addTarget(card.getId(), source, game);
|
||||||
isAddedSomething = true;
|
isAddedSomething = true;
|
||||||
if (target.isChoiceCompleted(game)) {
|
if (target.isChosen(game)) {
|
||||||
return true;
|
return isAddedSomething;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -854,39 +860,6 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
if (target.getOriginalTarget() instanceof TargetPermanentOrPlayer) {
|
if (target.getOriginalTarget() instanceof TargetPermanentOrPlayer) {
|
||||||
List<Permanent> targets;
|
List<Permanent> targets;
|
||||||
TargetPermanentOrPlayer origTarget = ((TargetPermanentOrPlayer) target.getOriginalTarget());
|
TargetPermanentOrPlayer origTarget = ((TargetPermanentOrPlayer) target.getOriginalTarget());
|
||||||
if (outcome.isGood()) {
|
|
||||||
targets = threats(abilityControllerId, source, ((FilterPermanentOrPlayer) origTarget.getFilter()).getPermanentFilter(), game, target.getTargets());
|
|
||||||
} else {
|
|
||||||
targets = threats(randomOpponentId, source, ((FilterPermanentOrPlayer) origTarget.getFilter()).getPermanentFilter(), game, target.getTargets());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targets.isEmpty()) {
|
|
||||||
if (outcome.isGood()) {
|
|
||||||
if (target.canTarget(abilityControllerId, getId(), source, game) && !target.contains(getId())) {
|
|
||||||
return tryAddTarget(target, getId(), source, game);
|
|
||||||
}
|
|
||||||
} else if (target.canTarget(abilityControllerId, randomOpponentId, source, game) && !target.contains(randomOpponentId)) {
|
|
||||||
return tryAddTarget(target, randomOpponentId, source, game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targets.isEmpty() && target.isRequired(source)) {
|
|
||||||
targets = game.getBattlefield().getActivePermanents(((FilterPermanentOrPlayer) origTarget.getFilter()).getPermanentFilter(), playerId, game);
|
|
||||||
}
|
|
||||||
for (Permanent permanent : targets) {
|
|
||||||
List<UUID> alreadyTargeted = target.getTargets();
|
|
||||||
if (target.canTarget(abilityControllerId, permanent.getId(), source, game) && !target.contains(permanent.getId())) {
|
|
||||||
if (alreadyTargeted != null && !alreadyTargeted.contains(permanent.getId())) {
|
|
||||||
return tryAddTarget(target, permanent.getId(), source, game);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target.getOriginalTarget() instanceof TargetPlayerOrPlaneswalker
|
|
||||||
|| target.getOriginalTarget() instanceof TargetOpponentOrPlaneswalker) {
|
|
||||||
List<Permanent> targets;
|
|
||||||
TargetPermanentOrPlayer origTarget = ((TargetPermanentOrPlayer) target.getOriginalTarget());
|
|
||||||
|
|
||||||
// TODO: in multiplayer game there many opponents - if random opponents don't have targets then AI must use next opponent, but it skips
|
// TODO: in multiplayer game there many opponents - if random opponents don't have targets then AI must use next opponent, but it skips
|
||||||
// (e.g. you randomOpponentId must be replaced by List<UUID> randomOpponents)
|
// (e.g. you randomOpponentId must be replaced by List<UUID> randomOpponents)
|
||||||
|
|
@ -1239,6 +1212,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
throw new IllegalStateException("Target wasn't handled in computer's chooseTarget method: " + target.getClass().getCanonicalName());
|
throw new IllegalStateException("Target wasn't handled in computer's chooseTarget method: " + target.getClass().getCanonicalName());
|
||||||
} //end of chooseTarget method
|
} //end of chooseTarget method
|
||||||
|
|
||||||
|
@Deprecated // TODO: replace by source only version
|
||||||
protected Card selectCard(UUID abilityControllerId, List<Card> cards, Outcome outcome, Target target, Game game) {
|
protected Card selectCard(UUID abilityControllerId, List<Card> cards, Outcome outcome, Target target, Game game) {
|
||||||
return selectCardInner(abilityControllerId, cards, outcome, target, null, game);
|
return selectCardInner(abilityControllerId, cards, outcome, target, null, game);
|
||||||
}
|
}
|
||||||
|
|
@ -1279,6 +1253,10 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
log.debug("chooseTarget: " + outcome.toString() + ':' + target.toString());
|
log.debug("chooseTarget: " + outcome.toString() + ':' + target.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target.getAmountRemaining() <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
UUID sourceId = source != null ? source.getSourceId() : null;
|
UUID sourceId = source != null ? source.getSourceId() : null;
|
||||||
|
|
||||||
// sometimes a target selection can be made from a player that does not control the ability
|
// sometimes a target selection can be made from a player that does not control the ability
|
||||||
|
|
@ -2576,6 +2554,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
tournament.submitDeck(playerId, deck);
|
tournament.submitDeck(playerId, deck);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated // TODO: replace by source only version
|
||||||
public Card selectBestCard(List<Card> cards, List<ColoredManaSymbol> chosenColors) {
|
public Card selectBestCard(List<Card> cards, List<ColoredManaSymbol> chosenColors) {
|
||||||
return selectBestCardInner(cards, chosenColors, null, null, null);
|
return selectBestCardInner(cards, chosenColors, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
@ -2623,14 +2602,6 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
return bestCard;
|
return bestCard;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Card selectWorstCard(List<Card> cards, List<ColoredManaSymbol> chosenColors) {
|
|
||||||
return selectWorstCardInner(cards, chosenColors, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Card selectWorstCardTarget(List<Card> cards, List<ColoredManaSymbol> chosenColors, Target target, Ability targetingSource, Game game) {
|
|
||||||
return selectWorstCardInner(cards, chosenColors, target, targetingSource, game);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param targetingSource null on non-target choice like choose and source on targeting choice like chooseTarget
|
* @param targetingSource null on non-target choice like choose and source on targeting choice like chooseTarget
|
||||||
*/
|
*/
|
||||||
|
|
@ -3097,6 +3068,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
return before != after;
|
return before != after;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated // TODO: replace by source only version
|
||||||
private boolean selectPlayer(Outcome outcome, Target target, UUID abilityControllerId, UUID randomOpponentId, Game game, boolean required) {
|
private boolean selectPlayer(Outcome outcome, Target target, UUID abilityControllerId, UUID randomOpponentId, Game game, boolean required) {
|
||||||
return selectPlayerInner(outcome, target, null, abilityControllerId, randomOpponentId, game, required);
|
return selectPlayerInner(outcome, target, null, abilityControllerId, randomOpponentId, game, required);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -288,6 +288,10 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) {
|
public boolean chooseTargetAmount(Outcome outcome, TargetAmount target, Ability source, Game game) {
|
||||||
|
if (target.getAmountRemaining() <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Set<UUID> possibleTargets = target.possibleTargets(playerId, source, game);
|
Set<UUID> possibleTargets = target.possibleTargets(playerId, source, game);
|
||||||
if (possibleTargets.isEmpty()) {
|
if (possibleTargets.isEmpty()) {
|
||||||
return !target.isRequired(source);
|
return !target.isRequired(source);
|
||||||
|
|
|
||||||
|
|
@ -1042,6 +1042,10 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (target.getAmountRemaining() <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2328,7 +2328,7 @@ public class TestPlayer implements Player {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (hasObjectTargetNameOrAlias(permanent, targetName)) {
|
if (hasObjectTargetNameOrAlias(permanent, targetName)) {
|
||||||
if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) {
|
if (target.canTarget(abilityControllerId, permanent.getId(), source, game) && !target.contains(permanent.getId())) {
|
||||||
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
|
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
|
||||||
target.add(permanent.getId(), game);
|
target.add(permanent.getId(), game);
|
||||||
isAddedSomething = true;
|
isAddedSomething = true;
|
||||||
|
|
@ -2336,7 +2336,7 @@ public class TestPlayer implements Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ((permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) { // TODO: remove search by exp code?
|
} else if ((permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) { // TODO: remove search by exp code?
|
||||||
if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) {
|
if (target.canTarget(abilityControllerId, permanent.getId(), source, game) && !target.contains(permanent.getId())) {
|
||||||
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
|
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
|
||||||
target.add(permanent.getId(), game);
|
target.add(permanent.getId(), game);
|
||||||
isAddedSomething = true;
|
isAddedSomething = true;
|
||||||
|
|
@ -2371,7 +2371,7 @@ public class TestPlayer implements Player {
|
||||||
isAddedSomething = false;
|
isAddedSomething = false;
|
||||||
for (Player player : game.getPlayers().values()) {
|
for (Player player : game.getPlayers().values()) {
|
||||||
if (player.getName().equals(choiceRecord)) {
|
if (player.getName().equals(choiceRecord)) {
|
||||||
if (target.canTarget(abilityControllerId, player.getId(), null, game) && !target.contains(player.getId())) {
|
if (target.canTarget(abilityControllerId, player.getId(), source, game) && !target.contains(player.getId())) {
|
||||||
target.add(player.getId(), game);
|
target.add(player.getId(), game);
|
||||||
isAddedSomething = true;
|
isAddedSomething = true;
|
||||||
}
|
}
|
||||||
|
|
@ -2538,7 +2538,8 @@ public class TestPlayer implements Player {
|
||||||
String playerName = targetDefinition.substring(targetDefinition.indexOf("targetPlayer=") + 13);
|
String playerName = targetDefinition.substring(targetDefinition.indexOf("targetPlayer=") + 13);
|
||||||
for (Player player : game.getPlayers().values()) {
|
for (Player player : game.getPlayers().values()) {
|
||||||
if (player.getName().equals(playerName)
|
if (player.getName().equals(playerName)
|
||||||
&& target.canTarget(abilityControllerId, player.getId(), source, game)) {
|
&& target.canTarget(abilityControllerId, player.getId(), source, game)
|
||||||
|
&& !target.contains(player.getId())) {
|
||||||
target.addTarget(player.getId(), source, game);
|
target.addTarget(player.getId(), source, game);
|
||||||
targets.remove(targetDefinition);
|
targets.remove(targetDefinition);
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -2887,7 +2888,7 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chooseUse(Outcome outcome, String message, String secondMessage, String trueText, String falseText, Ability source, Game game) {
|
public boolean chooseUse(Outcome outcome, String message, String secondMessage, String trueText, String falseText, Ability source, Game game) {
|
||||||
if (message.equals("Scry 1?")) {
|
if (message != null && message.equals("Scry 1?")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
assertAliasSupportInChoices(false);
|
assertAliasSupportInChoices(false);
|
||||||
|
|
@ -4326,7 +4327,9 @@ public class TestPlayer implements Player {
|
||||||
// chooseTargetAmount calls for EACH target cycle (e.g. one target per click, see TargetAmount)
|
// chooseTargetAmount calls for EACH target cycle (e.g. one target per click, see TargetAmount)
|
||||||
// if use want to stop choosing then chooseTargetAmount must return false (example: up to xxx)
|
// if use want to stop choosing then chooseTargetAmount must return false (example: up to xxx)
|
||||||
|
|
||||||
Assert.assertNotEquals("chooseTargetAmount needs non zero amount remaining", 0, target.getAmountRemaining());
|
if (target.getAmountRemaining() <= 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
assertAliasSupportInTargets(true);
|
assertAliasSupportInTargets(true);
|
||||||
if (!targets.isEmpty()) {
|
if (!targets.isEmpty()) {
|
||||||
|
|
|
||||||
|
|
@ -209,7 +209,7 @@ class OublietteTarget extends TargetSacrifice {
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||||
possibleTargets.removeIf(uuid -> !this.canTarget(sourceControllerId, uuid, null, game));
|
possibleTargets.removeIf(uuid -> !this.canTarget(sourceControllerId, uuid, source, game));
|
||||||
return possibleTargets;
|
return possibleTargets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue