games-request@tekred.TEK.COM (11/05/87)
Submitted by: bob@reed (Mythical Bob Ankeney) Comp.sources.games: Volume 2, Issue 82 Archive-name: hearts/Part01 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 1 (of 2)." # Contents: README MANIFEST NOTES dist.c heartsd.c opensock.c # Wrapped by billr@tekred on Thu Nov 5 10:21:00 1987 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f README -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"README\" else echo shar: Extracting \"README\" \(1209 characters\) sed "s/^X//" >README <<'END_OF_README' X This is the initial release of multi-player hearts. Hearts is just Xanother brain-damaged card game. This version supports multiple players Xacross a network (hopefully) and also multiple games. Users can select Xwhich game to join via a hearts distributor menu that shows games in Xprogress and their current status. X Hearts has been tested on a 4.3 BSD system and on a 4.1 BSD-based Xperversion of Unix. Unfortunately, it has not been tested across a network Xdue to lack of access to such an arrangement. Oh, well... I'm sure I'll Xhear about it if it doesn't work. X In a future release I plan to rewrite the computer strategy and add a Xhigh score list option to the menu. Not too sure what the form of the high Xscore list will take; perhaps top 10 averages for a given period or something. XI'm open to suggestions on that. X I'm releasing this version now, to get any bug reports out of the way Xbefore I release the next version, and because I'm not sure when I'll get Xaround to implementing the above-mentioned mods. I imagine it will partly Xbe up to the response I receive from this version. X X Send gripes, bug-fixes, amazing stories, etc. to: X Bob Ankeney X ...!tektronix!reed!bob X END_OF_README if test 1209 -ne `wc -c <README`; then echo shar: \"README\" unpacked with wrong size! fi # end of overwriting check fi if test -f MANIFEST -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"MANIFEST\" else echo shar: Extracting \"MANIFEST\" \(624 characters\) sed "s/^X//" >MANIFEST <<'END_OF_MANIFEST' X File Name Archive # Description X----------------------------------------------------------- X INSTALL 2 X MANIFEST 1 This shipping list X Makefile 2 X NOTES 1 X README 1 X connect.c 2 X defs.h 2 X dist.c 1 X hearts.c 2 X hearts.instr 2 X heartsd.c 1 X local.proto 2 X misc.h 2 X opensock.c 1 X select.c 2 X sockio.c 2 X start_dist.c 2 END_OF_MANIFEST if test 624 -ne `wc -c <MANIFEST`; then echo shar: \"MANIFEST\" unpacked with wrong size! fi # end of overwriting check fi if test -f NOTES -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"NOTES\" else echo shar: Extracting \"NOTES\" \(3578 characters\) sed "s/^X//" >NOTES <<'END_OF_NOTES' X Hearts consists of basically 3 parts: X1) The hearts distributor which keeps track of games in progress, and X invokes new games (dealers) upon request. X2) The hearts dealer, which is the smarts of hearts, handling the actual X deal, play, and computer strategy. X3) The hearts player, which is essentially an ignorant interface for each X human player to the hearts dealer. The dealer passes messages to the X player about what to display, and the player has a curses implemented X windowing interface for displaying those messages. Messages include X such information as what's in your hand, what's been played, the current X score, etc. Messages passed back to the dealer include what card to play, X and messages to send to other players. X X X The initial invocation of hearts forks off the distributor, which hangs Xaround till nobody needs it. The distributor passes info to the select_game Xportion of the hearts player showing games in progress, which are updated as Xgames progress. The player may join any existing game, or start a new game, Xat which point the distributor forks off a new dealer, complete with a good Xsocket port to listen for hearts players on. The port # of the desired game Xis passed back to the hearts player, which then connects to the dealer, and Xplay begins. X X Modifications to dump replace the cursed curses implementation should be Xeasy as all the code is inside hearts.c. Perhaps if I ever get X windows Xrunning on my workstation... X X Possible bugs and problems: X X If 2 people invoke hearts at the same time, it is possible that they will Xboth try to invoke the distributor. The second one will fail, at which point Xan error message will be printed. I've considered a few methods of dealing Xwith this. For one thing, once a player has connected to the dealer, the Xdistributor socket is closed. When all dealers and players are gone, the Xdistributor exits. Otherwise it is sitting in a select loop waiting for new Xplayer connections, or status information from the dealers (hand and round Xnumbers, players joined or left). Perhaps the distributor should hang around. XMaybe have it time out after, say, 30 minutes, then leave. Or, the fix I'm Xconsidering is having each hearts player do an exclusive lock on a file when Xstarted. When successful, the player will try to connect to the distributor. XIf successful, fine, else the distributor is invoked, and when ready for Xconnections, then free the lock. Thus is guaranteed no two players will try Xto start the distributor. Suggestions for how best to deal with this problem Xare welcome! X The strategy for the game came from an old Pascal program by Don Backus Xof Oregon Software, with "improvements" by Jeff Hemmerling of Tessi. I'm not Xtoo sure the improvements help that much in that they do fix some bad play Xproblems, but at the same time make it a bit easier to shoot, and seem to Xreduce the incidence of the computer accidentally shooting the moon. There Xat present is no algorithm for the computer to shoot, tho miraculously, it Xon occasion succeeds! I'm not to familiar with the rules of hearts, but I Xsee no rules regarding dumping hearts the first round or leading hearts when Xnot broken. The latter causes a potential problem if hearts have not been Xbroken yet, and the leader has only hearts left. If these rules are common, Xor desirable, let me know as I'm considering tossing them from the new Xstrategy I'm writing. X X I've been too lazy to write a man page for it. Maybe by next release... X X Bob Ankeney X ...!tektronix!reed!bob X END_OF_NOTES if test 3578 -ne `wc -c <NOTES`; then echo shar: \"NOTES\" unpacked with wrong size! fi # end of overwriting check fi if test -f dist.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"dist.c\" else echo shar: Extracting \"dist.c\" \(9988 characters\) sed "s/^X//" >dist.c <<'END_OF_dist.c' X/* X * hearts_dist - distributor for hearts X * X * Keeps track of games in progress and allows new players to select which X * game to join or start a new game. X * X * X * Commands to hearts player: X * tn Following info regards table n (n is unique identifier). X * hn Now playing hand n. X * rn Now playing round n. X * pn<name> Player n is <name>. X * X * xn Table n has exited (game over). X * g Get table number to join. X * cn Connect to dealer on port n. X * X * Commands from hearts player: X * jn Join table n. X * n Start new game. X * X * Commands to dealer: X * none yet?!? X * X * Commands from dealer: X * hn Now playing hand n. X * rn Now playing round n. X * pn<name> Player n is <name>. X * X */ X X#include "misc.h" X#include "defs.h" X#include "local.h" X Xtypedef struct table *table_ptr; X Xstruct table { X table_ptr next_table; /* Points to next table entry */ X int table_id; /* Unique identifier */ X char player_name[4][9]; /* Name of players at table */ X int hand; /* Current dealer hand (1..4) */ X int round; /* Current round (1..13) */ X int port_num; /* Port # assigned */ X int socket; /* File descriptor for dealer */ X int pid; /* Process id for dealer */ X}; X Xtypedef struct empty_table *empty_ptr; X Xstruct empty_table { X empty_ptr next_empty; X int port_num; /* Available port # */ X}; X Xtypedef struct new_comer *new_ptr; X Xstruct new_comer { X new_ptr next_new_comer; X int socket; /* File descriptor */ X}; X Xtable_ptr first_table; Xempty_ptr first_empty; Xnew_ptr first_new_comer; X Xint main_sock, X next_port, /* Next port to assign */ X unique_id; X X/* X * clear_table - dealer went away; drop that table. X */ Xclear_table(tbl_ptr) Xtable_ptr tbl_ptr; X{ X table_ptr prev_ptr = NULL; X table_ptr cur_ptr; X empty_ptr temp_empty, cur_empty; X new_ptr cur_new_comer; X char buf[16]; X char *malloc(); X X while (wait(0) != tbl_ptr->pid) /* Wait for process to die */ X ; X (void) close(tbl_ptr->socket); X /* X * Inform new-comers X */ X (void) sprintf(buf, "x%d", tbl_ptr->table_id); X for (cur_new_comer = first_new_comer; cur_new_comer; X cur_new_comer = cur_new_comer->next_new_comer) X write_socket(cur_new_comer->socket, buf); X for (cur_ptr = first_table; cur_ptr != tbl_ptr; X cur_ptr = cur_ptr->next_table) X prev_ptr = cur_ptr; X if (prev_ptr) X prev_ptr->next_table = tbl_ptr->next_table; X else X if ((first_table = tbl_ptr->next_table) == NULL) X exit(0); /* No more dealers */ X temp_empty = (empty_ptr) malloc(sizeof(struct empty_table)); X temp_empty->next_empty = NULL; X temp_empty->port_num = tbl_ptr->port_num; X free((char *) tbl_ptr); X if (first_empty) { X for (cur_empty = first_empty; cur_empty->next_empty; X cur_empty = cur_empty->next_empty) ; X cur_empty->next_empty = temp_empty; X } X else X first_empty = temp_empty; X} X X/* X * drop_new_comer - New comer exited X */ Xdrop_new_comer(dead_new_comer) Xnew_ptr dead_new_comer; X{ X new_ptr cur_new_comer, X prev_new_comer = NULL; X X (void) close(dead_new_comer->socket); X for (cur_new_comer = first_new_comer; cur_new_comer != dead_new_comer; X cur_new_comer = cur_new_comer->next_new_comer) X prev_new_comer = cur_new_comer; X if (prev_new_comer) X prev_new_comer->next_new_comer = dead_new_comer->next_new_comer; X else X first_new_comer = dead_new_comer->next_new_comer; X free((char *) dead_new_comer); X if ((first_table == NULL) && (first_new_comer == NULL)) X exit(0); /* Nobody connected */ X} X X/* X * new_player - New player has connected. Inform of games in progress and X * request game to be joined. X */ Xnew_player() X{ X int new_socket; /* new file descriptor */ X char buf[64]; X struct sockaddr_in sockad; X int ssize; /* makes accept happy */ X int i; X new_ptr cur_new_comer, temp_new_comer; X table_ptr cur_ptr; X char *malloc(); X X /* X * add whoever's waiting X */ X ssize = sizeof (sockad); X if ((new_socket = accept(main_sock, &sockad, &ssize)) == -1) { X perror("accept"); X exit(-1); X } X /* X * add to list of new_comers X */ X temp_new_comer = (new_ptr) malloc(sizeof(struct new_comer)); X temp_new_comer->next_new_comer = NULL; X temp_new_comer->socket = new_socket; X if (first_new_comer) { X for (cur_new_comer = first_new_comer; X cur_new_comer->next_new_comer; X cur_new_comer = cur_new_comer->next_new_comer) ; X cur_new_comer->next_new_comer = temp_new_comer; X } X else X first_new_comer = temp_new_comer; X /* X * send info on games in progress X */ X for (cur_ptr = first_table; cur_ptr; cur_ptr = cur_ptr->next_table) { X (void) sprintf(buf, "t%d", cur_ptr->table_id); X write_socket(new_socket, buf); X (void) sprintf(buf, "h%d", cur_ptr->hand); X write_socket(new_socket, buf); X (void) sprintf(buf, "r%d", cur_ptr->round); X write_socket(new_socket, buf); X for (i = 0; i < 4; i++) { X (void) sprintf(buf, "p%d%s", i, cur_ptr->player_name[i]); X write_socket(new_socket, buf); X } X } X write_socket(new_socket, "g"); X} X Xtell_new_comers(buf) Xchar *buf; X{ X new_ptr cur_new; X X for (cur_new = first_new_comer; cur_new; X cur_new = cur_new->next_new_comer) X write_socket(cur_new->socket, buf); X} X X/* X * join - join an existing table. table_num is unique identifier for table. X */ Xjoin(table_num, socket) Xint table_num, socket; X{ X table_ptr cur_ptr; X char buf[16]; X X for (cur_ptr = first_table; cur_ptr && cur_ptr->table_id != table_num; X cur_ptr = cur_ptr->next_table) ; X if (cur_ptr) { X (void) sprintf(buf, "c%d", cur_ptr->port_num); X write_socket(socket, buf); X } X else X write_socket(socket, "g"); /* Table doesn't exist?!? */ X} X X/* X * new_table - start new hearts game. X */ Xnew_table(cur_new_comer) Xnew_ptr cur_new_comer; X{ X table_ptr cur_table, new_tbl_ptr; X empty_ptr tmp_empty; X int i, socks[2]; X int dealer_port, dealer_socket, retry; X char buf[16]; X char *malloc(); X X new_tbl_ptr = (table_ptr) malloc(sizeof(struct table)); X new_tbl_ptr->next_table = NULL; X new_tbl_ptr->table_id = ++unique_id; X for (i = 0; i < 4; i++) X (void) strcpy(new_tbl_ptr->player_name[i], "<empty>"); X /* X * Assign a port. Reassign a used port if available. X */ X do { X if (first_empty) { X dealer_port = first_empty->port_num; X tmp_empty = first_empty; X first_empty = first_empty->next_empty; X free((char *) tmp_empty); X } X else X dealer_port = next_port++; X /* X * Make sure port is ok before assigning. X */ X if ((dealer_socket = open_socket(dealer_port)) == 0) X if (++ retry == 20) { X fputs("Can't open a dealer port!\n", stderr); X exit(1); X } X } X while (dealer_socket == 0); X new_tbl_ptr->port_num = dealer_port; X /* X * Open a socket pair to talk to dealer on. X */ X if (socketpair(AF_UNIX, SOCK_STREAM, 0, socks) == -1) { X perror("socketpair"); X exit(1); X } X switch (new_tbl_ptr->pid = fork()) { X case 0: X (void) setpgrp (0, getpid()); X (void) close(socks[0]); X if (socks[1] != 3) X (void) dup2(socks[1], 3); /* Remap to fd 3 */ X if (dealer_socket != 4) X (void) dup2(dealer_socket, 4); /* Remap to fd 4 */ X execl (HEARTSD, "heartsd", 0); X exit(1); X case -1: X perror("fork"); X exit(1); X } X (void) close(socks[1]); X (void) close(dealer_socket); X X new_tbl_ptr->socket = socks[0]; X if (first_table) { X for (cur_table = first_table; cur_table->next_table; X cur_table = cur_table->next_table) X ; X cur_table->next_table = new_tbl_ptr; X } X else X first_table = new_tbl_ptr; X /* X * Inform hearts player of port # to connect on. X */ X (void) sprintf(buf, "c%d", dealer_port); X write_socket(cur_new_comer->socket, buf); X} X Xmain(argc, argv) Xchar **argv; X{ X fd_type read_fd; X table_ptr cur_table; X new_ptr cur_new_comer; X char buf[64], tmp_buf[64]; X int ret; X int table_num; X char debug; X X while (*++argv) { X if (**argv == '-') { X while (*++*argv) { X switch (**argv) { X case 'd': X debug = TRUE; X break; X X default: X fprintf (stderr, "usage: hearts_dist [-d]\n"); X exit (1); X break; X } X } X } X } X X first_table = NULL; X first_empty = NULL; X first_new_comer = NULL; X next_port = DIST_PORT; X unique_id = 0; X X if ((main_sock = open_socket(PORT)) == 0) { X fputs("Distributor port in use!\n", stderr); X exit(1); X } X if (!debug) { X /* X * Fork off and die. Thus hearts invoker wait() returns. X * This signals ready to connect. X */ X if (fork()) X exit (0); X } X X for (;;) { X fd_init(main_sock, &read_fd); X /* X * Build mask for dealers X */ X for (cur_table = first_table; cur_table; X cur_table = cur_table->next_table) X fd_set(cur_table->socket, &read_fd); X /* X * Build mask for new_comers X */ X for (cur_new_comer = first_new_comer; cur_new_comer; X cur_new_comer = cur_new_comer->next_new_comer) X fd_set(cur_new_comer->socket, &read_fd); X X /* X * Wait for something to happen X */ X if (select(WIDTH, &read_fd, (fd_type *) 0, (fd_type *) 0, X (struct timeval *) 0)) { X if (fd_isset(main_sock, read_fd)) X new_player(); X for (cur_table = first_table; cur_table; X cur_table = cur_table->next_table) X if (fd_isset(cur_table->socket, read_fd)) { X /* X * Message from dealer X */ X ret = read_socket(cur_table->socket, buf); X if (!ret) X clear_table(cur_table); X else { X switch (buf[0]) { X case 'h' : X (void) sscanf(buf + 1, "%d", &cur_table->hand); X break; X case 'r' : X (void) sscanf(buf + 1, "%d", &cur_table->round); X break; X case 'p' : X (void) strcpy(cur_table->player_name[buf[1] - '0'], buf + 2); X } X (void) sprintf(tmp_buf, "t%d", cur_table->table_id); X tell_new_comers(tmp_buf); X tell_new_comers(buf); X } X } X for (cur_new_comer = first_new_comer; cur_new_comer; X cur_new_comer = cur_new_comer->next_new_comer) X if (fd_isset(cur_new_comer->socket, read_fd)) { X /* X * Message from newcomer X */ X ret = read_socket(cur_new_comer->socket, buf); X if (ret) X switch (buf[0]) { X case 'j' : X (void) sscanf(buf + 1, "%d", &table_num); X join(table_num, cur_new_comer->socket); X break; X X case 'n' : X new_table(cur_new_comer); X } X else X drop_new_comer(cur_new_comer); X } X } X } X} X END_OF_dist.c if test 9988 -ne `wc -c <dist.c`; then echo shar: \"dist.c\" unpacked with wrong size! fi # end of overwriting check fi if test -f heartsd.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"heartsd.c\" else echo shar: Extracting \"heartsd.c\" \(34560 characters\) sed "s/^X//" >heartsd.c <<'END_OF_heartsd.c' X/* X * dealer - more commonly known as heartsd X * X * Smarts of hearts program. Runs in background when invoked by initial X * caller of hearts. Contains all logic for communicating with other X * hearts players. Handles all computer players, and communicates play X * of others to other players. X * X * Computer strategy originally written in Pascal by: X * Don Baccus of Oregon Software. X * Strategy mods by: X * Jeff Hemmerling of Test Systems Strategies, Inc. X * X * Converted strategy to C, added curses and multi-player (sockets) X * interface by: X * Bob Ankeney of Generic Computer Products X * X * Thanks to Keith Packard, for his invaluable (is that the same as X * unvaluable?) assistance as a unix guru, for design suggestions, and X * (I suppose) for convincing me to rewrite the bloody thing. X * X * Bug reports to Bob Ankeney at: X * ...!tektronix!reed!bob X * X */ X X#include "misc.h" X#include "defs.h" X#include "local.h" X X#define BUF_SIZE 64 X#define FUNNY_FACTOR 2 X#define NPASS 3 /* # cards to pass */ X#define PLAYERS 4 /* # players total */ X#define NCARDS (52 / PLAYERS) X X#define ACE 13 X#define KING 12 X#define QUEEN 11 X#define JACK 10 X X#define HEARTS_PANIC 4 X#define PANIC_RATIO 0.85 X X#define COMPUTER 1 X#define HUMAN 2 X#define VOYEUR 3 X Xint debug = 0; X Xint num_humans, /* # HUMAN players */ X human_type, /* HUMAN or VOYEUR */ X player_mask; /* Bit n+1 is set for each human player n */ X Xchar someone_is_shooting, possibly_shooting, X queen_played, X safe_suit[MAX_SUIT + 1]; X Xtypedef struct node *ptr; X Xstruct card { X int suit, rank; X}; X Xstruct node { X ptr llink, rlink; X int rank; X}; X Xstruct suit_list { X ptr head, tail; X int length; X}; X Xtypedef struct suit_list card_hand[MAX_SUIT + 1]; X Xstruct player_status { X card_hand cards; X int pts, totalpts; X int passer; X struct card passed_cards[NPASS + 1]; X int num_passed; X int player_kind; X int socket; X char name[9]; X}; X Xstruct player_status card_table[PLAYERS + 1]; X Xcard_hand cards_played; X Xstruct card deck[53]; X Xstruct sockaddr_in sockaddr; X X Xint player_count, leader, X round, hand, X points_played, shooter; X Xchar hearts_broken; X Xstruct { X int card_count, highest_played, high_player, suit_led, pts; X char any_hearts; X struct card played[PLAYERS + 1]; X} trick; X Xchar *snames[] = { X "", X "clubs", X "diamonds", X "hearts", X "spades" X }, X rnames[] = " 23456789TJQKA", X *comp_names[] = { X "", X "zeppo", X "chico", X "harpo", X "groucho" X }; X X Xrnd(num) X{ X extern long random(); X X return (random() % num); X} X X Xinit_deck() X{ X int suit_temp, rank_temp, j; X X j = 0; X for (rank_temp = MIN_RANK; rank_temp <= MAX_RANK ; rank_temp++) X for (suit_temp = CLUBS; suit_temp <= SPADES; suit_temp++) { X deck[++j].suit = suit_temp; X deck[j].rank = rank_temp; X } X} X Xinit_suit(list) Xstruct suit_list *list; X{ X char *malloc(); X ptr p, p1; X X list->length = 0; X p = list->head = (ptr) malloc(sizeof(*p)); X p1 = list->tail = (ptr) malloc(sizeof(*p1)); X p->llink = NULL; X p1->rlink = NULL; X p->rlink = p1; X p1->llink = p; X p->rank = MAX_RANK + 1; X p1->rank = 0; X} X Xinit() X{ X int player; X X srandom(getpid()); /* Init random number gen */ X for (player = 1; player <= PLAYERS; player++) { X card_table[player].player_kind = COMPUTER; X (void) strcpy(card_table[player].name, comp_names[player]); X card_table[player].socket = -1; X } X num_humans = 0; X} X Xnew_game() X{ X int suit, player; X X init_deck(); X for (player = 1; player <= PLAYERS; player++) { X card_table[player].totalpts = 0; X for (suit = CLUBS; suit <= SPADES; suit++) X init_suit(&card_table[player].cards[suit]); X } X for (suit = CLUBS; suit <= SPADES; suit++) X init_suit(&cards_played[suit]); X} X Xget_rank(rch) Xchar rch; X{ X int i; X X for (i = 1; i <= MAX_RANK; i++) X if (rch == rnames[i]) X return(i); X return(0); X} X Xget_suit(sch) Xchar sch; X{ X int i; X X for (i = 1; i <= MAX_SUIT; i++) X if (sch == *snames[i]) X return(i); X return(0); X} X Xchar * Xget_name(player_num) Xint player_num; X{ X int i; X char unique_name = TRUE; X static char pname[16]; X X for (i = 1; i <= PLAYERS; i++) X if ((i != player_num) && X !strcmp(card_table[i].name, card_table[player_num].name)) X unique_name = FALSE; X if (unique_name) X (void) strcpy(pname, card_table[player_num].name); X else X (void) sprintf(pname, "%s (%d)", card_table[player_num].name, X player_num); X return(pname); X} X X/* X * Get a card from somebody. get_mask is a bit mask specifying who is X * acceptable to get a card from (i.e., bit n states player n can send X * a card.) Scans all sockets for input, handling messages and player X * leaving signals. Returns when one of the specified players passes X * a card. Returns 0 if one of the specified players exits the game. X */ Xget_card(get_mask, from, buf) Xint get_mask, *from; Xchar *buf; X{ X int i, player, ret; X char mesg_buf[64]; X fd_type read_fd; X X for (;;) { X fd_init(4, &read_fd); /* Watch for new arrivals */ X for (player = 1; player <= PLAYERS; player++) X if (card_table[player].player_kind != COMPUTER) X fd_set(card_table[player].socket, &read_fd); X if (select(WIDTH, &read_fd, (fd_type *) 0, (fd_type *) 0, X (struct timeval *) 0)) { X if (fd_isset(4, read_fd)) /* New arrival? */ X add_player(); X for (player = 1; player <= PLAYERS; player++) /* This player send? */ X if (fd_isset(card_table[player].socket, read_fd)) { X ret = read_socket(card_table[player].socket, buf); X *from = player; X if (ret && (buf[0] == 'M')) { X (void) sprintf(mesg_buf, "M%d%s: %s", MESG_WINDOW, X get_name(player), buf + 1); X send_to_all(mesg_buf); /* Message to everyone */ X } X else { X if (!ret) { X if (card_table[player].player_kind == HUMAN) X player_mask &= ~(1 << player); /* player went away */ X card_table[player].player_kind = COMPUTER; X (void) close(card_table[player].socket); X if (--num_humans == 0) X exit (0); /* give up if nobody wants to play (sniff) */ X for (i = 1; i <= PLAYERS; i++) { X if (card_table[i].player_kind != COMPUTER) { X (void) sprintf(buf, X "M%d%s vanishes in a puff of greasy black smoke!", X MESG_WINDOW, get_name(player)); /* Announce deletion */ X send_buf(i, buf); /* to others */ X (void) sprintf(buf, X /* X * Inform dealer. X */ X "p%d<empty>", player - 1); X write_socket(3, buf); X } X } X (void) strcpy(card_table[player].name, comp_names[player]); X send_all_totals(); X } X if (get_mask & (1 << player)) X return(ret); X } X } X } X } X} X Xtoss_card(player, card_to_toss, suit_to_toss) Xint player, suit_to_toss; Xptr card_to_toss; X{ X char buf[BUF_SIZE]; X X (void) sprintf(buf, "R%c%c", X rnames[card_to_toss->rank], *snames[suit_to_toss]); X send_buf(player, buf); /* Tell player to remove card */ X} X Xread_card(read_mask, player, card_to_play, suit_to_play) Xint read_mask, *player, *suit_to_play; Xptr *card_to_play; X{ X char buf[BUF_SIZE]; X int rank, suit; X ptr p; X char rank_ch, suit_ch; X X if (get_card(read_mask, player, buf)) { X do { X rank_ch = buf[1]; X suit_ch = buf[2]; X rank = get_rank(rank_ch); X suit = get_suit(suit_ch); X erase_window(*player, TEXT_WINDOW); X p = card_table[*player].cards[suit].head->rlink; X while (p && (p->rank != rank)) X p = p->rlink; X if (p == NULL) { X (void) sprintf(buf, "M%dPlay a card you have:", X TEXT_WINDOW); X send_buf(*player, buf); X send_buf(*player, "G"); X if (!get_card(read_mask, player, buf)) X return(0); X } X } X while (p == NULL); X *card_to_play = p; X *suit_to_play = suit; X return(1); X } X return(0); X} X Xsend_card(player, rank, suit) Xint player, rank, suit; X{ X char buf[BUF_SIZE]; X X (void) sprintf(buf, "A%c%c", rnames[rank], *snames[suit]); X send_buf(player, buf); X} X X/* X * send cards in hand to player X */ Xsend_hand(player) Xint player; X{ X int suit; X ptr p; X X for (suit = CLUBS; suit <= SPADES; suit++) { /* send cards in hand */ X p = card_table[player].cards[suit].head->rlink; X while (p->rank) { X send_card(player, p->rank, suit); X p = p->rlink; X } X } X} X Xget_first_player() X{ X fd_type read_fd; /* main socket bit mask */ X X /* X * wait for new player to show up X */ X fd_init(4, &read_fd); /* Wait for someone to show up */ X if (select(WIDTH, &read_fd, (fd_type *) 0, (fd_type *) 0, X (struct timeval *) 0) == -1) { X perror("select"); X exit(1); X } X} X Xadd_player() X{ X int new_socket; /* new file descriptor */ X char pname[128], buf[128]; X struct sockaddr_in sockad; X int ssize; /* to make accept happy */ X int new_player = 0; X int i; X X /* X * add whoever's waiting X */ X ssize = sizeof (sockad); X if ((new_socket = accept(4, &sockad, &ssize)) == -1) { X perror("accept"); X exit(1); X } X /* get user name */ X if (read_socket(new_socket, pname) > 0) { X pname[8] = '\0'; /* Name must be less than 9 chars */ X for (i = PLAYERS; i >= 1; i--) X if (card_table[i].player_kind == COMPUTER) X new_player = i; X } X if (new_player) { X /* X * Inform distributor X */ X (void) sprintf(buf, "p%d%s", new_player - 1, pname); X write_socket(3, buf); X card_table[new_player].player_kind = human_type; X card_table[new_player].socket = new_socket; X (void) strcpy(card_table[new_player].name, pname); X ++num_humans; X if (human_type == VOYEUR) { X (void) sprintf(buf, X "M%d Game in progress...", PLAY_WINDOW); X send_buf(new_player, buf); X (void) sprintf(buf, X "M%dYou may watch till next hand.", MESG_WINDOW); X send_buf(new_player, buf); X } X else X player_mask |= 1 << new_player; X for (i = 1; i <= PLAYERS; i++) { X if (card_table[i].player_kind != COMPUTER) { X (void) sprintf(buf, X "M%d%s added as player %d", MESG_WINDOW, X pname, new_player); X if (i != new_player) /* Announce addition */ X send_buf(i, buf); X send_totals(i); X } X } X send_hand(new_player); X } X else { X (void) sprintf(buf, "M%dAll seats are full!", MESG_WINDOW); X write_socket(new_socket, buf); X write_socket(new_socket, "X"); X (void) close(new_socket); X } X} X Xerase_window(player, which_window) Xint player, which_window; X{ X char buf[BUF_SIZE]; X X (void) sprintf(buf, "E%d", which_window); X send_buf(player, buf); X} X Xerase_all_window(which_window) Xint which_window; X{ X int player; X X for (player = 1; player <= PLAYERS; player++) X if (card_table[player].player_kind != COMPUTER) X erase_window(player, which_window); X} X Xsend_totals(player) Xint player; X{ X int i; X char buf[BUF_SIZE]; X X if (card_table[player].player_kind != COMPUTER) { X for (i = 1; i <= PLAYERS; i++) { X (void) sprintf(buf, "S%d%02d%02d%s", i, X card_table[i].pts, X card_table[i].totalpts, card_table[i].name); X send_buf(player, buf); X } X } X} X Xsend_all_totals() X{ X int player; X X for (player = 1; player <= PLAYERS; player++) X send_totals(player); X} X Xsend_all_winner() X{ X int max_score, X player, shooter; X char buf[BUF_SIZE]; X X (void) sprintf(buf, "M%d--------------------------------------------------------", MESG_WINDOW); X send_to_all(buf); X max_score = 0; X for (player = 1; player <= PLAYERS; player++) { X if (card_table[player].pts > max_score) { X max_score = card_table[player].pts; X shooter = player; X } X } X if (max_score == 26) { X for (player = 1; player <= PLAYERS; player++) X if (player != shooter) X card_table[player].totalpts += 26; X } X else { X for (player = 1; player <= PLAYERS; player++) X card_table[player].totalpts += card_table[player].pts; X } X if (max_score == 26) X (void) sprintf(buf, "M %s wins by shooting the moon!", X get_name(shooter)); X else { X get_winner(buf, FALSE); X (void) strcat(buf, "."); X } X buf[1] = MESG_WINDOW + '0'; X send_to_all(buf); X} X Xsend_final_winner() X{ X char buf[64]; X X get_winner(buf, TRUE); X buf[1] = PLAY_WINDOW + '0'; X (void) strcat(buf, " total!"); X erase_all_window(PLAY_WINDOW); X send_to_all(buf); X} X X/* X * Get buffer of form: "M groucho wins with nn points" X * or: "M groucho and chico tie with nn points" X */ Xget_winner(buf, use_totalpts) Xchar *buf; Xchar use_totalpts; X{ X int player, winning_score, temp, X scores[PLAYERS + 1], players[PLAYERS + 1]; X char sorted; X X for (player = 1; player <= PLAYERS; player++) { X scores[player] = use_totalpts ? card_table[player].totalpts X : card_table[player].pts; X players[player] = player; X } X do { /* Bubble sort the bloody thing */ X sorted = TRUE; X for (player = 1; player < PLAYERS; player++) X if (scores[player + 1] < scores[player]) { X temp = scores[player]; X scores[player] = scores[player + 1]; X scores[player + 1] = temp; X temp = players[player]; X players[player] = players[player + 1]; X players[player + 1] = temp; X sorted = FALSE; X } X } X while (!sorted); X winning_score = scores[1]; X (void) sprintf(buf, "M %s", card_table[players[1]].name); X for (player = 2; X (player <= PLAYERS) && (scores[player] == winning_score); player++) X (void) sprintf(buf + strlen(buf), " and %s", X card_table[players[player]].name); X if (scores[2] == winning_score) X (void) sprintf(buf + strlen(buf), X " tie with %d point", winning_score); X else X (void) sprintf(buf + strlen(buf), X " wins with %d point", winning_score); X if (winning_score != 1) X (void) strcat(buf, "s"); X} X Xsend_buf(player, buf) Xint player; Xchar *buf; X{ X write_socket(card_table[player].socket, buf); X} X Xsend_to_all(buf) Xchar *buf; X{ X int player; X X for (player = 1; player <= PLAYERS; player++) X if (card_table[player].player_kind != COMPUTER) X send_buf(player, buf); X} X Xnew_round() X{ X char buf[BUF_SIZE]; X X trick.pts = 0; X trick.any_hearts = FALSE; X player_count = 0; X erase_all_window(PLAY_WINDOW); X /* X * Inform distributor X */ X (void) sprintf(buf, "r%d", round); X write_socket(3, buf); X (void) sprintf(buf, "M%dHand: %d Round: %d", X ROUND_WINDOW, hand, round); X send_to_all(buf); X} X Xenter_card(which_card, which_hand) Xstruct card which_card; Xcard_hand which_hand; X X{ X ptr p, p1; X X p = which_hand[which_card.suit].head; X ++which_hand[which_card.suit].length; X while (p->rank > which_card.rank) X p = p->rlink; X p1 = (ptr) malloc(sizeof(*p1)); X p1->llink = p->llink; X p1->llink->rlink = p1; X p->llink = p1; X p1->rlink = p; X p1->rank = which_card.rank; X} X Xremove_node(p) Xptr p; X X{ X p->llink->rlink = p->rlink; X p->rlink->llink = p->llink; X free((char *) p); X} X Xclear_hand(which_hand) Xcard_hand which_hand; X{ X int suit; X ptr p, p1; X X for (suit = CLUBS; suit <= SPADES; suit++) { X safe_suit[suit] = TRUE; X which_hand[suit].length = 0; X p = which_hand[suit].head->rlink; X while (p->rank > 0) { X p1 = p; X p = p->rlink; X remove_node(p1); X } X } X safe_suit[HEARTS] = FALSE; X} X Xshuffle() X{ X int j, k; X struct card t; X X for (j = 52; j >= 1; j--) { X k = rnd(j) + 1; X t = deck[k]; X deck[k] = deck[j]; X deck[j] = t; X } X} X Xdeal() X{ X int i, j, player; X X for (player = 1; player <= PLAYERS; player++) { X j = NCARDS * (player - 1); X for (i = 1; i <= NCARDS; i++) X enter_card(deck[i+j], card_table[player].cards); X if (card_table[player].player_kind != COMPUTER) X send_hand(player); X } X} X Xnew_hand() X{ X int player; X char buf[BUF_SIZE]; X X points_played = 0; X someone_is_shooting = possibly_shooting = FALSE; X queen_played = hearts_broken = FALSE; X trick.suit_led = CLUBS; X for (player = 1; player <= PLAYERS; player++) { X card_table[player].pts = 0; X clear_hand(card_table[player].cards); X } X clear_hand(cards_played); X erase_all_window(PLAY_WINDOW); X send_all_totals(); X /* X * Inform distributor X */ X (void) sprintf(buf, "h%d", hand); X write_socket(3, buf); X write_socket(3, "r0"); X (void) sprintf(buf, "M%dNew hand...", ROUND_WINDOW); X send_to_all(buf); X shuffle(); /* Shuffle three times! */ X shuffle(); /* (just like vegas!) */ X shuffle(); X deal(); X} X Xptr Xnext_highest(player, rank, suit_to_play) Xint player, rank, suit_to_play; X{ X ptr p; X X p = card_table[player].cards[suit_to_play].head->rlink; X while (p && p->rank) X if (p->rank < rank) X return(p); X else X p = p->rlink; X return(NULL); X} X X#define highest(player, suit) (card_table[player].cards[suit].head->rlink) X X#define lowest(player, suit) (card_table[player].cards[suit].tail->llink) X X#define cards_out(suit) (MAX_SUIT - cards_played[suit].length) X X#define spades_safe(player) ((((cards_out(SPADES) / PLAYERS) + 1) < \ Xcard_table[player].cards[SPADES].length) && safe_suit[SPADES]) X X#define diamonds_safe(player) (( (float) (cards_out(DIAMONDS) - \ Xcard_table[player].cards[DIAMONDS].length) / (PLAYERS - 1.0) >= 2.0) &&\ X safe_suit[DIAMONDS]) X X#define clubs_safe(player) (( (float) (cards_out(CLUBS) - \ Xcard_table[player].cards[CLUBS].length) / (PLAYERS - 1.0) >= 2.0) &&\ X safe_suit[CLUBS]) X X#define only_hearts(player) ((14 - round) == \ Xcard_table[player].cards[HEARTS].length) X Xfind_high_card(player, card_to_play, suit_to_play, play_points) Xint player, *suit_to_play; Xchar play_points; Xptr *card_to_play; X{ X int high_card, suit; X ptr p; X X high_card = 0; X *card_to_play = NULL; X for (suit = CLUBS; suit <= SPADES; suit++) { X p = card_table[player].cards[suit].head->rlink; X if ((p->rank > high_card) && X (play_points || (point_value(p->rank, suit) == 0))) { X high_card = p->rank; X *card_to_play = p; X *suit_to_play = suit; X } X } X if (*card_to_play == NULL) { X /* X * No clubs or diamonds. Q spades is highest spade, or no X * spades at all. Try to play next highest spade. X */ X *card_to_play = next_highest(player, QUEEN, SPADES); X if (*card_to_play) X *suit_to_play = SPADES; X else { X /* Play highest heart */ X *card_to_play = highest(player, HEARTS); X *suit_to_play = HEARTS; X } X } X} X Xfind_low_card(player, card_to_play, suit_to_play) Xint player, *suit_to_play; Xptr *card_to_play; X{ X int low_card, suit; X ptr p; X X low_card = MAX_RANK + 1; X *card_to_play = NULL; X for (suit = CLUBS; suit <= SPADES; suit++) { X p = card_table[player].cards[suit].tail->llink; X if ((p->rank < low_card) && (suit != HEARTS)) { X low_card = p->rank; X *card_to_play = p; X *suit_to_play = suit; X } X if (*card_to_play == NULL) { X *suit_to_play = HEARTS; X *card_to_play = card_table[player].cards[HEARTS].tail->llink; X } X } X} X Xfind_low_club(leader) Xint *leader; X{ X int player, low_club, rank; X X low_club = MAX_RANK; X for (player = 1; player <= PLAYERS; player++) X if ((rank = card_table[player].cards[CLUBS].tail->llink->rank) < low_club) { X *leader = player; X low_club = rank; X } X} X Xfind_queen(player, card_to_play) Xint player; Xptr *card_to_play; X{ X ptr p; X X *card_to_play = NULL; X p = card_table[player].cards[SPADES].head->rlink; X while (p) X if (p->rank == QUEEN) { X *card_to_play = p; X p = NULL; X } X else X p = p->rlink; X} X Xprint_pass(player) Xint player; X{ X int i; X char buf[BUF_SIZE]; X struct card cd; X X erase_window(player, PLAY_WINDOW); X (void) sprintf(buf, "M%d%s passes you:", LEAD_WINDOW, X get_name(card_table[player].passer)); X send_buf(player, buf); X for (i = 1; i <= NPASS; i++) { X cd = card_table[player].passed_cards[i]; X send_card(player, cd.rank, cd.suit); X (void) sprintf(buf, "P%d%c%c", i, rnames[cd.rank], *snames[cd.suit]); X send_buf(player, buf); X } X} X X/* X * Get card to pass from computer player X */ Xget_pass(from, which_pass, card_to_pass, suit_to_pass) Xint from, which_pass, *suit_to_pass; Xptr *card_to_pass; X{ X ptr p1; X X p1 = highest(from, SPADES); X if (!spades_safe(from) && (p1->rank >= QUEEN) && X card_table[from].cards[SPADES].length) { X *card_to_pass = p1; X *suit_to_pass = SPADES; X } X else X if (((NPASS - which_pass) >= card_table[from].cards[DIAMONDS].length - 1) && X (card_table[from].cards[DIAMONDS].length > 0)) { X *card_to_pass = highest(from, DIAMONDS); X *suit_to_pass = DIAMONDS; X } X else X if (((NPASS - which_pass) >= card_table[from].cards[CLUBS].length - 2) && X (card_table[from].cards[CLUBS].length > 1)) { X *card_to_pass = highest(from, CLUBS); X *suit_to_pass = CLUBS; X } X else X if (card_table[from].cards[HEARTS].length) { X *card_to_pass = highest(from, HEARTS); X *suit_to_pass = HEARTS; X } X else X find_high_card(from, card_to_pass, suit_to_pass, TRUE); X} X X/* X * Pass card to player X */ Xpass_to(from, who_to, which_pass, card_to_pass, suit_to_pass) Xint from, who_to, which_pass; Xptr card_to_pass; Xchar suit_to_pass; X{ X char buf[BUF_SIZE]; X X card_table[who_to].passed_cards[which_pass].rank = card_to_pass->rank; X card_table[who_to].passed_cards[which_pass].suit = suit_to_pass; X --card_table[from].cards[suit_to_pass].length; X card_table[who_to].passer = from; X remove_node(card_to_pass); X if (card_table[from].player_kind != COMPUTER) { X erase_window(from, INP_WINDOW); X (void) sprintf(buf, "P%d%c%c", which_pass, X rnames[card_to_pass->rank], *snames[suit_to_pass]); X send_buf(from, buf); X toss_card(from, card_to_pass, suit_to_pass); X } X} X X#define map_player(leader, player) (((leader + player - 1) % PLAYERS) + 1) X Xplayer_pass(player) Xint player; X{ X char buf[BUF_SIZE]; X X (void) sprintf(buf, "M%dPass %d to %s:", X TEXT_WINDOW, NPASS, get_name(map_player(player, hand))); X send_buf(player, buf); X (void) sprintf(buf, "M%dCard 1:", LEAD_WINDOW); X send_buf(player, buf); X send_buf(player, "G"); X} X Xpass_cards() X{ X int player, pass, suit, old_mask; X int passed_mask = 0; X ptr p; X char buf[BUF_SIZE]; X X human_type = HUMAN; X for (player = 1; player <= PLAYERS; player++) { X card_table[player].num_passed = 0; X if (card_table[player].player_kind == HUMAN) X player_pass(player); X } X while (player_mask != passed_mask) { X old_mask = player_mask; X if (read_card(player_mask, &player, &p, &suit)) { X pass_to(player, map_player(player, hand), X ++card_table[player].num_passed, p, suit); X if (card_table[player].num_passed == NPASS) X /* X * Done passing X */ X passed_mask |= 1 << player; X else { X (void) sprintf(buf, "M%dCard %d:", LEAD_WINDOW, X card_table[player].num_passed + 1); X /* X * Ask for next card X */ X send_buf(player, buf); X send_buf(player, "G"); X } X } X else X /* X * Player left game X */ X passed_mask &= ~(1 << player); X /* X * Player added? X */ X for (player = 1; player <= PLAYERS; player++) X if (~old_mask & player_mask & (1 << player)) { X card_table[player].num_passed = 0; X player_pass(player); X } X } X erase_all_window(LEAD_WINDOW); X human_type = VOYEUR; X /* X * Let computer pass. X */ X for (player = 1; player <= PLAYERS; player++) X if (card_table[player].player_kind != HUMAN) X for (pass = ++card_table[player].num_passed; X pass <= NPASS; pass++) { X get_pass(player, pass, &p, &suit); X pass_to(player, map_player(player, hand), pass, p, suit); X } X for (player = 1; player <= PLAYERS; player++) { X for (pass = 1; pass <= NPASS; pass++) X enter_card(card_table[player].passed_cards[pass], X card_table[player].cards); X if (card_table[player].player_kind != COMPUTER) X print_pass(player); X } X} X Xpoint_value(rank, suit) Xint rank, suit; X{ X if (suit == HEARTS) X return(1); X else X if ((suit == SPADES) && (rank == QUEEN)) X return(13); X return(0); X} X Xshow(player, card_to_play, suit_to_play) Xint player, suit_to_play; Xptr card_to_play; X{ X int rank_to_play; X char buf[BUF_SIZE]; X X rank_to_play = card_to_play->rank; X remove_node(card_to_play); X if (card_table[player].player_kind != COMPUTER) { X erase_window(player, INP_WINDOW); X erase_window(player, LEAD_WINDOW); X toss_card(player, card_to_play, suit_to_play); X } X --card_table[player].cards[suit_to_play].length; X if (suit_to_play == HEARTS) X trick.any_hearts = TRUE; X else { X if ((suit_to_play == SPADES) && (rank_to_play == QUEEN)) X queen_played = TRUE; X } X if ((suit_to_play == trick.suit_led) && X (rank_to_play > trick.highest_played)) { X trick.highest_played = rank_to_play; X trick.high_player = player; X } X trick.played[player].rank = rank_to_play; X trick.played[player].suit = suit_to_play; X /* X * If points were dumped or someone couldn't follow suit, X * then the suit is unsafe. X */ X if ((trick.pts += point_value(rank_to_play, suit_to_play)) || X (suit_to_play != trick.suit_led)) X safe_suit[trick.suit_led] = FALSE; X enter_card(trick.played[player], cards_played); X ++trick.card_count; X (void) sprintf(buf, "P%d%c%c%s", player, rnames[rank_to_play], X *snames[suit_to_play], card_table[player].name); X send_to_all(buf); X} X Xreally_safe(suit) Xint suit; X{ X char safe; X int losers_out, low_rank; X ptr p; X X p = cards_played[suit].head->rlink; X low_rank = card_table[leader].cards[suit].tail->llink->rank; X /* X * Count # cards of _suit_ played that would be losers X * to leaders lowest of _suit_. X */ X losers_out = 0; X while (p->rank) { X if (p->rank < low_rank) X ++losers_out; X p = p->rlink; X } X /* X * Old strategy. X losers_out = low_rank - 2 - losers_out; X safe = (((card_table[leader].cards[suit].length + X cards_played[suit].length + losers_out) != 13) || X safe_suit[suit]); X */ X /* X * Card is guaranteed to lose the trick if: X * -- All cards of suit lower than low_rank have been played; X * -- There is at least one other card of the suit unplayed. X * X * Using this strategy means safe_suit[] is unused. X */ X safe = (losers_out == (low_rank - 1)) && X ((card_table[leader].cards[suit].length + cards_played[suit].length) != 13); X if (suit == SPADES) X safe &= ((card_table[leader].cards[SPADES].tail->llink->rank < X QUEEN) || queen_played); X return(safe); X} X Xfind_low_lead(card_to_play, suit_to_play, play_hearts) Xptr *card_to_play; Xint *suit_to_play; Xchar play_hearts; X{ X char last_gasp; X int low_card, suit; X ptr p; X X low_card = MAX_RANK + 1; X last_gasp = FALSE; X *card_to_play = NULL; X while (*card_to_play == NULL) { X for (suit = CLUBS; suit <= SPADES; suit++) { X p = card_table[leader].cards[suit].tail->llink; X if ((p->rank < low_card) && X (play_hearts || (suit != HEARTS)) && X (really_safe(suit) || last_gasp)) { X low_card = p->rank; X *card_to_play = p; X *suit_to_play = suit; X } X } X last_gasp = TRUE; X } X} X Xshow_lead(card_to_play, suit_to_play) Xint suit_to_play; Xptr card_to_play; X{ X trick.card_count = 1; X trick.highest_played = card_to_play->rank; X trick.suit_led = suit_to_play; X trick.high_player = leader; X show(leader, card_to_play, suit_to_play); X} X Xread_lead(leader, card_to_play, suit_to_play) Xint leader, *suit_to_play; Xptr *card_to_play; X{ X char good, alive, buf[64]; X int t; /* Ignored */ X X do { X send_buf(leader, "G"); X if (alive = read_card(1 << leader, &t, card_to_play, suit_to_play)) { X good = (((round != 1) || X (*card_to_play == card_table[leader].cards[CLUBS].tail->llink)) && X ((*suit_to_play != HEARTS) || hearts_broken || only_hearts(leader))); X if (!good) { X if (*suit_to_play == HEARTS) X (void) sprintf(buf,"M%dHearts not broken yet!", TEXT_WINDOW); X else X (void) sprintf(buf,"M%dLead your lowest club!", TEXT_WINDOW); X send_buf(leader, buf); X send_buf(leader, "G"); X } X } X } X while (alive && !good); X} X Xread_next_card(player, card_to_play, suit_to_play) Xint player, *suit_to_play; Xptr *card_to_play; X{ X char good, good_pts, alive, buf[64]; X int t; /* Ignored */ X X do { X send_buf(player, "G"); X if (alive = read_card(1 << player, &t, card_to_play, suit_to_play)) { X good_pts = (((*suit_to_play != SPADES) || X ((*card_to_play)->rank != QUEEN)) X && ((*suit_to_play != HEARTS) || only_hearts(player)) || X (round != 1)); X good = (((*suit_to_play == trick.suit_led) || X (card_table[player].cards[trick.suit_led].length == 0)) && good_pts); X if (!good) { X if (!good_pts) X (void) sprintf(buf, "M%dCan't dump points yet!", TEXT_WINDOW); X else X (void) sprintf(buf, "M%dTry following suit!", TEXT_WINDOW); X send_buf(player, buf); X send_buf(player, "G"); X } X } X } X while (alive && !good); X} X Xcomputer_lead(card_to_play, suit_to_play) Xint *suit_to_play; Xptr *card_to_play; X{ X ptr p; X X if (round == 1) { X /* X * Lead the 2 of clubs X */ X *card_to_play = card_table[leader].cards[CLUBS].tail->llink; X *suit_to_play = CLUBS; X } X else { X find_queen(leader, &p); X if (!queen_played && (p == NULL) && spades_safe(leader) && X card_table[leader].cards[SPADES].length) { X *suit_to_play = SPADES; X *card_to_play = next_highest(leader, QUEEN, SPADES); X } X else { X *card_to_play = NULL; X/* X * Old strategy X if (diamonds_safe(leader) && X (card_table[leader].cards[DIAMONDS].length)) { X *suit_to_play = DIAMONDS; X *card_to_play = highest(leader, DIAMONDS); X } else X if (clubs_safe(leader) && (card_table[leader].cards[CLUBS].length)) { X *suit_to_play = CLUBS; X *card_to_play = highest(leader, CLUBS); X } X */ X } X if (*card_to_play == NULL) X find_low_lead(card_to_play, suit_to_play, X (hearts_broken || only_hearts(leader))); X } X} X X#define max(a,b) ((a > b) ? a : b) X Xpick_a_loser(player, card_to_play, suit_to_play) Xint player, *suit_to_play; Xptr *card_to_play; X{ X ptr p; X X if (card_table[player].cards[trick.suit_led].length) { X *suit_to_play = trick.suit_led; X if ((trick.suit_led != SPADES) && (trick.pts == 0) && X (trick.card_count == PLAYERS)) X /* X * Play the highest card of suit led if player is last X * and there are no points in current trick. X */ X *card_to_play = highest(player, trick.suit_led); X else X switch (trick.suit_led) { X case SPADES: X if ((trick.card_count == PLAYERS) && (trick.pts == 0)) { X /* X * Play the highest spade. Don't play queen if X * it will win the trick. X */ X *card_to_play = highest(player, SPADES); X if (((*card_to_play)->rank == QUEEN) && X (QUEEN > trick.highest_played)) X *card_to_play = next_highest(player, QUEEN, SPADES); X } X else X if (spades_safe(player) && !queen_played) { X find_queen(player, &p); X *card_to_play = highest(player, SPADES); X if ((p == NULL) || (p == *card_to_play)) X *card_to_play = next_highest(player, X max(trick.highest_played, QUEEN), SPADES); X } X break; X X case CLUBS: X if (clubs_safe(player)) X *card_to_play = highest(player, CLUBS); X break; X X case DIAMONDS: X if (diamonds_safe(player)) X *card_to_play = highest(player,DIAMONDS); X break; X X case HEARTS: X break; X } X if (*card_to_play == NULL) X *card_to_play = next_highest(player, trick.highest_played, X *suit_to_play); X if (*card_to_play == NULL) { X *card_to_play = card_table[player].cards[trick.suit_led].tail->llink; X /* X * Don't play the Q spades if: X * 1. The Queen is your lowest spade and X * 2. You have a higher spade. X */ X if (((*card_to_play)->rank == QUEEN) && (trick.suit_led == SPADES) && X (card_table[player].cards[trick.suit_led].length > 1)) X *card_to_play = card_table[player].cards[trick.suit_led].head->rlink; X } X } X else { X if (round != 1) { X /* X * Play the queen of spades if player has it. X */ X if (*card_to_play) X *suit_to_play = SPADES; X if ((*card_to_play == NULL) && X (card_table[player].cards[HEARTS].length)) { X if (possibly_shooting) { X *card_to_play = next_highest(player, JACK, HEARTS); X if (*card_to_play == NULL) X *card_to_play = lowest(player, HEARTS); X } X else X *card_to_play = highest(player, HEARTS); X *suit_to_play = HEARTS; X } X } X if (*card_to_play == NULL) X find_high_card(player, card_to_play, suit_to_play, (round != 1)); X } X} X Xstop_the_bastard(player, card_to_play, suit_to_play) Xint player, *suit_to_play; Xptr *card_to_play; X{ X if (card_table[player].cards[trick.suit_led].length) { X *suit_to_play = trick.suit_led; X if ((trick.suit_led == SPADES) && (!queen_played)) { X *card_to_play = next_highest(player, QUEEN, SPADES); X if (*card_to_play == NULL) X *card_to_play = lowest(player, SPADES); X } X else X *card_to_play = highest(player, trick.suit_led); X if ((trick.pts < 13) && X !(((cards_out(trick.suit_led) < (PLAYERS - player_count)) || X trick.any_hearts) && X ((*card_to_play)->rank > trick.highest_played) && X (shooter == trick.high_player))) X *card_to_play = lowest(player, trick.suit_led); X } X else X if ((shooter != trick.high_player) && X (card_table[player].cards[HEARTS].length > 0)) { X *card_to_play = highest(player, HEARTS); X *suit_to_play = HEARTS; X } X else X find_low_card(player, card_to_play, suit_to_play); X} X Xcomputer_pick(player, card_to_play, suit_to_play) Xint player, *suit_to_play; Xptr *card_to_play; X{ X *card_to_play = NULL; X if (someone_is_shooting && (player != shooter)) X stop_the_bastard(player, card_to_play, suit_to_play); X else X pick_a_loser(player, card_to_play, suit_to_play); X} X Xlead(leader) Xint leader; X{ X int suit_to_play; X ptr card_to_play; X char buf[BUF_SIZE]; X X if (card_table[leader].player_kind == HUMAN) { X (void) sprintf(buf, "M%dYour lead:", LEAD_WINDOW); X send_buf(leader, buf); X read_lead(leader, &card_to_play, &suit_to_play); X } X if (card_table[leader].player_kind != HUMAN) /* If player left */ X computer_lead(&card_to_play, &suit_to_play); X show_lead(card_to_play, suit_to_play); X} X Xplay_next_card(player) Xint player; X{ X int suit_to_play; X ptr card_to_play; X char buf[BUF_SIZE]; X X if (card_table[player].player_kind == HUMAN) { X (void) sprintf(buf, "M%dYour play:", LEAD_WINDOW); X send_buf(player, buf); X read_next_card(player, &card_to_play, &suit_to_play); X } X if (card_table[player].player_kind != HUMAN) /* If player left */ X computer_pick(player, &card_to_play, &suit_to_play); X show(player, card_to_play, suit_to_play); X} X Xfind_winner(winner) Xint *winner; X{ X *winner = trick.high_player; X if (trick.any_hearts) X hearts_broken = TRUE; X points_played += trick.pts; X card_table[*winner].pts += trick.pts; X possibly_shooting = ((points_played > 0) && X (card_table[*winner].pts == points_played)); X someone_is_shooting = (possibly_shooting && X (card_table[*winner].pts >= HEARTS_PANIC)); X if (someone_is_shooting) X shooter = *winner; X} X X Xmain() X{ X int player; X X init(); X new_game(); X X human_type = HUMAN; X get_first_player(); X add_player(); X X for (;;) { X for (hand = 1; hand <= PLAYERS; hand++) { X new_hand(); X if (hand != PLAYERS) X pass_cards(); X find_low_club(&leader); X for (round = 1; round <= NCARDS; round++) { X new_round(); X lead(leader); X for (player_count = 1; player_count <= PLAYERS - 1; player_count++) X play_next_card(map_player(leader, player_count)); X find_winner(&leader); X send_all_totals(); X } X send_all_winner(); X for (player = 1; player <= PLAYERS; player++) X if (card_table[player].player_kind == VOYEUR) { X card_table[player].player_kind = HUMAN; X player_mask |= 1 << player; X } X } X send_all_totals(); X send_final_winner(); X send_to_all("X"); X new_game(); X } X} END_OF_heartsd.c if test 34560 -ne `wc -c <heartsd.c`; then echo shar: \"heartsd.c\" unpacked with wrong size! fi # end of overwriting check fi if test -f opensock.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"opensock.c\" else echo shar: Extracting \"opensock.c\" \(1587 characters\) sed "s/^X//" >opensock.c <<'END_OF_opensock.c' X/* X * opensock.c X * X * server creates a socket to listen for connections X */ X X#include <stdio.h> X#include "defs.h" X#include "local.h" X X X/* X * Open main socket on given port and set sock_num to it. X * If port is 0, get port from /etc/services. X */ Xopen_socket(port) Xint port; X{ X struct sockaddr_in sockaddr; X struct hostent *host; X struct servent *distributor; X int sock_num; X char hostnm[SLEN]; X X (void) gethostname(hostnm, SLEN); X if ((sock_num = socket(AF_INET, SOCK_STREAM, 0)) < 0) { X perror("socket"); X exit(1); X } X if ((host = gethostbyname(hostnm)) == NULL) { X perror("gethostbyname"); X exit(1); X } X bzero((char *) &sockaddr, sizeof (sockaddr)); X bcopy(host->h_addr, (char *) &sockaddr.sin_addr, host->h_length); X sockaddr.sin_family = AF_INET; X if (port) X sockaddr.sin_port = htons(port); X else { X if ((distributor = getservbyname(SERVICE, PROTO)) == NULL) { X fputs("hearts: service not found\n", stderr); X exit(1); X } X sockaddr.sin_port = distributor->s_port; X } X /* X * Allow reuse of local addresses. X * Speeds up reinvocation of distributor. X */ X (void) setsockopt(sock_num, SOL_SOCKET, SO_REUSEADDR, (char *) 0, 0); X /* X * Don't hang around if socket in use. X */ X#ifdef SO_DONTLINGER X (void) setsockopt(sock_num, SOL_SOCKET, SO_DONTLINGER, (char *) 0, 0); X#else X { X struct linger ling; X X (void) setsockopt(sock_num, SOL_SOCKET, SO_LINGER, (char *) ling, 0); X } X#endif X if (bind(sock_num, (struct sockaddr *) &sockaddr, sizeof(sockaddr)) < 0) { X return(0); X } X if ((listen(sock_num, 5)) == -1) { X perror("listen"); X exit(1); X } X return(sock_num); X} END_OF_opensock.c if test 1587 -ne `wc -c <opensock.c`; then echo shar: \"opensock.c\" unpacked with wrong size! fi # end of overwriting check fi echo shar: End of archive 1 \(of 2\). cp /dev/null ark1isdone MISSING="" for I in 1 2 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked both archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive1: case 3