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

games@tekred.TEK.COM (06/08/88)

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

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 2 (of 3)."
# Contents:  actions.c avg.c cubes.6 dieopts.c turn.c
# Wrapped by billr@saab on Tue Jun  7 16:36:06 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f actions.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"actions.c\"
else
echo shar: Extracting \"actions.c\" \(8308 characters\)
sed "s/^X//" >actions.c <<'END_OF_actions.c'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)actions.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	<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	<pwd.h>
X#include	"cubes.h"
X
Xstatic char			answer[BUFSIZ];		/* player answer to prompt */
Xplayer				plr[PLAYERS];		/* player status */
Xdiceset				dice;				/* the status of the dice */
Xint					mypnum	= -1;		/* my player number */
Xint					turnnum	= 0;		/* current turn number */
X
Xextern char		   *pname;				/* player name */
Xextern char		   *shost;				/* server hostname */
Xextern int			ssock;				/* server socket */
Xextern boolean		active;				/* True while active */
Xextern char		   *index();
Xstruct passwd	   *getpwuid();
X
X/*
X**	respond: send a reply to the server adding "\r\n"
X*/
Xrespond(reply)
Xchar   *reply;
X{
X	char	repbuf[BUFSIZ];
X
X	strcpy(repbuf, reply);
X	strcat(repbuf, "\r\n");
X
X	return write(ssock, repbuf, strlen(repbuf)) < 0 ? -1 : 0;
X}
X
X/*
X**	dispact: display an informative message to the player
X*/
X/*ARGSUSED*/
Xdispact(t,m)
Xchar *m;
X{
X	return infomesg(m+4);
X}
X
X/*
X**	pmptact: display a prompt to the player
X*/
X/*ARGSUSED*/
Xpmptact(t,m)
Xchar *m;
X{
X	return prompt(m+4, answer);
X}
X
X/*
X**	heloact: respond to the M_HELO message by giving player name.
X*/
X/*ARGSUSED*/
Xheloact(t,m)
Xchar *m;
X{
X	return respond(pname);
X}
X
X/*
X**	rqidact: respond to the M_RQID message by giving player identity
X*/
X/*ARGSUSED*/
Xrqidact(t,m)
Xchar *m;
X{
X	char			idbuf[IDLEN];
X	char			host[256];
X	int				uid;
X	struct passwd  *pw;
X
X	(void) gethostname(host, sizeof host);
X
X	/*
X	**	Look up login name in the password file.  If it's not there,
X	**	just use the user id number.
X	*/
X	uid = getuid();
X	if((pw = getpwuid(uid)) == 0)
X		sprintf(idbuf, "%d@%.*s", uid, IDLEN-10, host);
X	else
X		sprintf(idbuf, "%s@%.*s", pw->pw_name, IDLEN-10, host);
X
X	return respond(idbuf);
X}
X
X/*
X**	infoact: display an informational message
X*/
X/*ARGSUSED*/
Xinfoact(t,m)
Xchar *m;
X{
X	return infomesg(m+4);
X}
X
X#ifdef	notdef
X/*
X**	playact: display a play-by-play message
X*/
X/*ARGSUSED*/
Xplayact(t,m)
Xchar *m;
X{
X	return playmesg(m+4);
X}
X#endif	notdef
X
X/*
X**	rfstact: respond to "roll first?" message
X*/
Xrfstact(t,m)
Xchar *m;
X{
X	switch(pmptact(t,m)) {
X	case -2:	/* timeout */
X		return 0;
X	case -1:	/* error */
X		return -1;
X	}
X	if(respond(answer) < 0)
X		return -1;
X	return 0;
X}
X
X/*
X**	keepact: respond to "%d points:" message
X*/
Xkeepact(t,m)
Xchar *m;
X{
X	return rfstact(t,m);
X}
X
X/*
X**	anogact: respond to "another game?" message
X*/
Xanogact(t,m)
Xchar *m;
X{
X	return rfstact(t,m);
X}
X
X/*
X**	downact: print the shutdown message and exit gracefully
X*/
X/*ARGSUSED*/
Xdownact(t,m)
Xchar *m;
X{
X	(void) infomesg(m+4);
X	sleep(3);
X	cleanup(0);
X}
X
X/*
X**	turnact: cause turn indicator to advance
X*/
X/*ARGSUSED*/
Xturnact(t,m)
Xchar *m;
X{
X	int		pnum;
X
X	if(sscanf(m+4, "Turn %d: player %d", &turnnum, &pnum) != 2)
X		return -1;
X
X	return nowup(pnum);
X}
X
X/*
X**	cplract: clear player roster
X*/
X/*ARGSUSED*/
Xcplract(t,m)
Xchar *m;
X{
X	register int	c;
X
X	mypnum = -1;
X	for(c = 0;c < PLAYERS;++c) {
X		plr[c].p_stat = Inactive;
X		plr[c].p_name[0] = '\0';
X		plr[c].p_score = 0;
X		(void) clearplr(c);
X	}
X
X	return 0;
X}
X
X/*
X**	uareact: setup this player
X*/
X/*ARGSUSED*/
Xuareact(t,m)
Xchar *m;
X{
X	int		pnum;
X
X	if(sscanf(m+4, "You are player %d", &pnum) < 1)
X		return -1;
X
X	mypnum = pnum;
X	plr[pnum].p_score = 0;
X	plr[pnum].p_stat = Active;
X	plr[pnum].p_name[0] = '\0';
X	strncat(plr[pnum].p_name, pname, sizeof plr[pnum].p_name);
X
X	return showplr(pnum);
X}
X
X/*
X**	pnumact: setup another player
X*/
X/*ARGSUSED*/
Xpnumact(t,m)
Xchar *m;
X{
X	int		pnum;
X	char   *s;
X
X	if(sscanf(m+4, "player %d", &pnum) < 1)
X		return -1;
X	plr[pnum].p_score = 0;
X	plr[pnum].p_stat = Active;
X	plr[pnum].p_name[0] = '\0';
X	if((s = index(m+12, ' ')) == 0)
X		return -1;
X	strncat(plr[pnum].p_name, s+1, sizeof plr[pnum].p_name);
X
X	return showplr(pnum);
X}
X
X/*
X**	fareact: clean up when a player leaves
X*/
X/*ARGSUSED*/
Xfareact(t,m)
Xchar *m;
X{
X	int		pnum;
X	char	msgbuf[MESGLEN];
X
X	if(sscanf(m+4, "farewell %d", &pnum) < 1)
X		return -1;
X	sprintf(msgbuf, "%s has left the game.", plr[pnum].p_name);
X	(void) clearplr(pnum);
X	(void) infomesg(msgbuf);
X	sleep(1);
X
X	plr[pnum].p_stat = Inactive;
X	plr[pnum].p_name[0] = '\0';
X	plr[pnum].p_score = 0;
X
X	return 0;
X}
X
X/*
X**	mscoact: update my score
X*/
X/*ARGSUSED*/
Xmscoact(t,m)
Xchar *m;
X{
X	int		score;
X
X	if(sscanf(m+4, "You now have %d", &score) < 1)
X		return -1;
X	if(mypnum < 0)
X		return -1;
X
X	plr[mypnum].p_score = score;
X	return showplr(mypnum);
X}
X
X/*
X**	oscoact: update other player score
X*/
X/*ARGSUSED*/
Xoscoact(t,m)
Xchar *m;
X{
X	int		pnum, score;
X
X	if(sscanf(m+4, "Player %d now has %d", &pnum, &score) < 2)
X		return -1;
X
X	plr[pnum].p_score = score;
X	return showplr(pnum);
X}
X
X/*
X**	nwin: no winner message
X*/
X/*ARGSUSED*/
Xnwinact(t,m)
Xchar *m;
X{
X	return infomesg(m+4);
X}
X
X/*
X**	overact: game over with winner
X*/
X/*ARGSUSED*/
Xoveract(t,m)
Xchar *m;
X{
X	return infomesg(m+4);
X}
X
X/*
X**	diceact: parse the dice status message and redisplay the dice
X*/
X/*ARGSUSED*/
Xdiceact(t,m)
Xchar *m;
X{
X	register int	d;
X	char			stats[NDICE+1];
X	char			faces[NDICE+1];
X	char			combs[NDICE+1];
X	int				c;
X
X	dice.d_mesg[0] = '\0';
X	if(sscanf(m+4, "%d %s %s %s %d %d %d%*1c%[^\n]",
X	  &c, stats, faces, combs,
X	  &dice.d_pts_roll, &dice.d_pts_dice, &dice.d_pts_turn,
X	  dice.d_mesg) < 7) {
X		fprintf(stderr, "dieact: couldn't parse dice status\n");
X		return -1;
X	}
X	
X	for(d = 0;d < NDICE;++d) {
X		switch(stats[d]) {
X		case 'f':	dice.d_stat[d] = Free; break;
X		case 'h':	dice.d_stat[d] = Held; break;
X		case 't':	dice.d_stat[d] = Taken; break;
X		case 'r':	dice.d_stat[d] = Rolled; break;
X		default:	dice.d_stat[d] = Free; break;
X		}
X		dice.d_face[d] = isdigit(faces[d]) ? (faces[d] - '0') : BADFACE;
X		switch(combs[d]) {
X		case 'p':	dice.d_comb[d] = Previous; break;
X		case 'n':	dice.d_comb[d] = Nothing; break;
X		case '5':	dice.d_comb[d] = Five; break;
X		case '1':	dice.d_comb[d] = Ace; break;
X		case 't':	dice.d_comb[d] = Three_of_a_kind; break;
X		case 'l':	dice.d_comb[d] = Small_straight; break;
X		case 'f':	dice.d_comb[d] = Four_of_a_kind; break;
X		case 's':	dice.d_comb[d] = Straight; break;
X		case 'a':	dice.d_comb[d] = All_of_a_kind; break;
X		default:	dice.d_comb[d] = Nothing; break;
X		}
X	}
X
X	return showdice();
X}
X
X/*
X**	argeact: argument error, display error message
X*/
X/*ARGSUSED*/
Xargeact(t,m)
Xchar *m;
X{
X	(void) infomesg(m+4);
X	sleep(1);
X	return 0;
X}
X
X/*
X**	badmact: alert player to bad message
X*/
Xbadmact(t,m)
Xchar *m;
X{
X	return (dispact(t,"Bad message follows:") < 0 || dispact(t,m) < 0) ? -1 : 0;
X}
X
X/*
X**	unktact: alert player to unknown type message
X*/
Xunktact(t,m)
Xchar *m;
X{
X	return (dispact(t,"Unknown message follows:") < 0 || dispact(t,m) < 0) ? -1 : 0;
X}
X
X/*
X**	atable: action table
X*/
Xstruct action	atable[]	= {
X	{ M_INFO,	infoact },			/* informational or status messages */
X/*	{ M_PLAY,	playact },			/* play-by-play on other players */
X	{ M_KEEP,	keepact },			/* %d points: */
X	{ M_HELO,	heloact },			/* hello message (and name query) */
X	{ M_DOWN,	downact },			/* shutdown message */
X	{ M_RQID,	rqidact },			/* id request */
X	{ M_TURN,	turnact },			/* Player %d, ... up with %d points. */
X	{ M_NWIN,	nwinact },			/* game over, no winner */
X	{ M_OVER,	overact },			/* Player %d has won the game! */
X	{ M_CPLR,	cplract },			/* clear player roster */
X	{ M_UARE,	uareact },			/* you are player %d */
X	{ M_PNUM,	pnumact },			/* player %d %s */
X	{ M_FARE,	fareact },			/* farewell %d %s */
X	{ M_MSCO,	mscoact },			/* You now have %d points. */
X	{ M_OSCO,	oscoact },			/* Player %d now has %d points. */
X	{ M_ANOG,	anogact },			/* play another game? */
X	{ M_RFST,	rfstact },			/* 0 points, roll first? */
X	{ M_DICE,	diceact },			/* dice status */
X	{ M_ARGE,	argeact },			/* argument error, try again */		
X	{ M_BADM,	badmact },			/* bad message */
X};
X
Xint		nactions	= (sizeof atable / sizeof atable[0]);
X
X/*
X**	action: a dispatching function
X*/
Xaction(type, mesg)
Xint		type;
Xchar   *mesg;
X{
X	register int	n;
X	int				r;
X
X	for(n = 0;n < nactions;++n) {
X		if(atable[n].a_type == type) {
X			r = atable[n].a_action(type, mesg);
X			neutral();
X			return r;
X		}
X	}
X
X	r = unktact(type, mesg);	/* unknown type action */
X	neutral();
X	return r;
X}
END_OF_actions.c
if test 8308 -ne `wc -c <actions.c`; then
    echo shar: \"actions.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f avg.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"avg.c\"
