- Implemented AI for Chandra, Flamecaller and improved DamageAllAi for X

This commit is contained in:
excessum
2016-03-19 06:12:15 +00:00
parent 57d7dcad0b
commit 448bce81b7
3 changed files with 58 additions and 38 deletions

View File

@@ -7,6 +7,7 @@ import forge.game.ability.AbilityUtils;
import forge.game.card.Card; import forge.game.card.Card;
import forge.game.card.CardCollection; import forge.game.card.CardCollection;
import forge.game.card.CardLists; import forge.game.card.CardLists;
import forge.game.card.CounterType;
import forge.game.cost.Cost; import forge.game.cost.Cost;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.spellability.SpellAbility; import forge.game.spellability.SpellAbility;
@@ -21,27 +22,63 @@ public class DamageAllAi extends SpellAbilityAi {
protected boolean canPlayAI(Player ai, SpellAbility sa) { protected boolean canPlayAI(Player ai, SpellAbility sa) {
// AI needs to be expanded, since this function can be pretty complex // AI needs to be expanded, since this function can be pretty complex
// based on what the expected targets could be // based on what the expected targets could be
final Random r = MyRandom.getRandom();
final Cost abCost = sa.getPayCosts();
final Card source = sa.getHostCard(); final Card source = sa.getHostCard();
String validP = ""; // prevent run-away activations - first time will always return true
final Random r = MyRandom.getRandom();
if (r.nextFloat() > Math.pow(.9, sa.getActivationsThisTurn())) {
return false;
}
// abCost stuff that should probably be centralized...
final Cost abCost = sa.getPayCosts();
if (abCost != null) {
// AI currently disabled for some costs
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, null)) {
return false;
}
}
// wait until stack is empty (prevents duplicate kills)
if (!ai.getGame().getStack().isEmpty()) {
return false;
}
int x = -1;
final String damage = sa.getParam("NumDmg"); final String damage = sa.getParam("NumDmg");
int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa); int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), damage, sa);
if (damage.equals("X") && sa.getSVar(damage).equals("Count$Converge")) { if (damage.equals("X") && sa.getSVar(damage).equals("Count$Converge")) {
dmg = ComputerUtilMana.getConvergeCount(sa, ai); dmg = ComputerUtilMana.getConvergeCount(sa, ai);
} }
if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) { if (damage.equals("X") && sa.getSVar(damage).equals("Count$xPaid")) {
// Set PayX here to maximum value. x = ComputerUtilMana.determineLeftoverMana(sa, ai);
dmg = ComputerUtilMana.determineLeftoverMana(sa, ai);
source.setSVar("PayX", Integer.toString(dmg));
} }
if (damage.equals("ChosenX")) {
if (sa.hasParam("ValidPlayers")) { x = source.getCounters(CounterType.LOYALTY);
validP = sa.getParam("ValidPlayers");
} }
if (x == -1) {
return evaluateDamageAll(ai, sa, source, dmg) > 0;
} else {
int best = -1, best_x = -1;
for (int i = 0; i < x; i++) {
final int value = evaluateDamageAll(ai, sa, source, i);
if (value > best) {
best = value;
best_x = i;
}
}
if (best_x > 0) {
if (sa.getSVar(damage).equals("Count$xPaid")) {
source.setSVar("PayX", Integer.toString(best_x));
}
if (damage.equals("ChosenX")) {
source.setSVar("ChosenX", "Number$" + best_x);
}
return true;
}
return false;
}
}
private int evaluateDamageAll(Player ai, SpellAbility sa, final Card source, int dmg) {
Player opp = ai.getOpponent(); Player opp = ai.getOpponent();
final CardCollection humanList = getKillableCreatures(sa, opp, dmg); final CardCollection humanList = getKillableCreatures(sa, opp, dmg);
CardCollection computerList = getKillableCreatures(sa, ai, dmg); CardCollection computerList = getKillableCreatures(sa, ai, dmg);
@@ -53,34 +90,17 @@ public class DamageAllAi extends SpellAbilityAi {
computerList.clear(); computerList.clear();
} }
// abCost stuff that should probably be centralized... final String validP = sa.hasParam("ValidPlayers") ? sa.getParam("ValidPlayers") : "";
if (abCost != null) {
// AI currently disabled for some costs
if (!ComputerUtilCost.checkLifeCost(ai, abCost, source, 4, null)) {
return false;
}
}
// TODO: if damage is dependant on mana paid, maybe have X be human's max life // TODO: if damage is dependant on mana paid, maybe have X be human's max life
// Don't kill yourself // Don't kill yourself
if (validP.equals("Player") && (ai.getLife() <= ComputerUtilCombat.predictDamageTo(ai, dmg, source, false))) { if (validP.equals("Player") && (ai.getLife() <= ComputerUtilCombat.predictDamageTo(ai, dmg, source, false))) {
return false; return -1;
}
// prevent run-away activations - first time will always return true
if (r.nextFloat() > Math.pow(.9, sa.getActivationsThisTurn())) {
return false;
} }
// if we can kill human, do it // if we can kill human, do it
if ((validP.equals("Player") || validP.contains("Opponent")) if ((validP.equals("Player") || validP.contains("Opponent"))
&& (opp.getLife() <= ComputerUtilCombat.predictDamageTo(opp, dmg, source, false))) { && (opp.getLife() <= ComputerUtilCombat.predictDamageTo(opp, dmg, source, false))) {
return true; return 1;
}
// wait until stack is empty (prevents duplicate kills)
if (!ai.getGame().getStack().isEmpty()) {
return false;
} }
int minGain = 200; // The minimum gain in destroyed creatures int minGain = 200; // The minimum gain in destroyed creatures
@@ -95,13 +115,8 @@ public class DamageAllAi extends SpellAbilityAi {
minGain = 126; // prepare for attack minGain = 126; // prepare for attack
} }
// evaluate both lists and pass only if human creatures are more valuable return ComputerUtilCard.evaluateCreatureList(humanList) - ComputerUtilCard.evaluateCreatureList(computerList)
if ((ComputerUtilCard.evaluateCreatureList(computerList) + minGain) >= ComputerUtilCard - minGain;
.evaluateCreatureList(humanList)) {
return false;
}
return true;
} }
@Override @Override

