Date: Wed, 19 May 1999 14:53:04 -0400
From: xxx
To: James Holliday
Subject: Blackjack Strategy



\*      Here is the program bj-strat, which calculates blackjack
strategy numbers.  The program reads the point count values for cards
from the command line, and prints the corresponding count strategy on
the standard output.  Running time for the program is approximately two
minutes on the VS-2000, for the Hi-Opt I or Wong High-Low strategy, for
the count range +/- 10, if the infinite deck approximation is used.  For
a finite number of decks, the time required is roughly 35 minutes, for
the range +/- 6.

Usage:

        The first ten arguments to the program should be the point
values of the cards two through ace.  After the card values, the
following additional arguments are permitted, in any order:

-a value        value of ace in side count
-n decks        number of decks (infinite deck approximation if omitted)
-d              double-on-splits is allowed
-h              dealer hits soft 17
-r low high     only consider true count values from low to high, inclusive
-s step         only consider true count values that are multiples of step

Use the -a option if a side count is used for *playing* purposes.  The
value of the ace is is the number of points to add to the running
count for each ``missing'' ace, i.e. if 26 cards are left and no aces
are left, add 2 * value to the running count.  Do *not* use this option
if a side count is used for *betting* purposes.

        If the -r and -s options are omitted, the program considers
the full range of count values.

Examples:

1) Hi-Opt I numbers from -6 to 6; double on splits allowed, single deck:
bj-strat 0 1 1 1 1 0 0 0 -1 0  -d  -n 1  -r -6 6

2) High-Low with ace side count, -10 to +10 range, 2 decks:
bj-strat 1 1 1 1 1 0 0 0 -1 -1  -n 2  -a 1  -r -10 10

3) Wong halves numbers from -20 to +20; dealer hits soft 17, infinite deck:
bj-strat 1 2 2 3 2 1 0 -1 -2 -2  -h  -r -40 40  -s 2

Output numbers must be divided by 2 in this case, because the halves
count actually uses card values 1/2 to 1 1/2, not 1 to 3.  This method
is used because the program only accepts integer card values.

Output:

        In the output, the symbol * next to a number indicates that
the strategy changes in the opposite direction from that normally
expected, e.g. if the number 7* appears in the pair split table, you
should split only when the count is less than 7.  The notation a/b
indicates that the player should stand/double/split when the count is
in the range from a to b; the notation a^b indicates that the player
should stand/double/split _unless_ the count is between a and b.

        I have commented out the program lines that print ranges.
Currently, the program only prints the strategy change number closest
to zero if a range of numbers is involved.  These lines are near the
end of the of the program, and can be uncommented to print ranges, if
desired.

        The more extreme values in the strategy tables (e.g. greater
than +/- 20 for High-Low or Hi-Opt I) probably should not be taken too
seriously.  The approximation used to generate decks with specified
point counts tends to break down at extreme values.

How it works:

        The approach used in the program is fairly straightforward.
First, I generate a ``typical'' deck for each true count in the range
I'm interested in.  Next, I assume I have an infinite deck of this
composition.  I calculate the player's ideal strategy for each of
these decks, and look for the point count(s) at which the correct
strategy for a situation changes.

        The assumption of an infinite deck simplifies the calculations
and saves computer time, but introduces a small amount of inaccuracy.
My results should be good for multiple deck play, and may also be
adequate for single deck.  (Last summer, during the BJ comparison
contest, Matt Wilding found no measurable difference in performance
between my strategy numbers for the Hi-Opt I and the published
numbers).

        I use the following formula to construct a ``typical'' deck
for a given true count.  It is based on altering the frequency of
cards in proportion to their point values in the counting system,
until the desired true count is achieved.  (Stanford Wong and Peter
Griffin used similar formulas in their work).  This formula assumes
that the count is balanced, i.e. the point value of a complete deck is
zero.

Let Fo[i] = proportion of cards of rank i in a normal deck,
            i.e. 4/13 if i=10, 1/13 if 2 <= i <= 9 or i = Ace
Let Fj[i] be the proportion of cards of rank i in a ``typical''
            deck with a true count of j
Let V[i] be the point value for card i in the counting system

then Fj[i] = Fo[i] * (1 - (j/52)*V[i]/SS)
     where SS = sum over i in 2..Ace of Fo[i] * V[i]**2

        The above formula is used for the infinite deck approximation.
To approximate a finite number of decks, I first remove the dealer's
upcard and player's cards, if known, from a finite deck.  I then adjust
cards' frequencies in proportion to their point count values.  Finally,
I assume I have an infinite deck of the resulting composition.  (This
is what Stanford Wong did to estimate strategy numbers for a finite deck).

        If an ace side count is used, I combine the main count and
side count into a single point count system Vc, and use Vc instead of
V in all calculations.  Let Va be the value of the ace in the side
count (zero if a side count is not used).

then Vc[i]   = V[i] - Va / 13, for i in 2..10
     Vc[Ace] = V[Ace] + (12/13) * Va

        To determine the correct strategy for a given deck, I perform
an exact probability calculation (i.e. simulation is not used at all).
I calculate the player's expectation standing on a given hand vs. a
given dealer hand as follows:

