CSE 428S Lab 0: Cards and Decks

Due by Monday September 11, 2023, at 11:59 pm
Final grade percentage: 5 percent

Objective:

This lab is intended to give you hands-on practice with procedural, functional, object-oriented, and generic programming styles, and to refresh your familiarity with programming concepts in C++, including:

To do this, you will implement basic abstractions in C++ for decks and cards in two games that have different rules for the ranks, multiplicity, and ordering relations of the cards that constitute a standard deck used in each game. Specifically, you will use procedural, functional, object-oriented, and generic programming paradigms in the C++ language and libraries (as of the C++17 standard) to provide common abstractions and game-specific ones, for both (Partnership Auction) Pinochle and (Texas hold 'em) Poker.

You will then write a simple C++ program that constructs decks of cards for each of those games, and prints out the contents of the deck for each game so that you can can check easily that everything worked correctly.

In subsequent lab assignments you will use these abstractions to implement game-specific features like generating sets of cards, examining different combinations of the cards in a set, and ordering cards and different combinations of cards, according to various scoring functions. As you develop your code, please use good modularity: for example, if a function's logic has several separable parts (or is long enough to fill the editor's window) you may want to break it up into other smaller functions.

Note: even at this early stage some details of the assignment are intentionally slightly under-specified, leaving you some room to choose what you think is the best way to implement them, as long as what you do is reasonable and you explain your design decisions in comments in the code and in your readme file.

