games@tekred.CNA.TEK.COM (04/28/89)
Submitted-by: gmp@rayssdb.RAY.COM (Gregory M. Paris)
Posting-number: Volume 6, Issue 65
Archive-name: cubes2/Part07
#! /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 7 (of 8)."
# Contents: cubes.c cubestat.c risk.c strategies.c
# Wrapped by billr@saab on Thu Apr 27 12:13:40 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'cubes.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'cubes.c'\"
else
echo shar: Extracting \"'cubes.c'\" \(16290 characters\)
sed "s/^X//" >'cubes.c' <<'END_OF_FILE'
X/* vi:set sw=4 ts=4: */
X#ifndef lint
Xstatic char sccsid[] = "@(#)cubes.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 <ctype.h>
X#include <signal.h>
X#include <setjmp.h>
X#include <errno.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#include <sys/wait.h>
X#include <sys/file.h>
X#include <sys/dir.h>
X#include <sys/socket.h>
X#include <sys/ioctl.h>
X#include <netdb.h>
X#include <netinet/in.h>
X#ifdef UNIXSOCK
X#include <sys/un.h>
X#endif UNIXSOCK
X#include <pwd.h>
X#include "cubes.h"
X
Xchar *cubename = 0; /* player name */
Xchar *cubehist = 0; /* personal ranking history */
Xchar *cubehost = 0; /* server hostname */
Xint ssock = -1; /* server socket descriptor */
XFILE *fsock = 0; /* server socket stream */
Xint child = 0; /* child pid */
Xboolean active = True; /* True while active */
Xboolean gotintr = False; /* True if interrupt received */
Xboolean quiet = False; /* True if in background */
Xenterlate watch = Watch; /* what to do about late entry */
Xenterlate spider = Play; /* what to do about waiting */
Xwinpref preference = Nopref; /* gametype/winscore preference */
Xextern graphtype graphics; /* graphics type */
X
Xextern int errno;
Xextern char *sys_errlist[];
X
Xextern int optind;
Xextern int opterr;
Xextern char *optarg;
Xextern char *getenv();
Xextern char *malloc();
Xextern char *fgets();
Xextern char *fullname();
Xstatic char *save();
Xstatic int slowstop();
X
Xmain(ac, av)
Xchar *av[];
X{
X int c;
X register int type;
X union wait chexit;
X char *s, msgbuf[BUFSIZ];
X
X#ifdef GAMESDSO
X /*
X ** Specific to author's locale: don't accidentally charge contracts!
X */
X if(geteuid() == 0)
X (void) setdso(GAMESDSO);
X#endif GAMESDSO
X
X /*
X ** We always turn off any setuid status. Except for the GAMESDSO
X ** stuff above, there's no need for the client to run setuid.
X */
X (void) setuid(getuid());
X
X /*
X ** Attempt to ensure the efficiency of output stream.
X XXX This may reduce system load on some systems.
X */
X {
X static char outbuf[BUFSIZ];
X setbuf(stdout, outbuf);
X }
X
X irandom(); /* initialize the random number generator */
X
X opterr = 0;
X while((c = getopt(ac, av, "BSF:spwg:f:h:n:")) >= 0) {
X switch(c) {
X case 'B': preference = Blitz; break;
X case 'S': preference = Standard; break;
X case 'F':
X switch(*optarg) {
X case 'B': preference = Fblitz; break;
X case 'S': preference = Fstand; break;
X default:
X fprintf(stderr,
X "cubes: forced preference must be Standard or Blitz\n");
X exit(1);
X }
X break;
X case 'g': /* set graphics type */
X switch(*optarg) {
X case 'n': case 'N': graphics = Nographics; break;
X case 'd': case 'D': graphics = Digital; break;
X case 'z': case 'Z': graphics = Zenith; break;
X case 'r': case 'R':
X fprintf(stderr, "cubes: warning: no ReGIS -- using DEC\n");
X graphics = Digital;
X break;
X default:
X fprintf(stderr,
X "cubes: graphics type must be None, DEC, or Zenith\n");
X exit(1);
X }
X break;
X case 'h': /* daemon on remote host */
X cubehost = save(optarg);
X break;
X case 'n': /* set player name */
X cubename = save(optarg);
X break;
X case 'f':
X cubehist = optarg;
X break;
X case 'a': watch = Ask; break;
X case 'p': watch = Play; break;
X case 'w': watch = Watch; break; /* default */
X case 's': spider = Wait; break;
X/* case 'i': spider = Play; break; /* default */
X default:
X fprintf(stderr,
X"usage -- cubes [-[F]{BS}] [-s] [-{apw}] [-g{drz}] [-f hist] [-h host] [name]\n"
X );
X exit(1);
X }
X }
X
X /*
X ** We support -n above, but now recognize non-option arguments
X ** as the requested player name. If both are specified, then
X ** it's probably a goof, so complain and die.
X */
X if(optind < ac) {
X if(cubename != 0) {
X fprintf(stderr, "usage -- cubes name -OR- cubes -n name\n");
X exit(1);
X }
X strcpy(msgbuf, av[optind]);
X while(++optind < ac) {
X strcat(msgbuf, " ");
X strcat(msgbuf, av[optind]);
X }
X cubename = save(msgbuf);
X }
X
X /*
X ** If the cubename wasn't specified on the command line,
X ** supply a prompt which shows how long the name can be.
X ** If the answer to this prompt is <CR>, then come up with
X ** a name by looking at the user's password entry gecos field.
X */
X if(cubename == 0) {
X if((cubename = getenv("CUBENAME")) == 0) {
X printf("......... 123456789012345678\n");
X printf("CUBENAME? ");
X (void) fflush(stdout);
X if(fgets(msgbuf, sizeof msgbuf, stdin) == 0)
X exit(0); /* must have changed mind */
X if((s = index(msgbuf, '\n')) == 0) {
X fprintf(stderr,
X "cubes: name must be eighteen characters or less\n");
X exit(1);
X }
X *s = '\0';
X if(msgbuf[0] != '\0')
X cubename = save(msgbuf);
X else {
X struct passwd *pw, *getpwuid();
X
X if((pw = getpwuid(getuid())) == 0) {
X fprintf(stderr, "cubes: no password entry!\n");
X exit(1);
X }
X cubename = save(fullname(pw));
X }
X }
X }
X
X /*
X ** Server host was not specified on the command line, so check
X ** the environment for a name. If none, we'll use this host.
X */
X if(cubehost == 0)
X cubehost = getenv("CUBEHOST");
X
X /*
X ** The personal history file was not set on the command line,
X ** so check the environment for a value. If one is not found,
X ** then we'll default to "$HOME/.cubehist". The only way for
X ** the user to turn off this feature is to specify a null name.
X */
X if(cubehist == 0 && (cubehist = getenv("CUBEHIST")) == 0) {
X if((cubehist = getenv("HOME")) == 0)
X cubehist = ".";
X sprintf(msgbuf, "%s/.cubehist", cubehist);
X cubehist = save(msgbuf);
X }
X
X /*
X ** We're done processing arguments, so it's safe to blow them
X ** away for the purpose of disguise.
X */
X newname(ac, av);
X
X /*
X ** If this process isn't the process group leader, then we should be OK.
X ** (I'm not sure that this will work correctly in all situations.)
X ** Otherwise, we must fork for purposes of useful suspend action. In
X ** that case, the parent just waits here for child to exit.
X */
X if(getpid() == getpgrp(0)) /* comment out this line if necessary */
X switch(child = fork()) {
X case 0: /* child */
X break;
X case -1: /* error */
X perror("fork");
X exit(1);
X default: /* parent */
X (void) signal(SIGINT, SIG_IGN);
X (void) signal(SIGQUIT, SIG_IGN);
X (void) signal(SIGTSTP, slowstop);
X (void) signal(SIGTTOU, SIG_DFL); /* sent by child */
X (void) signal(SIGTTIN, SIG_IGN); /* ignored by child */
X parentname(av); /* need a new disguise */
X while(wait(&chexit) != child)
X ;
X if(chexit.w_termsig != 0)
X exit((int)chexit.w_termsig | 0x80);
X exit((int)chexit.w_retcode);
X }
X
X /*
X ** Initialize terminal and set signal handlers.
X */
X startup();
X
X /*
X ** Connect to the server.
X */
X opensocktoserv();
X
X /*
X ** Process messages from the server.
X */
X initactions();
X while(active == True) {
X if((type = rdmessage(msgbuf, sizeof msgbuf)) < 0)
X cleanup(1, "error reading message from server");
X if(action(type, msgbuf) < 0)
X cleanup(1, "action failed");
X if(gotintr == True)
X reallyquit();
X if(quiet == False)
X flushinput();
X }
X
X /*
X ** Nothing currently gets us here.
X */
X cleanup(2, "left main loop!");
X}
X
X/*
X** opensocktoserv: open connection to server
X*/
Xopensocktoserv()
X{
X int off = 0;
X boolean local = False;
X
X#ifdef lint
X local = local;
X#endif lint
X
X /*
X ** If a null hostname or no hostname was supplied,
X ** then assume the local host.
X */
X if(cubehost == 0 || *cubehost == '\0') {
X char thishost[256];
X
X (void) gethostname(thishost, sizeof thishost);
X cubehost = save(thishost);
X local = True;
X }
X
X#ifdef UNIXSOCK
X /*
X ** If the host the localhost, then try connecting to the
X ** UNIX domain server socket. If this fails, go on to
X ** try the INET domain socket.
X */
X if(local == True) {
X struct sockaddr_un userver;
X int len;
X
X bzero((char *)&userver, sizeof userver);
X userver.sun_family = AF_UNIX;
X strcpy(userver.sun_path, UNIXSOCK);
X len = sizeof userver.sun_family + strlen(userver.sun_path);
X
X if((ssock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
X /* XXX: syslog message here */
X goto sorry;
X }
X if(connect(ssock, &userver, len) < 0) {
X /* XXX: syslog message here */
X (void) close(ssock);
X ssock = -1;
X goto sorry;
X }
Xsorry: ;
X }
X#endif UNIXSOCK
X
X /*
X ** Connect to the server's INET socket.
X */
X if(ssock < 0) {
X struct sockaddr_in server;
X struct hostent *ph;
X struct servent *ps;
X
X if((ph = gethostbyname(cubehost)) == 0)
X cleanup(1, "unknown host");
X ps = getservbyname("cube", "tcp"); /* let client use default port */
X
X bzero((char *)&server, sizeof server);
X server.sin_family = AF_INET;
X server.sin_port = (ps != 0) ? ps->s_port : (htons(0xf00));
X bcopy(ph->h_addr, (char *)&server.sin_addr, ph->h_length);
X
X if((ssock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
X cleanup(1, sys_errlist[errno]);
X if(connect(ssock, &server, sizeof server) < 0) {
X if(errno == ECONNREFUSED)
X cleanup(1, "no cubeserver running");
X cleanup(1, sys_errlist[errno]);
X }
X }
X
X /*
X ** Make sure non-blocking I/O is turned off.
X */
X (void) ioctl(ssock, FIONBIO, (char *)&off);
X
X /*
X ** Allow receiving SIGURG on OOB data. Ignore SIGALRM too.
X */
X (void) signal(SIGALRM, SIG_IGN);
X (void) signal(SIGURG, SIG_IGN);
X (void) fcntl(ssock, F_SETOWN, getpid());
X
X /*
X ** Open a stream so we can do fgets on the socket.
X */
X if((fsock = fdopen(ssock, "r")) == 0)
X cleanup(1, sys_errlist[errno]);
X}
X
X/*
X** rd_jmp, rd_timo: handle server socket read timeout
X*/
Xjmp_buf rd_jmp;
Xstatic int
Xrd_timo()
X{
X longjmp(rd_jmp, 1);
X}
X
X/*
X** rdmessage: read a message from the server socket
X** return the message type number
X*/
Xrdmessage(mesg, size)
Xchar *mesg;
Xint size;
X{
X register int ntot, n;
X int type;
X int (*oldalrm)();
X fd_set rdy;
X
X /*
X ** Server could time out on system crash or other unrecoverable problem.
X */
X oldalrm = signal(SIGALRM, SIG_IGN);
X if(setjmp(rd_jmp) != 0) {
X (void) alarm(0);
X (void) signal(SIGALRM, oldalrm);
X sprintf(mesg, "%d %*s", M_ARGE, size-5, "Server may be hung...");
X return (int)M_ARGE; /* non-fatal, let player quit with interrupt */
X }
X (void) signal(SIGALRM, rd_timo);
X (void) alarm(3*READTIMO); /* let's hope nobody takes this long! */
X
X /*
X ** Loop here waiting for server socket to come ready. If
X ** anything is typed on the terminal, respond by calling flushinput.
X ** If we get interrupt, service it immediately.
X */
X while(fsock->_cnt <= 0) { /* XXX: eeek! we peeked in stdio.h */
X FD_ZERO(&rdy);
X if(quiet == False)
X FD_SET(0, &rdy);
X FD_SET(ssock, &rdy);
X if(select(ssock+1, &rdy, NOSEL, NOSEL, HANG) > 0) {
X if(quiet == False && FD_ISSET(0, &rdy))
X flushinput();
X if(FD_ISSET(ssock, &rdy))
X break;
X }
X if(gotintr == True)
X reallyquit();
X }
X
X /*
X ** Read a single line from the socket.
X */
X if(fgets(mesg, size, fsock) == 0) {
X (void) alarm(0);
X (void) signal(SIGALRM, oldalrm);
X sprintf(mesg, "%d %*s", M_DOWN, size-5, "Server closed connection.");
X return (int)M_DOWN;
X }
X
X /*
X ** Done reading, turn off alarm.
X */
X (void) alarm(0);
X (void) signal(SIGALRM, oldalrm);
X
X /*
X ** Strip control and space characters from end of message.
X */
X ntot = strlen(mesg);
X for(n = ntot - 1;n >= 0;--n) {
X if(!iscntrl(mesg[n]) && !isspace(mesg[n]))
X break;
X mesg[n] = '\0';
X }
X
X /*
X ** Get the message type from the front of the message.
X */
X if(sscanf(mesg, "%3d", &type) != 1)
X type = (int)M_BADM;
X
X return type;
X}
X
X/*
X** save: save a string in new dynamic memory
X*/
Xstatic char *
Xsave(old)
Xchar *old;
X{
X unsigned len;
X char *new;
X
X len = strlen(old) + 1;
X if((new = malloc(len * sizeof *old)) == 0) {
X fprintf(stderr, "cubes: no memory for saving arguments\n");
X exit(1);
X }
X strcpy(new, old);
X
X return new;
X}
X
X/*
X** randname: return a pointer to a random name
X*/
Xstatic char *
Xrandname(len)
Xint len;
X{
X DIR *dirp;
X struct direct *dp;
X register int n;
X static char name[MAXNAMLEN+1];
X
X if(len > MAXNAMLEN)
X len = MAXNAMLEN;
X
X /*
X ** First look for a filename in /tmp.
X */
X name[0] = '\0';
X if(len > 3 && (dirp = opendir("/tmp")) != 0) {
X while((dp = readdir(dirp)) != 0) {
X if(dp->d_namlen < 3 || dp->d_namlen > len)
X continue;
X (void) strcpy(name, dp->d_name);
X if(randint(7) == 3)
X break;
X }
X (void) closedir(dirp);
X }
X if(name[0] != '\0')
X return name;
X
X /*
X ** Generate a name with a random function.
X */
X for(n = 0;n < len;++n) {
X switch(randint(13)) {
X case 2: case 7: name[n] = ' '; break;
X case 3: name[n] = '/'; break;
X case 8: name[n] = '.'; break;
X case 1: case 4: name[n] = "-aeiouyweeeaai"[randint(13)]; break;
X default: name[n] = 'a' + randint(26) - 1; break;
X }
X }
X name[n] = '\0';
X
X return name;
X}
X
X/*
X** newname: rename this program (apparently, anyway)
X** This code assumes we can write over av[n] and
X** hopes that av[0] through av[ac-1] are contiguous.
X*/
Xnewname(ac, av)
Xint ac;
Xregister char *av[];
X{
X register int n;
X register char *name, *fake;
X
X /*
X ** Make the argument list into one continuous string.
X ** Only does this if the arguments seem contiguous.
X */
X for(n = 1;n < ac;++n) {
X if(*(name = av[n] - 1) == '\0') /* end of previous arg */
X *name = ' '; /* un-terminate it */
X av[n] = 0; /* none such, now */
X }
X
X /*
X ** Come up with a suitable replacement argument list.
X */
X name = av[0];
X switch(n = strlen(name)) {
X case 1: break;
X case 2: switch(randint(7)) {
X case 1: strcpy(name, "vi"); break;
X case 2: strcpy(name, "rn"); break;
X case 3: strcpy(name, "ex"); break;
X case 4: strcpy(name, "bc"); break;
X default:strcpy(name, "sh"); break;
X } break;
X case 3: switch(randint(7)) {
X case 1: strcpy(name, "ftp"); break;
X case 2: strcpy(name, "adb"); break;
X case 3: strcpy(name, "sdb"); break;
X case 4: strcpy(name, "dbx"); break;
X default:strcpy(name, "csh"); break;
X } break;
X case 4: switch(randint(7)) {
X case 1: strcpy(name, "mail"); break;
X case 2: strcpy(name, "xget"); break;
X case 3: strcpy(name, "view"); break;
X case 4: strcpy(name, "edit"); break;
X default:strcpy(name, "lock"); break;
X } break;
X case 5: switch(randint(7)) {
X case 1: strcpy(name, "learn"); break;
X case 2: strcpy(name, "sh -i"); break;
X case 3: strcpy(name, "a.out"); break;
X case 4: strcpy(name, "qcalc"); break;
X default:strcpy(name, "vnews"); break;
X } break;
X case 6: switch(randint(7)) {
X case 1: strcpy(name, "iostat"); break;
X case 2: strcpy(name, "vmstat"); break;
X case 3: strcpy(name, "kermit"); break;
X case 4: strcpy(name, "script"); break;
X default:strcpy(name, "telnet"); break;
X } break;
X default:switch(randint(7)) {
X case 1: strncpy(name, "more ", 5); name += 5, n -= 5; break;
X case 2: strncpy(name, "less ", 5); name += 5, n -= 5; break;
X case 3: strncpy(name, "sdb ", 4); name += 4, n -= 4; break;
X case 4: strncpy(name, "vu ", 3); name += 3, n -= 3; break;
X default:strncpy(name, "vi ", 3); name += 3, n -= 3; break;
X }
X fake = randname(n);
X while(*name != '\0')
X *name++ = (*fake == '\0') ? ' ' : *fake++;
X break;
X }
X
X /*
X ** Change remaining arguments to all spaces.
X ** Only necessary if arguments aren't contiguous.
X */
X for(n = 1;n < ac;++n)
X if(av[n] != 0)
X for(name = av[n];*name != '\0';++name)
X *name = ' ';
X
X /*
X ** Some shells store our name in $_, so check for it
X ** and if found, replace it with our newly chosen name.
X */
X if((name = getenv("_")) != 0) {
X fake = av[0];
X while(*name != '\0')
X *name++ = (*fake == '\0') ? ' ' : *fake++;
X }
X}
X
X/*
X** parentname: change av[0] for the parent to look believable
X*/
Xparentname(av)
Xchar *av[];
X{
X int len = strlen(av[0]);
X
X sprintf(av[0], "%-*.*s", len, len, "-sh");
X}
X
X/*
X** slowstop: give child a chance to reset the terminal on suspend signal
X*/
Xstatic int
Xslowstop(sig)
X{
X#ifndef sigmask
X#define sigmask(sig) (1 << ((sig) - 1))
X#endif sigmask
X
X int mask;
X
X /*
X ** Delay should probably depend on ospeed, but it isn't set in parent.
X */
X sleep(1);
X
X /*
X ** Reset and unblock sig and "kill" ourselves with it.
X */
X (void) signal(sig, SIG_DFL);
X mask = sigblock(0);
X mask &= ~(sigmask(sig));
X (void) sigsetmask(mask);
X (void) kill(getpid(), sig);
X /*
X ** Suspended here.
X */
X (void) signal(sig, slowstop);
X}
END_OF_FILE
if test 16290 -ne `wc -c <'cubes.c'`; then
echo shar: \"'cubes.c'\" unpacked with wrong size!
fi
# end of 'cubes.c'
fi
if test -f 'cubestat.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'cubestat.c'\"
else
echo shar: Extracting \"'cubestat.c'\" \(4072 characters\)
sed "s/^X//" >'cubestat.c' <<'END_OF_FILE'
X/* vi:set sw=4 ts=4: */
X#ifndef lint
Xstatic char sccsid[] = "@(#)cubestat.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 <sys/types.h>
X#include <sys/time.h>
X#include <sys/socket.h>
X#include <netinet/in.h>
X#include <netdb.h>
X#include <sysexits.h>
X#include "cubes.h"
X
X#define EX_CUBES_INPROGRESS 0 /* success */
X#define EX_CUBES_IDLE 1 /* fail, sort of */
X
Xextern int optind;
Xextern int opterr;
Xextern char *optarg;
Xextern char *getenv();
Xstruct hostent *gethostbyname();
Xstruct servent *getservbyname();
X
Xmain(ac, av)
Xchar *av[];
X{
X int c;
X char *cubehost = 0; /* server hostname */
X register int qsock;
X struct sockaddr_in server;
X struct servent *ps;
X struct hostent *ph;
X struct timeval timo;
X fd_set rdy;
X char query;
X char status[STATLEN];
X boolean waitforbeg = False;
X boolean waitforend = False;
X
X opterr = 0;
X while((c = getopt(ac, av, "beh:")) >= 0) {
X switch(c) {
X case 'b':
X waitforbeg = (waitforbeg == True) ? False : True;
X break;
X case 'e':
X waitforend = (waitforend == True) ? False : True;
X break;
X case 'h':
X cubehost = optarg;
X break;
X default:
X fprintf(stderr, "usage -- cubestat [-{be}] [-h host]\n");
X exit(EX_USAGE);
X }
X }
X
X if(waitforbeg == True && waitforend == True) {
X fprintf(stderr, "cubestat: can't wait for both begin and end\n");
X exit(EX_USAGE);
X }
X
X if(waitforbeg == True || waitforend == True) {
X static char fake[] = "lock -";
X int n;
X
X for(c = 0;c < ac;++c)
X for(n = 0;av[c][n] != '\0';++n)
X av[c][n] = (c > 0 || n >= sizeof fake - 1) ? ' ' : fake[n];
X }
X
X /*
X ** If cubehost hasn't been set, check the CUBEHOST environment variable.
X ** If cubehost is null or not set, use this host.
X */
X if(cubehost == 0)
X cubehost = getenv("CUBEHOST");
X if(cubehost == 0 || *cubehost == '\0') {
X static char thishost[256];
X
X (void) gethostname(thishost, sizeof thishost);
X cubehost = thishost;
X }
X if((ph = gethostbyname(cubehost)) == 0) {
X fprintf(stderr, "cubestat: unknown host `%s'\n", cubehost);
X exit(EX_NOHOST);
X }
X
X /*
X ** Look up the cubestat service.
X */
X if((ps = getservbyname("cubestat", "udp")) == 0) {
X fprintf(stderr, "cubestat: no cubestat/udp service listed\n");
X exit(EX_UNAVAILABLE);
X }
X
X /*
X ** Get a datagram socket then point it at the server.
X */
X if((qsock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
X perror("cubestat: socket");
X exit(EX_OSERR);
X }
X bzero((char *)&server, sizeof server);
X server.sin_family = AF_INET;
X server.sin_port = ps->s_port;
X bcopy(ph->h_addr, (char *)&server.sin_addr, ph->h_length);
X if(connect(qsock, &server, sizeof server) < 0) {
X perror("cubestat: connect");
X exit(EX_OSERR);
X }
X
X /*
X ** For now, we only know how to ask for a roster.
X ** Set up for a few tries. We'll wait a reasonable time before retrying.
X */
X query = Q_ROSTER;
X timo.tv_sec = 2 * READTIMO;
X timo.tv_usec = 0;
X for(c = 0;c < 3 || waitforbeg == True || waitforend == True;++c) {
X if(send(qsock, &query, sizeof query, 0) < 0) {
X perror("cubestat: send");
X exit(EX_IOERR);
X }
X
X FD_ZERO(&rdy);
X FD_SET(qsock, &rdy);
X if(select(qsock+1, &rdy, NOSEL, NOSEL, &timo) > 0) {
X if(recv(qsock, status, STATLEN, 0) > 0) {
X switch(status[0]) {
X case S_IDLE:
X if(waitforbeg == False) {
X fputs(&status[1], stdout);
X exit(EX_CUBES_IDLE);
X }
X if(c % 20 == 0)
X fputs(&status[1], stdout);
X sleep(3);
X break;
X case S_ROSTER:
X fputs(&status[1], stdout);
X if(waitforend == False)
X exit(EX_CUBES_INPROGRESS);
X sleep(1);
X break;
X case S_UNRECOG:
X fprintf(stderr,
X "cubestat: unrecognized query \\%o\n", query);
X exit(EX_PROTOCOL);
X default:
X fprintf(stderr,
X "cubestat: unrecognized response \\%o\n", status[0]);
X exit(EX_PROTOCOL);
X }
X }
X }
X }
X
X fprintf(stderr, "cubestat: no response from server on %s\n", cubehost);
X exit(EX_UNAVAILABLE);
X}
END_OF_FILE
if test 4072 -ne `wc -c <'cubestat.c'`; then
echo shar: \"'cubestat.c'\" unpacked with wrong size!
fi
# end of 'cubestat.c'
fi
if test -f 'risk.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'risk.c'\"
else
echo shar: Extracting \"'risk.c'\" \(16470 characters\)
sed "s/^X//" >'risk.c' <<'END_OF_FILE'
X/* vi:set sw=4 ts=4: */
X#ifndef lint
Xstatic char sccsid[] = "@(#)risk.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 "cubes.h"
X
Xextern boolean jokermode;
Xextern int winscore;
Xextern player plr[];
Xextern boolean iscomp();
Xextern boolean isproxy();
Xextern boolean iskamelion();
Xextern long histavgplr();
X
X/*
X** risk: values derived empirically using the avg program
X**
X** Yes, you can calculate all this stuff, but what a pain.
X** In any case, this table is by no means a complete listing
X** of all possible conditions, probabilities, and expectations.
X** You could come up with a much more complicated table, but
X** to what end? Some of computer players using this table
X** and quite simple strategies are able to best humans regularly.
X**
X** Notes:
X** 1. p_aok values for rolling NDICE are nonsense.
X** 2. p_aok values cannot be used with all aces, fives, or jokers
X** since they already count as scoring dice.
X** 3. p_any and p_all for rolling 3, 2, and 1 can be averaged.
X** 4. Values for rolling 4 are special, since avg is able to
X** detect asmstr in that case. Use only mix values.
X*/
Xrisk joker_risktbl[NDICE+1] = {
X/* any all aok mix aces fives jokers */
X { 0.000000, 0.000000, 0.000000, 0, 0, 0, 0 }, /* roll 0 */
X { 0.384615, 0.384615, 0.153846, 23, 406, 215, 715 }, /* roll 1 */
X { 0.621656, 0.147983, 0.023669, 46, 105, 76, 99 }, /* roll 2 */
X { 0.781163, 0.071125, 0.003641, 79, 86, 83, 83 }, /* roll 3 */
X { 0.885755, 0.086400, 0.000560, 145, 145, 145, 145 }, /* roll 4 */
X { 0.948547, 0.096702, 0.000086, 269, 269, 269, 269 }, /* roll 5 */
X};
X
Xrisk std_risktbl[NDICE+1] = {
X/* any all aok mix aces fives jokers */
X { 0.000000, 0.000000, 0.000000, 0, 0, 0, 0 }, /* roll 0 */
X { 0.333333, 0.333333, 0.166667, 25, 442, 233, 0 }, /* roll 1 */
X { 0.555513, 0.110736, 0.027778, 50, 119, 84, 0 }, /* roll 2 */
X { 0.722309, 0.055524, 0.004630, 86, 95, 91, 0 }, /* roll 3 */
X { 0.842288, 0.095481, 0.000772, 161, 161, 161, 0 }, /* roll 4 */
X { 0.922813, 0.099667, 0.000129, 309, 309, 309, 0 }, /* roll 5 */
X};
X
X#define risktbl (jokermode == True ? joker_risktbl : std_risktbl)
X
X/*
X** cquery: computer rolling choice
X*/
Xcquery(comp, pd)
Xint comp;
Xdiceset *pd;
X{
X diceset modi; /* dice after re-rolling strategy */
X diceset temp; /* a temporary copy of the dice */
X int stay; /* points if we hold */
X int need; /* points needed beyond kept so far */
X int xpct; /* expectation value of next roll */
X boolean canthold; /* true if can't hold */
X
X /*
X ** Fake human-like hesitation for kamelion and proxies.
X ** Otherwise, just allow time for screen update.
X */
X if(iskamelion(comp) == True || isproxy(comp) == True)
X hesitate(1800L, 3600L);
X else
X hesitate(1500L, 2000L);
X
X /*
X ** Mark Rolled dice as Taken, updating the value of d_rolling.
X */
X keepall(pd);
X
X /*
X ** Determine any minimum number of points we need above those
X ** accumulated on previous rolls. For now, we just worry about
X XXX onboard and offboard conditions, though in the future we
X XXX could also worry about stopping winners and setting minimums.
X */
X if(plr[comp].p_onboard == False && pd->d_pts_turn < ONBOARD)
X need = ONBOARD - pd->d_pts_turn;
X else if(pd->d_pts_turn < OFFBOARD && plr[comp].p_score < winscore) {
X if(plr[comp].p_score + pd->d_pts_turn >= winscore)
X need = OFFBOARD - pd->d_pts_turn;
X } else
X need = 0;
X
X /*
X ** We get a copy of the dice and then use the computer's re-rolling
X ** strategy on it. We can only do this once, because some players
X ** are using a fickle strategy that's different every time. Set
X ** this copy of the dice up to roll again.
X */
X modi = *pd;
X (*plr[comp].p_computer->c_strategy->s_func)(&modi, need);
X modi.d_again = True;
X
X /*
X ** Evaluate the number of points we'd have if we held.
X ** Set this copy of the dice up to hold.
X */
X pd->d_again = False;
X temp = *pd;
X scoredice(&temp);
X stay = temp.d_pts_turn;
X
X /*
X ** Take care of conditions where we can't hold.
X */
X if(plr[comp].p_onboard == False && stay < ONBOARD)
X canthold = True;
X else if(stay < OFFBOARD && plr[comp].p_score < winscore) {
X if(plr[comp].p_score + stay >= winscore)
X canthold = True;
X } else
X canthold = False;
X
X /*
X ** If we can't hold, use the diceset with the best expectation value.
X ** Maybe this decision should be made by the temperament routine,
X ** but they aren't set up with that assumption. Still, this should
X ** be an improvement over the previous situation, where the modified
X ** diceset was always selected.
X */
X if(canthold == True) {
X temp = *pd;
X stay = expect(&temp); /* stay is expectation of original */
X temp = modi;
X xpct = expect(&temp); /* xpct is expectation of modified */
X if(xpct >= stay) /* prefer modified version */
X *pd = modi; /* use modified diceset */
X pd->d_again = True; /* in case it's the orignal */
X return;
X }
X
X /*
X ** Fake some indecision sometimes. More rarely, call snoop to
X ** pretend to be a human interested in rankings. The randint
X ** calls below work out to about one snoop per game for kamelion
X ** and proxies vs. about one snoop per two games for others
X ** (before checking value of stay, anyway).
X */
X if(iscomp(comp) == False && randint(12) > 9) {
X int thresh;
X switch(modi.d_rolling) {
X case 1: thresh = P_SMSTR; break;
X case 2: thresh = P_ASMSTR; break;
X case 3: thresh = P_3OKMULT * P_ACEMULT; break;
X case 4: thresh = P_4OKMULT * P_ACEMULT; break;
X default:thresh = P_AOKMULT * P_ACEMULT; break;
X }
X if(stay >= thresh) {
X hesitate(500L, 2000L);
X switch(randint(56)) {
X case 21: if(iskamelion(comp) == True) snoop(comp); break;
X case 14: if(isproxy(comp) == True) snoop(comp); break;
X case 7: snoop(comp); break;
X }
X }
X }
X
X /*
X ** Calculate the expectation value of rolling again.
X */
X temp = modi;
X xpct = expect(&temp);
X
X /*
X ** Player's temperament will chose which diceset to use.
X ** If c_temper->t_func returns True, it means roll again.
X */
X if((*plr[comp].p_computer->c_temper->t_func)(comp,stay,xpct,&modi) == True)
X *pd = modi; /* modi is the roll-again diceset */
X}
X
X/*
X** sameface: all dice showing same face test
X** if scoring is False, considers only held dice
X** returns True if considered dice are all comb's
X** returns False in the case of no comb's or mixed faces
X*/
Xboolean
Xsameface(pd, comb, scoring)
Xdiceset *pd;
Xcombination comb;
Xboolean scoring;
X{
X register int d;
X register boolean all;
X int face;
X
X switch(comb) {
X case Joker: if(jokermode == False) return False; face = JOKER; break;
X case Five: face = FIVE; break;
X case Ace: face = ACE; break;
X default: face = BADFACE; break; /* non-scoring face */
X }
X
X if(scoring == False) {
X all = False;
X for(d = 0;d < NDICE;++d) {
X if(pd->d_comb[d] == Previous) {
X if(face == BADFACE) {
X switch(pd->d_face[d]) {
X case ACE: case FIVE: case JOKER: return False;
X default: face = pd->d_face[d]; break;
X }
X } else if(pd->d_face[d] != face)
X return False;
X all = True;
X }
X }
X return all;
X }
X
X all = False;
X for(d = 0;d < NDICE;++d) {
X switch(pd->d_comb[d]) {
X case Previous:
X case Three_of_a_kind:
X case Four_of_a_kind:
X if(face == BADFACE) { /* comb == Nothing too */
X switch(pd->d_face[d]) {
X case ACE: case FIVE: case JOKER: return False;
X default: face = pd->d_face[d]; break;
X }
X } else if(pd->d_face[d] != face)
X return False;
X break;
X case All_of_a_kind:
X if(comb == Nothing) {
X switch(pd->d_face[d]) {
X case ACE: case FIVE: case JOKER: return False;
X default: return True;
X }
X }
X return (pd->d_face[d] == face) ? True : False;
X case Ace: if(comb != Ace) return False; break;
X case Five: if(comb != Five) return False; break;
X case Joker: if(comb != Joker) return False; break;
X case Nothing: continue;
X default: return False; /* scored but mixed faces */
X }
X all = True;
X }
X
X return all;
X}
X
X/*
X** expect: return expectation value of rolling dice as they stand
X*/
Xexpect(pd)
Xregister diceset *pd;
X{
X int d;
X boolean allaces, allfives, alljokers, allofakind;
X risk *pr;
X diceset temp;
X double xpct;
X double any, all;
X
X /*
X ** Get a copy of the dice and forget the location of the original.
X */
X temp = *pd;
X pd = &temp;
X
X /*
X ** Four special conditions: all Aces, all Fives, all Jokers,
X ** or all of a kind (not in Aces, Fives, or Jokers).
X */
X allaces = allfives = alljokers = allofakind = False;
X if(pd->d_rolling != 0 && pd->d_rolling != NDICE)
X if((allaces = sameface(pd, Ace, True)) == False)
X if((allfives = sameface(pd, Five, True)) == False)
X if(jokermode == False
X || (alljokers = sameface(pd, Joker, True)) == False)
X allofakind = sameface(pd, Nothing, True);
X
X /*
X ** Point to the appropriate risk values. First get the expected
X ** value of the next roll, accounting for whether we have all Aces,
X ** all Fives, all Jokers or a mix.
X */
X pr = &risktbl[pd->d_rolling == 0 ? NDICE : pd->d_rolling];
X any = pr->r_p_any, all = pr->r_p_all;
X if(allaces == True) xpct = pr->r_e_aces;
X else if(allfives == True) xpct = pr->r_e_fives;
X else if(alljokers == True) xpct = pr->r_e_jokers;
X else xpct = pr->r_e_mix;
X
X scoredice(pd); /* sets d_pts values */
X
X /*
X ** Add additional expectation due to getting All_of_a_kind
X ** in what normally would be considered non-scoring dice.
X */
X if(allofakind == True) {
X any += pr->r_p_aok, all += pr->r_p_aok;
X for(d = 0;d < NDICE;++d) {
X if(pd->d_comb[d] != Nothing) {
X xpct += pr->r_p_aok
X * (P_AOKMULT * pd->d_face[d] - pd->d_pts_dice);
X break;
X }
X }
X }
X
X#ifdef ASMSTR
X /*
X ** Account for Asm_straight. There are three basic ways to
X ** complete an assembled straight. First is if an ace or five
X ** is held. Second is if an ace and five are held. Last is
X ** if a small straight is held, of which there are three varieties.
X */
X switch(pd->d_rolling) {
X case ASMSTR-1:
X if(allaces == True) {
X /*
X ** To assemble here, what would be a Small_straight
X ** would need to be rolled. That probability is
X ** already accounted for, but the expectation value
X ** of this combination is higher.
X */
X xpct += (jokermode == True) ? 3 : 4;
X } else if(allfives == True) {
X /*
X ** To assemble here, a Small_straight or a 2346 is needed.
X ** The probability of the first is accounted for, the
X ** second is not. Additional expectation from both.
X */
X if(jokermode == True)
X any += 0.013445, all += 0.013445, xpct += 13;
X else
X any += 0.018519, all += 0.018519, xpct += 18;
X }
X break;
X case ASMSTR-2:
X if(allaces == False && allfives == False) {
X /*
X ** To assemble here, we need 234 which is unaccounted for
X ** and has additional expectation.
X */
X if(jokermode == True)
X any += 0.021848, all += 0.021848, xpct += 13;
X else
X any += 0.027778, all += 0.027778, xpct += 16;
X }
X break;
X case 1:
X switch(heldsmall(pd)) {
X case ACE: /* low die is an ACE */
X /*
X ** To assemble here, we need a FIVE. The probability
X ** is accounted for, but the expectation is not.
X */
X xpct += (jokermode == True) ? 46 : 50;
X break;
X case 2: /* low die is a deuce */
X /*
X ** To assemble here, we need an ACE or a 6. The
X ** first is accounted for, the second is not. Both
X ** carry additional expectation.
X */
X if(jokermode == True)
X any += 0.153846, all += 0.153846, xpct += 92;
X else
X any += 0.166667, all += 0.166667, xpct += 100;
X break;
X case 3: /* low die is a three */
X /*
X ** To assemble here, we need a 2. The probability
X ** and additional expectation are unaccounted for.
X */
X if(jokermode == True)
X any += 0.153846, all += 0.153846, xpct += 53;
X else
X any += 0.166667, all += 0.166667, xpct += 58;
X break;
X default: /* not holding small straight */
X break;
X }
X break;
X }
X#endif ASMSTR
X
X /*
X ** Add the value of keeping what we've got.
X */
X xpct += any * pd->d_pts_turn;
X
X /*
X ** Add the value of getting to roll all dice again.
X ** This is probably the most faulty estimate due to the way
X ** we make use of r_e_mix and p_nothing * xpct.
X */
X pr = &risktbl[NDICE];
X xpct = xpct + all * (pr->r_e_mix - (1.0 - pr->r_p_any) * xpct);
X
X return (int)xpct;
X}
X
X/*
X** ziprisk: return the probability of rolling nothing
X** assumes that rolling zero really means rolling all
X*/
Xdouble
Xziprisk(pd)
Xregister diceset *pd;
X{
X double any;
X
X /*
X ** Get the probability of anything assuming mixed dice held.
X */
X any = risktbl[pd->d_rolling == 0 ? NDICE : pd->d_rolling].r_p_any;
X
X /*
X ** Check for all held/scoring dice having same (usu. non-scoring) face.
X ** We count both Previous and scoring dice.
X */
X if(pd->d_rolling != 0 && pd->d_rolling != NDICE)
X if(sameface(pd, Nothing, True) == True)
X any += risktbl[pd->d_rolling].r_p_aok;
X
X return 1.0 - any;
X}
X
X/*
X** mayendearly: return True if the game may end prematurely
X** (no human player is onboard == True)
X*/
Xboolean
Xmayendearly()
X{
X register int c;
X
X for(c = 0;c < PLAYERS;++c)
X if(plr[c].p_stat == Active && plr[c].p_onboard == True)
X return False;
X
X return True;
X}
X
X/*
X** cfirst: hesitation before first roll
X*/
Xcfirst(comp)
Xint comp;
X{
X int high, half;
X int winneed, halfneed;
X
X#define QUIT (winscore / 4)
X
X /*
X ** Fake human-like hesitation for kamelion and proxies.
X ** Otherwise, just allow time for screen update.
X */
X if(iskamelion(comp) == True || isproxy(comp) == True)
X hesitate(1800L, 3600L);
X else
X hesitate(1500L, 2000L);
X
X /*
X ** If this player is not yet on board, and
X ** if this is not the COMP computer and also not a proxy player, and
X ** if this player is behind by more than QUIT points, and
X ** if the game is in little danger of ending prematurely, and
X ** if this player's score is less than half of its average score, and
X ** if this player needs more to reach half than the leader needs to win,
X ** then there is a probability of quitting that is initially 50%
X */
X if(plr[comp].p_onboard == False) {
X if(iscomp(comp) == False && isproxy(comp) == False) {
X if((high = highscore(comp, (int *)0)) - plr[comp].p_score > QUIT) {
X hesitate(250L, 2000L); /* fake consideration of quit */
X if(mayendearly() == False) {
X half = (winscore * histavgplr(comp)) / (2 * WINSCORE);
X if((halfneed = half - plr[comp].p_score) > 0) {
X if((winneed = winscore - high) < halfneed) {
X if(randint(2 * winneed) < halfneed) {
X oldplayer(comp);
X return -1;
X }
X }
X }
X }
X }
X }
X }
X
X /*
X ** If this is a proxy computer, sometimes do more hesitating.
X */
X if(isproxy(comp) == True)
X hesitate(100L, 1500L);
X
X return 0;
X
X#undef QUIT
X}
X
X/*
X** snoop: make it look like a computer player is interested in rankings
X** It might be better to individualize this by putting calls
X** to the snooping routines in the temperament routines. For now,
X** we'll just make some decisions based on computer temperament.
X*/
Xsnoop(comp)
X{
X int r;
X temper *ctemp;
X extern temper te_robot, te_antsy, te_tracker, te_goalie;
X extern int highscore(), closescore();
X
X ctemp = plr[comp].p_computer->c_temper;
X
X if(ctemp == &te_robot)
X return;
X
X if(ctemp == &te_antsy)
X r = peekhigh(comp, highscore);
X else if(ctemp == &te_tracker)
X r = peekhigh(comp, closescore);
X else if(ctemp == &te_goalie) {
X r = randint(3) == 1 ? myrank(comp, False) : peekhigh(comp, highscore);
X } else switch(randint(5)) {
X case 1: r = aboveme(comp, False); break;
X case 2: r = belowme(comp, False); break;
X case 3: r = peekhigh(comp, highscore); break;
X case 4: r = peekhuman(comp); break;
X default:r = myrank(comp, False); break;
X }
X
X /*
X ** Pretend to consider the ranking info.
X */
X if(r >= 0)
X hesitate(1000L, 3000L);
X}
X
X/*
X** peekhigh: look at the high scorer's history
X** If there's a tie, don't bother.
X*/
Xpeekhigh(comp, track)
Xint comp;
Xint (*track)();
X{
X int highc;
X
X (void) (*track)(comp, &highc);
X if(highc < 0)
X return -1;
X return plrrank(comp, highc, False);
X}
X
X/*
X** peekhuman: look at a random human's history
X*/
Xpeekhuman(comp)
X{
X register int c, pick;
X extern int active, waiting, watching;
X
X pick = randint(active + waiting + watching);
X
X for(c = 0;c < PLAYERS;++c) {
X switch(plr[c].p_stat) {
X case Active:
X case Waiting:
X case Watching:
X if(--pick == 0)
X return plrrank(comp, c, False);
X break;
X }
X }
X
X return -1; /* huh? */
X}
END_OF_FILE
if test 16470 -ne `wc -c <'risk.c'`; then
echo shar: \"'risk.c'\" unpacked with wrong size!
fi
# end of 'risk.c'
fi
if test -f 'strategies.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'strategies.c'\"
else
echo shar: Extracting \"'strategies.c'\" \(14014 characters\)
sed "s/^X//" >'strategies.c' <<'END_OF_FILE'
X/* vi:set sw=4 ts=4: */
X#ifndef lint
Xstatic char sccsid[] = "@(#)strategies.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 <syslog.h>
X#include "cubes.h"
X
Xextern boolean jokermode;
Xextern boolean sameface();
Xextern char *malloc();
X
X#define P_HAND (NDICE * P_ACE) /* a threshold */
X
X/*
X** basic: (formerly) the most basic strategy
X*/
Xstatic
Xbasic(pd, need)
Xdiceset *pd;
Xint need;
X{
X /*
X ** The basic strategy doesn't use need.
X */
X#ifdef lint
X need = need;
X#endif lint
X
X /*
X ** Respect a complete dice set.
X */
X if(pd->d_rolling == 0)
X return;
X
X /*
X ** If we're one away from All_of_a_kind, we're done.
X */
X if(pd->d_rolling == 1) {
X if(sameface(pd, Ace, True) == True)
X return;
X if(sameface(pd, Five, True) == True)
X return;
X if(jokermode == True && sameface(pd, Joker, True) == True)
X return;
X }
X
X /*
X ** Discard Jokers and Fives.
X */
X if(jokermode == True)
X nojokers(pd);
X nofives(pd);
X}
X
X/*
X** merrier: the more dice to throw, the merrier
X*/
Xstatic
Xmerrier(pd, need)
Xdiceset *pd;
Xint need;
X{
X diceset temp;
X
X /*
X ** The merrier strategy doesn't use need.
X */
X#ifdef lint
X need = need;
X#endif lint
X
X /*
X ** Respect a complete dice set.
X */
X if(pd->d_rolling == 0)
X return;
X
X /*
X ** Discard Jokers, Fives, and Aces if the result is three or more dice.
X */
X temp = *pd;
X if(jokermode == True)
X nojokers(&temp);
X nofives(&temp);
X noaces(&temp);
X if(temp.d_rolling >= THREE)
X *pd = temp;
X}
X
X/*
X** respect: Respect the power of the Ace.
X*/
Xstatic
Xrespect(pd, need)
Xdiceset *pd;
Xint need;
X{
X diceset temp;
X
X /*
X ** We've completed all dice. Try discarding Jokers and Fives.
X ** If the result is one die to roll and all Aces showing,
X ** then do it -- going for All_of_a_kind in Aces.
X */
X if(pd->d_rolling == 0) {
X if(need > 0 && need < P_ACEMULT * P_AOKMULT)
X return;
X if(pd->d_pts_turn >= P_HAND)
X return;
X temp = *pd;
X if(jokermode == True)
X nojokers(&temp);
X nofives(&temp);
X if(temp.d_rolling == 1 && sameface(&temp, Ace, True) == True)
X *pd = temp;
X return;
X }
X
X /*
X ** Don't discard Aces if we're one away from All_of_a_kind.
X */
X if(pd->d_rolling == 1)
X if(sameface(pd, Ace, True) == True)
X return;
X
X /*
X ** If we've got some points accumulated, we must be a bit desperate
X ** to want to roll again. Improve our chances of getting something
X ** by scrapping junky Small_straights, freeing an Ace.
X */
X if(pd->d_best == Small_straight)
X if(need > P_SMSTR || (need == 0 && pd->d_pts_turn > P_SMSTR))
X noacesmall(pd);
X
X /*
X ** Get rid of 3o'kind in deuces. Scrap Jokers and Fives.
X */
X if(pd->d_best == Three_of_a_kind)
X no3deuce(pd);
X if(jokermode == True)
X nojokers(pd);
X nofives(pd);
X
X /*
X ** Discard Aces if we end up with three or more dice.
X */
X temp = *pd;
X noaces(&temp);
X if(temp.d_rolling >= THREE)
X *pd = temp;
X}
X
X/*
X** twohater: doesn't like 3o'kind in twos
X*/
Xstatic
Xtwohater(pd, need)
Xdiceset *pd;
Xint need;
X{
X /*
X ** The twohater strategy doesn't use need.
X */
X#ifdef lint
X need = need;
X#endif lint
X
X /*
X ** Respect completed dice set.
X */
X if(pd->d_rolling == 0)
X return;
X
X /*
X ** Hates Jokers even more than 3o'kind in twos.
X */
X if(jokermode == True)
X nojokers(pd);
X
X /*
X ** Prefers Fives and Aces over 3o'kind in twos.
X */
X if(pd->d_best == Three_of_a_kind)
X no3deuce(pd);
X
X /*
X ** Discard Fives, but only discard Aces if
X ** we're already rolling more than one die.
X */
X nofives(pd);
X if(pd->d_rolling > 1)
X noaces(pd);
X}
X
X/*
X** greedy: Tries for All_of_a_kind in Aces, Fives, and Jokers.
X*/
Xstatic
Xgreedy(pd, need)
Xdiceset *pd;
Xint need;
X{
X diceset temp;
X int d, held, jokers;
X
X /*
X ** We've completed all dice. If a single discard can get us
X ** closer to All_of_a_kind in Aces, Fives or Jokers, do it.
X */
X if(pd->d_rolling == 0) {
X if(pd->d_pts_turn >= P_HAND)
X return;
X
X /*
X ** Discard Fives and Aces. Check for all Jokers.
X */
X if(jokermode == True) {
X if(need > 0 && need < P_JOKERMULT * P_AOKMULT)
X return;
X temp = *pd;
X nofives(&temp);
X noaces(&temp);
X if(temp.d_rolling == 1 && sameface(&temp, Joker, True) == True) {
X *pd = temp;
X return;
X }
X }
X
X /*
X ** Discard Jokers and Fives. Check for all Aces.
X */
X if(need > 0 && need < P_ACEMULT * P_AOKMULT)
X return;
X temp = *pd;
X if(jokermode == True)
X nojokers(&temp);
X nofives(&temp);
X if(temp.d_rolling == 1 && sameface(&temp, Ace, True) == True) {
X *pd = temp;
X return;
X }
X
X /*
X ** Discard Jokers and Aces. Check for all Fives.
X */
X if(need > 0 && need < FIVE * P_AOKMULT)
X return;
X temp = *pd;
X if(jokermode == True)
X nojokers(&temp);
X noaces(&temp);
X if(temp.d_rolling == 1 && sameface(&temp, Five, True) == True) {
X *pd = temp;
X return;
X }
X
X return;
X }
X
X /*
X ** On the first roll, if we have the choice between holding Jokers
X ** and Fives, choose the Jokers -- going for AOK in Jokers.
X */
X if(jokermode == True) {
X if(need == 0 || need >= P_JOKERMULT * P_AOKMULT) {
X if(pd->d_pts_turn == 0 && pd->d_best == Five) {
X held = jokers = 0;
X for(d = 0;d < NDICE;++d) {
X switch(pd->d_comb[d]) {
X case Previous: ++held; break;
X case Joker: ++jokers; break;
X }
X }
X if(held == 0 && jokers > 0)
X nofives(pd);
X return;
X }
X }
X }
X
X /*
X ** Small_straights make getting 5o'kind difficult, but if
X ** ASMSTR is defined, they can be converted to Straights,
X ** giving us another chance with all dice. The Small_straight
X ** containing an Ace is the most difficult to convert.
X ** If no ASMSTR, discard all Small_straights, otherwise
X ** discard only the tough kind.
X */
X if(pd->d_best == Small_straight) {
X#ifdef ASMSTR
X noacesmall(pd); /* this kind too hard to convert */
X#else ASMSTR
X nosmall(pd); /* don't like any Small_straights */
X#endif ASMSTR
X return;
X }
X
X /*
X ** Try to get all Aces showing. If we can, we're done.
X */
X temp = *pd;
X if(temp.d_best == Three_of_a_kind) {
X no3three(&temp);
X if(temp.d_best == Three_of_a_kind)
X no3deuce(&temp);
X }
X nofives(&temp);
X if(jokermode == True)
X nojokers(&temp);
X if(sameface(&temp, Ace, True) == True) {
X *pd = temp;
X return;
X }
X
X /*
X ** Try to get all Fives showing. If we can, we're done.
X */
X if(need == 0 || need >= FIVE * P_AOKMULT) {
X temp = *pd;
X if(temp.d_best == Three_of_a_kind) {
X no3three(&temp);
X if(temp.d_best == Three_of_a_kind)
X no3deuce(&temp);
X }
X noaces(&temp);
X if(jokermode == True)
X nojokers(&temp);
X if(sameface(&temp, Five, True) == True) {
X *pd = temp;
X return;
X }
X }
X
X /*
X ** Try to get all Jokers showing. If we can, we're done.
X */
X if(jokermode == True) {
X if(need == 0 || need >= P_JOKERMULT * P_AOKMULT) {
X temp = *pd;
X if(temp.d_best == Three_of_a_kind) {
X no3three(&temp);
X if(temp.d_best == Three_of_a_kind)
X no3deuce(&temp);
X }
X noaces(&temp);
X nofives(&temp);
X if(sameface(&temp, Joker, True) == True) {
X *pd = temp;
X return;
X }
X }
X }
X
X /*
X ** Discard Jokers and Fives.
X ** Discard Aces if the result is three or more dice.
X */
X if(jokermode == True)
X nojokers(pd);
X nofives(pd);
X temp = *pd;
X noaces(&temp);
X if(temp.d_rolling >= THREE)
X *pd = temp;
X}
X
X/*
X** picky: be picky about what we'll keep
X*/
Xstatic
Xpicky(pd, need)
Xdiceset *pd;
Xint need;
X{
X diceset temp;
X
X /*
X ** Respect a completed dice set.
X */
X if(pd->d_rolling == 0)
X return;
X
X /*
X ** Don't like 3o'kind in twos or threes.
X */
X if(pd->d_best == Three_of_a_kind) {
X if(need == 0 || need > THREE * P_3OKMULT) {
X no3three(pd);
X if(pd->d_best == Three_of_a_kind)
X if(need == 0 || need > DEUCE * P_3OKMULT)
X no3deuce(pd);
X }
X }
X
X /*
X ** We've got held Aces, discard Jokers and Fives.
X ** If not all Aces showing, discard Aces too.
X */
X if(need == 0 || need >= P_ACEMULT * P_AOKMULT) {
X if(sameface(pd, Ace, False) == True) {
X if(jokermode == True)
X nojokers(pd);
X nofives(pd);
X if(sameface(pd, Ace, True) == False)
X noaces(pd);
X return;
X }
X }
X
X /*
X ** We've got held Fives, discard Jokers and Aces.
X ** If not all Fives showing, discard Fives too.
X */
X if(need == 0 || need >= FIVE * P_AOKMULT) {
X if(sameface(pd, Five, False) == True) {
X if(jokermode == True)
X nojokers(pd);
X noaces(pd);
X if(sameface(pd, Five, True) == False)
X nofives(pd);
X return;
X }
X }
X
X /*
X ** We've got held Jokers, discard Fives and Aces.
X ** If not all Jokers showing, discard Jokers too.
X */
X if(jokermode == True) {
X if(need == 0 || need >= P_JOKERMULT * P_AOKMULT) {
X if(sameface(pd, Joker, False) == True) {
X nofives(pd);
X noaces(pd);
X if(sameface(pd, Joker, True) == False)
X nojokers(pd);
X return;
X }
X }
X }
X
X /*
X ** We're holding a mix or nothing. Discard Jokers.
X ** Discard Fives and Aces if the result is three or more dice.
X */
X if(jokermode == True)
X nojokers(pd);
X temp = *pd;
X nofives(&temp);
X noaces(&temp);
X if(temp.d_rolling >= THREE)
X *pd = temp;
X}
X
X/*
X** finicky: a pickier picky
X*/
Xstatic
Xfinicky(pd, need)
Xdiceset *pd;
Xint need;
X{
X diceset temp;
X
X /*
X ** Respect a completed set of dice.
X */
X if(pd->d_rolling == 0)
X return;
X
X /*
X ** Discard three twos and Small_straights containing an Ace.
X */
X if(pd->d_best == Three_of_a_kind)
X if(need == 0 || need > DEUCE * P_3OKMULT)
X no3deuce(pd);
X if(pd->d_best == Small_straight)
X if(need == 0 || need > P_SMSTR)
X noacesmall(pd);
X
X /*
X ** We've held all Aces. Discard Jokers and Fives.
X ** If rolling more than one die, discard Aces too.
X */
X if(sameface(pd, Ace, False) == True) {
X if(jokermode == True)
X nojokers(pd);
X nofives(pd);
X if(pd->d_rolling > 1)
X noaces(pd);
X return;
X }
X
X /*
X ** We've held all Fives. Discard Jokers and Aces.
X ** If rolling more than one die, discard Fives too.
X */
X if(need == 0 || need >= FIVE * P_AOKMULT) {
X if(sameface(pd, Five, False) == True) {
X if(jokermode == True)
X nojokers(pd);
X noaces(pd);
X if(pd->d_rolling > 1)
X nofives(pd);
X return;
X }
X }
X
X /*
X ** We've held all Jokers. Discard Fives and Aces.
X ** If we're rolling more than one die, discard Jokers too.
X ** Else, if it will give us three dice, discard Jokers.
X */
X if(jokermode == True) {
X if(need == 0 || need >= P_JOKERMULT * P_AOKMULT) {
X if(sameface(pd, Joker, False) == True) {
X nofives(pd);
X noaces(pd);
X if(pd->d_rolling > 1)
X nojokers(pd);
X else {
X temp = *pd;
X nojokers(&temp);
X if(temp.d_rolling >= THREE)
X *pd = temp;
X }
X return;
X }
X }
X }
X
X /*
X ** Holding a mix or nothing. Discard Jokers and Fives.
X ** If we're rolling more than one, discard Aces too.
X ** Else, if we can get three dice, discard Aces.
X ** Else, discard 3o'kind in threes.
X */
X if(jokermode == True)
X nojokers(pd);
X nofives(pd);
X if(pd->d_rolling > 1)
X noaces(pd);
X else {
X temp = *pd;
X noaces(&temp);
X if(temp.d_rolling >= THREE)
X *pd = temp;
X else if(pd->d_best == Three_of_a_kind)
X no3three(pd);
X }
X}
X
X/*
X** holdall: hold all scoring dice
XXXX: Intended for choosy selection only -- not a valid strategy.
X*/
Xstatic
Xholdall(pd, need)
Xdiceset *pd;
Xint need;
X{
X#ifdef lint
X pd = pd;
X need = need;
X#endif lint
X}
X
X/*
X** tossall: always throw maximum number of dice
XXXX: Intended for choosy selection only -- not a valid strategy.
X*/
Xstatic
Xtossall(pd, need)
Xdiceset *pd;
Xint need;
X{
X#ifdef lint
X need = need;
X#endif lint
X
X /*
X ** Discard Small_straights, all Three_of_a_kinds, Jokers, Fives, and Aces.
X ** We never honor a complete dice set!
X */
X if(pd->d_best == Small_straight)
X nosmall(pd);
X if(pd->d_best == Three_of_a_kind)
X no3any(pd);
X if(jokermode == True)
X nojokers(pd);
X nofives(pd);
X noaces(pd);
X}
X
X/*
X** choosy: try all known strategies then pick the one
X** with the best expectation for this diceset
X*/
Xstatic
Xchoosy(pd, need)
Xdiceset *pd;
Xint need;
X{
X register int n, best, maxx;
X diceset temp;
X static int *xtbl = 0;
X extern int strategies;
X extern strategy *strattbl[];
X
X if(xtbl == 0) {
X xtbl = (int *)malloc((unsigned)(strategies * sizeof *xtbl));
X if(xtbl == 0) {
X syslog(LOG_WARNING, "choosy: no memory for xtbl");
X fickle(pd, need); /* emergency backup! */
X return;
X }
X }
X
X for(n = 0;n < strategies - 1;++n) { /* fickle is last */
X if(strattbl[n]->s_func == choosy) {
X *(xtbl+n) = -WINSCORE;
X continue;
X }
X temp = *pd;
X (*strattbl[n]->s_func)(&temp, need);
X *(xtbl+n) = expect(&temp);
X }
X
X maxx = *(xtbl+(best=0));
X for(n = 1;n < strategies - 1;++n) /* fickle is last */
X if(*(xtbl+n) > maxx)
X maxx = *(xtbl+(best=n));
X
X (*strattbl[best]->s_func)(pd, need);
X}
X
X/*
X** kamelion: should never be called
X*/
Xstatic
Xkamelion(pd, need)
Xdiceset *pd;
Xint need;
X{
X syslog(LOG_WARNING, "kamelion strategy called!");
X choosy(pd, need);
X}
X
X/*
X** fickle: pick another strategy randomly!
X** intended for proxy players only
X*/
Xstatic
Xfickle(pd, need)
Xdiceset *pd;
Xint need;
X{
X int n;
X extern int strategies;
X extern strategy *strattbl[];
X
X hesitate(100L, 1000L);
X n = randint(strategies - 1) - 1; /* this one is last -- don't pick it */
X (*strattbl[n]->s_func)(pd, need);
X}
X
X/*
X** strattbl: table listing available strategies
X*/
X
Xstrategy st_basic = { basic, "basic" };
Xstrategy st_merrier = { merrier, "merrier" };
Xstrategy st_respect = { respect, "respect" };
Xstrategy st_twohater = { twohater, "twohater" };
Xstrategy st_greedy = { greedy, "greedy" };
Xstrategy st_picky = { picky, "picky" };
Xstrategy st_finicky = { finicky, "finicky" };
Xstatic strategy st_holdall = { holdall, "holdall" }; /* table only */
Xstatic strategy st_tossall = { tossall, "tossall" }; /* table only */
Xstrategy st_choosy = { choosy, "choosy" };
Xstrategy st_fickle = { fickle, "fickle" };
Xstrategy st_kamelion = { kamelion, "kamelion" }; /* semi-dummy */
X
Xstrategy *strattbl[] = {
X &st_basic, &st_merrier, &st_respect, &st_twohater,
X &st_greedy, &st_picky, &st_finicky,
X &st_holdall, &st_tossall, &st_choosy,
X &st_fickle /* st_fickle must be last */
X};
X
Xint strategies = sizeof strattbl / sizeof strattbl[0];
END_OF_FILE
if test 14014 -ne `wc -c <'strategies.c'`; then
echo shar: \"'strategies.c'\" unpacked with wrong size!
fi
# end of 'strategies.c'
fi
echo shar: End of archive 7 \(of 8\).
cp /dev/null ark7isdone
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