standing_value(p, d) =
  if (dealer stands on d) then
    if (p beats d) then +1
    else if (d beats p) then -1
    else 0
  else <--- dealer hits
    sum over c in 2..ace
      probablity[c] * standing_value(p, d + c)

Each value calculated is stored in a table, to avoid calculating the
same values twice.  The program tries to look up the answer in the
table first, and performs the calculation only if necessary.

        The player's expectation hitting is calculated as follows:

hitting_value(p, d) =
  sum over c in 2..ace
    if (p + c is over 21) then probability[c] * -1
    else
      probability[c] * maximum(hitting_value (p + c, d),
                               standing_value(p + c, d))

        Hitting values are also stored in a table, to avoid redundant
calculations.   The strategy calls for a player to hit if his hitting
value is greater than his standing value.

        Constructing tables for doubling down and splitting is
straightforward; the hit and stand tables make it easy to compute the
values for the other tables. */

/* -------------------------------------------------------------------- */

/* Copyright (c) 1990, 1991  Steven C. Markowitz.  All rights reserved. */

#include 

#define ACE 11

#define FALSE 0
#define TRUE 1

#define HARD 0
#define SOFT 1
#define OVER 2

#define DUMMY 0

#ifdef __STDC__
extern void *calloc();
#define FREE(x) free((void *) (x))
#else
extern char *calloc();
#define FREE(x) free((char *) (x))
#endif

float max(x, y)
     double x, y;
{
  return ((x > y) ? x : y);
}

float min(x, y)
     double x, y;
{
  return ((x < y) ? x : y);
}
typedef int boolean;
typedef int Card;
typedef float Deck[12];  /* deck[i] = proportion of cards in deck
                         that are of type i.  Deck[0] and deck[1]
                         are not used. */

typedef float Method[12]; /* method [i] = point value of card i in
                           counting method.  Method[0] and method[1]
                           are not used. */

/* A total represents a blackjack hand;
   it may be hard, soft or over 21 */

typedef
  struct {
    int tag;
    int value;
  } Total;

/* A table holds real numbers.  Some table elements may not have been
   calculated yet; this is indicated by the unknown flag.  */

#define TOTALS 32 /* number of distinct blackjack totals */
#define CARDS  10 /* number different types of cards in a deck */

typedef
  struct {
    boolean unknown;
    float value;
  } Mfloat;
typedef Mfloat **Table;

/* A strategy describes the correct strategy for
   playing blackjack, for a given deck composition */

typedef
  struct {
    Deck  deck;
    boolean dbs, soft17;
    Table standing_values,   /* player's expectation when standing */
          hitting_values,    /* player's expectation hitting */
          doubling_values,   /* player's expectation doubling down */
          splitting_values;  /* player's expectation splitting */
  } Strategy;

/* A threshold t indicates the count at which a player should change strategy.
   There are six types of thresholds:
   POOR -- always hit/never double/never split
   RICH -- always stand/double/split
   NORMAL -- stand/double/split only if true count exceeds t.change_rich
   REVERSE -- stand/double/split when true count is less than t.change_poor
   RANGE -- stand/double/split if t.change_rich < true_count < t.change_poor
   REVRANGE -- stand/double/split UNLESS t.change_poor < count < t.change_rich
   */

#define POOR     0
#define RICH     1
#define NORMAL   2
#define REVERSE  3
#define RANGE    4
#define REVRANGE 5

typedef
  struct {
    int type, change_poor, change_rich;
  } Threshold;

Threshold *hit_thresholds[TOTALS][CARDS],
          *double_thresholds[TOTALS][CARDS],
          *split_thresholds[CARDS][CARDS];


/* Operations on totals: */

/* make_hard: returns a hard total with a specified value */
Total make_hard(value)
     int value;
{
  Total t;
  
  t.tag = HARD;
  t.value = value;
  return t;
}

/* make_soft: returns a soft total with a specified value */
Total make_soft(value)
     int value;
{
  Total t;
  
  t.tag = SOFT;
  t.value = value;
  return t;
}

/* make_over: returns a total that is over 21 */
Total make_over()
{
  Total t;
  t.tag = OVER;
  t.value = NULL;
  return t;
}

/* c2t: returns total corresponding to a given card */
Total c2t(c)
     Card c;
{
  if (c == ACE)
    return make_soft(c);
  else
    return make_hard(c);
}

/* over21: returns true if total t is over 21, false otherwise */
boolean over21(t)
     Total t;
{
  return (t.tag == OVER);
}

boolean soft21(t)
     Total t;
{
  return ((t.tag == SOFT) && (t.value == 21));
}

/* sum: returns the total obtained by adding a card c to a total t */
Total sum(t, c)
     Total t;
     Card c;
{
  int new_value;
  Total new_total;
  
  if (t.tag == OVER)
    return t;
  
  new_value = t.value + c;
  if ((t.tag) == SOFT)
    new_value = new_value - 10;
  if (c == ACE)
    new_value = new_value - 10;
  
  if (new_value > 21) {
    new_total.tag = OVER;
    new_total.value = NULL;
  }
  else if ((new_value <= 11) && ((t.tag == SOFT) || (c == ACE))) {
    new_total.tag = SOFT;
    new_total.value = new_value + 10;
  }
  else {
    new_total.tag = HARD;
    new_total.value = new_value;
  }
  return new_total;
}


/* Operations on tables */

