mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-20 12:48:00 +00:00
- Implemented a basic "good" triple gang block. Currently considers a 3-vs-1 block in which only one blocker dies *or* a 3-vs-1 block in which two blockers may die but one of which is a token "good". May not be optimal and may be a bit slow at the moment, please consider improving.
This commit is contained in:
@@ -335,6 +335,10 @@ public class AiBlockController {
|
|||||||
|
|
||||||
// Try to block an attacker without first strike with a gang of first strikers
|
// Try to block an attacker without first strike with a gang of first strikers
|
||||||
for (final Card attacker : attackersLeft) {
|
for (final Card attacker : attackersLeft) {
|
||||||
|
if (ComputerUtilCombat.attackerCantBeDestroyedInCombat(ai, attacker)) {
|
||||||
|
// don't bother with gang blocking if the attacker will regenerate or is indestructible
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!ComputerUtilCombat.dealsFirstStrikeDamage(attacker, false, combat)) {
|
if (!ComputerUtilCombat.dealsFirstStrikeDamage(attacker, false, combat)) {
|
||||||
blockers = getPossibleBlockers(combat, attacker, blockersLeft, false);
|
blockers = getPossibleBlockers(combat, attacker, blockersLeft, false);
|
||||||
final List<Card> firstStrikeBlockers = new ArrayList<>();
|
final List<Card> firstStrikeBlockers = new ArrayList<>();
|
||||||
@@ -376,16 +380,26 @@ public class AiBlockController {
|
|||||||
attackersLeft = (new ArrayList<>(currentAttackers));
|
attackersLeft = (new ArrayList<>(currentAttackers));
|
||||||
currentAttackers = new ArrayList<>(attackersLeft);
|
currentAttackers = new ArrayList<>(attackersLeft);
|
||||||
|
|
||||||
|
boolean considerTripleBlock = true;
|
||||||
|
|
||||||
// Try to block an attacker with two blockers of which only one will die
|
// Try to block an attacker with two blockers of which only one will die
|
||||||
for (final Card attacker : attackersLeft) {
|
for (final Card attacker : attackersLeft) {
|
||||||
|
if (ComputerUtilCombat.attackerCantBeDestroyedInCombat(ai, attacker)) {
|
||||||
|
// don't bother with gang blocking if the attacker will regenerate or is indestructible
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int evalAttackerValue = ComputerUtilCard.evaluateCreature(attacker);
|
||||||
|
|
||||||
blockers = getPossibleBlockers(combat, attacker, blockersLeft, false);
|
blockers = getPossibleBlockers(combat, attacker, blockersLeft, false);
|
||||||
List<Card> usableBlockers;
|
List<Card> usableBlockers;
|
||||||
final List<Card> blockGang = new ArrayList<>();
|
final List<Card> blockGang = new ArrayList<>();
|
||||||
int absorbedDamage; // The amount of damage needed to kill the first blocker
|
int absorbedDamage; // The amount of damage needed to kill the first blocker
|
||||||
int currentValue; // The value of the creatures in the blockgang
|
int currentValue; // The value of the creatures in the blockgang
|
||||||
|
boolean foundDoubleBlock = false; // if true, a good double block is found
|
||||||
|
|
||||||
// AI can't handle good triple blocks yet
|
// AI can't handle good blocks with more than three creatures yet
|
||||||
if (CombatUtil.needsBlockers(attacker) > 2) {
|
if (CombatUtil.needsBlockers(attacker) > (considerTripleBlock ? 3 : 2)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -411,6 +425,7 @@ public class AiBlockController {
|
|||||||
absorbedDamage = ComputerUtilCombat.getEnoughDamageToKill(leader, attacker.getNetCombatDamage(), attacker, true);
|
absorbedDamage = ComputerUtilCombat.getEnoughDamageToKill(leader, attacker.getNetCombatDamage(), attacker, true);
|
||||||
currentValue = ComputerUtilCard.evaluateCreature(leader);
|
currentValue = ComputerUtilCard.evaluateCreature(leader);
|
||||||
|
|
||||||
|
// consider a double block
|
||||||
for (final Card blocker : usableBlockers) {
|
for (final Card blocker : usableBlockers) {
|
||||||
// Add an additional blocker if the current blockers are not
|
// Add an additional blocker if the current blockers are not
|
||||||
// enough and the new one would deal the remaining damage
|
// enough and the new one would deal the remaining damage
|
||||||
@@ -425,7 +440,7 @@ public class AiBlockController {
|
|||||||
// The attacker will be killed
|
// The attacker will be killed
|
||||||
&& (absorbedDamage2 + absorbedDamage > attacker.getNetCombatDamage()
|
&& (absorbedDamage2 + absorbedDamage > attacker.getNetCombatDamage()
|
||||||
// only one blocker can be killed
|
// only one blocker can be killed
|
||||||
|| currentValue + addedValue - 50 <= ComputerUtilCard.evaluateCreature(attacker)
|
|| currentValue + addedValue - 50 <= evalAttackerValue
|
||||||
// or attacker is worth more
|
// or attacker is worth more
|
||||||
|| (lifeInDanger && ComputerUtilCombat.lifeInDanger(ai, combat)))
|
|| (lifeInDanger && ComputerUtilCombat.lifeInDanger(ai, combat)))
|
||||||
// or life is in danger
|
// or life is in danger
|
||||||
@@ -437,8 +452,69 @@ public class AiBlockController {
|
|||||||
if (CombatUtil.canBlock(attacker, leader, combat)) {
|
if (CombatUtil.canBlock(attacker, leader, combat)) {
|
||||||
combat.addBlocker(attacker, leader);
|
combat.addBlocker(attacker, leader);
|
||||||
}
|
}
|
||||||
|
foundDoubleBlock = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!foundDoubleBlock && (currentDamage + additionalDamage >= damageNeeded)) {
|
||||||
|
// a double block was tested which resulted in a potential kill but it was dismissed,
|
||||||
|
// no need to test for a triple block then to avoid suboptimal plays.
|
||||||
|
considerTripleBlock = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundDoubleBlock || !considerTripleBlock) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// consider a triple block if a double block was not found
|
||||||
|
blockerLoop:
|
||||||
|
for (final Card secondBlocker : usableBlockers) {
|
||||||
|
// consider the properties of the second blocker
|
||||||
|
final int currentDamage = ComputerUtilCombat.totalDamageOfBlockers(attacker, blockGang);
|
||||||
|
final int additionalDamage2 = ComputerUtilCombat.dealsDamageAsBlocker(attacker, secondBlocker);
|
||||||
|
final int absorbedDamage2 = ComputerUtilCombat.getEnoughDamageToKill(secondBlocker, attacker.getNetCombatDamage(), attacker, true);
|
||||||
|
final int addedValue2 = ComputerUtilCard.evaluateCreature(secondBlocker);
|
||||||
|
final int damageNeeded = ComputerUtilCombat.getDamageToKill(attacker)
|
||||||
|
+ ComputerUtilCombat.predictToughnessBonusOfAttacker(attacker, secondBlocker, combat, false);
|
||||||
|
|
||||||
|
List<Card> usableBlockersAsThird = new ArrayList<>();
|
||||||
|
usableBlockersAsThird.addAll(usableBlockers);
|
||||||
|
usableBlockersAsThird.remove(secondBlocker);
|
||||||
|
|
||||||
|
// loop over the remaining blockers in search of a good third blocker candidate
|
||||||
|
for (Card thirdBlocker : usableBlockersAsThird) {
|
||||||
|
final int additionalDamage3 = ComputerUtilCombat.dealsDamageAsBlocker(attacker, thirdBlocker);
|
||||||
|
final int absorbedDamage3 = ComputerUtilCombat.getEnoughDamageToKill(thirdBlocker, attacker.getNetCombatDamage(), attacker, true);
|
||||||
|
final int addedValue3 = ComputerUtilCard.evaluateCreature(secondBlocker);
|
||||||
|
final int netCombatDamage = attacker.getNetCombatDamage();
|
||||||
|
|
||||||
|
if ((damageNeeded > currentDamage || CombatUtil.needsBlockers(attacker) > blockGang.size())
|
||||||
|
&& !(damageNeeded > currentDamage + additionalDamage2 + additionalDamage3)
|
||||||
|
// The attacker will be killed
|
||||||
|
&& ((absorbedDamage2 + absorbedDamage > netCombatDamage && absorbedDamage3 + absorbedDamage > netCombatDamage
|
||||||
|
&& absorbedDamage3 + absorbedDamage2 > netCombatDamage)
|
||||||
|
// only one blocker can be killed
|
||||||
|
|| currentValue + addedValue2 + addedValue3 - 50 <= evalAttackerValue
|
||||||
|
// or attacker is worth more
|
||||||
|
|| (thirdBlocker.isToken() && absorbedDamage2 + absorbedDamage > netCombatDamage)
|
||||||
|
// or third blocker is a token and no more than two blockers will die, one of which is the third blocker (token)
|
||||||
|
|| (lifeInDanger && ComputerUtilCombat.lifeInDanger(ai, combat)))
|
||||||
|
// or life is in danger
|
||||||
|
&& CombatUtil.canBlock(attacker, secondBlocker, combat)
|
||||||
|
&& CombatUtil.canBlock(attacker, thirdBlocker, combat)) {
|
||||||
|
// this is needed for attackers that can't be blocked by
|
||||||
|
// more than 1
|
||||||
|
currentAttackers.remove(attacker);
|
||||||
|
combat.addBlocker(attacker, thirdBlocker);
|
||||||
|
if (CombatUtil.canBlock(attacker, secondBlocker, combat)) {
|
||||||
|
combat.addBlocker(attacker, secondBlocker);
|
||||||
|
}
|
||||||
|
if (CombatUtil.canBlock(attacker, leader, combat)) {
|
||||||
|
combat.addBlocker(attacker, leader);
|
||||||
|
}
|
||||||
|
break blockerLoop;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1577,6 +1577,36 @@ public class ComputerUtilCombat {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// can the attacker be potentially destroyed in combat or is it potentially indestructible?
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* attackerCantBeDestroyedNow.
|
||||||
|
* </p>
|
||||||
|
* @param ai
|
||||||
|
*
|
||||||
|
* @param attacker
|
||||||
|
* a {@link forge.game.card.Card} object.
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
public static boolean attackerCantBeDestroyedInCombat(Player ai, final Card attacker) {
|
||||||
|
// attacker is either indestructible or may regenerate
|
||||||
|
if (attacker.hasKeyword("Indestructible") || (ComputerUtil.canRegenerate(ai, attacker))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// attacker will regenerate
|
||||||
|
if (attacker.getShieldCount() > 0 && !attacker.hasKeyword("CARDNAME can't be regenerated.")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// all damage will be prevented
|
||||||
|
if (attacker.hasKeyword("PreventAllDamageBy Creature.blockingSource")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// can the blocker destroy the attacker?
|
// can the blocker destroy the attacker?
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
|
|||||||
Reference in New Issue
Block a user