DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T c

⟦23b9dfe2d⟧ TextFile

    Length: 21117 (0x527d)
    Types: TextFile
    Names: »comptbl.c«

Derivation

└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
    └─⟦this⟧ »EUUGD18/General/Cubes/comptbl.c« 

TextFile

/*	vi:set sw=4 ts=4: */
#ifndef	lint
static char	sccsid[] = "@(#)comptbl.c 5.1 (G.M. Paris) 89/01/22";
#endif	lint

/*
**
**	cubes 5.1  Copyright 1989 Gregory M. Paris
**		Permission granted to redistribute on a no charge basis.
**		All other rights are reserved.
**
*/

/*
**	This module is a real mixed bag.  Various functions and definitions
**	that were necessary to compile a standalone history file analyzer
**	have been moved here from all over, but mainly from cubeserver.c.
**	It still takes some dummy stubs to compile...
*/

#include	<stdio.h>
#include	<syslog.h>
#include	<strings.h>
#include	"cubes.h"

extern int			winscore;
extern boolean		inprogress;
extern winpref		gametype;
extern boolean		nameinuse();
extern boolean		iskamelion();
extern boolean		kamelionplaying();
extern unsigned		nhist;
extern history	   *hist;
extern history	   *histbyname();
extern char		   *malloc();
extern char		   *moniker();

player				plr[PLAYERS];	/* defined here for standalone use */

extern strategy		st_basic, st_merrier, st_respect, st_twohater, st_greedy;
extern strategy		st_picky, st_finicky, st_choosy, st_kamelion, st_fickle;
extern temper		te_robot, te_antsy, te_tracker, te_chicken, te_gambler;
extern temper		te_crafty, te_shakey, te_zeno, te_goalie, te_best;
extern temper		te_kamelion, te_schizo;

