mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
Simulated AI: Fix multi-target spell simulation. (#2181)
* Simulated AI: Fix multi-target spell simulation. Removes an incorrect check that was comparing two semantically different things (number of possible to choose from vs. number of targets chosen). Adds a test using Incremental Growth, where number of possible targets is 5, but the spell requires only 3 to be chosen. * Fix the root issue and eliminate incorrect simulations. * Fix infinite loop with invalid targets. * Fix logic.
This commit is contained in:
@@ -11,6 +11,10 @@ public class MultiTargetSelector {
|
||||
public static class Targets {
|
||||
private ArrayList<PossibleTargetSelector.Targets> targets;
|
||||
|
||||
public int size() {
|
||||
return targets.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@@ -24,8 +28,8 @@ public class MultiTargetSelector {
|
||||
}
|
||||
}
|
||||
|
||||
private List<PossibleTargetSelector> selectors;
|
||||
private List<SpellAbility> targetingSAs;
|
||||
private final List<PossibleTargetSelector> selectors;
|
||||
private final List<SpellAbility> targetingSAs;
|
||||
private int currentIndex;
|
||||
|
||||
public MultiTargetSelector(SpellAbility sa, List<AbilitySub> plannedSubs) {
|
||||
@@ -52,8 +56,8 @@ public class MultiTargetSelector {
|
||||
public Targets getLastSelectedTargets() {
|
||||
Targets targets = new Targets();
|
||||
targets.targets = new ArrayList<>(selectors.size());
|
||||
for (int i = 0; i < selectors.size(); i++) {
|
||||
targets.targets.add(selectors.get(i).getLastSelectedTargets());
|
||||
for (PossibleTargetSelector selector : selectors) {
|
||||
targets.targets.add(selector.getLastSelectedTargets());
|
||||
}
|
||||
return targets;
|
||||
}
|
||||
@@ -78,34 +82,62 @@ public class MultiTargetSelector {
|
||||
currentIndex = -1;
|
||||
}
|
||||
|
||||
public void selectTargetsByIndex(int i) {
|
||||
public boolean selectTargetsByIndex(int i) {
|
||||
// The caller is telling us to select the i-th possible set of targets.
|
||||
if (i < currentIndex) {
|
||||
reset();
|
||||
}
|
||||
while (currentIndex < i) {
|
||||
selectNextTargets();
|
||||
if (!selectNextTargets()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean selectNextTargets() {
|
||||
if (currentIndex == -1) {
|
||||
for (PossibleTargetSelector selector : selectors) {
|
||||
if (!selector.selectNextTargets()) {
|
||||
private boolean selectTargetsStartingFrom(int selectorIndex) {
|
||||
// Don't reset the current selector, as it still has the correct list of targets set and has
|
||||
// to remember its current/next target index. Subsequent selectors need a reset since their
|
||||
// possible targets may change based on what was chosen for earlier ones.
|
||||
if (selectors.get(selectorIndex).selectNextTargets()) {
|
||||
for (int i = selectorIndex + 1; i < selectors.size(); i++) {
|
||||
selectors.get(i).reset();
|
||||
if (!selectors.get(i).selectNextTargets()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
currentIndex = 0;
|
||||
return true;
|
||||
}
|
||||
for (int i = selectors.size() - 1; i >= 0; i--) {
|
||||
if (selectors.get(i).selectNextTargets()) {
|
||||
currentIndex++;
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean selectNextTargets() {
|
||||
if (selectors.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
if (currentIndex == -1) {
|
||||
// Select the first set of targets (calls selectNextTargets() on each selector).
|
||||
if (selectTargetsStartingFrom(0)) {
|
||||
currentIndex = 0;
|
||||
return true;
|
||||
}
|
||||
selectors.get(i).reset();
|
||||
selectors.get(i).selectNextTargets();
|
||||
// No possible targets.
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
// Subsequent call, first try selecting a new target for the last selector. If that doesn't
|
||||
// work, backtrack (decrement selector index) and try selecting targets from there.
|
||||
// This approach ensures that leaf selectors (end of list) are advanced first, before
|
||||
// previous ones, so that we get an AA,AB,BA,BB ordering.
|
||||
int selectorIndex = selectors.size() - 1;
|
||||
while (!selectTargetsStartingFrom(selectorIndex)) {
|
||||
if (selectorIndex == 0) {
|
||||
// No more possible targets.
|
||||
return false;
|
||||
}
|
||||
selectorIndex--;
|
||||
}
|
||||
currentIndex++;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean conditionsAreMet(SpellAbility saOrSubSa) {
|
||||
|
||||
@@ -17,12 +17,11 @@ import forge.game.spellability.TargetRestrictions;
|
||||
|
||||
public class PossibleTargetSelector {
|
||||
private final SpellAbility sa;
|
||||
private SpellAbility targetingSa;
|
||||
private int targetingSaIndex;
|
||||
private final SpellAbility targetingSa;
|
||||
private final int targetingSaIndex;
|
||||
private int maxTargets;
|
||||
private TargetRestrictions tgt;
|
||||
private int targetIndex;
|
||||
private List<GameObject> validTargets;
|
||||
private int nextTargetIndex;
|
||||
private final List<GameObject> validTargets = new ArrayList<>();
|
||||
|
||||
public static class Targets {
|
||||
final int targetingSaIndex;
|
||||
@@ -36,7 +35,7 @@ public class PossibleTargetSelector {
|
||||
this.targetIndex = targetIndex;
|
||||
this.description = description;
|
||||
|
||||
if (targetIndex < 0 || targetIndex >= originalTargetCount) {
|
||||
if (targetIndex != -1 && (targetIndex < 0 || targetIndex >= originalTargetCount)) {
|
||||
throw new IllegalArgumentException("Invalid targetIndex=" + targetIndex);
|
||||
}
|
||||
}
|
||||
@@ -51,12 +50,11 @@ public class PossibleTargetSelector {
|
||||
this.sa = sa;
|
||||
this.targetingSa = targetingSa;
|
||||
this.targetingSaIndex = targetingSaIndex;
|
||||
this.validTargets = new ArrayList<>();
|
||||
generateValidTargets(sa.getHostCard().getController());
|
||||
reset();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
targetIndex = 0;
|
||||
nextTargetIndex = 0;
|
||||
validTargets.clear();
|
||||
generateValidTargets(sa.getHostCard().getController());
|
||||
}
|
||||
@@ -67,7 +65,7 @@ public class PossibleTargetSelector {
|
||||
}
|
||||
sa.setActivatingPlayer(player, true);
|
||||
targetingSa.resetTargets();
|
||||
tgt = targetingSa.getTargetRestrictions();
|
||||
TargetRestrictions tgt = targetingSa.getTargetRestrictions();
|
||||
maxTargets = tgt.getMaxTargets(sa.getHostCard(), targetingSa);
|
||||
|
||||
SimilarTargetSkipper skipper = new SimilarTargetSkipper();
|
||||
@@ -80,8 +78,8 @@ public class PossibleTargetSelector {
|
||||
}
|
||||
|
||||
private static class SimilarTargetSkipper {
|
||||
private ArrayListMultimap<String, Card> validTargetsMap = ArrayListMultimap.create();
|
||||
private HashMap<Card, String> cardTypeStrings = new HashMap<>();
|
||||
private final ArrayListMultimap<String, Card> validTargetsMap = ArrayListMultimap.create();
|
||||
private final HashMap<Card, String> cardTypeStrings = new HashMap<>();
|
||||
private HashMap<Card, Integer> creatureScores;
|
||||
|
||||
private int getCreatureScore(Card c) {
|
||||
@@ -190,16 +188,7 @@ public class PossibleTargetSelector {
|
||||
}
|
||||
|
||||
public Targets getLastSelectedTargets() {
|
||||
return new Targets(targetingSaIndex, validTargets.size(), targetIndex - 1, targetingSa.getTargets().toString());
|
||||
}
|
||||
|
||||
public boolean selectTargetsByIndex(int targetIndex) {
|
||||
if (targetIndex >= validTargets.size()) {
|
||||
return false;
|
||||
}
|
||||
selectTargetsByIndexImpl(targetIndex);
|
||||
this.targetIndex = targetIndex + 1;
|
||||
return true;
|
||||
return new Targets(targetingSaIndex, validTargets.size(), nextTargetIndex - 1, targetingSa.getTargets().toString());
|
||||
}
|
||||
|
||||
public boolean selectTargets(Targets targets) {
|
||||
@@ -208,16 +197,16 @@ public class PossibleTargetSelector {
|
||||
return false;
|
||||
}
|
||||
selectTargetsByIndexImpl(targets.targetIndex);
|
||||
this.targetIndex = targets.targetIndex + 1;
|
||||
this.nextTargetIndex = targets.targetIndex + 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean selectNextTargets() {
|
||||
if (targetIndex >= validTargets.size()) {
|
||||
if (nextTargetIndex >= validTargets.size()) {
|
||||
return false;
|
||||
}
|
||||
selectTargetsByIndexImpl(targetIndex);
|
||||
targetIndex++;
|
||||
selectTargetsByIndexImpl(nextTargetIndex);
|
||||
nextTargetIndex++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user