#include "Main.h"
const int debugResourceBonus = 0;
const int debugRandomCardCount = 0;
const char *debugCardName = "gambler";
const int debugCardCount = 0;
bool DecisionState::IsTrivial() const
{
if(type == DecisionSelectCards && cardChoices.Length() == 1 && minimumCards == 1 && maximumCards == 1)
{
return true;
}
return false;
}
DecisionResponse DecisionState::TrivialResponse() const
{
DecisionResponse response;
if(cardChoices.Length() == 1 && minimumCards == 1 && maximumCards == 1)
{
response.cards.Allocate(1);
response.cards[0] = cardChoices[0];
}
return response;
}
void DecisionState::GainCardFromSupply(State &s, Card *c, int minCost, int maxCost, CardFilter filter)
{
SelectCards(c, 1, 1);
if(decisionText) text = "Select a card to gain:";
for(UINT supplyIndex = 0; supplyIndex < s.data->supplyCards.Length(); supplyIndex++)
{
int cost = s.SupplyCost(supplyIndex);
int count = s.supply[supplyIndex].count;
Card *candidate = s.data->supplyCards[supplyIndex];
bool cardAcceptable = (count > 0 && cost >= minCost && cost <= maxCost);
if(filter == FilterTreasure) cardAcceptable &= candidate->isTreasure;
if(cardAcceptable)
{
AddUniqueCard(candidate);
}
}
}
void DecisionState::GainTreasureFromSupply(State &s, Card *c, int minCost, int maxCost)
{
SelectCards(c, 1, 1);
if(decisionText) text = "Select a treasure to gain:";
for(UINT supplyIndex = 0; supplyIndex < s.data->supplyCards.Length(); supplyIndex++)
{
int cost = s.SupplyCost(supplyIndex);
int count = s.supply[supplyIndex].count;
if(count > 0 && cost >= minCost && cost <= maxCost && s.data->supplyCards[supplyIndex]->isTreasure)
{
AddUniqueCard(s.data->supplyCards[supplyIndex]);
}
}
}
void DecisionState::GainVictoryFromSupply(State &s, Card *c, int minCost, int maxCost)
{
SelectCards(c, 1, 1);
if(decisionText) text = "Select a treasure to gain:";
for(UINT supplyIndex = 0; supplyIndex < s.data->supplyCards.Length(); supplyIndex++)
{
int cost = s.SupplyCost(supplyIndex);
int count = s.supply[supplyIndex].count;
if(count > 0 && cost >= minCost && cost <= maxCost && s.data->supplyCards[supplyIndex]->isVictory)
{
AddUniqueCard(s.data->supplyCards[supplyIndex]);
}
}
}
void PlayerState::NewGame(const GameData &data)
{
playArea.FreeMemory();
discard.FreeMemory();
hand.FreeMemory();
deck.FreeMemory();
islandZone.FreeMemory();
if(data.options.startingCondition == StartingCondition34Split)
{
for(UINT copperIndex = 0; copperIndex < 3; copperIndex++) deck.PushEnd(data.cards->GetCard("copper"));
for(UINT estateIndex = 0; estateIndex < 2; estateIndex++) deck.PushEnd(data.cards->GetCard("estate"));
for(UINT copperIndex = 0; copperIndex < 4; copperIndex++) deck.PushEnd(data.cards->GetCard("copper"));
for(UINT estateIndex = 0; estateIndex < 1; estateIndex++) deck.PushEnd(data.cards->GetCard("estate"));
}
else if(data.options.startingCondition == StartingCondition25Split)
{
for(UINT copperIndex = 0; copperIndex < 2; copperIndex++) deck.PushEnd(data.cards->GetCard("copper"));
for(UINT estateIndex = 0; estateIndex < 3; estateIndex++) deck.PushEnd(data.cards->GetCard("estate"));
for(UINT copperIndex = 0; copperIndex < 5; copperIndex++) deck.PushEnd(data.cards->GetCard("copper"));
}
else
{
for(UINT copperIndex = 0; copperIndex < 7; copperIndex++) deck.PushEnd(data.cards->GetCard("copper"));
for(UINT estateIndex = 0; estateIndex < 3; estateIndex++) deck.PushEnd(data.cards->GetCard("estate"));
deck.Randomize();
}
for(UINT debugCardIndex = 0; debugCardIndex < debugCardCount; debugCardIndex++) deck.PushEnd(data.cards->GetCard(debugCardName));
for(UINT randomCards = 0; randomCards < debugRandomCardCount; randomCards++) deck.PushEnd(data.cards->RandomSupplyCard());
actions = 1 + debugResourceBonus;
buys = 1 + debugResourceBonus;
money = 0 + debugResourceBonus;
VPTokens = 0;
turns = 0;
}
int PlayerState::TotalCards() const
{
return deck.Length() + hand.Length() + discard.Length() + playArea.Length() + islandZone.Length();
}
int PlayerState::ActionCount() const
{
int result = 0;
for(Card *c : hand) if(c->isAction) result++;
return result;
}
int PlayerState::TreasureCount() const
{
int result = 0;
for(Card *c : hand) if(c->isTreasure) result++;
return result;
}
int PlayerState::VictoryCount() const
{
int result = 0;
for(Card *c : hand) if(c->isVictory) result++;
return result;
}
int PlayerState::MoneyTotal() const
{
int result = money;
for(Card *c : hand) if(c->isTreasure) result += c->treasure;
return result;
}
UINT State::SupplyCost(int supplyIndex) const
{
return SupplyCost(data->supplyCards[supplyIndex]);
}
UINT State::SupplyCost(Card *c) const
{
int cost = c->cost;
if(c == data->baseCards.peddler && phase == PhaseBuy)
{
int actionCardCount = 0;
for(const CardPlayInfo &c : players[player].playArea) if(c.card->isAction) actionCardCount++;
cost = Math::Max(0, cost - actionCardCount * 2);
}
if(data->bridgeInSupply)
{
const Vector<CardPlayInfo> &playArea = players[player].playArea;
for(UINT playIndex = 0; playIndex < playArea.Length(); playIndex++)
{
if(playArea[playIndex].card == data->baseCards.bridge) cost -= playArea[playIndex].copies;
}
if(cost < 0) cost = 0;
}
if(data->quarryInSupply)
{
const Vector<CardPlayInfo> &playArea = players[player].playArea;
for(UINT playIndex = 0; playIndex < playArea.Length(); playIndex++)
{
if(c->isAction && playArea[playIndex].card == data->baseCards.quarry) cost -= 2;
}
if(cost < 0) cost = 0;
}
if(data->plunderInSupply)
{
const Vector<CardPlayInfo> &playArea = players[player].playArea;
for(UINT playIndex = 0; playIndex < playArea.Length(); playIndex++)
{
if(c->isTreasure && playArea[playIndex].card == data->baseCards.plunder) cost -= 2;
}
if(cost < 0) cost = 0;
}
return cost;
}
UINT State::SupplyCount(Card *c) const
{
int supplyIndex = data->SupplyIndex(c);
if(supplyIndex == -1) return 0;
return supply[supplyIndex].count;
}
void State::NewGame(const GameData &_data)
{
data = &_data;
const UINT playerCount = data->players.Length();
for(UINT playerIndex = 0; playerIndex < playerCount; playerIndex++)
{
players[playerIndex].NewGame(*data);
DrawCards(playerIndex, 5);
}
player = rand() % playerCount;
players[player].turns = 1;
phase = PhaseAction;
stack.FreeMemory();
gainList.FreeMemory();
prevGainList.FreeMemory();
if(logging)
{
data->log.Reset();
data->log("New Game");
data->log("Players: " + data->players[0].name + ", " + data->players[1].name);
data->log("< " + data->players[player].name + "'s turn -- Turn 1>");
}
int victoryCardCount = 8;
if(playerCount >= 3)
{
victoryCardCount = 12;
}
tradeRouteValue = 0;
for(UINT supplyIndex = 0; supplyIndex < maxSupply; supplyIndex++) supply[supplyIndex].count = 0;
for(UINT supplyIndex = 0; supplyIndex < data->supplyCards.Length(); supplyIndex++)
{
Card *c = data->supplyCards[supplyIndex];
if(c->isVictory)
{
supply[supplyIndex].count = victoryCardCount;
supply[supplyIndex].tradeRouteToken = 1;
}
else
{
supply[supplyIndex].count = data->supplyCards[supplyIndex]->supply;
supply[supplyIndex].tradeRouteToken = 0;
}
}
supply[data->SupplyIndex(data->baseCards.copper)].count = 60 - 7 * playerCount;
supply[data->SupplyIndex(data->baseCards.curse)].count = 10 * (playerCount - 1);
decision.type = DecisionNone;
decision.text.FreeMemory();
AdvanceToNextDecision(0);
}
void State::Shuffle(UINT playerIndex)
{
if(logging) Log(playerIndex, "shuffles");
PlayerState &curPlayer = players[playerIndex];
curPlayer.deck.Append(curPlayer.discard);
curPlayer.discard.FreeMemory();
curPlayer.deck.Randomize();
}
void State::DrawCards(UINT playerIndex, UINT cardCount)
{
for(UINT cardIndex = 0; cardIndex < cardCount; cardIndex++) DrawCard(playerIndex);
}
Card* State::DrawCard(UINT playerIndex)
{
PlayerState &curPlayer = players[playerIndex];
if(curPlayer.deck.Length() == 0)
{
Shuffle(playerIndex);
}
if(curPlayer.deck.Length() > 0)
{
Card *c = curPlayer.deck.Last();
if(logging) LogIndent(1, playerIndex, "draws " + c->PrettyName());
curPlayer.hand.PushEnd(c);
curPlayer.deck.PopEnd();
return c;
}
else
{
if(logging) Log(playerIndex, "tries to draw, but has no cards left");
return NULL;
}
}
void State::DiscardCard(UINT playerIndex, Card *c)
{
PlayerState &p = players[playerIndex];
if(logging) Log(playerIndex, "discards " + c->PrettyName());
int index = p.hand.FindFirstIndex(c);
p.hand.RemoveSwap(index);
p.discard.PushEnd(c);
}
void State::PlayCard(UINT playerIndex, Card *c)
{
PlayerState &p = players[playerIndex];
if(logging) Log(playerIndex, "plays " + c->PrettyName());
int index = p.hand.FindFirstIndex(c);
p.hand.RemoveSwap(index);
int turnsLeft = 0;
if(c->isDuration) turnsLeft = 1;
if(c->isPermanent) turnsLeft = 999;
p.playArea.PushEnd(CardPlayInfo(c, turnsLeft));
}
void State::ReorderDeck(Card *source, UINT playerIndex, UINT cardCount)
{
for(UINT reorderIndex = 2; reorderIndex <= cardCount; reorderIndex++)
{
stack.PushEnd(new EventReorderDeck(source, playerIndex, reorderIndex));
}
}
void State::ProcessAction(Card *c)
{
PlayerState &p = players[player];
p.actions += c->actions;
p.buys += c->buys;
p.money += c->money;
p.VPTokens += c->victoryTokens;
if(logging)
{
if(c->victoryTokens > 0) LogIndent(1, "gains " + String(c->victoryTokens) + " VP");
if(c->victoryTokens < 0) LogIndent(1, "loses " + String(c->victoryTokens) + " VP");
if(c->money > 0) LogIndent(1, "gets $" + String(c->money));
if(c->money < 0) LogIndent(1, "pays $" + String(-c->money));
}
for(int cardIndex = 0; cardIndex < c->cards; cardIndex++)
{
DrawCard(player);
}
if(c->isAttack && data->championInSupply)
{
for(const PlayerInfo &p : data->players)
{
if(p.index != player)
{
for(CardPlayInfo &c : players[p.index].playArea)
{
if(c.card == data->baseCards.champion)
{
for(int copyIndex = 0; copyIndex < c.copies; copyIndex++)
{
if(logging) LogIndent(1, p.index, "gains 1 VP from " + data->baseCards.champion->PrettyName());
players[p.index].VPTokens++;
}
}
}
}
}
}
if(c->effect != NULL)
{
c->effect->PlayAction(*this);
}
}
void State::ProcessTreasure(Card *c)
{
PlayerState &p = players[player];
int treasureValue = c->treasure;
if(data->coppersmithInSupply)
{
const Vector<CardPlayInfo> &playArea = players[player].playArea;
for(UINT playIndex = 0; playIndex < playArea.Length(); playIndex++)
{
if(playArea[playIndex].card == data->baseCards.coppersmith) treasureValue += playArea[playIndex].copies;
}
}
if(c == data->baseCards.bank)
{
for(const CardPlayInfo &c : players[player].playArea) if(c.card->isTreasure) treasureValue++;
}
if(logging) LogIndent(1, "gets $" + String(treasureValue));
p.money += treasureValue;
p.buys += c->buys;
if(c->effect != NULL)
{
c->effect->PlayAction(*this);
}
}
void State::ProcessDecision(const DecisionResponse &response)
{
if(decision.type == DecisionGameOver) return;
Assert(decision.type != DecisionNone, "No decision active");
Card *singleCard = response.singleCard;
if(decision.type == DecisionSelectCards && decision.maximumCards <= 1)
{
if(response.cards.Length() == 1) singleCard = response.cards[0];
Assert(response.cards.Length() <= 1, "Invalid number of cards in response");
Assert(decision.minimumCards == 0 || singleCard != NULL, "No response chosen");
}
else
{
Assert(decision.type != DecisionSelectCards || (response.cards.Length() >= decision.minimumCards && response.cards.Length() <= decision.maximumCards), "Invalid number of cards in response");
Assert(decision.type != DecisionDiscreteChoice || (response.choice < decision.minimumCards), "Invalid choice");
}
decision.type = DecisionNone;
PlayerState &p = players[player];
if(decision.activeCard == NULL)
{
if(phase == PhaseAction)
{
if(singleCard == NULL)
{
if(logging) Log("chooses not to play an action");
phase = PhaseTreasure;
}
else
{
PlayCard(player, singleCard);
p.actions--;
ProcessAction(singleCard);
}
}
else if(phase == PhaseTreasure)
{
if(singleCard == NULL)
{
if(logging) Log("chooses not to play a teasure");
phase = PhaseBuy;
BuyPhaseStart();
}
else
{
PlayCard(player, singleCard);
ProcessTreasure(singleCard);
}
}
else if(phase == PhaseBuy)
{
if(singleCard == NULL)
{
if(logging) Log("chooses not to buy a card");
phase = PhaseCleanup;
}
else
{
data->players[player].ledger.RecordBuy(singleCard);
if(data->hoardInSupply && singleCard->isVictory)
{
const Vector<CardPlayInfo> &playArea = players[player].playArea;
for(UINT playIndex = 0; playIndex < playArea.Length(); playIndex++)
{
if(playArea[playIndex].card == data->baseCards.hoard) stack.PushEnd(new EventGainCard(player, data->baseCards.gold, false, false, GainToDiscard));
}
}
if(data->gardenerInSupply && singleCard->isVictory)
{
const Vector<CardPlayInfo> &playArea = players[player].playArea;
for(UINT playIndex = 0; playIndex < playArea.Length(); playIndex++)
{
if(playArea[playIndex].card == data->baseCards.gardener)
{
players[player].VPTokens++;
if(logging) LogIndent(1, "gains 1 VP from " + data->baseCards.gardener->PrettyName());
}
}
}
if(data->goonsInSupply)
{
const Vector<CardPlayInfo> &playArea = players[player].playArea;
for(UINT playIndex = 0; playIndex < playArea.Length(); playIndex++)
{
if(playArea[playIndex].card == data->baseCards.goons)
{
players[player].VPTokens++;
if(logging) LogIndent(1, "gains 1 VP from " + data->baseCards.goons->PrettyName());
}
}
}
if(data->mintInSupply && singleCard == data->baseCards.mint)
{
Vector<CardPlayInfo> &playArea = players[player].playArea;
Vector<Card*> treasuresToTrash;
for(UINT playIndex = 0; playIndex < playArea.Length(); playIndex++)
{
if(playArea[playIndex].card->isTreasure) treasuresToTrash.PushEnd(playArea[playIndex].card);
}
for(Card *c : treasuresToTrash) stack.PushEnd(new EventTrashCardFromPlay(player, c));
}
stack.PushEnd(new EventGainCard(player, singleCard, true, false, GainToDiscard));
}
}
}
else
{
if(decision.activeCard->effect != NULL &&
decision.activeCard->effect->CanProcessDecisions())
{
decision.activeCard->effect->ProcessDecision(*this, response);
}
else if(stack.Length() > 0 && stack.Last()->CanProcessDecisions())
{
stack.Last()->ProcessDecision(*this, response);
}
else
{
SignalError("Decision cannot be processed");
}
}
if(decision.controllingPlayer == -1) decision.controllingPlayer = player;
decision.maximumCards = Math::Min(decision.maximumCards, decision.cardChoices.Length());
}
void State::AdvancePhase()
{
PlayerState &p = players[player];
if(phase == PhaseAction)
{
if(p.ActionCount() == 0 || p.actions == 0) phase = PhaseTreasure;
else
{
if(decisionText) decision.text = "Choose an action to play:";
decision.SelectCards(NULL, 0, 1);
for(Card *c : p.hand)
{
if(c->isAction) decision.AddUniqueCard(c);
}
}
}
if(phase == PhaseTreasure)
{
if(p.TreasureCount() == 0)
{
phase = PhaseBuy;
BuyPhaseStart();
}
else
{
if(decisionText) decision.text = "Choose a treasure to play:";
decision.SelectCards(NULL, 0, 1);
bool bankPresent = false;
for(Card *c : p.hand)
{
if(c->isTreasure)
{
if(c == data->baseCards.bank) bankPresent = true;
else decision.AddUniqueCard(c);
}
}
if(bankPresent) decision.AddUniqueCard(data->baseCards.bank);
}
}
if(phase == PhaseBuy && stack.Length() == 0)
{
if(p.buys == 0) phase = PhaseCleanup;
else
{
if(decisionText) decision.text = "Choose a card to buy:";
decision.SelectCards(NULL, 0, 1);
for(UINT supplyIndex = 0; supplyIndex < data->supplyCards.Length(); supplyIndex++)
{
int cost = SupplyCost(supplyIndex);
int count = supply[supplyIndex].count;
if(cost <= p.money && count > 0)
{
Card *c = data->supplyCards[supplyIndex];
bool canBuyCard = true;
if(c == data->baseCards.grandMarket && p.CardInPlay(data->baseCards.copper))
{
canBuyCard = false;
}
if(c == data->baseCards.grandCourt)
{
for(CardPlayInfo &c : p.playArea)
{
if(c.card->isAction) canBuyCard = false;
}
}
if(canBuyCard) decision.cardChoices.PushEnd(c);
}
}
if(decision.cardChoices.Length() == 0)
{
decision.type = DecisionNone;
phase = PhaseCleanup;
if(logging) Log("cannot afford to buy any cards");
}
}
}
if(phase == PhaseCleanup)
{
while(p.hand.Length() > 0)
{
DiscardCard(player, p.hand.Last());
}
Vector<CardPlayInfo> newPlayArea;
for(CardPlayInfo &c : p.playArea)
{
if(c.turnsLeft >= 1)
{
CardPlayInfo newInfo = c;
newInfo.turnsLeft = c.turnsLeft - 1;
newPlayArea.PushEnd(newInfo);
}
else
{
bool discardCard = true;
if(data->treasuryInSupply && c.card == data->baseCards.treasury)
{
bool victoryCardGained = false;
for(Card *gainedCard : gainList)
{
if(gainedCard->isVictory) victoryCardGained = true;
}
if(!victoryCardGained)
{
discardCard = false;
if(logging) Log("puts " + c.card->PrettyName() + " on top of their deck");
p.deck.PushEnd(c.card);
}
}
if(discardCard) p.discard.PushEnd(c.card);
}
}
if(newPlayArea.Length() > 0) p.playArea = newPlayArea;
else p.playArea.FreeMemory();
if(logging) Log("draws a new hand");
int cardsToDraw = 5;
if(data->promisedLandInSupply)
{
for(CardPlayInfo &c : p.playArea)
{
if(c.card == data->baseCards.promisedLand)
{
cardsToDraw -= c.copies;
}
}
if(cardsToDraw < 0) cardsToDraw = 0;
}
for(int cardIndex = 0; cardIndex < cardsToDraw; cardIndex++)
{
DrawCard(player);
}
if(logging) Log("ends their turn\n");
CheckEndConditions();
if(decision.type == DecisionGameOver) return;
player = (player + 1) % playerMaximum;
phase = PhaseAction;
PlayerState &newPlayer = players[player];
newPlayer.actions = 1 + debugResourceBonus;
newPlayer.buys = 1 + debugResourceBonus;
newPlayer.money = 0 + debugResourceBonus;
newPlayer.turns++;
if(data->useGainList)
{
prevGainList.FreeMemory();
prevGainList = move(gainList);
}
if(logging) data->log("< " + data->players[player].name + "'s turn - Turn " + String(newPlayer.turns) + " >");
for(CardPlayInfo &c : players[player].playArea)
{
for(int copyIndex = 0; copyIndex < c.copies; copyIndex++)
{
if(c.card->effect) c.card->effect->ProcessDuration(*this);
}
}
}
}
void State::AdvanceToNextDecision(UINT recursionDepth)
{
if(recursionDepth >= 500)
{
SignalError("Excessive recusion depth -- top of stack is probably stuck");
}
if(decision.type == DecisionGameOver) return;
if(decision.type != DecisionNone)
{
if(decision.IsTrivial())
{
ProcessDecision(decision.TrivialResponse());
AdvanceToNextDecision(recursionDepth + 1);
}
return;
}
if(stack.Length() == 0)
{
AdvancePhase();
}
else
{
UINT eventStackIndex = stack.Length() - 1;
Event *curEvent = stack[eventStackIndex];
bool skipEventProcessing = false;
bool lighthouseActive = false;
if(curEvent->IsAttack()) {
Assert(curEvent->AttackedPlayer() != -1, "Invalid player");
const PlayerState &p = players[curEvent->AttackedPlayer()];
AttackAnnotations &annotations = *curEvent->Annotations();
if(!annotations.moatProcessed && p.hand.Contains(data->baseCards.moat))
{
stack.PushEnd(new EventMoatReveal(data->baseCards.moat, curEvent->AttackedPlayer()));
annotations.moatProcessed = true;
skipEventProcessing = true;
}
if(p.CardInPlay(data->baseCards.lighthouse))
{
if(logging) LogIndent(1, curEvent->AttackedPlayer(), " is protected from the attack by " + data->baseCards.lighthouse->PrettyName());
Event *nextEvent = stack.Last();
stack.PopEnd();
delete nextEvent;
skipEventProcessing = true;
}
}
if(!skipEventProcessing)
{
bool eventCompleted = curEvent->Advance(*this);
if(eventCompleted)
{
bool destroyNextEvent = lighthouseActive || curEvent->DestroyNextEventOnStack();
stack.RemoveSlow(eventStackIndex);
delete curEvent;
if(destroyNextEvent)
{
Event *nextEvent = stack.Last();
stack.PopEnd();
delete nextEvent;
}
}
}
}
if(decision.type == DecisionNone) AdvanceToNextDecision(recursionDepth + 1);
if(decision.controllingPlayer == -1) decision.controllingPlayer = player;
decision.maximumCards = Math::Min(decision.maximumCards, decision.cardChoices.Length());
if(decision.IsTrivial())
{
ProcessDecision(decision.TrivialResponse());
AdvanceToNextDecision(recursionDepth + 1);
}
Assert(decisionText || decision.text.Length() == 0, "Decision text is disabled!");
if(decision.type == DecisionSelectCards && decision.cardChoices.Length() == 0)
{
ofstream debugFile("debug.txt");
debugFile << decision.activeCard->name << ' ' << decision.minimumCards << "," << decision.maximumCards << endl;
debugFile.flush();
debugFile.close();
__asm int 3;
}
}
void State::Log(const String &s)
{
Assert(logging, "Logging is disabled!");
data->log(data->players[player].name + " " + s);
}
void State::Log(UINT playerIndex, const String &s)
{
Assert(logging, "Logging is disabled!");
data->log(data->players[playerIndex].name + " " + s);
}
void State::LogIndent(UINT indentLevel, const String &s)
{
Assert(logging, "Logging is disabled!");
String preamble;
for(UINT i = 0; i < indentLevel; i++) preamble += " ";
data->log(preamble + data->players[player].name + " " + s);
}
void State::LogIndent(UINT indentLevel, UINT playerIndex, const String &s)
{
Assert(logging, "Logging is disabled!");
String preamble;
for(UINT i = 0; i < indentLevel; i++) preamble += " ";
data->log(preamble + data->players[playerIndex].name + " " + s);
}
int State::PlayerScore(UINT playerIndex) const
{
const PlayerState &p = players[playerIndex];
int sum = p.VPTokens;
for(Card *c : p.hand)
{
sum += c->victoryPoints;
if(c->isVictory && c->effect != NULL) sum += c->effect->VictoryPoints(*this, playerIndex);
}
for(Card *c : p.deck)
{
sum += c->victoryPoints;
if(c->isVictory && c->effect != NULL) sum += c->effect->VictoryPoints(*this, playerIndex);
}
for(Card *c : p.discard)
{
sum += c->victoryPoints;
if(c->isVictory && c->effect != NULL) sum += c->effect->VictoryPoints(*this, playerIndex);
}
for(Card *c : p.islandZone)
{
sum += c->victoryPoints;
if(c->isVictory && c->effect != NULL) sum += c->effect->VictoryPoints(*this, playerIndex);
}
for(const CardPlayInfo &c : p.playArea)
{
sum += c.card->victoryPoints;
if(c.card->isVictory && c.card->effect != NULL) sum += c.card->effect->VictoryPoints(*this, playerIndex);
}
return sum;
}
Vector<int> State::WinningPlayers() const
{
Vector<int> result;
double bestScore = -100000.0;
for(const PlayerInfo &player : data->players)
{
int score = PlayerScore(player.index);
double trueScore = score + 1.0 - players[player.index].turns * 0.0001;
if(trueScore > bestScore)
{
bestScore = trueScore;
result.Allocate(1, player.index);
}
else if(trueScore == bestScore)
{
result.PushEnd(player.index);
}
}
return result;
}
void State::CheckEndConditions()
{
if(players[player].turns >= 100 || EmptySupplyPiles() >= 3 || SupplyCount(data->baseCards.province) == 0 ||
(data->supplyCards.Contains(data->baseCards.colony) && SupplyCount(data->baseCards.colony) == 0))
{
decision.type = DecisionGameOver;
if(logging)
{
data->log("\n\n\n");
double bestScore = -100000.0;
for(const PlayerInfo &player : data->players)
{
data->log(player.name + ": " + String(PlayerScore(player.index)) + " VP");
}
for(int playerIndex : WinningPlayers())
{
data->log(data->players[playerIndex].name + " is the winner!!");
}
data->log("\n\n");
}
}
}
UINT State::EmptySupplyPiles() const
{
UINT result = 0;
for(UINT supplyIndex = 0; supplyIndex < data->supplyCards.Length(); supplyIndex++)
{
if(supply[supplyIndex].count == 0) result++;
}
return result;
}
UINT State::ActionsPlayedThisTurn() const
{
int result = 0;
const PlayerState &p = players[player];
for(const CardPlayInfo &c : p.playArea)
{
if(c.card->isDuration)
{
if(c.turnsLeft == 1) result++;
}
if(c.card->isPermanent)
{
if(c.turnsLeft == 999) result++;
}
else result++;
}
return result;
}
void State::BuyPhaseStart()
{
if(data->furnaceInSupply)
{
for(CardPlayInfo &c : players[player].playArea)
{
if(c.card == data->baseCards.furnace)
{
for(int copyIndex = 0; copyIndex < c.copies; copyIndex++) stack.PushEnd(new EventDiscardNCards(c.card, player, 0, 1));
}
}
}
}