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