The Card Game Assignment

 

John K. Estell

Electrical & Computer Engineering and Computer Science Department

Ohio Northern University

Last updated 24 February 2004

 

 

 

1.     Overview

 

Card game programs are both visual and event-driven; playing cards serve as a well-recognized graphical element and the play of the game progresses through the handling of discrete user-generated events.   As assignments, games are often challenging to write, but provide both a definite goal to strive for and a greater sense of accomplishment as the completed program actually does something.  Along with the motivational value of such assignments, the writing of games promotes strategic thinking.  A programmer must consider how to properly utilize data structures to represent the elements of the game and how to establish the necessary heuristics for evaluating the status of the game.

 

In the past, each card game program had to be essentially written from scratch; however, when you think about, what really changes from implementation of one game to the next?  How does the concept of a card or a deck differ?  There is a great deal of functionality that stays the same, regardless of the card game being implemented.  In this assignment, we will take an object-oriented programming approach to the problem and determine the constituent parts of a card game.  In particular, we will look at the card as an object and examine both the properties of a card and how cards are used, then design our classes accordingly.  By taking this approach, we will find that the writing of the code can be compartmentalized into classes that are easy to write and can be readily reused, leaving only a small amount of code that has to be written for a particular application.

 

 

 

2.     The Rank and Suit classes

 

When describing a playing card, we refer to two properties of a card.  The suit of a card refers to one of four possible sets of playing cards in a deck: clubs, diamonds, hearts, and spades.  The rank of a card refers to the name of a card within a suit: ace, two, three, four, five, six, seven, eight, nine, ten, jack, queen, and king. Traditionally, the rank is used to specify the ordering of cards within a suit, e.g. the two comes before the three, and the jack comes before the queen.  The combination of suit and rank uniquely describe a card found in a standard deck of playing cards.

 

To express the rank and suit values, static objects privately instantiated from the Rank and Suit classes are used.  This methodology allows for the necessary values to be readily available without the headache of erroneous values potentially being instantiated by the client; it also gets away from the use of “magic numbers” to represent conceptual values such as a club or a jack. The static list of VALUES in each class allows for iterators to be constructed when generating Card objects.  Both classes implement the Comparable interface so that it allows for the sorting of a hand or a deck of cards to be accomplished.  As it is commonplace for card games to specify that either the rank of king or ace constitutes the highest rank within a suit, the static methods setAceHigh() and setKingHigh() in the Rank class allows comparisons to be performed with whatever rank is appropriate for the game being implemented set by the client as the highest ranking card.  The Rank class is also used as the primary basis for establishing the value of a card when performing the evaluation of a hand of cards in a game.

 

The complete specifications for the Suit class are provided in the Class Suit javadoc-generated document.  The corresponding code is provided in the file Suit.java.

 

The complete specifications for the Rank class are provided in the Class Rank javadoc-generated document.  The corresponding skeleton code is provided in the file Rank.java.  For this portion of the assignment, use the documentation and the example code provided in Suit.java to implement the Rank class.

 

 

 

3.     The Card class

 

For uniquely describing a card, it is sufficient to use the rank and suit properties.  However, in order to work in a visually-oriented environment, a third property is required: the image, or graphical representation, of the card. One of the problems with GUI implementations of card games is finding images of cards that are not encumbered by copyrights; fortunately, there is a set of card images available through the GNU General Public License. The format of the filenames for these images is such that the process of reading in the images can be automated.  All of the standard card images are stored in individual files using filenames in the form of:

 

RS.gif

 

where R is a single character used to represent the rank of the card and S is a single character used to represent the suit of the card.  The characters used for R are: 'a' (ace), '2', '3', '4', '5', '6', '7', '8', '9', 't' (for 10), 'j' (jack), 'q' (queen), and 'k' (king).  The characters used for S are: 'c' (clubs), 'd' (diamonds), 'h' (hearts), and 's' (spades).  Two other cards are also available: b.gif (back of card) and j.gif (joker).  To assist with the generation of filenames, the constants defined in the Rank and Suit classes store the characters associated with each rank and suit value. The static getFilename() method refers to these values and bases its generation of the filenames according to the rules specified above.

 

