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: CardDeck, 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:

  1. Generic Components: We have generic components like CardDeck, and Suit that can be used universally.
  2. Specialized Classes: By subclassing these components, we tailor the data structures for specific games without altering the base structure.
  3. Extensibility: This design allows easy addition of new games by subclassing and modifying only relevant parts of the functionality.