Concepts / Designing Card Game Classes

Designing Card Game Classes

A subclass is created by extending an existing class, so it inherits what the parent provides and adds only its changes and additions.

  • Programming

Reusing a Class Instead of Starting Over

Imagine you already have a class that does most of what your program needs. Perhaps it stores a collection of cards and knows how to add a card to that collection. Now your new project needs something that is nearly the same but differs in a few specific ways. You have three options. You could rewrite the whole class, which is wasteful because most of the work is already done. You could copy the code into a new file, which creates two versions that must be maintained separately and can drift apart over time. Or you could use the third option that object-oriented languages offer: build the new class on top of the old one and write only the parts that are different.

This article follows that idea through a card game. As you read, pay attention to which part of the program is inherited without being rewritten and which part is new. Those two parts are where most errors in subclass code come from, so learning to tell them apart is the real skill here.

What do you think happens?

If you need a hand that behaves like a generic Hand but also knows Blackjack scoring, is it better to rewrite Hand, copy Hand into a new file, or subclass Hand?

  • Rewrite Hand from scratch

  • Copy Hand into a new file

  • Subclass Hand

Reveal answer
Answer:

Subclass Hand

A subclass keeps everything Hand already does and adds only the one capability the game requires. Rewriting repeats finished work, and copying creates a second version that must be maintained separately.

Reading the Subclass Header

The general form of a subclass header has three parts in a fixed order. The name of the new class comes first, followed by the keyword extends and then the name of the existing class. The braces that follow hold the changes and additions. The ellipsis in the general pattern stands for code you would write yourself, not code you copy literally.

java
BlackjackHandname of the new subclassextendslinks the new class to aparentHandthe existing parent class{ ... }only the changes andadditions
What does each part of the class header (subclass name, the extends keyword, parent name) refer to?

Read the header from left to right as a sentence: BlackjackHand is a class that extends Hand. The name on the left is the new class, the name after extends is the class it builds on, and the braces are where you write the differences. Nothing inside the braces needs to repeat what Hand already provides.

Why Blackjack Hands Are a Natural Subclass

The program aims to play Blackjack and can reuse the Card, Hand, and Deck classes already developed. A generic hand of cards, however, is not quite the same as a Blackjack hand. A Blackjack hand must be able to report its value according to the rules of the game, while a generic hand has no reason to know those rules. That makes the Blackjack hand a natural candidate for a subclass of Hand. It keeps everything a Hand already does and adds the one capability the game requires.

The direction of the relationship matters. The arrow in a class diagram runs from the subclass to the class it extends, which means BlackjackHand depends on Hand and not the other way around. A subclass can use what its parent provides, and it adds what the parent lacks.

ApproachWhat you writeWhat you get for freeMain risk
Rewrite from scratchThe full class, including storage and adding cardsNothing; all work is repeatedWasted effort on code that already works
Copy the old classA full duplicate in a new fileThe code as it was at the time of copyingTwo versions that must be maintained separately
Subclass the old classOnly the header and the changes or additionsEverything the parent already providesConfusing inherited code with new code

Three ways to build a Blackjack hand from an existing Hand class

Subclassing is most useful when you already have a working class that can be adapted with a few changes or additions. Designing a whole family of related classes from nothing is a bigger undertaking and is less common, especially for programmers still getting comfortable with objects. Subclassing lets you reuse the work already done so the new class stays small and focused on what makes it different.

The Rules for Hand Value

Before writing any value logic, the rules must be clear, because subclass code is simply a translation of them. The value of a hand is found by adding up the values of its cards. Face cards are worth 10. The Ace is the only card whose value depends on context, and that dependence is what makes hand-value code tricky.

Definition

The Ace rule: an Ace is counted as 11 unless doing so would push the total of the hand above 21. In that case it is counted as 1.

A consequence of this rule is that the first Ace in a hand may be counted as 11, but any further Aces will always be counted as 1. A second 11 would almost always push the total over 21 when combined with the first.

Key point

Hand value is not one addition. It is a sequence of checks: count the Ace as 11, test the running total against 21, and fall back to 1 when needed. Each check is a place where the code can go wrong.

Tracing an Ace Through a Hand

The clearest way to understand the Ace rule is to watch the running total change one card at a time. The following hand contains an Ace, a 6, and a 9. The 6 and 9 count at their printed values. Follow the total as each card arrives and notice the exact moment the Ace changes its value.

Acecounted as 11, total 116total 179total 26, above 21Ace drops to 1total 16next cardnext cardover 21, so Ace is 1
How does the running total change card by card, and at what point does an Ace switch from counting as 11 to counting as 1?

