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

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

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

#! /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 3 (of 3)."
# Contents:  Makefile cuberank.tmplt cubes.c cubes.h history.c random.c
#   risk.c strategies.c tactics.c tempers.c
# Wrapped by billr@saab on Tue Jun  7 16:36:07 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f Makefile -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"Makefile\"
else
echo shar: Extracting \"Makefile\" \(1817 characters\)
sed "s/^X//" >Makefile <<'END_OF_Makefile'
X#! /bin/make -f
X# vi:set sw=4 ts=4:
X
XOWNER=		gdaemon
XGAMEDIR=	/usr/games
XGLIBDIR=	$(GAMEDIR)/lib
XPATHNAME=	$(GLIBDIR)/cubeserver
XLOGFILE=	$(GLIBDIR)/cubes.log
XHISTFILE=	$(GLIBDIR)/cubes.hist
XMANPAGE=	/usr/man/man6/cubes.6
XCFLAGS=		-OG
XLDFLAGS=
XSRVFLAGS=	$(CFLAGS) -DPATHNAME=\"$(PATHNAME)\" -DLOGFILE=\"$(LOGFILE)\"
XHISTFLAGS=	$(CFLAGS) -DHISTFILE=\"$(HISTFILE)\"
XLIBS=		-lcurses -ltermcap
X
XSRVOBJS=	cubeserver.o history.o turn.o risk.o \
X			tempers.o strategies.o tactics.o dieopts.o random.o
XCLIOBJS=	cubes.o actions.o screen.o random.o
XAVGOBJS=	avg.o dieopts.o random.o
X
Xall:		cubeserver cubes cuberank avg
X
Xinstall:	all files
X	install -c -m 4700 -o $(OWNER) cubeserver $(GLIBDIR)
X	install -c -m  711 -o $(OWNER) cubes      $(GAMEDIR)
X	install -c -m  755 -o $(OWNER) cuberank   $(GAMEDIR)
X
Xcubeserver:	$(SRVOBJS)
X	cc $(LDFLAGS) -o cubeserver $(SRVOBJS) $(LIBS)
X
Xcubes:	$(CLIOBJS)
X	cc $(LDFLAGS) -o cubes $(CLIOBJS) $(LIBS)
X
Xavg:	$(AVGOBJS)
X	cc $(LDFLAGS) -o avg $(AVGOBJS) $(LIBS)
X
Xcuberank:	Makefile cuberank.tmplt
X	sed -e "s;%HISTFILE;$(HISTFILE);g" cuberank.tmplt > cuberank
X	chmod 700 cuberank
X
Xcubeserver.o:	cubeserver.c cubes.h Makefile
X	cc $(SRVFLAGS) -c cubeserver.c
X
Xhistory.o:	history.c cubes.h Makefile
X	cc $(HISTFLAGS) -c history.c
X
X$(SRVOBJS):	cubes.h
X$(CLIOBJS):	cubes.h
X$(AVGOBJS):	cubes.h
X
Xfiles:	$(LOGFILE) $(HISTFILE) $(MANPAGE)
X
X$(LOGFILE):		/dev/null
X	cp /dev/null $(LOGFILE)
X	chown $(OWNER) $(LOGFILE)
X	chmod 644 $(LOGFILE)
X
X$(HISTFILE):	/dev/null
X	-if test ! -f $(HISTFILE);      \
X	then cp /dev/null $(HISTFILE);  \
X		chown $(OWNER) $(HISTFILE); \
X		chmod 644 $(HISTFILE);      \
X	fi
X
X$(MANPAGE):	cubes.6
X	cp cubes.6 $(MANPAGE)
X	chmod 644 $(MANPAGE)
X
Xclean:
X	rm -f *.o cubes cubeserver cuberank avg
X
Xdistribution:
X	makekit -p -s60k -ncubes README cubes.6 Makefile cubes.h *.c cuberank.tmplt
END_OF_Makefile
if test 1817 -ne `wc -c <Makefile`; then
    echo shar: \"Makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f cuberank.tmplt -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"cuberank.tmplt\"
else
echo shar: Extracting \"cuberank.tmplt\" \(868 characters\)
sed "s/^X//" >cuberank.tmplt <<'END_OF_cuberank.tmplt'
X#! /bin/sh
X#
X# cuberank: display cubes player rankings
X#
X# Note that this script uses the same weighting scheme as found in history.c.
X# If that comparison function is changed, this should be changed too.
X#
Xawk '
X{
X	id="";
X	for(n=5;n <= NF;++n)
X		id = id $n " ";
X	len = length(id);
X	if(len > maxlen)
X		maxlen = len;
X	g[id] = $1;
X	w[id] = $2 / $1;
X	p[id] = $3;
X	t[id] = $4 / $1
X	a[id] = p[id] / g[id];
X	W[id] = (1000 * $2 + $3 + $4) / $1;
X	++players;
X}
XEND {
X	fmt = "%2s %-" maxlen "s %4s %6s %6s %6s %6s\n";
X	printf(fmt, "##", "Player", "GP", "WinRt", "AvGmPt", "AvTnPt", "WtPts");
X	fmt = "%2d %-" maxlen "s %4d %6.3f %6d %6d %6d\n";
X
X	rnk = 1;
X	for(n = 0;n < players;++n) {
X		bW = -1;
X		for(id in W) {
X			if(W[id] > bW) {
X				bid = id;
X				bW = W[id];
X			}
X		}
X		printf(fmt, rnk, bid, g[bid], w[bid], a[bid], t[bid], W[bid]);
X		W[bid] = -1;
X		++rnk;
X	}
X}
X' %HISTFILE
END_OF_cuberank.tmplt
if test 868 -ne `wc -c <cuberank.tmplt`; then
    echo shar: \"cuberank.tmplt\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f cubes.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"cubes.c\"
