From 3c8915b00258726a7ee2f1286b9be0b76e5767dc Mon Sep 17 00:00:00 2001 From: Agetian Date: Tue, 24 Apr 2018 10:07:59 +0300 Subject: [PATCH 1/4] - AI should not count permanent cards in hand which it can't cast at the moment as possible sources of noncombat damage when predicting an assault attack. - Some AI prediction for LoseLife triggers when predicting noncombat damage --- .../java/forge/ai/AiAttackController.java | 8 ++++--- .../src/main/java/forge/ai/ComputerUtil.java | 21 +++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/AiAttackController.java b/forge-ai/src/main/java/forge/ai/AiAttackController.java index a3b078c5285..3e88146d9bd 100644 --- a/forge-ai/src/main/java/forge/ai/AiAttackController.java +++ b/forge-ai/src/main/java/forge/ai/AiAttackController.java @@ -525,13 +525,15 @@ public class AiAttackController { } } - if (ComputerUtilCombat.sumDamageIfUnblocked(unblockedAttackers, opp) + ComputerUtil.possibleNonCombatDamage(ai) - + trampleDamage >= opp.getLife() + int totalCombatDamage = ComputerUtilCombat.sumDamageIfUnblocked(unblockedAttackers, opp) + trampleDamage; + int totalPoisonDamage = ComputerUtilCombat.sumPoisonIfUnblocked(unblockedAttackers, opp); + + if (totalCombatDamage + ComputerUtil.possibleNonCombatDamage(ai) >= opp.getLife() && !((opp.cantLoseForZeroOrLessLife() || ai.cantWin()) && opp.getLife() < 1)) { return true; } - if (ComputerUtilCombat.sumPoisonIfUnblocked(unblockedAttackers, opp) >= 10 - opp.getPoisonCounters()) { + if (totalPoisonDamage >= 10 - opp.getPoisonCounters()) { return true; } diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index d7ee664fd33..f80c388ec41 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -1360,6 +1360,11 @@ public class ComputerUtil { if (sa.getApi() != ApiType.DealDamage) { continue; } + if (c.getZone().getZoneType() == ZoneType.Hand + && c.isPermanent() + && !ComputerUtilMana.canPayManaCost(c.getSpellPermanent(), ai, 0)) { + continue; + } final String numDam = sa.getParam("NumDmg"); int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), numDam, sa); if (dmg <= damage) { @@ -1378,7 +1383,23 @@ public class ComputerUtil { } damage = dmg; } + + // Triggered abilities + if (c.isCreature() && c.isInZone(ZoneType.Battlefield) && CombatUtil.canAttack(c)) { + for (final Trigger t : c.getTriggers()) { + if ("Attacks".equals(t.getParam("Mode")) && t.hasParam("Execute")) { + SpellAbility trigSa = AbilityFactory.getAbility(c.getSVar(t.getParam("Execute")), c); + trigSa.setHostCard(c); + if (trigSa != null && trigSa.getApi() == ApiType.LoseLife + && trigSa.getParamOrDefault("Defined", "").contains("Opponent")) { + damage += AbilityUtils.calculateAmount(trigSa.getHostCard(), trigSa.getParam("LifeAmount"), trigSa); + } + } + } + } + } + return damage; } From 71b2f2fa2686f4d4cf23612c8d1e97ef14f17827 Mon Sep 17 00:00:00 2001 From: Agetian Date: Tue, 24 Apr 2018 10:11:43 +0300 Subject: [PATCH 2/4] - NPE prevention. --- forge-ai/src/main/java/forge/ai/ComputerUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index f80c388ec41..95bb5a9a100 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -1389,9 +1389,9 @@ public class ComputerUtil { for (final Trigger t : c.getTriggers()) { if ("Attacks".equals(t.getParam("Mode")) && t.hasParam("Execute")) { SpellAbility trigSa = AbilityFactory.getAbility(c.getSVar(t.getParam("Execute")), c); - trigSa.setHostCard(c); if (trigSa != null && trigSa.getApi() == ApiType.LoseLife && trigSa.getParamOrDefault("Defined", "").contains("Opponent")) { + trigSa.setHostCard(c); damage += AbilityUtils.calculateAmount(trigSa.getHostCard(), trigSa.getParam("LifeAmount"), trigSa); } } From c97353cb3d9632ed06134b695fe479646e048d41 Mon Sep 17 00:00:00 2001 From: Agetian Date: Tue, 24 Apr 2018 11:53:57 +0300 Subject: [PATCH 3/4] - Filter out permanent cards in the AI's hand when looking for possible non-combat damage in combat (the logic is too indirect and will result in the AI making mistakes anyway, e.g. due to inability to pay for both the permanent and the ability and guarantee a win). --- forge-ai/src/main/java/forge/ai/ComputerUtil.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/ComputerUtil.java b/forge-ai/src/main/java/forge/ai/ComputerUtil.java index 95bb5a9a100..42d4eca7c5c 100644 --- a/forge-ai/src/main/java/forge/ai/ComputerUtil.java +++ b/forge-ai/src/main/java/forge/ai/ComputerUtil.java @@ -1353,18 +1353,13 @@ public class ComputerUtil { int damage = 0; final CardCollection all = new CardCollection(ai.getCardsIn(ZoneType.Battlefield)); all.addAll(ai.getCardsActivableInExternalZones(true)); - all.addAll(ai.getCardsIn(ZoneType.Hand)); + all.addAll(CardLists.filter(ai.getCardsIn(ZoneType.Hand), Predicates.not(Presets.PERMANENTS))); for (final Card c : all) { for (final SpellAbility sa : c.getSpellAbilities()) { if (sa.getApi() != ApiType.DealDamage) { continue; } - if (c.getZone().getZoneType() == ZoneType.Hand - && c.isPermanent() - && !ComputerUtilMana.canPayManaCost(c.getSpellPermanent(), ai, 0)) { - continue; - } final String numDam = sa.getParam("NumDmg"); int dmg = AbilityUtils.calculateAmount(sa.getHostCard(), numDam, sa); if (dmg <= damage) { From 4ae6d9030a1c6e04ed83561bda7eaf8475807b03 Mon Sep 17 00:00:00 2001 From: Agetian Date: Tue, 24 Apr 2018 12:01:13 +0300 Subject: [PATCH 4/4] - Fixed the AI not targeting creatures with DealDamage anymore (e.g. Walking Ballista). --- forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java b/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java index b49e6c5fb81..e62b2f230d7 100644 --- a/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java +++ b/forge-ai/src/main/java/forge/ai/ability/DamageDealAi.java @@ -272,7 +272,7 @@ public class DamageDealAi extends DamageAiBase { final Player activator = sa.getActivatingPlayer(); final Card source = sa.getHostCard(); final Game game = source.getGame(); - List hPlay = CardLists.filter(getTargetableCards(ai, sa, pl, tgt, activator, source, game), CardPredicates.Presets.PLANESWALKERS); + List hPlay = getTargetableCards(ai, sa, pl, tgt, activator, source, game); List killables = CardLists.filter(hPlay, new Predicate() { @Override