games@tekred.CNA.TEK.COM (04/28/89)
Submitted-by: gmp@rayssdb.RAY.COM (Gregory M. Paris) Posting-number: Volume 6, Issue 63 Archive-name: cubes2/Part05 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 5 (of 8)." # Contents: announce.c dieopts.c histanal.c scr.sh # Wrapped by billr@saab on Thu Apr 27 12:13:38 1989 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'announce.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'announce.c'\" else echo shar: Extracting \"'announce.c'\" \(12245 characters\) sed "s/^X//" >'announce.c' <<'END_OF_FILE' X/* vi:set sw=4 ts=4: */ X#ifndef lint Xstatic char sccsid[] = "@(#)announce.c 5.1 (G.M. Paris) 89/01/22"; X#endif lint X X/* X** X** cubes 5.1 Copyright 1989 Gregory M. Paris X** Permission granted to redistribute on a no charge basis. X** All other rights are reserved. X** X*/ X X#include <stdio.h> X#include <strings.h> X#include <syslog.h> X#include <signal.h> X#include <errno.h> X#include <setjmp.h> X#include <ctype.h> X#include <sys/types.h> X#include <sys/time.h> X#include <sys/socket.h> X#include <sys/ioctl.h> X#include "cubes.h" X Xextern int errno; Xextern player plr[]; Xextern int waiting; Xextern int watching; Xextern int winscore; Xextern boolean jokermode; Xextern boolean clearobs; Xextern boolean adjroster; Xextern boolean isproxy(); Xextern struct timeval poll; X X/* X** announce: send a message to all human players but one X*/ Xannounce(but, mesg) Xint but; Xchar *mesg; X{ X register int c; X X for(c = 0;c < PLAYERS;++c) if(c != but) { X switch(plr[c].p_stat) { X case Active: X case Waiting: X case Watching: X case Spider: X (void) message(c, mesg); X break; X } X } X} X X/* X** annscore: announce player score X** announces differently for affected player X*/ Xannscore(sc) Xint sc; X{ X char msgbuf[MESGLEN+64]; X X sprintf(msgbuf, "%d Player %d now has %d points (squandered %d).\r\n", X M_OSCO, sc+1, plr[sc].p_score, plr[sc].p_squander); X announce(sc, msgbuf); X if(plr[sc].p_stat != Computer) { X sprintf(msgbuf, "%d You now have %d points (squandered %d).\r\n", X M_MSCO, plr[sc].p_score, plr[sc].p_squander); X (void) message(sc, msgbuf); X } X} X X/* X** annfarewell: bid farewell to player X** doesn't tell player about own leave X*/ Xannfarewell(oldc) Xint oldc; X{ X char msgbuf[MESGLEN+64]; X X sprintf(msgbuf, "%d farewell %d %s\r\n", X M_FARE, oldc+1, plr[oldc].p_name); X announce(oldc, msgbuf); X adjroster = True; X} X X/* X** clearroster: send clear roster message to all clients X*/ Xclearroster() X{ X register int c; X X clearobs = False; X for(c = 0;c < PLAYERS;++c) { X switch(plr[c].p_stat) { X case Active: X case Waiting: X case Watching: X case Spider: X (void) simp(c, M_CPLR, "Clear player roster."); X break; X } X } X} X X/* X** heartbeat: send a noop to Spiders X*/ Xheartbeat() X{ X register int c; X X for(c = 0;c < PLAYERS;++c) X if(plr[c].p_stat == Spider) X (void) simp(c, M_NOOP, "Bum-Bum Bum-Bump."); X} X X/* X** observers: tell how many observers there are X** If clearobs is True the observers are showing in the X** roster, so report unseen observers as zero. X*/ Xobservers(c) X{ X register int cc; X char msgbuf[MESGLEN]; X X cc = (clearobs == True) ? 0 : (waiting + watching); X sprintf(msgbuf, "%d %d observer%s\r\n", M_NOBS, cc, cc == 1 ? "" : "s"); X X if(c != -1) X return message(c, msgbuf); X X for(cc = 0;cc < PLAYERS;++cc) { X switch(plr[cc].p_stat) { X case Active: X case Waiting: X case Watching: X case Spider: X (void) message(cc, msgbuf); X break; X } X } X X return 0; X} X X/* X** roster: broadcast the roster to all players X*/ Xroster(only, showobs) Xint only; Xboolean showobs; X{ X register int c, cc; X char msgbuf[MESGLEN]; X X clearobs = False; /* no observers showing (yet) */ X X /* X ** Tell each player who he/she is then give a roster. X ** Tell Waiting and Watching players, but don't show them X ** unless showobjs is True. X */ X for(c = 0;c < PLAYERS;++c) { X if(only != -1 && c != only) X continue; X if(plr[c].p_stat == Inactive || plr[c].p_stat == Computer) X continue; X X if(simp(c, M_CPLR, "New player roster.") < 0) X continue; X X for(cc = 0;cc < PLAYERS;++cc) { X switch(plr[cc].p_stat) { X case Watching: X case Waiting: X case Spider: X if(showobs == False) X break; X clearobs = True; X /* fall */ X case Active: X case Computer: X if(cc == c) X sprintf(msgbuf, "%d You are player %d %s\r\n", X M_UARE, cc+1, plr[cc].p_name); X else X sprintf(msgbuf, "%d player %d %s\r\n", X M_PNUM, cc+1, plr[cc].p_name); X if(message(c, msgbuf) < 0) X goto nextrecip; X X if(cc == c) X sprintf(msgbuf, X "%d You now have %d points (squandered %d).\r\n", X M_MSCO, plr[cc].p_score, plr[cc].p_squander); X else X sprintf(msgbuf, X "%d Player %d now has %d points (squandered %d).\r\n", X M_OSCO, cc+1, plr[cc].p_score, plr[cc].p_squander); X if(message(c, msgbuf) < 0) X goto nextrecip; X break; X } X } X X (void) observers(c); Xnextrecip: ; X } X X /* X ** If this was a broadcast, the roster is now up to date. X */ X if(only == -1) X adjroster = False; X} X X/* X** tellstatus: report a player's status X*/ Xtellstatus(c) X{ X m_num t; X char *m; X X switch(plr[c].p_stat) { X case Inactive: X return -1; X case Active: X if(isproxy(c) == False) { X t = M_ACTV, m = "You're now an active player."; X break; X } X t = M_AUTO, m = "You're on autopilot; type g<return> to gain control."; X break; X case Waiting: X t = M_WAIT, m = "You're waiting to be added..."; X break; X case Watching: X t = M_VOYR, X m = "You're now a voyeur; type g<return> to enter this game."; X break; X case Spider: X t = M_SPDR, m = "You're now a spider; type g<return> to start a game."; X break; X case Computer: X if(plr[c].p_fd < 0) X return -1; X t = M_AUTO, m = "You've somehow become a computer!!!"; X syslog(LOG_DEBUG, "tellstatus: `%s' is a Computer", plr[c].p_name); X break; X default: X t = M_ARGE, m = "You're now in an unknown mode!"; X syslog(LOG_ERR, "tellstatus: `%s' is in an unknown mode (%d)", X plr[c].p_name, plr[c].p_stat); X break; X } X X return simp(c, t, m); X} X X/* X** tellwinscore: notify one or all players about the current winscore X** also notifies player as to whether jokermode is True or False. X*/ Xtellwinscore(c) X{ X register int cc; X char msgbuf[MESGLEN]; X X sprintf(msgbuf, "%d This is a %d point game%s.\r\n", M_PARM, winscore, X jokermode == True ? " with jokers" : ""); X X if(c >= 0) X (void) message(c, msgbuf); X else { X for(cc = 0;cc < PLAYERS;++cc) { X switch(plr[cc].p_stat) { X case Active: X case Waiting: X case Watching: X case Spider: X (void) message(cc, msgbuf); X break; X } X } X } X} X X/* X** simp: format and send a simple message X*/ Xsimp(c, type, mtxt) Xint c; Xm_num type; Xchar *mtxt; X{ X char msgbuf[MESGLEN]; X X sprintf(msgbuf, "%3d %.*s\r\n", type, MESGLEN-7, mtxt); X return message(c, msgbuf); X} X X/* X** invalid: invalid command message X*/ Xinvalid(c) Xint c; X{ X return simp(c, M_ARGE, "Invalid command; type ?<return> for help."); X} X X/* X** multimesg: send multi-line message X*/ Xmultimesg(c, type, marr) Xint c; Xm_num type; Xchar *marr[]; X{ X int n; X char msgbuf[MESGLEN]; X X /* X ** Send each line of the message. All but the final X ** has a period between the type number and the message. X */ X for(n = 0;marr[n] != 0;++n) { X sprintf(msgbuf, "%d%c%s\r\n", X type, marr[n+1] == 0 ? ' ' : '.', marr[n]); X if(message(c, msgbuf) < 0) X return -1; X } X X return n; X} X X/* X** messagejmp, messagetimo: message timeout handler X*/ Xstatic jmp_buf messagejmp; Xstatic int Xmessagetimo(sig) X{ X longjmp(messagejmp, sig); X} X X/* X** message: write a message on a channel X*/ Xmessage(c, mesg) Xint c; Xchar *mesg; X{ X int n, mesglen; X int (*oldalrm)(); X int (*oldpipe)(); X unsigned alarmv; X fd_set rdy; X X switch(plr[c].p_stat) { X case Inactive: X syslog(LOG_DEBUG, "message: to Inactive: %s", mesg); X return -1; X case Computer: X syslog(LOG_DEBUG, "message: to Computer: %s", mesg); X return 0; /* "succeeds" */ X } X X if(plr[c].p_fd < 0) { X if(plr[c].p_stat == Waiting && plr[c].p_computer != 0) X return 0; /* OK, Waiting Computer */ X syslog(LOG_DEBUG, X "message: bad fd (c=%d;stat=%d): %s", c, plr[c].p_stat, mesg); X oldplayer(c); X return -1; X } X X if((mesglen = strlen(mesg)) == 0) X return 0; X X oldalrm = signal(SIGALRM, SIG_IGN); X oldpipe = signal(SIGPIPE, SIG_IGN); X alarmv = alarm(0); X switch(setjmp(messagejmp)) { X case 0: X break; X case SIGALRM: X syslog(LOG_DEBUG, "message: timeout: %s", mesg); X goto common; X case SIGPIPE: X (void) alarm(0); X syslog(LOG_DEBUG, "message: dead pipe: %s", mesg); X goto common; X default: X (void) alarm(0); X syslog(LOG_DEBUG, "message: unexpected signal: %s", mesg); Xcommon: X oldplayer(c); X (void) signal(SIGALRM, oldalrm); X (void) signal(SIGPIPE, oldpipe); X (void) alarm(alarmv); X return -1; X } X X (void) signal(SIGALRM, messagetimo); X (void) signal(SIGPIPE, messagetimo); /* treat like timeout */ X (void) alarm(WRITETIMO); X FD_ZERO(&rdy); X FD_SET(plr[c].p_fd, &rdy); X (void) select(plr[c].p_fd+1, NOSEL, &rdy, NOSEL, &poll); X if((n = write(plr[c].p_fd, mesg, mesglen)) < 0) { X if(errno != EPIPE && errno != ENOTCONN) X syslog(LOG_DEBUG, "message: write: %m: %s", mesg); X (void) alarm(0); X oldplayer(c); X (void) signal(SIGALRM, oldalrm); X (void) signal(SIGPIPE, oldpipe); X (void) alarm(alarmv); X return -1; X } X (void) alarm(0); X (void) signal(SIGALRM, oldalrm); X (void) signal(SIGPIPE, oldpipe); X (void) alarm(alarmv); X return n; X} X X/* X** replyjmp, replytimo: reply timeout handler X*/ Xstatic jmp_buf replyjmp; Xstatic int Xreplytimo() X{ X longjmp(replyjmp, 1); X} X X/* X** reply: read a reply from a channel X*/ Xreply(c, mesg, mesglen) Xint c, mesglen; Xchar *mesg; X{ X int n, ntot; X int (*oldalrm)(); X unsigned alarmv; X fd_set rdy; X X switch(plr[c].p_stat) { X case Inactive: X syslog(LOG_DEBUG, "reply: from Inactive"); X return -1; X case Computer: X syslog(LOG_DEBUG, "reply: from Computer"); X return -1; X } X X /* X ** Handle read timeout. X */ X oldalrm = signal(SIGALRM, SIG_IGN); X alarmv = alarm(0); X if(setjmp(replyjmp) != 0) { X (void) signal(SIGALRM, oldalrm); X (void) alarm(alarmv); X /* X ** Increment this player's timeout count. If we've X ** reached or exceeded MAXTIMO, then say goodbye. X */ X if(++plr[c].p_timeouts >= MAXTIMO) { X syslog(LOG_DEBUG, "reply: too many timeouts"); X (void) simp(c, M_DOWN, "Too many timeouts -- goodbye!"); X oldplayer(c); X return -1; X } X /* X ** Send a message to the client indicating timeout. X */ X syslog(LOG_DEBUG, "reply: timeout"); X if(simp(c, M_INFO, "Timed out waiting for your response!") < 0) X return -1; X (void) send(plr[c].p_fd, "\001", 1, MSG_OOB); X return -2; X } X X /* X ** Read until we get a newline. X */ X (void) signal(SIGALRM, replytimo); X (void) alarm(READTIMO); X ntot = 0, mesg[0] = '\0'; X while(index(mesg, '\n') == 0) { X FD_ZERO(&rdy); X FD_SET(plr[c].p_fd, &rdy); X (void) select(plr[c].p_fd+1, &rdy, NOSEL, NOSEL, HANG); X if((n = read(plr[c].p_fd, mesg+ntot, mesglen-ntot)) <= 0) { X (void) alarm(0); X#ifdef notdef X if(n < 0) X syslog(LOG_DEBUG, "reply: read: %m"); X else X syslog(LOG_DEBUG, "reply: read got zero bytes"); X#endif notdef X oldplayer(c); X (void) signal(SIGALRM, oldalrm); X (void) alarm(alarmv); X return -1; X } X ntot += n, mesg[ntot] = '\0'; X } X (void) alarm(0); X X /* X ** Had a successful read, so zero timeout count. X */ X plr[c].p_timeouts = 0; X X /* X ** Strip trailing control characters and spaces. X ** Then get rid of control characters in the message. X ** We must be careful to preserve ASYNCMARK at position 0. X XXX Assumes iscntrl(ASYNCMARK) is true. X */ X for(n = ntot - 1;n >= 0;--n) { X mesg[n] &= 0x7f; /* strip high bit */ X if(iscntrl(mesg[n])) { X if(n != 0 || mesg[n] != ASYNCMARK) X mesg[n] = '\0'; X } else if(isspace(mesg[n])) X mesg[n] = '\0'; X else X break; X } X for(n = 0;mesg[n] != '\0';++n) X if(iscntrl(mesg[n])) X if(n != 0 || mesg[n] != ASYNCMARK) X mesg[n] = '?'; X X (void) signal(SIGALRM, oldalrm); X (void) alarm(alarmv); X return n; X} X X/* X** dialogue: flush pending data, send message, read reply X*/ Xdialogue(c, msg, len) Xint c, len; Xchar *msg; X{ X long q; X int n; X char junk[MESGLEN]; X X if(plr[c].p_fd < 0) X return -1; X if(plr[c].p_stat == Inactive || plr[c].p_stat == Computer) X return -1; X X /* X ** Because of the way this protocol is set up, any pending input at X ** this point should be discarded, because it means that the client X ** has somehow gotten out of sync. X */ X while(ioctl(plr[c].p_fd, FIONREAD, (char *)&q) != -1 && q > 0) { X if(q > sizeof junk) X q = sizeof junk; X (void) read(plr[c].p_fd, junk, (int)q); X } X X /* X ** Send message. X */ X if(message(c, msg) < 0) X return -1; X X /* X ** We keep reading here until we get a non-asynchronous reply. X */ X for(;;) { X if((n = reply(c, msg, len)) < 0) /* error or timeout */ X return n; X if(msg[0] != ASYNCMARK) /* not asynchronous */ X return n; X } X} END_OF_FILE if test 12245 -ne `wc -c <'announce.c'`; then echo shar: \"'announce.c'\" unpacked with wrong size! fi # end of 'announce.c' fi if test -f 'dieopts.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dieopts.c'\" else echo shar: Extracting \"'dieopts.c'\" \(18796 characters\) sed "s/^X//" >'dieopts.c' <<'END_OF_FILE' X/* vi:set sw=4 ts=4: */ X#ifndef lint Xstatic char sccsid[] = "@(#)dieopts.c 5.1 (G.M. Paris) 89/01/22"; X#endif lint X X/* X** X** cubes 5.1 Copyright 1989 Gregory M. Paris X** Permission granted to redistribute on a no charge basis. X** All other rights are reserved. X** X*/ X X#include <strings.h> X#include "cubes.h" X Xextern boolean jokermode; X X#define SIDESIZE (JOKER + 1) /* enough room for all faces */ X#define JSIDES (jokermode == True ? JOKER : SIDES) X#define JFACE(f) ((jokermode == True && (f) == JOKER) ? P_JOKERMULT : (f)) X#define FVALUE(f) ((f) == ACE ? P_ACEMULT : JFACE(f)) X X/* X** names: names of die faces, singular and plural X** Must be filled in to at least max of SIDES and NDICE! X*/ Xchar *numnames[][2] = { X { "zero", "zeroes" }, X { "one", "ones" }, X { "two", "twos" }, X { "three", "threes" }, X { "four", "fours" }, X { "five", "fives" }, X { "six", "sixes" }, X { "seven", "sevens" }, X { "eight", "eights" }, X { "joker", "jokers" }, X}; X X/* X** initdice: initialize dice status X*/ Xinitdice(pd) Xregister diceset *pd; X{ X register int d; X X for(d = 0;d < NDICE;++d) { X pd->d_stat[d] = Free; /* rolling this die */ X pd->d_face[d] = BADFACE; /* impossible value */ X pd->d_comb[d] = Nothing; /* no combination */ X } X zapdesc(pd); /* clear scoring description */ X pd->d_best = Nothing; /* no combination */ X pd->d_again = True; /* rolling again */ X pd->d_rolling = NDICE; /* rolling all dice */ X pd->d_pts_roll = 0; /* no points rolled */ X pd->d_pts_dice = 0; /* no points in set */ X pd->d_pts_turn = 0; /* no points in turn */ X pd->d_pts_max = 0; /* no max in turn yet */ X pd->d_mesg[0] = '\0'; /* no message */ X} X X/* X** pickup: similar to initdice but only does initialization X** for re-roll of (usually partial) dice set X** X** non-scoring dice are Freed X** change Rolled dice to Free X** set Free dice faces to BADFACE and combs to Nothing X** change Taken dice to Held X** set Held dice combs to Previous X** resets d_rolling, d_best, d_pts_roll, and d_mesg X*/ Xpickup(pd) Xregister diceset *pd; X{ X register int d; X X zapdesc(pd); X pd->d_best = Nothing; X pd->d_again = True; X pd->d_rolling = 0; X pd->d_pts_roll = 0; X pd->d_mesg[0] = '\0'; X X for(d = 0;d < NDICE;++d) { X if(pd->d_comb[d] == Nothing) X pd->d_stat[d] = Free; X switch(pd->d_stat[d]) { X case Rolled: X pd->d_stat[d] = Free; /* fall */ X case Free: X pd->d_face[d] = BADFACE; X pd->d_comb[d] = Nothing; X ++pd->d_rolling; X break; X case Taken: X pd->d_stat[d] = Held; /* fall */ X case Held: X pd->d_comb[d] = Previous; X break; X } X } X X /* X ** If no dice are free, then free them all. X */ X if(pd->d_rolling == 0) { X for(d = 0;d < NDICE;++d) { X pd->d_stat[d] = Free; X pd->d_face[d] = BADFACE; X pd->d_comb[d] = Nothing; X } X pd->d_rolling = NDICE; X pd->d_pts_dice = 0; X } X} X X/* X** rolldice: roll free dice X** sets d_rolling to number of dice rolled X** sets d_again to False X*/ Xrolldice(pd) Xregister diceset *pd; X{ X register int d; X X pd->d_again = False; X zapdesc(pd); X pd->d_best = Nothing; X pd->d_rolling = 0; X for(d = 0;d < NDICE;++d) { X if(pd->d_stat[d] == Free) { X pd->d_stat[d] = Rolled; X pd->d_face[d] = dieroll(); X pd->d_comb[d] = Nothing; X ++pd->d_rolling; X } X } X} X X/* X** zapdesc: clear scoring combination description X*/ Xzapdesc(pd) Xdiceset *pd; X{ X#ifdef DESC X register int d; X X for(d = 0;d < NDICE;++d) { X pd->d_desc[d].sc_comb = Nothing; /* no combination */ X pd->d_desc[d].sc_num = 0; /* none of them */ X pd->d_desc[d].sc_face = BADFACE; /* no face value */ X } X#else DESC X#ifdef lint X pd = pd; X#endif lint X#endif DESC X} X X/* X** evaluate: categorize dice marked as Rolled or Taken X*/ Xevaluate(pd) Xregister diceset *pd; X{ X register int d, s; X#ifdef DESC X register int nc = 0; X#endif DESC X int held, rolled; X int rshow[SIDESIZE]; X int minshow, maxshow; X int len, aces, fives, jokers; X boolean ok; X X /* X ** We start with s == 0 to allow accounting for BADFACE. X */ X for(s = 0;s <= JSIDES;++s) X rshow[s] = 0; X X pd->d_mesg[0] = '\0'; X pd->d_best = Nothing; X zapdesc(pd); X rolled = held = 0; X for(d = 0;d < NDICE;++d) { X s = pd->d_face[d]; X switch(pd->d_stat[d]) { X case Rolled: X case Taken: X pd->d_comb[d] = Nothing; X ++rolled, ++rshow[s]; X break; X case Held: X pd->d_comb[d] = Previous; X ++held; X break; X default: X pd->d_stat[d] = Free; X pd->d_comb[d] = Nothing; X break; X } X } X X /* X ** Check for All_of_a_kind. All dice count. X ** Uses all dice. X */ X if((rolled + held) == NDICE) { X s = pd->d_face[0]; X for(d = 1;d < NDICE;++d) X if(pd->d_face[d] != s) X break; X if(d == NDICE) { X for(d = 0;d < NDICE;++d) X pd->d_comb[d] = All_of_a_kind; X pd->d_best = All_of_a_kind; X X#ifdef DESC X pd->d_desc[nc].sc_comb = All_of_a_kind; X pd->d_desc[nc].sc_num = 1; X pd->d_desc[nc].sc_face = pd->d_face[0]; X/* ++nc; /* not needed */ X#endif DESC X X sprintf(pd->d_mesg, "%s of a kind in %s", X NUMBER(NDICE), FACE(pd->d_face[0], NDICE)); X return; X } X } X X /* X ** Check for Straight. Only rolled dice count. X ** Uses all dice. X */ X if(rolled == NDICE) { X if(rshow[BADFACE] != 0) X ok = False; X else { X ok = True, minshow = JSIDES + 1, maxshow = 0; X for(s = 0;ok == True && s <= JSIDES;++s) { X switch(rshow[s]) { X case 1: X if(s < minshow) minshow = s; X if(s > maxshow) maxshow = s; X break; X case 0: break; X default: ok = False; break; X } X } X } X X if(ok == True && maxshow - minshow == NDICE - 1) { X for(d = 0;d < NDICE;++d) X pd->d_comb[d] = Straight; X pd->d_best = Straight; X X#ifdef DESC X pd->d_desc[nc].sc_comb = Straight; X pd->d_desc[nc].sc_num = 1; X/* ++nc; /* not needed */ X#endif DESC X X strcpy(pd->d_mesg, "a straight"); X return; X } X } X X#ifdef ASMSTR X /* X ** Check for Assembled Straight. Held and Rolled dice count. X ** Uses all dice. X */ X if((rolled + held) == NDICE) { X int ashow[SIDESIZE]; X X /* X ** We start with s == 0 to allow accounting for BADFACE. X */ X for(s = 0;s <= JSIDES;++s) X ashow[s] = 0; X for(d = 0;d < NDICE;++d) X ++ashow[pd->d_face[d]]; X X if(ashow[BADFACE] != 0) X ok = False; X else { X ok = True, minshow = JSIDES + 1, maxshow = 0; X for(s = 0;ok == True && s <= JSIDES;++s) { X switch(ashow[s]) { X case 1: X if(s < minshow) minshow = s; X if(s > maxshow) maxshow = s; X break; X case 0: break; X default: ok = False; break; X } X } X } X X if(ok == True && maxshow - minshow == NDICE - 1) { X for(d = 0;d < NDICE;++d) X pd->d_comb[d] = Asm_straight; X pd->d_best = Asm_straight; X X#ifdef DESC X pd->d_desc[nc].sc_comb = Asm_straight; X pd->d_desc[nc].sc_num = 1; X /* ++nc; /* not needed */ X#endif DESC X X switch(rolled) { X case ASMSTR-1: strcpy(pd->d_mesg, "a completed straight"); break; X case ASMSTR-2: strcpy(pd->d_mesg, "an inside straight"); break; X case 1: strcpy(pd->d_mesg, "an outside straight"); break; X default: /*?*/ strcpy(pd->d_mesg, "an assembled straight"); break; X } X return; X } X } X#endif ASMSTR X X#ifdef FOUR X /* X ** Check for Four_of_a_kind. Only rolled dice count. X ** Assumes that only one is possible. X */ X if(rolled >= FOUR) { X for(s = 1;s <= JSIDES;++s) { X while(rshow[s] >= FOUR) { X maxshow = FOUR; X for(d = 0;d < NDICE && maxshow > 0;++d) { X switch(pd->d_stat[d]) { X case Rolled: X case Taken: X if(pd->d_comb[d] == Nothing && pd->d_face[d] == s) { X pd->d_comb[d] = Four_of_a_kind; X --rshow[s]; X --maxshow; X } X break; X } X } X X if(pd->d_best == Nothing) X pd->d_best = Four_of_a_kind; X X#ifdef DESC X pd->d_desc[nc].sc_comb = Four_of_a_kind; X pd->d_desc[nc].sc_num = 1; X pd->d_desc[nc].sc_face = s; X ++nc; X#endif DESC X X if((len = strlen(pd->d_mesg)) > 0) { X strcat(pd->d_mesg, ", "); X len += 2; X } X sprintf(pd->d_mesg + len, "%s of a kind in %s", X NUMBER(FOUR), FACE(s, FOUR)); X } X } X } X#endif FOUR X X#ifdef SMSTR X /* X ** Check for Small_straight. Only rolled dice count. X ** Assumes that only one is possible. X */ X if(rolled >= NDICE - 1) { X ok = False; X if(rshow[BADFACE] == 0) { X minshow = maxshow = 0; X for(s = 1;ok == False && s <= JSIDES;++s) { X switch(rshow[s]) { X case 1: case 2: X if(minshow == 0) X minshow = maxshow = s; X else if((maxshow = s) - minshow == NDICE - 2) X ok = True; X break; X case 0: minshow = maxshow = 0; break; X default: s = JSIDES; break; X } X } X } X X if(ok == True) { X for(s = minshow;s <= maxshow;++s) { X for(ok = True, d = 0;ok == True && d < NDICE;++d) { X switch(pd->d_stat[d]) { X case Rolled: X case Taken: X if(pd->d_comb[d] == Nothing && pd->d_face[d] == s) { X pd->d_comb[d] = Small_straight; X --rshow[s]; X ok = False; /* got it */ X } X break; X } X } X } X if(pd->d_best == Nothing) X pd->d_best = Small_straight; X X#ifdef DESC X pd->d_desc[nc].sc_comb = Small_straight; X pd->d_desc[nc].sc_num = 1; X ++nc; X#endif DESC X X if(pd->d_mesg[0] != '\0') X strcat(pd->d_mesg, ", "); X strcat(pd->d_mesg, "a small straight"); X } X } X#endif SMSTR X X /* X ** Check for Three_of_a_kind. Only rolled dice count. X ** Code is supposed to be able to handle more than one X ** Three_of_a_kind, but is untested with current NDICE val. X */ X if(rolled >= THREE) { X for(s = 1;s <= JSIDES;++s) { X while(rshow[s] >= THREE) { X maxshow = THREE; X for(d = 0;d < NDICE && maxshow > 0;++d) { X switch(pd->d_stat[d]) { X case Rolled: X case Taken: X if(pd->d_comb[d] == Nothing && pd->d_face[d] == s) { X pd->d_comb[d] = Three_of_a_kind; X --rshow[s]; X --maxshow; X } X break; X } X } X X if(pd->d_best == Nothing) X pd->d_best = Three_of_a_kind; X X#ifdef DESC X pd->d_desc[nc].sc_comb = Three_of_a_kind; X pd->d_desc[nc].sc_num = 1; X pd->d_desc[nc].sc_face = s; X ++nc; X#endif DESC X X if((len = strlen(pd->d_mesg)) > 0) { X strcat(pd->d_mesg, ", "); X len += 2; X } X sprintf(pd->d_mesg + len, "%s of a kind in %s", X NUMBER(THREE), FACE(s, THREE)); X } X } X } X X /* X ** Check for stray Aces, Fives, and Jokers. Only rolled dice count. X */ X aces = fives = jokers = 0; X for(d = 0;d < NDICE;++d) { X switch(pd->d_stat[d]) { X case Rolled: X case Taken: X if(pd->d_comb[d] == Nothing) { X switch(pd->d_face[d]) { X case ACE: X ++aces; X pd->d_comb[d] = Ace; X if(pd->d_best == Nothing X || pd->d_best == Joker || pd->d_best == Five) X pd->d_best = Ace; X break; X case FIVE: X ++fives; X pd->d_comb[d] = Five; X if(pd->d_best == Nothing || pd->d_best == Joker) X pd->d_best = Five; X break; X case JOKER: X /* X ** The test for jokermode here prevents any Joker X ** dice being marked, thus it is unnecessary to X ** test jokermode when testing Joker in other places. X */ X if(jokermode == True) { X ++jokers; X pd->d_comb[d] = Joker; X if(pd->d_best == Nothing) X pd->d_best = Joker; X } X break; X } X } X break; X } X } X X if(pd->d_best != Nothing) { X if(aces > 0) { X#ifdef DESC X pd->d_desc[nc].sc_comb = Ace; X pd->d_desc[nc].sc_num = aces; X pd->d_desc[nc].sc_face = ACE; X ++nc; X#endif DESC X if((len = strlen(pd->d_mesg)) > 0) { X strcat(pd->d_mesg, ", "); X len += 2; X } X sprintf(pd->d_mesg + len, "%s %s", X NUMBER(aces), FACE(ACE, aces)); X } X X if(fives > 0) { X#ifdef DESC X pd->d_desc[nc].sc_comb = Five; X pd->d_desc[nc].sc_num = fives; X pd->d_desc[nc].sc_face = FIVE; X ++nc; X#endif DESC X if((len = strlen(pd->d_mesg)) > 0) { X strcat(pd->d_mesg, ", "); X len += 2; X } X sprintf(pd->d_mesg + len, "%s %s", X NUMBER(fives), FACE(FIVE, fives)); X } X X if(jokermode == True && jokers > 0) { X#ifdef DESC X pd->d_desc[nc].sc_comb = Joker; X pd->d_desc[nc].sc_num = jokers; X pd->d_desc[nc].sc_face = JOKER; X ++nc; X#endif DESC X if((len = strlen(pd->d_mesg)) > 0) { X strcat(pd->d_mesg, ", "); X len += 2; X } X sprintf(pd->d_mesg + len, "%s %s", X NUMBER(jokers), FACE(JOKER, jokers)); X } X } X X if(pd->d_best == Nothing) { X strcpy(pd->d_mesg, "nothing"); X#ifdef DESC X pd->d_desc[nc].sc_num = 1; /* to be strictly accurate */ X/* ++nc; /* not needed */ X#endif DESC X } X} X X/* X** keepall: mark all scoring dice (based on combinations) as Taken X** frees Nothing dice and marks as Held Previous dice X** sets d_rolling to number of Free dice X** does not alter d_again or d_pts_???? X*/ Xkeepall(pd) Xregister diceset *pd; X{ X register int d; X X pd->d_rolling = 0; X for(d = 0;d < NDICE;++d) { X switch(pd->d_comb[d]) { X case Previous: X pd->d_stat[d] = Held; X break; X case Nothing: X pd->d_stat[d] = Free; X ++pd->d_rolling; X break; X case All_of_a_kind: X case Asm_straight: X if(pd->d_stat[d] != Held) X pd->d_stat[d] = Taken; X break; X default: X pd->d_stat[d] = Taken; X break; X } X } X} X X/* X** freeall: mark all non-held dice (based on combinations) as Free X** sets d_rolling to number of Free dice X** does not alter d_again or d_pts_???? X*/ Xfreeall(pd) Xregister diceset *pd; X{ X register int d; X X pd->d_rolling = 0; X for(d = 0;d < NDICE;++d) { X switch(pd->d_comb[d]) { X case Previous: X pd->d_stat[d] = Held; X break; X default: X pd->d_stat[d] = Free; X ++pd->d_rolling; X break; X } X } X} X X/* X** scoredice: update points values after roll and decision X*/ Xscoredice(pd) Xregister diceset *pd; X{ X register int d, dd, n; X register int points; X int face; X combination comb[NDICE]; X X /* X ** The easiest of all is the Nothing combination. The points for the roll X ** are the negative of the points accumulated so far in this turn. We X ** leave d_pts_max alone -- signifying the true loss due to the reroll. X */ X if(pd->d_best == Nothing) { X pd->d_pts_roll = -pd->d_pts_turn; X pd->d_pts_dice = pd->d_pts_turn = 0; X/* pd->d_pts_max = pd->d_pts_max; /* leave alone */ X return; X } X X /* X ** All_of_a_kind can occur at any time and changes the value of any saved X ** dice. The value of d_pts_dice must be used to update d_pts_roll, X ** d_pts_turn, and d_pts_max. Further, the value of All_of_a_kind depends X ** on its denomination. X */ X if(pd->d_best == All_of_a_kind) { X face = pd->d_face[0]; X points = P_AOKMULT * FVALUE(face); X pd->d_pts_roll = points - pd->d_pts_dice; X if((pd->d_pts_turn += pd->d_pts_roll) > pd->d_pts_max) X pd->d_pts_max = pd->d_pts_turn; X return; X } X X /* X ** A Straight can occur only when all dice are being rolled, so d_pts_roll X ** and d_pts_dice should be zero. The value of a Straight is a constant. X */ X if(pd->d_best == Straight) { X points = P_STRAIGHT; X pd->d_pts_roll = points; X pd->d_pts_dice = points; X if((pd->d_pts_turn += points) > pd->d_pts_max) X pd->d_pts_max = pd->d_pts_turn; X return; X } X X#ifdef ASMSTR X /* X ** An Asm_straight can occur only when there were previously held dice. X ** As with All_of_a_kind, the value of the saved dice is altered. X ** This combination uses all dice and its value is a constant. X */ X if(pd->d_best == Asm_straight) { X points = P_ASMSTR; X pd->d_pts_roll = points - pd->d_pts_dice; X if((pd->d_pts_turn += pd->d_pts_roll) > pd->d_pts_max) X pd->d_pts_max = pd->d_pts_turn; X return; X } X#endif ASMSTR X X /* X ** The rest of the combinations can occur in concert. X */ X pd->d_pts_roll = 0; X X#ifdef FOUR X /* X ** Four_of_a_kind is tricky. The value is dependent on the denomination, X ** but not all dice are used up. Even worse, for the sake of generality, X ** we must assume that there could be more than one Four_of_a_kind. X ** This code assumes that Four_of_a_kind and Small_straight will X ** not occur at the same time. X */ X if(pd->d_best == Four_of_a_kind) { X for(d = 0;d < NDICE;++d) X comb[d] = pd->d_comb[d]; X for(d = 0;d < NDICE;++d) { X if(comb[d] == Four_of_a_kind) { X /* X ** Award the points for this one. X */ X face = pd->d_face[d]; X points = P_4OKMULT * FVALUE(face); X pd->d_pts_roll += points; X pd->d_pts_dice += points; X if((pd->d_pts_turn += points) > pd->d_pts_max) X pd->d_pts_max = pd->d_pts_turn; X X /* X ** Obliterate the scoring dice. X */ X for(n = FOUR, dd = d;dd < NDICE;++dd) { X if(comb[dd] != Four_of_a_kind) X continue; X if(pd->d_face[dd] != face) X continue; X comb[dd] = Nothing; X if(--n == 0) X break; X } X } X } X } X#endif FOUR X X#ifdef SMSTR X /* X ** Score a Small_straight. This code assumes that Four_of_a_kind X ** (or Three_of_a_kind) and Small_straight will not occur at the X ** same time. It also assumes that only one Small_straight can X ** occur at a time. X */ X if(pd->d_best == Small_straight) { X points = P_SMSTR; X pd->d_pts_roll += points; X pd->d_pts_dice += points; X if((pd->d_pts_turn += points) > pd->d_pts_max) X pd->d_pts_max = pd->d_pts_turn; X } X#endif SMSTR X X /* X ** Three_of_a_kind is tricky. The value is dependent on the denomination, X ** but not all dice are used up. Even worse, for the sake of generality, X ** we must assume that there could be more than one Three_of_a_kind. X ** Assumes that Three_of_a_kind does not occur in conjunction with X ** Four_of_a_kind or Small_straight. X */ X if(pd->d_best == Three_of_a_kind) { X for(d = 0;d < NDICE;++d) X comb[d] = pd->d_comb[d]; X for(d = 0;d < NDICE;++d) { X if(comb[d] == Three_of_a_kind) { X /* X ** Award the points for this one. X */ X face = pd->d_face[d]; X points = P_3OKMULT * FVALUE(face); X pd->d_pts_roll += points; X pd->d_pts_dice += points; X if((pd->d_pts_turn += points) > pd->d_pts_max) X pd->d_pts_max = pd->d_pts_turn; X X /* X ** Obliterate the scoring dice. X */ X for(n = THREE, dd = d;dd < NDICE;++dd) { X if(comb[dd] != Three_of_a_kind) X continue; X if(pd->d_face[dd] != face) X continue; X comb[dd] = Nothing; X if(--n == 0) X break; X } X } X } X } X X /* X ** Finally we have Aces, Fives and Jokers. Easy. X */ X for(d = 0;d < NDICE;++d) { X switch(pd->d_comb[d]) { X case Joker: points = P_JOKER; goto addscore; X case Five: points = P_FIVE; goto addscore; X case Ace: points = P_ACE; goto addscore; Xaddscore: X pd->d_pts_roll += points; X pd->d_pts_dice += points; X if((pd->d_pts_turn += points) > pd->d_pts_max) X pd->d_pts_max = pd->d_pts_turn; X break; X default: X break; X } X } X} X X#ifdef ASMSTR X/* X** heldsmall: if Small_straight held, return lowest face, else BADFACE X*/ Xheldsmall(pd) Xdiceset *pd; X{ X register int d, low; X diceset temp; X X if(pd->d_rolling != 1) X return BADFACE; X X /* X ** Pretend like we just rolled this hand then evaluate it. X */ X temp = *pd; X for(d = 0;d < NDICE;++d) X temp.d_stat[d] = Rolled; X evaluate(&temp); X if(temp.d_best != Small_straight) X return BADFACE; X X /* X ** Find lowest face in the Small_straight. X */ X low = JOKER+1; X for(d = 0;d < NDICE;++d) X if(temp.d_comb[d] == Small_straight && temp.d_face[d] < low) X low = temp.d_face[d]; X X return low; X} X#endif ASMSTR END_OF_FILE if test 18796 -ne `wc -c <'dieopts.c'`; then echo shar: \"'dieopts.c'\" unpacked with wrong size! fi # end of 'dieopts.c' fi if test -f 'histanal.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'histanal.c'\" else echo shar: Extracting \"'histanal.c'\" \(19000 characters\) sed "s/^X//" >'histanal.c' <<'END_OF_FILE' X/* vi:set sw=4 ts=4: */ X#ifndef lint Xstatic char sccsid[] = "@(#)histanal.c 5.1 (G.M. Paris) 89/01/22"; X#endif lint X X/* X** X** cubes 5.1 Copyright 1989 Gregory M. Paris X** Permission granted to redistribute on a no charge basis. X** All other rights are reserved. X** X*/ X X#include <stdio.h> X#include <pwd.h> X#include <syslog.h> X#include <strings.h> X#include "cubes.h" X X#ifdef lint X#ifndef HISTFILE X#define HISTFILE "dummy-histfile" X#endif HISTFILE X#endif lint X X/* X** rating: a structure for rating strategies and temperaments X*/ Xtypedef struct { X history r_hist; X long r_count; X char *r_name; X} rating; X Xextern unsigned nhist; Xextern history *hist; Xextern ptstype ptsmode; Xextern computer comptbl[]; Xextern int tempers, strategies; Xextern long gamenum; Xstatic int rwid; Xstatic int gwid; X Xextern int optind; Xextern int opterr; Xextern char *optarg; Xextern char *calloc(); Xextern double log10(); Xextern int nextupcmp(); Xextern char *fullname(); Xstruct passwd *getpwnam(); Xstruct passwd *getpwuid(); X Xboolean inprogress = False; /* dummy */ Xboolean jokermode = False; /* dummy */ Xwinpref gametype = Standard; /* dummy */ Xint winscore = WINSCORE; /* dummy */ Xint turnnum; /* dummy */ Xint nojokers(pd) diceset *pd; { pd = pd; } /* stub */ Xint nofives(pd) diceset *pd; { pd = pd; } /* stub */ Xint noaces(pd) diceset *pd; { pd = pd; } /* stub */ Xint no3deuce(pd) diceset *pd; { pd = pd; } /* stub */ Xint no3three(pd) diceset *pd; { pd = pd; } /* stub */ Xint no3any(pd) diceset *pd; { pd = pd; } /* stub */ Xint nosmall(pd) diceset *pd; { pd = pd; } /* stub */ Xint noacesmall(pd) diceset *pd; { pd = pd; } /* stub */ Xint expect(pd) diceset *pd; { pd = pd; } /* stub */ Xdouble ziprisk(n) { n = n; } /* stub */ Xchar *moniker() { return "Kamelion"; } /* stub */ Xboolean sameface(pd,c,s) diceset *pd;combination c;boolean s; X { pd = pd, c = c; return s; } /* stub */ X X/* X** Fake definitions for humans and cubex to allow easy tallying. X*/ Xstatic strategy st_human = { 0, "human" }; Xstatic temper te_human = { 0, "human" }; Xstatic computer human = { "**human**", &st_human, &te_human, Fickle }; Xstatic strategy st_cubex = { 0, "cubex" }; Xstatic temper te_cubex = { 0, "cubex" }; X Xstatic char *ptsmodename; Xstatic char **namelist; Xstatic boolean inlist(); Xstatic boolean careabout(); X Xstatic char *histfile = HISTFILE; /* the file to analyze */ Xstatic boolean dorank = True; /* show individual rankings */ Xstatic boolean nextup = False; /* rankings in next-up order */ Xstatic boolean tallystrat = False; /* produce strategy tally */ Xstatic boolean tallytemp = False; /* produce temperament tally */ Xstatic boolean humansonly = False; /* consider only humans */ Xstatic boolean ancient = False; /* consider ancient players */ Xstatic boolean showpers = False; /* show "personalities" */ Xstatic boolean showlast = False; /* show last game played */ Xstatic boolean nospaces = False; /* convert spaces in names */ Xstatic int onlylast = -1; /* only players of recent games */ Xstatic long myrank = -1; /* user's rank */ Xstatic int range = -1; /* a range around user's rank */ Xstatic char myid[IDLEN] = ""; /* user's id */ X Xmain(ac, av) Xchar *av[]; X{ X register int c, h; X register history *phist; X double winrt, bwinrt; X long avgmpt, bavgmpt; X long avtnpt, bavtnpt, bweight; X rating *temprat, *stratrat; X struct passwd *pw; X char name[NAMELEN]; X X opterr = 0; X while((c = getopt(ac, av, "rtRLCPSTahlsuf:g:n:p:")) >= 0) { X switch(c) { X case 'C': /* use combined (lifetime+recent) points in ranking */ X ptsmode = Combined; X break; X case 'L': /* use lifetime points in ranking */ X ptsmode = Lifetime; X break; X case 'R': /* use recent points in ranking */ X ptsmode = Recent; X break; X case 'P': /* toggle show personality */ X showpers = (showpers == True) ? False : True; X break; X case 'S': /* toggle strategy tally */ X tallystrat = (tallystrat == True) ? False : True; X break; X case 'T': /* toggle temperament tally */ X tallytemp = (tallytemp == True) ? False : True; X break; X case 'r': /* XXX: backwards compat */ X fprintf(stderr, "cuberank: warning: -r obsolete; use -[PST]\n"); X dorank = (dorank == True) ? False : True; X break; X case 't': /* XXX: backwards compat */ X fprintf(stderr, "cuberank: warning: -t obsolete; use -[PST]\n"); X tallystrat = (tallystrat == True) ? False : True; X tallytemp = (tallytemp == True) ? False : True; X break; X case 'a': /* toggle ancient mode */ X ancient = (ancient == True) ? False : True; X break; X case 'h': /* toggle humans only */ X humansonly = (humansonly == True) ? False : True; X break; X case 'l': /* toggle showlast mode */ X showlast = (showlast == True) ? False : True; X break; X case 's': /* toggle replacing spaces in names */ X nospaces = (nospaces == True) ? False : True; X break; X case 'u': /* toggle next-up mode */ X nextup = (nextup == True) ? False : True; X break; X case 'f': /* specify the history file to use */ X histfile = optarg; X break; X case 'g': /* consider players of onlylast most recent games */ X if((onlylast = atoi(optarg)) <= 0) { X fprintf(stderr, X "cuberank: recent game count must be positive\n"); X exit(1); X } X break; X case 'n': /* a range about a player (default this one) */ X if((range = atoi(optarg)) < 0) { X fprintf(stderr, X "cuberank: range around your rank must be non-negative\n"); X exit(1); X } X break; X case 'p': /* specify player id for use with the range option */ X strncpy(myid, optarg, IDLEN); X myid[IDLEN-1] = '\0'; X if(range == -1) X range = 1; /* maybe should be an error? */ X break; X default: X fprintf(stderr, X"usage -- cuberank [-{ahlsuRLCPST}] [-g games] [-n range [-p plr]] [plr ...]\n" X ); X exit(1); X } X } X X /* X ** Normally tallytemp and tallystrat want to turn off printing X ** of rankings, but we allow certain options to keep it on. X */ X if(showpers==False && nextup==False && humansonly==False && range==-1) X if(tallytemp == True || tallystrat == True) X dorank = False; X X /* X ** Remaining arguments are a list of names of players that we X ** care about. All others are ignored. X */ X namelist = &av[optind]; X X /* X ** Reconcile incompatible options. X */ X if(range != -1 && nextup == True) { X fprintf(stderr, "cuberank: range cannot be used with nextup\n"); X exit(1); X } X if(nextup == True && humansonly == True) { X fprintf(stderr, "cuberank: humans cannot be used with nextup\n"); X exit(1); X } X X /* X ** Suitable symbolic for ptsmode. X */ X switch(ptsmode) { X case Lifetime: ptsmodename = "(Life.)"; break; X case Recent: ptsmodename = "(Recent)"; break; X case Combined: ptsmodename = "(Comb.)"; break; X default: ptsmodename = "(Error)"; break; X } X X /* X ** Call openlog to provide for syslog calls in shared routines. X */ X#ifdef LOG_USER X openlog("cuberank", LOG_PID, LOG_USER); X setlogmask(LOG_UPTO(LOG_ERR)); X#else LOG_USER X openlog("cuberank", LOG_PID); X#endif LOG_USER X X /* X ** Read in the player histories. X */ X initcomptbl(); X if(histread(histfile) < 0) X exit(1); X setgamenum(); X if(ancient == False) X histprune(ANALCREDIT); X histsort(); X if(nextup == True) X qsort((char *)hist, (int)nhist, sizeof *hist, nextupcmp); X X /* X ** Massage things a bit for tallying/printing purposes. X XXX This must be done after nextup sort. X */ X for(phist = hist, h = 0;h < nhist;++h, ++phist) { X if(phist->h_computer == 0) X phist->h_computer = &human; X else if(phist->h_computer == &comptbl[0]) { X phist->h_computer->c_strategy = &st_cubex; X phist->h_computer->c_temper = &te_cubex; X } X } X X rwid = (nhist < 100) ? 2 : ((int)log10((double)nhist) + 1); X gwid = (gamenum < 100) ? 2 : ((int)log10((double)gamenum) + 1); X X /* X ** If necessary, get this user's id from the password file. X ** Find the ranking of this user. X */ X if(range != -1) { X if(myid[0] == '\0') { X if((pw = getpwuid(getuid())) == 0) { X syslog(LOG_ERR, "no password entry for uid %d", getuid()); X fprintf(stderr, "cuberank: you have no password file entry\n"); X exit(1); X } X strncpy(myid, pw->pw_name, IDLEN); X myid[IDLEN-1] = '\0'; X } X X myrank = -1; X for(phist = hist, h = 0;h < nhist;++h, ++phist) { X if(strncmp(myid, phist->h_id, IDLEN) == 0) { X myrank = phist->h_rank; X break; X } X } X if(myrank == -1) { X fprintf(stderr, "cuberank: you (%s) are not ranked\n", myid); X exit(1); X } X } X X /* X ** If temperament tally is requested, initialize the ranking tallies. X ** We need room for three extras: kamelion, human, and cubex. X */ X if(tallytemp == True) { X temprat = (rating *)calloc((unsigned)(tempers+3), sizeof *temprat); X if(temprat == 0) { X syslog(LOG_ERR, "no memory for temperaments"); X fprintf(stderr, X "cuberank: no memory for temperament rating table\n"); X exit(1); X } X for(phist = hist, h = 0;h < nhist;++h, ++phist) X if(careabout(phist) == True) X addtemp(h, temprat); X } X X /* X ** If strategy tally is requested, initialize the ranking tallies. X ** We need room for three extras: kamelion, human, and cubex. X */ X if(tallystrat == True) { X stratrat = (rating *)calloc((unsigned)(strategies+3), sizeof *stratrat); X if(stratrat == 0) { X syslog(LOG_ERR, "no memory for strategies"); X fprintf(stderr, "cuberank: no memory for strategy rating table\n"); X exit(1); X } X for(phist = hist, h = 0;h < nhist;++h, ++phist) X if(careabout(phist) == True) X addstrat(h, stratrat); X } X X if(dorank == True) { X /* X ** Find the players with the best win rate, best average game points, X ** best average turn points, and best weighted points. X */ X bwinrt = -1.0; X bavgmpt = bavtnpt = -1; X bweight = nextup == False ? hist->h_weight : -1; X for(phist = hist, h = 0;h < nhist;++h, ++phist) { X if(phist->h_games == 0) X continue; X histcalc(phist, &winrt, &avgmpt, &avtnpt); X X if(winrt > bwinrt) X bwinrt = winrt; X if(avgmpt > bavgmpt) X bavgmpt = avgmpt; X if(avtnpt > bavtnpt) X bavtnpt = avtnpt; X if(nextup == True && phist->h_weight > bweight) X bweight = phist->h_weight; X } X X /* X ** Print the players in ranking order. Ancient records are X ** ranked even if they're not displayed. X */ X if(nextup == True) X printf("%*.*s ", rwid, rwid, "Up"); X printf("%.*s Player %-*.*s %*s ", X rwid, "######", DISPLEN-7, DISPLEN-7, ptsmodename, gwid, "GP"); X if(showlast == True) X printf("%*s ", gwid, "LG"); X printf("%5s %5s %4s %5s ", "WinRt", "GamPt", "TnPt", "WgtPt"); X if(showpers == True) X printf(" %-2.2s %-3.3s %-3.3s", "Pref", "Tmp", "Str"); X printf("\n"); X for(phist = hist, h = 0;h < nhist;++h, ++phist) { X if(careabout(phist) == False) X continue; X histcalc(phist, &winrt, &avgmpt, &avtnpt); X X if(phist->h_computer == &human && index(phist->h_id, '@') == 0 X && (pw = getpwnam(phist->h_id)) != 0 && pw->pw_gecos[0] != '\0') X strcpy(name, fullname(pw)); X else X sprintf(name, "%.*s", DISPLEN, phist->h_id); X X /* X ** Convert spaces to underscores when required. X */ X if(nospaces == True) { X char *space; X X while((space = index(name, ' ')) != 0) X *space = '_'; X } X X if(nextup == True) X printf("%*d ", rwid, h + 1); X printf("%*ld %-*.*s %*ld ", rwid, phist->h_rank, X DISPLEN, DISPLEN, name, gwid, phist->h_games); X X if(showlast == True) X printf("%*ld ", gwid, phist->h_lastgame); X printf("%5.3f%c %5ld%c %4ld%c %5ld%c", X winrt, (winrt > bwinrt - 1e-5) ? '*' : ' ', /* episilon == 1e-5 */ X avgmpt, avgmpt == bavgmpt ? '*' : ' ', X avtnpt, avtnpt == bavtnpt ? '*' : ' ', X phist->h_weight, phist->h_weight == bweight ? '*' : ' ' X ); X if(showpers == True) { X char *pref; X switch(phist->h_computer->c_pref) { X case Nopref: pref = "-"; break; X case Standard: pref = "S"; break; X case Blitz: pref = "B"; break; X case Fickle: pref = "?"; break; X case Fstand: pref = "FS"; break; X case Fblitz: pref = "FB"; break; X default: pref = "??"; break; X } X printf(" %-2.2s %-3.3s %-3.3s", pref, X phist->h_computer->c_temper->t_name, X phist->h_computer->c_strategy->s_name X ); X } X printf("\n"); X } X } X X /* X ** If temperament tally was requested, print it. X */ X if(tallytemp == True) { X if(dorank == True) X putchar('\n'); X ratranksort(temprat); X ratprint(temprat, "Temper "); X } X X /* X ** If strategy tally was requested, print it. X */ X if(tallystrat == True) { X if(dorank == True || tallytemp == True) X putchar('\n'); X ratranksort(stratrat); X ratprint(stratrat, "Strat "); X } X X exit(0); X} X X/* X** words: break a string into words X** Fills passed array with pointers to words, returns number of words. X** Destroys the original string. X*/ Xwords(s, w, maxw) Xregister char *s, **w; Xint maxw; X{ X register int nw = 0; X boolean inword = False; X X for(;*s != '\0';++s) { X if(*s == ' ' || *s == '\t') { X *s = '\0'; X inword = False; X } else if(inword == False) { X if(--maxw < 0) X break; X w[nw++] = s; X inword = True; X } X } X X return nw; X} X X/* X** inlist: return true if name matches one in namelist X** Does word by word match. X*/ Xstatic boolean Xinlist(test) Xchar *test; X{ X#define MAXWORDS 16 X X register int tt, ll, ofs, len; X int ntw, nlw, i; X char tbuf[NAMELEN], lbuf[NAMELEN]; X char *tw[MAXWORDS], *lw[MAXWORDS]; X boolean match; X X if(namelist[0] == 0) /* first item is NULL */ X return True; /* no namelist */ X X /* X ** Break the test string into words. X */ X strncpy(tbuf, test, NAMELEN); X tbuf[NAMELEN-1] = '\0'; X ntw = words(tbuf, tw, MAXWORDS); X X /* X ** Search the namelist. If the namelist string has more X ** words than the test string, it can't be a match. X ** Otherwise, try various matching methods. X */ X for(i = 0;namelist[i] != 0;++i) { X strncpy(lbuf, namelist[i], NAMELEN); X lbuf[NAMELEN-1] = '\0'; X if((nlw = words(lbuf, lw, MAXWORDS)) == 0) X continue; X X /* X ** If there's more words in the namelist string than in X ** the test string, this cannot be a match. X */ X if(nlw > ntw) X continue; X X /* X ** Try to match each word in the namelist string with a word X ** in the test string. Treat the namelist words as initial X ** substrings. We can skip words in the test string, but we X ** must match every word in the namelist string. X */ X ofs = 0; X match = True; X for(ll = 0;ll < nlw;++ll) { X len = strlen(lw[ll]); X for(tt = ofs;tt < ntw;++tt) { X if(strncmp(tw[tt], lw[ll], len) == 0) { X ofs = tt + 1; X break; X } X } X if(tt == ntw) { X match = False; X break; X } X } X if(match == True) X return True; X } X X return False; X X#undef MAXWORDS X} X X/* X** careabout: do we care about this history entry? X*/ Xstatic boolean Xcareabout(phist) Xregister history *phist; X{ X struct passwd *pw; X X if(range != -1) { X if(phist->h_rank < myrank - range) X return False; X if(phist->h_rank > myrank + range) X return False; X } X X if(onlylast != -1) X if(phist->h_lastgame + onlylast < gamenum) X return False; X X if(humansonly == True && phist->h_computer != &human) X return False; X X if(nextup == True && phist->h_computer == &human) X return False; X X if(namelist[0] != 0) { X if(inlist(phist->h_id) == True) X return True; X if(phist->h_computer == &human && index(phist->h_id, '@') == 0) X if((pw = getpwnam(phist->h_id)) != 0 && pw->pw_gecos[0] != '\0') X if(inlist(fullname(pw)) == True) X return True; X return False; X } X X return True; X} X X/* X** addtemp: add temperament stats to rating table X*/ Xaddtemp(h, prat) Xint h; Xregister rating *prat; X{ X register history *phist = hist + h; X register history *phacc; X X#define T_NAME phist->h_computer->c_temper->t_name X X for(;;++prat) { /* assumes prat is large enough */ X if(prat->r_name == 0) X prat->r_name = T_NAME; X else if(strcmp(prat->r_name, T_NAME) != 0) /* no pointer compare! */ X continue; X phacc = &prat->r_hist; X phacc->h_points += phist->h_points; X phacc->h_avgturn += phist->h_avgturn; X phacc->h_wins += phist->h_wins; X phacc->h_games += phist->h_games; X phacc->h_mvpoints += phist->h_mvpoints; X phacc->h_mvavgturn += phist->h_mvavgturn; X phacc->h_mvwins += phist->h_mvwins; X phacc->h_mvgames += phist->h_mvgames; X phacc->h_lastgame += phist->h_lastgame; X phacc->h_weight += phist->h_weight; X phacc->h_rank += h + 1; X prat->r_count++; X break; X } X X#undef T_NAME X} X X/* X** addstrat: add strategy stats to rating table X*/ Xaddstrat(h, prat) Xint h; Xregister rating *prat; X{ X register history *phist = hist + h; X register history *phacc; X X#define S_NAME phist->h_computer->c_strategy->s_name X X for(;;++prat) { /* assumes prat is large enough */ X if(prat->r_name == 0) X prat->r_name = S_NAME; X else if(strcmp(prat->r_name, S_NAME) != 0) /* no pointer compare! */ X continue; X phacc = &prat->r_hist; X phacc->h_points += phist->h_points; X phacc->h_avgturn += phist->h_avgturn; X phacc->h_wins += phist->h_wins; X phacc->h_games += phist->h_games; X phacc->h_mvpoints += phist->h_mvpoints; X phacc->h_mvavgturn += phist->h_mvavgturn; X phacc->h_mvwins += phist->h_mvwins; X phacc->h_mvgames += phist->h_mvgames; X phacc->h_lastgame += phist->h_lastgame; X phacc->h_weight += phist->h_weight; X phacc->h_rank += h + 1; X prat->r_count++; X break; X } X X#undef S_NAME X} X X/* X** ratcomp: rating comparison function X*/ Xratcomp(r1, r2) Xrating *r1, *r2; X{ X int diff; X X if((diff = r1->r_hist.h_rank - r2->r_hist.h_rank) != 0) X return diff; X if((diff = r2->r_hist.h_weight - r1->r_hist.h_weight) != 0) X return diff; X return 0; X} X X/* X** ratranksort: reorder a rating table based on average ranking X*/ Xratranksort(rattbl) Xrating *rattbl; X{ X register rating *pr; X register int n; X X for(n = 0, pr = rattbl;pr->r_name != 0;++pr, ++n) { X if(pr->r_count != 0) { X pr->r_hist.h_rank /= pr->r_count; /* normalize rank */ X pr->r_hist.h_weight /= pr->r_count; /* normalize weight */ X pr->r_hist.h_lastgame /= pr->r_count; /* normalize lastgame */ X } X } X X qsort((char *)rattbl, n, sizeof *rattbl, ratcomp); X} X X/* X** ratprint: print a rating table with the given heading X*/ Xratprint(rattbl, heading) Xrating *rattbl; Xchar *heading; X{ X register rating *pr; X long avgmpt, avtnpt; X double winrt; X X printf("%.*s %-6.6s %-*.*s %1.1s %*s", rwid, "######", heading, X DISPLEN-9, DISPLEN-9, ptsmodename, "NP", gwid, "GP"); X if(showlast == True) X printf(" %*s", gwid, "LG"); X printf(" %5s %5s %4s %5s\n", X "WinRt", "GamPt", "TnPt", "WgtPt"); X for(pr = rattbl;pr->r_name != 0;++pr) { X if(pr->r_count == 0 || pr->r_hist.h_games == 0) X continue; X histcalc(&pr->r_hist, &winrt, &avgmpt, &avtnpt); X printf("%*ld %-*.*s %3d %*ld", X rwid, pr->r_hist.h_rank, /* normalized by ratranksort */ X DISPLEN-5, DISPLEN-5, pr->r_name, pr->r_count, X gwid, pr->r_hist.h_games X ); X if(showlast == True) X printf(" %*ld", gwid, X pr->r_hist.h_lastgame /* normalized by ratranksort */ X ); X printf(" %5.3f %5ld %4ld %5ld\n", X winrt, avgmpt, avtnpt, X pr->r_hist.h_weight /* normalized by ratranksort */ X ); X } X} END_OF_FILE if test 19000 -ne `wc -c <'histanal.c'`; then echo shar: \"'histanal.c'\" unpacked with wrong size! fi # end of 'histanal.c' fi if test -f 'scr.sh' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'scr.sh'\" else echo shar: Extracting \"'scr.sh'\" \(832 characters\) sed "s/^X//" >'scr.sh' <<'END_OF_FILE' X#! /bin/sh X# vi:set sw=4 ts=4: X# X# scr: Super CubeRank (cuberank combining your favorite options) X# X XPATH=/usr/games:/bin:/usr/bin Xumask 066 Xcmds=/tmp/scr.cmds$$ text=/tmp/scr.text$$ Xtrap "rm -f $cmds $text;exit 1" 1 2 3 15 Xrm -f $cmds $text X X# players in last game played Xcuberank -g1 $* >> $text X X# players ranked within 3 Xcuberank -n3 $* >> $text X X# top 4 humans Xcuberank -h $* | sed 5q >> $text X X# top 5 players, record-holding players, and favorites Xcuberank $* | sed -n -e ' X 1d X 2,6{;p;d;} X /\*/{;p;d;} X / Kamelion /{;p;d;} X' >> $text X X# next 5 up and marking commands Xcuberank -u $* | sed -n -e ' X 1d X s/^ *[0-9]* //p X s/^\( *[0-9]*\) .*/\/^\1 \/s\/\/\1*\//w '$cmds' X 6q X' >> $text X X# sort by rank uniquely and mark next up Xsort +0nu $text | sed -f $cmds X Xrm -f $cmds $text Xexit 0 END_OF_FILE if test 832 -ne `wc -c <'scr.sh'`; then echo shar: \"'scr.sh'\" unpacked with wrong size! fi chmod +x 'scr.sh' # end of 'scr.sh' fi echo shar: End of archive 5 \(of 8\). cp /dev/null ark5isdone MISSING="" for I in 1 2 3 4 5 6 7 8 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 8 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0