package forge; import com.esotericsoftware.minlog.Log; import forge.card.cardFactory.CardFactoryUtil; import forge.card.spellability.SpellAbility; import forge.card.spellability.Spell_Permanent; import java.util.ArrayList; import static forge.error.ErrorViewer.showError; /** * <p>ComputerAI_General class.</p> * * @author Forge * @version $Id: $ */ public class ComputerAI_General implements Computer { /** * <p>Constructor for ComputerAI_General.</p> */ public ComputerAI_General() { } /** * <p>main1.</p> */ public void main1() { ComputerUtil.chooseLandsToPlay(); if (AllZone.getStack().size() == 0) playCards(Constant.Phase.Main1); else stackResponse(); }//main1() /** * <p>main2.</p> */ public void main2() { ComputerUtil.chooseLandsToPlay(); if (AllZone.getStack().size() == 0) playCards(Constant.Phase.Main2); else stackResponse(); } /** * <p>playCards.</p> * * @param phase a {@link java.lang.String} object. */ private void playCards(final String phase) { SpellAbility[] sp = phase.equals(Constant.Phase.Main1) ? getMain1() : getMain2(); boolean nextPhase = ComputerUtil.playCards(sp); if (nextPhase) { AllZone.getPhase().passPriority(); } }//playCards() /** * <p>getMain1.</p> * * @return an array of {@link forge.card.spellability.SpellAbility} objects. */ private SpellAbility[] getMain1() { //Card list of all cards to consider CardList hand = AllZoneUtil.getPlayerHand(AllZone.getComputerPlayer()); if (AllZone.getComputerManaPool().isEmpty()) hand = hand.filter(new CardListFilter() { public boolean addCard(Card c) { if (c.getSVar("PlayMain1").equals("TRUE")) return true; if (c.isSorcery()) //timing should be handled by the AF's return true; if (c.isCreature() && (c.hasKeyword("Haste")) || c.hasKeyword("Exalted")) return true; CardList buffed = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); //get all cards the computer controls with BuffedBy for (int j = 0; j < buffed.size(); j++) { Card buffedcard = buffed.get(j); if (buffedcard.getSVar("BuffedBy").length() > 0) { String buffedby = buffedcard.getSVar("BuffedBy"); String bffdby[] = buffedby.split(","); if (c.isValidCard(bffdby, c.getController(), c)) return true; } }//BuffedBy CardList antibuffed = AllZoneUtil.getPlayerCardsInPlay(AllZone.getHumanPlayer()); //get all cards the human controls with AntiBuffedBy for (int k = 0; k < antibuffed.size(); k++) { Card buffedcard = antibuffed.get(k); if (buffedcard.getSVar("AntiBuffedBy").length() > 0) { String buffedby = buffedcard.getSVar("AntiBuffedBy"); String bffdby[] = buffedby.split(","); if (c.isValidCard(bffdby, c.getController(), c)) return true; } }//AntiBuffedBy if (c.isLand()) return false; CardList vengevines = AllZoneUtil.getPlayerGraveyard(AllZone.getComputerPlayer(), "Vengevine"); if (vengevines.size() > 0) { CardList creatures = AllZoneUtil.getPlayerHand(AllZone.getComputerPlayer()); CardList creatures2 = new CardList(); for (int i = 0; i < creatures.size(); i++) { if (creatures.get(i).isCreature() && CardUtil.getConvertedManaCost(creatures.get(i).getManaCost()) <= 3) { creatures2.add(creatures.get(i)); } } if (creatures2.size() + Phase.getComputerCreatureSpellCount() > 1 && c.isCreature() && CardUtil.getConvertedManaCost(c.getManaCost()) <= 3) return true; } // AI Improvement for Vengevine // Beached As End return false; } }); CardList all = AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer()); all.addAll(hand); CardList humanPlayable = AllZoneUtil.getPlayerCardsInPlay(AllZone.getHumanPlayer()); humanPlayable = humanPlayable.filter(new CardListFilter() { public boolean addCard(Card c) { return (c.canAnyPlayerActivate()); } }); all.addAll(humanPlayable); return getPlayable(all); }//getMain1() /** * <p>getMain2.</p> * * @return an array of {@link forge.card.spellability.SpellAbility} objects. */ private SpellAbility[] getMain2() { //Card list of all cards to consider CardList all = AllZoneUtil.getPlayerHand(AllZone.getComputerPlayer()); //Don't play permanents with Flash before humans declare attackers step all = all.filter(new CardListFilter() { public boolean addCard(Card c) { if (c.isPermanent() && c.hasKeyword("Flash") && (AllZone.getPhase().isPlayerTurn(AllZone.getComputerPlayer()) || AllZone.getPhase().isBefore(Constant.Phase.Combat_Declare_Attackers_InstantAbility))) return false; return true; } }); all.addAll(AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer())); all.addAll(CardFactoryUtil.getExternalZoneActivationCards(AllZone.getComputerPlayer())); // Prevent the computer from summoning Ball Lightning type creatures during main phase 2 all = all.getNotKeyword("At the beginning of the end step, sacrifice CARDNAME."); all = all.filter(new CardListFilter() { public boolean addCard(Card c) { if (c.isLand()) return false; return true; } }); CardList humanPlayable = AllZoneUtil.getPlayerCardsInPlay(AllZone.getHumanPlayer()); humanPlayable = humanPlayable.filter(new CardListFilter() { public boolean addCard(Card c) { return (c.canAnyPlayerActivate()); } }); all.addAll(humanPlayable); return getPlayable(all); }//getMain2() /** * <p>getAvailableSpellAbilities.</p> * * @return a {@link forge.CardList} object. */ private CardList getAvailableSpellAbilities() { CardList all = AllZoneUtil.getPlayerHand(AllZone.getComputerPlayer()); //Don't play permanents with Flash before humans declare attackers step all = all.filter(new CardListFilter() { public boolean addCard(Card c) { if (c.isPermanent() && c.hasKeyword("Flash") && (AllZone.getPhase().isPlayerTurn(AllZone.getComputerPlayer()) || AllZone.getPhase().isBefore(Constant.Phase.Combat_Declare_Attackers_InstantAbility))) return false; return true; } }); all.addAll(AllZoneUtil.getPlayerCardsInPlay(AllZone.getComputerPlayer())); all.addAll(CardFactoryUtil.getExternalZoneActivationCards(AllZone.getComputerPlayer())); CardList humanPlayable = AllZoneUtil.getPlayerCardsInPlay(AllZone.getHumanPlayer()); humanPlayable = humanPlayable.filter(new CardListFilter() { public boolean addCard(Card c) { return (c.canAnyPlayerActivate()); } }); all.addAll(humanPlayable); return all; } /** * <p>getOtherPhases.</p> * * @return an array of {@link forge.card.spellability.SpellAbility} objects. */ private SpellAbility[] getOtherPhases() { return getPlayable(getAvailableSpellAbilities()); } /** * <p>getPossibleCounters.</p> * * @return a {@link java.util.ArrayList} object. */ private ArrayList<SpellAbility> getPossibleCounters() { return getPlayableCounters(getAvailableSpellAbilities()); } /** * <p>getPossibleETBCounters.</p> * * @return a {@link java.util.ArrayList} object. */ private ArrayList<SpellAbility> getPossibleETBCounters() { return getETBCounters(getAvailableSpellAbilities()); } /** * Returns the spellAbilities from the card list that the computer is able to play * * @param l a {@link forge.CardList} object. * @return an array of {@link forge.card.spellability.SpellAbility} objects. */ private SpellAbility[] getPlayable(CardList l) { ArrayList<SpellAbility> spellAbility = new ArrayList<SpellAbility>(); for (Card c : l) for (SpellAbility sa : c.getSpellAbility()) // if SA is from AF_Counter don't add to getPlayable //This try/catch should fix the "computer is thinking" bug try { sa.setActivatingPlayer(AllZone.getComputerPlayer()); if (ComputerUtil.canBePlayedAndPayedByAI(sa)) { spellAbility.add(sa); } } catch (Exception ex) { showError(ex, "There is an error in the card code for %s:%n", c.getName(), ex.getMessage()); } return spellAbility.toArray(new SpellAbility[spellAbility.size()]); } /** * <p>getPlayableCounters.</p> * * @param l a {@link forge.CardList} object. * @return a {@link java.util.ArrayList} object. */ private ArrayList<SpellAbility> getPlayableCounters(CardList l) { ArrayList<SpellAbility> spellAbility = new ArrayList<SpellAbility>(); for (Card c : l) { for (SpellAbility sa : c.getSpellAbility()) { // Check if this AF is a Counterpsell if (sa.getAbilityFactory() != null && sa.getAbilityFactory().getAPI().equals("Counter")) spellAbility.add(sa); } } return spellAbility; } /** * <p>getETBCounters.</p> * * @param l a {@link forge.CardList} object. * @return a {@link java.util.ArrayList} object. */ private ArrayList<SpellAbility> getETBCounters(CardList l) { ArrayList<SpellAbility> spellAbility = new ArrayList<SpellAbility>(); for (Card c : l) { for (SpellAbility sa : c.getSpellAbility()) { // Or if this Permanent has an ETB ability with Counter if (sa instanceof Spell_Permanent) { if (Spell_Permanent.checkETBEffects(c, sa, "Counter")) spellAbility.add(sa); } } } return spellAbility; } /** * <p>begin_combat.</p> */ public void begin_combat() { stackResponse(); } /** * <p>declare_attackers.</p> */ public void declare_attackers() { // 12/2/10(sol) the decision making here has moved to getAttackers() AllZone.setCombat(ComputerUtil.getAttackers()); Card[] att = AllZone.getCombat().getAttackers(); if (att.length > 0) AllZone.getPhase().setCombat(true); for (int i = 0; i < att.length; i++) { // tapping of attackers happens after Propaganda is paid for //if (!att[i].hasKeyword("Vigilance")) att[i].tap(); Log.debug("Computer just assigned " + att[i].getName() + " as an attacker."); } AllZone.getComputerBattlefield().updateObservers(); CombatUtil.showCombat(); AllZone.getPhase().setNeedToNextPhase(true); } /** * <p>declare_attackers_after.</p> */ public void declare_attackers_after() { stackResponse(); } /** * <p>declare_blockers.</p> */ public void declare_blockers() { CardList blockers = AllZoneUtil.getCreaturesInPlay(AllZone.getComputerPlayer()); AllZone.setCombat(ComputerUtil_Block2.getBlockers(AllZone.getCombat(), blockers)); CombatUtil.showCombat(); AllZone.getPhase().setNeedToNextPhase(true); } /** * <p>declare_blockers_after.</p> */ public void declare_blockers_after() { stackResponse(); } /** * <p>end_of_combat.</p> */ public void end_of_combat() { stackResponse(); } //end of Human's turn /** * <p>end_of_turn.</p> */ public void end_of_turn() { stackResponse(); } /** * <p>stack_not_empty.</p> */ public void stack_not_empty() { stackResponse(); } /** * <p>stackResponse.</p> */ public void stackResponse() { // if top of stack is empty SpellAbility[] sas = null; if (AllZone.getStack().size() == 0) { sas = getOtherPhases(); boolean pass = (sas.length == 0) || AllZone.getPhase().is(Constant.Phase.Upkeep, AllZone.getComputerPlayer()) || AllZone.getPhase().is(Constant.Phase.Draw, AllZone.getComputerPlayer()) || AllZone.getPhase().is(Constant.Phase.End_Of_Turn, AllZone.getComputerPlayer()); if (!pass) { // Each AF should check the phase individually pass = ComputerUtil.playCards(sas); } if (pass) AllZone.getPhase().passPriority(); return; } // if top of stack is owned by me if (AllZone.getStack().peekInstance().getActivatingPlayer().isComputer()) { // probably should let my stuff resolve to force Human to respond to it AllZone.getPhase().passPriority(); return; } // top of stack is owned by human, ArrayList<SpellAbility> possibleCounters = getPossibleCounters(); if (possibleCounters.size() > 0 && ComputerUtil.playCounterSpell(possibleCounters)) { // Responding CounterSpell is on the Stack trying to Counter the Spell // If playCounterSpell returns true, a Spell is hitting the Stack return; } possibleCounters.clear(); possibleCounters = getPossibleETBCounters(); if (possibleCounters.size() > 0 && !ComputerUtil.playCards(possibleCounters)) { // Responding Permanent w/ ETB Counter is on the Stack // AllZone.getPhase().passPriority(); return; } sas = getOtherPhases(); if (sas.length > 0) { // Spell not Countered if (!ComputerUtil.playCards(sas)) return; } // if this hasn't been covered above, just PassPriority() AllZone.getPhase().passPriority(); } }