else
echo shar: Extracting \"cubes.c\" \(5075 characters\)
sed "s/^X//" >cubes.c <<'END_OF_cubes.c'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)cubes.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif	lint
X
X#include	<stdio.h>
X#include	<strings.h>
X#include	<ctype.h>
X#include	<signal.h>
X#include	<errno.h>
X#include	<sys/types.h>
X#include	<sys/time.h>
X#include	<sys/file.h>
X#include	<sys/socket.h>
X#include	<sys/ioctl.h>
X#include	<netdb.h>
X#include	<netinet/in.h>
X#include	"cubes.h"
X
Xchar			   *pname;				/* player name */
Xchar			   *shost	= 0;		/* server hostname */
Xint					ssock	= -1;		/* server socket */
Xboolean				active	= True;		/* True while active */
Xboolean				gotintr	= False;	/* True if interrupt received */
Xextern graphtype	graphics;			/* graphics type */
X
Xextern int			errno;
Xextern int			optind;
Xextern int			opterr;
Xextern char		   *optarg;
Xextern char		   *getenv();
X
Xmain(ac, av)
Xchar   *av[];
X{
X	int				c;
X	register int	type;
X	char			msgbuf[BUFSIZ];
X
X	newname(av[0]);
X
X	opterr = 0;
X	while((c = getopt(ac, av, "g:h:n:")) >= 0) {
X		switch(c) {
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			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			shost = optarg;
X			break;
X		case 'n':	/* set player name */
X			pname = optarg;
X			break;
X		default:
X			fprintf(stderr, "usage -- cubes [-g type] [-h host] [-n name]\n");
X			exit(1);
X		}
X	}
X
X	if(pname == 0)
X		getplrname();
X	if(opensocktoserv() < 0)
X		exit(1);
X
X	startup();
X
X	while(active == True) {
X		if((type = rdmessage(msgbuf, sizeof msgbuf)) < 0) {
X			fprintf(stderr, "cubes: error reading message from server\n");
X			active = False;
X			break;
X		}
X		if(action(type, msgbuf) < 0) {
X			fprintf(stderr, "cubes: action type %d failed\n", type);
X			active = False;
X			break;
X		}
X		if(gotintr == True)
X			reallyquit();
X	}
X	
X	cleanup(1);
X}
X
X/*
X**	getplrname: get player name
X*/
Xgetplrname()
X{
X	if((pname = getenv("CUBENAME")) == 0) {
X		if((pname = getenv("USER")) == 0) {
X			static char	namebuf[32];
X			sprintf(namebuf, "Cuber#%d", getuid());
X			pname = namebuf;
X		}
X	}
X}
X
X/*
X**	opensocktoserv: open connection to server
X*/
Xopensocktoserv()
X{
X	char				thishost[256];
X	struct sockaddr_in	server;
X	struct hostent	   *ph;
X	struct servent	   *ps;
X	int					off	= 0;
X
X	if(shost == 0) {
X		(void) gethostname(thishost, sizeof thishost);
X		shost = thishost;
X	}
X
X	if((ph = gethostbyname(shost)) == 0) {
X		fprintf(stderr, "cubes: can't find `%s' in host table\n", shost);
X		return (ssock = -1);
X	}
X	if((ps = getservbyname("cube", "tcp")) == 0) {
X		fprintf(stderr, "cubes: no cube service listed on this system\n");
X		return (ssock = -1);
X	}
X
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
X	if((ssock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
X		perror("cubes: socket");
X		return (ssock = -1);
X	}
X	if(connect(ssock, &server, sizeof server) < 0) {
X		if(errno == ECONNREFUSED)
X			fprintf(stderr, "cubes: no cube server running on %s\n", shost);
X		else
X			perror("cubes: connect");
X		(void) close(ssock);
X		return (ssock = -1);
X	}
X	(void) ioctl(ssock, FIONBIO, (char *)&off);
X
X	/*
X	**	Allow receiving SIGURG on OOB data.
X	*/
X	(void) signal(SIGURG, SIG_IGN);
X	(void) fcntl(ssock, F_SETOWN, getpid());
X
X	return 0;
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
X	/*
X	**	Read one character at a time from the socket
X	**	until we get a newline.
X	*/
X	for(ntot = 0;;) {
X		switch(read(ssock, mesg+ntot, 1)) {
X		case -1:
X			perror("cubes: read");
X			return -1;
X		case 0:
X			sprintf(mesg, "%d %*s",
X				M_DOWN, size-5, "Server closed connection.");
X			return M_DOWN;
X		}
X		if(mesg[ntot] == '\n') {
X			mesg[ntot] = '\0';	/* zaps newline */
X			break;
X		}
X		if(++ntot >= size)
X			--ntot;
X	}
X
X	/*
X	**	Strip control and space characters from end of message.
X	*/
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 = M_BADM;
X
X	return type;
X}
X
X/*
X**	newname: rename this program (apparently, anyway)
X*/
Xnewname(name)
Xregister char  *name;
X{
X	register int	len;
X
X	if((len = strlen(name)) < 4)
X		strncpy(name, "vu  ", len);
X	else if(len == 4)
X		strcpy(name, "more");
X	else if(len == 5)
X		strcpy(name, "qcalc");
X	else {
X		irandom();
X		switch(dieroll(5)) {
X		case 1: case 3:		strncpy(name, "less ", 5); name += 5; break;
X		default:			strncpy(name, "vi ", 3); name += 3; break;
X		}
X		while(*name != '\0') {
X			switch(dieroll(13)) {
X			case 2: case 7:		*name++ = ' '; break;
X			case 3:				*name++ = '/'; break;
X			case 8:				*name++ = '.'; break;
X			case 1: case 4:	    *name++ = "-aeiouyweeeaai"[dieroll(13)]; break;
X			default:			*name++ = 'a' + dieroll(26) - 1; break;
X			}
X		}
X	}
X}
END_OF_cubes.c
if test 5075 -ne `wc -c <cubes.c`; then
    echo shar: \"cubes.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f cubes.h -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"cubes.h\"
else
echo shar: Extracting \"cubes.h\" \(6239 characters\)
sed "s/^X//" >cubes.h <<'END_OF_cubes.h'
X/* vi:set sw=4 ts=4: */
X/*
X**	sccsid: @(#)cubes.h 1.1 6/2/88 Copyright 1988 Gregory M. Paris
X*/
X
X/*
X**	If you don't want Four_of_a_kind or Small_straight, then comment these out.
X**	Unfortunately, you'll have to recalculate the values in risktbl[] if you do.
X*/
X#define	FOUR		4		/* how many in a four of a kind (if defined) */
X#define	SMSTR		4		/* number of dice in small straight (if def) */
X
X/*
X**	Although you could change any of the following parameters, the nature of
X**	the game would be altered.  Also, some things like 5o'kind are hard coded
X**	in places and would not change automatically.  Best to leave be.
X*/
X#define	SIDES		6		/* six sided dice */
X#define	NDICE		5		/* number of dice in set */
X#define	ONBOARD		500		/* turn score needed to get on scoreboard */
X#define	OFFBOARD	500		/* turn score needed to cross WINSCORE thresh */
X#define	WINSCORE	10000	/* minimum score needed to win */
X#define	WINMARGIN	250		/* winner must best others by this margin */
X
X/*
X**	These values are the basis for scoring.  Again, if you change them, the
X**	play of the game will be altered.  Leave them as they are.
X*/
X#define	P_ACEMULT	10		/* ace is worth disproportionate amount */
X#define	P_AOKMULT	300		/* multiplier for all of a kind */
X#define	P_STRAIGHT	1500	/* points for straight */
X#define	P_4OKMULT	200		/* multiplier for three of a kind */
X#define	P_SMSTR		400		/* points for a small straight */
X#define	P_3OKMULT	100		/* multiplier for three of a kind */
X#define	P_ACE		100		/* points for single ace */
X#define	P_FIVE		50		/* points for single five */
X
X/*
X**	The following defines are just symbolic values for numbers.
X*/
X#define	BADFACE		0		/* impossible die face */
X#define	ACE			1		/* an ACE is a one, a scoring die */
X#define	DEUCE		2		/* an DEUCE is a two, the most worthless die */
X#define	THREE		3		/* how many in a Three_of_a_kind or the face name */
X#define	FIVE		5		/* FIVE is five, a scoring die */
X#define	MESGLEN		128		/* length of message buffer */
X#define	NAMELEN		64		/* player name buffer length */
X#define	IDLEN		64		/* player identity buffer length */
X#define	COMP		0		/* computer starts as player zero */
X#define	PLAYERS		12		/* max players (typical screen length limits) */
X#define	WRITETIMO	20		/* timeout on socket writes */
X#define	READTIMO	60		/* timeout on socket reads */
X
X/*
X**	Message numbers.  Started out trying to pick them logically,
X**	but gave up after a while.  They don't really matter anyway.
X*/
X#define	M_INFO		211			/* informational or status messages */
X#define	M_PLAY		212			/* play-by-play on other players */
X#define	M_DICE		213			/* complete dice status */
X#define	M_HELO		220			/* hello message */
X#define	M_DOWN		221			/* shutdown message */
X#define	M_RQID		222			/* id request */
X#define	M_TURN		310			/* Player %d, ... up with %d points. */
X#define	M_NWIN		311			/* game over, no winner */
X#define	M_OVER		312			/* Player %d has won the game! */
X#define	M_CPLR		320			/* clear all players */
X#define	M_UARE		321			/* you are player %d */
X#define	M_PNUM		322			/* player %d %s */
X#define	M_FARE		323			/* farewell %d %s */
X#define	M_MSCO		330			/* You now have %d points. */
X#define	M_OSCO		331			/* Player %d now has %d points. */
X#define	M_ANOG		340			/* play another game? */
X#define	M_RFST		341			/* Ready to roll? */
X#define	M_KEEP		342			/* %d points: */
X#define	M_ARGE		501			/* argument error */		
X#define	M_BADM		999			/* bad message */
X
X/*
X**	Some stuff for converting numbers to names.  Probably should be elsewhere.
X*/
Xextern char	   *numnames[][2];
X#define	NUMBER(n)			(numnames[n][0])
X#define	FACE(show,ndice)	(numnames[show][(ndice)!=1])
X
X/*
X**	boolean: remind me never to do this again
X*/
Xtypedef enum { False, True } boolean;
X
X/*
X**	diestat: the four states a die can be in
X*/
Xtypedef enum { Free, Held, Rolled, Taken } diestat;
X
X/*
X**	combination: names for all scoring combinations
X*/
Xtypedef enum {
X	Previous = -1, Nothing, Five, Ace, Three_of_a_kind,
X	Small_straight, Four_of_a_kind, Straight, All_of_a_kind
X} combination;
X
X/*
X**	diceset: structure for tracking status of all dice
X*/
Xtypedef struct {
X	int			d_face[NDICE];		/* number showing on each die */
X	diestat		d_stat[NDICE];		/* status of each die */
X	combination	d_comb[NDICE];		/* die's scoring combination */
X	combination	d_best;				/* best combination scored */
X	boolean		d_again;			/* true if rolling again */
X	int			d_rolling;			/* number of dice rolling */
X	int			d_pts_roll;			/* points due to last roll */
X	int			d_pts_dice;			/* points by this set of NDICE */
X	int			d_pts_turn;			/* points accumulated this turn */
X	char		d_mesg[MESGLEN];	/* status message */
X} diceset;
X
X/*
X**	cstat: player/connection status
X*/
Xtypedef enum {
X	Inactive,	/* no player */
X	Waiting,	/* human waiting to get in */
X	Active,		/* active human player */
X	Computer,	/* active computer player */
X} cstat;
X
X/*
X**	player: player/connection status
X*/
Xtypedef struct {
X	cstat		p_stat;				/* connection status */
X	int			p_fd;				/* communications socket */
X	int			p_score;			/* player score */
X	boolean		p_onboard;			/* True if player on board */
X	int		  (*p_strategy)();		/* computer player strategy */
X	boolean	  (*p_temper)();		/* computer player temperment */
X	char		p_name[NAMELEN];	/* player name */
X	char		p_id[IDLEN];		/* "unique" player id */
X} player;
X
X/*
X**	action: relates message types to actions
X*/
Xstruct action {
X	int		a_type;			/* message type M_???? */
X	int	  (*a_action)();	/* action to take */
X};
X
X/*
X**	graphtype: type of graphics the terminal supports
X*/
Xtypedef enum	{
X	Nographics, Digital, Zenith
X} graphtype;
X
X/*
X**	history: player scoring history
X*/
Xtypedef struct	{
X	long	h_points;			/* total number of points */
X	long	h_avgturn;			/* average points per turn */
X	long	h_wins;				/* number of games won */
X	long	h_games;			/* number of games played */
X	char	h_id[IDLEN];		/* "unique" player id */
X} history;
X
X/*
X**	risk: rolling risks/expectations
X*/
Xtypedef struct	{
X	float	r_p_any;	/* probability of scoring anything (assumes mix) */
X	float	r_p_all;	/* probability of scoring all (assumes mix) */
X	int		r_e_mix;	/* <rolled> with a mix of held dice */
X	int		r_e_aces;	/* <rolled> with held dice all aces (no 3o'kind) */
X	int		r_e_fives;	/* <rolled> with held dice all fives (no 3o'kind) */
X}	risk;
END_OF_cubes.h
if test 6239 -ne `wc -c <cubes.h`; then
    echo shar: \"cubes.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f history.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"history.c\"
else
echo shar: Extracting \"history.c\" \(6118 characters\)
sed "s/^X//" >history.c <<'END_OF_history.c'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)history.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif	lint
X
X#include	<stdio.h>
X#include	<sys/file.h>
X#include	<strings.h>
X#include	"cubes.h"
X
Xhistory		   *hist;			/* history array */
Xunsigned		nhist	= 0;	/* entries in history array */
Xextern int		turnnum;
Xextern player	plr[];
Xextern char	   *compname[];
Xextern int		plrcmp();
X
Xextern char	   *fgets();
Xextern char	   *malloc();
Xextern char	   *realloc();
X
X/*
X**	histread: read history file
X*/
Xhistread()
X{
X	register int	n;
X	register FILE  *fp;
X	char			line[BUFSIZ];
X
X	if(hist != 0) {
X		free((char *)hist);
X		hist = 0, nhist = 0;
X	}
X
X	/*
X	**	Open the file for reading.  The file must exist.
X	*/
X	if((fp = fopen(HISTFILE, "r")) == 0) {
X		perror("histread: fopen");
X		return -1;
X	}
X
X	/*
X	**	Count the lines in the file.  If none, we're all done.
X	*/
X	for(n = 0;fgets(line, sizeof line, fp) != 0;++n)
X		;
X	if(n == 0) {
X		(void) fclose(fp);
X		return 0;
X	}
X
X	/*
X	**	Rewind the file, allocate space, and read in the entries.
X	*/
X	clearerr(fp);
X	rewind(fp);
X	nhist = n;
X	if((hist = (history *)malloc(nhist * sizeof *hist)) == 0) {
X		fprintf(stderr, "histread: malloc: can't allocate space for history\n");
X		(void) fclose(fp);
X		return -1;
X	}
X	n = 0;
X	while(fgets(line, sizeof line, fp) != 0) {
X		if(sscanf(line, "%ld %ld %ld %ld%*c%[^\n]",
X		  &(hist+n)->h_games, &(hist+n)->h_wins,
X		  &(hist+n)->h_points, &(hist+n)->h_avgturn,
X		  (hist+n)->h_id) < 4) {
X			fprintf(stderr, "histread: ignored bad line in %s\n", HISTFILE);
X			continue;
X		}
X		++n;
X	}
X
X	(void) fclose(fp);
X	return 0;
X}
X
X/*
X**	histcmp: history comparison function
X**		best win average, then best point average
X*/
Xhistcmp(h1, h2)
Xhistory	   *h1, *h2;
X{
X	double	v1, v2, diff;
X
X	v1 = h1->h_games == 0 ? 0 : (h1->h_wins / h1->h_games);
X	v2 = h2->h_games == 0 ? 0 : (h2->h_wins / h2->h_games);
X	if((diff = v2 - v1) != 0)		/* reversed */
X		return diff < 0 ? -1 : 1;
X	v1 = h1->h_games == 0 ? 0 : (h1->h_points / h1->h_games);
X	v2 = h2->h_games == 0 ? 0 : (h2->h_points / h2->h_games);
X	if((diff = v2 - v1) != 0)		/* reversed */
X		return diff < 0 ? -1 : 1;
X	v1 = h1->h_games == 0 ? 0 : (h1->h_avgturn / h1->h_games);
X	v2 = h2->h_games == 0 ? 0 : (h2->h_avgturn / h2->h_games);
X	if((diff = v2 - v1) != 0)		/* reversed */
X		return diff < 0 ? -1 : 1;
X	return strncmp(h1->h_id, h2->h_id, IDLEN);
X}
X
X/*
X**	histsort: sort the history array
X*/
Xhistsort()
X{
X	if(nhist != 0 && hist != 0)
X		qsort((char *)hist, (int)nhist, sizeof *hist, histcmp);
X}
X
X/*
X**	histwrite: write the history file
X*/
Xhistwrite()
X{
X	register FILE  *fp;
X	register int	n;
X
X	(void) umask(0644);
X	if((fp = fopen(HISTFILE, "w")) == 0) {
X		perror("histwrite: fopen");
X		return -1;
X	}
X
X	if(nhist != 0) {
X		histsort();
X		for(n = 0;n < nhist;++n) {
X			fprintf(fp, "%10ld %10ld %10ld %10ld %s\n",
X			  (hist+n)->h_games, (hist+n)->h_wins,
X			  (hist+n)->h_points, (hist+n)->h_avgturn,
X			  (hist+n)->h_id);
X		}
X	}
X
X	(void) fclose(fp);
X	return 0;
X}
X
X/*
X**	histadd: add a player to the history array
X*/
Xhistadd(c)
Xint		c;
X{
X	register int	n	= nhist;
X	char		   *id;
X
X	if(nhist == 0) {
X		if((hist = (history *)malloc(sizeof *hist)) == 0) {
X			fprintf(stderr, "histadd: malloc failed\n");
X			return -1;
X		}
X		nhist = 1;
X	} else {
X		++nhist;
X		if((hist =
X		  (history *)realloc((char *)hist, nhist * sizeof *hist)) == 0) {
X			fprintf(stderr, "histadd: realloc failed\n");
X			nhist = 0;
X			return -1;
X		}
X	}
X
X	if(*(id = plr[c].p_id) == '\0')
X		id = plr[c].p_name;
X	strncpy((hist+n)->h_id, id, IDLEN);
X	(hist+n)->h_id[IDLEN-1] = '\0';
X	(hist+n)->h_games = 0;
X	(hist+n)->h_wins = 0;
X	(hist+n)->h_points = 0;
X	(hist+n)->h_avgturn = 0;
X
X	return n;
X}
X
X/*
X**	histpoints: add player's points to history
X**		updates game count and adds new entries as needed
X*/
Xhistpoints(c)
Xint		c;
X{
X	register char  *id;
X	register int	n;
X	boolean			found;
X
X	/*
X	**	Locate player's entry in history array.
X	*/
X	if(*(id = plr[c].p_id) == '\0')
X		id = plr[c].p_name;
X	found = False;
X	for(n = 0;found == False && n < nhist;++n) {
X		if(strncmp(id, (hist+n)->h_id, IDLEN) == 0) {
X			found = True;
X			break;
X		}
X	}
X
X	/*
X	**	If player doesn't have an entry, call histadd
X	**	to add one.  Histadd returns the index of the
X	**	new entry or -1 on error.
X	*/
X	if(found == False) {
X		if((n = histadd(c)) < 0) {
X			fprintf(stderr, "histpoints: histadd failed\n");
X			return -1;
X		}
X	}
X
X	/*
X	**	Update points and avgturn and increment game count.
X	**	Game count is incremented only here.
X	*/
X	(hist+n)->h_points += plr[c].p_score;
X	if(turnnum > 0)
X		(hist+n)->h_avgturn += plr[c].p_score / turnnum;
X	++(hist+n)->h_games;
X
X	return 0;
X}
X
X/*
X**	histwins: update win count for game winner
X**		assumes that histpoints has been called to update game count
X**		doesn't add new history entries as needed
X*/
Xhistwins(c)
Xint		c;
X{
X	register char  *id;
X	register int	n;
X
X	if(*(id = plr[c].p_id) == '\0')
X		id = plr[c].p_name;
X
X	for(n = 0;n < nhist;++n) {
X		if(strncmp(id, (hist+n)->h_id, IDLEN) == 0) {
X			++(hist+n)->h_wins;
X			return 0;
X		}
X	}
X
X	fprintf(stderr, "histwins: can't find <%s> in history\n", id);
X	return -1;
X}
X
X/*
X**	histwgt: return player's historical wins/points weighting
X*/
Xlong
Xhistwgt(c)
Xint		c;
X{
X	register char  *id;
X	register int	n;
X	long			wgt;
X
X	if(*(id = plr[c].p_id) == '\0')
X		id = plr[c].p_name;
X	
X	for(n = 0;n < nhist;++n) {
X		if(strncmp(id, (hist+n)->h_id, IDLEN) == 0) {
X			if((hist+n)->h_games == 0)
X				return 0L;
X			wgt = 1000L * (hist+n)->h_wins;	/* win bonus */
X			wgt += (hist+n)->h_points;		/* plus points */
X			wgt += (hist+n)->h_avgturn;		/* plus average turn */
X			wgt /= (hist+n)->h_games;		/* to average */
X			return wgt;
X		}
X	}
X
X	return 0L;
X}
X
X/*
X**	historder: reorder players by historical wins/points weighting
X*/
Xhistorder()
X{
X	register int	c;
X
X	/*
X	**	Give active players a score based on their history.
X	*/
X	for(c = 0;c < PLAYERS;++c) {
X		switch(plr[c].p_stat) {
X		case Computer:
X		case Active:
X			plr[c].p_score = (int)histwgt(c);
X			break;
X		default:
X			plr[c].p_score = 0;
X			break;
X		}
X	}
X
X	/*
X	**	Uses plrcmp() defined in dsrv.c.
X	*/
X	qsort((char *)plr, PLAYERS, sizeof plr[0], plrcmp);
X}
END_OF_history.c
if test 6118 -ne `wc -c <history.c`; then
    echo shar: \"history.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f random.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"random.c\"
