Computing Discard Decision Tables

The following C program was used to determine the pone/dealer discard decision tables. This is a DOS program, intended to be run from a command-line prompt.

[In the HTML source, symbols &quot;, &lt;, &gt;, and &amp; replace ", <, >, and &, respectively. Use the copy text (rather than view source) function of your browser to extract the source code.]

//----------------------------------------------------------------------------
// DUMP.C determines 91-entry table of dealer/pone average crib scores by
// discard AA-KK and also 1820-entry frequency tables for dealer/pone held
// cards AAAA-KKKK.  Run program with no argument passed for a day or so until
// level 4 stabilizes, then rerun with any passed argument to dump C tables.
//
// Compile/link:  CL DUMP.C  (default small model)
//
// Summary of 6-card hands by rank needed to understand calculations here:
//
//   Types                 Count                 Weight        Total
//   ------                -----                 ------     --------
//   111111    (13 6) * 1 = 1716     4*4*4*4*4*4 = 4096      7028736
//   11112     (13 5) * 5 = 6435     4*4*4*4*6   = 1536      9884160
//   1122      (13 4) * 6 = 4290     4*4*6*6     =  576      2471040
//   1113      (13 4) * 4 = 2860     4*4*4*4     =  256       732160
//   114       (13 3) * 3 =  858     4*4*1       =   16        13728
//   123       (13 3) * 6 = 1716     4*6*4       =   96       164736
//   222       (13 3) * 1 =  286     6*6*6       =  216        61776
//   24        (13 2) * 2 =  156     6*1         =    6          936
//   33        (13 2) * 1 =   78     4*4         =   16         1248
//   15        (13 2) * 2 =  156     4*0         =    0
//   6         (13 1) * 1 =   13     0           =    0
//                         -----                            --------
//                         18564                            20358520 = (52 6)
//
// Similar charts are computed for (51 6), ..., (45 6) possible hands when
// up to 7 specific cards are known to be unavailable.
//
// In the calculations, flushes are ignored and discard decisions are based
// solely on average hand score plus or minus expected crib score.
//
// Program starts with crib tables zeroed.  That is, first pass makes discard
// decisions based only on best hand scores.  Successive iterations use tables
// output from previous iteration.  The tables converge to a point where the
// expected crib average as pone is the same as that expected as dealer (about
// 4.8 points).
//----------------------------------------------------------------------------

#include <stdio.h>
#include <malloc.h>

#define UCHAR unsigned char
#define UINT  unsigned int
#define ULONG unsigned long

#define LOBYTE(x) ((x) & 0xFF)
#define HIBYTE(x) LOBYTE((x) >> 8)
#define LOWORD(x) ((x) & 0xFFFF)
#define HIWORD(x) LOWORD((x) >> 16)

#define CUT_OFF  400                // determines when levels change
#define DAMP_FAC 0.50               // change dampening factor

char Cards[] = "A23456789TJQK";

int Wgts[]     = {0,0,0,0,0,0,0,0,  // 0 cards available
                  1,1,0,0,0,0,0,0,
                  1,2,1,0,0,0,0,0,
                  1,3,3,1,0,0,0,0,
                  1,4,6,4,1,0,0,0}; // 4 cards available

int QkWts2[] = {0,1,3,6, 10,15, 21, 28, 36, 45, 55,  66,  78};
int QkWts1[] = {0,1,4,10,20,35, 56, 84,120,165,220, 286, 364};
int QkWts0[] = {0,1,5,15,35,70,126,210,330,495,715,1001,1365};

ULONG LevSum[] = {20358520L, 18009460L, 15890700L, 13983816L,
                  12271512L, 10737573L,  9366819L,  8145060L};

int Level = 0,                    // increases to 4 in main()
    Iter = 0,                     // reset each Level then increases
    WantFreq = 0;                 // final flag for LoadTot()

double TotChg = 0, MaxChg = 0,    // set by Eval()
       PnAve = 0, DlAve = 0;      // set by SetAves()

int    Hash[256];                     // hash table for next
ULONG  RankFreq[13];
ULONG  DlHnTot[1820], PnHnTot[1820];  // weighted totals for AAAA-KKKK
ULONG  DlTot[91], PnTot[91];          // weighted totals for AA-KK
double DlTbl[91], PnTbl[91];          // average crib scores for AA-KK
double DlNew[91], PnNew[91];          // new values for previous

UCHAR Pts[23660];  // scores of all 4-card hands plus cut by rank
UINT far *Dscd;    // 18564 discards for all 6-card hands by rank

//----------------------------------------------------------------------------
// Compute tables for use in cribbage discarding decisions.  Tables give
// expected crib scores for each of 91 possible two-card combinations by rank
// (AA-KK) as dealer and as pone.
//----------------------------------------------------------------------------
int main(int Argc, char *Argv[])
  {int n;

   printf("Calculates cribbage discard/frequency tables--\
pass any arg later for C dump\n");

   // allocate far memory for Dscd array
   if ((Dscd = (UINT far *)_fmalloc(sizeof(UINT) * 18564)) == NULL)
     {printf("Not enough memory\n"); exit(1);}

   // one-time load of quarter point scores for all 4-card hands plus cut
   LoadPts();

   // clear state info, but override if load succeeds
   Level = 0; Iter = 0;
   for (n = 0; n < 91; n++) PnTbl[n] = DlTbl[n] = PnNew[n] = DlNew[n] = 0;
   Dump(LoadBin());

   // check for passed arg to dump data and exit
   if (Argc > 1)
     {WantFreq = 1;
      LoadDscd();
      for (n = 0; n < 1820; n++) PnHnTot[n] = DlHnTot[n] = 0L;
      LoadTot(0, 0, 0, 0, 0, 0, 0);
      LastDump();
      exit(0);
     }

   // keypress exits internally
   while (1)
     {LoadDscd();
      EvalAll();
      Dump(UpdtTbls());
      DumpBin();

      if (MaxChg < (double)(CUT_OFF >> Level) / 1000 && Level < 4)
         (++Level, Iter = 0);
      ++Iter;
     }

   return n;
  }

//----------------------------------------------------------------------------
// Dump PnTbl/DlTbl in hex for use in C program.
//----------------------------------------------------------------------------
int LastDump()
  {int n, m;
   UINT u, v, w, x, z;
   char Buf[128];
   ULONG Mx, Fac, PnSum = 0L, DlSum = 0L;
   FILE *fout;

   if ((fout = fopen("DUMP.TXT","a")) == NULL)
     {printf("\nCan't open DUMP.TXT for output\n"); exit(1);}

   // dump to disk and console
   sprintf(Buf,
     "\nCrib averages for discards AA-KK: dealer/pone is hi/lo\n    {\n    ");
   fprintf(fout, Buf); printf(Buf);

   u = 0;
   for (n = 1; n < 14; n++)
     {
      for (m = 1; m <= n; m++)
        {v = (int)(DlTbl[u] * 16 + .5) << 8 |
             (int)(PnTbl[u] * 16 + .5);
         if (m == 11) {fprintf(fout, "\n      "); printf("\n      ");}
         sprintf(Buf, "0x%04X%c", v, u == 90 ? ' ' : ',');
         fprintf(fout, Buf); printf(Buf);
         u++;
        }
      fprintf(fout, "\n    "); printf("\n    ");
     }
   fprintf(fout, "}\n"); printf("}\n");

   // dump to disk and console
   sprintf(Buf,
     "\nCrib averages less 2 in 1/32ths: dealer/pone is hi/lo\n    {\n    ");
   fprintf(fout, Buf); printf(Buf);

   u = 0;
   for (n = 1; n < 14; n++)
     {
      for (m = 1; m <= n; m++)
        {v = (int)(DlTbl[u] * 32 - 64 + .5) << 8 |
             (int)(PnTbl[u] * 32 - 64 + .5);
         if (m == 11) {fprintf(fout, "\n      "); printf("\n      ");}
         sprintf(Buf, "0x%04X%c", v, u == 90 ? ' ' : ',');
         fprintf(fout, Buf); printf(Buf);
         u++;
        }
      fprintf(fout, "\n    "); printf("\n    ");
     }
   fprintf(fout, "}\n"); printf("}\n");

   // dump to disk and console
   sprintf(Buf,
     "\nCrib decimal averages when pone discards AA-KK:\n    {\n    ");
   fprintf(fout, Buf); printf(Buf);

   u = 0;
   for (n = 1; n < 14; n++)
     {
      for (m = 1; m <= n; m++)
        {v = (int)(PnTbl[u] * 100 + .5);
         sprintf(Buf, "%4.2f,", (float)v / 100);
         fprintf(fout, Buf); printf(Buf);
         u++;
        }
      fprintf(fout, "\n    "); printf("\n    ");
     }
   fprintf(fout, "}\n"); printf("}\n");

   // dump to disk and console
   sprintf(Buf,
     "\nCrib decimal averages when dealer discards AA-KK:\n    {\n    ");
   fprintf(fout, Buf); printf(Buf);

   u = 0;
   for (n = 1; n < 14; n++)
     {
      for (m = 1; m <= n; m++)
        {v = (int)(DlTbl[u] * 100 + .5);
         sprintf(Buf, "%4.2f,", (float)v / 100);
         fprintf(fout, Buf); printf(Buf);
         u++;
        }
      fprintf(fout, "\n    "); printf("\n    ");
     }
   fprintf(fout, "}\n"); printf("}\n");

   // scale arrays so max element is FF7F
   Mx = 1L;
   for (n = 0; n < 1820; n++)
     {if (PnHnTot[n] > Mx) Mx = PnHnTot[n];
      if (DlHnTot[n] > Mx) Mx = DlHnTot[n];
     }
   Fac = (ULONG)0xFF800000 / Mx;
   for (n = 0; n < 1820; n++)
     {z = HIWORD(PnHnTot[n] * Fac);
      PnSum += z;
      PnHnTot[n] = z;
      z = HIWORD(DlHnTot[n] * Fac);
      DlSum += z;
      DlHnTot[n] = z;
     }

   sprintf(Buf, "\nMaximum and scale factor: 0x%lX 0x%lX\n", Mx, Fac);
   fprintf(fout, Buf); printf(Buf);

   sprintf(Buf,
     "\nPone frequency for held AAAA-KKKK: Sum = 0x%lX\n    {\n    ", PnSum);
   fprintf(fout, Buf); printf(Buf);

   for (n = 0; n < 1820; n++)
     {z = PnHnTot[n];
      sprintf(Buf, "0x%04X%c", z, n == 1819 ? ' ' : ',');
      fprintf(fout, Buf); printf(Buf);
      if ((n+1) % 10 == 0) (fprintf(fout, "\n    "), printf("\n    "));
     }
   fprintf(fout, "}\n"); printf("}\n");

   // count and display pone frequencies
   for (n = 0; n < 256; n++) Hash[n] = 0;
   for (n = 0; n < 1820; n++)
     {z = PnHnTot[n];
      z = HIBYTE(z) + ((z & 0x80) ? 1 : 0);
      ++Hash[z];
     }
   sprintf(Buf, "\nPone frequency distribution rounded hi bytes (0-255):\n");
   fprintf(fout, Buf); printf(Buf);
   n = m = z = 0;
   for (u = 0; u < 16; u++)
     {for (v = 0; v < 16; v++)
        {sprintf(Buf, "%3d ", Hash[n]);
         fprintf(fout, Buf); printf(Buf);
         m += Hash[n]; z += n*Hash[n]; n++;
        }
      sprintf(Buf, " %4d %5d\n", m, z);
      fprintf(fout, Buf); printf(Buf);
     }

   // find percentage hands containing specific ranks
   sprintf(Buf, "\nPercent pone hands containing each rank:\n");
   fprintf(fout, Buf); printf(Buf);

   for (n = 0; n < 13; n++) RankFreq[n] = 0L;

   n = 0;
   for (u = 1; u < 14; u++)
   for (v = 1; v <= u;  v++)
   for (w = 1; w <= v;  w++)
   for (x = 1; x <= w;  x++)
     {z = PnHnTot[n];
      for (m = 1; m < 14; m++)
        if (m == u || m == v || m == w || m == x) RankFreq[m-1] += z;
      n++;
     }

   for (n = 0; n < 13; n++)
     {z = 1000 * RankFreq[n] / PnSum;
      sprintf(Buf, "%4.1f ", (float)z/10);
      fprintf(fout, Buf); printf(Buf);
     }
   fprintf(fout, "\n"); printf("\n");

   sprintf(Buf, "\nPone 3-card combos with 4 or fewer non-zero weight 4th cards:\n");
   fprintf(fout, Buf); printf(Buf);

   n = 0;
   for (u = 1; u < 14; u++)
   for (v = 1; v <= u;  v++)
   for (w = 1; w <= v;  w++)
     {z = Count(PnHnTot, u, v, w);
      if (z != 0xFFFF)
        {sprintf(Buf, "%1X%1X%1X-%4X ", u,v,w,z);
         fprintf(fout, Buf); printf(Buf);
         if ((++n) % 8 == 0) (fprintf(fout, "\n"), printf("\n"));
        }
     }
   fprintf(fout, "\n"); printf("\n");

   // prepare for sort by putting value high and hand low
   n = 0;
   for (u = 1; u < 14; u++)
   for (v = 1; v <= u;  v++)
   for (w = 1; w <= v;  w++)
   for (x = 1; x <= w;  x++)
     {PnHnTot[n] = PnHnTot[n] << 16 | u << 12 | v << 8 | w << 4 | x;
      n++;
     }
   Sort(PnHnTot, 1820);

   sprintf(Buf, "\nPone hands sorted by frequency:\n    {\n    ");
   fprintf(fout, Buf); printf(Buf);

   for (n = 0; n < 1820; n++)
     {z = LOWORD(PnHnTot[n]);
      sprintf(Buf, "0x%04X%c", z, n == 1819 ? ' ' : ',');
      fprintf(fout, Buf); printf(Buf);
      if ((n+1) % 10 == 0) (fprintf(fout, "\n    "), printf("\n    "));
     }
   fprintf(fout, "}\n"); printf("}\n");

   sprintf(Buf,
     "\nDealer frequency for held AAAA-KKKK: Sum = 0x%lX\n    {\n    ", DlSum);
   fprintf(fout, Buf); printf(Buf);

   for (n = 0; n < 1820; n++)
     {z = DlHnTot[n];
      sprintf(Buf, "0x%04X%c", z, n == 1819 ? ' ' : ',');
      fprintf(fout, Buf); printf(Buf);
      if ((n+1) % 10 == 0) (fprintf(fout, "\n    "), printf("\n    "));
     }
   fprintf(fout, "}\n"); printf("}\n");

   // count and display dealer frequencies
   for (n = 0; n < 256; n++) Hash[n] = 0;
   for (n = 0; n < 1820; n++)
     {z = DlHnTot[n];
      z = HIBYTE(z) + ((z & 0x80) ? 1 : 0);
      ++Hash[z];
     }
   sprintf(Buf, "\nDealer frequency distribution rounded hi bytes (0-255):\n");
   fprintf(fout, Buf); printf(Buf);
   n = m = z = 0;
   for (u = 0; u < 16; u++)
   for (u = 0; u < 16; u++)
     {for (v = 0; v < 16; v++)
        {sprintf(Buf, "%3d ", Hash[n]);
         fprintf(fout, Buf); printf(Buf);
         m += Hash[n]; z += n*Hash[n]; n++;
        }
      sprintf(Buf, " %4d %5d\n", m, z);
      fprintf(fout, Buf); printf(Buf);
     }

   // find percentage hands containing specific ranks
   sprintf(Buf, "\nPercent dealer hands containing each rank:\n");
   fprintf(fout, Buf); printf(Buf);

   for (n = 0; n < 13; n++) RankFreq[n] = 0L;

   n = 0;
   for (u = 1; u < 14; u++)
   for (v = 1; v <= u;  v++)
   for (w = 1; w <= v;  w++)
   for (x = 1; x <= w;  x++)
     {z = DlHnTot[n];
      for (m = 1; m < 14; m++)
        if (m == u || m == v || m == w || m == x) RankFreq[m-1] += z;
      n++;
     }

   for (n = 0; n < 13; n++)
     {z = 1000 * RankFreq[n] / DlSum;
      sprintf(Buf, "%4.1f ", (float)z/10);
      fprintf(fout, Buf); printf(Buf);
     }
   fprintf(fout, "\n"); printf("\n");

   sprintf(Buf, "\nDealer 3-card combos with 4 or fewer non-zero weight 4th cards:\n");
   fprintf(fout, Buf); printf(Buf);

   n = 0;
   for (u = 1; u < 14; u++)
   for (v = 1; v <= u;  v++)
   for (w = 1; w <= v;  w++)
     {z = Count(DlHnTot, u, v, w);
      if (z != 0xFFFF)
        {sprintf(Buf, "%1X%1X%1X-%4X ", u,v,w,z);
         fprintf(fout, Buf); printf(Buf);
         if ((++n) % 8 == 0) (fprintf(fout, "\n"), printf("\n"));
        }
     }
   fprintf(fout, "\n"); printf("\n");

   // prepare for sort by putting value high and hand low
   n = 0;
   for (u = 1; u < 14; u++)
   for (v = 1; v <= u;  v++)
   for (w = 1; w <= v;  w++)
   for (x = 1; x <= w;  x++)
     {DlHnTot[n] = DlHnTot[n] << 16 | u << 12 | v << 8 | w << 4 | x;
      n++;
     }
   Sort(DlHnTot, 1820);

   sprintf(Buf, "\nDealer hands sorted by frequency:\n    {\n    ");
   fprintf(fout, Buf); printf(Buf);

   for (n = 0; n < 1820; n++)
     {z = LOWORD(DlHnTot[n]);
      sprintf(Buf, "0x%04X%c", z, n == 1819 ? ' ' : ',');
      fprintf(fout, Buf); printf(Buf);
      if ((n+1) % 10 == 0) (fprintf(fout, "\n    "), printf("\n    "));
     }
   fprintf(fout, "}\n"); printf("}\n");

   fclose(fout);

   return 0;
  }

//----------------------------------------------------------------------------
// Sort double word array highest to lowest in high word, but lowest to
// highest in low word.
//----------------------------------------------------------------------------
int Sort(ULONG Ary[], int Length)
  {int n, m;
   ULONG Temp;

   for (n = 0; n < Length-1; n++)
   for (m = n+1; m < Length; m++)
   if (HIWORD(Ary[n]) < HIWORD(Ary[m]) ||
      (HIWORD(Ary[n]) == HIWORD(Ary[m]) && LOWORD(Ary[n]) > LOWORD(Ary[m])))
     {Temp = Ary[n]; Ary[n] = Ary[m]; Ary[m] = Temp;
     }

   return 0;
  }

//----------------------------------------------------------------------------
// Count ranks (of 13) that would fill out hand with non-zero frequency.
// Assume r1 >= r2 >= r3.
//----------------------------------------------------------------------------
int Count(ULONG Ary[], int r1, int r2, int r3)
  {int n, Cnt = 0, Ndx, Ranks = 0;

   for (n = 1; n < 14; n++)
     {if      (n >= r1) Ndx = QkWts0[ n-1]+QkWts1[r1-1]+QkWts2[r2-1]+r3-1;
      else if (n >= r2) Ndx = QkWts0[r1-1]+QkWts1[ n-1]+QkWts2[r2-1]+r3-1;
      else if (n >= r3) Ndx = QkWts0[r1-1]+QkWts1[r2-1]+QkWts2[ n-1]+r3-1;
      else              Ndx = QkWts0[r1-1]+QkWts1[r2-1]+QkWts2[r3-1]+ n-1;
      if (Ary[Ndx]) (++Cnt, Ranks = Ranks << 4 | n);
     }

   return Cnt > 4 ? -1 : Ranks;
  }

//----------------------------------------------------------------------------
// Load DlTbl/PnTbl arrays from binary file.
//----------------------------------------------------------------------------
int LoadBin()
  {FILE *fin;

   if ((fin = fopen("DUMP.BIN","rb")) == NULL)
     {printf("Can't open DUMP.BIN for input--using startup defaults\n");
      return -2;
     }

   fread(&Level, sizeof(int),     1, fin);
   fread(&Iter,  sizeof(int),     1, fin);
   fread(DlTbl,  sizeof(double), 91, fin);
   fread(PnTbl,  sizeof(double), 91, fin);
   fread(DlNew,  sizeof(double), 91, fin);
   fread(PnNew,  sizeof(double), 91, fin);

   fclose(fin);
   return -1;
  }

//----------------------------------------------------------------------------
// Dump state to disk as binary output.
//----------------------------------------------------------------------------
int DumpBin()
  {FILE *fout;

   if ((fout = fopen("DUMP.BIN","wb")) == NULL)
     {printf("\nCan't open DUMP.BIN for output\n"); exit(1);}

   fwrite(&Level, sizeof(int),     1, fout);
   fwrite(&Iter,  sizeof(int),     1, fout);
   fwrite(DlTbl,  sizeof(double), 91, fout);
   fwrite(PnTbl,  sizeof(double), 91, fout);
   fwrite(DlNew,  sizeof(double), 91, fout);
   fwrite(PnNew,  sizeof(double), 91, fout);

   fclose(fout);
   return 0;
  }

//----------------------------------------------------------------------------
// Load Pts array with scores of all 4-card hands plus cuts.  Called once.
//----------------------------------------------------------------------------
int LoadPts()
  {int u, v, w, x, y, n = 0;
   char Hn[5];

   // illegal hands (5-of-a-kind) are ignored later
   for (u = 1; (Hn[0] = u) < 14; u++)
   for (v = 1; (Hn[1] = v) <= u; v++)
   for (w = 1; (Hn[2] = w) <= v; w++)
   for (x = 1; (Hn[3] = x) <= w; x++)
   for (y = 1; (Hn[4] = y) < 14; y++)
     {Pts[n] = Score5(Hn);
      if (Pts[n] != Quick5(Hn)) {printf("LoadPts error\n"); exit(1);}
      ++n;
     }
   if (n != 23660) {printf("LoadPts index error: %d\n", n); exit(1);}
   return n;
  }

//----------------------------------------------------------------------------
// Dump PnTbl/DlTbl rounded to 1/1000ths to log and console.
//----------------------------------------------------------------------------
int Dump(int DispChg)
  {int u, v, n, m;
   char Buf[128];
   FILE *fout;

   if ((fout = fopen("DUMP.TXT","a")) == NULL)
     {printf("\nCan't open DUMP.TXT for output\n"); exit(1);}

   // dump to disk and console
   if (DispChg < 0)
     {sprintf(Buf, "\nLevel: %d  Iter: %d  %s...\n",
         Level, Iter, DispChg == -2 ? "Start" : "Restart");
      fprintf(fout, Buf); printf(Buf);
     }
   else
     {sprintf(Buf,
        "\nLevel: %d  Iter: %d  TotChg: %.2f  MaxChg: %.2f  DispChg: %d\n",
          Level, Iter, TotChg * 1000, MaxChg * 1000, DispChg);
      fprintf(fout, Buf); printf(Buf);
      sprintf(Buf,"PnAve: %.5f  DlAve: %.5f\n", PnAve, DlAve);
      fprintf(fout, Buf); printf(Buf);
     }

   u = 0; v = 90;
   for (n = 1; n < 14; n++)
     {
      for (m = 1; m <= n; m++)
        {sprintf(Buf, "%04d ", (int)(DlTbl[u++] * 1000 + .5));
         fprintf(fout, Buf); printf(Buf);
        }
      fprintf(fout, "     "); printf("     ");
      for (m = 14 - n; m; m--)
        {sprintf(Buf, "%04d ", (int)(PnTbl[v--] * 1000 + .5));
         fprintf(fout, Buf); printf(Buf);
        }
      fprintf(fout, "\n"); printf("\n");
     }

   fclose(fout);

   return 0;
  }

//----------------------------------------------------------------------------
// Update PnTbl/DlTbl from PnNew/DlNew and return count of display changes.
//----------------------------------------------------------------------------
int UpdtTbls()
  {int n, DispChg = 0;

   for (n = 0; n < 91; n++)
     {
      // update DispChg to count display changes--see Dump()
      if ((int)(PnTbl[n] * 1000 + .5) != (int)(PnNew[n] * 1000 + .5)) ++DispChg;
      if ((int)(DlTbl[n] * 1000 + .5) != (int)(DlNew[n] * 1000 + .5)) ++DispChg;

      PnTbl[n] = PnNew[n];
      DlTbl[n] = DlNew[n];
      PnNew[n] = DlNew[n] = 0;
     }

   UpdtAves();
   return DispChg;
  }

//----------------------------------------------------------------------------
// Update global averages.
//----------------------------------------------------------------------------
int UpdtAves()
  {int n;

   // reload totals
   LoadTot(0, 0, 0, 0, 0, 0, 0);

   PnAve = DlAve = 0;
   for (n = 0; n < 91; n++)
     {PnAve += PnTbl[n] * DlTot[n];
      DlAve += DlTbl[n] * PnTot[n];
     }
   PnAve /= LevSum[0];
   DlAve /= LevSum[0];

   return 1;
  }

//----------------------------------------------------------------------------
// Return quarter point score of 4-card hand plus cut from pre-computed array.
// This is about 3x faster than Score5.
//----------------------------------------------------------------------------
int Quick5(char Hand[])
  {int n, m;
   char Hn[5], Card;

   // copy hand then sort first four cards
   for (n = 0; n < 5; n++) Hn[n] = Hand[n] - 1;
   for (n = 0; n < 3; n++) for (m = n+1; m < 4; m++)
      if (Hn[n] < Hn[m]) (Card = Hn[n], Hn[n] = Hn[m], Hn[m] = Card);

   // construct index into pre-computed array using fixed weights
   n = 13 * (QkWts0[Hn[0]] + QkWts1[Hn[1]] + QkWts2[Hn[2]] + Hn[3]) + Hn[4];

   if (n < 0 || n >= 23660) {printf("Quick5 index error\n"); exit(1);}
   return Pts[n];
  }

//----------------------------------------------------------------------------
// From 6 ranks sorted high to low and discard index 0-90, return corresponding
// 4-card index 0-1819.  Called only by final hand frequency dump.
//----------------------------------------------------------------------------
int GetNdx4(int Cd1, int Cd2, int Cd3, int Cd4, int Cd5, int Cd6, int Ndx)
  {int n, m, Hi, Lo;
   char Hn[6], Cd[4];

   // copy sorted cards to array, one-based
   Hn[0] = Cd1; Hn[1] = Cd2; Hn[2] = Cd3;
   Hn[3] = Cd4; Hn[4] = Cd5; Hn[5] = Cd6;

   // convert discard index to Hi/Lo cards, one-based
   for (Hi = 12; Hi >= 0; Hi--) if ((Lo = Ndx - QkWts2[Hi]) >= 0) break;
   ++Hi; ++Lo;

   if (Hi < 1 || Hi > 13 || Lo < 1 || Lo > 13 || Hi < Lo)
        {printf("GetNdx4 hi/lo error\n"); exit(1);}

   // kill Hi/Lo cards in array on first pass
   for (n = 0; n < 6; n++)
     {if (Hi == Hn[n]) Hi = Hn[n] = 0;
      if (Lo == Hn[n]) Lo = Hn[n] = 0;
     }

   // copy held 4 cards, zero-based
   m = 0;
   for (n = 0; n < 6; n++) if (Hn[n]) Cd[m++] = Hn[n] - 1;

   // construct index from four cards
   n = QkWts0[Cd[0]] + QkWts1[Cd[1]] + QkWts2[Cd[2]] + Cd[3];

   if (n < 0 || n >= 1820 || m != 4) {printf("GetNdx4 index error\n"); exit(1);}
   return n;
  }

//----------------------------------------------------------------------------
// Evaluate discards from all 6-card hands and save to Dscd array.
//----------------------------------------------------------------------------
int LoadDscd()
  {int u, v, w, x, y, z, n = 0, Ties = 0, Matches = 0;
   char Hn[6];

   // illegal hands (5-and 6-of-a-kind) are ignored later
   for (u = 1; (Hn[0] = u, u < 14); u++)
   for (v = 1; (Hn[1] = v, v <= u); v++)
   for (w = 1; (Hn[2] = w, w <= v); w++)
   for (x = 1; (Hn[3] = x, x <= w); x++)
   for (y = 1; (Hn[4] = y, y <= x); y++)
   for (z = 1; (Hn[5] = z, z <= y); z++)
     {Dscd[n] = PickDis(Hn, &Ties, &Matches);
      if (n % 1000 == 0) {printf("\rLoadDscd: %d", n); if (kbhit()) exit(0);}
      ++n;
     }
   printf("\rLoadDscd: %d  Matches: %d  Ties: %d\n", n, Matches, Ties);

   if (n != 18564) {printf("LoadDscd index error: %d\n", n); exit(1);}
   return Ties;
  }

//----------------------------------------------------------------------------
// Load PnTot/DlTot arrays with weights for discards to all 6-card hands out
// 52-card deck with up to 7 cards missing.  Supply dummy 0's as needed.
// This is innermost routine at level 4.
//----------------------------------------------------------------------------
int LoadTot(int Cd1, int Cd2, int Cd3, int Cd4, int Cd5, int Cd6, int Cd7)
  {int u, v, w, x, y, z, n, Wt, Ndx = 0, k = 0, Missing = 0, Ct[6], WtRow[16];
   long Sum = 0L;

   // clear sum arrays and initialize weight counts
   for (n = 0; n < 91; n++) DlTot[n] = PnTot[n] = 0L;
   for (n = 1; n < 14; n++) WtRow[n] = 33;
   for (n = 0; n < 6; n++)  Ct[n] = 32;

   if (Cd1) {++Missing; if (WtRow[Cd1] > 1) WtRow[Cd1] -= 8;};
   if (Cd2) {++Missing; if (WtRow[Cd2] > 1) WtRow[Cd2] -= 8;};
   if (Cd3) {++Missing; if (WtRow[Cd3] > 1) WtRow[Cd3] -= 8;};
   if (Cd4) {++Missing; if (WtRow[Cd4] > 1) WtRow[Cd4] -= 8;};
   if (Cd5) {++Missing; if (WtRow[Cd5] > 1) WtRow[Cd5] -= 8;};
   if (Cd6) {++Missing; if (WtRow[Cd6] > 1) WtRow[Cd6] -= 8;};
   if (Cd7) {++Missing; if (WtRow[Cd7] > 1) WtRow[Cd7] -= 8;};

   // update sum arrays
   for (u = 1; u < 14 ?          (Ct[k]   = WtRow[u])            | 1 : 0;
        u++)
   for (v = 1; v <= u ? (v < u ? (Ct[++k] = WtRow[v]) : ++Ct[k]) | 1 : 0;
        v++ < u ? (Ct[k--] = 32) : --Ct[k])
   for (w = 1; w <= v ? (w < v ? (Ct[++k] = WtRow[w]) : ++Ct[k]) | 1 : 0;
        w++ < v ? (Ct[k--] = 32) : --Ct[k])
   for (x = 1; x <= w ? (x < w ? (Ct[++k] = WtRow[x]) : ++Ct[k]) | 1 : 0;
        x++ < w ? (Ct[k--] = 32) : --Ct[k])
   for (y = 1; y <= x ? (y < x ? (Ct[++k] = WtRow[y]) : ++Ct[k]) | 1 : 0;
        y++ < x ? (Ct[k--] = 32) : --Ct[k])
   for (z = 1; z <= y ? (z < y ? (Ct[++k] = WtRow[z]) : ++Ct[k]) | 1 : 0;
        z++ < y ? (Ct[k--] = 32) : --Ct[k])
     {
      // weight is zero for illegal hands--also Wgts[32] is 1
      Wt = Wgts[Ct[0]] * Wgts[Ct[1]] * Wgts[Ct[2]] *
           Wgts[Ct[3]] * Wgts[Ct[4]] * Wgts[Ct[5]];

      // bump pone and dealer totals by weights
      n = Dscd[Ndx];
      PnTot[HIBYTE(n)] += Wt;
      DlTot[LOBYTE(n)] += Wt;
      if (WantFreq)  // late patch to get final hand frequencies
        {PnHnTot[GetNdx4(u, v, w, x, y, z, LOBYTE(n))] += Wt;
         DlHnTot[GetNdx4(u, v, w, x, y, z, HIBYTE(n))] += Wt;
        }
      Sum += Wt;
      ++Ndx;
     }

   if (Ndx != 18564) {printf("LoadTot Ndx error\n"); exit(1);}
   if (Sum != LevSum[Missing]) {printf("LoadTot Sum error\n"), exit(1);}

   return Ndx;
  }

//----------------------------------------------------------------------------
// Set all 91 PnNew/DlNew entries, updating global MaxChg/TotChg.  Note that
// at level 4, there will be about 28,500 LoadTot() calls inside LongEval().
// Could reduce to about 18,500 by doing all 91 evaluations in one pass.
//----------------------------------------------------------------------------
int EvalAll()
  {int u, v, n = 0;
   double Diff;

   MaxChg = TotChg = 0;

   // initial trial uses same PnTot/DlTot for all 91 evaluations
   if (Level == 0) LoadTot(0, 0, 0, 0, 0, 0, 0);
   for (u = 1; u < 14; u++)
     {if (Level == 1) LoadTot(u, 0, 0, 0, 0, 0, 0);
      for (v = 1; v <= u; v++)
        {if (Level == 2) LoadTot(u, v, 0, 0, 0, 0, 0);

         // evaluate then halve change to dampen oscillation afterward
         if (PnNew[n] == 0 && DlNew[n] == 0)
           {if (Level < 4) Eval(u, v, n);
            else           LongEval(u, v, n);

            PnNew[n] = PnTbl[n] + DAMP_FAC * (PnNew[n] - PnTbl[n]);
            DlNew[n] = DlTbl[n] + DAMP_FAC * (DlNew[n] - DlTbl[n]);
            if (Level > 3) DumpBin();
           }

         // update global MaxChg and TotChg
         Diff = PnNew[n] - PnTbl[n];
         if (Diff < 0) Diff = -Diff;
         TotChg += Diff;
         if (Diff > MaxChg) MaxChg = Diff;

         Diff = DlNew[n] - DlTbl[n];
         if (Diff < 0) Diff = -Diff;
         TotChg += Diff;
         if (Diff > MaxChg) MaxChg = Diff;

         printf(
 "\rNdx: %02d  Cds: %c%c  Dl: %.3f  Pn: %.3f  TotChg: %.2f  MaxChg: %.2f",
 n, Cards[u-1], Cards[v-1], DlNew[n], PnNew[n], TotChg * 1000, MaxChg * 1000);
         if (Level > 3) printf("\n");
         ++n;         if (kbhit()) exit(0);
        }
     }

   printf("\n");
   return 0;
  }

//----------------------------------------------------------------------------
// Set one PnNew/DlNew entry.
//----------------------------------------------------------------------------
int Eval(int Rank1, int Rank2, int Ndx)
  {int u, v, n, Cut, Wt;
   ULONG  DlSum, PnSum;
   double DlSm,  PnSm;
   char Hn[5];

   DlSm = PnSm = 0;
   Hn[2] = Rank1; Hn[3] = Rank2;
   for (Cut = 1; (Hn[4] = Cut, Cut < 14); Cut++)
     {
      // Level 3 uses 13 refined PnTot/DlTot for each evaluation
      if (Level == 3) LoadTot(Rank1, Rank2, Cut, 0, 0, 0, 0);

      DlSum = PnSum = 0L;
      n = 0;
      for (u = 1; (Hn[0] = u, u < 14); u++)
      for (v = 1; (Hn[1] = v, v <= u); v++)
        {Wt  = Quick5(Hn);
         PnSum += PnTot[n] * Wt;
         DlSum += DlTot[n] * Wt;
         ++n;
        }

      // PnSum and DlSum can reach billion, so switch to double
      Wt = 4 - (Cut == Rank1 ? 1 : 0) - (Cut == Rank2 ? 1 : 0);
      PnSm += (double)PnSum * Wt;
      DlSm += (double)DlSum * Wt;
     }

   // 50 is 52 cards less the 2 being evaluated
   PnNew[Ndx] = PnSm / 4 / (50 * LevSum[Level]);
   DlNew[Ndx] = DlSm / 4 / (50 * LevSum[Level]);

   return 0;
  }

//----------------------------------------------------------------------------
// Set one PnNew/DlNew entry--lengthy evaluation.
//----------------------------------------------------------------------------
int LongEval(int Rank1, int Rank2, int Ndx)
  {int u, v, w, x, y, z, n, Wt, m = 0, k = 0, Ct[6], PnMatch, DlMatch,
       Cut, i, j, t, QtWt, CutWt, PnCnt = 0, DlCnt = 0, OrCnt = 0;
   char Hn[5];
   ULONG PnWtSum = 0L, DlWtSum = 0L, DlSum, PnSum;
   double PnValSum = 0L, DlValSum = 0L, PnSm, DlSm;

   // initialize weight counts
   for (n = 0; n < 6; n++)  Ct[n] = 32;

   // update sum arrays
   for (u = 1; u < 14 ?          (Ct[k]   = 33)            | 1 : 0;
        u++)
   for (v = 1; v <= u ? (v < u ? (Ct[++k] = 33) : ++Ct[k]) | 1 : 0;
        v++ < u ? (Ct[k--] = 32) : --Ct[k])
   for (w = 1; w <= v ? (w < v ? (Ct[++k] = 33) : ++Ct[k]) | 1 : 0;
        w++ < v ? (Ct[k--] = 32) : --Ct[k])
   for (x = 1; x <= w ? (x < w ? (Ct[++k] = 33) : ++Ct[k]) | 1 : 0;
        x++ < w ? (Ct[k--] = 32) : --Ct[k])
   for (y = 1; y <= x ? (y < x ? (Ct[++k] = 33) : ++Ct[k]) | 1 : 0;
        y++ < x ? (Ct[k--] = 32) : --Ct[k])
   for (z = 1; z <= y ? (z < y ? (Ct[++k] = 33) : ++Ct[k]) | 1 : 0;
        z++ < y ? (Ct[k--] = 32) : --Ct[k])
     {
      // weight is zero for illegal hands--also Wgts[32] is 1
      Wt = Wgts[Ct[0]] * Wgts[Ct[1]] * Wgts[Ct[2]] *
           Wgts[Ct[3]] * Wgts[Ct[4]] * Wgts[Ct[5]];

      n = Dscd[m];
      DlMatch = (HIBYTE(n) == Ndx); PnMatch = (LOBYTE(n) == Ndx);

      if ((DlMatch || PnMatch) && Wt > 0)
        {
         DlSm = PnSm = 0;
         Hn[2] = Rank1; Hn[3] = Rank2;
         for (Cut = 1; (Hn[4] = Cut, Cut < 14); Cut++)
           {
            CutWt = 4 - (Cut == u ? 1 : 0) - (Cut == v ? 1 : 0)
                      - (Cut == w ? 1 : 0) - (Cut == x ? 1 : 0)
                      - (Cut == y ? 1 : 0) - (Cut == z ? 1 : 0);
            if (CutWt > 0)
              {LoadTot(u, v, w, x, y, z, Cut);

               DlSum = PnSum = 0L;
               t = 0;
               for (i = 1; (Hn[0] = i, i < 14); i++)
               for (j = 1; (Hn[1] = j, j <= i); j++)
                 {QtWt  = Quick5(Hn);
                  if (PnMatch) PnSum += PnTot[t] * QtWt;
                  if (DlMatch) DlSum += DlTot[t] * QtWt;
                  ++t;
                 }

               // PnSum and DlSum can get large, so switch to double
               if (PnMatch) PnSm += (double)PnSum * CutWt;
               if (DlMatch) DlSm += (double)DlSum * CutWt;
              }
           }

         // 46 is 52 cards less the 6 missing cards
         if (PnMatch)
           {PnSm = PnSm / 4 / (46 * LevSum[7]);
            PnValSum += PnSm * Wt;
            PnWtSum  += Wt;
            ++PnCnt;
           }
         if (DlMatch)
           {DlSm = DlSm / 4 / (46 * LevSum[7]);
            DlValSum += DlSm * Wt;
            DlWtSum  += Wt;
            ++DlCnt;
           }
         ++OrCnt;

         printf(
"\rNdx: %02d  Dscd: %c%c  Cards: %c%c%c%c%c%c  DlCnt: %d  PnCnt: %d  OrCnt: %d",
      Ndx, Cards[Rank1-1], Cards[Rank2-1],
      Cards[u-1], Cards[v-1], Cards[w-1], Cards[x-1], Cards[y-1], Cards[z-1],
      DlCnt, PnCnt, OrCnt);

         if (kbhit()) exit(0);
        }
      ++m;
     }

   PnNew[Ndx] = PnValSum / PnWtSum;
   DlNew[Ndx] = DlValSum / DlWtSum;

   if (m != 18564) {printf("LongEval index error\n"); exit(1);}

   return m;
  }

//----------------------------------------------------------------------------
// Return quarter point score of 5-card hand by rank.  Flushes ignored.
//----------------------------------------------------------------------------
int Score5(char Hand[])
  {int  n, m, Sum = 0, Tot = 0;
   char Cnt[16];

   // check 15-counts
   for (n = 0; n < 5; n++)
      Sum += Cnt[n] = (m = Hand[n] & 0x0F) < 10 ? m : 10;

   if (Sum == 15) Tot += 2;
   for (n = 0; n < 5; n++)
     {if (Sum - Cnt[n] == 15) Tot += 2;
      for (m = n+1; m < 5; m++)
        {if (Cnt[n] + Cnt[m] == 15) Tot += 2;
         if (Sum - Cnt[n] - Cnt[m] == 15) Tot += 2;
        }
      }

   // check pairs/trips/quads
   for (n = 0; n < 14; n++) Cnt[n] = 0;
   for (n = 0; n < 5; n++) Tot += Cnt[Hand[n] & 0x0F]++ << 1;

   // check runs
   Sum = 0; m = 1;
   for (n = 1; n < 14; n++)
     {if (Cnt[n]) (++Sum, m *= Cnt[n]);
      else if (Sum < 3) (Sum = 0, m = 1);
      else break;
     }
   if (Sum >= 3) Tot += Sum * m;

   // convert to quarter points with nobs adjustment
   Tot = (Tot << 2) + (((Hand[4] & 0x0F) == 11) ? 0 : Cnt[11]);

   // return Tot unless bad hand (5-of-a-kind)
   return Cnt[Hand[0] & 0x0F] == 5 ? 0 : Tot;
  }

//----------------------------------------------------------------------------
// Return quarter point total for first 4 of 6 cards over 46 cuts.  Illegal
// hands have more than 46 cuts, but are ignored later.
//----------------------------------------------------------------------------
int Score4(char Hand[])
  {int  n, Tot = 0, Wt[16];
   char Hn[5];

   // make temp copy of first 4 cards
   for (n = 0; n < 4; n++) Hn[n] = Hand[n];

   // count outstanding cards to Wt[] array
   for (n = 1; n < 14; n++) Wt[n] = 4;
   for (n = 0; n < 6; n++) --Wt[Hand[n] & 0x0F];

   // evaluate 13 ranks as cuts, weighed by Wt[]
   for (n = 1; (Hn[4] = n, n < 14); n++)
     if (Wt[n] > 0) Tot += Wt[n] * Quick5(Hn);

   return Tot;
  }

//----------------------------------------------------------------------------
// From 6-card hand, return dealer/pone discards in hi/lo bytes of return word.
// Discards are indexes 0 - 90.  Assume hand sorted high to low.
//----------------------------------------------------------------------------
int PickDis(char Hand[], int *Ties, int *Matches)
  {int n, m, k, DlPicks = 0, PnPicks = 0;
   double HnAve, DlComb, PnComb, DlMxAve = -128, PnMxAve = -128;
   char Hn[6];

   // loop through 15 possible hands, ignoring repetitions
   for (n = 0; n < 5; n++)   if (n == 0   || Hand[n] != Hand[n-1])
   for (m = n+1; m < 6; m++) if (m == n+1 || Hand[m] != Hand[m-1])
     {
      // copy hand, then swap discards to end positions
      for (k = 0; k < 6; k++) Hn[k] = Hand[k];
      k = Hn[m]; Hn[m] = Hn[5]; Hn[5] = k;
      k = Hn[n]; Hn[n] = Hn[4]; Hn[4] = k;

      // get discard index 0-90 from ranks 1-13
      k = ((k * (k - 1)) >> 1) + Hn[5] - 1;

      // get hand average from quarter point total for 46 cuts
      HnAve = Score4(Hn);
      HnAve /= (4 * 46);

      // get combined scores for normal evaluation at neutral risk
      DlComb = HnAve + DlTbl[k];
      PnComb = HnAve - PnTbl[k];

      // on ties, higher rank cards discarded since Hn[] sorted, but
      // due to double precision, ties only arise on first iteration
      if      (DlComb > DlMxAve) (DlMxAve = DlComb, DlPicks = k);
      else if (DlComb == DlMxAve) ++(*Ties);
      if      (PnComb > PnMxAve) (PnMxAve = PnComb, PnPicks = k);
      else if (PnComb == PnMxAve) ++(*Ties);
     }

   if (DlPicks == PnPicks) ++(*Matches);

   // pack indexes into return value
   return DlPicks << 8 | PnPicks;
  }

// End DUMP.C

Home | Top

1