else
echo shar: Extracting \"avg.c\" \(5416 characters\)
sed "s/^X//" >avg.c <<'END_OF_avg.c'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)avg.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif	lint
X
X#include	<stdio.h>
X#include	"cubes.h"
X
Xextern int		optind;
Xextern int		opterr;
Xextern char	   *optarg;
Xextern long		atol();
X
X#define	ROLLS		1000000L	/* one million trials */
X
X#define	T_STR		0	/* 5	/* Straight */
X#define	T_AOK		1	/* 5	/* All_of_a_kind */
X#define	T_4OK1A		2	/* 5	/* Four_of_a_kind w/ one ace */
X#define	T_4OK1F		3	/* 5	/* Four_of_a_kind w/ one five */
X#define	T_SMSTR1A	4	/* 5	/* Small_straight w/ one ace */
X#define	T_SMSTR1F	5	/* 5	/* Small_straight w/ one five */
X#define	T_3OK2A		6	/* 5	/* Three_of_a_kind w/ two aces */
X#define	T_3OK2F		7	/* 5	/* Three_of_a_kind w/ two fives */
X#define	T_3OK1A1F	8	/* 5	/* Three_of_a_kind w/ one aces and one five */
X#define	T_4OK		9	/* 4	/* Four_of_a_kind */
X#define	T_SMSTR		10	/* 4	/* Small_straight */
X#define	T_3OK1A		11	/* 4	/* Three_of_a_kind w/one ace */
X#define	T_3OK1F		12	/* 4	/* Three_of_a_kind w/one five */
X#define	T_2A2F		13	/* 4	/* two aces and two fives */
X#define	T_3OK		14	/* 3	/* Three_of_a_kind */
X#define	T_2A1F		15	/* 3	/* two aces and one five */
X#define	T_1A2F		16	/* 3	/* one ace and two fives */
X#define	T_2A		17	/* 2	/* two aces */
X#define	T_2F		18	/* 2	/* two fives */
X#define	T_1A1F		19	/* 2	/* one ace and one five */
X#define	T_1A		20	/* 1	/* one ace */
X#define	T_1F		21	/* 1	/* one five */
X#define	T_ZIP		22	/* 0	/* Nothing */
X#define	T_SIZE		23
X
Xstatic char	   *t_name[]	= {
X	"Straight",
X	"All_of_a_kind",
X	"Four_of_a_kind w/ one Ace",
X	"Four_of_a_kind w/ one Five",
X	"Small_straight w/ one Ace",
X	"Small_straight w/ one Five",
X	"Three_of_a_kind w/ two Aces",
X	"Three_of_a_kind w/ two Fives",
X	"Three_of_a_kind w/ one Aces and one Five",
X	"Four_of_a_kind",
X	"Small_straight",
X	"Three_of_a_kind w/one Ace",
X	"Three_of_a_kind w/one Five",
X	"two Aces and two Fives",
X	"Three_of_a_kind",
X	"two Aces and one Five",
X	"one Ace and two Fives",
X	"two Aces",
X	"two Fives",
X	"one Ace and one Five",
X	"one Ace",
X	"one Five",
X	"Nothing",
X};
X
Xmain(ac, av)
Xchar   *av[];
X{
X	register diceset   *pd;
X	register int		d, aces, fives, c, r;
X	diceset				dice;
X	long				cnt[T_SIZE];
X	long				pts[T_SIZE];
X	long				tpts;
X	long				rolls			= ROLLS;
X	boolean				aces_are_fives	= False;
X	int					haces			= 0;
X	int					held			= 0;
X
X	opterr = 0;
X	while((c = getopt(ac, av, "fa:h:r:")) >= 0) {
X		switch(c) {
X		case 'f':	/* toggle aces_are_fives */
X			aces_are_fives = aces_are_fives == True ? False : True;
X			break;
X		case 'a':	/* number of aces held */
X			if((haces = atoi(optarg)) < 0 || haces >= NDICE) {
X				fprintf(stderr,
X					"avg: aces must be in range 0 to %d\n", NDICE-1);
X				exit(1);
X			}
X			break;
X		case 'h':	/* number of generic dice held */
X			if((held = atoi(optarg)) < 0 || held >= NDICE) {
X				fprintf(stderr,
X					"avg: held must be in range 0 to %d\n", NDICE-1);
X				exit(1);
X			}
X			break;
X		case 'r':	/* number of trials */
X			if((rolls = atol(optarg)) <= 0) {
X				fprintf(stderr, "avg: rolls must be positive\n");
X				exit(1);
X			}
X			break;
X		default:
X			fprintf(stderr,
X				"usage -- avg [-r rolls] [[-f] -a aces] [-h held]\n");
X			exit(1);
X		}
X	}
X
X	if(haces + held >= NDICE) {
X		fprintf(stderr, "avg: aces plus held must be less than %d\n", NDICE);
X		exit(1);
X	}
X
X	/*
X	**	Initialize.
X	*/
X	irandom();
X	pd = &dice;
X	for(c = 0;c < T_SIZE;++c)
X		cnt[c] = pts[c] = 0;
X
X	/*
X	**	Roll and tally.
X	*/
X	for(r = 0;r < rolls;++r) {
X		initdice(pd);
X		for(d = 0;d < haces;++d) {
X			pd->d_stat[d] = Held;
X			pd->d_comb[d] = Previous;
X			pd->d_face[d] = aces_are_fives == True ? FIVE : ACE;
X/*			pd->d_pts_dice += aces_are_fives == True ? P_FIVE : P_ACE;	*/
X/*			pd->d_pts_turn += aces_are_fives == True ? P_FIVE : P_ACE;	*/
X		}
X		for(;d < haces + held;++d) {
X			pd->d_stat[d] = Held;
X			pd->d_comb[d] = Previous;
X			pd->d_face[d] = BADFACE;
X/*			pd->d_pts_dice += P_FIVE;	*/
X/*			pd->d_pts_turn += P_FIVE;	*/
X		}
X
X		rolldice(pd);
X		evaluate(pd);
X		for(aces = fives = d = 0;d < NDICE;++d) {
X			switch(pd->d_comb[d]) {
X			case Ace:	++aces; break;
X			case Five:	++fives; break;
X			default:	break;
X			}
X		}
X
X		switch(pd->d_best) {
X		case Straight:			c = T_STR; break;
X		case All_of_a_kind:
X			c = T_AOK;
X			/* next line accounts for <rolled> being added value only */
X			pd->d_pts_roll -= aces * (aces_are_fives == True ? P_FIVE : P_ACE);
X			break;
X		case Four_of_a_kind:
X			c = aces ? T_4OK1A : (fives ? T_4OK1F : T_4OK);
X			break;
X		case Small_straight:
X			c = aces ? T_SMSTR1A : (fives ? T_SMSTR1F : T_SMSTR);
X			break;
X		case Three_of_a_kind:
X			if(aces > 1)		c = T_3OK2A;
X			else if(fives > 1)	c = T_3OK2F;
X			else if(aces)		c = fives ? T_3OK1A1F : T_3OK1A;
X			else if(fives)		c = T_3OK1F;
X			else				c = T_3OK;
X			break;
X		case Ace:
X			if(aces > 1)		c = fives ? (fives > 1 ? T_2A2F : T_2A1F) : T_2A;
X			else				c = fives ? (fives > 1 ? T_1A2F : T_1A1F) : T_1A;
X			break;
X		case Five:				c = fives == 2 ? T_2F : T_1F; break;
X		case Nothing:			c = T_ZIP; break;
X		default:				continue;
X		}
X		scoredice(pd);
X		pts[c] += pd->d_pts_roll;
X		++cnt[c];
X	}
X
X	/*
X	**	Print a summary.
X	*/
X	tpts = 0;
X	printf("%-40s %6s %6s %8s\n", "Combination", "Ptsper", "Expect", "Count");
X	for(c = 0;c < T_SIZE;++c) {
X		if(cnt[c] != 0) {
X			tpts += pts[c];
X			printf("%-40s %6ld %6ld %8ld\n",
X				t_name[c], pts[c] / cnt[c], pts[c] / rolls, cnt[c]);
X		}
X	}
X	printf("%-40s %6s %6ld %8ld\n", "Total", "", tpts / rolls, rolls);
X
X	exit(0);
X}
X
X
END_OF_avg.c
if test 5416 -ne `wc -c <avg.c`; then
    echo shar: \"avg.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f cubes.6 -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"cubes.6\"
