Problem statement
Design the object model and core APIs for a reusable playing-card library that many different card games can build on. The library models the cards themselves, holds them in a deck, shuffles that deck with a randomness source the caller supplies, and deals cards out to hands or piles as a game requests them.
Operating context. A single in-process library, imported by game code (a solitaire engine, a poker table, a rummy match) that runs in one process at a time. A game constructs a deck, shuffles it, and repeatedly asks for the next card or a batch of cards; the deck tracks what has been drawn and what remains. The caller owns the randomness source and passes it in, so a shuffle can be replayed exactly in a test. There is no UI, no networking, and no persistence between runs.
Out of scope. The rules of any specific game (scoring, legal moves, betting), rendering or ASCII art of cards, multi-process or networked play, saving a deck to disk, and card-image assets. Focus on the in-process object model of cards, decks, and dealing, not any game's rulebook or transport layer.
What to produce. The class hierarchy (value objects, the deck, and the dealing service), the public API each type exposes, and the state transitions of a deck (fresh to shuffled to partially dealt to exhausted). Be explicit about: why a card and its suit and rank are immutable value objects; how the shuffle takes an injected randomness source so a test can reproduce an exact ordering; how dealing tracks remaining versus drawn cards and behaves when the deck runs out; and how the design extends to jokers, a stripped 32-card deck, and a multi-deck shoe (six standard decks stacked) without rewriting the shuffle or deal core.
Functional requirements
- Build a standard 52-card deck as the full cross-product of four suits and thirteen ranks, with no duplicate cards.
- Shuffle the deck into a new order using a randomness source provided by the caller.
- Deal the next card from the top of the deck, moving it from remaining to drawn.
- Deal a batch of N cards in one call, or report cleanly that too few remain to satisfy the request.
- Report how many cards remain undealt and reset the deck back to its full, ordered state on demand.
- Construct non-standard decks: add jokers, drop low ranks for a 32-card deck, or stack several decks into one shoe.
Non-functional requirements
- A Card, its Suit, and its Rank are immutable value objects; two cards of the same suit and rank compare equal.
- The shuffle uses an injected randomness source so a seeded stub reproduces an exact ordering in a unit test.
- Dealing the next card and querying the remaining count are each O(1); a full shuffle is O(n) in deck size.
- The same deck, shuffle, and deal types are reused unchanged across different card games, with no per-game branching.
- Adding jokers, a stripped deck, or a multi-deck shoe is a localised change that does not edit the shuffle or deal code.
- The library is unit-testable with no wall clock, no real randomness, and no I/O.
Topics
- System Design LLD
- Oop Solid
- Patterns Strategy
- Patterns Factory
- Testability Clock