DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T h

⟦0c56830aa⟧ TextFile

    Length: 16883 (0x41f3)
    Types: TextFile
    Names: »hearts.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/General/Hearts/hearts.c« 

TextFile

/*
 * hearts - human interface to hearts program
 *
 * All smarts are in heartsd, which is invoked by initial caller of hearts.
 *
 * By Bob Ankeney or Generic Computer Products
 * Bug reports to:
 * ...!tektronix!reed!bob
 *
 *
 * Commands to hearts client (r = rank, s = suit):
 *
 * Ars		Add card to hand.
 * Rrs		Remove card from hand.
 * En		Erase window n.
 * G		Get a card.
 * Pnrs<name>	Play card from player n, whose name is <name>.
 * Snpptt<name>	Score points (pp) and total points (tt) for player n.
 * Mn<text>	Message <text> is placed in window n.
 * X		Go away.
 *
 * Messages from client:
 *
 * Prs		Pass/Play card from hand.
 * M<text>		Send message <text> to all players.
 *
 */

#include <curses.h>
#include <sys/errno.h>
#include "misc.h"
#include "defs.h"
#include "local.h"

int	dist_socket, dealer_socket;
char	host[20];

typedef struct node *ptr;

struct node {
	ptr	llink, rlink;
	int	rank;
};

struct	suit_list {
	ptr	head, tail;
	int	length;
} 
my_hand[MAX_SUIT + 1];

WINDOW	*card_window[MAX_SUIT + 1],
	*round_window, *lead_window, *inp_window, *text_window,
	*play_window, *tplay_window, *mesg_window, *tmesg_window,
	*header_window, *score_window,
	**window_ptr[MESG_WINDOW + 1];

int	mesg_bottom,	/* Bottom line of lower message window */
	tmesg_bottom;	/* Bottom line of upper message window */
char	show_play,	/* TRUE = show play/score window, FALSE = show mesg */
	first_game,
	game_over;

char	*snames[] = {
	    "",
	    "clubs",
	    "diamonds",
	    "hearts",
	    "spades"
	},
	rnames[] = " 23456789TJQKA",
	got_char;


init_suit(list)
struct suit_list *list;
{
	char *malloc();

	list->length = 0;
	list->head = (ptr) malloc(sizeof(struct node));
	list->tail = (ptr) malloc(sizeof(struct node));
	list->head->llink = NULL; 
	list->tail->rlink = NULL;
	list->head->rlink = list->tail; 
	list->tail->llink = list->head;
	list->head->rank = MAX_RANK + 1; 
	list->tail->rank = 0;
}

init()
{
	int	wimp_out();
	int	suit;
	char	ch;
	char	buffer[128];
	char	*pager, *getenv();

	first_game = TRUE;

	(void) signal(SIGINT, wimp_out);
	initscr();
	clearok(stdscr, FALSE);
	crmode();
	init_windows();

	mvwaddstr(play_window, 0, 0, "*** The Game of Hearts ***");
	mvwaddstr(play_window, 2, 0, "Do you need instructions? ");
	wrefresh(play_window);
	ch = get_char();
	if ((ch == 'y') || (ch == 'Y')) {
		endwin();
		clear();
		refresh();
		if (!(pager = getenv ("PAGER")))
			pager = "more";
		(void) sprintf (buffer, "%s %s", pager, INSTRUCT);
		(void) system(buffer);
		crmode();
		printf ("Type any key to continue: "); 
		(void) fflush (stdout);
		(void) get_char();
	}
	noecho();

	for (suit = CLUBS; suit <= SPADES; suit++)
		init_suit(&my_hand[suit]);
}

