From dfc5fad357f9be26e5f9a29e8491e9477050daf4 Mon Sep 17 00:00:00 2001 From: Agetian Date: Thu, 12 Jan 2017 17:04:33 +0000 Subject: [PATCH] - 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). --- .../src/main/java/forge/ai/ComputerUtil.java | 99 ++++++++++--------- .../main/java/forge/ai/ComputerUtilCard.java | 11 +++ .../res/cardsfolder/d/desecration_demon.txt | 3 +- 3 files changed, 68 insertions(+), 45 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 07d9147eb3c..15955be71d6 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -308,47 +308,27 @@ public class ComputerUtil { if (prefValid[0].equals(pref)) { final CardCollection prefList = CardLists.getValidCards(typeList, prefValid[1].split(","), activate.getController(), activate, null); - // check if there are any additional conditions - if (activate.hasSVar("AIPreferenceParams")) { - int threshold = -1; - int minNeeded = -1; + int threshold = getAIPreferenceParameter(activate, "CreatureEvalThreshold"); + int minNeeded = getAIPreferenceParameter(activate, "MinCreaturesBelowThreshold"); + System.out.println("Threshold = " + threshold + ", minNeeded = " + minNeeded); - 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) { - List toRemove = Lists.newArrayList(); - for (Card c : prefList) { - if (c.isCreature()) { - if (ComputerUtilCard.isUselessCreature(ai, c) || ComputerUtilCard.evaluateCreature(c) <= threshold) { - continue; - } - toRemove.add(c); + if (threshold != -1) { + List toRemove = Lists.newArrayList(); + for (Card c : prefList) { + if (c.isCreature()) { + if (ComputerUtilCard.isUselessCreature(ai, c) || ComputerUtilCard.evaluateCreature(c) <= threshold) { + continue; + } else if (ComputerUtilCard.hasActiveUndyingOrPersist(c)) { + continue; } + toRemove.add(c); } - prefList.removeAll(toRemove); } - if (minNeeded != -1) { - if (prefList.size() < minNeeded) { - return null; - } + prefList.removeAll(toRemove); + } + if (minNeeded != -1) { + if (prefList.size() < minNeeded) { + return null; } } @@ -452,6 +432,38 @@ public class ComputerUtil { 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) { final Card source = ability.getHostCard(); 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); final CardCollection sacrificed = new CardCollection(); 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(!source.getActivatingPlayer().isOpponentOf(ai)) { @@ -671,12 +685,12 @@ public class ComputerUtil { if (!ai.canLoseLife() || ai.cantLose()) { return sacrificed; // sacrifice none } - } else { + } else if (!considerSacLogic) { 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")) { for (Trigger t : host.getTriggers()) { if (t.getMode() == TriggerType.Exploited) { @@ -701,14 +715,11 @@ public class ComputerUtil { remaining = CardLists.filter(remaining, new Predicate() { @Override 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; } - if (c.hasKeyword("Undying") && c.getCounters(CounterType.P1P1) == 0) { - return true; - } - if (c.hasKeyword("Persist") && c.getCounters(CounterType.M1M1) == 0) { + if (ComputerUtilCard.hasActiveUndyingOrPersist(c)) { return true; } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java index dd6ae90dfdd..8a9aae1eab2 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtilCard.java @@ -1430,4 +1430,15 @@ public class ComputerUtilCard { } 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; + } + } diff --git a/forge-gui/res/cardsfolder/d/desecration_demon.txt b/forge-gui/res/cardsfolder/d/desecration_demon.txt index 69285b1a8bf..19017d6b386 100644 --- a/forge-gui/res/cardsfolder/d/desecration_demon.txt +++ b/forge-gui/res/cardsfolder/d/desecration_demon.txt @@ -4,10 +4,11 @@ Types:Creature Demon PT:6/6 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. -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: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:X:Remembered$Amount +SVar:AIPreferenceParams:CreatureEvalThreshold$ 150 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.