[comp.sources.games] v02i083: hearts - multiplayer card game, Part02/02

games-request@tekred.TEK.COM (11/05/87)

Submitted by: bob@reed (Mythical Bob Ankeney)
Comp.sources.games: Volume 2, Issue 83
Archive-name: hearts/Part02

	[The file 'hearts.instr' has several ^H characters in it;
	 don't be too suprised if they don't make it thru.  -br]

#! /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 2 (of 2)."
# Contents:  INSTALL Makefile connect.c defs.h hearts.c hearts.instr
#   local.proto misc.h select.c sockio.c start_dist.c
# Wrapped by billr@tekred on Thu Nov  5 10:21:04 1987
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f INSTALL -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"INSTALL\"
else
echo shar: Extracting \"INSTALL\" \(497 characters\)
sed "s/^X//" >INSTALL <<'END_OF_INSTALL'
X     Installation of hearts is pretty straight-forward.  Just edit the
XMakefile to specify the BIN and LIB directories, and select the port
Xnumbers desired.  If you wish, you can add an entry into /etc/services for
Xthe hearts distributor.  If you do so, set PORT=0.  Each dealer invoked
Xrequires a seperate port #, so set DIST_PORT to the starting port to be
Xused.  If the distributor finds a port is already in use, it will use the
Xnext-higher one.
X
X     Bob Ankeney
X     ...!tektronix!reed!bob
X
END_OF_INSTALL
if test 497 -ne `wc -c <INSTALL`; then
    echo shar: \"INSTALL\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f Makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Makefile\"
else
echo shar: Extracting \"Makefile\" \(1938 characters\)
sed "s/^X//" >Makefile <<'END_OF_Makefile'
X#
X# Makefile for hearts
X#
X
X# If you can edit /etc/services, define PORT as 0, else
X# define PORT as an unused internet port address (PORT = xxxx)
X#
XPORT=1030
X#
X# Dealer port numbers are handed out starting at DIST_PORT.
X# Allocate a block of ports starting at DIST_PORT = xxxx.
XDIST_PORT=1031
X
X#
X# where the distributor, dealer and instructions live
X#
X#LIB=/usr/games/lib
XLIB=.
X#
X# where the executable lives
X#
XBIN=/usr/games
X
XCFLAGS = -O
X
XHCFILES =hearts.c select.c connect.c sockio.c start_dist.c
XDCFILES =heartsd.c sockio.c
XHDCFILES =hearts_dist.c opensock.c sockio.c
X
XHOFILES =hearts.o select.o connect.o sockio.o start_dist.o
XDOFILES =heartsd.o sockio.o
XHDOFILES =hearts_dist.o opensock.o sockio.o
X
Xall:	hearts heartsd hearts_dist
X
Xinstall: $(LIB)/heartsd $(LIB)/hearts_dist $(LIB)/hearts.instr $(BIN)/hearts
X
X$(LIB)/heartsd: heartsd
X	cp heartsd $(LIB)
X
X$(LIB)/hearts_dist: hearts_dist
X	cp hearts_dist $(LIB)
X
X$(LIB)/hearts.instr: hearts.instr
X	cp hearts.instr $(LIB)
X
X$(BIN)/hearts: hearts
X	cp hearts $(BIN)
X
Xhearts:	$(HOFILES)
X	cc $(CFLAGS) -o $@ $(HOFILES) -lcurses -ltermlib
X
Xheartsd: $(DOFILES)
X	cc $(CFLAGS) -o $@ $(DOFILES)
X
Xhearts_dist: $(HDOFILES)
X	cc $(CFLAGS) -o $@ $(HDOFILES)
X
Xlocal.h: .local.h
X
X.local.h: Makefile local.proto
X	sed -e 's;HEARTSLIB;$(LIB);' \
X	    -e 's;DIST_PORTNUM;$(DIST_PORT);' \
X	    -e 's;PORTNUM;$(PORT);' \
X	 	local.proto > .local.h
X	-if cmp -s .local.h local.h; then\
X 		:;\
X 	else\
X 		cp .local.h local.h;\
X 	fi
X
Xalways:
X
Xhearts.o: misc.h defs.h local.h
Xheartsd.o: misc.h defs.h local.h
Xhearts_dist.o: misc.h defs.h local.h
Xselect.o: misc.h defs.h
Xsockio.o: defs.h
Xstart_dist.o: local.h
Xconnect.o: defs.h local.h
Xopensock.o: defs.h local.h
X
Xclean:	
X	rm -f $(HOFILES) $(DOFILES) $(HDOFILES) hearts hearts_dist local.h .local.h
X
Xlint:	linthearts lintheartsd linthearts_dist
X
Xlinthearts:
X	lint $(DEFS) $(HCFILES)
X
Xlintheartsd:
X	lint $(DEFS) $(DCFILES)
X
Xlinthearts_dist:
X	lint $(DEFS) $(HDCFILES)
X
END_OF_Makefile
if test 1938 -ne `wc -c <Makefile`; then
    echo shar: \"Makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f connect.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"connect.c\"
