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 t

⟦dbd5195c8⟧ TextFile

    Length: 21222 (0x52e6)
    Types: TextFile
    Names: »tempers.c«

Derivation

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

TextFile

/*	vi:set sw=4 ts=4: */
#ifndef	lint
static char	sccsid[] = "@(#)tempers.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.
**
*/

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

extern unsigned		nhist;
extern history	   *hist;
extern int			winscore;
extern int			turnnum;
extern boolean		jokermode;
extern player		plr[];
extern double		ziprisk();
extern int			highscore();
extern int			closescore();
extern int			winner();
extern long			histavgplr();

#define	P_MAXROLL	(P_ACEMULT * P_AOKMULT)	/* largest single roll */

/*
**	atbreakeven: returns True if player has reached the quit-penalty
**		break-even point (roughly half of player's point average)
**		or can be expected to reach it before the game ends
*/
boolean
atbreakeven(comp, mscore, hscore)
int		comp, mscore, hscore;
{
	long	half, need;

	if((half = histavgplr(comp)) == 0)			/* no average */
		return True;							/* passed it, right? */
	half = (half * winscore) / (2 * WINSCORE);	/* derate and half */
	if((need = half - mscore) <= 0)				/* passed it? */
		return True;
	if(winscore - hscore > 2 * need)			/* probably can reach it? */
		return True;
	return False;								/* not yet */
}

/*
**	robot: just plays the expectations and ignores other players, etc.
*/
/*ARGSUSED*/
static boolean
robot(comp, stay, xpct, pd)
int			comp, stay, xpct;
diceset	   *pd;
{
	/*
	**	Don't roll if there's no advantage.
	*/
	return (xpct <= stay) ? False : True;
}

/*
**	xx_antsy: gets antsy when it gets behind; uses supplied tracking function
**		a little bit more conservative than robot when ahead
*/
/*ARGSUSED*/
static boolean
xx_antsy(comp, stay, xpct, pd, track)
int			comp, stay, xpct;
diceset	   *pd;
int		  (*track)();
{
	int		mscore;
	int		hscore, hc;
	double	livefac;

#define	TOOLITTLE	(2 * P_ACE)
#define	GAINFAC		1.1
#define	WAYBEHIND	((P_MAXROLL * winscore) / WINSCORE)
#define	CLOSE		WINMARGIN
#define	AVGTURN		400			/* empirically derived */
#define	SANITY		0.40

	/*
	**	Avoid starting out too far in the hole by not getting on board
	**	way behind -- the leave-the-game algorithm will eventually "save"
	**	us, but we pay a price.  We know what the price is, so if we
	**	reach the break-even point, we hold.
	*/
	mscore = plr[comp].p_score + stay;
	if(plr[comp].p_onboard == False /* && stay >= ONBOARD */) {
		hscore = highscore(comp, (int *)0);	/* not (*track) */
		if(hscore - mscore > WAYBEHIND) {
			if(atbreakeven(comp, mscore, hscore) == True)
				return False;				/* hold */
			return True;					/* desperation */
		}
	}

	/*
	**	Never stop with too little points.
	*/
	if(stay <= TOOLITTLE)
		return True;
	
	/*
	**	If there's gain to be had, roll again.
	*/
	if(xpct > (int)(GAINFAC * stay))
		return True;

	/*
	**	Find our score and the score we're tracking.  If we're ahead
	**	or close, then don't reroll.  Our definition of close depends
	**	on whether the leader has already taken its turn this round.
	*/
	hscore = (*track)(comp, &hc);
	if(hc < comp) {		/* leader has taken turn */
		if(hscore - mscore <= CLOSE)
			return False;
	} else {			/* leader's turn still to come */
		if(hscore - mscore <= CLOSE - AVGTURN)
			return False;
	}

	/*
	**	Calculate the expectation value we can live with.
	*/
	livefac = GAINFAC - (double)(hscore - mscore) / winscore;
	if(livefac < SANITY)
		livefac = SANITY;
	if(xpct > (int)(livefac * stay))
		return True;
	
	return False;

#undef	TOOLITTLE
#undef	GAINFAC
#undef	WAYBEHIND
#undef	CLOSE
#undef	AVGTURN
#undef	SANITY
}

