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