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 h

⟦ee345af55⟧ TextFile

    Length: 19000 (0x4a38)
    Types: TextFile
    Names: »histanal.c«

Derivation

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

TextFile

/*	vi:set sw=4 ts=4: */
#ifndef	lint
static char	sccsid[] = "@(#)histanal.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	<stdio.h>
#include	<pwd.h>
#include	<syslog.h>
#include	<strings.h>
#include	"cubes.h"

#ifdef	lint
#ifndef	HISTFILE
#define	HISTFILE	"dummy-histfile"
#endif	HISTFILE
#endif	lint

/*
**	rating: a structure for rating strategies and temperaments
*/
typedef struct {
	history		r_hist;
	long		r_count;
	char	   *r_name;
} rating;

extern unsigned		nhist;
extern history	   *hist;
extern ptstype		ptsmode;
extern computer		comptbl[];
extern int			tempers, strategies;
extern long			gamenum;
static int			rwid;
static int			gwid;

extern int			optind;
extern int			opterr;
extern char		   *optarg;
extern char		   *calloc();
extern double		log10();
extern int			nextupcmp();
extern char		   *fullname();
struct passwd	   *getpwnam();
struct passwd	   *getpwuid();

boolean	inprogress	= False;						/* dummy */
boolean	jokermode	= False;						/* dummy */
winpref	gametype	= Standard;						/* dummy */
int		winscore	= WINSCORE;						/* dummy */
int		turnnum;									/* dummy */
int		nojokers(pd)   diceset *pd; { pd = pd; }	/* stub */
int		nofives(pd)    diceset *pd; { pd = pd; }	/* stub */
int		noaces(pd)     diceset *pd; { pd = pd; }	/* stub */
int		no3deuce(pd)   diceset *pd; { pd = pd; }	/* stub */
int		no3three(pd)   diceset *pd; { pd = pd; }	/* stub */
int		no3any(pd)	   diceset *pd; { pd = pd; }	/* stub */
int		nosmall(pd)    diceset *pd; { pd = pd; }	/* stub */
int		noacesmall(pd) diceset *pd; { pd = pd; }	/* stub */
int		expect(pd)     diceset *pd; { pd = pd; }	/* stub */
double	ziprisk(n) { n = n; }						/* stub */
char   *moniker() { return "Kamelion"; }			/* stub */
boolean	sameface(pd,c,s) diceset *pd;combination c;boolean s;
	{ pd = pd, c = c; return s; }					/* stub */

/*
**	Fake definitions for humans and cubex to allow easy tallying.
*/
static strategy	st_human	= { 0, "human" };
static temper	te_human	= { 0, "human" };
static computer	human	= { "**human**", &st_human, &te_human, Fickle };
static strategy	st_cubex	= { 0, "cubex" };
static temper	te_cubex	= { 0, "cubex" };

static char		   *ptsmodename;
static char		  **namelist;
static boolean		inlist();
static boolean		careabout();

static char		   *histfile	= HISTFILE;	/* the file to analyze */
static boolean		dorank		= True;		/* show individual rankings */
static boolean		nextup		= False;	/* rankings in next-up order */
static boolean		tallystrat	= False;	/* produce strategy tally */
static boolean		tallytemp	= False;	/* produce temperament tally */
static boolean		humansonly	= False;	/* consider only humans */
static boolean		ancient		= False;	/* consider ancient players */
static boolean		showpers	= False;	/* show "personalities" */
static boolean		showlast	= False;	/* show last game played */
static boolean		nospaces	= False;	/* convert spaces in names */
static int			onlylast	= -1;		/* only players of recent games */
static long			myrank		= -1;		/* user's rank */
static int			range		= -1;		/* a range around user's rank */
static char			myid[IDLEN]	= "";		/* user's id */