else
echo shar: Extracting \"cubes.6\" \(13761 characters\)
sed "s/^X//" >cubes.6 <<'END_OF_cubes.6'
X.TH CUBES 6-USENET comp.sources.games USENET "UN*X Gaming Manual"
X.\" Copyright 1988 Gregory M. Paris
X.SH NAME
Xcubes \- multi-player dice game
X.SH SYNOPSIS
X.B cubes
X[
X.B \-g graphics
X] [
X.B \-h host
X] [
X.B \-n name
X]
X.SH OVERVIEW
XThe game of
X.I cubes
Xis a computer implementation of a dice game played with
Xfive six-sided dice.
XThe goal of the game is to be the first player to
Xscore 10,000 points.
XPlayers take turns rolling the dice in the hopes of getting one or more
Xof several scoring combinations.
XA player can continue rolling as long as the previous roll produced
Xa scoring combination.
XFailing that,
Xany points accumulated during the turn are lost and the player's turn ends.
XBefore that happens,
Xthe player may (prudently or cowardly) elect to quit rolling,
Xat which time the points accumulated during the turn are added to the player's score.
XContrary to what you might think by reading this manual page,
X.I cubes
Xis both easy to learn and easy to play.
X.SH "BEGINNING A GAME"
X.I Cubes
Xis a multi-player game,
Xwith the game logic implemented in a cube server,
Xand player interaction via a client process, the
X.I cubes
Xprogram.
XFor more than one person to play at a time,
Xeach must each begin
X.I cubes
Xat about the same time, and must connect to the same server.
XThe program assumes that the server is on the same system as the player.
XIf this is not so, the
X.RB ` \-h \  host '
Xoption must be used to tell
X.I cubes
Xwhich system the server is running on.
X.PP
XIf your terminal supports either DEC or Zenith graphics character sets,
Xyou can tell
X.I cubes
Xto take advantage of this fact when drawing the five dice.
X(This feature assumes that the
X.RB ` as '
Xand
X.RB ` ae '
Xtermcap capabilities describe how to put your terminal into and take it out of
Xgraphics mode.)
XTo do this, supply the
X.RB ` \-g \  graphics '
Xoption on the command line, where
X.B graphics
Xis either
X.RB ` d '
Xfor DEC graphics
Xor
X.RB ` z '
Xfor Zenith graphics.
X.PP
XIf you'd like your name on the scoreboard to show up as something other
Xthan your login name, you can either set the
X.B CUBENAME
Xenvironment variable to the desired value,
Xor use the
X.RB ` \-n \  name '
Xcommand line option.
X.SH "PLAYING THE GAME"
XThe rules of the game are described in more depth below.
XFortunately, it is not necessary to understand the rules before playing
X.IR cubes ,
Xsince the cube server handles all of that for you.
XThe only thing to know before playing is what to do when prompted.
X.PP
XThere are only four questions the program will ask you.
XThe first question is
X.PP
X.B "	Ready to roll? [ye]"
X.PP
Xwhich is asking you to make the first roll of your turn.
XThe letters
X.RB ` ye '
Xindicate that the two valid answers
Xare
X.RB ` y '
Xfor yes and
X.RB ` e '
Xfor exit (quit the game).
XThe default if you just type a carriage return is yes.
X.PP
XThe second question, of the form
X.PP
X.B "	nnn saved, mmm showing; command? [rhke]"
X.PP
Xis asking you whether you want to quit rolling or not,
Xand if not, which dice you want to roll.
XThe value of
X.B nnn
Xis the number of points you have from previous rolls.
XThe value of
X.B mmm
Xis the number of points from the dice you just rolled.
XIf you hold, the sum of
X.B nnn
Xand
X.B mmm
Xwill be added to your score.
XTo hold, type
X.RB ` h '
Xand a carriage return.
X.PP
XIf you wish to roll again, you can just type carriage return, and all
Xnonscoring dice will be rerolled (or all dice, if they've all scored).
XIf in addition to the nonscoring dice, you wish to roll some of the
Xscoring dice, you can type
X.RB ` r '
Xfollowed by the die numbers.
XThe
X.RB ` r '
Xis default, so you can omit it if you like (and you probably will).
XAlso, if you omit the dice numbers, only the nonscoring dice will be rolled.
XNote that the nonscoring dice will always be rolled,
Xwhether or not you specify them explicitly,
Xso there's really no point in doing so.
X.PP
XSometimes it may be more convenient to tell
X.I cubes
Xwhich dice you wish to keep rather than which dice you wish to roll.
XTo do this,
Xyou can type
X.RB ` k '
Xfollowed by a list of the dice you want to hold back.
XIf no dice are specified, all scoring dice are kept and only nonscoring
Xdice are rolled.
X.PP
XYou can also type
X.RB ` e '
Xin response to this question,
Xwhich will get you yet another question
X.PP
X.B "	Do you really want to quit the game? [ny]"
X.PP
Xthe default answer being
X.RB ` n '
Xfor no.
X.PP
XThe only other question the game will ask you is
X.PP
X.B "	Play another game? [y]"
X.PP
Xwhich is asking you if you'd like to try it all over again.
XYou have to type
X.RB ` y '
Xor carriage return to mean yes.
X.SH "SCORING COMBINATIONS"
XThe following is a description of each of the
X.I cubes
Xscoring combinations.
XBelow, the word ``face'' is used as shorthand for face value.
XFor a one, the face value is ten.
XFor all other faces, the face value is the same as the number of spots
Xon the face, that is, two for a two, three for a three, etc.
X.IP Points 12
XDescription
X.IP "300 * face"
X.I Five of a Kind
Xis the only combination that includes previously held dice.
X.I Five of a Kind
Xis when all five dice show the same face.
XThis combination can occur any time the dice are rolled
X(assuming that any held dice are showing the same face).
XSince held dice are part of the new combination,
Xto not be counted twice,
Xthe points for the old combination(s) are forgotten.
XAll five dice score.
X.IP 1500
XA
X.I Straight
Xis when all five dice are rolled to produce an uninterrupted sequence of
Xfive numbers, either one through five or two through six.
XAll five dice score.
X.IP "200 * face"
X.I Four of a Kind
Xis when four rolled dice show the same face.
XThis combination can occur only when four or five dice are rolled.
XFour dice score.
X.IP 400
XA
X.I Small Straight
Xis an uninterrupted sequence of four numbers.
XThe possibilities are one through four, two through five, or three through six.
XThis combination can occur only when four or five dice are rolled.
XFour dice score.
X.IP "100 * face"
X.I Three of a Kind
Xis three rolled dice showing the same face.
XThis combination can occur when three, four, or five dice are rolled.
XThree dice score.
X.IP 100
XAny
X.IR One ,
Xnot part of another combination, is a scoring combination unto itself.
XA
X.I One
Xcan occur at any time.
XEach
X.I One
Xis a scoring die.
X.IP 50
XAny
X.IR Five ,
Xnot part of another combination, is a scoring combination unto itself.
XA
X.I Five
Xcan occur at any time.
XEach
X.I Five
Xis a scoring die.
X.SH "ROLLING THE DICE"
XEach turn starts with the player rolling all five dice.
XWhenever the dice are rolled, if no scoring combination appears,
Xthe turn is over and any points accumulated during the turn are lost.
XIf some dice score,
Xthen the player can choose to roll again or to quit rolling.
XWhen the player quits, the points accumulated during the turn are
Xadded to the player's score.
XThis is the only way to score points.
X.PP
XWhen rerolling, all nonscoring dice are always part of the reroll.
XIf more than one scoring combination occurred on the prior roll,
Xthe player may also choose to reroll some scoring dice,
Xwith the restriction that at least one scoring combination must be held back.
XWhen scoring dice are rerolled,
Xthe points for the combinations they were part of are not added to
Xthe accumulated turn points.
X.PP
XWhen all five dice score, either in a single roll or in multiple rolls,
Xthe player may continue rolling with all five dice.
XThis is the same as beginning the turn afresh,
Xexcept that the turn points earned so far are retained (but are still
Xvulnerable to being lost).
X.PP
XA player must quit rolling at some time in order to earn points on the
Xscoreboard.
XThere are only two times when the player cannot choose to quit.
XThe first time is when the player has zero points, or in other words,
Xis not yet on the scoreboard.
XThe player must score at least 500 points in a turn to get
X.I On Board
Xand cannot quit before earning those points.
XSimilarly, to get
X.IR Off \  Board ,
Xor cross the 10,000 point
X.I Win Score
Xline, the player needs at least 500 turn points.
XThis threshold need be matched only when trying to cross 10,000 points;
Xonce beyond, the player can quit with any number of turn points.
X.SH "WINNING THE GAME"
XA player with 10,000 points or more at the end of a round of
Xturns is the winner, but only if that player's score exceeds
Xthe next best score by a
X.I Win Margin
Xof 250 points.
XWhile this condition is not met, play continues.
X.I Cubes
Xwill warn of a player's impending win.
X.SH "COMPUTER PLAYERS"
XSince it would be pointless to play the game alone,
Xthe cube server provides some automatic competition in the form of one or
Xmore computer players.
XOne such player,
X.BR CUBEX ,
Xplays in every game,
Xbut depending on the number of human players and some random factors,
Xone or more additional computer players may also join.
XEach computer player has its own distinct playing style that
Xdoes not vary from game to game.
XTheir competence ranges from mildly so to expert,
Xbut no matter how it may seem at times,
Xthe game is not rigged in their favor.
X.SH "SCORE FILE"
XThe cube server keeps a score file which contains information about each
Xhuman and computer player that has played
X.I cubes
Xon that system.
XRegardless of the name chosen to display on the scoreboard,
Xhuman players are recorded in the score file by login name.
XComputer players are recorded by their one and only name.
XAn auxiliary shell script called
X.I cuberank
Xis provided to display the contents of this system's
X.I cubes
Xscore file.
X.PP
XIt should be noted that quitting a game before it ends may result in a
Xlow score being added to your record.
XThis will not happen if you have zero points when you quit.
XQuitting at any other time could lower your ranking against other players.
X.SH MISCELLANY
XIt is possible to join a game in progress.
XYou will initially enter the game as an unseen observer,
Xand if the game is not too near ending
X(defined as the high score being 9,000 points or better),
Xyou will be added as an active player at the end of the current round.
XSince coming into a game late with no points could ruin your ranking,
Xthe cube server will give you an initial score of about 75% of the lowest score,
Xbut you still must meet the 500 turn point
X.I On Board
Xrequirement in order to begin advancing your score.
X.PP
XOn some systems,
Xit is possible to tell the state of the cube server by doing a
X.IR ps ( 1 )
X(or something similar) on it.
XIf a game is in progress,
Xthe command line of the process will have been altered to
Xshow the number of players, turn number, high score, and player up.
X.PP
XThere is some advantage to being the last player in the turn order.
XWhen a game is begun after the cube server has been idle,
Xthe players will be ordered in reverse-rank order (top-ranked player last).
XIn subsequent games, players will be ordered based on the score from
Xthe previous game, with best score last.
X.PP
XA control-Z (or whatever your suspend character is) will not cause
X.I cubes
Xto be suspended.
XSuspending your copy of the game would tie up other players.
XControl-Z is not without effect however;
Xtyping it will cause your screen to be cleared and the program
Xto enter a ``quiet'' mode.
XAnother control-Z will restore the program to its normal operation.
X.PP
XTo keep the game moving,
Xthere is a sixty second timeout on every question asked by
X.IR cubes .
XAlthough the program tries to handle timeouts gracefully,
Xthe best you can expect is that you will miss a turn
Xif you wait too long to roll.
X.PP
XComputer players, like some human players,
Xtend to get frustrated when they can't get on the board.
XOccasionally, computer players will quit the game with zero points.
X.BR CUBEX ,
Xbeing oblivious to other players,
Xand possessing a singularly strong desire to play
X.IR cubes ,
Xwill never quit the game.
X.PP
XSometimes computer proxies will take over for players who have quit
Xthe game (when and why this happens is a mystery).
XThese proxies play with a somewhat erratic style,
Xso they should be almost indiscernible from humans.
XNo notice is given to anybody when this happens.
XThe absentee player will be credited with the score that the proxy
Xmanages to attain by game end.
X.PP
XWhen rerolling,
Xthere are some synonyms for the four commands;
Xyou may find some more convenient to type.
XFor hold,
Xthe synonyms are
X.RB ` q '
Xfor quit
Xand
X.RB ` n '
Xfor no (don't roll again).
XFor roll,
Xthe synonyms are
X.RB ` t '
Xfor throw and
X.RB ` y '
Xfor yes (roll again).
XFor keep there is a single alias,
X.RB ` s '
Xfor save.
XYou may also type
X.RB ` x '
Xto mean exit.
X.SH HISTORY
XThis game is an adaptation of a ``traditional'' dice game the author learned
Xfrom a friend twelve years prior to writing this program.
XIf the name of the game was learned at the time, it has since been forgotten.
XThe name
X.I cubes
Xwas coined by the author as the name of this computer implementation of
Xthe game.
XIn the process of fine tuning the game of
X.IR cubes ,
Xsome liberties have been taken with the rules as learned.
XWhat follows is a list of differences between
X.I cubes
Xand the original (name unknown) dice game.
X.IP (1) 4
XThe 500 point
X.I Off Board
Xthreshold did not exist in the original.
XNo minimum turn score was necessary to cross 10,000 points.
X.IP (2)
XThe 250 point
X.I Win Margin
Xdid not exist in the original.
XThe player with the highest score (at or above 10,000, of course)
Xat the end of the round was the winner.
X.IP (3)
XThe scoring combination of
X.I Four of a Kind
Xdid not exist in the original.
X.IP (4)
XThe scoring combination
X.I Small Straight
Xdid not exist in the original.
X.IP (5)
XIn the original,
X.I Five of a Kind
Xcould be scored only in ones or fives.
X(Even in
X.IR cubes ,
Xscoring
X.I Five of a Kind
Xin anything other than ones or fives is rare.)
X.IP (6)
XAnd of course, in the original game, there were no computer players!
X.SH AUTHOR
XGreg Paris <gmp@rayssd.ray.com>
END_OF_cubes.6
if test 13761 -ne `wc -c <cubes.6`; then
    echo shar: \"cubes.6\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f dieopts.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"dieopts.c\"
else
echo shar: Extracting \"dieopts.c\" \(12872 characters\)
sed "s/^X//" >dieopts.c <<'END_OF_dieopts.c'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)dieopts.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif	lint
X
X#include	<strings.h>
X#include	"cubes.h"
X
X/*
X**	names: names of die faces, singular and plural
X**		Must be filled in to at least max of SIDES and NDICE!
X*/
Xchar   *numnames[][2]	= {
X	{ "zero",	"zeroes" },
X	{ "one",	"ones" },
X	{ "two",	"twos" },
X	{ "three",	"threes" },
X	{ "four",	"fours" },
X	{ "five",	"fives" },
X	{ "six",	"sixes" },
X};
X
X/*
X**	initdice: initialize dice status
X*/
Xinitdice(pd)
Xregister diceset   *pd;
X{
X	register int	d;
X
X	for(d = 0;d < NDICE;++d) {
X		pd->d_stat[d] = Free;		/* rolling this die */
X		pd->d_face[d] = BADFACE;	/* impossible value */
X		pd->d_comb[d] = Nothing;	/* no combination */
X	}
X	pd->d_best = Nothing;			/* no combination */
X	pd->d_again = True;				/* rolling again */
X	pd->d_rolling = NDICE;			/* rolling all dice */
X	pd->d_pts_roll = 0;				/* no points rolled */
X	pd->d_pts_dice = 0;				/* no points in set */
X	pd->d_pts_turn = 0;				/* no points in turn */
X	pd->d_mesg[0] = '\0';			/* no message */
X}
X
X/*
X**	pickup: similar to initdice but only does initialization
X**		for re-roll of (usually partial) dice set
X**
X**		non-scoring dice are Freed
X**		change Rolled dice to Free
X**		set Free dice faces to BADFACE and combs to Nothing
X**		change Taken dice to Held
X**		set Held dice combs to Previous
X**		resets d_rolling, d_best, d_pts_roll, and d_mesg
X*/
Xpickup(pd)
Xregister diceset   *pd;
X{
X	register int	d;
X
X	pd->d_best = Nothing;
X	pd->d_again = True;
X	pd->d_rolling = 0;
X	pd->d_pts_roll = 0;
X	pd->d_mesg[0] = '\0';
X
X	for(d = 0;d < NDICE;++d) {
X		if(pd->d_comb[d] == Nothing)
X			pd->d_stat[d] = Free;
X		switch(pd->d_stat[d]) {
X		case Rolled:
X			pd->d_stat[d] = Free;	/* fall */
X		case Free:
X			pd->d_face[d] = BADFACE;
X			pd->d_comb[d] = Nothing;
X			++pd->d_rolling;
X			break;
X		case Taken:
X			pd->d_stat[d] = Held;	/* fall */
X		case Held:
X			pd->d_comb[d] = Previous;
X			break;
X		}
X	}
X
X	/*
X	**	If no dice are free, then free them all.
X	*/
X	if(pd->d_rolling == 0) {
X		for(d = 0;d < NDICE;++d) {
X			pd->d_stat[d] = Free;
X			pd->d_face[d] = BADFACE;
X			pd->d_comb[d] = Nothing;
X		}
X		pd->d_rolling = NDICE;
X		pd->d_pts_dice = 0;
X	}
X}
X
X/*
X**	rolldice: roll free dice
X**		sets d_rolling to number of dice rolled
X**		sets d_again to False
X*/
Xrolldice(pd)
Xregister diceset   *pd;
X{
X	register int	d;
X
X	pd->d_again = False;
X	pd->d_best = Nothing;
X	pd->d_rolling = 0;
X	for(d = 0;d < NDICE;++d) {
X		if(pd->d_stat[d] == Free) {
X			pd->d_stat[d] = Rolled;
X			pd->d_face[d] = dieroll(SIDES);
X			pd->d_comb[d] = Nothing;
X			++pd->d_rolling;
X		}
X	}
X}
X
X/*
X**	evaluate: categorize dice marked as Rolled or Taken
X*/
Xevaluate(pd)
Xregister diceset   *pd;
X{
X	register int	d, s;
X	int				rolled, rshow[SIDES+1];
X	int				held;
X	int				minshow, maxshow;
X	int				len, aces, fives;
X	boolean			ok;
X
X	for(s = 1;s <= SIDES;++s)
X		rshow[s] = 0;
X		
X	pd->d_mesg[0] = '\0';
X	pd->d_best = Nothing;
X	rolled = held = 0;
X	for(d = 0;d < NDICE;++d) {
X		s = pd->d_face[d];
X		switch(pd->d_stat[d]) {
X		case Rolled:
X		case Taken:
X			pd->d_comb[d] = Nothing;
X			++rolled, ++rshow[s];
X			break;
X		case Held:
X			pd->d_comb[d] = Previous;
X			++held;
X			break;
X		default:
X			pd->d_stat[d] = Free;
X			pd->d_comb[d] = Nothing;
X			break;
X		}
X	}
X
X	/*
X	**	Check for All_of_a_kind.  All dice count.
X	**	Uses all dice.
X	*/
X	if((rolled + held) == NDICE) {
X		s = pd->d_face[0];
X		for(d = 1;d < NDICE;++d)
X			if(pd->d_face[d] != s)
X				break;
X		if(d == NDICE) {
X			for(d = 0;d < NDICE;++d)
X				pd->d_comb[d] = All_of_a_kind;
X			pd->d_best = All_of_a_kind;
X			sprintf(pd->d_mesg, "%s of a kind in %s",
X				NUMBER(NDICE), FACE(pd->d_face[0], NDICE));
X			return;
X		}
X	}
X
X	/*
X	**	Check for Straight.  Only rolled dice count.
X	**	Uses all dice.
X	*/
X	if(rolled == NDICE) {
X		ok = True, minshow = SIDES + 1, maxshow = 0;
X		for(s = 1;ok == True && s <= SIDES;++s) {
X			switch(rshow[s]) {
X			case 1:
X				if(s < minshow) minshow = s;
X				if(s > maxshow) maxshow = s;
X				break;
X			case 0:		break;
X			default:	ok = False; break;
X			}
X		}
X
X		if(ok == True && maxshow - minshow == NDICE - 1) {
X			for(d = 0;d < NDICE;++d)
X				pd->d_comb[d] = Straight;
X			pd->d_best = Straight;
X			strcpy(pd->d_mesg, "a straight");
X			return;
X		}
X	}
X
X#ifdef	FOUR
X	/*
X	**	Check for Four_of_a_kind.  Only rolled dice count.
X	**	Assumes that only one is possible.
X	*/
X	if(rolled >= FOUR) {
X		for(s = 1;s <= SIDES;++s) {
X			while(rshow[s] >= FOUR) {
X				maxshow = FOUR;
X				for(d = 0;d < NDICE && maxshow > 0;++d) {
X					switch(pd->d_stat[d]) {
X					case Rolled:
X					case Taken:
X						if(pd->d_comb[d] == Nothing && pd->d_face[d] == s) {
X							pd->d_comb[d] = Four_of_a_kind;
X							--rshow[s];
X							--maxshow;
X						}
X						break;
X					}
X				}
X
X				if(pd->d_best == Nothing)
X					pd->d_best = Four_of_a_kind;
X
X				if((len = strlen(pd->d_mesg)) > 0) {
X					strcat(pd->d_mesg, ", ");
X					len += 2;
X				}
X				sprintf(pd->d_mesg + len, "%s of a kind in %s",
X					NUMBER(FOUR), FACE(s, FOUR));
X			}
X		}
X	}
X#endif	FOUR
X
X#ifdef	SMSTR
X	/*
X	**	Check for Small_straight.  Only rolled dice count.
X	**	Assumes that only one is possible.
X	*/
X	if(rolled >= NDICE - 1) {
X		ok = False;
X		minshow = maxshow = 0;
X		for(s = 1;ok == False && s <= SIDES;++s) {
X			switch(rshow[s]) {
X			case 1: case 2:
X				if(minshow == 0)
X					minshow = maxshow = s;
X				else if((maxshow = s) - minshow == NDICE - 2)
X					ok = True;
X				break;
X			case 0:		minshow = maxshow = 0; break;
X			default:	s = SIDES; break;
X			}
X		}
X
X		if(ok == True) {
X			for(s = minshow;s <= maxshow;++s) {
X				for(ok = True, d = 0;ok == True && d < NDICE;++d) {
X					switch(pd->d_stat[d]) {
X					case Rolled:
X					case Taken:
X						if(pd->d_comb[d] == Nothing && pd->d_face[d] == s) {
X							pd->d_comb[d] = Small_straight;
X							--rshow[s];
X							ok = False;	/* got it */
X						}
X						break;
X					}
X				}
X			}
X			if(pd->d_best == Nothing)
X				pd->d_best = Small_straight;
X			if(pd->d_mesg[0] != '\0')
X				strcat(pd->d_mesg, ", ");
X			strcat(pd->d_mesg, "a small straight");
X		}
X	}
X#endif	SMSTR
X
X	/*
X	**	Check for Three_of_a_kind.  Only rolled dice count.
X	**	Code is supposed to be able to handle more than one
X	**	Three_of_a_kind, but is untested with current NDICE val.
X	*/
X	if(rolled >= THREE) {
X		for(s = 1;s <= SIDES;++s) {
X			while(rshow[s] >= THREE) {
X				maxshow = THREE;
X				for(d = 0;d < NDICE && maxshow > 0;++d) {
X					switch(pd->d_stat[d]) {
X					case Rolled:
X					case Taken:
X						if(pd->d_comb[d] == Nothing && pd->d_face[d] == s) {
X							pd->d_comb[d] = Three_of_a_kind;
X							--rshow[s];
X							--maxshow;
X						}
X						break;
X					}
X				}
X
X				if(pd->d_best == Nothing)
X					pd->d_best = Three_of_a_kind;
X
X				if((len = strlen(pd->d_mesg)) > 0) {
X					strcat(pd->d_mesg, ", ");
X					len += 2;
X				}
X				sprintf(pd->d_mesg + len, "%s of a kind in %s",
X					NUMBER(THREE), FACE(s, THREE));
X			}
X		}
X	}
X
X	/*
X	**	Check for stray Aces and Fives.  Only rolled dice count.
X	*/
X	aces = fives = 0;
X	for(d = 0;d < NDICE;++d) {
X		switch(pd->d_stat[d]) {
X		case Rolled:
X		case Taken:
X			if(pd->d_comb[d] == Nothing) {
X				switch(pd->d_face[d]) {
X				case ACE:
X					++aces;
X					pd->d_comb[d] = Ace;
X					if(pd->d_best == Nothing || pd->d_best == Five)
X						pd->d_best = Ace;
X					break;
X				case FIVE:
X					++fives;
X					pd->d_comb[d] = Five;
X					if(pd->d_best == Nothing)
X						pd->d_best = Five;
X					break;
X				}
X			}
X			break;
X		}
X	}
X
X	if(pd->d_best != Nothing) {
X		if(aces > 0) {
X			if((len = strlen(pd->d_mesg)) > 0) {
X				strcat(pd->d_mesg, ", ");
X				len += 2;
X			}
X			sprintf(pd->d_mesg + len, "%s %s",
X				NUMBER(aces), FACE(ACE, aces));
X		}
X
X		if(fives > 0) {
X			if((len = strlen(pd->d_mesg)) > 0) {
X				strcat(pd->d_mesg, ", ");
X				len += 2;
X			}
X			sprintf(pd->d_mesg + len, "%s %s",
X				NUMBER(fives), FACE(FIVE, fives));
X		}
X	}
X
X	if(pd->d_best == Nothing)
X		strcpy(pd->d_mesg, "nothing");
X}
X
X/*
X**	keepall: mark all scoring dice (based on combinations) as Taken
X**		frees Nothing dice and marks as Held Previous dice
X**		sets d_rolling to number of Free dice
X**		does not alter d_again or d_pts_????
X*/
Xkeepall(pd)
Xregister diceset   *pd;
X{
X	register int	d;
X
X	pd->d_rolling = 0;
X	for(d = 0;d < NDICE;++d) {
X		switch(pd->d_comb[d]) {
X		case Previous:
X			pd->d_stat[d] = Held;
X			break;
X		case Nothing:
X			pd->d_stat[d] = Free;
X			++pd->d_rolling;
X			break;
X		default:
X			pd->d_stat[d] = Taken;
X			break;
X		}
X	}
X}
X
X/*
X**	freeall: mark all non-held dice (based on combinations) as Free
X**		sets d_rolling to number of Free dice
X**		does not alter d_again or d_pts_????
X*/
Xfreeall(pd)
Xregister diceset   *pd;
X{
X	register int	d;
X
X	pd->d_rolling = 0;
X	for(d = 0;d < NDICE;++d) {
X		switch(pd->d_comb[d]) {
X		case Previous:
X			pd->d_stat[d] = Held;
X			break;
X		default:
X			pd->d_stat[d] = Free;
X			++pd->d_rolling;
X			break;
X		}
X	}
X}
X
X/*
X**	scoredice: update points values after roll and decision
X*/
Xscoredice(pd)
Xregister diceset   *pd;
X{
X	register int	d, dd, n;
X	register int	points;
X	int				face;
X	combination		comb[NDICE];
X
X	/*
X	**	The easiest of all is the Nothing combination.  The points for the roll
X	**	are the negative of the points accumulated so far in this turn.
X	*/
X	if(pd->d_best == Nothing) {
X		pd->d_pts_roll = -pd->d_pts_turn;
X		pd->d_pts_dice = pd->d_pts_turn = 0;
X		return;
X	}
X
X	/*
X	**	A Straight can only occur when all dice are being rolled, so d_pts_roll
X	**	and d_pts_dice should be zero.  The value of a Straight is a constant.
X	*/
X	if(pd->d_best == Straight) {
X		points = P_STRAIGHT;
X		pd->d_pts_roll = points;
X		pd->d_pts_dice = points;
X		pd->d_pts_turn += points;
X		return;
X	}
X
X	/*
X	**	All_of_a_kind can occur at any time and changes the value of any saved
X	**	dice.  The value of d_pts_dice must be used to update d_pts_roll and
X	**	d_pts_turn.  Further, the value of All_of_a_kind depends on its
X	**	denomination.
X	*/
X	if(pd->d_best == All_of_a_kind) {
X		face = pd->d_face[0];
X		points = P_AOKMULT * (face == ACE ? P_ACEMULT : face);
X		pd->d_pts_roll = points - pd->d_pts_dice;
X		pd->d_pts_turn += pd->d_pts_roll;
X		return;
X	}
X
X	/*
X	**	The rest of the combinations can occur in concert.
X	*/
X	pd->d_pts_roll = 0;
X
X#ifdef	FOUR
X	/*
X	**	Four_of_a_kind is tricky. The value is dependent on the denomination,
X	**	but not all dice are used up.  Even worse, for the sake of generality,
X	**	we must assume that there could be more than one Four_of_a_kind.
X	**	This code assumes that Four_of_a_kind and Small_straight will
X	**	not occur at the same time.
X	*/
X	if(pd->d_best == Four_of_a_kind) {
X		for(d = 0;d < NDICE;++d)
X			comb[d] = pd->d_comb[d];
X		for(d = 0;d < NDICE;++d) {
X			if(comb[d] == Four_of_a_kind) {
X				/*
X				**	Award the points for this one.
X				*/
X				face = pd->d_face[d];
X				points = P_4OKMULT * (face == ACE ? P_ACEMULT : face);
X				pd->d_pts_roll += points;
X				pd->d_pts_dice += points;
X				pd->d_pts_turn += points;
X
X				/*
X				**	Obliterate the scoring dice.
X				*/
X				for(n = FOUR, dd = d;dd < NDICE;++dd) {
X					if(comb[dd] != Four_of_a_kind)
X						continue;
X					if(pd->d_face[dd] != face)
X						continue;
X					comb[dd] = Nothing;
X					if(--n == 0)
X						break;
X				}
X			}
X		}
X	}
X#endif	FOUR
X
X#ifdef	SMSTR
X	/*
X	**	Score a Small_straight.  This code assumes that Four_of_a_kind
X	**	(or Three_of_a_kind) and Small_straight will not occur at the
X	**	same time.  It also assumes that only one Small_straight can
X	**	occur at a time.
X	*/
X	if(pd->d_best == Small_straight) {
X		points = P_SMSTR;
X		pd->d_pts_roll += points;
X		pd->d_pts_dice += points;
X		pd->d_pts_turn += points;
X	}
X#endif	SMSTR
X
X	/*
X	**	Three_of_a_kind is tricky. The value is dependent on the denomination,
X	**	but not all dice are used up.  Even worse, for the sake of generality,
X	**	we must assume that there could be more than one Three_of_a_kind.
X	**	Assumes that Three_of_a_kind does not occur in conjunction with
X	**	Four_of_a_kind or Small_straight.
X	*/
X	if(pd->d_best == Three_of_a_kind) {
X		for(d = 0;d < NDICE;++d)
X			comb[d] = pd->d_comb[d];
X		for(d = 0;d < NDICE;++d) {
X			if(comb[d] == Three_of_a_kind) {
X				/*
X				**	Award the points for this one.
X				*/
X				face = pd->d_face[d];
X				points = P_3OKMULT * (face == ACE ? P_ACEMULT : face);
X				pd->d_pts_roll += points;
X				pd->d_pts_dice += points;
X				pd->d_pts_turn += points;
X
X				/*
X				**	Obliterate the scoring dice.
X				*/
X				for(n = THREE, dd = d;dd < NDICE;++dd) {
X					if(comb[dd] != Three_of_a_kind)
X						continue;
X					if(pd->d_face[dd] != face)
X						continue;
X					comb[dd] = Nothing;
X					if(--n == 0)
X						break;
X				}
X			}
X		}
X	}
X
X	/*
X	**	Finally we have Aces and Fives.  Easy.
X	*/
X	for(d = 0;d < NDICE;++d) {
X		switch(pd->d_comb[d]) {
X		case Ace:
X			points = P_ACE;
X			pd->d_pts_roll += points;
X			pd->d_pts_dice += points;
X			pd->d_pts_turn += points;
X			break;
X		case Five:
X			points = P_FIVE;
X			pd->d_pts_roll += points;
X			pd->d_pts_dice += points;
X			pd->d_pts_turn += points;
X			break;
X		default:
X			break;
X		}
X	}
X}
END_OF_dieopts.c
if test 12872 -ne `wc -c <dieopts.c`; then
    echo shar: \"dieopts.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f turn.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"turn.c\"
else
echo shar: Extracting \"turn.c\" \(10360 characters\)
sed "s/^X//" >turn.c <<'END_OF_turn.c'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)turn.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	"cubes.h"
X
Xextern int		turnnum;
Xextern player	plr[];
Xstatic boolean	reallyquit();
X
X/*
X**	turn: do a single player's turn
X**		return turn score or -1 if player left game
X*/
Xturn(c)
Xregister int	c;
X{
X	diceset		dice;
X
X	/*
X	**	Announce who's up to the world.
X	*/
X	if(annturn(c) < 0)
X		return -1;
X
X	/*
X	**	Reset the dice to their initial state.
X	*/
X	initdice(&dice);
X	showdice(c, &dice);
X	if(plr[c].p_stat != Computer)
X		if(pfirst(c) == -2)		/* timeout */
X			return 0;			/* missed turn */
X	if(plr[c].p_stat == Computer)
X		if(cfirst(c) < 0)
X			return -1;
X
X	for(;;) {
X		/*
X		**	Roll the dice, evaluate the roll and display it to all players.
X		**	If Nothing rolled, then this turn is over with no points scored.
X		*/
X		rolldice(&dice);
X		evaluate(&dice);
X		if(dice.d_best == Nothing) {
X			scoredice(&dice);
X			showdice(c, &dice);
X			sleep(2);
X			return 0;
X		}
X		showdice(c, &dice);
X
X		/*
X		**	If something was scored on the roll, then we ask the player what
X		**	to do about it.  We must re-evaluate and redisplay the roll after
X		**	asking since the scoring combinations may have changed.  We check
X		**	for Nothing scored as a precaution, but that situation is not
X		**	allowed by cquery or pquery.  Adjust point values.
X		*/
X		if(plr[c].p_stat != Computer)
X			if(pquery(c, &dice) == -2)	/* timeout */
X				return 0;				/* missed turn */
X		if(plr[c].p_stat == Computer)
X			cquery(c, &dice);
X		evaluate(&dice);
X		scoredice(&dice);
X		if(dice.d_best == Nothing) {
X			fprintf(stderr, "turn: %s `%s' tried to roll all scoring dice\n",
X			  plr[c].p_stat == Computer ? "Computer" : "Player",
X			  plr[c].p_name);
X			showdice(c, &dice);
X			sleep(2);
X			return 0;
X		}
X
X		/*
X		**	If the player held, we display the dice and return points scored.
X		*/
X		if(dice.d_again == False) {
X			strcpy(dice.d_mesg, "held");
X			showdice(c, &dice);
X			return dice.d_pts_turn;
X		}
X
X		/*
X		**	Reset and redisplay dice prior to next roll.
X		*/
X		pickup(&dice);
X		showdice(c, &dice);
X	}
X}
X
X/*
X**	pquery: ask the player what to do next
X**
X**	choices are:
X**		EXit			leave the game
X**		Hold/Quit		end turn (no more rolling)
X**		Keep/Save		score named dice and roll again
X**		Roll/Throw		throw named dice (scoring those left)
X*/
Xpquery(c, pd)
Xint					c;
Xregister diceset   *pd;
X{
X	register int	n, d;
X	int				stay;
X	boolean			error;
X	char			msgbuf[MESGLEN];
X	char			cmd[32], digits[32];
X	diceset			save, temp;
X
X	/*
X	**	If we got passed a nothing roll then we wonder why we are here
X	**	and return with a little comment.
X	*/
X	if(pd->d_best == Nothing) {
X		fprintf(stderr, "pquery: got a nothing roll\n");
X		sprintf(msgbuf, "%d You rolled a big zip.\r\n", M_INFO);
X		if(message(c, msgbuf) < 0)
X			return -1;
X		return 0;
X	}
X
X	/*
X	**	Calculate the number of points if we hold now.
X	*/
X	temp = *pd;
X	scoredice(&temp);
X	stay = temp.d_pts_turn;
X
X	save = *pd;			/* save a copy of the dice */
X	do {
X		error = False;	/* no error yet */
X		*pd = save;		/* get back the saved version */
X
X		/*
X		**	Send prompt.  Read and parse response.
X		*/
X		sprintf(msgbuf, "%d %d saved, %d showing; command? [rkhe]\r\n",
X			M_KEEP, pd->d_pts_turn, stay - pd->d_pts_turn);
X		if(message(c, msgbuf) < 0)
X			return -1;
X		switch(reply(c, msgbuf, sizeof msgbuf)) {
X		case -2: msgbuf[0] = '\0'; break;	/* timeout, do default */
X		case -1: return -1;					/* left game */
X		}
X		cmd[0] = '\0', digits[0] = '\0';
X		(void) sscanf(msgbuf, "%[^0123456789]%[0123456789]", cmd, digits);
X
X		switch(cmd[0]) {
X		case 'e': case 'E':		/* Exit -- leave game */
X		case 'x': case 'X':		/* eXit -- leave game */
X			if(reallyquit(c) == True)
X				return -1;
X			error = True;
X			break;
X
X		case 'n': case 'N':		/* No -- don't roll again */
X		case 'h': case 'H':		/* Hold -- hold at current score */
X		case 'q': case 'Q':		/* Quit -- quit rolling */
X			pd->d_again = False;		/* quit rolling */
X			keepall(pd);				/* keep all scoring dice */
X			temp = *pd;					/* temporary for score evaluation */
X			scoredice(&temp);			/* calculate turn points */
X			if(plr[c].p_onboard == False) {	/* not on board yet */
X				if(temp.d_pts_turn < ONBOARD) {	/* threshold */
X					sprintf(msgbuf,
X						"%d You can't hold until you get %d points.\r\n",
X						M_ARGE, ONBOARD);
X					if(message(c, msgbuf) < 0)
X						return -1;
X					error = True;
X					break;
X				}
X			} else if(plr[c].p_score + temp.d_pts_turn >= WINSCORE
X			  && plr[c].p_score < WINSCORE) {	/* about to go off board */
X				if(temp.d_pts_turn < OFFBOARD) {	/* threshold */
X					sprintf(msgbuf,
X						"%d You can't hold until you get %d points.\r\n",
X						M_ARGE, OFFBOARD);
X					if(message(c, msgbuf) < 0)
X						return -1;
X					error = True;
X					break;
X				}
X			}
X			return 0;					/* all done */
X
X		case 'k': case 'K':		/* Keep -- keep only named dice */
X		case 's': case 'S':		/* Save -- save only named dice */
X			pd->d_again = True;			/* roll again */
X			if(digits[0] == '\0') {		/* no dice named */
X				keepall(pd);			/* default is to keep all */
X				return 0;				/* all done */
X			}
X			freeall(pd);				/* free all rolled dice */
X			for(n = 0;digits[n] != '\0';++n) {
X				if((d = digits[n] - '1') < 0 || d >= NDICE) {
X					sprintf(msgbuf, "%d Die number is out of range.\r\n",
X						M_ARGE);
X					if(message(c, msgbuf) < 0)
X						return -1;
X					error = True;
X					break;
X				}
X				if(pd->d_comb[d] == Nothing) {
X					sprintf(msgbuf,
X						"%d You can keep only scoring dice.\r\n", M_ARGE);
X					if(message(c, msgbuf) < 0)
X						return -1;
X					error = True;
X					break;
X				}
X				if(pd->d_comb[d] == Previous || pd->d_stat[d] != Free) {
X					sprintf(msgbuf,
X						"%d You can keep only rolled dice.\r\n", M_ARGE);
X					if(message(c, msgbuf) < 0)
X						return -1;
X					error = True;
X					break;
X				}
X				pd->d_stat[d] = Taken;
X			}
X			break;
X
X		case '\0':				/* default command */
X		case 'y': case 'Y':		/* Yes -- roll again */
X		case 'r': case 'R':		/* Roll -- roll named dice and non-scoring */
X		case 't': case 'T':		/* Throw -- throw named dice and non-scoring */
X			pd->d_again = True;			/* roll again */
X			if(digits[0] == '\0') {		/* no dice named */
X				keepall(pd);			/* default is to keep all */
X				return 0;				/* all done */
X			}
X			keepall(pd);				/* keep all rolled, scoring dice */
X			for(n = 0;digits[n] != '\0';++n) {
X				if((d = digits[n] - '1') < 0 || d >= NDICE) {
X					sprintf(msgbuf, "%d Die number is out of range.\r\n",
X						M_ARGE);
X					if(message(c, msgbuf) < 0)
X						return -1;
X					error = True;
X					break;
X				}
X				if(pd->d_comb[d] == Previous || pd->d_stat[d] == Held) {
X					sprintf(msgbuf,
X						"%d You can't roll previously held dice.\r\n", M_ARGE);
X					if(message(c, msgbuf) < 0)
X						return -1;
X					error = True;
X					break;
X				}
X				if(pd->d_stat[d] != Free) {
X					pd->d_stat[d] = Free;
X					++pd->d_rolling;
X				}
X			}
X			break;
X
X		default:
X			sprintf(msgbuf,
X				"%d Valid commands are Exit, Hold, Keep, Roll.\r\n", M_ARGE);
X			if(message(c, msgbuf) < 0)
X				return -1;
X			error = True;
X			break;
X		}
X
X		/*
X		**	If we've arrived here without error, then a subset of the rolled
X		**	dice was chosen.  Get a temporary copy of the roll as it stands
X		**	and evaluate it.  If nothing scored it's an error.
X		*/
X		if(error == False) {
X			temp = *pd;
X			evaluate(&temp);
X			if(temp.d_best == Nothing) {
X				sprintf(msgbuf,
X					"%d You must save a scoring combination.\r\n", M_ARGE);
X				if(message(c, msgbuf) < 0)
X					return -1;
X				error = True;
X			}
X		}
X	} while(error == True);
X
X	return 0;
X}
X
X/*
X**	annturn: announce who's turn it is
X*/
Xannturn(c)
Xint		c;
X{
X	char	msgbuf[MESGLEN];
X
X	if(plr[c].p_stat != Computer) {
X		sprintf(msgbuf,
X			"%d Turn %d: player %d, you are up.\r\n", M_TURN, turnnum, c);
X		if(message(c, msgbuf) < 0)
X			return -1;
X	}
X	sprintf(msgbuf, "%d Turn %d: player %d, %s, is up.\r\n",
X		M_TURN, turnnum, c, plr[c].p_name);
X	(void) announce(c, msgbuf);
X	return 0;
X}
X
X/*
X**	statchar: map die status to a single character
X*/
Xstatchar(s)
Xdiestat	s;
X{
X	switch(s) {
X	case Free:		return 'f';
X	case Held:		return 'h';
X	case Taken:		return 't';
X	case Rolled:	return 'r';
X	default:		return '?';
X	}
X}
X
X/*
X**	facechar: map die face to a single character
X*/
Xfacechar(f)
Xint		f;
X{
X	return (f == BADFACE) ? '*' : (f + '0');
X}
X
X/*
X**	combchar: map die combination to a single character
X*/
Xcombchar(c)
Xcombination	c;
X{
X	switch(c) {
X	case Previous:			return 'p';
X	case Nothing:			return 'n';
X	case Five:				return '5';
X	case Ace:				return '1';
X	case Three_of_a_kind:	return 't';
X	case Small_straight:	return 'l';	/* l for little */
X	case Four_of_a_kind:	return 'f';
X	case Straight:			return 's';
X	case All_of_a_kind:		return 'a';
X	default:				return '?';
X	}
X}
X
X/*
X**	showdice: present the current status of the dice to all players
X*/
Xshowdice(c, pd)
Xint					c;
Xregister diceset   *pd;
X{
X	register int	d, cc;
X	char			stats[NDICE+1];
X	char			faces[NDICE+1];
X	char			combs[NDICE+1];
X	char			msgbuf[MESGLEN+4*NDICE+32];
X
X	for(d = 0;d < NDICE;++d) {
X		stats[d] = statchar(pd->d_stat[d]);
X		faces[d] = facechar(pd->d_face[d]);
X		combs[d] = combchar(pd->d_comb[d]);
X	}
X	stats[d] = faces[d] = combs[d] = '\0';
X
X	sprintf(msgbuf, "%d %d %s %s %s %d %d %d %s\r\n",
X		M_DICE, c, stats, faces, combs,
X		pd->d_pts_roll, pd->d_pts_dice, pd->d_pts_turn,
X		pd->d_mesg);
X
X	for(cc = 0;cc < PLAYERS;++cc) {
X		switch(plr[cc].p_stat) {
X		case Active:
X		case Waiting:
X			(void) message(cc, msgbuf);
X			break;
X		}
X	}
X}
X
X/*
X**	pfirst: query player for first roll of dice
X*/
Xpfirst(c)
Xint		c;
X{
X	char	msgbuf[MESGLEN];
X	int		n;
X
X	sprintf(msgbuf, "%d Ready to roll? [ye]\r\n", M_RFST);
X	if(message(c, msgbuf) < 0)
X		return -1;
X	if((n = reply(c, msgbuf, sizeof msgbuf)) < 0)
X		return n;
X
X	switch(msgbuf[0]) {
X	case 'e': case 'E':
X	case 'x': case 'X':
X		if(reallyquit(c) == True)
X			return -1;
X		break;
X	}
X
X	return 0;
X}
X
X/*
X**	reallyquit: does player really want to quit?
X*/
Xstatic boolean
Xreallyquit(c)
Xint		c;
X{
X	char	msgbuf[128];
X
X	sprintf(msgbuf, "%d Do you really want to quit the game? [ny]\r\n", M_RFST);
X	if(message(c, msgbuf) < 0 || reply(c, msgbuf, sizeof msgbuf) < 0)
X		return True;
X
X	if(msgbuf[0] == 'y' || msgbuf[0] == 'Y') {
X		sprintf(msgbuf, "%d Quitters never win.\r\n", M_DOWN);
X		if(message(c, msgbuf) >= 0)
X			oldplayer(c);
X		return True;
X	}
X
X	return False;
X}
END_OF_turn.c
if test 10360 -ne `wc -c <turn.c`; then
    echo shar: \"turn.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 2 \(of 3\).
cp /dev/null ark2isdone
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