/* Pan2 -- poker analysis * Copyright (C) 2006-2008, 2017 Daniel Beer * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Compile with: * * gcc -O1 -Wall -o pan2 pan2.c -pthread */ #include #include #include #include #include #include #include #include #include /*********************************************************************** * PRNG */ #define MT_N 624 struct mt { unsigned int i; uint32_t state[MT_N]; }; static void mt_twist(struct mt *mt) { for (int i = 0; i < MT_N; i++) { const uint32_t xk = mt->state[i]; const uint32_t xk1 = mt->state[(i + 1) % MT_N]; const uint32_t xkm = mt->state[(i + 397) % MT_N]; const uint32_t concat = (xk & 0x80000000) | (xk1 & 0x7fffffff); const uint32_t xkn = xkm ^ (concat >> 1) ^ ((concat & 1) ? 0x9908B0DF : 0); mt->state[i] = xkn; } mt->i = 0; } static uint32_t mt_next(struct mt *mt) { if (mt->i >= MT_N) mt_twist(mt); uint32_t y = mt->state[mt->i++]; y ^= y >> 11; y ^= (y >> 7) & 0x9D2C5680; y ^= (y >> 15) & 0xEFC60000; y ^= y >> 18; return y; } static void mt_init(struct mt *mt) { uint32_t x = 0; for (int i = 0; i < MT_N; i++) { x = 1812433253 * (x ^ (x >> 30)) + i + mt->state[i]; mt->state[i] = x; } mt->i = MT_N; } /*********************************************************************** * Cards and hands */ /* Values of cards range [0, 52). The cards appear in the same order as their * intrinsic value. So, "2" is represented by 0, "10" by 8, "Jack", by 9, * "Queen" by 10, "King" by 11 and "Ace" by 12. */ typedef int card_t; #define CARD(f, s) (((f) << 2) | (s)) #define CARD_FACE(c) (c >> 2) #define CARD_SUIT(c) (c & 3) #define CARD_NUM 52 #define CARD_MAX_JOKERS 8 #define CARD_SUIT_SPADES 0 #define CARD_SUIT_CLUBS 1 #define CARD_SUIT_DIAMONDS 2 #define CARD_SUIT_HEARTS 3 #define CARD_FACE_TWO 0 #define CARD_FACE_THREE 1 #define CARD_FACE_FOUR 2 #define CARD_FACE_FIVE 3 #define CARD_FACE_SIX 4 #define CARD_FACE_SEVEN 5 #define CARD_FACE_EIGHT 6 #define CARD_FACE_NINE 7 #define CARD_FACE_TEN 8 #define CARD_FACE_JACK 9 #define CARD_FACE_QUEEN 10 #define CARD_FACE_KING 11 #define CARD_FACE_ACE 12 /* Sets of cards are represented using bitfields. card_set_t must be a * >=CARD_NUM bit type. */ typedef uint64_t card_set_t; #define CARD_ALL (((card_set_t)1 << CARD_NUM) - 1) #define CARD_SINGLE(c) (((card_set_t)1 << (c))) #define CARD_ALL_OF_SUIT(s) (((card_set_t)0x1111111111111111LL << (s))) #define CARD_ALL_OF_FACE(f) (((card_set_t)0xf << (f * 4))) /* Print a representation of a set of cards. */ static void card_set_str(card_set_t s, char *out, int max_len) { int i = 0; card_t c; for (c = 0; c < CARD_NUM && max_len; c++) if (s & CARD_SINGLE(c)) { if (i && i + 1 < max_len) out[i++] = ' '; if (CARD_FACE(c) == 8 && i + 1 < max_len) out[i++] = '1'; if (i + 3 < max_len) { out[i++] = "234567890JQKA"[CARD_FACE(c)]; out[i++] = "SCDH"[CARD_SUIT(c)]; } } for (c = CARD_NUM; c < 64; c++) if (s & CARD_SINGLE(c)) { if (i && i + 1 < max_len) out[i++] = ' '; if (i + 1 < max_len) out[i++] = '*'; } out[i] = 0; } /* Count the number of cards in a set */ static int card_set_size(card_set_t s) { int count = 0; while (s) { s &= (s - 1); count++; } return count; } /* Parse a single card name */ static card_t card_parse(const char *s, const char **endptr) { if (!*s) { fprintf(stderr, "Expected a card\n"); return -1; } int face = -1; char c = toupper(*(s++)); if (c == '*') { *endptr = s; return 52; } if (c >= '2' && c <= '9') { face = c - '2' + CARD_FACE_TWO; } else if (c == '0') { face = CARD_FACE_TEN; } else if (toupper(c) == 'J') { face = CARD_FACE_JACK; } else if (toupper(c) == 'Q') { face = CARD_FACE_QUEEN; } else if (toupper(c) == 'K') { face = CARD_FACE_KING; } else if (toupper(c) == 'A') { face = CARD_FACE_ACE; } else if ((c == '1') && (*s == '0')) { face = CARD_FACE_TEN; s++; } else { fprintf(stderr, "Invalid card face: %c\n", c); return -1; } if (!*s) { fprintf(stderr, "Expected a suit\n"); return -1; } c = toupper(*(s++)); int suit = -1; switch (c) { case 'S': suit = CARD_SUIT_SPADES; break; case 'C': suit = CARD_SUIT_CLUBS; break; case 'D': suit = CARD_SUIT_DIAMONDS; break; case 'H': suit = CARD_SUIT_HEARTS; break; default: fprintf(stderr, "Invalid card suit: %c\n", c); return -1; } if (endptr) *endptr = s; return CARD(face, suit); } /* Parse a card set */ static int card_set_parse(const char *s, card_set_t *out, unsigned int *joker_index) { card_set_t r = 0; while (isspace(*s)) s++; while (*s) { card_t c = card_parse(s, &s); if (c < 0) return -1; if (c == 52) { if (!joker_index) { fprintf(stderr, "Jokers not allowed here\n"); return -1; } if (*joker_index >= CARD_MAX_JOKERS) { fprintf(stderr, "Too many " "jokers distributed\n"); return -1; } c = 52 + (*joker_index)++; } r |= CARD_SINGLE(c); while (isspace(*s)) s++; if (r && *s == ',') s++; while (isspace(*s)) s++; } *out = r; return 0; } /************************************************************************ * Poker hand ranking and simple scoring */ /* Ranks for all possible hands */ typedef int rank_t; #define RANK_HIGH_CARD 0 #define RANK_PAIR 1 #define RANK_TWO_PAIR 2 #define RANK_THREES 3 #define RANK_STRAIGHT 4 #define RANK_FLUSH 5 #define RANK_FULL_HOUSE 6 #define RANK_FOURS 7 #define RANK_ST_FLUSH 8 #define RANK_NUM 9 static const char *rank_cat_name(rank_t r) { static const char *cat_name[] = { "High card", "Pair", "Two pair", "Threes", "Straight", "Flush", "Full house", "Fours", "Straight flush" }; return cat_name[r]; } /* Hand scores */ typedef uint32_t score_t; #define SCORE(r, k) (((r) << 24) | (k)) #define SCORE_RANK(r) ((r) >> 24) #define SCORE_KICKER(k) ((r) & 0xffffff) #define SCORE_MAX 0x08ffffff /* Calculate the kicker rank of a set of cards. This is based on the instrinsic value of the n highest cards. Note that it is only meaningful to compare kicker ranks of same-sized sets. */ static score_t kicker_rank(card_set_t hand, int n, int jokers) { card_t c; score_t r = 0; while (n && jokers) { r = (r << 4) | 0xf; n--; jokers--; } for (c = CARD_NUM - 1; c >= 0 && n && hand; c--) if (hand & CARD_SINGLE(c)) { r = (r << 4) | CARD_FACE(c); n--; } r <<= (4 * n); return r; } /* Calculate the rank of a hand and return it. It's kicker rank is used to distinguish between hands which have the same major rank e.g. king-high straight vs queen-high straight. */ static score_t score_hand(card_set_t hand, card_set_t wild) { card_set_t s; unsigned int face_count[13]; unsigned int suit_count[4]; unsigned int jokers; memset(face_count, 0, sizeof(face_count)); memset(suit_count, 0, sizeof(suit_count)); s = hand & ~wild; for (unsigned int i = 0; i < 52; i++) { if (s & 1) { face_count[i >> 2]++; suit_count[i & 3]++; } s >>= 1; } jokers = card_set_size(s) + card_set_size(hand & wild); hand &= ~wild; /* Is the hand a straight flush? */ s = CARD_SINGLE(51) | CARD_SINGLE(47) | CARD_SINGLE(43) | CARD_SINGLE(39) | CARD_SINGLE(35); for (int i = 12; i >= 3; i--) { if (i == 3) s |= CARD_SINGLE(48); /* s now equals the spades straight flush with i high */ for (int j = 0; j < 4; j++) { if (card_set_size(hand & s) + jokers >= 5) return SCORE(RANK_ST_FLUSH, i); s >>= 1; } } /* Is it four of a kind? */ for (int i = 12; i >= 0; i--) if (face_count[i] + jokers >= 4) return SCORE(RANK_FOURS, i * 13 + kicker_rank(hand & ~CARD_ALL_OF_FACE(i), 1, jokers + face_count[i] - 4)); /* Is it a full house? */ for (int i = 12; i >= 0; i--) if (face_count[i] + jokers >= 3) for (int j = 12; j >= 0; j--) if (j != i && face_count[i] + face_count[j] >= 5) return SCORE(RANK_FULL_HOUSE, (i << 4) | j); /* A flush? */ for (int i = 0; i < 4; i++) if (suit_count[i] + jokers >= 5) return SCORE(RANK_FLUSH, kicker_rank(hand & CARD_ALL_OF_SUIT(i), 5, jokers + suit_count[i] - 5)); /* A straight? */ card_set_t f = hand; f |= f << 2; f |= f << 1; s = CARD_SINGLE(51) | CARD_SINGLE(47) | CARD_SINGLE(43) | CARD_SINGLE(39) | CARD_SINGLE(35); for (int i = 12; i >= 3; i--) { if (i == 3) s |= CARD_SINGLE(48); if (card_set_size(f & s) + jokers >= 5) return SCORE(RANK_STRAIGHT, i); s >>= 4; } /* Three of a kind? */ for (int i = 12; i >= 0; i--) if (face_count[i] + jokers >= 3) return SCORE(RANK_THREES, (i << 8) | kicker_rank(hand & ~CARD_ALL_OF_FACE(i), 2, jokers + face_count[i] - 3)); /* Two pairs, or a pair */ for (int i = 12; i >= 0; i--) if (face_count[i] + jokers >= 2) { for (int j = i - 1; j >= 0; j--) if (face_count[i] + face_count[j] + jokers >= 4) return SCORE(RANK_TWO_PAIR, (i << 8) | (j << 4) | kicker_rank(hand & ~CARD_ALL_OF_FACE(i) & ~CARD_ALL_OF_FACE(j), 1, jokers + face_count[i] + face_count[j] - 4)); return SCORE(RANK_PAIR, (i << 12) | kicker_rank(hand & ~CARD_ALL_OF_FACE(i), 3, jokers + face_count[i] - 2)); } return SCORE(RANK_HIGH_CARD, kicker_rank(hand, 5, jokers)); } /************************************************************************ * High/low Omaha score search */ static void low_leaf(score_t *best, card_set_t wild, card_set_t hand) { const score_t s = score_hand(hand, wild); if (s < *best) *best = s; } /* Search over hands of exactly 5 cards */ static void low_search_hand(score_t *best, card_set_t wild, card_set_t chosen, card_set_t remaining) { /* Size-based search cut-offs */ const int csize = card_set_size(chosen); const int rsize = card_set_size(remaining); if (csize >= 5) { low_leaf(best, wild, chosen); return; } if (csize + rsize == 5) { low_leaf(best, wild, chosen | remaining); return; } if (csize + rsize < 5) return; const card_set_t tail = remaining & (remaining - 1); const card_set_t head = remaining ^ tail; low_search_hand(best, wild, chosen, tail); low_search_hand(best, wild, chosen | head, tail); } /* Search over subsets of the private hand whose size ranges from * min_choice to max_choice. */ static void low_search_private(score_t *best, card_set_t wild, card_set_t public, int min_choice, int max_choice, card_set_t chosen, card_set_t remaining) { /* Size-based search cut-offs */ const int csize = card_set_size(chosen); const int rsize = card_set_size(remaining); if ((csize >= max_choice) || !remaining) { low_search_hand(best, wild, chosen, public); return; } if (csize + rsize == min_choice) { low_search_hand(best, wild, chosen | remaining, public); return; } if (csize + rsize < min_choice) return; const card_set_t tail = remaining & (remaining - 1); const card_set_t head = remaining ^ tail; low_search_private(best, wild, public, min_choice, max_choice, chosen, tail); low_search_private(best, wild, public, min_choice, max_choice, chosen | head, tail); } static void high_leaf(score_t *best, card_set_t wild, card_set_t hand) { const score_t s = score_hand(hand, wild); if (s > *best) *best = s; } /* Search over hands of exactly eq_choice in size */ static void high_search_hand(score_t *best, card_set_t wild, int eq_choice, card_set_t chosen, card_set_t remaining) { /* Size-based search cut-offs */ const int csize = card_set_size(chosen); const int rsize = card_set_size(remaining); if (csize >= eq_choice) { high_leaf(best, wild, chosen); return; } if (csize + rsize == eq_choice) { high_leaf(best, wild, chosen | remaining); return; } if (csize + rsize < eq_choice) return; const card_set_t tail = remaining & (remaining - 1); const card_set_t head = remaining ^ tail; high_search_hand(best, wild, eq_choice, chosen, tail); high_search_hand(best, wild, eq_choice, chosen | head, tail); } /* Search over subsets of the private hand of exactly eq_private in size */ static void high_search_private(score_t *best, card_set_t wild, card_set_t public, int eq_all, int eq_private, card_set_t chosen, card_set_t remaining) { /* Size-based search cut-offs */ const int csize = card_set_size(chosen); const int rsize = card_set_size(remaining); if (csize >= eq_private) { high_search_hand(best, wild, eq_all, chosen, public); return; } if (csize + rsize == eq_private) { high_search_hand(best, wild, eq_all, chosen | remaining, public); return; } if (csize + rsize < eq_private) return; const card_set_t tail = remaining & (remaining - 1); const card_set_t head = remaining ^ tail; high_search_private(best, wild, public, eq_all, eq_private, chosen, tail); high_search_private(best, wild, public, eq_all, eq_private, chosen | head, tail); } static score_t hilow_score(card_set_t wild, int lowball, card_set_t public, int pub_limit, card_set_t private, int prv_limit) { if (lowball) { score_t best = SCORE_MAX; int prv_min = 5 - pub_limit; if (prv_min < 0) prv_min = 0; low_search_private(&best, wild, public, prv_min, prv_limit, 0, private); return best; } else { score_t best = 0; high_search_private(&best, wild, public, pub_limit + prv_limit, prv_limit, 0, private); return best; } } /************************************************************************ * Game scenarios */ /* Game scenario */ #define POKER_MAX_OPPONENTS 10 struct poker { /* Total number of cards that a player holds (e.g. 5 for draw, 2 * for Texas hold-em. */ unsigned int prv_size; /* Total number of public cards (0 for draw, 5 for Texas hold-em) */ unsigned int pub_size; /* Fixed/known cards in the public hand (the cards shared by all * players) */ card_set_t pub_hand; /* Undealable cards */ card_set_t dead; /* Wildcards */ card_set_t wild; /* Number of jokers in the pack */ unsigned int jokers; /* Lowball? */ int lowball; /* This can be used to enforce a limit on the number of cards that a * player is allowed to use from his/her own hand. Set to 0 to * disable this functionality. * * Omaha is played by setting prv_size to 4 and prv_limit to 2. */ unsigned int prv_limit; unsigned int pub_limit; /* Number of opponents and the fixed/known cards in each of their * hands. */ unsigned int op_count; card_set_t op_hands[POKER_MAX_OPPONENTS]; /* Extra information computed during validation */ card_set_t used; unsigned int pub_hand_size; unsigned int op_hand_sizes[POKER_MAX_OPPONENTS]; }; static int poker_validate(struct poker *p) { if (p->op_count > POKER_MAX_OPPONENTS) { fprintf(stderr, "Maximum number of opponents is %d\n", POKER_MAX_OPPONENTS); return -1; } if (!p->op_count) { fprintf(stderr, "No players in game\n"); return -1; } if (p->jokers > CARD_MAX_JOKERS) { fprintf(stderr, "Too many jokers\n"); return -1; } /* Are there enough cards in the deck? */ unsigned int available = 52 + p->jokers; if (available / p->op_count < p->prv_size) { fprintf(stderr, "Too many private cards required\n"); return -1; } available -= p->op_count * p->prv_size; if (p->pub_size > available) { fprintf(stderr, "Too many public cards required\n"); return -1; } /* Default limits */ if (!p->prv_limit) p->prv_limit = p->prv_size; if (!p->pub_limit) p->pub_limit = p->pub_size; /* Check limits */ if (p->prv_limit > p->prv_size) { fprintf(stderr, "Private limit is too big\n"); return -1; } if (p->pub_limit > p->pub_size) { fprintf(stderr, "Public limit is too big\n"); return -1; } /* Do the rules allow for actual poker hands? */ if (p->prv_limit + p->pub_limit < 5) { fprintf(stderr, "Not enough cards to make a poker hand\n"); return -1; } /* Are the scenario cards few enough and not duplicated? */ p->used = p->dead; if (p->used & p->pub_hand) { fprintf(stderr, "Duplicated public cards\n"); return -1; } p->used |= p->pub_hand; p->pub_hand_size = card_set_size(p->pub_hand); if (p->pub_hand_size > p->pub_size) { fprintf(stderr, "Too many public cards\n"); return -1; } for (unsigned int i = 0; i < p->op_count; i++) { p->op_hand_sizes[i] = card_set_size(p->op_hands[i]); if (p->op_hand_sizes[i] > p->prv_size) { fprintf(stderr, "Too many private cards for opponent %d\n", i + 1); return -1; } if (p->used & p->op_hands[i]) { fprintf(stderr, "Duplicated cards for opponent %d\n", i); return -1; } p->used |= p->op_hands[i]; } if (card_set_size(p->used >> CARD_NUM) > p->jokers) { fprintf(stderr, "Too many jokers distributed\n"); return -1; } return 0; } struct poker_outcome_player { card_set_t hand; score_t score; unsigned int win; }; struct poker_outcome { card_set_t public; struct poker_outcome_player players[POKER_MAX_OPPONENTS]; unsigned int win_total; }; /* Deal one outcome */ static void poker_deal(const struct poker *p, struct mt *mt, struct poker_outcome *o) { /* Construct deck */ card_set_t m = 1; card_set_t deck[64]; unsigned int deck_size = 0; for (unsigned int i = 0; i < 52 + p->jokers; i++) { if (!(p->used & m)) deck[deck_size++] = m; m <<= 1; } /* Fisher-Yates shuffle */ for (unsigned int i = 0; i < deck_size; i++) { unsigned int k = deck_size - i - 1; unsigned int j = mt_next(mt) % (deck_size - i); card_set_t temp = deck[j]; deck[j] = deck[k]; deck[k] = temp; } /* Deal out public cards */ o->public = p->pub_hand; for (unsigned int i = p->pub_hand_size; i < p->pub_size; i++) o->public |= deck[--deck_size]; /* Deal out cards to each opponent */ score_t best = p->lowball ? SCORE_MAX : 0; for (unsigned int i = 0; i < p->op_count; i++) { struct poker_outcome_player *pl = &o->players[i]; pl->hand = p->op_hands[i]; for (unsigned int j = p->op_hand_sizes[i]; j < p->prv_size; j++) pl->hand |= deck[--deck_size]; pl->score = hilow_score( p->wild, p->lowball, o->public, p->pub_limit, pl->hand, p->prv_limit); if ((p->lowball && (pl->score < best)) || (!p->lowball && (pl->score > best))) best = pl->score; } /* Figure out who wins */ o->win_total = 0; for (unsigned int i = 0; i < p->op_count; i++) { struct poker_outcome_player *pl = &o->players[i]; if (pl->score == best) { pl->win = 1; o->win_total++; } else { pl->win = 0; } } } struct poker_stats_player { unsigned int win; unsigned int tie; unsigned int value; unsigned int ranks[RANK_NUM]; }; struct poker_stats { struct poker_stats_player players[POKER_MAX_OPPONENTS]; unsigned int value_total; unsigned int iterations; }; /* Analyze outcome and produce stats. Player stats array must have * sufficient elements. */ static void poker_sim(const struct poker *p, unsigned int n, struct poker_stats *s, unsigned int thread_id) { struct mt mt; /* Seed based on scenario */ memset(&mt, 0, sizeof(mt)); mt.state[0] = thread_id; mt.state[1] = p->prv_size; mt.state[2] = p->pub_size; mt.state[3] = p->pub_hand; mt.state[4] = p->pub_hand >> 32; mt.state[5] = p->prv_limit; mt.state[6] = p->op_count; for (unsigned i = 0; i < p->op_count; i++) { mt.state[i * 2 + 7] = p->op_hands[i]; mt.state[i * 2 + 8] = p->op_hands[i] >> 32; } mt_init(&mt); memset(s, 0, sizeof(*s)); s->iterations = n; for (unsigned int i = 0; i < n; i++) { struct poker_outcome o; poker_deal(p, &mt, &o); s->value_total += o.win_total; for (unsigned int j = 0; j < p->op_count; j++) { const struct poker_outcome_player *pl = &o.players[j]; struct poker_stats_player *ps = &s->players[j]; ps->value += pl->win; if (o.win_total > 1) ps->tie += pl->win; else ps->win += pl->win; ps->ranks[SCORE_RANK(pl->score)]++; } } } static void poker_print_rules(const struct poker *p) { char buf[128]; printf("Private cards: %d (%d usable)\n", p->prv_size, p->prv_limit); printf("Public cards: %d (%d usable)\n", p->pub_size, p->pub_limit); card_set_str(p->wild, buf, sizeof(buf)); printf("Wild cards: %s\n", buf); printf("Ranking: %s\n", p->lowball ? "lowball" : "normal"); printf("\n"); card_set_str(p->pub_hand, buf, sizeof(buf)); printf("Public dealt: %s\n", buf); card_set_str(p->dead, buf, sizeof(buf)); printf("Dead cards: %s\n", buf); card_set_str(p->wild, buf, sizeof(buf)); for (unsigned int i = 0; i < p->op_count; i++) { card_set_str(p->op_hands[i], buf, sizeof(buf)); printf("Player %d: %s\n", i + 1, buf); } } static void print_hline(unsigned int op_count) { printf("----------------"); for (unsigned int i = 0; i < op_count; i++) printf("-+------"); printf("\n"); } static void poker_print_stats(unsigned int op_count, const struct poker_stats *s) { print_hline(op_count); printf("%-16s", "Player"); for (unsigned int i = 0; i < op_count; i++) printf(" | %5d", i + 1); printf("\n"); print_hline(op_count); for (unsigned int i = 0; i < RANK_NUM; i++) { printf("%-16s", rank_cat_name(i)); for (unsigned int j = 0; j < op_count; j++) printf(" | %5.1f", 100.0 * (double)s->players[j].ranks[i] / (double)s->iterations); printf("\n"); } print_hline(op_count); printf("%-16s", "Win"); for (unsigned int i = 0; i < op_count; i++) printf(" | %5.1f", 100.0 * (double)s->players[i].win / (double)s->iterations); printf("\n"); printf("%-16s", "Tie"); for (unsigned int i = 0; i < op_count; i++) printf(" | %5.1f", 100.0 * (double)s->players[i].tie / (double)s->iterations); printf("\n"); printf("%-16s", "Value"); for (unsigned int i = 0; i < op_count; i++) printf(" | %5.1f", 100.0 * (double)s->players[i].value / (double)s->value_total); printf("\n"); print_hline(op_count); } /************************************************************************ * Parallel processing */ struct task { const struct poker *poker; unsigned int index; unsigned int n; struct poker_stats result; pthread_t thread; int r; }; static void *parallel_task(void *arg) { struct task *t = (struct task *)arg; poker_sim(t->poker, t->n, &t->result, t->index); return NULL; } static int poker_sim_parallel(const struct poker *p, unsigned int n, struct poker_stats *s, unsigned int nthread) { struct task tasks[nthread]; for (unsigned int i = 0; i < nthread; i++) { struct task *t = &tasks[i]; t->poker = p; t->index = i; t->n = n; if (pthread_create(&t->thread, NULL, parallel_task, t) < 0) { perror("pthread_create"); while (i) pthread_join(tasks[--i].thread, NULL); return -1; } } memset(s, 0, sizeof(*s)); for (unsigned int i = 0; i < nthread; i++) { struct task *t = &tasks[i]; pthread_join(t->thread, NULL); s->value_total += t->result.value_total; s->iterations += t->result.iterations; for (unsigned int j = 0; j < p->op_count; j++) { s->players[j].win += t->result.players[j].win; s->players[j].tie += t->result.players[j].tie; s->players[j].value += t->result.players[j].value; for (unsigned int k = 0; k < RANK_NUM; k++) s->players[j].ranks[k] += t->result.players[j].ranks[k]; } } return 0; } /************************************************************************ * Command-line interface */ static void usage(const char *progname) { printf("Usage: %s [options] [ ...]\n" "\n" "General options:\n" " --help Show this text\n" " --version Show program version\n" " -i Set number of iterations per player (default 10000)\n" " -t Set number of parallel threads (default 1)\n" "\n" "Options for specifying game rules:\n" " -n Number of private cards per player (default 5)\n" " -s Number of public cards in the game (default 0)\n" " -N Number of private cards usable (default all)\n" " -S Number of public cards usable (default all)\n" " -j Set number of wildcards/jokers in pack (default 0)\n" " -w Specify wildcards\n" " -l Lowball (worst hand wins)\n" "\n" "Options for specifying game scenarios:\n" " -p Public cards already dealt\n" " -d Dead cards\n" " Private cards already dealt to a given player\n" "\n" "Examples of game rules:\n" " 5-card draw: -n 5\n" " Texas hold 'em: -n 2 -s 5\n" " Omaha hold 'em: -n 4 -s 5 -N 2 -S 3\n" "\n" "Hands should be specified as comma-separated lists of cards. Each\n" "card is specified as a face (2..9, 10, J, Q, K, A) and a suit\n" "character (S, C, D, H), or a single * character to indicate a joker.\n" "Use an empty string for a player whose cards are all unknown or\n" "undealt.\n", progname); } #define OPT_HELP 0x01 #define OPT_VERSION 0x02 struct options { int flags; unsigned int iterations; unsigned int threads; struct poker scenario; }; static int options_parse(struct options *o, int argc, char **argv) { enum { LOPT_HELP = 1000, LOPT_VERSION }; static const struct option longopts[] = { {"help", 0, NULL, LOPT_HELP}, {"version", 0, NULL, LOPT_VERSION}, {NULL, 0, NULL, 0} }; int opt; unsigned int joker_index = 0; memset(o, 0, sizeof(*o)); o->iterations = 10000; o->scenario.prv_size = 5; while ((opt = getopt_long(argc, argv, "n:s:N:S:p:i:t:d:j:w:l", longopts, NULL)) >= 0) switch (opt) { case LOPT_HELP: o->flags |= OPT_HELP; break; case LOPT_VERSION: o->flags |= OPT_VERSION; break; case 'l': o->scenario.lowball = 1; break; case 'w': if (card_set_parse(optarg, &o->scenario.wild, NULL) < 0) return -1; break; case 'j': o->scenario.jokers = atoi(optarg); break; case 't': o->threads = atoi(optarg); break; case 'n': o->scenario.prv_size = atoi(optarg); break; case 's': o->scenario.pub_size = atoi(optarg); break; case 'N': o->scenario.prv_limit = atoi(optarg); break; case 'S': o->scenario.pub_limit = atoi(optarg); break; case 'i': o->iterations = atoi(optarg); break; case 'p': if (card_set_parse(optarg, &o->scenario.pub_hand, &joker_index) < 0) return -1; break; case 'd': if (card_set_parse(optarg, &o->scenario.dead, &joker_index) < 0) return -1; break; case '?': fprintf(stderr, "Unknown option. Try --help.\n"); return -1; } argc -= optind; argv += optind; while (argc) { if (o->scenario.op_count >= POKER_MAX_OPPONENTS) { fprintf(stderr, "Too many players\n"); return -1; } if (card_set_parse(*argv, &o->scenario.op_hands[o->scenario.op_count], &joker_index) < 0) return -1; o->scenario.op_count++; argc--; argv++; } return 0; } int main(int argc, char **argv) { struct options opt; if (options_parse(&opt, argc, argv) < 0) return -1; if (opt.flags & OPT_HELP) { usage(argv[0]); return 0; } if (opt.flags & OPT_VERSION) { printf("pan2 version 20171205\n"); printf("Copyright (C) 2017 Daniel Beer \n"); return 0; } if (poker_validate(&opt.scenario) < 0) return -1; if (opt.threads > 64) { fprintf(stderr, "Too many threads\n"); return -1; } struct poker_stats s; const unsigned int iterations = opt.iterations * opt.scenario.op_count; if (opt.threads > 1) { if (poker_sim_parallel(&opt.scenario, iterations / opt.threads, &s, opt.threads) < 0) return -1; } else { poker_sim(&opt.scenario, iterations, &s, 0); } poker_print_rules(&opt.scenario); printf("\n"); poker_print_stats(opt.scenario.op_count, &s); return 0; }