Go Fish/C

From Rosetta Code
Go Fish/C is part of Go Fish. You may find other members of Go Fish at Category:Go Fish.

Reasonably smart computer AI. Programs require utf-8 locale. AI keeps a record of probabilities of each card player may have and always asks for the card with highest score (which backfires quite often btw). <lang c>#include <stdio.h>

  1. include <stdlib.h>
  2. include <locale.h>
  3. include <ctype.h>
  4. include <string.h>
  1. include <termios.h>
  2. include <unistd.h>
  3. include <fcntl.h>
  4. include <time.h>

int irand(int n) { int r, rand_max = RAND_MAX - (RAND_MAX % n); while ((r = rand()) >= rand_max); return r / (rand_max / n); }

/* ------------ keyboard stuff ------------- */ void set_mode(int want_key) { static struct termios old, new; if (!want_key) { tcsetattr(STDIN_FILENO, TCSANOW, &old); return; }

tcgetattr(STDIN_FILENO, &old); new = old; new.c_lflag &= ~(ICANON); tcsetattr(STDIN_FILENO, TCSANOW, &new); }

int getkey(const char *prompt) { int c = 0; fd_set fs;

printf("%s: ", prompt); fflush(stdout);

set_mode(1); FD_ZERO(&fs); FD_SET(STDIN_FILENO, &fs);

select(STDIN_FILENO + 1, &fs, 0, 0, 0); if (FD_ISSET(STDIN_FILENO, &fs)) { c = getchar(); set_mode(0); } return c; }

int anykey() { return getkey("Press any key to continue"); }

/* display functions */ void xy(int x, int y) { printf("\033[%d;%dH", y + 1, x + 1); } void clear() { printf("\033[J"); }

/* ---------- card functions ------------- */ wchar_t ssuit[] = L" ♠♥♦♣", snum[] = L" A23456789TJQK"; typedef unsigned char cnum; typedef struct { cnum suit, num; wchar_t name[3]; } card_t, *card; typedef struct { card_t card[52]; int n; } deck_t, *deck;

typedef struct { card_t c; int n; } shuffle_t; int shuffle_cmp(const void * a, const void *b) { int x = ((const shuffle_t*)a)->n, y = ((const shuffle_t*)b)->n; return x < y ? -1 : x > y; }

int card_cmp(const void *aa, const void *bb) { card a = *(const card*)aa, b = *(const card*)bb; if (a == b) return 0; if (!a) return 1; if (!b) return -1; if (a->num < b->num) return -1; if (a->num > b->num) return 1; if (a->suit < b->suit) return -1; return a->suit > b->suit; }

void card_shuffle(deck d) { int i; shuffle_t x[52]; for (i = 0; i < d->n; i++) { x[i].c = d->card[i]; x[i].n = rand(); } qsort(x, d->n, sizeof(shuffle_t), shuffle_cmp); for (i = 0; i < d->n; i++) d->card[i] = x[i].c; }

deck card_init_deck(deck d) { int j; card c = d->card; for (j = 0; j < 52; j++, c++) { c->suit = j / 13 + 1; c->num = (j % 13) + 1; c->name[0] = ssuit[c->suit]; c->name[1] = snum[c->num]; c->name[2] = 0; } d->n = 52; card_shuffle(d); return d; }

card card_deal(deck d) { if (!d->n) return 0; return d->card + --d->n; }

/* ---------- player functions ------------ */ typedef struct player_t player_t, *player; typedef cnum (*ask_func)(player); typedef struct { int init_done; double prob[14]; int n_wild[14], draws[14]; } ai_record_t;

enum { init_deal, opponent_has, opponent_hasnot, opponent_draw, self_draw, book_down }; void tell_ai(player, int action, cnum n);

cnum human_ask(player); cnum computer_ask(player);

struct player_t { player opponent; card cards[52]; cnum books[14]; int n_books, n_cards; const char *name; ask_func ask; ai_record_t * ai; };

