/// <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); }
/// <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); }
/// <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); }
/// <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."); } }
/// <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); }
void IDeckEvents <TElement> .Adding(ref TElement element, ref DeckSide side, ref Location location) { }
void IDeckEvents <TElement> .Added(TElement element, DeckSide side, Location location) { }