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#
1
2
3
|
public enum Suit {
HEARTS, DIAMONDS, CLUBS, SPADES;
}
|
Card Class#
A card is compose of suit and a value.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
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.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
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#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
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#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
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
, and Suit
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.