[comp.sources.games] v06i061: cubes2 - a networked dice game

games@tekred.CNA.TEK.COM (04/28/89)

Submitted-by: gmp@rayssdb.RAY.COM (Gregory M. Paris)
Posting-number: Volume 6, Issue 61
Archive-name: cubes2/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 8)."
# Contents:  cubes.long.6 moniker.c tempers.c
# Wrapped by billr@saab on Thu Apr 27 12:13:36 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'cubes.long.6' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cubes.long.6'\"
else
echo shar: Extracting \"'cubes.long.6'\" \(26844 characters\)
sed "s/^X//" >'cubes.long.6' <<'END_OF_FILE'
X.TH CUBES.LONG 6 "cubes 5.1" GMP "UNIX Gaming Manual"
X.SH NAME
Xcubes.long \- the nitty-gritty details of the game of cubes
X.\"
X.\" sccsid: @(#)cubes.long.6 5.1 (G.M. Paris) 89/01/22
X.\"
X.\"
X.\"
X.\"	cubes 5.1  Copyright 1988 Gregory M. Paris
X.\"		Permission granted to redistribute on a no charge basis.
X.\"		All other rights are reserved.
X.\"
X.\"
X.SH SYNOPSIS
X.B cubes
X[
X.BR \- [ F ]{ BS }
X] [
X.B \-s
X] [
X.BR \- { pw }
X] [
X.B \-g graphics
X] [
X.B \-f cubehist
X] [
X.B \-h cubehost
X] [
X.B cubename
X]
X.SH OVERVIEW
X[ Note: This is the long and complicated manual page for
X.IR cubes ,
Xwhich exists for the purpose of detailing the finer points of the game.
XIf you are a beginner at
X.IR cubes ,
Xthe shorter manual page,
X.IR cubes (6),
Xshould be all you need to start playing the game. ]
X.PP
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
X.I Win Score
Xpoints (usually 10,000).
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 elect to quit rolling (prudently or cowardly),
Xcausing the points accumulated during the turn
Xto be added to the player's score.
XIt takes about twenty minutes to play a single 10,000 point game.
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 both a single-player and 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 nearly the same time, and must connect to the same server.
X.PP
XThere are two
X.I cubes
Xgame modes:
X.I Standard
Xand
X.IR Blitz .
XThe first corresponds to games played to 10,000 points;
Xthe second to shorter games played to 7,500 points.
XSupplying the
X.B \-B
Xor
X.B \-S
Xoption informs the server that you'd prefer to play
X.I Blitz
Xmode or
X.I Standard
Xmode games respectively.
XThe
X.B \-FB
X.RI ( Force \  Blitz )
Xor
X.B \-FS
X.RI ( Force \  Standard )
Xoption informs the server that you will play only
X.I Blitz
Xor
X.I Standard
Xmode games respectively.
XYour preference may be taken into account when the server
Xdecides the mode for each game.
XIf the server does not honor your preference
Xand if you specified one of the forced preferences,
Xthe server will reject you.
XIf you do not supply a preference option,
Xthe server assumes you have no preference.
X.PP
XIf
X.I cubes
Xis invoked with the
X.B \-s
X(spider mode) option,
Xand if there are no humans already playing,
Xthe program will hang,
Xwaiting for another human to begin a game.
X(You may wish to suspend
X.I cubes
Xat this point -- the program will alert you by
Xringing your terminal bell when a game is about to begin.)\ 
XIf you get tired of waiting for another player,
Xyou can quit waiting and begin a game by typing the letter
X.RB ` g '
Xfor `go'.
XYou also can quit waiting with an interrupt,
Xwhich will cause
X.I cubes
Xto exit, with no penalty to your ranking.
X.PP
XWhen a person enters a game already in progress
X(explained more fully below),
X.I cubes
Xwill add that person as a watcher.
XThe watcher can join the game using
X.RB ` g '
Xfor `go'.
XThis step can be skipped by supplying
X.RB ` \-p '
Xswitch on the command line, causing
X.I cubes
Xthe user to be added as a player, rather than as a watcher.
X.PP
XIf your terminal supports DEC or Zenith graphics mode,
Xyou can tell
X.I cubes
Xto take advantage of the graphics when drawing the screen.
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 one of
X.RB ` d '
Xfor DEC graphics,
Xor
X.RB ` z '
Xfor Zenith graphics.
XThe
X.B \-g
Xoption need not be supplied if your terminal's termcap
Xentry specifies the
X.RB ` Cg '
X.RI ( C ubes
X.IR g raphics)
Xstring capability to be a letter corresponding to
Xone of the valid graphics types
X.RB ( d 
Xor
X.BR z ).
X.PP
XAfter each completed game,
X.I cubes
Xappends a single line to the file ``.cubehist'' in
Xyour home directory.
XThe line provides a summary of what the game did to your
Xstanding with regard to other players of
X.IR cubes .
XIf you wish to change the location of this file,
Xyou may do so with the
X.RB ` \-f \  cubehist '
Xcommand line option,
Xor with the
X.B CUBEHIST
Xenvironment variable.
XThe command line option overrides the environment variable setting.
XTo disable the feature entirely, specify a null filename.
X.PP
XThe
X.I cubes
Xprogram assumes that the server is on the same system as the player.
XIf this is not so, the
X.RB ` \-h \  cubehost '
Xoption can be used to tell
X.I cubes
Xwhich system the server is running on.
XIf you normally connect to a server on another system,
Xyou may find it more convenient to set the
X.B CUBEHOST
Xenvironment variable to that system's name.
X(The command line option overrides the environment variable.)
X.PP
XIf you'd like your name during the game
Xto be something other than your login name,
Xyou can supply a new name on the command line
X(multi-word names need not be quoted).
XIf you have a favorite name,
Xyou can set the environment variable
X.B CUBENAME
Xto that value, which
X.I cubes
Xwill use when none is supplied on the command line.
XIn either case,
X.I cubes
Xwill display only the first eighteen characters of the name.
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 that for you.
XThe only thing to know before playing is what to do when prompted.
X.PP
XThe
X.I cubes
Xprogram will ask you only a few questions.
XThe first question of each turn is
X.PP
X.B "	Ready to roll? [yxe]"
X.PP
Xwhich is asking you to make the first roll of your turn.
XThe letters
X.RB ` yxe '
Xindicate that the three valid answers are
X.RB ` y '
Xfor yes (to roll), and
X.RB ` x '
Xto enter autopilot mode (see
X.B AUTOPILOT
Xbelow),
X.RB ` e '
Xfor exit, to quit the game (see
X.B QUITTING EARLY
Xbelow).
XThe default if you type just a carriage return is yes.
X.PP
XThe second question, of the form
X.PP
X.B "	nnn saved, mmm showing; command? [rkhmabpxe]"
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 type just a 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 always will be rolled,
Xeven if you don't 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
XSome players find it advantageous to check the score file
X(see
X.B THE SCORE FILE
Xbelow)
Xbefore deciding whether to roll again or to hold.
XFour commands are available for this purpose.
XThey are
X.RB ` m '
Xfor my rank,
X.RB ` a '
Xfor above me,
X.RB ` b '
Xfor below me,
Xand
X.RB ` p < number >'
Xfor player rank.
XThese commands will cause
X.I cubes
Xto display the score file information for you,
Xfor the player ranked immediately above you,
Xfor the player ranked immediately below you,
Xor for the selected player in the current game.
X.PP
XIf you are going to be temporarily interrupted,
Xyou might want enter autopilot mode by answering
Xwith
X.RB ` x '.
X.PP
XYou also can 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.
XYou should read the section titled
X.B QUITTING EARLY
Xbefore you answer
X.RB ` y '
Xto this question.
X.PP
XAt the conclusion of a game,
X.I cubes
Xwill prompt with
X.PP
X.B "	Play another game? [ynmabp]"
X.PP
Xif you played in the last game, or if you were watching
X.PP
X.B "	Play in next game? [ynmabp]"
X.PP
Xeither of which is asking you if you'd like to play in the next game.
XThere is no default answer to this question because it is too easy
Xto confuse this prompt with the "Ready to roll?" prompt,
Xwhich is answered almost invariably with a carriage return.
XThe
X.RB ` m ',
X.RB ` a ',
X.RB ` b ',
Xand
X.RB ` p '
Xcommands, as described above, are available for use at this prompt.
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,
Xand for a joker (see
X.I Joker
Xbelow) the face value is thirty.
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 all five dice showing the same face.
XThis combination can occur any time the dice are rolled
X(if 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/750
XA
X.I Straight
Xis an uninterrupted sequence of
Xfive numbers, either one through five or two through six.
XAll five dice score.
XIf previously held dice are used in making a
X.IR Straight,
Xit is worth 750 points.
XOtherwise, if all five dice were rolled to produce the
X.IR Straight,
Xit is worth 1500 points.
X.IP "200 * face"
X.I Four of a Kind
Xis four rolled dice showing 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.IP 0
XAny
X.IR Joker ,
Xnot part of another combination, is a scoring combination unto itself,
Xbut unlike the
X.I One
Xand
X.IR Five ,
Xa single
X.I Joker
Xis not worth any points. 
XHowever, the face value of a
X.I Joker
Xis a whopping thirty, thus making
X.IR Three ,
X.IR Four ,
Xand
X.I Five of a Kind
Xin
X.I Jokers
Xworth 3000, 6000, and 9000 points respectively.
X.B Note:
Xnot all cube servers will be configured to play
X.I cubes
Xwith dice that have
X.I Jokers
Xon them, and even if so configured,
Xwill not play all games with such dice.
XIf the game is to be played with
X.I Joker
Xdice, you will be notified before the game begins
X(or when you enter, if the game is in progress).
XYou may think of these dice as having thirteen sides,
Xwith two of each of the faces one through six,
Xand one
X.I Joker
Xface.
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 also may 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 to earn points on the scoreboard,
Xbut there are two times when the player cannot 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
X.I Win Score
Xline, the player needs at least 500 turn points.
XThis threshold need be matched only when trying to cross
X.I Win Score
Xpoints;
Xonce beyond, the player can quit with any number of turn points.
X.SH "WINNING THE GAME"
XA player with
X.I Win Score
Xpoints 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.PP
XNormally, 10,000 points is the
X.IR Win \  Score ,
Xor minimum score, needed to win a game.
XOn some systems, however, the cube server may be configured
Xto run in
X.I Blitz
Xmode during some parts of the day (usually working hours),
Xor when all players with a preference desire to play in
X.I Blitz
Xmode.
X(See
X.IR cubeserver (6)
Xfor more details.)
XIn a
X.I Blitz
Xmode game,
X.I Win Score
Xwill be less than 10,000 points,
Xusually 7,500 points (unless changed at compile time).
XA reduced
X.I Win Score
Xmakes for shorter, but riskier games
Xsince there's less time to catch up if you get behind,
Xand also because entries made to the score file are
Xscaled to 10,000 points \- thus making a loss by 500 points
Xinto a recorded loss by 750 points, if
X.I Win Score
Xis 7,500 points.
X(This effect cuts both ways, however, since a win
Xis also scaled up to being a bigger win.)\ 
XThe current
X.I Win Score
Xis always displayed on the screen,
Xand is set only at the beginning of each game.
X.SH "COMPUTER PLAYERS"
XSince it would be pointless to play the game alone,
Xthe cube server provides some automatic competition as one or
Xmore computer players.
XOne such player,
X.BR \s-2CUBEX\s+2 ,
Xplays in every game,
Xbut depending on the number of human players and some random factors,
Xone or more additional computer players also may join.
XEach computer player has its own distinct playing style that
Xdoes not vary from game to game.
XEach may also have a preference for
X.I Standard
Xor
X.I Blitz
Xmode games, though most have no preference.
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 "THE SCORE FILE"
XThe cube server keeps a score file that 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 program called
X.IR cuberank (6)
Xis provided to display the contents of this system's
X.I cubes
Xscore file.
X.PP
XAs a convenient mechanism for keeping track of your personal ranking
Xhistory,
X.I cubes
Xwill append your current ranking information to the file ".cubehist"
Xin your home directory
X(as specified by the
X.B HOME
Xenvironment variable)
Xafter each game played to completion.
X.SH "JOINING A GAME IN PROGRESS"
XA person may join a game in progress as either a new player,
Xor as an unnamed observer.
XIf the game is near ending
X(defined as the high score being 75% or more of the
X.IR Win \  Score ),
Xor if the
X.RB ` \-p '
Xcommand line option was not specified,
Xyou will be added to the game as an observer.
XWhen the current game ends,
Xwill be asked whether you wish to play in the next game.
XYou can join the game in progress with the
X.RB ` g '
Xcommand.
XIf you choose to play,
Xyou will be added as an active player at the end of the current round
Xand you will appear as the first player in the turn order.
XSince coming into a game late with no points could ruin your ranking,
Xthe cube server will give you an initial score
Xthat is 500 points less than the lowest score,
Xbut you still must meet the
X.I On Board
Xturn point requirement to begin advancing your score.
X.PP
XComputer players will sometimes join particularly close games in progress.
XChalk it up to love of excitement, if you must.
X.SH "QUITTING EARLY"
XIt is important to note that quitting a game before it ends
Xwill probably result in a low score being added to your record,
Xpossibly lowering your ranking against other players.
XExcept as described in the next paragraph,
Xif you quit a game before you get
X.IR On \  Board ,
Xyour average game point value,
X.I GamPt
X(see
X.IR cuberank (6)),
Xis penalized by being adjusted to the value you would have had if
Xyou had stayed in the game and lost with a score of half your average.
XThis adjustment is done by subtracting points from your cumulative point record;
Xyour game count and other records are not altered.
XExcept as described in the next paragraph,
Xif you quit a game when you are
X.IR On \  Board ,
Xyour current score is recorded in the score file.
X.PP
XIn games with more than one human player,
Xit is possible that a computer proxy will take over for
Xa player that quits the game early.
XThe chances of this happening are 20% if the exiting player is not
X.IR On \  Board ,
Xand 60% if the player is
X.IR On \  Board .
XProxies play with a somewhat erratic style,
Xso they should be almost indiscernible from humans.
XNo notice is given to anybody when a proxy takes over.
XThe absentee player will be credited with the score that the proxy
Xmanages to attain by game end (for good or bad).
X.PP
XThe cube server will not conduct a game that has no human players.
XTo meet that requirement,
Xwhen the only human player quits the game,
Xno proxy is assigned and the game is ended at that point.
XThe human player's score will be recorded in the score file
Xonly if that player was 
X.I On Board
Xat the time of quitting.
XThe scores of the computer players (proxies are included in this category)
Xare not recorded,
Xregardless of their
X.I On Board
Xstatus.
XIn short, if the only human player quits a game before getting
X.IR On \  Board ,
Xno record of the game is added to the score file,
Xalthough the human player will suffer the penalty described above.
X.PP
XUnlike previous versions of this game,
Xthere is no point at which it is safe to quit early.
XThere is always a risk, if not a certainty, of being penalized for quitting.
X.PP
XSome computer players, like cowardly human players,
Xquit rather than get
X.I On Board
Xvery far behind,
Xsuffering the same penalty that humans do when they quit.
XOnce the high score has reached 25% of the
X.IR Win \  Score ,
Xbefore each turn, computer players that are not
X.I On Board
Xdecide whether to continue in the game.
XIf the computer needs more points to reach its average
Xthan the leader needs to win, the computer may quit.
X.BR \s-2CUBEX\s+2 ,
Xbeing oblivious to other players,
Xand possessing a singularly strong desire to play
X.IR cubes ,
Xwill not quit the game.
XIt should be noted that proxy players also will not quit a game.
X.SH AUTOPILOT
XA game of
X.I cubes
Xusually takes about 20 minutes to play.
XStrangely, starting a game of
X.I cubes
Xseems to generate phone calls and visits from associates demanding
Xattention.
XTo allow players to deal with interruptions,
Xthere is an autopilot mechanism provided.
XThe command to invoke the autopilot is
X.RB ` x '
Xand can be used during your turn, or asynchronously.
XYou'll probably want to suspend
X.I cubes
Xwith a control-Z once you engage the autopilot.
XThe autopilot will play for you until you type
X.RB ` g '
Xfor go.
X.PP
XIt should be noted that the autopilot is not a very good player.
X.SH MISCELLANY
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.
XEven more useful is an auxiliary program, called
X.IR cubestat (6),
Xthat produces a more detailed report of the server status.
X.PP
XThe last player in the turn order has somewhat of an advantage.
XTherefore,
X.I cubes
Xuses player order as a reward for good playing.
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.
XAny player that joins a game in progress,
Xor begins in a game that contains players from a just ended game,
Xis put at the beginning of the turn order.
XIf more than one such player enters a game on the same turn,
Xthe new players will be added to the beginning of the turn order,
Xbut, amongst themselves, will be ordered in reverse-rank order.
X.PP
XA control-Z (or whatever your suspend character is) will cause
X.I cubes
Xto return you to the shell,
Xassuming that your shell supports job control.
XThe
X.I cubes
Xprocess will continue to run silently in the background,
Xbut will beep your terminal whenever you are being prompted.
XBring
X.I cubes
Xback to the foreground (usually by typing
X.RB ` fg ')
Xto resume play at the current point in the game.
X.PP
XShould the screen become garbled,
Xit can be redrawn by typing control-L
Xor, if you prefer, your terminal reprint character (usually control-R).
XThere may be a slight delay before
X.I cubes
Xresponds to the screen redraw request.
X.PP
XTo keep the game moving,
Xthere is a sixty second timeout on every question asked by
X.IR cubes .
XThe program will beep your terminal bell every twenty
Xseconds until the timeout is reached.
XAlthough the program tries to handle timeouts gracefully
Xand may engage the autopilot automatically
Xif you wait too long to roll.
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 rolling,
X.RB ` n '
Xfor no (don't roll again),
Xand
X.RB ` . '
X(period) for those who like to use a numeric keypad.
XFor roll,
Xthere is a synonym of
X.RB ` t '
Xfor throw.
XFor keep there is
X.RB ` s '
Xfor save,
Xand, for the numeric keypad crowd,
X.RB ` , '
X(komma, er rather, comma).
X.PP
XThe cube server uses a pseudorandom number generator to generate the die rolls.
XUnlike real life, it is possible to see what a player would have rolled
Xby looking at what the following player rolls.
XFor instance, if a player debates whether to roll three dice,
Xthen decides to hold, that player can check dice one, two, and three
Xon the following player's roll to see what would have been rolled.
XThis type of hindsight is not available in the physical world.
X.PP
XIf you wish to use a shell script to invoke
X.IR cubes ,
Xthe script must take into account that
X.I cubes
Xchanges the meaning of the interrupt and quit signals.
XYour shell script must not respond to these signals.
XOne way to accomplish this is to have your shell script
X.I exec cubes
X(see
X.IR sh (1)),
Xthus avoiding the problem altogether,
Xsince the shell script will ``vanish'' at the point of the
X.IR exec .
XBourne or Korn shell example:
X.PP
X.nf
X	exec /usr/games/cubes "$@"
X.fi
X.PP
XIf you have more processing that you wish to do after
X.I cubes
Xexits, the
X.I exec
Xmethod will not suffice.
XInstead, your shell script must
X.I trap
Xand do nothing with (but NOT ignore)
Xthe two signals in question.
XBourne or Korn shell example:
X.PP
X.nf
X	trap : INT QUIT
X	/usr/games/cubes "$@"
X	/usr/games/cuberank -n5
X.fi
X.PP
X(If you prefer to write your shell scripts using
X.IR csh (1),
Xyou're on your own...)
X.SH HISTORY
XThis game is an adaptation of a ``traditional'' dice game the author learned
Xfrom a friend twelve years before writing this program.
XIf the name of the game was learned at the time, it since has been forgotten.
XThe name
X.I cubes
Xwas coined as the name of this computer implementation of the 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
X.I Win Score
Xpoints.
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
X.IR Win \  Score ,
Xof 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)
XScoring a
X.I Straight
Xin multiple rolls has been added by popular demand.
XIt turns out that this combination adds quite a bit
Xmore strategy to the game.
X(Hint: compute the probabilities.)
X.IP (7)
XIn the original game, there were no computer players!
X.IP (8)
XQuitting as a tactic to preserve ranking is a seemingly
Xunavoidable side effect of this implementation of the game.
XIn the real life dice game, people usually don't up and quit,
Xeven if they start out way behind.
XThe quitting penalty is the fairest way, so far devised,
Xto mimic the peer pressure of a real life game.
X.SH "ENVIRONMENT VARIABLES"
X.DT
X.nf
XHOME		home directory
XUSER		login name
XCUBEHIST	default ranking history file
XCUBEHOST	default server host
XCUBENAME	default player name
X.fi
X.SH FILES
X.DT
X.nf
X$HOME/.cubehist	user's ranking history file
X.fi
X.SH "SEE ALSO"
Xcubes(6), cuberank(6), cubestat(6), cubeserver(6), ps(1), sh(1), termcap(5)
X.SH AUTHOR
XGreg Paris <gmp@rayssd.ray.com>
END_OF_FILE
if test 26844 -ne `wc -c <'cubes.long.6'`; then
    echo shar: \"'cubes.long.6'\" unpacked with wrong size!
fi
# end of 'cubes.long.6'
fi
if test -f 'moniker.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'moniker.c'\"
else
echo shar: Extracting \"'moniker.c'\" \(3417 characters\)
sed "s/^X//" >'moniker.c' <<'END_OF_FILE'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)moniker.c 5.1 (G.M. Paris) 89/01/22";
X#endif	lint
X
X/*
X**
X**	cubes 5.1  Copyright 1989 Gregory M. Paris
X**		Permission granted to redistribute on a no charge basis.
X**		All other rights are reserved.
X**
X*/
X
X#include	<stdio.h>
X#include	<syslog.h>
X#include	<strings.h>
X#include	"cubes.h"
X
X#ifdef	lint
X#ifndef	MONFILE
X#define	MONFILE	"dummy-monfile"
X#endif	MONFILE
X#endif	lint
X
Xstruct mon {
X	char		m_name[NAMELEN];
X	struct mon *m_next;
X};
X
X/*
X**	seed: names to seed the moniker list
X*/
Xstatic struct mon	seed[]	= {
X	{ "Ronald Reagan",		seed+ 1 },
X	{ "Nancy Reagan",		seed+ 2 },
X	{ "George Bush",		seed+ 3 },
X	{ "Barbara Bush",		seed+ 4 },
X	{ "Mikhail Gorbachev",	seed+ 5 },
X	{ "Raisa Gorbachev",	seed+ 6 },
X	{ "Benny Hill",			seed+ 7 },
X	{ "Margaret Thatcher", 	seed+ 8 },
X	{ "Piotr Chekov", 		seed+ 9 },
X	{ "Jamie Finney", 		seed+10 },
X	{ "Touche Turtle", 		seed+11 },
X	{ "Judy Jetson", 		seed+12 },
X	{ "Cobb Anderson", 		seed+13 },
X	{ "Della Taze", 		seed+14 },
X	{ "Mickey Mouse", 		seed+15 },
X	{ "Minnie Mouse", 		seed+16 },
X	{ "Edison Carter", 		seed+17 },
X	{ "Theora Jones",		0 },
X};
X
Xstatic int			seedcnt	= sizeof seed / sizeof seed[0];
Xstatic struct mon  *monlist	= 0;
Xstatic int			moncnt	= 0;
X
Xextern char		   *malloc();
Xextern char		   *fgets();
Xextern computer	   *compbyname();
X
X/*
X**	savename: add a name to the moniker list and save the new list
X*/
Xsavename(name)
Xchar   *name;
X{
X	register struct mon	   *m;
X	FILE				   *fp;
X
X	if(monlist == 0)
X		readmonfile();
X
X	if(strlen(name) < 5)		/* small names are boring */
X		return;
X	if(compbyname(name) != 0)	/* don't save computer names */
X		return;
X	
X	/*
X	**	Check for duplicate while finding the end of the list.
X	*/
X	for(m = monlist;;m = m->m_next) {
X		if(strncmp(name, m->m_name, NAMELEN) == 0)
X			return;
X		if(m->m_next == 0)
X			break;
X	}
X
X	/*
X	**	Add the new name to the end of the list.
X	*/
X	if((m->m_next = (struct mon *)malloc(sizeof *m)) == 0) {
X		syslog(LOG_WARNING, "savename: out of memory");
X		return;
X	}
X	m = m->m_next;
X	m->m_next = 0;
X	strncpy(m->m_name, name, NAMELEN);
X	m->m_name[NAMELEN-1] = '\0';
X	++moncnt;
X
X	/*
X	**	Append the new name to the monikers file.
X	*/
X	(void) umask(022);
X	if((fp = fopen(MONFILE, "a")) == 0) {
X		syslog(LOG_WARNING, "savename: fopen(%s,a): %m", MONFILE);
X		return;
X	}
X	fprintf(fp, "%s\n", m->m_name);
X	(void) fclose(fp);
X}
X
X/*
X**	moniker: randomly select a name from the moniker list
X*/
Xchar *
Xmoniker()
X{
X	register struct mon	   *m;
X	register int			n;
X
X	if(monlist == 0)
X		readmonfile();
X
X	n = randint(moncnt) - 1;
X	for(m = monlist;n > 0 && m->m_next != 0;--n, m = m->m_next)
X		;
X	
X	return m->m_name;
X}
X
X/*
X**	readmonfile: seed the monikers list then read in the monikers file
X*/
Xreadmonfile()
X{
X	register struct mon	   *m;
X	register FILE		   *fp;
X	char				   *s, line[2*NAMELEN];
X
X	monlist = &seed[0];
X	moncnt = seedcnt;
X
X	if((fp = fopen(MONFILE, "r")) == 0) {
X		syslog(LOG_WARNING, "readmonfile: fopen(%s,r): %m", MONFILE);
X		return;
X	}
X
X	for(m = monlist;m->m_next != 0;++m)
X		;
X
X	while(fgets(line, sizeof line, fp) != 0) {
X		if(*(s = &line[strlen(line)-1]) == '\n')
X			*s = '\0';
X		if((m->m_next = (struct mon *)malloc(sizeof *m)) == 0) {
X			syslog(LOG_WARNING, "readmonfile: out of memory");
X			return;
X		}
X		m = m->m_next;
X		m->m_next = 0;
X		strncpy(m->m_name, line, NAMELEN);
X		m->m_name[NAMELEN-1] = '\0';
X		++moncnt;
X	}
X
X	(void) fclose(fp);
X}
END_OF_FILE
if test 3417 -ne `wc -c <'moniker.c'`; then
    echo shar: \"'moniker.c'\" unpacked with wrong size!
fi
# end of 'moniker.c'
fi
if test -f 'tempers.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tempers.c'\"
else
echo shar: Extracting \"'tempers.c'\" \(21222 characters\)
sed "s/^X//" >'tempers.c' <<'END_OF_FILE'
X/*	vi:set sw=4 ts=4: */
X#ifndef	lint
Xstatic char	sccsid[] = "@(#)tempers.c 5.1 (G.M. Paris) 89/01/22";
X#endif	lint
X
X/*
X**
X**	cubes 5.1  Copyright 1989 Gregory M. Paris
X**		Permission granted to redistribute on a no charge basis.
X**		All other rights are reserved.
X**
X*/
X
X#include	<syslog.h>
X#include	"cubes.h"
X
Xextern unsigned		nhist;
Xextern history	   *hist;
Xextern int			winscore;
Xextern int			turnnum;
Xextern boolean		jokermode;
Xextern player		plr[];
Xextern double		ziprisk();
Xextern int			highscore();
Xextern int			closescore();
Xextern int			winner();
Xextern long			histavgplr();
X
X#define	P_MAXROLL	(P_ACEMULT * P_AOKMULT)	/* largest single roll */
X
X/*
X**	atbreakeven: returns True if player has reached the quit-penalty
X**		break-even point (roughly half of player's point average)
X**		or can be expected to reach it before the game ends
X*/
Xboolean
Xatbreakeven(comp, mscore, hscore)
Xint		comp, mscore, hscore;
X{
X	long	half, need;
X
X	if((half = histavgplr(comp)) == 0)			/* no average */
X		return True;							/* passed it, right? */
X	half = (half * winscore) / (2 * WINSCORE);	/* derate and half */
X	if((need = half - mscore) <= 0)				/* passed it? */
X		return True;
X	if(winscore - hscore > 2 * need)			/* probably can reach it? */
X		return True;
X	return False;								/* not yet */
X}
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, hc;
X	double	livefac;
X
X#define	TOOLITTLE	(2 * P_ACE)
X#define	GAINFAC		1.1
X#define	WAYBEHIND	((P_MAXROLL * winscore) / WINSCORE)
X#define	CLOSE		WINMARGIN
X#define	AVGTURN		400			/* empirically derived */
X#define	SANITY		0.40
X
X	/*
X	**	Avoid starting out too far in the hole by not getting on board
X	**	way behind -- the leave-the-game algorithm will eventually "save"
X	**	us, but we pay a price.  We know what the price is, so if we
X	**	reach the break-even point, we hold.
X	*/
X	mscore = plr[comp].p_score + stay;
X	if(plr[comp].p_onboard == False /* && stay >= ONBOARD */) {
X		hscore = highscore(comp, (int *)0);	/* not (*track) */
X		if(hscore - mscore > WAYBEHIND) {
X			if(atbreakeven(comp, mscore, hscore) == True)
X				return False;				/* hold */
X			return True;					/* desperation */
X		}
X	}
X
X	/*
X	**	Never stop with too little points.
X	*/
X	if(stay <= TOOLITTLE)
X		return True;
X	
X	/*
X	**	If there's gain to be had, roll again.
X	*/
X	if(xpct > (int)(GAINFAC * stay))
X		return True;
X
X	/*
X	**	Find our score and the score we're tracking.  If we're ahead
X	**	or close, then don't reroll.  Our definition of close depends
X	**	on whether the leader has already taken its turn this round.
X	*/
X	hscore = (*track)(comp, &hc);
X	if(hc < comp) {		/* leader has taken turn */
X		if(hscore - mscore <= CLOSE)
X			return False;
X	} else {			/* leader's turn still to come */
X		if(hscore - mscore <= CLOSE - AVGTURN)
X			return False;
X	}
X
X	/*
X	**	Calculate the expectation value we can live with.
X	*/
X	livefac = GAINFAC - (double)(hscore - mscore) / winscore;
X	if(livefac < SANITY)
X		livefac = SANITY;
X	if(xpct > (int)(livefac * stay))
X		return True;
X	
X	return False;
X
X#undef	TOOLITTLE
X#undef	GAINFAC
X#undef	WAYBEHIND
X#undef	CLOSE
X#undef	AVGTURN
X#undef	SANITY
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**		won't get on board if high score is out of range
X*/
X/*ARGSUSED*/
Xstatic boolean
Xchicken(comp, stay, xpct, pd)
Xint			comp, stay, xpct;
Xdiceset	   *pd;
X{
X	int		mscore, hscore, hc;
X	int		atrisk, maxrisk;
X	int		range, magic;
X
X#define	OUTRNG		((P_STRAIGHT * winscore) / WINSCORE)
X#define	AVGTURN		375		/* empirically, a bit low */
X
X	/*
X	**	We have the opportunity to get on board if we quit.  We hold
X	**	if the high score is not out of range.  We roll again if we
X	**	have no average, thus cannot be penalized.  We hold if we've
X	**	reached the break-even point.  Otherwise, roll again.
X	*/
X	if(plr[comp].p_onboard == False /* && stay >= ONBOARD */) {
X		mscore = plr[comp].p_score + stay;
X		hscore = highscore(comp, &hc);
X		if(hc > comp)
X			hscore += AVGTURN;
X		if(hscore - mscore < OUTRNG)
X			return False;				/* close enough, hold! */
X		if(atbreakeven(comp, mscore, hscore) == True)
X			return False;				/* too risky to roll */
X		return True;					/* desperation... */
X	}
X	
X	/*
X	**	If the leader's turn has already been taken, then if we
X	**	can win, hold.  If the leader has yet to take its turn,
X	**	if we can block it from winning, hold.  Adjust hscore
X	**	here for leader's predicted turn.
X	*/
X	if((mscore = plr[comp].p_score + stay) >= winscore) {
X		hscore = highscore(comp, &hc);
X		if(hc < comp) {							/* leader had turn */
X			if(mscore - hscore >= WINMARGIN)	/* can we win? */
X				return False;
X		} else {								/* leader's turn to come */
X			hscore += AVGTURN;					/* predict future */
X			if(hscore - mscore < WINMARGIN)		/* can we block? */
X				return False;
X		}
X	}
X
X	/*
X	**	Our behavior is dependent on our mood.
X	*/
X	switch(plr[comp].p_mood) {
X	case 2: magic = P_FIVE / 2; break;
X	case 3: magic = P_FIVE * 2; break;
X	default:magic = P_FIVE; break;
X	}
X
X	/*
X	**	We are conservative when close, getting more desperate
X	**	as we get further behind.  We get a little wild when
X	**	we're ahead too.
X	*/
X	atrisk = (int)(stay * ziprisk(pd));
X	if((range = (mscore - hscore + 1) / WINMARGIN) <= 0)
X		maxrisk = ((1 - range) * magic);	/* not winning */
X	else
X		maxrisk = (range * magic) / 3;		/* winning */
X	if(maxrisk > 4 * magic)
X		maxrisk = 4 * magic;
X
X	if(atrisk > maxrisk)
X		return False;
X
X	return True;
X
X#undef	OUTRNG
X#undef	AVGTURN
X}
X
X/*
X**	gambler: cautious sometimes, not so others
X**		stops when it gets on board unless the risk is small or far behind
X**		stops when it has won or could block unless the risk is small
X**		rolls when the expectation is favorable
X**		rolls to try to stop a winner until too much at risk
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, hscore, hc;
X	double	luck;
X
X#define	SMALLRISK	0.15
X#define	EVENODDS	0.50
X#define	FARBEHIND	(winscore / 5)
X#define	AVGTURN		(jokermode == True ? 350 : 450)
X
X	/*
X	**	Calculate our score.  Calculate the other highest score
X	**	and if the leader hasn't taken its turn yet, guess at
X	**	what that turn might amount to.
X	*/
X	mscore = plr[comp].p_score + stay;
X	hscore = highscore(comp, &hc);
X	if(hc > comp)						/* leader hasn't taken this turn */
X		hscore += AVGTURN;				/* adjust for future earnings */
X
X	/*
X	**	If we can get on board, we roll again anyway if we're
X	**	far behind.  Otherwise, we hold unless it's really tempting.
X	*/
X	if(plr[comp].p_onboard == False /* && stay >= ONBOARD */) {
X		if(hscore - mscore > FARBEHIND) {
X			if(atbreakeven(comp, mscore, hscore) == False)
X				return True;	/* too risky to quit */
X		}
X		if(xpct > stay && ziprisk(pd) < SMALLRISK)
X			return True;
X		return False;
X	}
X	
X	/*
X	**	If we could win or block, hold unless it's really tempting.
X	*/
X	if((mscore = plr[comp].p_score + stay) >= winscore) {
X		if(	(hc < comp && mscore - hscore >= WINMARGIN)		/* can win */
X		||	(hc > comp && hscore - mscore < WINMARGIN)) {	/* can block */
X			if(xpct > stay && ziprisk(pd) < SMALLRISK)
X				return True;
X			return False;
X		}
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 another player is about to win, try to stop it.  This isn't strictly
X	**	corrrect code, since we may not be the second-best score, but we can't
X	**	count on others to save us.  We give up if we risk losing too much.
X	*/
X	if(hscore >= winscore && hscore - mscore >= WINMARGIN)
X		return (int)(stay * ziprisk(pd)) > P_ASMSTR ? False : True;
X	
X	/*
X	**	If we're behind, roll when the odds are favorable and we feel lucky.
X	**	We feel lucky if we've been lucky -- if we've squandered less than
X	**	half of what we've scored, then we have some luck.  If our luck
X	**	multiplied by our desperation is greater than what we'd have if
X	**	we held, then we roll.
X	*/
X	if(hscore > mscore && mscore > 0 && ziprisk(pd) <= EVENODDS)
X		if((luck = 1.0 - (2.0 * plr[comp].p_squander) / mscore) > 0)
X			if(luck * (hscore - mscore) > stay)
X				return True;
X	
X	/*
X	**	Finally, don't hold with too few points.
X	*/
X	return (stay < 2 * P_ACE) ? True : False;
X
X#undef	SMALLRISK
X#undef	EVENODDS
X#undef	FARBEHIND
X#undef	AVGTURN
X}
X
X/*
X**	xx_crafty:
X**		Won't get on board if too far behind.
X**		Quits when it can win, unless really tempting.
X**		Tries to roll to beat a winner.
X**		Otherwise, tries to get and protect a safelead lead
X**		becoming more daring as it gets ahead or behind,
X**		but with a limit to its stupidity.
X*/
X/*ARGSUSED*/
Xstatic boolean
Xxx_crafty(comp, stay, xpct, pd, safelead, maxgainfac, mingainfac, smallrisk)
Xint			comp, stay, xpct;
Xdiceset	   *pd;
Xint			safelead;
Xdouble		maxgainfac, mingainfac, smallrisk;
X{
X	int		mscore, hscore;
X	int		live, winc;
X	double	gainfac;
X
X#define	TOOFAC	4
X
X	hscore = highscore(comp, (int *)0);
X	mscore = plr[comp].p_score + stay;
X
X	/*
X	**	We can get on board here.  If we're too far behind, and not
X	**	yet at the quit-penalty break-even point, we keep rolling.
X	**	If too risky, we hold.  Otherwise, use the usual decision function.
X	*/
X	if(plr[comp].p_onboard == False /* && stay >= ONBOARD */) {
X		if(hscore - mscore > TOOFAC * safelead) {	/* too far behind */
X			if(atbreakeven(comp, mscore, hscore) == False)
X				return True;						/* must roll */
X		}
X		if(ziprisk(pd) >= smallrisk)				/* too risky */
X			return False;							/* must hold */
X	}
X	
X	/*
X	**	If we could win, hold unless it's really tempting.
X	*/
X	if(mscore >= winscore)
X		if(mscore - hscore >= WINMARGIN && ziprisk(pd) >= smallrisk)
X			return False;
X
X	/*
X	**	If we have a winner (other than us), try to beat them, but only
X	**	if the expected gainfac is greater than or equal to mingainfac.
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 * mingainfac)
X			if(hscore - mscore >= WINMARGIN || winc > comp)
X				return True;
X
X	/*
X	**	If there's enough gain to be had, roll again.
X	*/
X	if(xpct > (int)(maxgainfac * stay))
X		return True;
X
X	/*
X	**	Calculate the "instantaneous" gain factor.  Changed gainfac
X	**	calculation to use p_score when ahead, since this temperament
X	**	used to routinely squander huge rolls.
X	*/
X	if(mscore < hscore)
X		gainfac = (double)(mscore - hscore - safelead) / P_MAXROLL;
X	else
X		gainfac = (double)(plr[comp].p_score - hscore - safelead) / P_MAXROLL;
X	if(gainfac < 0)
X		gainfac = -gainfac;
X	gainfac = maxgainfac - gainfac;
X	if(gainfac < mingainfac)
X		gainfac = mingainfac;
X
X	/*
X	**	Calculate the expectation value we can live with.
X	*/
X	live = (int)(stay * gainfac);
X	if(xpct > live)
X		return True;
X	
X	return False;
X
X#undef	TOOFAC
X}
X
X/*
X**	crafty: call xx_crafty with original set of parameters
X*/
Xstatic boolean
Xcrafty(comp, stay, xpct, pd)
Xint			comp, stay, xpct;
Xdiceset	   *pd;
X{
X	return xx_crafty(comp, stay, xpct, pd, WINMARGIN, 1.20, 0.85, 0.20);
X}
X
X/*
X**	shakey: call xx_crafty with revised set of parameters
X*/
Xstatic boolean
Xshakey(comp, stay, xpct, pd)
Xint			comp, stay, xpct;
Xdiceset	   *pd;
X{
X	/*
X	**	We have four distinct moods.
X	*/
X	switch(plr[comp].p_mood) {
X	case 2: return xx_crafty(comp,stay,xpct,pd, P_STRAIGHT, 1.30, 0.75, 0.10);
X	case 3: return xx_crafty(comp,stay,xpct,pd, 2*OFFBOARD, 1.30, 0.80, 0.15);
X	case 4: return xx_crafty(comp,stay,xpct,pd, OFFBOARD,   1.20, 0.85, 0.20);
X	default:return xx_crafty(comp,stay,xpct,pd, OFFBOARD,   1.30, 0.75, 0.10);
X	}
X}
X
X/*
X**	zeno: uses ratio of distance to finish over total distance
X*/
X/*ARGSUSED*/
Xstatic boolean
Xzeno(comp, stay, xpct, pd)
Xint			comp, stay, xpct;
Xdiceset	   *pd;
X{
X#define	MINWANT		(WINMARGIN-1)
X#define	SANITY		(1.10)			/* fudge factor for endgame sanity */
X#define	AVGTURN		400				/* empirical */
X
X	int		hscore, mscore, hc;
X	int		lead, want;
X	double	zenofac, riskfac, wantfac;
X
X	mscore = plr[comp].p_score;						/* ignore this turn */
X	hscore = highscore(comp, &hc);					/* of best competitor */
X	zenofac = (double)hscore / winscore;			/* distance ratio */
X	if(hc < comp) {									/* leader had turn */
X		zenofac = (double)hscore					/* distance ratio */
X				/ (winscore * SANITY);
X		lead = (mscore-WINMARGIN) - hscore;			/* positive when winning */
X	} else {										/* leader's turn to come */
X		zenofac = (double)(hscore + AVGTURN)		/* distance ratio */
X				/ (winscore * SANITY);
X		lead = (mscore-WINMARGIN)-(hscore+AVGTURN);	/* positive when winning */
X	}
X
X	if(lead > 0) {									/* we're winning */
X		riskfac = 0.90 + 0.35 * zenofac;			/* 0.90 - 1.25 */
X		if(xpct < (int)(riskfac * stay))			/* not willing to risk it */
X			return False;							/* hold */
X		wantfac = 1.5 - 1.4 * zenofac;				/* 1.5 - 0.1 */
X		if((want = lead * wantfac) < MINWANT)		/* magic greed limit */
X			want = MINWANT;							/* ... but keep moving */
X		if(stay > want)								/* we're satisfied */
X			return False;							/* hold */
X	}
X
X	else if(lead > -WINMARGIN) {					/* it's a dead heat */
X		riskfac = 1.0 +  0.35 * zenofac;			/* 1.00 - 1.35 */
X		if(xpct < (int)(riskfac * stay))			/* not willing to risk it */
X			return False;							/* hold */
X													/* no greed test */
X	}
X
X	else {											/* we're losing */
X		riskfac = 1.1 - 0.35 * zenofac;				/* 1.1 - 0.75 */
X		if(xpct < (int)(riskfac * stay))			/* not willing to risk it */
X			return False;							/* hold */
X		wantfac = 0.2 + 0.7 * zenofac;				/* 0.2 - 0.9 */
X		if((want = -lead * wantfac) < MINWANT)		/* magic greed limit */
X			want = MINWANT;							/* ... but keep moving */
X		if(stay > want)								/* we're satisfied */
X			return False;							/* hold */
X	}
X
X	return True;									/* roll again */
X
X#undef	MINWANT
X#undef	SANITY
X#undef	AVGTURN
X}
X
X/*
X**	goalie: strives for four goals:
X**		winscore, 6/5ths average, 3/5ths average, and pace leader
X*/
X/*ARGSUSED*/
Xstatic boolean
Xgoalie(comp, stay, xpct, pd)
Xint			comp, stay, xpct;
Xdiceset	   *pd;
X{
X	int		cuscore, stscore, hiscore, hc;
X	int		turnsleft, avscore, totneed, perturn;
X	int		toolittle, tooclose;
X	double	gainfac, avgturn;
X
X#define	WAYBEHIND	((P_MAXROLL * winscore) / WINSCORE)
X#define	SANITY		0.50
X#define	MAXTURN		(P_ACEMULT * P_3OKMULT)
X
X	/*
X	**	Avoid starting out too far in the hole by not getting on board
X	**	way behind -- the leave-the-game algorithm will eventually "save"
X	**	us, but we pay a price.  We know what the price is, so if we
X	**	reach the break-even point, we hold.
X	*/
X	cuscore = plr[comp].p_score;
X	stscore = cuscore + stay;
X	if(plr[comp].p_onboard == False /* && stay >= ONBOARD */) {
X		hiscore = highscore(comp, (int *)0);
X		if(hiscore - stscore > WAYBEHIND) {
X			if(atbreakeven(comp, stscore, hiscore) == True)
X				return False;			/* hold */
X			return True;				/* desperation */
X		}
X	}
X
X	/*
X	**	Some parameters depend on our mood.
X	*/
X	switch(plr[comp].p_mood) {
X	case 2:		/* most agressive */
X		toolittle = 3 * P_ACE;
X		tooclose = P_FIVE;
X		gainfac = 1.025;
X		break;
X	case 3:		/* more agressive */
X		toolittle = 2 * P_ACE + P_FIVE;
X		tooclose = P_ACE;
X		gainfac = 1.050;
X		break;
X	default:	/* normal */
X		toolittle = 2 * P_ACE;
X		tooclose = P_ACE + P_FIVE;
X		gainfac = 1.075;
X		break;
X	}
X
X	/*
X	**	Never stop with too little points.  If there's gain to be had, roll.
X	**	If it would be insane to roll again, don't.
X	*/
X	if(stay <= toolittle)
X		return True;
X	if(xpct > (int)(gainfac * stay))
X		return True;
X	if(xpct < (int)(SANITY * stay))
X		return False;
X	
X	/*
X	**	If we're ahead or close to the high score, don't reroll.  Definition
X	**	of close depends on whether the leader has already taken its turn this
X	**	round.  We calculate the leader's average turn for use here or below.
X	*/
X	if((hiscore = highscore(comp, &hc)) == 0)
X		return False;
X	if(hc < comp) {
X		if(hiscore - stscore <= tooclose)
X			return False;
X		avgturn = (double)hiscore / (turnnum + 1);
X	} else {
X		avgturn = (double)hiscore / turnnum;
X		if(hiscore - stscore <= tooclose - (int)avgturn)
X			return False;
X	}
X	if(avgturn < P_ACE)				/* too low to believe */
X		avgturn = P_ACE;
X	else if(avgturn > P_STRAIGHT)	/* too high to believe */
X		avgturn = P_STRAIGHT;
X	
X	/*
X	**	We're behind, so it's a race to one of our goals.  We estimate how many
X	**	turns left in the game based on the leader's average turn.  From that,
X	**	we figure out what we need per turn to reach a reasonable goal.
X	**	If we've got that much, we hold, otherwise we roll again.
X	*/
X	if((turnsleft = (winscore - hiscore) / avgturn) <= 0)
X		turnsleft = 1;
X	
X	/*
X	**	Primary goal: strive for winscore.
X	*/
X	if((totneed = winscore - cuscore) <= 0)
X		goto pace;
X	if((perturn = totneed / turnsleft) <= MAXTURN)
X		return (stay >= perturn) ? False: True;
X
X	/*
X	**	Calculate our historical average score.
X	*/
X	if((avscore = histavgplr(comp)) == 0)			/* no record */
X		avscore = (3 * WINSCORE) / 4;				/* fake one */
X	avscore = (avscore * winscore) / WINSCORE;		/* derate */
X
X	/*
X	**	Secondary goal: strive for 6/5ths of average.
X	*/
X	if((totneed = (6 * avscore) / 5 - cuscore) <= 0)
X		goto pace;
X	if((perturn = totneed / turnsleft) <= MAXTURN)
X		return (stay >= perturn) ? False: True;
X
X	/*
X	**	Tertiary goal: strive for 3/5ths of average.
X	*/
X	if((totneed = (3 * avscore) / 5 - cuscore) <= 0)
X		goto pace;
X	if((perturn = totneed / turnsleft) <= MAXTURN)
X		return (stay >= perturn) ? False: True;
X
X	/*
X	**	Last resort: try to keep ahead of leader.
X	**	Else, keep moving at an arbitrary pace.
X	*/
Xpace:
X	if((totneed = hiscore + WINMARGIN - cuscore) > 0)
X		if((perturn = totneed / turnsleft) <= MAXTURN)
X			return (stay >= perturn) ? False: True;
X	return (stay >= 2 * P_ACE) ? False : True;
X
X#undef	WAYBEHIND
X#undef	SANITY
X#undef	MAXTURN
X}
X
X/*
X**	best: use the temperament currently ranked best
X*/
Xstatic boolean
Xbest(comp, stay, xpct, pd)
Xint			comp, stay, xpct;
Xdiceset	   *pd;
X{
X	static history	   *phist	= 0;
X	static computer	   *last	= 0;
X	register int		n;
X	extern temper		te_best;
X	extern computer	   *pkamelion;
X
X	/*
X	**	If this is our first time through, or if we're not
X	**	pointing to the same computer as last time, look
X	**	for the top ranked computer.  On occasion we might
X	**	not re-search for the top when we should, but this
X	**	should happen only rarely.
X	*/
X	if(phist == 0 || last == 0 || phist->h_computer != last) {
X		last = 0;
X		for(phist = hist, n = 0;n < nhist;++n, ++phist) {
X			if(phist->h_computer == 0 || phist->h_computer == pkamelion)
X				continue;
X			if(phist->h_computer->c_temper == &te_best)
X				continue;
X			last = phist->h_computer;
X			break;
X		}
X	}
X
X	/*
X	**	If we've got a best computer, use its temperament,
X	**	else we default to using the robot temperament.
X	*/
X	if(last != 0)
X		return (last->c_temper->t_func)(comp, stay, xpct, pd);
X	return robot(comp, stay, xpct, pd);
X}
X
X/*
X**	kamelion: should never be called
X*/
Xstatic boolean
Xkamelion(comp, stay, xpct, pd)
Xint			comp, stay, xpct;
Xdiceset	   *pd;
X{
X	syslog(LOG_WARNING, "kamelion temperament called!");
X	return best(comp, stay, xpct, pd);
X}
X
X/*
X**	schizo: pick another temperament randomly!
X**		intended for proxy players only
X*/
Xstatic boolean
Xschizo(comp, stay, xpct, pd)
Xint			comp, stay, xpct;
Xdiceset	   *pd;
X{
X	int				n;
X	extern int		tempers;
X	extern temper  *temptbl[];
X
X	hesitate(100L, 1000L);
X	plr[comp].p_mood = randint(MAXMOOD);	/* new mood every time */
X	n = randint(tempers - 1) - 1;			/* schizo last -- don't pick it */
X	return (*temptbl[n]->t_func)(comp, stay, xpct, pd);
X}
X
X/*
X**	temptbl: table of available temperaments
X*/
X
Xtemper	te_robot	= { robot,		"robot" };
Xtemper	te_antsy	= { antsy,		"antsy" };
Xtemper	te_tracker	= { tracker,	"tracker" };
Xtemper	te_chicken	= { chicken,	"chicken" };
Xtemper	te_gambler	= { gambler,	"gambler" };
Xtemper	te_crafty	= { crafty,		"crafty" };
Xtemper	te_shakey	= { shakey,		"shakey" };
Xtemper	te_zeno		= { zeno,		"zeno" };
Xtemper	te_goalie	= { goalie,		"goalie" };
Xtemper	te_best		= { best,		"best" };
Xtemper	te_schizo	= { schizo,		"schizo" };
Xtemper	te_kamelion	= { kamelion,	"kamelion" };	/* semi-dummy */
X
Xtemper	   *temptbl[]	= {
X	&te_robot, &te_antsy, &te_tracker, &te_chicken, &te_gambler,
X	&te_crafty, &te_shakey, &te_zeno, &te_goalie, &te_best,
X	&te_schizo		/* te_schizo must be last */
X};
X
Xint	tempers	= sizeof temptbl / sizeof temptbl[0];
END_OF_FILE
if test 21222 -ne `wc -c <'tempers.c'`; then
    echo shar: \"'tempers.c'\" unpacked with wrong size!
fi
# end of 'tempers.c'
fi
echo shar: End of archive 3 \(of 8\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 8 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0