/* t_create: constructs and returns a new table with the specified
   number of rows and columns, and all elements unknown */

Table t_create(rows, columns)
int rows, columns;
{
  int i,j;
  Table tab = (Table) calloc((unsigned) (rows + 1), sizeof(Mfloat *));
  
  for (i = 0; i < rows; i++) {
    tab[i] = (Mfloat *) calloc((unsigned) columns, sizeof(Mfloat));
    for (j = 0; j < columns; j++)
      tab[i][j].unknown = TRUE;
  }
  tab[rows] = NULL;
  return tab;
}

Table tt_create()
{
  return t_create(TOTALS, TOTALS);
}

Table ct_create()
{
  return t_create(TOTALS, CARDS);
}

Table pt_create()
{
  return t_create(CARDS, CARDS);
}

/* t_destroy: frees storage associated with a table */
void t_destroy(tab)
     Table tab;
{
  int i;

  for (i = 0; tab[i]; i++)
    FREE(tab[i]);
  FREE(tab);
}

/* tt_fetch: returns the table element indexed by totals t1 and t2 */
Mfloat tt_fetch(tab, t1, t2)
     Table tab;
     Total t1, t2;
{
  return tab[t2i(t1)][t2i(t2)];
}

/* tt_store: stores value into a the table
   location specified by totals t1 and t2 */
void tt_store(tab, t1, t2, value)
     Table tab;
     Total t1, t2;
     double value;
{
  int i = t2i(t1);
  int j = t2i(t2);
  
  tab[i][j].unknown = FALSE;
  tab[i][j].value = (float) value;
}

/* ct_fetch: returns the table element indexed by total tot and card c */
Mfloat ct_fetch(tab, tot, c)
     Table tab;
     Total tot;
     Card c;
{
  return tab[t2i(tot)][c2i(c)];
}

/* ct_store: stores value into the table
   location specified by total tot and card c */
void ct_store(tab, tot, c, value)
     Table tab;
     Total tot;
     Card c;
     double value;
{
  int i = t2i(tot);
  int j = c2i(c);
  
  tab[i][j].unknown = FALSE;
  tab[i][j].value = (float) value;
}

/* pt_fetch: returns the table element indexed by cards c1 and c2 */
Mfloat pt_fetch(tab, c1, c2)
     Table tab;
     Card c1, c2;
{
  return tab[c2i(c1)][c2i(c2)];
}

/* pt_store: stores value into a the table
   location specified by cards c1 and c2 */
void pt_store(tab, c1, c2, value)
     Table tab;
     Card c1, c2;
     double value;
{
  int i = c2i(c1);
  int j = c2i(c2);
  
  tab[i][j].unknown = FALSE;
  tab[i][j].value = (float) value;
}

/* t2i: returns the table index corresponding to a total */
int t2i(t)
     Total t;
{
  switch (t.tag) {
  case OVER:               /* over 21 corresponds to index 31 */
    return 31;
  case HARD:               /* hard totals 2..10 correspond to indexes 0 to 8 */
    if (t.value <= 10)     /* hard 11-21 corresponds to indexes 20 to 30 */
      return t.value - 2;
    else
      return(t.value + 9);
  case SOFT:               /* soft totals correspond to indexes 9 to 19 */
    return t.value - 2;
  }
}

/* returns the table index corresponding to a card */
int c2i(c)
     Card c;
{
 return (c - 2);           /* cards 2..ace correspond to indexes 0 to 9 */
} 


/* Operations on strategies */

/* s_create: constructs and returns strategy object for specified deck */
Strategy *s_create(d, dbs, soft17)
     Deck d;
     boolean dbs, soft17;
{
  Card c;
  Strategy *s = (Strategy *) calloc(1, sizeof(Strategy));
  
  for (c = 2; c <= ACE; c++)
    s->deck[c] = d[c];

  s->dbs = dbs;
  s->soft17 = soft17;
  s->standing_values  = tt_create();
  s->hitting_values   = ct_create();
  s->doubling_values  = ct_create();
  s->splitting_values = pt_create();
  
  return s;
}

/* s_destroy: frees heap space allocated for strategy object */
void s_destroy(s)
     Strategy *s;
{
  t_destroy(s->standing_values);
  t_destroy(s->hitting_values);
  t_destroy(s->doubling_values);
  t_destroy(s->splitting_values);
  FREE(s);
}

boolean final(s, t)
     Strategy *s;
     Total t;
{
  return ((t.tag == OVER) || (t.value > 17) ||
          (t.value == 17) && ((t.tag == HARD) || (! s->soft17)));
}

/* standing_value: returns player's expectation standing with player_hand
   when dealer has dealer_hand.  If check_bj is true then the dealer's
   next card must not give him soft 21; otherwise the next card may be of
   any value */