The Card class implements the Comparable interface for the purpose of sorting a hand of cards. As in some games it is preferable to sort the cards in rank-major order whereas in others suit-major order is preferred, the Card class has two static methods, setRankMajorSort() and setSuitMajorSort(), that allows the client to specify the appropriate ordering method for the card game being implemented.

 

The complete specifications for the Card class are provided in the Class Card javadoc-generated document.  The corresponding skeleton code is provided in the file Card.java.  For this portion of the assignment, implement the Card class.

 

 

 

 

 

4.     The Deck class

 

The Deck class serves as a container for Card objects, and possesses the functionality of a typical deck of cards.  When instantiated, decks are empty; they need to be populated with cards via iteration on the sets of rank and suit values.  For each card, the relative pathname of the image associated with the current rank and suit value combination must be generated; this is usually a combination of the name of the directory storing the card images and the filename of the specified image.  The following code fragment would be implemented in the init() method of an applet to populated a Deck object:

 

cardDeck = new Deck();

String directory = "cards/";

Iterator suitIterator = Suit.VALUES.iterator();

while ( suitIterator.hasNext() ) {

    Suit suit = (Suit) suitIterator.next();

    Iterator rankIterator = Rank.VALUES.iterator();

    while ( rankIterator.hasNext() ) {

        Rank rank = (Rank) rankIterator.next();

        String imageFile = directory + Card.getFilename( suit, rank );

        ImageIcon cardImage = new ImageIcon( getImage( getCodeBase(), imageFile ) );

        Card card = new Card( suit, rank, cardImage );

        cardDeck.addCard( card );

    }

}

 

Once the deck has been populated with cards, it is normally shuffled, then used to "deal" cards.  When dealing cards, it is best not to implement the activity literally; i.e., to remove cards from the deck, as this often add needless complexity to the implementation of the game, such as how to deal with the transition from one hand to the next.  Instead, the set of cards in the deck is treated as immutable after initialization (although the listing of these cards is mutated through invocation of the shuffle() method), with the drawing of cards performed by using an index to refer to the "top of deck" location in the list.  To restore the deck, the invocation of the restoreDeck() method resets the index to the beginning.

 

The complete specifications for the Deck class are provided in the Class Deck javadoc-generated document.  The corresponding skeleton code is provided in the file Deck.java.  For this portion of the assignment, implement the Deck class.

 

 

 

5.     First Program: exercising the Card and Deck classes

 

At this point, we can implement our first test program to exercise the implementation of the Card and Deck classes.  The CardDeckDemo applet uses a JLabel to display both the image and the name of the card just drawn from a deck.  Two JButtons are used to handle user interactions: one to draw a card from the deck, and the other to both restore and shuffle the deck.  A second JLabel is used to indicate the number of cards remaining in the deck; when the deck is empty, the "Draw a card" button is disabled until the "New & shuffled deck" button is pressed.  Figure 1 consists of a snapshot from the CardDeckDemo applet.

 

 

Figure 1. Snapshot from CardDeckDemo applet.

 

 

The CardDeckDemo applet is available as part of the provided demonstration applets for this assignment.  At this time, write CardDeckDemo.java such that it behaves identically to the supplied demonstration applet.

 

 

 

6.     The Hand superclass

 

The Hand class represents the basic functionality of a hand of cards.  Those operations that are normally conducted upon a hand, such as adding or removing cards, are supported; however, the evaluation of the cards contained in the hand is defined as an abstract method.  This allows code common to the implementation of a hand in various games to be written once and reused as needed.  The code specifically required for the evaluation of a hand in a particular game is developed within a class extended from Hand by providing a definition of the evaluateHand() method; this method can then be accessed directly or via a superclass reference when comparing or evaluating hands.

 

The complete specifications for the Hand superclass are provided in the Class Hand javadoc-generated document.  The corresponding skeleton code is provided in the file Hand.java.  For this portion of the assignment, implement the Hand class.

 

 

 

7.     Second Program: the “DumbGame” card game

 

