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

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

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



#! /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 4 (of 8)."
# Contents:  comptbl.c cubes.6 fullname.c turn.c
# Wrapped by billr@saab on Thu Apr 27 12:13:37 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'comptbl.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'comptbl.c'\"
else
echo shar: Extracting \"'comptbl.c'\" \(21117 characters\)
sed "s/^X//" >'comptbl.c' <<'END_OF_FILE'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)comptbl.c 5.1 (G.M. Paris) 89/01/22";
X#endif	lint
X
X/*
X**
X**	cubes 5.1  Copyright 1989 Gregory M. Paris
X**		Permission granted to redistribute on a no charge basis.
X**		All other rights are reserved.
X**
X*/
X
X/*
X**	This module is a real mixed bag.  Various functions and definitions
X**	that were necessary to compile a standalone history file analyzer
X**	have been moved here from all over, but mainly from cubeserver.c.
X**	It still takes some dummy stubs to compile...
X*/
X
X#include	<stdio.h>
X#include	<syslog.h>
X#include	<strings.h>
X#include	"cubes.h"
X
Xextern int			winscore;
Xextern boolean		inprogress;
Xextern winpref		gametype;
Xextern boolean		nameinuse();
Xextern boolean		iskamelion();
Xextern boolean		kamelionplaying();
Xextern unsigned		nhist;
Xextern history	   *hist;
Xextern history	   *histbyname();
Xextern char		   *malloc();
Xextern char		   *moniker();
X
Xplayer				plr[PLAYERS];	/* defined here for standalone use */
X
Xextern strategy		st_basic, st_merrier, st_respect, st_twohater, st_greedy;
Xextern strategy		st_picky, st_finicky, st_choosy, st_kamelion, st_fickle;
Xextern temper		te_robot, te_antsy, te_tracker, te_chicken, te_gambler;
Xextern temper		te_crafty, te_shakey, te_zeno, te_goalie, te_best;
Xextern temper		te_kamelion, te_schizo;
X
X/*
X**	comptbl: list of available computer players
X**		comments indicate the source of each name
X*/
Xcomputer	comptbl[]	= {
X{ "CUBEX",			&st_basic,		&te_robot,		Nopref },	/* original */
X{ "UNIBLAB",		&st_merrier,	&te_antsy,		Fickle },	/* Jetsons */
X{ "Rosie",			&st_choosy,		&te_goalie,		Nopref },	/* Jetsons */
X{ "Brainiac",		&st_twohater,	&te_chicken,	Standard },	/* comics */
X{ "Wotan",			&st_greedy,		&te_goalie,		Nopref },	/* Dr. Who */
X{ "K-2",			&st_choosy,		&te_robot,		Nopref },	/* Dr. Who */
X{ "K-9",			&st_basic,		&te_goalie,		Nopref },	/* Dr. Who */
X{ "M5",				&st_merrier,	&te_tracker,	Nopref },	/* Star Trek */
X{ "NOMAD",			&st_respect,	&te_goalie,		Nopref },	/* Star Trek */
X{ "Landrew",		&st_twohater,	&te_gambler,	Blitz },	/* Star Trek */
X{ "Norman",			&st_choosy,		&te_zeno,		Nopref },	/* Star Trek */
X{ "Stella 500",		&st_picky,		&te_antsy,		Nopref },	/* Star Trek */
X{ "Alice 3",		&st_choosy,		&te_tracker,	Nopref },	/* Star Trek */
X{ "The Old Man",	&st_merrier,	&te_chicken,	Fstand },	/* T. Zone */
X{ "Agnes",			&st_respect,	&te_gambler,	Blitz },	/* T. Zone */
X{ "Dr. Theopolis",	&st_twohater,	&te_robot,		Nopref },	/* B. Rogers */
X{ "Tweekie",		&st_greedy,		&te_antsy,		Nopref },	/* B. Rogers */
X{ "Gort",			&st_picky,		&te_tracker,	Nopref },	/* tDtESS */
X{ "Robbie",			&st_basic,		&te_chicken,	Standard },	/* various */
X{ "HAL9000",		&st_merrier,	&te_gambler,	Fblitz },	/* Clarke */
X{ "SAL9000",		&st_respect,	&te_robot,		Nopref },	/* Clarke */
X{ "Mother",			&st_twohater,	&te_antsy,		Nopref },	/* Alien */
X{ "Ashe",			&st_greedy,		&te_tracker,	Nopref },	/* Alien */
X{ "Bishop",			&st_picky,		&te_chicken,	Standard },	/* Aliens */
X{ "W.O.P.R.",		&st_basic,		&te_gambler,	Blitz },	/* War Games */
X{ "The Terminator",	&st_merrier,	&te_robot,		Nopref },	/* title role */
X{ "Number Five",	&st_respect,	&te_antsy,		Nopref },	/* title role */
X{ "Jenkins",		&st_twohater,	&te_tracker,	Nopref },	/* Simak */
X{ "R. Daneel Olivaw",&st_greedy,	&te_chicken,	Standard },	/* Asimov */
X{ "GAX",			&st_picky,		&te_gambler,	Blitz },	/* Software */
X{ "TEX",			&st_basic,		&te_robot,		Nopref },	/* Software */
X{ "MEX",			&st_choosy,		&te_antsy,		Nopref },	/* Software */
X{ "BEX",			&st_respect,	&te_tracker,	Nopref },	/* Software */
X{ "DEX",			&st_choosy,		&te_chicken,	Standard },	/* Software */
X{ "LIBEX",			&st_greedy,		&te_gambler,	Blitz },	/* Wetware */
X{ "Mr. Frostee",	&st_picky,		&te_robot,		Nopref },	/* Software */
X{ "Ralph Numbers",	&st_basic,		&te_antsy,		Nopref },	/* Software */
X{ "Wagstaff",		&st_merrier,	&te_goalie,		Nopref },	/* Software */
X{ "Berenice",		&st_respect,	&te_chicken,	Standard },	/* Wetware */
X{ "Emul",			&st_twohater,	&te_goalie,		Nopref },	/* Wetware */
X{ "Ulalume",		&st_greedy,		&te_robot,		Nopref },	/* Wetware */
X{ "Oozer",			&st_picky,		&te_goalie,		Nopref },	/* Wetware */
X{ "Kkandio",		&st_basic,		&te_tracker,	Nopref },	/* Wetware */
X{ "Jander Panell",	&st_finicky,	&te_crafty,		Fickle },	/* Asimov */
X{ "Eddie",			&st_picky,		&te_shakey,		Fickle },	/* HHGttG */
X{ "Artoo Detoo",	&st_basic,		&te_zeno,		Nopref },	/* Star Wars */
X{ "Marvin",			&st_finicky,	&te_tracker,	Nopref },	/* HHGttG */
X{ "Giskard Reventlov",&st_picky,	&te_crafty,		Fickle },	/* Asimov */
X{ "Speedy",			&st_basic,		&te_shakey,		Fblitz },	/* Asimov */
X{ "See Threepio",	&st_finicky,	&te_zeno,		Nopref },	/* Star Wars */
X{ "R. Geronimo",	&st_basic,		&te_crafty,		Fickle },	/* Asimov */
X{ "Pondermatic",	&st_finicky,	&te_shakey,		Fstand },	/* HHGttG */
X{ "Deep Thought",	&st_picky,		&te_zeno,		Nopref },	/* HHGttG */
X{ "Xoanon",			&st_finicky,	&te_antsy,		Fickle },	/* Dr. Who */
X{ "R. Sammy",		&st_merrier,	&te_crafty,		Fickle },	/* Asimov */
X{ "Helen",			&st_merrier,	&te_zeno,		Nopref },	/* Wetware */
X{ "Huey",			&st_merrier,	&te_shakey,		Blitz },	/* Sil. Run. */
X{ "Duey",			&st_respect,	&te_shakey,		Blitz },	/* Sil. Run. */
X{ "Luey",			&st_choosy,		&te_shakey,		Blitz },	/* Sil. Run. */
X{ "the robot (B9)",	&st_finicky,	&te_robot,		Nopref },	/* L.i.Space */
X{ "SV7",			&st_respect,	&te_zeno,		Nopref },	/* Dr. Who */
X{ "Earth",			&st_choosy,		&te_gambler,	Fstand },	/* HHGttG */
X{ "Roderick",		&st_respect,	&te_crafty,		Fickle },	/* Sladek */
X{ "Tik-Tok",		&st_choosy,		&te_crafty,		Fickle },	/* Sladek */
X{ "IG-88",			&st_finicky,	&te_goalie,		Nopref },	/* S. Wars II */
X{ "Roger Korby",	&st_twohater,	&te_zeno,		Fickle },	/* Star Trek */
X{ "Rayna Kapec",	&st_finicky,	&te_chicken,	Nopref },	/* Star Trek */
X{ "V'ger",			&st_finicky,	&te_gambler,	Nopref },	/* ST:TMP */
X{ "M. Gargantubrain",&st_greedy,	&te_crafty,		Standard },	/* HHGttG */
X{ "Neutron Wrangler",&st_greedy,	&te_shakey,		Standard },	/* HHGttG */
X{ "M.P. Titan Muller",&st_greedy,	&te_zeno,		Standard },	/* HHGttG */
X{ "Ruk",			&st_twohater,	&te_crafty,		Fickle },	/* Star Trek */
X{ "Andrea",			&st_twohater,	&te_shakey,		Fickle },	/* Star Trek */
X{ "Orac",			&st_choosy,		&te_best,		Nopref },	/* Blake's 7 */
X/*
X**	If you have more names, mail them to me...
X**
X**	Kamelion must be last.
X*/
X{ "Kamelion",		&st_kamelion,	&te_kamelion,	Fickle }	/* Dr. Who */
X};
X
X#define	COMPUTERS	(sizeof comptbl / sizeof comptbl[0])
X
Xint			computers	= COMPUTERS;
Xcomputer   *pkamelion	= &comptbl[COMPUTERS - 1];
Xcomputer	proxy		= { "**proxy**", &st_fickle, &te_schizo, Nopref };
X
X/*
X**	initcomptbl: initialize computer table (no-op)
X*/
Xinitcomptbl() { }
X
X/*
X**	compbyname: return a pointer to the computer with the matching name
X*/
Xcomputer *
Xcompbyname(name)
Xregister char  *name;
X{
X	register int	n;
X
X	for(n = 0;n < computers;++n)
X		if(strncmp(name, comptbl[n].c_name, IDLEN) == 0)
X			return &comptbl[n];
X	
X	return (computer *)0;
X}
X
X/*
X**	addcomp: add the COMP computer
X**		a game is never in progress when this happens
X*/
Xaddcomp(c)
Xint		c;
X{
X	plr[c].p_fd = -1;
X	plr[c].p_score = 0;
X	plr[c].p_onboard = False;
X	plr[c].p_stat = Computer;
X	plr[c].p_id[0] = '\0';	/* non-proxies are null */
X	plr[c].p_computer = &comptbl[0];
X	strcpy(plr[c].p_name, plr[c].p_computer->c_name);
X	setpref(c);
X}
X
X/*
X**	pickproxy: give this player the proxy personality
X**		Doesn't change p_stat, in case this is a temporary proxy.
X*/
Xpickproxy(c)
Xint		c;
X{
X	if(plr[c].p_computer != &proxy)
X		plr[c].p_computer = &proxy;
X	if(plr[c].p_mood == NOMOOD)
X		plr[c].p_mood = randint(MAXMOOD);
X	/* don't change preference */
X}
X
X/*
X**	nextupcmp: comparison function for producing next-up order
X**		sorts computers before humans
X**		sorts the COMP computer before other computers
X**		sorts computers to have best play more often than worst
X**	NOTE: histsort() must be called first for proper operation.
X*/
Xnextupcmp(h1, h2)
Xhistory	   *h1, *h2;
X{
X	register int	diff;
X
X	/*
X	**	Computers before humans.
X	*/
X	if((h1->h_computer == 0) != (h2->h_computer == 0))
X		return h1->h_computer == 0 ? 1 : -1;
X	
X	/*
X	**	COMP computer before all others.
X	*/
X	if(h1->h_computer == &comptbl[0])
X		return -1;
X	if(h2->h_computer == &comptbl[0])
X		return  1;
X
X	/*
X	**	We cycle through the list of computers, but with the modification
X	**	that the higher ranked players will play more often than the lower.
X	*/
X	diff = 2 * (h1->h_lastgame - h2->h_lastgame);
X	diff += h1->h_rank - h2->h_rank;
X	if(diff != 0)
X		return diff;
X
X	/*
X	**	For tie breaking, the player with the least games played goes next.
X	*/
X	if((diff = h1->h_games - h2->h_games) != 0)
X		return diff;
X
X	/*
X	**	For further tiebreaking, prefer more wins, higher points, and
X	**	higher turn average, in that order.  (Note: games equal here.)
X	*/
X	if((diff = h2->h_wins - h1->h_wins) != 0)
X		return diff;
X	if((diff = h2->h_points - h1->h_points) != 0)
X		return diff;
X	if((diff = h2->h_avgturn - h1->h_avgturn) != 0)
X		return diff;
X
X	return 0;
X}
X
X/*
X**	pickcomputer: give this player an inactive computer personality
X*/
Xpickcomputer(c)
Xint		c;
X{
X	register int		comp;
X	register history   *thist;
X	unsigned			size;
X	static boolean		allinhist	= False;
X
X	plr[c].p_stat = Computer;
X	plr[c].p_id[0] = '\0';				/* not a proxy */
X	plr[c].p_mood = randint(MAXMOOD);	/* mood set on entering */
X
X	/*
X	**	Usually, we'd like to select computers that haven't played much
X	**	(the more highly ranked the better), but this method will never
X	**	pick a computer that's not in the history file.  What we do then
X	**	is randomly select this method, and if it fails, we still fall
X	**	back on random selection from the comptbl.
X	*/
X	thist = 0;
X	if(randint(100) <= 85) {
X		size = nhist * sizeof *hist;
X		if((thist = (history *)malloc(size)) == 0)
X			syslog(LOG_WARNING, "pickcomputer: no memory for dup history");
X	}
X	if(thist != 0) {
X		/*
X		**	Copy the history info and sort it into a desirable order.
X		**	Step through the list to find a computer player.
X		*/
X		bcopy((char *)hist, (char *)thist, (int)size);
X		qsort((char *)thist, (int)nhist, sizeof *thist, nextupcmp);
X
X		for(comp = 0;comp < (int)nhist;++comp) {
X			if((thist+comp)->h_computer == 0)
X				continue;
X			if(randint(3) != 1)		/* 67% chance */
X				continue;
X			plr[c].p_computer = (thist+comp)->h_computer;
X			if(iskamelion(c) == True) {
X				if(kamelionplaying(c) == True)
X					continue;
X				setkamelion(c);
X			} else {
X				if(nameinuse(c, plr[c].p_computer->c_name) == True)
X					continue;
X				strcpy(plr[c].p_name, plr[c].p_computer->c_name);
X				setpref(c);
X				if(inprogress == True) {	/* picky late joiners */
X					switch(plr[c].p_pref) {
X					case Fstand:	if(gametype != Standard) continue; break;
X					case Fblitz:	if(gametype != Blitz) continue; break;
X					case Standard:	if(gametype != Standard) continue; break;
X					case Blitz:		if(gametype != Blitz) continue; break;
X					}
X				}
X			}
X			free((char *)thist);
X			return;
X		}
X		free((char *)thist);
X	}
X
X	/*
X	**	Check to see if there are any computer players that have never played.
X	**	If one is found use it.  If none are found, make sure we don't waste
X	**	our time here again.  We don't do this every time we get here because
X	**	we want to ration out the new computers.
X	*/
X	if(allinhist == False && randint(100) <= 20) {
X		for(comp = 1;comp < computers;++comp) {
X			if(comptbl[comp].c_name[0] == '\0')		/* placeholder */
X				continue;
X			if(histbyname(comptbl[comp].c_name) != 0)
X				continue;
X			plr[c].p_computer = &comptbl[comp];
X			if(iskamelion(c) == True) {
X				if(kamelionplaying(c) == True)
X					continue;
X				setkamelion(c);
X			} else {
X				if(nameinuse(c, plr[c].p_computer->c_name) == True)
X					continue;
X				strcpy(plr[c].p_name, plr[c].p_computer->c_name);
X				setpref(c);
X				if(inprogress == True) {	/* semi-picky late joiners */
X					switch(plr[c].p_pref) {
X					case Fstand:	if(gametype != Standard) continue; break;
X					case Fblitz:	if(gametype != Blitz) continue; break;
X					}
X				}
X			}
X			return;
X		}
X		allinhist = True;
X	}
X
X	/*
X	**	Randomly select a computer from the comptbl.  This routine cannot
X	**	fail as long as PLAYERS is less than the number of computers.
X	*/
X	for(;;) {
X		comp = randint(computers - 1);					/* don't pick zero */
X		if(comptbl[comp].c_name[0] == '\0')				/* placeholder */
X			continue;
X		plr[c].p_computer = &comptbl[comp];
X		if(iskamelion(c) == True) {
X			if(kamelionplaying(c) == True)
X				continue;
X			setkamelion(c);
X		} else {
X			if(nameinuse(c, plr[c].p_computer->c_name) == True)
X				continue;
X			strcpy(plr[c].p_name, plr[c].p_computer->c_name);
X			setpref(c);
X			if(inprogress == True) {	/* semi-picky late joiners */
X				switch(plr[c].p_pref) {
X				case Fstand:	if(gametype != Standard) continue; break;
X				case Fblitz:	if(gametype != Blitz) continue; break;
X				}
X			}
X		}
X		return;
X	}
X}
X
X/*
X**	setkamelion: Kamelion adopts the personality of other computers
X**		and the moniker of other players
X*/
Xsetkamelion(c)
X{
X	register int		comp;
X	register char	   *aka;
X	register history   *phist;
X
X	/*
X	**	Pick a computer player's personality for Kamelion to mimic.
X	**	Kamelion wants to be a good player, so we pick from the top
X	**	of the history (best ranked players).
X	*/
X	for(comp = 0;comp < nhist;++comp) {
X		phist = hist + comp;
X		if(phist->h_computer == 0 || phist->h_computer == pkamelion)
X			continue;
X		if(inprogress == True) {	/* semi-picky late joiners */
X			switch(phist->h_computer->c_pref) {
X			case Fstand:	if(gametype != Standard) continue; break;
X			case Fblitz:	if(gametype != Blitz) continue; break;
X			}
X		}
X		if(randint(4) != 1)		/* 75% chance */
X			continue;
X		pkamelion->c_strategy->s_func = phist->h_computer->c_strategy->s_func;
X		pkamelion->c_temper->t_func = phist->h_computer->c_temper->t_func;
X		pkamelion->c_pref = phist->h_computer->c_pref;
X		break;
X	}
X
X	/*
X	**	If the above method failed, just select randomly from comptbl.
X	*/
X	if(comp >= nhist) {
X		do {
X			comp = randint(computers - 1);	/* don't pick zero, COMP */
X		} while(&comptbl[comp] == pkamelion);
X		pkamelion->c_strategy->s_func = comptbl[comp].c_strategy->s_func;
X		pkamelion->c_temper->t_func = comptbl[comp].c_temper->t_func;
X		pkamelion->c_pref = comptbl[comp].c_pref;
X	}
X
X	/*
X	**	Pick a name for Kamelion to assume.  Kamelion appears to be a proxy.
X	*/
X	while(nameinuse(c, (aka = moniker())) == True)
X		;
X	strncpy(plr[c].p_name, aka, NAMELEN);
X	plr[c].p_name[NAMELEN-1] = '\0';
X	strncpy(plr[c].p_id, pkamelion->c_name, IDLEN);
X	plr[c].p_id[IDLEN-1] = '\0';
X
X	/*
X	**	Make sure that p_computer is set.  Set preference.
X	*/
X	plr[c].p_computer = pkamelion;
X	setpref(c);
X}
X
X/*
X**	setpref: set p_pref for a computer player
X*/
Xsetpref(c)
X{
X	player *p	= &plr[c];
X
X	if(p->p_computer != 0) {
X		if((p->p_pref = p->p_computer->c_pref) == Fickle) {
X			switch(randint(9)) {
X			case 1: case 5: p->p_pref = Standard; break;
X			case 2: case 6: p->p_pref = Blitz; break;
X			case 3:         p->p_pref = Fstand; break;
X			case 4:         p->p_pref = Fblitz; break;
X			default:        p->p_pref = Nopref; break;
X			}
X		}
X	}
X}
X
X/*
X**	iscomp: return True if player is the COMP computer
X*/
Xboolean
Xiscomp(c)
X{
X	if(plr[c].p_stat != Computer)
X		return False;
X	if(plr[c].p_computer != &comptbl[0])
X		return False;
X	return True;
X}
X
X/*
X**	isproxy: return True if player is a proxy computer
X**		There are two kinds of proxies, permanent and temporary.
X**		Permanent proxy: p_stat == Computer, p_computer == &proxy.
X**		Temporary proxy: p_stat == Active, p_computer == &proxy.
X*/
Xboolean
Xisproxy(c)
X{
X	if(plr[c].p_computer == &proxy)
X		if(plr[c].p_stat == Computer || plr[c].p_stat == Active)
X			return True;
X	return False;
X}
X
X/*
X**	iskamelion: return True if player is Kamelion
X*/
Xboolean
Xiskamelion(c)
X{
X	if(plr[c].p_stat != Computer)
X		return False;
X	if(plr[c].p_computer != pkamelion)
X		return False;
X	return True;
X}
X
X/*
X**	nameinuse: return True if name in use by other than numbered player
X*/
Xboolean
Xnameinuse(c, name)
Xint		c;
Xchar   *name;
X{
X	register int	cc;
X
X	for(cc = 0;cc < PLAYERS;++cc)
X		if(cc != c && plr[cc].p_stat != Inactive)
X			if(strncmp(plr[cc].p_name, name, NAMELEN-1) == 0)
X				return True;
X
X	return False;
X}
X
X/*
X**	kamelionplaying: return True if Kamelion is already playing
X*/
Xboolean
Xkamelionplaying(comp)
X{
X	register int	c;
X
X	for(c = 0;c < PLAYERS;++c)
X		if(c != comp && iskamelion(c) == True)
X			return True;
X
X	return False;
X}
X
X/*
X**	highscore: find highest score amongst other players
X**		If phighc is not zero, stores index of high scorer
X**		or -1 if there's a tie. 
X*/
Xhighscore(comp, phighc)
Xint		comp, *phighc;
X{
X	register int	c;
X	int				high, highc;
X
X	high = 0, highc = -1;
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[(highc = c)].p_score;
X			else if(plr[c].p_score == high)
X				highc = -1;
X			break;
X		}
X	}
X
X	if(phighc != 0)
X		*phighc = highc;
X
X	return high;
X}
X
X/*
X**	sc_: structure for sorting scores
X*/
Xstruct sc_ { int sc_c, sc_p; };
X
X/*
X**	sc_comp: compare struct sc_ values, largest sc_p first
X*/
Xstatic int
Xsc_comp(s1, s2)
Xstruct sc_ *s1, *s2;
X{
X	return s2->sc_p - s1->sc_p;
X}
X
X/*
X**	closescore: find the highest score that's close enough to compete with
X**		If closec is not zero, stores index of the selected scorer
X**		or -1 if there's a tie.
X*/
Xclosescore(comp, pclosec)
Xint		comp, *pclosec;
X{
X	register int	c;
X	int				mine, diff;
X	int				high, highc;
X	struct sc_		sc[PLAYERS];
X
X#define	UPPERLIM	 3000	/* beyond this is too far ahead */
X#define	LOWERLIM	-1000	/* below this is too far behind */
X
X	/*
X	**	Sort the scores best first.
X	*/
X	for(c = 0;c < PLAYERS;++c) {
X		if((sc[c].sc_c = c) == comp) {
X			sc[c].sc_p = -1;	/* cause end search */
X			continue;
X		}
X		switch(plr[c].p_stat) {
X		case Computer:
X		case Active:
X			sc[c].sc_p = plr[c].p_score;
X			break;
X		default:
X			sc[c].sc_p = -2;	/* cause end search */
X			break;
X		}
X	}
X	qsort((char *)sc, PLAYERS, sizeof sc[0], sc_comp);
X
X	/*
X	**	Find a close score to track.
X	*/
X	mine = plr[comp].p_score;
X	high = 0, highc = -1;
X	for(c = 0;c < PLAYERS && sc[c].sc_p >= 0;++c) {
X		if((diff = sc[c].sc_p - mine) > UPPERLIM)
X			continue;
X		if(diff < LOWERLIM && c > 0)
X			--c;
X		high = sc[c].sc_p;
X		if(pclosec != 0) {
X			if(c+1 >= PLAYERS || sc[c+1].sc_p != high)
X				highc = sc[c].sc_c;
X		}
X		break;
X	}
X
X	if(pclosec != 0)
X		*pclosec = highc;
X
X	return high;
X
X#undef	UPPERLIM
X#undef	LOWERLIM
X}
X
X/*
X**	lowscore: report the lowest score
X**		Also calculates an average squander value.
X*/
Xlowscore(psqu)
Xint	   *psqu;
X{
X	register int	cc, low, squ, n;
X
X#define	HIGHVAL		(2^15)		/* "impossible" high score */
X
X	/*
X	**	Look for lowest score.  Sum squandered points.
X	*/
X	low = HIGHVAL, squ = 0, n = 0;
X	for(cc = 0;cc < PLAYERS;++cc) {
X		switch(plr[cc].p_stat) {
X		case Active:
X		case Computer:
X			if(plr[cc].p_score < low)
X				low = plr[cc].p_score;
X			if(psqu != 0)
X				squ += plr[cc].p_squander, ++n;
X			break;
X		}
X	}
X	if(low == HIGHVAL)
X		low = 0;
X
X	/*
X	**	If a non-null pointer was passed, calculate an
X	**	average squandered value and pass it back.
X	*/
X	if(psqu != 0) {
X		if(n == 0)
X			*psqu = 0;						/* no players?! */
X		else {
X			squ = (squ + n - 1) / n;		/* average, rounded up */
X			squ = squ - (squ % P_FIVE);		/* mult. of P_FIVE, rounded down */
X			*psqu = squ;
X		}
X	}
X
X	return low;
X
X#undef	HIGHVAL
X}
X
X/*
X**	winner: return winner number or -1 if none
X*/
Xwinner()
X{
X	register int	c;
X	register int	high, next;
X	register int	highc;
X
X	/*
X	**	Find high score.  If less than winscore, no winner.
X	*/
X	for(high = c = 0;c < PLAYERS;++c) {
X		switch(plr[c].p_stat) {
X		case Computer:
X		case Active:
X			if(plr[c].p_score > high)
X				high = plr[(highc = c)].p_score;
X			break;
X		}
X	}
X	if(high < winscore)
X		return -1;
X
X	/*
X	**	Find next to highest score.  If margin is less than
X	**	WINMARGIN, there's no winner yet.
X	*/
X	for(next = c = 0;c < PLAYERS;++c) {
X		if(c == highc)
X			continue;
X		switch(plr[c].p_stat) {
X		case Computer:
X		case Active:
X			if(plr[c].p_score > next)
X				next = plr[c].p_score;
X			break;
X		}
X	}
X	if(high - next < WINMARGIN)
X		return -1;
X	
X	/*
X	**	We have a winner!
X	*/
X	return highc;
X}
X
X/*
X**	plrcmp: player comparison function
X*/
Xplrcmp(p1, p2)
Xplayer *p1, *p2;
X{
X	int		diff;
X
X	/*
X	**	Sort by status first.
X	*/
X	if(p1->p_stat == p2->p_stat) {
X		switch(p1->p_stat) {
X		case Inactive:
X		case Watching:
X		case Waiting:
X			return 0;
X		}
X	} else {
X		switch(p2->p_stat) {
X		case Computer: diff = 3; break;		/* Computer == Active */
X		case Active:   diff = 3; break;		/* Computer == Active */
X		case Waiting:  diff = 2; break;
X		case Watching: diff = 1; break;
X		default:       diff = 0; break;
X		}
X		switch(p1->p_stat) {
X		case Computer: diff -= 3; break;	/* Computer == Active */
X		case Active:   diff -= 3; break;	/* Computer == Active */
X		case Waiting:  diff -= 2; break;
X		case Watching: diff -= 1; break;
X		default:                  break;
X		}
X		if(diff != 0) return diff;
X	}
X
X	/*
X	**	Sort in order of increasing score.
X	*/
X	if((diff = p1->p_score - p2->p_score) != 0)
X		return diff;
X	
X	/*
X	**	Put Computer players after humans.
X	*/
X	if(p1->p_stat != p2->p_stat)
X		return (p1->p_stat == Computer) ? 1 : -1;
X
X	/*
X	**	Sort alphabetically.
X	*/
X	return strcmp(p1->p_name, p2->p_name);
X}
END_OF_FILE
if test 21117 -ne `wc -c <'comptbl.c'`; then
    echo shar: \"'comptbl.c'\" unpacked with wrong size!