get_going()
{
	int	dealer_port;
	char	*name, *getenv();
	/*
	 * Connect to distributor.  If not available, start it.
	 */
	if ((dist_socket = connect_to(host, PORT)) == 0) {
		start_distributor();
		if ((dist_socket = connect_to(host, PORT)) == 0)
			death("Can't invoke distributor!");
	}
	/*
	 * Get dealer port and connect to it.
	 * If port returned == 0, restart game with old dealer.
	 */
	if (dealer_port = select_game()) {
		if (!first_game)
			(void) close(dealer_socket);
		if ((dealer_socket = connect_to(host, dealer_port)) == 0)
			death("Can't connect to dealer!\n");
		if ((name = getenv("HEARTS")) == NULL)	/* get user name */
			if ((name = getenv("NAME")) == NULL)
				name = getenv("USER");
		write_socket(dealer_socket, name);	/* tell dealer */
	}

	werase(play_window);
	mvwaddstr(header_window, 0, 1, "player  score  total");
	show_play = FALSE;
	toggle_mesg();			/* Show play and score windows */
}

get_rank(rch)
char	rch;
{
	int	i;

	for (i = 1; i <= MAX_RANK; i++)
		if (rch == rnames[i])
			return(i);
	return(0);
}

get_suit(sch)
char	sch;
{
	int	i;

	for (i = 1; i <= MAX_SUIT; i++)
		if (sch == *snames[i])
			return(i);
	return(0);
}

/*
 *			Screen window layout
 *
 *                   2             4             6             7
 *     0             0             0             0             9
 *      --------------------------------------------------------
 *   0 |             |             |             |             |
 *   1 | card_window | card_window | card_window | card_window |
 *   2 |   [CLUBS]   |  [DIAMONDS] |   [HEARTS]  |   [SPADES]  |
 *     ~             ~             ~             ~             ~
 *     ~             ~             ~             ~             ~
 *  16 |             |             |             |             |
 *     |-------------------------------------------------------|
 *     |                                                       |
 *                         if show_play:
 *
 *                    2                      5                 7
 *     0              2                      8                 9
 *     |                                                       |
 *     ---------------------------------------------------------
 *  17 | round_window |                      |score_text_window|
 *     |--------------|                      |-----------------|
 *  18 |              |                      |                 |
 *     |--------------|                      |                 |
 *  19 | lead_window  |                      |                 |
 *     |--------------|                      |                 |
 *  20 | inp_window   |    play_window       |                 |
 *     |--------------|                      |                 |
 *  21 |              |                      |                 |
 *     |--------------|                      |                 |
 *  22 | text_window  |                      |                 |
 *     |--------------|----------------------------------------|
 *  23 |              |                                        |
 *     ~              ~                 mesg_window            ~
 *     ~              ~                                        ~
 *LINES|              |                                        |
 *     ---------------------------------------------------------
 *
 *                         if !show_play:
 *
 *                    2                      5                 7
 *     0              2                      8                 9
 *     |                                                       |
 *     ---------------------------------------------------------
 *  17 | round_window |     tplay_window     |                 |
 *     |--------------|----------------------------------------|
 *  18 |              |                                        |
 *     |--------------|                                        |
 *  19 | lead_window  |                                        |
 *     |--------------|                                        |
 *  20 | inp_window   |                tmesg_window            |
 *     |--------------|                                        |
 *  21 |              |                                        |
 *     |--------------|                                        |
 *  22 | text_window  |                                        |
 *     |--------------|----------------------------------------|
 *  23 |              |                                        |
 *     ~              ~                 mesg_window            ~
 *     ~              ~                                        ~
 *LINES|              |                                        |
 *     ---------------------------------------------------------
 */
init_windows()
{
	int i;

	for (i = CLUBS; i <= SPADES; i++) {
		card_window[i] = newwin(17, 20, 0, (i - 1) * 20);
		leaveok(card_window[i], TRUE);
		werase(card_window[i]);
	}
	round_window	= newwin(1, 22, 17,  0);
	lead_window	= newwin(1, 22, 19,  0);
	inp_window	= newwin(1, 22, 20,  0);
	text_window	= newwin(1, 22, 22,  0);
	play_window	= newwin(6, 35, 17, 23);
	tplay_window	= newwin(1, 57, 17, 23);
	header_window	= newwin(1, 22, 17, 58);
	score_window	= newwin(5, 22, 18, 58);
	mesg_window	= newwin(0, 57, 23, 23);
	tmesg_window	= newwin(5, 57, 18, 23);
	mesg_bottom = LINES - 24;
	tmesg_bottom = 4;
	scrollok(mesg_window, TRUE);
	scrollok(tmesg_window, TRUE);
	window_ptr[ROUND_WINDOW] = &round_window;
	window_ptr[LEAD_WINDOW]  = &lead_window;
	window_ptr[INP_WINDOW]   = &inp_window;
	window_ptr[TEXT_WINDOW]  = &text_window;
	window_ptr[PLAY_WINDOW]  = &play_window;
	window_ptr[SCORE_WINDOW] = &score_window;
	window_ptr[MESG_WINDOW]  = &mesg_window;
}

