No description
Find a file
2026-08-03 00:02:37 +02:00
CardKit refactor: split into CardKit lib and Solitaire app 2026-08-01 23:00:04 +02:00
Solitaire refactor: split into CardKit lib and Solitaire app 2026-08-01 23:00:04 +02:00
.gitignore init project solitaire 2026-07-21 05:14:52 +02:00
CardGames.slnx refactor: split into CardKit lib and Solitaire app 2026-08-01 23:00:04 +02:00
README.md added readme 2026-08-03 00:02:37 +02:00

CardKit

A small .NET library for building things with playing cards. CardKit handles the cards themselves — a deck you can fill, shuffle, deal from, peek into and move around — and everything else is built on top of it.

Right now the repo ships two things:

  • CardKit — the actual library. A Card type and a Deck that behaves like a real stack of cards.
  • Solitaire — a console Klondike game that doubles as a demo of what CardKit can do.

The long-term idea is for the Solitaire project to grow into a more general CLI "card game engine", but that's just a direction, not a promise. For now: it's a deck of cards, with Solitaire as the demo.


Requirements

  • .NET 10 SDK
  • A terminal with 24-bit (truecolor) ANSI support — the rendering uses \e[38;2;r;g;b escape sequences, so an 8/16-color-only terminal will look wrong.

Running the demos

Everything runs through the Solitaire project, which takes a mode argument:

dotnet run --project Solitaire game    # play Klondike Solitaire
dotnet run --project Solitaire full    # render a full 52-card deck, reshuffle on keypress (default)
dotnet run --project Solitaire stack   # render solitaire-style stacks of up to 7
dotnet run --project Solitaire debug   # tiny sanity check

If you omit the argument it defaults to full.


Playing Solitaire

Standard Klondike: seven tableau columns, a stock + waste, and four foundations. The goal is to move every card up onto the foundations (which empties the tableau — that's the win condition the game checks for).

Controls:

Key Action
17 Select a tableau column. Valid moves auto-resolve.
Space Cycle a card from the stock into the waste.
Enter Move the top waste card onto the tableau or a foundation.
F Move a card from a foundation back down to the tableau.
Q Quit.
E Forfeit and reveal all cards.

The column selection is a single-key auto-resolve: pressing a column number tries to place its cards (a foundation move first, otherwise onto another column), rather than asking you to pick a source and a destination.


Using CardKit in your own project

CardKit isn't published to NuGet, so reference the project directly. Add it to your .csproj:

<ItemGroup>
  <ProjectReference Include="path/to/CardKit/CardKit.csproj" />
</ItemGroup>

or from the CLI:

dotnet add reference path/to/CardKit/CardKit.csproj

Then the basics:

using CardKit;

var deck = new Deck(52);   // capacity 52, face-down by default
deck.Fill();               // populate with a sorted deck (up to the capacity)
deck.Shuffle();            // in-place FisherYates

Card drawn = deck.Top();   // remove and return the top card
Card look  = deck.Peek();  // look at the top without removing it

deck.Insert(drawn);        // put a card back on top (adopts the deck's facing)

Note: the console-drawing helper (PaintUtil) lives in the Solitaire project, not in CardKit — it's part of the demo/rendering layer. CardKit itself is render-agnostic. If you want colored output you can lift PaintUtil as a starting point and adapt it.

Deck Of Cards — API reference

Card(Suit suit, Rank rank, bool faceUp)

Member Description
Suit, Rank The card's suit and rank (Rank.Ace == 1 … Rank.King).
FaceUp Whether the card is face up. Settable.
IsRed True for Hearts and Diamonds.
Glyph Suit symbol (♣ ♦ ❤ ♠).
RankName Short rank label (A, 2T, J, Q, K).
ToString() e.g. "♠ K".

Deck : IEnumerable<Card>

Constructors:

  • Deck(int cardMax = 52, bool faceUp = false) — empty deck with a capacity and default facing.
  • Deck(List<Card> cards, bool faceUp = false) — wrap an existing list.
Member Description
Count / IsEmpty Number of cards / whether the deck is empty.
Fill() Add sorted cards until the deck reaches CardMaximum.
Shuffle() In-place FisherYates shuffle.
Insert(Card) Add a card on top; the card adopts the deck's FaceUp.
Peek(int index = 0) Look at a card from the top (0 = topmost). Throws DeckIsEmptyException if empty.
TryPeek() Like Peek() but returns null on an empty deck instead of throwing.
Top() Remove and return the top card. Throws DeckIsEmptyException if empty.
TakeTop(int count) Remove the top count cards, returned bottom-to-top (ready to Insert() in sequence).
Cycle() Move the top card to the bottom and return the new top — handy for looping displays.
MoveToDeck(Deck other) Move every card into another deck (used e.g. to recycle the waste back into the stock).
CorrectFacing() / (bool) Flip all cards to the deck's facing, or to an explicit direction.

DeckIsEmptyException is thrown by the operations noted above when they can't act on an empty deck.


AI usage

AI was used during development to help design the deck/card system, to write some utility helpers (notably the ANSI truecolor drawing in PaintUtil), and to draft this README. The rest of the code was written by me.