/* * mec - match equity calculator for backgammon. calculate equity table * given match length, gammon rate and winning probabilities. * * Copyright (C) 1996 Claes Tornberg * Copyright (C) 2004-2013 the AUTHORS * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * * Modified for usage with GNUbg by Joern Thyssen : * * (1) make external entry for usage in GNUbg * (2) change "double" to "metentry", and typedef metentry to float * or double depending on MEC_STANDALONE * (3) Compile with MEC_STANDALONE to get Tornberg's original program, e.g., * * gcc -DMEC_STANDALONE mec.c -o mec */ #include "config.h" #include #include #ifndef MEC_STANDALONE #include "mec.h" #endif struct dp { double e; double w; }; typedef struct dp dp; static void post_crawford(double gr, double wpf, int ml, double **E, double fd2, double fd4); static void crawford(double gr, double wpf, int ml, double **E); static void pre_crawford(double gr, double wpf, int ml, double **E); static dp dpt(int p, int o, int c, double gr, double wpp, double **E); /* * Arguments are (in this order): * match length * gammon rate * winning percentage (favorite) */ #ifdef MEC_STANDALONE int main(int argc, char **argv) { /* match length */ int ml = argc > 1 ? atoi(argv[1]) : 9; /* gammon rate, i.e. how many of games won/lost will be gammons */ double gr = argc > 2 ? atof(argv[2]) : 0; /* Here one could argue for different approaches. Does the underdog * in match (current score) have different gammon rate. Or does the * underdog in match (equity at current score) have different gammon * rate. This could be solved, but for now, we assume same rates. */ /* winning percentage for favorite in one game. NB: when one player * is favorite, there is no symmetry in the equity table! I.e. * E[i][1] = 1 - E[1][i] doesn't necessarily hold. This figure must * be higher than or equal to .5, i.e. 50% */ double wpf = argc > 3 ? atof(argv[3]) : 0.5; /* Equity chart, E[p][o] = equity for p when p is p away, and o is o away. * If there is a game favorite, i.e. wpf > 0.5, p above is considered * to be the favorite in each game. For now we also assume no free drop * vigourish. This will only affect the computation of post-Crawford * equities, and is quite easy to correct. */ double **E = (double **) malloc((ml + 1) * sizeof(double *)); double *ec = (double *) calloc((ml + 1) * (ml + 1), sizeof(double)); if (E == NULL || ec == NULL) exit(EXIT_FAILURE); { int i; /* Initialize E to point to correct positions */ for (i = 0; i <= ml; i++) { E[i] = &ec[i * (ml + 1)]; } /* Initialize E for 0-away scores */ for (i = 1; i <= ml; i++) { E[0][i] = 1; E[i][0] = 0; } } /* Compute post-Crawford equities, given gammon rate, winning percentage * for favorite (game), match length. Fill in the equity table. */ post_crawford(gr, wpf, ml, E, 0.0, 0.0); { int i; printf("Post-Crawford:\n"); for (i = 1; i < ml; ++i) printf("%6.3f", E[i][1]); printf("\n"); for (i = 1; i < ml; ++i) printf("%6.3f", E[1][i]); printf("\n"); } /* Compute Crawford equities, given gammon rate, winning percentage * for favorite (game), match length, and post-Crawford equities. * Fill in the table. */ crawford(gr, wpf, ml, E); /* Compute pre-Crawford equities, given gammon rate, winning percentage * for favorite (game), match length, and Crawford equities. * Fill in the table. */ pre_crawford(gr, wpf, ml, E); /* Print the equity table. Nothing fancy. */ { int i, j; printf("Gammon rate %5.4f\n" "Winning %% %5.4f\n\n", gr, wpf); printf("%3s", ""); for (i = 1; i <= ml; i++) { printf("%6d", i); } printf("\n"); for (i = 1; i <= ml; i++) { printf("%3d", i); for (j = 1; j <= ml; j++) printf("%6.3f", E[i][j]); printf("\n"); } } free(ec); free(E); } #else /* MEC_STANDALONE */ extern void mec_pc(const float rGammonRate, const float rFreeDrop2Away, const float rFreeDrop4Away, const float rWinRate, float arMetPC[MAXSCORE]) { unsigned int i; /* match length */ const unsigned int ml = 64; /* gammon rate, i.e. how many of games won/lost will be gammons */ double gr = rGammonRate; /* Here one could argue for different approaches. Does the underdog * in match (current score) have different gammon rate. Or does the * underdog in match (equity at current score) have different gammon * rate. This could be solved, but for now, we assume same rates. */ /* winning percentage for favorite in one game. NB: when one player * is favorite, there is no symmetry in the equity table! I.e. * E[i][1] = 1 - E[1][i] doesn't necessarily hold. This figure must * be higher than or equal to .5, i.e. 50% */ double wpf = rWinRate; /* Equity chart, E[p][o] = equity for p when p is p away, and o is o away. * If there is a game favorite, i.e. wpf > 0.5, p above is considered * to be the favorite in each game. For now we also assume no free drop * vigourish. This will only affect the computation of post-Crawford * equities, and is quite easy to correct. */ double **E = (double **) malloc((ml + 1) * sizeof(double *)); double *ec = (double *) calloc((ml + 1) * (ml + 1), sizeof(double)); if (!E || !ec) exit(-1); /* We're in trouble... */ { /* Initialize E to point to correct positions */ for (i = 0; i <= ml; i++) { E[i] = &ec[i * (ml + 1)]; } /* Initialize E for 0-away scores */ for (i = 1; i <= ml; i++) { E[0][i] = 1; E[i][0] = 0; } } /* Compute post-Crawford equities, given gammon rate, winning percentage * for favorite (game), match length. Fill in the equity table. */ post_crawford(gr, wpf, ml, E, (double) rFreeDrop2Away, (double) rFreeDrop4Away); /* save post Crawford equities */ for (i = 0; i < ml; ++i) arMetPC[i] = (float) E[i + 1][1]; /* garbage collect */ free(ec); free(E); } extern void mec(const float rGammonRate, const float rWinRate, /* const *//*lint -e{818} */ float aarMetPC[2][MAXSCORE], float aarMet[MAXSCORE][MAXSCORE]) { unsigned int i, j; /* match length */ const unsigned int ml = 64; /* gammon rate, i.e. how many of games won/lost will be gammons */ double gr = rGammonRate; /* Here one could argue for different approaches. Does the underdog * in match (current score) have different gammon rate. Or does the * underdog in match (equity at current score) have different gammon * rate. This could be solved, but for now, we assume same rates. */ /* winning percentage for favorite in one game. NB: when one player * is favorite, there is no symmetry in the equity table! I.e. * E[i][1] = 1 - E[1][i] doesn't necessarily hold. This figure must * be higher than or equal to .5, i.e. 50% */ double wpf = rWinRate; /* Equity chart, E[p][o] = equity for p when p is p away, and o is o away. * If there is a game favorite, i.e. wpf > 0.5, p above is considered * to be the favorite in each game. For now we also assume no free drop * vigourish. This will only affect the computation of post-Crawford * equities, and is quite easy to correct. */ double **E = (double **) malloc((ml + 1) * sizeof(double *)); double *ec = (double *) calloc((ml + 1) * (ml + 1), sizeof(double)); if (!E || !ec) exit(-1); /* We're in trouble... */ { /* Initialize E to point to correct positions */ for (i = 0; i <= ml; i++) { E[i] = &ec[i * (ml + 1)]; } /* Initialize E for 0-away scores */ for (i = 1; i <= ml; i++) { E[0][i] = 1; E[i][0] = 0; } } /* Compute post-Crawford equities, given gammon rate, winning percentage * for favorite (game), match length. Fill in the equity table. */ for (i = 0; i < ml; ++i) E[i + 1][1] = aarMetPC[0][i]; for (i = 0; i < ml; ++i) E[1][i + 1] = 1.0 - (double)aarMetPC[1][i]; /* Compute Crawford equities, given gammon rate, winning percentage * for favorite (game), match length, and post-Crawford equities. * Fill in the table. */ crawford(gr, wpf, ml, E); /* Compute pre-Crawford equities, given gammon rate, winning percentage * for favorite (game), match length, and Crawford equities. * Fill in the table. */ pre_crawford(gr, wpf, ml, E); /* save the match equiy table */ for (i = 0; i < ml; ++i) for (j = 0; j < ml; ++j) aarMet[i][j] = (float) E[i + 1][j + 1]; /* garbage collect */ free(ec); free(E); } #endif /* ! MEC_STANDALONE */ /* If last bit is zero, then x is even. */ #define even(x) (((x)&0x1)==0) /* sq returns x if x is greater than zero, else it returns zero. */ #define sq(x) ((x)>0?(x):0) static void post_crawford(double gr, double wpf, int ml, /*lint -e{818} */ double **E, double fd2, double fd4) { int i; E[1][1] = wpf; for (i = 2; i <= ml; i++) { if (even(i)) { /* Free drop condition exists */ E[1][i] = E[1][i - 1]; E[i][1] = E[i - 1][1]; /* jth: add empirical values for free drop */ if (i == 2) { /* 2-away */ E[1][i] += fd2; E[i][1] -= fd2; } else if (i == 4) { /* 4-away */ E[1][i] += fd4; E[i][1] += fd4; } } else { E[1][i] = /* Equity for favorite when 1-away, i-away */ E[0][i] * wpf /* Favorite wins */ + E[1][sq(i - 2)] * (1 - wpf) * (1 - gr) /* Favorite loses single */ +E[1][sq(i - 4)] * (1 - wpf) * gr; /* Favorite loses gammon */ E[i][1] = /* Equity for favorite when i-away, 1-away */ E[i][0] * (1 - wpf) /* Favorite loses */ +E[sq(i - 2)][1] * wpf * (1 - gr) /* Favorite wins single */ +E[sq(i - 4)][1] * wpf * gr; /* Favorite wins gammon */ } } } static void crawford(double gr, double wpf, int ml, /*lint -e{818} */ double **E) { int i; /* Compute Crawford equities. Do this backwards, since * we overwrite post-Crawford equities with Crawford equities. * In this way we only overwrite equities no longer needed. */ for (i = ml; i >= 2; i--) { E[1][i] = /* Equity for favorite when 1-away,i-away */ E[0][i] * wpf /* Favorite wins */ + E[1][i - 1] * (1 - wpf) * (1 - gr) /* Favorite loses single */ +E[1][i - 2] * (1 - wpf) * gr; /* Favorite loses gammon */ E[i][1] = /* Equity for favorite when i-away, 1-away */ E[i][0] * (1 - wpf) /* Favorite loses */ +E[i - 1][1] * wpf * (1 - gr) /* Favorite wins single */ +E[i - 2][1] * wpf * gr; /* Favorite wins gammon */ } } static void pre_crawford(double gr, double wpf, int ml, double **E) { int i, j; dp dpf, dpu; double eq; for (i = 2; i <= ml; i++) for (j = i; j <= ml; j++) { dpf = dpt(i, j, 2, gr, wpf, E); dpu = dpt(j, i, 2, gr, 1 - wpf, E); dpu.e = 1 - dpu.e; dpu.w = 1 - dpu.w; eq = dpu.e + (dpf.e - dpu.e) * (wpf - dpu.w) / (dpf.w - dpu.w); E[i][j] = eq; if (i != j) { dpf = dpt(j, i, 2, gr, wpf, E); dpu = dpt(i, j, 2, gr, 1 - wpf, E); dpu.e = 1 - dpu.e; dpu.w = 1 - dpu.w; eq = dpu.e + (dpf.e - dpu.e) * (wpf - dpu.w) / (dpf.w - dpu.w); E[j][i] = eq; } } } /* Compute point when p doubles o to c, assuming gammon rate gr, * p wins wpp % of games, and equities E for favorite. At this * point o does equally well passing the double as taking it. */ static dp dpt(int p, int o, int c, double gr, double wpp, double **E) { dp dpo, dpp; double e0, edp, wdp; if (p <= c / 2) { /* No reason for p to double o, since a single win * is enough to win the match. */ dpp.e = 1; dpp.w = 1; return dpp; } /* p might double o to c since he needs more than a single * game to win the match. */ /* Find out when o (re)doubles p to 2*c */ dpo = dpt(o, p, 2 * c, gr, 1 - wpp, E); /* Find out equity for o if p does well and wins * all games here (assuming no recube from o), i.e. * o loses all games sitting on a c-cube. */ if (wpp > 0.5) { /* o isn't game favorite, equity for o at o-away x-away is 1 - E[x][o]. */ e0 = (1 - E[sq(p - c)][o]) * (1 - gr) + (1 - E[sq(p - 2 * c)][o]) * gr; } else { /* o is game favorite, equity for o at o-away x-away is E[o][x]. */ e0 = E[o][sq(p - c)] * (1 - gr) + E[o][sq(p - 2 * c)] * gr; } /* Find out o:s equity if o passes the double to c, i.e. loses c / 2 points. */ if (wpp > 0.5) { /* o isn't game favorite, equity for o at o-away x-away is 1 - E[x][o]. */ edp = 1 - E[sq(p - c / 2)][o]; } else { /* o is game favorite, equity for o at o-away x-away is E[o][x]. */ edp = E[o][sq(p - c / 2)]; } /* Find the winning percentage, which on the line from * (w0?,e0) to (dpo.w,dpo.e) gives o an equity equal to O's * equity passing the double to c (i.e. losing c / 2 pts) */ wdp = (edp - e0) * dpo.w / (dpo.e - e0); /* Now we know when p should double o, expressed as * winning percentage and equity for o. Return this * expressed in figures for p */ dpp.e = 1 - edp; dpp.w = 1 - wdp; return dpp; }