/*
**	antsy: gets antsy when it gets behind; tracks leader
*/
static boolean
antsy(comp, stay, xpct, pd)
int			comp, stay, xpct;
diceset	   *pd;
{
	return xx_antsy(comp, stay, xpct, pd, highscore);
}

/*
**	tracker: same as antsy but tracks highest close score rather than higest
*/
static boolean
tracker(comp, stay, xpct, pd)
int			comp, stay, xpct;
diceset	   *pd;
{
	return xx_antsy(comp, stay, xpct, pd, closescore);
}

/*
**	chicken: gets scared when it has lots of points in hand
**		doesn't care about expectation or other players
**		also chickens out as soon as it thinks it has won
**		won't get on board if high score is out of range
*/
/*ARGSUSED*/
static boolean
chicken(comp, stay, xpct, pd)
int			comp, stay, xpct;
diceset	   *pd;
{
	int		mscore, hscore, hc;
	int		atrisk, maxrisk;
	int		range, magic;

#define	OUTRNG		((P_STRAIGHT * winscore) / WINSCORE)
#define	AVGTURN		375		/* empirically, a bit low */

	/*
	**	We have the opportunity to get on board if we quit.  We hold
	**	if the high score is not out of range.  We roll again if we
	**	have no average, thus cannot be penalized.  We hold if we've
	**	reached the break-even point.  Otherwise, roll again.
	*/
	if(plr[comp].p_onboard == False /* && stay >= ONBOARD */) {
		mscore = plr[comp].p_score + stay;
		hscore = highscore(comp, &hc);
		if(hc > comp)
			hscore += AVGTURN;
		if(hscore - mscore < OUTRNG)
			return False;				/* close enough, hold! */
		if(atbreakeven(comp, mscore, hscore) == True)
			return False;				/* too risky to roll */
		return True;					/* desperation... */
	}
	
	/*
	**	If the leader's turn has already been taken, then if we
	**	can win, hold.  If the leader has yet to take its turn,
	**	if we can block it from winning, hold.  Adjust hscore
	**	here for leader's predicted turn.
	*/
	if((mscore = plr[comp].p_score + stay) >= winscore) {
		hscore = highscore(comp, &hc);
		if(hc < comp) {							/* leader had turn */
			if(mscore - hscore >= WINMARGIN)	/* can we win? */
				return False;
		} else {								/* leader's turn to come */
			hscore += AVGTURN;					/* predict future */
			if(hscore - mscore < WINMARGIN)		/* can we block? */
				return False;
		}
	}

	/*
	**	Our behavior is dependent on our mood.
	*/
	switch(plr[comp].p_mood) {
	case 2: magic = P_FIVE / 2; break;
	case 3: magic = P_FIVE * 2; break;
	default:magic = P_FIVE; break;
	}

	/*
	**	We are conservative when close, getting more desperate
	**	as we get further behind.  We get a little wild when
	**	we're ahead too.
	*/
	atrisk = (int)(stay * ziprisk(pd));
	if((range = (mscore - hscore + 1) / WINMARGIN) <= 0)
		maxrisk = ((1 - range) * magic);	/* not winning */
	else
		maxrisk = (range * magic) / 3;		/* winning */
	if(maxrisk > 4 * magic)
		maxrisk = 4 * magic;

	if(atrisk > maxrisk)
		return False;

	return True;

#undef	OUTRNG
#undef	AVGTURN
}