else
echo shar: Extracting \"random.c\" \(704 characters\)
sed "s/^X//" >random.c <<'END_OF_random.c'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)random.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif	lint
X
X#include	<sys/types.h>
X#include	<sys/time.h>
X
Xextern char	   *initstate();
Xextern long		random();
Xextern time_t	time();
X
X/*
X**	irandom: initialize random number generator
X*/
Xirandom()
X{
X	static long			state[256];
X	struct timeval		tv;
X	struct timezone		tz;
X	register unsigned	n;
X
X	(void) gettimeofday(&tv, &tz);
X	n = (unsigned)(tv.tv_sec * 10000L + tv.tv_usec / 100);
X	(void) initstate(n, state, sizeof state);
X	for(n %= 49;n != 0;--n)
X		(void) random();
X}
X
X/*
X**	dieroll: roll an n-sided die (assumes initialized random)
X*/
Xdieroll(n)
X{
X	return (int)(random() % n) + 1;
X}
END_OF_random.c
if test 704 -ne `wc -c <random.c`; then
    echo shar: \"random.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f risk.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"risk.c\"
else
echo shar: Extracting \"risk.c\" \(6831 characters\)
sed "s/^X//" >risk.c <<'END_OF_risk.c'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)risk.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif	lint
X
X#include	"cubes.h"
X
Xextern player		plr[];
Xextern char		   *compname[];
Xextern int			compnames;
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*/
Xrisk	risktbl[NDICE+1]	= {
X/*	      any      all    mix   aces  fives	*/
X	{ 0.00000, 0.00000,     0,     0,     0 },	/* rolling 0 */
X	{ 0.33333, 0.33333,    25,   507,   268 },	/* rolling 1 */
X	{ 0.55650, 0.11197,    50,   127,    89 },	/* rolling 2 */
X	{ 0.72159, 0.05588,    86,    96,    91 },	/* rolling 3 */
X	{ 0.84192, 0.09651,   162,   162,   162 },	/* rolling 4 */
X	{ 0.92254, 0.19985,   310,   310,   310 },	/* rolling 5 */
X};
X
X#define	R_E_ALLDICE		(risktbl[NDICE].r_e_mix)
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 as dice are now */
X	int					cscore;		/* player current score */
X	int					sscore;		/* player score if stays */
X	int					xpct;		/* expectation value of next roll */
X	boolean				canthold;	/* true if can't hold */
X
X	/*
X	**	Fake some recognition time.
X	*/
X	hesitate();
X
X	/*
X	**	Mark Rolled dice as Taken, updating the value of d_rolling.
X	*/
X	keepall(pd);
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_strategy)(&modi);
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	canthold = False;
X	cscore = plr[comp].p_score;
X	sscore = cscore + stay;
X	if(plr[comp].p_onboard == False && sscore < ONBOARD)
X		canthold = True;
X	else if(sscore >= WINSCORE && cscore < WINSCORE && stay < OFFBOARD)
X		canthold = True;
X	if(canthold == True) {
X		*pd = modi;	/* modi is the roll-again diceset */
X		return;
X	}
X
X	/*
X	**	Fake some indecision (sometimes).
X	*/
X	if(dieroll(12) > 8)
X		hesitate();
X	
X	/*
X	**	Calculate the expectation value of rolling again.
X	*/
X	temp = modi;
X	xpct = expect(&temp);
X
X	/*
X	**	Player's temperment will chose which diceset to use.
X	**	If p_temper returns True, it means roll again.
X	*/
X	if((*plr[comp].p_temper)(comp, stay, xpct, &modi) == True)
X		*pd = modi;		/* modi is the roll-again diceset */
X}
X
X/*
X**	expect: return expectation value of rolling dice as they stand
X**		yes, I know these values aren't quite right...
X*/
Xexpect(pd)
Xregister diceset   *pd;
X{
X	register int	d;
X	int				xpct;
X	boolean			allaces;
X	boolean			allfives;
X	risk		   *pr;
X	diceset			temp;
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	if(pd->d_rolling == 0)
X		allaces = allfives = False;
X	else {
X		/*
X		**	Find out if we have all Aces or all Fives.
X		*/
X		allaces = allfives = True;
X		for(d = 0;d < NDICE;++d) {
X			switch(pd->d_comb[d]) {
X			case Previous:
X				if(pd->d_face[d] != ACE)	/* oops! held 3o'kind gets by */
X					allaces = False;
X				if(pd->d_face[d] != FIVE)	/* oops! held 3o'kind gets by */
X					allfives = False;
X				break;
X			case Ace:		allfives = False; break;
X			case Five:		allaces = False; break;
X			case Nothing:	break;
X			default:		allaces = allfives = False; break;
X			}
X		}
X	}
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	**	or all Fives or a mix.  Then add the expectation value of retaining
X	**	the points we already have and the value of being able to roll
X	**	a complete set of dice.
X	*/
X	pr = &risktbl[pd->d_rolling == 0 ? NDICE : pd->d_rolling];
X	xpct = allaces == allfives ? pr->r_e_mix 
X		: (allaces == True ? pr->r_e_aces : pr->r_e_fives);
X	scoredice(pd);
X	xpct += (int)(
X			pd->d_pts_turn * pr->r_p_any	/* keeping what we've got */
X		+	R_E_ALLDICE * pr->r_p_all		/* getting to roll again */
X		);
X	
X	return xpct;
X}
X
X/*
X**	ziprisk: return the probability of rolling nothing
X**		assumes that rolling zero really means rolling all
X*/
Xdouble
Xziprisk(rolling)
Xint		rolling;
X{
X	return 1.0 - risktbl[rolling == 0 ? NDICE : rolling].r_p_any;
X}
X
X/*
X**	highscore: find highest score amongst other players
X*/
Xhighscore(comp)
Xint		comp;
X{
X	register int	c;
X	int				high;
X
X	for(high = c = 0;c < PLAYERS;++c) if(c != comp) {
X		switch(plr[c].p_stat) {
X		case Computer:
X		case Active:
X			if(plr[c].p_score > high)
X				high = plr[c].p_score;
X			break;
X		}
X	}
X
X	return high;
X}
X
X/*
X**	closescore: find the highest score that's close enough to compete with
X*/
Xclosescore(comp)
Xint		comp;
X{
X	register int	c, high, hc;
X	int				oldhigh;
X	int				score[PLAYERS];
X
X#define	CLOSE	3000
X
X	/*
X	**	Copy the scores into an array.
X	*/
X	score[comp] = 0;
X	for(c = 0;c < PLAYERS;++c) if(c != comp) {
X		switch(plr[c].p_stat) {
X		case Computer:
X		case Active:
X			score[c] = plr[c].p_score;
X			break;
X		default:
X			score[c] = 0;
X			break;
X		}
X	}
X
X	/*
X	**	Search the score array for the highest score.  If that one
X	**	happens to be too far away, zap it and try again.  If we
X	**	threw out the only other score (or the last score), then
X	**	put it back.
X	*/
X	oldhigh = 0;
X	do {
X		high = -1;
X		for(c = 0;c < PLAYERS;++c)
X			if(score[c] > high)
X				high = score[(hc = c)];
X		if(high - plr[comp].p_score > CLOSE) {
X			oldhigh = score[hc];
X			score[hc] = 0;
X			high = -1;
X		}
X	} while(high == -1);
X	if(high <= 0)
X		high = oldhigh;
X
X	return high;
X}
X
X/*
X**	cfirst: hesitate before first roll
X*/
Xcfirst(comp)
Xint		comp;
X{
X	hesitate();
X
X	/*
X	**	If this computer has no score, and is not the COMP computer,
X	**	then if the leader is more than a quarter of the way to winning,
X	**	there's a 25% chance that this computer will quit the game.
X	*/
X	if(plr[comp].p_score == 0 && strcmp(plr[comp].p_name, compname[0]) != 0) {
X		if(dieroll(12) > 8 && highscore(comp) > WINSCORE / 4) {
X			oldplayer(comp);
X			return -1;
X		}
X	}
X
X	/*
X	**	If this is a proxy computer, sometimes do some more hesitating.
X	*/
X	if(plr[comp].p_id[0] != '\0' && dieroll(20) < 4)
X		hesitate();
X
X	return 0;
X}
X
X/*
X**	hesitate: fake some human-like hesitation
X*/
Xhesitate()
X{
X	sleep((unsigned)dieroll(3)+1);	/* 2 to 4 seconds */
X}
END_OF_risk.c
if test 6831 -ne `wc -c <risk.c`; then
    echo shar: \"risk.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f strategies.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"strategies.c\"