else
echo shar: Extracting \"connect.c\" \(1157 characters\)
sed "s/^X//" >connect.c <<'END_OF_connect.c'
X/*
X * connect.c
X *
X * client connection to hearts server
X */
X
X#include <stdio.h>
X#include "defs.h"
X#include "local.h"
X
Xchar *getenv();
X
X/*
X * Make connection to host running the hearts distributor server,
X * return fd of new socket.
X */
Xconnect_to(servhost, port)
Xchar	*servhost;			/* name of host running server */
Xint	port;
X{
X	int	sock;
X	struct	hostent *host;
X	struct	servent *distributor;
X	struct	sockaddr_in sockaddr;
X	char	buf[64];
X
X	if ((host = gethostbyname(servhost)) == NULL)  {
X		perror("gethostbyname");
X		exit(1);
X	}
X	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)  {
X		perror("socket");
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			(void) sprintf(buf, "%s: service not found\n", SERVICE);
X			fputs(buf, stderr);
X			exit(1);
X		}
X		sockaddr.sin_port = distributor->s_port;
X	}
X	if (connect(sock, (struct sockaddr *) &sockaddr, sizeof(sockaddr)) < 0) {
X		(void) close (sock);
X		return(0);
X	}
X	return(sock);
X}
END_OF_connect.c
if test 1157 -ne `wc -c <connect.c`; then
    echo shar: \"connect.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f defs.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"defs.h\"
else
echo shar: Extracting \"defs.h\" \(772 characters\)
sed "s/^X//" >defs.h <<'END_OF_defs.h'
X#include <sys/types.h>
X#include <sys/socket.h>
X#include <sys/file.h>
X#include <netinet/in.h>
X#include <netdb.h>
X
X#ifndef NULL
X#define NULL 0
X#endif
X
X#define	SLEN	40		/* short string */
X#define	PROTO	"tcp"		/* protocol */
X#define	SERVICE	"hearts"	/* official service name */
X
X#ifdef	FD_SETSIZE
X
X#define WIDTH FD_SETSIZE
Xtypedef	fd_set	fd_type;
X
X#define fd_init(sock, fds) FD_ZERO(fds); FD_SET(sock, fds)
X#define fd_set(sock, fds)  FD_SET(sock, fds)
X#define fd_zero(sock, fds) FD_ZERO(sock, fds)
X#define fd_isset(sock, fds)  FD_ISSET(sock, &fds)
X
X#else
X
X#define WIDTH 32
Xtypedef	int	fd_type;
X
X#define fd_init(sock, fds) *fds = 1 << sock
X#define fd_set(sock, fds)  *fds |= 1 << sock
X#define fd_zero(sock, fds) *fds = 0
X#define fd_isset(sock, fds)  fds & (1 << sock)
X
X#endif
X
END_OF_defs.h
if test 772 -ne `wc -c <defs.h`; then
    echo shar: \"defs.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f hearts.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"hearts.c\"