float standing_value(s, player_hand, dealer_hand, check_bj)
     Strategy *s;
     Total player_hand, dealer_hand;
     boolean check_bj;
{
  extern float correct_bj();
  Mfloat answer;
  
  answer = tt_fetch(s->standing_values, player_hand, dealer_hand);
  if (answer.unknown) {
    float expectation;
    
    if (over21(player_hand))
      expectation = -1.0;
    else if (final(s, dealer_hand))
      if (over21(dealer_hand) || (dealer_hand.value < player_hand.value))
        expectation = 1.0;
      else if (dealer_hand.value > player_hand.value)
        expectation = -1.0;
      else
        expectation = 0.0;
    else {
      Card c;
      
      expectation = 0.0;
      for (c = 2; c <= ACE; c++) {
        Total new_hand;
        
        new_hand = sum(dealer_hand, c);
        expectation = expectation +
          s->deck[c] * standing_value(s, player_hand, new_hand, FALSE);
      } 
    }
    tt_store(s->standing_values, player_hand, dealer_hand, expectation);
      
    if (check_bj)
      return correct_bj(expectation, player_hand, dealer_hand, s->deck);
    else return expectation;
  }
  else {
    /* answer already calculated */
    if (check_bj)
      return correct_bj(answer.value, player_hand, dealer_hand, s->deck);
    else return answer.value;
  }
}

/* correct_bj: adjust expectation to take into account
   the fact that the dealer cannot have soft 21 */

float correct_bj(expectation, player_hand, dealer_hand, d)
     double expectation;
     Total player_hand, dealer_hand;
     Deck d;
{
  float bj_prob, adjustment;

  if ((dealer_hand.tag == HARD) && (dealer_hand.value == 10))
    bj_prob = d[ACE];
  else if ((dealer_hand.tag == SOFT) && (dealer_hand.value == ACE))
    bj_prob = d[10];
  else bj_prob = 0.0;

  if (over21(player_hand) || (player_hand.value < 21))
    adjustment = 1.0;
  else adjustment = 0.0;

  return((expectation + adjustment * bj_prob) / (1.0 - bj_prob));
}

/* hitting_value: returns player's expectation
   hitting with player_hand when dealer has upcard. */

float hitting_value(s, player_hand, upcard)
     Strategy *s;
     Total player_hand;
     Card  upcard;
{
  Mfloat answer;
  
  answer = ct_fetch(s->hitting_values, player_hand, upcard);
  if (answer.unknown) {
    float expectation;
    
    if (over21(player_hand))
      expectation = -1.0;
    else {
      Card c;
      
      expectation = 0.0;
      for (c = 2; c <= ACE; c++) {
        Total new_hand;
        
        new_hand = sum(player_hand, c);
        expectation = expectation +
          s->deck[c] * max(standing_value(s, new_hand, c2t(upcard), TRUE),
                           hitting_value (s, new_hand, upcard));
      }
    }
    ct_store(s->hitting_values, player_hand, upcard, expectation);
    return expectation;
  }
  else
    return answer.value;
}

/* hit: returns TRUE if player should hit player_hand
   vs. upcard; otherwise returns FALSE */

boolean hit(s, player_hand, upcard)
     Strategy *s;
     Total player_hand;
     Card  upcard;
{
  return(hitting_value (s, player_hand, upcard) >
         standing_value(s, player_hand, c2t(upcard), TRUE));
}

/* doubling_value: returns player's expectation
   doubling down with player_hand vs. upcard. */

float doubling_value(s, player_hand, upcard)
     Strategy *s;
     Total player_hand;
     Card  upcard;
{
  Mfloat answer;
  
  answer = ct_fetch(s->doubling_values, player_hand, upcard);
  if (answer.unknown) {
    Card c;
    float expectation = 0.0;
    
    for (c = 2; c <= ACE; c++) {
      Total new_hand;
      
      new_hand = sum(player_hand, c);
      expectation = expectation +
        2.0 * s->deck[c] * standing_value(s, new_hand, c2t(upcard), TRUE);
    }
    ct_store(s->doubling_values, player_hand, upcard, expectation);
    return expectation;
  }
  else
    return answer.value;
}

/* double_down: returns TRUE if player should double down
   with player_hand vs. upcard; otherwise returns FALSE */

boolean double_down(s, player_hand, upcard)
     Strategy *s;
     Total player_hand;
     Card  upcard;
{
  return(doubling_value(s, player_hand, upcard) >
         max(standing_value(s, player_hand, c2t(upcard), TRUE),
             hitting_value (s, player_hand, upcard)));
}

/* splitting_value: returns player's expectation splitting
   a pair of rank pair when the dealer has upcard. */

float splitting_value(s, pair, upcard)
     Strategy *s;
     Card pair, upcard;
{
  Mfloat answer;
  
  answer = pt_fetch(s->splitting_values, pair, upcard);
  if (answer.unknown) {
    Card c;
    float expectation = 0.0;
    
    for (c = 2; c <= ACE; c++) {
      Total new_hand;
      
      new_hand = sum(c2t(pair), c);
      if (pair == ACE)
      expectation = expectation +
        2.0 * s->deck[c] * standing_value(s, new_hand, c2t(upcard), TRUE);
      else
        expectation = expectation +
          2.0 * s->deck[c] *
            max(standing_value(s, new_hand, c2t(upcard), TRUE),
                s->dbs ?
                max(hitting_value (s, new_hand, upcard),
                    doubling_value(s, new_hand, upcard))
                : hitting_value(s, new_hand, upcard));
    }
    pt_store(s->splitting_values, pair, upcard, expectation);
    return expectation;
  }
  else
    return answer.value;
}

