Framework/Shoe.cs

Represents a host-only shuffled stack of card face indices (a "shoe"). It builds a list of face indices from a DeckDefinition or accepts a preset order, supports shuffling on construction, and deals cards via Draw(), returning Card instances while tracking remaining count.

namespace CardGames;

/// <summary>
/// A runtime, host-only shuffled stack of cards (a "shoe"). The order is the authoritative
/// secret - it never leaves the host. Clients only ever learn a card once it's dealt and revealed.
/// </summary>
public sealed class Shoe
{
	private readonly List<int> _cards = new();
	private int _next;

	public int Remaining => _cards.Count - _next;

	public Shoe( DeckDefinition definition, int decks = 1 )
	{
		for ( int d = 0; d < decks; d++ )
			for ( int i = 0; i < definition.Faces.Count; i++ )
				_cards.Add( i );

		Shuffle();
	}

	/// <summary>
	/// A shoe that deals an exact, pre-set order of face indices (top first) - no shuffle. Used to set up
	/// a known deal for debugging or tests (see <see cref="CardGame.RiggedDeal"/>).
	/// </summary>
	public Shoe( IReadOnlyList<int> order )
	{
		_cards.AddRange( order );
	}

	public Card Draw()
	{
		if ( Remaining <= 0 ) throw new InvalidOperationException( "Shoe is empty." );
		return new Card( _cards[_next++] );
	}

	private void Shuffle()
	{
		var rng = new Random();
		for ( int i = _cards.Count - 1; i > 0; i-- )
		{
			int j = rng.Next( i + 1 );
			(_cards[i], _cards[j]) = (_cards[j], _cards[i]);
		}
	}
}