/*
**	gambler: cautious sometimes, not so others
**		stops when it gets on board unless the risk is small or far behind
**		stops when it has won or could block unless the risk is small
**		rolls when the expectation is favorable
**		rolls to try to stop a winner until too much at risk
**		rolls when there's less than 50% risk and is behind and "lucky"
*/
/*ARGSUSED*/
static boolean
gambler(comp, stay, xpct, pd)
int			comp, stay, xpct;
diceset	   *pd;
{
	int		mscore, hscore, hc;
	double	luck;

#define	SMALLRISK	0.15
#define	EVENODDS	0.50
#define	FARBEHIND	(winscore / 5)
#define	AVGTURN		(jokermode == True ? 350 : 450)

	/*
	**	Calculate our score.  Calculate the other highest score
	**	and if the leader hasn't taken its turn yet, guess at
	**	what that turn might amount to.
	*/
	mscore = plr[comp].p_score + stay;
	hscore = highscore(comp, &hc);
	if(hc > comp)						/* leader hasn't taken this turn */
		hscore += AVGTURN;				/* adjust for future earnings */

	/*
	**	If we can get on board, we roll again anyway if we're
	**	far behind.  Otherwise, we hold unless it's really tempting.
	*/
	if(plr[comp].p_onboard == False /* && stay >= ONBOARD */) {
		if(hscore - mscore > FARBEHIND) {
			if(atbreakeven(comp, mscore, hscore) == False)
				return True;	/* too risky to quit */
		}
		if(xpct > stay && ziprisk(pd) < SMALLRISK)
			return True;
		return False;
	}
	
	/*
	**	If we could win or block, hold unless it's really tempting.
	*/
	if((mscore = plr[comp].p_score + stay) >= winscore) {
		if(	(hc < comp && mscore - hscore >= WINMARGIN)		/* can win */
		||	(hc > comp && hscore - mscore < WINMARGIN)) {	/* can block */
			if(xpct > stay && ziprisk(pd) < SMALLRISK)
				return True;
			return False;
		}
	}

	/*
	**	If the expectation value is better than what we've got, go ahead.
	*/
	if(xpct > stay)
		return True;

	/*
	**	If another player is about to win, try to stop it.  This isn't strictly
	**	corrrect code, since we may not be the second-best score, but we can't
	**	count on others to save us.  We give up if we risk losing too much.
	*/
	if(hscore >= winscore && hscore - mscore >= WINMARGIN)
		return (int)(stay * ziprisk(pd)) > P_ASMSTR ? False : True;
	
	/*
	**	If we're behind, roll when the odds are favorable and we feel lucky.
	**	We feel lucky if we've been lucky -- if we've squandered less than
	**	half of what we've scored, then we have some luck.  If our luck
	**	multiplied by our desperation is greater than what we'd have if
	**	we held, then we roll.
	*/
	if(hscore > mscore && mscore > 0 && ziprisk(pd) <= EVENODDS)
		if((luck = 1.0 - (2.0 * plr[comp].p_squander) / mscore) > 0)
			if(luck * (hscore - mscore) > stay)
				return True;
	
	/*
	**	Finally, don't hold with too few points.
	*/
	return (stay < 2 * P_ACE) ? True : False;

#undef	SMALLRISK
#undef	EVENODDS
#undef	FARBEHIND
#undef	AVGTURN
}

/*
**	xx_crafty:
**		Won't get on board if too far behind.
**		Quits when it can win, unless really tempting.
**		Tries to roll to beat a winner.
**		Otherwise, tries to get and protect a safelead lead
**		becoming more daring as it gets ahead or behind,
**		but with a limit to its stupidity.
*/
/*ARGSUSED*/
static boolean
xx_crafty(comp, stay, xpct, pd, safelead, maxgainfac, mingainfac, smallrisk)
int			comp, stay, xpct;
diceset	   *pd;
int			safelead;
double		maxgainfac, mingainfac, smallrisk;
{
	int		mscore, hscore;
	int		live, winc;
	double	gainfac;

#define	TOOFAC	4

	hscore = highscore(comp, (int *)0);
	mscore = plr[comp].p_score + stay;

	/*
	**	We can get on board here.  If we're too far behind, and not
	**	yet at the quit-penalty break-even point, we keep rolling.
	**	If too risky, we hold.  Otherwise, use the usual decision function.
	*/
	if(plr[comp].p_onboard == False /* && stay >= ONBOARD */) {
		if(hscore - mscore > TOOFAC * safelead) {	/* too far behind */
			if(atbreakeven(comp, mscore, hscore) == False)
				return True;						/* must roll */
		}
		if(ziprisk(pd) >= smallrisk)				/* too risky */
			return False;							/* must hold */
	}
	
	/*
	**	If we could win, hold unless it's really tempting.
	*/
	if(mscore >= winscore)
		if(mscore - hscore >= WINMARGIN && ziprisk(pd) >= smallrisk)
			return False;

	/*
	**	If we have a winner (other than us), try to beat them, but only
	**	if the expected gainfac is greater than or equal to mingainfac.
	**	We try to get within WINMARGIN of the player's score,
	**	but if their turn is after ours, we try to beat.
	**	Assumes that hscore is winner's score (but what else?).
	*/
	if((winc = winner()) != -1 && winc != comp)
		if(xpct >= stay * mingainfac)
			if(hscore - mscore >= WINMARGIN || winc > comp)
				return True;

	/*
	**	If there's enough gain to be had, roll again.
	*/
	if(xpct > (int)(maxgainfac * stay))
		return True;

	/*
	**	Calculate the "instantaneous" gain factor.  Changed gainfac
	**	calculation to use p_score when ahead, since this temperament
	**	used to routinely squander huge rolls.
	*/
	if(mscore < hscore)
		gainfac = (double)(mscore - hscore - safelead) / P_MAXROLL;
	else
		gainfac = (double)(plr[comp].p_score - hscore - safelead) / P_MAXROLL;
	if(gainfac < 0)
		gainfac = -gainfac;
	gainfac = maxgainfac - gainfac;
	if(gainfac < mingainfac)
		gainfac = mingainfac;

	/*
	**	Calculate the expectation value we can live with.
	*/
	live = (int)(stay * gainfac);
	if(xpct > live)
		return True;
	
	return False;

#undef	TOOFAC
}

