AI: fixed that computer can stop to use some equip and level up abilities, fixed small memory leaks (related to #11285, #4520)

This commit is contained in:
Oleg Agafonov 2023-10-12 07:56:41 +04:00
parent 10ccab3caa
commit 0d9121bf10
6 changed files with 24 additions and 48 deletions

View file

@ -5,7 +5,9 @@ import mage.abilities.Ability;
import mage.game.Game; import mage.game.Game;
/** /**
* Interface for ai optimizer that cuts the tree of decision. * AI: interface for ai optimizer that cuts the tree of decision.
*
* Warning, it's a static objects for all AI instances, don't store any data in it
* *
* @author ayratn * @author ayratn
*/ */

View file

@ -8,54 +8,27 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* Base class for tree optimizers. * AI: base class for tree optimizers.
* *
* @author ayratn * @author ayratn
*/ */
public abstract class BaseTreeOptimizer implements TreeOptimizer { public abstract class BaseTreeOptimizer implements TreeOptimizer {
/** /**
* List of abilities that should be removed because of optimization. * Filter and ignore bad actions
*
*/
protected List<Ability> toRemove;
/**
* Inner method for filtering actions.
* Should be implemented by classes.
* *
* @param game * @param game
* @param actions * @param actions
* @param actionsToRemove that must be removed/ignored from a possible AI actions
*/ */
abstract void filter(Game game, List<Ability> actions); abstract void filter(Game game, List<Ability> actions, List<Ability> actionsToRemove);
/**
* Template method for optimization.
*
* @param game
* @param actions
*/
@Override @Override
public final void optimize(Game game, List<Ability> actions) { public final void optimize(Game game, List<Ability> actions) {
filter(game, actions); List<Ability> actionsToRemove = new ArrayList<>();
filter(game, actions, actionsToRemove);
if (toRemove != null) { for (Ability r : actionsToRemove) {
for (Ability r : toRemove) {
actions.remove(r); actions.remove(r);
} }
} }
} }
/**
* Mark an ability to be removed
* Not thread-safe for performance reasons.
*
* @param ability
*/
protected void removeAbility(Ability ability) {
if (toRemove == null) {
toRemove = new ArrayList<>();
}
toRemove.add(ability);
}
}

View file

@ -5,17 +5,18 @@ import mage.abilities.Ability;
import mage.game.Game; import mage.game.Game;
/** /**
* Removes abilities that require only discard a card for activation. * AI: removes abilities that require only discard a card for activation.
* *
* @author magenoxx_at_gmail.com * @author magenoxx_at_gmail.com
*/ */
public class DiscardCardOptimizer extends BaseTreeOptimizer { public class DiscardCardOptimizer extends BaseTreeOptimizer {
@Override @Override
public void filter(Game game, List<Ability> actions) { public void filter(Game game, List<Ability> actions, List<Ability> actionsToRemove) {
for (Ability ability : actions) { for (Ability ability : actions) {
// TODO: add more discard restictions here? See ExileSourceUnlessPaysEffect for a possible list
if (ability.toString().startsWith("Discard card")) { if (ability.toString().startsWith("Discard card")) {
removeAbility(ability); actionsToRemove.add(ability);
} }
} }
} }

View file

@ -7,21 +7,21 @@ import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
/** /**
* Make sure that AI won't equip the same creature equip already attached to. * AI: make sure that AI won't equip the same creature equip already attached to.
* *
* @author ayratn * @author ayratn
*/ */
public class EquipOptimizer extends BaseTreeOptimizer { public class EquipOptimizer extends BaseTreeOptimizer {
@Override @Override
public void filter(Game game, List<Ability> actions) { public void filter(Game game, List<Ability> actions, List<Ability> actionsToRemove) {
for (Ability ability : actions) { for (Ability ability : actions) {
if (ability instanceof EquipAbility) { if (ability instanceof EquipAbility) {
Permanent permanent = game.getPermanent(ability.getFirstTarget()); Permanent permanent = game.getPermanent(ability.getFirstTarget());
if (permanent != null) { if (permanent != null) {
// check that equipment is not already attached to {this} // check that equipment is not already attached to {this}
if (permanent.getAttachments().contains(ability.getSourceId())) { if (permanent.getAttachments().contains(ability.getSourceId())) {
removeAbility(ability); actionsToRemove.add(ability);
} }
} }
} }

View file

@ -10,7 +10,7 @@ import mage.game.permanent.PermanentCard;
import java.util.List; import java.util.List;
/** /**
* Make sure that AI won't level up whenever there are maximum possible level up counters. * AI: make sure that AI won't level up whenever there are maximum possible level up counters.
* *
* @author ayratn * @author ayratn
*/ */
@ -23,7 +23,7 @@ public class LevelUpOptimizer extends BaseTreeOptimizer {
* @param actions * @param actions
*/ */
@Override @Override
public void filter(Game game, List<Ability> actions) { public void filter(Game game, List<Ability> actions, List<Ability> actionsToRemove) {
for (Ability ability : actions) { for (Ability ability : actions) {
if (ability instanceof LevelUpAbility) { if (ability instanceof LevelUpAbility) {
Permanent permanent = game.getPermanent(ability.getSourceId()); Permanent permanent = game.getPermanent(ability.getSourceId());
@ -31,7 +31,7 @@ public class LevelUpOptimizer extends BaseTreeOptimizer {
PermanentCard leveler = (PermanentCard) permanent; PermanentCard leveler = (PermanentCard) permanent;
// check already existing Level counters and compare to maximum that make sense // check already existing Level counters and compare to maximum that make sense
if (permanent.getCounters(game).getCount(CounterType.LEVEL) >= leveler.getMaxLevelCounters()) { if (permanent.getCounters(game).getCount(CounterType.LEVEL) >= leveler.getMaxLevelCounters()) {
removeAbility(ability); actionsToRemove.add(ability);
} }
} }
} }

View file

@ -8,18 +8,18 @@ import mage.game.Game;
import java.util.List; import java.util.List;
/** /**
* Removes abilities that require only discard a card for activation. * AI: removes abilities that AI don't know how to use
* *
* @author LevelX2 * @author LevelX2
*/ */
public class OutcomeOptimizer extends BaseTreeOptimizer { public class OutcomeOptimizer extends BaseTreeOptimizer {
@Override @Override
public void filter(Game game, List<Ability> actions) { public void filter(Game game, List<Ability> actions, List<Ability> actionsToRemove) {
for (Ability ability : actions) { for (Ability ability : actions) {
for (Effect effect : ability.getEffects()) { for (Effect effect : ability.getEffects()) {
if (ability.getCustomOutcome() == Outcome.AIDontUseIt || effect.getOutcome() == Outcome.AIDontUseIt) { if (ability.getCustomOutcome() == Outcome.AIDontUseIt || effect.getOutcome() == Outcome.AIDontUseIt) {
removeAbility(ability); actionsToRemove.add(ability);
break; break;
} }
} }