using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading; using DominionBase.Cards; using DominionBase.Piles; namespace DominionBase.Players { public abstract class PlayerMessage { public String Message { get; set; } public AutoResetEvent ReturnEvent { get; set; } public PlayerMessage() { } public PlayerMessage(String message) : this(null, message) { } public PlayerMessage(AutoResetEvent returnEvent) { ReturnEvent = returnEvent; } public PlayerMessage(AutoResetEvent returnEvent, String message) : this(returnEvent) { Message = message; } } public class PlayerResponseMessage : PlayerMessage { } public class PlayerChoiceMessage : PlayerMessage { public Player Player { get; set; } public ChoiceResult ChoiceResult { get; set; } public PlayerChoiceMessage() { } public PlayerChoiceMessage(Player player, ChoiceResult choiceResult) : this(null, player, choiceResult) { } public PlayerChoiceMessage(AutoResetEvent returnEvent, Player player, ChoiceResult choiceResult) : base(returnEvent) { Player = player; ChoiceResult = choiceResult; } } public enum DeckLocation { Hand, Revealed, Discard, Deck, Tableau, PreviousTableau, PlayerMat, Private } public enum PlayerType { Human, Computer } public delegate void TakeTurn(Game game, Player player); public abstract class Player : IDisposable { #region Delegates & Events public delegate void AttackedEventHandler(object sender, AttackedEventArgs e); public virtual event AttackedEventHandler Attacked = null; public delegate void CardsDrawnEventHandler(object sender, CardsDrawnEventArgs e); public virtual event CardsDrawnEventHandler CardsDrawn = null; public delegate void CardsAddedToDeckEventHandler(object sender, CardsAddedToDeckEventArgs e); public virtual event CardsAddedToDeckEventHandler CardsAddedToDeck = null; public delegate void CardsAddedToHandEventHandler(object sender, CardsAddedToHandEventArgs e); public virtual event CardsAddedToHandEventHandler CardsAddedToHand = null; public delegate void CardsDiscardingEventHandler(object sender, CardsDiscardEventArgs e); public virtual event CardsDiscardingEventHandler CardsDiscarding = null; public delegate void CardsDiscardedEventHandler(object sender, CardsDiscardEventArgs e); public virtual event CardsDiscardedEventHandler CardsDiscarded = null; public delegate void CardBuyingEventHandler(object sender, CardBuyEventArgs e); public virtual event CardBuyingEventHandler CardBuying = null; public delegate void CardBoughtEventHandler(object sender, CardBuyEventArgs e); public virtual event CardBoughtEventHandler CardBought = null; public delegate void CardBuyFinishedEventHandler(object sender, CardBuyEventArgs e); public virtual event CardBuyFinishedEventHandler CardBuyFinished = null; public delegate void CardGainingEventHandler(object sender, CardGainEventArgs e); public virtual event CardGainingEventHandler CardGaining = null; public delegate void CardGainedEventHandler(object sender, CardGainEventArgs e); public virtual event CardGainedEventHandler CardGained = null; public delegate void CardGainedIntoEventHandler(object sender, CardGainEventArgs e); public virtual event CardGainedIntoEventHandler CardGainedInto = null; public delegate void CardGainFinishedEventHandler(object sender, CardGainEventArgs e); public virtual event CardGainFinishedEventHandler CardGainFinished = null; public delegate void CardReceivedEventHandler(object sender, CardReceivedEventArgs e); public virtual event CardReceivedEventHandler CardReceived = null; public delegate void CardsLostEventHandler(object sender, CardsLostEventArgs e); public virtual event CardsLostEventHandler CardsLost = null; public delegate void CardPlayingEventHandler(object sender, CardPlayingEventArgs e); public virtual event CardPlayingEventHandler CardPlaying = null; public delegate void CardPlayedEventHandler(object sender, CardPlayedEventArgs e); public virtual event CardPlayedEventHandler CardPlayed = null; public delegate void PhaseChangingEventHandler(object sender, PhaseChangingEventArgs e); public virtual event PhaseChangingEventHandler PhaseChanging = null; public delegate void PhaseChangedEventHandler(object sender, PhaseChangedEventArgs e); public virtual event PhaseChangedEventHandler PhaseChanged = null; public delegate void TrashingEventHandler(object sender, TrashEventArgs e); public virtual event TrashingEventHandler Trashing = null; public delegate void TrashedEventHandler(object sender, TrashEventArgs e); public virtual event TrashedEventHandler Trashed = null; public delegate void TrashedFinishedEventHandler(object sender, TrashEventArgs e); public virtual event TrashedFinishedEventHandler TrashedFinished = null; public delegate void TurnStartingEventHandler(object sender, TurnStartingEventArgs e); public virtual event TurnStartingEventHandler TurnStarting = null; public delegate void TurnStartedEventHandler(object sender, TurnStartedEventArgs e); public virtual event TurnStartedEventHandler TurnStarted = null; public delegate void ShufflingEventHandler(object sender, ShuffleEventArgs e); public virtual event ShufflingEventHandler Shuffling = null; public delegate void ShuffledEventHandler(object sender, ShuffleEventArgs e); public virtual event ShuffledEventHandler Shuffled = null; public delegate void CleaningUpEventHandler(object sender, CleaningUpEventArgs e); public virtual event CleaningUpEventHandler CleaningUp = null; public delegate void CleanedUpEventHandler(object sender, CleanedUpEventArgs e); public virtual event CleanedUpEventHandler CleanedUp = null; public delegate void TurnEndedEventHandler(object sender, TurnEndedEventArgs e); public virtual event TurnEndedEventHandler TurnEnded = null; public delegate void BenefitReceivingEventHandler(object sender, BenefitReceivingEventArgs e); public virtual event BenefitReceivingEventHandler BenefitReceiving = null; public delegate void BenefitsChangedEventHandler(object sender, BenefitsChangedEventArgs e); public virtual event BenefitsChangedEventHandler BenefitsChanged = null; public virtual event Token.TokenActionEventHandler TokenActedOn = null; #endregion protected PlayerType _PlayerType; private String _Name = String.Empty; internal Game _Game = null; private Deck _Hand = new Deck(DeckLocation.Hand, Visibility.All, VisibilityTo.All, new DominionBase.Cards.Sorting.ByTypeName(DominionBase.Cards.Sorting.SortDirection.Descending), true); private Deck _DrawPile = new Deck(DeckLocation.Deck, Visibility.None, VisibilityTo.All); private Deck _DiscardPile = new Deck(DeckLocation.Discard, Visibility.Top, VisibilityTo.All); private Deck _Tableau = new Deck(DeckLocation.Tableau, Visibility.All, VisibilityTo.All, null, true); private Deck _PreviousTableau = new Deck(DeckLocation.PreviousTableau, Visibility.All, VisibilityTo.All, null, true); private Deck _Revealed = new Deck(DeckLocation.Revealed, Visibility.All, VisibilityTo.All, null, true); private Deck _Private = new Deck(DeckLocation.Private, Visibility.All, VisibilityTo.Owner, null, true); private PhaseEnum _PhaseEnum = PhaseEnum.Waiting; private int _Actions = 1; private int _Buys = 1; private Currency _Currency = new Currency(); private int _VictoryChits = 0; private int _ActionsPlayed = 0; private CardMats _PlayerMats = new CardMats(); private TokenCollections _TokenPiles = new TokenCollections(); public Choose Choose = null; public TakeTurn TakeTurn = null; private Turn _CurrentTurn = null; protected Boolean _AsynchronousDrawing = false; protected CardsDrawnEventArgs _AsynchronousCardsDrawnEventArgs = null; private Queue _MessageRequestQueue = new Queue(); private Queue _MessageResponseQueue = new Queue(); public Queue MessageRequestQueue { get { return _MessageRequestQueue; } } public Queue MessageResponseQueue { get { return _MessageResponseQueue; } } public AutoResetEvent WaitEvent = new AutoResetEvent(false); public Player(Game game, String name) { _Game = game; _Name = name; } #region IDisposable variables, properties, & methods // Track whether Dispose has been called. private bool disposed = false; public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. if (!this.disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if (disposing) { // Dispose managed resources. this._Currency = null; this._CurrentTurn = null; this._DiscardPile = null; this._DrawPile = null; this._Game = null; this._Hand = null; this._MessageRequestQueue = null; this._MessageResponseQueue = null; this._PlayerMats = null; this._PreviousTableau = null; this._Private = null; this._Revealed = null; this._Tableau = null; this._TokenPiles = null; } // Call the appropriate methods to clean up // unmanaged resources here. // If disposing is false, // only the following code is executed. // Note disposing has been done. disposed = true; } } ~Player() { Dispose(false); } #endregion public void InitializeDeck() { this.Gain(_Game.Table.Copper, 7); this.Gain(_Game.Table.Estate, 3); } public void FinalizeDeck() { DrawHand(5); } /// /// Fires off the Attack event, so any listeners can pick it up. /// /// The attacking player /// The card that triggered the Attack event /// "true" if the Attack can proceed (wasn't blocked). "false" if the Attack was blocked. /// public Boolean AttackedBy(Player attacker, Card attackingCard) { if (Attacked != null) { AttackedEventArgs aea = null; Boolean cancelled = false; do { aea = new AttackedEventArgs(attacker, attackingCard); aea.Cancelled |= cancelled; Attacked(this, aea); CardCollection cards = new CardCollection(aea.Revealable.Values.Select(s => s.Card)); if (cards.Count > 0) { cards.Sort(); Choice choice = new Choice("Reveal a card?", null, attackingCard, cards, this, true, aea); ChoiceResult result = this.MakeChoice(choice); if (result.Cards.Count > 0) aea.Revealable[result.Cards[0].CardType].Method(this, ref aea); else break; } cancelled |= aea.Cancelled; } while (Attacked != null && aea.HandledBy.Count > 0); if (aea != null) cancelled |= aea.Cancelled; return !cancelled; } return true; } /// /// Fires off the TokenAction event, so any listeners can pick it up. /// /// The acting player (the one playing the card) /// The card that triggered the TokenAction event /// "true" if the TokenAction can proceed (wasn't blocked). "false" if the TokenAction was blocked. public Boolean TokenActOn(Player actor, Card actingCard) { if (TokenActedOn != null) { TokenActionEventArgs taea = new TokenActionEventArgs(actor, this, actingCard); TokenActedOn(this, taea); return !taea.Cancelled; } return true; } public PlayerType PlayerType { get { return _PlayerType; } } public String Name { get { return _Name; } } public Deck Hand { get { return _Hand; } } public Deck DrawPile { get { return _DrawPile; } } public Deck DiscardPile { get { return _DiscardPile; } } public Deck Tableau { get { return _Tableau; } } public Deck PreviousTableau { get { return _PreviousTableau; } } public Deck Revealed { get { return _Revealed; } } public Deck Private { get { return _Private; } } public CardMats PlayerMats { get { return _PlayerMats; } } public TokenCollections TokenPiles { get { return _TokenPiles; } } public Turn CurrentTurn { get { return _CurrentTurn; } } public ChoiceResult MakeChoice(Choice choice) { PhaseEnum previous = this.Phase; Phase = PhaseEnum.Choosing; CardCollection cards = null; ChoiceResult result = null; switch (choice.ChoiceType) { case ChoiceType.Options: if (!choice.IsOrdered && choice.Minimum >= choice.Options.Count) result = new ChoiceResult(choice.Options); break; case ChoiceType.Cards: //if (choice.IsSpecific && choice.Minimum == 1 && choice.Cards.Count() == 1) // result = new ChoiceResult(new CardCollection(choice.Cards)); //else IEnumerable nonDummyCards = choice.Cards.Where(c => c.CardType != Cards.Universal.TypeClass.Dummy); if (nonDummyCards.Count() == 0) cards = new CardCollection(); else if (!choice.IsOrdered && choice.Minimum >= nonDummyCards.Count()) result = new ChoiceResult(new CardCollection(nonDummyCards)); else if (choice.Maximum == 0) cards = new CardCollection(); else if (choice.Maximum == choice.Minimum && (!choice.IsOrdered || nonDummyCards.Count(card => card.CardType == nonDummyCards.ElementAt(0).CardType) == nonDummyCards.Count())) cards = new CardCollection(nonDummyCards); break; case ChoiceType.Supplies: if (choice.Supplies.Count == 1) result = new ChoiceResult(choice.Supplies.Values.First()); else if (choice.Supplies.Count == 0) result = new ChoiceResult(); break; case ChoiceType.SuppliesAndCards: if (choice.Supplies.Count == 1 && choice.Cards.Count() == 0) result = new ChoiceResult(choice.Supplies.Values.First()); else if (choice.Supplies.Count == 0 && choice.Cards.Count() == 1) result = new ChoiceResult(new CardCollection { choice.Cards.First() }); else if (choice.Supplies.Count == 0) result = new ChoiceResult(); break; default: throw new Exception("Unable to do anything with this Choice Type!"); } if (result == null && cards != null) { if (cards.All(delegate(Card c) { return c.CardType == cards[0].CardType; })) result = new ChoiceResult(new CardCollection(cards.Take(choice.Minimum))); } if (result == null) { Thread choiceThread = new Thread(delegate() { Choose.Invoke(this, choice); }); choiceThread.Start(); WaitEvent.WaitOne(); choiceThread = null; if (_MessageRequestQueue.Count > 0) { lock (_MessageRequestQueue) { PlayerMessage message = _MessageRequestQueue.Dequeue(); //System.Diagnostics.Trace.WriteLine(String.Format("Message: {0}", message.Message)); PlayerMessage response = new PlayerResponseMessage(); response.Message = "ACK"; if (message is PlayerChoiceMessage) result = ((PlayerChoiceMessage)message).ChoiceResult; lock (_MessageResponseQueue) { _MessageResponseQueue.Enqueue(response); } if (message.ReturnEvent != null) message.ReturnEvent.Set(); } } } this.Phase = previous; return result; } public PhaseEnum Phase { get { return _PhaseEnum; } private set { try { Boolean phaseChanged = false; PhaseEnum newValue = value; PhaseEnum oldValue = _PhaseEnum; // If we're going into the Action phase and there are no Action cards, // then we can immediately go to the Treasure-playing phase. if (newValue == PhaseEnum.Action && (this.Actions == 0 || Hand[Category.Action].Count == 0)) { newValue = PhaseEnum.Treasure; } // Similarly, if we're going into the Treasure-playing phase and there // are no Treasure cards, then we can immediately go to the Buy phase. if (newValue == PhaseEnum.Treasure && Hand[Category.Treasure].Count == 0) { newValue = PhaseEnum.Buy; } if (_PhaseEnum != newValue) { phaseChanged = true; if (PhaseChanging != null) { PhaseChangingEventArgs pcea = new PhaseChangingEventArgs(this, newValue); PhaseChanging(this, pcea); if (pcea.Cancelled) return; } } _PhaseEnum = newValue; // Perform benefits of Duration cards if (newValue == PhaseEnum.Starting) { CardCollection cc = new CardCollection(_PreviousTableau); foreach (Card card in cc) card.PlayDuration(this); _PreviousTableau.Refresh(this); } //if (newValue == PhaseEnum.Cleanup) //{ // this.PerformCleanup(); //} if (phaseChanged && PhaseChanged != null) { PhaseChangedEventArgs pcea = new PhaseChangedEventArgs(this, oldValue); PhaseChanged(this, pcea); } } catch (Exception ex) { Utilities.Logging.LogError(ex); throw; } } } public int Actions { get { return _Actions; } internal set { _Actions = value; if (BenefitsChanged != null) { BenefitsChangedEventArgs bcea = new BenefitsChangedEventArgs(this); BenefitsChanged(this, bcea); } } } public int Buys { get { return _Buys; } protected set { _Buys = value; if (BenefitsChanged != null) { BenefitsChangedEventArgs bcea = new BenefitsChangedEventArgs(this); BenefitsChanged(this, bcea); } // Done buying cards -- no more left if (value == 0 && this.Phase == PhaseEnum.Buy) this.Cleanup(); } } public Currency Currency { get { if (_PhaseEnum == PhaseEnum.Starting || _PhaseEnum == PhaseEnum.Action || _PhaseEnum == PhaseEnum.Treasure || _PhaseEnum == PhaseEnum.Buy || _PhaseEnum == PhaseEnum.Playing || _PhaseEnum == PhaseEnum.Choosing) return _Currency; return new Currency(); } protected set { _Currency = value; if (BenefitsChanged != null) { BenefitsChangedEventArgs bcea = new BenefitsChangedEventArgs(this); BenefitsChanged(this, bcea); } } } public int VictoryChits { get { return _VictoryChits; } protected set { _VictoryChits = value; } } public int ActionsPlayed { get { return _ActionsPlayed; } } internal void ActionPlayed() { _ActionsPlayed++; this.Actions--; } public int VictoryPoints { get { int vp = _VictoryChits; if (this.Phase == PhaseEnum.Endgame) lock (_Hand) vp += _Hand.VictoryPoints; lock (_Tableau) vp += _Tableau.VictoryPoints; lock (_Revealed) vp += _Revealed.VictoryPoints; lock (_PreviousTableau) vp += _PreviousTableau.VictoryPoints; lock (_Private) vp += _Private.VictoryPoints; foreach (Deck deck in this._PlayerMats.Values) lock (deck) vp += deck.VictoryPoints; return vp; } } internal virtual void Clear() { this._Game = null; this._Hand.Clear(); this._DrawPile.Clear(); this._DiscardPile.Clear(); this._Tableau.Clear(); this._PreviousTableau.Clear(); this._Revealed.Clear(); this._Private.Clear(); this._PlayerMats.Clear(); this._TokenPiles.Clear(); if (this._CurrentTurn != null) this._CurrentTurn.Clear(); this._MessageRequestQueue.Clear(); this._MessageResponseQueue.Clear(); } internal virtual void TearDown() { this._Hand.TearDown(); this._DrawPile.TearDown(); this._DiscardPile.TearDown(); this._Tableau.TearDown(); this._PreviousTableau.TearDown(); this._Revealed.TearDown(); this._Private.TearDown(); this._PlayerMats.TearDown(); this._TokenPiles.TearDown(); } public void TestFireAllEvents() { if (Attacked != null) Attacked(this, new AttackedEventArgs(null, null)); if (BenefitReceiving != null) BenefitReceiving(this, new BenefitReceivingEventArgs(null, null)); if (BenefitsChanged != null) BenefitsChanged(this, new BenefitsChangedEventArgs(null)); CardBuyEventArgs cbea = new CardBuyEventArgs(null, null); if (CardBought != null) CardBought(this, cbea); if (CardBuyFinished != null) CardBuyFinished(this, cbea); if (CardBuying != null) CardBuyFinished(this, cbea); CardGainEventArgs cgea = new CardGainEventArgs(null, null, DeckLocation.Discard, DeckPosition.Automatic, false); if (CardGained != null) CardGained(this, cgea); if (CardGainedInto != null) CardGainedInto(this, cgea); if (CardGainFinished != null) CardGainFinished(this, cgea); if (CardGaining != null) CardGaining(this, cgea); if (CardPlayed != null) CardPlayed(this, new CardPlayedEventArgs(null, new Cards.Universal.Copper())); if (CardPlaying != null) CardPlaying(this, new CardPlayingEventArgs(null, new Cards.Universal.Copper(), null)); if (CardReceived != null) CardReceived(this, new CardReceivedEventArgs(null, null, DeckLocation.Discard, DeckPosition.Automatic)); if (CardsAddedToDeck != null) CardsAddedToDeck(this, new CardsAddedToDeckEventArgs(new Cards.Universal.Copper(), DeckPosition.Automatic)); if (CardsAddedToHand != null) CardsAddedToHand(this, new CardsAddedToHandEventArgs(new Cards.Universal.Copper())); if (CardsDiscarded != null) CardsDiscarded(this, new CardsDiscardEventArgs(DeckLocation.Discard, new Cards.Universal.Copper())); if (CardsDiscarding != null) CardsDiscarding(this, new CardsDiscardEventArgs(DeckLocation.Discard, new Cards.Universal.Copper())); if (CardsDrawn != null) CardsDrawn(this, new CardsDrawnEventArgs(null, DeckPosition.Automatic, 0)); if (CardsLost != null) CardsLost(this, new CardsLostEventArgs(null)); if (CleanedUp != null) CleanedUp(this, new CleanedUpEventArgs(null, 0)); if (CleaningUp != null) { CardMovementCollection cmc = new CardMovementCollection(); CleaningUp(this, new CleaningUpEventArgs(null, 0, ref cmc)); } if (PhaseChanged != null) PhaseChanged(this, new PhaseChangedEventArgs(null, PhaseEnum.Endgame)); if (PhaseChanging != null) PhaseChanging(this, new PhaseChangingEventArgs(null, PhaseEnum.Endgame)); if (Shuffling != null) Shuffling(this, new ShuffleEventArgs(null)); if (Shuffled != null) Shuffled(this, new ShuffleEventArgs(null)); if (Trashing != null) Trashing(this, new TrashEventArgs(null, null)); if (Trashed != null) Trashed(this, new TrashEventArgs(null, null)); if (TrashedFinished != null) TrashedFinished(this, new TrashEventArgs(null, null)); if (TurnEnded != null) TurnEnded(this, new TurnEndedEventArgs(null)); if (TurnStarted != null) TurnStarted(this, new TurnStartedEventArgs(null)); if (TurnStarting != null) TurnStarting(this, new TurnStartingEventArgs(null)); } public Deck ResolveDeck(DeckLocation location) { switch (location) { case DeckLocation.Hand: return this.Hand; case DeckLocation.Revealed: return this.Revealed; case DeckLocation.Discard: return this.DiscardPile; case DeckLocation.Deck: return this.DrawPile; case DeckLocation.Tableau: return this.Tableau; case DeckLocation.PreviousTableau: return this.PreviousTableau; case DeckLocation.Private: return this.Private; } return null; } public DeckPosition ResolveDeckPosition(DeckLocation location, DeckPosition position) { switch (location) { case DeckLocation.Hand: return position == DeckPosition.Automatic ? DeckPosition.Bottom : position; case DeckLocation.Revealed: return position == DeckPosition.Automatic ? DeckPosition.Bottom : position; case DeckLocation.Discard: return position == DeckPosition.Automatic ? DeckPosition.Top : position; case DeckLocation.Deck: return position == DeckPosition.Automatic ? DeckPosition.Top : position; case DeckLocation.Tableau: return position == DeckPosition.Automatic ? DeckPosition.Bottom : position; case DeckLocation.PreviousTableau: return position == DeckPosition.Automatic ? DeckPosition.Bottom : position; case DeckLocation.PlayerMat: return position == DeckPosition.Automatic ? DeckPosition.Bottom : position; case DeckLocation.Private: return position == DeckPosition.Automatic ? DeckPosition.Bottom : position; } return position; } public virtual void BeginDrawing() { this.Revealed.BeginChanges(); _AsynchronousDrawing = true; _AsynchronousCardsDrawnEventArgs = null; } public virtual void EndDrawing() { _AsynchronousDrawing = false; if (_AsynchronousCardsDrawnEventArgs != null && CardsDrawn != null) { CardsDrawn(this, _AsynchronousCardsDrawnEventArgs); } _AsynchronousCardsDrawnEventArgs = null; this.Revealed.EndChanges(); } public Boolean CanDraw { get { return _DrawPile.Count + _DiscardPile.Count > 0; } } public void DrawHand(int number) { Draw(number, DeckLocation.Hand); Phase = PhaseEnum.Waiting; } public Card Draw(DeckLocation destination) { return Draw(1, destination).FirstOrDefault(); } public CardCollection Draw(int number, DeckLocation destination) { CardCollection cardsDrawn = DrawFrom(DeckPosition.Top, number, destination); return cardsDrawn; } public Card Draw(Type deckType) { return Draw(1, deckType).FirstOrDefault(); } public CardCollection Draw(int number, Type deckType) { CardCollection cardsDrawn = DrawFrom(DeckPosition.Top, number, deckType); return cardsDrawn; } public CardCollection DrawFrom(DeckPosition deckPosition, int number, Object destination) { CardCollection cards = new CardCollection(); if (number <= 0) return cards; CardCollection cardsFirst = _DrawPile.Retrieve(this, deckPosition, c => true, number); cards.AddRange(cardsFirst); cards.RemovedFrom(DeckLocation.Deck, this); if (_AsynchronousDrawing) { if (_AsynchronousCardsDrawnEventArgs == null) _AsynchronousCardsDrawnEventArgs = new CardsDrawnEventArgs(cardsFirst, deckPosition, number); else _AsynchronousCardsDrawnEventArgs.Cards.AddRange(cardsFirst); } else if (CardsDrawn != null) { CardsDrawnEventArgs cdea = new CardsDrawnEventArgs(cardsFirst, deckPosition, number); CardsDrawn(this, cdea); } if (destination is Type) this.AddCardsInto((Type)destination, cardsFirst); else if (destination is DeckLocation) this.AddCardsInto((DeckLocation)destination, cardsFirst); else throw new Exception(String.Format("Destination of {0} ({1}) is not supported", destination, destination.GetType())); if (cardsFirst.Count < number && _DrawPile.Count == 0 && _DiscardPile.Count > 0) { this.ShuffleForDrawing(); CardCollection cardsSecond = _DrawPile.Retrieve(this, deckPosition, c => true, number < 0 ? number : number - cards.Count); cards.AddRange(cardsSecond); cardsSecond.RemovedFrom(DeckLocation.Deck, this); if (_AsynchronousDrawing) { if (_AsynchronousCardsDrawnEventArgs == null) _AsynchronousCardsDrawnEventArgs = new CardsDrawnEventArgs(cardsSecond, deckPosition, number); else _AsynchronousCardsDrawnEventArgs.Cards.AddRange(cardsSecond); } else if (CardsDrawn != null) { CardsDrawnEventArgs cdea = new CardsDrawnEventArgs(cardsSecond, deckPosition, number); CardsDrawn(this, cdea); } if (destination is Type) this.AddCardsInto((Type)destination, cardsSecond); else if (destination is DeckLocation) this.AddCardsInto((DeckLocation)destination, cardsSecond); else throw new Exception(String.Format("Destination of {0} ({1}) is not supported", destination, destination.GetType())); } return cards; } protected void ShuffleForDrawing() { this.AddCardsInto(DeckLocation.Deck, this.RetrieveCardsFrom(DeckLocation.Discard), DeckPosition.Bottom); ShuffleDrawPile(); } internal void ShuffleDrawPile() { if (Shuffling != null) { ShuffleEventArgs sea = new ShuffleEventArgs(this); Shuffling(this, sea); } _DrawPile.Shuffle(); if (Shuffled != null) { ShuffleEventArgs sea = new ShuffleEventArgs(this); Shuffled(this, sea); } } public void AddCardToDeck(Card card, DeckPosition deckPosition) { AddCardInto(DeckLocation.Deck, card, deckPosition); if (CardsAddedToDeck != null) { CardsAddedToDeckEventArgs catdea = new CardsAddedToDeckEventArgs(card, deckPosition); CardsAddedToDeck(this, catdea); } } public void AddCardsToDeck(IEnumerable cards, DeckPosition deckPosition) { AddCardsInto(DeckLocation.Deck, cards, deckPosition); if (CardsAddedToDeck != null) { CardsAddedToDeckEventArgs catdea = new CardsAddedToDeckEventArgs(cards, deckPosition); CardsAddedToDeck(this, catdea); } } public void AddCardToHand(Card card) { AddCardInto(DeckLocation.Hand, card, DeckPosition.Bottom); if (CardsAddedToHand != null) { CardsAddedToHandEventArgs cathea = new CardsAddedToHandEventArgs(card); CardsAddedToHand(this, cathea); } } public void AddCardsToHand(IEnumerable cards) { AddCardsInto(DeckLocation.Hand, cards, DeckPosition.Bottom); if (CardsAddedToHand != null) { CardsAddedToHandEventArgs cathea = new CardsAddedToHandEventArgs(cards); CardsAddedToHand(this, cathea); } } public void AddCardsToHand(DeckLocation location) { this.AddCardsToHand(this.RetrieveCardsFrom(location)); } public void Discard(DeckLocation fromLocation) { Discard(fromLocation, -1); } public void Discard(DeckLocation fromLocation, int count) { Discard(fromLocation, c => true, count); } public void Discard(DeckLocation fromLocation, Card card) { Discard(fromLocation, c => c == card); } public void Discard(DeckLocation fromLocation, IEnumerable cards) { Discard(fromLocation, c => cards.Contains(c)); } public void Discard(DeckLocation fromLocation, Type cardType, int count) { Discard(fromLocation, c => c.CardType == cardType, count); } public void Discard(DeckLocation fromLocation, Predicate match) { Discard(fromLocation, match, -1); } public void Discard(DeckLocation fromLocation, Predicate match, int count) { CardCollection matchingCards = this.ResolveDeck(fromLocation)[match]; if (count >= 0 && count < matchingCards.Count) { matchingCards.RemoveRange(count, matchingCards.Count - count); if (matchingCards.Count != count) throw new Exception("Incorrect number of cards found!"); } if (matchingCards.Count == 0) return; if (CardsDiscarding != null) { CardsDiscardEventArgs cdea = null; List handledBy = new List(); Boolean actionPerformed = false; Boolean cancelled = false; do { actionPerformed = false; cdea = new CardsDiscardEventArgs(fromLocation, matchingCards); cdea.Cancelled = cancelled; cdea.HandledBy.AddRange(handledBy); CardsDiscarding(this, cdea); handledBy = cdea.HandledBy; matchingCards = cdea.Cards; cancelled |= cdea.Cancelled; List options = new List(); IEnumerable cardTypes = cdea.Actions.Keys; Boolean anyOptional = cdea.Actions.Any(a => !a.Value.IsMandatory); foreach (Type key in cardTypes) options.Add(cdea.Actions[key].Text); if (options.Count > 0) { options.Sort(); Choice choice = new Choice(String.Format("You are discarding {0}", Utilities.StringUtility.Plural("card", matchingCards.Count)), options, this, anyOptional, cdea); ChoiceResult result = this.MakeChoice(choice); if (result.Options.Count > 0) { cdea.Actions.First(kvp => kvp.Value.Text == result.Options[0]).Value.Method(this, ref cdea); actionPerformed = true; handledBy = cdea.HandledBy; matchingCards = cdea.Cards; cancelled |= cdea.Cancelled; } } } while (CardsDiscarding != null && actionPerformed); if (cancelled) return; } this.RetrieveCardsFrom(fromLocation, matchingCards); this.AddCardsInto(DeckLocation.Discard, matchingCards); if (CardsDiscarded != null) { CardsDiscardEventArgs cdea = null; List handledBy = new List(); Boolean actionPerformed = false; do { actionPerformed = false; cdea = new CardsDiscardEventArgs(fromLocation, matchingCards); cdea.HandledBy.AddRange(handledBy); CardsDiscarded(this, cdea); handledBy = cdea.HandledBy; List options = new List(); IEnumerable cardTypes = cdea.Actions.Keys; foreach (Type key in cardTypes) options.Add(cdea.Actions[key].Text); if (options.Count > 0) { options.Sort(); Choice choice = new Choice(String.Format("You discarded {0}", Utilities.StringUtility.Plural("card", matchingCards.Count)), options, this, true, cdea); ChoiceResult result = this.MakeChoice(choice); if (result.Options.Count > 0) { cdea.Actions.First(kvp => kvp.Value.Text == result.Options[0]).Value.Method(this, ref cdea); actionPerformed = true; } } } while (CardsDiscarded != null && actionPerformed); } } public void AddCardInto(DeckLocation location, Card card) { AddCardInto(location, card, DeckPosition.Automatic); } public void AddCardInto(DeckLocation location, Card card, DeckPosition position) { AddCardsInto(location, new CardCollection() { card }, position); } public void AddCardsInto(DeckLocation location, IEnumerable cards) { AddCardsInto(location, cards, DeckPosition.Automatic); } public void AddCardsInto(DeckLocation location, IEnumerable cards, DeckPosition position) { if (this.Phase != PhaseEnum.Endgame) { foreach (Card card in cards) card.AddedTo(location, this); } DeckPosition realPosition = ResolveDeckPosition(location, position); switch (location) { case DeckLocation.Hand: foreach (Card card in cards) card.ModifiedBy = null; _Hand.AddRange(this, cards, realPosition); break; case DeckLocation.Revealed: foreach (Card card in cards) card.ModifiedBy = null; _Revealed.AddRange(this, cards, realPosition); break; case DeckLocation.Discard: foreach (Card card in cards) card.ModifiedBy = null; _DiscardPile.AddRange(this, cards, realPosition); break; case DeckLocation.Deck: foreach (Card card in cards) card.ModifiedBy = null; _DrawPile.AddRange(this, cards, realPosition); break; case DeckLocation.Tableau: _Tableau.AddRange(this, cards, realPosition); break; case DeckLocation.PreviousTableau: _PreviousTableau.AddRange(this, cards, realPosition); break; case DeckLocation.Private: foreach (Card card in cards) card.ModifiedBy = null; _Private.AddRange(this, cards, realPosition); break; } } public void AddCardInto(Type deckType, Card card) { AddCardInto(deckType, card, DeckPosition.Automatic); } public void AddCardInto(Type deckType, Card card, DeckPosition position) { AddCardsInto(deckType, new CardCollection() { card }, position); } public void AddCardsInto(Type deckType, IEnumerable cards) { AddCardsInto(deckType, cards, DeckPosition.Automatic); } public void AddCardsInto(Type deckType, IEnumerable cards, DeckPosition position) { if (cards.Count() == 0) return; foreach (Card card in cards) { card.ModifiedBy = null; card.AddedTo(deckType, this); } PlayerMats.Add(this, deckType, cards); } internal Card RetrieveCardFrom(DeckLocation location, Card card) { CardCollection cc = RetrieveCardsFrom(location, DeckPosition.Automatic, c => c == card, -1); if (cc.Count == 1) return cc[0]; throw new Exception("Found incorrect number of cards. Should return exactly 1"); } internal CardCollection RetrieveCardsFrom(DeckLocation location) { return RetrieveCardsFrom(location, DeckPosition.Automatic, c => true, -1); } internal CardCollection RetrieveCardsFrom(DeckLocation location, CardCollection cards) { return new CardCollection(RetrieveCardsFrom(location, DeckPosition.Automatic, c => cards.Contains(c), -1).OrderBy(c => cards.IndexOf(c))); } internal CardCollection RetrieveCardsFrom(DeckLocation location, Category cardType) { return RetrieveCardsFrom(location, cardType, -1); } internal CardCollection RetrieveCardsFrom(DeckLocation location, Category cardType, int count) { return RetrieveCardsFrom(location, DeckPosition.Automatic, c => (c.Category & cardType) == cardType, count); } internal CardCollection RetrieveCardsFrom(DeckLocation location, Type type, int count) { return RetrieveCardsFrom(location, DeckPosition.Automatic, c => c.CardType == type, count); } internal CardCollection RetrieveCardsFrom(DeckLocation location, Predicate match) { return RetrieveCardsFrom(location, DeckPosition.Automatic, match, -1); } internal CardCollection RetrieveCardsFrom(DeckLocation location, DeckPosition position, Predicate match, int count) { CardCollection cards; switch (location) { case DeckLocation.Hand: cards = _Hand.Retrieve(this, position, match, count); break; case DeckLocation.Revealed: cards = _Revealed.Retrieve(this, position, match, count); break; case DeckLocation.Discard: cards = _DiscardPile.Retrieve(this, position, match, count); break; case DeckLocation.Deck: cards = _DrawPile.Retrieve(this, position, match, count); if (cards.Count < count && _DrawPile.Count == 0 && _DiscardPile.Count > 0) this.ShuffleForDrawing(); cards.AddRange(_DrawPile.Retrieve(this, position, match, count < 0 ? count : count - cards.Count)); break; case DeckLocation.Tableau: cards = _Tableau.Retrieve(this, position, match, count); break; case DeckLocation.PreviousTableau: cards = _PreviousTableau.Retrieve(this, position, match, count); break; case DeckLocation.Private: cards = _Private.Retrieve(this, position, match, count); break; default: cards = new CardCollection(); break; } cards.RemovedFrom(location, this); return cards; } internal CardCollection RetrieveCardsFrom(Type deckType) { return RetrieveCardsFrom(deckType, c => true, -1); } internal CardCollection RetrieveCardsFrom(Type deckType, Predicate match) { return RetrieveCardsFrom(deckType, match, -1); } internal CardCollection RetrieveCardsFrom(Type deckType, Predicate match, int count) { CardCollection cards = PlayerMats.Retrieve(this, deckType, match, count); cards.RemovedFrom(deckType, this); return cards; } /// /// Short-hand for discarding all cards in Hand /// public void DiscardHand(Boolean visible) { if (visible) this.Discard(DeckLocation.Hand); else this.AddCardsInto(DeckLocation.Discard, this.RetrieveCardsFrom(DeckLocation.Hand)); } /// /// Short-hand for discarding all Revealed cards /// public void DiscardRevealed() { this.Discard(DeckLocation.Revealed); } /// /// Short-hand for discarding all Revealed cards matching the Predicate /// public void DiscardRevealed(Predicate match) { this.Discard(DeckLocation.Revealed, match); } /// /// Short-hand for revealing Hand /// public CardCollection RevealHand() { CardCollection hand = this.RetrieveCardsFrom(DeckLocation.Hand); this.AddCardsInto(DeckLocation.Revealed, hand); return hand; } /// /// Short-hand for returning Revealed to Hand. This version also doesn't trigger the CardsAddedToHandEventArgs event /// public void ReturnHand(CardCollection hand) { this.AddCardsInto(DeckLocation.Hand, this.RetrieveCardsFrom(DeckLocation.Revealed, hand), DeckPosition.Bottom); } private CardGainEventArgs CardGainCheckAllowed(Card card, DeckLocation location, DeckPosition position, Boolean isBought) { Boolean cancelled = false; CardGainEventArgs cgea = new CardGainEventArgs(_Game, card, location, position, isBought); if (CardGaining != null) { do { cgea = new CardGainEventArgs(_Game, card, location, position, isBought); cgea.Cancelled = cancelled; CardGaining(this, cgea); Boolean isAnyRequired = false; List options = new List(); IEnumerable cardTypes = cgea.Actions.Keys; foreach (Type key in cardTypes) { options.Add(cgea.Actions[key].Text); isAnyRequired |= cgea.Actions[key].IsRequired; } if (options.Count > 0) { options.Sort(); Choice choice = new Choice(String.Format("You are gaining {0}", card), null, new CardCollection() { card }, options, this, cgea, false, isAnyRequired ? 1 : 0, 1); ChoiceResult result = this.MakeChoice(choice); if (result.Options.Count > 0) cgea.Actions.First(kvp => kvp.Value.Text == result.Options[0]).Value.Method(this, ref cgea); } cancelled = cgea.Cancelled; location = cgea.Location; position = cgea.Position; } while (CardGaining != null && cgea.HandledBy.Count > 0); } return cgea; } public Boolean Gain(Trash trash, Card card) { return Gain(trash, card, DeckLocation.Discard, DeckPosition.Automatic); } public Boolean Gain(Trash trash, Card card, DeckLocation location, DeckPosition position) { if (trash.Contains(card)) { CardGainEventArgs cgea = CardGainCheckAllowed(card, location, position, false); if (!cgea.Cancelled) return this.Gain(trash.Retrieve(this, card), cgea.Location, cgea.Position, cgea.Bought); else { CardGainFinish(card, cgea.Location, cgea.Position, cgea.Bought, cgea.Cancelled); return true; } } return false; } public Boolean Gain(Supply supply) { return this.Gain(supply, supply.TopCard == null ? null : supply.TopCard.CardType, DeckLocation.Discard, DeckPosition.Automatic, false); } public Boolean Gain(Supply supply, int count) { Boolean cancelled = false; for (int i = 0; i < count; i++) cancelled |= this.Gain(supply, supply.TopCard == null ? null : supply.TopCard.CardType, DeckLocation.Discard, DeckPosition.Automatic, false); return cancelled; } public Boolean Gain(Supply supply, Boolean isBought) { return this.Gain(supply, supply.TopCard == null ? null : supply.TopCard.CardType, DeckLocation.Discard, DeckPosition.Automatic, isBought); } public Boolean Gain(Supply supply, DeckLocation location, DeckPosition position) { return Gain(supply, supply.TopCard == null ? null : supply.TopCard.CardType, location, position, false); } public Boolean Gain(Supply supply, DeckLocation location, DeckPosition position, int count) { Boolean cancelled = false; for (int i = 0; i < count; i++) cancelled |= this.Gain(supply, supply.TopCard == null ? null : supply.TopCard.CardType, location, position, false); return cancelled; } public Boolean Gain(Supply supply, Type cardType, DeckLocation location, DeckPosition position) { return Gain(supply, cardType, location, position, false); } public Boolean Gain(Supply supply, Type cardType, DeckLocation location, DeckPosition position, Boolean isBought) { if (supply.CanGain(cardType)) { Card supplyCard = supply[cardType].First(); CardGainEventArgs cgea = CardGainCheckAllowed(supplyCard, location, position, isBought); if (!cgea.Cancelled) return this.Gain(supply.Take(cardType), cgea.Location, cgea.Position, cgea.Bought); else { CardGainFinish(supplyCard, cgea.Location, cgea.Position, cgea.Bought, cgea.Cancelled); return true; } } return false; } private Boolean Gain(Card card, DeckLocation location, DeckPosition position, Boolean isBought) { if (card == null) return false; Boolean cancelled = false; if (CurrentTurn != null) CurrentTurn.Gained(card); CardGainInto(card, location, position, isBought, false); if (CardGained != null) { List handledBy = new List(); CardGainEventArgs cgea = null; Boolean actionPerformed = false; do { actionPerformed = false; cgea = new CardGainEventArgs(_Game, card, location, position, isBought); cgea.HandledBy.AddRange(handledBy); cgea.Cancelled = cancelled; CardGained(this, cgea); handledBy = cgea.HandledBy; cancelled |= cgea.Cancelled; location = cgea.Location; position = cgea.Position; IEnumerator enumerator = this._Game.GetPlayersStartingWithEnumerator(this); while (enumerator.MoveNext()) { Boolean isAnyRequired = false; List options = new List(); IEnumerable cardTypes = cgea.Actions.Keys; foreach (Type key in cardTypes) { if (enumerator.Current == cgea.Actions[key].Player) { options.Add(cgea.Actions[key].Text); isAnyRequired |= cgea.Actions[key].IsRequired; } } if (options.Count > 0) { Choice choice = new Choice(String.Format("{0} gained {1}", this == enumerator.Current ? "You" : this.ToString(), card), null, new CardCollection() { card }, options, this, cgea, false, isAnyRequired ? 1 : 0, 1); ChoiceResult result = enumerator.Current.MakeChoice(choice); if (result.Options.Count > 0) { options.Sort(); cgea.Actions.First(kvp => kvp.Value.Text == result.Options[0]).Value.Method(enumerator.Current, ref cgea); actionPerformed = true; } if (enumerator.Current == this && (cgea.Location != location || cgea.Position != position)) CardGainInto(card, cgea.Location, cgea.Position, cgea.Bought, cgea.Cancelled); cancelled |= cgea.Cancelled; location = cgea.Location; position = cgea.Position; } } } while (CardGained != null && actionPerformed); } CardGainFinish(card, location, position, isBought, cancelled); return false; } private void CardGainInto(Card card, DeckLocation location, DeckPosition position, Boolean isBought, Boolean isCancelled) { if (this.DiscardPile.Contains(card)) this.RetrieveCardFrom(DeckLocation.Discard, card); card.Gaining(this, ref location, ref position); this.AddCardInto(location, card, position); card.Gained(this); if (CardGainedInto != null) { CardGainEventArgs cgea = new CardGainEventArgs(_Game, card, location, position, isBought); cgea.Cancelled = isCancelled; CardGainedInto(this, cgea); } } private void CardGainFinish(Card card, DeckLocation location, DeckPosition position, Boolean isBought, Boolean isCancelled) { if (CardGainFinished != null) { CardGainEventArgs cgea = new CardGainEventArgs(_Game, card, location, position, isBought); cgea.Cancelled = isCancelled; CardGainFinished(this, cgea); } } public Boolean Buy(Supply supply) { Card supplyCard = supply.TopCard; this.Phase = PhaseEnum.Buy; Boolean cancelled = false; if (CardBuying != null) { CardBuyEventArgs cbea = new CardBuyEventArgs(_Game, supplyCard); CardBuying(this, cbea); cancelled = cbea.Cancelled; } if (!cancelled) { CurrentTurn.Bought(supplyCard); supplyCard.Bought(this); supply.Bought(this); if (CardBought != null) { CardBuyEventArgs cbea = null; List handledBy = new List(); Boolean actionPerformed = false; do { actionPerformed = false; cbea = new CardBuyEventArgs(_Game, supplyCard); cbea.HandledBy.AddRange(handledBy); CardBought(this, cbea); handledBy = cbea.HandledBy; Boolean isAnyRequired = false; List options = new List(); IEnumerable cardTypes = cbea.Actions.Keys; foreach (Type key in cardTypes) { options.Add(cbea.Actions[key].Text); isAnyRequired |= cbea.Actions[key].IsRequired; } if (options.Count > 0) { options.Sort(); Choice choice = new Choice(String.Format("You bought {0}", supplyCard), null, new CardCollection() { supplyCard }, options, this, cbea, false, isAnyRequired ? 1 : 0, 1); ChoiceResult result = this.MakeChoice(choice); if (result.Options.Count > 0) { cbea.Actions.First(kvp => kvp.Value.Text == result.Options[0]).Value.Method(this, ref cbea); actionPerformed = true; } } } while (CardBought != null && actionPerformed); } this.Gain(supply, true); } if (CardBuyFinished != null) { CardBuyEventArgs cbea = new CardBuyEventArgs(_Game, supplyCard); cbea.Cancelled = cancelled; CardBuyFinished(this, cbea); } if (!cancelled) { this.Currency -= _Game.Cost(supplyCard); this.Buys--; } return cancelled; } public void Receive(Player fromPlayer, Card card, DeckLocation location, DeckPosition position) { if (CurrentTurn != null) CurrentTurn.Received(card); this.AddCardInto(location, card, position); card.ReceivedBy(this); if (CardReceived != null) { CardReceivedEventArgs crea = new CardReceivedEventArgs(fromPlayer, card, location, position); CardReceived(this, crea); } } public void Lose(CardCollection cards) { cards.LostBy(this); if (CardsLost != null) { CardsLostEventArgs clea = new CardsLostEventArgs(cards); CardsLost(this, clea); } } public void Lose(Card card) { Lose(new CardCollection() { card }); } public void Cleanup() { this.Phase = PhaseEnum.Cleanup; PerformCleanup(); } private void PerformCleanup() { // Sets up card movements to indicate where each card should go. CardMovementCollection cardsToMove = new CardMovementCollection(this.PreviousTableau, DeckLocation.PreviousTableau, DeckLocation.Discard); cardsToMove.AddRange(this.Hand, DeckLocation.Hand, DeckLocation.Discard); cardsToMove.AddRange(this.Tableau, DeckLocation.Tableau, DeckLocation.Discard); foreach (Card durationCard in this.Tableau[Category.Duration].Where(c => !c.CanCleanUp)) cardsToMove[durationCard].Destination = DeckLocation.PreviousTableau; IEnumerable tableauCards = cardsToMove.Where(cm => cm.CurrentLocation == DeckLocation.Tableau); ParallelQuery pqCardsToMove = cardsToMove.AsParallel().Where(cm => cm.CurrentLocation == DeckLocation.Tableau && cm.Destination == DeckLocation.PreviousTableau && cm.Card.ModifiedBy != null && cardsToMove.Contains(cm.Card.ModifiedBy) && cardsToMove[cm.Card.ModifiedBy].Destination == DeckLocation.Discard); pqCardsToMove.ForAll(cm => cardsToMove[cm.Card.ModifiedBy].Destination = DeckLocation.PreviousTableau); int drawSize = 5; if (CleaningUp != null) { Boolean cancelled = false; // Possibly changing events that can happen in the game CleaningUpEventArgs cuea = null; do { cuea = new CleaningUpEventArgs(this, 5, ref cardsToMove); cuea.Cancelled |= cancelled; CleaningUp(this, cuea); List options = new List(); IEnumerable cardTypes = cuea.Actions.Keys; foreach (Type key in cardTypes) options.Add(cuea.Actions[key].Text); if (options.Count > 0) { options.Sort(); Choice choice = new Choice("Performing Clean-up", options, this, true, cuea); ChoiceResult result = this.MakeChoice(choice); if (result.Options.Count > 0) cuea.Actions.First(kvp => kvp.Value.Text == result.Options[0]).Value.Method(this, ref cuea); else break; } else break; cancelled |= cuea.Cancelled; } while (CleaningUp != null); if (cuea != null) cancelled |= cuea.Cancelled; if (cancelled) return; if (cuea.NextPlayer != null) _CurrentTurn.NextPlayer = cuea.NextPlayer; _CurrentTurn.NextGrantedBy = cuea.NextGrantedBy; drawSize = cuea.DrawSize; } // Discard Hand this.AddCardsInto(DeckLocation.Discard, this.RetrieveCardsFrom(DeckLocation.Hand, c => cardsToMove[c].CurrentLocation == DeckLocation.Hand && cardsToMove[c].Destination == DeckLocation.Discard)); // Discard any Revealed cards (should be none?) this.DiscardRevealed(); // Discard non-Duration (or Duration-modifying) cards in Tableau this.Discard(DeckLocation.Tableau, cardsToMove.Where(cm => cm.CurrentLocation == DeckLocation.Tableau && cm.Destination == DeckLocation.Discard).Select(cm => cm.Card)); // Discard Previous Tableau this.Discard(DeckLocation.PreviousTableau, cardsToMove.Where(cm => cm.CurrentLocation == DeckLocation.PreviousTableau && cm.Destination == DeckLocation.Discard).Select(cm => cm.Card)); // Move Duration (and Duration-modifying) cards from Tableau into Previous Tableau this.AddCardsInto(DeckLocation.PreviousTableau, this.RetrieveCardsFrom(DeckLocation.Tableau, c => cardsToMove[c].CurrentLocation == DeckLocation.Tableau && cardsToMove[c].Destination == DeckLocation.PreviousTableau)); // Move any cards that have had their Destination changed to their appropriate locations this.AddCardsToDeck(this.RetrieveCardsFrom(DeckLocation.Tableau, c => cardsToMove[c].CurrentLocation == DeckLocation.Tableau && cardsToMove[c].Destination == DeckLocation.Deck), DeckPosition.Top); this.AddCardsToDeck(this.RetrieveCardsFrom(DeckLocation.PreviousTableau, c => cardsToMove[c].CurrentLocation == DeckLocation.PreviousTableau && cardsToMove[c].Destination == DeckLocation.Deck), DeckPosition.Top); #if DEBUG if (this.Tableau.Count > 0) throw new Exception("Something happened -- there are cards left in the player's tableau!"); #endif if (CurrentTurn != null) CurrentTurn.Finished(); _Actions = _Buys = 0; _Currency.Coin.Value = 0; _Currency.Potion.Value = 0; _ActionsPlayed = 0; #if DEBUG // Check to see that there are no duplicate cards anywhere CardCollection allCards = new CardCollection(); allCards.AddRange(this.Hand); allCards.AddRange(this.Revealed); allCards.AddRange(this.Private); allCards.AddRange(this.Tableau); allCards.AddRange(this.PreviousTableau); allCards.AddRange(this.DrawPile.LookThrough(c => true)); allCards.AddRange(this.DiscardPile.LookThrough(c => true)); foreach (CardMat mat in this.PlayerMats.Values) allCards.AddRange(mat); ParallelQuery duplicateCards = allCards.AsParallel().Where(c => allCards.Count(ct => ct == c) > 1); //IEnumerable duplicateCards = allCards.FindAll(c => allCards.Count(ct => ct == c) > 1); if (duplicateCards.Count() > 0) { // Ruh Roh throw new Exception("Duplicate cards found! Something went wrong!"); } #endif DrawHand(drawSize); if (CleanedUp != null) { CleanedUp(this, new CleanedUpEventArgs(this, drawSize)); } if (TurnEnded != null) { TurnEnded(this, new TurnEndedEventArgs(this)); } } public void PlayTreasures(Game game) { if (Phase == PhaseEnum.Buy || Phase == PhaseEnum.Choosing || Phase == PhaseEnum.Playing || Phase == PhaseEnum.Cleanup || Phase == PhaseEnum.Endgame || Phase == PhaseEnum.Waiting) throw new Exception("Can't play treasures right now!"); if (Phase == PhaseEnum.Action) Phase = PhaseEnum.Treasure; // Play all Treasure cards that have no special Play method defined PlayCards(this.Hand[c => (c.Category & Category.Treasure) == Category.Treasure && (c.Location == Location.General || c.CardType.GetMethod("Play", new Type[] { typeof(Player) }).DeclaringType == typeof(Card))]); Phase = PhaseEnum.Treasure; } public void GoToTreasurePhase() { if (Phase == PhaseEnum.Action) Phase = PhaseEnum.Treasure; } public void GoToBuyPhase() { if (Phase == PhaseEnum.Action || Phase == PhaseEnum.Treasure) Phase = PhaseEnum.Buy; } public void PlayNothing() { PlayNothing(String.Empty); } public void PlayNothing(String modifier) { if (CardPlaying != null) { CardPlayingEventArgs cpgea = new CardPlayingEventArgs(this, (CardCollection)null, modifier); CardPlaying(this, cpgea); } if (CardPlayed != null) { CardPlayedEventArgs cpdea = new CardPlayedEventArgs(this, (CardCollection)null); CardPlayed(this, cpdea); } } public void PlayCard(Card card) { PlayCards(new CardCollection() { card }); } public void PlayCards(CardCollection cards) { if (this.Actions == 0 && cards.Any(c => (c.Category & Category.Action) == Category.Action)) throw new Exception("You cannot play any Action cards right now!"); PlayCardsInternal(cards); // Check Phase after playing -- we may need to switch phases if (this.Phase == PhaseEnum.Action && (this.Actions == 0 || this.Hand[Category.Action].Count == 0)) this.Phase = PhaseEnum.Treasure; else if (this.Phase == PhaseEnum.Treasure && this.Hand[Category.Treasure].Count == 0) this.Phase = PhaseEnum.Buy; } internal void PlayCardInternal(Card card) { PlayCardInternal(card, String.Empty); } internal void PlayCardInternal(Card card, String modifier) { PlayCardsInternal(new CardCollection() { card }, modifier); } internal void PlayCardsInternal(CardCollection cards) { PlayCardsInternal(cards, String.Empty); } internal void PlayCardsInternal(CardCollection cards, String modifier) { // Don't even bother; just return straight away if (cards.Count == 0) return; // So the AI doesn't blow things up, just return immediately if the Phase is Endgame if (this.Phase == PhaseEnum.Endgame) return; foreach (Card card in cards) { if (Phase == PhaseEnum.Action && (card.Category & Category.Action) != Category.Action) this.Phase = PhaseEnum.Treasure; } if (this.Phase != PhaseEnum.Action && this.Phase != PhaseEnum.Playing && cards.Any(c => (c.Category & Category.Action) == Category.Action)) throw new Exception("You cannot play any Action cards right now!"); if (this.Phase != PhaseEnum.Treasure && this.Phase != PhaseEnum.Playing && cards.Any(c => (c.Category & Category.Treasure) == Category.Treasure)) throw new Exception("You cannot play any Treasure cards right now!"); PhaseEnum currentPhase = this.Phase; this.Phase = PhaseEnum.Playing; if (CardPlaying != null) { CardPlayingEventArgs cpgea = new CardPlayingEventArgs(this, cards, modifier); CardPlaying(this, cpgea); } // Retrieve the actual card instead of the one we're passed. It might not exist // Also, we need to remove them from the Hand, Revealed, or Private (these are the 3 places cards can be played from) CardCollection actualCards = this.RetrieveCardsFrom(DeckLocation.Hand, cards); actualCards.AddRange(this.RetrieveCardsFrom(DeckLocation.Revealed, cards)); actualCards.AddRange(this.RetrieveCardsFrom(DeckLocation.Private, cards)); // Add them to the Tableau, add them to the Played list for the turn, and Play them (individually) foreach (Card card in cards) { if (actualCards.Contains(card)) this.AddCardInto(DeckLocation.Tableau, card); this.CurrentTurn.Played(card); card.Play(this); card.PlayFinished(this); } //this.AddCardsInto(DeckLocation.Tableau, actualCards); //this.CurrentTurn.Played(cards); //cards.Play(this); this.Tableau.Refresh(this); if (CardPlayed != null) { CardPlayedEventArgs cpdea = new CardPlayedEventArgs(this, cards); CardPlayed(this, cpdea); } this.Phase = currentPhase; } public Boolean AnyActions { get { if (_Hand[Category.Action].Count == 0) return false; return true; } } internal void Reset() { _Hand.Reset(); _DrawPile.Reset(); _DiscardPile.Reset(); _Revealed.Reset(); _PreviousTableau.Reset(); _Tableau.Reset(); _Private.Reset(); foreach (Deck deck in this.PlayerMats.Values) deck.Reset(); } public override string ToString() { return _Name; } internal void End() { this.Phase = PhaseEnum.Endgame; _Hand.BeginChanges(); _Hand.Collate = true; _Hand.Comparer = new DominionBase.Cards.Sorting.ByVictoryPoints(DominionBase.Cards.Sorting.SortDirection.Descending); this.AddCardsInto(DeckLocation.Hand, this.RetrieveCardsFrom(DeckLocation.Tableau)); this.AddCardsInto(DeckLocation.Hand, this.RetrieveCardsFrom(DeckLocation.Revealed)); this.AddCardsInto(DeckLocation.Hand, this.RetrieveCardsFrom(DeckLocation.PreviousTableau)); this.AddCardsInto(DeckLocation.Hand, this.RetrieveCardsFrom(DeckLocation.Discard)); this.AddCardsInto(DeckLocation.Hand, this.RetrieveCardsFrom(DeckLocation.Deck)); this.AddCardsInto(DeckLocation.Hand, this.RetrieveCardsFrom(DeckLocation.Private)); foreach (Type deckType in this.PlayerMats.Keys) this.AddCardsInto(DeckLocation.Hand, this.RetrieveCardsFrom(deckType)); _Hand.End(this); _Hand.EndChanges(); } internal void Trash(CardCollection cards) { if (cards.Count == 0) return; TrashEventArgs tea = null; if (Trashing != null) { do { tea = new TrashEventArgs(this, cards); Trashing(this, tea); Boolean isAnyRequired = false; List options = new List(); IEnumerable cardTypes = tea.Actions.Keys; foreach (Type key in cardTypes) { options.Add(tea.Actions[key].Text); isAnyRequired |= tea.Actions[key].IsRequired; } if (options.Count > 0) { options.Sort(); Choice choice = new Choice(String.Format("You are trashing {0} cards", cards.Count), null, cards, options, this, tea, false, isAnyRequired ? 1 : 0, 1); ChoiceResult result = this.MakeChoice(choice); if (result.Options.Count > 0) tea.Actions.First(kvp => kvp.Value.Text == result.Options[0]).Value.Method(this, ref tea); } } while (Trashing != null && tea.HandledBy.Count > 0); } _Game.Table.Trash.AddRange(cards); CurrentTurn.Trashed(cards); if (Trashed != null) { List handledBy = new List(); Boolean actionPerformed = false; do { actionPerformed = false; tea = new TrashEventArgs(this, cards); tea.HandledBy.AddRange(handledBy); Trashed(this, tea); handledBy = tea.HandledBy; IEnumerator enumerator = this._Game.GetPlayersStartingWithEnumerator(this); while (enumerator.MoveNext()) { Boolean isAnyRequired = false; List options = new List(); IEnumerable cardTypes = tea.Actions.Keys; foreach (Type key in cardTypes) { if (enumerator.Current == tea.Actions[key].Player) { options.Add(tea.Actions[key].Text); isAnyRequired |= tea.Actions[key].IsRequired; } } if (options.Count > 0) { options.Sort(); Choice choice = new Choice(String.Format("{0} trashed {1}", this == enumerator.Current ? "You" : this.ToString(), Utilities.StringUtility.Plural("card", cards.Count)), null, cards, options, this, tea, false, isAnyRequired ? 1 : 0, 1); ChoiceResult result = enumerator.Current.MakeChoice(choice); if (result.Options.Count > 0) { tea.Actions.First(kvp => kvp.Value.Text == result.Options[0]).Value.Method(enumerator.Current, ref tea); actionPerformed = true; } } } } while (Trashed != null && actionPerformed); } cards.TrashedBy(this); Lose(cards); if (TrashedFinished != null) { tea = new TrashEventArgs(this, cards); TrashedFinished(this, tea); } } internal void Trash(Card card) { this.Trash(new CardCollection() { card }); //_Game.Table.Trash.Add(card); //CurrentTurn.Trashed(card); //card.TrashedBy(this); //Lose(card); //if (Trashed != null) //{ // CardCollection cc = new CardCollection() { card }; // TrashedEventArgs tea = new TrashedEventArgs(this, cc); // Trashed(this, tea); //} } internal void Start(Turn turn) { if (TurnStarting != null) { TurnStartingEventArgs tsea = new TurnStartingEventArgs(this); tsea.GrantedBy = turn.GrantedBy; TurnStarting(this, tsea); if (tsea.Cancelled) return; } _CurrentTurn = turn; _Actions = _Buys = 1; Phase = PhaseEnum.Starting; if (BenefitsChanged != null) { BenefitsChangedEventArgs bcea = new BenefitsChangedEventArgs(this); BenefitsChanged(this, bcea); } if (TurnStarted != null) { TurnStartedEventArgs tsea = new TurnStartedEventArgs(this); TurnStarted(this, tsea); } Phase = PhaseEnum.Action; //TakeTurn(this._Game, this); } internal void ReceiveBenefit(Card sourceOfBenefit, CardBenefit benefit) { ReceiveBenefit(sourceOfBenefit, benefit, false); } internal void ReceiveBenefit(Card sourceOfBenefit, CardBenefit benefit, Boolean isInternal) { if (benefit.Any && BenefitReceiving != null && ((sourceOfBenefit.Category & Category.Action) == Category.Action || ((sourceOfBenefit.Category & Category.Treasure) == Category.Treasure && !isInternal))) { BenefitReceivingEventArgs brea = new BenefitReceivingEventArgs(this, benefit); BenefitReceiving(sourceOfBenefit, brea); } if (benefit.Cards > 0) this.Draw(benefit.Cards, DeckLocation.Hand); this.Actions += benefit.Actions; this.Buys += benefit.Buys; this.Currency += benefit.Currency; for (int count = 0; count < benefit.VictoryPoints; count++) this.TokenPiles.Add(new Cards.Prosperity.VictoryToken(), this); this.VictoryChits += benefit.VictoryPoints; if (benefit.Cards < 0) { Choice choice = new Choice(String.Format("Discard {0}.", Utilities.StringUtility.Plural("card", -benefit.Cards)), sourceOfBenefit, this.Hand, this, false, -benefit.Cards, -benefit.Cards); ChoiceResult result = this.MakeChoice(choice); this.Discard(DeckLocation.Hand, result.Cards); } } internal int CountAll() { return CountAll(this, c => true, true, false); } internal int CountAll(Player fromPlayer, Predicate predicate) { return CountAll(fromPlayer, predicate, true, false); } internal int CountAll(Player fromPlayer, Predicate predicate, Boolean onlyObtainable, Boolean onlyCurrentlyDrawable) { int count = 0; count += this.DiscardPile.LookThrough(predicate).Count; count += this.DrawPile.LookThrough(predicate).Count; if (!onlyCurrentlyDrawable) { count += this.Hand[predicate].Count; count += this.Private[predicate].Count; count += this.Revealed[predicate].Count; count += this.Tableau[predicate].Count; count += this.PreviousTableau[predicate].Count; foreach (Type deckType in this.PlayerMats.Keys) { // Don't count cards on mats that you can't obtain cards from if (onlyObtainable && !this.PlayerMats[deckType].IsObtainable) continue; count += this.PlayerMats[deckType].LookThrough(predicate).Count; } } return count; } internal int SumAll(Player fromPlayer, Predicate filterpredicate, Func sumpredicate) { return SumAll(fromPlayer, filterpredicate, sumpredicate, true, false); } internal int SumAll(Player fromPlayer, Predicate filterpredicate, Func sumpredicate, Boolean onlyObtainable, Boolean onlyCurrentlyDrawable) { int sum = 0; sum += this.DiscardPile.LookThrough(filterpredicate).Sum(sumpredicate); sum += this.DrawPile.LookThrough(filterpredicate).Sum(sumpredicate); if (!onlyCurrentlyDrawable) { sum += this.Hand[filterpredicate].Sum(sumpredicate); sum += this.Private[filterpredicate].Sum(sumpredicate); sum += this.Revealed[filterpredicate].Sum(sumpredicate); sum += this.Tableau[filterpredicate].Sum(sumpredicate); sum += this.PreviousTableau[filterpredicate].Sum(sumpredicate); foreach (Type deckType in this.PlayerMats.Keys) { // Don't count cards on mats that you can't obtain cards from if (onlyObtainable && !this.PlayerMats[deckType].IsObtainable) continue; sum += this.PlayerMats[deckType].LookThrough(filterpredicate).Sum(sumpredicate); } } return sum; } internal void AddToken(Token token) { this.TokenPiles.Add(token, this); } internal void SetupDeckAs(Player player) { CardCollection cards = new CardCollection(this.RetrieveCardsFrom(DeckLocation.Hand).Union(this.RetrieveCardsFrom(DeckLocation.Deck))); foreach (Card sourceCard in player.Hand) { Card myFoundCard = cards.First(c => c.CardType == sourceCard.CardType); cards.Remove(myFoundCard); this.AddCardInto(DeckLocation.Hand, myFoundCard); } this.AddCardsInto(DeckLocation.Deck, cards); } } }