/*
**	crafty: call xx_crafty with original set of parameters
*/
static boolean
crafty(comp, stay, xpct, pd)
int			comp, stay, xpct;
diceset	   *pd;
{
	return xx_crafty(comp, stay, xpct, pd, WINMARGIN, 1.20, 0.85, 0.20);
}

/*
**	shakey: call xx_crafty with revised set of parameters
*/
static boolean
shakey(comp, stay, xpct, pd)
int			comp, stay, xpct;
diceset	   *pd;
{
	/*
	**	We have four distinct moods.
	*/
	switch(plr[comp].p_mood) {
	case 2: return xx_crafty(comp,stay,xpct,pd, P_STRAIGHT, 1.30, 0.75, 0.10);
	case 3: return xx_crafty(comp,stay,xpct,pd, 2*OFFBOARD, 1.30, 0.80, 0.15);
	case 4: return xx_crafty(comp,stay,xpct,pd, OFFBOARD,   1.20, 0.85, 0.20);
	default:return xx_crafty(comp,stay,xpct,pd, OFFBOARD,   1.30, 0.75, 0.10);
	}
}

/*
**	zeno: uses ratio of distance to finish over total distance
*/
/*ARGSUSED*/
static boolean
zeno(comp, stay, xpct, pd)
int			comp, stay, xpct;
diceset	   *pd;
{
#define	MINWANT		(WINMARGIN-1)
#define	SANITY		(1.10)			/* fudge factor for endgame sanity */
#define	AVGTURN		400				/* empirical */

	int		hscore, mscore, hc;
	int		lead, want;
	double	zenofac, riskfac, wantfac;

	mscore = plr[comp].p_score;						/* ignore this turn */
	hscore = highscore(comp, &hc);					/* of best competitor */
	zenofac = (double)hscore / winscore;			/* distance ratio */
	if(hc < comp) {									/* leader had turn */
		zenofac = (double)hscore					/* distance ratio */
				/ (winscore * SANITY);
		lead = (mscore-WINMARGIN) - hscore;			/* positive when winning */
	} else {										/* leader's turn to come */
		zenofac = (double)(hscore + AVGTURN)		/* distance ratio */
				/ (winscore * SANITY);
		lead = (mscore-WINMARGIN)-(hscore+AVGTURN);	/* positive when winning */
	}

	if(lead > 0) {									/* we're winning */
		riskfac = 0.90 + 0.35 * zenofac;			/* 0.90 - 1.25 */
		if(xpct < (int)(riskfac * stay))			/* not willing to risk it */
			return False;							/* hold */
		wantfac = 1.5 - 1.4 * zenofac;				/* 1.5 - 0.1 */
		if((want = lead * wantfac) < MINWANT)		/* magic greed limit */
			want = MINWANT;							/* ... but keep moving */
		if(stay > want)								/* we're satisfied */
			return False;							/* hold */
	}

	else if(lead > -WINMARGIN) {					/* it's a dead heat */
		riskfac = 1.0 +  0.35 * zenofac;			/* 1.00 - 1.35 */
		if(xpct < (int)(riskfac * stay))			/* not willing to risk it */
			return False;							/* hold */
													/* no greed test */
	}

	else {											/* we're losing */
		riskfac = 1.1 - 0.35 * zenofac;				/* 1.1 - 0.75 */
		if(xpct < (int)(riskfac * stay))			/* not willing to risk it */
			return False;							/* hold */
		wantfac = 0.2 + 0.7 * zenofac;				/* 0.2 - 0.9 */
		if((want = -lead * wantfac) < MINWANT)		/* magic greed limit */
			want = MINWANT;							/* ... but keep moving */
		if(stay > want)								/* we're satisfied */
			return False;							/* hold */
	}

	return True;									/* roll again */

#undef	MINWANT
#undef	SANITY
#undef	AVGTURN
}