View File

@@ -45,6 +45,11 @@ public class DiscardAi extends SpellAbilityAi {
} }
} }
if ("Chandra, Flamecaller".equals(source.getName())) {
final int hand = ai.getCardsIn(ZoneType.Hand).size();
return MyRandom.getRandom().nextFloat() < (1.0 / (1 + hand));
}
final boolean humanHasHand = ai.getOpponent().getCardsIn(ZoneType.Hand).size() > 0; final boolean humanHasHand = ai.getOpponent().getCardsIn(ZoneType.Hand).size() > 0;

View File

@@ -12,6 +12,6 @@ SVar:DBDraw:DB$ Draw | NumCards$ Y | Defined$ You | SubAbility$ DBCleanup | Spel
SVar:Y:Remembered$Amount.Plus.1 SVar:Y:Remembered$Amount.Plus.1
A:AB$ DamageAll | Cost$ SubCounter<X/LOYALTY> | NumDmg$ ChosenX | References$ X | ValidCards$ Creature | Planeswalker$ True | Ultimate$ True | ValidDescription$ each creature. | SpellDescription$ CARDNAME deals X damage to each creature. A:AB$ DamageAll | Cost$ SubCounter<X/LOYALTY> | NumDmg$ ChosenX | References$ X | ValidCards$ Creature | Planeswalker$ True | Ultimate$ True | ValidDescription$ each creature. | SpellDescription$ CARDNAME deals X damage to each creature.
SVar:X:XChoice SVar:X:XChoice
SVar:PlayMain1:TRUE SVar:PlayMain1:ALWAYS
SVar:Picture:http://www.wizards.com/global/images/magic/general/chandra_flamecaller.jpg SVar:Picture:http://www.wizards.com/global/images/magic/general/chandra_flamecaller.jpg
Oracle:[+1] Put two 3/1 red Elemental creature tokens with haste onto the battlefield. Exile them at the beginning of the next end step.\n[0] Discard all the cards in your hand, then draw that many cards plus one.\n[-X] Chandra, Flamecaller deals X damage to each creature. Oracle:[+1] Put two 3/1 red Elemental creature tokens with haste onto the battlefield. Exile them at the beginning of the next end step.\n[0] Discard all the cards in your hand, then draw that many cards plus one.\n[-X] Chandra, Flamecaller deals X damage to each creature.