[comp.sources.games] v06i065: cubes2 - a networked dice game

games@tekred.CNA.TEK.COM (04/28/89)

Submitted-by: gmp@rayssdb.RAY.COM (Gregory M. Paris)
Posting-number: Volume 6, Issue 65
Archive-name: cubes2/Part07



#! /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 7 (of 8)."
# Contents:  cubes.c cubestat.c risk.c strategies.c
# Wrapped by billr@saab on Thu Apr 27 12:13:40 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'cubes.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cubes.c'\"
else
echo shar: Extracting \"'cubes.c'\" \(16290 characters\)
sed "s/^X//" >'cubes.c' <<'END_OF_FILE'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)cubes.c 5.1 (G.M. Paris) 89/01/22";
X#endif	lint
X
X/*
X**
X**	cubes 5.1  Copyright 1989 Gregory M. Paris
X**		Permission granted to redistribute on a no charge basis.
X**		All other rights are reserved.
X**
X*/
X
X#include	<stdio.h>
X#include	<strings.h>
X#include	<ctype.h>
X#include	<signal.h>
X#include	<setjmp.h>
X#include	<errno.h>
X#include	<sys/types.h>
X#include	<sys/time.h>
X#include	<sys/wait.h>
X#include	<sys/file.h>
X#include	<sys/dir.h>
X#include	<sys/socket.h>
X#include	<sys/ioctl.h>
X#include	<netdb.h>
X#include	<netinet/in.h>
X#ifdef	UNIXSOCK
X#include	<sys/un.h>
X#endif	UNIXSOCK
X#include	<pwd.h>
X#include	"cubes.h"
X
Xchar			   *cubename	= 0;		/* player name */
Xchar			   *cubehist	= 0;		/* personal ranking history */
Xchar			   *cubehost	= 0;		/* server hostname */
Xint					ssock		= -1;		/* server socket descriptor */
XFILE			   *fsock		= 0;		/* server socket stream */
Xint					child		= 0;		/* child pid */
Xboolean				active		= True;		/* True while active */
Xboolean				gotintr		= False;	/* True if interrupt received */
Xboolean				quiet		= False;	/* True if in background */
Xenterlate			watch		= Watch;	/* what to do about late entry */
Xenterlate			spider		= Play;		/* what to do about waiting */
Xwinpref				preference	= Nopref;	/* gametype/winscore preference */
Xextern graphtype	graphics;				/* graphics type */
X
Xextern int			errno;
Xextern char		   *sys_errlist[];
X
Xextern int			optind;
Xextern int			opterr;
Xextern char		   *optarg;
Xextern char		   *getenv();
Xextern char		   *malloc();
Xextern char		   *fgets();
Xextern char		   *fullname();
Xstatic char		   *save();
Xstatic int			slowstop();
X
Xmain(ac, av)
Xchar   *av[];
X{
X	int				c;
X	register int	type;
X	union wait		chexit;
X	char		   *s, msgbuf[BUFSIZ];
X
X#ifdef	GAMESDSO
X	/*
X	**	Specific to author's locale: don't accidentally charge contracts!
X	*/
X	if(geteuid() == 0)
X		(void) setdso(GAMESDSO);
X#endif	GAMESDSO
X
X	/*
X	**	We always turn off any setuid status.  Except for the GAMESDSO
X	**	stuff above, there's no need for the client to run setuid.
X	*/
X	(void) setuid(getuid());
X
X	/*
X	**	Attempt to ensure the efficiency of output stream.
X	XXX	This may reduce system load on some systems.
X	*/
X	{
X		static char	outbuf[BUFSIZ];
X		setbuf(stdout, outbuf);
X	}
X
X	irandom();	/* initialize the random number generator */
X
X	opterr = 0;
X	while((c = getopt(ac, av, "BSF:spwg:f:h:n:")) >= 0) {
X		switch(c) {
X		case 'B': preference = Blitz; break;
X		case 'S': preference = Standard; break;
X		case 'F':
X			switch(*optarg) {
X			case 'B': preference = Fblitz; break;
X			case 'S': preference = Fstand; break;
X			default:
X				fprintf(stderr,
X					"cubes: forced preference must be Standard or Blitz\n");
X				exit(1);
X			}
X			break;
X		case 'g':	/* set graphics type */
X			switch(*optarg) {
X			case 'n': case 'N':	graphics = Nographics; break;
X			case 'd': case 'D':	graphics = Digital; break;
X			case 'z': case 'Z':	graphics = Zenith; break;
X			case 'r': case 'R':
X				fprintf(stderr, "cubes: warning: no ReGIS -- using DEC\n");
X				graphics = Digital;
X				break;
X			default:
X				fprintf(stderr,
X					"cubes: graphics type must be None, DEC, or Zenith\n");
X				exit(1);
X			}
X			break;
X		case 'h':	/* daemon on remote host */
X			cubehost = save(optarg);
X			break;
X		case 'n':	/* set player name */
X			cubename = save(optarg);
X			break;
X		case 'f':
X			cubehist = optarg;
X			break;
X		case 'a': watch = Ask; break;
X		case 'p': watch = Play; break;
X		case 'w': watch = Watch; break;	/* default */
X		case 's': spider = Wait; break;
X/*		case 'i': spider = Play; break;	/* default */
X		default:
X			fprintf(stderr,
X"usage -- cubes [-[F]{BS}] [-s] [-{apw}] [-g{drz}] [-f hist] [-h host] [name]\n"
X			);
X			exit(1);
X		}
X	}
X
X	/*
X	**	We support -n above, but now recognize non-option arguments
X	**	as the requested player name.  If both are specified, then
X	**	it's probably a goof, so complain and die.
X	*/
X	if(optind < ac) {
X		if(cubename != 0) {
X			fprintf(stderr, "usage -- cubes name -OR- cubes -n name\n");
X			exit(1);
X		}
X		strcpy(msgbuf, av[optind]);
X		while(++optind < ac) {
X			strcat(msgbuf, " ");
X			strcat(msgbuf, av[optind]);
X		}
X		cubename = save(msgbuf);
X	}
X
X	/*
X	**	If the cubename wasn't specified on the command line,
X	**	supply a prompt which shows how long the name can be.
X	**	If the answer to this prompt is <CR>, then come up with
X	**	a name by looking at the user's password entry gecos field.
X	*/
X	if(cubename == 0) {
X		if((cubename = getenv("CUBENAME")) == 0) {
X			printf("......... 123456789012345678\n");
X			printf("CUBENAME? ");
X			(void) fflush(stdout);
X			if(fgets(msgbuf, sizeof msgbuf, stdin) == 0)
X				exit(0);	/* must have changed mind */
X			if((s = index(msgbuf, '\n')) == 0) {
X				fprintf(stderr,
X					"cubes: name must be eighteen characters or less\n");
X				exit(1);
X			}
X			*s = '\0';
X			if(msgbuf[0] != '\0')
X				cubename = save(msgbuf);
X			else {
X				struct passwd  *pw, *getpwuid();
X
X				if((pw = getpwuid(getuid())) == 0) {
X					fprintf(stderr, "cubes: no password entry!\n");
X					exit(1);
X				}
X				cubename = save(fullname(pw));
X			}
X		}
X	}
X
X	/*
X	**	Server host was not specified on the command line, so check
X	**	the environment for a name.  If none, we'll use this host.
X	*/
X	if(cubehost == 0)
X		cubehost = getenv("CUBEHOST");
X
X	/*
X	**	The personal history file was not set on the command line,
X	**	so check the environment for a value.  If one is not found,
X	**	then we'll default to "$HOME/.cubehist".  The only way for
X	**	the user to turn off this feature is to specify a null name.
X	*/
X	if(cubehist == 0 && (cubehist = getenv("CUBEHIST")) == 0) {
X		if((cubehist = getenv("HOME")) == 0)
X			cubehist = ".";
X		sprintf(msgbuf, "%s/.cubehist", cubehist);
X		cubehist = save(msgbuf);
X	}
X	
X	/*
X	**	We're done processing arguments, so it's safe to blow them
X	**	away for the purpose of disguise.
X	*/
X	newname(ac, av);
X
X	/*
X	**	If this process isn't the process group leader, then we should be OK.
X	**	(I'm not sure that this will work correctly in all situations.)
X	**	Otherwise, we must fork for purposes of useful suspend action.  In
X	**	that case, the parent just waits here for child to exit.
X	*/
X	if(getpid() == getpgrp(0))		/* comment out this line if necessary */
X		switch(child = fork()) {
X		case 0:		/* child */
X			break;
X		case -1:	/* error */
X			perror("fork");
X			exit(1);
X		default:	/* parent */
X			(void) signal(SIGINT,  SIG_IGN);
X			(void) signal(SIGQUIT, SIG_IGN);
X			(void) signal(SIGTSTP, slowstop);
X			(void) signal(SIGTTOU, SIG_DFL);	/* sent by child */
X			(void) signal(SIGTTIN, SIG_IGN);	/* ignored by child */
X			parentname(av);						/* need a new disguise */
X			while(wait(&chexit) != child)
X				;
X			if(chexit.w_termsig != 0)
X				exit((int)chexit.w_termsig | 0x80);
X			exit((int)chexit.w_retcode);
X		}
X
X	/*
X	**	Initialize terminal and set signal handlers.
X	*/
X	startup();
X
X	/*
X	**	Connect to the server.
X	*/
X	opensocktoserv();
X	
X	/*
X	**	Process messages from the server.
X	*/
X	initactions();
X	while(active == True) {
X		if((type = rdmessage(msgbuf, sizeof msgbuf)) < 0)
X			cleanup(1, "error reading message from server");
X		if(action(type, msgbuf) < 0)
X			cleanup(1, "action failed");
X		if(gotintr == True)
X			reallyquit();
X		if(quiet == False)
X			flushinput();
X	}
X	
X	/*
X	**	Nothing currently gets us here.
X	*/
X	cleanup(2, "left main loop!");
X}
X
X/*
X**	opensocktoserv: open connection to server
X*/
Xopensocktoserv()
X{
X	int			off		= 0;
X	boolean		local	= False;
X
X#ifdef	lint
X	local = local;
X#endif	lint
X
X	/*
X	**	If a null hostname or no hostname was supplied,
X	**	then assume the local host.
X	*/
X	if(cubehost == 0 || *cubehost == '\0') {
X		char	thishost[256];
X
X		(void) gethostname(thishost, sizeof thishost);
X		cubehost = save(thishost);
X		local = True;
X	}
X
X#ifdef	UNIXSOCK
X	/*
X	**	If the host the localhost, then try connecting to the
X	**	UNIX domain server socket.  If this fails, go on to
X	**	try the INET domain socket.
X	*/
X	if(local == True) {
X		struct sockaddr_un	userver;
X		int					len;
X
X		bzero((char *)&userver, sizeof userver);
X		userver.sun_family = AF_UNIX;
X		strcpy(userver.sun_path, UNIXSOCK);
X		len = sizeof userver.sun_family + strlen(userver.sun_path);
X
X		if((ssock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
X			/* XXX: syslog message here */
X			goto sorry;
X		}
X		if(connect(ssock, &userver, len) < 0) {
X			/* XXX: syslog message here */
X			(void) close(ssock);
X			ssock = -1;
X			goto sorry;
X		}
Xsorry:	;
X	}
X#endif	UNIXSOCK
X
X	/*
X	**	Connect to the server's INET socket.
X	*/
X	if(ssock < 0) {
X		struct sockaddr_in	server;
X		struct hostent	   *ph;
X		struct servent	   *ps;
X
X		if((ph = gethostbyname(cubehost)) == 0)
X			cleanup(1, "unknown host");
X		ps = getservbyname("cube", "tcp");	/* let client use default port */
X
X		bzero((char *)&server, sizeof server);
X		server.sin_family = AF_INET;
X		server.sin_port = (ps != 0) ? ps->s_port : (htons(0xf00));
X		bcopy(ph->h_addr, (char *)&server.sin_addr, ph->h_length);
X
X		if((ssock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
X			cleanup(1, sys_errlist[errno]);
X		if(connect(ssock, &server, sizeof server) < 0) {
X			if(errno == ECONNREFUSED)
X				cleanup(1, "no cubeserver running");
X			cleanup(1, sys_errlist[errno]);
X		}
X	}
X
X	/*
X	**	Make sure non-blocking I/O is turned off.
X	*/
X	(void) ioctl(ssock, FIONBIO, (char *)&off);
X
X	/*
X	**	Allow receiving SIGURG on OOB data.  Ignore SIGALRM too.
X	*/
X	(void) signal(SIGALRM, SIG_IGN);
X	(void) signal(SIGURG,  SIG_IGN);
X	(void) fcntl(ssock, F_SETOWN, getpid());
X
X	/*
X	**	Open a stream so we can do fgets on the socket.
X	*/
X	if((fsock = fdopen(ssock, "r")) == 0)
X		cleanup(1, sys_errlist[errno]);
X}
X
X/*
X**	rd_jmp, rd_timo: handle server socket read timeout
X*/
Xjmp_buf		rd_jmp;
Xstatic int
Xrd_timo()
X{
X	longjmp(rd_jmp, 1);
X}
X
X/*
X**	rdmessage: read a message from the server socket
X**		return the message type number
X*/
Xrdmessage(mesg, size)
Xchar   *mesg;
Xint		size;
X{
X	register int	ntot, n;
X	int				type;
X	int			  (*oldalrm)();
X	fd_set			rdy;
X
X	/*
X	**	Server could time out on system crash or other unrecoverable problem.
X	*/
X	oldalrm = signal(SIGALRM, SIG_IGN);
X	if(setjmp(rd_jmp) != 0) {
X		(void) alarm(0);
X		(void) signal(SIGALRM, oldalrm);
X		sprintf(mesg, "%d %*s", M_ARGE, size-5, "Server may be hung...");
X		return (int)M_ARGE;		/* non-fatal, let player quit with interrupt */
X	}
X	(void) signal(SIGALRM, rd_timo);
X	(void) alarm(3*READTIMO);	/* let's hope nobody takes this long! */
X
X	/*
X	**	Loop here waiting for server socket to come ready.  If
X	**	anything is typed on the terminal, respond by calling flushinput.
X	**	If we get interrupt, service it immediately.
X	*/
X	while(fsock->_cnt <= 0) {	/* XXX: eeek! we peeked in stdio.h */
X		FD_ZERO(&rdy);
X		if(quiet == False)
X			FD_SET(0, &rdy);
X		FD_SET(ssock, &rdy);
X		if(select(ssock+1, &rdy, NOSEL, NOSEL, HANG) > 0) {
X			if(quiet == False && FD_ISSET(0, &rdy))
X				flushinput();
X			if(FD_ISSET(ssock, &rdy))
X				break;
X		}
X		if(gotintr == True)
X			reallyquit();
X	}
X
X	/*
X	**	Read a single line from the socket.
X	*/
X	if(fgets(mesg, size, fsock) == 0) {
X		(void) alarm(0);
X		(void) signal(SIGALRM, oldalrm);
X		sprintf(mesg, "%d %*s", M_DOWN, size-5, "Server closed connection.");
X		return (int)M_DOWN;
X	}
X
X	/*
X	**	Done reading, turn off alarm.
X	*/
X	(void) alarm(0);
X	(void) signal(SIGALRM, oldalrm);
X
X	/*
X	**	Strip control and space characters from end of message.
X	*/
X	ntot = strlen(mesg);
X	for(n = ntot - 1;n >= 0;--n) {
X		if(!iscntrl(mesg[n]) && !isspace(mesg[n]))
X			break;
X		mesg[n] = '\0';
X	}
X
X	/*
X	**	Get the message type from the front of the message.
X	*/
X	if(sscanf(mesg, "%3d", &type) != 1)
X		type = (int)M_BADM;
X
X	return type;
X}
X
X/*
X**	save: save a string in new dynamic memory
X*/
Xstatic char *
Xsave(old)
Xchar   *old;
X{
X	unsigned	len;
X	char	   *new;
X
X	len = strlen(old) + 1;
X	if((new = malloc(len * sizeof *old)) == 0) {
X		fprintf(stderr, "cubes: no memory for saving arguments\n");
X		exit(1);
X	}
X	strcpy(new, old);
X
X	return new;
X}
X
X/*
X**	randname: return a pointer to a random name
X*/
Xstatic char *
Xrandname(len)
Xint		len;
X{
X	DIR			   *dirp;
X	struct direct  *dp;
X	register int	n;
X	static char		name[MAXNAMLEN+1];
X
X	if(len > MAXNAMLEN)
X		len = MAXNAMLEN;
X
X	/*
X	**	First look for a filename in /tmp.
X	*/
X	name[0] = '\0';
X	if(len > 3 && (dirp = opendir("/tmp")) != 0) {
X		while((dp = readdir(dirp)) != 0) {
X			if(dp->d_namlen < 3 || dp->d_namlen > len)
X				continue;
X			(void) strcpy(name, dp->d_name);
X			if(randint(7) == 3)
X				break;
X		}
X		(void) closedir(dirp);
X	}
X	if(name[0] != '\0')
X		return name;
X	
X	/*
X	**	Generate a name with a random function.
X	*/
X	for(n = 0;n < len;++n) {
X		switch(randint(13)) {
X		case 2: case 7:		name[n] = ' '; break;
X		case 3:				name[n] = '/'; break;
X		case 8:				name[n] = '.'; break;
X		case 1: case 4:	    name[n] = "-aeiouyweeeaai"[randint(13)]; break;
X		default:			name[n] = 'a' + randint(26) - 1; break;
X		}
X	}
X	name[n] = '\0';
X
X	return name;
X}
X
X/*
X**	newname: rename this program (apparently, anyway)
X**		This code assumes we can write over av[n] and
X**		hopes that av[0] through av[ac-1] are contiguous.
X*/
Xnewname(ac, av)
Xint				ac;
Xregister char  *av[];
X{
X	register int	n;
X	register char  *name, *fake;
X
X	/*
X	**	Make the argument list into one continuous string.
X	**	Only does this if the arguments seem contiguous.
X	*/
X	for(n = 1;n < ac;++n) {
X		if(*(name = av[n] - 1) == '\0')		/* end of previous arg */
X			*name = ' ';					/* un-terminate it */
X			av[n] = 0;						/* none such, now */
X	}
X
X	/*
X	**	Come up with a suitable replacement argument list.
X	*/
X	name = av[0];
X	switch(n = strlen(name)) {
X	case 1: break;
X	case 2: switch(randint(7)) {
X			case 1: strcpy(name, "vi"); break;
X			case 2: strcpy(name, "rn"); break;
X			case 3: strcpy(name, "ex"); break;
X			case 4: strcpy(name, "bc"); break;
X			default:strcpy(name, "sh"); break;
X			} break;
X	case 3: switch(randint(7)) {
X			case 1: strcpy(name, "ftp"); break;
X			case 2: strcpy(name, "adb"); break;
X			case 3: strcpy(name, "sdb"); break;
X			case 4: strcpy(name, "dbx"); break;
X			default:strcpy(name, "csh"); break;
X			} break;
X	case 4: switch(randint(7)) {
X			case 1: strcpy(name, "mail"); break;
X			case 2: strcpy(name, "xget"); break;
X			case 3: strcpy(name, "view"); break;
X			case 4: strcpy(name, "edit"); break;
X			default:strcpy(name, "lock"); break;
X			} break;
X	case 5: switch(randint(7)) {
X			case 1: strcpy(name, "learn"); break;
X			case 2: strcpy(name, "sh -i"); break;
X			case 3: strcpy(name, "a.out"); break;
X			case 4: strcpy(name, "qcalc"); break;
X			default:strcpy(name, "vnews"); break;
X			} break;
X	case 6: switch(randint(7)) {
X			case 1: strcpy(name, "iostat"); break;
X			case 2: strcpy(name, "vmstat"); break;
X			case 3: strcpy(name, "kermit"); break;
X			case 4: strcpy(name, "script"); break;
X			default:strcpy(name, "telnet"); break;
X			} break;
X	default:switch(randint(7)) {
X			case 1: strncpy(name, "more ", 5); name += 5, n -= 5; break;
X			case 2: strncpy(name, "less ", 5); name += 5, n -= 5; break;
X			case 3: strncpy(name, "sdb ", 4);  name += 4, n -= 4; break;
X			case 4: strncpy(name, "vu ", 3);   name += 3, n -= 3; break;
X			default:strncpy(name, "vi ", 3);   name += 3, n -= 3; break;
X			}
X			fake = randname(n);
X			while(*name != '\0')
X				*name++ = (*fake == '\0') ? ' ' : *fake++;
X			break;
X	}
X
X	/*
X	**	Change remaining arguments to all spaces.
X	**	Only necessary if arguments aren't contiguous.
X	*/
X	for(n = 1;n < ac;++n)
X		if(av[n] != 0)
X			for(name = av[n];*name != '\0';++name)
X				*name = ' ';
X	
X	/*
X	**	Some shells store our name in $_, so check for it
X	**	and if found, replace it with our newly chosen name.
X	*/
X	if((name = getenv("_")) != 0) {
X		fake = av[0];
X		while(*name != '\0')
X			*name++ = (*fake == '\0') ? ' ' : *fake++;
X	}
X}
X
X/*
X**	parentname: change av[0] for the parent to look believable
X*/
Xparentname(av)
Xchar   *av[];
X{
X	int		len	= strlen(av[0]);
X
X	sprintf(av[0], "%-*.*s", len, len, "-sh");
X}
X
X/*
X**	slowstop: give child a chance to reset the terminal on suspend signal
X*/
Xstatic int
Xslowstop(sig)
X{
X#ifndef	sigmask
X#define	sigmask(sig)	(1 << ((sig) - 1))
X#endif	sigmask
X
X	int	mask;
X
X	/*
X	**	Delay should probably depend on ospeed, but it isn't set in parent.
X	*/
X	sleep(1);
X
X	/*
X	**	Reset and unblock sig and "kill" ourselves with it.
X	*/
X	(void) signal(sig, SIG_DFL);
X	mask = sigblock(0);
X	mask &= ~(sigmask(sig));
X	(void) sigsetmask(mask);
X	(void) kill(getpid(), sig);
X	/*
X	**	Suspended here.
X	*/
X	(void) signal(sig, slowstop);
X}
END_OF_FILE
if test 16290 -ne `wc -c <'cubes.c'`; then
    echo shar: \"'cubes.c'\" unpacked with wrong size!
fi
# end of 'cubes.c'
fi
if test -f 'cubestat.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cubestat.c'\"
else
echo shar: Extracting \"'cubestat.c'\" \(4072 characters\)
sed "s/^X//" >'cubestat.c' <<'END_OF_FILE'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)cubestat.c 5.1 (G.M. Paris) 89/01/22";
X#endif	lint
X
X/*
X**
X**	cubes 5.1  Copyright 1989 Gregory M. Paris
X**		Permission granted to redistribute on a no charge basis.
X**		All other rights are reserved.
X**
X*/
X
X#include	<stdio.h>
X#include	<sys/types.h>
X#include	<sys/time.h>
X#include	<sys/socket.h>
X#include	<netinet/in.h>
X#include	<netdb.h>
X#include	<sysexits.h>
X#include	"cubes.h"
X
X#define	EX_CUBES_INPROGRESS	0	/* success */
X#define	EX_CUBES_IDLE		1	/* fail, sort of */
X
Xextern int			optind;
Xextern int			opterr;
Xextern char		   *optarg;
Xextern char		   *getenv();
Xstruct hostent	   *gethostbyname();
Xstruct servent	   *getservbyname();
X
Xmain(ac, av)
Xchar   *av[];
X{
X	int						c;
X	char				   *cubehost	= 0;		/* server hostname */
X	register int			qsock;
X	struct sockaddr_in		server;
X	struct servent		   *ps;
X	struct hostent		   *ph;
X	struct timeval			timo;
X	fd_set					rdy;
X	char					query;
X	char					status[STATLEN];
X	boolean					waitforbeg	= False;
X	boolean					waitforend	= False;
X
X	opterr = 0;
X	while((c = getopt(ac, av, "beh:")) >= 0) {
X		switch(c) {
X		case 'b':
X			waitforbeg = (waitforbeg == True) ? False : True;
X			break;
X		case 'e':
X			waitforend = (waitforend == True) ? False : True;
X			break;
X		case 'h':
X			cubehost = optarg;
X			break;
X		default:
X			fprintf(stderr, "usage -- cubestat [-{be}] [-h host]\n");
X			exit(EX_USAGE);
X		}
X	}
X
X	if(waitforbeg == True && waitforend == True) {
X		fprintf(stderr, "cubestat: can't wait for both begin and end\n");
X		exit(EX_USAGE);
X	}
X
X	if(waitforbeg == True || waitforend == True) {
X		static char	fake[] = "lock -";
X		int			n;
X
X		for(c = 0;c < ac;++c)
X			for(n = 0;av[c][n] != '\0';++n)
X				av[c][n] = (c > 0 || n >= sizeof fake - 1) ? ' ' : fake[n];
X	}
X
X	/*
X	**	If cubehost hasn't been set, check the CUBEHOST environment variable.
X	**	If cubehost is null or not set, use this host.
X	*/
X	if(cubehost == 0)
X		cubehost = getenv("CUBEHOST");
X	if(cubehost == 0 || *cubehost == '\0') {
X		static char		thishost[256];
X
X		(void) gethostname(thishost, sizeof thishost);
X		cubehost = thishost;
X	}
X	if((ph = gethostbyname(cubehost)) == 0) {
X		fprintf(stderr, "cubestat: unknown host `%s'\n", cubehost);
X		exit(EX_NOHOST);
X	}
X
X	/*
X	**	Look up the cubestat service.
X	*/
X	if((ps = getservbyname("cubestat", "udp")) == 0) {
X		fprintf(stderr, "cubestat: no cubestat/udp service listed\n");
X		exit(EX_UNAVAILABLE);
X	}
X
X	/*
X	**	Get a datagram socket then point it at the server.
X	*/
X	if((qsock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
X		perror("cubestat: socket");
X		exit(EX_OSERR);
X	}
X	bzero((char *)&server, sizeof server);
X	server.sin_family = AF_INET;
X	server.sin_port = ps->s_port;
X	bcopy(ph->h_addr, (char *)&server.sin_addr, ph->h_length);
X	if(connect(qsock, &server, sizeof server) < 0) {
X		perror("cubestat: connect");
X		exit(EX_OSERR);
X	}
X
X	/*
X	**	For now, we only know how to ask for a roster.
X	**	Set up for a few tries.  We'll wait a reasonable time before retrying.
X	*/
X	query = Q_ROSTER;
X	timo.tv_sec = 2 * READTIMO;
X	timo.tv_usec = 0;
X	for(c = 0;c < 3 || waitforbeg == True || waitforend == True;++c) {
X		if(send(qsock, &query, sizeof query, 0) < 0) {
X			perror("cubestat: send");
X			exit(EX_IOERR);
X		}
X
X		FD_ZERO(&rdy);
X		FD_SET(qsock, &rdy);
X		if(select(qsock+1, &rdy, NOSEL, NOSEL, &timo) > 0) {
X			if(recv(qsock, status, STATLEN, 0) > 0) {
X				switch(status[0]) {
X				case S_IDLE:
X					if(waitforbeg == False) {
X						fputs(&status[1], stdout);
X						exit(EX_CUBES_IDLE);
X					}
X					if(c % 20 == 0)
X						fputs(&status[1], stdout);
X					sleep(3);
X					break;
X				case S_ROSTER:
X					fputs(&status[1], stdout);
X					if(waitforend == False)
X						exit(EX_CUBES_INPROGRESS);
X					sleep(1);
X					break;
X				case S_UNRECOG:
X					fprintf(stderr,
X						"cubestat: unrecognized query \\%o\n", query);
X					exit(EX_PROTOCOL);
X				default:
X					fprintf(stderr,
X						"cubestat: unrecognized response \\%o\n", status[0]);
X					exit(EX_PROTOCOL);
X				}
X			}
X		}
X	}
X
X	fprintf(stderr, "cubestat: no response from server on %s\n", cubehost);
X	exit(EX_UNAVAILABLE);
X}
END_OF_FILE
if test 4072 -ne `wc -c <'cubestat.c'`; then
    echo shar: \"'cubestat.c'\" unpacked with wrong size!
fi
# end of 'cubestat.c'
fi
if test -f 'risk.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'risk.c'\"
else
echo shar: Extracting \"'risk.c'\" \(16470 characters\)
sed "s/^X//" >'risk.c' <<'END_OF_FILE'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)risk.c 5.1 (G.M. Paris) 89/01/22";
X#endif	lint
X
X/*
X**
X**	cubes 5.1  Copyright 1989 Gregory M. Paris
X**		Permission granted to redistribute on a no charge basis.
X**		All other rights are reserved.
X**
X*/
X
X#include	"cubes.h"
X
Xextern boolean		jokermode;
Xextern int			winscore;
Xextern player		plr[];
Xextern boolean		iscomp();
Xextern boolean		isproxy();
Xextern boolean		iskamelion();
Xextern long			histavgplr();
X
X/*
X**	risk: values derived empirically using the avg program
X**
X**	Yes, you can calculate all this stuff, but what a pain.
X**	In any case, this table is by no means a complete listing
X**	of all possible conditions, probabilities, and expectations.
X**	You could come up with a much more complicated table, but
X**	to what end?  Some of computer players using this table
X**	and quite simple strategies are able to best humans regularly.
X**
X**	Notes:
X**		1. p_aok values for rolling NDICE are nonsense.
X**		2. p_aok values cannot be used with all aces, fives, or jokers
X**		   since they already count as scoring dice.
X**		3. p_any and p_all for rolling 3, 2, and 1 can be averaged.
X**		4. Values for rolling 4 are special, since avg is able to
X**		   detect asmstr in that case.  Use only mix values.
X*/
Xrisk	joker_risktbl[NDICE+1]	= {
X/*	       any       all       aok  mix aces fives jokers	*/
X	{ 0.000000, 0.000000, 0.000000,   0,   0,   0,   0  },	/* roll 0 */
X	{ 0.384615, 0.384615, 0.153846,  23, 406, 215, 715  },	/* roll 1 */
X	{ 0.621656, 0.147983, 0.023669,  46, 105,  76,  99  },	/* roll 2 */
X	{ 0.781163, 0.071125, 0.003641,  79,  86,  83,  83  },	/* roll 3 */
X	{ 0.885755, 0.086400, 0.000560, 145, 145, 145, 145  },	/* roll 4 */
X	{ 0.948547, 0.096702, 0.000086, 269, 269, 269, 269  },	/* roll 5 */
X};
X
Xrisk	std_risktbl[NDICE+1]	= {
X/*	       any       all       aok  mix aces fives jokers	*/
X	{ 0.000000, 0.000000, 0.000000,   0,   0,   0,   0  },	/* roll 0 */
X	{ 0.333333, 0.333333, 0.166667,  25, 442, 233,   0  },	/* roll 1 */
X	{ 0.555513, 0.110736, 0.027778,  50, 119,  84,   0  },	/* roll 2 */
X	{ 0.722309, 0.055524, 0.004630,  86,  95,  91,   0  },	/* roll 3 */
X	{ 0.842288, 0.095481, 0.000772, 161, 161, 161,   0  },	/* roll 4 */
X	{ 0.922813, 0.099667, 0.000129, 309, 309, 309,   0  },	/* roll 5 */
X};
X
X#define	risktbl			(jokermode == True ? joker_risktbl : std_risktbl)
X
X/*
X**	cquery: computer rolling choice
X*/
Xcquery(comp, pd)
Xint			comp;
Xdiceset	   *pd;
X{
X	diceset				modi;		/* dice after re-rolling strategy */
X	diceset				temp;		/* a temporary copy of the dice */
X	int					stay;		/* points if we hold */
X	int					need;		/* points needed beyond kept so far */
X	int					xpct;		/* expectation value of next roll */
X	boolean				canthold;	/* true if can't hold */
X
X	/*
X	**	Fake human-like hesitation for kamelion and proxies.
X	**	Otherwise, just allow time for screen update.
X	*/
X	if(iskamelion(comp) == True || isproxy(comp) == True)
X		hesitate(1800L, 3600L);
X	else
X		hesitate(1500L, 2000L);
X
X	/*
X	**	Mark Rolled dice as Taken, updating the value of d_rolling.
X	*/
X	keepall(pd);
X
X	/*
X	**	Determine any minimum number of points we need above those
X	**	accumulated on previous rolls.  For now, we just worry about
X	XXX	onboard and offboard conditions, though in the future we
X	XXX	could also worry about stopping winners and setting minimums.
X	*/
X	if(plr[comp].p_onboard == False && pd->d_pts_turn < ONBOARD)
X		need = ONBOARD - pd->d_pts_turn;
X	else if(pd->d_pts_turn < OFFBOARD && plr[comp].p_score < winscore) {
X		if(plr[comp].p_score + pd->d_pts_turn >= winscore)
X			need = OFFBOARD - pd->d_pts_turn;
X	} else
X		need = 0;
X
X	/*
X	**	We get a copy of the dice and then use the computer's re-rolling
X	**	strategy on it.  We can only do this once, because some players
X	**	are using a fickle strategy that's different every time.  Set
X	**	this copy of the dice up to roll again.
X	*/
X	modi = *pd;
X	(*plr[comp].p_computer->c_strategy->s_func)(&modi, need);
X	modi.d_again = True;
X
X	/*
X	**	Evaluate the number of points we'd have if we held.
X	**	Set this copy of the dice up to hold.
X	*/
X	pd->d_again = False;
X	temp = *pd;
X	scoredice(&temp);
X	stay = temp.d_pts_turn;
X
X	/*
X	**	Take care of conditions where we can't hold.
X	*/
X	if(plr[comp].p_onboard == False && stay < ONBOARD)
X		canthold = True;
X	else if(stay < OFFBOARD && plr[comp].p_score < winscore) {
X		if(plr[comp].p_score + stay >= winscore)
X			canthold = True;
X	} else
X		canthold = False;
X
X	/*
X	**	If we can't hold, use the diceset with the best expectation value.
X	**	Maybe this decision should be made by the temperament routine,
X	**	but they aren't set up with that assumption.  Still, this should
X	**	be an improvement over the previous situation, where the modified
X	**	diceset was always selected.
X	*/	
X	if(canthold == True) {
X		temp = *pd; 
X		stay = expect(&temp);	/* stay is expectation of original */
X		temp = modi;
X		xpct = expect(&temp);	/* xpct is expectation of modified */
X		if(xpct >= stay)		/* prefer modified version */
X			*pd = modi;			/* use modified diceset */
X		pd->d_again = True;		/* in case it's the orignal */
X		return;
X	}
X
X	/*
X	**	Fake some indecision sometimes.  More rarely, call snoop to
X	**	pretend to be a human interested in rankings.  The randint
X	**	calls below work out to about one snoop per game for kamelion
X	**	and proxies vs. about one snoop per two games for others
X	**	(before checking value of stay, anyway).
X	*/
X	if(iscomp(comp) == False && randint(12) > 9) {
X		int		thresh;
X		switch(modi.d_rolling) {
X		case 1: thresh = P_SMSTR; break;
X		case 2: thresh = P_ASMSTR; break;
X		case 3: thresh = P_3OKMULT * P_ACEMULT; break;
X		case 4: thresh = P_4OKMULT * P_ACEMULT; break;
X		default:thresh = P_AOKMULT * P_ACEMULT; break;
X		}
X		if(stay >= thresh) {
X			hesitate(500L, 2000L);
X			switch(randint(56)) {
X			case 21: if(iskamelion(comp) == True) snoop(comp); break;
X			case 14: if(isproxy(comp) == True) snoop(comp); break;
X			case  7: snoop(comp); break;
X			}
X		}
X	}
X	
X	/*
X	**	Calculate the expectation value of rolling again.
X	*/
X	temp = modi;
X	xpct = expect(&temp);
X
X	/*
X	**	Player's temperament will chose which diceset to use.
X	**	If c_temper->t_func returns True, it means roll again.
X	*/
X	if((*plr[comp].p_computer->c_temper->t_func)(comp,stay,xpct,&modi) == True)
X		*pd = modi;		/* modi is the roll-again diceset */
X}
X
X/*
X**	sameface: all dice showing same face test
X**		if scoring is False, considers only held dice
X**		returns True if considered dice are all comb's
X**		returns False in the case of no comb's or mixed faces
X*/
Xboolean
Xsameface(pd, comb, scoring)
Xdiceset		   *pd;
Xcombination		comb;
Xboolean			scoring;
X{
X	register int		d;
X	register boolean	all;
X	int					face;
X
X	switch(comb) {
X	case Joker:	if(jokermode == False) return False; face = JOKER; break;
X	case Five:	face = FIVE; break;
X	case Ace:	face = ACE; break;
X	default:	face = BADFACE; break;	/* non-scoring face */
X	}
X
X	if(scoring == False) {
X		all = False;
X		for(d = 0;d < NDICE;++d) {
X			if(pd->d_comb[d] == Previous) {
X				if(face == BADFACE) {
X					switch(pd->d_face[d]) {
X					case ACE: case FIVE: case JOKER: return False;
X					default: face = pd->d_face[d]; break;
X					}
X				} else if(pd->d_face[d] != face)
X					return False;
X				all = True;
X			}
X		}
X		return all;
X	}
X
X	all = False;
X	for(d = 0;d < NDICE;++d) {
X		switch(pd->d_comb[d]) {
X		case Previous:
X		case Three_of_a_kind:
X		case Four_of_a_kind:
X			if(face == BADFACE) {	/* comb == Nothing too */
X				switch(pd->d_face[d]) {
X				case ACE: case FIVE: case JOKER: return False;
X				default: face = pd->d_face[d]; break;
X				}
X			} else if(pd->d_face[d] != face)
X				return False;
X			break;
X		case All_of_a_kind:
X			if(comb == Nothing) {
X				switch(pd->d_face[d]) {
X				case ACE: case FIVE: case JOKER: return False;
X				default: return True;
X				}
X			}
X			return (pd->d_face[d] == face) ? True : False;
X		case Ace:		if(comb != Ace) return False; break;
X		case Five:		if(comb != Five) return False; break;
X		case Joker:		if(comb != Joker) return False; break;
X		case Nothing:	continue;
X		default:		return False;	/* scored but mixed faces */
X		}
X		all = True;
X	}
X
X	return all;
X}
X
X/*
X**	expect: return expectation value of rolling dice as they stand
X*/
Xexpect(pd)
Xregister diceset   *pd;
X{
X	int				d;
X	boolean			allaces, allfives, alljokers, allofakind;
X	risk		   *pr;
X	diceset			temp;
X	double			xpct;
X	double			any, all;
X
X	/*
X	**	Get a copy of the dice and forget the location of the original.
X	*/
X	temp = *pd;
X	pd = &temp;
X
X	/*
X	**	Four special conditions: all Aces, all Fives, all Jokers,
X	**	or all of a kind (not in Aces, Fives, or Jokers).
X	*/
X	allaces = allfives = alljokers = allofakind = False;
X	if(pd->d_rolling != 0 && pd->d_rolling != NDICE)
X		if((allaces = sameface(pd, Ace, True)) == False)
X			if((allfives = sameface(pd, Five, True)) == False)
X				if(jokermode == False
X				|| (alljokers = sameface(pd, Joker, True)) == False)
X					allofakind = sameface(pd, Nothing, True);
X
X	/*
X	**	Point to the appropriate risk values.  First get the expected
X	**	value of the next roll, accounting for whether we have all Aces,
X	**	all Fives, all Jokers or a mix.
X	*/
X	pr = &risktbl[pd->d_rolling == 0 ? NDICE : pd->d_rolling];
X	any = pr->r_p_any, all = pr->r_p_all;
X	if(allaces == True)			xpct = pr->r_e_aces;
X	else if(allfives == True)	xpct = pr->r_e_fives;
X	else if(alljokers == True)	xpct = pr->r_e_jokers;
X	else						xpct = pr->r_e_mix;
X
X	scoredice(pd);				/* sets d_pts values */
X
X	/*
X	**	Add additional expectation due to getting All_of_a_kind
X	**	in what normally would be considered non-scoring dice.
X	*/
X	if(allofakind == True) {
X		any += pr->r_p_aok, all += pr->r_p_aok;
X		for(d = 0;d < NDICE;++d) {
X			if(pd->d_comb[d] != Nothing) {
X				xpct += pr->r_p_aok
X					* (P_AOKMULT * pd->d_face[d] - pd->d_pts_dice);
X				break;
X			}
X		}
X	}
X
X#ifdef	ASMSTR
X	/*
X	**	Account for Asm_straight.  There are three basic ways to
X	**	complete an assembled straight.  First is if an ace or five
X	**	is held.  Second is if an ace and five are held.  Last is
X	**	if a small straight is held, of which there are three varieties.
X	*/
X	switch(pd->d_rolling) {
X	case ASMSTR-1:
X		if(allaces == True) {
X			/*
X			**	To assemble here, what would be a Small_straight
X			**	would need to be rolled.  That probability is
X			**	already accounted for, but the expectation value
X			**	of this combination is higher.
X			*/
X			xpct += (jokermode == True) ? 3 : 4;
X		} else if(allfives == True) {
X			/*
X			**	To assemble here, a Small_straight or a 2346 is needed.
X			**	The probability of the first is accounted for, the
X			**	second is not.  Additional expectation from both.
X			*/
X			if(jokermode == True)
X				any += 0.013445, all += 0.013445, xpct += 13;
X			else
X				any += 0.018519, all += 0.018519, xpct += 18;
X		}
X		break;
X	case ASMSTR-2:
X		if(allaces == False && allfives == False) {
X			/*
X			**	To assemble here, we need 234 which is unaccounted for
X			**	and has additional expectation.
X			*/
X			if(jokermode == True)
X				any += 0.021848, all += 0.021848, xpct += 13;
X			else
X				any += 0.027778, all += 0.027778, xpct += 16;
X		}
X		break;
X	case 1:
X		switch(heldsmall(pd)) {
X		case ACE:	/* low die is an ACE */
X			/*
X			**	To assemble here, we need a FIVE.  The probability
X			**	is accounted for, but the expectation is not.
X			*/
X			xpct += (jokermode == True) ? 46 : 50;
X			break;
X		case 2:		/* low die is a deuce */
X			/*
X			**	To assemble here, we need an ACE or a 6.  The
X			**	first is accounted for, the second is not.  Both
X			**	carry additional expectation.
X			*/
X			if(jokermode == True)
X				any += 0.153846, all += 0.153846, xpct += 92;
X			else
X				any += 0.166667, all += 0.166667, xpct += 100;
X			break;
X		case 3:		/* low die is a three */
X			/*
X			**	To assemble here, we need a 2.  The probability
X			**	and additional expectation are unaccounted for.
X			*/
X			if(jokermode == True)
X				any += 0.153846, all += 0.153846, xpct += 53;
X			else
X				any += 0.166667, all += 0.166667, xpct += 58;
X			break;
X		default:	/* not holding small straight */
X			break;
X		}
X		break;
X	}
X#endif	ASMSTR
X
X	/*
X	**	Add the value of keeping what we've got.
X	*/
X	xpct += any * pd->d_pts_turn;
X
X	/*
X	**	Add the value of getting to roll all dice again.
X	**	This is probably the most faulty estimate due to the way
X	**	we make use of r_e_mix and p_nothing * xpct.
X	*/
X	pr = &risktbl[NDICE];
X	xpct = xpct + all * (pr->r_e_mix - (1.0 - pr->r_p_any) * xpct);
X
X	return (int)xpct;
X}
X
X/*
X**	ziprisk: return the probability of rolling nothing
X**		assumes that rolling zero really means rolling all
X*/
Xdouble
Xziprisk(pd)
Xregister diceset   *pd;
X{
X	double			any;
X
X	/*
X	**	Get the probability of anything assuming mixed dice held.
X	*/
X	any = risktbl[pd->d_rolling == 0 ? NDICE : pd->d_rolling].r_p_any;
X
X	/*
X	**	Check for all held/scoring dice having same (usu. non-scoring) face.
X	**	We count both Previous and scoring dice.
X	*/
X	if(pd->d_rolling != 0 && pd->d_rolling != NDICE)
X		if(sameface(pd, Nothing, True) == True)
X			any += risktbl[pd->d_rolling].r_p_aok;
X
X	return 1.0 - any;
X}
X
X/*
X**	mayendearly: return True if the game may end prematurely
X**		(no human player is onboard == True)
X*/
Xboolean
Xmayendearly()
X{
X	register int	c;
X
X	for(c = 0;c < PLAYERS;++c)
X		if(plr[c].p_stat == Active && plr[c].p_onboard == True)
X			return False;
X	
X	return True;
X}
X
X/*
X**	cfirst: hesitation before first roll
X*/
Xcfirst(comp)
Xint		comp;
X{
X	int		high, half;
X	int		winneed, halfneed;
X
X#define	QUIT	(winscore / 4)
X
X	/*
X	**	Fake human-like hesitation for kamelion and proxies.
X	**	Otherwise, just allow time for screen update.
X	*/
X	if(iskamelion(comp) == True || isproxy(comp) == True)
X		hesitate(1800L, 3600L);
X	else
X		hesitate(1500L, 2000L);
X
X	/*
X	**	If this player is not yet on board, and
X	**	if this is not the COMP computer and also not a proxy player, and
X	**	if this player is behind by more than QUIT points, and
X	**	if the game is in little danger of ending prematurely, and
X	**	if this player's score is less than half of its average score, and
X	**	if this player needs more to reach half than the leader needs to win,
X	**	then there is a probability of quitting that is initially 50%
X	*/
X	if(plr[comp].p_onboard == False) {
X		if(iscomp(comp) == False && isproxy(comp) == False) {
X			if((high = highscore(comp, (int *)0)) - plr[comp].p_score > QUIT) {
X				hesitate(250L, 2000L);		/* fake consideration of quit */
X				if(mayendearly() == False) {
X					half = (winscore * histavgplr(comp)) / (2 * WINSCORE);
X					if((halfneed = half - plr[comp].p_score) > 0) {
X						if((winneed = winscore - high) < halfneed) {
X							if(randint(2 * winneed) < halfneed) {
X								oldplayer(comp);
X								return -1;
X							}
X						}
X					}
X				}
X			}
X		}
X	}
X
X	/*
X	**	If this is a proxy computer, sometimes do more hesitating.
X	*/
X	if(isproxy(comp) == True)
X		hesitate(100L, 1500L);
X
X	return 0;
X
X#undef	QUIT
X}
X
X/*
X**	snoop: make it look like a computer player is interested in rankings
X**		It might be better to individualize this by putting calls
X**		to the snooping routines in the temperament routines.  For now,
X**		we'll just make some decisions based on computer temperament.
X*/
Xsnoop(comp)
X{
X	int				r;
X	temper		   *ctemp;
X	extern temper	te_robot, te_antsy, te_tracker, te_goalie;
X	extern int		highscore(), closescore();
X
X	ctemp = plr[comp].p_computer->c_temper;
X
X	if(ctemp == &te_robot)
X		return;
X	
X	if(ctemp == &te_antsy)
X		r = peekhigh(comp, highscore);
X	else if(ctemp == &te_tracker)
X		r = peekhigh(comp, closescore);
X	else if(ctemp == &te_goalie) {
X		r = randint(3) == 1 ? myrank(comp, False) : peekhigh(comp, highscore);
X	} else switch(randint(5)) {
X		case 1: r = aboveme(comp, False); break;
X		case 2: r = belowme(comp, False); break;
X		case 3: r = peekhigh(comp, highscore); break;
X		case 4: r = peekhuman(comp); break;
X		default:r = myrank(comp, False); break;
X	}
X
X	/*
X	**	Pretend to consider the ranking info.
X	*/
X	if(r >= 0)
X		hesitate(1000L, 3000L);
X}
X
X/*
X**	peekhigh: look at the high scorer's history
X**		If there's a tie, don't bother.
X*/
Xpeekhigh(comp, track)
Xint		comp;
Xint	  (*track)();
X{
X	int		highc;
X
X	(void) (*track)(comp, &highc);
X	if(highc < 0)
X		return -1;
X	return plrrank(comp, highc, False);
X}
X
X/*
X**	peekhuman: look at a random human's history
X*/
Xpeekhuman(comp)
X{
X	register int	c, pick;
X	extern int		active, waiting, watching;
X
X	pick = randint(active + waiting + watching);
X
X	for(c = 0;c < PLAYERS;++c) {
X		switch(plr[c].p_stat) {
X		case Active:
X		case Waiting:
X		case Watching:
X			if(--pick == 0)
X				return plrrank(comp, c, False);
X			break;
X		}
X	}
X
X	return -1;	/* huh? */
X}
END_OF_FILE
if test 16470 -ne `wc -c <'risk.c'`; then
    echo shar: \"'risk.c'\" unpacked with wrong size!
fi
# end of 'risk.c'
fi
if test -f 'strategies.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'strategies.c'\"
else
echo shar: Extracting \"'strategies.c'\" \(14014 characters\)
sed "s/^X//" >'strategies.c' <<'END_OF_FILE'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)strategies.c 5.1 (G.M. Paris) 89/01/22";
X#endif	lint
X
X/*
X**
X**	cubes 5.1  Copyright 1989 Gregory M. Paris
X**		Permission granted to redistribute on a no charge basis.
X**		All other rights are reserved.
X**
X*/
X
X#include	<stdio.h>
X#include	<syslog.h>
X#include	"cubes.h"
X
Xextern boolean	jokermode;
Xextern boolean	sameface();
Xextern char	   *malloc();
X
X#define	P_HAND	(NDICE * P_ACE)		/* a threshold */
X
X/*
X**	basic: (formerly) the most basic strategy
X*/
Xstatic
Xbasic(pd, need)
Xdiceset	   *pd;
Xint			need;
X{
X	/*
X	**	The basic strategy doesn't use need.
X	*/
X#ifdef	lint
X	need = need;
X#endif	lint
X
X	/*
X	**	Respect a complete dice set.
X	*/
X	if(pd->d_rolling == 0)
X		return;
X
X	/*
X	**	If we're one away from All_of_a_kind, we're done.
X	*/
X	if(pd->d_rolling == 1) {
X		if(sameface(pd, Ace, True) == True)
X			return;
X		if(sameface(pd, Five, True) == True)
X			return;
X		if(jokermode == True && sameface(pd, Joker, True) == True)
X			return;
X	}
X
X	/*
X	**	Discard Jokers and Fives.
X	*/
X	if(jokermode == True)
X		nojokers(pd);
X	nofives(pd);
X}
X
X/*
X**	merrier: the more dice to throw, the merrier
X*/
Xstatic
Xmerrier(pd, need)
Xdiceset	   *pd;
Xint			need;
X{
X	diceset	temp;
X
X	/*
X	**	The merrier strategy doesn't use need.
X	*/
X#ifdef	lint
X	need = need;
X#endif	lint
X
X	/*
X	**	Respect a complete dice set.
X	*/
X	if(pd->d_rolling == 0)
X		return;
X
X	/*
X	**	Discard Jokers, Fives, and Aces if the result is three or more dice.
X	*/
X	temp = *pd;
X	if(jokermode == True)
X		nojokers(&temp);
X	nofives(&temp);
X	noaces(&temp);
X	if(temp.d_rolling >= THREE)
X		*pd = temp;
X}
X
X/*
X**	respect: Respect the power of the Ace.
X*/
Xstatic
Xrespect(pd, need)
Xdiceset	   *pd;
Xint			need;
X{
X	diceset	temp;
X
X	/*
X	**	We've completed all dice.  Try discarding Jokers and Fives.
X	**	If the result is one die to roll and all Aces showing,
X	**	then do it -- going for All_of_a_kind in Aces.
X	*/
X	if(pd->d_rolling == 0) {
X		if(need > 0 && need < P_ACEMULT * P_AOKMULT)
X			return;
X		if(pd->d_pts_turn >= P_HAND)
X			return;
X		temp = *pd;
X		if(jokermode == True)
X			nojokers(&temp);
X		nofives(&temp);
X		if(temp.d_rolling == 1 && sameface(&temp, Ace, True) == True)
X			*pd = temp;
X		return;
X	}
X
X	/*
X	**	Don't discard Aces if we're one away from All_of_a_kind.
X	*/
X	if(pd->d_rolling == 1)
X		if(sameface(pd, Ace, True) == True)
X			return;
X
X	/*
X	**	If we've got some points accumulated, we must be a bit desperate
X	**	to want to roll again.  Improve our chances of getting something
X	**	by scrapping junky Small_straights, freeing an Ace.
X	*/
X	if(pd->d_best == Small_straight)
X		if(need > P_SMSTR || (need == 0 && pd->d_pts_turn > P_SMSTR))
X			noacesmall(pd);
X
X	/*
X	**	Get rid of 3o'kind in deuces.  Scrap Jokers and Fives.
X	*/
X	if(pd->d_best == Three_of_a_kind)
X		no3deuce(pd);
X	if(jokermode == True)
X		nojokers(pd);
X	nofives(pd);
X
X	/*
X	**	Discard Aces if we end up with three or more dice.
X	*/
X	temp = *pd;
X	noaces(&temp);
X	if(temp.d_rolling >= THREE)
X		*pd = temp;
X}
X
X/*
X**	twohater: doesn't like 3o'kind in twos
X*/
Xstatic
Xtwohater(pd, need)
Xdiceset	   *pd;
Xint			need;
X{
X	/*
X	**	The twohater strategy doesn't use need.
X	*/
X#ifdef	lint
X	need = need;
X#endif	lint
X
X	/*
X	**	Respect completed dice set.
X	*/
X	if(pd->d_rolling == 0)
X		return;
X
X	/*
X	**	Hates Jokers even more than 3o'kind in twos.
X	*/
X	if(jokermode == True)
X		nojokers(pd);
X
X	/*
X	**	Prefers Fives and Aces over 3o'kind in twos.
X	*/
X	if(pd->d_best == Three_of_a_kind)
X		no3deuce(pd);
X
X	/*
X	**	Discard Fives, but only discard Aces if
X	**	we're already rolling more than one die.
X	*/
X	nofives(pd);
X	if(pd->d_rolling > 1)
X		noaces(pd);
X}
X
X/*
X**	greedy: Tries for All_of_a_kind in Aces, Fives, and Jokers.
X*/
Xstatic
Xgreedy(pd, need)
Xdiceset	   *pd;
Xint			need;
X{
X	diceset	temp;
X	int		d, held, jokers;
X
X	/*
X	**	We've completed all dice.  If a single discard can get us
X	**	closer to All_of_a_kind in Aces, Fives or Jokers, do it.
X	*/
X	if(pd->d_rolling == 0) {
X		if(pd->d_pts_turn >= P_HAND)
X			return;
X
X		/*
X		**	Discard Fives and Aces.  Check for all Jokers.
X		*/
X		if(jokermode == True) {
X			if(need > 0 && need < P_JOKERMULT * P_AOKMULT)
X				return;
X			temp = *pd;
X			nofives(&temp);
X			noaces(&temp);
X			if(temp.d_rolling == 1 && sameface(&temp, Joker, True) == True) {
X				*pd = temp;
X				return;
X			}
X		}
X
X		/*
X		**	Discard Jokers and Fives.  Check for all Aces.
X		*/
X		if(need > 0 && need < P_ACEMULT * P_AOKMULT)
X			return;
X		temp = *pd;
X		if(jokermode == True)
X			nojokers(&temp);
X		nofives(&temp);
X		if(temp.d_rolling == 1 && sameface(&temp, Ace, True) == True) {
X			*pd = temp;
X			return;
X		}
X
X		/*
X		**	Discard Jokers and Aces.  Check for all Fives.
X		*/
X		if(need > 0 && need < FIVE * P_AOKMULT)
X			return;
X		temp = *pd;
X		if(jokermode == True)
X			nojokers(&temp);
X		noaces(&temp);
X		if(temp.d_rolling == 1 && sameface(&temp, Five, True) == True) {
X			*pd = temp;
X			return;
X		}
X
X		return;
X	}
X
X	/*
X	**	On the first roll, if we have the choice between holding Jokers
X	**	and Fives, choose the Jokers -- going for AOK in Jokers.
X	*/
X	if(jokermode == True) {
X		if(need == 0 || need >= P_JOKERMULT * P_AOKMULT) {
X			if(pd->d_pts_turn == 0 && pd->d_best == Five) {
X				held = jokers = 0;
X				for(d = 0;d < NDICE;++d) {
X					switch(pd->d_comb[d]) {
X					case Previous:	++held; break;
X					case Joker:		++jokers; break;
X					}
X				}
X				if(held == 0 && jokers > 0)
X					nofives(pd);
X				return;
X			}
X		}
X	}
X
X	/*
X	**	Small_straights make getting 5o'kind difficult, but if
X	**	ASMSTR is defined, they can be converted to Straights,
X	**	giving us another chance with all dice.  The Small_straight
X	**	containing an Ace is the most difficult to convert.
X	**	If no ASMSTR, discard all Small_straights, otherwise
X	**	discard only the tough kind.
X	*/
X	if(pd->d_best == Small_straight) {
X#ifdef	ASMSTR
X		noacesmall(pd);		/* this kind too hard to convert */
X#else	ASMSTR
X		nosmall(pd);		/* don't like any Small_straights */
X#endif	ASMSTR
X		return;
X	}
X
X	/*
X	**	Try to get all Aces showing.  If we can, we're done.
X	*/
X	temp = *pd;
X	if(temp.d_best == Three_of_a_kind) {
X		no3three(&temp);
X		if(temp.d_best == Three_of_a_kind)
X			no3deuce(&temp);
X	}
X	nofives(&temp);
X	if(jokermode == True)
X		nojokers(&temp);
X	if(sameface(&temp, Ace, True) == True) {
X		*pd = temp;
X		return;
X	}
X
X	/*
X	**	Try to get all Fives showing.  If we can, we're done.
X	*/
X	if(need == 0 || need >= FIVE * P_AOKMULT) {
X		temp = *pd;
X		if(temp.d_best == Three_of_a_kind) {
X			no3three(&temp);
X			if(temp.d_best == Three_of_a_kind)
X				no3deuce(&temp);
X		}
X		noaces(&temp);
X		if(jokermode == True)
X			nojokers(&temp);
X		if(sameface(&temp, Five, True) == True) {
X			*pd = temp;
X			return;
X		}
X	}
X
X	/*
X	**	Try to get all Jokers showing.  If we can, we're done.
X	*/
X	if(jokermode == True) {
X		if(need == 0 || need >= P_JOKERMULT * P_AOKMULT) {
X			temp = *pd;
X			if(temp.d_best == Three_of_a_kind) {
X				no3three(&temp);
X				if(temp.d_best == Three_of_a_kind)
X					no3deuce(&temp);
X			}
X			noaces(&temp);
X			nofives(&temp);
X			if(sameface(&temp, Joker, True) == True) {
X				*pd = temp;
X				return;
X			}
X		}
X	}
X
X	/*
X	**	Discard Jokers and Fives.
X	**	Discard Aces if the result is three or more dice.
X	*/
X	if(jokermode == True)
X		nojokers(pd);
X	nofives(pd);
X	temp = *pd;
X	noaces(&temp);
X	if(temp.d_rolling >= THREE)
X		*pd = temp;
X}
X
X/*
X**	picky: be picky about what we'll keep
X*/
Xstatic
Xpicky(pd, need)
Xdiceset	   *pd;
Xint			need;
X{
X	diceset	temp;
X
X	/*
X	**	Respect a completed dice set.
X	*/
X	if(pd->d_rolling == 0)
X		return;
X
X	/*
X	**	Don't like 3o'kind in twos or threes.
X	*/
X	if(pd->d_best == Three_of_a_kind) {
X		if(need == 0 || need > THREE * P_3OKMULT) {
X			no3three(pd);
X			if(pd->d_best == Three_of_a_kind)
X				if(need == 0 || need > DEUCE * P_3OKMULT)
X					no3deuce(pd);
X		}
X	}
X
X	/*
X	**	We've got held Aces, discard Jokers and Fives.
X	**	If not all Aces showing, discard Aces too.
X	*/
X	if(need == 0 || need >= P_ACEMULT * P_AOKMULT) {
X		if(sameface(pd, Ace, False) == True) {
X			if(jokermode == True)
X				nojokers(pd);
X			nofives(pd);
X			if(sameface(pd, Ace, True) == False)
X				noaces(pd);
X			return;
X		}
X	}
X	
X	/*
X	**	We've got held Fives, discard Jokers and Aces.
X	**	If not all Fives showing, discard Fives too.
X	*/
X	if(need == 0 || need >= FIVE * P_AOKMULT) {
X		if(sameface(pd, Five, False) == True) {
X			if(jokermode == True)
X				nojokers(pd);
X			noaces(pd);
X			if(sameface(pd, Five, True) == False)
X				nofives(pd);
X			return;
X		}
X	}
X
X	/*
X	**	We've got held Jokers, discard Fives and Aces.
X	**	If not all Jokers showing, discard Jokers too.
X	*/
X	if(jokermode == True) {
X		if(need == 0 || need >= P_JOKERMULT * P_AOKMULT) {
X			if(sameface(pd, Joker, False) == True) {
X				nofives(pd);
X				noaces(pd);
X				if(sameface(pd, Joker, True) == False)
X					nojokers(pd);
X				return;
X			}
X		}
X	}
X	
X	/*
X	**	We're holding a mix or nothing.  Discard Jokers.
X	**	Discard Fives and Aces if the result is three or more dice.
X	*/
X	if(jokermode == True)
X		nojokers(pd);
X	temp = *pd;
X	nofives(&temp);
X	noaces(&temp);
X	if(temp.d_rolling >= THREE)
X		*pd = temp;
X}
X
X/*
X**	finicky: a pickier picky
X*/
Xstatic
Xfinicky(pd, need)
Xdiceset	   *pd;
Xint			need;
X{
X	diceset	temp;
X
X	/*
X	**	Respect a completed set of dice.
X	*/
X	if(pd->d_rolling == 0)
X		return;
X
X	/*
X	**	Discard three twos and Small_straights containing an Ace.
X	*/
X	if(pd->d_best == Three_of_a_kind)
X		if(need == 0 || need > DEUCE * P_3OKMULT)
X			no3deuce(pd);
X	if(pd->d_best == Small_straight)
X		if(need == 0 || need > P_SMSTR)
X			noacesmall(pd);
X	
X	/*
X	**	We've held all Aces.  Discard Jokers and Fives.
X	**	If rolling more than one die, discard Aces too.
X	*/
X	if(sameface(pd, Ace, False) == True) {
X		if(jokermode == True)
X			nojokers(pd);
X		nofives(pd);
X		if(pd->d_rolling > 1)
X			noaces(pd);
X		return;
X	}
X	
X	/*
X	**	We've held all Fives.  Discard Jokers and Aces.
X	**	If rolling more than one die, discard Fives too.
X	*/
X	if(need == 0 || need >= FIVE * P_AOKMULT) {
X		if(sameface(pd, Five, False) == True) {
X			if(jokermode == True)
X				nojokers(pd);
X			noaces(pd);
X			if(pd->d_rolling > 1)
X				nofives(pd);
X			return;
X		}
X	}
X	
X	/*
X	**	We've held all Jokers.  Discard Fives and Aces.
X	**	If we're rolling more than one die, discard Jokers too.
X	**	Else, if it will give us three dice, discard Jokers.
X	*/
X	if(jokermode == True) {
X		if(need == 0 || need >= P_JOKERMULT * P_AOKMULT) {
X			if(sameface(pd, Joker, False) == True) {
X				nofives(pd);
X				noaces(pd);
X				if(pd->d_rolling > 1)
X					nojokers(pd);
X				else {
X					temp = *pd;
X					nojokers(&temp);
X					if(temp.d_rolling >= THREE)
X						*pd = temp;
X				}
X				return;
X			}
X		}
X	}
X	
X	/*
X	**	Holding a mix or nothing.  Discard Jokers and Fives.
X	**	If we're rolling more than one, discard Aces too.
X	**	Else, if we can get three dice, discard Aces.
X	**	Else, discard 3o'kind in threes.
X	*/
X	if(jokermode == True)
X		nojokers(pd);
X	nofives(pd);
X	if(pd->d_rolling > 1)
X		noaces(pd);
X	else {
X		temp = *pd;
X		noaces(&temp);
X		if(temp.d_rolling >= THREE)
X			*pd = temp;
X		else if(pd->d_best == Three_of_a_kind)
X			no3three(pd);
X	}
X}
X
X/*
X**	holdall: hold all scoring dice
XXXX:	Intended for choosy selection only -- not a valid strategy.
X*/
Xstatic
Xholdall(pd, need)
Xdiceset	   *pd;
Xint			need;
X{
X#ifdef	lint
X	pd = pd;
X	need = need;
X#endif	lint
X}
X
X/*
X**	tossall: always throw maximum number of dice
XXXX:	Intended for choosy selection only -- not a valid strategy.
X*/
Xstatic
Xtossall(pd, need)
Xdiceset	   *pd;
Xint			need;
X{
X#ifdef	lint
X	need = need;
X#endif	lint
X
X	/*
X	**	Discard Small_straights, all Three_of_a_kinds, Jokers, Fives, and Aces.
X	**	We never honor a complete dice set!
X	*/
X	if(pd->d_best == Small_straight)
X		nosmall(pd);
X	if(pd->d_best == Three_of_a_kind)
X		no3any(pd);
X	if(jokermode == True)
X		nojokers(pd);
X	nofives(pd);
X	noaces(pd);
X}
X
X/*
X**	choosy: try all known strategies then pick the one
X**		with the best expectation for this diceset
X*/
Xstatic
Xchoosy(pd, need)
Xdiceset	   *pd;
Xint			need;
X{
X	register int		n, best, maxx;
X	diceset				temp;
X	static int		   *xtbl	= 0;
X	extern int			strategies;
X	extern strategy	   *strattbl[];
X
X	if(xtbl == 0) {
X		xtbl = (int *)malloc((unsigned)(strategies * sizeof *xtbl));
X		if(xtbl == 0) {
X			syslog(LOG_WARNING, "choosy: no memory for xtbl");
X			fickle(pd, need);	/* emergency backup! */
X			return;
X		}
X	}
X
X	for(n = 0;n < strategies - 1;++n) {	/* fickle is last */
X		if(strattbl[n]->s_func == choosy) {
X			*(xtbl+n) = -WINSCORE;
X			continue;
X		}
X		temp = *pd;
X		(*strattbl[n]->s_func)(&temp, need);
X		*(xtbl+n) = expect(&temp);
X	}
X
X	maxx = *(xtbl+(best=0));
X	for(n = 1;n < strategies - 1;++n)	/* fickle is last */
X		if(*(xtbl+n) > maxx)
X			maxx = *(xtbl+(best=n));
X
X	(*strattbl[best]->s_func)(pd, need);
X}
X
X/*
X**	kamelion: should never be called
X*/
Xstatic
Xkamelion(pd, need)
Xdiceset	   *pd;
Xint			need;
X{
X	syslog(LOG_WARNING, "kamelion strategy called!");
X	choosy(pd, need);
X}
X
X/*
X**	fickle: pick another strategy randomly!
X**		intended for proxy players only
X*/
Xstatic
Xfickle(pd, need)
Xdiceset	   *pd;
Xint			need;
X{
X	int					n;
X	extern int			strategies;
X	extern strategy	   *strattbl[];
X
X	hesitate(100L, 1000L);
X	n = randint(strategies - 1) - 1;	/* this one is last -- don't pick it */
X	(*strattbl[n]->s_func)(pd, need);
X}
X
X/*
X**	strattbl: table listing available strategies
X*/
X
Xstrategy		st_basic	= { basic,		"basic" };
Xstrategy		st_merrier	= { merrier,	"merrier" };
Xstrategy		st_respect	= { respect,	"respect" };
Xstrategy		st_twohater	= { twohater,	"twohater" };
Xstrategy		st_greedy	= { greedy,		"greedy" };
Xstrategy		st_picky	= { picky,		"picky" };
Xstrategy		st_finicky	= { finicky,	"finicky" };
Xstatic strategy	st_holdall	= { holdall,	"holdall" };	/* table only */
Xstatic strategy	st_tossall	= { tossall,	"tossall" };	/* table only */
Xstrategy		st_choosy	= { choosy,		"choosy" };
Xstrategy		st_fickle	= { fickle,		"fickle" };
Xstrategy		st_kamelion	= { kamelion,	"kamelion" };	/* semi-dummy */
X
Xstrategy   *strattbl[]	= {
X	&st_basic, &st_merrier, &st_respect, &st_twohater,
X	&st_greedy, &st_picky, &st_finicky,
X	&st_holdall, &st_tossall, &st_choosy,
X	&st_fickle		/* st_fickle must be last */
X};
X
Xint	strategies	= sizeof strattbl / sizeof strattbl[0];
END_OF_FILE
if test 14014 -ne `wc -c <'strategies.c'`; then
    echo shar: \"'strategies.c'\" unpacked with wrong size!
fi
# end of 'strategies.c'
fi
echo shar: End of archive 7 \(of 8\).
cp /dev/null ark7isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 8 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