// BLACKJACK: Author: David Henke
// converted to C++

/* Features to add:
 *      a. neater display formats
 *      b. graphics (color for card images)
 *      c. emulate players mode to play n games with different betting
 *         and playing styles (simulation)
 *      d. handle n decks, inform player of reshuffle
 *      e. handle: No more cards in deck
 */

#include 
#include 
#include 
#include 
#include 
#include 

typedef char BOOLEAN;
const TRUE  =   1;
const FALSE =   0;
const ERROR = -1;
const NORMAL = 0;

const MAX_HANDS_SIM = 1000000000;

const WORDSIZE =  32;
const MAX_STRING = 256;

const NUM_COUNTS        = 22;
const NUM_CARDS         = 52;
const NUM_SUITS         =  4;
const NUM_DENOMINATIONS = 13;
const CLUB              =  0;
const DIAMOND           =  1;
const HEART             =  2;
const SPADE             =  3;
const MAX_CLUB          = 12;
const MAX_DIAMOND       = 25;
const MAX_HEART         = 38;
const MAX_SPADE         = 51;
const DISPLAY_DOWN      =  0;
const DISPLAY_UP        =  1;
const MAX_CARDS_IN_HAND = 12;
const MIN_TEN_COUNT     = 10;
const MAX_CARDS_PLAYED  = 44;
const MAX_HANDS         =  4; 
const TWENTY_ONE        = 21;
const SEVENTEEN         = 17;
const ACES_TEN          = 10;
const SHOW_DOWN_INDEX   = 13;
const FIRST             =  0;
const SECOND            =  1;

const ACE     = 0;
const TWO     = 1;
const THREE   = 2;
const FOUR    = 3;
const FIVE    = 4;
const SIX     = 5;
const SEVEN   = 6;
const EIGHT   = 7;
const NINE    = 8;
const TEN     = 9;
const JACK    = 10;
const QUEEN   = 11;
const KING    = 12;

// globals for play control
BOOLEAN debug = FALSE;
BOOLEAN check_rules;
BOOLEAN interactive;
BOOLEAN double_all;
int num_hands;

// tables for rules to play by 
BOOLEAN split_table[NUM_DENOMINATIONS][NUM_DENOMINATIONS] = 
   {
//   A   2   3   4   5   6   7   8   9  10   J   Q   K        Dealer hole card

    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // A always split
    {0,  0,  0,  1,  1,  1,  1,  0,  0,  0,  0,  0,  0},   // 2 split 4-7
    {0,  0,  0,  1,  1,  1,  1,  0,  0,  0,  0,  0,  0},   // 3 split 4-7
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 4 never split
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 5 never split
    {0,  0,  1,  1,  1,  1,  0,  0,  0,  0,  0,  0,  0},   // 6 split 3-6
    {0,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,  0},   // 7 split 2-7
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 8 always split
    {0,  1,  1,  1,  1,  1,  0,  1,  1,  0,  0,  0,  0},   // 9 split 2-6, 8-9
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 10 never split
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // J never split
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // Q never split
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // K never split
   };

