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.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
@@ -89,6 +90,8 @@ public class CardLists {
return aLen - bLen;
}
};
public static final List<Card> emptyList = ImmutableList.of();
/**
* <p>

View File

@@ -1,26 +1,18 @@
package forge.card.ability.effects;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import com.google.common.base.Predicate;
import forge.Card;
import forge.CardLists;
import forge.card.ability.AbilityUtils;
import forge.card.cardfactory.CardFactoryUtil;
import forge.card.spellability.SpellAbility;
import forge.card.spellability.Target;
import forge.game.ai.ComputerUtilCard;
import forge.game.player.AIPlayer;
import forge.game.player.Player;
import forge.game.zone.ZoneType;
import forge.gui.GuiChoose;
import forge.util.Aggregates;
public class DiscardEffect extends RevealEffectBase {
@Override
@@ -87,44 +79,6 @@ public class DiscardEffect extends RevealEffectBase {
return sb.toString();
} // 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
public void resolve(SpellAbility sa) {
final Card source = sa.getSourceCard();
@@ -194,11 +148,7 @@ public class DiscardEffect extends RevealEffectBase {
// Reveal
final List<Card> dPHand = p.getCardsIn(ZoneType.Hand);
if (p.isHuman()) {
// "reveal to computer" for information gathering
} else {
GuiChoose.oneOrNone("Revealed computer hand", dPHand);
}
p.getOpponent().getController().reveal("Reveal " + p + " hand" , dPHand, ZoneType.Hand, p);
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")) {
// Is Reveal you choose right? I think the wrong player is
// being used?
List<Card> dPHand = new ArrayList<Card>(p.getCardsIn(ZoneType.Hand));
List<Card> dPHand = p.getCardsIn(ZoneType.Hand);
if (dPHand.isEmpty())
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));
dPHand = getRevealedList(p, dPHand, amount, false);
}
List<Card> dPChHand = new ArrayList<Card>(dPHand);
final String valid = sa.hasParam("DiscardValid") ? sa.getParam("DiscardValid") : "Card";
String[] dValid = ArrayUtils.EMPTY_STRING_ARRAY;
dValid = valid.split(",");
dPChHand = CardLists.getValidCards(dPHand, dValid, source.getController(), source);
String[] dValid = valid.split(",");
List<Card> validCards = CardLists.getValidCards(dPHand, dValid, source.getController(), source);
Player chooser = p;
if (mode.equals("RevealYouChoose")) {
@@ -237,60 +185,16 @@ public class DiscardEffect extends RevealEffectBase {
chooser = source.getController().getOpponent();
}
List<Card> toBeDiscarded = new ArrayList<Card>();
if (chooser.isComputer()) {
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);
List<Card> list = new ArrayList<Card>();
if (!p.isOpponentOf(chooser) && p instanceof AIPlayer) { // discard AI cards
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")) {
GuiChoose.oneOrNone("Computer has chosen", list);
}
if (mode.startsWith("Reveal") && p != chooser)
chooser.getController().reveal("Revealed " + p + " hand", dPHand, ZoneType.Hand, p);
int minDiscardAmount = sa.hasParam("AnyNumber") || sa.hasParam("Optional") ? 0 : numCards;
int max = Math.min(validCards.size(), minDiscardAmount);
} else {
// human
if (mode.startsWith("Reveal")) {
GuiChoose.oneOrNone("Revealed " + p + " hand", dPHand);
}
List<Card> toBeDiscarded = validCards.isEmpty() ? CardLists.emptyList : chooser.getController().chooseCardsToDiscardFrom(p, sa, validCards, max);
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 (mode.startsWith("Reveal") ) {
p.getController().reveal(chooser + " has chosen", toBeDiscarded, ZoneType.Hand, p);
}
if (toBeDiscarded != null) {
@@ -311,34 +215,4 @@ public class DiscardEffect extends RevealEffectBase {
}
} // 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();
min = Math.min(min, s);
}
Iterator<List<Card>> hh = hands.iterator();
for (Player p : Singletons.getModel().getGame().getPlayers()) {
List<Card> h = hh.next();
int sac = h.size() - min;
for (Player p : Singletons.getModel().getGame().getPlayers()) {
int sac = p.getCardsIn(ZoneType.Hand).size() - min;
if (sac == 0) {
continue;
}

View File

@@ -525,16 +525,21 @@ public class AiController {
*/
public List<Card> getCardsToDiscard(final int numDiscard, final String[] uTypes, final SpellAbility sa) {
List<Card> hand = new ArrayList<Card>(player.getCardsIn(ZoneType.Hand));
Card sourceCard = null;
if ((uTypes != null) && (sa != null)) {
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;
}
Card sourceCard = null;
final List<Card> discardList = new ArrayList<Card>();
int count = 0;
if (sa != null) {
@@ -545,7 +550,7 @@ public class AiController {
while (count < numDiscard) {
Card prefCard = null;
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,"
+ " put it onto the battlefield instead of putting it into your graveyard.")) {
prefCard = c;
@@ -554,11 +559,11 @@ public class AiController {
}
}
if (prefCard == null) {
prefCard = ComputerUtil.getCardPreference(player, sourceCard, "DiscardCost", hand);
prefCard = ComputerUtil.getCardPreference(player, sourceCard, "DiscardCost", validCards);
}
if (prefCard != null) {
discardList.add(prefCard);
hand.remove(prefCard);
validCards.remove(prefCard);
count++;
} else {
break;
@@ -569,11 +574,11 @@ public class AiController {
// choose rest
for (int i = 0; i < discardsLeft; i++) {
if (hand.isEmpty()) {
if (validCards.isEmpty()) {
continue;
}
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();
// Discard a land
@@ -582,21 +587,21 @@ public class AiController {
if (canDiscardLands) {
discardList.add(landsInHand.get(0));
hand.remove(landsInHand.get(0));
validCards.remove(landsInHand.get(0));
} else { // Discard other stuff
CardLists.sortByCmcDesc(hand);
CardLists.sortByCmcDesc(validCards);
int numLandsAvailable = numLandsInPlay;
if (numLandsInHand > 0) {
numLandsAvailable++;
}
//Discard unplayable card
if (hand.get(0).getCMC() > numLandsAvailable) {
discardList.add(hand.get(0));
hand.remove(hand.get(0));
if (validCards.get(0).getCMC() > numLandsAvailable) {
discardList.add(validCards.get(0));
validCards.remove(validCards.get(0));
} else { //Discard worst card
Card worst = ComputerUtilCard.getWorstAI(hand);
Card worst = ComputerUtilCard.getWorstAI(validCards);
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 SpellAbility sa : c.getSpellAbility()) {
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")) {
if (sa.getApi() == ApiType.Pump && sa.hasParam("KW") && sa.getParam("KW").contains("Haste")) {
return true;
}
}
@@ -1239,4 +1225,62 @@ public class ComputerUtil {
}
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) {
int max = this.getCardsIn(ZoneType.Hand).size();
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++) {
this.doDiscard(toDiscard.get(i), sa);
}
@@ -125,8 +125,7 @@ public class AIPlayer extends Player {
if (tHand.size() > 0) {
Card toDiscard = Aggregates.itemWithMin(tHand, CardPredicates.Accessors.fnGetCmc);
toDiscard.getController().discard(toDiscard, sa); // this got changed
// to doDiscard basically
discard(toDiscard, sa); // this got changed to doDiscard basically
return;
}
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 ImmutablePair<List<Card>, List<Card>> arrangeForScry(List<Card> topN);
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)
}
/* (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) {
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);
}
}