else
echo shar: Extracting \"hearts.c\" \(16883 characters\)
sed "s/^X//" >hearts.c <<'END_OF_hearts.c'
X/*
X * hearts - human interface to hearts program
X *
X * All smarts are in heartsd, which is invoked by initial caller of hearts.
X *
X * By Bob Ankeney or Generic Computer Products
X * Bug reports to:
X * ...!tektronix!reed!bob
X *
X *
X * Commands to hearts client (r = rank, s = suit):
X *
X * Ars		Add card to hand.
X * Rrs		Remove card from hand.
X * En		Erase window n.
X * G		Get a card.
X * Pnrs<name>	Play card from player n, whose name is <name>.
X * Snpptt<name>	Score points (pp) and total points (tt) for player n.
X * Mn<text>	Message <text> is placed in window n.
X * X		Go away.
X *
X * Messages from client:
X *
X * Prs		Pass/Play card from hand.
X * M<text>		Send message <text> to all players.
X *
X */
X
X#include <curses.h>
X#include <sys/errno.h>
X#include "misc.h"
X#include "defs.h"
X#include "local.h"
X
Xint	dist_socket, dealer_socket;
Xchar	host[20];
X
Xtypedef struct node *ptr;
X
Xstruct node {
X	ptr	llink, rlink;
X	int	rank;
X};
X
Xstruct	suit_list {
X	ptr	head, tail;
X	int	length;
X} 
Xmy_hand[MAX_SUIT + 1];
X
XWINDOW	*card_window[MAX_SUIT + 1],
X	*round_window, *lead_window, *inp_window, *text_window,
X	*play_window, *tplay_window, *mesg_window, *tmesg_window,
X	*header_window, *score_window,
X	**window_ptr[MESG_WINDOW + 1];
X
Xint	mesg_bottom,	/* Bottom line of lower message window */
X	tmesg_bottom;	/* Bottom line of upper message window */
Xchar	show_play,	/* TRUE = show play/score window, FALSE = show mesg */
X	first_game,
X	game_over;
X
Xchar	*snames[] = {
X	    "",
X	    "clubs",
X	    "diamonds",
X	    "hearts",
X	    "spades"
X	},
X	rnames[] = " 23456789TJQKA",
X	got_char;
X
X
Xinit_suit(list)
Xstruct suit_list *list;
X{
X	char *malloc();
X
X	list->length = 0;
X	list->head = (ptr) malloc(sizeof(struct node));
X	list->tail = (ptr) malloc(sizeof(struct node));
X	list->head->llink = NULL; 
X	list->tail->rlink = NULL;
X	list->head->rlink = list->tail; 
X	list->tail->llink = list->head;
X	list->head->rank = MAX_RANK + 1; 
X	list->tail->rank = 0;
X}
X
Xinit()
X{
X	int	wimp_out();
X	int	suit;
X	char	ch;
X	char	buffer[128];
X	char	*pager, *getenv();
X
X	first_game = TRUE;
X
X	(void) signal(SIGINT, wimp_out);
X	initscr();
X	clearok(stdscr, FALSE);
X	crmode();
X	init_windows();
X
X	mvwaddstr(play_window, 0, 0, "*** The Game of Hearts ***");
X	mvwaddstr(play_window, 2, 0, "Do you need instructions? ");
X	wrefresh(play_window);
X	ch = get_char();
X	if ((ch == 'y') || (ch == 'Y')) {
X		endwin();
X		clear();
X		refresh();
X		if (!(pager = getenv ("PAGER")))
X			pager = "more";
X		(void) sprintf (buffer, "%s %s", pager, INSTRUCT);
X		(void) system(buffer);
X		crmode();
X		printf ("Type any key to continue: "); 
X		(void) fflush (stdout);
X		(void) get_char();
X	}
X	noecho();
X
X	for (suit = CLUBS; suit <= SPADES; suit++)
X		init_suit(&my_hand[suit]);
X}
X
Xget_going()
X{
X	int	dealer_port;
X	char	*name, *getenv();
X	/*
X	 * Connect to distributor.  If not available, start it.
X	 */
X	if ((dist_socket = connect_to(host, PORT)) == 0) {
X		start_distributor();
X		if ((dist_socket = connect_to(host, PORT)) == 0)
X			death("Can't invoke distributor!");
X	}
X	/*
X	 * Get dealer port and connect to it.
X	 * If port returned == 0, restart game with old dealer.
X	 */
X	if (dealer_port = select_game()) {
X		if (!first_game)
X			(void) close(dealer_socket);
X		if ((dealer_socket = connect_to(host, dealer_port)) == 0)
X			death("Can't connect to dealer!\n");
X		if ((name = getenv("HEARTS")) == NULL)	/* get user name */
X			if ((name = getenv("NAME")) == NULL)
X				name = getenv("USER");
X		write_socket(dealer_socket, name);	/* tell dealer */
X	}
X
X	werase(play_window);
X	mvwaddstr(header_window, 0, 1, "player  score  total");
X	show_play = FALSE;
X	toggle_mesg();			/* Show play and score windows */
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
X/*
X *			Screen window layout
X *
X *                   2             4             6             7
X *     0             0             0             0             9
X *      --------------------------------------------------------
X *   0 |             |             |             |             |
X *   1 | card_window | card_window | card_window | card_window |
X *   2 |   [CLUBS]   |  [DIAMONDS] |   [HEARTS]  |   [SPADES]  |
X *     ~             ~             ~             ~             ~
X *     ~             ~             ~             ~             ~
X *  16 |             |             |             |             |
X *     |-------------------------------------------------------|
X *     |                                                       |
X *                         if show_play:
X *
X *                    2                      5                 7
X *     0              2                      8                 9
X *     |                                                       |
X *     ---------------------------------------------------------
X *  17 | round_window |                      |score_text_window|
X *     |--------------|                      |-----------------|
X *  18 |              |                      |                 |
X *     |--------------|                      |                 |
X *  19 | lead_window  |                      |                 |
X *     |--------------|                      |                 |
X *  20 | inp_window   |    play_window       |                 |
X *     |--------------|                      |                 |
X *  21 |              |                      |                 |
X *     |--------------|                      |                 |
X *  22 | text_window  |                      |                 |
X *     |--------------|----------------------------------------|
X *  23 |              |                                        |
X *     ~              ~                 mesg_window            ~
X *     ~              ~                                        ~
X *LINES|              |                                        |
X *     ---------------------------------------------------------
X *
X *                         if !show_play:
X *
X *                    2                      5                 7
X *     0              2                      8                 9
X *     |                                                       |
X *     ---------------------------------------------------------
X *  17 | round_window |     tplay_window     |                 |
X *     |--------------|----------------------------------------|
X *  18 |              |                                        |
X *     |--------------|                                        |
X *  19 | lead_window  |                                        |
X *     |--------------|                                        |
X *  20 | inp_window   |                tmesg_window            |
X *     |--------------|                                        |
X *  21 |              |                                        |
X *     |--------------|                                        |
X *  22 | text_window  |                                        |
X *     |--------------|----------------------------------------|
X *  23 |              |                                        |
X *     ~              ~                 mesg_window            ~
X *     ~              ~                                        ~
X *LINES|              |                                        |
X *     ---------------------------------------------------------
X */
Xinit_windows()
X{
X	int i;
X
X	for (i = CLUBS; i <= SPADES; i++) {
X		card_window[i] = newwin(17, 20, 0, (i - 1) * 20);
X		leaveok(card_window[i], TRUE);
X		werase(card_window[i]);
X	}
X	round_window	= newwin(1, 22, 17,  0);
X	lead_window	= newwin(1, 22, 19,  0);
X	inp_window	= newwin(1, 22, 20,  0);
X	text_window	= newwin(1, 22, 22,  0);
X	play_window	= newwin(6, 35, 17, 23);
X	tplay_window	= newwin(1, 57, 17, 23);
X	header_window	= newwin(1, 22, 17, 58);
X	score_window	= newwin(5, 22, 18, 58);
X	mesg_window	= newwin(0, 57, 23, 23);
X	tmesg_window	= newwin(5, 57, 18, 23);
X	mesg_bottom = LINES - 24;
X	tmesg_bottom = 4;
X	scrollok(mesg_window, TRUE);
X	scrollok(tmesg_window, TRUE);
X	window_ptr[ROUND_WINDOW] = &round_window;
X	window_ptr[LEAD_WINDOW]  = &lead_window;
X	window_ptr[INP_WINDOW]   = &inp_window;
X	window_ptr[TEXT_WINDOW]  = &text_window;
X	window_ptr[PLAY_WINDOW]  = &play_window;
X	window_ptr[SCORE_WINDOW] = &score_window;
X	window_ptr[MESG_WINDOW]  = &mesg_window;
X}
X
X/*
X * toggle_mesg - toggle whether showing message window or score/play windows
X */
Xtoggle_mesg()
X{
X	if (show_play) {
X		touchwin(tplay_window);
X		touchwin(tmesg_window);
X		wrefresh(tplay_window);
X		wrefresh(tmesg_window);
X	}
X	else {
X		touchwin(play_window);
X		touchwin(header_window);
X		touchwin(score_window);
X		wrefresh(play_window);
X		wrefresh(header_window);
X		wrefresh(score_window);
X	}
X	show_play = !show_play;
X}
X
X/*
X * ovl_refresh - refresh window only if visible
X */
Xovl_refresh(window_num)
Xint	window_num;
X{
X	switch (window_num) {
X	case PLAY_WINDOW:
X		if (show_play)
X			wrefresh(play_window);
X		break;
X	case SCORE_WINDOW:
X		if (show_play)
X			wrefresh(score_window);
X		break;
X	default:
X		wrefresh(*window_ptr[window_num]);
X	}
X}
X
X/*
X * scroll_mesg - scroll mesg_window onto tmesg_window
X */
Xscroll_mesg()
X{
X	int	i;
X	char	ch;
X
X	wmove(tmesg_window, tmesg_bottom, 56);
X	scroll(tmesg_window);
X	wmove(tmesg_window, tmesg_bottom, 0);
X	for (i = 0; i < 56; i++) {
X		wmove(mesg_window, 0, i);
X		ch = winch(mesg_window);
X		waddch(tmesg_window, ch);
X	}
X	scroll(mesg_window);
X	if (!show_play)
X		wrefresh(tmesg_window);
X}
X	
Xput_card(window, y, x, rank, suit)
XWINDOW	*window;
Xint	y, x,
Xrank, suit;
X{
X	mvwaddstr(window, y,     x, "+-----+");
X	mvwaddstr(window, y + 1, x, "|     |");
X	mvwaddstr(window, y + 2, x, "|     |");
X	mvwaddstr(window, y + 3, x, "|     |");
X	mvwaddstr(window, y + 4, x, "+-----+");
X	if ((suit == HEARTS) || (suit == DIAMONDS))
X		wstandout(window);
X	mvwaddch(window, y + 1, x + 1, rnames[rank]);
X	waddch(window, *snames[suit]);
X	mvwaddch(window, y + 3, x + 4, rnames[rank]);
X	waddch(window, *snames[suit]);
X	wstandend(window);
X}
X
Xprint_cards(suit)
Xint	suit;
X{
X	int y, x;
X	ptr p;
X
X	werase(card_window[suit]);
X	x = y = 0;
X	p = my_hand[suit].head->rlink;
X	while (p->rank) {
X		if (x == 7) {
X			++x;
X			y = 0;
X		}
X		put_card(card_window[suit], y, x++, p->rank, suit);
X		y += 2;
X		p = p->rlink;
X	}
X	wrefresh(card_window[suit]);
X}
X
Xget_char()
X{
X	char	ch;
X
X	if (read(0, &ch, 1) == -1) {
X		perror("read");
X		abort();
X	}
X	return(ch);
X}
X
Xread_char()
X{
X	char	ch;
X
X	while (!got_char)
X		scan();
X	ch = got_char;
X	got_char = FALSE;
X	return(ch);
X}
X
Xread_card()
X{
X	int	rank, suit;
X	char	buf[64];
X
X	do {
X		do {
X			werase(inp_window);
X			wrefresh(inp_window);
X			buf[0] = 'P';
X			buf[1] = read_char();	/* Get rank (not smelly) */
X			if (buf[1] >= 'a')
X				buf[1] += 'A' - 'a';
X			if (!(rank = get_rank(buf[1]))) {
X				werase(text_window);
X				waddstr(text_window, "Bad rank!");
X				wrefresh(text_window);
X			}
X		} 
X		while (!rank);
X		werase(text_window);
X		wrefresh(text_window);
X		wprintw(inp_window, "%c ", buf[1]);
X		wrefresh(inp_window);
X		buf[2] = read_char();		/* Get suit (not tuxedo) */
X		if ((buf[2] >= 'A') && (buf[2] <= 'Z'))
X			buf[2] += 'a' - 'A';
X		if (!(suit = get_suit(buf[2])) &&
X		    (buf[2] != _tty.sg_erase) && (buf[2] != _tty.sg_kill)) {
X			werase(text_window);
X			waddstr(text_window, "Bad suit!");
X			wrefresh(text_window);
X		}
X	} 
X	while (!suit);
X	wprintw(inp_window, "of %s", snames[suit]);
X	wrefresh(inp_window);
X	buf[3] = '\0';
X	write_socket(dealer_socket, buf);
X}
X
Xenter_card(rank, suit)
Xint	rank, suit;
X{
X	ptr p, p1;
X
X	p = my_hand[suit].head;
X	++my_hand[suit].length;
X	while (p->rank > rank)
X		p = p->rlink;
X	p1 = (ptr) malloc(sizeof(struct node));
X	p1->llink = p->llink;
X	p1->llink->rlink = p1;
X	p->llink = p1;
X	p1->rlink = p;
X	p1->rank = 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
X
Xptr
Xfind_card(rank_to_play, suit_to_play)
Xint	rank_to_play, suit_to_play;
X{
X	ptr	card_to_play;
X
X	card_to_play = my_hand[suit_to_play].head->rlink;
X	while (card_to_play->rank != rank_to_play)
X		card_to_play = card_to_play->rlink;
X	remove_node(card_to_play);
X	--my_hand[suit_to_play].length;
X	return(card_to_play);
X}
X
Xdo_command(buf)
Xchar	*buf;
X{
X	char	rch, sch;
X	int	player, suit, pts, total_pts, window_num;
X	int	temp_y, temp_x;
X
X	switch (buf[0]) {
X	case 'A' :		/* Add card to hand */
X		enter_card(get_rank(buf[1]), suit = get_suit(buf[2]));
X		print_cards(suit);
X		break;
X
X	case 'R' :		/* Remove card from hand */
X		suit = get_suit(buf[2]);
X		remove_node(find_card(get_rank(buf[1]), suit));
X		print_cards(suit);
X		break;
X
X	case 'E' :		/* Erase window */
X		window_num = buf[1] - '0';
X		if (window_num == PLAY_WINDOW) {
X			werase(tplay_window);
X			if (!show_play)
X				wrefresh(tplay_window);
X		}
X		werase(*window_ptr[window_num]);
X		ovl_refresh(window_num);
X		break;
X
X	case 'G' :		/* Get card */
X		read_card();
X		break;
X
X	case 'P' :		/* Play card from player */
X		(void) sscanf(buf+1, "%1d %c %c", &player, &rch, &sch);
X		/*
X		 * Update standard play window
X		 */
X		put_card(play_window, 0, (player - 1) * 9,
X		get_rank(rch), get_suit(sch));
X		if (buf[4] != '\0')
X			mvwaddstr(play_window, 5, (player - 1) * 9, buf + 4);
X		/*
X		 * Update alternate play window
X		 */
X		if ((sch == 'h') || (sch == 'd'))
X			wstandout(tplay_window);
X		mvwaddch(tplay_window, 0, (player - 1) * 9, rch);
X		waddch(tplay_window, sch);
X		wstandend(tplay_window);
X		if (show_play)
X			wrefresh(play_window);
X		else
X			wrefresh(tplay_window);
X		sleep(1);
X		break;
X
X	case 'S' :		/* Score points for player */
X		(void) sscanf(buf+1, "%1d %2d %2d", &player, &pts, &total_pts);
X		mvwaddstr(score_window, player - 1, 1, "        ");
X		mvwaddstr(score_window, player - 1, 1, buf + 6);
X		mvwprintw(score_window, player - 1, 9, " %2d     %2d",
X				pts, total_pts);
X		ovl_refresh(SCORE_WINDOW);
X		break;
X
X	case 'M' :		/* Print message in window */
X		getyx(curscr, temp_y, temp_x);
X		window_num = buf[1] - '0';
X		if (window_num == MESG_WINDOW) {
X			scroll_mesg();
X			mvwaddstr(mesg_window, mesg_bottom, 0, buf + 2);
X		}
X		else {
X			werase(*window_ptr[window_num]);
X			mvwaddstr(*window_ptr[window_num], 0, 0, buf + 2);
X		}
X		ovl_refresh(window_num);
X		move(temp_y, temp_x);
X		refresh();
X		break;
X
X	case 'X' :		/* Game over */
X		if (!show_play)
X			toggle_mesg();
X		werase(round_window);
X		wrefresh(round_window);
X		werase(lead_window);
X		wstandout(lead_window);
X		mvwaddstr(lead_window, 0, 0, "--More--");
X		wstandend(lead_window);
X		wrefresh(lead_window);
X		(void) get_char();
X		first_game = FALSE;
X		game_over = TRUE;
X	}
X}
X
X/*
X * Scan input for redraw screen requests or ':' messages.
X * Also scan socket for incoming commands.
X */
Xscan()
X{
X	int		i, temp_y, temp_x;
X	char		buf[64];
X	fd_type	read_fd;
X	int		nready;
X	extern int	errno;
X
X	fd_init(dealer_socket, &read_fd);
X	if (!got_char)
X		fd_set(0, &read_fd);
X	do {
X		nready = select(WIDTH, &read_fd, (fd_type *) 0, (fd_type *) 0,
X		(struct timeval *) 0);
X	}
X	while (nready == -1 && errno == EINTR);
X	if (nready == 0)
X		return;
X	if (fd_isset(0, read_fd)) {
X		got_char = get_char();
X		getyx(curscr, temp_y, temp_x);
X		switch (got_char) {
X		case '\f' :
X			wrefresh(curscr);	/* Redraw screen */
X			got_char = FALSE;
X			break;
X
X		case ' '  :
X		case '\t' :
X		case '\n' :
X		case '\r' :
X			got_char = FALSE;	/* Ignore white space */
X			break;
X
X		case 'M' :
X		case 'm' :
X			toggle_mesg();
X			got_char = FALSE;
X			break;
X
X		case ':' :
X			scroll_mesg();		/* Message to all players */
X			mvwaddstr(mesg_window, mesg_bottom, 0, "message:");
X			wrefresh(mesg_window);
X			buf[0] = 'M';
X			for (i = 1; (i < 48) && ((buf[i] = get_char()) != '\n');
X					i++) {
X				if (buf[i] == _tty.sg_erase) {
X					i -= 2;
X					if (i < 0)
X						i = 0;
X					else {
X						wmove(mesg_window, mesg_bottom, i+8);
X						wdelch(mesg_window);
X					}
X				} 
X				else {
X					if (buf[i] == _tty.sg_kill) {
X						wmove(mesg_window, mesg_bottom, 8);
X						wclrtoeol(mesg_window);
X						i = 0;
X					} 
X					else
X						mvwaddch(mesg_window, mesg_bottom, i + 7, buf[i]);
X				}
X				wrefresh(mesg_window);
X			}
X			buf[i] = '\0';
X			write_socket(dealer_socket, buf);
X			got_char = FALSE;
X		}
X		move(temp_y, temp_x);
X		refresh();
X	}
X	if (fd_isset(dealer_socket, read_fd)) {
X		if (!read_socket(dealer_socket, buf))
X			death("Dealer died!!");
X		do_command(buf);
X	}
X}
X
Xclose_windows()
X{
X	(void) signal(SIGINT, SIG_IGN);
X	clear();
X	refresh();
X	(void) fflush(stdout);
X	endwin();
X}
X
Xdeath(buf)
Xchar	*buf;
X{
X	close_windows();
X	printf("%s\n", buf);
X	exit(1);
X}
X
Xwimp_out()
X{
X	close_windows();
X	exit(0);
X}
X
X
Xmain(argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	if (argc > 2) {
X		fprintf(stderr, "usage: %s [hostname]\n", *argv);
X		exit(1);
X	}
X	if (argc == 2)
X		(void) strcpy (host, *++argv);
X	else
X		(void) gethostname(host, 20);	/* host is this machine */
X
X	init();
X
X	for (;;) {
X		game_over = FALSE;
X		get_going();
X		do {
X			scan();
X		}
X		while (!game_over);
X	}
X}
X
END_OF_hearts.c
if test 16883 -ne `wc -c <hearts.c`; then
    echo shar: \"hearts.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f hearts.instr -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"hearts.instr\"
else
echo shar: Extracting \"hearts.instr\" \(1833 characters\)
sed "s/^X//" >hearts.instr <<'END_OF_hearts.instr'
X                               T_h_e_ G_a_m_e_
X
X     Hearts is yet another benign, mindless card game.  This particular
Xvariation is four-handed, with up to four human players allowed.  The
Xremaining hands are played by the computer.  Players may join at any time.
XA computer player will replace any player that leaves the game.
X     The object of the game is to score as few points as possible.  Points
Xare scored by taking tricks with either hearts in them (each heart
Xscores one point), or the Queen of Spades (scores 13).  A player that
Xtakes all 26 points is said to "shoot the moon" and scores zero, while
Xall opponents score 26.
X
X                               P_a_s_s_i_n_g_
X
X     Four hands are played, in which each player is dealt 13 cards.  
XEach player passes three cards to another player, except on the final
Xround, when no cards are passed.
X
X                               T_h_e_ P_l_a_y_
X
X     The lead card is the 2 of Clubs.  Each player must follow suit, if
Xpossible.  Otherwise, any card may be played.  Points may not be dumped
Xthe first round.  Hearts may not be led until a heart has been dumped.
X     Play continues till all 13 rounds have been played.
X
X                                  N_o_t_e_s_
X
X     To specify a card, the player must enter the card rank (2-9, T, J,
XQ, K, or A) and, if needed, the first letter of the suit (C, D, H, or S).
XFor example, entering "jd" will appear as "Jack of Diamonds".
X
X     Messages may be passed to other players by typing ':' followed by the
Xmessage.  A scrolling message window exists consisting of lines 23 to the
Xbottom of the screen (just 1 line on 24-line terminals).  A hidden window
Xbehind the play and score windows contains an additional 5 message lines,
Xabove which are the cards being played.  To toggle this window, type 'm'.
X
X
END_OF_hearts.instr
echo shar: 26 control characters may be missing from \"hearts.instr\"
if test 1833 -ne `wc -c <hearts.instr`; then
    echo shar: \"hearts.instr\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f local.proto -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"local.proto\"
else
echo shar: Extracting \"local.proto\" \(286 characters\)
sed "s/^X//" >local.proto <<'END_OF_local.proto'
X/*
X * local.proto
X *
X * prototype for local.h -- creates pathnames for various files
X * from the makefile.
X */
X
X#define INSTRUCT "HEARTSLIB/hearts.instr"
X#define HEARTSD "HEARTSLIB/heartsd"
X#define HEARTS_DIST "HEARTSLIB/hearts_dist"
X#define DIST_PORT DIST_PORTNUM
X#define PORT PORTNUM
END_OF_local.proto
if test 286 -ne `wc -c <local.proto`; then
    echo shar: \"local.proto\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f misc.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"misc.h\"
else
echo shar: Extracting \"misc.h\" \(480 characters\)
sed "s/^X//" >misc.h <<'END_OF_misc.h'
X
X#include <stdio.h>
X#include <signal.h>
X#include <strings.h>
X#include <sys/time.h>
X
X#ifndef	TRUE
X#define	TRUE 	1
X#endif
X
X#ifndef	FALSE
X#define	FALSE	0
X#endif
X
X
X#define CLUBS 		1
X#define DIAMONDS	2
X#define HEARTS		3
X#define SPADES		4
X
X#define MIN_RANK	1	/*  2  */
X#define MAX_RANK	13	/* Ace */
X#define MAX_SUIT	4	/* 1..4 */
X
X#define	ROUND_WINDOW	1
X#define	LEAD_WINDOW	2
X#define INP_WINDOW	3
X#define	TEXT_WINDOW	4
X#define	PLAY_WINDOW	5
X#define	SCORE_WINDOW	6
X#define	MESG_WINDOW	7
X
END_OF_misc.h
if test 480 -ne `wc -c <misc.h`; then
    echo shar: \"misc.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f select.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"select.c\"
else
echo shar: Extracting \"select.c\" \(7179 characters\)
sed "s/^X//" >select.c <<'END_OF_select.c'
X#include <curses.h>
X#include "misc.h"
X#include "defs.h"
X
Xextern WINDOW	*card_window[],
X		*round_window, *lead_window, *text_window, *play_window;
X
Xextern	int	dist_socket;
Xextern	char	first_game;
X
Xtypedef struct table *table_ptr;
X
Xstruct table {
X	table_ptr next_table;		/* Points to next table entry */
X	int	table_id;		/* Unique ID from distributor */
X	char	player_name[4][9];	/* Player names */
X	int	hand;			/* Current table hand */
X	int	round;			/* Current table round */
X	char	closed;			/* If game over */
X};
X
Xtable_ptr	first_table,
X		cur_table;
X
Xint		table_count,		/* # tables existing */
X		cur_screen_table,	/* nth table shown as table 1 */
X		screen_table_id[8];	/* table_id of tables on screen */
X
Xchar		buf[64];
X
X
X/*
X * Find table given unique table_id in buf[1].
X */
Xtable_ptr
Xfind_table(buf)
Xchar	*buf;
X{
X	table_ptr	cur_table;
X	int		table_num;
X
X	(void) sscanf(buf + 1, "%d", &table_num);
X	for (cur_table = first_table;
X			cur_table && cur_table->table_id != table_num;
X			cur_table = cur_table->next_table)
X		;
X	return(cur_table);
X}
X
X/*
X * Create new table given table_id in buf[1].  Return pointer to new table.
X */
Xtable_ptr
Xnew_table(buf)
Xchar	*buf;
X{
X	table_ptr	cur_ptr, new_table_ptr;
X	int		i, table_num;
X	char		*malloc();
X
X	(void) sscanf(buf + 1, "%d", &table_num);
X	new_table_ptr = (table_ptr) malloc(sizeof(struct table));
X	new_table_ptr->next_table = NULL;
X	new_table_ptr->table_id = table_num;
X	new_table_ptr->closed = FALSE;
X	for (i = 0; i < 4; i++)
X		(void) strcpy(new_table_ptr->player_name[i], "<empty>");
X	if (first_table) {
X		for (cur_ptr = first_table; cur_ptr->next_table;
X				cur_ptr = cur_ptr->next_table)
X			;
X		cur_ptr->next_table = new_table_ptr;
X	}
X	else
X		first_table = new_table_ptr;
X	++table_count;
X	return(new_table_ptr);
X}
X
X/*
X * Update current table entry based on buf.  Return table # modified.
X */
Xupdate_table(buf)
Xchar	*buf;
X{
X	int		i;
X	table_ptr	tmp_table;
X
X	switch (buf[0]) {
X	case 't' :
X		if ((cur_table = find_table(buf)) == NULL)
X			cur_table = new_table(buf);
X		break;
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		break;
X	case 'x' :
X		if (tmp_table = find_table(buf)) {
X			for (i = 0; i < 8; i++)
X				if (screen_table_id[i] = tmp_table->table_id)
X					screen_table_id[i] = 0;
X			tmp_table->table_id = 0;
X			tmp_table->closed = TRUE;
X		}
X	}
X}
X
X/*
X * show_table - display table which is position table_pos on screen.
X */
Xshow_table(cur_ptr, table_pos)
Xtable_ptr	cur_ptr;
Xint		table_pos;
X{
X	int	window_num, window_pos;
X	int 	i;
X
X	window_num = table_pos % 4 + 1;
X	window_pos =  (table_pos + 4) & 8;
X	for (i = 0; i < 6; i++) {
X		wmove(card_window[window_num], window_pos + i, 0);
X		wclrtoeol(card_window[window_num]);
X	}
X	mvwprintw(card_window[window_num], window_pos++, 0,
X		"     Table %d", table_pos + 1);
X	if (cur_ptr->closed)
X		mvwaddstr(card_window[window_num], window_pos++, 0,
X			"    <game over>");
X	else {
X		if (cur_ptr->hand == 0)
X			mvwaddstr(card_window[window_num], window_pos++, 0,
X				"     <starting>");
X		else
X			mvwprintw(card_window[window_num], window_pos++, 0,
X					"Hand: %d  Round: %d",
X					cur_ptr->hand, cur_ptr->round);
X		for (i = 0; i <4; i++)
X			mvwaddstr(card_window[window_num], window_pos++, 5,
X				cur_ptr->player_name[i]);
X	}
X	wrefresh(card_window[window_num]);
X}
X
X/*
X * show_tables - display up to 8 tables starting with table # start_id.
X */
Xshow_tables(start_id)
Xint	start_id;
X{
X	table_ptr	cur_ptr;
X	int		cur_id, i;
X
X	for (i = 0; i < 8; i++)
X		screen_table_id[i] = 0;
X	werase(round_window);
X	if (table_count)
X		mvwprintw(round_window, 0, 0, "Page %d of %d",
X				(start_id + 7) / 8, (table_count + 7) / 8);
X	wrefresh(round_window);
X	cur_ptr = first_table;
X	for (cur_id = 1; cur_id < start_id; cur_id++)
X		cur_ptr = cur_ptr->next_table;
X	for (cur_id = 0; (cur_id < 8) && cur_ptr;
X			cur_ptr = cur_ptr->next_table) {
X		screen_table_id[cur_id] = cur_ptr->table_id;
X		show_table(cur_ptr, cur_id++);
X	}
X}
X
Xask_option()
X{
X	werase(lead_window);
X	mvwaddstr(lead_window, 0, 0, "Option: ");
X	wrefresh(lead_window);
X}
X
Xshow_options()
X{
X	werase(play_window);
X	mvwaddstr(play_window, 0, 0, "Options are:");
X	if (!first_game)
X		mvwaddstr(play_window, 1, 0, "<A>nother game");
X	mvwaddstr(play_window, 2, 0, "<N>ew game");
X	if (table_count)
X		mvwaddstr(play_window, 3, 0, "<J>oin game");
X	if (table_count > 8)
X		mvwaddstr(play_window, 4, 0, "<M>ore games");
X	mvwaddstr(play_window, 5, 0, "<Q>uit");
X	wrefresh(play_window);
X	ask_option();
X}
X
Xdist_died()
X{
X	death("Distributor died!!");
X}
X
Xselect_game()
X{
X	int	dealer_port;
X	fd_type	read_fd;
X	int	i;
X	char	ch;
X	char	joined, joining;
X	table_ptr	temp_table;
X
X	clear();
X	refresh();
X	/*
X	 * Get current games info
X	 */
X	do {
X		if (read_socket(dist_socket, buf) == 0)
X			dist_died();
X		if (buf[0] != 'g')
X			update_table(buf);
X	}
X	while (buf[0] != 'g');
X
X	show_tables(cur_screen_table = 1);
X	show_options();
X
X	/*
X	 * Wait for user input or table update info
X	 */
X	joined = joining = FALSE;
X
X	do {
X		fd_init(0, &read_fd);		/* stdin */
X		fd_set(dist_socket, &read_fd);
X		if (select(WIDTH, &read_fd, (fd_type *) 0, (fd_type *) 0,
X				(struct timeval *) 0) == -1)
X			dist_died();
X		if (fd_isset(dist_socket, read_fd)) {
X			if (read_socket(dist_socket, buf) == 0)
X				dist_died();
X			i = update_table(buf);
X			show_tables(cur_screen_table);
X		}
X		if (fd_isset(0, read_fd)) {
X			ch = get_char();
X			werase(text_window);
X			wrefresh(text_window);
X			switch (ch) {
X			case 'Q' :
X			case 'q' :
X				wimp_out();
X			case 'A' :
X			case 'a' :
X				if (!first_game)
X					joined = TRUE;
X				break;
X			case 'N' :
X			case 'n' :
X				write_socket(dist_socket, "n");
X				joined = TRUE;
X				break;
X			case 'J' :
X			case 'j' :
X				mvwaddstr(lead_window, 0, 0, "Join table #:");
X				wrefresh(lead_window);
X				joining = TRUE;
X				break;
X			case 'M' :
X			case 'm' :
X				if (table_count > 8) {
X					if ((cur_screen_table += 8) > table_count)
X						cur_screen_table = 1;
X					for (i = CLUBS; i <= SPADES; i++) {
X						werase(card_window[i]);
X						wrefresh(card_window[i]);
X					}
X					show_tables(cur_screen_table);
X				}
X				break;
X			default:
X				if (joining && (ch >= '1') && (ch <= '8')) {
X					if (i = screen_table_id[ch - '1']) {
X						(void) sprintf(buf, "j%d", i);
X						write_socket(dist_socket, buf);
X						joined = TRUE;
X					}
X					else {
X						mvwaddstr(text_window, 0, 0, "Table not open.");
X						wrefresh(text_window);
X						ask_option();
X					}
X				}
X				else {
X					mvwaddstr(text_window, 0, 0, "Huh?");
X					wrefresh(text_window);
X					ask_option();
X				}
X				joining = FALSE;
X				break;
X			}
X		}
X	}
X	while (!joined);
X
X	/*
X	 * Now free up malloced table space
X	 */
X	while (first_table) {
X		temp_table = first_table;
X		first_table = first_table->next_table;
X		free ((char *) temp_table);
X	}
X	clear();
X	refresh();
X
X	if (!first_game && ((ch == 'a') || (ch == 'A')))
X		/*
X		 * Play another game with current dealer.
X		 */
X		dealer_port = 0;
X	else {
X		if (read_socket(dist_socket, buf) == 0)
X			dist_died();
X		(void) sscanf(buf + 1, "%d", &dealer_port);
X	}
X	(void) close(dist_socket);
X	return(dealer_port);
X}
END_OF_select.c
if test 7179 -ne `wc -c <select.c`; then
    echo shar: \"select.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f sockio.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"sockio.c\"
else
echo shar: Extracting \"sockio.c\" \(676 characters\)
sed "s/^X//" >sockio.c <<'END_OF_sockio.c'
X#include <stdio.h>
X/*
X * Routines to send and receive on sockets.  Four bytes of length are
X * sent, followed by the null terminated string.
X *
X */
Xread_socket(s, buf)
Xint	s;			/* socket to talk on */
Xchar	*buf;			/* string to send */
X{
X	int nbytes;
X
X	if (read(s, (char *) &nbytes, sizeof(int)) != sizeof (int))
X		return 0;
X	nbytes = ntohl(nbytes);
X	if (read(s, buf, nbytes) != nbytes)
X		return 0;
X	return nbytes;
X}
X
X
Xwrite_socket(s, buf)
Xint	s;			/* socket to talk on */
Xchar	*buf;			/* string to read on */
X{
X	int nbytes, netnbytes;
X
X	nbytes = strlen(buf) + 1;
X	netnbytes = htonl(nbytes);
X	(void) write(s, (char *) &netnbytes, sizeof(int));
X	(void) write(s, buf, nbytes);
X}
END_OF_sockio.c
if test 676 -ne `wc -c <sockio.c`; then
    echo shar: \"sockio.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f start_dist.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"start_dist.c\"
else
echo shar: Extracting \"start_dist.c\" \(328 characters\)
sed "s/^X//" >start_dist.c <<'END_OF_start_dist.c'
X/*
X * autostart - start up the distributor if it's not around.
X */
X
X#include "local.h"
X
Xstart_distributor ()
X{
X	int	pid;
X
X	switch (pid = fork()) {
X	case 0:
X		(void) setpgrp (0, getpid());
X		execl (HEARTS_DIST, "hearts_dist", 0);
X		exit (1);
X	case -1:
X		perror ("fork");
X		exit (1);
X	default:
X		while (wait (0) != pid)
X			;
X	}
X}
END_OF_start_dist.c
if test 328 -ne `wc -c <start_dist.c`; then
    echo shar: \"start_dist.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 2 \(of 2\).
cp /dev/null ark2isdone
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 archive.
exit 0