// -*- mode:C++; tab-width:4; c-basic-offset:4; indent-tabs-mode:nil -*-

///////////////////////////////////////////////////////////////////////////////
//
// Blackjack.h
// Version 5.0
// Copyright (C) 1999, 2001, 2002 Eric Farmer
//
// Blackjack strategy calculator.  Contains classes for computing exact
// probabilities and expected values, for outcomes of the dealer's hand and
// play options for all possible player hands.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation; either version 2 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 59 Temple
// Place, Suite 330, Boston, MA  02111-1307  USA
//

#ifndef STRATEGY_H
#define STRATEGY_H

///////////////////////////////////////////////////////////////////////////////
//
// Return values for BJStrategy::getOption() (see below)
//

#define BJ_MAX_VALUE    0
#define BJ_STAND        1
#define BJ_HIT          2
#define BJ_DOUBLE_DOWN  3
#define BJ_SPLIT        4
#define BJ_SURRENDER    5

///////////////////////////////////////////////////////////////////////////////
//
// BJHand
//
// A BJHand object represents a blackjack hand held by the dealer or a player.
// The (hard or soft) count and the number of cards (total as well as of each
// value) in the hand are maintained.
//

class BJHand {
    friend class BJShoe;
    friend class BJDealer;
    friend class BJPlayer;

public:

// BJHand() creates an empty blackjack hand.

    BJHand();

// BJHand(cards) creates a blackjack hand with cards[i - 1] number of cards of
// value i, for each i in the range 1 (ace) through 10.

    BJHand(const int cards[]);

// getCards(card) returns the number of cards with the given value in the hand,
// where card is in the range 1 (ace) through 10.

    int getCards(int card) const;

// getCards() returns the total number of cards in the hand.

    int getCards() const;

// getCount() returns the hand's (hard or soft) count.  Soft hands count ace as
// 11.

    int getCount() const;

// getSoft() returns true iff the hand is soft.

    bool getSoft() const;

// reset() removes all cards from the hand.

    void reset();

// reset(cards) sets the hand to contain cards[i - 1] number of cards of value
// i, for each i in the range 1 (ace) through 10.

    void reset(const int cards[]);

// deal(card) deals a card of the given value to the hand (possibly busting the
// hand), where card is in the range 1 (ace) through 10.

    void deal(int card);

// undeal(card) removes a card of the given value from the hand, where card is
// in the range 1 (ace) through 10.

    void undeal(int card);

protected:
    int cards[10],
        numCards,
        count;
    bool soft;
};

///////////////////////////////////////////////////////////////////////////////
//
// BJShoe
//
// A BJShoe object represents a blackjack shoe containing dealt and undealt
// cards.  As in a BJHand object, only the distribution (not the order) of
// cards is maintained.
//

class BJShoe {
    friend class BJDealer;
    friend class BJPlayer;

public:

// BJShoe(numDecks) creates a blackjack shoe containing the given number of
// 52-card decks, with all cards in the shoe (i.e., undealt).

    BJShoe(int numDecks = 1);

// BJShoe(cards) creates a blackjack shoe containing cards[i - 1] number of
// cards of value i, for each i in the range 1 (ace) through 10, with all cards
// in the shoe (i.e., undealt).

    BJShoe(const int cards[]);

// getCards(card) returns the number of undealt cards with the given value
// remaining in the shoe, where card is in the range 1 (ace) through 10.

    int getCards(int card) const;

// getCards() returns the total number of undealt cards remaining in the shoe.

    int getCards() const;

// getProbability(card) returns the probability of a card with the given value
// being dealt next from the shoe, where card is in the range 1 (ace) through
// 10.

    double getProbability(int card) const;

// reset() returns all dealt cards to the shoe.

    void reset();

// reset(hand) returns all dealt cards to the shoe, then deals the given
// blackjack hand from the "full" shoe.

    void reset(const BJHand & hand);

// reset(numDecks) sets the shoe to contain the given number of 52-card decks,
// with all cards in the shoe (i.e., undealt).

