Project 4: Crazy Eights + Uno = Cruno

Due: Thursday, April 21, Friday, April 22, 2016 by 9:00PM


For Project 4, the cleanest way to implement the CrunoSkip and CrunoReverse cards involves "downcasting" a base class pointer to a derived class pointer.

Here's the problem. The base Game class doesn't have any way to deal with skip and reverse. So, you want the derived CrunoGame class to do things like remember the direction of play and whether the next player has to be skipped. You might even implement a couple of functions to allow the CrunoSkip card and the CrunoReverse card to tell a CrunoGame object what to do.

However, these new functions are in the CrunoGame class and not in the base Game class. Specifically, the prototype of playCard is:

virtual void playCard(Game *gptr, Player *pptr) ;

So, inside the playCard() function you only have access to Game functions (virtual and otherwise) but not to the new functions in CrunoGame, even though gptr is really pointing to a CrunoGame.

C++ has a dynamic_cast facility that allows you to "downcast" a base class pointer to a derived class pointer. Inside playCard() we want to have something like:

CrunoGame *cgptr ; cgptr = dynamic_cast<CrunoGame *>(gptr) ; Note that "gptr" is the first parameter of playCard(). Now you can use cgptr to access the new functions that are defined in CrunoGame to help the skip and reverse cards do their thing.

The dynamic_cast facility actually uses run time type-info to check that gptr is actually pointing to a CrunoGame and not a base Game. So, this is fairly safe.

Also, if you are wondering why dynamic_cast even exists in C++, it's because there are situations like this one that need it.

Since dynamic_cast was not covered in class, we will extend the deadline for Project 4 by 24 hours. The new due time is Friday April 22, 9:00pm.


The objectives of this project are 1) to practice working with class derivation in C++, and 2) to practice using polymorphism in C++.


Crazy Eights and Uno are both "shedding-type" card games where the objective of the game is to get rid of your cards. The first person to get rid of all his/her cards wins. There is a pile of cards that you draw new cards from called "the stock". There is another pile of cards called the "discard". When you play a card from your hand, it goes into the discard pile. There are many variations on how Crazy Eights and Uno can be played (people follow their own "house rules"). Generally, the card that you play must somehow match the top card of the discard pile (with some exception). If you do not have a card that is playable you have to draw from the stock. Some rules have you draw just one card, others have you draw until you can play. There are also variations on whether you have to play a card if you have a playable card. Some rules allow you to 'pass'. There are also variations on what happens when the stock runs out. Some rules end the game when the stock runs out. In other games, you shuffle the discard pile (except for the last card played) to replenish the stock. Finally, "action cards" are added to the mix. In Crazy Eights, which is played with standard playing cards, an 8 card can be played at any time. Furthermore, the person who plays an 8 can name the suit that next card must follow. The wild card in Uno does the same thing. In Uno, there are additional action cards that skips the next player, reverses the direction of play, and makes another player draw extra cards. In this project, you will see that variations on these card games can be implemented in C++ using class derivation and polymorphism without rewriting too much of the code.


For this project, you are given the source code for a program that plays a basic shedding-type card game. This game is played with the standard 52 playing cards for 2 to 10 players. If more than 5 players are in the game, two decks of cards are used. Here are the rules:

Here's the source code for the basic game.

These classes are designed so that much of the game playing details are handled by the Card class. The Player class handles game playing strategy (or lack thereof). The Game class handles who plays next, but it is blissfully unaware of the strategy used by each player or what each card actually does. This design allows you to add new types of cards to the game without having to rewrite too much of the game.

Here is the general design of the program. In each turn a Game object ask a particular Player to play a card by calling playOneCard(). If the function returns NULL, then cards are drawn, one at a time, from the stock and given to that player until playOneCard() returns a non-NULL value or the stock runs out. If a card is played, then this card is "shown" to all of the other players. This gives the other players information that might be needed for their strategizing. Each player is also "shown" to the card. This allows action cards to "interact" with the player (for example, to make them draw additional cards). Finally, the card is given an opportunity to interact with the Game object and the Player that played the card. This allows the player to do things like choose a new suit. In this setup, Card objects must be able to determine whether they are playable in the current setting. This allows a very simple strategy for a naive Player: try every card in his/her hand and play the first card that is playable.

Your assignment is to add new features to the basic game:

Let's call this new game "Cruno" for "Crazy Eights" + "Uno". (Sort of like "croissants" + "donuts" = "cronuts".) Your program should have at least 6 classes Cruno8, CrunoSkip, CrunoReverse, CrunoDraw2, CrunoPlayer and CrunoGame. If you implement more than one type of player, you will have a separate class for each type. You should write as little code as possible and take advantage of the base classes that have been given to you.

Implementation Issues:

  • The source files linked on this web page are also available in the following directory on GL. /afs/ This is a directory, not a command. Use the Unix cp command to copy files.

    Submitting your program

    You should submit these files to the proj4 subdirectory:

    • Cruno8.h, Cruno8.cpp

    • CrunoSkip.h, CrunoSkip.cpp

    • CrunoReverse.h, CrunoReverse.cpp

    • CrunoDraw2.h, CrunoDraw2.cpp

    • CrunoGame.h, CrunoGame.cpp

    • CrunoPlayer.h, CrunoPlayer.cpp

    • mytest.cpp
      This file should have a main() function that exercises the working parts of your submission. This is where you tell us what works.
    You can check that your program compiles and runs in the proj4 directory, but please clean up any .o and executable files. Again, do not develop your code in this directory and you should not have the only copy of your program here.

    If you are submitting late, you need to copy to proj4-late1, or proj2-late2 instead of proj4. Make sure you understand the course late submission policy.