|
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 */ ); } }