    void reset(int numDecks);

// reset(cards) sets the shoe to contain cards[i - 1] number of cards of value
// i, for each i in the range 1 (ace) through 10, with all cards in the shoe
// (i.e., undealt).

    void reset(const int cards[]);

// deal(card) deals a card of the given value from the shoe, where card is in
// the range 1 (ace) through 10.

    void deal(int card);

// undeal(card) returns a card of the given value to the shoe, where card is in
// the range 1 (ace) through 10.

    void undeal(int card);

protected:
    int totalCards[10],
        cards[10],
        numCards;
};

///////////////////////////////////////////////////////////////////////////////
//
// BJDealer
//
// A BJDealer object contains functions for computing the probabilities of
// outcomes of the dealer's hand for a given blackjack shoe.
//

class BJDealer {
    friend class BJPlayer;

public:

// BJDealer(hitSoft17) prepares for computing probabilities, where hitSoft17 is
// true iff the dealer hits soft 17.

    BJDealer(bool hitSoft17);

// computeProbabilities(shoe) computes the probabilities of outcomes of the
// dealer's hand for the given shoe (i.e., for the distribution of undealt
// cards in the given shoe).  It is assumed that the dealer's up card also
// remains undealt; e.g., given a full shoe with no cards dealt, the
// appropriate probabilities will be computed.

    void computeProbabilities(const BJShoe & shoe);

// getProbabilityBust(upCard) returns the probability (computed by
// computeProbabilities) of the dealer busting with the given up card, where
// upCard is in the range 1 (ace) through 10.

    double getProbabilityBust(int upCard) const;

// getProbabilityBust() returns the overall probability (computed by
// computeProbabilities) of the dealer busting.

    double getProbabilityBust() const;

// getProbabilityCount(count, upCard) returns the probability (computed by
// computeProbabilities) of the dealer drawing to the given count, where count
// is in the range 17 through 21, for the given up card, where upCard is in the
// range 1 (ace) through 10.

    double getProbabilityCount(int count, int upCard) const;

// getProbabilityCount(count) returns the overall probability (computed by
// computeProbabilities) of the dealer drawing to the given count, where count
// is in the range 17 through 21.

    double getProbabilityCount(int count) const;

// getProbabilityBlackjack(upCard) returns the probability (computed by
// computeProbabilities) of the dealer having blackjack, for the given up card,
// where upCard is in the range 1 (ace) through 10.

    double getProbabilityBlackjack(int upCard) const;

// getProbabilityBlackjack() returns the overall probability (computed by
// computeProbabilities) of the dealer having blackjack.

    double getProbabilityBlackjack() const;

protected:
    bool hitSoft17;
    struct DealerHand {
        int cards[10],
            multiplier[10];
    };
    struct DealerHandCount;
    friend struct DealerHandCount; // make DealerHand accessible
    struct DealerHandCount {
        int numHands;
        DealerHand dealerHands[423];
    } dealerHandCount[5];
    BJHand currentHand;
    int upCard;
    double probabilityBust[10],
        probabilityCount[5][10],
        probabilityBlackjack[10],
        probabilityCard[10],
        lookup[13][10][12];
    static const int maxSvalues[10],
        maxHvalues[10];

    void countHands();
};

///////////////////////////////////////////////////////////////////////////////
//
// BJRules
//
// The BJRules interface allows specification of a particular set of rules for
// casino blackjack, indicating whether the dealer stands or hits with soft 17,
// whether pairs may be split more than once, etc.
//

class BJRules {
public:

// BJRules() is a default constructor allowing derivation from BJRules, and is
// equivalent to BJRules(false, true, true, true, false, true, true, false,
// false).

    BJRules();

// BJRules(...) is a convenience constructor for creating most common rule
// variations, specified by:
//
// hitSoft17            true iff the dealer hits soft 17
// doubleAnyTotal       true if doubling down is allowed on any hand total,
//                          false if only on (9 or) 10 or 11
// double9              true iff doubling down is allowed on 9 (valid only if
//                          doubleAnyTotal is false)
// doubleSoft           true iff doubling down is allowed on soft hands
// doubleAfterHit       true iff doubling down is allowed on more than 2 cards
// doubleAfterSplit     true iff doubling down is allowed after splitting pairs
// resplit              true iff pairs may be resplit (up to 4 hands)
// resplitAces          true iff aces may be resplit (up to 4 hands; valid only
//                          if resplit is true)
// lateSurrender        true iff late surrender is allowed