/* split: returns TRUE if pair should be split vs. upcard, otherwise FALSE */
boolean split(s, pair, upcard)
     Strategy *s;
     Card pair, upcard;
{
  Total player_hand;

  player_hand = sum(c2t(pair), pair);
  return (splitting_value(s, pair, upcard) >
          max(standing_value(s, player_hand, c2t(upcard), TRUE),
              max(hitting_value (s, player_hand, upcard),
                  doubling_value(s, player_hand, upcard))));
}

/* make_deck: constructs and returns typical deck having a true count of
   count, where counting_method[i] indicates the value of cards of rank i
   in the counting system, for 2 <= i <= ACE, decks is the number of decks
   in use (zero means infinite deck), p1 and p2 are the player's cards and
   upcard is the dealer's upcard.  P1, p2 and upcard are ignored if zero. */

Deck *make_deck(counting_method, true_count, decks, p1, p2, upcard)
     Method counting_method;
     double true_count;
     int decks;
     Card p1, p2, upcard;
{
  Card c;
  Deck *d;
  Deck base_deck;
  float total_cards = 52.0 * decks;
  int undealt_cards = total_cards;
  float mean = 0.0, variance = 0.0;

  for (c = 2; c <= ACE; c++)
      base_deck[c] = ((c == 10) ? 4.0 : 1.0) / 13.0;

  if (decks > 0) {
    if (p1 > 0) {
      base_deck[p1] = base_deck[p1] - 1.0 / total_cards;
      undealt_cards--;
    }
    if (p2 > 0) {
      base_deck[p2] = base_deck[p2] - 1.0 / total_cards;
      undealt_cards--;
    }
    if (upcard > 0) {
      base_deck[upcard] = base_deck[upcard] - 1.0 / total_cards;
      undealt_cards--;
    }
    for (c = 2; c <= ACE; c++)
      base_deck[c] = base_deck[c] * total_cards / undealt_cards;
  }
  
  for (c = 2; c <= ACE; c++)
    mean = mean + base_deck[c] * counting_method[c];

  for (c = 2; c <= ACE; c++)
    variance = variance +
      base_deck[c] * counting_method[c] * counting_method[c];
  variance = variance - mean * mean;

  d = (Deck *) calloc(1, sizeof(Deck));
  for (c = 2; c <= ACE; c++)
    (*d)[c] = base_deck[c] *
      (1.0 - (true_count / 52.0 + mean) * (counting_method[c] - mean) /
       variance);

  return d;
}
  
/* main: takes a counting method description from the command line and prints
   the corresponding count strategy adjustments on the standard output */

