Separate compilation, more poker

These are from 9 December. The calendar program that we split up into files to learn about separate compilation is available in calendar.zip. It contains:

  • Makefile
  • calendar.h
  • cal-main.cpp
  • cal-print.cpp
  • cal-ui.cpp
  • cal-calc.cpp

Then we also revamped part of our Card structure using a class, with a “member function” for print():

Another example of a struct that overloads the << operator is:

Finally, the solution to A11 and a start on the next-generation Poker game for A12 is below.

card-nextgen.cpp

// cards.cpp -- start of a Poker game!

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;

// A playing card is one of 4 suits
const int HEARTS = 0;
const int CLUBS = 1;
const int SPADES = 2;
const int DIAMONDS = 3;
const int NUM_SUITS = 4;
// and 13 ranks (A,2,3,4,5,6,7,8,9,10,J,Q,K)
const int ACE = 1;
const int JACK = 11;
const int QUEEN = 12;
const int KING = 13;
const int NUM_RANKS = 13;
const int NUM_CARDS = NUM_SUITS * NUM_RANKS; // 52

// Define my own type for a playing card
struct Card {
    int rank; // these are called FIELDS
    int suit;
}; // note semi-colon

// Function prototypes
void create_deck         (Card cards[NUM_CARDS]);
void shuffle_deck        (Card deck[NUM_CARDS]);
void print_card          (Card c);
void print_cards         (Card cards[], int start, int how_many);
void tally_ranks         (Card hand[], int count, int tally[NUM_RANKS+1]);
void tally_suits         (Card hand[], int count, int tally[NUM_SUITS]);
int  detect_N_of_a_kind  (Card hand[], int count, int n);
int  detect_pair         (Card hand[], int count);
int  detect_three_of_kind(Card hand[], int count);
int  detect_four_of_kind (Card hand[], int count);
int  detect_full_house   (Card hand[], int count);
int  detect_flush        (Card hand[], int count);
void detect_best_hand    (Card hand[], int count);
void test_detector       (string name, Card hand[], int count,
                          int expected, int actualy);
void test_everything     ();

/********************************************************
 *                     MAIN PROGRAM                     *
 ********************************************************/

int main()
{
    //test_everything();
    // Initialize the random numbers
    long seed = time(NULL);
    cout << "Seed is " << seed << "\n";
    srand(seed);
    // Declare my deck
    Card deck[NUM_CARDS];
    create_deck(deck);
    shuffle_deck(deck);
    int top = 48; // the current top of the deck
    Card hand[5];
    // Deal 5 cards into the player's hand
    for(int i = 0; i < 5; i++) {
        hand[i] = deck[top]; top = (top+1)%NUM_CARDS;
    }
    print_cards(hand, 0, 5);
    detect_best_hand(hand, 5);

    while(1) {
        // Ask for discards
        cout << "Discard? ";
        string discards;
        getline(cin, discards);
        if(discards == "") break; // game over
        for(int i = 0; i < discards.size(); i++) {
            if(discards[i] >= 'a' && discards[i] <= 'e') {
                int j = discards[i] - 'a';
                hand[j] = deck[top]; top = (top+1)%NUM_CARDS;
            } else {
                cout << "?error you typed '" << discards[i] << "'\n";
            }
        }

        // Show again
        print_cards(hand, 0, 5);
        detect_best_hand(hand, 5);
    }
    cout << "GAME OVER, thanks for playing.\n";
    return 0;
}

void detect_best_hand(Card hand[], int count)
{
    // This should detect in order from best to worst hands.
    int p = detect_pair(hand, count);
    if(p > 0) {
        cout << "You have a pair of " << p << "s\n"; 
        // Above should use "Ace", "Jack", etc. if appropriate
    }
    else {
        cout << "You've got a lot of nothing.\n";
    }
}

