|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T h
Length: 34560 (0x8700) Types: TextFile Names: »heartsd.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987 └─⟦this⟧ »EUUGD18/General/Hearts/heartsd.c«
/* * dealer - more commonly known as heartsd * * Smarts of hearts program. Runs in background when invoked by initial * caller of hearts. Contains all logic for communicating with other * hearts players. Handles all computer players, and communicates play * of others to other players. * * Computer strategy originally written in Pascal by: * Don Baccus of Oregon Software. * Strategy mods by: * Jeff Hemmerling of Test Systems Strategies, Inc. * * Converted strategy to C, added curses and multi-player (sockets) * interface by: * Bob Ankeney of Generic Computer Products * * Thanks to Keith Packard, for his invaluable (is that the same as * unvaluable?) assistance as a unix guru, for design suggestions, and * (I suppose) for convincing me to rewrite the bloody thing. * * Bug reports to Bob Ankeney at: * ...!tektronix!reed!bob * */ #include "misc.h" #include "defs.h" #include "local.h" #define BUF_SIZE 64 #define FUNNY_FACTOR 2 #define NPASS 3 /* # cards to pass */ #define PLAYERS 4 /* # players total */ #define NCARDS (52 / PLAYERS) #define ACE 13 #define KING 12 #define QUEEN 11 #define JACK 10 #define HEARTS_PANIC 4 #define PANIC_RATIO 0.85 #define COMPUTER 1 #define HUMAN 2 #define VOYEUR 3 int debug = 0; int num_humans, /* # HUMAN players */ human_type, /* HUMAN or VOYEUR */ player_mask; /* Bit n+1 is set for each human player n */ char someone_is_shooting, possibly_shooting, queen_played, safe_suit[MAX_SUIT + 1]; typedef struct node *ptr; struct card { int suit, rank; }; struct node { ptr llink, rlink; int rank; }; struct suit_list { ptr head, tail; int length; }; typedef struct suit_list card_hand[MAX_SUIT + 1]; struct player_status { card_hand cards; int pts, totalpts; int passer; struct card passed_cards[NPASS + 1]; int num_passed; int player_kind; int socket; char name[9]; }; struct player_status card_table[PLAYERS + 1]; card_hand cards_played; struct card deck[53]; struct sockaddr_in sockaddr; int player_count, leader, round, hand, points_played, shooter; char hearts_broken; struct { int card_count, highest_played, high_player, suit_led, pts; char any_hearts; struct card played[PLAYERS + 1]; } trick; char *snames[] = { "", "clubs", "diamonds", "hearts", "spades" }, rnames[] = " 23456789TJQKA", *comp_names[] = { "", "zeppo", "chico", "harpo", "groucho" }; rnd(num) { extern long random(); return (random() % num); } init_deck() { int suit_temp, rank_temp, j; j = 0; for (rank_temp = MIN_RANK; rank_temp <= MAX_RANK ; rank_temp++) for (suit_temp = CLUBS; suit_temp <= SPADES; suit_temp++) { deck[++j].suit = suit_temp; deck[j].rank = rank_temp; } } init_suit(list) struct suit_list *list; { char *malloc(); ptr p, p1; list->length = 0; p = list->head = (ptr) malloc(sizeof(*p)); p1 = list->tail = (ptr) malloc(sizeof(*p1)); p->llink = NULL; p1->rlink = NULL; p->rlink = p1; p1->llink = p; p->rank = MAX_RANK + 1; p1->rank = 0; } init() { int player; srandom(getpid()); /* Init random number gen */ for (player = 1; player <= PLAYERS; player++) { card_table[player].player_kind = COMPUTER; (void) strcpy(card_table[player].name, comp_names[player]); card_table[player].socket = -1; } num_humans = 0; } new_game() { int suit, player; init_deck(); for (player = 1; player <= PLAYERS; player++) { card_table[player].totalpts = 0; for (suit = CLUBS; suit <= SPADES; suit++) init_suit(&card_table[player].cards[suit]); } for (suit = CLUBS; suit <= SPADES; suit++) init_suit(&cards_played[suit]); } get_rank(rch) char rch; { int i; for (i = 1; i <= MAX_RANK; i++) if (rch == rnames[i]) return(i); return(0); } get_suit(sch) char sch; { int i; for (i = 1; i <= MAX_SUIT; i++) if (sch == *snames[i]) return(i); return(0); } char * get_name(player_num) int player_num; { int i; char unique_name = TRUE; static char pname[16]; for (i = 1; i <= PLAYERS; i++) if ((i != player_num) && !strcmp(card_table[i].name, card_table[player_num].name)) unique_name = FALSE; if (unique_name) (void) strcpy(pname, card_table[player_num].name); else (void) sprintf(pname, "%s (%d)", card_table[player_num].name, player_num); return(pname); } /* * Get a card from somebody. get_mask is a bit mask specifying who is * acceptable to get a card from (i.e., bit n states player n can send * a card.) Scans all sockets for input, handling messages and player * leaving signals. Returns when one of the specified players passes * a card. Returns 0 if one of the specified players exits the game. */ get_card(get_mask, from, buf) int get_mask, *from; char *buf; { int i, player, ret; char mesg_buf[64]; fd_type read_fd; for (;;) { fd_init(4, &read_fd); /* Watch for new arrivals */ for (player = 1; player <= PLAYERS; player++) if (card_table[player].player_kind != COMPUTER) fd_set(card_table[player].socket, &read_fd); if (select(WIDTH, &read_fd, (fd_type *) 0, (fd_type *) 0, (struct timeval *) 0)) { if (fd_isset(4, read_fd)) /* New arrival? */ add_player(); for (player = 1; player <= PLAYERS; player++) /* This player send? */ if (fd_isset(card_table[player].socket, read_fd)) { ret = read_socket(card_table[player].socket, buf); *from = player; if (ret && (buf[0] == 'M')) { (void) sprintf(mesg_buf, "M%d%s: %s", MESG_WINDOW, get_name(player), buf + 1); send_to_all(mesg_buf); /* Message to everyone */ } else { if (!ret) { if (card_table[player].player_kind == HUMAN) player_mask &= ~(1 << player); /* player went away */ card_table[player].player_kind = COMPUTER; (void) close(card_table[player].socket); if (--num_humans == 0) exit (0); /* give up if nobody wants to play (sniff) */ for (i = 1; i <= PLAYERS; i++) { if (card_table[i].player_kind != COMPUTER) { (void) sprintf(buf, "M%d%s vanishes in a puff of greasy black smoke!", MESG_WINDOW, get_name(player)); /* Announce deletion */ send_buf(i, buf); /* to others */ (void) sprintf(buf, /* * Inform dealer. */ "p%d<empty>", player - 1); write_socket(3, buf); } } (void) strcpy(card_table[player].name, comp_names[player]); send_all_totals(); } if (get_mask & (1 << player)) return(ret); } } } } } toss_card(player, card_to_toss, suit_to_toss) int player, suit_to_toss; ptr card_to_toss; { char buf[BUF_SIZE]; (void) sprintf(buf, "R%c%c", rnames[card_to_toss->rank], *snames[suit_to_toss]); send_buf(player, buf); /* Tell player to remove card */ } read_card(read_mask, player, card_to_play, suit_to_play) int read_mask, *player, *suit_to_play; ptr *card_to_play; { char buf[BUF_SIZE]; int rank, suit; ptr p; char rank_ch, suit_ch; if (get_card(read_mask, player, buf)) { do { rank_ch = buf[1]; suit_ch = buf[2]; rank = get_rank(rank_ch); suit = get_suit(suit_ch); erase_window(*player, TEXT_WINDOW); p = card_table[*player].cards[suit].head->rlink; while (p && (p->rank != rank)) p = p->rlink; if (p == NULL) { (void) sprintf(buf, "M%dPlay a card you have:", TEXT_WINDOW); send_buf(*player, buf); send_buf(*player, "G"); if (!get_card(read_mask, player, buf)) return(0); } } while (p == NULL); *card_to_play = p; *suit_to_play = suit; return(1); } return(0); } send_card(player, rank, suit) int player, rank, suit; { char buf[BUF_SIZE]; (void) sprintf(buf, "A%c%c", rnames[rank], *snames[suit]); send_buf(player, buf); } /* * send cards in hand to player */ send_hand(player) int player; { int suit; ptr p; for (suit = CLUBS; suit <= SPADES; suit++) { /* send cards in hand */ p = card_table[player].cards[suit].head->rlink; while (p->rank) { send_card(player, p->rank, suit); p = p->rlink; } } } get_first_player() { fd_type read_fd; /* main socket bit mask */ /* * wait for new player to show up */ fd_init(4, &read_fd); /* Wait for someone to show up */ if (select(WIDTH, &read_fd, (fd_type *) 0, (fd_type *) 0, (struct timeval *) 0) == -1) { perror("select"); exit(1); } } add_player() { int new_socket; /* new file descriptor */ char pname[128], buf[128]; struct sockaddr_in sockad; int ssize; /* to make accept happy */ int new_player = 0; int i; /* * add whoever's waiting */ ssize = sizeof (sockad); if ((new_socket = accept(4, &sockad, &ssize)) == -1) { perror("accept"); exit(1); } /* get user name */ if (read_socket(new_socket, pname) > 0) { pname[8] = '\0'; /* Name must be less than 9 chars */ for (i = PLAYERS; i >= 1; i--) if (card_table[i].player_kind == COMPUTER) new_player = i; } if (new_player) { /* * Inform distributor */ (void) sprintf(buf, "p%d%s", new_player - 1, pname); write_socket(3, buf); card_table[new_player].player_kind = human_type; card_table[new_player].socket = new_socket; (void) strcpy(card_table[new_player].name, pname); ++num_humans; if (human_type == VOYEUR) { (void) sprintf(buf, "M%d Game in progress...", PLAY_WINDOW); send_buf(new_player, buf); (void) sprintf(buf, "M%dYou may watch till next hand.", MESG_WINDOW); send_buf(new_player, buf); } else player_mask |= 1 << new_player; for (i = 1; i <= PLAYERS; i++) { if (card_table[i].player_kind != COMPUTER) { (void) sprintf(buf, "M%d%s added as player %d", MESG_WINDOW, pname, new_player); if (i != new_player) /* Announce addition */ send_buf(i, buf); send_totals(i); } } send_hand(new_player); } else { (void) sprintf(buf, "M%dAll seats are full!", MESG_WINDOW); write_socket(new_socket, buf); write_socket(new_socket, "X"); (void) close(new_socket); } } erase_window(player, which_window) int player, which_window; { char buf[BUF_SIZE]; (void) sprintf(buf, "E%d", which_window); send_buf(player, buf); } erase_all_window(which_window) int which_window; { int player; for (player = 1; player <= PLAYERS; player++) if (card_table[player].player_kind != COMPUTER) erase_window(player, which_window); } send_totals(player) int player; { int i; char buf[BUF_SIZE]; if (card_table[player].player_kind != COMPUTER) { for (i = 1; i <= PLAYERS; i++) { (void) sprintf(buf, "S%d%02d%02d%s", i, card_table[i].pts, card_table[i].totalpts, card_table[i].name); send_buf(player, buf); } } } send_all_totals() { int player; for (player = 1; player <= PLAYERS; player++) send_totals(player); } send_all_winner() { int max_score, player, shooter; char buf[BUF_SIZE]; (void) sprintf(buf, "M%d--------------------------------------------------------", MESG_WINDOW); send_to_all(buf); max_score = 0; for (player = 1; player <= PLAYERS; player++) { if (card_table[player].pts > max_score) { max_score = card_table[player].pts; shooter = player; } } if (max_score == 26) { for (player = 1; player <= PLAYERS; player++) if (player != shooter) card_table[player].totalpts += 26; } else { for (player = 1; player <= PLAYERS; player++) card_table[player].totalpts += card_table[player].pts; } if (max_score == 26) (void) sprintf(buf, "M %s wins by shooting the moon!", get_name(shooter)); else { get_winner(buf, FALSE); (void) strcat(buf, "."); } buf[1] = MESG_WINDOW + '0'; send_to_all(buf); } send_final_winner() { char buf[64]; get_winner(buf, TRUE); buf[1] = PLAY_WINDOW + '0'; (void) strcat(buf, " total!"); erase_all_window(PLAY_WINDOW); send_to_all(buf); } /* * Get buffer of form: "M groucho wins with nn points" * or: "M groucho and chico tie with nn points" */ get_winner(buf, use_totalpts) char *buf; char use_totalpts; { int player, winning_score, temp, scores[PLAYERS + 1], players[PLAYERS + 1]; char sorted; for (player = 1; player <= PLAYERS; player++) { scores[player] = use_totalpts ? card_table[player].totalpts : card_table[player].pts; players[player] = player; } do { /* Bubble sort the bloody thing */ sorted = TRUE; for (player = 1; player < PLAYERS; player++) if (scores[player + 1] < scores[player]) { temp = scores[player]; scores[player] = scores[player + 1]; scores[player + 1] = temp; temp = players[player]; players[player] = players[player + 1]; players[player + 1] = temp; sorted = FALSE; } } while (!sorted); winning_score = scores[1]; (void) sprintf(buf, "M %s", card_table[players[1]].name); for (player = 2; (player <= PLAYERS) && (scores[player] == winning_score); player++) (void) sprintf(buf + strlen(buf), " and %s", card_table[players[player]].name); if (scores[2] == winning_score) (void) sprintf(buf + strlen(buf), " tie with %d point", winning_score); else (void) sprintf(buf + strlen(buf), " wins with %d point", winning_score); if (winning_score != 1) (void) strcat(buf, "s"); } send_buf(player, buf) int player; char *buf; { write_socket(card_table[player].socket, buf); } send_to_all(buf) char *buf; { int player; for (player = 1; player <= PLAYERS; player++) if (card_table[player].player_kind != COMPUTER) send_buf(player, buf); } new_round() { char buf[BUF_SIZE]; trick.pts = 0; trick.any_hearts = FALSE; player_count = 0; erase_all_window(PLAY_WINDOW); /* * Inform distributor */ (void) sprintf(buf, "r%d", round); write_socket(3, buf); (void) sprintf(buf, "M%dHand: %d Round: %d", ROUND_WINDOW, hand, round); send_to_all(buf); } enter_card(which_card, which_hand) struct card which_card; card_hand which_hand; { ptr p, p1; p = which_hand[which_card.suit].head; ++which_hand[which_card.suit].length; while (p->rank > which_card.rank) p = p->rlink; p1 = (ptr) malloc(sizeof(*p1)); p1->llink = p->llink; p1->llink->rlink = p1; p->llink = p1; p1->rlink = p; p1->rank = which_card.rank; } remove_node(p) ptr p; { p->llink->rlink = p->rlink; p->rlink->llink = p->llink; free((char *) p); } clear_hand(which_hand) card_hand which_hand; { int suit; ptr p, p1; for (suit = CLUBS; suit <= SPADES; suit++) { safe_suit[suit] = TRUE; which_hand[suit].length = 0; p = which_hand[suit].head->rlink; while (p->rank > 0) { p1 = p; p = p->rlink; remove_node(p1); } } safe_suit[HEARTS] = FALSE; } shuffle() { int j, k; struct card t; for (j = 52; j >= 1; j--) { k = rnd(j) + 1; t = deck[k]; deck[k] = deck[j]; deck[j] = t; } } deal() { int i, j, player; for (player = 1; player <= PLAYERS; player++) { j = NCARDS * (player - 1); for (i = 1; i <= NCARDS; i++) enter_card(deck[i+j], card_table[player].cards); if (card_table[player].player_kind != COMPUTER) send_hand(player); } } new_hand() { int player; char buf[BUF_SIZE]; points_played = 0; someone_is_shooting = possibly_shooting = FALSE; queen_played = hearts_broken = FALSE; trick.suit_led = CLUBS; for (player = 1; player <= PLAYERS; player++) { card_table[player].pts = 0; clear_hand(card_table[player].cards); } clear_hand(cards_played); erase_all_window(PLAY_WINDOW); send_all_totals(); /* * Inform distributor */ (void) sprintf(buf, "h%d", hand); write_socket(3, buf); write_socket(3, "r0"); (void) sprintf(buf, "M%dNew hand...", ROUND_WINDOW); send_to_all(buf); shuffle(); /* Shuffle three times! */ shuffle(); /* (just like vegas!) */ shuffle(); deal(); } ptr next_highest(player, rank, suit_to_play) int player, rank, suit_to_play; { ptr p; p = card_table[player].cards[suit_to_play].head->rlink; while (p && p->rank) if (p->rank < rank) return(p); else p = p->rlink; return(NULL); } #define highest(player, suit) (card_table[player].cards[suit].head->rlink) #define lowest(player, suit) (card_table[player].cards[suit].tail->llink) #define cards_out(suit) (MAX_SUIT - cards_played[suit].length) #define spades_safe(player) ((((cards_out(SPADES) / PLAYERS) + 1) < \ card_table[player].cards[SPADES].length) && safe_suit[SPADES]) #define diamonds_safe(player) (( (float) (cards_out(DIAMONDS) - \ card_table[player].cards[DIAMONDS].length) / (PLAYERS - 1.0) >= 2.0) &&\ safe_suit[DIAMONDS]) #define clubs_safe(player) (( (float) (cards_out(CLUBS) - \ card_table[player].cards[CLUBS].length) / (PLAYERS - 1.0) >= 2.0) &&\ safe_suit[CLUBS]) #define only_hearts(player) ((14 - round) == \ card_table[player].cards[HEARTS].length) find_high_card(player, card_to_play, suit_to_play, play_points) int player, *suit_to_play; char play_points; ptr *card_to_play; { int high_card, suit; ptr p; high_card = 0; *card_to_play = NULL; for (suit = CLUBS; suit <= SPADES; suit++) { p = card_table[player].cards[suit].head->rlink; if ((p->rank > high_card) && (play_points || (point_value(p->rank, suit) == 0))) { high_card = p->rank; *card_to_play = p; *suit_to_play = suit; } } if (*card_to_play == NULL) { /* * No clubs or diamonds. Q spades is highest spade, or no * spades at all. Try to play next highest spade. */ *card_to_play = next_highest(player, QUEEN, SPADES); if (*card_to_play) *suit_to_play = SPADES; else { /* Play highest heart */ *card_to_play = highest(player, HEARTS); *suit_to_play = HEARTS; } } } find_low_card(player, card_to_play, suit_to_play) int player, *suit_to_play; ptr *card_to_play; { int low_card, suit; ptr p; low_card = MAX_RANK + 1; *card_to_play = NULL; for (suit = CLUBS; suit <= SPADES; suit++) { p = card_table[player].cards[suit].tail->llink; if ((p->rank < low_card) && (suit != HEARTS)) { low_card = p->rank; *card_to_play = p; *suit_to_play = suit; } if (*card_to_play == NULL) { *suit_to_play = HEARTS; *card_to_play = card_table[player].cards[HEARTS].tail->llink; } } } find_low_club(leader) int *leader; { int player, low_club, rank; low_club = MAX_RANK; for (player = 1; player <= PLAYERS; player++) if ((rank = card_table[player].cards[CLUBS].tail->llink->rank) < low_club) { *leader = player; low_club = rank; } } find_queen(player, card_to_play) int player; ptr *card_to_play; { ptr p; *card_to_play = NULL; p = card_table[player].cards[SPADES].head->rlink; while (p) if (p->rank == QUEEN) { *card_to_play = p; p = NULL; } else p = p->rlink; } print_pass(player) int player; { int i; char buf[BUF_SIZE]; struct card cd; erase_window(player, PLAY_WINDOW); (void) sprintf(buf, "M%d%s passes you:", LEAD_WINDOW, get_name(card_table[player].passer)); send_buf(player, buf); for (i = 1; i <= NPASS; i++) { cd = card_table[player].passed_cards[i]; send_card(player, cd.rank, cd.suit); (void) sprintf(buf, "P%d%c%c", i, rnames[cd.rank], *snames[cd.suit]); send_buf(player, buf); } } /* * Get card to pass from computer player */ get_pass(from, which_pass, card_to_pass, suit_to_pass) int from, which_pass, *suit_to_pass; ptr *card_to_pass; { ptr p1; p1 = highest(from, SPADES); if (!spades_safe(from) && (p1->rank >= QUEEN) && card_table[from].cards[SPADES].length) { *card_to_pass = p1; *suit_to_pass = SPADES; } else if (((NPASS - which_pass) >= card_table[from].cards[DIAMONDS].length - 1) && (card_table[from].cards[DIAMONDS].length > 0)) { *card_to_pass = highest(from, DIAMONDS); *suit_to_pass = DIAMONDS; } else if (((NPASS - which_pass) >= card_table[from].cards[CLUBS].length - 2) && (card_table[from].cards[CLUBS].length > 1)) { *card_to_pass = highest(from, CLUBS); *suit_to_pass = CLUBS; } else if (card_table[from].cards[HEARTS].length) { *card_to_pass = highest(from, HEARTS); *suit_to_pass = HEARTS; } else find_high_card(from, card_to_pass, suit_to_pass, TRUE); } /* * Pass card to player */ pass_to(from, who_to, which_pass, card_to_pass, suit_to_pass) int from, who_to, which_pass; ptr card_to_pass; char suit_to_pass; { char buf[BUF_SIZE]; card_table[who_to].passed_cards[which_pass].rank = card_to_pass->rank; card_table[who_to].passed_cards[which_pass].suit = suit_to_pass; --card_table[from].cards[suit_to_pass].length; card_table[who_to].passer = from; remove_node(card_to_pass); if (card_table[from].player_kind != COMPUTER) { erase_window(from, INP_WINDOW); (void) sprintf(buf, "P%d%c%c", which_pass, rnames[card_to_pass->rank], *snames[suit_to_pass]); send_buf(from, buf); toss_card(from, card_to_pass, suit_to_pass); } } #define map_player(leader, player) (((leader + player - 1) % PLAYERS) + 1) player_pass(player) int player; { char buf[BUF_SIZE]; (void) sprintf(buf, "M%dPass %d to %s:", TEXT_WINDOW, NPASS, get_name(map_player(player, hand))); send_buf(player, buf); (void) sprintf(buf, "M%dCard 1:", LEAD_WINDOW); send_buf(player, buf); send_buf(player, "G"); } pass_cards() { int player, pass, suit, old_mask; int passed_mask = 0; ptr p; char buf[BUF_SIZE]; human_type = HUMAN; for (player = 1; player <= PLAYERS; player++) { card_table[player].num_passed = 0; if (card_table[player].player_kind == HUMAN) player_pass(player); } while (player_mask != passed_mask) { old_mask = player_mask; if (read_card(player_mask, &player, &p, &suit)) { pass_to(player, map_player(player, hand), ++card_table[player].num_passed, p, suit); if (card_table[player].num_passed == NPASS) /* * Done passing */ passed_mask |= 1 << player; else { (void) sprintf(buf, "M%dCard %d:", LEAD_WINDOW, card_table[player].num_passed + 1); /* * Ask for next card */ send_buf(player, buf); send_buf(player, "G"); } } else /* * Player left game */ passed_mask &= ~(1 << player); /* * Player added? */ for (player = 1; player <= PLAYERS; player++) if (~old_mask & player_mask & (1 << player)) { card_table[player].num_passed = 0; player_pass(player); } } erase_all_window(LEAD_WINDOW); human_type = VOYEUR; /* * Let computer pass. */ for (player = 1; player <= PLAYERS; player++) if (card_table[player].player_kind != HUMAN) for (pass = ++card_table[player].num_passed; pass <= NPASS; pass++) { get_pass(player, pass, &p, &suit); pass_to(player, map_player(player, hand), pass, p, suit); } for (player = 1; player <= PLAYERS; player++) { for (pass = 1; pass <= NPASS; pass++) enter_card(card_table[player].passed_cards[pass], card_table[player].cards); if (card_table[player].player_kind != COMPUTER) print_pass(player); } } point_value(rank, suit) int rank, suit; { if (suit == HEARTS) return(1); else if ((suit == SPADES) && (rank == QUEEN)) return(13); return(0); } show(player, card_to_play, suit_to_play) int player, suit_to_play; ptr card_to_play; { int rank_to_play; char buf[BUF_SIZE]; rank_to_play = card_to_play->rank; remove_node(card_to_play); if (card_table[player].player_kind != COMPUTER) { erase_window(player, INP_WINDOW); erase_window(player, LEAD_WINDOW); toss_card(player, card_to_play, suit_to_play); } --card_table[player].cards[suit_to_play].length; if (suit_to_play == HEARTS) trick.any_hearts = TRUE; else { if ((suit_to_play == SPADES) && (rank_to_play == QUEEN)) queen_played = TRUE; } if ((suit_to_play == trick.suit_led) && (rank_to_play > trick.highest_played)) { trick.highest_played = rank_to_play; trick.high_player = player; } trick.played[player].rank = rank_to_play; trick.played[player].suit = suit_to_play; /* * If points were dumped or someone couldn't follow suit, * then the suit is unsafe. */ if ((trick.pts += point_value(rank_to_play, suit_to_play)) || (suit_to_play != trick.suit_led)) safe_suit[trick.suit_led] = FALSE; enter_card(trick.played[player], cards_played); ++trick.card_count; (void) sprintf(buf, "P%d%c%c%s", player, rnames[rank_to_play], *snames[suit_to_play], card_table[player].name); send_to_all(buf); } really_safe(suit) int suit; { char safe; int losers_out, low_rank; ptr p; p = cards_played[suit].head->rlink; low_rank = card_table[leader].cards[suit].tail->llink->rank; /* * Count # cards of _suit_ played that would be losers * to leaders lowest of _suit_. */ losers_out = 0; while (p->rank) { if (p->rank < low_rank) ++losers_out; p = p->rlink; } /* * Old strategy. losers_out = low_rank - 2 - losers_out; safe = (((card_table[leader].cards[suit].length + cards_played[suit].length + losers_out) != 13) || safe_suit[suit]); */ /* * Card is guaranteed to lose the trick if: * -- All cards of suit lower than low_rank have been played; * -- There is at least one other card of the suit unplayed. * * Using this strategy means safe_suit[] is unused. */ safe = (losers_out == (low_rank - 1)) && ((card_table[leader].cards[suit].length + cards_played[suit].length) != 13); if (suit == SPADES) safe &= ((card_table[leader].cards[SPADES].tail->llink->rank < QUEEN) || queen_played); return(safe); } find_low_lead(card_to_play, suit_to_play, play_hearts) ptr *card_to_play; int *suit_to_play; char play_hearts; { char last_gasp; int low_card, suit; ptr p; low_card = MAX_RANK + 1; last_gasp = FALSE; *card_to_play = NULL; while (*card_to_play == NULL) { for (suit = CLUBS; suit <= SPADES; suit++) { p = card_table[leader].cards[suit].tail->llink; if ((p->rank < low_card) && (play_hearts || (suit != HEARTS)) && (really_safe(suit) || last_gasp)) { low_card = p->rank; *card_to_play = p; *suit_to_play = suit; } } last_gasp = TRUE; } } show_lead(card_to_play, suit_to_play) int suit_to_play; ptr card_to_play; { trick.card_count = 1; trick.highest_played = card_to_play->rank; trick.suit_led = suit_to_play; trick.high_player = leader; show(leader, card_to_play, suit_to_play); } read_lead(leader, card_to_play, suit_to_play) int leader, *suit_to_play; ptr *card_to_play; { char good, alive, buf[64]; int t; /* Ignored */ do { send_buf(leader, "G"); if (alive = read_card(1 << leader, &t, card_to_play, suit_to_play)) { good = (((round != 1) || (*card_to_play == card_table[leader].cards[CLUBS].tail->llink)) && ((*suit_to_play != HEARTS) || hearts_broken || only_hearts(leader))); if (!good) { if (*suit_to_play == HEARTS) (void) sprintf(buf,"M%dHearts not broken yet!", TEXT_WINDOW); else (void) sprintf(buf,"M%dLead your lowest club!", TEXT_WINDOW); send_buf(leader, buf); send_buf(leader, "G"); } } } while (alive && !good); } read_next_card(player, card_to_play, suit_to_play) int player, *suit_to_play; ptr *card_to_play; { char good, good_pts, alive, buf[64]; int t; /* Ignored */ do { send_buf(player, "G"); if (alive = read_card(1 << player, &t, card_to_play, suit_to_play)) { good_pts = (((*suit_to_play != SPADES) || ((*card_to_play)->rank != QUEEN)) && ((*suit_to_play != HEARTS) || only_hearts(player)) || (round != 1)); good = (((*suit_to_play == trick.suit_led) || (card_table[player].cards[trick.suit_led].length == 0)) && good_pts); if (!good) { if (!good_pts) (void) sprintf(buf, "M%dCan't dump points yet!", TEXT_WINDOW); else (void) sprintf(buf, "M%dTry following suit!", TEXT_WINDOW); send_buf(player, buf); send_buf(player, "G"); } } } while (alive && !good); } computer_lead(card_to_play, suit_to_play) int *suit_to_play; ptr *card_to_play; { ptr p; if (round == 1) { /* * Lead the 2 of clubs */ *card_to_play = card_table[leader].cards[CLUBS].tail->llink; *suit_to_play = CLUBS; } else { find_queen(leader, &p); if (!queen_played && (p == NULL) && spades_safe(leader) && card_table[leader].cards[SPADES].length) { *suit_to_play = SPADES; *card_to_play = next_highest(leader, QUEEN, SPADES); } else { *card_to_play = NULL; /* * Old strategy if (diamonds_safe(leader) && (card_table[leader].cards[DIAMONDS].length)) { *suit_to_play = DIAMONDS; *card_to_play = highest(leader, DIAMONDS); } else if (clubs_safe(leader) && (card_table[leader].cards[CLUBS].length)) { *suit_to_play = CLUBS; *card_to_play = highest(leader, CLUBS); } */ } if (*card_to_play == NULL) find_low_lead(card_to_play, suit_to_play, (hearts_broken || only_hearts(leader))); } } #define max(a,b) ((a > b) ? a : b) pick_a_loser(player, card_to_play, suit_to_play) int player, *suit_to_play; ptr *card_to_play; { ptr p; if (card_table[player].cards[trick.suit_led].length) { *suit_to_play = trick.suit_led; if ((trick.suit_led != SPADES) && (trick.pts == 0) && (trick.card_count == PLAYERS)) /* * Play the highest card of suit led if player is last * and there are no points in current trick. */ *card_to_play = highest(player, trick.suit_led); else switch (trick.suit_led) { case SPADES: if ((trick.card_count == PLAYERS) && (trick.pts == 0)) { /* * Play the highest spade. Don't play queen if * it will win the trick. */ *card_to_play = highest(player, SPADES); if (((*card_to_play)->rank == QUEEN) && (QUEEN > trick.highest_played)) *card_to_play = next_highest(player, QUEEN, SPADES); } else if (spades_safe(player) && !queen_played) { find_queen(player, &p); *card_to_play = highest(player, SPADES); if ((p == NULL) || (p == *card_to_play)) *card_to_play = next_highest(player, max(trick.highest_played, QUEEN), SPADES); } break; case CLUBS: if (clubs_safe(player)) *card_to_play = highest(player, CLUBS); break; case DIAMONDS: if (diamonds_safe(player)) *card_to_play = highest(player,DIAMONDS); break; case HEARTS: break; } if (*card_to_play == NULL) *card_to_play = next_highest(player, trick.highest_played, *suit_to_play); if (*card_to_play == NULL) { *card_to_play = card_table[player].cards[trick.suit_led].tail->llink; /* * Don't play the Q spades if: * 1. The Queen is your lowest spade and * 2. You have a higher spade. */ if (((*card_to_play)->rank == QUEEN) && (trick.suit_led == SPADES) && (card_table[player].cards[trick.suit_led].length > 1)) *card_to_play = card_table[player].cards[trick.suit_led].head->rlink; } } else { if (round != 1) { /* * Play the queen of spades if player has it. */ if (*card_to_play) *suit_to_play = SPADES; if ((*card_to_play == NULL) && (card_table[player].cards[HEARTS].length)) { if (possibly_shooting) { *card_to_play = next_highest(player, JACK, HEARTS); if (*card_to_play == NULL) *card_to_play = lowest(player, HEARTS); } else *card_to_play = highest(player, HEARTS); *suit_to_play = HEARTS; } } if (*card_to_play == NULL) find_high_card(player, card_to_play, suit_to_play, (round != 1)); } } stop_the_bastard(player, card_to_play, suit_to_play) int player, *suit_to_play; ptr *card_to_play; { if (card_table[player].cards[trick.suit_led].length) { *suit_to_play = trick.suit_led; if ((trick.suit_led == SPADES) && (!queen_played)) { *card_to_play = next_highest(player, QUEEN, SPADES); if (*card_to_play == NULL) *card_to_play = lowest(player, SPADES); } else *card_to_play = highest(player, trick.suit_led); if ((trick.pts < 13) && !(((cards_out(trick.suit_led) < (PLAYERS - player_count)) || trick.any_hearts) && ((*card_to_play)->rank > trick.highest_played) && (shooter == trick.high_player))) *card_to_play = lowest(player, trick.suit_led); } else if ((shooter != trick.high_player) && (card_table[player].cards[HEARTS].length > 0)) { *card_to_play = highest(player, HEARTS); *suit_to_play = HEARTS; } else find_low_card(player, card_to_play, suit_to_play); } computer_pick(player, card_to_play, suit_to_play) int player, *suit_to_play; ptr *card_to_play; { *card_to_play = NULL; if (someone_is_shooting && (player != shooter)) stop_the_bastard(player, card_to_play, suit_to_play); else pick_a_loser(player, card_to_play, suit_to_play); } lead(leader) int leader; { int suit_to_play; ptr card_to_play; char buf[BUF_SIZE]; if (card_table[leader].player_kind == HUMAN) { (void) sprintf(buf, "M%dYour lead:", LEAD_WINDOW); send_buf(leader, buf); read_lead(leader, &card_to_play, &suit_to_play); } if (card_table[leader].player_kind != HUMAN) /* If player left */ computer_lead(&card_to_play, &suit_to_play); show_lead(card_to_play, suit_to_play); } play_next_card(player) int player; { int suit_to_play; ptr card_to_play; char buf[BUF_SIZE]; if (card_table[player].player_kind == HUMAN) { (void) sprintf(buf, "M%dYour play:", LEAD_WINDOW); send_buf(player, buf); read_next_card(player, &card_to_play, &suit_to_play); } if (card_table[player].player_kind != HUMAN) /* If player left */ computer_pick(player, &card_to_play, &suit_to_play); show(player, card_to_play, suit_to_play); } find_winner(winner) int *winner; { *winner = trick.high_player; if (trick.any_hearts) hearts_broken = TRUE; points_played += trick.pts; card_table[*winner].pts += trick.pts; possibly_shooting = ((points_played > 0) && (card_table[*winner].pts == points_played)); someone_is_shooting = (possibly_shooting && (card_table[*winner].pts >= HEARTS_PANIC)); if (someone_is_shooting) shooter = *winner; } main() { int player; init(); new_game(); human_type = HUMAN; get_first_player(); add_player(); for (;;) { for (hand = 1; hand <= PLAYERS; hand++) { new_hand(); if (hand != PLAYERS) pass_cards(); find_low_club(&leader); for (round = 1; round <= NCARDS; round++) { new_round(); lead(leader); for (player_count = 1; player_count <= PLAYERS - 1; player_count++) play_next_card(map_player(leader, player_count)); find_winner(&leader); send_all_totals(); } send_all_winner(); for (player = 1; player <= PLAYERS; player++) if (card_table[player].player_kind == VOYEUR) { card_table[player].player_kind = HUMAN; player_mask |= 1 << player; } } send_all_totals(); send_final_winner(); send_to_all("X"); new_game(); } }