The "Dumb Game" card game is exactly that (when viewed as a game).  However, its real purpose is to allow you to write a simple extension of the Hand class to implement the evaluation algorithm and to exercise some of the methods in the classes we have so far written.  The operation of the game is simple.  Eight cards are drawn from a full deck of cards and placed into the player's hand; the cards are displayed with both their graphical image and their name of separate JLabels. Each card is worth its displayed pip value (ace = 1, two = 2, etc.) in points with face cards worth ten points.  The value of a hand is equal to the summation of the points of all the cards held in the hand; this value is displayed to the user.  Figure 2 presents the initial display of the applet; note that in this instance the hand is deliberately dealt from an unsorted deck to further verify the proper instantiation of the cards.

 

 

Figure 2. Initial display for DumbGame applet.

 

 

When the "Draw a Hand" button is pressed, the generated event is used to restore the deck, shuffle the cards, draw a new hand, and display the unsorted result to the user, as shown in Figure 3.

 

 

Figure 3.  A new hand is drawn.

 

 

The player now has the exciting options of sorting the drawn cards in either rank-major or suit-major order through use of the "Perform Rank-Major Sort" and "Perform Suit-Major Sort" JButtons.  The results of pressing these two buttons are shown in Figures 4 and 5.

 

 

Figure 4.  The hand is displayed in rank-major order.

 

 

 

Figure 5.  The hand is displayed in suit-major order.

 

 

The implementation of this program is relatively simple, as most of the code has already been written!  Two source code files need to be developed: DumbGameHand, which is a subclass of Hand, and DumbGame, which is an extension of JApplet and contains the definitions of the graphical user interface and the event handlers for this game.

 

The DumbGame applet is available as part of the provided demonstration applets for this assignment.  The complete specifications for the DumbGameHand subclass are provided in the Class DumbGameHand javadoc-generated document.  The corresponding skeleton code is provided in the file DumbGameHand.java.  For this portion of the assignment, implement the DumbGameHand class then write CardDeckDemo.java such that it behaves identically to the supplied demonstration applet.

 

Variations:

a.       Implement mouse event handlers for each of the JLabel objects. When a mouse click occurs, replace the selected card with the next card from the deck and recalculate the score. If the deck runs out of cards, temporarily disable the mouse events and display an appropriate message to draw a new hand. 

b.      Keep track of the high score and display it next to the score for the current hand.

 

 

 

 

8.     Third Program: Beginner’s Blackjack

 

In section 7 you have written your first, albeit simplistic, card game.  In this section, we will demonstrate the power of the object-oriented programming paradigm by writing a second card game, beginner's blackjack.  In this version of blackjack, only two cards are dealt.  The scoring of the hand is according to the rules of blackjack: the face cards are each worth ten points, and the other cards are worth the number of pips displayed, save for the ace.  In regular blackjack, the ace can be worth either one or eleven points; however, in this version, the ace is always worth eleven points as it is impossible to go over 21 in this game.  The two cards drawn from the deck are displayed on JLabels and the resulting score is also shown on a JLabel.  Each time the draw a hand event occurs, the deck is restored and shuffled, after which two cards are drawn from the deck and placed into the player's hand.  Figure 6 illustrates the look of a typical Beginner's Blackjack applet:

 

 

 

Figure 6.  Snapshot of Beginner's Blackjack applet.

 

 

The object of Beginner's Blackjack is to score a blackjack; when this happens, the display should be modified such that the score has an exclamation point appended to it and is displayed in a different color.  Figure 7 shows how the demonstration applet displays a blackjack to the user:

 

 

Figure 7.  Scoring a blackjack.

 

 

To implement this game, all that is needed is to write BlackjackHand, a subclass of Hand, and the applet BlackjackHandDemo that implements the graphical user interface and the draw hand event. The BlackjackHandDemo applet implements the Beginner's Blackjack game and is available as part of the provided demonstration applets for this assignment. 

 

Variations:

a.       Continue to deal from the same deck, and keep track of the number of cards left in the deck.  When the deck is out of cards, display an appropriate message, disable the "Draw a Hand" button, and making visible a "New Deck" button.  The new deck event will restore the deck of card to a full deck, shuffle the cards, and re-enable the draw a hand button.

b.      Create a dealer hand and a player hand; keep track of the number of hands the dealer and the player wins.

 

 

 

 

 

9.     Fourth Program: A Real Card Game!!!

 

 

 

 

 

 

Insert the final game you want your students to implement here.

 

Basic rules for many card games can be found at:

http://www.usplayingcard.com