mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 20:28:00 +00:00
- Some additions to the response sac AI (take persist and undying into account).
- Added some sacrifice prioritizing to Desecration Demon (but still not sure what is best - let the AI deal with a persistent 6/6 flyer or risk pumping it up a bit but keep it in check for a while in presence of several weak creatures, could use improvement).
This commit is contained in:
@@ -308,30 +308,9 @@ public class ComputerUtil {
|
|||||||
if (prefValid[0].equals(pref)) {
|
if (prefValid[0].equals(pref)) {
|
||||||
final CardCollection prefList = CardLists.getValidCards(typeList, prefValid[1].split(","), activate.getController(), activate, null);
|
final CardCollection prefList = CardLists.getValidCards(typeList, prefValid[1].split(","), activate.getController(), activate, null);
|
||||||
|
|
||||||
// check if there are any additional conditions
|
int threshold = getAIPreferenceParameter(activate, "CreatureEvalThreshold");
|
||||||
if (activate.hasSVar("AIPreferenceParams")) {
|
int minNeeded = getAIPreferenceParameter(activate, "MinCreaturesBelowThreshold");
|
||||||
int threshold = -1;
|
System.out.println("Threshold = " + threshold + ", minNeeded = " + minNeeded);
|
||||||
int minNeeded = -1;
|
|
||||||
|
|
||||||
String[] params = StringUtils.split(activate.getSVar("AIPreferenceParams"), '|');
|
|
||||||
for (String param : params) {
|
|
||||||
String[] props = StringUtils.split(param, "$");
|
|
||||||
String parName = props[0].trim();
|
|
||||||
String parValue = props[1].trim();
|
|
||||||
|
|
||||||
switch (parName) {
|
|
||||||
case "CreatureEvalThreshold":
|
|
||||||
// Threshold of 150 is just below the level of a 1/1 mana dork or a 2/2 baseline creature with no keywords
|
|
||||||
threshold = Integer.parseInt(parValue);
|
|
||||||
break;
|
|
||||||
case "MinCreaturesBelowThreshold":
|
|
||||||
minNeeded = Integer.parseInt(parValue);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
System.err.println("Warning: unknown parameter " + parName + " in AIPreferenceParams for card " + activate);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (threshold != -1) {
|
if (threshold != -1) {
|
||||||
List<Card> toRemove = Lists.newArrayList();
|
List<Card> toRemove = Lists.newArrayList();
|
||||||
@@ -339,6 +318,8 @@ public class ComputerUtil {
|
|||||||
if (c.isCreature()) {
|
if (c.isCreature()) {
|
||||||
if (ComputerUtilCard.isUselessCreature(ai, c) || ComputerUtilCard.evaluateCreature(c) <= threshold) {
|
if (ComputerUtilCard.isUselessCreature(ai, c) || ComputerUtilCard.evaluateCreature(c) <= threshold) {
|
||||||
continue;
|
continue;
|
||||||
|
} else if (ComputerUtilCard.hasActiveUndyingOrPersist(c)) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
toRemove.add(c);
|
toRemove.add(c);
|
||||||
}
|
}
|
||||||
@@ -350,7 +331,6 @@ public class ComputerUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!prefList.isEmpty()) {
|
if (!prefList.isEmpty()) {
|
||||||
return ComputerUtilCard.getWorstAI(prefList);
|
return ComputerUtilCard.getWorstAI(prefList);
|
||||||
@@ -452,6 +432,38 @@ public class ComputerUtil {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getAIPreferenceParameter(final Card c, final String paramName) {
|
||||||
|
if (!c.hasSVar("AIPreferenceParams")) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] params = StringUtils.split(c.getSVar("AIPreferenceParams"), '|');
|
||||||
|
for (String param : params) {
|
||||||
|
String[] props = StringUtils.split(param, "$");
|
||||||
|
String parName = props[0].trim();
|
||||||
|
String parValue = props[1].trim();
|
||||||
|
|
||||||
|
switch (parName) {
|
||||||
|
case "CreatureEvalThreshold":
|
||||||
|
// Threshold of 150 is just below the level of a 1/1 mana dork or a 2/2 baseline creature with no keywords
|
||||||
|
if (paramName.equals(parName)) {
|
||||||
|
return Integer.parseInt(parValue);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "MinCreaturesBelowThreshold":
|
||||||
|
if (paramName.equals(parName)) {
|
||||||
|
return Integer.parseInt(parValue);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
System.err.println("Warning: unknown parameter " + parName + " in AIPreferenceParams for card " + c);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
public static CardCollection chooseSacrificeType(final Player ai, final String type, final SpellAbility ability, final Card target, final int amount) {
|
public static CardCollection chooseSacrificeType(final Player ai, final String type, final SpellAbility ability, final Card target, final int amount) {
|
||||||
final Card source = ability.getHostCard();
|
final Card source = ability.getHostCard();
|
||||||
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), source.getController(), source, null);
|
CardCollection typeList = CardLists.getValidCards(ai.getCardsIn(ZoneType.Battlefield), type.split(";"), source.getController(), source, null);
|
||||||
@@ -661,6 +673,8 @@ public class ComputerUtil {
|
|||||||
CardCollection remaining = new CardCollection(cardlist);
|
CardCollection remaining = new CardCollection(cardlist);
|
||||||
final CardCollection sacrificed = new CardCollection();
|
final CardCollection sacrificed = new CardCollection();
|
||||||
final Card host = source.getHostCard();
|
final Card host = source.getHostCard();
|
||||||
|
final boolean considerSacLogic = "ConsiderSac".equals(source.getParam("AILogic"));
|
||||||
|
final int considerSacThreshold = getAIPreferenceParameter(host, "CreatureEvalThreshold");
|
||||||
|
|
||||||
if ("OpponentOnly".equals(source.getParam("AILogic"))) {
|
if ("OpponentOnly".equals(source.getParam("AILogic"))) {
|
||||||
if(!source.getActivatingPlayer().isOpponentOf(ai)) {
|
if(!source.getActivatingPlayer().isOpponentOf(ai)) {
|
||||||
@@ -671,12 +685,12 @@ public class ComputerUtil {
|
|||||||
if (!ai.canLoseLife() || ai.cantLose()) {
|
if (!ai.canLoseLife() || ai.cantLose()) {
|
||||||
return sacrificed; // sacrifice none
|
return sacrificed; // sacrifice none
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!considerSacLogic) {
|
||||||
return sacrificed; // sacrifice none
|
return sacrificed; // sacrifice none
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isOptional && source.hasParam("Devour") || source.hasParam("Exploit")) {
|
if (isOptional && source.hasParam("Devour") || source.hasParam("Exploit") || considerSacLogic) {
|
||||||
if (source.hasParam("Exploit")) {
|
if (source.hasParam("Exploit")) {
|
||||||
for (Trigger t : host.getTriggers()) {
|
for (Trigger t : host.getTriggers()) {
|
||||||
if (t.getMode() == TriggerType.Exploited) {
|
if (t.getMode() == TriggerType.Exploited) {
|
||||||
@@ -701,14 +715,11 @@ public class ComputerUtil {
|
|||||||
remaining = CardLists.filter(remaining, new Predicate<Card>() {
|
remaining = CardLists.filter(remaining, new Predicate<Card>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(final Card c) {
|
public boolean apply(final Card c) {
|
||||||
if (c.hasSVar("SacMe") || ComputerUtilCard.evaluateCreature(c) < 190) {
|
if (c.hasSVar("SacMe") || ComputerUtilCard.evaluateCreature(c) < (considerSacThreshold != -1 ? considerSacThreshold : 190)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c.hasKeyword("Undying") && c.getCounters(CounterType.P1P1) == 0) {
|
if (ComputerUtilCard.hasActiveUndyingOrPersist(c)) {
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (c.hasKeyword("Persist") && c.getCounters(CounterType.M1M1) == 0) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1430,4 +1430,15 @@ public class ComputerUtilCard {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean hasActiveUndyingOrPersist(final Card c) {
|
||||||
|
if (c.hasKeyword("Undying") && c.getCounters(CounterType.P1P1) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (c.hasKeyword("Persist") && c.getCounters(CounterType.M1M1) == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ Types:Creature Demon
|
|||||||
PT:6/6
|
PT:6/6
|
||||||
K:Flying
|
K:Flying
|
||||||
T:Mode$ Phase | Phase$ BeginCombat | TriggerZones$ Battlefield | Execute$ TrigSac | TriggerDescription$ At the beginning of each combat, any opponent may sacrifice a creature. If a player does, tap CARDNAME and put a +1/+1 counter on it.
|
T:Mode$ Phase | Phase$ BeginCombat | TriggerZones$ Battlefield | Execute$ TrigSac | TriggerDescription$ At the beginning of each combat, any opponent may sacrifice a creature. If a player does, tap CARDNAME and put a +1/+1 counter on it.
|
||||||
SVar:TrigSac:AB$ Sacrifice | Cost$ 0 | Defined$ Opponent | Amount$ 1 | SacValid$ Creature | RememberSacrificed$ True | Optional$ True | SubAbility$ DBSacSelf
|
SVar:TrigSac:AB$ Sacrifice | Cost$ 0 | Defined$ Opponent | Amount$ 1 | SacValid$ Creature | RememberSacrificed$ True | Optional$ True | AILogic$ ConsiderSac | SubAbility$ DBSacSelf
|
||||||
SVar:DBSacSelf:DB$ Tap | Cost$ 0 | Defined$ Self | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | References$ X | SubAbility$ DBPutCounter
|
SVar:DBSacSelf:DB$ Tap | Cost$ 0 | Defined$ Self | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | References$ X | SubAbility$ DBPutCounter
|
||||||
SVar:DBPutCounter:DB$ PutCounter | Cost$ 0 | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | References$ X | SubAbility$ DBCleanup
|
SVar:DBPutCounter:DB$ PutCounter | Cost$ 0 | Defined$ Self | CounterType$ P1P1 | CounterNum$ 1 | ConditionCheckSVar$ X | ConditionSVarCompare$ GE1 | References$ X | SubAbility$ DBCleanup
|
||||||
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True
|
||||||
SVar:X:Remembered$Amount
|
SVar:X:Remembered$Amount
|
||||||
|
SVar:AIPreferenceParams:CreatureEvalThreshold$ 150
|
||||||
SVar:Picture:http://www.wizards.com/global/images/magic/general/desecration_demon.jpg
|
SVar:Picture:http://www.wizards.com/global/images/magic/general/desecration_demon.jpg
|
||||||
Oracle:Flying\nAt the beginning of each combat, any opponent may sacrifice a creature. If a player does, tap Desecration Demon and put a +1/+1 counter on it.
|
Oracle:Flying\nAt the beginning of each combat, any opponent may sacrifice a creature. If a player does, tap Desecration Demon and put a +1/+1 counter on it.
|
||||||
|
|||||||
Reference in New Issue
Block a user