/*
 * toggle_mesg - toggle whether showing message window or score/play windows
 */
toggle_mesg()
{
	if (show_play) {
		touchwin(tplay_window);
		touchwin(tmesg_window);
		wrefresh(tplay_window);
		wrefresh(tmesg_window);
	}
	else {
		touchwin(play_window);
		touchwin(header_window);
		touchwin(score_window);
		wrefresh(play_window);
		wrefresh(header_window);
		wrefresh(score_window);
	}
	show_play = !show_play;
}

/*
 * ovl_refresh - refresh window only if visible
 */
ovl_refresh(window_num)
int	window_num;
{
	switch (window_num) {
	case PLAY_WINDOW:
		if (show_play)
			wrefresh(play_window);
		break;
	case SCORE_WINDOW:
		if (show_play)
			wrefresh(score_window);
		break;
	default:
		wrefresh(*window_ptr[window_num]);
	}
}

/*
 * scroll_mesg - scroll mesg_window onto tmesg_window
 */
scroll_mesg()
{
	int	i;
	char	ch;

	wmove(tmesg_window, tmesg_bottom, 56);
	scroll(tmesg_window);
	wmove(tmesg_window, tmesg_bottom, 0);
	for (i = 0; i < 56; i++) {
		wmove(mesg_window, 0, i);
		ch = winch(mesg_window);
		waddch(tmesg_window, ch);
	}
	scroll(mesg_window);
	if (!show_play)
		wrefresh(tmesg_window);
}
	
put_card(window, y, x, rank, suit)
WINDOW	*window;
int	y, x,
rank, suit;
{
	mvwaddstr(window, y,     x, "+-----+");
	mvwaddstr(window, y + 1, x, "|     |");
	mvwaddstr(window, y + 2, x, "|     |");
	mvwaddstr(window, y + 3, x, "|     |");
	mvwaddstr(window, y + 4, x, "+-----+");
	if ((suit == HEARTS) || (suit == DIAMONDS))
		wstandout(window);
	mvwaddch(window, y + 1, x + 1, rnames[rank]);
	waddch(window, *snames[suit]);
	mvwaddch(window, y + 3, x + 4, rnames[rank]);
	waddch(window, *snames[suit]);
	wstandend(window);
}

print_cards(suit)
int	suit;
{
	int y, x;
	ptr p;

	werase(card_window[suit]);
	x = y = 0;
	p = my_hand[suit].head->rlink;
	while (p->rank) {
		if (x == 7) {
			++x;
			y = 0;
		}
		put_card(card_window[suit], y, x++, p->rank, suit);
		y += 2;
		p = p->rlink;
	}
	wrefresh(card_window[suit]);
}

get_char()
{
	char	ch;

	if (read(0, &ch, 1) == -1) {
		perror("read");
		abort();
	}
	return(ch);
}

read_char()
{
	char	ch;

	while (!got_char)
		scan();
	ch = got_char;
	got_char = FALSE;
	return(ch);
}