void player_sort_hand(player p) { qsort(p->cards, p->n_cards, sizeof(card), card_cmp); }

void player_add_card(player p, card c) { p->cards[p->n_cards++] = c; player_sort_hand(p); }

card player_remove_card(player p, int i) { card c = p->cards[i]; memmove(p->cards + i, p->cards + i + 1, (p->n_cards - i) * sizeof(card)); p->n_cards--; return c; }

card player_draw_card(player p, deck d) { card c = card_deal(d); if (!c) return 0; printf(p->ai ? "* %s drew a card\n" : "* %s went fishing and got a %ls\n", p->name, c->name); player_add_card(p, c); tell_ai(p, self_draw, c->num); tell_ai(p->opponent, opponent_draw, 0); return c; }

int player_has(player p, cnum n) { int i; for (i = 0; i < p->n_cards; i++) if (p->cards[i]->num == n) return 1; return 0; }

int player_book_check(player p) { int i, j; cnum c; for (i = 0; i < p->n_cards - 3; i++) if (p->cards[i]->num == p->cards[i + 3]->num) break; if (i >= p->n_cards - 3) return 0;

printf("* %s put down book of %lc\n", p->name, snum[ p->cards[i]->num ]); c = p->books[p->n_books++] = p->cards[i]->num; for (j = 0; j < 4; j++) player_remove_card(p, i); tell_ai(p, book_down, c); tell_ai(p->opponent, book_down, c); return 1; }

/* ---------- game stuff -------------*/ typedef struct { player_t player[2]; deck_t deck; int current; /* whose turn is it */ ai_record_t puter_knows; } game_t, *game;

int game_move(game g); void game_human_move(game, player);

void game_new(game g, int first) { int i; memset(g, 0, sizeof(game_t)); card_init_deck(&g->deck); g->current = !!first;

player puter = g->player, human = g->player + 1;

puter->ai = &g->puter_knows; puter->opponent = human; puter->name = "Puter"; puter->ask = computer_ask;

human->ask = human_ask; human->opponent = puter; human->name = "You";

for (i = 0; i < 9; i++) { player_draw_card(puter, &g->deck); player_draw_card(human, &g->deck); } puter->ai->init_done = 1; tell_ai(puter, init_deal, 0); while(game_move(g)); }

void game_display(game g) { int i; player p1 = g->player, p2 = p1 + 1;

xy(0, 0); clear(); for (i = 1; i < 14; i++) printf("[%lc]%.1f ", snum[i], g->player[0].ai->prob[i]); printf("\n");

//xy(0, 0); clear(); printf("[ %s ]", p1->name); xy(0, 1); printf("Cards:"); for (i = 0; i < p1->n_cards; i++) printf("%ls", L" ▒ ");

xy(0, 2); printf("Books:"); for (i = 0; i < p1->n_books; i++) printf(" %lc", snum[ p1->books[i] ]);

xy(7, 4); printf("Deck: "); printf(g->deck.n ? "%d cards" : "empty", g->deck.n);

xy(0, 6); printf("Books:"); for (i = 0; i < p2->n_books; i++) printf(" %lc", snum[ p2->books[i] ]);

xy(0, 7); printf("Cards:"); for (i = 0; i < p2->n_cards; i++) printf(" %ls", p2->cards[i]->name);

xy(0, 8); printf("[ %s ]", p2->name); xy(0, 9); for (i = 0; i < 35; i++) printf("%lc", L'─'); xy(0, 10); printf("Current move: %s.", g->player[g->current].name); fflush(stdout); }

void game_transfer_cards(game g, player from, player to, cnum n) { int i; card c; printf("* %s gave %s", from->name, to->name); for (i = 0; i < from->n_cards && from->cards[i]->num != n; i++); while (i < from->n_cards && from->cards[i]->num == n) { c = player_remove_card(from, i); player_add_card(to, c); printf(" %ls", c->name); } printf("\n"); tell_ai(to, opponent_hasnot, n);

player_book_check(to); if (!from->n_cards) player_draw_card(from, &g->deck); if (!to->n_cards) player_draw_card(to, &g->deck); }

