/*
- * cardprobs -- compute combination probabilities for Magic: The Gathering
+ * cardprobs -- compute combination probabilities for card draws
*
- * by Jeremy C. York <jeremy@stat.cmu.edu>; cleanup, comments, and
- * interface improvements by Eric Raymond <esr@snark.thyrsus.com>.
+ * by Jeremy C. York <jeremy@stat.cmu.edu>; cleanup, comments,
+ * generalization, and interface improvements by Eric Raymond
+ * <esr@snark.thyrsus.com>.
*
- * If I have M special cards (for example, land cards) in my deck, and N
- * total cards in my deck, and I draw k cards from that deck (*without
- * replacement* -- the fact that you don't put them back into the deck
- * can be important for some values of M and N), and if X counts the
- * number of special cards contained in those k drawn cards...
+ * If I have M special cards in my deck, and N total cards in my deck,
+ * and I draw k cards from that deck (*without replacement* -- the
+ * fact that you don't put them back into the deck can be important
+ * for some values of M and N), and if X counts the number of special
+ * cards contained in those k drawn cards...
*
* Then X has a hypergeometric distribution, with the following formula
* for probabilities:
* cc cardprobs.c -lm -o cardprobs
*
* In the table output, a dot designates a probability less than .5% that
- * rounds to zero.
- */
+ * rounds to zero. */
#include <stdio.h>
#include <math.h>
#include <signal.h>
-#define BASEHAND 7 /* # cards in initial deal */
+#define BASE_DEFAULT 7 /* # cards in initial deal */
#define TURNS_DEFAULT 20 /* show this many turns if total is greater */
static int sx, sk, sM, sN;
+static int sa, sb;
-void catcher(int sig)
+static void hypercatch(int sig)
{
putchar('\n');
if (sig == SIGFPE)
(void) printf("Died while computing H(%d, %d, %d, %d)\n", sx,sk,sM,sN);
else
- (void) printf("Caught signal %d\n", sig);
+ (void) printf("Caught signal %d during hyper computation\n", sig);
+}
+
+static void choosecatch(int sig)
+{
+ putchar('\n');
+ if (sig == SIGFPE)
+ (void) printf("Died while computing choose(%d, %d)\n", sa, sb);
+ else
+ (void) printf("Caught signal %d during choose computation\n", sig);
+}
+
+static void usage(void)
+{
+ (void) printf("usage: draw [-h] [-b initial] [-t turns] [-c columns] specials total\n");
+ exit(0);
}
main(argc, argv)
- int argc;
- char **argv;
+int argc;
+char **argv;
{
- int turns, special, total, columns, i, j;
+ extern char *optarg; /* set by getopt */
+ extern int optind; /* set by getopt */
double hyper_prob();
- if (argc < 3) {
- (void) printf("usage: cardprobs M N [t] [c]\n");
- (void) printf(" M = # special cards\n");
- (void) printf(" N = # total cards\n");
- (void) printf(" t = # of turns (optional)\n");
- (void) printf(" c = columns in the report (optional)\n");
- } else {
- special = atoi(argv[1]);
-
- total = atoi(argv[2]);
- if (total < special) {
- (void) printf("Can't have more specials than total cards!\n");
- exit(-1);
- } else if (total <= BASEHAND) {
- (void) printf("All specials would be in the initial hand!\n");
- exit(-1);
+ int status, special, total, columns, i, j;
+ int base = BASE_DEFAULT;
+ int turns = TURNS_DEFAULT;
+ int headers = 1;
+
+ while ((status = getopt(argc, argv, "b:c:ht:")) != EOF)
+ {
+ switch (status)
+ {
+ case 'b':
+ base = atoi(optarg);
+ break;
+
+ case 'c':
+ columns = atoi(optarg);
+ break;
+
+ case 'h':
+ headers = 0;
+ break;
+
+ case 't':
+ turns = atoi(optarg);
+ break;
+
+ default:
+ usage();
}
+ }
+
+ if (optind + 2 > argc)
+ usage();
+
+ special = atoi(argv[optind]);
+ total = atoi(argv[optind + 1]);
+
+ if (total < special) {
+ (void) printf("Can't have more specials than total cards!\n");
+ exit(-1);
+ } else if (total <= base) {
+ (void) printf("All specials would be in the initial hand!\n");
+ exit(-1);
+ }
- if (total - BASEHAND < TURNS_DEFAULT)
- turns = total - BASEHAND;
- else
- turns = TURNS_DEFAULT;
+ if (total - base < TURNS_DEFAULT)
+ turns = total - base;
- if (argc >= 4)
- turns = atoi(argv[3]);
- if (turns > total - BASEHAND) {
- (void) printf("The deck can't last that long!\n");
- exit(-1);
- }
+ if (turns > total - base) {
+ (void) printf("The deck can't last that long!\n");
+ exit(-1);
+ }
+ columns = special + 1;
+ if (argc >= 5)
+ columns = atoi(argv[4]);
+ if (columns > special + 1) {
+ (void) printf("using %d columns because can only have ",special+1);
+ (void) printf("0 to %d specials\n",special);
columns = special + 1;
- if (argc >= 5)
- columns = atoi(argv[4]);
- if (columns > special + 1) {
- (void) printf("using %d columns because can only have ",special+1);
- (void) printf("0 to %d specials\n",special);
- columns = special + 1;
- }
-
- (void) printf("%d specials out of %d total; %d turns, %d columns\n",
- special, total, turns, columns);
-
- (void) signal(SIGFPE, catcher);
-
- (void) printf(" | %% chance of this number of specials\n");
- (void) printf("Turn | ");
- for (j = 0; j < columns; j++)
- (void) printf("%2d ",j);
- (void) printf("\n");
- (void) printf("-----+");
- for (j = 0; j < columns; j++)
- (void) printf("---",j);
- (void) printf("\n");
-
- for (i = 1; i <= turns; i++) {
- (void) printf("%2d | ",i);
- for (j = 0; j < columns && j <= i + BASEHAND && j <= special; j++)
- {
- int p = (int)rint(hyper_prob(j, i + BASEHAND, special, total) * 100);
-
- if (p == 0)
- (void) fputs(" . ", stdout);
- else
- (void) printf("%2d ", p);
- }
-
- (void) printf("\n");
- }
+ }
+
+ (void) signal(SIGFPE, hypercatch);
+
+ if (headers)
+ {
+ (void) printf("%d in initial hand, %d specials out of %d total\n",
+ base, special, total);
+
+ (void) printf(" | %% chance of given number of specials (. means < 0.5%%)\n");
+ (void) printf("Turn | ");
+ for (j = 0; j < columns; j++)
+ (void) printf("%2d ",j);
+ (void) printf("\n");
+ (void) printf("-----+");
+ for (j = 0; j < columns; j++)
+ (void) printf("---",j);
+ (void) printf("\n");
+ }
+
+ for (i = 1; i <= turns; i++) {
+ if (headers)
+ (void) printf("%2d | ",i);
+ for (j = 0; j < columns && j <= i + base && j <= special; j++)
+ {
+ int p = (int)rint(hyper_prob(j, i + base, special, total) * 100);
+
+ if (p == 0)
+ (void) fputs(" . ", stdout);
+ else
+ (void) printf("%2d ", p);
+ }
+
+ (void) printf("\n");
}
}
{
double lgamma();
+ sa = a;
+ sb = b;
+
+ signal(SIGFPE, choosecatch);
+
return(lgamma((double) (a+1)) - lgamma((double) (b+1)) -
lgamma((double) (a-b+1)));
+
+ signal(SIGFPE, hypercatch);
+
}
+
+/* cardprobs.c ends here */