fi
# end of 'comptbl.c'
fi
if test -f 'cubes.6' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cubes.6'\"
else
echo shar: Extracting \"'cubes.6'\" \(8205 characters\)
sed "s/^X//" >'cubes.6' <<'END_OF_FILE'
X.TH CUBES 6 "cubes 5.1" GMP "UNIX Gaming Manual"
X.SH NAME
Xcubes \- single- or multi-player dice game
X.\"
X.\" sccsid: @(#)cubes.6 5.1 (G.M. Paris) 89/01/22
X.\"
X.\"
X.\"
X.\"	cubes 5.1  Copyright 1988 Gregory M. Paris
X.\"		Permission granted to redistribute on a no charge basis.
X.\"		All other rights are reserved.
X.\"
X.\"
X.SH SYNOPSIS
X.B cubes
X[
X.BR \- < options >
X] [
X.B name
X]
X.SH OVERVIEW
XThe game of
X.I cubes
Xis a dice game played with five dice.
X.PP
XPlayers take turns rolling the dice.
XPoints are scored by rolling one or more scoring combinations.
XAny time a roll fails to produce at least one scoring combination,
Xthat player's turn is over and no points are added to his score.
XEach time a roll produces one or more scoring combinations,
Xthe player has the choice to hold or roll again.
XIf he holds, his turn is over and
Xthe sum of points accumulated during the turn are added to his score.
X.PP
XThe first player to reach 10,000 points is declared the winner
Xand the game ends.
XAt that point,
Xthe score of each player is recorded in the score file.
XAnother program,
X.IR cuberank (6),
Xcan be used to display the score file,
Xranking all
X.I cubes
Xplayers from best to worst.
X.SH "THIS MANUAL PAGE"
X.nf
XThe rest of this manual page contains the following sections:
X.sp 1
X.in +0.5i
X.B BEFORE PLAYING
X.B ROLLING AND REROLLING
X.B SCORING COMBINATIONS
X.B STANDARD VS. BLITZ GAMES
X.B GETTING ON BOARD
X.B GETTING OFF BOARD
X.B OVERTIME GAMES
X.B MORE DOCUMENTATION
X.B AUTHOR
X.in -0.5i
X.fi
X.SH BEFORE PLAYING
XAlthough
X.I cubes
Xprovides prompts and error messages,
Xgives help messages in response to the ``?'' command,
Xcounts the points, determines the winner, and enforces the rules of the game,
Xit's a good idea to at least scan all of this manual page
Xbefore starting your first game.
X.PP
XAnother friendly piece of advice is, ``Don't quit!''\ 
XIt's possible to leave the game before it's over,
Xbut you'll probably get a lousy score recorded in the score file.
XIf you become a regular
X.IR cubes
Xplayer, you'll appreciate the fact that you
Xdidn't start off with a crummy ranking because you quit.
X.SH "ROLLING AND REROLLING"
XThe first roll of each turn is made with all five dice.
XIf no scoring combinations come up,
Xthe player's turn is over and no points are added to her score.
X.PP
XIf one or more scoring combinations show,
Xshe may choose to hold or roll again.
XIf she holds, her turn is over,
Xand the sum of the points of all showing combinations
Xare added to her score.
X.PP
XIf she chooses to reroll,
Xshe risks losing the points scored so far this turn.
XPoints due to each reroll are added to those of previous rolls,
Xbut none of them are added to her score until she holds.
X.PP
XUnless all five dice scored on the last roll,
Xthe player will reroll less than five dice.
XIf more than one combination scored on the last turn,
Xshe can pick up some of the dice that scored and reroll them along
Xwith the dice that didn't score.
XIf any scoring dice are picked up in this way,
Xthe points due to them are given up,
Xbut rolling more dice increases the chances of scoring
Xon the next roll.
XAt least one scoring combination must be left behind.
XSuch dice left behind are referred to as ``held'' dice.
X.PP
XRerolling can continue as long as dice score each time.
XWhenever all five dice have scored,
Xthe player gets to reroll all five dice.
X.SH "SCORING COMBINATIONS"
XA list of scoring combinations and what they're worth
Xare displayed on the screen when playing
X.IR cubes .
XA more complete description of each scoring combination
Xis described below.
XIn the descriptions,
Xthe word ``face'' is shorthand for face value.
XFor a one the face value is ten,
Xand for two through six,
Xthe face value is the same as the number of spots.
X.IP Points 12
XDescription
X.IP "face * 300"
X.I Five of a Kind
Xis one of two combinations that can include previously held dice.
X.I Five of a Kind
Xis all five dice showing the same face.
XThis combination can occur any time the dice are rolled.
XWhen held dice are part of this combination,
Xthe points they earned as part of previous combinations are forgotten.
XAll five dice score.
X.IP "face * 200"
X.I Four of a Kind
Xis four rolled dice showing the same face.
XThis combination can occur only when four or five dice are rolled.
XFour dice score.
X.IP "face * 100"
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 1500/750
XA
X.I Straight
Xis an uninterrupted sequence of five numbers,
Xeither one through five or two through six.
XIf all five dice are rolled to produce a
X.IR Straight ,
Xthe combination is worth 1500 points.
XIf it takes more than one roll to complete a
X.I Straight
X(thus using held dice),
Xthe combination is worth 750 points.
XAll five 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
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.IP 0
XAny
X.IR Joker ,
Xnot part of another combination, is a scoring combination unto itself,
Xbut unlike the
X.I One
Xand
X.IR Five ,
Xa single
X.I Joker
Xis not worth any points 
X(that's why a
X.I Joker
Xhas no spots, looking more like a big zero).
XHowever, the face value of a
X.I Joker
Xis a whopping thirty, thus making
X.IR Three ,
X.IR Four ,
Xand
X.I Five of a Kind
Xin
X.I Jokers
Xworth 3000, 6000, and 9000 points respectively.
X.B Note:
Xon your system
X.I cubes
Xmight not be configured to use dice with
X.I Jokers
Xon them, and even if so configured,
Xwill play only about one of six games with
X.IR Jokers .
XIf the game is to be played with
X.IR Jokers ,
Xyou will be notified when the game begins.
XYou may think of
X.I Joker
Xdice as having thirteen sides,
Xwith two of each of the faces one through six,
Xand a single
X.I Joker
Xface.
X.SH "STANDARD VS. BLITZ GAMES"
XOn your system,
X.I cubes
Xmay be configured to play two different types of games:
X.I Standard
Xand
X.IR Blitz .
X.I Standard
Xgames are to 10,000 points.
X.I Blitz
Xgames are to 7,500 points.
XYou will be told what kind of game you're playing
Xat the beginning of each, but
X.I cubes
Xalways displays this information on the screen during play.
X.SH "GETTING ON BOARD"
XSimilar to
X``Jack or better to open'' in poker,
X.I cubes
Xrequires each player to score at least 500 points in a turn
Xto ``get on board.''\ 
XEach player is not allowed to hold with less than 500 points
Xuntil he completes a turn that scores 500 or more points.
XSometimes it takes several turns to reach this magic threshold,
Xbut once reached,
Xon subsequent turns the player is free to hold with less than 500 points.
X.SH "GETTING OFF BOARD"
XSimilar to getting on board,
Xthere is a threshold to get off board.
XGetting off board means crossing 10,000 points in a
X.I Standard
Xgame, or crossing 7,500 points in a
X.I Blitz
Xgame.
XAs with getting on board,
X500 or more points in a turn are needed to get off board.
XIt may take several turns to accomplish this feat.
X.SH "OVERTIME GAMES"
XThere is a possibility that any game of
X.I cubes
Xcan go into overtime.
XThis is because there is an additional requirement to winning a game,
Xbeyond crossing the 10,000 point or 7,500 point threshold.
XThat requirement is that the winner's score must exceed
Xthe second place player's score by at least 250 points.
XUntil that happens, the game continues as usual.
X.SH "MORE DOCUMENTATION"
XThis manual page is intended to be a short and sweet introduction to
X.IR cubes .
XThere are many finer points about
X.I cubes
Xthat you may wish to learn about
Xonce you become familiar with the game.
XThe manual page detailing these points is in section 6
Xand is called
X.IR cubes.long .
XThere are also two auxiliary programs designed to keep you
Xup to date on the status of
X.I cubes
Xand its players.
XThe relevant manual pages (in section 6) are
X.I cuberank
Xand
X.IR cubestat .
X.SH "AUTHOR"
XGreg Paris <gmp@rayssd.ray.com>
END_OF_FILE
if test 8205 -ne `wc -c <'cubes.6'`; then
    echo shar: \"'cubes.6'\" unpacked with wrong size!