main(ac, av)
char   *av[];
{
	register int		c, h;
	register history   *phist;
	double				winrt, bwinrt;
	long				avgmpt, bavgmpt;
	long				avtnpt, bavtnpt, bweight;
	rating			   *temprat, *stratrat;
	struct passwd	   *pw;
	char				name[NAMELEN];

	opterr = 0;
	while((c = getopt(ac, av, "rtRLCPSTahlsuf:g:n:p:")) >= 0) {
		switch(c) {
		case 'C':	/* use combined (lifetime+recent) points in ranking */
			ptsmode = Combined;
			break;
		case 'L':	/* use lifetime points in ranking */
			ptsmode = Lifetime;
			break;
		case 'R':	/* use recent points in ranking */
			ptsmode = Recent;
			break;
		case 'P':	/* toggle show personality */
			showpers = (showpers == True) ? False : True;
			break;
		case 'S':	/* toggle strategy tally */
			tallystrat = (tallystrat == True) ? False : True;
			break;
		case 'T':	/* toggle temperament tally */
			tallytemp = (tallytemp == True) ? False : True;
			break;
		case 'r':	/* XXX: backwards compat */
			fprintf(stderr, "cuberank: warning: -r obsolete; use -[PST]\n");
			dorank = (dorank == True) ? False : True;
			break;
		case 't':	/* XXX: backwards compat */
			fprintf(stderr, "cuberank: warning: -t obsolete; use -[PST]\n");
			tallystrat = (tallystrat == True) ? False : True;
			tallytemp  = (tallytemp  == True) ? False : True;
			break;
		case 'a':	/* toggle ancient mode */
			ancient = (ancient == True) ? False : True;
			break;
		case 'h':	/* toggle humans only */
			humansonly = (humansonly == True) ? False : True;
			break;
		case 'l':	/* toggle showlast mode */
			showlast = (showlast == True) ? False : True;
			break;
		case 's':	/* toggle replacing spaces in names */
			nospaces = (nospaces == True) ? False : True;
			break;
		case 'u':	/* toggle next-up mode */
			nextup = (nextup == True) ? False : True;
			break;
		case 'f':	/* specify the history file to use */
			histfile = optarg;
			break;
		case 'g':	/* consider players of onlylast most recent games */
			if((onlylast = atoi(optarg)) <= 0) {
				fprintf(stderr,
					"cuberank: recent game count must be positive\n");
				exit(1);
			}
			break;
		case 'n':	/* a range about a player (default this one) */
			if((range = atoi(optarg)) < 0) {
				fprintf(stderr,
					"cuberank: range around your rank must be non-negative\n");
				exit(1);
			}
			break;
		case 'p':	/* specify player id for use with the range option */
			strncpy(myid, optarg, IDLEN);
			myid[IDLEN-1] = '\0';
			if(range == -1)
				range = 1;	/* maybe should be an error? */
			break;
		default:
			fprintf(stderr,
"usage -- cuberank [-{ahlsuRLCPST}] [-g games] [-n range [-p plr]] [plr ...]\n"
				);
			exit(1);
		}
	}

	/*
	**	Normally tallytemp and tallystrat want to turn off printing
	**	of rankings, but we allow certain options to keep it on.
	*/
	if(showpers==False && nextup==False && humansonly==False && range==-1)
		if(tallytemp == True || tallystrat == True)
			dorank = False;

	/*
	**	Remaining arguments are a list of names of players that we
	**	care about.  All others are ignored.
	*/
	namelist = &av[optind];

	/*
	**	Reconcile incompatible options.
	*/
	if(range != -1 && nextup == True) {
		fprintf(stderr, "cuberank: range cannot be used with nextup\n");
		exit(1);
	}
	if(nextup == True && humansonly == True) {
		fprintf(stderr, "cuberank: humans cannot be used with nextup\n");
		exit(1);
	}

	/*
	**	Suitable symbolic for ptsmode.
	*/
	switch(ptsmode) {
	case Lifetime:	ptsmodename = "(Life.)"; break;
	case Recent:	ptsmodename = "(Recent)"; break;
	case Combined:	ptsmodename = "(Comb.)"; break;
	default:		ptsmodename = "(Error)"; break;
	}

	/*
	**	Call openlog to provide for syslog calls in shared routines.
	*/
#ifdef	LOG_USER
	openlog("cuberank", LOG_PID, LOG_USER);
	setlogmask(LOG_UPTO(LOG_ERR));
#else	LOG_USER
	openlog("cuberank", LOG_PID);
#endif	LOG_USER

	/*
	**	Read in the player histories.
	*/
	initcomptbl();
	if(histread(histfile) < 0)
		exit(1);
	setgamenum();
	if(ancient == False)
		histprune(ANALCREDIT);
	histsort();
	if(nextup == True)
		qsort((char *)hist, (int)nhist, sizeof *hist, nextupcmp);

	/*
	**	Massage things a bit for tallying/printing purposes.
	XXX	This must be done after nextup sort.
	*/
	for(phist = hist, h = 0;h < nhist;++h, ++phist) {
		if(phist->h_computer == 0)
			phist->h_computer = &human;
		else if(phist->h_computer == &comptbl[0]) {
			phist->h_computer->c_strategy = &st_cubex;
			phist->h_computer->c_temper = &te_cubex;
		}
	}

	rwid = (nhist < 100) ? 2 : ((int)log10((double)nhist) + 1);
	gwid = (gamenum < 100) ? 2 : ((int)log10((double)gamenum) + 1);

	/*
	**	If necessary, get this user's id from the password file.
	**	Find the ranking of this user.
	*/
	if(range != -1) {
		if(myid[0] == '\0') {
			if((pw = getpwuid(getuid())) == 0) {
				syslog(LOG_ERR, "no password entry for uid %d", getuid());
				fprintf(stderr, "cuberank: you have no password file entry\n");
				exit(1);
			}
			strncpy(myid, pw->pw_name, IDLEN);
			myid[IDLEN-1] = '\0';
		}

		myrank = -1;
		for(phist = hist, h = 0;h < nhist;++h, ++phist) {
			if(strncmp(myid, phist->h_id, IDLEN) == 0) {
				myrank = phist->h_rank;
				break;
			}
		}
		if(myrank == -1) {
			fprintf(stderr, "cuberank: you (%s) are not ranked\n", myid);
			exit(1);
		}
	}

	/*
	**	If temperament tally is requested, initialize the ranking tallies.
	**	We need room for three extras: kamelion, human, and cubex.
	*/
	if(tallytemp == True) {
		temprat = (rating *)calloc((unsigned)(tempers+3), sizeof *temprat);
		if(temprat == 0) {
			syslog(LOG_ERR, "no memory for temperaments");
			fprintf(stderr,
				"cuberank: no memory for temperament rating table\n");
			exit(1);
		}
		for(phist = hist, h = 0;h < nhist;++h, ++phist)
			if(careabout(phist) == True)
				addtemp(h, temprat);
	}

	/*
	**	If strategy tally is requested, initialize the ranking tallies.
	**	We need room for three extras: kamelion, human, and cubex.
	*/
	if(tallystrat == True) {
		stratrat = (rating *)calloc((unsigned)(strategies+3), sizeof *stratrat);
		if(stratrat == 0) {
			syslog(LOG_ERR, "no memory for strategies");
			fprintf(stderr, "cuberank: no memory for strategy rating table\n");
			exit(1);
		}
		for(phist = hist, h = 0;h < nhist;++h, ++phist)
			if(careabout(phist) == True)
				addstrat(h, stratrat);
	}

	if(dorank == True) {
		/*
		**	Find the players with the best win rate, best average game points,
		**	best average turn points, and best weighted points.
		*/
		bwinrt = -1.0;
		bavgmpt = bavtnpt = -1;
		bweight = nextup == False ? hist->h_weight : -1;
		for(phist = hist, h = 0;h < nhist;++h, ++phist) {
			if(phist->h_games == 0)
				continue;
			histcalc(phist, &winrt, &avgmpt, &avtnpt);

			if(winrt > bwinrt)
				bwinrt = winrt;
			if(avgmpt > bavgmpt)
				bavgmpt = avgmpt;
			if(avtnpt > bavtnpt)
				bavtnpt = avtnpt;
			if(nextup == True && phist->h_weight > bweight)
				bweight = phist->h_weight;
		}

		/*
		**	Print the players in ranking order.  Ancient records are
		**	ranked even if they're not displayed.
		*/
		if(nextup == True)
			printf("%*.*s  ", rwid, rwid, "Up");
		printf("%.*s  Player %-*.*s  %*s  ",
			rwid, "######", DISPLEN-7, DISPLEN-7, ptsmodename, gwid, "GP");
		if(showlast == True)
			printf("%*s  ", gwid, "LG");
		printf("%5s  %5s  %4s  %5s ", "WinRt", "GamPt", "TnPt", "WgtPt");
		if(showpers == True)
			printf(" %-2.2s  %-3.3s  %-3.3s", "Pref", "Tmp", "Str");
		printf("\n");
		for(phist = hist, h = 0;h < nhist;++h, ++phist) {
			if(careabout(phist) == False)
				continue;
			histcalc(phist, &winrt, &avgmpt, &avtnpt);

			if(phist->h_computer == &human && index(phist->h_id, '@') == 0
			&& (pw = getpwnam(phist->h_id)) != 0 && pw->pw_gecos[0] != '\0')
				strcpy(name, fullname(pw));
			else
				sprintf(name, "%.*s", DISPLEN, phist->h_id);

			/*
			**	Convert spaces to underscores when required.
			*/
			if(nospaces == True) {
				char   *space;

				while((space = index(name, ' ')) != 0)
					*space = '_';
			}

			if(nextup == True)
				printf("%*d  ", rwid, h + 1);
			printf("%*ld  %-*.*s  %*ld  ", rwid, phist->h_rank,
				DISPLEN, DISPLEN, name, gwid, phist->h_games);

			if(showlast == True)
				printf("%*ld  ", gwid, phist->h_lastgame);
			printf("%5.3f%c %5ld%c %4ld%c %5ld%c",
				winrt, (winrt > bwinrt - 1e-5) ? '*' : ' ',	/* episilon == 1e-5 */
				avgmpt, avgmpt == bavgmpt ? '*' : ' ',
				avtnpt, avtnpt == bavtnpt ? '*' : ' ',
				phist->h_weight, phist->h_weight == bweight ? '*' : ' '
			);
			if(showpers == True) {
				char   *pref;
				switch(phist->h_computer->c_pref) {
				case Nopref:	pref = "-";  break;
				case Standard:	pref = "S";  break;
				case Blitz:		pref = "B";  break;
				case Fickle:	pref = "?";  break;
				case Fstand:	pref = "FS"; break;
				case Fblitz:	pref = "FB"; break;
				default:		pref = "??"; break;
				}
				printf(" %-2.2s  %-3.3s  %-3.3s", pref,
					phist->h_computer->c_temper->t_name,
					phist->h_computer->c_strategy->s_name
				);
			}
			printf("\n");
		}
	}

	/*
	**	If temperament tally was requested, print it.
	*/
	if(tallytemp == True) {
		if(dorank == True)
			putchar('\n');
		ratranksort(temprat);
		ratprint(temprat, "Temper  ");
	}

	/*
	**	If strategy tally was requested, print it.
	*/
	if(tallystrat == True) {
		if(dorank == True || tallytemp == True)
			putchar('\n');
		ratranksort(stratrat);
		ratprint(stratrat, "Strat   ");
	}
	
	exit(0);
}