/*
**	comptbl: list of available computer players
**		comments indicate the source of each name
*/
computer	comptbl[]	= {
{ "CUBEX",			&st_basic,		&te_robot,		Nopref },	/* original */
{ "UNIBLAB",		&st_merrier,	&te_antsy,		Fickle },	/* Jetsons */
{ "Rosie",			&st_choosy,		&te_goalie,		Nopref },	/* Jetsons */
{ "Brainiac",		&st_twohater,	&te_chicken,	Standard },	/* comics */
{ "Wotan",			&st_greedy,		&te_goalie,		Nopref },	/* Dr. Who */
{ "K-2",			&st_choosy,		&te_robot,		Nopref },	/* Dr. Who */
{ "K-9",			&st_basic,		&te_goalie,		Nopref },	/* Dr. Who */
{ "M5",				&st_merrier,	&te_tracker,	Nopref },	/* Star Trek */
{ "NOMAD",			&st_respect,	&te_goalie,		Nopref },	/* Star Trek */
{ "Landrew",		&st_twohater,	&te_gambler,	Blitz },	/* Star Trek */
{ "Norman",			&st_choosy,		&te_zeno,		Nopref },	/* Star Trek */
{ "Stella 500",		&st_picky,		&te_antsy,		Nopref },	/* Star Trek */
{ "Alice 3",		&st_choosy,		&te_tracker,	Nopref },	/* Star Trek */
{ "The Old Man",	&st_merrier,	&te_chicken,	Fstand },	/* T. Zone */
{ "Agnes",			&st_respect,	&te_gambler,	Blitz },	/* T. Zone */
{ "Dr. Theopolis",	&st_twohater,	&te_robot,		Nopref },	/* B. Rogers */
{ "Tweekie",		&st_greedy,		&te_antsy,		Nopref },	/* B. Rogers */
{ "Gort",			&st_picky,		&te_tracker,	Nopref },	/* tDtESS */
{ "Robbie",			&st_basic,		&te_chicken,	Standard },	/* various */
{ "HAL9000",		&st_merrier,	&te_gambler,	Fblitz },	/* Clarke */
{ "SAL9000",		&st_respect,	&te_robot,		Nopref },	/* Clarke */
{ "Mother",			&st_twohater,	&te_antsy,		Nopref },	/* Alien */
{ "Ashe",			&st_greedy,		&te_tracker,	Nopref },	/* Alien */
{ "Bishop",			&st_picky,		&te_chicken,	Standard },	/* Aliens */
{ "W.O.P.R.",		&st_basic,		&te_gambler,	Blitz },	/* War Games */
{ "The Terminator",	&st_merrier,	&te_robot,		Nopref },	/* title role */
{ "Number Five",	&st_respect,	&te_antsy,		Nopref },	/* title role */
{ "Jenkins",		&st_twohater,	&te_tracker,	Nopref },	/* Simak */
{ "R. Daneel Olivaw",&st_greedy,	&te_chicken,	Standard },	/* Asimov */
{ "GAX",			&st_picky,		&te_gambler,	Blitz },	/* Software */
{ "TEX",			&st_basic,		&te_robot,		Nopref },	/* Software */
{ "MEX",			&st_choosy,		&te_antsy,		Nopref },	/* Software */
{ "BEX",			&st_respect,	&te_tracker,	Nopref },	/* Software */
{ "DEX",			&st_choosy,		&te_chicken,	Standard },	/* Software */
{ "LIBEX",			&st_greedy,		&te_gambler,	Blitz },	/* Wetware */
{ "Mr. Frostee",	&st_picky,		&te_robot,		Nopref },	/* Software */
{ "Ralph Numbers",	&st_basic,		&te_antsy,		Nopref },	/* Software */
{ "Wagstaff",		&st_merrier,	&te_goalie,		Nopref },	/* Software */
{ "Berenice",		&st_respect,	&te_chicken,	Standard },	/* Wetware */
{ "Emul",			&st_twohater,	&te_goalie,		Nopref },	/* Wetware */
{ "Ulalume",		&st_greedy,		&te_robot,		Nopref },	/* Wetware */
{ "Oozer",			&st_picky,		&te_goalie,		Nopref },	/* Wetware */
{ "Kkandio",		&st_basic,		&te_tracker,	Nopref },	/* Wetware */
{ "Jander Panell",	&st_finicky,	&te_crafty,		Fickle },	/* Asimov */
{ "Eddie",			&st_picky,		&te_shakey,		Fickle },	/* HHGttG */
{ "Artoo Detoo",	&st_basic,		&te_zeno,		Nopref },	/* Star Wars */
{ "Marvin",			&st_finicky,	&te_tracker,	Nopref },	/* HHGttG */
{ "Giskard Reventlov",&st_picky,	&te_crafty,		Fickle },	/* Asimov */
{ "Speedy",			&st_basic,		&te_shakey,		Fblitz },	/* Asimov */
{ "See Threepio",	&st_finicky,	&te_zeno,		Nopref },	/* Star Wars */
{ "R. Geronimo",	&st_basic,		&te_crafty,		Fickle },	/* Asimov */
{ "Pondermatic",	&st_finicky,	&te_shakey,		Fstand },	/* HHGttG */
{ "Deep Thought",	&st_picky,		&te_zeno,		Nopref },	/* HHGttG */
{ "Xoanon",			&st_finicky,	&te_antsy,		Fickle },	/* Dr. Who */
{ "R. Sammy",		&st_merrier,	&te_crafty,		Fickle },	/* Asimov */
{ "Helen",			&st_merrier,	&te_zeno,		Nopref },	/* Wetware */
{ "Huey",			&st_merrier,	&te_shakey,		Blitz },	/* Sil. Run. */
{ "Duey",			&st_respect,	&te_shakey,		Blitz },	/* Sil. Run. */
{ "Luey",			&st_choosy,		&te_shakey,		Blitz },	/* Sil. Run. */
{ "the robot (B9)",	&st_finicky,	&te_robot,		Nopref },	/* L.i.Space */
{ "SV7",			&st_respect,	&te_zeno,		Nopref },	/* Dr. Who */
{ "Earth",			&st_choosy,		&te_gambler,	Fstand },	/* HHGttG */
{ "Roderick",		&st_respect,	&te_crafty,		Fickle },	/* Sladek */
{ "Tik-Tok",		&st_choosy,		&te_crafty,		Fickle },	/* Sladek */
{ "IG-88",			&st_finicky,	&te_goalie,		Nopref },	/* S. Wars II */
{ "Roger Korby",	&st_twohater,	&te_zeno,		Fickle },	/* Star Trek */
{ "Rayna Kapec",	&st_finicky,	&te_chicken,	Nopref },	/* Star Trek */
{ "V'ger",			&st_finicky,	&te_gambler,	Nopref },	/* ST:TMP */
{ "M. Gargantubrain",&st_greedy,	&te_crafty,		Standard },	/* HHGttG */
{ "Neutron Wrangler",&st_greedy,	&te_shakey,		Standard },	/* HHGttG */
{ "M.P. Titan Muller",&st_greedy,	&te_zeno,		Standard },	/* HHGttG */
{ "Ruk",			&st_twohater,	&te_crafty,		Fickle },	/* Star Trek */
{ "Andrea",			&st_twohater,	&te_shakey,		Fickle },	/* Star Trek */
{ "Orac",			&st_choosy,		&te_best,		Nopref },	/* Blake's 7 */
/*
**	If you have more names, mail them to me...
**
**	Kamelion must be last.
*/
{ "Kamelion",		&st_kamelion,	&te_kamelion,	Fickle }	/* Dr. Who */
};