fi
# end of 'cubes.6'
fi
if test -f 'fullname.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'fullname.c'\"
else
echo shar: Extracting \"'fullname.c'\" \(1675 characters\)
sed "s/^X//" >'fullname.c' <<'END_OF_FILE'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)fullname.c 5.1 (G.M. Paris) 89/01/22";
X#endif	lint
X
X/*
X**
X**	cubes 5.1  Copyright 1989 Gregory M. Paris
X**		Permission granted to redistribute on a no charge basis.
X**		All other rights are reserved.
X**
X*/
X
X#ifdef	GECOS
X#include	<local/gecos.h>
X#else	GECOS
X#include	<pwd.h>
X#endif	GECOS
X#include	<strings.h>
X#include	"cubes.h"
X
X/*
X**	fullname: get full name from password file gecos field
XXXX		Returns a pointer to a static buffer.
X*/
Xchar *
Xfullname(pw)
Xstruct passwd  *pw;
X{
X	static char		name[NAMELEN];
X	register int	n;
X
X#ifdef	GECOS
X	/*
X	**	Use the SSD gecos routines to format the name optimally.
X	*/
X	if(ssd_set(pw) != 0) {
X		int		mlen;
X
X		if(strlen(pw->full_name) <= DISPLEN || pw->first_name[0] == '\0')
X			sprintf(name, "%.*s", DISPLEN, pw->full_name);
X		else if((mlen = strlen(pw->last_name)) >= DISPLEN-3)
X			sprintf(name, "%.*s", DISPLEN, pw->last_name);
X		else if(mlen + strlen(pw->first_name) <= DISPLEN-1)
X			sprintf(name, "%s %s", pw->first_name, pw->last_name);
X		else if(pw->middle_name[0] != '\0' && mlen <= DISPLEN-6)
X			sprintf(name, "%.1s. %.1s. %.*s", pw->first_name, pw->middle_name,
X				DISPLEN-6, pw->last_name);
X		else
X			sprintf(name, "%.1s. %.*s", pw->first_name,
X				DISPLEN-3, pw->last_name);
X		ssd_unset(pw);
X		return name;
X	}
X#endif	GECOS
X
X	/*
X	**	Copy up to comma or DISPLEN characters, whichever is shorter.
X	*/
X	for(n = 0;n < DISPLEN;++n) {
X		if((name[n] = pw->pw_gecos[n]) == ',')
X			break;
X		if(name[n] == '\0')
X			break;
X	}
X	name[n] = '\0';
X
X	/*
X	**	No name in gecos, so use login.
X	*/
X	if(name[0] == '\0')
X		sprintf(name, "%.*s", DISPLEN, pw->pw_name);
X
X	return name;
X}
END_OF_FILE
if test 1675 -ne `wc -c <'fullname.c'`; then
    echo shar: \"'fullname.c'\" unpacked with wrong size!