main(argc, argv)
     int argc;
     char **argv;
{
  boolean dbs = FALSE,
          soft17 = FALSE,
          found_decks = FALSE,
          found_range = FALSE,
          found_step = FALSE,
          found_side_count = FALSE;
  int main_count[12];      /* point values of cards in main count */
  Method counting_method;  /* point values of cards after combining
                             main count and ace side count */
  Card c, c2, pair;
  Strategy *s;
  Deck *d;
  int lower, upper, step, true_count;
  int decks = 0;
  int ace_side_value = 0;
  float sum_of_squares;
  extern void error(),   /* prints specified error message and halt program */
         error_usage();  /* prints usage error message and halts program */
  extern int parse_int();

  /* read counting method from command line */
  if (argc < 11)
    error_usage();
  for (c = 2; c <= ACE; c++)
    main_count[c] = parse_int(*++argv);

  /* read remaining arguments */
  argc = argc - 11;
  while ((argc)  && ((*++argv)[0] == '-')) {
    if (((*argv)[1] == 'd') || ((*argv)[1] == 'h')) {
      char next;
      while ((next = *++*argv) != '\0')
        switch (next) {
        case 'd':
          dbs = TRUE;
          break;
        case 'h':
          soft17 = TRUE;
          break;
        default:
          error_usage();
          break;
        }
      argc--;
    }
    else if ((! strcmp(*argv, "-n")) && (! found_decks) && (argc >= 2)) {
      found_decks = TRUE;
      decks = parse_int(*++argv);
      argc = argc - 2;
    }
    else if ((! strcmp(*argv, "-a")) && (! found_side_count) && (argc >= 2)) {
      found_side_count = TRUE;
      ace_side_value = parse_int(*++argv);
      argc = argc - 2;
    }
    else if ((! strcmp(*argv, "-r")) && (! found_range) && (argc >= 3)) {
      found_range  = TRUE;
      lower = parse_int(*++argv);
      upper = parse_int(*++argv);
      argc = argc - 3;
    }
    else if ((! strcmp(*argv, "-s")) && (! found_step) && (argc >= 2)) {
      found_step = TRUE;
      step = parse_int(*++argv);
      argc = argc - 2;
    }
    else
      error_usage();
  }
  if (argc > 0)
    error_usage();

  if (found_decks && decks <= 0)
    error("number of decks must be positive");

  {
    /* adjust counting method for ace side count and check
       for illegal counting method (unbalanced or all zero) */
    
    int adjusted_value;
    int total_points = 0;
    boolean all_zero = TRUE;

    for (c = 2; c <= ACE; c++) {
      if (c == ACE)
        adjusted_value = 13 * main_count[c] + ace_side_value * 12;
      else
        adjusted_value = 13 * main_count[c] - ace_side_value;

      if (c == 10)
         total_points += 4 * adjusted_value;
      else
        total_points += adjusted_value;

      if (adjusted_value != 0)
        all_zero = FALSE;
      counting_method[c] = adjusted_value / 13.;
    }
    if (total_points != 0)
      error("unbalanced count not allowed");

    if (all_zero)
      if (ace_side_value == 0)
        error("all card values are zero");
      else error("ace side count cancels out main count");
  }
  if (found_range && (lower > upper))
    error("lower limit greater than upper limit");

  if (found_step)
    if (step < 1)
      error("step value must be positive");
    else ; /* step value OK */
  else
    step = 1;

  /* print title and copyright notice */
  fprintf(stderr, "bj-strat version 1.2 -- May 11, 1991\n");
  fprintf(stderr, "Copyright (c) 1990, 1991  Steven C. Markowitz.  ");
  fprintf(stderr, "All rights reserved.\n");
  {
    /* trim lower and upper limits to legal range */

    float vmin = counting_method[2];
    float vmax = vmin;
    float total_cards = 52.0 * decks;
    float lowest_allowed, highest_allowed;
    
    sum_of_squares = 0.0;
    for (c = 2; c <= ACE; c++)
      sum_of_squares = sum_of_squares +
        ((c == 10) ? 4.0 : 1.0) / 13.0 *
          (counting_method[c] * counting_method[c]);

    for (c = 3; c <= ACE; c++) {
      float card_value = counting_method[c];
      vmin = min(vmin, card_value);
      vmax = max(vmax, card_value);
    }
    lowest_allowed = 52.0 *
      ((sum_of_squares +
        (decks ? 3 * vmax * (vmin - vmax) / total_cards : 0)) /
       (vmin + (decks ? 3 * (vmax - vmin) / total_cards : 0)))
        + 0.5 * step;
    highest_allowed = 52.0 *
      ((sum_of_squares +
        (decks ? 3 * vmin * (vmax - vmin) / total_cards : 0)) /
       (vmax + (decks ? 3 * (vmin - vmax) / total_cards : 0)))
        - 0.5 * step;
    
    if (found_range) {
      lower = max((double) lower, lowest_allowed);
      upper = min((double) upper, highest_allowed);
    }
    else {
      lower = lowest_allowed;
      upper = highest_allowed;
    }
    lower = step * (lower / step);
    upper = step * (upper / step);
  }

  /* initialize threshold tables using strategy for most negative deck */

  fprintf(stderr, "Calculating strategy for most negative deck\n");
  if (decks == 0) {
    d = make_deck(counting_method, lower - 0.5 * step, decks,
                  DUMMY, DUMMY, DUMMY);
    s = s_create(*d, dbs, soft17);
    FREE(d);
  }
  for (c = 2; c <= ACE; c++) {
    Total player_hand;
    Threshold *t;
    int i;

    if (decks > 0) {
      d = make_deck(counting_method, lower - 0.5 * step, decks,
                    DUMMY, DUMMY, c);
      s = s_create(*d, dbs, soft17);
      FREE(d);
    }
    for (i = 12; i <= 21; i++) {
      t = (Threshold *) calloc(1, sizeof(Threshold));
      player_hand = make_hard(i);
      t->type = hit(s, player_hand, c) ? POOR : RICH;
      hit_thresholds[t2i(player_hand)][c2i(c)] = t;
    }
    for (i = 17; i <= 21; i++) {
      t = (Threshold *) calloc(1, sizeof(Threshold));
      player_hand = make_soft(i);
      t->type = hit(s, player_hand, c) ? POOR : RICH;
      hit_thresholds[t2i(player_hand)][c2i(c)] = t;
    }
    for (i = 4; i <= 11; i++) {
      t = (Threshold *) calloc(1, sizeof(Threshold));
      player_hand = make_hard(i);
      t->type = double_down(s, player_hand, c) ? RICH : POOR;
      double_thresholds[t2i(player_hand)][c2i(c)] = t;
    }
    for (c2 = 2; c2 <= c; c2++) {
      if (decks > 0) {
        s_destroy(s);
        d = make_deck(counting_method, lower - 0.5 * step, decks, ACE, c, c2);
        s = s_create(*d, dbs, soft17);
        FREE(d);
      }
      t = (Threshold *) calloc(1, sizeof(Threshold));
      player_hand = sum(c2t(c2), ACE);
      t->type = double_down(s, player_hand, c) ? RICH : POOR;
      double_thresholds[t2i(player_hand)][c2i(c)] = t;
      if (c2 != c) {
        t = (Threshold *) calloc(1, sizeof(Threshold));
        player_hand = sum(c2t(c), ACE);
        t->type = double_down(s, player_hand, c2) ? RICH : POOR;
        double_thresholds[t2i(player_hand)][c2i(c2)] = t;
      }
      if (c == ACE) {
        t = (Threshold *) calloc(1, sizeof(Threshold));
        t->type = split(s, ACE, c2) ? RICH : POOR;
        split_thresholds[c2i(ACE)][c2i(c2)] = t;
      }
      else if (c == c2) {
        t = (Threshold *) calloc(1, sizeof(Threshold));
        t->type = split(s, c, ACE) ? RICH : POOR;
        split_thresholds[c2i(c)][c2i(ACE)] = t;
      } 
    }
    if (c != ACE)
      for (pair = 2; pair <= 10; pair++) {
        if (decks > 0) {
          s_destroy(s);
          d = make_deck(counting_method, lower - 0.5 * step, decks,
                        pair, pair, c);
          s = s_create(*d, dbs, soft17);
          FREE(d);
        }
        t = (Threshold *) calloc(1, sizeof(Threshold));
        t->type = split(s, pair, c) ? RICH : POOR;
        split_thresholds[c2i(pair)][c2i(c)] = t;
      }
    if (decks > 0)
      s_destroy(s);
  }
  if (decks == 0)
    s_destroy(s);

  /* calculate strategy changes for other counts */

  for (true_count = lower; true_count <= upper; true_count += step) {
    extern void update_threshold();

    fprintf(stderr, "Considering true count of %3d\n", true_count);
    if (decks == 0) {
      d = make_deck(counting_method, true_count + 0.5 * step, decks,
                    DUMMY, DUMMY, DUMMY);
      s = s_create(*d, dbs, soft17);
      FREE(d);
    }
    for (c = 2; c <= ACE; c++) {
      Total player_hand;
      Threshold *t;
      int i;

      if (decks > 0) {
        d = make_deck(counting_method, true_count + 0.5 * step, decks,
                      DUMMY, DUMMY, c);
        s = s_create(*d, dbs, soft17);
        FREE(d);
      }
      for (i = 12; i <= 21; i++) {
        player_hand = make_hard(i);
        t = hit_thresholds[t2i(player_hand)][c2i(c)];
        update_threshold(t, (! hit(s, player_hand, c)), true_count);
      }
      for (i = 17; i <= 21; i++) {
        player_hand = make_soft(i);
        t = hit_thresholds[t2i(player_hand)][c2i(c)];
        update_threshold(t, (! hit(s, player_hand, c)), true_count);
      }
      for (i = 4; i <= 11; i++) {
        player_hand = make_hard(i);
        t = double_thresholds[t2i(player_hand)][c2i(c)];
        update_threshold(t, double_down(s, player_hand, c), true_count);
      }
      for (c2 = 2; c2 <= c; c2++) {
        if (decks > 0) {
          s_destroy(s);
          d = make_deck(counting_method, true_count + 0.5 * step, decks,
                        ACE, c, c2);
          s = s_create(*d, dbs, soft17);
          FREE(d);
        }
        player_hand = sum(c2t(c2), ACE);
        t = double_thresholds[t2i(player_hand)][c2i(c)];
        update_threshold(t, double_down(s, player_hand, c), true_count);
        if (c2 != c) {
          player_hand = sum(c2t(c), ACE);
          t = double_thresholds[t2i(player_hand)][c2i(c2)];
          update_threshold(t, double_down(s, player_hand, c2), true_count);
        }
        if (c == ACE) {
          t = split_thresholds[c2i(ACE)][c2i(c2)];
          update_threshold(t, split(s, ACE, c2), true_count);
        }
        else if (c == c2) {
          t = split_thresholds[c2i(c)][c2i(ACE)];
          update_threshold(t, split(s, c, ACE), true_count);
        }       
      }
      if (c != ACE)
        for (pair = 2; pair <= 10; pair++) {
          if (decks > 0) {
            s_destroy(s);
            d = make_deck(counting_method, true_count + 0.5 * step, decks,
                          pair, pair, c);
            s = s_create(*d, dbs, soft17);
            FREE(d);
          }
          t = split_thresholds[c2i(pair)][c2i(c)];
          update_threshold(t, split(s, pair, c), true_count);
        }
      if (decks > 0)
        s_destroy(s);
    }
    if (decks == 0)
      s_destroy(s);
  }

  /* print out the results */

  /* print strategy header */
  printf("%21s%s", "", "Blackjack Count Strategy Adjustments\n\n");
  printf("%16s%s", "", "Cards:    2   3   4   5   6   7   8   9  10 Ace\n");
  printf("%16s%s", "", "Values:");
  for (c = 2; c <= ACE; c++)
    printf("%4d",  main_count[c]);
  printf("\n");
  if (found_side_count)
    printf("%27s%s%d\n", "", "Ace side count value:  ", ace_side_value);
  printf("\n");
  {
    int length;

    if (decks == 0)
      length = 60;
    else if (decks == 1)
      length = 53;
    else
      length = 54;

    if (dbs)
      length = length - 3;
    if (soft17)
      length = length - 5;
    
    printf("%*s", (79 - length) / 2, "");
    if (decks > 0)
      printf("%d deck%s, ", decks, ((decks > 1) ? "s" : ""));
    else
      printf("Infinite deck, ");
    printf("%s%s", (dbs ? "" : "no "), "double on splits, dealer ");
    printf("%s%s", (soft17 ? "hits " : "stands on "), "soft 17\n\n\n");
  }
  {
    extern void print_threshold();
    Total player_hand;
    int i;

    /* hit/stand strategy */

    printf("%30s%s", "", "Hit/Stand Strategy\n\n");
    printf("%32s%s", "", "Dealer's Upcard\n\n");
    printf("Player's Total%6d%6d%6d%6d%6d%6d%6d%6d%6d%7s\n\n",
           2, 3, 4, 5, 6, 7, 8, 9, 10, "Ace");

    for (i = 21; i >= 12; i--) {
      player_hand = make_hard(i);
      printf("   Hard %2d        ", i);
      for (c = 2; c <= ACE; c++)
        print_threshold(hit_thresholds[t2i(player_hand)][c2i(c)],
                        "Hit", "...");
      printf("\n");
    }
    printf("\n");
    for (i = 21; i >= 17; i--) {
      player_hand = make_soft(i);
      printf("   Soft %2d        ", i);
      for (c = 2; c <= ACE; c++)
        print_threshold(hit_thresholds[t2i(player_hand)][c2i(c)],
                        "Hit", "...");
      printf("\n");
    }
    printf("\n\n");

    /* double down strategy */

    printf("%29s%s", "", "Double Down Strategy\n\n");
    printf("%32s%s", "", "Dealer's Upcard\n\n");
    printf("Player's Total%6d%6d%6d%6d%6d%6d%6d%6d%6d%7s\n\n",
         2, 3, 4, 5, 6, 7, 8, 9, 10, "Ace");
    
    for (i = 11; i >= 4; i--) {
      player_hand = make_hard(i);
      printf("   Hard %2d        ", i);
      for (c = 2; c <= ACE; c++)
        print_threshold(double_thresholds[t2i(player_hand)][c2i(c)],
                        "...", "dbl");
      printf("\n");
    }
    printf("\n");
    for (i = 20; i >= 13; i--) {
      player_hand = make_soft(i);
      printf("   Soft %2d        ", i);
      for (c = 2; c <= ACE; c++)
        print_threshold(double_thresholds[t2i(player_hand)][c2i(c)],
                        "...", "dbl");
      printf("\n");
    }
    printf("\n\n");

    /* pair split strategy */

    printf("%30s%s", "", "Pair Split Strategy\n\n");
    printf("%32s%s", "", "Dealer's Upcard\n\n");
    printf("Player's Pair %6d%6d%6d%6d%6d%6d%6d%6d%6d%7s\n\n",
         2, 3, 4, 5, 6, 7, 8, 9, 10, "Ace");
    
    for (pair = ACE; pair >= 2; pair--) {
      if (pair == ACE)
        printf("   Ace,Ace        ");
      else
        printf("   %3d,%-3d        ", pair, pair);
      for (c = 2; c <= ACE; c++)
        print_threshold(split_thresholds[c2i(pair)][c2i(c)],
                        "...", "split");
      printf("\n");
    }
  }
}

