Code that chooses cards to be discarded moved to PlayerController and ComputerUtil

This commit is contained in:
Maxmtg
2013-03-16 20:41:02 +00:00
parent d20b5f02f9
commit 3b0a3e6658
9 changed files with 121 additions and 175 deletions

View File

@@ -24,6 +24,7 @@ import java.util.List;
import com.google.common.base.Predicate; import com.google.common.base.Predicate;
import com.google.common.base.Predicates; import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables; import com.google.common.collect.Iterables;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
@@ -90,6 +91,8 @@ public class CardLists {
} }
}; };
public static final List<Card> emptyList = ImmutableList.of();
/** /**
* <p> * <p>
* Sorts a List<Card> by "best" using the EvaluateCreature function. * Sorts a List<Card> by "best" using the EvaluateCreature function.

View File

@@ -1,26 +1,18 @@
package forge.card.ability.effects; package forge.card.ability.effects;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import com.google.common.base.Predicate;
import forge.Card; import forge.Card;
import forge.CardLists; import forge.CardLists;
import forge.card.ability.AbilityUtils; import forge.card.ability.AbilityUtils;
import forge.card.cardfactory.CardFactoryUtil; import forge.card.cardfactory.CardFactoryUtil;
import forge.card.spellability.SpellAbility; import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target; import forge.card.spellability.Target;
import forge.game.ai.ComputerUtilCard;
import forge.game.player.AIPlayer;
import forge.game.player.Player; import forge.game.player.Player;
import forge.game.zone.ZoneType; import forge.game.zone.ZoneType;
import forge.gui.GuiChoose;
import forge.util.Aggregates;
public class DiscardEffect extends RevealEffectBase { public class DiscardEffect extends RevealEffectBase {
@Override @Override
@@ -87,44 +79,6 @@ public class DiscardEffect extends RevealEffectBase {
return sb.toString(); return sb.toString();
} // discardStackDescription() } // discardStackDescription()
/**
* TODO: Write javadoc for this method.
* @param sa
* @param opponentHand
* @param list
*/
private Card chooseCardToDiscardFromOpponent(SpellAbility sa, List<Card> opponentHand) {
List<Card> goodChoices = CardLists.filter(opponentHand, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
if (!c.getSVar("DiscardMeByOpp").equals("") || !c.getSVar("DiscardMe").equals("")) {
return false;
}
return true;
}
});
if (goodChoices.isEmpty()) {
goodChoices = opponentHand;
}
final List<Card> dChoices = new ArrayList<Card>();
if (sa.hasParam("DiscardValid")) {
final String validString = sa.getParam("DiscardValid");
if (validString.contains("Creature") && !validString.contains("nonCreature")) {
final Card c = ComputerUtilCard.getBestCreatureAI(goodChoices);
if (c != null) {
dChoices.add(ComputerUtilCard.getBestCreatureAI(goodChoices));
}
}
}
Collections.sort(goodChoices, CardLists.TextLenComparator);
CardLists.sortByCmcDesc(goodChoices);
dChoices.add(goodChoices.get(0));
return Aggregates.random(goodChoices);
}
@Override @Override
public void resolve(SpellAbility sa) { public void resolve(SpellAbility sa) {
final Card source = sa.getSourceCard(); final Card source = sa.getSourceCard();
@@ -194,11 +148,7 @@ public class DiscardEffect extends RevealEffectBase {
// Reveal // Reveal
final List<Card> dPHand = p.getCardsIn(ZoneType.Hand); final List<Card> dPHand = p.getCardsIn(ZoneType.Hand);
if (p.isHuman()) { p.getOpponent().getController().reveal("Reveal " + p + " hand" , dPHand, ZoneType.Hand, p);
// "reveal to computer" for information gathering
} else {
GuiChoose.oneOrNone("Revealed computer hand", dPHand);
}
String valid = sa.hasParam("DiscardValid") ? sa.getParam("DiscardValid") : "Card"; String valid = sa.hasParam("DiscardValid") ? sa.getParam("DiscardValid") : "Card";
@@ -215,7 +165,7 @@ public class DiscardEffect extends RevealEffectBase {
} else if (mode.equals("RevealYouChoose") || mode.equals("RevealOppChoose") || mode.equals("TgtChoose")) { } else if (mode.equals("RevealYouChoose") || mode.equals("RevealOppChoose") || mode.equals("TgtChoose")) {
// Is Reveal you choose right? I think the wrong player is // Is Reveal you choose right? I think the wrong player is
// being used? // being used?
List<Card> dPHand = new ArrayList<Card>(p.getCardsIn(ZoneType.Hand)); List<Card> dPHand = p.getCardsIn(ZoneType.Hand);
if (dPHand.isEmpty()) if (dPHand.isEmpty())
continue; // for loop over players continue; // for loop over players
@@ -224,11 +174,9 @@ public class DiscardEffect extends RevealEffectBase {
int amount = StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) : CardFactoryUtil.xCount(source, source.getSVar(amountString)); int amount = StringUtils.isNumeric(amountString) ? Integer.parseInt(amountString) : CardFactoryUtil.xCount(source, source.getSVar(amountString));
dPHand = getRevealedList(p, dPHand, amount, false); dPHand = getRevealedList(p, dPHand, amount, false);
} }
List<Card> dPChHand = new ArrayList<Card>(dPHand);
final String valid = sa.hasParam("DiscardValid") ? sa.getParam("DiscardValid") : "Card"; final String valid = sa.hasParam("DiscardValid") ? sa.getParam("DiscardValid") : "Card";
String[] dValid = ArrayUtils.EMPTY_STRING_ARRAY; String[] dValid = valid.split(",");
dValid = valid.split(","); List<Card> validCards = CardLists.getValidCards(dPHand, dValid, source.getController(), source);
dPChHand = CardLists.getValidCards(dPHand, dValid, source.getController(), source);
Player chooser = p; Player chooser = p;
if (mode.equals("RevealYouChoose")) { if (mode.equals("RevealYouChoose")) {
@@ -237,60 +185,16 @@ public class DiscardEffect extends RevealEffectBase {
chooser = source.getController().getOpponent(); chooser = source.getController().getOpponent();
} }
List<Card> toBeDiscarded = new ArrayList<Card>(); if (mode.startsWith("Reveal") && p != chooser)
if (chooser.isComputer()) { chooser.getController().reveal("Revealed " + p + " hand", dPHand, ZoneType.Hand, p);
List<Card> dPChHand1 = new ArrayList<Card>(p.getCardsIn(ZoneType.Hand));
dPChHand1 = CardLists.getValidCards(dPChHand1, dValid, source.getController(), source);
int max = Math.min(dPChHand1.size(), numCards); int minDiscardAmount = sa.hasParam("AnyNumber") || sa.hasParam("Optional") ? 0 : numCards;
List<Card> list = new ArrayList<Card>(); int max = Math.min(validCards.size(), minDiscardAmount);
if (!p.isOpponentOf(chooser) && p instanceof AIPlayer) { // discard AI cards List<Card> toBeDiscarded = validCards.isEmpty() ? CardLists.emptyList : chooser.getController().chooseCardsToDiscardFrom(p, sa, validCards, max);
toBeDiscarded = ((AIPlayer) p).getAi().getCardsToDiscard(max, dValid, sa);
} else {
// discard hostile or human opponent
for (int i = 0; i < max; i++) {
Card dC = chooseCardToDiscardFromOpponent(sa, dPChHand1);
dPChHand1.remove(dC);
toBeDiscarded.add(dC);
}
}
if (mode.startsWith("Reveal") ) { if (mode.startsWith("Reveal") ) {
GuiChoose.oneOrNone("Computer has chosen", list); p.getController().reveal(chooser + " has chosen", toBeDiscarded, ZoneType.Hand, p);
}
} else {
// human
if (mode.startsWith("Reveal")) {
GuiChoose.oneOrNone("Revealed " + p + " hand", dPHand);
}
if (sa.hasParam("AnyNumber")) {
List<Card> chosen = getDiscardedList(p, dPChHand);
for (Card c : chosen) {
dPChHand.remove(c);
toBeDiscarded.add(c);
}
} else
for (int i = 0; i < numCards; i++) {
if (dPChHand.isEmpty()) {
break;
}
Card dC = null;
if (sa.hasParam("Optional")) {
dC = GuiChoose.oneOrNone("Choose a card to be discarded", dPChHand);
} else {
dC = GuiChoose.one("Choose a card to be discarded", dPChHand);
}
if (dC != null) {
dPChHand.remove(dC);
toBeDiscarded.add(dC);
}
else break;
}
} }
if (toBeDiscarded != null) { if (toBeDiscarded != null) {
@@ -311,34 +215,4 @@ public class DiscardEffect extends RevealEffectBase {
} }
} // discardResolve() } // discardResolve()
public static List<Card> getDiscardedList(final Player player, final List<Card> valid) {
final List<Card> chosen = new ArrayList<Card>();
final int validamount = Math.min(valid.size(), valid.size());
if (player.isHuman() && validamount > 0) {
final List<Card> selection = GuiChoose.order("Choose Which Cards to Discard", "Discarded", -1, valid, null, null);
for (final Object o : selection) {
if (o != null && o instanceof Card) {
chosen.add((Card) o);
}
}
} else {
for (int i = 0; i < validamount; i++) {
if (player.isHuman()) {
final Card o = GuiChoose.one("Choose card(s) to discard", valid);
if (o != null) {
chosen.add(o);
valid.remove(o);
} else {
break;
}
} else { // Computer
chosen.add(valid.get(0));
valid.remove(valid.get(0));
}
}
}
return chosen;
}
} }

View File

@@ -473,11 +473,9 @@ public class CardFactorySorceries {
int s = h.size(); int s = h.size();
min = Math.min(min, s); min = Math.min(min, s);
} }
Iterator<List<Card>> hh = hands.iterator();
for (Player p : Singletons.getModel().getGame().getPlayers()) {
List<Card> h = hh.next(); for (Player p : Singletons.getModel().getGame().getPlayers()) {
int sac = h.size() - min; int sac = p.getCardsIn(ZoneType.Hand).size() - min;
if (sac == 0) { if (sac == 0) {
continue; continue;
} }

View File

@@ -525,16 +525,21 @@ public class AiController {
*/ */
public List<Card> getCardsToDiscard(final int numDiscard, final String[] uTypes, final SpellAbility sa) { public List<Card> getCardsToDiscard(final int numDiscard, final String[] uTypes, final SpellAbility sa) {
List<Card> hand = new ArrayList<Card>(player.getCardsIn(ZoneType.Hand)); List<Card> hand = new ArrayList<Card>(player.getCardsIn(ZoneType.Hand));
Card sourceCard = null;
if ((uTypes != null) && (sa != null)) { if ((uTypes != null) && (sa != null)) {
hand = CardLists.getValidCards(hand, uTypes, sa.getActivatingPlayer(), sa.getSourceCard()); hand = CardLists.getValidCards(hand, uTypes, sa.getActivatingPlayer(), sa.getSourceCard());
} }
return getCardsToDiscard(numDiscard, hand, sa);
}
if (hand.size() < numDiscard) { public List<Card> getCardsToDiscard(final int numDiscard, final List<Card> validCards, final SpellAbility sa) {
if (validCards.size() < numDiscard) {
return null; return null;
} }
Card sourceCard = null;
final List<Card> discardList = new ArrayList<Card>(); final List<Card> discardList = new ArrayList<Card>();
int count = 0; int count = 0;
if (sa != null) { if (sa != null) {
@@ -545,7 +550,7 @@ public class AiController {
while (count < numDiscard) { while (count < numDiscard) {
Card prefCard = null; Card prefCard = null;
if (sa != null && sa.getActivatingPlayer() != null && sa.getActivatingPlayer().isOpponentOf(player)) { if (sa != null && sa.getActivatingPlayer() != null && sa.getActivatingPlayer().isOpponentOf(player)) {
for (Card c : hand) { for (Card c : validCards) {
if (c.hasKeyword("If a spell or ability an opponent controls causes you to discard CARDNAME," if (c.hasKeyword("If a spell or ability an opponent controls causes you to discard CARDNAME,"
+ " put it onto the battlefield instead of putting it into your graveyard.")) { + " put it onto the battlefield instead of putting it into your graveyard.")) {
prefCard = c; prefCard = c;
@@ -554,11 +559,11 @@ public class AiController {
} }
} }
if (prefCard == null) { if (prefCard == null) {
prefCard = ComputerUtil.getCardPreference(player, sourceCard, "DiscardCost", hand); prefCard = ComputerUtil.getCardPreference(player, sourceCard, "DiscardCost", validCards);
} }
if (prefCard != null) { if (prefCard != null) {
discardList.add(prefCard); discardList.add(prefCard);
hand.remove(prefCard); validCards.remove(prefCard);
count++; count++;
} else { } else {
break; break;
@@ -569,11 +574,11 @@ public class AiController {
// choose rest // choose rest
for (int i = 0; i < discardsLeft; i++) { for (int i = 0; i < discardsLeft; i++) {
if (hand.isEmpty()) { if (validCards.isEmpty()) {
continue; continue;
} }
final int numLandsInPlay = Iterables.size(Iterables.filter(player.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS)); final int numLandsInPlay = Iterables.size(Iterables.filter(player.getCardsIn(ZoneType.Battlefield), CardPredicates.Presets.LANDS));
final List<Card> landsInHand = CardLists.filter(hand, CardPredicates.Presets.LANDS); final List<Card> landsInHand = CardLists.filter(validCards, CardPredicates.Presets.LANDS);
final int numLandsInHand = landsInHand.size(); final int numLandsInHand = landsInHand.size();
// Discard a land // Discard a land
@@ -582,21 +587,21 @@ public class AiController {
if (canDiscardLands) { if (canDiscardLands) {
discardList.add(landsInHand.get(0)); discardList.add(landsInHand.get(0));
hand.remove(landsInHand.get(0)); validCards.remove(landsInHand.get(0));
} else { // Discard other stuff } else { // Discard other stuff
CardLists.sortByCmcDesc(hand); CardLists.sortByCmcDesc(validCards);
int numLandsAvailable = numLandsInPlay; int numLandsAvailable = numLandsInPlay;
if (numLandsInHand > 0) { if (numLandsInHand > 0) {
numLandsAvailable++; numLandsAvailable++;
} }
//Discard unplayable card //Discard unplayable card
if (hand.get(0).getCMC() > numLandsAvailable) { if (validCards.get(0).getCMC() > numLandsAvailable) {
discardList.add(hand.get(0)); discardList.add(validCards.get(0));
hand.remove(hand.get(0)); validCards.remove(validCards.get(0));
} else { //Discard worst card } else { //Discard worst card
Card worst = ComputerUtilCard.getWorstAI(hand); Card worst = ComputerUtilCard.getWorstAI(validCards);
discardList.add(worst); discardList.add(worst);
hand.remove(worst); validCards.remove(worst);
} }
} }
} }

View File

@@ -1001,21 +1001,7 @@ public class ComputerUtil {
for (final Card c : all) { for (final Card c : all) {
for (final SpellAbility sa : c.getSpellAbility()) { for (final SpellAbility sa : c.getSpellAbility()) {
if (sa.getApi() == ApiType.Pump && sa.hasParam("KW") && sa.getParam("KW").contains("Haste")) {
if (sa.getApi() == null) {
continue;
}
/// ????
// if ( sa.isAbility() || sa.isSpell() && sa.getApi() != ApiType.Pump ) continue
if (sa.hasParam("AB") && !sa.getParam("AB").equals("Pump")) {
continue;
}
if (sa.hasParam("SP") && !sa.getParam("SP").equals("Pump")) {
continue;
}
if (sa.hasParam("KW") && sa.getParam("KW").contains("Haste")) {
return true; return true;
} }
} }
@@ -1239,4 +1225,62 @@ public class ComputerUtil {
} }
return bottom; return bottom;
} }
/**
* TODO: Write javadoc for this method.
* @param chooser
* @param discarder
* @param sa
* @param validCards
* @param min
* @return
*/
public static List<Card> getCardsToDiscardFromOpponent(AIPlayer chooser, Player discarder, SpellAbility sa, List<Card> validCards, int min) {
List<Card> goodChoices = CardLists.filter(validCards, new Predicate<Card>() {
@Override
public boolean apply(final Card c) {
if (!c.getSVar("DiscardMeByOpp").equals("") || !c.getSVar("DiscardMe").equals("")) {
return false;
}
return true;
}
});
if (goodChoices.isEmpty()) {
goodChoices = validCards;
}
final List<Card> dChoices = new ArrayList<Card>();
if (sa.hasParam("DiscardValid")) {
final String validString = sa.getParam("DiscardValid");
if (validString.contains("Creature") && !validString.contains("nonCreature")) {
final Card c = ComputerUtilCard.getBestCreatureAI(goodChoices);
if (c != null) {
dChoices.add(ComputerUtilCard.getBestCreatureAI(goodChoices));
}
}
}
Collections.sort(goodChoices, CardLists.TextLenComparator);
CardLists.sortByCmcDesc(goodChoices);
dChoices.add(goodChoices.get(0));
return Aggregates.random(goodChoices, min);
}
/**
* TODO: Write javadoc for this method.
* @param aiChoser
* @param p
* @param sa
* @param validCards
* @param min
* @return
*/
public static List<Card> getCardsToDiscardFromFriend(AIPlayer aiChooser, Player p, SpellAbility sa, List<Card> validCards, int min) {
if (p instanceof AIPlayer) { // ask that ai player what he would like to discard
return ((AIPlayer) p).getAi().getCardsToDiscard(min, validCards, sa);
}
// no special options for human or remote friends
return getCardsToDiscardFromOpponent(aiChooser, p, sa, validCards, min);
}
} }

View File

@@ -111,7 +111,7 @@ public class AIPlayer extends Player {
public final void discard(final int num, final SpellAbility sa) { public final void discard(final int num, final SpellAbility sa) {
int max = this.getCardsIn(ZoneType.Hand).size(); int max = this.getCardsIn(ZoneType.Hand).size();
max = Math.min(max, num); max = Math.min(max, num);
final List<Card> toDiscard = this.getAi().getCardsToDiscard(max, null, sa); final List<Card> toDiscard = this.getAi().getCardsToDiscard(max, (String[])null, sa);
for (int i = 0; i < toDiscard.size(); i++) { for (int i = 0; i < toDiscard.size(); i++) {
this.doDiscard(toDiscard.get(i), sa); this.doDiscard(toDiscard.get(i), sa);
} }
@@ -125,8 +125,7 @@ public class AIPlayer extends Player {
if (tHand.size() > 0) { if (tHand.size() > 0) {
Card toDiscard = Aggregates.itemWithMin(tHand, CardPredicates.Accessors.fnGetCmc); Card toDiscard = Aggregates.itemWithMin(tHand, CardPredicates.Accessors.fnGetCmc);
toDiscard.getController().discard(toDiscard, sa); // this got changed discard(toDiscard, sa); // this got changed to doDiscard basically
// to doDiscard basically
return; return;
} }
this.discard(num, sa); this.discard(num, sa);

View File

@@ -108,4 +108,7 @@ public abstract class PlayerController {
public abstract void reveal(String string, List<Card> cards, ZoneType zone, Player owner); public abstract void reveal(String string, List<Card> cards, ZoneType zone, Player owner);
public abstract ImmutablePair<List<Card>, List<Card>> arrangeForScry(List<Card> topN); public abstract ImmutablePair<List<Card>, List<Card>> arrangeForScry(List<Card> topN);
public abstract boolean willPutCardOnTop(Card c); public abstract boolean willPutCardOnTop(Card c);
/** p = target player, validCards - possible discards, min cards to discard */
public abstract List<Card> chooseCardsToDiscardFrom(Player p, SpellAbility sa, List<Card> validCards, int min);
} }

View File

@@ -255,4 +255,16 @@ public class PlayerControllerAi extends PlayerController {
return true; // AI does not know what will happen next (another clash or that would become his topdeck) return true; // AI does not know what will happen next (another clash or that would become his topdeck)
} }
/* (non-Javadoc)
* @see forge.game.player.PlayerController#chooseCardsToDiscardFrom(forge.game.player.Player, java.util.List, int)
*/
@Override
public List<Card> chooseCardsToDiscardFrom(Player p, SpellAbility sa, List<Card> validCards, int min) {
boolean isTargetFriendly = !p.isOpponentOf(getPlayer());
return isTargetFriendly
? ComputerUtil.getCardsToDiscardFromFriend(player, p, sa, validCards, min)
: ComputerUtil.getCardsToDiscardFromOpponent(player, p, sa, validCards, min);
}
} }

View File

@@ -318,4 +318,12 @@ public class PlayerControllerHuman extends PlayerController {
public boolean willPutCardOnTop(Card c) { public boolean willPutCardOnTop(Card c) {
return GuiDialog.confirm(c, "Where will you put " + c.getName() + " in your library", new String[]{"Top", "Bottom"} ); return GuiDialog.confirm(c, "Where will you put " + c.getName() + " in your library", new String[]{"Top", "Bottom"} );
} }
/* (non-Javadoc)
* @see forge.game.player.PlayerController#chooseCardsToDiscardFrom(forge.game.player.Player, java.util.List, int)
*/
@Override
public List<Card> chooseCardsToDiscardFrom(Player p, SpellAbility sa, List<Card> valid, int minDiscard) {
return GuiChoose.order("Choose cards to Discard", "Discarded", minDiscard == 0 ? -1 : minDiscard, valid, null, null);
}
} }