gmp@rayssd.RAY.COM (06/17/88)
Submitted by: gmp@rayssd.RAY.COM (Gregory M. Paris) Comp.sources.games: Volume 4, Issue 33 Archive-name: cubes/Part02 #! /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 2 (of 3)." # Contents: actions.c avg.c cubes.6 dieopts.c turn.c # Wrapped by billr@saab on Tue Jun 7 16:36:06 1988 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f actions.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"actions.c\" else echo shar: Extracting \"actions.c\" \(8308 characters\) sed "s/^X//" >actions.c <<'END_OF_actions.c' X/* vi:set sw=4 ts=4: */ X#ifndef lint Xstatic char sccsid[] = "@(#)actions.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 <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 <pwd.h> X#include "cubes.h" X Xstatic char answer[BUFSIZ]; /* player answer to prompt */ Xplayer plr[PLAYERS]; /* player status */ Xdiceset dice; /* the status of the dice */ Xint mypnum = -1; /* my player number */ Xint turnnum = 0; /* current turn number */ X Xextern char *pname; /* player name */ Xextern char *shost; /* server hostname */ Xextern int ssock; /* server socket */ Xextern boolean active; /* True while active */ Xextern char *index(); Xstruct passwd *getpwuid(); X X/* X** respond: send a reply to the server adding "\r\n" X*/ Xrespond(reply) Xchar *reply; X{ X char repbuf[BUFSIZ]; X X strcpy(repbuf, reply); X strcat(repbuf, "\r\n"); X X return write(ssock, repbuf, strlen(repbuf)) < 0 ? -1 : 0; X} X X/* X** dispact: display an informative message to the player X*/ X/*ARGSUSED*/ Xdispact(t,m) Xchar *m; X{ X return infomesg(m+4); X} X X/* X** pmptact: display a prompt to the player X*/ X/*ARGSUSED*/ Xpmptact(t,m) Xchar *m; X{ X return prompt(m+4, answer); X} X X/* X** heloact: respond to the M_HELO message by giving player name. X*/ X/*ARGSUSED*/ Xheloact(t,m) Xchar *m; X{ X return respond(pname); X} X X/* X** rqidact: respond to the M_RQID message by giving player identity X*/ X/*ARGSUSED*/ Xrqidact(t,m) Xchar *m; X{ X char idbuf[IDLEN]; X char host[256]; X int uid; X struct passwd *pw; X X (void) gethostname(host, sizeof host); X X /* X ** Look up login name in the password file. If it's not there, X ** just use the user id number. X */ X uid = getuid(); X if((pw = getpwuid(uid)) == 0) X sprintf(idbuf, "%d@%.*s", uid, IDLEN-10, host); X else X sprintf(idbuf, "%s@%.*s", pw->pw_name, IDLEN-10, host); X X return respond(idbuf); X} X X/* X** infoact: display an informational message X*/ X/*ARGSUSED*/ Xinfoact(t,m) Xchar *m; X{ X return infomesg(m+4); X} X X#ifdef notdef X/* X** playact: display a play-by-play message X*/ X/*ARGSUSED*/ Xplayact(t,m) Xchar *m; X{ X return playmesg(m+4); X} X#endif notdef X X/* X** rfstact: respond to "roll first?" message X*/ Xrfstact(t,m) Xchar *m; X{ X switch(pmptact(t,m)) { X case -2: /* timeout */ X return 0; X case -1: /* error */ X return -1; X } X if(respond(answer) < 0) X return -1; X return 0; X} X X/* X** keepact: respond to "%d points:" message X*/ Xkeepact(t,m) Xchar *m; X{ X return rfstact(t,m); X} X X/* X** anogact: respond to "another game?" message X*/ Xanogact(t,m) Xchar *m; X{ X return rfstact(t,m); X} X X/* X** downact: print the shutdown message and exit gracefully X*/ X/*ARGSUSED*/ Xdownact(t,m) Xchar *m; X{ X (void) infomesg(m+4); X sleep(3); X cleanup(0); X} X X/* X** turnact: cause turn indicator to advance X*/ X/*ARGSUSED*/ Xturnact(t,m) Xchar *m; X{ X int pnum; X X if(sscanf(m+4, "Turn %d: player %d", &turnnum, &pnum) != 2) X return -1; X X return nowup(pnum); X} X X/* X** cplract: clear player roster X*/ X/*ARGSUSED*/ Xcplract(t,m) Xchar *m; X{ X register int c; X X mypnum = -1; X for(c = 0;c < PLAYERS;++c) { X plr[c].p_stat = Inactive; X plr[c].p_name[0] = '\0'; X plr[c].p_score = 0; X (void) clearplr(c); X } X X return 0; X} X X/* X** uareact: setup this player X*/ X/*ARGSUSED*/ Xuareact(t,m) Xchar *m; X{ X int pnum; X X if(sscanf(m+4, "You are player %d", &pnum) < 1) X return -1; X X mypnum = pnum; X plr[pnum].p_score = 0; X plr[pnum].p_stat = Active; X plr[pnum].p_name[0] = '\0'; X strncat(plr[pnum].p_name, pname, sizeof plr[pnum].p_name); X X return showplr(pnum); X} X X/* X** pnumact: setup another player X*/ X/*ARGSUSED*/ Xpnumact(t,m) Xchar *m; X{ X int pnum; X char *s; X X if(sscanf(m+4, "player %d", &pnum) < 1) X return -1; X plr[pnum].p_score = 0; X plr[pnum].p_stat = Active; X plr[pnum].p_name[0] = '\0'; X if((s = index(m+12, ' ')) == 0) X return -1; X strncat(plr[pnum].p_name, s+1, sizeof plr[pnum].p_name); X X return showplr(pnum); X} X X/* X** fareact: clean up when a player leaves X*/ X/*ARGSUSED*/ Xfareact(t,m) Xchar *m; X{ X int pnum; X char msgbuf[MESGLEN]; X X if(sscanf(m+4, "farewell %d", &pnum) < 1) X return -1; X sprintf(msgbuf, "%s has left the game.", plr[pnum].p_name); X (void) clearplr(pnum); X (void) infomesg(msgbuf); X sleep(1); X X plr[pnum].p_stat = Inactive; X plr[pnum].p_name[0] = '\0'; X plr[pnum].p_score = 0; X X return 0; X} X X/* X** mscoact: update my score X*/ X/*ARGSUSED*/ Xmscoact(t,m) Xchar *m; X{ X int score; X X if(sscanf(m+4, "You now have %d", &score) < 1) X return -1; X if(mypnum < 0) X return -1; X X plr[mypnum].p_score = score; X return showplr(mypnum); X} X X/* X** oscoact: update other player score X*/ X/*ARGSUSED*/ Xoscoact(t,m) Xchar *m; X{ X int pnum, score; X X if(sscanf(m+4, "Player %d now has %d", &pnum, &score) < 2) X return -1; X X plr[pnum].p_score = score; X return showplr(pnum); X} X X/* X** nwin: no winner message X*/ X/*ARGSUSED*/ Xnwinact(t,m) Xchar *m; X{ X return infomesg(m+4); X} X X/* X** overact: game over with winner X*/ X/*ARGSUSED*/ Xoveract(t,m) Xchar *m; X{ X return infomesg(m+4); X} X X/* X** diceact: parse the dice status message and redisplay the dice X*/ X/*ARGSUSED*/ Xdiceact(t,m) Xchar *m; X{ X register int d; X char stats[NDICE+1]; X char faces[NDICE+1]; X char combs[NDICE+1]; X int c; X X dice.d_mesg[0] = '\0'; X if(sscanf(m+4, "%d %s %s %s %d %d %d%*1c%[^\n]", X &c, stats, faces, combs, X &dice.d_pts_roll, &dice.d_pts_dice, &dice.d_pts_turn, X dice.d_mesg) < 7) { X fprintf(stderr, "dieact: couldn't parse dice status\n"); X return -1; X } X X for(d = 0;d < NDICE;++d) { X switch(stats[d]) { X case 'f': dice.d_stat[d] = Free; break; X case 'h': dice.d_stat[d] = Held; break; X case 't': dice.d_stat[d] = Taken; break; X case 'r': dice.d_stat[d] = Rolled; break; X default: dice.d_stat[d] = Free; break; X } X dice.d_face[d] = isdigit(faces[d]) ? (faces[d] - '0') : BADFACE; X switch(combs[d]) { X case 'p': dice.d_comb[d] = Previous; break; X case 'n': dice.d_comb[d] = Nothing; break; X case '5': dice.d_comb[d] = Five; break; X case '1': dice.d_comb[d] = Ace; break; X case 't': dice.d_comb[d] = Three_of_a_kind; break; X case 'l': dice.d_comb[d] = Small_straight; break; X case 'f': dice.d_comb[d] = Four_of_a_kind; break; X case 's': dice.d_comb[d] = Straight; break; X case 'a': dice.d_comb[d] = All_of_a_kind; break; X default: dice.d_comb[d] = Nothing; break; X } X } X X return showdice(); X} X X/* X** argeact: argument error, display error message X*/ X/*ARGSUSED*/ Xargeact(t,m) Xchar *m; X{ X (void) infomesg(m+4); X sleep(1); X return 0; X} X X/* X** badmact: alert player to bad message X*/ Xbadmact(t,m) Xchar *m; X{ X return (dispact(t,"Bad message follows:") < 0 || dispact(t,m) < 0) ? -1 : 0; X} X X/* X** unktact: alert player to unknown type message X*/ Xunktact(t,m) Xchar *m; X{ X return (dispact(t,"Unknown message follows:") < 0 || dispact(t,m) < 0) ? -1 : 0; X} X X/* X** atable: action table X*/ Xstruct action atable[] = { X { M_INFO, infoact }, /* informational or status messages */ X/* { M_PLAY, playact }, /* play-by-play on other players */ X { M_KEEP, keepact }, /* %d points: */ X { M_HELO, heloact }, /* hello message (and name query) */ X { M_DOWN, downact }, /* shutdown message */ X { M_RQID, rqidact }, /* id request */ X { M_TURN, turnact }, /* Player %d, ... up with %d points. */ X { M_NWIN, nwinact }, /* game over, no winner */ X { M_OVER, overact }, /* Player %d has won the game! */ X { M_CPLR, cplract }, /* clear player roster */ X { M_UARE, uareact }, /* you are player %d */ X { M_PNUM, pnumact }, /* player %d %s */ X { M_FARE, fareact }, /* farewell %d %s */ X { M_MSCO, mscoact }, /* You now have %d points. */ X { M_OSCO, oscoact }, /* Player %d now has %d points. */ X { M_ANOG, anogact }, /* play another game? */ X { M_RFST, rfstact }, /* 0 points, roll first? */ X { M_DICE, diceact }, /* dice status */ X { M_ARGE, argeact }, /* argument error, try again */ X { M_BADM, badmact }, /* bad message */ X}; X Xint nactions = (sizeof atable / sizeof atable[0]); X X/* X** action: a dispatching function X*/ Xaction(type, mesg) Xint type; Xchar *mesg; X{ X register int n; X int r; X X for(n = 0;n < nactions;++n) { X if(atable[n].a_type == type) { X r = atable[n].a_action(type, mesg); X neutral(); X return r; X } X } X X r = unktact(type, mesg); /* unknown type action */ X neutral(); X return r; X} END_OF_actions.c if test 8308 -ne `wc -c <actions.c`; then echo shar: \"actions.c\" unpacked with wrong size! fi # end of overwriting check fi if test -f avg.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"avg.c\" else echo shar: Extracting \"avg.c\" \(5416 characters\) sed "s/^X//" >avg.c <<'END_OF_avg.c' X/* vi:set sw=4 ts=4: */ X#ifndef lint Xstatic char sccsid[] = "@(#)avg.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris"; X#endif lint X X#include <stdio.h> X#include "cubes.h" X Xextern int optind; Xextern int opterr; Xextern char *optarg; Xextern long atol(); X X#define ROLLS 1000000L /* one million trials */ X X#define T_STR 0 /* 5 /* Straight */ X#define T_AOK 1 /* 5 /* All_of_a_kind */ X#define T_4OK1A 2 /* 5 /* Four_of_a_kind w/ one ace */ X#define T_4OK1F 3 /* 5 /* Four_of_a_kind w/ one five */ X#define T_SMSTR1A 4 /* 5 /* Small_straight w/ one ace */ X#define T_SMSTR1F 5 /* 5 /* Small_straight w/ one five */ X#define T_3OK2A 6 /* 5 /* Three_of_a_kind w/ two aces */ X#define T_3OK2F 7 /* 5 /* Three_of_a_kind w/ two fives */ X#define T_3OK1A1F 8 /* 5 /* Three_of_a_kind w/ one aces and one five */ X#define T_4OK 9 /* 4 /* Four_of_a_kind */ X#define T_SMSTR 10 /* 4 /* Small_straight */ X#define T_3OK1A 11 /* 4 /* Three_of_a_kind w/one ace */ X#define T_3OK1F 12 /* 4 /* Three_of_a_kind w/one five */ X#define T_2A2F 13 /* 4 /* two aces and two fives */ X#define T_3OK 14 /* 3 /* Three_of_a_kind */ X#define T_2A1F 15 /* 3 /* two aces and one five */ X#define T_1A2F 16 /* 3 /* one ace and two fives */ X#define T_2A 17 /* 2 /* two aces */ X#define T_2F 18 /* 2 /* two fives */ X#define T_1A1F 19 /* 2 /* one ace and one five */ X#define T_1A 20 /* 1 /* one ace */ X#define T_1F 21 /* 1 /* one five */ X#define T_ZIP 22 /* 0 /* Nothing */ X#define T_SIZE 23 X Xstatic char *t_name[] = { X "Straight", X "All_of_a_kind", X "Four_of_a_kind w/ one Ace", X "Four_of_a_kind w/ one Five", X "Small_straight w/ one Ace", X "Small_straight w/ one Five", X "Three_of_a_kind w/ two Aces", X "Three_of_a_kind w/ two Fives", X "Three_of_a_kind w/ one Aces and one Five", X "Four_of_a_kind", X "Small_straight", X "Three_of_a_kind w/one Ace", X "Three_of_a_kind w/one Five", X "two Aces and two Fives", X "Three_of_a_kind", X "two Aces and one Five", X "one Ace and two Fives", X "two Aces", X "two Fives", X "one Ace and one Five", X "one Ace", X "one Five", X "Nothing", X}; X Xmain(ac, av) Xchar *av[]; X{ X register diceset *pd; X register int d, aces, fives, c, r; X diceset dice; X long cnt[T_SIZE]; X long pts[T_SIZE]; X long tpts; X long rolls = ROLLS; X boolean aces_are_fives = False; X int haces = 0; X int held = 0; X X opterr = 0; X while((c = getopt(ac, av, "fa:h:r:")) >= 0) { X switch(c) { X case 'f': /* toggle aces_are_fives */ X aces_are_fives = aces_are_fives == True ? False : True; X break; X case 'a': /* number of aces held */ X if((haces = atoi(optarg)) < 0 || haces >= NDICE) { X fprintf(stderr, X "avg: aces must be in range 0 to %d\n", NDICE-1); X exit(1); X } X break; X case 'h': /* number of generic dice held */ X if((held = atoi(optarg)) < 0 || held >= NDICE) { X fprintf(stderr, X "avg: held must be in range 0 to %d\n", NDICE-1); X exit(1); X } X break; X case 'r': /* number of trials */ X if((rolls = atol(optarg)) <= 0) { X fprintf(stderr, "avg: rolls must be positive\n"); X exit(1); X } X break; X default: X fprintf(stderr, X "usage -- avg [-r rolls] [[-f] -a aces] [-h held]\n"); X exit(1); X } X } X X if(haces + held >= NDICE) { X fprintf(stderr, "avg: aces plus held must be less than %d\n", NDICE); X exit(1); X } X X /* X ** Initialize. X */ X irandom(); X pd = &dice; X for(c = 0;c < T_SIZE;++c) X cnt[c] = pts[c] = 0; X X /* X ** Roll and tally. X */ X for(r = 0;r < rolls;++r) { X initdice(pd); X for(d = 0;d < haces;++d) { X pd->d_stat[d] = Held; X pd->d_comb[d] = Previous; X pd->d_face[d] = aces_are_fives == True ? FIVE : ACE; X/* pd->d_pts_dice += aces_are_fives == True ? P_FIVE : P_ACE; */ X/* pd->d_pts_turn += aces_are_fives == True ? P_FIVE : P_ACE; */ X } X for(;d < haces + held;++d) { X pd->d_stat[d] = Held; X pd->d_comb[d] = Previous; X pd->d_face[d] = BADFACE; X/* pd->d_pts_dice += P_FIVE; */ X/* pd->d_pts_turn += P_FIVE; */ X } X X rolldice(pd); X evaluate(pd); X for(aces = fives = d = 0;d < NDICE;++d) { X switch(pd->d_comb[d]) { X case Ace: ++aces; break; X case Five: ++fives; break; X default: break; X } X } X X switch(pd->d_best) { X case Straight: c = T_STR; break; X case All_of_a_kind: X c = T_AOK; X /* next line accounts for <rolled> being added value only */ X pd->d_pts_roll -= aces * (aces_are_fives == True ? P_FIVE : P_ACE); X break; X case Four_of_a_kind: X c = aces ? T_4OK1A : (fives ? T_4OK1F : T_4OK); X break; X case Small_straight: X c = aces ? T_SMSTR1A : (fives ? T_SMSTR1F : T_SMSTR); X break; X case Three_of_a_kind: X if(aces > 1) c = T_3OK2A; X else if(fives > 1) c = T_3OK2F; X else if(aces) c = fives ? T_3OK1A1F : T_3OK1A; X else if(fives) c = T_3OK1F; X else c = T_3OK; X break; X case Ace: X if(aces > 1) c = fives ? (fives > 1 ? T_2A2F : T_2A1F) : T_2A; X else c = fives ? (fives > 1 ? T_1A2F : T_1A1F) : T_1A; X break; X case Five: c = fives == 2 ? T_2F : T_1F; break; X case Nothing: c = T_ZIP; break; X default: continue; X } X scoredice(pd); X pts[c] += pd->d_pts_roll; X ++cnt[c]; X } X X /* X ** Print a summary. X */ X tpts = 0; X printf("%-40s %6s %6s %8s\n", "Combination", "Ptsper", "Expect", "Count"); X for(c = 0;c < T_SIZE;++c) { X if(cnt[c] != 0) { X tpts += pts[c]; X printf("%-40s %6ld %6ld %8ld\n", X t_name[c], pts[c] / cnt[c], pts[c] / rolls, cnt[c]); X } X } X printf("%-40s %6s %6ld %8ld\n", "Total", "", tpts / rolls, rolls); X X exit(0); X} X X END_OF_avg.c if test 5416 -ne `wc -c <avg.c`; then echo shar: \"avg.c\" unpacked with wrong size! fi # end of overwriting check fi if test -f cubes.6 -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"cubes.6\" else echo shar: Extracting \"cubes.6\" \(13761 characters\) sed "s/^X//" >cubes.6 <<'END_OF_cubes.6' X.TH CUBES 6-USENET comp.sources.games USENET "UN*X Gaming Manual" X.\" Copyright 1988 Gregory M. Paris X.SH NAME Xcubes \- multi-player dice game X.SH SYNOPSIS X.B cubes X[ X.B \-g graphics X] [ X.B \-h host X] [ X.B \-n name X] X.SH OVERVIEW XThe game of X.I cubes Xis a computer implementation of a dice game played with Xfive six-sided dice. XThe goal of the game is to be the first player to Xscore 10,000 points. XPlayers take turns rolling the dice in the hopes of getting one or more Xof several scoring combinations. XA player can continue rolling as long as the previous roll produced Xa scoring combination. XFailing that, Xany points accumulated during the turn are lost and the player's turn ends. XBefore that happens, Xthe player may (prudently or cowardly) elect to quit rolling, Xat which time the points accumulated during the turn are added to the player's score. XContrary to what you might think by reading this manual page, X.I cubes Xis both easy to learn and easy to play. X.SH "BEGINNING A GAME" X.I Cubes Xis a multi-player game, Xwith the game logic implemented in a cube server, Xand player interaction via a client process, the X.I cubes Xprogram. XFor more than one person to play at a time, Xeach must each begin X.I cubes Xat about the same time, and must connect to the same server. XThe program assumes that the server is on the same system as the player. XIf this is not so, the X.RB ` \-h \ host ' Xoption must be used to tell X.I cubes Xwhich system the server is running on. X.PP XIf your terminal supports either DEC or Zenith graphics character sets, Xyou can tell X.I cubes Xto take advantage of this fact when drawing the five dice. X(This feature assumes that the X.RB ` as ' Xand X.RB ` ae ' Xtermcap capabilities describe how to put your terminal into and take it out of Xgraphics mode.) XTo do this, supply the X.RB ` \-g \ graphics ' Xoption on the command line, where X.B graphics Xis either X.RB ` d ' Xfor DEC graphics Xor X.RB ` z ' Xfor Zenith graphics. X.PP XIf you'd like your name on the scoreboard to show up as something other Xthan your login name, you can either set the X.B CUBENAME Xenvironment variable to the desired value, Xor use the X.RB ` \-n \ name ' Xcommand line option. X.SH "PLAYING THE GAME" XThe rules of the game are described in more depth below. XFortunately, it is not necessary to understand the rules before playing X.IR cubes , Xsince the cube server handles all of that for you. XThe only thing to know before playing is what to do when prompted. X.PP XThere are only four questions the program will ask you. XThe first question is X.PP X.B " Ready to roll? [ye]" X.PP Xwhich is asking you to make the first roll of your turn. XThe letters X.RB ` ye ' Xindicate that the two valid answers Xare X.RB ` y ' Xfor yes and X.RB ` e ' Xfor exit (quit the game). XThe default if you just type a carriage return is yes. X.PP XThe second question, of the form X.PP X.B " nnn saved, mmm showing; command? [rhke]" X.PP Xis asking you whether you want to quit rolling or not, Xand if not, which dice you want to roll. XThe value of X.B nnn Xis the number of points you have from previous rolls. XThe value of X.B mmm Xis the number of points from the dice you just rolled. XIf you hold, the sum of X.B nnn Xand X.B mmm Xwill be added to your score. XTo hold, type X.RB ` h ' Xand a carriage return. X.PP XIf you wish to roll again, you can just type carriage return, and all Xnonscoring dice will be rerolled (or all dice, if they've all scored). XIf in addition to the nonscoring dice, you wish to roll some of the Xscoring dice, you can type X.RB ` r ' Xfollowed by the die numbers. XThe X.RB ` r ' Xis default, so you can omit it if you like (and you probably will). XAlso, if you omit the dice numbers, only the nonscoring dice will be rolled. XNote that the nonscoring dice will always be rolled, Xwhether or not you specify them explicitly, Xso there's really no point in doing so. X.PP XSometimes it may be more convenient to tell X.I cubes Xwhich dice you wish to keep rather than which dice you wish to roll. XTo do this, Xyou can type X.RB ` k ' Xfollowed by a list of the dice you want to hold back. XIf no dice are specified, all scoring dice are kept and only nonscoring Xdice are rolled. X.PP XYou can also type X.RB ` e ' Xin response to this question, Xwhich will get you yet another question X.PP X.B " Do you really want to quit the game? [ny]" X.PP Xthe default answer being X.RB ` n ' Xfor no. X.PP XThe only other question the game will ask you is X.PP X.B " Play another game? [y]" X.PP Xwhich is asking you if you'd like to try it all over again. XYou have to type X.RB ` y ' Xor carriage return to mean yes. X.SH "SCORING COMBINATIONS" XThe following is a description of each of the X.I cubes Xscoring combinations. XBelow, the word ``face'' is used as shorthand for face value. XFor a one, the face value is ten. XFor all other faces, the face value is the same as the number of spots Xon the face, that is, two for a two, three for a three, etc. X.IP Points 12 XDescription X.IP "300 * face" X.I Five of a Kind Xis the only combination that includes previously held dice. X.I Five of a Kind Xis when all five dice show the same face. XThis combination can occur any time the dice are rolled X(assuming that any held dice are showing the same face). XSince held dice are part of the new combination, Xto not be counted twice, Xthe points for the old combination(s) are forgotten. XAll five dice score. X.IP 1500 XA X.I Straight Xis when all five dice are rolled to produce an uninterrupted sequence of Xfive numbers, either one through five or two through six. XAll five dice score. X.IP "200 * face" X.I Four of a Kind Xis when four rolled dice show the same face. XThis combination can occur only when four or five dice are rolled. XFour dice score. X.IP 400 XA X.I Small Straight Xis an uninterrupted sequence of four numbers. XThe possibilities are one through four, two through five, or three through six. XThis combination can occur only when four or five dice are rolled. XFour dice score. X.IP "100 * face" X.I Three of a Kind Xis three rolled dice showing the same face. XThis combination can occur when three, four, or five dice are rolled. XThree dice score. X.IP 100 XAny X.IR One , Xnot part of another combination, is a scoring combination unto itself. XA X.I One Xcan occur at any time. XEach X.I One Xis a scoring die. X.IP 50 XAny X.IR Five , Xnot part of another combination, is a scoring combination unto itself. XA X.I Five Xcan occur at any time. XEach X.I Five Xis a scoring die. X.SH "ROLLING THE DICE" XEach turn starts with the player rolling all five dice. XWhenever the dice are rolled, if no scoring combination appears, Xthe turn is over and any points accumulated during the turn are lost. XIf some dice score, Xthen the player can choose to roll again or to quit rolling. XWhen the player quits, the points accumulated during the turn are Xadded to the player's score. XThis is the only way to score points. X.PP XWhen rerolling, all nonscoring dice are always part of the reroll. XIf more than one scoring combination occurred on the prior roll, Xthe player may also choose to reroll some scoring dice, Xwith the restriction that at least one scoring combination must be held back. XWhen scoring dice are rerolled, Xthe points for the combinations they were part of are not added to Xthe accumulated turn points. X.PP XWhen all five dice score, either in a single roll or in multiple rolls, Xthe player may continue rolling with all five dice. XThis is the same as beginning the turn afresh, Xexcept that the turn points earned so far are retained (but are still Xvulnerable to being lost). X.PP XA player must quit rolling at some time in order to earn points on the Xscoreboard. XThere are only two times when the player cannot choose to quit. XThe first time is when the player has zero points, or in other words, Xis not yet on the scoreboard. XThe player must score at least 500 points in a turn to get X.I On Board Xand cannot quit before earning those points. XSimilarly, to get X.IR Off \ Board , Xor cross the 10,000 point X.I Win Score Xline, the player needs at least 500 turn points. XThis threshold need be matched only when trying to cross 10,000 points; Xonce beyond, the player can quit with any number of turn points. X.SH "WINNING THE GAME" XA player with 10,000 points or more at the end of a round of Xturns is the winner, but only if that player's score exceeds Xthe next best score by a X.I Win Margin Xof 250 points. XWhile this condition is not met, play continues. X.I Cubes Xwill warn of a player's impending win. X.SH "COMPUTER PLAYERS" XSince it would be pointless to play the game alone, Xthe cube server provides some automatic competition in the form of one or Xmore computer players. XOne such player, X.BR CUBEX , Xplays in every game, Xbut depending on the number of human players and some random factors, Xone or more additional computer players may also join. XEach computer player has its own distinct playing style that Xdoes not vary from game to game. XTheir competence ranges from mildly so to expert, Xbut no matter how it may seem at times, Xthe game is not rigged in their favor. X.SH "SCORE FILE" XThe cube server keeps a score file which contains information about each Xhuman and computer player that has played X.I cubes Xon that system. XRegardless of the name chosen to display on the scoreboard, Xhuman players are recorded in the score file by login name. XComputer players are recorded by their one and only name. XAn auxiliary shell script called X.I cuberank Xis provided to display the contents of this system's X.I cubes Xscore file. X.PP XIt should be noted that quitting a game before it ends may result in a Xlow score being added to your record. XThis will not happen if you have zero points when you quit. XQuitting at any other time could lower your ranking against other players. X.SH MISCELLANY XIt is possible to join a game in progress. XYou will initially enter the game as an unseen observer, Xand if the game is not too near ending X(defined as the high score being 9,000 points or better), Xyou will be added as an active player at the end of the current round. XSince coming into a game late with no points could ruin your ranking, Xthe cube server will give you an initial score of about 75% of the lowest score, Xbut you still must meet the 500 turn point X.I On Board Xrequirement in order to begin advancing your score. X.PP XOn some systems, Xit is possible to tell the state of the cube server by doing a X.IR ps ( 1 ) X(or something similar) on it. XIf a game is in progress, Xthe command line of the process will have been altered to Xshow the number of players, turn number, high score, and player up. X.PP XThere is some advantage to being the last player in the turn order. XWhen a game is begun after the cube server has been idle, Xthe players will be ordered in reverse-rank order (top-ranked player last). XIn subsequent games, players will be ordered based on the score from Xthe previous game, with best score last. X.PP XA control-Z (or whatever your suspend character is) will not cause X.I cubes Xto be suspended. XSuspending your copy of the game would tie up other players. XControl-Z is not without effect however; Xtyping it will cause your screen to be cleared and the program Xto enter a ``quiet'' mode. XAnother control-Z will restore the program to its normal operation. X.PP XTo keep the game moving, Xthere is a sixty second timeout on every question asked by X.IR cubes . XAlthough the program tries to handle timeouts gracefully, Xthe best you can expect is that you will miss a turn Xif you wait too long to roll. X.PP XComputer players, like some human players, Xtend to get frustrated when they can't get on the board. XOccasionally, computer players will quit the game with zero points. X.BR CUBEX , Xbeing oblivious to other players, Xand possessing a singularly strong desire to play X.IR cubes , Xwill never quit the game. X.PP XSometimes computer proxies will take over for players who have quit Xthe game (when and why this happens is a mystery). XThese proxies play with a somewhat erratic style, Xso they should be almost indiscernible from humans. XNo notice is given to anybody when this happens. XThe absentee player will be credited with the score that the proxy Xmanages to attain by game end. X.PP XWhen rerolling, Xthere are some synonyms for the four commands; Xyou may find some more convenient to type. XFor hold, Xthe synonyms are X.RB ` q ' Xfor quit Xand X.RB ` n ' Xfor no (don't roll again). XFor roll, Xthe synonyms are X.RB ` t ' Xfor throw and X.RB ` y ' Xfor yes (roll again). XFor keep there is a single alias, X.RB ` s ' Xfor save. XYou may also type X.RB ` x ' Xto mean exit. X.SH HISTORY XThis game is an adaptation of a ``traditional'' dice game the author learned Xfrom a friend twelve years prior to writing this program. XIf the name of the game was learned at the time, it has since been forgotten. XThe name X.I cubes Xwas coined by the author as the name of this computer implementation of Xthe game. XIn the process of fine tuning the game of X.IR cubes , Xsome liberties have been taken with the rules as learned. XWhat follows is a list of differences between X.I cubes Xand the original (name unknown) dice game. X.IP (1) 4 XThe 500 point X.I Off Board Xthreshold did not exist in the original. XNo minimum turn score was necessary to cross 10,000 points. X.IP (2) XThe 250 point X.I Win Margin Xdid not exist in the original. XThe player with the highest score (at or above 10,000, of course) Xat the end of the round was the winner. X.IP (3) XThe scoring combination of X.I Four of a Kind Xdid not exist in the original. X.IP (4) XThe scoring combination X.I Small Straight Xdid not exist in the original. X.IP (5) XIn the original, X.I Five of a Kind Xcould be scored only in ones or fives. X(Even in X.IR cubes , Xscoring X.I Five of a Kind Xin anything other than ones or fives is rare.) X.IP (6) XAnd of course, in the original game, there were no computer players! X.SH AUTHOR XGreg Paris <gmp@rayssd.ray.com> END_OF_cubes.6 if test 13761 -ne `wc -c <cubes.6`; then echo shar: \"cubes.6\" unpacked with wrong size! fi # end of overwriting check fi if test -f dieopts.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"dieopts.c\" else echo shar: Extracting \"dieopts.c\" \(12872 characters\) sed "s/^X//" >dieopts.c <<'END_OF_dieopts.c' X/* vi:set sw=4 ts=4: */ X#ifndef lint Xstatic char sccsid[] = "@(#)dieopts.c 1.1 6/2/88 Copyright 1988 Gregory M. Paris"; X#endif lint X X#include <strings.h> X#include "cubes.h" X X/* X** names: names of die faces, singular and plural X** Must be filled in to at least max of SIDES and NDICE! X*/ Xchar *numnames[][2] = { X { "zero", "zeroes" }, X { "one", "ones" }, X { "two", "twos" }, X { "three", "threes" }, X { "four", "fours" }, X { "five", "fives" }, X { "six", "sixes" }, X}; X X/* X** initdice: initialize dice status X*/ Xinitdice(pd) Xregister diceset *pd; X{ X register int d; X X for(d = 0;d < NDICE;++d) { X pd->d_stat[d] = Free; /* rolling this die */ X pd->d_face[d] = BADFACE; /* impossible value */ X pd->d_comb[d] = Nothing; /* no combination */ X } X pd->d_best = Nothing; /* no combination */ X pd->d_again = True; /* rolling again */ X pd->d_rolling = NDICE; /* rolling all dice */ X pd->d_pts_roll = 0; /* no points rolled */ X pd->d_pts_dice = 0; /* no points in set */ X pd->d_pts_turn = 0; /* no points in turn */ X pd->d_mesg[0] = '\0'; /* no message */ X} X X/* X** pickup: similar to initdice but only does initialization X** for re-roll of (usually partial) dice set X** X** non-scoring dice are Freed X** change Rolled dice to Free X** set Free dice faces to BADFACE and combs to Nothing X** change Taken dice to Held X** set Held dice combs to Previous X** resets d_rolling, d_best, d_pts_roll, and d_mesg X*/ Xpickup(pd) Xregister diceset *pd; X{ X register int d; X X pd->d_best = Nothing; X pd->d_again = True; X pd->d_rolling = 0; X pd->d_pts_roll = 0; X pd->d_mesg[0] = '\0'; X X for(d = 0;d < NDICE;++d) { X if(pd->d_comb[d] == Nothing) X pd->d_stat[d] = Free; X switch(pd->d_stat[d]) { X case Rolled: X pd->d_stat[d] = Free; /* fall */ X case Free: X pd->d_face[d] = BADFACE; X pd->d_comb[d] = Nothing; X ++pd->d_rolling; X break; X case Taken: X pd->d_stat[d] = Held; /* fall */ X case Held: X pd->d_comb[d] = Previous; X break; X } X } X X /* X ** If no dice are free, then free them all. X */ X if(pd->d_rolling == 0) { X for(d = 0;d < NDICE;++d) { X pd->d_stat[d] = Free; X pd->d_face[d] = BADFACE; X pd->d_comb[d] = Nothing; X } X pd->d_rolling = NDICE; X pd->d_pts_dice = 0; X } X} X X/* X** rolldice: roll free dice X** sets d_rolling to number of dice rolled X** sets d_again to False X*/ Xrolldice(pd) Xregister diceset *pd; X{ X register int d; X X pd->d_again = False; X pd->d_best = Nothing; X pd->d_rolling = 0; X for(d = 0;d < NDICE;++d) { X if(pd->d_stat[d] == Free) { X pd->d_stat[d] = Rolled; X pd->d_face[d] = dieroll(SIDES); X pd->d_comb[d] = Nothing; X ++pd->d_rolling; X } X } X} X X/* X** evaluate: categorize dice marked as Rolled or Taken X*/ Xevaluate(pd) Xregister diceset *pd; X{ X register int d, s; X int rolled, rshow[SIDES+1]; X int held; X int minshow, maxshow; X int len, aces, fives; X boolean ok; X X for(s = 1;s <= SIDES;++s) X rshow[s] = 0; X X pd->d_mesg[0] = '\0'; X pd->d_best = Nothing; X rolled = held = 0; X for(d = 0;d < NDICE;++d) { X s = pd->d_face[d]; X switch(pd->d_stat[d]) { X case Rolled: X case Taken: X pd->d_comb[d] = Nothing; X ++rolled, ++rshow[s]; X break; X case Held: X pd->d_comb[d] = Previous; X ++held; X break; X default: X pd->d_stat[d] = Free; X pd->d_comb[d] = Nothing; X break; X } X } X X /* X ** Check for All_of_a_kind. All dice count. X ** Uses all dice. X */ X if((rolled + held) == NDICE) { X s = pd->d_face[0]; X for(d = 1;d < NDICE;++d) X if(pd->d_face[d] != s) X break; X if(d == NDICE) { X for(d = 0;d < NDICE;++d) X pd->d_comb[d] = All_of_a_kind; X pd->d_best = All_of_a_kind; X sprintf(pd->d_mesg, "%s of a kind in %s", X NUMBER(NDICE), FACE(pd->d_face[0], NDICE)); X return; X } X } X X /* X ** Check for Straight. Only rolled dice count. X ** Uses all dice. X */ X if(rolled == NDICE) { X ok = True, minshow = SIDES + 1, maxshow = 0; X for(s = 1;ok == True && s <= SIDES;++s) { X switch(rshow[s]) { X case 1: X if(s < minshow) minshow = s; X if(s > maxshow) maxshow = s; X break; X case 0: break; X default: ok = False; break; X } X } X X if(ok == True && maxshow - minshow == NDICE - 1) { X for(d = 0;d < NDICE;++d) X pd->d_comb[d] = Straight; X pd->d_best = Straight; X strcpy(pd->d_mesg, "a straight"); X return; X } X } X X#ifdef FOUR X /* X ** Check for Four_of_a_kind. Only rolled dice count. X ** Assumes that only one is possible. X */ X if(rolled >= FOUR) { X for(s = 1;s <= SIDES;++s) { X while(rshow[s] >= FOUR) { X maxshow = FOUR; X for(d = 0;d < NDICE && maxshow > 0;++d) { X switch(pd->d_stat[d]) { X case Rolled: X case Taken: X if(pd->d_comb[d] == Nothing && pd->d_face[d] == s) { X pd->d_comb[d] = Four_of_a_kind; X --rshow[s]; X --maxshow; X } X break; X } X } X X if(pd->d_best == Nothing) X pd->d_best = Four_of_a_kind; X X if((len = strlen(pd->d_mesg)) > 0) { X strcat(pd->d_mesg, ", "); X len += 2; X } X sprintf(pd->d_mesg + len, "%s of a kind in %s", X NUMBER(FOUR), FACE(s, FOUR)); X } X } X } X#endif FOUR X X#ifdef SMSTR X /* X ** Check for Small_straight. Only rolled dice count. X ** Assumes that only one is possible. X */ X if(rolled >= NDICE - 1) { X ok = False; X minshow = maxshow = 0; X for(s = 1;ok == False && s <= SIDES;++s) { X switch(rshow[s]) { X case 1: case 2: X if(minshow == 0) X minshow = maxshow = s; X else if((maxshow = s) - minshow == NDICE - 2) X ok = True; X break; X case 0: minshow = maxshow = 0; break; X default: s = SIDES; break; X } X } X X if(ok == True) { X for(s = minshow;s <= maxshow;++s) { X for(ok = True, d = 0;ok == True && d < NDICE;++d) { X switch(pd->d_stat[d]) { X case Rolled: X case Taken: X if(pd->d_comb[d] == Nothing && pd->d_face[d] == s) { X pd->d_comb[d] = Small_straight; X --rshow[s]; X ok = False; /* got it */ X } X break; X } X } X } X if(pd->d_best == Nothing) X pd->d_best = Small_straight; X if(pd->d_mesg[0] != '\0') X strcat(pd->d_mesg, ", "); X strcat(pd->d_mesg, "a small straight"); X } X } X#endif SMSTR X X /* X ** Check for Three_of_a_kind. Only rolled dice count. X ** Code is supposed to be able to handle more than one X ** Three_of_a_kind, but is untested with current NDICE val. X */ X if(rolled >= THREE) { X for(s = 1;s <= SIDES;++s) { X while(rshow[s] >= THREE) { X maxshow = THREE; X for(d = 0;d < NDICE && maxshow > 0;++d) { X switch(pd->d_stat[d]) { X case Rolled: X case Taken: X if(pd->d_comb[d] == Nothing && pd->d_face[d] == s) { X pd->d_comb[d] = Three_of_a_kind; X --rshow[s]; X --maxshow; X } X break; X } X } X X if(pd->d_best == Nothing) X pd->d_best = Three_of_a_kind; X X if((len = strlen(pd->d_mesg)) > 0) { X strcat(pd->d_mesg, ", "); X len += 2; X } X sprintf(pd->d_mesg + len, "%s of a kind in %s", X NUMBER(THREE), FACE(s, THREE)); X } X } X } X X /* X ** Check for stray Aces and Fives. Only rolled dice count. X */ X aces = fives = 0; X for(d = 0;d < NDICE;++d) { X switch(pd->d_stat[d]) { X case Rolled: X case Taken: X if(pd->d_comb[d] == Nothing) { X switch(pd->d_face[d]) { X case ACE: X ++aces; X pd->d_comb[d] = Ace; X if(pd->d_best == Nothing || pd->d_best == Five) X pd->d_best = Ace; X break; X case FIVE: X ++fives; X pd->d_comb[d] = Five; X if(pd->d_best == Nothing) X pd->d_best = Five; X break; X } X } X break; X } X } X X if(pd->d_best != Nothing) { X if(aces > 0) { X if((len = strlen(pd->d_mesg)) > 0) { X strcat(pd->d_mesg, ", "); X len += 2; X } X sprintf(pd->d_mesg + len, "%s %s", X NUMBER(aces), FACE(ACE, aces)); X } X X if(fives > 0) { X if((len = strlen(pd->d_mesg)) > 0) { X strcat(pd->d_mesg, ", "); X len += 2; X } X sprintf(pd->d_mesg + len, "%s %s", X NUMBER(fives), FACE(FIVE, fives)); X } X } X X if(pd->d_best == Nothing) X strcpy(pd->d_mesg, "nothing"); X} X X/* X** keepall: mark all scoring dice (based on combinations) as Taken X** frees Nothing dice and marks as Held Previous dice X** sets d_rolling to number of Free dice X** does not alter d_again or d_pts_???? X*/ Xkeepall(pd) Xregister diceset *pd; X{ X register int d; X X pd->d_rolling = 0; X for(d = 0;d < NDICE;++d) { X switch(pd->d_comb[d]) { X case Previous: X pd->d_stat[d] = Held; X break; X case Nothing: X pd->d_stat[d] = Free; X ++pd->d_rolling; X break; X default: X pd->d_stat[d] = Taken; X break; X } X } X} X X/* X** freeall: mark all non-held dice (based on combinations) as Free X** sets d_rolling to number of Free dice X** does not alter d_again or d_pts_???? X*/ Xfreeall(pd) Xregister diceset *pd; X{ X register int d; X X pd->d_rolling = 0; X for(d = 0;d < NDICE;++d) { X switch(pd->d_comb[d]) { X case Previous: X pd->d_stat[d] = Held; X break; X default: X pd->d_stat[d] = Free; X ++pd->d_rolling; X break; X } X } X} X X/* X** scoredice: update points values after roll and decision X*/ Xscoredice(pd) Xregister diceset *pd; X{ X register int d, dd, n; X register int points; X int face; X combination comb[NDICE]; X X /* X ** The easiest of all is the Nothing combination. The points for the roll X ** are the negative of the points accumulated so far in this turn. X */ X if(pd->d_best == Nothing) { X pd->d_pts_roll = -pd->d_pts_turn; X pd->d_pts_dice = pd->d_pts_turn = 0; X return; X } X X /* X ** A Straight can only occur when all dice are being rolled, so d_pts_roll X ** and d_pts_dice should be zero. The value of a Straight is a constant. X */ X if(pd->d_best == Straight) { X points = P_STRAIGHT; X pd->d_pts_roll = points; X pd->d_pts_dice = points; X pd->d_pts_turn += points; X return; X } X X /* X ** All_of_a_kind can occur at any time and changes the value of any saved X ** dice. The value of d_pts_dice must be used to update d_pts_roll and X ** d_pts_turn. Further, the value of All_of_a_kind depends on its X ** denomination. X */ X if(pd->d_best == All_of_a_kind) { X face = pd->d_face[0]; X points = P_AOKMULT * (face == ACE ? P_ACEMULT : face); X pd->d_pts_roll = points - pd->d_pts_dice; X pd->d_pts_turn += pd->d_pts_roll; X return; X } X X /* X ** The rest of the combinations can occur in concert. X */ X pd->d_pts_roll = 0; X X#ifdef FOUR X /* X ** Four_of_a_kind is tricky. The value is dependent on the denomination, X ** but not all dice are used up. Even worse, for the sake of generality, X ** we must assume that there could be more than one Four_of_a_kind. X ** This code assumes that Four_of_a_kind and Small_straight will X ** not occur at the same time. X */ X if(pd->d_best == Four_of_a_kind) { X for(d = 0;d < NDICE;++d) X comb[d] = pd->d_comb[d]; X for(d = 0;d < NDICE;++d) { X if(comb[d] == Four_of_a_kind) { X /* X ** Award the points for this one. X */ X face = pd->d_face[d]; X points = P_4OKMULT * (face == ACE ? P_ACEMULT : face); X pd->d_pts_roll += points; X pd->d_pts_dice += points; X pd->d_pts_turn += points; X X /* X ** Obliterate the scoring dice. X */ X for(n = FOUR, dd = d;dd < NDICE;++dd) { X if(comb[dd] != Four_of_a_kind) X continue; X if(pd->d_face[dd] != face) X continue; X comb[dd] = Nothing; X if(--n == 0) X break; X } X } X } X } X#endif FOUR X X#ifdef SMSTR X /* X ** Score a Small_straight. This code assumes that Four_of_a_kind X ** (or Three_of_a_kind) and Small_straight will not occur at the X ** same time. It also assumes that only one Small_straight can X ** occur at a time. X */ X if(pd->d_best == Small_straight) { X points = P_SMSTR; X pd->d_pts_roll += points; X pd->d_pts_dice += points; X pd->d_pts_turn += points; X } X#endif SMSTR X X /* X ** Three_of_a_kind is tricky. The value is dependent on the denomination, X ** but not all dice are used up. Even worse, for the sake of generality, X ** we must assume that there could be more than one Three_of_a_kind. X ** Assumes that Three_of_a_kind does not occur in conjunction with X ** Four_of_a_kind or Small_straight. X */ X if(pd->d_best == Three_of_a_kind) { X for(d = 0;d < NDICE;++d) X comb[d] = pd->d_comb[d]; X for(d = 0;d < NDICE;++d) { X if(comb[d] == Three_of_a_kind) { X /* X ** Award the points for this one. X */ X face = pd->d_face[d]; X points = P_3OKMULT * (face == ACE ? P_ACEMULT : face); X pd->d_pts_roll += points; X pd->d_pts_dice += points; X pd->d_pts_turn += points; X X /* X ** Obliterate the scoring dice. X */ X for(n = THREE, dd = d;dd < NDICE;++dd) { X if(comb[dd] != Three_of_a_kind) X continue; X if(pd->d_face[dd] != face) X continue; X comb[dd] = Nothing; X if(--n == 0) X break; X } X } X } X } X X /* X ** Finally we have Aces and Fives. Easy. X */ X for(d = 0;d < NDICE;++d) { X switch(pd->d_comb[d]) { X case Ace: X points = P_ACE; X pd->d_pts_roll += points; X pd->d_pts_dice += points; X pd->d_pts_turn += points; X break; X case Five: X points = P_FIVE; X pd->d_pts_roll += points; X pd->d_pts_dice += points; X pd->d_pts_turn += points; X break; X default: X break; X } X } X} END_OF_dieopts.c if test 12872 -ne `wc -c <dieopts.c`; then echo shar: \"dieopts.c\" unpacked with wrong size! fi # end of overwriting check fi if test -f turn.c -a "${1}" != "-c" ; then echo shar: Will not over-write existing file \"turn.c\" else echo shar: Extracting \"turn.c\" \(10360 characters\) sed "s/^X//" >turn.c <<'END_OF_turn.c' X/* vi:set sw=4 ts=4: */ X#ifndef lint Xstatic char sccsid[] = "@(#)turn.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 "cubes.h" X Xextern int turnnum; Xextern player plr[]; Xstatic boolean reallyquit(); X X/* X** turn: do a single player's turn X** return turn score or -1 if player left game X*/ Xturn(c) Xregister int c; X{ X diceset dice; X X /* X ** Announce who's up to the world. X */ X if(annturn(c) < 0) X return -1; X X /* X ** Reset the dice to their initial state. X */ X initdice(&dice); X showdice(c, &dice); X if(plr[c].p_stat != Computer) X if(pfirst(c) == -2) /* timeout */ X return 0; /* missed turn */ X if(plr[c].p_stat == Computer) X if(cfirst(c) < 0) X return -1; X X for(;;) { X /* X ** Roll the dice, evaluate the roll and display it to all players. X ** If Nothing rolled, then this turn is over with no points scored. X */ X rolldice(&dice); X evaluate(&dice); X if(dice.d_best == Nothing) { X scoredice(&dice); X showdice(c, &dice); X sleep(2); X return 0; X } X showdice(c, &dice); X X /* X ** If something was scored on the roll, then we ask the player what X ** to do about it. We must re-evaluate and redisplay the roll after X ** asking since the scoring combinations may have changed. We check X ** for Nothing scored as a precaution, but that situation is not X ** allowed by cquery or pquery. Adjust point values. X */ X if(plr[c].p_stat != Computer) X if(pquery(c, &dice) == -2) /* timeout */ X return 0; /* missed turn */ X if(plr[c].p_stat == Computer) X cquery(c, &dice); X evaluate(&dice); X scoredice(&dice); X if(dice.d_best == Nothing) { X fprintf(stderr, "turn: %s `%s' tried to roll all scoring dice\n", X plr[c].p_stat == Computer ? "Computer" : "Player", X plr[c].p_name); X showdice(c, &dice); X sleep(2); X return 0; X } X X /* X ** If the player held, we display the dice and return points scored. X */ X if(dice.d_again == False) { X strcpy(dice.d_mesg, "held"); X showdice(c, &dice); X return dice.d_pts_turn; X } X X /* X ** Reset and redisplay dice prior to next roll. X */ X pickup(&dice); X showdice(c, &dice); X } X} X X/* X** pquery: ask the player what to do next X** X** choices are: X** EXit leave the game X** Hold/Quit end turn (no more rolling) X** Keep/Save score named dice and roll again X** Roll/Throw throw named dice (scoring those left) X*/ Xpquery(c, pd) Xint c; Xregister diceset *pd; X{ X register int n, d; X int stay; X boolean error; X char msgbuf[MESGLEN]; X char cmd[32], digits[32]; X diceset save, temp; X X /* X ** If we got passed a nothing roll then we wonder why we are here X ** and return with a little comment. X */ X if(pd->d_best == Nothing) { X fprintf(stderr, "pquery: got a nothing roll\n"); X sprintf(msgbuf, "%d You rolled a big zip.\r\n", M_INFO); X if(message(c, msgbuf) < 0) X return -1; X return 0; X } X X /* X ** Calculate the number of points if we hold now. X */ X temp = *pd; X scoredice(&temp); X stay = temp.d_pts_turn; X X save = *pd; /* save a copy of the dice */ X do { X error = False; /* no error yet */ X *pd = save; /* get back the saved version */ X X /* X ** Send prompt. Read and parse response. X */ X sprintf(msgbuf, "%d %d saved, %d showing; command? [rkhe]\r\n", X M_KEEP, pd->d_pts_turn, stay - pd->d_pts_turn); X if(message(c, msgbuf) < 0) X return -1; X switch(reply(c, msgbuf, sizeof msgbuf)) { X case -2: msgbuf[0] = '\0'; break; /* timeout, do default */ X case -1: return -1; /* left game */ X } X cmd[0] = '\0', digits[0] = '\0'; X (void) sscanf(msgbuf, "%[^0123456789]%[0123456789]", cmd, digits); X X switch(cmd[0]) { X case 'e': case 'E': /* Exit -- leave game */ X case 'x': case 'X': /* eXit -- leave game */ X if(reallyquit(c) == True) X return -1; X error = True; X break; X X case 'n': case 'N': /* No -- don't roll again */ X case 'h': case 'H': /* Hold -- hold at current score */ X case 'q': case 'Q': /* Quit -- quit rolling */ X pd->d_again = False; /* quit rolling */ X keepall(pd); /* keep all scoring dice */ X temp = *pd; /* temporary for score evaluation */ X scoredice(&temp); /* calculate turn points */ X if(plr[c].p_onboard == False) { /* not on board yet */ X if(temp.d_pts_turn < ONBOARD) { /* threshold */ X sprintf(msgbuf, X "%d You can't hold until you get %d points.\r\n", X M_ARGE, ONBOARD); X if(message(c, msgbuf) < 0) X return -1; X error = True; X break; X } X } else if(plr[c].p_score + temp.d_pts_turn >= WINSCORE X && plr[c].p_score < WINSCORE) { /* about to go off board */ X if(temp.d_pts_turn < OFFBOARD) { /* threshold */ X sprintf(msgbuf, X "%d You can't hold until you get %d points.\r\n", X M_ARGE, OFFBOARD); X if(message(c, msgbuf) < 0) X return -1; X error = True; X break; X } X } X return 0; /* all done */ X X case 'k': case 'K': /* Keep -- keep only named dice */ X case 's': case 'S': /* Save -- save only named dice */ X pd->d_again = True; /* roll again */ X if(digits[0] == '\0') { /* no dice named */ X keepall(pd); /* default is to keep all */ X return 0; /* all done */ X } X freeall(pd); /* free all rolled dice */ X for(n = 0;digits[n] != '\0';++n) { X if((d = digits[n] - '1') < 0 || d >= NDICE) { X sprintf(msgbuf, "%d Die number is out of range.\r\n", X M_ARGE); X if(message(c, msgbuf) < 0) X return -1; X error = True; X break; X } X if(pd->d_comb[d] == Nothing) { X sprintf(msgbuf, X "%d You can keep only scoring dice.\r\n", M_ARGE); X if(message(c, msgbuf) < 0) X return -1; X error = True; X break; X } X if(pd->d_comb[d] == Previous || pd->d_stat[d] != Free) { X sprintf(msgbuf, X "%d You can keep only rolled dice.\r\n", M_ARGE); X if(message(c, msgbuf) < 0) X return -1; X error = True; X break; X } X pd->d_stat[d] = Taken; X } X break; X X case '\0': /* default command */ X case 'y': case 'Y': /* Yes -- roll again */ X case 'r': case 'R': /* Roll -- roll named dice and non-scoring */ X case 't': case 'T': /* Throw -- throw named dice and non-scoring */ X pd->d_again = True; /* roll again */ X if(digits[0] == '\0') { /* no dice named */ X keepall(pd); /* default is to keep all */ X return 0; /* all done */ X } X keepall(pd); /* keep all rolled, scoring dice */ X for(n = 0;digits[n] != '\0';++n) { X if((d = digits[n] - '1') < 0 || d >= NDICE) { X sprintf(msgbuf, "%d Die number is out of range.\r\n", X M_ARGE); X if(message(c, msgbuf) < 0) X return -1; X error = True; X break; X } X if(pd->d_comb[d] == Previous || pd->d_stat[d] == Held) { X sprintf(msgbuf, X "%d You can't roll previously held dice.\r\n", M_ARGE); X if(message(c, msgbuf) < 0) X return -1; X error = True; X break; X } X if(pd->d_stat[d] != Free) { X pd->d_stat[d] = Free; X ++pd->d_rolling; X } X } X break; X X default: X sprintf(msgbuf, X "%d Valid commands are Exit, Hold, Keep, Roll.\r\n", M_ARGE); X if(message(c, msgbuf) < 0) X return -1; X error = True; X break; X } X X /* X ** If we've arrived here without error, then a subset of the rolled X ** dice was chosen. Get a temporary copy of the roll as it stands X ** and evaluate it. If nothing scored it's an error. X */ X if(error == False) { X temp = *pd; X evaluate(&temp); X if(temp.d_best == Nothing) { X sprintf(msgbuf, X "%d You must save a scoring combination.\r\n", M_ARGE); X if(message(c, msgbuf) < 0) X return -1; X error = True; X } X } X } while(error == True); X X return 0; X} X X/* X** annturn: announce who's turn it is X*/ Xannturn(c) Xint c; X{ X char msgbuf[MESGLEN]; X X if(plr[c].p_stat != Computer) { X sprintf(msgbuf, X "%d Turn %d: player %d, you are up.\r\n", M_TURN, turnnum, c); X if(message(c, msgbuf) < 0) X return -1; X } X sprintf(msgbuf, "%d Turn %d: player %d, %s, is up.\r\n", X M_TURN, turnnum, c, plr[c].p_name); X (void) announce(c, msgbuf); X return 0; X} X X/* X** statchar: map die status to a single character X*/ Xstatchar(s) Xdiestat s; X{ X switch(s) { X case Free: return 'f'; X case Held: return 'h'; X case Taken: return 't'; X case Rolled: return 'r'; X default: return '?'; X } X} X X/* X** facechar: map die face to a single character X*/ Xfacechar(f) Xint f; X{ X return (f == BADFACE) ? '*' : (f + '0'); X} X X/* X** combchar: map die combination to a single character X*/ Xcombchar(c) Xcombination c; X{ X switch(c) { X case Previous: return 'p'; X case Nothing: return 'n'; X case Five: return '5'; X case Ace: return '1'; X case Three_of_a_kind: return 't'; X case Small_straight: return 'l'; /* l for little */ X case Four_of_a_kind: return 'f'; X case Straight: return 's'; X case All_of_a_kind: return 'a'; X default: return '?'; X } X} X X/* X** showdice: present the current status of the dice to all players X*/ Xshowdice(c, pd) Xint c; Xregister diceset *pd; X{ X register int d, cc; X char stats[NDICE+1]; X char faces[NDICE+1]; X char combs[NDICE+1]; X char msgbuf[MESGLEN+4*NDICE+32]; X X for(d = 0;d < NDICE;++d) { X stats[d] = statchar(pd->d_stat[d]); X faces[d] = facechar(pd->d_face[d]); X combs[d] = combchar(pd->d_comb[d]); X } X stats[d] = faces[d] = combs[d] = '\0'; X X sprintf(msgbuf, "%d %d %s %s %s %d %d %d %s\r\n", X M_DICE, c, stats, faces, combs, X pd->d_pts_roll, pd->d_pts_dice, pd->d_pts_turn, X pd->d_mesg); X X for(cc = 0;cc < PLAYERS;++cc) { X switch(plr[cc].p_stat) { X case Active: X case Waiting: X (void) message(cc, msgbuf); X break; X } X } X} X X/* X** pfirst: query player for first roll of dice X*/ Xpfirst(c) Xint c; X{ X char msgbuf[MESGLEN]; X int n; X X sprintf(msgbuf, "%d Ready to roll? [ye]\r\n", M_RFST); X if(message(c, msgbuf) < 0) X return -1; X if((n = reply(c, msgbuf, sizeof msgbuf)) < 0) X return n; X X switch(msgbuf[0]) { X case 'e': case 'E': X case 'x': case 'X': X if(reallyquit(c) == True) X return -1; X break; X } X X return 0; X} X X/* X** reallyquit: does player really want to quit? X*/ Xstatic boolean Xreallyquit(c) Xint c; X{ X char msgbuf[128]; X X sprintf(msgbuf, "%d Do you really want to quit the game? [ny]\r\n", M_RFST); X if(message(c, msgbuf) < 0 || reply(c, msgbuf, sizeof msgbuf) < 0) X return True; X X if(msgbuf[0] == 'y' || msgbuf[0] == 'Y') { X sprintf(msgbuf, "%d Quitters never win.\r\n", M_DOWN); X if(message(c, msgbuf) >= 0) X oldplayer(c); X return True; X } X X return False; X} END_OF_turn.c if test 10360 -ne `wc -c <turn.c`; then echo shar: \"turn.c\" unpacked with wrong size! fi # end of overwriting check fi echo shar: End of archive 2 \(of 3\). cp /dev/null ark2isdone 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