Read the diagram as a loop that visits each card in order. The Ace first counts as 11, giving a total of 11. The 6 raises the total to 17, which is still at or below 21, so nothing changes. The 9 raises it to 26. Because 26 is above 21, the Ace check fires: the Ace is recounted as 1, which lowers the total by 10 to 16. The change happens at the step where the running total is compared against the limit, which is the only point in the sequence where that comparison occurs.

Worked example

Two Aces in one hand

Compute the value of the hand Ace, Ace, 9 using the Ace rule.

  1. 1
    First Ace

    Count it as 11, since the running total of 11 is at most 21. The total is 11.

  2. 2
    Second Ace

    Counting it as 11 would bring the total to 22, which is above 21. So it counts as 1 and the total is 12.

  3. 3
    The 9

    Add 9 to the running total of 12. The total is 21, which is at or below 21, so no further Ace adjustment is needed.

Result

The hand is worth 21.

What do you think happens?

Before reading the result, predict the value of the hand King, Ace, Ace. Count the Aces using the rule above.

Reveal answer
Answer:

12

The King is worth 10, giving 10. The first Ace counts as 11, giving 21. The second Ace would push the total to 32 if counted as 11, so it counts as 1, giving 22. Check this against the rule: 22 is above 21, so the second Ace must not be counted as 11 and the correct final total is 12 when the first Ace also falls back to 1 because the total with it at 11 would exceed 21 once the second Ace is considered. The first Ace is therefore also recounted as 1, for a total of 12.

Mistakes When Writing Value Logic

  • Common mistake

    Always counting an Ace as 11

    What it looks like

    For Ace, 6, 9 the code returns 26.

    Why it fails

    The rule says an Ace counts as 11 only when the total stays at or below 21. Here the total would exceed 21, so the Ace must count as 1.

    Do this instead

    After adding each card, test whether the total is above 21 and, if so, recount an Ace as 1 before continuing.

  • Common mistake

    Checking the Ace rule only once at the end

    What it looks like

    The code adds all cards as 11 for Aces, then checks the total once at the end.

    Why it fails

    The rule depends on the running total at the moment each Ace is counted, so a single final check can miss the step where the total first passed 21.

    Do this instead

    Perform the check as a sequence of steps that follows the running total card by card.

  • Common mistake

    Rewriting inherited code in the subclass

    What it looks like

    BlackjackHand redefines how cards are stored even though Hand already does that correctly.

    Why it fails

    This repeats work the parent already does and creates a second place to fix bugs.

    Do this instead

    Inside the braces, write only the value logic that differs from Hand.

  • Common mistake

    Forgetting that the parent link goes from subclass to parent in the header

Finding Where a Subclass Diverges

When a subclass gives an unexpected total, the most effective debugging question is not 'why is the program wrong' but 'at which step does the value stop matching the rules'. Compare each intermediate value against the rule it should follow. The first step where the two disagree is where the divergence starts. For Blackjack hands, the most likely location is the Ace check, because it is the only step that compares the running total against a limit.

Read next cardAce, 6, 9 in orderAdd card valuerunning total 17 after the6Ace checkis total above 21?Recount Ace as 1buggy version skips thisReturn totalexpected 16, buggy 26repeat per cardafter each cardyesadjusted total
Where in the sequence of computed steps does the subclass's running value first differ from the expected value?
Worked example

Locating the divergence

A subclass returns 26 for the hand Ace, 6, 9, but the expected value is 16. Find the first step where the values disagree.

  1. 1
    Compare the running total after the Ace

    Expected 11, computed 11. The values agree, so the divergence has not started yet.

  2. 2
    Compare the running total after the 6

    Expected 17, computed 17. Still in agreement.

  3. 3
    Compare the running total after the 9

    The running total is 26 in the buggy version. The expected sequence has already passed the Ace check at this point and recounted the Ace as 1, so the expected running total is 16. The first disagreement is at the Ace check, not at any addition.

Result

The divergence begins at the Ace check. The fix belongs in the step that recounts an Ace as 1 when the total is above 21, not in the addition of the 9.

MEDIUM

Trace the hand Ace, Ace, 9 step by step. Write the running total after each card, mark the first point where an Ace is recounted, and state the final value.

Hints
  • Start with the first Ace counted as 11.
  • Before adding the second Ace as 11, check whether the total would exceed 21.
  • The final value should match the worked example earlier in this article.

Key Takeaways

  • A subclass is created by extending an existing class with the extends keyword, so it inherits what the parent provides and writes only its changes and additions.
  • A Blackjack hand is a natural subclass of Hand because it adds one capability the generic hand does not have: computing its value under the game's rules.
  • Hand value is a sequence of checks. An Ace counts as 11 unless the total would exceed 21, in which case it counts as 1, so further Aces are always counted as 1.
  • When a subclass gives an unexpected value, compare each intermediate step against its rule and locate the first step where the two disagree, which for Blackjack is usually the Ace check.