package com.vdom.core;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import com.vdom.api.ActionCard;
import com.vdom.api.Card;
import com.vdom.api.CurseCard;
import com.vdom.api.DurationCard;
import com.vdom.api.GameEvent;
import com.vdom.api.TreasureCard;
import com.vdom.api.VictoryCard;
public abstract class Player {
Random rand = new Random(System.currentTimeMillis());
public static final String RANDOM_AI = "Random AI";
public static final String DISTINCT_CARDS = "Distinct Cards";
public static final String VICTORY_TOKENS = "Victory Tokens";
// Only used by InteractivePlayer currently
private String name;
public int playerNumber;
public int shuffleCount = 0;
protected int turnCount = 0;
public int vps;
public boolean win = false;
public int pirateShipTreasure;
// The number of coin tokens held by the player
private int guildsCoinTokenCount;
private Card checkLeadCard;
private int victoryTokens;
protected CardList hand;
protected CardList deck;
protected CardList discard;
protected CardList playedCards;
protected CardList nextTurnCards;
protected CardList nativeVillage;
protected CardList island;
protected CardList haven;
protected CardList horseTraders;
public Game game;
public Player controlPlayer = this;
public boolean isPossessed() {
return !this.equals(controlPlayer);
}
public boolean achievementSingleCardFailed;
public Card achievementSingleCardFirstKingdomCardBought;
public void addVictoryTokens(MoveContext context, int vt) {
victoryTokens += vt;
context.vpsGainedThisTurn += vt;
}
public int getTotalCardsBoughtThisTurn(MoveContext context) {
return context.getTotalCardsBoughtThisTurn();
}
public boolean isAi() {
return true;
}
public void setName(String name) {
this.name = name.replace("_", " ");
}
public int getCurrencyTotal(MoveContext context) {
return getMyCardCount(Cards.copper) + getMyCardCount(Cards.silver) * 2 + getMyCardCount(Cards.gold) * 3 + getMyCardCount(Cards.platinum) * 5;
}
public int getMyCardCount(Card card) {
return Util.getCardCount(getAllCards(), card);
}
public int getTurnCount() {
return turnCount;
}
public void newTurn() {
turnCount++;
}
public ArrayList<Card> getActionCards(Card[] cards) {
ArrayList<Card> actionCards = new ArrayList<Card>();
for (Card card : cards) {
if (card instanceof ActionCardImpl) {
actionCards.add(card);
}
}
return actionCards;
}
public int getActionCardCount(Card[] cards) {
return getActionCards(cards).size();
}
public int getMyAddActionCardCount() {
int addActionsCards = 0;
for (Card card : getAllCards()) {
if (card instanceof ActionCard) {
if (((ActionCard) card).getAddActions() > 0) {
addActionsCards++;
}
}
}
return addActionsCards;
}
public int getMyAddCardCardCount() {
int addCards = 0;
for (Card card : getAllCards()) {
if (card instanceof ActionCard) {
if (((ActionCard) card).getAddActions() > 0) {
addCards++;
}
}
}
return addCards;
}
public int getMyAddActions() {
int addActions = 0;
for (Card card : getAllCards()) {
if (card instanceof ActionCard) {
addActions += ((ActionCard) card).getAddActions();
}
}
return addActions;
}
public int getMyAddCards() {
int addCards = 0;
for (Card card : getAllCards()) {
if (card instanceof ActionCard) {
addCards += ((ActionCard) card).getAddCards();
}
}
return addCards;
}
public int getMyAddBuys() {
int addBuys = 0;
for (Card card : getAllCards()) {
if (card instanceof ActionCard) {
addBuys += ((ActionCard) card).getAddBuys();
}
}
return addBuys;
}
public boolean inHand(Card card) {
for (Card thisCard : hand) {
if (thisCard.equals(card)) {
return true;
}
}
return false;
}
public int mineableCards(Card[] hand) {
int mineableCards = 0;
for (Card card : hand) {
if (card.equals(Cards.potion) || card.equals(Cards.loan) || card.equals(Cards.copper) || card.equals(Cards.silver) || card.equals(Cards.gold)) {
mineableCards++;
}
}
return mineableCards;
}
public int inHandCount(Card card) {
return Util.getCardCount(getHand(), card);
}
public Card fromHand(Card card) {
for (Card thisCard : getHand()) {
if (thisCard.equals(card)) {
return thisCard;
}
}
return null;
}
public boolean getWin() {
return win;
}
public void initCards() {
hand = new CardList(this, "Hand");
deck = new CardList(this, "Deck");
discard = new CardList(this, "Discard");
playedCards = new CardList(this, "InPlay");
nextTurnCards = new CardList(this, "Duration");
nativeVillage = new CardList(this, "Native Village");
island = new CardList(this, "Island");
haven = new CardList(this, "Haven");
horseTraders = new CardList(this, "Horse Traders");
}
private List<PutBackOption> getPutBackOptions(MoveContext context) {
// Determine if criteria were met for certain action cards
boolean victoryBought = context.getVictoryCardsBoughtThisTurn() > 0;
boolean potionPlayed = context.countCardsInPlay(Cards.potion) > 0;
boolean treasurePlayed = context.countTreasureCardsInPlayThisTurn() > 0;
int actionsPlayed = context.countActionCardsInPlayThisTurn();
List<PutBackOption> options = new ArrayList<PutBackOption>();
for (Card c: playedCards) {
if (c.behaveAsCard().equals(Cards.treasury) && !victoryBought) {
options.add(PutBackOption.Treasury);
} else if (c.behaveAsCard().equals(Cards.alchemist) && potionPlayed) {
options.add(PutBackOption.Alchemist);
} else if (c.behaveAsCard().equals(Cards.walledVillage) && actionsPlayed == 1) {
options.add(PutBackOption.WalledVillage);
} else if (c.behaveAsCard().equals(Cards.herbalist) && treasurePlayed) {
options.add(PutBackOption.Coin);
}
}
if (actionsPlayed > 0) {
for (int i = 0; i < context.schemesPlayed; i++) {
options.add(PutBackOption.Action);
}
}
return options;
}
private Card findCard(MoveContext context, Card template) {
for (Card c: playedCards) {
if (c.behaveAsCard().equals(template)) {
return c;
}
}
return null;
}
protected void cleanup(MoveContext context) {
// /////////////////////////////////
// Discard played cards
// /////////////////////////////////
// reset any lingering CloneCounts
for (Card card : playedCards) {
CardImpl actualCard = (CardImpl) card;
actualCard.cloneCount = 1;
}
// Check for return-to-deck options
List<PutBackOption> putBackOptions;
ArrayList<Card> putBackCards = new ArrayList<Card>();
while (!(putBackOptions = controlPlayer.getPutBackOptions(context)).isEmpty()) {
PutBackOption putBackOption = controlPlayer.selectPutBackOption(context, putBackOptions);
if (putBackOption == PutBackOption.None || (isPossessed() && controlPlayer.isAi())) {
break;
} else {
if (putBackOption == PutBackOption.Treasury) {
Card treasury = findCard(context, Cards.treasury);
playedCards.remove(treasury);
putBackCards.add(treasury);
} else if (putBackOption == PutBackOption.Alchemist) {
Card alchemist = findCard(context, Cards.alchemist);
playedCards.remove(alchemist);
putBackCards.add(alchemist);
} else if (putBackOption == PutBackOption.WalledVillage) {
Card walledVillage = findCard(context, Cards.walledVillage);
playedCards.remove(walledVillage);
putBackCards.add(walledVillage);
} else if (putBackOption == PutBackOption.Coin) {
Card herbalist = findCard(context, Cards.herbalist);
playedCards.remove(herbalist);
discard(herbalist, null, null, false);
ArrayList<TreasureCard> treasureCards = new ArrayList<TreasureCard>();
for(Card card : playedCards) {
if(card instanceof TreasureCard) {
treasureCards.add((TreasureCard) card);
}
}
if(treasureCards.size() > 0) {
TreasureCard treasureCard = controlPlayer.herbalist_backOnDeck(context, treasureCards.toArray(new TreasureCard[0]));
if(treasureCard != null && playedCards.contains(treasureCard)) {
playedCards.remove(treasureCard);
putBackCards.add(treasureCard);
}
}
} else if (putBackOption == PutBackOption.Action) {
context.schemesPlayed --;
ArrayList<Card> actions = new ArrayList<Card>();
for(Card c : playedCards) {
if(c instanceof ActionCard) {
actions.add(c);
}
}
if(actions.size() == 0) {
break;
}
ActionCard actionToPutBack = controlPlayer.scheme_actionToPutOnTopOfDeck(((MoveContext) context), actions.toArray(new ActionCard[0]));
if(actionToPutBack == null) {
break;
}
int index = playedCards.indexOf((Card) actionToPutBack);
if(index == -1) {
Util.playerError(this, "Scheme returned invalid card to put back on top of deck, ignoring");
break;
}
Card card = playedCards.remove(index);
if (card.behaveAsCard().equals(Cards.hermit) &&
(context != null) &&
(context.totalCardsBoughtThisTurn == 0)) {
controlPlayer.gainNewCard(Cards.madman, card, context);
}
putBackCards.add(card);
}
}
}
if (!putBackCards.isEmpty()) {
// reset any lingering Impersonations
for (Card card : putBackCards) {
((CardImpl) card).stopImpersonatingCard();
}
if (putBackCards.size() == 1) {
putOnTopOfDeck(putBackCards.get(0), context, true);
} else {
Card[] orderedCards = controlPlayer.topOfDeck_orderCards(context, putBackCards.toArray(new Card[0]));
for (int i = orderedCards.length - 1; i >= 0; i--) {
Card card = orderedCards[i];
putOnTopOfDeck(card, context, true);
}
}
}
while (!playedCards.isEmpty()) {
discard(playedCards.remove(0), null, context, false);
}
if (isPossessed()) {
while (!game.possessedTrashPile.isEmpty()) {
discard(game.possessedTrashPile.remove(0), null, null, false);
}
game.possessedBoughtPile.clear();
}
// /////////////////////////////////
// Discard hand
// /////////////////////////////////
while (getHand().size() > 0) {
discard(hand.remove(0, false), null, null, false);
}
// /////////////////////////////////
// Double check that deck/discard/hand all have valid cards.
// /////////////////////////////////
checkCardsValid();
}
public void debug(String msg) {
Util.debug(this, msg, false);
}
public CardList getHand() {
return hand;
}
public CardList getDiscard() {
return discard;
}
public int getDeckSize() {
return deck.size();
}
public int getDiscardSize() {
return discard.size();
}
public CardList getNativeVillage() {
return nativeVillage;
}
public CardList getIsland() {
return island;
}
public int getPirateShipTreasure() {
return pirateShipTreasure;
}
public int getGuildsCoinTokenCount()
{
return guildsCoinTokenCount;
}
public void gainGuildsCoinTokens(int tokenCount)
{
guildsCoinTokenCount += tokenCount;
}
public void spendGuildsCoinTokens(int tokenCount)
{
if (tokenCount <= guildsCoinTokenCount)
{
guildsCoinTokenCount -= tokenCount;
}
else
{
Util.playerError(this, "spendGuildsCoinTokens() - Can't spend " + tokenCount + " coin tokens, only have " + guildsCoinTokenCount);
}
}
public int getVictoryTokens() {
return victoryTokens;
}
public int getAllCardCount() {
return this.getAllCards().size();
}
public ArrayList<Card> getAllCards() {
ArrayList<Card> allCards = new ArrayList<Card>();
for (Card card : playedCards) {
allCards.add(card);
}
for (Card card : hand) {
allCards.add(card);
}
for (Card card : discard) {
allCards.add(card);
}
for (Card card : deck) {
allCards.add(card);
}
for (Card card : nextTurnCards) {
allCards.add(card);
}
for (Card card : nativeVillage) {
allCards.add(card);
}
for (Card card : haven) {
allCards.add(card);
}
for (Card card : island) {
allCards.add(card);
}
for (Card card : horseTraders) {
allCards.add(card);
}
if (checkLeadCard != null) {
allCards.add(checkLeadCard);
}
return allCards;
}
public Map<Object, Integer> getVictoryCardCounts() {
final HashSet<String> distinctCards = new HashSet<String>();
final Map<Object, Integer> cardCounts = new HashMap<Object, Integer>();
// seed counts with all victory cards in play
for (AbstractCardPile pile : this.game.piles.values()) {
Card card = pile.card();
if(card instanceof VictoryCard || card instanceof CurseCard) {
cardCounts.put(card, 0);
}
}
for(Card card : this.getAllCards()) {
distinctCards.add(card.getName());
if (card instanceof VictoryCard || card instanceof CurseCard) {
if(cardCounts.containsKey(card)) {
cardCounts.put(card, cardCounts.get(card) + 1);
} else {
cardCounts.put(card, 1);
}
}
}
cardCounts.put(DISTINCT_CARDS, distinctCards.size());
return cardCounts;
}
public Map<Object, Integer> getAllCardCounts() {
final HashSet<String> distinctCards = new HashSet<String>();
final Map<Object, Integer> cardCounts = new HashMap<Object, Integer>();
for(Card card : this.getAllCards()) {
distinctCards.add(card.getName());
if(cardCounts.containsKey(card)) {
cardCounts.put(card, cardCounts.get(card) + 1);
} else {
cardCounts.put(card, 1);
}
}
cardCounts.put(DISTINCT_CARDS, distinctCards.size());
return cardCounts;
}
public int getCardCount(final Class<?> cardClass) {
return this.getCardCount(cardClass, getAllCards());
}
public int getCardCount(final Class<?> cardClass, ArrayList<Card> cards) {
int cardCount = 0;
for (Card card : cards) {
if (cardClass.isInstance(card)) {
cardCount++;
}
}
return cardCount;
}
public int getActionCardCount() {
return this.getCardCount(ActionCard.class);
}
public int getActionCardCount(ArrayList<Card> cards) {
return this.getCardCount(ActionCard.class, cards);
}
public int getVictoryCardCount() {
return this.getCardCount(VictoryCard.class);
}
public int getDistinctCardCount() {
return getDistinctCardCount(null);
}
public int getDistinctCardCount(ArrayList<Card> cards) {
if (cards==null) cards = this.getAllCards();
// int cardCount = 0;
final HashSet<String> distinctCards = new HashSet<String>();
for(Card card : cards) {
distinctCards.add(card.getName());
}
return distinctCards.size();
}
public int calculateLead(Card card) {
checkLeadCard = card;
int lead = getVPs();
checkLeadCard = null;
return lead;
}
public int getVPs() {
return getVPs(null);
}
public int getVPs(Map<Card, Integer> totals) {
if (totals==null) totals = this.getVictoryPointTotals();
int vp = 0;
for(Integer total : totals.values())
vp += total;
return vp;
}
public Map<Card, Integer> getVictoryPointTotals() {
return getVictoryPointTotals(null);
}
public Map<Card, Integer> getVictoryPointTotals(Map<Object, Integer> counts) {
if (counts == null) counts = this.getVictoryCardCounts();
Map<Card, Integer> totals = new HashMap<Card, Integer>();
for(Map.Entry<Object, Integer> entry : counts.entrySet()) {
if(entry.getKey() instanceof VictoryCard) {
VictoryCard victoryCard = (VictoryCard) entry.getKey();
totals.put(victoryCard, victoryCard.getVictoryPoints() * entry.getValue());
} else if(entry.getKey() instanceof CurseCard) {
CurseCard curseCard = (CurseCard) entry.getKey();
totals.put(curseCard, curseCard.getVictoryPoints() * entry.getValue());
}
}
if(counts.containsKey(Cards.gardens))
totals.put(Cards.gardens, counts.get(Cards.gardens) * (this.getAllCards().size() / 10));
if(counts.containsKey(Cards.duke))
totals.put(Cards.duke, counts.get(Cards.duke) * counts.get(Cards.duchy));
if(counts.containsKey(Cards.fairgrounds))
totals.put(Cards.fairgrounds, counts.get(Cards.fairgrounds) * ((counts.get(DISTINCT_CARDS) / 5) * 2));
if(counts.containsKey(Cards.vineyard))
totals.put(Cards.vineyard, counts.get(Cards.vineyard) * (this.getActionCardCount() / 3));
if(counts.containsKey(Cards.silkRoad))
totals.put(Cards.silkRoad, counts.get(Cards.silkRoad) * (this.getVictoryCardCount() / 4));
if(counts.containsKey(Cards.feodum))
totals.put(Cards.feodum, counts.get(Cards.feodum) * (Util.getCardCount(getAllCards(), Cards.silver) / 3));
totals.put(Cards.victoryTokens, this.getVictoryTokens());
return totals;
}
public Card peekAtDeckBottom() {
return deck.get(deck.size() - 1);
}
public void removeFromDeckBottom() {
deck.remove(deck.size() - 1);
}
public void putOnTopOfDeck(Card card, MoveContext context, boolean UI) {
putOnTopOfDeck(card);
if (UI) {
GameEvent event = new GameEvent(GameEvent.Type.CardOnTopOfDeck, context);
event.card = card;
game.broadcastEvent(event);
}
}
public void putOnTopOfDeck(Card card) {
deck.add(0, card);
}
public void replenishDeck() {
shuffleCount++;
while (discard.size() > 0) {
deck.add(discard.remove(Game.rand.nextInt(discard.size())));
}
}
public void shuffleDeck() {
ArrayList<Card> tempDeck = new ArrayList<Card>();
while (deck.size() > 0) {
tempDeck.add(deck.remove(Game.rand.nextInt(deck.size())));
}
for(Card c : tempDeck) {
deck.add(c);
}
}
public void checkCardsValid() {
hand.checkValid();
discard.checkValid();
deck.checkValid();
}
// protected void discardRemainingCardsFromHand(MoveContext context, Card[] cardsToKeep) {
// discardRemainingCardsFromHand(context, cardsToKeep, null, -1);
// }
protected void discardRemainingCardsFromHand(MoveContext context, Card[] cardsToKeep, Card responsibleCard, int keepCardCount) {
ArrayList<Card> keepCards = new ArrayList<Card>(Arrays.asList(cardsToKeep));
if (keepCardCount > 0) {
boolean bad = false;
if (cardsToKeep == null || cardsToKeep.length != keepCardCount) {
bad = true;
} else {
ArrayList<Card> handCopy = Util.copy(hand);
for (Card cardToKeep : cardsToKeep) {
if (!handCopy.remove(cardToKeep)) {
bad = true;
break;
}
}
}
if (bad) {
Util.playerError(this, responsibleCard.getName() + " discard error, just keeping first " + keepCardCount);
cardsToKeep = new Card[keepCardCount];
for (int i = 0; i < keepCardCount; i++) {
cardsToKeep[i] = hand.get(i);
}
}
}
// Discard remaining cards
for (int i = hand.size(); i > 0; ) {
Card card = hand.get(--i);
if (keepCards.contains(card)) {
keepCards.remove(card);
} else {
hand.remove(i, false);
discard(card, responsibleCard, context);
}
}
}
public void discard(Card card, Card responsible, MoveContext context) {
discard(card, responsible, context, true);
}
// TODO make similar way to put cards back on the deck (remove as well?)
public void discard(Card card, Card responsible, MoveContext context, boolean commandedDiscard) { // See rules explanation of Tunnel for what commandedDiscard means.
if(commandedDiscard && card.equals(Cards.tunnel)) {
MoveContext tunnelContext = new MoveContext(game, this);
if(game.pileSize(Cards.gold) > 0 && controlPlayer.tunnel_shouldReveal(tunnelContext)) {
reveal(card, card, tunnelContext);
gainNewCard(Cards.gold, card, tunnelContext);
}
}
if (card.behaveAsCard().equals(Cards.hermit)) {
if (!commandedDiscard &&
(context != null) &&
(context.totalCardsBoughtThisTurn == 0))
{
trash(card, card, context);
controlPlayer.gainNewCard(Cards.madman, card, context);
}
else
{
discard.add(card);
}
}
else
{
discard.add(card);
}
// card left play - stop impersonations
((CardImpl) card).stopImpersonatingCard();
// XXX making game slow; is this necessary? For that matter, are discarded cards public?
if(context != null && commandedDiscard) {
GameEvent event = new GameEvent(GameEvent.Type.CardDiscarded, context);
event.card = card;
event.responsible = responsible;
event.setPlayer(this);
context.game.broadcastEvent(event);
}
}
public boolean gainNewCard(Card cardToGain, Card responsible, MoveContext context) {
Card card = game.takeFromPileCheckTrader(cardToGain, context);
if (card != null) {
GameEvent gainEvent = new GameEvent(GameEvent.Type.CardObtained, (MoveContext) context);
gainEvent.card = card;
gainEvent.responsible = responsible;
gainEvent.newCard = true;
// Check if Trader swapped the card, so it can be made responsible, putting the card in the discard
// pile rather than were it would go otherwise (according to faq)
if(!cardToGain.equals(card) && card.equals(Cards.silver)) {
gainEvent.responsible = Cards.trader;
}
context.game.broadcastEvent(gainEvent);
// invoke different actions on gain
//cardToGain.isGained(context);
return true;
}
return false;
}
public boolean gainNewCardFromPile(AbstractCardPile pile, Card responsible, MoveContext context) {
switch (pile.type) {
case SingleCardPile:
return gainNewCard(pile.card(), responsible, context);
case RuinsPile:
return gainNewCard(game.getTopRuinsCard(), responsible, context);
case KnightsPile:
return gainNewCard(game.getTopKnightCard(), responsible, context);
default:
break;
}
return false;
}
public void gainCardAlreadyInPlay(Card card, Card responsible, MoveContext context) {
if (context != null) {
GameEvent event = new GameEvent(GameEvent.Type.CardObtained, context);
event.card = card;
event.responsible = responsible;
event.newCard = false;
context.game.broadcastEvent(event);
}
}
public void broadcastEvent(GameEvent event) {
game.broadcastEvent(event);
}
public Card takeFromPile(Card card) {
return game.takeFromPile(card);
}
public void trash(Card card, Card responsible, MoveContext context) {
if(context != null) {
// TODO: Track in main game event listener instead
context.cardsTrashedThisTurn++;
}
GameEvent event = new GameEvent(GameEvent.Type.CardTrashed, context);
event.card = card;
event.responsible = responsible;
context.game.broadcastEvent(event);
// Add to trash pile
if (isPossessed()) {
context.game.possessedTrashPile.add(card);
} else {
context.game.trashPile.add(card);
}
// Execute special card logic when the trashing occurs
card.isTrashed(context);
// Market Square trashing reaction
if (Util.getCardCount(hand, Cards.marketSquare) > 0) {
ArrayList<Card> marketSquaresInHand = new ArrayList<Card>();
for (Card c : hand) {
if (c.getType() == Cards.Type.MarketSquare) {
marketSquaresInHand.add(c);
}
}
for (Card c : marketSquaresInHand) {
if (controlPlayer.marketSquare_shouldDiscard(context)) {
hand.remove(c);
discard(c, card, context);
gainNewCard(Cards.gold, c, context);
}
}
}
}
public abstract HuntingGroundsOption huntingGrounds_chooseOption(MoveContext context);
public abstract Card catacombs_cardToObtain(MoveContext context);
public void namedCard(Card card, Card responsible, MoveContext context) {
GameEvent event = new GameEvent(GameEvent.Type.CardNamed, context);
event.card = card;
event.responsible = responsible;
context.game.broadcastEvent(event);
}
public void revealFromHand(Card card, Card responsible, MoveContext context) {
GameEvent event = new GameEvent(GameEvent.Type.CardRevealedFromHand, context);
event.card = card;
event.responsible = responsible;
context.game.broadcastEvent(event);
}
public void reveal(Card card, Card responsible, MoveContext context) {
GameEvent event = new GameEvent(GameEvent.Type.CardRevealed, context);
event.card = card;
event.responsible = responsible;
context.game.broadcastEvent(event);
}
public void attacked(Card card, MoveContext context) {
context.attackedPlayer = this;
GameEvent event = new GameEvent(GameEvent.Type.PlayerAttacking, context);
event.attackedPlayer = this;
event.card = card.behaveAsCard();
context.game.broadcastEvent(event);
}
public static enum NoblesOption {
AddCards,
AddActions
}
public static enum TorturerOption {
TakeCurse,
DiscardTwoCards
}
public static enum MinionOption {
AddGold,
RolloverCards
}
public static enum PawnOption {
AddCard,
AddAction,
AddBuy,
AddGold
}
public static enum StewardOption {
AddCards,
AddGold,
TrashCards
}
public static enum WatchTowerOption {
TopOfDeck,
Trash,
Normal
}
public static enum JesterOption {
GainCopy,
GiveCopy
}
public static enum TournamentOption {
GainPrize,
GainDuchy
}
public static enum TrustySteedOption {
AddCards,
AddActions,
AddGold,
GainSilvers
}
public static enum SpiceMerchantOption {
AddCardsAndAction,
AddGoldAndBuy
}
public static enum PutBackOption {
Treasury,
Alchemist,
WalledVillage,
Coin,
Action,
None
}
public static enum SquireOption {
AddActions,
AddBuys,
GainSilver
}
public enum CountFirstOption {
Discard,
PutOnDeck,
GainCopper
}
public enum CountSecondOption {
Coins,
TrashHand,
GainDuchy
}
public enum GraverobberOption {
GainFromTrash,
TrashActionCard
}
public enum HuntingGroundsOption {
GainDuchy, GainEstates
}
public enum GovernorOption {
AddCards,
GainTreasure,
Upgrade
}
public enum DoctorOverpayOption
{
TrashIt,
DiscardIt,
PutItBack
}
// Context is passed for the player to add a GameEventListener
// if they want or to see what cards the game has, etc.
public void newGame(MoveContext context) {
}
public ArrayList<TreasureCard> getTreasuresInHand() {
ArrayList<TreasureCard> treasures = new ArrayList<TreasureCard>();
for (Card c : getHand())
if (c instanceof TreasureCard)
treasures.add((TreasureCard) c);
return treasures;
}
public ArrayList<VictoryCard> getVictoryInHand() {
ArrayList<VictoryCard> victory = new ArrayList<VictoryCard>();
for (Card c : getHand())
if (c instanceof VictoryCard)
victory.add((VictoryCard) c);
return victory;
}
public ArrayList<ActionCard> getActionsInHand() {
ArrayList<ActionCard> actions = new ArrayList<ActionCard>();
for (Card c : getHand())
if (c instanceof ActionCard)
actions.add((ActionCard) c);
return actions;
}
public abstract String getPlayerName();
public abstract String getPlayerName(boolean maskName);
public abstract Card doAction(MoveContext context);
public abstract Card[] actionCardsToPlayInOrder(MoveContext context);
public abstract Card doBuy(MoveContext context);
public abstract Card[] topOfDeck_orderCards(MoveContext context, Card[] cards);
// ////////////////////////////////////////////
// Card interactions - cards from the base game
// ////////////////////////////////////////////
public abstract Card workshop_cardToObtain(MoveContext context);
public abstract Card feast_cardToObtain(MoveContext context);
public abstract Card remodel_cardToTrash(MoveContext context);
public abstract Card remodel_cardToObtain(MoveContext context, int maxCost, boolean potion);
public abstract Card[] militia_attack_cardsToKeep(MoveContext context);
public abstract TreasureCard thief_treasureToTrash(MoveContext context, TreasureCard[] treasures);
public abstract TreasureCard[] thief_treasuresToGain(MoveContext context, TreasureCard[] treasures);
public abstract boolean chancellor_shouldDiscardDeck(MoveContext context);
public abstract TreasureCard mine_treasureFromHandToUpgrade(MoveContext context);
public abstract TreasureCard mine_treasureToObtain(MoveContext context, int maxCost, boolean potion);
public abstract Card[] chapel_cardsToTrash(MoveContext context);
public abstract Card[] cellar_cardsToDiscard(MoveContext context);
public abstract boolean library_shouldKeepAction(MoveContext context, ActionCard action);
public abstract boolean spy_shouldDiscard(MoveContext context, Player targetPlayer, Card card);
public abstract VictoryCard bureaucrat_cardToReplace(MoveContext context);
// ////////////////////////////////////////////
// Card interactions - cards from Intrigue
// ////////////////////////////////////////////
public abstract Card[] secretChamber_cardsToDiscard(MoveContext context);
public abstract PawnOption[] pawn_chooseOptions(MoveContext context);
public abstract TorturerOption torturer_attack_chooseOption(MoveContext context);
public abstract StewardOption steward_chooseOption(MoveContext context);
public abstract Card swindler_cardToSwitch(MoveContext context, int cost, boolean potion);
public abstract Card[] steward_cardsToTrash(MoveContext context);
public abstract Card[] torturer_attack_cardsToDiscard(MoveContext context);
public abstract Card courtyard_cardToPutBackOnDeck(MoveContext context);
public abstract boolean baron_shouldDiscardEstate(MoveContext context);
public abstract Card ironworks_cardToObtain(MoveContext context);
public abstract Card masquerade_cardToPass(MoveContext context);
public abstract Card masquerade_cardToTrash(MoveContext context);
public abstract boolean miningVillage_shouldTrashMiningVillage(MoveContext context);
public abstract Card saboteur_cardToObtain(MoveContext context, int maxCost, boolean potion);
public abstract Card[] scout_orderCards(MoveContext context, Card[] cards);
public abstract Card[] mandarin_orderCards(MoveContext context, Card[] cards);
public abstract NoblesOption nobles_chooseOptions(MoveContext context);
// Either return two cards, or null if you do not want to trash any cards.
public abstract Card[] tradingPost_cardsToTrash(MoveContext context);
public abstract Card wishingWell_cardGuess(MoveContext context, ArrayList<Card> cardList);
public abstract Card upgrade_cardToTrash(MoveContext context);
public abstract Card upgrade_cardToObtain(MoveContext context, int exactCost, boolean potion);
public abstract MinionOption minion_chooseOption(MoveContext context);
public abstract Card[] secretChamber_cardsToPutOnDeck(MoveContext context);
// ////////////////////////////////////////////
// Card interactions - cards from Seaside
// ////////////////////////////////////////////
public abstract Card[] ghostShip_attack_cardsToPutBackOnDeck(MoveContext context);
public abstract Card salvager_cardToTrash(MoveContext context);
public abstract Card[] warehouse_cardsToDiscard(MoveContext context);
public abstract boolean pirateShip_takeTreasure(MoveContext context);
public abstract TreasureCard pirateShip_treasureToTrash(MoveContext context, TreasureCard[] treasures);
public abstract boolean nativeVillage_takeCards(MoveContext context);
public abstract Card smugglers_cardToObtain(MoveContext context);
public abstract Card island_cardToSetAside(MoveContext context);
public abstract Card haven_cardToSetAside(MoveContext context);
public abstract boolean navigator_shouldDiscardTopCards(MoveContext context, Card[] cards);
public abstract Card[] navigator_cardOrder(MoveContext context, Card[] cards);
public abstract Card embargo_supplyToEmbargo(MoveContext context);
// Will be passed all three cards
public abstract Card lookout_cardToTrash(MoveContext context, Card[] cards);
// Will be passed the two cards leftover after trashing one
public abstract Card lookout_cardToDiscard(MoveContext context, Card[] cards);
public abstract Card ambassador_revealedCard(MoveContext context);
public abstract int ambassador_returnToSupplyFromHand(MoveContext context, Card card);
public abstract boolean pearlDiver_shouldMoveToTop(MoveContext context, Card card);
public abstract boolean explorer_shouldRevealProvince(MoveContext context);
// ////////////////////////////////////////////
// Card interactions - cards from Alchemy
// ////////////////////////////////////////////
public abstract Card transmute_cardToTrash(MoveContext context);
public abstract ArrayList<Card> apothecary_cardsForDeck(MoveContext context, ArrayList<Card> cards);
public abstract boolean alchemist_backOnDeck(MoveContext context);
public abstract TreasureCard herbalist_backOnDeck(MoveContext context, TreasureCard[] cards);
public abstract Card apprentice_cardToTrash(MoveContext context);
public abstract ActionCard university_actionCardToObtain(MoveContext context);
public abstract boolean scryingPool_shouldDiscard(MoveContext context, Player targetPlayer, Card card);
public abstract ActionCard[] golem_cardOrder(MoveContext context, ActionCard[] cards);
// ////////////////////////////////////////////
// Card interactions - cards from Prosperity
// ////////////////////////////////////////////
public abstract Card bishop_cardToTrashForVictoryTokens(MoveContext context);
public abstract Card bishop_cardToTrash(MoveContext context);
public abstract Card contraband_cardPlayerCantBuy(MoveContext context);
public abstract Card expand_cardToTrash(MoveContext context);
public abstract Card expand_cardToObtain(MoveContext context, int maxCost, boolean potion);
public abstract Card[] forge_cardsToTrash(MoveContext context);
public abstract Card forge_cardToObtain(MoveContext context, int exactCost);
public abstract Card[] goons_attack_cardsToKeep(MoveContext context);
public abstract ActionCard kingsCourt_cardToPlay(MoveContext context);
public abstract ActionCard throneRoom_cardToPlay(MoveContext context);
public abstract boolean loan_shouldTrashTreasure(MoveContext context, TreasureCard treasure);
public abstract TreasureCard mint_treasureToMint(MoveContext context);
public abstract boolean mountebank_attack_shouldDiscardCurse(MoveContext context);
public abstract Card[] rabble_attack_cardOrder(MoveContext context, Card[] cards);
public abstract boolean royalSeal_shouldPutCardOnDeck(MoveContext context, Card card);
public abstract Card tradeRoute_cardToTrash(MoveContext context);
public abstract Card[] vault_cardsToDiscardForGold(MoveContext context);
public abstract Card[] vault_cardsToDiscardForCard(MoveContext context);
public abstract WatchTowerOption watchTower_chooseOption(MoveContext context, Card card);
public abstract ArrayList<TreasureCard> treasureCardsToPlayInOrder(MoveContext context);
// ////////////////////////////////////////////
// Card interactions - cards from Cornucopia
// ////////////////////////////////////////////
public abstract Card hamlet_cardToDiscardForAction(MoveContext context);
public abstract Card hamlet_cardToDiscardForBuy(MoveContext context);
public abstract Card hornOfPlenty_cardToObtain(MoveContext context, int maxCost);
public abstract Card[] horseTraders_cardsToDiscard(MoveContext context);
public abstract JesterOption jester_chooseOption(MoveContext context, Player targetPlayer, Card card);
public abstract Card remake_cardToTrash(MoveContext context);
public abstract Card remake_cardToObtain(MoveContext context, int exactCost, boolean potion);
public abstract boolean tournament_shouldRevealProvince(MoveContext context);
public abstract TournamentOption tournament_chooseOption(MoveContext context);
public abstract Card tournament_choosePrize(MoveContext context);
public abstract Card[] youngWitch_cardsToDiscard(MoveContext context);
public abstract Card[] followers_attack_cardsToKeep(MoveContext context);
public abstract TrustySteedOption[] trustySteed_chooseOptions(MoveContext context);
// ////////////////////////////////////////////
// Card interactions - cards from Hinterlands
// ////////////////////////////////////////////
public abstract Card borderVillage_cardToObtain(MoveContext context);
public abstract Card farmland_cardToTrash(MoveContext context);
public abstract Card farmland_cardToObtain(MoveContext context, int cost, boolean potion);
public abstract TreasureCard stables_treasureToDiscard(MoveContext context);
public abstract boolean duchess_shouldDiscardCardFromTopOfDeck(MoveContext context, Card card);
public abstract boolean duchess_shouldGainBecauseOfDuchy(MoveContext context);
public abstract Card develop_cardToTrash(MoveContext context);
public abstract Card develop_lowCardToGain(MoveContext context, int cost, boolean potion);
public abstract Card develop_highCardToGain(MoveContext context, int cost, boolean potion);
public abstract Card[] develop_orderCards(MoveContext context, Card[] cards);
public abstract Card oasis_cardToDiscard(MoveContext context);
public abstract boolean foolsGold_shouldTrash(MoveContext context);
public abstract TreasureCard nobleBrigand_silverOrGoldToTrash(MoveContext context, TreasureCard[] silverOrGoldCards);
public abstract boolean jackOfAllTrades_shouldDiscardCardFromTopOfDeck(MoveContext context, Card card);
public abstract Card jackOfAllTrades_nonTreasureToTrash(MoveContext context);
public abstract TreasureCard spiceMerchant_treasureToTrash(MoveContext context);
public abstract SpiceMerchantOption spiceMerchant_chooseOption(MoveContext context);
public abstract Card[] embassy_cardsToDiscard(MoveContext context);
public abstract Card[] cartographer_cardsFromTopOfDeckToDiscard(MoveContext context, Card[] cards);
public abstract Card[] cartographer_cardOrder(MoveContext context, Card[] cards);
public abstract ActionCard scheme_actionToPutOnTopOfDeck(MoveContext context, ActionCard[] actions);
public abstract boolean tunnel_shouldReveal(MoveContext context);
public abstract boolean trader_shouldGainSilverInstead(MoveContext context, Card card);
public abstract Card trader_cardToTrash(MoveContext context);
public abstract boolean oracle_shouldDiscard(MoveContext context, Player player, ArrayList<Card> cards);
public abstract Card[] oracle_orderCards(MoveContext context, Card[] cards);
public abstract boolean illGottenGains_gainCopper(MoveContext context);
public abstract Card haggler_cardToObtain(MoveContext context, int maxCost, boolean potion);
public abstract Card[] inn_cardsToDiscard(MoveContext context);
public abstract boolean inn_shuffleCardBackIntoDeck(MoveContext context, ActionCard card);
public abstract Card mandarin_cardToReplace(MoveContext context);
public abstract Card[] margrave_attack_cardsToKeep(MoveContext context);
public abstract Card getAttackReaction(MoveContext context, Card responsible, boolean defended, Card lastCard);
public abstract boolean revealBane(MoveContext context);
public abstract PutBackOption selectPutBackOption(MoveContext context, List<PutBackOption> options);
// ////////////////////////////////////////////
// Card interactions - cards from Dark Ages
// ////////////////////////////////////////////
public abstract Card rats_cardToTrash(MoveContext context);
public abstract SquireOption squire_chooseOption(MoveContext context);
public abstract Card altar_cardToTrash(MoveContext context);
public abstract Card altar_cardToObtain(MoveContext context);
public abstract boolean beggar_shouldDiscard(MoveContext context);
public abstract Card armory_cardToObtain(MoveContext context);
public abstract Card squire_cardToObtain(MoveContext context);
public abstract boolean catacombs_shouldDiscardTopCards(MoveContext context, Card[] array);
public abstract CountFirstOption count_chooseFirstOption(MoveContext context);
public abstract CountSecondOption count_chooseSecondOption(MoveContext context);
public abstract Card[] count_cardsToDiscard(MoveContext context);
public abstract Card count_cardToPutBackOnDeck(MoveContext context);
public abstract Card forager_cardToTrash(MoveContext context);
public abstract GraverobberOption graverobber_chooseOption(MoveContext context);
public abstract Card graverobber_cardToGainFromTrash(MoveContext context);
public abstract Card graverobber_cardToTrash(MoveContext context);
public abstract Card graverobber_cardToReplace(MoveContext context, int maxCost, boolean potion);
public abstract boolean ironmonger_shouldDiscard(MoveContext context, Card card);
public abstract Card junkDealer_cardToTrash(MoveContext context);
public abstract boolean marketSquare_shouldDiscard(MoveContext context);
public abstract Card mystic_cardGuess(MoveContext context, ArrayList<Card> cardList);
public abstract boolean scavenger_shouldDiscardDeck(MoveContext context);
public abstract Card scavenger_cardToPutBackOnDeck(MoveContext context);
public abstract Card[] storeroom_cardsToDiscardForCards(MoveContext context);
public abstract Card[] storeroom_cardsToDiscardForCoins(MoveContext context);
public abstract ActionCard procession_cardToPlay(MoveContext context);
public abstract Card procession_cardToGain(MoveContext context, int maxCost, boolean potion);
public abstract Card rebuild_cardToPick(MoveContext context);
public abstract Card rebuild_cardToGain(MoveContext context, int maxCost, boolean costPotion);
public abstract Card rogue_cardToGain(MoveContext context);
public abstract Card rogue_cardToTrash(MoveContext context, ArrayList<Card> canTrash);
public abstract TreasureCard counterfeit_cardToPlay(MoveContext context);
public abstract Card pillage_opponentCardToDiscard(MoveContext context, ArrayList<Card> handCards);
public abstract boolean hovel_shouldTrash(MoveContext context);
public abstract Card deathCart_actionToTrash(MoveContext context);
public abstract Card[] urchin_attack_cardsToKeep(MoveContext context);
public abstract boolean urchin_shouldTrashForMercenary(MoveContext context);
public abstract Card[] mercenary_cardsToTrash(MoveContext context);
public abstract Card[] mercenary_attack_cardsToKeep(MoveContext context);
public abstract boolean madman_shouldReturnToPile(MoveContext context);
public abstract Card hermit_cardToTrash(MoveContext context, ArrayList<Card> cardList, int nonTreasureCountInDiscard);
public abstract Card hermit_cardToGain(MoveContext context);
public abstract ActionCard bandOfMisfits_actionCardToImpersonate(MoveContext context);
// ////////////////////////////////////////////
// Card interactions - Guilds Expansion
// ////////////////////////////////////////////
public abstract int numGuildsCoinTokensToSpend(MoveContext context);
public abstract int amountToOverpay(MoveContext context, int cardCost);
public abstract int overpayByPotions(MoveContext context, int availablePotions);
public abstract TreasureCard taxman_treasureToTrash(MoveContext context);
public abstract TreasureCard taxman_treasureToObtain(MoveContext context, int maxCost);
public abstract TreasureCard plaza_treasureToDiscard(MoveContext context);
public abstract Card butcher_cardToTrash(MoveContext context);
public abstract Card butcher_cardToObtain(MoveContext context, int maxCost, boolean potion);
public abstract Card advisor_cardToDiscard(MoveContext context, Card[] cards);
public abstract Card journeyman_cardToPick(MoveContext context);
public abstract Card stonemason_cardToTrash(MoveContext context);
public abstract Card stonemason_cardToGain(MoveContext context, int maxCost, boolean potion);
public abstract Card stonemason_cardToGainOverpay(MoveContext context, int overpayAmount, boolean potion);
public abstract Card doctor_cardToPick(MoveContext context);
public abstract ArrayList<Card> doctor_cardsForDeck(MoveContext context, ArrayList<Card> cards);
public abstract DoctorOverpayOption doctor_chooseOption(MoveContext context, Card card);
public abstract Card herald_cardTopDeck(MoveContext context, Card[] cardList);
// ////////////////////////////////////////////
// Card interactions - Promotional Cards
// ////////////////////////////////////////////
public abstract boolean walledVillage_backOnDeck(MoveContext context);
public abstract GovernorOption governor_chooseOption(MoveContext context);
public abstract Card governor_cardToTrash(MoveContext context);
public abstract Card governor_cardToObtain(MoveContext context, int exactCost, boolean potion);
public abstract Card envoy_cardToDiscard(MoveContext context, Card[] revealedCards);
public abstract boolean survivors_shouldDiscardTopCards(MoveContext context, Card[] array);
public abstract Card[] survivors_cardOrder(MoveContext context, Card[] array);
public abstract boolean cultist_shouldPlayNext(MoveContext context);
public abstract Card[] dameAnna_cardsToTrash(MoveContext context);
public abstract Card knight_cardToTrash(MoveContext context, ArrayList<Card> canTrash);
public abstract Card[] sirMichael_attack_cardsToKeep(MoveContext context);
public abstract Card dameNatalie_cardToObtain(MoveContext context);
}