else
echo shar: Extracting \"strategies.c\" \(3520 characters\)
sed "s/^X//" >strategies.c <<'END_OF_strategies.c'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)strategies.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif	lint
X
X#include	"cubes.h"
X
Xextern player	plr[];
Xextern char	   *compname[];
Xextern int		compnames;
X
X/*
X**	basic: the most basic strategy, throw out Fives
X*/
Xstatic
Xbasic(pd)
Xdiceset	   *pd;
X{
X	if(pd->d_rolling == 0)
X		return;
X	nofives(pd);
X}
X
X/*
X**	merrier: the more dice to throw, the merrier
X**		throw out Fives then Aces
X*/
Xstatic
Xmerrier(pd)
Xdiceset	   *pd;
X{
X	if(pd->d_rolling == 0)
X		return;
X	nofives(pd);
X	noaces(pd);
X}
X
X/*
X**	respect: respects Aces if they won't help with 3o'kind
X**		throws out Fives, throws out Aces if result >= THREE free
X*/
Xstatic
Xrespect(pd)
Xdiceset	   *pd;
X{
X	diceset	temp;
X
X	if(pd->d_rolling == 0)
X		return;
X	nofives(pd);
X	temp = *pd;
X	noaces(&temp);
X	if(temp.d_rolling >= THREE)
X		*pd = temp;
X}
X
X/*
X**	twohater: throws out 3o'kind in DEUCEs, Fives, and Aces
X*/
Xstatic
Xtwohater(pd)
Xdiceset	   *pd;
X{
X	if(pd->d_rolling == 0)
X		return;
X	if(pd->d_best == Three_of_a_kind)
X		no3deuce(pd);
X	nofives(pd);
X	noaces(pd);
X}
X
X/*
X**	greedy: throws out 3o'kind in THREEs and DEUCEs,
X**		Small_straights, Fives, and Aces
X*/
Xstatic
Xgreedy(pd)
Xdiceset	   *pd;
X{
X	if(pd->d_rolling == 0)
X		return;
X	if(pd->d_best == Three_of_a_kind)
X		no3three(pd);
X	if(pd->d_best == Three_of_a_kind)
X		no3deuce(pd);
X	if(pd->d_best == Small_straight)
X		nosmall(pd);
X	nofives(pd);
X	noaces(pd);
X}
X
X/*
X**	picky:
X**		always discards 3o'kind in DEUCEs and THREEs
X**		discards only Aces when all Fives are saved
X**		discards only Fives when all Aces are saved
X**		else discards excess Fives then Aces
X*/
Xstatic
Xpicky(pd)
Xregister diceset   *pd;
X{
X	register int	d;
X	boolean			allaces;
X	boolean			allfives;
X
X	if(pd->d_rolling == 0)
X		return;
X
X	if(pd->d_best == Three_of_a_kind)
X		no3three(pd);
X	if(pd->d_best == Three_of_a_kind)
X		no3deuce(pd);
X
X	allaces = allfives = True;
X	for(d = 0;d < NDICE;++d) {
X		switch(pd->d_comb[d]) {
X		case Ace:
X		case Five:
X		case Nothing:
X			break;
X		case Previous:
X			if(pd->d_face[d] != ACE)
X				allaces = False;
X			if(pd->d_face[d] != FIVE)
X				allfives = False;
X			break;
X		default:
X			allaces = allfives = False;
X			break;
X		}
X	}
X
X	if(allfives == True && allaces == False)
X		noaces(pd);
X	else if(allfives == False && allaces == True)
X		nofives(pd);
X	else {
X		nofives(pd);
X		noaces(pd);
X	}
X}
X
X/*
X**	strategy: an array of available strategies
X*/
Xint	  (*strategy[])()	= {
X	basic,	/* the COMP strategy */
X	merrier,
X	respect,
X	twohater,
X	greedy,
X	picky,
X};
X
Xint		strategies	= sizeof strategy / sizeof strategy[0];
X
X/*
X**	fickle: pick another strategy randomly!
X**		for proxy players only
X*/
Xstatic
Xfickle(pd)
Xdiceset	   *pd;
X{
X	int			n;
X
X	if(dieroll(12) < 4)
X		hesitate();
X	n = dieroll(strategies) - 1;
X	(*strategy[n])(pd);
X}
X
X/*
X**	pickstrategy: personalize each computer player
X**		proxy players get the fickle strategy
X**		calls picktemper to choose player temperment
X*/
Xpickstrategy(c)
Xint		c;
X{
X	register int	n;
X	register char  *name;
X
X	/*
X	**	First, elect the player temperment.
X	*/
X	picktemper(c);
X
X	/*
X	**	Proxies are always fickle, since they are emulating humans.
X	*/
X	if(plr[c].p_id[0] != '\0') {
X		plr[c].p_strategy = fickle;
X		return;
X	}
X
X	/*
X	**	Match a name to a strategy.
X	*/
X	name = plr[c].p_name;
X	for(n = 0;n < compnames;++n) {
X		if(strcmp(compname[n], name) == 0) {
X			plr[c].p_strategy = strategy[n % strategies];
X			return;
X		}
X	}
X
X	/*
X	**	No match above, so pick one randomly.
X	*/
X	n = dieroll(strategies) - 1;
X	plr[c].p_strategy = strategy[n];
X}
END_OF_strategies.c
if test 3520 -ne `wc -c <strategies.c`; then
    echo shar: \"strategies.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f tactics.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"tactics.c\"