fi
# end of 'fullname.c'
fi
if test -f 'turn.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'turn.c'\"
else
echo shar: Extracting \"'turn.c'\" \(19603 characters\)
sed "s/^X//" >'turn.c' <<'END_OF_FILE'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)turn.c 5.1 (G.M. Paris) 89/01/22";
X#endif	lint
X
X/*
X**
X**	cubes 5.1  Copyright 1989 Gregory M. Paris
X**		Permission granted to redistribute on a no charge basis.
X**		All other rights are reserved.
X**
X*/
X
X#include	<stdio.h>
X#include	<syslog.h>
X#include	<strings.h>
X#include	<ctype.h>
X#include	"cubes.h"
X
Xextern boolean	inprogress;
Xextern int		winscore;
Xextern int		turnnum;
Xextern player	plr[];
Xextern char	   *histfmtplr();
Xextern long		histavgplr();
Xstatic boolean	reallyquit();
Xextern boolean	isproxy();
X
X/*
X**	turn: do a single player's turn
X**		Return turn score or 0 if player left game.
X**		If points were thrown away, return them as negative.
X*/
Xturn(c)
Xregister int	c;
X{
X	diceset		dice, hold;
X	int			winsqu;
X
X	/*
X	**	Announce who's up to the world.
X	*/
X	if(annturn(c) < 0)
X		return 0;				/* left game */
X
X	/*
X	**	Reset the dice to their initial state.
X	*/
X	initdice(&dice);
X	showdice(c, &dice);
X	if(plr[c].p_stat == Active && isproxy(c) == False) {
X		if(pfirst(c) == -2) {		/* timeout */
X			pickproxy(c);			/* auto-autopilot */
X			(void) tellstatus(c);	/* inform player */
X		}
X	}
X	if(plr[c].p_stat == Computer || isproxy(c) == True)
X		if(cfirst(c) < 0)
X			return 0;			/* left game */
X	if(plr[c].p_stat == Inactive)
X		return 0;				/* quit */
X
X	winsqu = 0;
X	for(;;) {
X		/*
X		**	Check for asynchronous requests from other players.
X		**	Then roll the dice and evaluate the roll.
X		*/
X		async(c);
X		rolldice(&dice);
X		evaluate(&dice);
X
X		/*
X		**	If Nothing rolled, this turn is over with no points scored.
X		**	We jump to the end of the routine for final processing.
X		*/
X		if(dice.d_best == Nothing)
X			goto gotnothing;
X
X		/*
X		**	Display the current state of the dice.
X		**	Score the points we'd have were we to hold now.
X		*/
X		showdice(c, &dice);
X		hold = dice;
X		scoredice(&hold);
X
X		/*
X		**	Trying to properly assess when points have been squandered,
X		**	we adjust the value of winsqu here.  This value represents
X		**	points that could have been held with before crossing the
X		**	winscore threshold.  If we've already crossed winscore,
X		**	we don't adjust this value.
X		*/
X		if(plr[c].p_score + hold.d_pts_turn < winscore)
X			if(hold.d_pts_turn > winsqu)
X				winsqu = hold.d_pts_turn;
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 == Active && isproxy(c) == False) {
X			if(pquery(c, &dice) == -2) {		/* timeout */
X				pickproxy(c);					/* auto-autopilot */
X				(void) tellstatus(c);			/* inform player */
X			}
X		}
X		if(plr[c].p_stat == Computer || isproxy(c) == True)
X			cquery(c, &dice);
X		if(plr[c].p_stat == Inactive)
X			return 0;							/* quit */
X		evaluate(&dice);
X		scoredice(&dice);
X		if(hold.d_pts_max > dice.d_pts_max)		/* discarded some points */
X			dice.d_pts_max = hold.d_pts_max;	/* reflect that fact */
X		if(dice.d_best == Nothing) {
X			syslog(LOG_WARNING, "turn: %s `%s' tried to roll all scoring dice",
X				plr[c].p_stat == Computer ? "Computer" : "Player",
X				plr[c].p_name);
X			goto gotnothing;
X		}
X
X		/*
X		**	If d_again is False, the player has chosen to hold.
X		**	Leave the loop and do the hold processing at routine end.
X		*/
X		if(dice.d_again == False) 
X			break;
X
X		/*
X		**	Otherwise, reset and redisplay dice prior to next roll.
X		*/
X		pickup(&dice);
X		showdice(c, &dice);
X	}
X
X	/*
X	**	We get here when dice.d_again is False.  Don't sleep or hesistate
X	**	here because it disrupts the rhythm of the game.  There's not much
X	**	new information at this point anyway.
X	*/
X	strcpy(dice.d_mesg, "held");
X	showdice(c, &dice);
X
X	/*
X	**	If not onboard yet, mark p_onboard True.  The [cp]query
X	**	routines should enforce ONBOARD, but note any anomaly.
X	*/
X	if(plr[c].p_onboard == False) {
X		if(dice.d_pts_turn < ONBOARD)
X			syslog(LOG_WARNING, "turn: %s held %d getting onboard",
X				plr[c].p_name, dice.d_pts_turn);
X		plr[c].p_onboard = True;
X	}
X
X	/*
X	**	If we reach winscore with less than OFFBOARD points,
X	**	note the condition.  OFFBOARD should be enforced by
X	**	the [cp]query routines.
X	*/
X	if(plr[c].p_score < winscore && dice.d_pts_turn < OFFBOARD)
X		if(plr[c].p_score + dice.d_pts_turn >= winscore)
X			syslog(LOG_WARNING, "turn: %s held %d getting offboard",
X				plr[c].p_name, dice.d_pts_turn);
X
X	/*
X	**	Turn points are added to the player's score.
X	**	The player may have discarded some points and not
X	**	won them back.  If so, add those points to p_squander.
X	*/
X	plr[c].p_score += dice.d_pts_turn;
X	if(dice.d_pts_turn < dice.d_pts_max)
X		plr[c].p_squander += dice.d_pts_max - dice.d_pts_turn;
X	return dice.d_pts_turn;				/* normal */
X
Xgotnothing:
X	/*
X	**	We get here if we rolled a nothing.  Score the dice.
X	*/
X	scoredice(&dice);
X
X	/*
X	**	If we aren't on board and didn't get ONBOARD points
X	**	during the turn, we don't count any points as squandered,
X	**	since the player had no choice.
X	*/
X	if(plr[c].p_onboard == False && dice.d_pts_max < ONBOARD)
X		dice.d_pts_max = 0;
X
X	/*
X	**	If the max points during the turn would have put us over,
X	**	but we didn't get OFFBOARD points, we don't count the
X	**	points as squandered, since the player had little choice.
X	**	This isn't always correct, since the player might have been
X	**	able to hold before crossing winscore.  We account for this
X	**	situation by making use of the value of winsqu, which is
X	**	the maximum number of points that could have been held
X	**	before going over the winscore threshold.
X	*/
X	if(plr[c].p_score < winscore && dice.d_pts_max < OFFBOARD)
X		if(plr[c].p_score + dice.d_pts_max >= winscore)
X			dice.d_pts_max = winsqu;
X
X	/*
X	**	Now that d_pts_max has been adjusted, we can show the dice.
X	*/
X	showdice(c, &dice);
X	sleep(2);
X
X	/*
X	**	Squandered d_pts_max, no points gained.
X	*/
X	plr[c].p_squander += dice.d_pts_max;
X	return -dice.d_pts_max;		/* normal */
X}
X
X/*
X**	keephelp: long help message for M_KEEP prompt
XXXX		We assume there's only seven lines of help window.
X*/
Xstatic char	   *keephelp[] = {
X  "{list}   Roll       roll non-scoring and listed dice (synonyms=[rt]{list})",
X  "k{list}  Keep       roll all but the dice listed (synonyms=[,s]{list})",
X  "h        Hold       don't roll again, save points scored (synonyms=[.nq])",
X  "m,a,b    Ranks      display ranking info for you, or above or below you",
X  "p{num}   Player     display ranking info for player {num} in roster",
X  "x        Autopilot  engage the autopilot (in case of interruptions)",
X  "e        Exit       leave the game early (not recommended)",
X  (char *)0	/* end marker */
X};
X
X/*
X**	pquery: ask the player what to do next
X**
X**	choices are:
X**		Exit			leave the game
X**		Autopilot (x)	engage the autopilot
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**		Myrank			display player's ranking info
X**		Aboveme			display ranking of next better player
X**		Belowme			display ranking of next worse player
X*/
Xpquery(c, pd)
Xint					c;
Xregister diceset   *pd;
X{
X	register int	n, d;
X	int				stay, rankq, errs;
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		syslog(LOG_DEBUG, "pquery: got a nothing roll");
X		if(simp(c, M_INFO, "You rolled a big zip.") < 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#define	MAXRANKQ	3				/* limit abuse of rank queries */
X#define	MAXERRS		(2 * MAXRANKQ)	/* limit abuse of error recovery */
X	rankq = errs = 0;
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		**	We've got to prevent a player from causing everybody else
X		**	to time out by giving three minutes worth of erroneous
X		**	responses.  If we've reached MAXERRS, give a message
X		**	and do the default action.
X		*/
X		if(errs < MAXERRS) {
X			sprintf(msgbuf, "%d %d saved, %d showing; command? [rkh%sxe?]\r\n",
X				M_KEEP, pd->d_pts_turn, stay - pd->d_pts_turn,
X				rankq < MAXRANKQ ? "mabp" : "");
X			switch(dialogue(c, msgbuf, sizeof msgbuf)) {
X			case -2: msgbuf[0] = '\0'; break;	/* timeout, do default */
X			case -1: return -1;					/* left game */
X			}
X		} else {
X			if(++plr[c].p_timeouts >= MAXTIMO) {
X				(void) simp(c, M_DOWN, "Too many errors -- goodbye!");
X				oldplayer(c);
X				return -1;
X			}
X			if(simp(c, M_ARGE, "Too many errors -- doing default.") < 0)
X				return -1;
X			msgbuf[0] = '\0';
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			if(reallyquit(c) == True)
X				return -1;
X			error = True;
X			break;
X
X		case 'x': case 'X':		/* enter autopilot (temporary proxy) mode */
X			return -2;			/* pretend timeout, let caller do work */
X		
X		case 'm': case 'M':		/* Myrank -- send player's ranking info */
X			if(rankq >= MAXRANKQ)
X				goto toomany;
X			if(myrank(c, False) < 0)
X				return -1;
X			error = True;		/* not an error, but we must continue */
X			break;
X
X		case 'a': case 'A':		/* Aboveme -- send better player's ranking */
X			if(rankq >= MAXRANKQ)
X				goto toomany;
X			if(aboveme(c, False) < 0)
X				return -1;
X			error = True;		/* not an error, but we must continue */
X			break;
X
X		case 'b': case 'B':		/* Belowme -- send worse player's ranking */
X			if(rankq >= MAXRANKQ)
X				goto toomany;
X			if(belowme(c, False) < 0)
X				return -1;
X			error = True;		/* not an error, but we must continue */
X			break;
X		
X		case 'p': case 'P':		/* Player: send info about numbered player */
X			if(rankq >= MAXRANKQ)
X				goto toomany;
X			if(plrrank(c, atoi(digits)-1, False) < 0)
X				return -1;
X			error = True;		/* maybe not an error, but we must continue */
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		case '.':				/* Hold (for numeric keypads) */
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		case ',':				/* Keep (komma, for numeric keypads) */
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					if(simp(c, M_ARGE, "Die number is out of range.") < 0)
X						return -1;
X					error = True;
X					break;
X				}
X				if(pd->d_comb[d] == Nothing) {
X					if(simp(c, M_ARGE, "You can keep only scoring dice.") < 0)
X						return -1;
X					error = True;
X					break;
X				}
X				if(pd->d_comb[d] == Previous || pd->d_stat[d] != Free) {
X					if(simp(c, M_ARGE, "You can keep only rolled dice.") < 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					if(simp(c, M_ARGE, "Die number is out of range.") < 0)
X						return -1;
X					error = True;
X					break;
X				}
X				if(pd->d_comb[d] == Previous || pd->d_stat[d] == Held) {
X					if(simp(c,M_ARGE,"You can't roll previously held dice.")<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		case '?':	/* help */
X			if(multimesg(c, M_HELP, keephelp) < 0)
X				return -1;
X			error = True;
X			break;
X
Xtoomany:
X		default:
X			if(invalid(c) > 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				if(simp(c, M_ARGE, "You must save a scoring combination.") < 0)
X					return -1;
X				error = True;
X			}
X		}
X	} while(++errs, error == True);
X
X	return 0;
X
X#undef	MAXRANKQ
X#undef	MAXERRS
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+1);
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+1, 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 Joker:				return 'j';
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 Asm_straight:		return 'b';	/* b for built */
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 %d %s\r\n",
X		M_DICE, c, stats, faces, combs,
X		pd->d_pts_roll, pd->d_pts_dice, pd->d_pts_turn, pd->d_pts_max,
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		case Watching:
X			(void) message(cc, msgbuf);
X			break;
X		}
X	}
X}
X
X/*
X**	rfsthelp: long help message for M_RFST prompt
X*/
Xstatic char	   *rfsthelp[] = {
X  "<return>  Roll       roll the dice (synonyms=[yrt])",
X  "m         Myrank     display your ranking info",
X  "x         Autopilot  engage the autopilot (in case of interruptions)",
X  "e         Exit       leave the game early (not recommended)",
X  (char *)0	/* end marker */
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, errs;
X
X#define	MAXERRS	3	/* limit abuse */
X	for(errs = 0;;++errs) {
X		if(errs < MAXERRS) {
X			sprintf(msgbuf, "%d Ready to roll? [ymxe?]\r\n", M_RFST);
X			if((n = dialogue(c, msgbuf, sizeof msgbuf)) < 0)
X				return n;
X		} else {
X			if(++plr[c].p_timeouts >= MAXTIMO) {
X				(void) simp(c, M_ARGE, "Too many errors -- goodbye!");
X				oldplayer(c);
X				return -1;
X			}
X			if(simp(c, M_ARGE, "Too much tomfoolery -- rolling!") < 0)
X				return -1;
X			return 0;	/* roll */
X		}
X
X		switch(msgbuf[0]) {
X		case '\0':				/* default */
X		case 'y': case 'Y':		/* Yes */
X		case 'r': case 'R':		/* Roll */
X			return 0;
X		case 'e': case 'E':		/* Exit */
X			if(reallyquit(c) == True)
X				return -1;
X			break;
X		case 'x': case 'X':		/* autopilot */
X			return -2;			/* pretend timeout */
X		case 'm': case 'M':		/* Myrank */
X			if(myrank(c, False) < 0)
X				return -1;
X			break;
X		case '?':				/* Help */
X			if(multimesg(c, M_HELP, rfsthelp) < 0)
X				return -1;
X			break;
X		default:
X			if(invalid(c) < 0)
X				return -1;
X			break;
X		}
X	}
X#undef	MAXERRS
X}
X
X/*
X**	reallyquit: does player really want to quit?
X*/
Xstatic boolean
Xreallyquit(c)
Xint		c;
X{
X	int			half;
X	boolean		wise	= True;
X	char		msgbuf[128];
X
X	if(inprogress == True && plr[c].p_stat == Active) {
X		if(plr[c].p_onboard == True)
X			wise = False;
X		else if((half = histavgplr(c)) > 0) {
X			half = (half * winscore) / (2 * WINSCORE);
X			if(plr[c].p_score > half
X			|| winscore - highscore(c, (int *)0) > half - plr[c].p_score)
X				wise = False;
X		}
X	}
X
X	sprintf(msgbuf, "%d %s [ny]\r\n", M_RFST,
X		wise == True ? "Do you really want to quit?"
X		: "It seems unwise to quit now.  Are you sure?");
X	if(dialogue(c, msgbuf, sizeof msgbuf) < 0)
X		return True;
X
X	if(msgbuf[0] == 'y' || msgbuf[0] == 'Y') {
X		(void) simp(c, M_DOWN, "Quitters never win.");
X		oldplayer(c);
X		return True;
X	}
X
X	return False;
X}
X
X/*
X**	myrank: look up player's rank in history then broadcast it
X*/
Xmyrank(c, isasync)
Xboolean	isasync;
X{
X	char	msgbuf[MESGLEN];
X
X	if(isasync == False) {
X		sprintf(msgbuf, "%d rank: %s\r\n", M_INFO, histfmtplr(c, 0, False));
X		announce(c, msgbuf);
X	}
X	if(plr[c].p_stat == Computer)
X		return 0;
X	sprintf(msgbuf, "%d rank: %s\r\n", M_INFO, histfmtplr(c, 0, True));
X	return message(c, msgbuf);
X}
X
X/*
X**	aboveme: look up player's rank in history
X**		then broadcast the one directly below it
X*/
Xaboveme(c, isasync)
Xboolean	isasync;
X{
X	char	msgbuf[MESGLEN];
X
X	sprintf(msgbuf, "%d rank: %s\r\n", M_INFO, histfmtplr(c, -1, True));
X	if(isasync == True)
X		return message(c, msgbuf);
X	announce(-1, msgbuf);
X	return 0;
X}
X
X/*
X**	belowme: look up player's rank in history
X**		then broadcast the one directly below it
X*/
Xbelowme(c, isasync)
Xboolean	isasync;
X{
X	char	msgbuf[MESGLEN];
X
X	sprintf(msgbuf, "%d rank: %s\r\n", M_INFO, histfmtplr(c, 1, True));
X	if(isasync == True)
X		return message(c, msgbuf);
X	announce(-1, msgbuf);
X	return 0;
X}
X
X/*
X**	plrrank: look up player p's rank in history
X**		then broadcast it, using the player's moniker
X*/
X/*ARGSUSED*/
Xplrrank(c, p, isasync)
Xboolean	isasync;
X{
X	char	msgbuf[MESGLEN];
X
X	sprintf(msgbuf, "%d rank: %s\r\n", M_INFO, histfmtplr(p, 0, False));
X	if(isasync == True)
X		return message(c, msgbuf);
X	announce(-1, msgbuf);
X	return 0;
X}
END_OF_FILE
if test 19603 -ne `wc -c <'turn.c'`; then
    echo shar: \"'turn.c'\" unpacked with wrong size!
fi
# end of 'turn.c'
fi
echo shar: End of archive 4 \(of 8\).
cp /dev/null ark4isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 8 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0