package com.vdom.core;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Random;
import com.vdom.api.ActionCard;
import com.vdom.api.Card;
import com.vdom.api.CardCostComparator;
import com.vdom.api.DurationCard;
import com.vdom.api.FrameworkEvent;
import com.vdom.api.FrameworkEventHelper;
import com.vdom.api.GameEvent;
import com.vdom.api.GameEventListener;
import com.vdom.api.GameType;
import com.vdom.api.TreasureCard;
import com.vdom.api.VictoryCard;
import com.vdom.core.Player.WatchTowerOption;
public class Game {
public static boolean junit = false;
public static boolean debug = false;
public static Integer cardSequence = 1;
public static HashMap<String, Double> GAME_TYPE_WINS = new HashMap<String, Double>();
public static HashMap<String, Integer> winStats = new HashMap<String, Integer>();
public static final String QUICK_PLAY = "(QuickPlay)";
public static final String BANE = "bane+";
public static String[] cardsSpecifiedAtLaunch;
public static ArrayList<String> unfoundCards = new ArrayList<String>();
String cardListText = "";
String unfoundCardText = "";
int totalCardCountGameBegin = 0;
int totalCardCountGameEnd = 0;
/**
* Player classes and optionally the url to the jar on the web that the class is located in. Player class
* is in index [0] of the array and the jar is in index [1]. Index [1] is null (but still there) if the
* default class loader should be used.
*/
static ArrayList<String[]> playerClassesAndJars = new ArrayList<String[]>();
/**
* The card set to use for the game.
*
* @see com.vdom.api.GameType
*/
public static GameType gameType = GameType.Random;
public static String gameTypeStr = null;
public static boolean showUsage = false;
public static boolean alwaysIncludePlatColony = false;
public static boolean alwaysUseShelters = false;
public static boolean sheltersPassedIn = false;
public static boolean quickPlay = false;
public static boolean sortCards = false;
public static boolean actionChains = false;
public static boolean suppressRedundantReactions = false;
public static boolean equalStartHands = false;
public static boolean maskPlayerNames = false;
public static final HashSet<GameEvent.Type> showEvents = new HashSet<GameEvent.Type>();
public static final HashSet<String> showPlayers = new HashSet<String>();
static boolean test = false;
static boolean ignoreAllPlayerErrors = false;
static boolean ignoreSomePlayerErrors = false;
static HashSet<String> ignoreList = new HashSet<String>();
static ArrayList<GameStats> gameTypeStats = new ArrayList<GameStats>();
static int numGames = -1;
static int gameCounter = 0;
public ArrayList<GameEventListener> listeners = new ArrayList<GameEventListener>();
public GameEventListener gameListener;
static boolean forceDownload = false;
static HashMap<String, Double> overallWins = new HashMap<String, Double>();
public static Random rand = new Random(System.currentTimeMillis());
public HashMap<String, AbstractCardPile> piles = new HashMap<String, AbstractCardPile>();
public HashMap<String, Integer> embargos = new HashMap<String, Integer>();
public ArrayList<Card> trashPile = new ArrayList<Card>();
public ArrayList<Card> possessedTrashPile = new ArrayList<Card>();
public ArrayList<Card> possessedBoughtPile = new ArrayList<Card>();
public int tradeRouteValue = 0;
public Card baneCard = null;
double chanceForPlatColony = -1;
double chanceForShelters = 0.0;
public boolean bakerInPlay = false;
private static final int kingdomCardPileSize = 10;
public static int victoryCardPileSize = 12;
ArrayList<Card>[] cardsObtainedLastTurn;
static int playersTurn;
// public UI ui;
int turnCount = 0;
int consecutiveTurnCounter = 0;
public static HashMap<String, Player> cachedPlayers = new HashMap<String, Player>();
public static HashMap<String, Class<?>> cachedPlayerClasses = new HashMap<String, Class<?>>();
public static Player[] players;
public boolean sheltersInPlay = false;
public int possessionsToProcess = 0;
public Player possessingPlayer = null;
public int nextPossessionsToProcess = 0;
public Player nextPossessingPlayer = null;
public static int numPlayers;
boolean gameOver = false;
private static HashMap<String, Player> playerCache = new HashMap<String, Player>();
public static void main(String[] args) {
try {
go(args, false);
} catch (ExitException e) {
// This is what we would correctly need to do.
// May break some code though which relies on vdom being less strict
System.exit(-1);
}
}
public static void go(String[] args, boolean html) throws ExitException {
/*
* Don't catch ExitException here. If someone throws an ExitException, it means the game is over, which should be handled by whoever owns us.
*/
processArgs(args);
Util.debug("");
// Start game(s)
if (gameTypeStr != null) {
gameType = GameType.fromName(gameTypeStr);
new Game().start();
} else {
for (String[] className : playerClassesAndJars) {
overallWins.put(className[0], 0.0);
}
for (GameType p : GameType.values()) {
gameType = p;
new Game().start();
}
if (test) {
for (int i = 0; i < 5; i++) {
gameType = GameType.RandomBaseGame;
new Game().start();
}
for (int i = 0; i < 5; i++) {
gameType = GameType.RandomIntrigue;
new Game().start();
}
for (int i = 0; i < 5; i++) {
gameType = GameType.RandomSeaside;
new Game().start();
}
for (int i = 0; i < 5; i++) {
gameType = GameType.RandomDarkAges;
new Game().start();
}
for (int i = 0; i < 5; i++) {
gameType = GameType.Random;
new Game().start();
}
}
if (!debug && !test) {
Util.log("----------------------------------------------------");
}
printStats(overallWins, numGames * GameType.values().length, "Total");
printStats(GAME_TYPE_WINS, GameType.values().length, "Types");
}
if (test) {
printGameTypeStats();
}
FrameworkEvent frameworkEvent = new FrameworkEvent(FrameworkEvent.Type.AllDone);
FrameworkEventHelper.broadcastEvent(frameworkEvent);
}
void start() throws ExitException {
HashMap<String, Double> gameTypeSpecificWins = new HashMap<String, Double>();
for (String[] className : playerClassesAndJars) {
gameTypeSpecificWins.put(className[0], 0.0);
if (!GAME_TYPE_WINS.containsKey(className[0])) {
GAME_TYPE_WINS.put(className[0], 0.0);
}
}
long turnCountTotal = 0;
long vpTotal = 0;
long numCardsTotal = 0;
// if (test) {
// System.out.print(gameType + " ");
// }
for (int gameCount = 0; gameCount < numGames; gameCount++) {
// if (test) {
// System.out.print(gameCount + " ");
// System.out.flush();
// }
Util.debug("---------------------", false);
Util.debug("New Game:" + gameType);
initGameBoard();
if (test) {
Util.log(gameType.toString());
totalCardCountGameBegin = totalCardCount();
}
gameOver = false;
gameCounter++;
playersTurn = 0;
turnCount = 1;
Util.debug("Turn " + turnCount);
while (!gameOver) {
Player player = players[playersTurn];
MoveContext context = new MoveContext(this, player);
playerBeginTurn(player, context);
// /////////////////////////////////
// Actions
// /////////////////////////////////
playerAction(player, context);
// /////////////////////////////////
// Select Treasure for Buy
// /////////////////////////////////
playTreasures(player, context);
// Spend Guilds coin tokens if applicable
playGuildsTokens(player, context);
// /////////////////////////////////
// Buy Phase
// /////////////////////////////////
playerBuy(player, context);
if (context.totalCardsBoughtThisTurn == 0) {
GameEvent event = new GameEvent(GameEvent.Type.NoBuy, context);
broadcastEvent(event);
Util.debug(player.getPlayerName() + " did not buy a card with coins:" + context.getCoinAvailableForBuy());
}
// /////////////////////////////////
// Discard, draw new hand
// /////////////////////////////////
player.cleanup(context);
boolean takeAnotherTurn = playerEndTurn(player, context);
gameOver = checkGameOver();
if (!gameOver) {
setPlayersTurn(takeAnotherTurn);
}
}
// unmask players
maskPlayerNames = false;
int vps[] = gameOver(gameTypeSpecificWins);
if (test) {
// Compute game stats
turnCountTotal += turnCount;
for (int i = 0; i < vps.length; i++) {
vpTotal += vps[i];
numCardsTotal += players[i].getAllCards().size();
}
totalCardCountGameEnd = totalCardCount();
assert (totalCardCountGameBegin == totalCardCountGameEnd);
}
}
// Java program ending
if (!debug) {
markWinner(gameTypeSpecificWins);
printStats(gameTypeSpecificWins, numGames, gameType.toString());
// Util.log("---------------------");
}
if (test) {
// System.out.println();
ArrayList<Card> gameCards = new ArrayList<Card>();
for (AbstractCardPile pile : piles.values()) {
Card card = pile.card();
if (!card.equals(Cards.copper) && !card.equals(Cards.silver) && !card.equals(Cards.gold) && !card.equals(Cards.platinum)
&& !card.equals(Cards.estate) && !card.equals(Cards.duchy) && !card.equals(Cards.province) && !card.equals(Cards.colony)
&& !card.equals(Cards.curse)) {
gameCards.add(card);
}
}
GameStats stats = new GameStats();
stats.gameType = gameType;
stats.cards = gameCards.toArray(new Card[0]);
stats.aveTurns = (int) (turnCountTotal / numGames);
stats.aveNumCards = (int) (numCardsTotal / (numGames * numPlayers));
stats.aveVictoryPoints = (int) (vpTotal / (numGames * numPlayers));
gameTypeStats.add(stats);
}
FrameworkEvent frameworkEvent = new FrameworkEvent(FrameworkEvent.Type.GameTypeOver);
frameworkEvent.setGameType(gameType);
frameworkEvent.setGameTypeWins(gameTypeSpecificWins);
FrameworkEventHelper.broadcastEvent(frameworkEvent);
}
protected void setPlayersTurn(boolean takeAnotherTurn) {
if (!takeAnotherTurn && consecutiveTurnCounter > 0) {
// next player
consecutiveTurnCounter = 0;
if (++playersTurn >= numPlayers) {
playersTurn = 0;
Util.debug("Turn " + ++turnCount, true);
}
}
}
public int cardsInLowestPiles (int numPiles) {
int[] ips = new int[piles.size() - 1 - (isColonyInGame() ? 1 : 0)];
int count = 0;
for (AbstractCardPile pile : piles.values()) {
if (pile.card() != Cards.province && pile.card() != Cards.colony)
ips[count++] = pile.getCount();
}
Arrays.sort(ips);
count = 0;
for (int i = 0; i < numPiles; i++) {
count += ips[i];
}
return count;
}
protected void playTreasures(Player player, MoveContext context) {
// Set the turn gold to the correct amount
context.gold = context.addGold;
context.addGold = 0;
context.potions = 0;
context.buyPhase = true;
boolean selectingCoins = playerShouldSelectCoinsToPlay(context, player.getHand());
ArrayList<TreasureCard> treasures = null;
treasures = (selectingCoins) ? player.controlPlayer.treasureCardsToPlayInOrder(context) : player.getTreasuresInHand();
while (treasures != null && !treasures.isEmpty()) {
while (!treasures.isEmpty()) {
TreasureCard card = treasures.remove(0);
if (player.hand.contains(card)) // this is needed due to counterfeit which trashes cards during this loop
card.playTreasure(context);
}
treasures = (selectingCoins) ? player.controlPlayer.treasureCardsToPlayInOrder(context) : player.getTreasuresInHand();
}
}
protected void playGuildsTokens(Player player, MoveContext context)
{
int coinTokenTotal = player.getGuildsCoinTokenCount();
if (coinTokenTotal > 0)
{
// Offer the player the option of "spending" Guilds coin tokens prior to buying cards
int numTokensToSpend = player.numGuildsCoinTokensToSpend(context);
if (numTokensToSpend > 0 && numTokensToSpend <= coinTokenTotal)
{
player.spendGuildsCoinTokens(numTokensToSpend);
context.addGold += numTokensToSpend;
Util.debug(player, "Spent " + numTokensToSpend + " Guilds coin tokens");
}
}
}
private void markWinner(HashMap<String, Double> gameTypeSpecificWins) {
double highWins = 0;
int winners = 0;
for (String player : gameTypeSpecificWins.keySet()) {
if (gameTypeSpecificWins.get(player) > highWins) {
highWins = gameTypeSpecificWins.get(player);
}
}
for (String player : gameTypeSpecificWins.keySet()) {
if (gameTypeSpecificWins.get(player) == highWins) {
winners++;
}
}
double points = 1.0 / winners;
for (String player : gameTypeSpecificWins.keySet()) {
if (gameTypeSpecificWins.get(player) == highWins) {
double playerWins = 0;
if (GAME_TYPE_WINS.containsKey(player)) {
playerWins = GAME_TYPE_WINS.get(player);
}
playerWins += points;
GAME_TYPE_WINS.put(player, playerWins);
}
}
GAME_TYPE_WINS.toString();
}
protected int[] gameOver(HashMap<String, Double> gameTypeSpecificWins) {
if (debug)
printPlayerTurn();
int[] vps = calculateVps();
for (int i = 0; i < numPlayers; i++) {
int tieCount = 0;
boolean loss = false;
for (int j = 0; j < numPlayers; j++) {
if (i == j) {
continue;
}
if (vps[i] < vps[j]) {
loss = true;
break;
}
if (vps[i] == vps[j]) {
tieCount++;
}
}
if (!loss) {
double num = gameTypeSpecificWins.get(players[i].getClass().getName());
Double overall = overallWins.get(players[i].getClass().getName());
boolean trackOverall = (overall != null);
if (tieCount == 0) {
num += 1.0;
if (trackOverall) {
overall += 1.0;
}
} else {
num += 1.0 / (tieCount + 1);
if (trackOverall) {
overall += 1.0 / (tieCount + 1);
}
}
gameTypeSpecificWins.put(players[i].getClass().getName(), num);
if (trackOverall) {
overallWins.put(players[i].getClass().getName(), overall);
}
}
Player player = players[i];
player.vps = vps[i];
player.win = !loss;
MoveContext context = new MoveContext(this, player);
broadcastEvent(new GameEvent(GameEvent.Type.GameOver, context));
}
int index = 0;
for (Player player : players) {
int vp = vps[index++];
Util.debug(player.getPlayerName() + ":Victory Points=" + vp, true);
GameEvent event = new GameEvent(GameEvent.Type.VictoryPoints, null);
event.setPlayer(player);
event.setComment(":" + vp);
broadcastEvent(event);
}
return vps;
}
protected void printPlayerTurn() {
for (Player player : players) {
Util.debug("", true);
ArrayList<Card> allCards = player.getAllCards();
StringBuilder msg = new StringBuilder();
msg.append(" " + allCards.size() + " Cards: ");
final HashMap<String, Integer> cardCounts = new HashMap<String, Integer>();
for (Card card : allCards) {
String key = card.getName() + " -> " + card.getDescription();
Integer count = cardCounts.get(key);
if (count == null) {
cardCounts.put(key, 1);
} else {
cardCounts.put(key, count + 1);
}
}
ArrayList<Card> removeDuplicates = new ArrayList<Card>();
for (Card card : allCards) {
if (!removeDuplicates.contains(card)) {
removeDuplicates.add(card);
}
}
allCards = removeDuplicates;
Collections.sort(allCards, new Comparator<Card>() {
public int compare(Card o1, Card o2) {
String keyOne = o1.getName() + " -> " + o1.getDescription();
String keyTwo = o2.getName() + " -> " + o2.getDescription();
return cardCounts.get(keyTwo) - cardCounts.get(keyOne);
}
});
boolean first = true;
for (Card card : allCards) {
String key = card.getName() + " -> " + card.getDescription();
if (first) {
first = false;
} else {
msg.append(", ");
}
msg.append("" + cardCounts.get(key) + " " + card.getName());
}
Util.debug(player.getPlayerName() + ":" + msg, true);
}
Util.debug("", true);
}
protected boolean playerEndTurn(Player player, MoveContext context) {
int handCount = 5;
boolean takeAnotherTurn = false;
// Can only have at most two consecutive turns
for (Card card : player.nextTurnCards) {
Card behaveAsCard = card.behaveAsCard();
if ((behaveAsCard instanceof DurationCard) && ((DurationCard) behaveAsCard).takeAnotherTurn()) {
handCount = ((DurationCard) behaveAsCard).takeAnotherTurnCardCount();
if (consecutiveTurnCounter <= 1) {
takeAnotherTurn = true;
break;
}
}
}
// draw next hand
for (int i = 0; i < handCount; i++) {
drawToHand(player, null, false);
}
// /////////////////////////////////
// Reset context for status update
// /////////////////////////////////
context.actionsPlayedSoFar = 0;
context.actions = 1;
context.buys = 1;
context.addGold = 0;
context.coppersmithsPlayed = 0;
context.gold = context.getCoinAvailableForBuy();
GameEvent event = new GameEvent(GameEvent.Type.NewHand, context);
broadcastEvent(event);
event = null;
// /////////////////////////////////
// Turn End
// /////////////////////////////////
if (player.isPossessed()) {
if (--possessionsToProcess == 0)
player.controlPlayer = player;
} else if (nextPossessionsToProcess > 0) {
possessionsToProcess = nextPossessionsToProcess;
possessingPlayer = nextPossessingPlayer;
nextPossessionsToProcess = 0;
nextPossessingPlayer = null;
}
event = new GameEvent(GameEvent.Type.TurnEnd, context);
broadcastEvent(event);
return takeAnotherTurn;
}
protected void playerAction(Player player, MoveContext context) {
// TODO move this check to action and buy (and others?)
// if(player.hand.size() > 0)
Card action = null;
do {
action = null;
ArrayList<ActionCard> actionCards = null;
if (!actionChains || player.controlPlayer.isAi()) {
action = (ActionCard) player.controlPlayer.doAction(context);
if (action != null) {
actionCards = new ArrayList<ActionCard>();
actionCards.add((ActionCard) action);
}
} else {
Card[] cs = player.controlPlayer.actionCardsToPlayInOrder(context);
if (cs != null && cs.length != 0) {
actionCards = new ArrayList<ActionCard>();
for (int i = 0; i < cs.length; i++) {
actionCards.add((ActionCard) cs[i]);
}
}
}
while (context.actions > 0 && actionCards != null && !actionCards.isEmpty()) {
action = actionCards.remove(0);
if (action != null) {
if (isValidAction(context, action)) {
GameEvent event = new GameEvent(GameEvent.Type.Status, (MoveContext) context);
broadcastEvent(event);
try {
((ActionCardImpl) action).play(this, (MoveContext) context, true);
} catch (RuntimeException e) {
e.printStackTrace();
}
} else {
Util.debug("Error:Invalid action selected");
// action = null;
}
}
}
} while (context.actions > 0 && action != null);
}
protected void playerBuy(Player player, MoveContext context) {
Card buy = null;
context.goldAvailable = context.getCoinAvailableForBuy();
do {
try {
buy = player.controlPlayer.doBuy(context);
} catch (Throwable t) {
Util.playerError(player, t);
}
if (buy != null) {
if (isValidBuy(context, buy)) {
context.totalCardsBoughtThisTurn++;
GameEvent statusEvent = new GameEvent(GameEvent.Type.Status, (MoveContext) context);
broadcastEvent(statusEvent);
playBuy(context, buy);
} else {
// TODO report?
buy = null;
}
}
} while (context.buys > 0 && buy != null);
context.buyPhase = false;
}
protected void playerBeginTurn(Player player, MoveContext context) {
if (context.game.possessionsToProcess > 0) {
player.controlPlayer = context.game.possessingPlayer;
} else {
player.controlPlayer = player;
consecutiveTurnCounter++;
}
cardsObtainedLastTurn[playersTurn].clear();
if (consecutiveTurnCounter == 1)
player.newTurn();
GameEvent gevent = new GameEvent(GameEvent.Type.TurnBegin, context);
broadcastEvent(gevent);
for (Card card : player.nextTurnCards) {
if (card.behaveAsCard() instanceof DurationCard) {
DurationCard thisCard = (DurationCard) card.behaveAsCard();
for (int clone = ((CardImpl) card).cloneCount; clone > 0; clone--) {
GameEvent event = new GameEvent(GameEvent.Type.PlayingDurationAction, context);
event.card = thisCard;
broadcastEvent(event);
context.actions += thisCard.getAddActionsNextTurn();
context.addGold += thisCard.getAddGoldNextTurn();
context.buys += thisCard.getAddBuysNextTurn();
int AddCardsNextTurn = thisCard.getAddCardsNextTurn();
if (thisCard.getType() == Cards.Type.Tactician) {
context.actions += 1;
context.addGold += 0;
context.buys += 1;
AddCardsNextTurn = 5;
}
for (int i = 0; i < AddCardsNextTurn; i++) {
drawToHand(player, thisCard, true);
}
}
} else if (!card.equals(Cards.throneRoom) && !card.equals(Cards.kingsCourt) && !card.equals(Cards.procession) && !card.equals(Cards.bandOfMisfits)) {
Util.debug(player, "Bad duration card: " + card);
} else {
GameEvent event = new GameEvent(GameEvent.Type.PlayingDurationAction, context);
event.card = card;
broadcastEvent(event);
}
}
while (!player.nextTurnCards.isEmpty()) {
Card card = player.nextTurnCards.remove(0);
CardImpl behaveAsCard = (CardImpl) card.behaveAsCard();
behaveAsCard.cloneCount = 1;
if (!behaveAsCard.trashAfterPlay) {
player.playedCards.add(card);
} else {
behaveAsCard.trashAfterPlay = false;
}
}
while (!player.haven.isEmpty()) {
player.hand.add(player.haven.remove(0));
}
while (!player.horseTraders.isEmpty()) {
Card horseTrader = player.horseTraders.remove(0);
player.hand.add(horseTrader);
drawToHand(player, horseTrader);
}
}
private static void printStats(HashMap<String, Double> wins, int gameCount, String gameType) {
if (!test || gameCount == 1) {
return;
}
double totalGameCount = 0;
Iterator<Entry<String, Double>> it = wins.entrySet().iterator();
while (it.hasNext()) {
Entry<String, Double> e = it.next();
totalGameCount += e.getValue();
}
gameCount = (int) totalGameCount;
StringBuilder sb = new StringBuilder();
String s = gameType + ":";
String start = "" + gameCount;
if (gameCount > 1) {
s = start + (gameType.equals("Types") ? " types " : " games ") + s;
}
if (!debug) {
while (s.length() < 30) { // (24 + start.length())) {
s += " ";
}
}
sb.append(s);
String winner = null;
double high = 0.0;
Iterator<String> keyIter = wins.keySet().iterator();
while (keyIter.hasNext()) {
String className = keyIter.next();
double num = wins.get(className);
double val = Math.round((num * 100 / gameCount));
if (val > high) {
high = val;
winner = className;
}
}
keyIter = wins.keySet().iterator();
while (keyIter.hasNext()) {
String className = keyIter.next();
double num = wins.get(className);
String name;
Player pclass = playerCache.get(className);
if (pclass != null) {
name = pclass.getPlayerName();
} else {
name = className;
}
double val = Math.round((num * 100 / gameCount));
String numStr = "" + (int) val;
while (numStr.length() < 3) {
numStr += " ";
}
sb.append(" ");
if (className.equals(winner)) {
sb.append("*");
} else {
sb.append(" ");
}
sb.append(name + " " + numStr + "%");
winStats.put(name, Integer.parseInt(numStr.trim()));
}
Util.log(sb.toString());
}
private static void printGameTypeStats() {
for (int i=0; i < gameTypeStats.size(); i++) {
GameStats stats = gameTypeStats.get(i);
StringBuilder sb = new StringBuilder();
sb.append(stats.gameType);
if (stats.gameType.toString().length() < 8) {
sb.append("\t");
}
if (stats.gameType.toString().length() < 16) {
sb.append("\t");
}
sb.append("\tvp=" + stats.aveVictoryPoints + "\t\tcards=" + stats.aveNumCards + "\tturns=" + stats.aveTurns + "\t"
+ Util.cardArrayToString(stats.cards));
Util.log(sb.toString());
}
}
public int calculateLead(Player player) {
int playerVictoryPoints = -999;
int otherHigh = -999;
int[] vps = calculateVps();
for (int i = 0; i < vps.length; i++) {
if (players[i].equals(player)) {
playerVictoryPoints = vps[i];
} else if (vps[i] > otherHigh) {
otherHigh = vps[i];
}
}
return playerVictoryPoints - otherHigh;
}
private static int[] calculateVps() {
int[] vps = new int[numPlayers];
for (int i = 0; i < players.length; i++) {
vps[i] = players[i].getVPs();
}
return vps;
}
protected static void processArgs(String[] args) throws ExitException {
// dont remove tabs in following to keep easy upstream-mergeability
processNewGameArgs(args);
processUserPrefArgs(args);
}
protected static void processNewGameArgs(String[] args) throws ExitException {
numPlayers = 0;
cardsSpecifiedAtLaunch = null;
overallWins.clear();
GAME_TYPE_WINS.clear();
gameTypeStats.clear();
playerClassesAndJars.clear();
playerCache.clear();
String gameCountArg = "-count";
String debugArg = "-debug";
String showEventsArg = "-showevents";
String gameTypeArg = "-type";
String gameTypeStatsArg = "-test";
String ignorePlayerErrorsArg = "-ignore";
String showPlayersArg = "-showplayers";
String siteArg = "-site=";
String cardArg = "-cards=";
for (String arg : args) {
if (arg == null) {
continue;
}
if (arg.startsWith("#")) {
continue;
}
if (arg.startsWith("-")) {
if (arg.toLowerCase().equals(debugArg)) {
debug = true;
if (showEvents.isEmpty()) {
for (GameEvent.Type eventType : GameEvent.Type.values()) {
showEvents.add(eventType);
}
}
} else if (arg.toLowerCase().startsWith(showEventsArg)) {
String showEventsString = arg.substring(showEventsArg.length() + 1);
for (String event : showEventsString.split(",")) {
showEvents.add(GameEvent.Type.valueOf(event));
}
} else if (arg.toLowerCase().startsWith(showPlayersArg)) {
String showPlayersString = arg.substring(showPlayersArg.length() + 1);
for (String player : showPlayersString.split(",")) {
showPlayers.add(player);
}
} else if (arg.toLowerCase().startsWith(ignorePlayerErrorsArg)) {
if (arg.trim().toLowerCase().equals(ignorePlayerErrorsArg)) {
ignoreAllPlayerErrors = true;
} else {
ignoreSomePlayerErrors = true;
try {
String player = arg.substring(ignorePlayerErrorsArg.length()).trim();
ignoreList.add(player);
} catch (Exception e) {
Util.log(e);
throw new ExitException();
}
}
} else if (arg.toLowerCase().equals(gameTypeStatsArg)) {
test = true;
} else if (arg.toLowerCase().startsWith(gameCountArg)) {
try {
numGames = Integer.parseInt(arg.substring(gameCountArg.length()));
} catch (Exception e) {
Util.log(e);
throw new ExitException();
}
} else if (arg.toLowerCase().startsWith(cardArg)) {
try {
cardsSpecifiedAtLaunch = arg.substring(cardArg.length()).split("-");
} catch (Exception e) {
Util.log(e);
throw new ExitException();
}
} else if (arg.toLowerCase().startsWith(siteArg)) {
try {
// UI.downloadSite =
// arg.substring(siteArg.length());
} catch (Exception e) {
Util.log(e);
throw new ExitException();
}
} else if (arg.toLowerCase().startsWith(gameTypeArg)) {
try {
gameTypeStr = arg.substring(gameTypeArg.length());
} catch (Exception e) {
Util.log(e);
throw new ExitException();
}
// } else {
// Util.log("Invalid arg:" + arg);
// showUsage = true;
}
} else {
String options = "";
String name = null;
int starIndex = arg.indexOf("*");
if (starIndex != -1) {
name = arg.substring(starIndex + 1);
arg = arg.substring(0, starIndex);
}
if (arg.endsWith(QUICK_PLAY)) {
arg = arg.substring(0, arg.length() - QUICK_PLAY.length());
options += "q";
}
int atIndex = arg.indexOf("@");
String className = arg;
String jar = null;
if (atIndex != -1) {
className = className.substring(0, atIndex);
jar = arg.substring(atIndex + 1);
}
playerClassesAndJars.add(new String[] { className, jar, name, options });
}
}
numPlayers = playerClassesAndJars.size();
if (numPlayers < 1 || numPlayers > 6 || showUsage) {
Util.log("Usage: [-debug][-ignore(playername)][-count(# of Games)][-type(Game type)] class1 class2 [class3] [class4]");
throw new ExitException();
}
if (gameTypeStr == null) {
if (debug) {
gameTypeStr = "FirstGame";
}
}
if (numGames == -1) {
numGames = 1;
}
}
public static void processUserPrefArgs(String[] args) throws ExitException {
quickPlay = false;
sortCards = false;
maskPlayerNames = false;
actionChains = false;
suppressRedundantReactions = false;
alwaysIncludePlatColony = false;
alwaysUseShelters = false;
equalStartHands = false;
String quickPlayArg = "-quickplay";
String maskPlayerNamesArg = "-masknames";
String sortCardsArg = "-sortcards";
String actionChainsArg = "-actionchains";
String suppressRedundantReactionsArg = "-suppressredundantreactions";
String platColonyArg = "-platcolony";
String useSheltersArg = "-useshelters";
String equalStartHandsArg = "-equalstarthands";
for (String arg : args) {
if (arg == null) {
continue;
}
if (arg.startsWith("#")) {
continue;
}
if (arg.startsWith("-")) {
if (arg.toLowerCase().equals(quickPlayArg)) {
quickPlay = true;
} else if (arg.toLowerCase().equals(sortCardsArg)) {
sortCards = true;
} else if (arg.toLowerCase().equals(maskPlayerNamesArg)) {
maskPlayerNames = true;
} else if (arg.toLowerCase().equals(actionChainsArg)) {
actionChains = true;
} else if (arg.toLowerCase().equals(suppressRedundantReactionsArg)) {
suppressRedundantReactions = true;
} else if (arg.toLowerCase().equals(platColonyArg)) {
alwaysIncludePlatColony = true;
} else if (arg.toLowerCase().equals(useSheltersArg)) {
alwaysUseShelters = true;
} else if (arg.toLowerCase().equals(equalStartHandsArg)) {
equalStartHands = true;
}
}
}
}
public boolean isValidAction(MoveContext context, Card action) {
if (action == null) {
return false;
}
if (!(action instanceof ActionCard)) {
return false;
}
for (Card card : context.getPlayer().hand) {
if (action.equals(card)) {
return true;
}
}
return false;
}
public boolean isValidBuy(MoveContext context, Card card) {
return isValidBuy(context, card, context.getCoinAvailableForBuy());
}
public boolean isValidBuy(MoveContext context, Card card, int gold) {
if (card == null) {
return true;
}
// TODO: Temp hack to prevent AI from buying possession, even though human player can, since it only half works
// (AI will make decisions while possessed, but will try to make "good" ones)
// if(card.equals(Cards.possession) && context != null && context.getPlayer() != null && context.getPlayer().isAi()) {
// return false;
// }
AbstractCardPile thePile = getPile(card);
if (thePile == null || thePile.isSupply() == false)
{
return false;
}
if (!Cards.isSupplyCard(card)) {
return false;
}
if (isPileEmpty(card)) {
return false;
}
if (context.cantBuy.contains(card)) {
return false;
}
if ((context.countCardsInPlay(Cards.copper) > 0) && card.equals(Cards.grandMarket)) {
return false;
}
int cost = card.getCost(context, true);
int potions = context.getPotions();
if (cost <= gold && (!card.costPotion() || potions > 0)) {
return true;
}
return false;
}
void playBuy(MoveContext context, Card buy) {
Player player = context.getPlayer();
context.buys--;
int embargos = getEmbargos(buy);
for (int i = 0; i < embargos; i++) {
player.gainNewCard(Cards.curse, Cards.embargo, context);
}
Card card = takeFromPileCheckTrader(buy, context);
if (card != null) {
GameEvent event = new GameEvent(GameEvent.Type.BuyingCard, (MoveContext) context);
event.card = card;
event.newCard = true;
broadcastEvent(event);
// Swap in the real knight
if (buy.equals(Cards.virtualKnight)) {
buy = card;
}
}
// cost adjusted based on any cards played or card being bought
int cost = buy.getCost(context);
// If card can be overpaid for, do so now
if (buy.isOverpay())
{
context.overpayAmount = player.amountToOverpay(context, cost);
if (context.potions > 0)
{
context.overpayPotions = player.overpayByPotions(context, context.potions);
context.potions -= context.overpayPotions;
}
if (context.overpayAmount > 0 || context.overpayPotions > 0)
{
GameEvent event = new GameEvent(GameEvent.Type.OverpayForCard, (MoveContext) context);
event.card = card;
event.newCard = true;
broadcastEvent(event);
}
}
else
{
context.overpayAmount = 0;
context.overpayPotions = 0;
}
context.gold -= (buy.getCost(context) + context.overpayAmount);
if (buy.costPotion()) {
context.potions--;
} else if (!(buy instanceof VictoryCard) && !buy.isKnight() && cost < 5) {
for (int i = 1; i <= context.countCardsInPlay(Cards.talisman); i++) {
if (!buy.isRuins() || (card != null && card.equals(getTopRuinsCard()))) {
context.getPlayer().gainNewCard(buy, Cards.talisman, context);
}
}
}
player.addVictoryTokens(context, context.countGoonsInPlayThisTurn());
if (context.countMerchantGuildsInPlayThisTurn() > 0)
{
player.gainGuildsCoinTokens(context.countMerchantGuildsInPlayThisTurn());
GameEvent event = new GameEvent(GameEvent.Type.GuildsTokenObtained, context);
broadcastEvent(event);
}
if (buy instanceof VictoryCard) {
context.victoryCardsBoughtThisTurn++;
for (int i = 1; i <= context.countCardsInPlay(Cards.hoard); i++) {
player.gainNewCard(Cards.gold, Cards.hoard, context);
}
}
buy.isBought(context);
haggler(context, buy);
}
private void haggler(MoveContext context, Card cardBought) {
if(!context.game.piles.containsKey(Cards.haggler.getName()))
return;
int hagglers = context.countCardsInPlay(Cards.haggler);
int cost = cardBought.getCost(context);
boolean potion = cardBought.costPotion();
List<Card> validCards = new ArrayList<Card>();
for (int i = 0; i < hagglers; i++) {
validCards.clear();
for (Card card : getCardsInGame()) {
if (!(card instanceof VictoryCard) && Cards.isSupplyCard(card) && getCardsLeftInPile(card) > 0) {
int gainCardCost = card.getCost(context);
boolean gainCardPotion = card.costPotion();
if (gainCardCost < cost || (gainCardCost == cost && !gainCardPotion && potion)) {
validCards.add(card);
}
}
}
if (validCards.size() > 0) {
Card toGain = context.getPlayer().controlPlayer.haggler_cardToObtain(context, cost - 1, potion);
if(toGain != null) {
if (!validCards.contains(toGain)) {
Util.playerError(context.getPlayer(), "Invalid card returned from Haggler, ignoring.");
}
else {
context.getPlayer().gainNewCard(toGain, Cards.haggler, context);
}
}
}
}
}
public boolean buyWouldEndGame(Card card) {
if (isColonyInGame() && card.equals(Cards.colony)) {
if (pileSize(card) <= 1) {
return true;
}
}
if (card.equals(Cards.province)) {
if (pileSize(card) <= 1) {
return true;
}
}
if (emptyPiles() >= 2 && pileSize(card) <= 1) {
return true;
}
return false;
}
private boolean checkGameOver() {
if (isColonyInGame() && isPileEmpty(Cards.colony)) {
return true;
}
if (isPileEmpty(Cards.province)) {
return true;
}
switch (numPlayers) {
case 1:
case 2:
case 3:
case 4:
/* Ends game for 1, 2, 3 or 4 players */
if (emptyPiles() >= 3) {
return true;
}
break;
case 5:
case 6:
/* Ends game for 5 or 6 players */
if (emptyPiles() >= 4) {
return true;
}
}
return false;
}
// TODO: all calls should use this but initial turn draws...
boolean drawToHand(Player player, Card responsible) {
return drawToHand(player, responsible, true);
}
boolean drawToHand(Player player, Card responsible, boolean showUI) {
Card card = draw(player);
if (card == null)
return false;
if (responsible != null) {
Util.debug(player, responsible.getName() + " draw:" + card.getName(), true);
}
player.hand.add(card, showUI);
return true;
}
Card draw(Player player) {
if (player.deck.isEmpty()) {
if (player.discard.isEmpty()) {
return null;
} else {
replenishDeck(player);
}
}
return player.deck.remove(0);
}
public void replenishDeck(Player player) {
player.replenishDeck();
MoveContext context = new MoveContext(this, player);
GameEvent event = new GameEvent(GameEvent.Type.DeckReplenished, context);
broadcastEvent(event);
}
private void handleShowEvent(GameEvent event) {
if (showEvents.contains(event.getType())) {
Player player = event.getPlayer();
if (player == null || (player != null && !showPlayers.isEmpty() && !showPlayers.contains(player.getPlayerName()))) {
return;
}
if (event.getType() == GameEvent.Type.TurnEnd) {
Util.debug("");
return;
}
StringBuilder msg = new StringBuilder();
msg.append(player.getPlayerName() + "::" + turnCount + ":" + event.getType());
if (event.getType() == GameEvent.Type.BuyingCard) {
msg.append(":" + event.getContext().getCoinAvailableForBuy() + " gold");
if (event.getContext().getBuysLeft() > 0) {
msg.append(", buys remaining: " + event.getContext().getBuysLeft() + ")");
}
} else if (event.getType() == GameEvent.Type.PlayingAction || event.getType() == GameEvent.Type.TurnBegin
|| event.getType() == GameEvent.Type.NoBuy) {
msg.append(":" + getHandString(player));
} else if (event.attackedPlayer != null) {
msg.append(":" + event.attackedPlayer.getPlayerName() + " with " + event.card.getName());
}
if (event.card != null) {
msg.append(" -> " + event.card.getName());
}
if (event.getComment() != null) {
msg.append(event.getComment());
}
Util.debug(msg.toString());
}
}
int totalCardCount() {
HashMap<String, Integer> cardCounts = new HashMap<String, Integer>();
for (String cardName : piles.keySet()) {
cardCounts.put(cardName, piles.get(cardName).getCount());
}
for (Card card : trashPile) {
cardCounts.put(card.getName(), cardCounts.get(card.getName()) + 1);
}
for (int i = 0; i < numPlayers; i++) {
for (Card card : players[i].getAllCards()) {
cardCounts.put(card.getName(), cardCounts.get(card.getName()) + 1);
}
}
Util.log(cardCounts.toString());
int totalCardCount = 0;
for (Integer v : cardCounts.values()) {
totalCardCount += v;
}
return totalCardCount;
}
void initGameBoard() throws ExitException {
cardSequence = 1;
baneCard = null;
initGameListener();
initCards();
initPlayers(numPlayers);
initPlayerCards();
gameOver = false;
}
protected void initPlayers(int numPlayers) throws ExitException {
initPlayers(numPlayers, true);
}
@SuppressWarnings("unchecked")
protected void initPlayers(int numPlayers, boolean isRandom) throws ExitException {
players = new Player[numPlayers];
cardsObtainedLastTurn = new ArrayList[numPlayers];
for (int i = 0; i < numPlayers; i++) {
cardsObtainedLastTurn[i] = new ArrayList<Card>();
}
ArrayList<String[]> randomize = new ArrayList<String[]>();
while (!playerClassesAndJars.isEmpty()) {
if(isRandom) {
randomize.add(playerClassesAndJars.remove(rand.nextInt(playerClassesAndJars.size())));
} else {
randomize.add(playerClassesAndJars.remove(0));
}
}
playerClassesAndJars = randomize;
playerCache.clear();
playersTurn = 0;
for (int i = 0; i < numPlayers; i++) {
try {
String[] playerStartupInfo = playerClassesAndJars.get(i);
if (playerStartupInfo[1] == null) {
players[i] = (Player) Class.forName(playerStartupInfo[0]).newInstance();
} else {
String key = playerStartupInfo[0] + "::" + playerStartupInfo[1];
// players[i] = cachedPlayers.get(key);
//
// if(players[i] == null) {
// URLClassLoader classLoader = new URLClassLoader(new URL[] { new URL(classAndJar[1]) });
// players[i] = classLoader.loadClass(classAndJar[0]).newInstance();
// cachedPlayers.put(key, players[i]);
// }
Class<?> playerClass = cachedPlayerClasses.get(key);
if (playerClass == null) {
URLClassLoader classLoader = new URLClassLoader(new URL[] { new URL(playerStartupInfo[1]) });
playerClass = classLoader.loadClass(playerStartupInfo[0]);
cachedPlayerClasses.put(key, playerClass);
}
players[i] = (Player) playerClass.newInstance();
}
if(playerStartupInfo[2] != null) {
players[i].setName(playerStartupInfo[2]);
}
//String options = playerStartupInfo[3];
playerCache.put(playerStartupInfo[0], players[i]);
} catch (Exception e) {
Util.log(e);
throw new ExitException();
}
players[i].game = this;
players[i].playerNumber = i;
// Interactive player needs this called once for each player on startup so internal counts work properly.
players[i].getPlayerName();
MoveContext context = new MoveContext(this, players[i]);
players[i].newGame(context);
players[i].initCards();
context = new MoveContext(this, players[i]);
String s = cardListText + "\n---------------\n\n";
if (chanceForPlatColony > -1) {
s += "Chance for Platinum/Colony\n " + (Math.round(chanceForPlatColony * 100)) + "% ... " + (isPlatInGame() ? "included\n" : "not included\n");
}
else
{
s += "Platinum/Colony included...\n";
}
if (baneCard != null) {
s += "Bane card: " + baneCard.getName() + "\n";
}
// When Baker is included in the game, each Player starts with 1 coin token
if (bakerInPlay)
{
players[i].gainGuildsCoinTokens(1);
}
if (alwaysUseShelters || sheltersPassedIn)
{
s += "Shelters included...\n";
}
else
{
s += "Chance for Shelters\n " + (Math.round(chanceForShelters * 100)) + "% ... " + (sheltersInPlay ? "included\n" : "not included\n");
}
s += unfoundCardText;
context.message = s;
broadcastEvent(new GameEvent(GameEvent.Type.GameStarting, context));
}
}
protected void initPlayerCards() {
Player player;
for (int i = 0; i < numPlayers; i++) {
player = players[i];
player.discard(takeFromPile(Cards.copper), null, null);
player.discard(takeFromPile(Cards.copper), null, null);
player.discard(takeFromPile(Cards.copper), null, null);
player.discard(takeFromPile(Cards.copper), null, null);
player.discard(takeFromPile(Cards.copper), null, null);
player.discard(takeFromPile(Cards.copper), null, null);
player.discard(takeFromPile(Cards.copper), null, null);
if (sheltersInPlay)
{
player.discard(takeFromPile(Cards.necropolis), null, null);
player.discard(takeFromPile(Cards.overgrownEstate), null, null);
player.discard(takeFromPile(Cards.hovel), null, null);
// Also need to remove the Estates that were put in the pile prior to
// determining if Shelters would be used
takeFromPile(Cards.estate);
takeFromPile(Cards.estate);
takeFromPile(Cards.estate);
}
else
{
player.discard(takeFromPile(Cards.estate), null, null);
player.discard(takeFromPile(Cards.estate), null, null);
player.discard(takeFromPile(Cards.estate), null, null);
}
if (!equalStartHands || i == 0) {
while (player.hand.size() < 5)
drawToHand(player, null, false);
}
else {
// make subsequent player hands equal
for (int j = 0; j < 5; j++) {
Card card = players[0].hand.get(j);
player.discard.remove(card);
player.hand.add(card);
}
player.replenishDeck();
}
}
// Add tradeRoute tokens if tradeRoute in play
tradeRouteValue = 0;
if (isCardInGame(Cards.tradeRoute)) {
for (AbstractCardPile pile : piles.values()) {
if (pile.card() instanceof VictoryCard) {
pile.setTradeRouteToken();
}
}
}
}
protected void initCards() {
piles.clear();
embargos.clear();
trashPile.clear();
boolean platColonyNotPassedIn = false;
boolean platColonyPassedIn = false;
int provincePileSize = -1;
int curseCount = -1;
int treasureMultiplier = 1;
switch (numPlayers) {
case 1:
case 2:
curseCount = 10;
provincePileSize = 8;
victoryCardPileSize = 8;
break;
case 3:
curseCount = 20;
provincePileSize = 12;
victoryCardPileSize = 12;
break;
case 4:
curseCount = 30;
provincePileSize = 12;
victoryCardPileSize = 12;
break;
case 5:
curseCount = 40;
provincePileSize = 15;
victoryCardPileSize = 12;
treasureMultiplier = 2;
break;
case 6:
curseCount = 50;
provincePileSize = 18;
victoryCardPileSize = 12;
treasureMultiplier = 2;
break;
}
addPile(Cards.gold, 30 * treasureMultiplier);
addPile(Cards.silver, 40 * treasureMultiplier);
addPile(Cards.copper, 60 * treasureMultiplier);
addPile(Cards.curse, curseCount);
addPile(Cards.province, provincePileSize);
addPile(Cards.duchy, victoryCardPileSize);
addPile(Cards.estate, victoryCardPileSize + (3 * numPlayers));
unfoundCards.clear();
int added = 0;
if(cardsSpecifiedAtLaunch != null) {
platColonyNotPassedIn = true;
for(String cardName : cardsSpecifiedAtLaunch) {
Card card = null;
boolean bane = false;
if(cardName.startsWith(BANE)) {
bane = true;
cardName = cardName.substring(BANE.length());
}
/*
StringBuilder sb = new StringBuilder();
for(char c : cardName.toCharArray()) {
if(Character.isLetterOrDigit(c)) {
sb.append(c);
}
}
String s = sb.toString();
for (Card c : Cards.actionCards) {
if(c.getSafeName().equalsIgnoreCase(s)) { // hotspot
card = c;
break;
}
}*/
card = Cards.actionCardsMap.get(cardName);
String s = cardName;
if (card == null) { // maybe we need equalsIgnoreCase
for (Card c : Cards.actionCards) {
if(c.getSafeName().equalsIgnoreCase(s)) {
card = c;
break;
}
}
}
if(card != null && bane) {
baneCard = card;
}
if (cardName.equalsIgnoreCase("Knights")) {
card = Cards.virtualKnight;
}
if(card != null
// && !card.equals(Cards.possession)
) {
addPile(card);
added += 1;
} else if ( s.equalsIgnoreCase(Cards.curse.getSafeName()) ||
s.equalsIgnoreCase(Cards.estate.getSafeName()) ||
s.equalsIgnoreCase(Cards.duchy.getSafeName()) ||
s.equalsIgnoreCase(Cards.province.getSafeName()) ||
s.equalsIgnoreCase(Cards.copper.getSafeName()) ||
s.equalsIgnoreCase(Cards.silver.getSafeName()) ||
s.equalsIgnoreCase(Cards.potion.getSafeName()) ||
s.equalsIgnoreCase(Cards.gold.getSafeName()) ||
s.equalsIgnoreCase(Cards.diadem.getSafeName()) ||
s.equalsIgnoreCase(Cards.followers.getSafeName()) ||
s.equalsIgnoreCase(Cards.princess.getSafeName()) ||
s.equalsIgnoreCase(Cards.trustySteed.getSafeName()) ||
s.equalsIgnoreCase(Cards.bagOfGold.getSafeName()) ) {
// do nothing
} else if ( s.equalsIgnoreCase(Cards.platinum.getSafeName()) ||
s.equalsIgnoreCase(Cards.colony.getSafeName()) ) {
platColonyPassedIn = true;
} else if (s.equalsIgnoreCase("Shelter") ||
s.equalsIgnoreCase(Cards.hovel.getSafeName()) ||
s.equalsIgnoreCase(Cards.overgrownEstate.getSafeName()) ||
s.equalsIgnoreCase(Cards.necropolis.getSafeName()) ) {
sheltersPassedIn = true;
} else {
unfoundCards.add(s);
Util.debug("ERROR::Could not find card:" + s);
}
}
for(String s : unfoundCards) {
if (added >= 10)
break;
Card c = null;
int replacementCost = -1;
if(s.equalsIgnoreCase("blackmarket")) {
replacementCost = 3;
}
else if(s.equalsIgnoreCase("stash")) {
replacementCost = 5;
}
if(replacementCost != -1) {
ArrayList<Card> cardsWithSameCost = new ArrayList<Card>();
for (Card card : Cards.actionCards) {
if(card.getCost(null) == replacementCost && !cardInGame(card)) {
cardsWithSameCost.add(card);
}
}
if(cardsWithSameCost.size() > 0) {
c = cardsWithSameCost.get(rand.nextInt(cardsWithSameCost.size()));
}
}
while(c == null) {
c = Cards.actionCards.get(rand.nextInt(Cards.actionCards.size()));
if(cardInGame(c)) {
c = null;
}
}
addPile(c);
added += 1;
}
gameType = GameType.Specified;
} else {
CardSet cardSet = CardSet.getCardSet(gameType);
if(cardSet == null) {
cardSet = CardSet.getCardSet(CardSet.defaultGameType);
}
for(Card card : cardSet.getCards()) {
this.addPile(card);
}
if(cardSet.getBaneCard() != null) {
this.baneCard = cardSet.getBaneCard();
//Adding the bane card could probably be done in the CardSet class, but it seems better to call it out explicitly.
this.addPile(this.baneCard);
}
}
if (piles.containsKey(Cards.virtualKnight.getName())) {
VariableCardPile kp = (VariableCardPile) this.getPile(Cards.virtualKnight);
for (Card k : Cards.knightsCards) {
kp.addLinkedPile((SingleCardPile) addPile(k, 1, false));
}
}
chanceForShelters = 0.0;
if (alwaysUseShelters || sheltersPassedIn) {
sheltersInPlay = true;
} else {
sheltersInPlay = false;
boolean alreadyCountedKnights = false;
for (AbstractCardPile pile : piles.values())
{
if (pile != null &&
pile.card() != null &&
pile.card().getExpansion() != null &&
pile.card().isShelter() == false &&
pile.card().isRuins() == false &&
(pile.card().isKnight() == false || !alreadyCountedKnights) &&
pile.card().getExpansion().equals("Dark Ages"))
{
chanceForShelters += 0.1;
}
if (chanceForShelters >= 1.0)
{
chanceForShelters = 1.0;
break;
}
if (pile.card().isKnight())
{
alreadyCountedKnights = true;
}
}
if (rand.nextDouble() < chanceForShelters)
{
sheltersInPlay = true;
}
}
if (sheltersInPlay) {
addPile(Cards.necropolis, numPlayers, false);
addPile(Cards.overgrownEstate, numPlayers, false);
addPile(Cards.hovel, numPlayers, false);
}
// Check for PlatColony
boolean addPlatColony = false;
if (alwaysIncludePlatColony || platColonyPassedIn) {
addPlatColony = true;
} else if (platColonyNotPassedIn) {
addPlatColony = false;
} else {
chanceForPlatColony = 0;
for (AbstractCardPile pile : piles.values()) {
if (pile != null && pile.card() != null && pile.card().getExpansion() != null && pile.card().getExpansion().equals("Prosperity")) {
chanceForPlatColony += 0.1;
}
}
if (rand.nextDouble() < chanceForPlatColony) {
addPlatColony= true;
}
}
if (addPlatColony) {
addPile(Cards.platinum, 12);
addPile(Cards.colony);
}
// Add the potion if there are any cards that need them.
for (AbstractCardPile pile : piles.values()) {
if (pile.card().costPotion()) {
addPile(Cards.potion, 16);
break;
}
}
// We have to add one "invisible" pile for each ruins card and a "virtual" visible pile
boolean looter = false;
for (AbstractCardPile pile : piles.values()) {
if (pile.card() instanceof ActionCard && ((ActionCard)pile.card()).isLooter()) {
looter = true;
}
}
if (looter) {
VariableCardPile rp = (VariableCardPile) this.addPile(Cards.virtualRuins, (numPlayers * 10) - 10);
for (Card r : Cards.ruinsCards) {
rp.addLinkedPile((SingleCardPile) this.addPile(r, 10, false));
}
}
if (piles.containsKey(Cards.tournament.getName()) && !piles.containsKey(Cards.bagOfGold.getName())) {
addPile(Cards.bagOfGold, 1, false);
addPile(Cards.diadem, 1, false);
addPile(Cards.followers, 1, false);
addPile(Cards.princess, 1, false);
addPile(Cards.trustySteed, 1, false);
}
// If Bandit Camp, Pillage, or Marauder is in play, we'll need Spoils (non-supply)
if (piles.containsKey(Cards.banditCamp.getName()) ||
piles.containsKey(Cards.pillage.getName()) ||
piles.containsKey(Cards.marauder.getName()))
{
addPile(Cards.spoils, 15, false);
}
// If Urchin is in play, we'll need Mercenary (non-supply)
if (piles.containsKey(Cards.urchin.getName()))
{
addPile(Cards.mercenary, 10, false);
}
// If Hermit is in play, we'll need Madman (non-supply)
if (piles.containsKey(Cards.hermit.getName()))
{
addPile(Cards.madman, 10, false);
}
// If Baker is in play, each player starts with one coin token
if (piles.containsKey(Cards.baker.getName()))
{
bakerInPlay = true;
}
boolean oldDebug = debug;
if (!debug && !showEvents.isEmpty()) {
debug = true;
}
Util.debug("");
Util.debug("Cards in Play", true);
Util.debug("---------------", true);
cardListText += "Cards in play\n---------------\n";
int cost = 0;
while (cost < 10) {
for (AbstractCardPile pile : piles.values()) {
if (!Cards.nonKingdomCards.contains(pile.card())) {
if (pile.card().getCost(null) == cost) {
Util.debug(Util.getShortText(pile.card()), true);
cardListText += Util.getShortText(pile.card()) + "\n";
}
}
}
cost++;
}
if (baneCard != null) {
Util.debug("(Bane) " + Util.getShortText(baneCard), true);
}
Util.debug("");
debug = oldDebug;
if (unfoundCards != null && unfoundCards.size() > 0) {
unfoundCardText += "\n";
String cardList = "";
boolean first = true;
for (String s : unfoundCards) {
if (first) {
first = false;
} else {
cardList += "\n";
}
cardList += s;
}
cardList += "\n\n";
unfoundCardText += "The following cards are not \navailable, so replacements \nhave been used:\n" + cardList;
}
// context = new MoveContext(this, null);
// context.message = "" + ((int) chance * 100) + "% - " +
// (platInPlay?"Yes":"No");
// broadcastEvent(new GameEvent(GameEvent.Type.PlatAndColonyChance,
// context));
}
protected void initGameListener() {
listeners.clear();
gameListener = new GameEventListener() {
public void gameEvent(GameEvent event) {
handleShowEvent(event);
if (event.getType() == GameEvent.Type.GameStarting || event.getType() == GameEvent.Type.GameOver) {
return;
}
if (event.getType() == GameEvent.Type.CardObtained || event.getType() == GameEvent.Type.BuyingCard) {
MoveContext context = event.getContext();
Player player = context.getPlayer();
if (player.isPossessed()) {
possessedBoughtPile.add(event.card);
MoveContext controlContext = new MoveContext(context.game, context.getPlayer().controlPlayer);
controlContext.getPlayer().gainCardAlreadyInPlay(event.card, Cards.possession, controlContext);
return;
}
if (context != null && event.card instanceof VictoryCard) {
context.vpsGainedThisTurn += ((VictoryCard) event.card).getVictoryPoints();
}
if (Cards.inn.equals(event.responsible))
Util.debug((String.format("discard pile: %d", player.discard.size())), true);
// See rules explanation of Tunnel for what commandedDiscard means.
boolean commandedDiscard = true;
if(event.getType() == GameEvent.Type.BuyingCard
|| event.getType() == GameEvent.Type.CardObtained) {
commandedDiscard = false;
} else if(event.responsible != null) {
Card r = event.responsible;
if(r.equals(Cards.borderVillage) ||
r.equals(Cards.feast) ||
r.equals(Cards.remodel) ||
r.equals(Cards.swindler) ||
r.equals(Cards.ironworks) ||
r.equals(Cards.saboteur) ||
r.equals(Cards.upgrade) ||
r.equals(Cards.ambassador) ||
r.equals(Cards.smugglers) ||
r.equals(Cards.talisman) ||
r.equals(Cards.expand) ||
r.equals(Cards.forge) ||
r.equals(Cards.remake) ||
r.equals(Cards.hornOfPlenty) ||
r.equals(Cards.jester) ||
r.equals(Cards.develop) ||
r.equals(Cards.haggler) ||
r.equals(Cards.workshop) ||
r.equals(Cards.hermit) ||
r.equals(Cards.dameNatalie))
{
commandedDiscard = false;
}
}
boolean handled = false;
//Not sure if this is exactly right for the Trader, but it seems to be based on detailed card explanation in the rules
//The handling for new cards is done before taking the card from the pile in a different method below.
if(!event.newCard) {
if(player.hand.contains(Cards.trader)) {
if(player.controlPlayer.trader_shouldGainSilverInstead((MoveContext) context, event.card)) {
player.reveal(Cards.trader, null, context);
player.trash(event.card, Cards.trader, (MoveContext) context);
event.card = Cards.silver;
player.gainNewCard(Cards.silver, Cards.trader, context);
return;
}
}
}
if (event.getPlayer() == players[playersTurn]) {
cardsObtainedLastTurn[playersTurn].add(event.card);
}
if (player.hand.contains(Cards.watchTower)) {
WatchTowerOption choice = context.player.controlPlayer.watchTower_chooseOption((MoveContext) context, event.card);
if (choice == WatchTowerOption.TopOfDeck) {
handled = true;
GameEvent watchTowerEvent = new GameEvent(GameEvent.Type.CardRevealed, context);
watchTowerEvent.card = Cards.watchTower;
watchTowerEvent.responsible = null;
context.game.broadcastEvent(watchTowerEvent);
player.putOnTopOfDeck(event.card, context, true);
} else if (choice == WatchTowerOption.Trash) {
handled = true;
GameEvent watchTowerEvent = new GameEvent(GameEvent.Type.CardRevealed, context);
watchTowerEvent.card = Cards.watchTower;
watchTowerEvent.responsible = null;
context.game.broadcastEvent(watchTowerEvent);
player.trash(event.card, Cards.watchTower, context);
}
}
if(!handled) {
if (context.isRoyalSealInPlay() && context.player.controlPlayer.royalSeal_shouldPutCardOnDeck((MoveContext) context, event.card)) {
player.putOnTopOfDeck(event.card);
} else if (event.card.equals(Cards.nomadCamp)) {
player.putOnTopOfDeck(event.card);
} else if (event.responsible != null) {
Card r = event.responsible;
if (r.equals(Cards.bagOfGold) || r.equals(Cards.develop) || r.equals(Cards.bureaucrat) || r.equals(Cards.seaHag) || r.equals(Cards.treasureMap) || r.equals(Cards.tournament) || r.equals(Cards.foolsGold) || r.equals(Cards.graverobber) || r.equals(Cards.armory)) {
player.putOnTopOfDeck(event.card);
} else if (r.equals(Cards.beggar)) {
if (event.card.equals(Cards.copper)) {
player.hand.add(event.card);
} else if (event.card.equals(Cards.silver) && context.beggarSilverIsOnTop++ == 0) {
player.putOnTopOfDeck(event.card);
} else if (event.card.equals(Cards.silver)) {
player.discard.add(event.card);
}
} else if (r.equals(Cards.tradingPost) || r.equals(Cards.mine) || r.equals(Cards.explorer) || r.equals(Cards.torturer)) {
player.hand.add(event.card);
} else if (r.equals(Cards.illGottenGains) && event.card.equals(Cards.copper)) {
player.hand.add(event.card);
} else {
player.discard(event.card, null, null, commandedDiscard);
}
} else {
player.discard(event.card, null, null, commandedDiscard);
}
}
if (event.card.equals(Cards.illGottenGains)) {
for(Player targetPlayer : getPlayersInTurnOrder()) {
if(targetPlayer != player) {
MoveContext targetContext = new MoveContext(Game.this, targetPlayer);
// TODO: Is this really not an attack? Doesn't seem to be based on card, but not sure...
// targetPlayer.attacked(Cards.illGottenGains, targetContext);
targetPlayer.gainNewCard(Cards.curse, event.card, targetContext);
}
}
} else if(event.card.equals(Cards.province)) {
for(Player targetPlayer : getPlayersInTurnOrder()) {
if(targetPlayer != player) {
int foolsGoldCount = 0;
// Check all of the cards, not just for existence, since there may be more than 1
for(Card c : targetPlayer.hand) {
if(c.equals(Cards.foolsGold)) {
foolsGoldCount++;
}
}
while(foolsGoldCount-- > 0) {
MoveContext targetContext = new MoveContext(Game.this, targetPlayer);
if(targetPlayer.controlPlayer.foolsGold_shouldTrash(targetContext)) {
targetPlayer.hand.remove(Cards.foolsGold);
targetPlayer.trash(Cards.foolsGold, Cards.foolsGold, targetContext);
targetPlayer.gainNewCard(Cards.gold, Cards.foolsGold, targetContext);
}
}
}
}
} else if(event.card.equals(Cards.duchy)) {
if (getCardsLeftInPile(Cards.duchess) > 0) {
if(player.controlPlayer.duchess_shouldGainBecauseOfDuchy((MoveContext) context)) {
player.gainNewCard(Cards.duchess, Cards.duchess, context);
}
}
} else if(event.card.equals(Cards.embassy)) {
for(Player targetPlayer : getPlayersInTurnOrder()) {
if(targetPlayer != player) {
MoveContext targetContext = new MoveContext(Game.this, targetPlayer);
targetPlayer.gainNewCard(Cards.silver, event.card, targetContext);
}
}
} else if(event.card.equals(Cards.cache)) {
for(int i=0; i < 2; i++) {
player.gainNewCard(Cards.copper, event.card, context);
}
} else if(event.card.equals(Cards.inn)) {
ArrayList<Card> cards = new ArrayList<Card>();
int actionCardsFound = 0;
for(int i=player.discard.size() - 1; i >= 0; i--) {
Card c = player.discard.get(i);
if(c instanceof ActionCard) {
actionCardsFound++;
if(player.controlPlayer.inn_shuffleCardBackIntoDeck(event.getContext(), (ActionCard) c)) {
cards.add(c);
}
}
}
Util.debug((String.format("Inn: %d action(s) found in %d-card discard pile", actionCardsFound, player.discard.size())), true);
if (cards.size() > 0) {
for(Card c : cards) {
player.discard.remove(c);
player.deck.add(c);
}
player.shuffleDeck();
}
} else if (event.card.equals(Cards.borderVillage)) {
boolean validCard = false;
for(Card c : event.context.getCardsInGame()) {
if(c.getCost(context) < Cards.borderVillage.getCost(context) && !c.costPotion() && event.context.getCardsLeftInPile(c) > 0) {
validCard = true;
break;
}
}
if(validCard) {
Card card = context.player.controlPlayer.borderVillage_cardToObtain((MoveContext) context);
if (card != null) {
if(card.getCost(context) < Cards.borderVillage.getCost(context) && !card.costPotion()) {
player.controlPlayer.gainNewCard(card, event.card, (MoveContext) context);
}
else {
Util.playerError(player, "Border Village returned invalid card, ignoring.");
}
}
}
} else if (event.card.equals(Cards.mandarin)) {
CardList playedCards = ((MoveContext) context).getPlayedCards();
ArrayList<Card> treasureCardsInPlay = new ArrayList<Card>();
for(Card c : playedCards) {
if(c instanceof TreasureCard) {
treasureCardsInPlay.add(c);
}
}
if(treasureCardsInPlay.size() > 0) {
Card[] order ;
if (treasureCardsInPlay.size() == 1)
order = treasureCardsInPlay.toArray(new Card[treasureCardsInPlay.size()]);
else
order = player.controlPlayer.mandarin_orderCards(context, treasureCardsInPlay.toArray(new Card[treasureCardsInPlay.size()]));
for (int i = order.length - 1; i >= 0; i--) {
Card c = order[i];
player.putOnTopOfDeck(c);
playedCards.remove(c);
}
}
} else if (event.card.equals(Cards.deathCart)) {
context.player.controlPlayer.gainNewCard(Cards.virtualRuins, event.card, context);
context.player.controlPlayer.gainNewCard(Cards.virtualRuins, event.card, context);
}
// Achievement check...
if(event.getType() == GameEvent.Type.BuyingCard && !player.achievementSingleCardFailed) {
if (Cards.isKingdomCard(event.getCard())) {
if(player.achievementSingleCardFirstKingdomCardBought == null) {
player.achievementSingleCardFirstKingdomCardBought = event.getCard();
}
else {
if(!player.achievementSingleCardFirstKingdomCardBought.equals(event.getCard())) {
player.achievementSingleCardFailed = true;
player.achievementSingleCardFirstKingdomCardBought = null;
}
}
}
}
}
boolean shouldShow = (debug || junit);
if (!shouldShow) {
if (event.getType() != GameEvent.Type.TurnBegin && event.getType() != GameEvent.Type.TurnEnd
&& event.getType() != GameEvent.Type.DeckReplenished && event.getType() != GameEvent.Type.GameStarting) {
shouldShow = true;
}
}
if (!showEvents.contains(event.getType()) && shouldShow) {
StringBuilder msg = new StringBuilder();
msg.append(event.getPlayer().getPlayerName() + ":" + event.getType());
if (event.card != null) {
msg.append(":" + event.card.getName());
if (event.card.getControlCard() != event.card) {
msg.append(" <" + event.card.getControlCard().getName() + ">");
}
if (event.card.isImpersonatingAnotherCard()) {
msg.append(" (as " + event.card.behaveAsCard().getName() + ")");
}
}
if (event.getType() == GameEvent.Type.TurnBegin && event.getPlayer().isPossessed()) {
msg.append(" possessed by " + event.getPlayer().controlPlayer.getPlayerName() + "!");
}
if (event.attackedPlayer != null) {
msg.append(", attacking:" + event.attackedPlayer.getPlayerName());
}
if (event.getType() == GameEvent.Type.BuyingCard) {
msg.append(" (with gold: " + event.getContext().getCoinAvailableForBuy() + ", buys remaining: " + event.getContext().getBuysLeft());
}
Util.debug(msg.toString(), true);
}
}
};
}
boolean hasMoat(Player player) {
for (Card card : player.hand) {
if (card.equals(Cards.moat)) {
return true;
}
}
return false;
}
boolean hasLighthouse(Player player) {
for (Card card : player.nextTurnCards) {
if (card.behaveAsCard().equals(Cards.lighthouse) && !((CardImpl) card).trashAfterPlay)
return true;
}
return false;
}
/*
Note that any cards in the supply can have Embargo coins added.
This includes the basic seven cards (Victory, Curse, Treasure),
any of the 10 game piles, and Colony/Platinum when included.
However, this does NOT include any Prizes from Cornucopia.
*/
AbstractCardPile addEmbargo(Card card) {
if (isValidEmbargoPile(card)) {
String name = card.getName();
embargos.put(name, getEmbargos(card) + 1);
return piles.get(name);
}
return null;
}
public boolean isValidEmbargoPile(Card card) {
return !(card == null || !cardInGame(card) || !Cards.isSupplyCard(card) );
}
public int getEmbargos(Card card) {
Integer count = embargos.get(card.getName());
return (count == null) ? 0 : count;
}
// Only is valid for cards in play...
// protected Card readCard(String name) {
// AbstractCardPile pile = piles.get(name);
// if (pile == null || pile.getCount() <= 0) {
// return null;
// }
// return pile.card();
// }
protected Card takeFromPile(Card card) {
if (card.isKnight()) card = Cards.virtualKnight;
if (card.isRuins()) card = Cards.virtualRuins;
AbstractCardPile pile = getPile(card);
if (pile == null || pile.getCount() <= 0) {
return null;
}
Card thisCard;
tradeRouteValue += pile.takeTradeRouteToken();
if (card.equals(Cards.virtualRuins) || card.equals(Cards.virtualKnight)) {
SingleCardPile cp = ((VariableCardPile) pile).getTopLinkedPile();
if (cp == null) return null;
thisCard = cp.removeCard();
pile.removeCard();
} else {
thisCard = pile.removeCard();
}
return thisCard;
}
protected Card takeFromPileCheckTrader(Card cardToGain, MoveContext context) {
if(!isPileEmpty(cardToGain) && context.getPlayer().hand.contains(Cards.trader) && !cardToGain.equals(Cards.silver)) {
if (context.player.controlPlayer.trader_shouldGainSilverInstead((MoveContext) context, cardToGain)) {
cardToGain = Cards.silver;
context.player.reveal(Cards.trader, null, context);
}
}
return takeFromPile(cardToGain);
}
public int pileSize(Card card) {
AbstractCardPile pile = getPile(card);
if (pile == null) {
return -1;
}
return pile.getCount();
}
public boolean isPileEmpty(Card card) {
return pileSize(card) <= 0;
}
public int emptyPiles() {
int emptyPiles = 0;
for (AbstractCardPile pile : piles.values()) {
if (pile.getCount() <= 0 && pile.isSupply()) {
emptyPiles++;
}
}
return emptyPiles;
}
public boolean isCardInGame(Card card) {
AbstractCardPile pile = getPile(card);
if (pile == null) {
return false;
}
return true;
}
public Card[] getCardsInGame() {
return getCardsInGame(null);
}
public Card[] getCardsInGame(Class<?> c) {
ArrayList<Card> cards = new ArrayList<Card>();
for (AbstractCardPile pile : piles.values()) {
if (c == null) {
if (pile.type.equals(AbstractCardPile.PileType.RuinsPile)) {
cards.add(Cards.virtualRuins);
} else if (pile.type.equals(AbstractCardPile.PileType.KnightsPile)) {
cards.add(Cards.virtualKnight);
} else {
cards.add(pile.card());
}
} else if (c.isInstance(pile.card()) && pile.isSupply) {
cards.add(pile.card());
}
}
return cards.toArray(new Card[0]);
}
public Card[] getActionsInGame() {
return getCardsInGame(ActionCard.class);
}
public boolean cardInGame(Card c) {
for (AbstractCardPile pile : piles.values()) {
if(c.equals(pile.card())) {
return true;
}
}
return false;
}
public boolean isPlatInGame() {
return cardInGame(Cards.platinum);
}
public boolean isColonyInGame() {
return cardInGame(Cards.colony);
}
public Card[] getTreasureCardsInGame() {
return getCardsInGame(TreasureCard.class);
}
public Card[] getVictoryCardsInGame() {
return getCardsInGame(VictoryCard.class);
}
public Card[] getCardsInGameOrderByCost() {
Card[] cardsInGame = getCardsInGame();
Arrays.sort(cardsInGame, new CardCostComparator());
return cardsInGame;
}
public int getCardsLeftInPile(Card card) {
AbstractCardPile pile = getPile(card);
if (pile == null || pile.getCount() < 0) {
return 0;
}
return pile.getCount();
}
public ArrayList<Card> GetTrashPile()
{
return trashPile;
}
protected AbstractCardPile addPile(Card card) {
// the Rats hack is dirty and should be cleaned up, but it works
return addPile(card, ((card instanceof VictoryCard) ? victoryCardPileSize : (card.equals(Cards.rats) ? 20 : kingdomCardPileSize)));
}
protected AbstractCardPile addPile(Card card, int count) {
return addPile(card, count, true);
}
protected AbstractCardPile addPile(Card card, int count, boolean isSupply) {
AbstractCardPile pile;
if (card.equals(Cards.virtualRuins)) {
pile = new VariableCardPile(AbstractCardPile.PileType.RuinsPile, Math.max(10, Math.min(50, (numPlayers * 10) - 10)));
} else if (card.equals(Cards.virtualKnight)) {
pile = new VariableCardPile(AbstractCardPile.PileType.KnightsPile, Math.min(Cards.knightsCards.size(), 10));
} else {
pile = new SingleCardPile(card, count);
}
if (!isSupply) {
pile.notInSupply();
}
piles.put(card.getName(), pile);
return pile;
}
private ArrayList<Card> getCardsObtainedByPlayer(int PlayerNumber) {
return cardsObtainedLastTurn[PlayerNumber];
}
public ArrayList<Card> getCardsObtainedByPlayer() {
return getCardsObtainedByPlayer(playersTurn);
}
public ArrayList<Card> getCardsObtainedByLastPlayer() {
int playerOnRight = playersTurn - 1;
if (playerOnRight < 0) {
playerOnRight = numPlayers - 1;
}
return getCardsObtainedByPlayer(playerOnRight);
}
public Player getNextPlayer() {
int next = playersTurn + 1;
if (next >= numPlayers) {
next = 0;
}
return players[next];
}
public Player[] getPlayersInTurnOrder() {
Player[] ordered = new Player[numPlayers];
int at = playersTurn;
for (int i = 0; i < numPlayers; i++) {
ordered[i] = players[at];
at++;
if (at >= numPlayers) {
at = 0;
}
}
return ordered;
}
public void broadcastEvent(GameEvent event) {
for (GameEventListener listener : listeners) {
listener.gameEvent(event);
}
// notify this class' listener last for proper action/logging order
if(gameListener != null)
gameListener.gameEvent(event);
}
String getHandString(Player player) {
String handString = null;
Card[] hand = player.getHand().toArray();
Arrays.sort(hand, new CardCostComparator());
for (Card card : hand) {
if (card == null) {
continue;
}
if (handString == null) {
handString = card.getName();
} else {
handString += ", " + card.getName();
}
}
return handString;
}
public boolean playerShouldSelectCoinsToPlay(MoveContext context, CardList cards) {
if(!quickPlay) {
return true;
}
if(cards == null) {
return false;
}
AbstractCardPile grandMarket = getPile(Cards.grandMarket);
for(Card card : cards) {
if (
card.equals(Cards.philosophersStone) ||
card.equals(Cards.bank) ||
card.equals(Cards.contraband) ||
card.equals(Cards.loan) ||
card.equals(Cards.quarry) ||
card.equals(Cards.talisman) ||
card.equals(Cards.hornOfPlenty) ||
card.equals(Cards.diadem) ||
(card.equals(Cards.copper) && grandMarket != null && grandMarket.getCount() > 0)
)
{
return true;
}
}
return false;
}
static boolean checkForInteractive() throws ExitException {
for (int i = 0; i < numPlayers; i++) {
Player player;
try {
String[] classAndJar = playerClassesAndJars.get(i);
if (classAndJar[1] == null) {
player = (Player) Class.forName(classAndJar[0]).newInstance();
} else {
URLClassLoader classLoader = new URLClassLoader(new URL[] { new URL(classAndJar[1]) });
player = (Player) classLoader.loadClass(classAndJar[0]).newInstance();
}
if(classAndJar[2] != null) {
player.setName(classAndJar[2]);
}
} catch (Exception e) {
Util.log(e);
throw new ExitException();
}
}
return false;
}
/**
* @return Card on top of the Ruins pile
*/
public Card getTopRuinsCard() {
AbstractCardPile p = getPile(Cards.virtualRuins);
if (p == null) return null;
return p.card();
}
public Card getTopKnightCard() {
AbstractCardPile p = getPile(Cards.virtualKnight);
if (p == null) return null;
return p.card();
}
public AbstractCardPile getPile(Card card) {
return piles.get(card.getName());
}
public void trashHovelsInHandOption(Player player, MoveContext context, Card responsible)
{
// If player has a Hovel (or multiple Hovels), offer the option to trash...
ArrayList<Card> hovelsToTrash = new ArrayList<Card>();
for (Card c : player.hand)
{
if (c.getType() == Cards.Type.Hovel && player.controlPlayer.hovel_shouldTrash(context))
{
hovelsToTrash.add(c);
}
}
if (hovelsToTrash.size() > 0)
{
for (Card c : hovelsToTrash)
{
player.hand.remove(c);
player.trash(c, responsible, context);
}
}
}
}