    BJRules(bool hitSoft17, bool doubleAnyTotal, bool double9, bool doubleSoft,
            bool doubleAfterHit, bool doubleAfterSplit, bool resplit,
            bool resplitAces, bool lateSurrender);

// ~BJRules() allows appropriate destruction of objects derived from BJRules.

    virtual ~BJRules();

// getHitSoft17() returns true iff the dealer hits soft 17.

    virtual bool getHitSoft17();

    virtual bool getDoubleAnyTotal ();
    virtual bool getDouble9 ();
    virtual bool getDoubleSoft ();
    virtual bool getDoubleAfterHit ();
    virtual bool getDoubleAfterSplit ();
    virtual bool getResplit ();
    virtual bool getResplitAces ();

// getDoubleDown(hand) returns true iff doubling down is allowed on the given
// blackjack hand.

    virtual bool getDoubleDown(const BJHand & hand);

// getDoubleAfterSplit(hand) returns true iff doubling down is allowed on the
// given blackjack hand after splitting pairs.

    virtual bool getDoubleAfterSplit(const BJHand & hand);

// getResplit(pairCard) returns the maximum number of hands to which the given
// pairCard may be split.  Valid return values are 1 (no splits), 2 (no
// resplit), 3, or 4.

    virtual int getResplit(int pairCard);

// getLateSurrender() returns true iff late surrender is allowed.

    virtual bool getLateSurrender();

protected:
    bool hitSoft17,
        doubleAnyTotal,
        double9,
        doubleSoft,
        doubleAfterHit,
        doubleAfterSplit,
        resplit,
        resplitAces,
        lateSurrender;
};

///////////////////////////////////////////////////////////////////////////////
//
// The BJStrategy interface allows specification of a possibly sub-optimal
// playing strategy, for which corresponding expected values may be computed
// (see BJPlayer).  Examples of such strategies include total-dependent vs.
// composition-dependent, "mimic the dealer," etc.
//

class BJStrategy {
public:

// ~BJStrategy() allows appropriate destruction of objects derived from
// BJStrategy.

    virtual ~BJStrategy();

// getOption(...) returns one of the constants BJ_* specified above, indicating
// which player option is to be taken in the situation specified by:
//
// hand         the player's hand
// upCard       the dealer's up card, where upCard is in the range 1 (ace)
//                  through 10
// doubleDown   true iff doubling down is allowed
// split        true iff splitting is allowed
// surrender    true iff surrender is allowed
//
// A return value of BJ_MAX_VALUE indicates that the option maximizing the
// expected value of the player's hand is to be taken.  Other return values
// indicate the corresponding option; it is an error to return BJ_DOUBLE_DOWN
// if doubleDown is false, or BJ_SPLIT if split is false, or BJ_SURRENDER if
// surrender is false.
//
// BJStrategy::getOption(...) returns BJ_MAX_VALUE.

    virtual int getOption(const BJHand & hand, int upCard, bool doubleDown,
                          bool split, bool surrender);
};

///////////////////////////////////////////////////////////////////////////////
//
// BJProgress
//
// The BJProgress interface allows some indication of progress of the creation
// of a BJPlayer object (or the execution of BJPlayer::reset()).
//

class BJProgress {
public:

// ~BJProgress() allows appropriate destruction of objects derived from
// BJProgress.

    virtual ~BJProgress();

// indicate(percentComplete) is called repeatedly during the creation of a
// BJPlayer object (or the execution of BJPlayer::reset()), indicating the
// progress of the computation, where percentComplete is in the range 0 through
// 100 (complete).

    virtual void indicate(int percentComplete);
};

