Esempio n. 1
0
        /// <summary>
        /// Draws the tableau up to its configured size.
        /// </summary>
        /// <remarks>
        /// This uses the <see cref="TableauOverflowRule"/> set in the options to
        /// explain how to handle overage.  Underage is handled by drawing from
        /// the top deck.
        /// </remarks>
        /// <param name="from">Which side of the draw pile we're drawing from.</param>
        /// <returns>This tableau (for fluent purposes).</returns>
        public ITableau <TElement> DrawUp(DeckSide from = DeckSide.Top)
        {
            Contract.Requires(Enum.IsDefined(typeof(TableauOverflowRule), Options.OverflowRule));
            ((IDeckStackInternal <TElement>) this).CheckEnabled();

            Deck.Events.DrawingInto(this);

            var size = Options.InitialSize;

            TElement element = null;

            while (Count < size &&
                   (!Options.DrawsUpSafely ||
                    (Deck.DrawPile.Count + Deck.DiscardPile.Count) > 0))
            {
                element = Deck.DrawPileStack.Draw(from);
                Contents.Add(element);

                Deck.Events.DrewInto(this, element);
            }
            if (Options.MaximumSize.HasValue)
            {
                var max = Options.MaximumSize.Value;
                while (Count > max)
                {
                    var index = 0;
                    switch (Options.OverflowRule)
                    {
                    case TableauOverflowRule.DiscardNewest:
                        index = Count - 1;
                        break;

                    case TableauOverflowRule.DiscardOldest:
                        index = 0;
                        break;

                    case TableauOverflowRule.DiscardRandom:
                        index = Extensions.Rand.Next(0, Count);
                        break;

                    case TableauOverflowRule.Ignore:
                        return(this);    // Intentionally return from method, we do nothing here.

                    case TableauOverflowRule.Ask:
                        index = Deck.Events.PickElementToDiscard(this);
                        Contract.Assert(index >= 0 && index < Contents.Count);
                        break;

                    default:
                        throw new NotImplementedException($"Haven't coded for {Options.MaintainSize} yet.");
                    }
                    element = Contents[index];
                    Contents.RemoveAt(index);
                    Deck.DiscardPileStack.Add(element);
                }
            }

            return(this);
        }
Esempio n. 2
0
        /// <summary>
        /// Adds a card to a specific location in the deck.
        /// </summary>
        /// <param name="element">The element to add.</param>
        /// <param name="side">What side of the deck the item goes to.</param>
        /// <param name="location">The location to add it to.</param>
        /// <returns>This same deck (for FLUID interface reasons).</returns>
        public IDeck <TElement> Add(TElement element, DeckSide side, Location location = Location.DrawPile)
        {
            Contract.Requires(Enum.IsDefined(typeof(DeckSide), side));
            Contract.Requires(Enum.IsDefined(typeof(Location), location));
            CheckAllowAdd();

            switch (location)
            {
            case Location.DrawPile:
                ((Internal.IDrawPileInternal <TElement>)_drawPile).Add(element, side);
                break;

            case Location.DiscardPile:
                ((Internal.IDiscardPileInternal <TElement>)_discards).Add(element, side);
                break;

            case Location.Table:
                if (side != DeckSide.Default)
                {
                    throw new InvalidOperationException("Cannot add to the table on a specific side.");
                }
                else
                {
                    ((Internal.ITableInternal <TElement>)_table).Add(element);
                }
                break;

            case Location.Tableau:
                if (side != DeckSide.Default)
                {
                    throw new InvalidOperationException("Cannot add to the tableau on a specific side.");
                }
                else
                {
                    ((Internal.ITableauInternal <TElement>)_tableau).Add(element);
                }
                break;

            case Location.Hand:
                throw new InvalidOperationException("Cannot add directly to a hand.");

            default:
                throw new NotImplementedException($"Don't know about the {location} location.");
            }
            Known.Add(element);

            Events.Added(element, side, location);
            return(this);
        }
Esempio n. 3
0
        /// <summary>
        /// Draws a card from the draw pile into the hand.
        /// </summary>
        /// <param name="from">Where to draw the card from, top or bottom of the deck.</param>
        /// <returns>This hand (for fluent purposes).</returns>
        public IHand <TElement> Draw(DeckSide side = DeckSide.Top)
        {
            InvalidCheck();
            Contract.Requires(Enum.IsDefined(typeof(DeckSide), side));

            Deck.Events.DrawingInto(this);

            var card = Deck.DrawPileStack.Draw(side);

            Contents.Add(card);

            Deck.Events.DrewInto(this, card);

            return(this);
        }
Esempio n. 4
0
        /// <summary>
        /// Adds an element to the draw pile.
        /// </summary>
        /// <param name="element">The element to add.</param>
        /// <param name="side">Which side of the stack to add the element to.  Allows for putting cards on the bottom of the deck.</param>
        void IDrawPileInternal <TElement> .Add(TElement element, DeckSide side)
        {
            switch (side)
            {
            case DeckSide.Bottom:
                Contents.Add(element);
                break;

            case DeckSide.Default:
            case DeckSide.Top:
                Contents.Insert(0, element);
                break;

            default:
                throw new NotImplementedException($"The value of {side} wasn't coded for.");
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Draws a card from the deck.
        /// </summary>
        /// <param name="side">The side to draw from.  Allows for botom-dealing.</param>
        /// <returns>The drawn element.</returns>
        TElement IDrawPileInternal <TElement> .Draw(DeckSide side)
        {
            if ((Count == 0 && Deck.DiscardPile.Count == 0) ||
                (Count == 0 && !Deck.Options.Discards.AutoShuffle))
            {
                throw new BottomDeckException();
            }
            else if (Count == 0) // Must be auto-shuffle with a discard.
            {
                Shuffle();
            }

            Deck.Events.Drawing(this);

            TElement card  = null;
            var      index = 0;

            switch (side)
            {
            case DeckSide.Bottom:
                index = Contents.Count - 1;
                break;

            case DeckSide.Default:
            case DeckSide.Top:
                index = 0;
                break;

            default:
                throw new NotImplementedException($"The value of {side} wasn't coded for.");
            }

            card = Contents[index];
            Contents.RemoveAt(index);

            Deck.Events.Drew(this, card);

            return(card);
        }
Esempio n. 6
0
 void IDeckEvents <TElement> .Adding(ref TElement element, ref DeckSide side, ref Location location)
 {
 }
Esempio n. 7
0
 void IDeckEvents <TElement> .Added(TElement element, DeckSide side, Location location)
 {
 }