/*
**	words: break a string into words
**		Fills passed array with pointers to words, returns number of words.
**		Destroys the original string.
*/
words(s, w, maxw)
register char  *s, **w;
int				maxw;
{
	register int	nw		= 0;
	boolean			inword	= False;

	for(;*s != '\0';++s) {
		if(*s == ' ' || *s == '\t') {
			*s = '\0';
			inword = False;
		} else if(inword == False) {
			if(--maxw < 0)
				break;
			w[nw++] = s;
			inword = True;
		}
	}

	return nw;
}

/*
**	inlist: return true if name matches one in namelist
**		Does word by word match.
*/
static boolean
inlist(test)
char   *test;
{
#define	MAXWORDS	16

	register int	tt, ll, ofs, len;
	int				ntw, nlw, i;
	char			tbuf[NAMELEN], lbuf[NAMELEN];
	char		   *tw[MAXWORDS], *lw[MAXWORDS];
	boolean			match;

	if(namelist[0] == 0)		/* first item is NULL */
		return True;			/* no namelist */
	
	/*
	**	Break the test string into words.
	*/
	strncpy(tbuf, test, NAMELEN);
	tbuf[NAMELEN-1] = '\0';
	ntw = words(tbuf, tw, MAXWORDS);

	/*
	**	Search the namelist.  If the namelist string has more
	**	words than the test string, it can't be a match.
	**	Otherwise, try various matching methods.
	*/
	for(i = 0;namelist[i] != 0;++i) {
		strncpy(lbuf, namelist[i], NAMELEN);
		lbuf[NAMELEN-1] = '\0';
		if((nlw = words(lbuf, lw, MAXWORDS)) == 0)
			continue;

		/*
		**	If there's more words in the namelist string than in
		**	the test string, this cannot be a match.
		*/
		if(nlw > ntw)
			continue;

		/*
		**	Try to match each word in the namelist string with a word
		**	in the test string.  Treat the namelist words as initial
		**	substrings.  We can skip words in the test string, but we
		**	must match every word in the namelist string.
		*/
		ofs = 0;
		match = True;
		for(ll = 0;ll < nlw;++ll) {
			len = strlen(lw[ll]);
			for(tt = ofs;tt < ntw;++tt) {
				if(strncmp(tw[tt], lw[ll], len) == 0) {
					ofs = tt + 1;
					break;
				}
			}
			if(tt == ntw) {
				match = False;
				break;
			}
		}
		if(match == True)
			return True;
	}
	
	return False;

#undef	MAXWORDS
}