BOOLEAN hard_double_table[NUM_COUNTS][NUM_DENOMINATIONS] = 
   {
//   A   2   3   4   5   6   7   8   9  10   J   Q   K        Dealer hole card

                                                           // two card count, no ace
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 0 never 0
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 1 never 1
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 2 never double down
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 3 never double down
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 4 never double down
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 5 never double down
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 6 never double down
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 7 never double down
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 8 never double down
    {0,  0,  1,  1,  1,  1,  0,  0,  0,  0,  0,  0,  0},   // 9 double down 3-6
    {0,  1,  1,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0},   // 10 double down 2-9
    {0,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 11 double down 2-10
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 12 never double down
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 13 never double down
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 14 never double down
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 15 never double down
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 16 never double down
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 17 never double down
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 18 never double down
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 19 never double down
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 20 never double down
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 21 never double down
   };

BOOLEAN soft_double_table[NUM_COUNTS][NUM_DENOMINATIONS] = 
   {
//   A   2   3   4   5   6   7   8   9  10   J   Q   K        Dealer hole card

                                                           // two card count, one is ace
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 0 never 
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 1 never 
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 2 never
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 3 never
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 4 never
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 5 never
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 6 never
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 7 never
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 8 never
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 9 never
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 10 never
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 11 never
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 12 never
    {0,  0,  0,  0,  1,  1,  0,  0,  0,  0,  0,  0,  0},   // 13 double down 5-6
    {0,  0,  0,  0,  1,  1,  0,  0,  0,  0,  0,  0,  0},   // 14 double down 5-6
    {0,  0,  0,  1,  1,  1,  0,  0,  0,  0,  0,  0,  0},   // 15 double down 4-6
    {0,  0,  0,  1,  1,  1,  0,  0,  0,  0,  0,  0,  0},   // 16 double down 4-6
    {0,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,  0,  0},   // 17 double down 2-6
    {0,  0,  0,  1,  1,  1,  0,  0,  0,  0,  0,  0,  0},   // 18 double down 4-6
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 19 never
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 20 never
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 21 never
   };

BOOLEAN hard_hit_table[NUM_COUNTS][NUM_DENOMINATIONS] = 
   {
//   A   2   3   4   5   6   7   8   9  10   J   Q   K        Dealer hole card

                                                           // card count, no ace
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 0 never happens
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 1 never happens
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 2 always hit
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 3 always hit
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 4 always hit
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 5 always hit
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 6 always hit
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 7 always hit
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 8 always hit
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 9 always hit
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 10 always hit
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 11 always hit
    {1,  1,  1,  0,  0,  0,  1,  1,  1,  1,  1,  1,  1},   // 12 hit all but 4-6
    {1,  0,  0,  0,  0,  0,  1,  1,  1,  1,  1,  1,  1},   // 13 hit all but 2-6
    {1,  0,  0,  0,  0,  0,  1,  1,  1,  1,  1,  1,  1},   // 14 hit all but 2-6
    {1,  0,  0,  0,  0,  0,  1,  1,  1,  1,  1,  1,  1},   // 15 hit all but 2-6
    {1,  0,  0,  0,  0,  0,  1,  1,  1,  1,  1,  1,  1},   // 16 hit all but 2-6
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 17 never hit
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 18 never hit
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 19 never hit
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 20 never hit
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 21 never hit
   };

BOOLEAN soft_hit_table[NUM_COUNTS][NUM_DENOMINATIONS] =
   {
//   A   2   3   4   5   6   7   8   9  10   J   Q   K        Dealer hole card

                                                           // two card count, soft ace
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 0 nop
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 1 nop
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 2 nop
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 3 nop
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 4 nop
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 5 nop
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 6 nop
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 7 nop
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 8 nop
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 9 nop
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 10 nop
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 11 nop
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 12 nop
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 13 always hit
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 14 always hit
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 15 always hit
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 16 always hit
    {1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1},   // 17 always hit
    {1,  0,  0,  0,  0,  0,  0,  0,  1,  1,  1,  1,  1},   // 18 hit all but 2-8
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 19 never hit
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 20 never hit
    {0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0},   // 21 never hit
   };
    
    
class card
   {
   public:
    int denomination;
    int suit;
    int count;
    int display();
   };

class default_deck
   {
   public:
    static card ddeck[NUM_CARDS];
   };

card default_deck::ddeck[] = 
   {
    {ACE,   CLUB,    1},
    {TWO,   CLUB,    2},
    {THREE, CLUB,    3},
    {FOUR,  CLUB,    4},
    {FIVE,  CLUB,    5},
    {SIX,   CLUB,    6},
    {SEVEN, CLUB,    7},
    {EIGHT, CLUB,    8},
    {NINE,  CLUB,    9},
    {TEN,   CLUB,   10},
    {JACK,  CLUB,   10},
    {QUEEN, CLUB,   10},
    {KING,  CLUB,   10},


    {ACE,   DIAMOND,    1},
    {TWO,   DIAMOND,    2},
    {THREE, DIAMOND,    3},
    {FOUR,  DIAMOND,    4},
    {FIVE,  DIAMOND,    5},
    {SIX,   DIAMOND,    6},
    {SEVEN, DIAMOND,    7},
    {EIGHT, DIAMOND,    8},
    {NINE,  DIAMOND,    9},
    {TEN,   DIAMOND,   10},
    {JACK,  DIAMOND,   10},
    {QUEEN, DIAMOND,   10},
    {KING,  DIAMOND,   10},

    {ACE,   HEART,    1},
    {TWO,   HEART,    2},
    {THREE, HEART,    3},
    {FOUR,  HEART,    4},
    {FIVE,  HEART,    5},
    {SIX,   HEART,    6},
    {SEVEN, HEART,    7},
    {EIGHT, HEART,    8},
    {NINE,  HEART,    9},
    {TEN,   HEART,   10},
    {JACK,  HEART,   10},
    {QUEEN, HEART,   10},
    {KING,  HEART,   10},

    {ACE,   SPADE,    1},
    {TWO,   SPADE,    2},
    {THREE, SPADE,    3},
    {FOUR,  SPADE,    4},
    {FIVE,  SPADE,    5},
    {SIX,   SPADE,    6},
    {SEVEN, SPADE,    7},
    {EIGHT, SPADE,    8},
    {NINE,  SPADE,    9},
    {TEN,   SPADE,   10},
    {JACK,  SPADE,   10},
    {QUEEN, SPADE,   10},
    {KING,  SPADE,   10}
   };

class deck
   {
   private:
    int card_indices[NUM_CARDS];

   public:
    int deck_count;
    deck();
    void dump();
    void shuffle(unsigned long int modulus,
                 long int &random_number,
                 double &uniform_random_number);
    int deal_card();
   };

class hand
   {
   public:

    BOOLEAN show_total;
    BOOLEAN aces;
    BOOLEAN hard_ace;
    BOOLEAN over;
    BOOLEAN play;
    int hand_count;
    int card_indices[MAX_CARDS_IN_HAND];
    int card_count;

    void initialize();
    BOOLEAN blackjack();
    int count();
    void deal_card(int);
    virtual void display() = 0;
   };

class dealer_hand : public hand
   {
   public:

    BOOLEAN first_card_down;
    BOOLEAN insurance();
    void initialize();
    void display();
   };

class player_hand : public hand
   {
   public:

    BOOLEAN got_insurance;
    BOOLEAN split_aces;
    float bet;
    float insure;
    BOOLEAN split(int);
    BOOLEAN double_down(int);
    void initialize();
    void display();
   };

char *denom_array[] =
   { 
    " A", 
    " 2", " 3", " 4", " 5",
    " 6", " 7", " 8", " 9",
    "10", " J", " Q", " K",
    "XX"
   };


// Random number generating routines....

int clock_random()  // start random number generation based on time
   {
    extern my_clock();

    static int c = 0;

    if (c == 0) c = my_clock();

    srand(c);

    return(c = rand());
   } 

int my_clock()
   {
    long int c;
    struct tm *t;

    c = time(0);
    t = localtime(&c);

    return(t->tm_sec + (100 * (t->tm_min + (100 * (t->tm_hour
                  + (100 * (t->tm_mday + (100 * (t->tm_mon +
1)))))))));

   }

void random(unsigned long int modulus,
            long int &random_number,
            double &uniform_random_number)
   {
    // Xn+1 = (aXn + c) mod m
    random_number = ((524288 + 3) * (random_number)) % modulus;
 
    // Un = Xn/m
    uniform_random_number = (double) random_number / (double) modulus;
   }

void get_modulus(unsigned long int &modulus)
   {
    modulus = (unsigned long int) pow(2.0, (double) (WORDSIZE - 2));
   }

// Interactive routines .......

BOOLEAN get_answer()
   {
    char your_answer[MAX_STRING];

    BOOLEAN answer;
    BOOLEAN ok_answer;

    ok_answer = FALSE;

    while (ok_answer == FALSE)
       {
        cin >> your_answer;

        if ((your_answer[0] == 'y') || (your_answer[0] == 'Y'))
           {
            answer = TRUE;
            ok_answer = TRUE;
           }
        else if ((your_answer[0] == 'n') || (your_answer[0] == 'N'))
           {
            answer = FALSE;
            ok_answer = TRUE;
           }
        else
           {
            cout << "\n" << "Make up your mind!(Y/N)" << flush;
           }
       }
    return(answer);
   }

void print_rule_violated()
   {
    cout << "\nCHECK: MIKEY SAYS YOU BROKE THE RULE." << endl;
   }

// class deck methods....


deck::deck()
   {
    for (int i = 0; i < NUM_CARDS; i++)
       {
        card_indices[i] = i;
       }
   }

void deck::dump()
   {
    for (int i = 0; i < NUM_CARDS; i++)
       {
        cout << "deck[" << i << "]:" <<  card_indices[i] << endl;
       }
   } 

void deck::shuffle(unsigned long int modulus,
              long int &random_number,
              double &uniform_random_number)
   {
    int card_count;
    int card_num;
    int temp;
    extern void random(unsigned long int,
                       long int &,
                       double &);
    
    for (card_count = NUM_CARDS - 1; card_count > 0; 
         card_count--)
       {
        /* generate a random number U, uniformly distributed between 0
and 1 */ 

        random(modulus, random_number, uniform_random_number);

        /* get card_num to lie between 0 and 51 */

        card_num = (int) (card_count * uniform_random_number);
      
        /* exchange card_indices[card_num] with card_indices[card_count]
*/

        temp = card_indices[card_num];
        card_indices[card_num] = card_indices[card_count];
        card_indices[card_count] = temp;
       }

    deck_count = -1;
   }

int deck::deal_card()
   {
    deck_count++;

    if (deck_count >= NUM_CARDS)
       {
        // shuffle dead cards and finish this game
        // for now, recycle cards at beginning of deck to finish this hand
        if (debug)
           {
            cout << "No more cards in this deck, recycling." << endl;
           }
        return(card_indices[deck_count % NUM_CARDS]);
       }
    return(card_indices[deck_count]);
   }


// class card methods()
int card::display()
{
 return(NORMAL);
}
// class hand methods 
void hand::initialize()
   {
    show_total = FALSE;
    card_count = 0;
    play = TRUE;
    over = FALSE;
   }

int hand::count()
   {
    BOOLEAN hard_ace_ok = TRUE;
    aces = FALSE;
    hard_ace = FALSE;
    hand_count = 0;

    for (int i = 0; i < card_count; i++)
       {
        hand_count += default_deck::ddeck[card_indices[i]].count;

        if (default_deck::ddeck[card_indices[i]].denomination == ACE)
           { 
            aces = TRUE;
           }

        if (aces && !hard_ace && hard_ace_ok)
           {
            if ((hand_count +  ACES_TEN) <= TWENTY_ONE)
               {
                hand_count += ACES_TEN;
                hard_ace = TRUE;
               }
           }
                  
        if ((hand_count > TWENTY_ONE) && hard_ace)
           {
            hand_count -= ACES_TEN;
            hard_ace = FALSE;    // no more hard aces
            hard_ace_ok = FALSE;
           }
       } 

    // test for over and return the count
    if (hand_count > TWENTY_ONE)
       {
        over = TRUE;
       }
    return(hand_count);
   }

void hand::deal_card(int new_card)
   {
    card_indices[card_count] = new_card;
    card_count++;
   }

BOOLEAN hand::blackjack()
   {
    if (default_deck::ddeck[card_indices[FIRST]].denomination == ACE)
       {
        if (default_deck::ddeck[card_indices[SECOND]].count == 10)
           {
            return(TRUE);
           }
       }
    else if (default_deck::ddeck[card_indices[SECOND]].denomination ==
ACE)
       {
        if (default_deck::ddeck[card_indices[FIRST]].count == 10)
           {
            return(TRUE);
           }
       }
            
    return(FALSE);
   }

// sub class dealer_hand methods

void dealer_hand::initialize()
   {
    hand::initialize();
    first_card_down = TRUE;
   }

BOOLEAN dealer_hand::insurance()
   {
    if (default_deck::ddeck[card_indices[SECOND]].denomination == ACE)
       {
        cout << "\n" << "Insurance?(Y/N)" << flush;
        return(get_answer());
       }
    else
       {
        return(FALSE);
       }
   }

void dealer_hand::display()
   {
    int index;


    cout << "\nDealers hand: " << flush;

    for (int i = 0; i < card_count; i++)
       {
        if ((first_card_down == TRUE) && (i == 0))
           {
            index = SHOW_DOWN_INDEX;
           }
        else
           {
            index = default_deck::ddeck[card_indices[i]].denomination;
           }
        
        cout << " " << denom_array[index] << flush;
       } 

    if (show_total == TRUE)
       {
        cout << "  total: " << count() << flush;
       }
   }

// sub class player_hand methods

void player_hand::initialize()
   {
    hand::initialize();
    got_insurance = FALSE;
    split_aces = FALSE;
   }

BOOLEAN player_hand::split(int dealer_up_card)
   {
    if (default_deck::ddeck[card_indices[FIRST]].denomination == 
        default_deck::ddeck[card_indices[SECOND]].denomination)
       {
        if (interactive)
           {
            cout << "\n" << "Split?(Y/N)" << flush;
            BOOLEAN answer = get_answer();
            if (check_rules)
               {
                BOOLEAN rule_answer = (BOOLEAN) split_table
                        
[default_deck::ddeck[card_indices[FIRST]].denomination]
                         [dealer_up_card];
                if (answer != rule_answer)
                   {
                    print_rule_violated();
                   }
               }
            return(answer);
           }
        else
           {
            return((BOOLEAN) split_table
             [default_deck::ddeck[card_indices[FIRST]].denomination]
             [dealer_up_card]);
           }
       }
    else
       {
        return(FALSE);
       }
   } 

BOOLEAN player_hand::double_down(int dealer_up_card)
   {
    int card1_plus_card2;
    BOOLEAN can_ask = FALSE;

    if (double_all) 
       {
        can_ask = TRUE;
       }
    else
       {
        card1_plus_card2 =
default_deck::ddeck[card_indices[FIRST]].count + 
                          
default_deck::ddeck[card_indices[SECOND]].count;
        if ((card1_plus_card2 == 10) || (card1_plus_card2 == 11))
           {
            can_ask = TRUE;
           }
       }

    if (can_ask)
       {
        if (interactive)
           {
            cout << "\n" << "Double Down?(Y/N)" << flush;
            BOOLEAN answer = get_answer();
            if (check_rules)
               {
                BOOLEAN rule_answer;
                if (aces && hard_ace)
                   {
                    // means hand has an ace which could be softened
                    rule_answer = (BOOLEAN)
soft_double_table[hand_count]
                                                            
[dealer_up_card];
                   }
                else
                   {
                    rule_answer = (BOOLEAN)
hard_double_table[hand_count]
                                                            
[dealer_up_card];
                   }
                if (answer != rule_answer)
                   {
                    print_rule_violated();
                   }
               }
            return(answer);
           }
        else
           {
            if (aces && hard_ace)
               {
                // means hand has an ace which could be softened
                return((BOOLEAN)
soft_double_table[hand_count][dealer_up_card]);
               }
            else
               {
                return((BOOLEAN)
hard_double_table[hand_count][dealer_up_card]);
               }
           }
       }
    else
       {
        return(FALSE);
       }
   }


void player_hand::display()
   {
    int index;

    cout << "\n   Your hand: " << flush;

    for (int i = 0; i < card_count; i++)
       {
        index = default_deck::ddeck[card_indices[i]].denomination;
        
        cout << " " << denom_array[index] << flush;
       } 

    if (show_total == TRUE)
       {
        cout << "  total: " << count() << flush;
       }
   }

// Playing routines ....

int deal(deck &card_deck, hand &this_hand)
   {
    int new_card;

    if ((new_card = card_deck.deal_card()) == ERROR)
       {
        return(ERROR);
       }
    this_hand.deal_card(new_card);
    return(NORMAL);
   }


void do_split(deck &card_deck, player_hand &hand1, player_hand &hand2,
int &num_user_hands)
   {
    // reset the number of cards in split hand to 1
    hand1.card_count = 1;
    hand1.count();

    // initialize the new hand and take card from card 2 of split hand
    hand2.initialize();
    hand2.card_indices[FIRST] = hand1.card_indices[SECOND];
    hand2.card_count = 1;
    hand2.bet = hand1.bet;
    hand2.count();

    // check for split of aces
    if (default_deck::ddeck[hand1.card_indices[FIRST]].denomination ==
ACE)
       {
        hand1.split_aces = TRUE;
        hand2.split_aces = TRUE;
       }

    num_user_hands++;

    // deal another card to each hand 
    deal(card_deck, hand1);
    deal(card_deck, hand2);
   }

// Tallying routines ....

void settle_score(float &your_totals, 
                  player_hand &your_hand, dealer_hand &dealers_hand)
   {
    if (your_hand.over)
       {
        if (interactive)
           {
            cout << "\nYou busted!" << endl;
           }
        your_totals -= your_hand.bet;
       }
    else if (dealers_hand.over)
       {
        if (interactive)
           {
            cout << "\nDealer busted!" << endl;
           }
        your_totals += your_hand.bet;
       }
    else
       {
        if (your_hand.hand_count > dealers_hand.hand_count)
           {
            if (interactive)
               {
                cout << "\nYou win." << endl;
               }
            your_totals += your_hand.bet;
           }
        else if (your_hand.hand_count < dealers_hand.hand_count)
           {
            if (interactive)
               {
                cout << "\nYou lose." << endl;
               }
            your_totals -= your_hand.bet;
           }
        else
           {
            if (your_hand.hand_count == TWENTY_ONE)
               {
                if (your_hand.blackjack())
                   {
                    if (dealers_hand.blackjack())
                       {
                        if (interactive)
                           {
                            cout << "\nPush." << endl;
                           }
                       }
                    else
                       {
                        if (interactive)
                           {
                            cout << "\nYou win." << endl;
                           }
                        your_totals += your_hand.bet;
                       }
                   }
               }
            else
               {
                if (interactive)
                   {
                    cout << "\nPush." << endl;
                   }
               }
           }
       }
   }

void print_totals(float your_totals)
   {
    if (your_totals < 0.0)
       {
        printf("\nYou owe me $%9.2f\n", fabs(your_totals)); 
       }
    else if (your_totals == 0.0)
       {
        printf ("\nWe are dead even.\n");
       }
    else
       {
        printf("\nI owe you $%9.2f\n", your_totals); 
       }
   }

void print_usage()
   {
    cout << "usage: bj -i|-b numhands  [-c] [-d]" << endl;
    cout << "usage:    -i ==> interactive" << endl;
    cout << "usage:    -b ==> batch; numhands ==> number of hands to
play" << endl;
    cout << "usage:    -c ==> optional check against playing rules" <<
endl;
    cout << "usage:    -d ==> option to allow double down for any combo"
<< endl;
   }

int check_inputs(int argc, char * argv[])
   {
    check_rules = FALSE;
    double_all = FALSE;

    if ((argc == 1) || (argc > 5))
       {
        return(ERROR);
       }
    else
       {
        if (argv[1][0] == '-')
           { 
            // check for interactive versus batch
            if (argv[1][1] == 'i')
               {
                interactive = TRUE;
                if (argc > 4)
                   {
                    return(ERROR);
                   }
                else if ((argc == 3) || (argc == 4))
                   {
                    if (strcmp(&(argv[2][0]), "-c") == 0)
                       {
                        check_rules = TRUE;
                       }
                    else if (strcmp(&(argv[2][0]), "-d") == 0)
                       {
                        double_all = TRUE;
                       }
                    else
                       {
                        return(ERROR);
                       }
                   }
                if (argc == 4)
                   {
                    if (strcmp(&(argv[3][0]), "-c") == 0)
                       {
                        check_rules = TRUE;
                       }
                    else if (strcmp(&(argv[3][0]), "-d") == 0)
                       {
                        double_all = TRUE;
                       }
                    else
                       {
                        return(ERROR);
                       }
                   }
               }
            else if (argv[1][1] == 'b')
               {
                interactive = FALSE;
                if (argc == 2)
                   {
                    return(ERROR);
                   }
                // get the number of hands to play
                if (argv[2][0] == '-')  // expecting num_hands
                   {
                    return(ERROR);
                   }
                num_hands = atoi(&(argv[2][0]));
                if ((num_hands < 1) || (num_hands > MAX_HANDS_SIM))
                   {
                    return(ERROR);
                   }
   
                if (argc > 5)
                   {
                    return(ERROR);
                   }
                else if ((argc == 4) || (argc == 5)) 
                   {
                    if (strcmp(&(argv[3][0]), "-c") == 0)
                       {
                        check_rules = TRUE;
                       }
                    else if (strcmp(&(argv[3][0]), "-d") == 0)
                       {
                        double_all = TRUE;
                       }
                    else
                       {
                        return(ERROR);
                       }
                   }
                if (argc == 5)
                   {
                    if (strcmp(&(argv[4][0]), "-c") == 0)
                       {
                        check_rules = TRUE;
                       }
                    else if (strcmp(&(argv[4][0]), "-d") == 0)
                       {
                        double_all = TRUE;
                       }
                    else
                       {
                        return(ERROR);
                       }
                   }
               }
            else
               {
                return(ERROR);
               }
           }
        else
           {
            return(ERROR);
           }
       }
    return(NORMAL);
   }

int main (int argc, char * argv[])
   {
    deck card_deck;
    dealer_hand dealers_hand;
    int dealer_up_card;
    player_hand your_hand[MAX_HANDS];
    int num_user_hands;

    int hands_played;
    float your_totals;

    unsigned long int modulus;
    long int random_number;
    double uniform_random_number;
    extern void get_modulus(unsigned long int &);

    cout << " ******************** BLACKJACK **********************" <<
endl;

    if (check_inputs(argc, argv) == ERROR)
       {
        print_usage();
        exit(-1);
       }
 
    get_modulus(modulus);
    random_number = clock_random();

    card_deck.shuffle(modulus, random_number, uniform_random_number);
  
    your_totals = 0.0;
    hands_played = 0;

    // do until user terminates the game or for n hands in batch mode
    for (;;) 
       {
        hands_played++;
        if (interactive)
           {
            cout << "\n************ NEW GAME *****************" <<
endl;
            cout << "\nEnter your bet (0 or less ends the game):" <<
flush;
            cin >> your_hand[0].bet;

            if (your_hand[0].bet <= 0.0)
               {
                // terminate the game
                break;
               }
            your_hand[0].insure = your_hand[0].bet / 2.0;
           }
        else
           {
            
            your_hand[0].bet = 1.0;
            your_hand[0].insure = 0.5;
           }
            

        // reshuffle if minimum number of cards left
        if (card_deck.deck_count > MAX_CARDS_PLAYED)
           {
            card_deck.shuffle(modulus, random_number,
uniform_random_number);
           }

        // initialize hands
        num_user_hands = 1;
        your_hand[0].initialize();
        dealers_hand.initialize();

        // deal the hand (two cards apiece)
        deal(card_deck, your_hand[0]);
        deal(card_deck, dealers_hand);
        deal(card_deck, your_hand[0]);
        deal(card_deck, dealers_hand);
        dealer_up_card =
          
default_deck::ddeck[dealers_hand.card_indices[SECOND]].denomination;

        // display original hands
        if (interactive)
           {
            your_hand[0].display();
            dealers_hand.display();
           }

        // count original hands
        your_hand[0].count();
        dealers_hand.count();
        
        // test for your Blackjack
        if (your_hand[0].blackjack())
           {
            if (interactive)
               {
                cout << "\n You got blackjack." << endl;
               }
            your_hand[0].bet += your_hand[0].insure;
            your_hand[0].play = FALSE;
           }
        else
           {
            // test for insurance
            if (interactive && dealers_hand.insurance())
               {
                your_hand[0].got_insurance = TRUE;
               }
            else
               {
                your_hand[0].got_insurance = FALSE;
               }
           }

        // test for dealer Blackjack
        if (dealers_hand.blackjack())
           {
            if (interactive)
               {
                cout << "\n Dealer got blackjack." << endl;
               }
            dealers_hand.first_card_down = FALSE;
            if (interactive)
               {
                dealers_hand.display();
               }
            your_hand[0].play = FALSE;
            dealers_hand.play = FALSE;
           }
        else if (interactive && your_hand[0].blackjack())
           { 
            dealers_hand.display();
           }
    
        if (your_hand[0].got_insurance)
           {
            if (dealers_hand.blackjack())
               {
                // subtract insurance from original bet
                your_hand[0].bet -= your_hand[0].insure;
               }
            else
               {
                // pay out insurance
                your_totals -= your_hand[0].insure;
               }
           }

        // if neither of you got blackjack, play on
        if (your_hand[0].play && dealers_hand.play)
           {
            // test for split
            if (your_hand[0].split(dealer_up_card))
               {
                do_split(card_deck, your_hand[0], 
                         your_hand[num_user_hands], num_user_hands);

                if (your_hand[0].split_aces == FALSE)
                   {
                    if (your_hand[0].split(dealer_up_card))
                       {
                        do_split(card_deck, your_hand[0], 
                                 your_hand[num_user_hands],
num_user_hands);
                        if (your_hand[0].split(dealer_up_card))
                           {   
                            do_split(card_deck, your_hand[0],
                                     your_hand[num_user_hands],
num_user_hands);
                           }
                       }
                    if (your_hand[1].split(dealer_up_card))
                       {
                        do_split(card_deck, your_hand[1], 
                                 your_hand[num_user_hands],
num_user_hands);
                        if (your_hand[1].split(dealer_up_card))
                           {
                            do_split(card_deck, your_hand[1],
                                     your_hand[num_user_hands],
num_user_hands);
                           }
                       }
                    if (num_user_hands > 2)
                       {
                        if (your_hand[2].split(dealer_up_card))
                           {
                            do_split(card_deck, your_hand[2],
                                     your_hand[num_user_hands],
num_user_hands);
                           }
                       }
                   }
               }

            // play all the user hands
            for (int i = 0; i < num_user_hands; i++)
               {
                if (interactive)
                   {
                    if (num_user_hands > 1)
                       {
                        cout << "\n\nHand: " << i << flush;
                       }
                    else
                       {
                        cout << "\n" << endl;
                       }
                   }
                your_hand[i].count();
                if (interactive)
                   {
                    your_hand[i].display();
                   }

                if (your_hand[i].split_aces == TRUE)
                   {
                    // you got the one card you are entitled to
                    your_hand[i].play = FALSE;
                   }
                else if (your_hand[i].double_down(dealer_up_card))
                   {
                    // test for double down and play one card for you
                    your_hand[i].bet = your_hand[i].bet * 2.0;
                    deal(card_deck, your_hand[i]);
                    your_hand[i].count();
                    if (interactive)
                       {
                        your_hand[i].display();
                       }
                    your_hand[i].play = FALSE;
                   }
                else
                   {
                    // play out hand normally 
                    while (your_hand[i].play)
                       {
                        if (interactive)
                           {
                            cout << "\nHit?(Y/N)" << flush;
                            BOOLEAN answer = get_answer();

                            if (check_rules)
                               {
                                BOOLEAN rule_answer;
                                
                                if (your_hand[i].aces &&
your_hand[i].hard_ace)
                                   {
                                    rule_answer = 
                                    
soft_hit_table[your_hand[i].hand_count]
                                                   [dealer_up_card];
                                   }
                                else
                                   {
                                    rule_answer = 
                                    
hard_hit_table[your_hand[i].hand_count]
                                                   [dealer_up_card];
                                   }
                                if (answer != rule_answer)
                                   {
                                    print_rule_violated();
                                   }
                               }
   
                            if (answer == TRUE)
                               {
                                deal(card_deck, your_hand[i]);
                                your_hand[i].display();
                                your_hand[i].count();
                                if (your_hand[i].over)
                                   {
                                    your_hand[i].play = FALSE;
                                   }
                               }
                            else
                               {
                                your_hand[i].play = FALSE;
                               }
                           } 
                        else
                           {
                            if (your_hand[i].aces &&
your_hand[i].hard_ace)
                               {
                                your_hand[i].play = 
                                
soft_hit_table[your_hand[i].hand_count]
                                               [dealer_up_card];
                               }
                            else
                               {
                                your_hand[i].play = 
                                
hard_hit_table[your_hand[i].hand_count]
                                               [dealer_up_card];
                               }
                            if (your_hand[i].play)
                               {
                                deal(card_deck, your_hand[i]);
                                your_hand[i].count();
                                if (your_hand[i].over)
                                   {
                                    your_hand[i].play = FALSE;
                                   }
                               }
                           }
                       }
                    if (interactive || debug) 
                       {
                        your_hand[i].display();
                       }
                   }
               }
           }

        // dealers play 
        dealers_hand.first_card_down = FALSE;
        while (dealers_hand.play)
           {
            if (interactive || debug)
               {
                dealers_hand.display();
               }
            dealers_hand.count();

            if ((dealers_hand.hand_count < SEVENTEEN) || 
                 ((dealers_hand.hand_count == SEVENTEEN) && 
                  (dealers_hand.aces && dealers_hand.hard_ace)))
               {
                /* note that dealer must hit below 17 and stay at 17 or
over
                 * but he must also hit a soft 17
                 */

                deal(card_deck, dealers_hand);
               }
            else
               {
                // dealer must stay or is busted
                dealers_hand.play = FALSE;
               }
           } /* end while */

        // settle score for all hands played
        for (int i = 0; i < num_user_hands; i++)
           {
            if (interactive)
               {
                if (num_user_hands > 1)
                   {
                    cout << "\n\nHand: " << i << flush;
                   }
                else
                   {
                    cout << "\n" << endl;
                   }
               }
            settle_score(your_totals, your_hand[i], dealers_hand);
           }

        // print running tab
        if (interactive)
           {
            print_totals(your_totals);
           }
        else
           {
            if (hands_played == num_hands)
               {
                break;
               }
           }
       } // end for loop

    if (!interactive)
       {
        print_totals(your_totals);
       }
    exit(0);
   } // end main

    Source: geocities.com/drhenke