#define	COMPUTERS	(sizeof comptbl / sizeof comptbl[0])

int			computers	= COMPUTERS;
computer   *pkamelion	= &comptbl[COMPUTERS - 1];
computer	proxy		= { "**proxy**", &st_fickle, &te_schizo, Nopref };

/*
**	initcomptbl: initialize computer table (no-op)
*/
initcomptbl() { }

/*
**	compbyname: return a pointer to the computer with the matching name
*/
computer *
compbyname(name)
register char  *name;
{
	register int	n;

	for(n = 0;n < computers;++n)
		if(strncmp(name, comptbl[n].c_name, IDLEN) == 0)
			return &comptbl[n];
	
	return (computer *)0;
}

/*
**	addcomp: add the COMP computer
**		a game is never in progress when this happens
*/
addcomp(c)
int		c;
{
	plr[c].p_fd = -1;
	plr[c].p_score = 0;
	plr[c].p_onboard = False;
	plr[c].p_stat = Computer;
	plr[c].p_id[0] = '\0';	/* non-proxies are null */
	plr[c].p_computer = &comptbl[0];
	strcpy(plr[c].p_name, plr[c].p_computer->c_name);
	setpref(c);
}

/*
**	pickproxy: give this player the proxy personality
**		Doesn't change p_stat, in case this is a temporary proxy.
*/
pickproxy(c)
int		c;
{
	if(plr[c].p_computer != &proxy)
		plr[c].p_computer = &proxy;
	if(plr[c].p_mood == NOMOOD)
		plr[c].p_mood = randint(MAXMOOD);
	/* don't change preference */
}

