Problem
Design the data structures for a generic deck of cards. Explain how you would sub-class it to implement particular card games.
Solution
Before that lets understand deck of cards: Understanding Deck of Cards.
We’ll start by defining the basic structures: Card
, Deck
, and Suit
.
Basic Components
Suit Enum
public enum Suit {
HEARTS, DIAMONDS, CLUBS, SPADES;
}
Card Class
A card is compose of suit and a value.
public class Card {
private final Suit suit;
private final int value; // Assuming Ace = 1, Jack = 11, Queen = 12, King = 13
public Card(Suit suit, int value) {
this.suit = suit;
this.value = value;
}
public Suit getSuit() {
return suit;
}
public int getValue() {
return value;
}
@Override
public String toString() {
String[] valueNames = {
"", "Ace", "2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King"
};
return valueNames[value] + " of " + suit;
}
}
Deck Class
A deck has 52 cards.
public class Deck {
private final List<Card> cards;
public Deck() {
cards = new ArrayList<>();
for (Suit suit: Suit.values()) {
for (int i = 1; i <= 13; i++) {
cards.add(new Card(suit, i));
}
}
shuffle();
}
public void shuffle() {
Collections.shuffle(cards);
}
public Card draw() {
if (cards.isEmpty()) {
throw new IllegalStateException("No more cards in the deck");
}
return cards.remove(cards.size() - 1);
}
public int remainingCards() {
return cards.size();
}
}
Subclassing the deck for specific games
To manage different rules or specialized decks, we can subclass the Deck
and possibly Card
classes.
Poker Game
For a Poker game, you may not need to subclass Card
, but you may want a specialized Deck
that defines poker-specific shuffling or dealing rules.
public class PokerDeck extends Deck {
public PokerDeck() {
super();
}
public List<Card> dealHand(int handSize) {
List<Card> hand = new ArrayList<>();
for (int i = 0; i < handSize; i++) {
hand.add(draw());
}
return hand;
}
public static void main(String[] args) {
PokerDeck pokerDeck = new PokerDeck();
List<Card> hand = pokerDeck.dealHand(5);
System.out.println("Poker hand:");
for (Card card: hand) {
System.out.println(card);
}
}
}
Blackjack Game
For Blackjack, you might want to enhance Card
to include a more game-specific value computation (like treating an Ace as 1 or 11), and a specialized Deck
for dealing.
Blackjack Card Class
public class BlackjackCard extends Card {
public BlackjackCard(Suit suit, int value) {
super(suit, value);
}
public int getBlackjackValue() {
int value = getValue();
if (value > 10) {
return 10; // Face cards are worth 10
}
return value;
}
}
Blackjack Deck Class
public class BlackjackDeck extends Deck {
public BlackjackDeck() {
cards = new ArrayList<>();
for (Suit suit: Suit.values()) {
for (int i = 1; i <= 13; i++) {
cards.add(new BlackjackCard(suit, i));
}
}
shuffle();
}
public List<BlackjackCard> dealHand(int handSize) {
List<BlackjackCard> hand = new ArrayList<>();
for (int i = 0; i < handSize; i++) {
hand.add((BlackjackCard) draw()); // Cast to BlackjackCard
}
return hand;
}
public static void main(String[] args) {
BlackjackDeck blackjackDeck = new BlackjackDeck();
List<BlackjackCard> hand = blackjackDeck.dealHand(2);
System.out.println("Blackjack hand:");
for (BlackjackCard card: hand) {
System.out.println(card + " (Value: " + card.getBlackjackValue() + ")");
}
}
}
Here is the mermaid diagram of same:
classDiagram class Suit { } class Card { +Suit suit +int value +Card(Suit suit, int value) +Suit getSuit() +int getValue() +String toString() } class Deck { -List~Card~ cards +Deck() +void shuffle() +Card draw() +int remainingCards() } class PokerDeck { +List~Card~ dealHand(int handSize) } class BlackjackCard { +BlackjackCard(Suit suit, int value) +int getBlackjackValue() } class BlackjackDeck { +List~BlackjackCard~ dealHand(int handSize) } Card o--> Suit Deck o--> Card Deck <|-- PokerDeck Card <|-- BlackjackCard Deck <|-- BlackjackDeck
Summary
Here’s how the design achieves the flexibility to handle various card games:
- Generic Components: We have generic components like
Card
,Deck
, andSuit
that can be used universally. - Specialized Classes: By subclassing these components, we tailor the data structures for specific games without altering the base structure.
- Extensibility: This design allows easy addition of new games by subclassing and modifying only relevant parts of the functionality.