mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 18:28:00 +00:00
- Tweaks and cleanup in ComputerUtilAttack.
This commit is contained in:
@@ -1596,7 +1596,6 @@ public final class AbilityFactoryChangeZone {
|
|||||||
subAffected = subParams.get("Defined");
|
subAffected = subParams.get("Defined");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.out.println("changeZone: " + origin + destination + source);
|
|
||||||
|
|
||||||
if (tgt != null) {
|
if (tgt != null) {
|
||||||
tgt.resetTargets();
|
tgt.resetTargets();
|
||||||
|
|||||||
@@ -121,12 +121,26 @@ public class CombatUtil {
|
|||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
public static boolean canBlock(final Card blocker) {
|
public static boolean canBlock(final Card blocker) {
|
||||||
|
return canBlock(blocker, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// can the creature block at all?
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* canBlock.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param blocker
|
||||||
|
* a {@link forge.Card} object.
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
public static boolean canBlock(final Card blocker, final boolean nextTurn) {
|
||||||
|
|
||||||
if (blocker == null) {
|
if (blocker == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blocker.isTapped() && !blocker.hasKeyword("CARDNAME can block as though it were untapped.")) {
|
if (!nextTurn && blocker.isTapped() && !blocker.hasKeyword("CARDNAME can block as though it were untapped.")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -481,6 +495,22 @@ public class CombatUtil {
|
|||||||
|
|
||||||
return CombatUtil.canBlock(attacker, blocker);
|
return CombatUtil.canBlock(attacker, blocker);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// can the blocker block the attacker?
|
||||||
|
/**
|
||||||
|
* <p>
|
||||||
|
* canBlock.
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* @param attacker
|
||||||
|
* a {@link forge.Card} object.
|
||||||
|
* @param blocker
|
||||||
|
* a {@link forge.Card} object.
|
||||||
|
* @return a boolean.
|
||||||
|
*/
|
||||||
|
public static boolean canBlock(final Card attacker, final Card blocker) {
|
||||||
|
return canBlock(attacker, blocker, false);
|
||||||
|
}
|
||||||
|
|
||||||
// can the blocker block the attacker?
|
// can the blocker block the attacker?
|
||||||
/**
|
/**
|
||||||
@@ -494,12 +524,12 @@ public class CombatUtil {
|
|||||||
* a {@link forge.Card} object.
|
* a {@link forge.Card} object.
|
||||||
* @return a boolean.
|
* @return a boolean.
|
||||||
*/
|
*/
|
||||||
public static boolean canBlock(final Card attacker, final Card blocker) {
|
public static boolean canBlock(final Card attacker, final Card blocker, final boolean nextTurn) {
|
||||||
if ((attacker == null) || (blocker == null)) {
|
if ((attacker == null) || (blocker == null)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CombatUtil.canBlock(blocker)) {
|
if (!CombatUtil.canBlock(blocker, nextTurn)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!CombatUtil.canBeBlocked(attacker)) {
|
if (!CombatUtil.canBeBlocked(attacker)) {
|
||||||
|
|||||||
@@ -562,8 +562,8 @@ public class ComputerUtilAttack {
|
|||||||
// *******************
|
// *******************
|
||||||
|
|
||||||
int computerForces = 0;
|
int computerForces = 0;
|
||||||
int playerForces = 0;
|
int humanForces = 0;
|
||||||
int playerForcesForAttritionalAttack = 0;
|
int humanForcesForAttritionalAttack = 0;
|
||||||
|
|
||||||
// examine the potential forces
|
// examine the potential forces
|
||||||
final CardList nextTurnAttackers = new CardList();
|
final CardList nextTurnAttackers = new CardList();
|
||||||
@@ -578,13 +578,13 @@ public class ComputerUtilAttack {
|
|||||||
if (pCard.getNetCombatDamage() > 0) {
|
if (pCard.getNetCombatDamage() > 0) {
|
||||||
candidateCounterAttackDamage += pCard.getNetCombatDamage();
|
candidateCounterAttackDamage += pCard.getNetCombatDamage();
|
||||||
// candidateTotalBlockDamage += pCard.getNetCombatDamage();
|
// candidateTotalBlockDamage += pCard.getNetCombatDamage();
|
||||||
playerForces += 1; // player forces they might use to attack
|
humanForces += 1; // player forces they might use to attack
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// increment player forces that are relevant to an attritional
|
// increment player forces that are relevant to an attritional
|
||||||
// attack - includes walls
|
// attack - includes walls
|
||||||
if (CombatUtil.canBlock(pCard)) {
|
if (CombatUtil.canBlock(pCard, true)) {
|
||||||
playerForcesForAttritionalAttack += 1;
|
humanForcesForAttritionalAttack += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,10 +601,9 @@ public class ComputerUtilAttack {
|
|||||||
// if the creature can attack then it's a potential attacker this
|
// if the creature can attack then it's a potential attacker this
|
||||||
// turn, assume summoning sickness creatures will be able to
|
// turn, assume summoning sickness creatures will be able to
|
||||||
if (CombatUtil.canAttackNextTurn(pCard)) {
|
if (CombatUtil.canAttackNextTurn(pCard)) {
|
||||||
|
|
||||||
candidateAttackers.add(pCard);
|
candidateAttackers.add(pCard);
|
||||||
if (pCard.getNetCombatDamage() > 0) {
|
if (pCard.getNetCombatDamage() > 0) {
|
||||||
candidateUnblockedDamage += CombatUtil.damageIfUnblocked(pCard, AllZone.getHumanPlayer(), combat);
|
candidateUnblockedDamage += CombatUtil.damageIfUnblocked(pCard, AllZone.getHumanPlayer(), null);
|
||||||
computerForces += 1;
|
computerForces += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -612,9 +611,9 @@ public class ComputerUtilAttack {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// find the potential damage ratio the AI can cause
|
// find the potential damage ratio the AI can cause
|
||||||
double playerLifeToDamageRatio = 1000000;
|
double humanLifeToDamageRatio = 1000000;
|
||||||
if (candidateUnblockedDamage > 0) {
|
if (candidateUnblockedDamage > 0) {
|
||||||
playerLifeToDamageRatio = (double) AllZone.getHumanPlayer().getLife() / candidateUnblockedDamage;
|
humanLifeToDamageRatio = (double) AllZone.getHumanPlayer().getLife() / candidateUnblockedDamage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -625,10 +624,10 @@ public class ComputerUtilAttack {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// determine if the ai outnumbers the player
|
// determine if the ai outnumbers the player
|
||||||
final int outNumber = computerForces - playerForces;
|
final int outNumber = computerForces - humanForces;
|
||||||
|
|
||||||
// compare the ratios, higher = better for ai
|
// compare the ratios, higher = better for ai
|
||||||
final double ratioDiff = aiLifeToPlayerDamageRatio - playerLifeToDamageRatio;
|
final double ratioDiff = aiLifeToPlayerDamageRatio - humanLifeToDamageRatio;
|
||||||
/*
|
/*
|
||||||
* System.out.println(String.valueOf(ratioDiff) +
|
* System.out.println(String.valueOf(ratioDiff) +
|
||||||
* " = ratio difference, higher = better for ai");
|
* " = ratio difference, higher = better for ai");
|
||||||
@@ -649,31 +648,31 @@ public class ComputerUtilAttack {
|
|||||||
// get list of attackers ordered from low power to high
|
// get list of attackers ordered from low power to high
|
||||||
CardListUtil.sortAttackLowFirst(this.attackers);
|
CardListUtil.sortAttackLowFirst(this.attackers);
|
||||||
// get player life total
|
// get player life total
|
||||||
int playerLife = AllZone.getHumanPlayer().getLife();
|
int humanLife = AllZone.getHumanPlayer().getLife();
|
||||||
// get the list of attackers up to the first blocked one
|
// get the list of attackers up to the first blocked one
|
||||||
final CardList attritionalAttackers = new CardList();
|
final CardList attritionalAttackers = new CardList();
|
||||||
for (int x = 0; x < (this.attackers.size() - playerForces); x++) {
|
for (int x = 0; x < (this.attackers.size() - humanForces); x++) {
|
||||||
attritionalAttackers.add(this.attackers.getCard(x));
|
attritionalAttackers.add(this.attackers.getCard(x));
|
||||||
}
|
}
|
||||||
// until the attackers are used up or the player would run out of life
|
// until the attackers are used up or the player would run out of life
|
||||||
int attackRounds = 1;
|
int attackRounds = 1;
|
||||||
while ((attritionalAttackers.size() > 0) && (playerLife > 0) && (attackRounds < 99)) {
|
while (attritionalAttackers.size() > 0 && humanLife > 0 && attackRounds < 99) {
|
||||||
// sum attacker damage
|
// sum attacker damage
|
||||||
int damageThisRound = 0;
|
int damageThisRound = 0;
|
||||||
for (int y = 0; y < attritionalAttackers.size(); y++) {
|
for (int y = 0; y < attritionalAttackers.size(); y++) {
|
||||||
damageThisRound += attritionalAttackers.getCard(y).getNetCombatDamage();
|
damageThisRound += attritionalAttackers.getCard(y).getNetCombatDamage();
|
||||||
}
|
}
|
||||||
// remove from player life
|
// remove from player life
|
||||||
playerLife -= damageThisRound;
|
humanLife -= damageThisRound;
|
||||||
// shorten attacker list by the length of the blockers - assuming
|
// shorten attacker list by the length of the blockers - assuming
|
||||||
// all blocked are killed for convenience
|
// all blocked are killed for convenience
|
||||||
for (int z = 0; z < playerForcesForAttritionalAttack; z++) {
|
for (int z = 0; z < humanForcesForAttritionalAttack; z++) {
|
||||||
if (attritionalAttackers.size() > 0) {
|
if (attritionalAttackers.size() > 0) {
|
||||||
attritionalAttackers.remove(attritionalAttackers.size() - 1);
|
attritionalAttackers.remove(attritionalAttackers.size() - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
attackRounds += 1;
|
attackRounds += 1;
|
||||||
if (playerLife <= 0) {
|
if (humanLife <= 0) {
|
||||||
doAttritionalAttack = true;
|
doAttritionalAttack = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -686,6 +685,7 @@ public class ComputerUtilAttack {
|
|||||||
// see how long until unblockable attackers will be fatal
|
// see how long until unblockable attackers will be fatal
|
||||||
// *********************
|
// *********************
|
||||||
double unblockableDamage = 0;
|
double unblockableDamage = 0;
|
||||||
|
double nextUnblockableDamage = 0;
|
||||||
double turnsUntilDeathByUnblockable = 0;
|
double turnsUntilDeathByUnblockable = 0;
|
||||||
boolean doUnblockableAttack = false;
|
boolean doUnblockableAttack = false;
|
||||||
for (final Card attacker : this.attackers) {
|
for (final Card attacker : this.attackers) {
|
||||||
@@ -695,15 +695,31 @@ public class ComputerUtilAttack {
|
|||||||
for (final Card blocker : this.blockers) {
|
for (final Card blocker : this.blockers) {
|
||||||
if (CombatUtil.canBlock(attacker, blocker)) {
|
if (CombatUtil.canBlock(attacker, blocker)) {
|
||||||
isUnblockableCreature = false;
|
isUnblockableCreature = false;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isUnblockableCreature) {
|
if (isUnblockableCreature) {
|
||||||
unblockableDamage += CombatUtil.damageIfUnblocked(attacker, AllZone.getHumanPlayer(), combat);
|
unblockableDamage += CombatUtil.damageIfUnblocked(attacker, AllZone.getHumanPlayer(), combat);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (final Card attacker : nextTurnAttackers) {
|
||||||
|
boolean isUnblockableCreature = true;
|
||||||
|
// check blockers individually, as the bulk canBeBlocked doesn't
|
||||||
|
// check all circumstances
|
||||||
|
for (final Card blocker : this.computerList) {
|
||||||
|
if (CombatUtil.canBlock(attacker, blocker, true)) {
|
||||||
|
isUnblockableCreature = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isUnblockableCreature) {
|
||||||
|
nextUnblockableDamage += CombatUtil.damageIfUnblocked(attacker, AllZone.getHumanPlayer(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (unblockableDamage > 0 && !AllZone.getHumanPlayer().cantLoseForZeroOrLessLife()
|
if (unblockableDamage > 0 && !AllZone.getHumanPlayer().cantLoseForZeroOrLessLife()
|
||||||
&& AllZone.getHumanPlayer().canLoseLife()) {
|
&& AllZone.getHumanPlayer().canLoseLife()) {
|
||||||
turnsUntilDeathByUnblockable = AllZone.getHumanPlayer().getLife() / unblockableDamage;
|
turnsUntilDeathByUnblockable = 1+ (AllZone.getHumanPlayer().getLife() - unblockableDamage)
|
||||||
|
/ nextUnblockableDamage;
|
||||||
}
|
}
|
||||||
if (unblockableDamage > AllZone.getHumanPlayer().getLife() && AllZone.getHumanPlayer().canLoseLife()) {
|
if (unblockableDamage > AllZone.getHumanPlayer().getLife() && AllZone.getHumanPlayer().canLoseLife()) {
|
||||||
doUnblockableAttack = true;
|
doUnblockableAttack = true;
|
||||||
@@ -716,30 +732,26 @@ public class ComputerUtilAttack {
|
|||||||
// totals and other considerations
|
// totals and other considerations
|
||||||
// some bad "magic numbers" here, TODO replace with nice descriptive
|
// some bad "magic numbers" here, TODO replace with nice descriptive
|
||||||
// variable names
|
// variable names
|
||||||
if (((ratioDiff > 0) && doAttritionalAttack)) { // (playerLifeToDamageRatio
|
if (doAttritionalAttack) {
|
||||||
// <= 1 && ratioDiff >=
|
|
||||||
// 1
|
|
||||||
// && outNumber > 0) ||
|
|
||||||
this.aiAggression = 5; // attack at all costs
|
this.aiAggression = 5; // attack at all costs
|
||||||
} else if (((playerLifeToDamageRatio < 2) && (ratioDiff >= 0)) || (ratioDiff > 3)
|
} else if (ratioDiff >= 1 && (humanLifeToDamageRatio < 2 || outNumber > 0)) {
|
||||||
|| ((ratioDiff > 0) && (outNumber > 0))) {
|
this.aiAggression = 4; // attack expecting to trade or damage player.
|
||||||
this.aiAggression = 3; // attack expecting to kill creatures or
|
} else if ((humanLifeToDamageRatio < 2 && ratioDiff >= 0) || ratioDiff > 3
|
||||||
// damage
|
|| (ratioDiff > 0 && outNumber > 0)) {
|
||||||
// player.
|
this.aiAggression = 3; // attack expecting to make good trades or damage player.
|
||||||
} else if ((ratioDiff >= 0) || ((ratioDiff + outNumber) >= -1)) {
|
} else if (ratioDiff >= 0 || ratioDiff + outNumber >= -1) {
|
||||||
// at 0 ratio expect to potentially gain an advantage by attacking
|
// at 0 ratio expect to potentially gain an advantage by attacking first
|
||||||
// first
|
|
||||||
// if the ai has a slight advantage
|
// if the ai has a slight advantage
|
||||||
// or the ai has a significant advantage numerically but only a slight disadvantage damage/life
|
// or the ai has a significant advantage numerically but only a slight disadvantage damage/life
|
||||||
this.aiAggression = 2; // attack expecting to destroy creatures/be unblockable
|
this.aiAggression = 2; // attack expecting to destroy creatures/be unblockable
|
||||||
} else if ((ratioDiff < 0) && (aiLifeToPlayerDamageRatio > 1)) {
|
} else if (aiLifeToPlayerDamageRatio > 1) {
|
||||||
// the player is overmatched but there are a few turns before death
|
// the player is overmatched but there are a few turns before death
|
||||||
this.aiAggression = 2; // attack expecting to destroy creatures/be unblockable
|
this.aiAggression = 2; // attack expecting to destroy creatures/be unblockable
|
||||||
} else if (doUnblockableAttack || ((ratioDiff * -1) < turnsUntilDeathByUnblockable)) {
|
} else if (doUnblockableAttack || (ratioDiff * -1 < turnsUntilDeathByUnblockable)) {
|
||||||
this.aiAggression = 1;
|
this.aiAggression = 1;
|
||||||
// look for unblockable creatures that might be
|
// look for unblockable creatures that might be
|
||||||
// able to attack for a bit of fatal damage even if the player is significantly better
|
// able to attack for a bit of fatal damage even if the player is significantly better
|
||||||
} else if (ratioDiff < 0) {
|
} else {
|
||||||
this.aiAggression = 0;
|
this.aiAggression = 0;
|
||||||
} // stay at home to block
|
} // stay at home to block
|
||||||
System.out.println(String.valueOf(this.aiAggression) + " = ai aggression");
|
System.out.println(String.valueOf(this.aiAggression) + " = ai aggression");
|
||||||
@@ -757,7 +769,7 @@ public class ComputerUtilAttack {
|
|||||||
|
|
||||||
for (int i = 0; i < attackersLeft.size(); i++) {
|
for (int i = 0; i < attackersLeft.size(); i++) {
|
||||||
final Card attacker = attackersLeft.get(i);
|
final Card attacker = attackersLeft.get(i);
|
||||||
if ((this.aiAggression < 3) && !attacker.hasFirstStrike() && !attacker.hasDoubleStrike()
|
if (this.aiAggression < 5 && !attacker.hasFirstStrike() && !attacker.hasDoubleStrike()
|
||||||
&& CombatUtil.getTotalFirstStrikeBlockPower(attacker, AllZone.getHumanPlayer())
|
&& CombatUtil.getTotalFirstStrikeBlockPower(attacker, AllZone.getHumanPlayer())
|
||||||
>= attacker.getKillDamage()) {
|
>= attacker.getKillDamage()) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
Reference in New Issue
Block a user