[comp.sources.games] REPOST: cubes - a networked dice game, Part01/03

gmp@rayssd.RAY.COM (06/17/88)

Submitted by: gmp@rayssd.RAY.COM (Gregory M. Paris)
Comp.sources.games: Volume 4, Issue 32
Archive-name: cubes/Part01

	[Warning to System V types: this game uses BSD sockets for
	 its networking implementation.  -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 1 (of 3)."
# Contents:  README MANIFEST cubeserver.c screen.c
# Wrapped by billr@saab on Tue Jun  7 16:36:05 1988
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\" \(5591 characters\)
sed "s/^X//" >README <<'END_OF_README'
XThe game of cubes is a computer implementation of a dice game played with five
Xsix-sided dice.  The goal of the game is to be the first player to score
X10,000 points.  Players take turns rolling the dice in the hopes of getting
Xone or more of several scoring combinations.  A player can continue rolling as
Xlong as the previous roll produced a scoring combination.  Failing that, any
Xpoints accumulated during the turn are lost and the player's turn ends.
XBefore that happens, the player may (prudently or cowardly) elect to quit
Xrolling, at which time the points accumulated during the turn are added to the
Xplayer's score.
X
XThese programs should compile and run without change on any 4.3BSD system.
XExcept for missing the FD_ macros (faked in this distribution), it should work
Xon 4.2BSD systems too.  The program is based on a socket-based client/server
Xmodel, so I'm afraid that you're going to have some problems if you want to
Xrun it on something that doesn't have sockets.  Of course, if you do port it
Xto something like that, I'm interested in receiving the diffs...
X
XYou'll need to be root in order to install these programs.  The main reason
Xbeing that you must add the cube service to your /etc/services file.  The
Xline I've added on our systems is
X
Xcube		3840/tcp	cubes		# cube server
X
Xwhere the 3840 is the port number that I chose almost arbitrarily.  You can
Xpick anything you like, but since cubes doesn't need to use one of the
Xprivileged ports, you'll probably want to pick something larger than 1024.  If
Xyou're not root and you want to run this program, you'll have to edit
Xcubeserver.c and cubes.c to use a hard-coded port number where it now uses
Xs_port.  (Don't forget to convert the number to network byte order...)
X
XIf you want the server to start up when the system comes up, you'll have to
Xput something in your /etc/rc or /etc/rc.local file.  It is possible to modify
Xcubes.c to start up a local server when necessary, but there was no reason
Xhere to do that, so I didn't.
X
XAlso, root permissions will be helpful when installing the programs, unless
Xyou edit the Makefile to put the programs somewhere other than /usr/games.
XThe cubeserver is setuid to gdaemon, but that's only so it doesn't run as root
Xand so the scorefile can be 644 mode.
X
XNote that both the server and the client do funny things with their
Xarguments.  The server tries to display its state by modifying argv[0] and the
Xclients try to disguise themselves using a similar trick.  This stuff might
Xnot work on your system, but hopefully it won't keep the programs from
Xrunning.  Some may wish to take the argument-mangling stuff out even if it
Xdoes work.  If so, the execv stuff in cubeserver.c can be stripped out, since
Xit's only there to provide a larger argv[0] for the status display.
X
XThe player interface, cubes, is pretty much locked-into a 24 x 80 screen, but
Xworks OK with something larger.  If you're on a graphics workstation, then
Xyou'll find the whole thing pretty sorry.  Well, if I had one on my desk, I'd
Xdo something about that, but I don't, so I won't.  If any industrious person
Xwould like to write a player interface that takes advantage of a bit-mapped
Xgraphics display, you're more than welcome to do so.  Please send me whatever
Xyou come up with so I can see to it that it gets added to the distribution.
XNote that the game is playable via telnet (well, playable is an
Xoverstatement), so you can use it to get an idea of what the server's doing if
Xyou want to write the client from scratch.  (That's how I did it for cubes.)
X
XSpeaking of graphics, I've made it so the cubes program can take advantage of
Xan alternate graphics character set if your terminal has one.  At my disposal
XI had some vt100 clones and various Zenith terminals, so I made it handle
Xthem.  The dice do look somewhat better -- maybe too square -- though the
XDEC-graphics dice look like the ace through six of diamonds.  Additional
Xgraphics sets will be added if anybody sends me the necessary diffs.  Note
Xthat to have graphics work, the terminal's termcap entry must use the "as" and
X"ae" capabilities for entering and leaving graphics mode.
X
XThe biggest sore point I see with the game as it now stands is that it takes a
Xbit of coordination to get everybody to start the game at the same time.
XVerbal communication works, but when you're dialed-in from home, that method
Xisn't available.  If anybody comes up with a not-too-disruptive scheme to
Xnotify people to begin a game, I'm interested.
X
XAnother likely change would be to make the player ranking system use some kind
Xof moving average.  As it is, once you get a large number of games in your
Xrecord, you aren't able to effect much change in your ranking.
X
XAs far as the computer players go, it might be nice if you don't add or delete
Xnames from the list, or add or delete any of the temperments or strategies.
XNot that I want to stifle your creativity, but it might be interesting to see
Xa composite ranking of all computer and human players net-wide.  I must
Xconfess that some of the computers are doing quite well here, tho' maybe we're
Xjust a bad sample of humans.  Hard to say without more data.
X
XFeel free to redistribute this program, but only for free.  You can include it
Xin a commercial distribution, but only if you don't charge extra for it.  If
Xyou make changes to it, I'd like to hear about them so I can decide whether or
Xnot to include them in my version.  Otherwise, as they say, enjoy...
X
XGreg Paris, June 2, 1988
Xgmp@rayssd.ray.com or gmp%rayssd.ray.com@a.cs.uiuc.edu
X{att,decuac,gatech,necntc,sun,uiucdcs,ukma}!rayssd!gmp
END_OF_README
if test 5591 -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\" \(684 characters\)
sed "s/^X//" >MANIFEST <<'END_OF_MANIFEST'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X MANIFEST                  1	This shipping list
X Makefile                  3	
X README                    1	
X actions.c                 2	
X avg.c                     2	
X cuberank.tmplt            3	
X cubes.6                   2	
X cubes.c                   3	
X cubes.h                   3	
X cubeserver.c              1	
X dieopts.c                 2	
X history.c                 3	
X random.c                  3	
X risk.c                    3	
X screen.c                  1	
X strategies.c              3	
X tactics.c                 3	
X tempers.c                 3	
X turn.c                    2	
END_OF_MANIFEST
if test 684 -ne `wc -c <MANIFEST`; then
    echo shar: \"MANIFEST\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f cubeserver.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"cubeserver.c\"
else
echo shar: Extracting \"cubeserver.c\" \(28971 characters\)
sed "s/^X//" >cubeserver.c <<'END_OF_cubeserver.c'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)cubeserver.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif	lint
X
X#include	<stdio.h>
X#include	<ctype.h>
X#include	<strings.h>
X#include	<errno.h>
X#include	<signal.h>
X#include	<setjmp.h>
X#include	<sys/types.h>
X#include	<sys/time.h>
X#include	<sys/file.h>
X#include	<sys/ioctl.h>
X#include	<sys/socket.h>
X#include	<netinet/in.h>
X#include	<netdb.h>
X#include	"cubes.h"
X
X#ifndef	FD_ZERO
X/*
XXXX:	This system is missing the FD_ macros for select, so it's
XXXX:	probably a 4.2BSDish system.  That means that the fd_set
XXXX:	type is probably just a single int, so just fake it here.
X*/
X#define	FD_ZERO(p)		((p)->fds_bits[0] = 0)
X#define	FD_SET(n,p)		((p)->fds_bits[0] |= 1L << (n))
X#endif	FD_ZERO
X
X#define	NOSEL		((fd_set *)0)
X#define	HANG		((struct timeval *)0)
X
Xextern int		errno;
Xextern int		optind;
Xextern int		opterr;
Xextern char	   *optarg;
Xextern char	   *fgets();
Xextern char	   *gets();
Xextern boolean	wantin();
Xextern boolean	nameinuse();
Xextern boolean	idinuse();
Xextern int		plrcmp();
Xstatic int		srvshutdown();
X
Xint				ssock	= -1;			/* server socket */
Xint				active	= 0;			/* number of Active humans */
Xint				waiting	= 0;			/* number of Waiting humans */
Xint				turnnum	= 0;			/* current turn number */
Xunsigned		neq		= 0;			/* number of equivalent hosts */
Xchar		  **equiv;					/* equivalent hosts */
Xboolean			inprogress	= False;	/* True when game in progress */
Xboolean			firstgame	= True;		/* first game with set of players */
Xplayer			plr[PLAYERS];			/* player/connection list */
Xstruct timeval	poll	= { 0L, 0L };	/* for select polling */
X
X#define	STATLEN	55						/* length of "status line" */
Xchar		   *statusline;				/* really av[0] */
X
Xmain(ac, av)
Xchar   *av[];
X{
X	int			c;
X
X	/*
X	**	Make sure we have enough room to display our status.
X	*/
X	if(strlen(av[0]) < STATLEN) {
X		char   *newav0;
X		char   *malloc();
X
X		if((newav0 = malloc(STATLEN+1)) == 0) {
X			fprintf(stderr, "cubeserver: no memory for new argv zero\n");
X			exit(1);
X		}
X		sprintf(newav0, "cubes %-*s.", STATLEN-7, "startup");
X		av[0] = newav0;
X		execv(PATHNAME, av);
X		perror(PATHNAME);
X		exit(1);
X	}
X
X	(void) setuid(geteuid());
X	statusline = av[0];
X	updstat(-1);
X
X	opterr = 0;
X	while((c = getopt(ac, av, "")) >= 0) {
X		switch(c) {
X		default:
X			fprintf(stderr, "usage -- cubeserver\n");
X			exit(1);
X		}
X	}
X
X	/*
X	**	Close stdin and open server socket.
X	*/
X	(void) fclose(stdin);
X	if(openservsock() < 0)
X		exit(1);
X	
X	/*
X	**	Read history file.
X	*/
X	if(histread() < 0)
X		exit(1);
X
X	/*
X	**	Ignore keybord signals.  Catch TERM for controlled shutdown.
X	*/
X	(void) signal(SIGHUP, SIG_IGN);
X	(void) signal(SIGINT, SIG_IGN);
X	(void) signal(SIGQUIT, SIG_IGN);
X	(void) signal(SIGTSTP, SIG_IGN);
X	(void) signal(SIGTTIN, SIG_IGN);
X	(void) signal(SIGTTOU, SIG_IGN);
X	(void) signal(SIGTERM, srvshutdown);
X
X	/*
X	**	Fork.
X	*/
X	switch(c = fork()) {
X	case -1:	/* error */
X		perror("cubeserver: fork");
X		exit(1);
X	case 0:		/* child */
X		break;
X	default:	/* parent */
X		printf("%d\n", c);
X		exit(0);
X	}
X
X	/*
X	**	Disconnect from controlling tty.
X	*/
X	if((c = open("/dev/tty", O_RDWR)) >= 0) {
X		(void) ioctl(c, TIOCNOTTY, (char *)0);
X		(void) close(c);
X	}
X
X	/*
X	**	Reopen stdout and stderr as logfile.
X	*/
X	(void) close(2);
X	(void) open(LOGFILE, O_WRONLY|O_APPEND|O_CREAT, 0666);
X	(void) dup2(2, 1);
X	
X	/*
X	**	Initialize random number generator.
X	*/
X	irandom();
X
X	/*
X	**	Initialize player/connection list.
X	*/
X	active = 0;
X	waiting = 0;
X	for(c = 0;c < PLAYERS;++c) {
X		plr[c].p_stat = Inactive;
X		plr[c].p_fd = -1;
X		plr[c].p_score = 0;
X		plr[c].p_onboard = False;
X		plr[c].p_strategy = 0;
X		plr[c].p_temper = 0;
X		plr[c].p_name[0] = '\0';
X		plr[c].p_id[0] = '\0';
X	}
X	addcomp();	/* add COMP computer */
X
X	for(firstgame = True;;) {
X		inprogress = False;
X		newplayers(True);
X		begingame();
X		while(active > 0 && winner() < 0)
X			gameturn();
X		endgame();
X	}
X}
X
X/*
X**	openservsock: open, bind, and begin listening on server socket
X*/
Xopenservsock()
X{
X	struct servent	   *ps;
X	struct sockaddr_in	server;
X
X	if((ssock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
X		perror("cubeserver: socket");
X		return (ssock = -1);
X	}
X
X	if((ps = getservbyname("cube", "tcp")) == 0) {
X		fprintf(stderr, "cubeserver: no cube/tcp service listed\n");
X		(void) close(ssock);
X		return (ssock = -1);
X	}
X
X	server.sin_family = AF_INET;
X	server.sin_port = ps->s_port;
X	server.sin_addr.s_addr = INADDR_ANY;
X	if(bind(ssock, &server, sizeof server) < 0) {
X		perror("cubeserver: bind");
X		(void) close(ssock);
X		return (ssock = -1);
X	}
X
X	if(listen(ssock, 5) < 0) {
X		perror("cubeserver: listen");
X		(void) close(ssock);
X		return (ssock = -1);
X	}
X
X	return 0;
X}
X
X/*
X**	wantin: return true if more players want in
X*/
Xboolean
Xwantin(hang)
Xboolean		hang;
X{
X	fd_set	rdy;
X
X	FD_ZERO(&rdy);
X	FD_SET(ssock, &rdy);
X	if(select(ssock+1, &rdy, NOSEL, NOSEL, hang==True ? HANG : &poll) > 0)
X		return True;
X	return False;
X}
X
X/*
X**	newplayers: check for and add new players
X*/
Xnewplayers(hang)
Xboolean					hang;
X{
X	register int		c;
X	int					csock;
X	int					tries;
X	int					len;
X	struct sockaddr_in	client;
X	char				msgbuf[BUFSIZ];
X	int					off	= 0;
X
X	/*
X	**	Is this a new batch of players?
X	*/
X	if(hang == True && active == 0)
X		firstgame = True;
X
X	/*
X	**	If there are no active humans, just wait here in select.
X	*/
X	if(hang == True && active == 0 && waiting == 0)
X		while(wantin(True) == False)
X			;
X
X	/*
X	**	Keep looping until we run out of connection requests.
X	*/
X	for(;;) {
X		/*
X		**	If we are supposed to hang, then wait a while for
X		**	another connection request, elsewise, just return.
X		*/
X#define	MAXTRIES	5
X		for(tries = 0;wantin(False) == False;++tries) {
X			if(hang == False || tries > MAXTRIES)
X				return;
X			sleep(1);
X		}
X
X		/*
X		**	Accept a connection from a potential player.
X		*/
X		len = sizeof client;
X		if((csock = accept(ssock, &client, &len)) < 0) {
X			perror("cubeserver: accept");
X			break;
X		}
X		(void) ioctl(csock, FIONBIO, (char *)&off);
X
X		/*
X		**	Find an open slot.
X		*/
X		for(c = 0;c < PLAYERS;++c)
X			if(plr[c].p_stat == Inactive)
X				break;
X		if(c == PLAYERS) {
X			sprintf(msgbuf, "%d Sorry, full house.\r\n", M_ARGE);
X			(void) write(csock, msgbuf, strlen(msgbuf));
X			sprintf(msgbuf,
X				"%d Server closing connection.\r\n", M_DOWN);
X			(void) write(csock, msgbuf, strlen(msgbuf));
X			(void) close(csock);
X			continue;
X		}
X
X		/*
X		**	Fill in player parameters.
X		*/
X		plr[c].p_fd = csock;
X		plr[c].p_name[0] = plr[c].p_id[0] = '\0';
X		plr[c].p_stat = Waiting, ++waiting;
X		plr[c].p_score = 0, plr[c].p_onboard = False;
X		if(getplayername(c) < 0 || getplayerid(c) < 0)
X			continue;
X		sprintf(msgbuf, "%d %s, welcome to Cubes.\r\n",
X			M_INFO, plr[c].p_name);
X		if(message(c, msgbuf) < 0)
X			continue;
X		if(inprogress == True)
X			roster(c);	/* roster for this player only */
X	}
X}
X
X/*
X**	oldplayer: remove a player from the game
X*/
Xoldplayer(c)
Xregister int	c;
X{
X	/*
X	**	Close the socket if one is open.
X	*/
X	if(plr[c].p_fd != -1) {
X		(void) close(plr[c].p_fd);
X		plr[c].p_fd = -1;
X	}
X
X	/*
X	**	Change the player's status.
X	*/
X	switch(plr[c].p_stat) {
X	case Computer:
X		if(inprogress == True && plr[c].p_onboard == True)
X			histpoints(c);
X		plr[c].p_stat = Inactive;
X		plr[c].p_score = 0;
X		plr[c].p_onboard = False;
X		plr[c].p_strategy = 0;
X		plr[c].p_temper = 0;
X		plr[c].p_name[0] = '\0';
X		plr[c].p_id[0] = '\0';
X		annfarewell(c);
X		break;
X	case Active:
X		/*
X		**	If game in progress and this player has some points,
X		**	then there's a chance that a computer proxy will be
X		**	assigned to take over.  The proxy keeps the original
X		**	name so that other players are not made aware.  The
X		**	id is also left unchanged so that a single player can't
X		**	flood the game with proxies.
X		*/
X		if(inprogress == True && plr[c].p_onboard == True && dieroll(5) > 2) {
X			plr[c].p_stat = Computer;
X			pickstrategy(c);	/* also chooses temperment */
X		} else {
X			if(inprogress == True && plr[c].p_onboard == True)
X				histpoints(c);
X			plr[c].p_stat = Inactive;
X			plr[c].p_score = 0;
X			plr[c].p_onboard = False;
X			plr[c].p_name[0] = '\0';
X			plr[c].p_id[0] = '\0';
X			annfarewell(c);
X		}
X		--active;
X		break;
X	case Waiting:
X		plr[c].p_stat = Inactive;
X		--waiting;
X		break;
X	}
X	if(active <= 0) {
X		/*
X		**	XXX: update computer points history here?
X		*/
X		inprogress = False;
X		active = 0;
X	}
X}
X
X/*
X**	messagejmp, messagetimo: message timeout handler
X*/
Xstatic jmp_buf	messagejmp;
Xstatic int
Xmessagetimo()
X{
X	longjmp(messagejmp, 1);
X}
X
X/*
X**	message: write a message on a channel
X*/
Xmessage(c, mesg)
Xint		c;
Xchar   *mesg;
X{
X	int			n, mesglen;
X	int		  (*oldalrm)();
X	unsigned	alarmv;
X	fd_set		rdy;
X
X	switch(plr[c].p_stat) {
X	case Inactive:
X#ifdef	notdef
X		fprintf(stderr, "message: to Inactive: %s", mesg);
X#endif	notdef
X		return -1;
X	case Computer:
X		fprintf(stderr, "message: to Computer: %s", mesg);
X		return -1;
X	}
X
X	if((mesglen = strlen(mesg)) == 0)
X		return 0;
X
X	oldalrm = signal(SIGALRM, SIG_IGN);
X	alarmv = alarm(0);
X	if(setjmp(messagejmp) != 0) {
X		fprintf(stderr, "message: timeout\n");
X		oldplayer(c);
X		(void) signal(SIGALRM, oldalrm);
X		(void) alarm(alarmv);
X		return -1;
X	}
X
X	(void) signal(SIGALRM, messagetimo);
X	(void) alarm(WRITETIMO);
X	FD_ZERO(&rdy);
X	FD_SET(plr[c].p_fd, &rdy);
X	(void) select(plr[c].p_fd+1, NOSEL, &rdy, NOSEL, &poll);
X	if((n = write(plr[c].p_fd, mesg, mesglen)) < 0) {
X		if(errno != EPIPE)
X			perror("message: write");
X		(void) alarm(0);
X		oldplayer(c);
X		(void) signal(SIGALRM, oldalrm);
X		(void) alarm(alarmv);
X		return -1;
X	}
X	(void) alarm(0);
X	(void) signal(SIGALRM, oldalrm);
X	(void) alarm(alarmv);
X	return n;
X}
X
X/*
X**	replyjmp, replytimo: reply timeout handler
X*/
Xstatic jmp_buf	replyjmp;
Xstatic int
Xreplytimo()
X{
X	longjmp(replyjmp, 1);
X}
X
X/*
X**	reply: read a reply from a channel
X*/
Xreply(c, mesg, mesglen)
Xint		c, mesglen;
Xchar   *mesg;
X{
X	int			n, ntot;
X	int		  (*oldalrm)();
X	unsigned	alarmv;
X	fd_set		rdy;
X
X	switch(plr[c].p_stat) {
X	case Inactive:
X		fprintf(stderr, "reply: from Inactive\n");
X		return -1;
X	case Computer:
X		fprintf(stderr, "reply: from Computer\n");
X		return -1;
X	}
X
X	/*
X	**	Handle read timeout.
X	*/
X	oldalrm = signal(SIGALRM, SIG_IGN);
X	alarmv = alarm(0);
X	if(setjmp(replyjmp) != 0) {
X		char	timomsg[128];
X		fprintf(stderr, "reply: timeout\n");
X		sprintf(timomsg,
X			"%d Timed out waiting for your response!\r\n", M_INFO);
X		(void) message(c, timomsg);
X		(void) send(plr[c].p_fd, "\001", 1, MSG_OOB);
X/*		oldplayer(c);	*/
X		(void) signal(SIGALRM, oldalrm);
X		(void) alarm(alarmv);
X		return -2;
X	}
X
X	/*
X	**	Read until we get a newline.
X	*/
X	(void) signal(SIGALRM, replytimo);
X	(void) alarm(READTIMO);
X	ntot = 0, mesg[0] = '\0';
X	while(index(mesg, '\n') == 0) {
X		FD_ZERO(&rdy);
X		FD_SET(plr[c].p_fd, &rdy);
X		(void) select(plr[c].p_fd+1, &rdy, NOSEL, NOSEL, HANG);
X		if((n = read(plr[c].p_fd, mesg+ntot, mesglen-ntot)) <= 0) {
X			(void) alarm(0);
X#ifdef	notdef
X			if(n < 0)
X				perror("reply: read");
X			else
X				fprintf(stderr, "reply: read got zero bytes\n");
X#endif	notdef
X			oldplayer(c);
X			(void) signal(SIGALRM, oldalrm);
X			(void) alarm(alarmv);
X			return -1;
X		}
X		ntot += n, mesg[ntot] = '\0';
X	}
X	(void) alarm(0);
X
X	/*
X	**	Strip trailing control characters and spaces.
X	**	Then get rid of control characters in the message.
X	*/
X	for(n = ntot - 1;n >= 0;--n) {
X		if(iscntrl(mesg[n]) || isspace(mesg[n]))
X			mesg[n] = '\0';
X		else
X			break;
X	}
X	for(n = 0;mesg[n] != '\0';++n)
X		if(iscntrl(mesg[n]))
X			mesg[n] = '?';
X
X	(void) signal(SIGALRM, oldalrm);
X	(void) alarm(alarmv);
X	return n;
X}
X
X/*
X**	compname: names for computer players
X*/
Xchar	   *compname[]	= {
X/* 0*/	"CUBEX",	/* the COMP player */
X/* 1*/	"UNIBLAB",
X/* 2*/	"Rosie",
X/* 3*/	"Brainiac",
X/* 4*/	"Wotan",
X/* 5*/	"K-2",
X/* 6*/	"K-9",
X/* 7*/	"M5",
X/* 8*/	"NOMAD",
X/* 9*/	"Landrew",
X/*10*/	"Norman",
X/*11*/	"Stella 500",
X/*12*/	"Alice 3",
X/*13*/	"The Old Man" /* in the Cave */,
X/*14*/	"Agnes",
X/*15*/	"Dr. Theopolis",
X/*16*/	"Tweekie",
X/*17*/	"Gort",
X/*18*/	"Robbie",
X/*19*/	"HAL9000",
X/*20*/	"SAL9000",
X/*21*/	"Mother",
X/*22*/	"Ashe",
X/*23*/	"Bishop",
X/*24*/	"Woper",
X/*25*/	"The Terminator",
X/*26*/	"Number Five",
X/*27*/	"Jenkins",
X/*28*/	"R. Daneel Olivaw",
X/*29*/	"GAX",
X/*30*/	"TEX",
X/*31*/	"MEX",
X/*32*/	"BEX",
X/*33*/	"DEX",
X/*34*/	"LIBEX",
X/*35*/	"Mr. Frostee",
X/*36*/	"Ralph Numbers",
X/*37*/	"Wagstaff",
X/*38*/	"Berenice",
X/*39*/	"Emul",
X/*40*/	"Ulalume",
X/*41*/	"Oozer",
X/*42*/	"Kkandio",
X};
Xint		compnames	= (sizeof compname / sizeof compname[0]);
X
X/*
X**	begingame: setup prior to a game
X*/
Xbegingame()
X{
X	register int	c, cc;
X	int				ncomp;
X
X	inprogress = False;
X	turnnum = 0;
X	updstat(-1);
X
X	/*
X	**	Add waiting players to the game.
X	*/
X	for(c = 0;c < PLAYERS;++c) {
X		if(plr[c].p_stat == Waiting) {
X			plr[c].p_stat = Active;
X			plr[c].p_score = 0;
X			plr[c].p_onboard = False;
X			++active, --waiting;
X		}
X	}
X
X	/*
X	**	Count computer players.
X	*/
X	for(ncomp = c = 0;c < PLAYERS;++c)
X		if(plr[c].p_stat == Computer)
X			++ncomp;
X
X	/*
X	**	Add enough computer players to make at least two.
X	**	Add enough computer players to make four players total.
X	**	Add computer players randomly after that.
X	*/
X	while(ncomp < 2 || (active + ncomp < 4) || dieroll(7) > 5) {
X		for(c = 0;c < PLAYERS;++c) {
X			if(plr[c].p_stat == Inactive) {
X				cc = dieroll(compnames-1);	/* zero reserved */
X				if(nameinuse(c, compname[cc]) == False) {
X					plr[c].p_stat = Computer;
X					plr[c].p_fd = -1;
X					plr[c].p_score = 0;
X					plr[c].p_onboard = False;
X					strcpy(plr[c].p_name, compname[cc]);
X					plr[c].p_id[0] = '\0';	/* non-proxies are null */
X					pickstrategy(c);		/* also chooses temperment */
X					++ncomp;
X				}
X				break;
X			}
X		}
X	}
X
X	updstat(-1);
X
X	/*
X	**	If this is the first game with this set of players,
X	**	reorder them by wins/points ranking.  Else, reorder by
X	**	points scored in last game.
X	*/
X	if(firstgame == True)
X		historder();
X	else
X		qsort((char *)plr, PLAYERS, sizeof plr[0], plrcmp);
X
X	/*
X	**	Zero scores and distribute new roster.
X	*/
X	for(c = 0;c < PLAYERS;++c) {
X		plr[c].p_score = 0;
X		plr[c].p_onboard = False;
X	}
X	roster(-1);
X
X	inprogress = True;
X	updstat(-1);
X}
X
X/*
X**	announce: send a message to all active and waiting players but one
X*/
Xannounce(but, mesg)
Xint		but;
Xchar   *mesg;
X{
X	register int	c;
X
X	for(c = 0;c < PLAYERS;++c) if(c != but) {
X		switch(plr[c].p_stat) {
X		case Active:
X		case Waiting:
X			(void) message(c, mesg);
X			break;
X		}
X	}
X}
X
X/*
X**	annscore: announce player score
X**		announces differently for affected player
X*/
Xannscore(sc)
Xint		sc;
X{
X	char			msgbuf[MESGLEN+64];
X
X	sprintf(msgbuf, "%d Player %d now has %d points.\r\n",
X		M_OSCO, sc, plr[sc].p_score);
X	announce(sc, msgbuf);
X	if(plr[sc].p_stat != Computer) {
X		sprintf(msgbuf, "%d You now have %d points.\r\n",
X			M_MSCO, plr[sc].p_score);
X		(void) message(sc, msgbuf);
X	}
X}
X
X#ifdef	notdef
X/*
X**	annplayer: announce new player
X**		doesn't announce player to itself
X*/
Xannplayer(newc)
Xint		newc;
X{
X	char			msgbuf[MESGLEN+64];
X
X	sprintf(msgbuf, "%d player %d %s\r\n", M_PNUM, newc, plr[newc].p_name);
X	announce(newc, msgbuf);
X}
X#endif	notdef
X
X/*
X**	annfarewell: bid farewell to player
X**		doesn't tell player about own leave
X*/
Xannfarewell(oldc)
Xint		oldc;
X{
X	char			msgbuf[MESGLEN+64];
X
X	sprintf(msgbuf, "%d farewell %d %s\r\n",
X		M_FARE, oldc, plr[oldc].p_name);
X	announce(oldc, msgbuf);
X}
X
X/*
X**	gameturn: do a single turn for each active player
X*/
Xgameturn()
X{
X	register int	c, pts;
X	int				about, wasabout;
X	char			msgbuf[MESGLEN];
X
X	++turnnum;
X
X	updstat(-1);
X
X	wasabout = -1;
X	for(c = 0;active > 0 && c < PLAYERS;++c) {
X		switch(plr[c].p_stat) {
X		case Computer:
X		case Active:
X			updstat(c);
X			if((pts = turn(c)) > 0) {
X				plr[c].p_score += pts;
X				plr[c].p_onboard = True;
X			}
X			annscore(c);
X			updstat(-1);
X
X			/*
X			**	Tell about players about to win and players no longer
X			**	about to win.
X			*/
X			if((about = winner()) == c) {
X				sprintf(msgbuf, "%d %s is about to win...\r\n",
X					M_INFO, plr[c].p_name);
X				announce(c, msgbuf);
X				if(plr[c].p_stat == Active) {
X					sprintf(msgbuf, "%d You are about to win...\r\n", M_INFO);
X					(void) message(c, msgbuf);
X				}
X				sleep(2);	/* a chance for everybody to see... */
X			} else if(wasabout != about && wasabout >= 0) {
X				sprintf(msgbuf, "%d %s is no longer about to win.\r\n",
X					M_INFO, plr[wasabout].p_name);
X				announce(wasabout, msgbuf);
X				if(plr[wasabout].p_stat == Active) {
X					sprintf(msgbuf, "%d You're no longer about to win.\r\n",
X						M_INFO);
X					(void) message(wasabout, msgbuf);
X				}
X				sleep(2);	/* a chance for everybody to see... */
X			}
X			wasabout = about;
X
X			/*
X			**	Accept new players to waiting status.
X			*/
X			if(inprogress == True && wantin(False) == True) {
X				newplayers(False);
X				updstat(-1);
X			}
X			sleep(2);
X			break;
X		}
X	}
X	updstat(-1);
X
X	/*
X	**	Add any waiting players until the game is 9/10ths over.
X	*/
X	if(waiting > 0 && active > 0 && inprogress == True)
X		if(highscore(-1) < (9 * WINSCORE) / 10)
X			addwaiting();
X}
X
X/*
X**	endgame: cleanup after game finish
X*/
Xendgame()
X{
X	register int	c, highc;
X	char			msgbuf[BUFSIZ];
X
X	inprogress = False;
X	firstgame = False;
X/*	turnnum = 0;	/* don't zero; needed in histpoints */
X	updstat(-1);
X
X	/*
X	**	If there's a winner, update player score histories.
X	*/
X	if((highc = winner()) >= 0) {
X		for(c = 0;c < PLAYERS;++c) {
X			switch(plr[c].p_stat) {
X			case Computer:
X			case Active:
X				(void) histpoints(c);
X				break;
X			}
X		}
X		(void) histwins(highc);
X	}
X
X	/*
X	**	Write history whether or not there was a winner.
X	*/
X	(void) histwrite();
X
X	/*
X	**	Tell everybody who won.
X	*/
X	if(highc < 0)
X		sprintf(msgbuf, "%d The game has concluded with no winner.\r\n",
X			M_NWIN);
X	else {
X		sprintf(msgbuf, "%d Player %d %s has won the game!\r\n",
X			M_OVER, highc, plr[highc].p_name);
X	}
X	for(c = 0;c < PLAYERS;++c) {
X		switch(plr[c].p_stat) {
X		case Active:
X		case Waiting:
X			(void) message(c, msgbuf);
X			break;
X		}
X	}
X	
X	/*
X	**	Ask human players to play again.
X	*/
X	for(c = 0;c < PLAYERS;++c) {
X		if(plr[c].p_stat != Active) 
X			continue;
X		sprintf(msgbuf, "%d Play another game? [y]\r\n", M_ANOG);
X		if(message(c, msgbuf) < 0)
X			continue;
X		if(reply(c, msgbuf, sizeof msgbuf) < 0)
X			continue;
X		switch(msgbuf[0]) {
X		case 'n': case 'N':
X		case 'q': case 'Q':
X			sprintf(msgbuf,
X				"%d See you later %s...\r\n", M_DOWN, plr[c].p_name);
X			oldplayer(c);
X			break;
X		}
X	}
X
X	/*
X	**	If there are no active players left, then get rid of any computers.
X	**	Reinstall the COMP computer.
X	*/
X	if(active == 0) {
X		for(c = 0;c < PLAYERS;++c)
X			if(plr[c].p_stat == Computer)
X				oldplayer(c);
X		addcomp();
X	}
X
X	updstat(-1);
X}
X
X/*
X**	addcomp: add the COMP computer
X*/
Xaddcomp()
X{
X	plr[COMP].p_stat = Computer;
X	strcpy(plr[COMP].p_name, compname[0]);
X	plr[COMP].p_fd = -1;
X	plr[COMP].p_id[0] = '\0';
X	plr[COMP].p_score = 0;
X	plr[COMP].p_onboard = False;
X	pickstrategy(COMP);		/* also chooses temperment */
X}
X
X/*
X**	plrcmp: player comparison function
X*/
Xplrcmp(p1, p2)
Xplayer *p1, *p2;
X{
X	int		diff;
X
X	/*
X	**	Put inactive players last.
X	*/
X	if(p1->p_stat == Inactive)
X		return (p2->p_stat == Inactive) ? 0 : 1;
X	if(p2->p_stat == Inactive)
X		return -1;
X
X	/*
X	**	Sort in order of increasing score.
X	*/
X	if((diff = p1->p_score - p2->p_score) != 0)
X		return diff;
X	
X	/*
X	**	Put Computer players after humans.
X	*/
X	if(p1->p_stat != p2->p_stat)
X		return (p1->p_stat == Computer) ? 1 : -1;
X
X	/*
X	**	Sort alphabetically.
X	*/
X	return strcmp(p1->p_name, p2->p_name);
X}
X
X/*
X**	roster: broadcast the roster to all players
X*/
Xroster(only)
Xint		only;
X{
X	register int	c, cc;
X	char			msgbuf[MESGLEN];
X
X	/*
X	**	Tell each player who he/she is then give a roster.
X	**	Tell Waiting players, but don't show them.
X	*/
X	for(c = 0;c < PLAYERS;++c) {
X		if(only != -1 && c != only)
X			continue;
X		switch(plr[c].p_stat) {
X		case Active:
X		case Waiting:
X			sprintf(msgbuf, "%d New player roster.\r\n", M_CPLR);
X			if(message(c, msgbuf) < 0)
X				continue;
X			if(plr[c].p_stat == Active) {
X				sprintf(msgbuf, "%d You are player %d (%s).\r\n",
X					M_UARE, c, plr[c].p_name);
X				if(message(c, msgbuf) < 0)
X					continue;
X				sprintf(msgbuf, "%d You now have %d points.\r\n",
X					M_MSCO, plr[c].p_score);
X				if(message(c, msgbuf) < 0)
X					continue;
X			}
X			for(cc = 0;cc < PLAYERS;++cc) if(cc != c) {
X				if(plr[cc].p_stat == Active || plr[cc].p_stat == Computer) {
X					sprintf(msgbuf, "%d player %d %s\r\n",
X						M_PNUM, cc, plr[cc].p_name);
X					if(message(c, msgbuf) < 0)
X						break;
X					sprintf(msgbuf, "%d Player %d now has %d points.\r\n",
X						M_OSCO, cc, plr[cc].p_score);
X					if(message(c, msgbuf) < 0)
X						break;
X				}
X			}
X			break;
X		}
X	}
X}
X
X/*
X**	winner: return winner number or -1 if game in progress
X*/
Xwinner()
X{
X	register int	c;
X	register int	high, next;
X	register int	highc;
X
X	/*
X	**	Find high score.  If less than WINSCORE, no winner.
X	*/
X	for(high = c = 0;c < PLAYERS;++c) {
X		switch(plr[c].p_stat) {
X		case Computer:
X		case Active:
X			if(plr[c].p_score > high)
X				high = plr[(highc = c)].p_score;
X			break;
X		}
X	}
X	if(high < WINSCORE)
X		return -1;
X
X	/*
X	**	Find next to highest score.  If margin is less than
X	**	WINMARGIN, there's no winner yet.
X	*/
X	for(next = c = 0;c < PLAYERS;++c) {
X		if(c == highc)
X			continue;
X		switch(plr[c].p_stat) {
X		case Computer:
X		case Active:
X			if(plr[c].p_score > next)
X				next = plr[c].p_score;
X			break;
X		}
X	}
X	if(high - next < WINMARGIN)
X		return -1;
X	
X	/*
X	**	We have a winner!
X	*/
X	return highc;
X}
X
X/*
X**	nameinuse: return True if name in use by other than numbered player
X*/
Xboolean
Xnameinuse(c, name)
Xint		c;
Xchar   *name;
X{
X	register int	cc;
X
X	for(cc = 0;cc < PLAYERS;++cc)
X		if(cc != c && plr[cc].p_stat != Inactive)
X			if(strncmp(plr[cc].p_name, name, NAMELEN-1) == 0)
X				return True;
X
X	return False;
X}
X
X
X/*
X**	getequiv: determine equivalent hosts.
X*/
Xgetequiv()
X{
X	register FILE  *fp;
X	register int	n;
X	char			buf[512];
X
X	/*
X	**	Open the hosts.equiv file and count the entries.
X	**	Allocate memory for them and this host.
X	*/
X	neq = 1;
X	if((fp = fopen("/etc/hosts.equiv", "r")) != 0) {
X		while(fgets(buf, sizeof buf, fp) != 0)
X			++neq;
X		clearerr(fp);
X		rewind(fp);
X	}
X	if((equiv = (char **)malloc(neq * sizeof *equiv)) == 0) {
X		fprintf(stderr, "malloc: no memory for equivalent hosts\n");
X		exit(1);
X	}
X
X	/*
X	**	This host is first in the list.
X	*/
X	(void) gethostname(buf, sizeof buf);
X	if((equiv[0] = malloc((unsigned)(strlen(buf) + 1))) == 0) {
X		fprintf(stderr, "malloc: no memory for this host\n");
X		exit(1);
X	}
X	strcpy(equiv[0], buf);
X
X	/*
X	**	Add the hosts from hosts.equiv.
X	*/
X	if(fp != 0) {
X		for(n = 1;n < neq;++n) {
X			if(gets(buf) == 0) {
X				neq = n;
X				break;
X			}
X			if((equiv[n] = malloc((unsigned)(strlen(buf) + 1))) == 0) {
X				fprintf(stderr, "malloc: no memory for an equivalent host\n");
X				exit(1);
X			}
X			strcpy(equiv[n], buf);
X		}
X		(void) fclose(fp);
X	}
X}
X
X/*
X**	equivalence: strip "@thishost" or "@equivhost" from an id
X*/
Xequivalence(id)
Xchar   *id;
X{
X	register char  *at;
X	register int	n;
X
X	if((at = index(id, '@')) == 0)
X		return;
X	if(neq == 0)
X		getequiv();
X	for(n = 0;n < neq;++n) {
X		if(strcmp(at+1, equiv[n]) == 0) {
X			*at = '\0';
X			return;
X		}
X	}
X}
X
X/*
X**	idinuse: return True if id is in use by an Active or Waiting player
X**		or is in use by more than one Computer player
X**	side effect: converts user@thishost or user@equivhost to just user
X*/
Xboolean
Xidinuse(c, id)
Xint		c;
Xchar   *id;
X{
X	register int	cc;
X	int				humanuse;
X	int				computeruse;
X
X	equivalence(id);	/* strip @equivhost */
X
X	humanuse = computeruse = 0;
X	for(cc = 0;cc < PLAYERS;++cc) if(cc != c) {
X		if(strncmp(plr[cc].p_id, id, IDLEN-1) == 0) {
X			switch(plr[cc].p_stat) {
X			case Waiting:
X			case Active:
X				++humanuse;
X				break;
X			case Computer:
X				++computeruse;
X				break;
X			default:
X				break;
X			}
X		}
X	}
X
X	if(humanuse > 0 || computeruse > 1)
X		return True;
X
X	return False;
X}
X
X/*
X**	lowscore: give new player a low score
X*/
Xlowscore(c)
Xint		c;
X{
X	register int	cc, low;
X
X	low = 2 * WINSCORE;
X	for(cc = 0;cc < PLAYERS;++cc) {
X		if(cc != c && plr[cc].p_score < low) {
X			switch(plr[cc].p_stat) {
X			case Active:
X			case Computer:
X				low = plr[cc].p_score;
X				break;
X			}
X		}
X	}
X
X	/*
X	**	Set player score to 75% of lowest score.  Score must be a multiple of
X	**	P_FIVE and must be greater than or equal to ONBOARD.
X	*/
X	low = (3 * low) / 4;
X	low = (low / P_FIVE) * P_FIVE;
X	if(low < ONBOARD)
X		low = 0;
X	plr[c].p_score = low;		/* a putrid score, but... */
X	plr[c].p_onboard = False;	/* not on board yet */
X}
X
X/*
X**	updstat: update status line
X*/
Xupdstat(cup)
Xint		cup;
X{
X	register int	c, n, h;
X	char			buf[2*STATLEN+NAMELEN];
X
X	if(active == 0)
X		strcpy(buf, "idle");
X	else {
X		for(h = n = c = 0;c < PLAYERS;++c) {
X			if(plr[c].p_stat == Computer || plr[c].p_stat == Active) {
X				if(plr[c].p_score > h)
X					h = plr[c].p_score;
X				++n;
X			}
X		}
X		if(inprogress == False)
X			sprintf(buf, "waiting pl%d", n);
X		else if(cup < 0)
X			sprintf(buf, "active pl%d hs%d tn%d", n, h, turnnum);
X		else
X			sprintf(buf, "active pl%d hs%d tn%d %s",
X				n, h, turnnum, plr[cup].p_name);
X	}
X
X	sprintf(statusline, "cubes %-*s.", STATLEN-7, buf);
X}
X
X/*
X**	srvshutdown: controlled shutdown on SIGTERM
X*/
Xstatic int
Xsrvshutdown()
X{
X	register int	c;
X	char			msgbuf[MESGLEN];
X
X	sprintf(msgbuf, "%s Server shutdown, closing connection.\r\n", M_DOWN);
X	for(c = 0;c < PLAYERS;++c) {
X		switch(plr[c].p_stat) {
X		case Active:
X		case Waiting:
X			(void) message(c, msgbuf);
X			break;
X		}
X	}
X
X	sleep(3);
X	exit(0);
X}
X
X/*
X**	Get the player's name.
X*/
Xgetplayername(c)
Xint			c;
X{
X	char	msgbuf[MESGLEN];
X
X	/*
X	**	Prompt for and read name.
X	*/
X	sprintf(msgbuf,
X		"%d Hello, I'm the Cube daemon.  Who are you?\r\n", M_HELO);
X	if(message(c, msgbuf) < 0 || reply(c, msgbuf, sizeof msgbuf) < 0)
X		return -1;
X
X	/*
X	**	Check it for (surface) validity.
X	*/
X	if(msgbuf[0] == '\0') {
X		sprintf(msgbuf, "%d Your moniker is really dumb.\r\n", M_ARGE);
X		if(message(c, msgbuf) < 0)
X			return -1;
X		sprintf(msgbuf, "%d Server closing connection.\r\n", M_DOWN);
X		if(message(c, msgbuf) < 0)
X			return -1;
X		plr[c].p_stat = Inactive;
X		plr[c].p_name[0] = '\0';
X		plr[c].p_id[0] = '\0';
X		(void) close(plr[c].p_fd);
X		plr[c].p_fd = -1;
X		return -1;
X	}
X
X	/*
X	**	Check to see whether it's already in use.
X	*/
X	strncpy(plr[c].p_name, msgbuf, NAMELEN);
X	plr[c].p_name[NAMELEN-1] = '\0';
X	if(nameinuse(c, plr[c].p_name) == True) {
X		sprintf(msgbuf, "%d Your moniker is already in use.\r\n", M_ARGE);
X		if(message(c, msgbuf) < 0)
X			return -1;
X		sprintf(msgbuf, "%d Server closing connection.\r\n", M_DOWN);
X		if(message(c, msgbuf) < 0)
X			return -1;
X		plr[c].p_stat = Inactive;
X		plr[c].p_name[0] = '\0';
X		plr[c].p_id[0] = '\0';
X		(void) close(plr[c].p_fd);
X		plr[c].p_fd = -1;
X		return -1;
X	}
X
X	return 0;
X}
X
X/*
X**	getplayerid: get player's unique id (user@host)
X*/
Xgetplayerid(c)
Xint			c;
X{
X	char	msgbuf[MESGLEN];
X
X	/*
X	**	Prompt for and read the id.
X	*/
X	sprintf(msgbuf, "%d Please supply a unique id.\r\n", M_RQID);
X	if(message(c, msgbuf) < 0 || reply(c, msgbuf, sizeof msgbuf) < 0)
X		return -1;
X
X	/*
X	**	A null id is not valid.
X	*/
X	if(msgbuf[0] == '\0') {
X		sprintf(msgbuf, "%d Sorry, that's not unique.\r\n", M_ARGE);
X		if(message(c, msgbuf) < 0)
X			return -1;
X		sprintf(msgbuf, "%d Server closing connection.\r\n", M_DOWN);
X		if(message(c, msgbuf) < 0)
X			return -1;
X		plr[c].p_stat = Inactive;
X		plr[c].p_name[0] = '\0';
X		plr[c].p_id[0] = '\0';
X		(void) close(plr[c].p_fd);
X		plr[c].p_fd = -1;
X		return -1;
X	}
X
X	/*
X	**	Check to see that the id is not already in use.
X	*/
X	strncpy(plr[c].p_id, msgbuf, IDLEN);
X	plr[c].p_id[IDLEN-1] = '\0';
X	if(idinuse(c, plr[c].p_id) == True) {
X		sprintf(msgbuf, "%d You're already playing!\r\n", M_ARGE);
X		if(message(c, msgbuf) < 0)
X			return -1;
X		sprintf(msgbuf, "%d Server closing connection.\r\n", M_DOWN);
X		if(message(c, msgbuf) < 0)
X			return -1;
X		plr[c].p_stat = Inactive;
X		plr[c].p_name[0] = '\0';
X		plr[c].p_id[0] = '\0';
X		(void) close(plr[c].p_fd);
X		plr[c].p_fd = -1;
X		return -1;
X	}
X
X	return 0;
X}
X
X/*
X**	addwaiting: add waiting players to a game in progress.
X*/
Xaddwaiting()
X{
X	int		c, cc;
X	char	msgbuf[MESGLEN];
X
X	if(waiting <= 0 || inprogress == False)
X		return;
X
X	for(c = 0;c < PLAYERS;++c) {
X		if(plr[c].p_stat != Waiting)
X			continue;
X		plr[c].p_stat = Active;
X		++active, --waiting;
X
X		sprintf(msgbuf, "%d You are player %d (%s).\r\n",
X			M_UARE, c, plr[c].p_name);
X		if(message(c, msgbuf) < 0)
X			continue;
X		sprintf(msgbuf, "%d You have entered a game in progress.\r\n", M_INFO);
X		if(message(c, msgbuf) < 0)
X			continue;
X
X		lowscore(c);
X		for(cc = 0;cc < PLAYERS;++cc) {
X			switch(plr[cc].p_stat) {
X			case Computer:
X			case Active:
X			case Waiting:
X				sprintf(msgbuf, "%d player %d %s\r\n",
X					M_PNUM, cc, plr[cc].p_name);
X				if(message(c, msgbuf) < 0) {
X					cc = PLAYERS;
X					continue;
X				}
X				sprintf(msgbuf, "%d Player %d now has %d points.\r\n",
X					M_OSCO, cc, plr[cc].p_score);
X				if(message(c, msgbuf) < 0) {
X					cc = PLAYERS;
X					continue;
X				}
X				break;
X			}
X		}
X
X		sprintf(msgbuf, "%d player %d %s\r\n", M_PNUM, c, plr[c].p_name);
X		announce(c, msgbuf);
X		annscore(c);
X	}
X}
END_OF_cubeserver.c
if test 28971 -ne `wc -c <cubeserver.c`; then
    echo shar: \"cubeserver.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f screen.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"screen.c\"
else
echo shar: Extracting \"screen.c\" \(16193 characters\)
sed "s/^X//" >screen.c <<'END_OF_screen.c'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)screen.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif	lint
X
X#include	<stdio.h>
X#include	<curses.h>
X#include	<sys/ioctl.h>
X#include	<signal.h>
X#include	<setjmp.h>
X#include	<strings.h>
X#include	<ctype.h>
X#include	"cubes.h"
X
X#define		DICEROW			0				/* top line of dice subwindow */
X#define		DICECOL			0				/* first column of dice subwindow */
X#define		DIELINES		7				/* height of a single die */
X#define		DIEWID			10				/* width of a single die */
X#define		DICELINES		(DIELINES+5)	/* height of dice subwindow */
X#define		DICEWID			(NDICE*DIEWID)	/* width of dice subwindow */
XWINDOW	   *dicewin;						/* dice subwindow */
XWINDOW	   *die[NDICE];						/* subwindow for each die */
X
X#define		INFOCOL			0				/* where status messages appear */
X#define		INFOROW			(DICELINES+1)	/* where status messages appear */
X#define		INFOLINES		1				/* lines in subwindow */
X#define		INFOWID			DICEWID			/* same width as dice subwindow */
XWINDOW	   *infowin;						/* informational subwindow */
X
X#define		PROMPTCOL		0				/* where prompt appears */
X#define		PROMPTROW		(INFOROW+1)		/* where prompt appears */
X#define		PROMPTLINES		1				/* one line of prompt */
X#define		PROMPTWID		DICEWID			/* same width as dice subwindow */
XWINDOW	   *promptwin;						/* prompt subwindow */
X
X#define		HELPROW			(LINES-9)		/* where help subwindow begins */
X#define		HELPCOL			0				/* where help subwindow begins */
X#define		HELPLINES		0				/* extends to edge of screen */
X#define		HELPWID			0				/* extends to edge of screen */
XWINDOW	   *helpwin;						/* help message subwindow */
X
X#define		SCOREROW		0				/* where score subwindow begins */
X#define		SCORECOL		(DICEWID+2)		/* where score subwindow begins */
X#define		SCORELINES		HELPROW			/* extends to help subwindow */
X#define		SCOREWID		0				/* extends to edge of screen */
XWINDOW	   *scorewin;						/* player scores subwindow */
X
Xstruct ltchars			tty_lt;				/* local tty characters */
Xchar				   *AS, *AE;			/* graphics... */
Xgraphtype				graphics = Nographics;	/* terminal has no graphics */
Xboolean					quiet	= False;	/* in quiet mode */
X
Xstatic int				refdummy;
X#define	REFRESH()		(refdummy = (quiet == False) ? refresh() : 0)
X#define	WREFRESH(win)	(refdummy = (quiet == False) ? wrefresh(win) : 0)
X
Xextern int				mypnum;
Xextern int				turnnum;
Xextern boolean			gotintr;
Xextern player			plr[];
Xextern diceset			dice;
Xstatic int				beep();
Xstatic int				outc();
Xstatic int				suspend();
Xstatic char			   *combname();
X
X/*
X**	interrupt: handle interrupt signal
X*/
Xstatic int
Xinterrupt()
X{
X	gotintr = True;
X}
X
X/*
X**	reallyquit: ask player if they really want to quit
X*/
Xreallyquit()
X{
X	char	ans[128];
X
X	if(prompt("Do you really want to quit? [ny]", ans) >= 0) {
X		if(ans[0] != 'y' && ans[0] != 'Y') {
X			gotintr = False;
X			return;
X		}
X	}
X
X	(void) infomesg("So long, quitter...");
X	sleep(2);
X	cleanup(0);
X}
X
X#define	OWNgetcap
X#ifdef	OWNgetcap
X#define	getcap(x)	mygetcap(x)
X/*
X**	getcap: mimics functional curses getcap
X*/
Xstatic char *
Xmygetcap(cap)
Xchar   *cap;
X{
X	static char		entbuf[1024];
X	static char		capbuf[1024];
X	static char	   *acap	= 0;
X	char		   *term;
X	extern char	   *getenv();
X	extern char	   *tgetstr();
X
X	if(acap == 0) {
X		if((term = getenv("TERM")) == 0)
X			return (char *)0;
X		if(tgetent(entbuf, term) != 1)
X			return (char *)0;
X		acap = capbuf;
X	}
X
X	return tgetstr(cap, &acap);
X}
X#endif	OWNgetcap
X
X/*
X**	startup: initialize the screen
X*/
Xstartup()
X{
X	register int	d;
X
X	initscr();
X	(void) signal(SIGHUP, SIG_DFL);
X	gotintr = False;
X	if(signal(SIGINT, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGINT, interrupt);
X	if(signal(SIGQUIT, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGQUIT, interrupt);
X	if(signal(SIGTSTP, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGTSTP, suspend);
X
X	(void) ioctl(_tty_ch, TIOCGLTC, (char *)&tty_lt);
X
X	scrollok(stdscr, FALSE);
X	crmode();
X	noecho();
X	REFRESH();
X
X	if(graphics != Nographics) {
X		AS = getcap("as");
X		AE = getcap("ae");
X		if(AS == 0 || AE == 0)
X			graphics = Nographics;
X		else
X			SO = AS, SE = AE;
X	}
X
X	dicewin = subwin(stdscr, DICELINES, DICEWID, DICEROW, DICECOL);
X	if(dicewin == 0) {
X		fprintf(stderr, "cubes: can't create dicewin subwindow\n");
X		cleanup(1);
X	}
X
X	for(d = 0;d < NDICE;++d) {
X		die[d] = subwin(dicewin, DIELINES, DIEWID, 0, d*DIEWID);
X		if(die[d] == 0) {
X			fprintf(stderr, "cubes: can't create die[%d] subwindow\n", d);
X			cleanup(1);
X		}
X	}
X
X	infowin = subwin(stdscr, INFOLINES, INFOWID, INFOROW, INFOCOL);
X	if(infowin == 0) {
X		fprintf(stderr, "cubes: can't create infowin subwindow\n");
X		cleanup(1);
X	}
X
X	promptwin = subwin(stdscr, PROMPTLINES, PROMPTWID, PROMPTROW, PROMPTCOL);
X	if(promptwin == 0) {
X		fprintf(stderr, "cubes: can't create promptwin subwindow\n");
X		cleanup(1);
X	}
X
X	scorewin = subwin(stdscr, SCORELINES, SCOREWID, SCOREROW, SCORECOL);
X	if(scorewin == 0) {
X		fprintf(stderr, "cubes: can't create scorewin subwindow\n");
X		cleanup(1);
X	}
X
X	helpwin = subwin(stdscr, HELPLINES, HELPWID, HELPROW, HELPCOL);
X	if(helpwin == 0) {
X		fprintf(stderr, "cubes: can't create helpwin subwindow\n");
X		cleanup(1);
X	}
X
X	helpmesg();
X}
X
X/*
X**	cleanup: cleanup the screen and exit
X*/
Xcleanup(ex)
Xint		ex;
X{
X	if(quiet == False) {
X		endwin();
X		if(ex == 0)
X			tputs(CL, LINES, outc);
X	}
X	exit(ex);
X}
X
X/*
X**	infomesg: display an informational or status message
X*/
Xinfomesg(mesg)
Xchar   *mesg;
X{
X	wclear(infowin);
X	waddstr(infowin, mesg);
X	WREFRESH(infowin);
X	if(quiet == True) {
X		printf("%s\n", mesg);
X		(void) fflush(stdout);
X	}
X	return 0;
X}
X
X/*
X**	clearinfo: clear info message
X*/
Xclearinfo()
X{
X	wclear(infowin);
X	WREFRESH(infowin);
X	return 0;
X}
X
Xstatic char	   *hline[]	= {
X/*	"1234567890123456789012345678901234567890123456789",	*/
X	"+-----------------------------------------------+",
X	"| 5o'kind    300 * face (held dice count)       |",
X	"| 4o'kind    200 * face (held dice don't count) |",
X	"| 3o'kind    100 * face (held dice don't count) |",
X	"| Straight         1500 (held dice don't count) +",
X	"| Small Str.        400 (held dice don't count) |",
X	"| One               100 (face for ones is ten)  |",
X	"| Five               50                         |",
X	"+-----------------------------------------------+",
X	(char *)0
X};
X
Xstatic char	   *iline[]	= {
X/*	"012345678901234567890123456789",	*/
X	"-----------------------------+",
X	" ** Turn Point Thresholds ** |",
X	" To get on board         500 |",
X	" To go off board         500 |",
X	"-----------------------------+",
X	" ** Game Point Thresholds ** |",
X	" Total to win          10000 |",
X	" Margin to win by        250 |",
X	"-----------------------------+",
X	(char *)0
X};
X
X/*
X**	helpmesg: display instructions
X*/
Xhelpmesg()
X{
X	register int	row;
X
X	wmove(helpwin, 0, 0);
X	wclear(helpwin);
X	for(row = 0;hline[row] != 0;++row) {
X		wmove(helpwin, row, 0);
X		wclrtoeol(helpwin);
X		waddstr(helpwin, hline[row]);
X		waddstr(helpwin, iline[row]);
X	}
X	WREFRESH(helpwin);
X}
X
X/*
X**	urgjmp, urgent: SIGURG handler
X*/
Xstatic jmp_buf	urgjmp;
Xstatic int
Xurgent()
X{
X	longjmp(urgjmp, 1);
X}
X
X/*
X**	prompt: print a prompt message and read an answer
X*/
Xprompt(mesg, answer)
Xchar   *mesg, *answer;
X{
X	register int	c;
X	register char  *s;
X	int			  (*sigurg)();
X	int			  (*sigint)();
X	long			count;
X	char			dbuf[64];
X
X	/*
X	**	Disallow interrupts for now.
X	*/
X	if((sigint = signal(SIGINT, SIG_IGN)) != SIG_IGN)
X		(void) signal(SIGINT, beep);
X
X	/*
X	**	Flush any typeahead.
X	*/
X	while(ioctl(0, FIONREAD, (char *)&count) == 0 && count > 0)
X		(void) read(0, dbuf, sizeof dbuf);
X	rewind(stdin);
X
Xreprompt:
X	answer[0] = '\0';
X	wmove(promptwin, 0, 0);
X	wclear(promptwin);
X	waddstr(promptwin, mesg);
X	waddch(promptwin, ' ');
X	WREFRESH(promptwin);
X	if(quiet == True) {
X		printf("%s ", mesg);
X		(void) fflush(stdout);
X	}
X
X	/*
X	**	If server times out on us it sends out of band
X	**	data, which should cause us to receive a SIGURG.
X	*/
X	sigurg = signal(SIGURG, SIG_IGN);
X	if(setjmp(urgjmp) != 0) {
X		(void) signal(SIGINT, sigint);
X		(void) signal(SIGURG, sigurg);
X		wclear(promptwin);
X		(void) infomesg("The server timed out waiting for your response.");
X		return -2;	/* timeout */
X	}
X
X	(void) signal(SIGURG, urgent);
X	for(s = answer;;) {
X		c = wgetch(promptwin);
X		if(c == EOF || c == '\r' || c == '\n')
X			break;
X		if(c == _tty.sg_erase) {
X			if(--s < answer) {
X				s = answer;
X				beep();
X				continue;
X			}
X			waddch(promptwin, '\b');
X			wdelch(promptwin);
X			WREFRESH(promptwin);
X			continue;
X		}
X		if(c == _tty.sg_kill || c == tty_lt.t_werasc)
X			goto reprompt;
X		if(c == tty_lt.t_rprntc || c == '\f') {
X			redraw();
X			continue;
X		}
X		if(iscntrl(c)) {
X			beep();
X			continue;
X		}
X		waddch(promptwin, c);
X		WREFRESH(promptwin);
X		*s++ = c;
X	}
X	*s = '\0';
X	(void) signal(SIGURG, sigurg);
X
X	wmove(promptwin, 0, 0);
X	wclear(promptwin);
X	WREFRESH(promptwin);
X
X	wclear(infowin);
X	WREFRESH(infowin);
X
X	(void) signal(SIGINT, sigint);
X	return 0;
X}
X
X/*
X**	nowup: mark player number as being up
X*/
Xnowup(num)
Xint		num;
X{
X	static int	lastup	= -1;
X
X	(void) clearinfo();
X
X	wmove(scorewin, 0, 16);
X	wprintw(scorewin, "%5d", turnnum);
X
X	if(lastup != -1) {
X		wmove(scorewin, lastup+2, 0);
X		waddstr(scorewin, "    ");
X	}
X	wmove(scorewin, num+2, 0);
X	waddstr(scorewin, "=-> ");
X	lastup = num;
X	WREFRESH(scorewin);
X
X	if(num == mypnum)
X		beep();
X
X	return 0;
X}
X
X/*
X**	showplr: update player info
X*/
Xshowplr(c)
Xint		c;
X{
X	wmove(scorewin, 0, 0);
X	wclrtoeol(scorewin);
X	wprintw(scorewin,
X		"%-3s %-11.11s %5d %5s", "Up", "Player", turnnum, "Score");
X	wmove(scorewin, 1, 0);
X	wclrtoeol(scorewin);
X	wprintw(scorewin, "%3s %-17.17s %5s", "---", "-----------------", "-----");
X	wmove(scorewin, c+2, 0);
X	wclrtoeol(scorewin);
X	if(plr[c].p_stat == Active || plr[c].p_stat == Computer)
X		wprintw(scorewin, "    %-17.17s %5d", plr[c].p_name, plr[c].p_score);
X	WREFRESH(scorewin);
X
X	return 0;
X}
X
X/*
X**	clearplr: clear player info
X*/
Xclearplr(c)
Xint		c;
X{
X	wmove(scorewin, c+2, 0);
X	wclrtoeol(scorewin);
X	WREFRESH(scorewin);
X
X	return 0;
X}
X
X/*
X**	showdice: show dice status and faces and scoring summary
X*/
Xshowdice()
X{
X	int		d;
X	int		ndrawn;
X	boolean	drawn[NDICE];
X	char	msgbuf[MESGLEN];
X
X	/*
X	**	Draw all the dice.  First draw any held or free dice,
X	**	then draw the remaining dice in random order.  Note that
X	**	the inefficient algorithm used might add a little suspense.
X	*/
X	ndrawn = 0;
X	for(d = 0;d < NDICE;++d) {
X		switch(dice.d_stat[d]) {
X		case Held:
X		case Free:
X			(void) drawdie(d);
X			drawn[d] = True;
X			++ndrawn;
X			break;
X		default:
X			drawn[d] = False;
X			break;
X		}
X	}
X	while(ndrawn < NDICE) {
X		d = dieroll(NDICE) - 1;
X		if(drawn[d] == False) {
X			(void) drawdie(d);
X			drawn[d] = True;
X			++ndrawn;
X		}
X	}
X
X	/*
X	**	Show the combinations.
X	*/
X	wmove(dicewin, DICELINES-5, 0);
X	wclrtoeol(dicewin);
X	for(d = 0;d < NDICE;++d) {
X		wmove(dicewin, DICELINES-5, d * DIEWID);
X		if(dice.d_stat[d] != Free && dice.d_face[d] != BADFACE)
X			waddstr(dicewin, combname(dice.d_comb[d]));
X	}
X	WREFRESH(dicewin);
X
X	wmove(dicewin, DICELINES-3, 0);
X	wclrtoeol(dicewin);
X	if(dice.d_mesg[0] != '\0') {
X		d = (DICEWID - strlen(dice.d_mesg)) / 2;
X		if(d < 0) d = 0;
X		wprintw(dicewin, "%*s%s", d, "", dice.d_mesg);
X	}
X
X	wmove(dicewin, DICELINES-2, 0);
X	wclrtoeol(dicewin);
X	if(dice.d_pts_turn > 0
X		&& dice.d_mesg[0] != '\0' && strcmp(dice.d_mesg, "nothing") != 0) {
X		sprintf(msgbuf, "plus %d saved", dice.d_pts_turn);
X		d = (DICEWID - strlen(msgbuf)) / 2;
X		if(d < 0) d = 0;
X		wprintw(dicewin, "%*s%s", d, "", msgbuf);
X	} else if(dice.d_pts_roll < 0) {
X		sprintf(msgbuf, "threw away %d points", -dice.d_pts_roll);
X		d = (DICEWID - strlen(msgbuf)) / 2;
X		if(d < 0) d = 0;
X		wprintw(dicewin, "%*s%s", d, "", msgbuf);
X	}
X
X	wmove(dicewin, DICELINES-1, 0);
X	wclrtoeol(dicewin);
X	for(d = 0;d < NDICE;++d)
X		if(dice.d_comb[d] == Nothing)
X			break;
X	if(d == NDICE) {
X		strcpy(msgbuf, "scored with all dice");
X		d = (DICEWID - strlen(msgbuf)) / 2;
X		if(d < 0) d = 0;
X		wprintw(dicewin, "%*s%s", d, "", msgbuf);
X	}
X
X	WREFRESH(dicewin);
X
X	return 0;
X}
X
X/*
X**	cdieface: die faces for each die formed by characters
X*/
Xstatic char	   *cdieface[]	= {
X" in hand ",".-------,","|       |","|       |","|       |","`-------'",
X"   one   ",".-------,","|       |","|   *   |","|       |","`-------'",
X"   two   ",".-------,","| *     |","|       |","|     * |","`-------'",
X"  three  ",".-------,","|     * |","|   *   |","| *     |","`-------'",
X"   four  ",".-------,","| *   * |","|       |","| *   * |","`-------'",
X"   five  ",".-------,","| *   * |","|   *   |","| *   * |","`-------'",
X"   six   ",".-------,","| *   * |","| *   * |","| *   * |","`-------'",
X};
X
X/*
X**	ddieface: die faces for each die formed by DEC graphics characters
X*/
Xstatic char	   *ddieface[]	= {
X" in hand ","lqqqqqqqk","x       x","x       x","x       x","mqqqqqqqj",
X"   one   ","lqqqqqqqk","x       x","x   `   x","x       x","mqqqqqqqj",
X"   two   ","lqqqqqqqk","x `     x","x       x","x     ` x","mqqqqqqqj",
X"  three  ","lqqqqqqqk","x     ` x","x   `   x","x `     x","mqqqqqqqj",
X"   four  ","lqqqqqqqk","x `   ` x","x       x","x `   ` x","mqqqqqqqj",
X"   five  ","lqqqqqqqk","x `   ` x","x   `   x","x `   ` x","mqqqqqqqj",
X"   six   ","lqqqqqqqk","x `   ` x","x `   ` x","x `   ` x","mqqqqqqqj",
X};
X
X/*
X**	zdieface: die faces for each die formed by Zenith graphics characters
X*/
Xstatic char	   *zdieface[]	= {
X" in hand ","faaaaaaac","`       `","`       `","`       `","eaaaaaaad",
X"   one   ","faaaaaaac","`       `","`   ^   `","`       `","eaaaaaaad",
X"   two   ","faaaaaaac","` ^     `","`       `","`     ^ `","eaaaaaaad",
X"  three  ","faaaaaaac","`     ^ `","`   ^   `","` ^     `","eaaaaaaad",
X"   four  ","faaaaaaac","` ^   ^ `","`       `","` ^   ^ `","eaaaaaaad",
X"   five  ","faaaaaaac","` ^   ^ `","`   ^   `","` ^   ^ `","eaaaaaaad",
X"   six   ","faaaaaaac","` ^   ^ `","` ^   ^ `","` ^   ^ `","eaaaaaaad",
X};
X
X/*
X**	combname: return pointer to string describing combination
X**		all names are centered in nine spaces
X*/
Xstatic char *
Xcombname(comb)
Xcombination	comb;
X{
X	switch(comb) {
X	/*						return "123456789"; */
X	case Previous:			return "    +    ";
X	case Nothing:			return "         ";
X	case Five:				return "   five  ";
X	case Ace:				return "   one   ";
X	case Three_of_a_kind:	return " 3o'kind ";
X	case Small_straight:	return "  small  ";
X	case Four_of_a_kind:	return " 4o'kind ";
X	case Straight:			return " straight";
X	case All_of_a_kind:		return " 5o'kind ";
X	default:				return "    ?    ";
X	}
X}
X
X/*
X**	drawdie: draw die number d
X*/
Xdrawdie(d)
Xint		d;
X{
X	register int	row, s;
X	register char **pix;
X
X	wclear(die[d]);
X
X	wmove(die[d], 0, 0);
X	wprintw(die[d], "%*d", (DIEWID+1)/2, d+1);
X
X	if((s = dice.d_face[d]) == BADFACE)
X		s = 0;
X
X	switch(graphics) {
X	case Digital:	pix = &ddieface[s*(DIELINES-1)]; break;
X	case Zenith:	pix = &zdieface[s*(DIELINES-1)]; break;
X	default:		pix = &cdieface[s*(DIELINES-1)]; break;
X	}
X
X	for(row = 0;row < DIELINES-1;++row) {
X		if(row > 0 && graphics != Nographics)
X			wstandout(die[d]);
X		wmove(die[d], row+1, 0);
X		waddstr(die[d], *(pix+row));
X		if(row > 0 && graphics != Nographics)
X			wstandend(die[d]);
X	}
X
X	WREFRESH(die[d]);
X	return 0;
X}
X
X/*
X**	outc: write a single character
X*/
Xstatic
Xoutc(c)
Xchar	c;
X{
X	return write(_tty_ch, &c, 1);
X}
X
X/*
X**	beep: send a beep
X*/
Xstatic
Xbeep()
X{
X	if(quiet == False)
X		(void) outc('\007');
X}
X
X/*
X**	neutral: return cursor to a neutral position
X*/
Xneutral()
X{
X	move(PROMPTROW, PROMPTCOL);
X	REFRESH();
X}
X
X/*
X**	redraw: cause the screen to be redrawn
X*/
Xredraw()
X{
X	tputs(TI, 0, outc);
X	tputs(VS, 0, outc);
X	WREFRESH(curscr);
X}
X
X/*
X**	suspend: catch suspend signal
X*/
Xstatic int
Xsuspend()
X{
X	if(quiet == False)
X		beginquiet();
X	else
X		endquiet();
X}
X
Xstatic int	ttyflags;	/* set in beginquiet, used in endquiet */
X
X/*
X**	beginquiet: enter quiet mode
X*/
Xstatic int
Xbeginquiet()
X{
X	quiet = True;
X	ttyflags = _tty.sg_flags;	/* store curses state */
X	endwin();
X	(void) fflush(stdout);
X	tputs(CL, LINES, outc);
X	printf("Hit <suspend> again to leave quiet mode.\n");
X	(void) fflush(stdout);
X}
X/*
X**	endquiet: leave quiet mode
X*/
Xstatic int
Xendquiet()
X{
X	quiet = False;
X	savetty();	/* re-remember the virgin state */
X	_tty.sg_flags = ttyflags;	/* restore special curses state */
X	(void) ioctl(_tty_ch, TIOCSETN, &_tty);
X	tputs(TI, 0, outc);
X	tputs(VS, 0, outc);
X	(void) WREFRESH(curscr);
X}
END_OF_screen.c
if test 16193 -ne `wc -c <screen.c`; then
    echo shar: \"screen.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 1 \(of 3\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 3 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