/*
**	goalie: strives for four goals:
**		winscore, 6/5ths average, 3/5ths average, and pace leader
*/
/*ARGSUSED*/
static boolean
goalie(comp, stay, xpct, pd)
int			comp, stay, xpct;
diceset	   *pd;
{
	int		cuscore, stscore, hiscore, hc;
	int		turnsleft, avscore, totneed, perturn;
	int		toolittle, tooclose;
	double	gainfac, avgturn;

#define	WAYBEHIND	((P_MAXROLL * winscore) / WINSCORE)
#define	SANITY		0.50
#define	MAXTURN		(P_ACEMULT * P_3OKMULT)

	/*
	**	Avoid starting out too far in the hole by not getting on board
	**	way behind -- the leave-the-game algorithm will eventually "save"
	**	us, but we pay a price.  We know what the price is, so if we
	**	reach the break-even point, we hold.
	*/
	cuscore = plr[comp].p_score;
	stscore = cuscore + stay;
	if(plr[comp].p_onboard == False /* && stay >= ONBOARD */) {
		hiscore = highscore(comp, (int *)0);
		if(hiscore - stscore > WAYBEHIND) {
			if(atbreakeven(comp, stscore, hiscore) == True)
				return False;			/* hold */
			return True;				/* desperation */
		}
	}

	/*
	**	Some parameters depend on our mood.
	*/
	switch(plr[comp].p_mood) {
	case 2:		/* most agressive */
		toolittle = 3 * P_ACE;
		tooclose = P_FIVE;
		gainfac = 1.025;
		break;
	case 3:		/* more agressive */
		toolittle = 2 * P_ACE + P_FIVE;
		tooclose = P_ACE;
		gainfac = 1.050;
		break;
	default:	/* normal */
		toolittle = 2 * P_ACE;
		tooclose = P_ACE + P_FIVE;
		gainfac = 1.075;
		break;
	}

	/*
	**	Never stop with too little points.  If there's gain to be had, roll.
	**	If it would be insane to roll again, don't.
	*/
	if(stay <= toolittle)
		return True;
	if(xpct > (int)(gainfac * stay))
		return True;
	if(xpct < (int)(SANITY * stay))
		return False;
	
	/*
	**	If we're ahead or close to the high score, don't reroll.  Definition
	**	of close depends on whether the leader has already taken its turn this
	**	round.  We calculate the leader's average turn for use here or below.
	*/
	if((hiscore = highscore(comp, &hc)) == 0)
		return False;
	if(hc < comp) {
		if(hiscore - stscore <= tooclose)
			return False;
		avgturn = (double)hiscore / (turnnum + 1);
	} else {
		avgturn = (double)hiscore / turnnum;
		if(hiscore - stscore <= tooclose - (int)avgturn)
			return False;
	}
	if(avgturn < P_ACE)				/* too low to believe */
		avgturn = P_ACE;
	else if(avgturn > P_STRAIGHT)	/* too high to believe */
		avgturn = P_STRAIGHT;
	
	/*
	**	We're behind, so it's a race to one of our goals.  We estimate how many
	**	turns left in the game based on the leader's average turn.  From that,
	**	we figure out what we need per turn to reach a reasonable goal.
	**	If we've got that much, we hold, otherwise we roll again.
	*/
	if((turnsleft = (winscore - hiscore) / avgturn) <= 0)
		turnsleft = 1;
	
	/*
	**	Primary goal: strive for winscore.
	*/
	if((totneed = winscore - cuscore) <= 0)
		goto pace;
	if((perturn = totneed / turnsleft) <= MAXTURN)
		return (stay >= perturn) ? False: True;

	/*
	**	Calculate our historical average score.
	*/
	if((avscore = histavgplr(comp)) == 0)			/* no record */
		avscore = (3 * WINSCORE) / 4;				/* fake one */
	avscore = (avscore * winscore) / WINSCORE;		/* derate */

	/*
	**	Secondary goal: strive for 6/5ths of average.
	*/
	if((totneed = (6 * avscore) / 5 - cuscore) <= 0)
		goto pace;
	if((perturn = totneed / turnsleft) <= MAXTURN)
		return (stay >= perturn) ? False: True;

	/*
	**	Tertiary goal: strive for 3/5ths of average.
	*/
	if((totneed = (3 * avscore) / 5 - cuscore) <= 0)
		goto pace;
	if((perturn = totneed / turnsleft) <= MAXTURN)
		return (stay >= perturn) ? False: True;

	/*
	**	Last resort: try to keep ahead of leader.
	**	Else, keep moving at an arbitrary pace.
	*/
pace:
	if((totneed = hiscore + WINMARGIN - cuscore) > 0)
		if((perturn = totneed / turnsleft) <= MAXTURN)
			return (stay >= perturn) ? False: True;
	return (stay >= 2 * P_ACE) ? False : True;

#undef	WAYBEHIND
#undef	SANITY
#undef	MAXTURN
}

