From 4b07c8a920ab3c87beb50bc84bb5af907469e18b Mon Sep 17 00:00:00 2001 From: jendave Date: Sat, 6 Aug 2011 04:33:50 +0000 Subject: [PATCH] Made a number of AI combat improvements: - Computer should take into account creatures with trample now, when blocking. - Computer will not care about human attackers the next turn quite as much, hopefully this will prevent the AI from stopping to attack altogether if it's low on life. --- src/forge/CardListUtil.java | 13 ++ src/forge/ComputerUtil_Attack2.java | 6 +- src/forge/ComputerUtil_Block2.java | 190 ++++++++++++++++++---------- 3 files changed, 143 insertions(+), 66 deletions(-) diff --git a/src/forge/CardListUtil.java b/src/forge/CardListUtil.java index d16eac73400..a02b69b803f 100644 --- a/src/forge/CardListUtil.java +++ b/src/forge/CardListUtil.java @@ -271,6 +271,19 @@ public class CardListUtil return attack; }//sumAttack() + public static int sumDefense(CardList c) + { + int defense = 0; + + for(int i = 0; i < c.size(); i++){ + //if(c.get(i).isCreature() && c.get(i).hasSecondStrike()) { + if(c.get(i).isCreature() ) + defense += c.get(i).getNetDefense(); + } + //System.out.println("Total attack: " +attack); + return defense; + }//sumAttack() + public static int sumFirstStrikeAttack(CardList c) { int attack = 0; diff --git a/src/forge/ComputerUtil_Attack2.java b/src/forge/ComputerUtil_Attack2.java index 130e2574118..dfd454e4ee2 100644 --- a/src/forge/ComputerUtil_Attack2.java +++ b/src/forge/ComputerUtil_Attack2.java @@ -138,8 +138,10 @@ import java.util.*; for(int i = 0; i < stop; i++) totalAttack += getAttack(humanList.get(i)); - //-3 so the computer will try to stay at 3 life - return (AllZone.Computer_Life.getLife() - 3) <= totalAttack; + //originally -3 so the computer will try to stay at 3 life + //+1 now to prevent the AI from not attacking when it's got low life + //(seems to happen too often) + return (AllZone.Computer_Life.getLife() + 1) <= totalAttack; } private boolean doAssault() diff --git a/src/forge/ComputerUtil_Block2.java b/src/forge/ComputerUtil_Block2.java index 886ddd55d54..8e2ada9a91b 100644 --- a/src/forge/ComputerUtil_Block2.java +++ b/src/forge/ComputerUtil_Block2.java @@ -74,6 +74,16 @@ public class ComputerUtil_Block2 return list; } + + private CardList getPossibleBlockersWithFirstStrike(Card attacker) + { + CardList list = new CardList(); + for(int i = 0; i < possibleBlockers.size(); i++) + if(CombatUtil.canBlock(attacker, possibleBlockers.get(i)) && (attacker.hasFirstStrike() || attacker.hasDoubleStrike())) + list.add(possibleBlockers.get(i)); + + return list; + } //finds a blocker that destroys the attacker, the blocker is not destroyed //returns null if no blocker is found @@ -125,28 +135,60 @@ public class ComputerUtil_Block2 //returns an array of size 0 if not multiple blocking Card[] multipleBlock(Card attacker) { - CardList c = getPossibleBlockers(attacker); + CardList c; + if (attacker.hasDoubleStrike() || attacker.hasFirstStrike()) + c = getPossibleBlockersWithFirstStrike(attacker); + else + c = getPossibleBlockers(attacker); int defense = getDefense(attacker) - attacker.getDamage(); //if attacker cannot be destroyed if(defense > CardListUtil.sumAttack(c)) - return new Card[] {}; + return new Card[] {}; CardList block = new CardList(); c.shuffle(); while(defense > CardListUtil.sumAttack(block)) - block.add(c.remove(0)); + block.add(c.remove(0)); Card check[] = block.toArray(); //no single blockers, that should be handled somewhere else if(check.length == 1) - return new Card[] {}; + return new Card[] {}; return check; }//multipleBlock() + Card[] multipleTrampleBlock(Card attacker) + { + CardList c; + if (attacker.hasDoubleStrike() || attacker.hasFirstStrike()) + c = getPossibleBlockersWithFirstStrike(attacker); + else + c = getPossibleBlockers(attacker); + + int attack = getAttack(attacker); + //if computer can't save himself: + if(attack > (CardListUtil.sumDefense(c) + AllZone.GameAction.getPlayerLife(Constant.Player.Computer).getLife() )) + return new Card[] {}; + + CardList block = new CardList(); + c.shuffle(); + + while(attack >= (CardListUtil.sumDefense(block) + AllZone.GameAction.getPlayerLife(Constant.Player.Computer).getLife() ) + && c.size() != 0) + block.add(c.remove(0)); + + Card check[] = block.toArray(); + + //no single blockers, that should be handled somewhere else + if(check.length == 1) + return new Card[] {}; + + return check; + } //finds a blocker, both the attacker lives and blockers dies //returns null if no blocker is found @@ -219,6 +261,7 @@ public class ComputerUtil_Block2 { boolean doubleStrike = attackers.get(i).hasDoubleStrike(); + boolean trample = attackers.get(i).getKeyword().contains("Trample"); //the computer blocks 50% of the time or if the computer would lose the game shouldBlock = random.nextBoolean() || blockersLife <= sumUnblockedAttackers(combat); @@ -234,68 +277,87 @@ public class ComputerUtil_Block2 Random random = new Random(); int randomInt = random.nextInt(100); - if (randomInt >= 10) - { - c = safeSingleBlock(attackers.get(i)); + boolean multiTrample = false; + //multiblocks for trample + if (trample && AllZone.Computer_Life.getLife() <= getAttack(attackers.get(i)) ) + { + Card[] m = multipleTrampleBlock(attackers.get(i)); + for(int inner = 0; inner < m.length; inner++) + { + if(m.length != 1) + { + possibleBlockers.remove(m[inner]); + combat.addBlocker(attackers.get(i), m[inner]); + multiTrample = true; + } + }//for + } + + if (!multiTrample) + { + if (randomInt >= 10) + { + c = safeSingleBlock(attackers.get(i)); + if(c != null) + testing("safe"); + } + if(c == null && randomInt >= 15) + { + //shield block - attacker lives, blocker lives + c = shieldSingleBlock(attackers.get(i)); + if(c != null) + testing("shield"); + } + + if(c == null && shouldBlock) + { + //trade block - attacker dies, blocker dies + c = tradeSingleBlock(attackers.get(i)); + + if(c != null) + testing("trading"); + } + + + if(c == null && shouldBlock) + { + //chump block - attacker lives, blocker dies + c = chumpSingleBlock(attackers.get(i)); + if(c != null) + testing("chumping"); + } + + if(c == null && doubleStrike && AllZone.Computer_Life.getLife() <= (getAttack(attackers.get(i))*2)) + { + c = forceBlock(attackers.get(i)); + if (c != null) + testing("forcing"); + } + if(c != null) - testing("safe"); + { + + //if (!c.getKeyword().contains("This card can block any number of creatures.")) + possibleBlockers.remove(c); + combat.addBlocker(attackers.get(i), c); + } + + //multiple blockers + if(c == null && shouldBlock) + { + Card[] m = multipleBlock(attackers.get(i)); + for(int inner = 0; inner < m.length; inner++) + { + //to prevent a single flyer from blocking a single non-flyer + //tradeSingleBlock() checks for a flyer blocking a non-flyer also + if(m.length != 1) + { + possibleBlockers.remove(m[inner]); + combat.addBlocker(attackers.get(i), m[inner]); + } + }//for + }//if } - if(c == null && randomInt >= 15) - { - //shield block - attacker lives, blocker lives - c = shieldSingleBlock(attackers.get(i)); - if(c != null) - testing("shield"); - } - - if(c == null && shouldBlock) - { - //trade block - attacker dies, blocker dies - c = tradeSingleBlock(attackers.get(i)); - - if(c != null) - testing("trading"); - } - - - if(c == null && shouldBlock) - { - //chump block - attacker lives, blocker dies - c = chumpSingleBlock(attackers.get(i)); - if(c != null) - testing("chumping"); - } - - if(c == null && doubleStrike && AllZone.Computer_Life.getLife() <= (getAttack(attackers.get(i))*2)) - { - c = forceBlock(attackers.get(i)); - if (c != null) - testing("forcing"); - } - - if(c != null) - { - - //if (!c.getKeyword().contains("This card can block any number of creatures.")) - possibleBlockers.remove(c); - combat.addBlocker(attackers.get(i), c); - } - - //multiple blockers - if(c == null && shouldBlock) - { - Card[] m = multipleBlock(attackers.get(i)); - for(int inner = 0; inner < m.length; inner++) - { - //to prevent a single flyer from blocking a single non-flyer - //tradeSingleBlock() checks for a flyer blocking a non-flyer also - if(m.length != 1) - { - possibleBlockers.remove(m[inner]); - combat.addBlocker(attackers.get(i), m[inner]); - } - }//for - }//if }//for attackers return combat;