Skip to content

Instantly share code, notes, and snippets.

@aalmada
Last active June 18, 2021 13:21
Show Gist options
  • Save aalmada/fb474790e592145f9a998d963e2cfcc5 to your computer and use it in GitHub Desktop.
Save aalmada/fb474790e592145f9a998d963e2cfcc5 to your computer and use it in GitHub Desktop.
Cards deck implemented in C#
#nullable enable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
namespace NetFabric
{
public class Deck<T>
: IEnumerable<T>
{
struct Card
{
public T Value;
public bool Drawn;
}
readonly Card[] _cards;
public Deck(IReadOnlyCollection<T> cards)
{
_cards = new Card[cards.Count];
using var enumerator = cards.GetEnumerator();
var span = _cards;
for (var index = 0; enumerator.MoveNext() && index < span.Length; index++)
span[index] = new Card { Value = enumerator.Current, Drawn = false };
}
public bool TryDraw(out T value)
{
var cards = _cards;
for (var index = 0; index < cards.Length; index++)
{
ref var card = ref cards[index];
if (card.Drawn)
continue;
card.Drawn = true;
value = card.Value;
return true;
}
value = default!;
return false;
}
public IEnumerable<T> Draw(int count)
{
if (count <= 0)
yield break;
var cards = _cards;
// ReSharper disable once ForCanBeConvertedToForeach
for (var index = 0; index < cards.Length; index++)
{
var card = cards[index]; // iterators cannot have by-reference locals
if (card.Drawn)
continue;
cards[index] = new Card { Value = card.Value, Drawn = true };
yield return card.Value;
count--;
if (count == 0)
yield break;
}
}
public void Shuffle()
=> _cards.AsSpan().Shuffle();
public void Shuffle(Random random)
=> _cards.AsSpan().Shuffle(random);
public void Reset()
{
var cards = _cards;
for (var index = 0; index < cards.Length; index++)
{
ref var card = ref cards[index];
card.Drawn = false;
}
}
public Enumerator GetEnumerator()
=> new(this);
IEnumerator<T> IEnumerable<T>.GetEnumerator()
=> new DisposableEnumerator(this);
IEnumerator IEnumerable.GetEnumerator()
=> new DisposableEnumerator(this);
public struct Enumerator
{
readonly Card[] _cards;
int _index;
internal Enumerator(in Deck<T> deck)
{
_cards = deck._cards;
_index = -1;
}
public T Current
=> _cards[_index].Value;
public bool MoveNext()
{
var cards = _cards;
while (++_index < cards.Length)
{
var card = cards[_index];
if (card.Drawn)
continue;
return true;
}
return false;
}
}
class DisposableEnumerator
: IEnumerator<T>
{
readonly Card[] _cards;
int _index;
public DisposableEnumerator(in Deck<T> deck)
{
_cards = deck._cards;
_index = -1;
}
public T Current
=> _cards[_index].Value;
object? IEnumerator.Current
=> _cards[_index].Value;
public bool MoveNext()
{
var cards = _cards;
while (++_index < cards.Length)
{
var card = cards[_index];
if (card.Drawn)
continue;
return true;
}
return false;
}
public void Reset()
=> _index = -1;
public void Dispose()
{ }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment