mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
- Implemented AI for Chandra, Flamecaller and improved DamageAllAi for X
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
Reference in New Issue
Block a user