/* parse_int: returns the integer whose printed form is the string s. */
int parse_int(s)
     char *s;
{
  int answer;
  char next_char = '\0';

  if ((sscanf(s, "%d %c", &answer, &next_char) > 0) && (next_char == '\0'))
    return answer;
  else {
    fprintf(stderr, "not an integer -- %s\n", s);
    exit(1);
  }
}

/* update threshold: change strategy threshold value, if appropriate,
   based on information for current count.  If strategy changes at more than
   two counts, remember only the changes for the two counts closest to zero. */

void update_threshold(t, new_action_rich, true_count)
     Threshold *t;
     boolean new_action_rich; /* true if player should stand/double/split */
     int true_count;
{
  if (new_action_rich)
    if (t->type == POOR) {
      t->type = NORMAL;
      t->change_rich = true_count;
    }
    else if ((t->type == REVERSE) ||
             (t->type == RANGE) && (t->change_rich <= abs(true_count))) {
      t->type = REVRANGE;
      t->change_rich = true_count;
    }
    else ; /* no change in strategy */
  else if (t->type == RICH) {
    t->type = REVERSE;
    t->change_poor = true_count;
  }
  else if ((t->type == NORMAL) ||
           (t->type == REVRANGE) && (t->change_poor <= abs(true_count))) {
      t->type = RANGE;
      t->change_poor = true_count;
    }
  else ; /* no change in strategy */
}

