{"id":28173411,"url":"https://github.com/eluciusftw/cardgames","last_synced_at":"2025-10-31T19:02:44.118Z","repository":{"id":46416358,"uuid":"385236629","full_name":"EluciusFTW/CardGames","owner":"EluciusFTW","description":"A repository with domain models as a basis to implement card games in C#, as well as implementations of poker simulations within this framework.","archived":false,"fork":false,"pushed_at":"2024-10-13T12:01:03.000Z","size":245,"stargazers_count":79,"open_issues_count":1,"forks_count":6,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-19T18:37:37.125Z","etag":null,"topics":["card","card-game","card-games","csharp","domain-driven-design","poker"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/EluciusFTW.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-07-12T12:15:48.000Z","updated_at":"2025-03-25T12:15:26.000Z","dependencies_parsed_at":"2023-12-16T12:34:42.295Z","dependency_job_id":"7f01ad46-97d8-48b5-b148-b8b161398892","html_url":"https://github.com/EluciusFTW/CardGames","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EluciusFTW%2FCardGames","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EluciusFTW%2FCardGames/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EluciusFTW%2FCardGames/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/EluciusFTW%2FCardGames/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/EluciusFTW","download_url":"https://codeload.github.com/EluciusFTW/CardGames/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254414360,"owners_count":22067272,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["card","card-game","card-games","csharp","domain-driven-design","poker"],"created_at":"2025-05-15T20:13:30.328Z","updated_at":"2025-10-31T19:02:39.077Z","avatar_url":"https://github.com/EluciusFTW.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CardGames\n- [Introduction](#introduction)\n- [CardGames.Core](#cardgamescore)\n  * [Overview](#overview)\n  * [Card](#card)\n  * [Deck](#deck)\n  * [Dealer](#dealer)\n- [CardGames.Core.French](#cardgamescorefrench)\n- [CardGames.Poker](#cardgamespoker)\n- [Benchmarks](#benchmarks)\n- [License](#license)\n- [Versioning](#versioning)\n- [Feedback and Contributing](#feedback-and-contributing)\n\n[![CI Build](https://github.com/EluciusFTW/CardGames/actions/workflows/CI.yml/badge.svg)](https://github.com/EluciusFTW/CardGames/actions/workflows/CI.yml)\n\n# Introduction\nAfter writing simulations and tools for card games (poker in particular) in the last years, I have decided to take a step back, sort through the code bases, clean up here and there, and distill out small, reusable packages that hopefully can be useful for the open source community.\n\n### About the project\nI assume this project will be slow, because of several reasons. First of all, it is a _pure leisure project_. Secondly, because I value design. I try to model the entities and their apis as closely as possible to the real world concepts they represent (albeit adding some convenience apis if they are very useful). I value code quality and readability a lot as well. I'll spend lots of time rewriting algorithmically simple things if I feel I can express them even cleaner, or more performantly. \n\n# CardGames.Core\nAvailable at Nuget: [EluciusFTW.CardGames.Core](https://www.nuget.org/packages/EluciusFTW.CardGames.Core/)\n\n### Overview\nThere are thousands of different [card games](https://en.wikipedia.org/wiki/Card_game), with many different cards and collections of cards, with different rules and purposes. \n\nIn this package, we tried to follow a domain-driven approach to card games in general\u003csup\u003e1\u003c/sup\u003e. \nIt contains the basic entities needed for such a game: _Cards_ (duh!), _Card decks_ (which are the finite collections of all possible cards of given type) and _Dealers_.\n\n\u003csup\u003e1\u003c/sup\u003e Actually, up to abuse of language, any game of chance that contains a finite set of possibilities can be modelled using this package, e.g., we can interpret a die as a deck of six cards called: 1,2,3,4,5,6. We can implement a `DiceDealer` who 'shuffles the deck' (i.e. returns the dealt/rolled card/value back to the deck immediately) after dealing a card (rolling the die).\n\n### Card\nThe most elemental part of a card game is the card. There is actually nothing universal that describes a card, except for it being detemined by it's content. So in all later entities the card will be represented by a generic type `TCard`, with the constraint that it is a **class**. \n\n\u003e NOTE: Until recently, `TCard` was generically constrained to be a **struct**. However, I have decided to switch over to **class**, as in all my use-cases, the gain from passing-by-reference as default (in terms of performance) was higher than the benefit of creation on the stack, as they are often passed around into other methods, collections, etc.  Another reason is that the memory allocation needed to create an instance depends heavily on implementation, and might be big, depending on your card deck and game (think: Cards in a deck builder game with lot's of properties).\n\n### Deck\nThe finite collection of all different cards, in a bunch, is called a deck.\n\nThe package provides a generic interface for a deck which holds cards of the generic type `TCard`\n```cs\n// Interface definition for a generic deck\npublic interface IDeck\u003cTCard\u003e where TCard : class\n{\n    int NumberOfCardsLeft();\n    TCard GetFromRemaining(int index);\n    TCard GetSpecific(TCard specificCard);\n    void Reset();\n};\n````\n\n### Dealer\nThe dealer is the entity handling the deck. Instead of only providing an interface like for the deck, the library provides a generic implementation of a dealer, which can be specified (i.e., derived from) in order to add more specific functionality:\n```cs\npublic class Dealer\u003cTCard\u003e where TCard : class\n{\n    public Dealer(IDeck\u003cTCard\u003e deck) {...}\n    public Dealer(IDeck\u003cTCard\u003e deck, IRandomNumberGenerator numberGenerator) {...}\n\n    public TCard DealCard() {...}\n    public IReadOnlyCollection\u003cTCard\u003e DealCards(int amount) {...}\n    public void Shuffle() {...}\n}\n````\nThe dealer can deal one or many cards at once, and shuffle the deck. In order to do that, he needs a deck (duh!). In order to shuffle, he needs some source of randomness. We provide an interface you can implement,\n```cs\npublic interface IRandomNumberGenerator\n{\n    public int Next(int upperBound);\n}\n````\nand a standand implementation (which is just a wrapper holding an instance of `System.Random`).\n\n# CardGames.Core.French\nAvailable at Nuget: [EluciusFTW.CardGames.Core.French](https://www.nuget.org/packages/EluciusFTW.CardGames.Core.French/)\n\nThis library is an implementation of the core library for the arguably the most well-known playing card: the [french-suited playing card](https://en.wikipedia.org/wiki/French-suited_playing_cards).\n\n### French-suited playing cards\nA french-suited playing card is characterized by two properties: The _suit_ (Diamonds, Hearts, Clubs and Spades) and the _symbol_ (Deuce, Three ... King, Ace), both of which are represented as enums in the library. The _value_ of the card is a numeric value betwen 2 and 14 in bijective relation to it's symbol:\n```cs\n// Using the Suit and Symbol enum\nvar card = new Card(Suit.Hearts, Symbol.Deuce);\n\n// Using the Value instead of the Symbol\nvar card = new Card(Suit.Hearts, 8);\n````\n\n### Serialization and Deserialization\nThere are conventional string representations for these cards, which we support via extensions (resp. implementing the `ToString()` method):\n```cs\n// String extension expecting the format {symbol char}{suit char}\nvar card = \"Jc\".ToCard();\nvar serializedCard = card.ToString() // equals \"Jc\" again.\n\n// String extension expecting one or more cards separated by a space\nvar cards = \"2h 5d Qs\".ToCards();\nvar serializedCards = cards.ToStringRepresentation(); // equals \"2h 5d Qs\" again.\n````\n\n### Dealing with collections of cards\nSince most card games involve players having more than one card, handling collections of cards is needed. We provide several extensions on `IEnumerable\u003cCard\u003e` for convenience:\n\n```cs\n// Get cards by descending value\nIReadOnlyCollection\u003cCard\u003e ByDescendingValue(this IEnumerable\u003cCard\u003e cards)\n\n// Get values in several flavors\nIReadOnlyCollection\u003cint\u003e Values(this IEnumerable\u003cCard\u003e cards)\nIReadOnlyCollection\u003cint\u003e DescendingValues(this IEnumerable\u003cCard\u003e cards)\nIReadOnlyCollection\u003cint\u003e DistinctDescendingValues(this IEnumerable\u003cCard\u003e cards)\n\n// Get all distinct suits\nIReadOnlyCollection\u003cSuit\u003e Suits(this IEnumerable\u003cCard\u003e cards)\n\n// Check if given values are all in the cards\nbool ContainsValue(this IEnumerable\u003cCard\u003e cards, int value)\nbool ContainsValues(this IEnumerable\u003cCard\u003e cards, IEnumerable\u003cint\u003e valuesToContain)\n\n// Detemines highest value-duplicates\nint ValueOfBiggestPair(this IEnumerable\u003cCard\u003e cards) \nint ValueOfBiggestTrips(this IEnumerable\u003cCard\u003e cards)\nint ValueOfBiggestQuads(this IEnumerable\u003cCard\u003e cards)\n````\n\n## French decks\nOf course, as we have provided the french-suited card, we also provide some decks containing these cards in this library.\n\nFirst of all, there is a base class which provides a few more useful methods already using the fact that a french card has a `symbol` (resp. `value`) and a `suit`. The only thing an implementing class must provide is the collection of _all cards_ in the deck:\n```cs\npublic abstract class FrenchDeck : IDeck\u003cCards.French.Card\u003e\n{\n    protected abstract IReadOnlyCollection\u003cCard\u003e Cards();\n    public IReadOnlyCollection\u003cCard\u003e CardsLeft() {...}\n    public IReadOnlyCollection\u003cCard\u003e CardsLeftOfValue(int value) {...}\n    public IReadOnlyCollection\u003cCard\u003e CardsLeftOfSuit(Suit suit) {...}\n    public IReadOnlyCollection\u003cCard\u003e CardsLeftWith(Func\u003cCard, bool\u003e predicate) {...}\n}\n````\nThere are two implementations in the package:\n- `FullFrenchDeck`: The standard 52-card deck consisting of Deuce-to-Ace of all four suits.\n- `ShortFrenchDeck`: A 36-card deck consisting of Six-to-Ace of all four suits (like is used in [Short-deck poker](https://en.wikipedia.org/wiki/Six-plus_hold_%27em)).\n\n## French-deck dealer\nOne could wonder why a dealer cares whyt kind of deck he deals, i.e., why a specific dealer implementation for a given deck makes sense.\n\nIn our domain view, the dealer is the owner of the deck, and responsible for dealing the cards. He hence has knowledge about the deck (a dealer can peek if he wants!), and using this knowledge, combined with a specific deck let's us add convenience methods on the dealer. \n\nThe `FrenchDeckDealer` we provide in this package (including two factory methods for the decks we have defined earlier), can peek into the deck and try to narrow down the cards from which he deals the next, randomly:\n```cs\n// provides a dealer with full deck (or use .WithShortDeck() for a short deck)\nvar dealer = FrenchDeckDealer.WithfullDeck();\n\n// deals a random card of given value, suit or symbol. \n// Succeeds if there are still some in the deck, else fails.\n_ = dealer.TryDealCardOfValue(7, out var card);\n_ = dealer.TryDealCardOfSymbol(Symbol.King, out var card);\n_ = dealer.TryDealCardOfSuit(Suit.Spades, out var card);\n````\nThis is very useful and increases performance in simulation scenarios where certain specific situations have to be recreated over and over.\n\n# CardGames.Poker\nThis library utilizes the _French cards_ and proceeds to model Poker variants. It is not yet published as a package as it is not yet mature enough.\n\n## Hands\nThe library contains domain models for hands in these poker disciplines:\n - 5-card draw\n - Holdem \n - Omaha\n - Stud \n\nThe Holdem and Omaha hands derive from a more generic hand model called `CommunityCardsHand`, which can be used to model any kind of community card hand (any number of community cards, any number of hole cards, any requirement how many of them have to be used for a hand, and how many must at least be used. So in these parameters, a Holdem hand is (3-5, 2, 0, 2) and a Omaha hand (3-5, 4, 2, 2)). So using this as a basis, it is easy to implement, e.g., 5-card PLO and other lesser known variants.\n\nHands all implement `IComparable`, and the operators \"\u003c, \u003e\" are implemented by default. This is accompished by using two properties of the base class of any hand:  \nStrength (of type `long`) and Type (e.g. `HandType.Flush`). The calculations of the strength and type are directly performed when constructing the hand, and they are designed in such a fashion that the ordering of the types can be provided as well (because, e.g., in short deck, a flush beats a full-house). The classical orderign as well as the ordering for short-deck poker are provided in the class `HandTypeStrength`.\n\n## Simulations\nThe library contains models for Holdem (full and short deck) and Stud simulations (currently still in the `CardGames.Playground` project, but they will soon move to the `CardGames.Poker` project), and other simulations can easily be built in similar fashion. Both Simulations are configurable with a fluent builder pattern. Here's an example of a Holdem simulation configuration:\n```cs\n// any number of players can be added\n// each players hole cards can be specified by providing zero, one or two cards\n// optionally, a flop/turn/river can be provided\n// finally the simulation is executed by calling SimulateWithFullDeck resp. SimulateWithShortDeck\nprivate HoldemSimulationResult RunHoldemSimulation(int nrOfHAnds)\n    =\u003e new HoldemSimulation()\n        .WithPlayer(\"John\", \"Js Jd\".ToCards())\n        .WithPlayer(\"Jeremy\", \"8s 6d\".ToCards())\n        .WithPlayer(\"Jarvis\", \"Ad\".ToCards()) \n        .WithFlop(\"8d 8h 4d\".ToCards()) \n        .SimulateWithFullDeck(nrOfHAnds);\n````\n\nThe Stud simulation works similarly. However, since a player has different kinds of cards, one provides any number of `StudPlayers` to the simulation, which have a builder of their own. Here's what that looks like in an example:\n````cs\n// again, any number of players can be specified\n// and each players hole and board cards can be specified individually\nprivate StudSimulationResult RunStudSimulation(int nrOfHAnds)\n    =\u003e new SevenCardStudSimulation()\n        .WithPlayer(\n            new StudPlayer(\"John\")\n                .WithHoleCards(\"Js Jd\".ToCards())\n                .WithBoardCards(\"Qc\".ToCards()))\n        .WithPlayer(\n            new StudPlayer(\"Jeremy\")\n                .WithHoleCards(\"3s 4s\".ToCards())\n                .WithBoardCards(\"7s\".ToCards()))\n        .WithPlayer(\n            new StudPlayer(\"Jarvis\")\n                .WithBoardCards(\"Tc\".ToCards()))\n        .Simulate(nrOfHAnds);\n````\n\nIf you want to play around with these simulations, there is a console program in `CardGames.Playground.Runner` where you can run any simulation. It also contains some benchmarks (using [BenchmarkDotNet](https://benchmarkdotnet.org/)), which you can run. In fact, they have been instrumental in finding the right balance between design and performance, big shoutout to them!\n\nThe simulation result classes contain a complete collection of all run hands, as well as some predefined queries and aggregations, which can easily be extendend and customized due to the fact that the full collection of hands is available. \n\nHere is a simple printout of the above Stud simulation:\n![Screenshot of Stud Simulation](./sample/stud-simulation-screenshot.png)\n\nHere is a simple printout of the above Holdem simulation:\n![Screenshot of Holdem Simulation](./sample/holdem-simulation-screenshot.png)\n\n## Benchmarks\nThis repository also contians two benchmark projects: one for the core packages, one for the poker simulations. These are only meant to be utilities during development in order to test the implementations for their performance, resp. to prevent introduction of performance regressions. Once stable enough, baseline benchmarks _might be included_ in the documentation and in the workflows.\n\n## Versioning\nWe have switched from manual semantic versioning to using [NerdBank.GitVersioning](https://github.com/dotnet/Nerdbank.GitVersioning) and follow the version scheme: `\u003cmajor\u003e.\u003cminor\u003e.\u003cgit-depth\u003e` for out releases. All packages in this repository will have synchronized version numbers.\n\n\u003e \u003cb\u003eNote\u003c/b\u003e: In particular, the _third number_ in the version does not have the same meaning as the patches in SemVer. Increments in that number may contain breaking changes, in contrast to patch versions in SemVer.\n\n## Feedback and Contributing\nAll feedback welcome!\nAll contributions are welcome!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feluciusftw%2Fcardgames","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feluciusftw%2Fcardgames","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feluciusftw%2Fcardgames/lists"}