void test_everything()
{
    // Hard-code a particular hand for testing
    Card hand[5] = {{3, DIAMONDS},
                    {3, HEARTS},
                    {3, SPADES},
                    {3, CLUBS},
                    {ACE, SPADES}};
    test_detector("four of a kind", hand, 5, 3,
                  detect_four_of_kind(hand, 5));
    hand[2].rank = 2;
    test_detector("three of a kind", hand, 5, 3,
                  detect_three_of_kind(hand, 5));
    hand[2].rank = ACE;
    test_detector("full house", hand, 5, 3,
                  detect_full_house(hand, 5));

    test_detector("flush", hand, 5, -1,
                  detect_flush(hand, 5));
    hand[0].suit = DIAMONDS; hand[0].rank = 3;
    hand[1].suit = DIAMONDS; hand[1].rank = 4;
    hand[2].suit = DIAMONDS; hand[2].rank = 5;
    hand[3].suit = DIAMONDS; hand[3].rank = 6;
    hand[4].suit = DIAMONDS; hand[4].rank = 7;
    test_detector("flush", hand, 5, DIAMONDS,
                  detect_flush(hand, 5));
    hand[0].suit = DIAMONDS; hand[0].rank = 7;
    hand[1].suit = CLUBS;    hand[1].rank = 7;
    hand[2].suit = DIAMONDS; hand[2].rank = 5;
    hand[3].suit = SPADES;   hand[3].rank = 7;
    hand[4].suit = CLUBS;    hand[4].rank = 8;
    test_detector("full house", hand, 5, 0,
                  detect_full_house(hand, 5));
}

void print_card(Card c)
{
    switch(c.rank) {
    case ACE: cout << "Ace"; break;
    case JACK: cout << "Jack"; break;
    case QUEEN: cout << "Queen"; break;
    case KING: cout << "King"; break;
    default: cout << c.rank; break;
    }
    cout << " of ";
    switch(c.suit) {
    case HEARTS: cout << "hearts"; break;
    case CLUBS: cout << "clubs"; break;
    case SPADES: cout << "spades"; break;
    case DIAMONDS: cout << "diamonds"; break;
    }
}

void create_deck(Card cards[NUM_CARDS])
{
    int i = 0;
    for(int s = 0; s < NUM_SUITS; s++)
    {
        for(int r = 1; r <= NUM_RANKS; r++, i++)
        {
            cards[i].rank = r;
            cards[i].suit = s;
        }
    }
}

void print_cards(Card deck[NUM_CARDS], 
                 int start, int how_many)
{
    for(int i = 0; i < how_many; i++)
    {
        char c = 'a' + i;
        cout << c << ") ";
        print_card(deck[start+i]);
        cout << "\n";
    }
}

void shuffle_deck(Card deck[NUM_CARDS])
{
    int i;
    for(i = 0; i < 1000; i++) { // # of iterations
        // Pick two cards in the deck
        int j = rand() % NUM_CARDS;
        int k = rand() % NUM_CARDS;
        // Swap them
        Card c = deck[j];
        deck[j] = deck[k];
        deck[k] = c;
    }
}

void tally_ranks(Card hand[], int count,
                 int tally[NUM_RANKS+1])
{
    for(int i = 0; i < count; i++)
    {
        tally[hand[i].rank]++;
    }
}

void tally_suits(Card hand[], int count,
                 int tally[NUM_SUITS])
{
    for(int i = 0; i < count; i++)
    {
        tally[hand[i].suit]++;
    }
}

int detect_N_of_a_kind(Card hand[], int count, int n)
{
    int tally[NUM_RANKS+1] = { 0 };
    tally_ranks(hand, count, tally);
    // Look through tally for N of a kind
    for(int r = 1; r <= NUM_RANKS; r++)
    {
        if(tally[r] == n) return r; // found N at rank r
    }
    return 0; // no pair
}

/* Returns 0 if no pair found, otherwise
   returns rank of pair. */
int detect_pair(Card hand[], int count)
{
    return detect_N_of_a_kind(hand, count, 2);
}

int detect_four_of_kind(Card hand[], int count)
{
    return detect_N_of_a_kind(hand, count, 4);
}

int detect_three_of_kind(Card hand[], int count)
{
    return detect_N_of_a_kind(hand, count, 3);
}

int detect_full_house(Card hand[], int count)
{
    int p = detect_pair(hand, count);
    int q = detect_three_of_kind(hand, count);
    if(p > 0 && q > 0) {
        return q;
    }
    else {
        return 0;
    }
}

int detect_flush(Card hand[], int count)
{
    int tally[NUM_SUITS] = { 0 };
    tally_suits(hand, count, tally);
    for(int s = 0; s < NUM_SUITS; s++)
    {
        if(tally[s] == 5) return s; // found a flush in suit s
    }
    return -1; // no flush
}

void test_detector(string name, Card hand[], int count,
                   int expected, int actual)
{
    cout << "TEST " << name << ":\n";
    print_cards(hand, 0, count);
    if(expected == actual) {
        cout << " -> SUCCESS: " << actual << "\n";
    } else {
        cout << " -> FAIL: expected " << expected << ", got "
             << actual << "\n";
    }
}