read_card()
{
	int	rank, suit;
	char	buf[64];

	do {
		do {
			werase(inp_window);
			wrefresh(inp_window);
			buf[0] = 'P';
			buf[1] = read_char();	/* Get rank (not smelly) */
			if (buf[1] >= 'a')
				buf[1] += 'A' - 'a';
			if (!(rank = get_rank(buf[1]))) {
				werase(text_window);
				waddstr(text_window, "Bad rank!");
				wrefresh(text_window);
			}
		} 
		while (!rank);
		werase(text_window);
		wrefresh(text_window);
		wprintw(inp_window, "%c ", buf[1]);
		wrefresh(inp_window);
		buf[2] = read_char();		/* Get suit (not tuxedo) */
		if ((buf[2] >= 'A') && (buf[2] <= 'Z'))
			buf[2] += 'a' - 'A';
		if (!(suit = get_suit(buf[2])) &&
		    (buf[2] != _tty.sg_erase) && (buf[2] != _tty.sg_kill)) {
			werase(text_window);
			waddstr(text_window, "Bad suit!");
			wrefresh(text_window);
		}
	} 
	while (!suit);
	wprintw(inp_window, "of %s", snames[suit]);
	wrefresh(inp_window);
	buf[3] = '\0';
	write_socket(dealer_socket, buf);
}

enter_card(rank, suit)
int	rank, suit;
{
	ptr p, p1;

	p = my_hand[suit].head;
	++my_hand[suit].length;
	while (p->rank > rank)
		p = p->rlink;
	p1 = (ptr) malloc(sizeof(struct node));
	p1->llink = p->llink;
	p1->llink->rlink = p1;
	p->llink = p1;
	p1->rlink = p;
	p1->rank = rank;
}

remove_node(p)
ptr	p;

{
	p->llink->rlink = p->rlink;
	p->rlink->llink = p->llink;
	free((char *) p);
}


ptr
find_card(rank_to_play, suit_to_play)
int	rank_to_play, suit_to_play;
{
	ptr	card_to_play;

	card_to_play = my_hand[suit_to_play].head->rlink;
	while (card_to_play->rank != rank_to_play)
		card_to_play = card_to_play->rlink;
	remove_node(card_to_play);
	--my_hand[suit_to_play].length;
	return(card_to_play);
}

do_command(buf)
char	*buf;
{
	char	rch, sch;
	int	player, suit, pts, total_pts, window_num;
	int	temp_y, temp_x;

	switch (buf[0]) {
	case 'A' :		/* Add card to hand */
		enter_card(get_rank(buf[1]), suit = get_suit(buf[2]));
		print_cards(suit);
		break;

	case 'R' :		/* Remove card from hand */
		suit = get_suit(buf[2]);
		remove_node(find_card(get_rank(buf[1]), suit));
		print_cards(suit);
		break;

	case 'E' :		/* Erase window */
		window_num = buf[1] - '0';
		if (window_num == PLAY_WINDOW) {
			werase(tplay_window);
			if (!show_play)
				wrefresh(tplay_window);
		}
		werase(*window_ptr[window_num]);
		ovl_refresh(window_num);
		break;

	case 'G' :		/* Get card */
		read_card();
		break;

	case 'P' :		/* Play card from player */
		(void) sscanf(buf+1, "%1d %c %c", &player, &rch, &sch);
		/*
		 * Update standard play window
		 */
		put_card(play_window, 0, (player - 1) * 9,
		get_rank(rch), get_suit(sch));
		if (buf[4] != '\0')
			mvwaddstr(play_window, 5, (player - 1) * 9, buf + 4);
		/*
		 * Update alternate play window
		 */
		if ((sch == 'h') || (sch == 'd'))
			wstandout(tplay_window);
		mvwaddch(tplay_window, 0, (player - 1) * 9, rch);
		waddch(tplay_window, sch);
		wstandend(tplay_window);
		if (show_play)
			wrefresh(play_window);
		else
			wrefresh(tplay_window);
		sleep(1);
		break;

	case 'S' :		/* Score points for player */
		(void) sscanf(buf+1, "%1d %2d %2d", &player, &pts, &total_pts);
		mvwaddstr(score_window, player - 1, 1, "        ");
		mvwaddstr(score_window, player - 1, 1, buf + 6);
		mvwprintw(score_window, player - 1, 9, " %2d     %2d",
				pts, total_pts);
		ovl_refresh(SCORE_WINDOW);
		break;

	case 'M' :		/* Print message in window */
		getyx(curscr, temp_y, temp_x);
		window_num = buf[1] - '0';
		if (window_num == MESG_WINDOW) {
			scroll_mesg();
			mvwaddstr(mesg_window, mesg_bottom, 0, buf + 2);
		}
		else {
			werase(*window_ptr[window_num]);
			mvwaddstr(*window_ptr[window_num], 0, 0, buf + 2);
		}
		ovl_refresh(window_num);
		move(temp_y, temp_x);
		refresh();
		break;

	case 'X' :		/* Game over */
		if (!show_play)
			toggle_mesg();
		werase(round_window);
		wrefresh(round_window);
		werase(lead_window);
		wstandout(lead_window);
		mvwaddstr(lead_window, 0, 0, "--More--");
		wstandend(lead_window);
		wrefresh(lead_window);
		(void) get_char();
		first_game = FALSE;
		game_over = TRUE;
	}
}