else
echo shar: Extracting \"tactics.c\" \(4102 characters\)
sed "s/^X//" >tactics.c <<'END_OF_tactics.c'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)tactics.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif	lint
X
X#include	"cubes.h"
X
X/*
X**	nofives: discard excess fives
X*/
Xnofives(pd)
Xregister diceset   *pd;
X{
X	register int	d;
X	register int	fives;
X
X	for(fives = d = 0;d < NDICE;++d)
X		if(pd->d_comb[d] == Five)
X			++fives;
X	if(pd->d_best == Five)
X		--fives;
X
X	/*
X	**	Alternate zapping from left to right and right to left.
X	*/
X	if(dieroll(2) == 1) {
X		for(d = 0;fives > 0 && d < NDICE;++d) {
X			if(pd->d_comb[d] == Five) {
X				pd->d_comb[d] = Nothing;
X				pd->d_stat[d] = Free;
X				++pd->d_rolling;
X				--fives;
X			}
X		}
X	} else {
X		for(d = NDICE-1;fives > 0 && d >= 0;--d) {
X			if(pd->d_comb[d] == Five) {
X				pd->d_comb[d] = Nothing;
X				pd->d_stat[d] = Free;
X				++pd->d_rolling;
X				--fives;
X			}
X		}
X	}
X}
X
X/*
X**	noaces: discard excess aces
X*/
Xnoaces(pd)
Xregister diceset   *pd;
X{
X	register int	d;
X	register int	aces;
X	register int	fives;
X
X	aces = fives = 0;
X	for(d = 0;d < NDICE;++d) {
X		switch(pd->d_comb[d]) {
X		case Ace:	++aces; break;
X		case Five:	++fives; break;
X		}
X	}
X
X	if(pd->d_best == Ace) {
X		if(fives > 0)
X			pd->d_best = Five;
X		else
X			--aces;
X	}
X
X	/*
X	**	Alternate zapping LtoR and RtoL.
X	*/
X	if(dieroll(2) == 2) {
X		for(d = 0;aces > 0 && d < NDICE;++d) {
X			if(pd->d_comb[d] == Ace) {
X				pd->d_comb[d] = Nothing;
X				pd->d_stat[d] = Free;
X				++pd->d_rolling;
X				--aces;
X			}
X		}
X	} else {
X		for(d = NDICE-1;aces > 0 && d >= 0;--d) {
X			if(pd->d_comb[d] == Ace) {
X				pd->d_comb[d] = Nothing;
X				pd->d_stat[d] = Free;
X				++pd->d_rolling;
X				--aces;
X			}
X		}
X	}
X}
X
X/*
X**	no3deuce: zap 3o'kind in deuces if possible
X**		assumes only Aces and Fives will fit in
X**		the same hand as a Three_of_a_kind
X*/
Xno3deuce(pd)
Xregister diceset   *pd;
X{
X	register int	d;
X	boolean			present;
X	combination		nextbest;
X
X	if(pd->d_best != Three_of_a_kind)
X		return;
X
X	present = False;
X	nextbest = Nothing;
X	for(d = 0;d < NDICE;++d) {
X		switch(pd->d_comb[d]) {
X		case Three_of_a_kind:
X			if(pd->d_face[d] != DEUCE)
X				return;
X			present = True;
X			break;
X		case Ace:
X			nextbest = Ace;
X			break;
X		case Five:
X			if(nextbest == Nothing)
X				nextbest = Five;
X			break;
X		}
X	}
X
X	if(present == False || nextbest == Nothing)
X		return;
X	
X	for(d = 0;d < NDICE;++d) {
X		if(pd->d_comb[d] == Three_of_a_kind /*&& pd->d_face[d] == DEUCE*/) {
X			pd->d_comb[d] = Nothing;
X			pd->d_stat[d] = Free;
X			++pd->d_rolling;
X		}
X	}
X	pd->d_best = nextbest;
X}
X
X/*
X**	no3three: zap 3o'kind in threes if possible
X**		assumes only Aces and Fives will fit in
X**		the same hand as a Three_of_a_kind
X*/
Xno3three(pd)
Xregister diceset   *pd;
X{
X	register int	d;
X	boolean			present;
X	combination		nextbest;
X
X	if(pd->d_best != Three_of_a_kind)
X		return;
X
X	present = False;
X	nextbest = Nothing;
X	for(d = 0;d < NDICE;++d) {
X		switch(pd->d_comb[d]) {
X		case Three_of_a_kind:
X			if(pd->d_face[d] != THREE)
X				return;
X			present = True;
X			break;
X		case Ace:
X			nextbest = Ace;
X			break;
X		case Five:
X			if(nextbest == Nothing)
X				nextbest = Five;
X			break;
X		}
X	}
X
X	if(present == False || nextbest == Nothing)
X		return;
X	
X	for(d = 0;d < NDICE;++d) {
X		if(pd->d_comb[d] == Three_of_a_kind /*&& pd->d_face[d] == DEUCE*/) {
X			pd->d_comb[d] = Nothing;
X			pd->d_stat[d] = Free;
X			++pd->d_rolling;
X		}
X	}
X	pd->d_best = nextbest;
X}
X
X/*
X**	nosmall: discard small straight, retaining imbeded ace or five
X*/
Xnosmall(pd)
Xregister diceset   *pd;
X{
X	register int	d;
X	boolean			present;
X	combination		nextbest;
X
X	present = False;
X	nextbest = Nothing;
X	for(d = 0;d < NDICE;++d) {
X		switch(pd->d_comb[d]) {
X		case Small_straight:
X			present = True;
X			switch(pd->d_face[d]) {
X			case ACE:
X				pd->d_comb[d] = Ace;
X				nextbest = Ace;
X				break;
X			case FIVE:
X				pd->d_comb[d] = Five;
X				if(nextbest == Nothing)
X					nextbest = Five;
X				break;
X			default:
X				pd->d_comb[d] = Nothing;
X				pd->d_stat[d] = Free;
X				++pd->d_rolling;
X				break;
X			}
X			break;
X		case Ace:
X			nextbest = Ace;
X			break;
X		case Five:
X			if(nextbest == Nothing)
X				nextbest = Five;
X			break;
X		}
X	}
X	if(present == True)
X		pd->d_best = nextbest;
X}
END_OF_tactics.c
if test 4102 -ne `wc -c <tactics.c`; then
    echo shar: \"tactics.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f tempers.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"tempers.c\"