/*
**	nextupcmp: comparison function for producing next-up order
**		sorts computers before humans
**		sorts the COMP computer before other computers
**		sorts computers to have best play more often than worst
**	NOTE: histsort() must be called first for proper operation.
*/
nextupcmp(h1, h2)
history	   *h1, *h2;
{
	register int	diff;

	/*
	**	Computers before humans.
	*/
	if((h1->h_computer == 0) != (h2->h_computer == 0))
		return h1->h_computer == 0 ? 1 : -1;
	
	/*
	**	COMP computer before all others.
	*/
	if(h1->h_computer == &comptbl[0])
		return -1;
	if(h2->h_computer == &comptbl[0])
		return  1;

	/*
	**	We cycle through the list of computers, but with the modification
	**	that the higher ranked players will play more often than the lower.
	*/
	diff = 2 * (h1->h_lastgame - h2->h_lastgame);
	diff += h1->h_rank - h2->h_rank;
	if(diff != 0)
		return diff;

	/*
	**	For tie breaking, the player with the least games played goes next.
	*/
	if((diff = h1->h_games - h2->h_games) != 0)
		return diff;

	/*
	**	For further tiebreaking, prefer more wins, higher points, and
	**	higher turn average, in that order.  (Note: games equal here.)
	*/
	if((diff = h2->h_wins - h1->h_wins) != 0)
		return diff;
	if((diff = h2->h_points - h1->h_points) != 0)
		return diff;
	if((diff = h2->h_avgturn - h1->h_avgturn) != 0)
		return diff;

	return 0;
}

/*
**	pickcomputer: give this player an inactive computer personality
*/
pickcomputer(c)
int		c;
{
	register int		comp;
	register history   *thist;
	unsigned			size;
	static boolean		allinhist	= False;

	plr[c].p_stat = Computer;
	plr[c].p_id[0] = '\0';				/* not a proxy */
	plr[c].p_mood = randint(MAXMOOD);	/* mood set on entering */

	/*
	**	Usually, we'd like to select computers that haven't played much
	**	(the more highly ranked the better), but this method will never
	**	pick a computer that's not in the history file.  What we do then
	**	is randomly select this method, and if it fails, we still fall
	**	back on random selection from the comptbl.
	*/
	thist = 0;
	if(randint(100) <= 85) {
		size = nhist * sizeof *hist;
		if((thist = (history *)malloc(size)) == 0)
			syslog(LOG_WARNING, "pickcomputer: no memory for dup history");
	}
	if(thist != 0) {
		/*
		**	Copy the history info and sort it into a desirable order.
		**	Step through the list to find a computer player.
		*/
		bcopy((char *)hist, (char *)thist, (int)size);
		qsort((char *)thist, (int)nhist, sizeof *thist, nextupcmp);

		for(comp = 0;comp < (int)nhist;++comp) {
			if((thist+comp)->h_computer == 0)
				continue;
			if(randint(3) != 1)		/* 67% chance */
				continue;
			plr[c].p_computer = (thist+comp)->h_computer;
			if(iskamelion(c) == True) {
				if(kamelionplaying(c) == True)
					continue;
				setkamelion(c);
			} else {
				if(nameinuse(c, plr[c].p_computer->c_name) == True)
					continue;
				strcpy(plr[c].p_name, plr[c].p_computer->c_name);
				setpref(c);
				if(inprogress == True) {	/* picky late joiners */
					switch(plr[c].p_pref) {
					case Fstand:	if(gametype != Standard) continue; break;
					case Fblitz:	if(gametype != Blitz) continue; break;
					case Standard:	if(gametype != Standard) continue; break;
					case Blitz:		if(gametype != Blitz) continue; break;
					}
				}
			}
			free((char *)thist);
			return;
		}
		free((char *)thist);
	}

	/*
	**	Check to see if there are any computer players that have never played.
	**	If one is found use it.  If none are found, make sure we don't waste
	**	our time here again.  We don't do this every time we get here because
	**	we want to ration out the new computers.
	*/
	if(allinhist == False && randint(100) <= 20) {
		for(comp = 1;comp < computers;++comp) {
			if(comptbl[comp].c_name[0] == '\0')		/* placeholder */
				continue;
			if(histbyname(comptbl[comp].c_name) != 0)
				continue;
			plr[c].p_computer = &comptbl[comp];
			if(iskamelion(c) == True) {
				if(kamelionplaying(c) == True)
					continue;
				setkamelion(c);
			} else {
				if(nameinuse(c, plr[c].p_computer->c_name) == True)
					continue;
				strcpy(plr[c].p_name, plr[c].p_computer->c_name);
				setpref(c);
				if(inprogress == True) {	/* semi-picky late joiners */
					switch(plr[c].p_pref) {
					case Fstand:	if(gametype != Standard) continue; break;
					case Fblitz:	if(gametype != Blitz) continue; break;
					}
				}
			}
			return;
		}
		allinhist = True;
	}

	/*
	**	Randomly select a computer from the comptbl.  This routine cannot
	**	fail as long as PLAYERS is less than the number of computers.
	*/
	for(;;) {
		comp = randint(computers - 1);					/* don't pick zero */
		if(comptbl[comp].c_name[0] == '\0')				/* placeholder */
			continue;
		plr[c].p_computer = &comptbl[comp];
		if(iskamelion(c) == True) {
			if(kamelionplaying(c) == True)
				continue;
			setkamelion(c);
		} else {
			if(nameinuse(c, plr[c].p_computer->c_name) == True)
				continue;
			strcpy(plr[c].p_name, plr[c].p_computer->c_name);
			setpref(c);
			if(inprogress == True) {	/* semi-picky late joiners */
				switch(plr[c].p_pref) {
				case Fstand:	if(gametype != Standard) continue; break;
				case Fblitz:	if(gametype != Blitz) continue; break;
				}
			}
		}
		return;
	}
}

