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 ", <, >, and & 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