else
echo shar: Extracting \"tempers.c\" \(6057 characters\)
sed "s/^X//" >tempers.c <<'END_OF_tempers.c'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)tempers.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif	lint
X
X#include	"cubes.h"
X
Xextern player		plr[];
Xextern risk			risktbl[];
Xextern char		   *compname[];
Xextern int			compnames;
Xextern double		ziprisk();
Xextern int			highscore();
Xextern int			closescore();
Xextern int			winner();
X
X#define	P_MAXROLL	(P_ACEMULT * P_AOKMULT)	/* largest single roll */
X
X/*
X**	robot: just plays the expectations and ignores other players, etc.
X*/
X/*ARGSUSED*/
Xstatic boolean
Xrobot(comp, stay, xpct, pd)
Xint			comp, stay, xpct;
Xdiceset	   *pd;
X{
X	/*
X	**	Don't roll if there's no advantage.
X	*/
X	return (xpct <= stay) ? False : True;
X}
X
X/*
X**	xx_antsy: gets antsy when it gets behind; uses supplied tracking function
X**		a little bit more conservative than robot when ahead
X*/
X/*ARGSUSED*/
Xstatic boolean
Xxx_antsy(comp, stay, xpct, pd, track)
Xint			comp, stay, xpct;
Xdiceset	   *pd;
Xint		  (*track)();
X{
X	int		mscore;
X	int		hscore;
X	int		live;
X
X	/*
X	**	If there's gain to be had, roll again.
X	*/
X#define	GAINFAC	1.1
X	if(xpct > (int)(GAINFAC * stay))
X		return True;
X
X	/*
X	**	Find our score and the score we're tracking.
X	**	If we're ahead or close, then don't reroll.
X	*/
X	mscore = plr[comp].p_score + stay;
X	hscore = (*track)(comp);
X	if(mscore + P_ACE > hscore)
X		return False;
X
X	/*
X	**	Calculate the expectation value we can live with.
X	*/
X	live = (int)(stay * (GAINFAC - (double)(hscore - mscore) / WINSCORE));
X	if(xpct > live)
X		return True;
X	
X	return False;
X}
X
X/*
X**	antsy: gets antsy when it gets behind; tracks leader
X*/
Xstatic boolean
Xantsy(comp, stay, xpct, pd)
Xint			comp, stay, xpct;
Xdiceset	   *pd;
X{
X	return xx_antsy(comp, stay, xpct, pd, highscore);
X}
X
X/*
X**	tracker: same as antsy but tracks highest close score rather than higest
X*/
Xstatic boolean
Xtracker(comp, stay, xpct, pd)
Xint			comp, stay, xpct;
Xdiceset	   *pd;
X{
X	return xx_antsy(comp, stay, xpct, pd, closescore);
X}
X
X/*
X**	chicken: gets scared when it has lots of points in hand
X**		doesn't care about expectation or other players
X**		also chickens out as soon as it thinks it has won
X**		or as soon as it gets on board
X*/
X/*ARGSUSED*/
Xstatic boolean
Xchicken(comp, stay, xpct, pd)
Xint			comp, stay, xpct;
Xdiceset	   *pd;
X{
X	int		mscore;
X	int		hscore;
X	int		atrisk;
X	int		maxrisk;
X
X	/*
X	**	If we just got on board, stop.
X	*/
X	if((mscore = plr[comp].p_score) == 0) {
X		if(stay >= ONBOARD)	/* redundant */
X			return False;
X	}
X	
X	/*
X	**	If we could win here, stop.
X	*/
X	if((mscore += stay) >= WINSCORE) {
X		hscore = highscore(comp);
X		if(mscore - hscore >= WINMARGIN)
X			return False;
X	}
X
X	/*
X	**	We're not willing to risk much when we're ahead.
X	**	A little more desperate when behind...
X	*/
X	atrisk = (int)(stay * ziprisk(pd->d_rolling));
X	if(mscore > hscore + 5 * P_ACE)
X		maxrisk = P_ACE + P_FIVE;
X	else if(mscore > hscore)
X		maxrisk = 2 * P_ACE;
X	else if(mscore > hscore - 5 * P_ACE)
X		maxrisk = 2 * P_ACE + P_FIVE / 2;
X	else if(mscore > hscore - 10 * P_ACE)
X		maxrisk = 2 * P_ACE + P_FIVE;
X	else
X		maxrisk = 3 * P_ACE;
X	if(atrisk > maxrisk)
X		return False;
X
X	return True;
X}
X
X/*
X**	gambler: cautious sometimes, not so others
X**		stops when it gets on board unless the risk is small
X**		stops when it has won unless the risk is small
X**		rolls to try to stop a winner
X**		rolls when the expectation is favorable
X**		rolls when there's less than 50% risk and is behind and "lucky"
X*/
X/*ARGSUSED*/
Xstatic boolean
Xgambler(comp, stay, xpct, pd)
Xint			comp, stay, xpct;
Xdiceset	   *pd;
X{
X	int		mscore;
X	int		hscore;
X	int		winc;
X
X#define	SMALLRISK	0.20
X#define	EVENODDS	0.50
X
X	/*
X	**	If we just got on board, hold unless really tempting.
X	*/
X	if((mscore = plr[comp].p_score) == 0) {
X		if(stay >= ONBOARD) {	/* redundant */
X			if(xpct > stay && ziprisk(pd->d_rolling) < SMALLRISK)
X				return True;
X			return False;
X		}
X	}
X	
X	/*
X	**	If we could win, hold unless it's really tempting.
X	*/
X	hscore = highscore(comp);
X	if((mscore += stay) >= WINSCORE) {
X		if(mscore - hscore >= WINMARGIN) {
X			if(xpct > stay && ziprisk(pd->d_rolling) < SMALLRISK)
X				return True;
X			return False;
X		}
X	}
X
X	/*
X	**	If we have a winner (other than us), try to beat them,
X	**	but only if the expectation is neutral or we feel lucky.
X	**	We try to get within WINMARGIN of the player's score,
X	**	but if their turn is after ours, we try to beat.
X	**	Assumes that hscore is winner's score (but what else?).
X	*/
X	if((winc = winner()) != -1 && winc != comp) {
X		if(xpct >= stay || dieroll(WINSCORE) > stay)
X			if(hscore - mscore >= WINMARGIN || winc > comp)
X				return True;
X		return False;	/* too critical to continue */
X	}
X
X	/*
X	**	If the expectation value is better than what we've got, go ahead.
X	*/
X	if(xpct > stay)
X		return True;
X	
X	/*
X	**	If we're behind, roll when the odds are favorable
X	**	and we feel lucky.
X	*/
X	if(hscore > mscore && ziprisk(pd->d_rolling) < EVENODDS)
X		if(dieroll(WINSCORE) > stay)	/* feel lucky */
X			return True;
X	
X	return False;
X}
X
X/*
X**	strategy: an array of available strategies
X*/
Xboolean	  (*temper[])()	= {
X	robot,	/* the COMP strategy */
X	antsy,
X	tracker,
X	chicken,
X	gambler,
X};
X
Xint		tempers	= sizeof temper / sizeof temper[0];
X
X/*
X**	schizo: pick another temperment randomly!
X**		for proxy players only
X*/
Xstatic boolean
Xschizo(comp, stay, xpct, pd)
Xint			comp, stay, xpct;
Xdiceset	   *pd;
X{
X	int			n;
X
X	if(dieroll(20) > 16)
X		hesitate();
X	n = dieroll(tempers) - 1;
X	return (*temper[n])(comp, stay, xpct, pd);
X}
X
X/*
X**	picktemper: personalize each computer player
X**		proxy players get the schizo temperment
X*/
Xpicktemper(c)
Xint		c;
X{
X	register int	n;
X	register char  *name;
X
X	/*
X	**	Proxies get a schizo temperment in order to emulate humans.
X	*/
X	if(plr[c].p_id[0] != '\0') {
X		plr[c].p_temper = schizo;
X		return;
X	}
X
X	/*
X	**	Match a name to a temperment.
X	*/
X	name = plr[c].p_name;
X	for(n = 0;n < compnames;++n) {
X		if(strcmp(compname[n], name) == 0) {
X			plr[c].p_temper = temper[n % tempers];
X			return;
X		}
X	}
X
X	/*
X	**	No match above, so pick one randomly.
X	*/
X	n = dieroll(tempers) - 1;
X	plr[c].p_temper = temper[n];
X}
END_OF_tempers.c
if test 6057 -ne `wc -c <tempers.c`; then
    echo shar: \"tempers.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 3 \(of 3\).
cp /dev/null ark3isdone
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