/*
**	setkamelion: Kamelion adopts the personality of other computers
**		and the moniker of other players
*/
setkamelion(c)
{
	register int		comp;
	register char	   *aka;
	register history   *phist;

	/*
	**	Pick a computer player's personality for Kamelion to mimic.
	**	Kamelion wants to be a good player, so we pick from the top
	**	of the history (best ranked players).
	*/
	for(comp = 0;comp < nhist;++comp) {
		phist = hist + comp;
		if(phist->h_computer == 0 || phist->h_computer == pkamelion)
			continue;
		if(inprogress == True) {	/* semi-picky late joiners */
			switch(phist->h_computer->c_pref) {
			case Fstand:	if(gametype != Standard) continue; break;
			case Fblitz:	if(gametype != Blitz) continue; break;
			}
		}
		if(randint(4) != 1)		/* 75% chance */
			continue;
		pkamelion->c_strategy->s_func = phist->h_computer->c_strategy->s_func;
		pkamelion->c_temper->t_func = phist->h_computer->c_temper->t_func;
		pkamelion->c_pref = phist->h_computer->c_pref;
		break;
	}

	/*
	**	If the above method failed, just select randomly from comptbl.
	*/
	if(comp >= nhist) {
		do {
			comp = randint(computers - 1);	/* don't pick zero, COMP */
		} while(&comptbl[comp] == pkamelion);
		pkamelion->c_strategy->s_func = comptbl[comp].c_strategy->s_func;
		pkamelion->c_temper->t_func = comptbl[comp].c_temper->t_func;
		pkamelion->c_pref = comptbl[comp].c_pref;
	}

	/*
	**	Pick a name for Kamelion to assume.  Kamelion appears to be a proxy.
	*/
	while(nameinuse(c, (aka = moniker())) == True)
		;
	strncpy(plr[c].p_name, aka, NAMELEN);
	plr[c].p_name[NAMELEN-1] = '\0';
	strncpy(plr[c].p_id, pkamelion->c_name, IDLEN);
	plr[c].p_id[IDLEN-1] = '\0';

	/*
	**	Make sure that p_computer is set.  Set preference.
	*/
	plr[c].p_computer = pkamelion;
	setpref(c);
}

/*
**	setpref: set p_pref for a computer player
*/
setpref(c)
{
	player *p	= &plr[c];

	if(p->p_computer != 0) {
		if((p->p_pref = p->p_computer->c_pref) == Fickle) {
			switch(randint(9)) {
			case 1: case 5: p->p_pref = Standard; break;
			case 2: case 6: p->p_pref = Blitz; break;
			case 3:         p->p_pref = Fstand; break;
			case 4:         p->p_pref = Fblitz; break;
			default:        p->p_pref = Nopref; break;
			}
		}
	}
}