/* print threshold: prints a string representation
   of a threshold on the standard output */

void print_threshold(t, poor_symbol, rich_symbol)
     Threshold *t;
     char *poor_symbol, *rich_symbol;
{
  switch (t->type) {
  case POOR:
    printf("%-6s", poor_symbol);
    break;
  case RICH:
    printf("%-6s", rich_symbol);
    break;
  case NORMAL:
    printf("%3d   ", t->change_rich);
    break;
  case REVERSE:
    printf("%3d*  ", t->change_poor);
    break;
/* uncomment the next six lines to print data for ranges */
/*  case RANGE:
    printf("%-d/%-d ", t->change_rich, t->change_poor);
    break;
  case REVRANGE:
    printf("%-d^%-d ", t->change_poor, t->change_rich);
    break; */
  case RANGE:
  case REVRANGE:
    if ((abs(t->change_rich) < abs(t->change_poor)) ||
        (t->change_rich = abs(t->change_poor)))
      printf("%3d   ", t->change_rich);
    else printf("%3d*  ", t->change_poor);
    break;
  }
}

void error_usage()
{
  fprintf(stderr, "usage: bj-strat v2 v3 v4 v5 v6 v7 v8 v9 v10 vAce ");
  fprintf(stderr, "[-dh][-n decks][-r low high]\n");
  exit(1);
}

void error(message)
     char *message;
{
  fprintf(stderr, "error: %s\n", message);
  exit(1);
}

1
Hosted by www.Geocities.ws