/*
**	careabout: do we care about this history entry?
*/
static boolean
careabout(phist)
register history   *phist;
{
	struct passwd  *pw;

	if(range != -1) {
		if(phist->h_rank < myrank - range)
			return False;
		if(phist->h_rank > myrank + range)
			return False;
	}

	if(onlylast != -1)
		if(phist->h_lastgame + onlylast < gamenum)
			return False;

	if(humansonly == True && phist->h_computer != &human)
		return False;

	if(nextup == True && phist->h_computer == &human)
		return False;

	if(namelist[0] != 0) {
		if(inlist(phist->h_id) == True)
			return True;
		if(phist->h_computer == &human && index(phist->h_id, '@') == 0)
			if((pw = getpwnam(phist->h_id)) != 0 && pw->pw_gecos[0] != '\0')
				if(inlist(fullname(pw)) == True)
					return True;
		return False;
	}
	
	return True;
}

/*
**	addtemp: add temperament stats to rating table
*/
addtemp(h, prat)
int					h;
register rating	   *prat;
{
	register history   *phist	= hist + h;
	register history   *phacc;

#define	T_NAME	phist->h_computer->c_temper->t_name

	for(;;++prat) {							/* assumes prat is large enough */
		if(prat->r_name == 0)
			prat->r_name = T_NAME;
		else if(strcmp(prat->r_name, T_NAME) != 0)	/* no pointer compare! */
			continue;
		phacc = &prat->r_hist;
		phacc->h_points += phist->h_points;
		phacc->h_avgturn += phist->h_avgturn;
		phacc->h_wins += phist->h_wins;
		phacc->h_games += phist->h_games;
		phacc->h_mvpoints += phist->h_mvpoints;
		phacc->h_mvavgturn += phist->h_mvavgturn;
		phacc->h_mvwins += phist->h_mvwins;
		phacc->h_mvgames += phist->h_mvgames;
		phacc->h_lastgame += phist->h_lastgame;
		phacc->h_weight += phist->h_weight;
		phacc->h_rank += h + 1;
		prat->r_count++;
		break;
	}

#undef	T_NAME
}

