|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T h
Length: 19000 (0x4a38)
Types: TextFile
Names: »histanal.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
└─⟦this⟧ »EUUGD18/General/Cubes/histanal.c«
/* 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 */
);
}
}