Assignment:

    Part I - Readings:

  1. The following readings in the required text book may be useful as reference material while working on this lab assignment, and though we'll touch on some of the issues in the next few studio sessions, selectively looking up topics and/or reading ahead (on demand as you encounter issues while working on the lab) in the text book (or looking up topics in the on-line C++ Reference) to refresh your familiarity with C++ may be necessary.

  2. Please review the CSE 428 Programming Guidelines, and follow them as you develop your solution to each lab assignment this semester.

    Part II - Program Design and Implementation:

  3. Log into one of the Linux Lab machines via qlogin, and (as you did in Studio 0) confirm that the correct version (8.3.0) of the g++ compiler is installed in your environment there.

  4. If you have not done so already (e.g., when you completed Studio 0), please create a directory for this course this semester. In that directory, please create another directory where you will keep your files for your solution to this lab assignment, and cd into it.
  5. In that directory create a readme file (e.g., named readme.txt), in which you will record your observations, design decisions, and other information pertaining to the lab assignment as you develop your solution.
  6. Also copy the provided Makefile into that directory - you will use it to build your code into an executable program. Note that the provided Makefile assumes specific names for all the files that you will develop as part of your solution, so you may want to change those names, and (in the interest of working incrementally) you also may want to comment out some of the details in it initally, and then add them back in as you go.
  7. Add a new C++ header (.h) file and a new C++ source (.cpp) file to your lab directory. These will hold declarations and definitions (respectively) related to (1) the suits that cards may have in Pinochle or in Texas hold 'em poker, and to (2) operators involving those suits.

    In the header file, declare an enumerated type for the different suits a playing card may have: clubs, diamonds, hearts, and spades. Note that using an enum class style declaration and giving the enumerated type a distinct name (like Suit for example) can be helpful for readability because scoping is explicit.

    The values of the enumeration labels should be monotonically increasing (e.g., clubs should have the lowest value, diamonds should have a value that is greater, and so forth) which is what you get if you simply declare the enumaration labels in that order. Also add a highest-valued label to the enumeration, for an undefined suit, which will be useful for the purpose of iteration over those values (and also presumably could be useful for additional games in which some cards, like jokers, may not have a suit).

    In that same header and source file, declare and define (respectively) a "shift" operator (operator<<) that takes a reference to an ostream and a const reference to a variable of that enumerated type, and returns a reference to an ostream. That shift operator should insert a string corresponding to the suit ("C" for clubs, "D" for diamonds, "H" for hearts, "S" spades, or "?" for undefined) into the ostream and then return a reference to that same ostream.

    In that same header and source file, also declare and define (respectively) a prefix increment operator (operator++) that takes a reference to a variable of that same enumerated type, increments the variable's value to the next one (unless its value is already undefined in which case it should remain the same) and then returns a reference to that variable.

  8. Add a new C++ header (.h) file and a new C++ source (.cpp) file to your lab directory and in them declare and define (respectively) a struct template to represent a "playing card" type (e.g., named Card) that is parameterized by two other types (e.g., rank and suit enumerations) and has member variables of those types (for the card's rank and suit).

    Please make sure that (1) at the bottom of the header (.h) file for the Card struct template (within an inclusion guard that begins with #ifdef TEMPLATE_HEADERS_INCLUDE_SOURCE and ends with #endif) it includes the source (.cpp) file for the Card struct template, (2) that your Makefile provides the -DTEMPLATE_HEADERS_INCLUDE_SOURCE flag to g++

    The Card struct template should have a public constructor that takes arguments of the parameterized types for the rank and suit, and (in the type parameterized template definition of that constructor) uses them to initialize the member variables.

    In those same header and source files, but outside of the template declarations and definitions for the Card struct itself, declare and define (respectively) a template for a "shift" operator (operator<<) that (like the struct template) is parameterized with types for a card's rank and suit. That operator should take a reference to an ostream and a const reference to a variable of the card type (parameterized with the rank and suit types), and return a reference to an ostream. That shift operator should simply insert the card's rank and suit member variables into the ostream, and then return a reference to that same ostream.

    Note that each template declaration or template definition in these files will begin with a line that looks like

    template <typename R, typename S>

    which is then followed immediately by the rest of the template declaration or template definition. For more information about the syntax for C++ class and function templates, please see Chapter 16.1 of the [LLM] course text book.

  9. Add a new C++ header (.h) file to your lab directory and in it declare an abstract base class for a deck of cards (e.g., named Deck) that has a single public pure virtual print method that takes a reference to an ostream and has a void return type. Note that in this lab we will use a mainly object-oriented approach for the classes derived from this abstract base class, at a cost of some code duplication. In future labs we will combine this approach with generic programmign techniques to refactor and combine common code that differs mainly in its types, into parameterized templates.
  10. Add a new C++ header (.h) file and a new C++ source (.cpp) file to your lab directory and in them declare and define (respectively) (1) an enumerated type for the card ranks used in Pinochle, and operators involving that enumerated type; and (2) a class derived (via public inheritance) from the abstract base class described above, which implements a Pinochle deck.

    Like the enumerated type for cards' suits, the enumerated type for the different ranks a Pinochle card may have should again have monotonically increasing values in the following order: nine, jack, queen, king, ten, and ace. Again also add a highest-valued label to the enumeration, for an undefined rank, for the purpose of iteration over those values. Note that using an enum class style declaration and giving the enumerated type a distinct name (like PinochleRank for example) can be helpful for both (1) readability and (2) disambiguating labels for ranks that appear in both Pinochle and Texas hold 'em poker, because scoping is explicit.

    In that same header and source file, declare and define (respectively) a "shift" operator (operator<<) that takes a reference to an ostream and a const reference to a variable of that enumerated type, and returns a reference to an ostream. That shift operator should insert a string corresponding to the rank ("9" for nine, "J" for jack, "Q" for queen, "K" king, "10" for ten, "A" for ace, or "?" for undefined) into the ostream and then return a reference to that same ostream.

    In that same header and source file, also declare and define (respectively) a prefix increment operator (operator++) that takes a reference to a variable of that same enumerated type, increments the variable's value to the next one (unless its value is already undefined in which case it should remain the same) and then returns a reference to that variable.

    The derived class for the Pinochle deck should have (1) a private member variable that is a vector sequence container for cards parameterized with the rank enumeration for Pinochle and the suit enumeration; (2) a public default constructor that twice pushes back a card of each valid suit of each valid rank (using the appropriate prefix increment operator to traverse the values of the enumeration types but not inserting cards with undefined rank or suit); and (3) a public print method that overloads the inherited pure virtual method, taking a reference to an ostream and inserting each card in its vector into the ostream, with spaces between the cards and line breaks at appropriate intervals (how you design that is up to you, based largely on how you nest the iterations - please document your design decisions for those in your readme file).

    Note that when working with templates, using enough whitespace (and especially putting whitespace between distinct types and the symbols that surround them where necessary) is often essential in order for your code to compile. For example, vector< Foo<T> >::iterator may compile just fine as long as the appropriate header files are included, while vector<Foo<T>>::iterator may not compile because the compiler sees >> as a shift operator.

  11. Add a new C++ header (.h) file and a new C++ source (.cpp) file to your lab directory and in them declare and define (respectively) (1) an enumerated type for the card ranks used in Texas hold 'em poker, and operators involving that enumerated type; and (2) a class derived (via public inheritance) from the abstract base class described above, which implements a standard Texas hold 'em poker deck.

    The enumerated type for the different ranks of Texas hold 'em poker cards should have monotonically increasing values in the following order: two, three, four, five, six, seven, eight, nine, ten, jack, queen, king, and ace. Again also add a highest-valued label to the enumeration, for an undefined rank, for the purpose of iteration over those values. Note that using an enum class style declaration and giving the enumerated type a distinct name (like HoldEmRank for example) can be helpful for both (1) readability and (2) disambiguating labels for ranks that appear in both Pinochle and Texas hold 'em poker, because scoping is explicit.

    In that same header and source file, declare and define (respectively) a "shift" operator (operator<<) that takes a reference to an ostream and a const reference to a variable of that enumerated type, and returns a reference to an ostream. That shift operator should insert a string corresponding to the rank ("2" for two, "3" for three,"4" for four, "5" for five, "6" for six, "7" for seven, "8" for eight, "9" for nine, "10" for ten,"J" for jack, "Q" for queen, "K" king, "A" for ace, or "?" for undefined) into the ostream and then return a reference to that same ostream.

    In that same header and source file, also declare and define (respectively) a prefix increment operator (operator++) that takes a reference to a variable of that same enumerated type, increments the variable's value to the next one (unless its value is already undefined in which case it should remain the same) and then returns a reference to that variable.

    The derived class for the Texas hold 'em poker deck should have (1) a private member variable that is a vector sequence container for cards parameterized with the rank enumeration for Texas hold 'em poker and the suit enumeration; (2) a public default constructor that (only once) pushes back a card of each valid suit of each valid rank (using the appropriate prefix increment operator to traverse the values of the enumeration types but not inserting cards with undefined rank or suit); and (3) a public print method that overloads the inherited pure virtual method, taking a reference to an ostream and inserting each card in its vector into the ostream, with spaces between the cards and line breaks at appropriate intervals (how you design that is up to you, based largely on how you nest the iterations - please document your design decisions for those in your readme file).

  12. Add a new C++ source (.cpp) file to your lab directory and in it define the progam's main function. That function should simply declare stack variables of the Pinochle and Texas hold 'em deck types, and pass the standard output stream (cout) into a call to each one's print method (to show the cards that were put into them by their default constructors), and then return a zero value to indicate successful completion.
  13. In your lab directory, run the make command to compile your code using the Makefile, and fix any errors or warnings that occur. Please be sure to note the different kinds of errors or warnings you ran into (though you don't have to list every instance of each kind) in your readme file. If you were fortunate enough not to run into any errors or warnings, please note that instead, where you would have listed errors or warnings in your readme file.

  14. Run the executable program to confirm that the right number of cards of each suit and rank (two each for Pinochle, one each for Texas hold 'em poker) were printed out for each deck. In your readme file please document any runs in which you saw incorrect output and if so what caused it and how you fixed it, as well as the output you saw that indicates correct behavior of the program.

  15. In your readme file, make sure that your names, e-mail addresses, and the lab number (lab 0) are at the top of the file, and that you've documented whether you ran into any errors or warnings while developing your solution (and if so what they were) and what the executable program did for each of the trials you ran. Be sure to save your changes to your readme file and your Makefile, as well as those to your source and header files, before uploading your solution.
  16. Upload to the lab 0 assignment page for CSE 428 in Canvas: (1) your readme file (2) the Makefile you used to compile your solution; and (3) all your header and source files for your solution.

Posted Tuesday August 22, 2022, and updated Thursday August 24, 2023, by
Chris Gill