/*
**	iscomp: return True if player is the COMP computer
*/
boolean
iscomp(c)
{
	if(plr[c].p_stat != Computer)
		return False;
	if(plr[c].p_computer != &comptbl[0])
		return False;
	return True;
}

/*
**	isproxy: return True if player is a proxy computer
**		There are two kinds of proxies, permanent and temporary.
**		Permanent proxy: p_stat == Computer, p_computer == &proxy.
**		Temporary proxy: p_stat == Active, p_computer == &proxy.
*/
boolean
isproxy(c)
{
	if(plr[c].p_computer == &proxy)
		if(plr[c].p_stat == Computer || plr[c].p_stat == Active)
			return True;
	return False;
}

/*
**	iskamelion: return True if player is Kamelion
*/
boolean
iskamelion(c)
{
	if(plr[c].p_stat != Computer)
		return False;
	if(plr[c].p_computer != pkamelion)
		return False;
	return True;
}

/*
**	nameinuse: return True if name in use by other than numbered player
*/
boolean
nameinuse(c, name)
int		c;
char   *name;
{
	register int	cc;

	for(cc = 0;cc < PLAYERS;++cc)
		if(cc != c && plr[cc].p_stat != Inactive)
			if(strncmp(plr[cc].p_name, name, NAMELEN-1) == 0)
				return True;

	return False;
}

/*
**	kamelionplaying: return True if Kamelion is already playing
*/
boolean
kamelionplaying(comp)
{
	register int	c;

	for(c = 0;c < PLAYERS;++c)
		if(c != comp && iskamelion(c) == True)
			return True;

	return False;
}

/*
**	highscore: find highest score amongst other players
**		If phighc is not zero, stores index of high scorer
**		or -1 if there's a tie. 
*/
highscore(comp, phighc)
int		comp, *phighc;
{
	register int	c;
	int				high, highc;

	high = 0, highc = -1;
	for(high = c = 0;c < PLAYERS;++c) if(c != comp) {
		switch(plr[c].p_stat) {
		case Computer:
		case Active:
			if(plr[c].p_score > high)
				high = plr[(highc = c)].p_score;
			else if(plr[c].p_score == high)
				highc = -1;
			break;
		}
	}

	if(phighc != 0)
		*phighc = highc;

	return high;
}

/*
**	sc_: structure for sorting scores
*/
struct sc_ { int sc_c, sc_p; };

/*
**	sc_comp: compare struct sc_ values, largest sc_p first
*/
static int
sc_comp(s1, s2)
struct sc_ *s1, *s2;
{
	return s2->sc_p - s1->sc_p;
}

/*
**	closescore: find the highest score that's close enough to compete with
**		If closec is not zero, stores index of the selected scorer
**		or -1 if there's a tie.
*/
closescore(comp, pclosec)
int		comp, *pclosec;
{
	register int	c;
	int				mine, diff;
	int				high, highc;
	struct sc_		sc[PLAYERS];

#define	UPPERLIM	 3000	/* beyond this is too far ahead */
#define	LOWERLIM	-1000	/* below this is too far behind */

	/*
	**	Sort the scores best first.
	*/
	for(c = 0;c < PLAYERS;++c) {
		if((sc[c].sc_c = c) == comp) {
			sc[c].sc_p = -1;	/* cause end search */
			continue;
		}
		switch(plr[c].p_stat) {
		case Computer:
		case Active:
			sc[c].sc_p = plr[c].p_score;
			break;
		default:
			sc[c].sc_p = -2;	/* cause end search */
			break;
		}
	}
	qsort((char *)sc, PLAYERS, sizeof sc[0], sc_comp);

	/*
	**	Find a close score to track.
	*/
	mine = plr[comp].p_score;
	high = 0, highc = -1;
	for(c = 0;c < PLAYERS && sc[c].sc_p >= 0;++c) {
		if((diff = sc[c].sc_p - mine) > UPPERLIM)
			continue;
		if(diff < LOWERLIM && c > 0)
			--c;
		high = sc[c].sc_p;
		if(pclosec != 0) {
			if(c+1 >= PLAYERS || sc[c+1].sc_p != high)
				highc = sc[c].sc_c;
		}
		break;
	}

	if(pclosec != 0)
		*pclosec = highc;

	return high;

#undef	UPPERLIM
#undef	LOWERLIM
}

