gmp@rayssd.RAY.COM (06/17/88)
Submitted by: gmp@rayssd.RAY.COM (Gregory M. Paris)
Comp.sources.games: Volume 4, Issue 34
Archive-name: cubes/Part03
#! /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 3 (of 3)."
# Contents: Makefile cuberank.tmplt cubes.c cubes.h history.c random.c
# risk.c strategies.c tactics.c tempers.c
# Wrapped by billr@saab on Tue Jun 7 16:36:07 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f Makefile -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"Makefile\"
else
echo shar: Extracting \"Makefile\" \(1817 characters\)
sed "s/^X//" >Makefile <<'END_OF_Makefile'
X#! /bin/make -f
X# vi:set sw=4 ts=4:
X
XOWNER= gdaemon
XGAMEDIR= /usr/games
XGLIBDIR= $(GAMEDIR)/lib
XPATHNAME= $(GLIBDIR)/cubeserver
XLOGFILE= $(GLIBDIR)/cubes.log
XHISTFILE= $(GLIBDIR)/cubes.hist
XMANPAGE= /usr/man/man6/cubes.6
XCFLAGS= -OG
XLDFLAGS=
XSRVFLAGS= $(CFLAGS) -DPATHNAME=\"$(PATHNAME)\" -DLOGFILE=\"$(LOGFILE)\"
XHISTFLAGS= $(CFLAGS) -DHISTFILE=\"$(HISTFILE)\"
XLIBS= -lcurses -ltermcap
X
XSRVOBJS= cubeserver.o history.o turn.o risk.o \
X tempers.o strategies.o tactics.o dieopts.o random.o
XCLIOBJS= cubes.o actions.o screen.o random.o
XAVGOBJS= avg.o dieopts.o random.o
X
Xall: cubeserver cubes cuberank avg
X
Xinstall: all files
X install -c -m 4700 -o $(OWNER) cubeserver $(GLIBDIR)
X install -c -m 711 -o $(OWNER) cubes $(GAMEDIR)
X install -c -m 755 -o $(OWNER) cuberank $(GAMEDIR)
X
Xcubeserver: $(SRVOBJS)
X cc $(LDFLAGS) -o cubeserver $(SRVOBJS) $(LIBS)
X
Xcubes: $(CLIOBJS)
X cc $(LDFLAGS) -o cubes $(CLIOBJS) $(LIBS)
X
Xavg: $(AVGOBJS)
X cc $(LDFLAGS) -o avg $(AVGOBJS) $(LIBS)
X
Xcuberank: Makefile cuberank.tmplt
X sed -e "s;%HISTFILE;$(HISTFILE);g" cuberank.tmplt > cuberank
X chmod 700 cuberank
X
Xcubeserver.o: cubeserver.c cubes.h Makefile
X cc $(SRVFLAGS) -c cubeserver.c
X
Xhistory.o: history.c cubes.h Makefile
X cc $(HISTFLAGS) -c history.c
X
X$(SRVOBJS): cubes.h
X$(CLIOBJS): cubes.h
X$(AVGOBJS): cubes.h
X
Xfiles: $(LOGFILE) $(HISTFILE) $(MANPAGE)
X
X$(LOGFILE): /dev/null
X cp /dev/null $(LOGFILE)
X chown $(OWNER) $(LOGFILE)
X chmod 644 $(LOGFILE)
X
X$(HISTFILE): /dev/null
X -if test ! -f $(HISTFILE); \
X then cp /dev/null $(HISTFILE); \
X chown $(OWNER) $(HISTFILE); \
X chmod 644 $(HISTFILE); \
X fi
X
X$(MANPAGE): cubes.6
X cp cubes.6 $(MANPAGE)
X chmod 644 $(MANPAGE)
X
Xclean:
X rm -f *.o cubes cubeserver cuberank avg
X
Xdistribution:
X makekit -p -s60k -ncubes README cubes.6 Makefile cubes.h *.c cuberank.tmplt
END_OF_Makefile
if test 1817 -ne `wc -c <Makefile`; then
echo shar: \"Makefile\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f cuberank.tmplt -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"cuberank.tmplt\"
else
echo shar: Extracting \"cuberank.tmplt\" \(868 characters\)
sed "s/^X//" >cuberank.tmplt <<'END_OF_cuberank.tmplt'
X#! /bin/sh
X#
X# cuberank: display cubes player rankings
X#
X# Note that this script uses the same weighting scheme as found in history.c.
X# If that comparison function is changed, this should be changed too.
X#
Xawk '
X{
X id="";
X for(n=5;n <= NF;++n)
X id = id $n " ";
X len = length(id);
X if(len > maxlen)
X maxlen = len;
X g[id] = $1;
X w[id] = $2 / $1;
X p[id] = $3;
X t[id] = $4 / $1
X a[id] = p[id] / g[id];
X W[id] = (1000 * $2 + $3 + $4) / $1;
X ++players;
X}
XEND {
X fmt = "%2s %-" maxlen "s %4s %6s %6s %6s %6s\n";
X printf(fmt, "##", "Player", "GP", "WinRt", "AvGmPt", "AvTnPt", "WtPts");
X fmt = "%2d %-" maxlen "s %4d %6.3f %6d %6d %6d\n";
X
X rnk = 1;
X for(n = 0;n < players;++n) {
X bW = -1;
X for(id in W) {
X if(W[id] > bW) {
X bid = id;
X bW = W[id];
X }
X }
X printf(fmt, rnk, bid, g[bid], w[bid], a[bid], t[bid], W[bid]);
X W[bid] = -1;
X ++rnk;
X }
X}
X' %HISTFILE
END_OF_cuberank.tmplt
if test 868 -ne `wc -c <cuberank.tmplt`; then
echo shar: \"cuberank.tmplt\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f cubes.c -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"cubes.c\"
else
echo shar: Extracting \"cubes.c\" \(5075 characters\)
sed "s/^X//" >cubes.c <<'END_OF_cubes.c'
X/* vi:set sw=4 ts=4: */
X#ifndef lint
Xstatic char sccsid[] = "@(#)cubes.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif lint
X
X#include <stdio.h>
X#include <strings.h>
X#include <ctype.h>
X#include <signal.h>
X#include <errno.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#include <sys/file.h>
X#include <sys/socket.h>
X#include <sys/ioctl.h>
X#include <netdb.h>
X#include <netinet/in.h>
X#include "cubes.h"
X
Xchar *pname; /* player name */
Xchar *shost = 0; /* server hostname */
Xint ssock = -1; /* server socket */
Xboolean active = True; /* True while active */
Xboolean gotintr = False; /* True if interrupt received */
Xextern graphtype graphics; /* graphics type */
X
Xextern int errno;
Xextern int optind;
Xextern int opterr;
Xextern char *optarg;
Xextern char *getenv();
X
Xmain(ac, av)
Xchar *av[];
X{
X int c;
X register int type;
X char msgbuf[BUFSIZ];
X
X newname(av[0]);
X
X opterr = 0;
X while((c = getopt(ac, av, "g:h:n:")) >= 0) {
X switch(c) {
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 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 shost = optarg;
X break;
X case 'n': /* set player name */
X pname = optarg;
X break;
X default:
X fprintf(stderr, "usage -- cubes [-g type] [-h host] [-n name]\n");
X exit(1);
X }
X }
X
X if(pname == 0)
X getplrname();
X if(opensocktoserv() < 0)
X exit(1);
X
X startup();
X
X while(active == True) {
X if((type = rdmessage(msgbuf, sizeof msgbuf)) < 0) {
X fprintf(stderr, "cubes: error reading message from server\n");
X active = False;
X break;
X }
X if(action(type, msgbuf) < 0) {
X fprintf(stderr, "cubes: action type %d failed\n", type);
X active = False;
X break;
X }
X if(gotintr == True)
X reallyquit();
X }
X
X cleanup(1);
X}
X
X/*
X** getplrname: get player name
X*/
Xgetplrname()
X{
X if((pname = getenv("CUBENAME")) == 0) {
X if((pname = getenv("USER")) == 0) {
X static char namebuf[32];
X sprintf(namebuf, "Cuber#%d", getuid());
X pname = namebuf;
X }
X }
X}
X
X/*
X** opensocktoserv: open connection to server
X*/
Xopensocktoserv()
X{
X char thishost[256];
X struct sockaddr_in server;
X struct hostent *ph;
X struct servent *ps;
X int off = 0;
X
X if(shost == 0) {
X (void) gethostname(thishost, sizeof thishost);
X shost = thishost;
X }
X
X if((ph = gethostbyname(shost)) == 0) {
X fprintf(stderr, "cubes: can't find `%s' in host table\n", shost);
X return (ssock = -1);
X }
X if((ps = getservbyname("cube", "tcp")) == 0) {
X fprintf(stderr, "cubes: no cube service listed on this system\n");
X return (ssock = -1);
X }
X
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
X if((ssock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
X perror("cubes: socket");
X return (ssock = -1);
X }
X if(connect(ssock, &server, sizeof server) < 0) {
X if(errno == ECONNREFUSED)
X fprintf(stderr, "cubes: no cube server running on %s\n", shost);
X else
X perror("cubes: connect");
X (void) close(ssock);
X return (ssock = -1);
X }
X (void) ioctl(ssock, FIONBIO, (char *)&off);
X
X /*
X ** Allow receiving SIGURG on OOB data.
X */
X (void) signal(SIGURG, SIG_IGN);
X (void) fcntl(ssock, F_SETOWN, getpid());
X
X return 0;
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
X /*
X ** Read one character at a time from the socket
X ** until we get a newline.
X */
X for(ntot = 0;;) {
X switch(read(ssock, mesg+ntot, 1)) {
X case -1:
X perror("cubes: read");
X return -1;
X case 0:
X sprintf(mesg, "%d %*s",
X M_DOWN, size-5, "Server closed connection.");
X return M_DOWN;
X }
X if(mesg[ntot] == '\n') {
X mesg[ntot] = '\0'; /* zaps newline */
X break;
X }
X if(++ntot >= size)
X --ntot;
X }
X
X /*
X ** Strip control and space characters from end of message.
X */
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 = M_BADM;
X
X return type;
X}
X
X/*
X** newname: rename this program (apparently, anyway)
X*/
Xnewname(name)
Xregister char *name;
X{
X register int len;
X
X if((len = strlen(name)) < 4)
X strncpy(name, "vu ", len);
X else if(len == 4)
X strcpy(name, "more");
X else if(len == 5)
X strcpy(name, "qcalc");
X else {
X irandom();
X switch(dieroll(5)) {
X case 1: case 3: strncpy(name, "less ", 5); name += 5; break;
X default: strncpy(name, "vi ", 3); name += 3; break;
X }
X while(*name != '\0') {
X switch(dieroll(13)) {
X case 2: case 7: *name++ = ' '; break;
X case 3: *name++ = '/'; break;
X case 8: *name++ = '.'; break;
X case 1: case 4: *name++ = "-aeiouyweeeaai"[dieroll(13)]; break;
X default: *name++ = 'a' + dieroll(26) - 1; break;
X }
X }
X }
X}
END_OF_cubes.c
if test 5075 -ne `wc -c <cubes.c`; then
echo shar: \"cubes.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f cubes.h -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"cubes.h\"
else
echo shar: Extracting \"cubes.h\" \(6239 characters\)
sed "s/^X//" >cubes.h <<'END_OF_cubes.h'
X/* vi:set sw=4 ts=4: */
X/*
X** sccsid: @(#)cubes.h 1.1 6/2/88 Copyright 1988 Gregory M. Paris
X*/
X
X/*
X** If you don't want Four_of_a_kind or Small_straight, then comment these out.
X** Unfortunately, you'll have to recalculate the values in risktbl[] if you do.
X*/
X#define FOUR 4 /* how many in a four of a kind (if defined) */
X#define SMSTR 4 /* number of dice in small straight (if def) */
X
X/*
X** Although you could change any of the following parameters, the nature of
X** the game would be altered. Also, some things like 5o'kind are hard coded
X** in places and would not change automatically. Best to leave be.
X*/
X#define SIDES 6 /* six sided dice */
X#define NDICE 5 /* number of dice in set */
X#define ONBOARD 500 /* turn score needed to get on scoreboard */
X#define OFFBOARD 500 /* turn score needed to cross WINSCORE thresh */
X#define WINSCORE 10000 /* minimum score needed to win */
X#define WINMARGIN 250 /* winner must best others by this margin */
X
X/*
X** These values are the basis for scoring. Again, if you change them, the
X** play of the game will be altered. Leave them as they are.
X*/
X#define P_ACEMULT 10 /* ace is worth disproportionate amount */
X#define P_AOKMULT 300 /* multiplier for all of a kind */
X#define P_STRAIGHT 1500 /* points for straight */
X#define P_4OKMULT 200 /* multiplier for three of a kind */
X#define P_SMSTR 400 /* points for a small straight */
X#define P_3OKMULT 100 /* multiplier for three of a kind */
X#define P_ACE 100 /* points for single ace */
X#define P_FIVE 50 /* points for single five */
X
X/*
X** The following defines are just symbolic values for numbers.
X*/
X#define BADFACE 0 /* impossible die face */
X#define ACE 1 /* an ACE is a one, a scoring die */
X#define DEUCE 2 /* an DEUCE is a two, the most worthless die */
X#define THREE 3 /* how many in a Three_of_a_kind or the face name */
X#define FIVE 5 /* FIVE is five, a scoring die */
X#define MESGLEN 128 /* length of message buffer */
X#define NAMELEN 64 /* player name buffer length */
X#define IDLEN 64 /* player identity buffer length */
X#define COMP 0 /* computer starts as player zero */
X#define PLAYERS 12 /* max players (typical screen length limits) */
X#define WRITETIMO 20 /* timeout on socket writes */
X#define READTIMO 60 /* timeout on socket reads */
X
X/*
X** Message numbers. Started out trying to pick them logically,
X** but gave up after a while. They don't really matter anyway.
X*/
X#define M_INFO 211 /* informational or status messages */
X#define M_PLAY 212 /* play-by-play on other players */
X#define M_DICE 213 /* complete dice status */
X#define M_HELO 220 /* hello message */
X#define M_DOWN 221 /* shutdown message */
X#define M_RQID 222 /* id request */
X#define M_TURN 310 /* Player %d, ... up with %d points. */
X#define M_NWIN 311 /* game over, no winner */
X#define M_OVER 312 /* Player %d has won the game! */
X#define M_CPLR 320 /* clear all players */
X#define M_UARE 321 /* you are player %d */
X#define M_PNUM 322 /* player %d %s */
X#define M_FARE 323 /* farewell %d %s */
X#define M_MSCO 330 /* You now have %d points. */
X#define M_OSCO 331 /* Player %d now has %d points. */
X#define M_ANOG 340 /* play another game? */
X#define M_RFST 341 /* Ready to roll? */
X#define M_KEEP 342 /* %d points: */
X#define M_ARGE 501 /* argument error */
X#define M_BADM 999 /* bad message */
X
X/*
X** Some stuff for converting numbers to names. Probably should be elsewhere.
X*/
Xextern char *numnames[][2];
X#define NUMBER(n) (numnames[n][0])
X#define FACE(show,ndice) (numnames[show][(ndice)!=1])
X
X/*
X** boolean: remind me never to do this again
X*/
Xtypedef enum { False, True } boolean;
X
X/*
X** diestat: the four states a die can be in
X*/
Xtypedef enum { Free, Held, Rolled, Taken } diestat;
X
X/*
X** combination: names for all scoring combinations
X*/
Xtypedef enum {
X Previous = -1, Nothing, Five, Ace, Three_of_a_kind,
X Small_straight, Four_of_a_kind, Straight, All_of_a_kind
X} combination;
X
X/*
X** diceset: structure for tracking status of all dice
X*/
Xtypedef struct {
X int d_face[NDICE]; /* number showing on each die */
X diestat d_stat[NDICE]; /* status of each die */
X combination d_comb[NDICE]; /* die's scoring combination */
X combination d_best; /* best combination scored */
X boolean d_again; /* true if rolling again */
X int d_rolling; /* number of dice rolling */
X int d_pts_roll; /* points due to last roll */
X int d_pts_dice; /* points by this set of NDICE */
X int d_pts_turn; /* points accumulated this turn */
X char d_mesg[MESGLEN]; /* status message */
X} diceset;
X
X/*
X** cstat: player/connection status
X*/
Xtypedef enum {
X Inactive, /* no player */
X Waiting, /* human waiting to get in */
X Active, /* active human player */
X Computer, /* active computer player */
X} cstat;
X
X/*
X** player: player/connection status
X*/
Xtypedef struct {
X cstat p_stat; /* connection status */
X int p_fd; /* communications socket */
X int p_score; /* player score */
X boolean p_onboard; /* True if player on board */
X int (*p_strategy)(); /* computer player strategy */
X boolean (*p_temper)(); /* computer player temperment */
X char p_name[NAMELEN]; /* player name */
X char p_id[IDLEN]; /* "unique" player id */
X} player;
X
X/*
X** action: relates message types to actions
X*/
Xstruct action {
X int a_type; /* message type M_???? */
X int (*a_action)(); /* action to take */
X};
X
X/*
X** graphtype: type of graphics the terminal supports
X*/
Xtypedef enum {
X Nographics, Digital, Zenith
X} graphtype;
X
X/*
X** history: player scoring history
X*/
Xtypedef struct {
X long h_points; /* total number of points */
X long h_avgturn; /* average points per turn */
X long h_wins; /* number of games won */
X long h_games; /* number of games played */
X char h_id[IDLEN]; /* "unique" player id */
X} history;
X
X/*
X** risk: rolling risks/expectations
X*/
Xtypedef struct {
X float r_p_any; /* probability of scoring anything (assumes mix) */
X float r_p_all; /* probability of scoring all (assumes mix) */
X int r_e_mix; /* <rolled> with a mix of held dice */
X int r_e_aces; /* <rolled> with held dice all aces (no 3o'kind) */
X int r_e_fives; /* <rolled> with held dice all fives (no 3o'kind) */
X} risk;
END_OF_cubes.h
if test 6239 -ne `wc -c <cubes.h`; then
echo shar: \"cubes.h\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f history.c -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"history.c\"
else
echo shar: Extracting \"history.c\" \(6118 characters\)
sed "s/^X//" >history.c <<'END_OF_history.c'
X/* vi:set sw=4 ts=4: */
X#ifndef lint
Xstatic char sccsid[] = "@(#)history.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif lint
X
X#include <stdio.h>
X#include <sys/file.h>
X#include <strings.h>
X#include "cubes.h"
X
Xhistory *hist; /* history array */
Xunsigned nhist = 0; /* entries in history array */
Xextern int turnnum;
Xextern player plr[];
Xextern char *compname[];
Xextern int plrcmp();
X
Xextern char *fgets();
Xextern char *malloc();
Xextern char *realloc();
X
X/*
X** histread: read history file
X*/
Xhistread()
X{
X register int n;
X register FILE *fp;
X char line[BUFSIZ];
X
X if(hist != 0) {
X free((char *)hist);
X hist = 0, nhist = 0;
X }
X
X /*
X ** Open the file for reading. The file must exist.
X */
X if((fp = fopen(HISTFILE, "r")) == 0) {
X perror("histread: fopen");
X return -1;
X }
X
X /*
X ** Count the lines in the file. If none, we're all done.
X */
X for(n = 0;fgets(line, sizeof line, fp) != 0;++n)
X ;
X if(n == 0) {
X (void) fclose(fp);
X return 0;
X }
X
X /*
X ** Rewind the file, allocate space, and read in the entries.
X */
X clearerr(fp);
X rewind(fp);
X nhist = n;
X if((hist = (history *)malloc(nhist * sizeof *hist)) == 0) {
X fprintf(stderr, "histread: malloc: can't allocate space for history\n");
X (void) fclose(fp);
X return -1;
X }
X n = 0;
X while(fgets(line, sizeof line, fp) != 0) {
X if(sscanf(line, "%ld %ld %ld %ld%*c%[^\n]",
X &(hist+n)->h_games, &(hist+n)->h_wins,
X &(hist+n)->h_points, &(hist+n)->h_avgturn,
X (hist+n)->h_id) < 4) {
X fprintf(stderr, "histread: ignored bad line in %s\n", HISTFILE);
X continue;
X }
X ++n;
X }
X
X (void) fclose(fp);
X return 0;
X}
X
X/*
X** histcmp: history comparison function
X** best win average, then best point average
X*/
Xhistcmp(h1, h2)
Xhistory *h1, *h2;
X{
X double v1, v2, diff;
X
X v1 = h1->h_games == 0 ? 0 : (h1->h_wins / h1->h_games);
X v2 = h2->h_games == 0 ? 0 : (h2->h_wins / h2->h_games);
X if((diff = v2 - v1) != 0) /* reversed */
X return diff < 0 ? -1 : 1;
X v1 = h1->h_games == 0 ? 0 : (h1->h_points / h1->h_games);
X v2 = h2->h_games == 0 ? 0 : (h2->h_points / h2->h_games);
X if((diff = v2 - v1) != 0) /* reversed */
X return diff < 0 ? -1 : 1;
X v1 = h1->h_games == 0 ? 0 : (h1->h_avgturn / h1->h_games);
X v2 = h2->h_games == 0 ? 0 : (h2->h_avgturn / h2->h_games);
X if((diff = v2 - v1) != 0) /* reversed */
X return diff < 0 ? -1 : 1;
X return strncmp(h1->h_id, h2->h_id, IDLEN);
X}
X
X/*
X** histsort: sort the history array
X*/
Xhistsort()
X{
X if(nhist != 0 && hist != 0)
X qsort((char *)hist, (int)nhist, sizeof *hist, histcmp);
X}
X
X/*
X** histwrite: write the history file
X*/
Xhistwrite()
X{
X register FILE *fp;
X register int n;
X
X (void) umask(0644);
X if((fp = fopen(HISTFILE, "w")) == 0) {
X perror("histwrite: fopen");
X return -1;
X }
X
X if(nhist != 0) {
X histsort();
X for(n = 0;n < nhist;++n) {
X fprintf(fp, "%10ld %10ld %10ld %10ld %s\n",
X (hist+n)->h_games, (hist+n)->h_wins,
X (hist+n)->h_points, (hist+n)->h_avgturn,
X (hist+n)->h_id);
X }
X }
X
X (void) fclose(fp);
X return 0;
X}
X
X/*
X** histadd: add a player to the history array
X*/
Xhistadd(c)
Xint c;
X{
X register int n = nhist;
X char *id;
X
X if(nhist == 0) {
X if((hist = (history *)malloc(sizeof *hist)) == 0) {
X fprintf(stderr, "histadd: malloc failed\n");
X return -1;
X }
X nhist = 1;
X } else {
X ++nhist;
X if((hist =
X (history *)realloc((char *)hist, nhist * sizeof *hist)) == 0) {
X fprintf(stderr, "histadd: realloc failed\n");
X nhist = 0;
X return -1;
X }
X }
X
X if(*(id = plr[c].p_id) == '\0')
X id = plr[c].p_name;
X strncpy((hist+n)->h_id, id, IDLEN);
X (hist+n)->h_id[IDLEN-1] = '\0';
X (hist+n)->h_games = 0;
X (hist+n)->h_wins = 0;
X (hist+n)->h_points = 0;
X (hist+n)->h_avgturn = 0;
X
X return n;
X}
X
X/*
X** histpoints: add player's points to history
X** updates game count and adds new entries as needed
X*/
Xhistpoints(c)
Xint c;
X{
X register char *id;
X register int n;
X boolean found;
X
X /*
X ** Locate player's entry in history array.
X */
X if(*(id = plr[c].p_id) == '\0')
X id = plr[c].p_name;
X found = False;
X for(n = 0;found == False && n < nhist;++n) {
X if(strncmp(id, (hist+n)->h_id, IDLEN) == 0) {
X found = True;
X break;
X }
X }
X
X /*
X ** If player doesn't have an entry, call histadd
X ** to add one. Histadd returns the index of the
X ** new entry or -1 on error.
X */
X if(found == False) {
X if((n = histadd(c)) < 0) {
X fprintf(stderr, "histpoints: histadd failed\n");
X return -1;
X }
X }
X
X /*
X ** Update points and avgturn and increment game count.
X ** Game count is incremented only here.
X */
X (hist+n)->h_points += plr[c].p_score;
X if(turnnum > 0)
X (hist+n)->h_avgturn += plr[c].p_score / turnnum;
X ++(hist+n)->h_games;
X
X return 0;
X}
X
X/*
X** histwins: update win count for game winner
X** assumes that histpoints has been called to update game count
X** doesn't add new history entries as needed
X*/
Xhistwins(c)
Xint c;
X{
X register char *id;
X register int n;
X
X if(*(id = plr[c].p_id) == '\0')
X id = plr[c].p_name;
X
X for(n = 0;n < nhist;++n) {
X if(strncmp(id, (hist+n)->h_id, IDLEN) == 0) {
X ++(hist+n)->h_wins;
X return 0;
X }
X }
X
X fprintf(stderr, "histwins: can't find <%s> in history\n", id);
X return -1;
X}
X
X/*
X** histwgt: return player's historical wins/points weighting
X*/
Xlong
Xhistwgt(c)
Xint c;
X{
X register char *id;
X register int n;
X long wgt;
X
X if(*(id = plr[c].p_id) == '\0')
X id = plr[c].p_name;
X
X for(n = 0;n < nhist;++n) {
X if(strncmp(id, (hist+n)->h_id, IDLEN) == 0) {
X if((hist+n)->h_games == 0)
X return 0L;
X wgt = 1000L * (hist+n)->h_wins; /* win bonus */
X wgt += (hist+n)->h_points; /* plus points */
X wgt += (hist+n)->h_avgturn; /* plus average turn */
X wgt /= (hist+n)->h_games; /* to average */
X return wgt;
X }
X }
X
X return 0L;
X}
X
X/*
X** historder: reorder players by historical wins/points weighting
X*/
Xhistorder()
X{
X register int c;
X
X /*
X ** Give active players a score based on their history.
X */
X for(c = 0;c < PLAYERS;++c) {
X switch(plr[c].p_stat) {
X case Computer:
X case Active:
X plr[c].p_score = (int)histwgt(c);
X break;
X default:
X plr[c].p_score = 0;
X break;
X }
X }
X
X /*
X ** Uses plrcmp() defined in dsrv.c.
X */
X qsort((char *)plr, PLAYERS, sizeof plr[0], plrcmp);
X}
END_OF_history.c
if test 6118 -ne `wc -c <history.c`; then
echo shar: \"history.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f random.c -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"random.c\"
else
echo shar: Extracting \"random.c\" \(704 characters\)
sed "s/^X//" >random.c <<'END_OF_random.c'
X/* vi:set sw=4 ts=4: */
X#ifndef lint
Xstatic char sccsid[] = "@(#)random.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif lint
X
X#include <sys/types.h>
X#include <sys/time.h>
X
Xextern char *initstate();
Xextern long random();
Xextern time_t time();
X
X/*
X** irandom: initialize random number generator
X*/
Xirandom()
X{
X static long state[256];
X struct timeval tv;
X struct timezone tz;
X register unsigned n;
X
X (void) gettimeofday(&tv, &tz);
X n = (unsigned)(tv.tv_sec * 10000L + tv.tv_usec / 100);
X (void) initstate(n, state, sizeof state);
X for(n %= 49;n != 0;--n)
X (void) random();
X}
X
X/*
X** dieroll: roll an n-sided die (assumes initialized random)
X*/
Xdieroll(n)
X{
X return (int)(random() % n) + 1;
X}
END_OF_random.c
if test 704 -ne `wc -c <random.c`; then
echo shar: \"random.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f risk.c -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"risk.c\"
else
echo shar: Extracting \"risk.c\" \(6831 characters\)
sed "s/^X//" >risk.c <<'END_OF_risk.c'
X/* vi:set sw=4 ts=4: */
X#ifndef lint
Xstatic char sccsid[] = "@(#)risk.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif lint
X
X#include "cubes.h"
X
Xextern player plr[];
Xextern char *compname[];
Xextern int compnames;
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*/
Xrisk risktbl[NDICE+1] = {
X/* any all mix aces fives */
X { 0.00000, 0.00000, 0, 0, 0 }, /* rolling 0 */
X { 0.33333, 0.33333, 25, 507, 268 }, /* rolling 1 */
X { 0.55650, 0.11197, 50, 127, 89 }, /* rolling 2 */
X { 0.72159, 0.05588, 86, 96, 91 }, /* rolling 3 */
X { 0.84192, 0.09651, 162, 162, 162 }, /* rolling 4 */
X { 0.92254, 0.19985, 310, 310, 310 }, /* rolling 5 */
X};
X
X#define R_E_ALLDICE (risktbl[NDICE].r_e_mix)
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 as dice are now */
X int cscore; /* player current score */
X int sscore; /* player score if stays */
X int xpct; /* expectation value of next roll */
X boolean canthold; /* true if can't hold */
X
X /*
X ** Fake some recognition time.
X */
X hesitate();
X
X /*
X ** Mark Rolled dice as Taken, updating the value of d_rolling.
X */
X keepall(pd);
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_strategy)(&modi);
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 canthold = False;
X cscore = plr[comp].p_score;
X sscore = cscore + stay;
X if(plr[comp].p_onboard == False && sscore < ONBOARD)
X canthold = True;
X else if(sscore >= WINSCORE && cscore < WINSCORE && stay < OFFBOARD)
X canthold = True;
X if(canthold == True) {
X *pd = modi; /* modi is the roll-again diceset */
X return;
X }
X
X /*
X ** Fake some indecision (sometimes).
X */
X if(dieroll(12) > 8)
X hesitate();
X
X /*
X ** Calculate the expectation value of rolling again.
X */
X temp = modi;
X xpct = expect(&temp);
X
X /*
X ** Player's temperment will chose which diceset to use.
X ** If p_temper returns True, it means roll again.
X */
X if((*plr[comp].p_temper)(comp, stay, xpct, &modi) == True)
X *pd = modi; /* modi is the roll-again diceset */
X}
X
X/*
X** expect: return expectation value of rolling dice as they stand
X** yes, I know these values aren't quite right...
X*/
Xexpect(pd)
Xregister diceset *pd;
X{
X register int d;
X int xpct;
X boolean allaces;
X boolean allfives;
X risk *pr;
X diceset temp;
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 if(pd->d_rolling == 0)
X allaces = allfives = False;
X else {
X /*
X ** Find out if we have all Aces or all Fives.
X */
X allaces = allfives = True;
X for(d = 0;d < NDICE;++d) {
X switch(pd->d_comb[d]) {
X case Previous:
X if(pd->d_face[d] != ACE) /* oops! held 3o'kind gets by */
X allaces = False;
X if(pd->d_face[d] != FIVE) /* oops! held 3o'kind gets by */
X allfives = False;
X break;
X case Ace: allfives = False; break;
X case Five: allaces = False; break;
X case Nothing: break;
X default: allaces = allfives = False; break;
X }
X }
X }
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 ** or all Fives or a mix. Then add the expectation value of retaining
X ** the points we already have and the value of being able to roll
X ** a complete set of dice.
X */
X pr = &risktbl[pd->d_rolling == 0 ? NDICE : pd->d_rolling];
X xpct = allaces == allfives ? pr->r_e_mix
X : (allaces == True ? pr->r_e_aces : pr->r_e_fives);
X scoredice(pd);
X xpct += (int)(
X pd->d_pts_turn * pr->r_p_any /* keeping what we've got */
X + R_E_ALLDICE * pr->r_p_all /* getting to roll again */
X );
X
X return xpct;
X}
X
X/*
X** ziprisk: return the probability of rolling nothing
X** assumes that rolling zero really means rolling all
X*/
Xdouble
Xziprisk(rolling)
Xint rolling;
X{
X return 1.0 - risktbl[rolling == 0 ? NDICE : rolling].r_p_any;
X}
X
X/*
X** highscore: find highest score amongst other players
X*/
Xhighscore(comp)
Xint comp;
X{
X register int c;
X int high;
X
X for(high = c = 0;c < PLAYERS;++c) if(c != comp) {
X switch(plr[c].p_stat) {
X case Computer:
X case Active:
X if(plr[c].p_score > high)
X high = plr[c].p_score;
X break;
X }
X }
X
X return high;
X}
X
X/*
X** closescore: find the highest score that's close enough to compete with
X*/
Xclosescore(comp)
Xint comp;
X{
X register int c, high, hc;
X int oldhigh;
X int score[PLAYERS];
X
X#define CLOSE 3000
X
X /*
X ** Copy the scores into an array.
X */
X score[comp] = 0;
X for(c = 0;c < PLAYERS;++c) if(c != comp) {
X switch(plr[c].p_stat) {
X case Computer:
X case Active:
X score[c] = plr[c].p_score;
X break;
X default:
X score[c] = 0;
X break;
X }
X }
X
X /*
X ** Search the score array for the highest score. If that one
X ** happens to be too far away, zap it and try again. If we
X ** threw out the only other score (or the last score), then
X ** put it back.
X */
X oldhigh = 0;
X do {
X high = -1;
X for(c = 0;c < PLAYERS;++c)
X if(score[c] > high)
X high = score[(hc = c)];
X if(high - plr[comp].p_score > CLOSE) {
X oldhigh = score[hc];
X score[hc] = 0;
X high = -1;
X }
X } while(high == -1);
X if(high <= 0)
X high = oldhigh;
X
X return high;
X}
X
X/*
X** cfirst: hesitate before first roll
X*/
Xcfirst(comp)
Xint comp;
X{
X hesitate();
X
X /*
X ** If this computer has no score, and is not the COMP computer,
X ** then if the leader is more than a quarter of the way to winning,
X ** there's a 25% chance that this computer will quit the game.
X */
X if(plr[comp].p_score == 0 && strcmp(plr[comp].p_name, compname[0]) != 0) {
X if(dieroll(12) > 8 && highscore(comp) > WINSCORE / 4) {
X oldplayer(comp);
X return -1;
X }
X }
X
X /*
X ** If this is a proxy computer, sometimes do some more hesitating.
X */
X if(plr[comp].p_id[0] != '\0' && dieroll(20) < 4)
X hesitate();
X
X return 0;
X}
X
X/*
X** hesitate: fake some human-like hesitation
X*/
Xhesitate()
X{
X sleep((unsigned)dieroll(3)+1); /* 2 to 4 seconds */
X}
END_OF_risk.c
if test 6831 -ne `wc -c <risk.c`; then
echo shar: \"risk.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f strategies.c -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"strategies.c\"
else
echo shar: Extracting \"strategies.c\" \(3520 characters\)
sed "s/^X//" >strategies.c <<'END_OF_strategies.c'
X/* vi:set sw=4 ts=4: */
X#ifndef lint
Xstatic char sccsid[] = "@(#)strategies.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif lint
X
X#include "cubes.h"
X
Xextern player plr[];
Xextern char *compname[];
Xextern int compnames;
X
X/*
X** basic: the most basic strategy, throw out Fives
X*/
Xstatic
Xbasic(pd)
Xdiceset *pd;
X{
X if(pd->d_rolling == 0)
X return;
X nofives(pd);
X}
X
X/*
X** merrier: the more dice to throw, the merrier
X** throw out Fives then Aces
X*/
Xstatic
Xmerrier(pd)
Xdiceset *pd;
X{
X if(pd->d_rolling == 0)
X return;
X nofives(pd);
X noaces(pd);
X}
X
X/*
X** respect: respects Aces if they won't help with 3o'kind
X** throws out Fives, throws out Aces if result >= THREE free
X*/
Xstatic
Xrespect(pd)
Xdiceset *pd;
X{
X diceset temp;
X
X if(pd->d_rolling == 0)
X return;
X nofives(pd);
X temp = *pd;
X noaces(&temp);
X if(temp.d_rolling >= THREE)
X *pd = temp;
X}
X
X/*
X** twohater: throws out 3o'kind in DEUCEs, Fives, and Aces
X*/
Xstatic
Xtwohater(pd)
Xdiceset *pd;
X{
X if(pd->d_rolling == 0)
X return;
X if(pd->d_best == Three_of_a_kind)
X no3deuce(pd);
X nofives(pd);
X noaces(pd);
X}
X
X/*
X** greedy: throws out 3o'kind in THREEs and DEUCEs,
X** Small_straights, Fives, and Aces
X*/
Xstatic
Xgreedy(pd)
Xdiceset *pd;
X{
X if(pd->d_rolling == 0)
X return;
X if(pd->d_best == Three_of_a_kind)
X no3three(pd);
X if(pd->d_best == Three_of_a_kind)
X no3deuce(pd);
X if(pd->d_best == Small_straight)
X nosmall(pd);
X nofives(pd);
X noaces(pd);
X}
X
X/*
X** picky:
X** always discards 3o'kind in DEUCEs and THREEs
X** discards only Aces when all Fives are saved
X** discards only Fives when all Aces are saved
X** else discards excess Fives then Aces
X*/
Xstatic
Xpicky(pd)
Xregister diceset *pd;
X{
X register int d;
X boolean allaces;
X boolean allfives;
X
X if(pd->d_rolling == 0)
X return;
X
X if(pd->d_best == Three_of_a_kind)
X no3three(pd);
X if(pd->d_best == Three_of_a_kind)
X no3deuce(pd);
X
X allaces = allfives = True;
X for(d = 0;d < NDICE;++d) {
X switch(pd->d_comb[d]) {
X case Ace:
X case Five:
X case Nothing:
X break;
X case Previous:
X if(pd->d_face[d] != ACE)
X allaces = False;
X if(pd->d_face[d] != FIVE)
X allfives = False;
X break;
X default:
X allaces = allfives = False;
X break;
X }
X }
X
X if(allfives == True && allaces == False)
X noaces(pd);
X else if(allfives == False && allaces == True)
X nofives(pd);
X else {
X nofives(pd);
X noaces(pd);
X }
X}
X
X/*
X** strategy: an array of available strategies
X*/
Xint (*strategy[])() = {
X basic, /* the COMP strategy */
X merrier,
X respect,
X twohater,
X greedy,
X picky,
X};
X
Xint strategies = sizeof strategy / sizeof strategy[0];
X
X/*
X** fickle: pick another strategy randomly!
X** for proxy players only
X*/
Xstatic
Xfickle(pd)
Xdiceset *pd;
X{
X int n;
X
X if(dieroll(12) < 4)
X hesitate();
X n = dieroll(strategies) - 1;
X (*strategy[n])(pd);
X}
X
X/*
X** pickstrategy: personalize each computer player
X** proxy players get the fickle strategy
X** calls picktemper to choose player temperment
X*/
Xpickstrategy(c)
Xint c;
X{
X register int n;
X register char *name;
X
X /*
X ** First, elect the player temperment.
X */
X picktemper(c);
X
X /*
X ** Proxies are always fickle, since they are emulating humans.
X */
X if(plr[c].p_id[0] != '\0') {
X plr[c].p_strategy = fickle;
X return;
X }
X
X /*
X ** Match a name to a strategy.
X */
X name = plr[c].p_name;
X for(n = 0;n < compnames;++n) {
X if(strcmp(compname[n], name) == 0) {
X plr[c].p_strategy = strategy[n % strategies];
X return;
X }
X }
X
X /*
X ** No match above, so pick one randomly.
X */
X n = dieroll(strategies) - 1;
X plr[c].p_strategy = strategy[n];
X}
END_OF_strategies.c
if test 3520 -ne `wc -c <strategies.c`; then
echo shar: \"strategies.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f tactics.c -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"tactics.c\"
else
echo shar: Extracting \"tactics.c\" \(4102 characters\)
sed "s/^X//" >tactics.c <<'END_OF_tactics.c'
X/* vi:set sw=4 ts=4: */
X#ifndef lint
Xstatic char sccsid[] = "@(#)tactics.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif lint
X
X#include "cubes.h"
X
X/*
X** nofives: discard excess fives
X*/
Xnofives(pd)
Xregister diceset *pd;
X{
X register int d;
X register int fives;
X
X for(fives = d = 0;d < NDICE;++d)
X if(pd->d_comb[d] == Five)
X ++fives;
X if(pd->d_best == Five)
X --fives;
X
X /*
X ** Alternate zapping from left to right and right to left.
X */
X if(dieroll(2) == 1) {
X for(d = 0;fives > 0 && d < NDICE;++d) {
X if(pd->d_comb[d] == Five) {
X pd->d_comb[d] = Nothing;
X pd->d_stat[d] = Free;
X ++pd->d_rolling;
X --fives;
X }
X }
X } else {
X for(d = NDICE-1;fives > 0 && d >= 0;--d) {
X if(pd->d_comb[d] == Five) {
X pd->d_comb[d] = Nothing;
X pd->d_stat[d] = Free;
X ++pd->d_rolling;
X --fives;
X }
X }
X }
X}
X
X/*
X** noaces: discard excess aces
X*/
Xnoaces(pd)
Xregister diceset *pd;
X{
X register int d;
X register int aces;
X register int fives;
X
X aces = fives = 0;
X for(d = 0;d < NDICE;++d) {
X switch(pd->d_comb[d]) {
X case Ace: ++aces; break;
X case Five: ++fives; break;
X }
X }
X
X if(pd->d_best == Ace) {
X if(fives > 0)
X pd->d_best = Five;
X else
X --aces;
X }
X
X /*
X ** Alternate zapping LtoR and RtoL.
X */
X if(dieroll(2) == 2) {
X for(d = 0;aces > 0 && d < NDICE;++d) {
X if(pd->d_comb[d] == Ace) {
X pd->d_comb[d] = Nothing;
X pd->d_stat[d] = Free;
X ++pd->d_rolling;
X --aces;
X }
X }
X } else {
X for(d = NDICE-1;aces > 0 && d >= 0;--d) {
X if(pd->d_comb[d] == Ace) {
X pd->d_comb[d] = Nothing;
X pd->d_stat[d] = Free;
X ++pd->d_rolling;
X --aces;
X }
X }
X }
X}
X
X/*
X** no3deuce: zap 3o'kind in deuces if possible
X** assumes only Aces and Fives will fit in
X** the same hand as a Three_of_a_kind
X*/
Xno3deuce(pd)
Xregister diceset *pd;
X{
X register int d;
X boolean present;
X combination nextbest;
X
X if(pd->d_best != Three_of_a_kind)
X return;
X
X present = False;
X nextbest = Nothing;
X for(d = 0;d < NDICE;++d) {
X switch(pd->d_comb[d]) {
X case Three_of_a_kind:
X if(pd->d_face[d] != DEUCE)
X return;
X present = True;
X break;
X case Ace:
X nextbest = Ace;
X break;
X case Five:
X if(nextbest == Nothing)
X nextbest = Five;
X break;
X }
X }
X
X if(present == False || nextbest == Nothing)
X return;
X
X for(d = 0;d < NDICE;++d) {
X if(pd->d_comb[d] == Three_of_a_kind /*&& pd->d_face[d] == DEUCE*/) {
X pd->d_comb[d] = Nothing;
X pd->d_stat[d] = Free;
X ++pd->d_rolling;
X }
X }
X pd->d_best = nextbest;
X}
X
X/*
X** no3three: zap 3o'kind in threes if possible
X** assumes only Aces and Fives will fit in
X** the same hand as a Three_of_a_kind
X*/
Xno3three(pd)
Xregister diceset *pd;
X{
X register int d;
X boolean present;
X combination nextbest;
X
X if(pd->d_best != Three_of_a_kind)
X return;
X
X present = False;
X nextbest = Nothing;
X for(d = 0;d < NDICE;++d) {
X switch(pd->d_comb[d]) {
X case Three_of_a_kind:
X if(pd->d_face[d] != THREE)
X return;
X present = True;
X break;
X case Ace:
X nextbest = Ace;
X break;
X case Five:
X if(nextbest == Nothing)
X nextbest = Five;
X break;
X }
X }
X
X if(present == False || nextbest == Nothing)
X return;
X
X for(d = 0;d < NDICE;++d) {
X if(pd->d_comb[d] == Three_of_a_kind /*&& pd->d_face[d] == DEUCE*/) {
X pd->d_comb[d] = Nothing;
X pd->d_stat[d] = Free;
X ++pd->d_rolling;
X }
X }
X pd->d_best = nextbest;
X}
X
X/*
X** nosmall: discard small straight, retaining imbeded ace or five
X*/
Xnosmall(pd)
Xregister diceset *pd;
X{
X register int d;
X boolean present;
X combination nextbest;
X
X present = False;
X nextbest = Nothing;
X for(d = 0;d < NDICE;++d) {
X switch(pd->d_comb[d]) {
X case Small_straight:
X present = True;
X switch(pd->d_face[d]) {
X case ACE:
X pd->d_comb[d] = Ace;
X nextbest = Ace;
X break;
X case FIVE:
X pd->d_comb[d] = Five;
X if(nextbest == Nothing)
X nextbest = Five;
X break;
X default:
X pd->d_comb[d] = Nothing;
X pd->d_stat[d] = Free;
X ++pd->d_rolling;
X break;
X }
X break;
X case Ace:
X nextbest = Ace;
X break;
X case Five:
X if(nextbest == Nothing)
X nextbest = Five;
X break;
X }
X }
X if(present == True)
X pd->d_best = nextbest;
X}
END_OF_tactics.c
if test 4102 -ne `wc -c <tactics.c`; then
echo shar: \"tactics.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f tempers.c -a "${1}" != "-c" ; then
echo shar: Will not over-write existing file \"tempers.c\"
else
echo shar: Extracting \"tempers.c\" \(6057 characters\)
sed "s/^X//" >tempers.c <<'END_OF_tempers.c'
X/* vi:set sw=4 ts=4: */
X#ifndef lint
Xstatic char sccsid[] = "@(#)tempers.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris";
X#endif lint
X
X#include "cubes.h"
X
Xextern player plr[];
Xextern risk risktbl[];
Xextern char *compname[];
Xextern int compnames;
Xextern double ziprisk();
Xextern int highscore();
Xextern int closescore();
Xextern int winner();
X
X#define P_MAXROLL (P_ACEMULT * P_AOKMULT) /* largest single roll */
X
X/*
X** robot: just plays the expectations and ignores other players, etc.
X*/
X/*ARGSUSED*/
Xstatic boolean
Xrobot(comp, stay, xpct, pd)
Xint comp, stay, xpct;
Xdiceset *pd;
X{
X /*
X ** Don't roll if there's no advantage.
X */
X return (xpct <= stay) ? False : True;
X}
X
X/*
X** xx_antsy: gets antsy when it gets behind; uses supplied tracking function
X** a little bit more conservative than robot when ahead
X*/
X/*ARGSUSED*/
Xstatic boolean
Xxx_antsy(comp, stay, xpct, pd, track)
Xint comp, stay, xpct;
Xdiceset *pd;
Xint (*track)();
X{
X int mscore;
X int hscore;
X int live;
X
X /*
X ** If there's gain to be had, roll again.
X */
X#define GAINFAC 1.1
X if(xpct > (int)(GAINFAC * stay))
X return True;
X
X /*
X ** Find our score and the score we're tracking.
X ** If we're ahead or close, then don't reroll.
X */
X mscore = plr[comp].p_score + stay;
X hscore = (*track)(comp);
X if(mscore + P_ACE > hscore)
X return False;
X
X /*
X ** Calculate the expectation value we can live with.
X */
X live = (int)(stay * (GAINFAC - (double)(hscore - mscore) / WINSCORE));
X if(xpct > live)
X return True;
X
X return False;
X}
X
X/*
X** antsy: gets antsy when it gets behind; tracks leader
X*/
Xstatic boolean
Xantsy(comp, stay, xpct, pd)
Xint comp, stay, xpct;
Xdiceset *pd;
X{
X return xx_antsy(comp, stay, xpct, pd, highscore);
X}
X
X/*
X** tracker: same as antsy but tracks highest close score rather than higest
X*/
Xstatic boolean
Xtracker(comp, stay, xpct, pd)
Xint comp, stay, xpct;
Xdiceset *pd;
X{
X return xx_antsy(comp, stay, xpct, pd, closescore);
X}
X
X/*
X** chicken: gets scared when it has lots of points in hand
X** doesn't care about expectation or other players
X** also chickens out as soon as it thinks it has won
X** or as soon as it gets on board
X*/
X/*ARGSUSED*/
Xstatic boolean
Xchicken(comp, stay, xpct, pd)
Xint comp, stay, xpct;
Xdiceset *pd;
X{
X int mscore;
X int hscore;
X int atrisk;
X int maxrisk;
X
X /*
X ** If we just got on board, stop.
X */
X if((mscore = plr[comp].p_score) == 0) {
X if(stay >= ONBOARD) /* redundant */
X return False;
X }
X
X /*
X ** If we could win here, stop.
X */
X if((mscore += stay) >= WINSCORE) {
X hscore = highscore(comp);
X if(mscore - hscore >= WINMARGIN)
X return False;
X }
X
X /*
X ** We're not willing to risk much when we're ahead.
X ** A little more desperate when behind...
X */
X atrisk = (int)(stay * ziprisk(pd->d_rolling));
X if(mscore > hscore + 5 * P_ACE)
X maxrisk = P_ACE + P_FIVE;
X else if(mscore > hscore)
X maxrisk = 2 * P_ACE;
X else if(mscore > hscore - 5 * P_ACE)
X maxrisk = 2 * P_ACE + P_FIVE / 2;
X else if(mscore > hscore - 10 * P_ACE)
X maxrisk = 2 * P_ACE + P_FIVE;
X else
X maxrisk = 3 * P_ACE;
X if(atrisk > maxrisk)
X return False;
X
X return True;
X}
X
X/*
X** gambler: cautious sometimes, not so others
X** stops when it gets on board unless the risk is small
X** stops when it has won unless the risk is small
X** rolls to try to stop a winner
X** rolls when the expectation is favorable
X** rolls when there's less than 50% risk and is behind and "lucky"
X*/
X/*ARGSUSED*/
Xstatic boolean
Xgambler(comp, stay, xpct, pd)
Xint comp, stay, xpct;
Xdiceset *pd;
X{
X int mscore;
X int hscore;
X int winc;
X
X#define SMALLRISK 0.20
X#define EVENODDS 0.50
X
X /*
X ** If we just got on board, hold unless really tempting.
X */
X if((mscore = plr[comp].p_score) == 0) {
X if(stay >= ONBOARD) { /* redundant */
X if(xpct > stay && ziprisk(pd->d_rolling) < SMALLRISK)
X return True;
X return False;
X }
X }
X
X /*
X ** If we could win, hold unless it's really tempting.
X */
X hscore = highscore(comp);
X if((mscore += stay) >= WINSCORE) {
X if(mscore - hscore >= WINMARGIN) {
X if(xpct > stay && ziprisk(pd->d_rolling) < SMALLRISK)
X return True;
X return False;
X }
X }
X
X /*
X ** If we have a winner (other than us), try to beat them,
X ** but only if the expectation is neutral or we feel lucky.
X ** We try to get within WINMARGIN of the player's score,
X ** but if their turn is after ours, we try to beat.
X ** Assumes that hscore is winner's score (but what else?).
X */
X if((winc = winner()) != -1 && winc != comp) {
X if(xpct >= stay || dieroll(WINSCORE) > stay)
X if(hscore - mscore >= WINMARGIN || winc > comp)
X return True;
X return False; /* too critical to continue */
X }
X
X /*
X ** If the expectation value is better than what we've got, go ahead.
X */
X if(xpct > stay)
X return True;
X
X /*
X ** If we're behind, roll when the odds are favorable
X ** and we feel lucky.
X */
X if(hscore > mscore && ziprisk(pd->d_rolling) < EVENODDS)
X if(dieroll(WINSCORE) > stay) /* feel lucky */
X return True;
X
X return False;
X}
X
X/*
X** strategy: an array of available strategies
X*/
Xboolean (*temper[])() = {
X robot, /* the COMP strategy */
X antsy,
X tracker,
X chicken,
X gambler,
X};
X
Xint tempers = sizeof temper / sizeof temper[0];
X
X/*
X** schizo: pick another temperment randomly!
X** for proxy players only
X*/
Xstatic boolean
Xschizo(comp, stay, xpct, pd)
Xint comp, stay, xpct;
Xdiceset *pd;
X{
X int n;
X
X if(dieroll(20) > 16)
X hesitate();
X n = dieroll(tempers) - 1;
X return (*temper[n])(comp, stay, xpct, pd);
X}
X
X/*
X** picktemper: personalize each computer player
X** proxy players get the schizo temperment
X*/
Xpicktemper(c)
Xint c;
X{
X register int n;
X register char *name;
X
X /*
X ** Proxies get a schizo temperment in order to emulate humans.
X */
X if(plr[c].p_id[0] != '\0') {
X plr[c].p_temper = schizo;
X return;
X }
X
X /*
X ** Match a name to a temperment.
X */
X name = plr[c].p_name;
X for(n = 0;n < compnames;++n) {
X if(strcmp(compname[n], name) == 0) {
X plr[c].p_temper = temper[n % tempers];
X return;
X }
X }
X
X /*
X ** No match above, so pick one randomly.
X */
X n = dieroll(tempers) - 1;
X plr[c].p_temper = temper[n];
X}
END_OF_tempers.c
if test 6057 -ne `wc -c <tempers.c`; then
echo shar: \"tempers.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of archive 3 \(of 3\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 3 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