///////////////////////////////////////////////////////////////////////////////
//
// BJPlayer
//
// A BJPlayer object contains functions for computing the expected values of
// all player options for all possible player hands against each dealer up
// card, for a given blackjack shoe, rule variations, and playing strategy.
//

class BJPlayer : public BJStrategy {
public:

// BJPlayer(shoe, rules, strategy, progress) and reset(...) compute expected
// values, both overall and for all possible player hands and dealer up cards,
// for the given blackjack shoe, rule variations, and playing strategy.  Only
// undealt cards in the shoe are considered; progress.indicate() is called
// repeatedly during execution.

    BJPlayer(const BJShoe & shoe, BJRules & rules, BJStrategy & strategy,
             BJProgress & progress);
    void reset(const BJShoe & shoe, BJRules & rules, BJStrategy & strategy,
               BJProgress & progress);

// getValue*(hand, upCard) returns the expected value (as a fraction of initial
// wager), conditioned on the dealer not having blackjack, of the indicated
// player option for the given hand and dealer up card, where upCard is in the
// range 1 (ace) through 10.  Results are undefined and may cause an error if
// the hand is a bust hand or if the hand and up card are not possible in the
// given shoe.

    double getValueStand(const BJHand & hand, int upCard) const;
    double getValueHit(const BJHand & hand, int upCard) const;
    double getValueDoubleDown(const BJHand & hand, int upCard) const;

// getValueSplit(pairCard, upCard) returns the expected value (as a fraction of
// initial wager), conditioned on the dealer not having blackjack, of splitting
// a pair of cards with the given value against the given dealer up card, where
// pairCard and upCard are in the range 1 (ace) through 10.  Results are
// undefined if the pair hand and up card are not possible in the given shoe.

    double getValueSplit(int pairCard, int upCard) const;

// getValue(upCard) returns the overall expected value (as a fraction of
// initial wager) of a player hand against the given dealer up card, where
// upCard is in the range 1 (ace) through 10.  Results are undefined if the up
// card is not in the given shoe.

    double getValue(int upCard) const;

// getValue() returns the overall expected value (as a fraction of initial
// wager) of a player hand.

    double getValue() const;

// getOption() implements the BJStrategy interface, returning the player option
// which maximizes the expected value of the hand (assuming, if necessary,
// subsequent following of the given strategy).

    int getOption(const BJHand & hand, int upCard, bool doubleDown, bool split,
                  bool surrender);

protected:
    int numHands,
        playerHandCount[22][2];
    struct PlayerHand {
        int cards[10],
            hitHand[10],
            nextHand;
        float valueStand[2][10],
            valueHit[2][10],
            valueDoubleDown[2][10],

            probabilityBust[10],
            probabilityCount[5][10],
            probabilityBlackjack[10];
    } playerHands[16373];
    BJHand currentHand;
    BJShoe shoe;
    int resplit[10];
    double valueSplit[10][10],
        overallValues[10],
        overallValue;

    int findHand(const BJHand & hand) const;
    bool record(const BJHand & hand);
    void countHands(int i, int maxCard);
    void linkHands();
    void computeDealer(BJRules & rules, BJProgress & progress);
    void linkHandCounts(bool split = false, int pairCard = 0,
                        int splitHands = 1);

    void computeStand(bool split = false, int pairCard = 0,
                      int splitHands = 1);
    void computeStandCount(int count, bool soft, bool split, int pairCard,
                           int splitHands);

    void computeDoubleDown(bool split = false, int pairCard = 0,
                           int splitHands = 1);
    void computeDoubleDownCount(int count, bool soft, bool split, int pairCard,
                                int splitHands);

    void computeHit(BJRules & rules, BJStrategy & strategy, bool split = false,
                    int pairCard = 0, int splitHands = 1);
    void computeHitCount(int count, bool soft, BJRules & rules,
                         BJStrategy & strategy, bool split, int pairCard,
                         int splitHands);

    void computeSplit(BJRules & rules, BJStrategy & strategy);
    void correctStandBlackjack();
    void computeOverall(BJRules & rules, BJStrategy & strategy);
    double computeSurrender(int upCard);
    void conditionNoBlackjack();
};

#endif