/*
**	best: use the temperament currently ranked best
*/
static boolean
best(comp, stay, xpct, pd)
int			comp, stay, xpct;
diceset	   *pd;
{
	static history	   *phist	= 0;
	static computer	   *last	= 0;
	register int		n;
	extern temper		te_best;
	extern computer	   *pkamelion;

	/*
	**	If this is our first time through, or if we're not
	**	pointing to the same computer as last time, look
	**	for the top ranked computer.  On occasion we might
	**	not re-search for the top when we should, but this
	**	should happen only rarely.
	*/
	if(phist == 0 || last == 0 || phist->h_computer != last) {
		last = 0;
		for(phist = hist, n = 0;n < nhist;++n, ++phist) {
			if(phist->h_computer == 0 || phist->h_computer == pkamelion)
				continue;
			if(phist->h_computer->c_temper == &te_best)
				continue;
			last = phist->h_computer;
			break;
		}
	}

	/*
	**	If we've got a best computer, use its temperament,
	**	else we default to using the robot temperament.
	*/
	if(last != 0)
		return (last->c_temper->t_func)(comp, stay, xpct, pd);
	return robot(comp, stay, xpct, pd);
}

/*
**	kamelion: should never be called
*/
static boolean
kamelion(comp, stay, xpct, pd)
int			comp, stay, xpct;
diceset	   *pd;
{
	syslog(LOG_WARNING, "kamelion temperament called!");
	return best(comp, stay, xpct, pd);
}

/*
**	schizo: pick another temperament randomly!
**		intended for proxy players only
*/
static boolean
schizo(comp, stay, xpct, pd)
int			comp, stay, xpct;
diceset	   *pd;
{
	int				n;
	extern int		tempers;
	extern temper  *temptbl[];

	hesitate(100L, 1000L);
	plr[comp].p_mood = randint(MAXMOOD);	/* new mood every time */
	n = randint(tempers - 1) - 1;			/* schizo last -- don't pick it */
	return (*temptbl[n]->t_func)(comp, stay, xpct, pd);
}

/*
**	temptbl: table of available temperaments
*/

temper	te_robot	= { robot,		"robot" };
temper	te_antsy	= { antsy,		"antsy" };
temper	te_tracker	= { tracker,	"tracker" };
temper	te_chicken	= { chicken,	"chicken" };
temper	te_gambler	= { gambler,	"gambler" };
temper	te_crafty	= { crafty,		"crafty" };
temper	te_shakey	= { shakey,		"shakey" };
temper	te_zeno		= { zeno,		"zeno" };
temper	te_goalie	= { goalie,		"goalie" };
temper	te_best		= { best,		"best" };
temper	te_schizo	= { schizo,		"schizo" };
temper	te_kamelion	= { kamelion,	"kamelion" };	/* semi-dummy */

temper	   *temptbl[]	= {
	&te_robot, &te_antsy, &te_tracker, &te_chicken, &te_gambler,
	&te_crafty, &te_shakey, &te_zeno, &te_goalie, &te_best,
	&te_schizo		/* te_schizo must be last */
};

int	tempers	= sizeof temptbl / sizeof temptbl[0];