/*
**	addstrat: add strategy stats to rating table
*/
addstrat(h, prat)
int					h;
register rating	   *prat;
{
	register history   *phist	= hist + h;
	register history   *phacc;

#define	S_NAME	phist->h_computer->c_strategy->s_name

	for(;;++prat) {							/* assumes prat is large enough */
		if(prat->r_name == 0)
			prat->r_name = S_NAME;
		else if(strcmp(prat->r_name, S_NAME) != 0)	/* no pointer compare! */
			continue;
		phacc = &prat->r_hist;
		phacc->h_points += phist->h_points;
		phacc->h_avgturn += phist->h_avgturn;
		phacc->h_wins += phist->h_wins;
		phacc->h_games += phist->h_games;
		phacc->h_mvpoints += phist->h_mvpoints;
		phacc->h_mvavgturn += phist->h_mvavgturn;
		phacc->h_mvwins += phist->h_mvwins;
		phacc->h_mvgames += phist->h_mvgames;
		phacc->h_lastgame += phist->h_lastgame;
		phacc->h_weight += phist->h_weight;
		phacc->h_rank += h + 1;
		prat->r_count++;
		break;
	}

#undef	S_NAME
}

/*
**	ratcomp: rating comparison function
*/
ratcomp(r1, r2)
rating *r1, *r2;
{
	int		diff;

	if((diff = r1->r_hist.h_rank - r2->r_hist.h_rank) != 0)
		return diff;
	if((diff = r2->r_hist.h_weight - r1->r_hist.h_weight) != 0)
		return diff;
	return 0;
}