int game_move(game g) { cnum req; int i; player p = g->player + g->current; player o = p->opponent;

game_display(g); for (i = 0; i < 2; i++) { if (g->player[i].n_books >= 7) { xy(0, 10); clear(); printf("%s won!\n", g->player[i].name); anykey(); return 0; } }

if (p->ask) { req = p->ask(p); tell_ai(o, opponent_has, req);

xy(0, 10); clear(); printf("%s: \"Got any %lc?\"\n", p->name, snum[ req ]); xy(0, 11); printf("%s: ", o->name); if (player_has(o, req)) { printf("\"Yes.\"\n"); game_transfer_cards(g, o, p, req); anykey(); return 1; } else { tell_ai(p, opponent_hasnot, req); printf("\"Go fish.\"\n"); if (!g->deck.n) printf("* But %s can't go fish because deck is empty\n", p->name); else { player_draw_card(p, &g->deck); player_book_check(p); } } if (!p->n_cards) player_draw_card(p, &g->deck); if (!o->n_cards) player_draw_card(o, &g->deck); anykey(); }

g->current = !g->current; return 1; }

/* let human request a card from opponent */ cnum human_ask(player p) { int i, c; do { xy(0, 11); clear(); printf("You may ask for"); for (i = 1; i < 14; i++) if (player_has(p, i)) printf(" [%lc]", snum[i]); c = toupper(getkey(". Your choice")); for (i = 1; i < 14; i++) { if (c != snum[i]) continue; if (!player_has(p, i)) break; return i; } xy(0, 13); printf(i < 14 ? "You can't ask for that card. " : "Dude, that's not a card. "); } while(anykey()); return 0; }

/* ------------- AI stuff ------------ */ cnum computer_ask(player p) { int i, j = 1; cnum r = 0; double prob = 0; for (i = 1; i < 14; i++) { if (!player_has(p, i)) continue; if (p->ai->prob[i] > prob) { r = i; prob = p->ai->prob[i]; } j++; }

return r; }

void check_opponent_draw(player p) { ai_record_t *ai = p->ai; int sum = 0, i, j; double ch;

if (!ai) return;

for (i = 1; i < 14; i++) { if (ai->prob[i] < 0 || ai->prob[i] >= 1) continue; sum += ai->n_wild[i]; } for (i = 1; i < 14; i++) { if (ai->prob[i] < 0 || ai->prob[i] >= 1) continue; if (!ai->n_wild[i]) continue; ch = 1; ai->draws[i] ++; for (j = 0; j < ai->draws[i] && j < sum; j++) ch *= (sum - j - ai->n_wild[i]) * 1.0 / (sum - j); ai->prob[i] = 1 - ch; } }

void tell_ai(player p, int action, cnum n) { int i; ai_record_t *ai = p->ai; if (!ai) return; if (!ai->init_done) return;

/* count cards */ for (i = 1; i < 14; i++) ai->n_wild[i] = 4; for (i = 0; i < p->n_cards; i++) ai->n_wild[ p->cards[i]->num ]--; for (i = 0; i < p->n_books; i++) ai->n_wild[ p->books[i] ] = 0; for (i = 0; i < p->opponent->n_books; i++) ai->n_wild[ p->opponent->books[i] ] = 0;

if (action == init_deal) { for (i = 0; i < 9; i++) check_opponent_draw(p); return; }

if (action == book_down) { ai->prob[n] = -1; ai->n_wild[n] = 0; return; }

if (action == opponent_hasnot) { ai->prob[n] = 0; ai->draws[n] = 0; return; }

if (action == opponent_has) { ai->prob[n] = 1; return; }

if (action == self_draw) { --ai->n_wild[n]; return; }

if (action == opponent_draw) { check_opponent_draw(p); } }

int main() { game_t g; setlocale(LC_CTYPE, ""); srand(time(0)); game_new(&g, 1); game_display(&g); return 0; }</lang>