/*
 * Scan input for redraw screen requests or ':' messages.
 * Also scan socket for incoming commands.
 */
scan()
{
	int		i, temp_y, temp_x;
	char		buf[64];
	fd_type	read_fd;
	int		nready;
	extern int	errno;

	fd_init(dealer_socket, &read_fd);
	if (!got_char)
		fd_set(0, &read_fd);
	do {
		nready = select(WIDTH, &read_fd, (fd_type *) 0, (fd_type *) 0,
		(struct timeval *) 0);
	}
	while (nready == -1 && errno == EINTR);
	if (nready == 0)
		return;
	if (fd_isset(0, read_fd)) {
		got_char = get_char();
		getyx(curscr, temp_y, temp_x);
		switch (got_char) {
		case '\f' :
			wrefresh(curscr);	/* Redraw screen */
			got_char = FALSE;
			break;

		case ' '  :
		case '\t' :
		case '\n' :
		case '\r' :
			got_char = FALSE;	/* Ignore white space */
			break;

		case 'M' :
		case 'm' :
			toggle_mesg();
			got_char = FALSE;
			break;

		case ':' :
			scroll_mesg();		/* Message to all players */
			mvwaddstr(mesg_window, mesg_bottom, 0, "message:");
			wrefresh(mesg_window);
			buf[0] = 'M';
			for (i = 1; (i < 48) && ((buf[i] = get_char()) != '\n');
					i++) {
				if (buf[i] == _tty.sg_erase) {
					i -= 2;
					if (i < 0)
						i = 0;
					else {
						wmove(mesg_window, mesg_bottom, i+8);
						wdelch(mesg_window);
					}
				} 
				else {
					if (buf[i] == _tty.sg_kill) {
						wmove(mesg_window, mesg_bottom, 8);
						wclrtoeol(mesg_window);
						i = 0;
					} 
					else
						mvwaddch(mesg_window, mesg_bottom, i + 7, buf[i]);
				}
				wrefresh(mesg_window);
			}
			buf[i] = '\0';
			write_socket(dealer_socket, buf);
			got_char = FALSE;
		}
		move(temp_y, temp_x);
		refresh();
	}
	if (fd_isset(dealer_socket, read_fd)) {
		if (!read_socket(dealer_socket, buf))
			death("Dealer died!!");
		do_command(buf);
	}
}

close_windows()
{
	(void) signal(SIGINT, SIG_IGN);
	clear();
	refresh();
	(void) fflush(stdout);
	endwin();
}

death(buf)
char	*buf;
{
	close_windows();
	printf("%s\n", buf);
	exit(1);
}

wimp_out()
{
	close_windows();
	exit(0);
}


main(argc, argv)
int	argc;
char	**argv;
{
	if (argc > 2) {
		fprintf(stderr, "usage: %s [hostname]\n", *argv);
		exit(1);
	}
	if (argc == 2)
		(void) strcpy (host, *++argv);
	else
		(void) gethostname(host, 20);	/* host is this machine */

	init();

	for (;;) {
		game_over = FALSE;
		get_going();
		do {
			scan();
		}
		while (!game_over);
	}
}