/*
**	ratranksort: reorder a rating table based on average ranking
*/
ratranksort(rattbl)
rating *rattbl;
{
	register rating	   *pr;
	register int		n;

	for(n = 0, pr = rattbl;pr->r_name != 0;++pr, ++n) {
		if(pr->r_count != 0) {
			pr->r_hist.h_rank /= pr->r_count;		/* normalize rank */
			pr->r_hist.h_weight /= pr->r_count;		/* normalize weight */
			pr->r_hist.h_lastgame /= pr->r_count;	/* normalize lastgame */
		}
	}

	qsort((char *)rattbl, n, sizeof *rattbl, ratcomp);
}

/*
**	ratprint: print a rating table with the given heading
*/
ratprint(rattbl, heading)
rating *rattbl;
char   *heading;
{
	register rating	   *pr;
	long				avgmpt, avtnpt;
	double				winrt;

	printf("%.*s  %-6.6s %-*.*s %1.1s  %*s", rwid, "######", heading,
		DISPLEN-9, DISPLEN-9, ptsmodename, "NP", gwid, "GP");
	if(showlast == True)
		printf("  %*s", gwid, "LG");
	printf("  %5s  %5s  %4s  %5s\n",
		"WinRt", "GamPt", "TnPt", "WgtPt");
	for(pr = rattbl;pr->r_name != 0;++pr) {
		if(pr->r_count == 0 || pr->r_hist.h_games == 0)
			continue;
		histcalc(&pr->r_hist, &winrt, &avgmpt, &avtnpt);
		printf("%*ld  %-*.*s  %3d  %*ld",
			rwid, pr->r_hist.h_rank,	/* normalized by ratranksort */
			DISPLEN-5, DISPLEN-5, pr->r_name, pr->r_count,
			gwid, pr->r_hist.h_games
		);
		if(showlast == True)
			printf("  %*ld", gwid,
				pr->r_hist.h_lastgame	/* normalized by ratranksort */
			);
		printf("  %5.3f  %5ld  %4ld  %5ld\n",
			winrt, avgmpt, avtnpt,
			pr->r_hist.h_weight			/* normalized by ratranksort */
		);
	}
}