/*
**	lowscore: report the lowest score
**		Also calculates an average squander value.
*/
lowscore(psqu)
int	   *psqu;
{
	register int	cc, low, squ, n;

#define	HIGHVAL		(2^15)		/* "impossible" high score */

	/*
	**	Look for lowest score.  Sum squandered points.
	*/
	low = HIGHVAL, squ = 0, n = 0;
	for(cc = 0;cc < PLAYERS;++cc) {
		switch(plr[cc].p_stat) {
		case Active:
		case Computer:
			if(plr[cc].p_score < low)
				low = plr[cc].p_score;
			if(psqu != 0)
				squ += plr[cc].p_squander, ++n;
			break;
		}
	}
	if(low == HIGHVAL)
		low = 0;

	/*
	**	If a non-null pointer was passed, calculate an
	**	average squandered value and pass it back.
	*/
	if(psqu != 0) {
		if(n == 0)
			*psqu = 0;						/* no players?! */
		else {
			squ = (squ + n - 1) / n;		/* average, rounded up */
			squ = squ - (squ % P_FIVE);		/* mult. of P_FIVE, rounded down */
			*psqu = squ;
		}
	}

	return low;

#undef	HIGHVAL
}

/*
**	winner: return winner number or -1 if none
*/
winner()
{
	register int	c;
	register int	high, next;
	register int	highc;

	/*
	**	Find high score.  If less than winscore, no winner.
	*/
	for(high = c = 0;c < PLAYERS;++c) {
		switch(plr[c].p_stat) {
		case Computer:
		case Active:
			if(plr[c].p_score > high)
				high = plr[(highc = c)].p_score;
			break;
		}
	}
	if(high < winscore)
		return -1;

	/*
	**	Find next to highest score.  If margin is less than
	**	WINMARGIN, there's no winner yet.
	*/
	for(next = c = 0;c < PLAYERS;++c) {
		if(c == highc)
			continue;
		switch(plr[c].p_stat) {
		case Computer:
		case Active:
			if(plr[c].p_score > next)
				next = plr[c].p_score;
			break;
		}
	}
	if(high - next < WINMARGIN)
		return -1;
	
	/*
	**	We have a winner!
	*/
	return highc;
}

/*
**	plrcmp: player comparison function
*/
plrcmp(p1, p2)
player *p1, *p2;
{
	int		diff;

	/*
	**	Sort by status first.
	*/
	if(p1->p_stat == p2->p_stat) {
		switch(p1->p_stat) {
		case Inactive:
		case Watching:
		case Waiting:
			return 0;
		}
	} else {
		switch(p2->p_stat) {
		case Computer: diff = 3; break;		/* Computer == Active */
		case Active:   diff = 3; break;		/* Computer == Active */
		case Waiting:  diff = 2; break;
		case Watching: diff = 1; break;
		default:       diff = 0; break;
		}
		switch(p1->p_stat) {
		case Computer: diff -= 3; break;	/* Computer == Active */
		case Active:   diff -= 3; break;	/* Computer == Active */
		case Waiting:  diff -= 2; break;
		case Watching: diff -= 1; break;
		default:                  break;
		}
		if(diff != 0) return diff;
	}

	/*
	**	Sort in order of increasing score.
	*/
	if((diff = p1->p_score - p2->p_score) != 0)
		return diff;
	
	/*
	**	Put Computer players after humans.
	*/
	if(p1->p_stat != p2->p_stat)
		return (p1->p_stat == Computer) ? 1 : -1;

	/*
	**	Sort alphabetically.
	*/
	return strcmp(p1->p_name, p2->p_name);
}