2278 lines
		
	
	
		
			58 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2278 lines
		
	
	
		
			58 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* backgmmn.c */
 | ||
| 
 | ||
| /***************************************************************************
 | ||
| 
 | ||
|    GAMMON IV, Version 2
 | ||
| 
 | ||
|    Author:  David C. Oshel
 | ||
|             1219 Harding Avenue
 | ||
|             Ames, Iowa 50010
 | ||
| 
 | ||
|    Date:    March 26, 1986
 | ||
| 
 | ||
|    Gammon IV is probably the best backgammon game currently available for
 | ||
|    CP/M.  I wrote it because I was disgusted with the price and dullness
 | ||
|    of all other programs which allegedly play backgammon on CP/M systems.
 | ||
| 
 | ||
|    This program has THREE DIFFERENT PLAYING STYLES, any one of which can
 | ||
|    consistently beat a novice player, and occasionally beat a good player.
 | ||
|    In all three levels, the computer's strategy can even seem brilliant;
 | ||
|    there is nothing routine about it.
 | ||
| 
 | ||
|    This version incorporates a few minor changes and bug fixes which make it
 | ||
|    different from previous editions which have appeared on various bulletin 
 | ||
|    boards and club offerings around the country.  This is a public domain
 | ||
|    program.  Feel free to distribute or improve it.  Credit to the original
 | ||
|    author (me) will be appreciated, but is not strictly required since the
 | ||
|    copyright owners are abnormally mild-mannered (and extremely distant;
 | ||
|    see Acknowledgments, below.)
 | ||
| 
 | ||
|    Version 2:
 | ||
| 
 | ||
|       a) Fixed the bug in which the doubling cube might revert to its
 | ||
|          default value if the computer took back a move, and if the cube
 | ||
|          had been doubled one or more times during the opening roll-off
 | ||
|          but not yet offered.
 | ||
| 
 | ||
|       b) Fixed an odd bug in the Arrange command which occasionally caused 
 | ||
|          stones to switch allegiance to the opponent's color, or else to
 | ||
|          mysteriously multiply to more than 15 on a side.
 | ||
| 
 | ||
|       c) An improved error handler now hints that BAR and HOME are words
 | ||
|          used in the game; also, slows down the incorrect entry routine so
 | ||
|          that it is less mysterious than before.  
 | ||
| 
 | ||
|       d) Added a Graphic toggle to main command line.  Same as Control-K,
 | ||
|          as in previous versions; turns on/off the Kaypro '84 video graphics;
 | ||
|          uses the alternate token set defined in the CRT module below.
 | ||
| 
 | ||
|       e) Changed the break character back to Control-C.  The proximity of
 | ||
|          the ESCAPE key to the 1 digit on the Kaypro keyboard sometimes
 | ||
|          caused a game to be inadvertently paused during play.
 | ||
| 
 | ||
|       f) There is no longer a sanction against cheating; if you type
 | ||
|          Control-C during play and re-Arrange the stones, your opponent
 | ||
|          does not change.
 | ||
| 
 | ||
|       g) As before, the computer will not always take (or find) a forced 
 | ||
|          move.  The rule is that the player must take both dice if possible,
 | ||
|          or the larger if both are possible singly but not together.
 | ||
|          The playing algorithm attempts to find the legally required move, 
 | ||
|          but will sometimes fail.  It is sometimes advantageous to avoid a
 | ||
|          forced move but the computer cannot make that distinction, in this
 | ||
|          version; the program allows the same lenience for the human player.
 | ||
| 
 | ||
|          The cube is doubled when doubles are thrown during the opening
 | ||
|          rolloff to decide who goes first.  However, if the cube reaches 8,
 | ||
|          further pairs on the dice during rolloff have no effect.
 | ||
| 
 | ||
|          Gammon IV knows all the other OFFICIAL rules of the game; 
 | ||
|          U.S. Navy and/or Saloon rules don't count.
 | ||
| 
 | ||
|       h) Improved coding in some routines, especially putstone(), which was
 | ||
|          far more baroque than necessary.  There is also some occasional 
 | ||
|          re-coding, where I could not prevent myself.  The game strategy
 | ||
|          has not been changed, mostly because I don't fully understand it
 | ||
|          anymore, even though the documentation there is better than usual.
 | ||
|          It was written months ago in a white heat of inspiration; but I
 | ||
|          feel reluctant to submit myself to that strain again, without the
 | ||
|          prospect of remuneration.
 | ||
| 
 | ||
| 
 | ||
|    Acknowledgments:
 | ||
| 
 | ||
|       The opening dialogue, in which you chose your computer opponent,
 | ||
|       is freely adapted from Alexei Panshin's long out-of-print, and
 | ||
|       now classic, "Star Well" travelogues.  Scholars of computer history 
 | ||
|       will recognize many allusions to this same opus in various passages 
 | ||
|       of Mike Goetz's 550-point Adventure (especially in the Ice Caverns!).  
 | ||
| 
 | ||
|       The Xochitl Sodality, which owns the copyright on Gammon IV, is a 
 | ||
|       philanthropic society first publicized by Panshin.  Persons who wish 
 | ||
|       to obtain information on site licensing for Gammon IV should contact 
 | ||
|       the Monist Association imaginary properties secretary at the following 
 | ||
|       address:
 | ||
|                          Monist Association I.P.S.
 | ||
|                          c/o Xochitl Sodality
 | ||
|                          Semichastny House
 | ||
|                          Delbalso, Nash. Emp.
 | ||
|  
 | ||
| 
 | ||
|    Special Instructions:
 | ||
| 
 | ||
|       Terminal must be Lear-Siegler ADM-3A compatible, or else have
 | ||
|       the same graphics capability as a Kaypro 10, 2X or 4'84.  This
 | ||
|       requirement is fully configurable in the video section below,
 | ||
|       however.
 | ||
| 
 | ||
|       This program requires the Software Toolworks' C/80 v3.1 compiler
 | ||
|       for CP/M 2.2.  C/80 has a configuration program.  C/80 must be
 | ||
|       configured as follows, or else this program WILL NOT compile:
 | ||
| 
 | ||
|       Symbol table size: 512
 | ||
|       String constant table: 3200
 | ||
|       Dump constants after each routine: YES
 | ||
|       Macro table size: 500
 | ||
|       Switch table size: 128
 | ||
|       Structure table size: 200
 | ||
|       Merge duplicate string constants: YES
 | ||
| NB:   Assembler: C/80's AS
 | ||
|       Initialize arrays to zero:  < 256 BYES ONLY
 | ||
|       Generate ROMable code in Macro-80: YES
 | ||
|       Screen size:  24 (doesn't matter)
 | ||
|       Generate slightly larger, faster code: NO
 | ||
|       Sign extension on char to int conversion: YES 
 | ||
|       Device for library files: A: (your choice)
 | ||
| 
 | ||
|    Compilation:
 | ||
|    
 | ||
| NB:   Microsoft's MACRO-80 assembler and LINK-80 linker are required,
 | ||
|       and are specified in the (-m) compiler switch!  This is a moderately
 | ||
|       complex compile, so the procedure is directed by batch SUBMIT files.
 | ||
|       Distribute files as follows:
 | ||
| 
 | ||
|             On Drive A:
 | ||
| 
 | ||
|             BACKGMMN.C, BACKGMMN.SUB, CLIBRARY.REL, GAMEPLAN.C, 
 | ||
|             GAMEPLAN.HDR, MYLIB2.C, PRINTF.C, STDLIB.REL, SUBMIT.COM
 | ||
| 
 | ||
|             On Drive B:
 | ||
| 
 | ||
|             C.COM, L80.COM, M80.COM, WS.COM, WSMSGS.OVR, WSOVLY1.OVR
 | ||
| 
 | ||
|      Then, SUBMIT BACKGMMN to compile, assemble & link the game.  You will
 | ||
|      need about 180k of free space on Drive A.
 | ||
| 
 | ||
|      You must pay strict attention to the Special Instructions above,
 | ||
|      regarding C/80 configuration.
 | ||
| 
 | ||
| 
 | ||
|    Absent Files:
 | ||
| 
 | ||
|      M80.COM and L80.COM are from Microsoft, and are not part
 | ||
|      of this distribution.  C.COM, STDLIB.REL, CLIBRARY.REL and
 | ||
|      PRINTF.C are from Software Toolworks, and are not part of
 | ||
|      this distribution either.  SUBMIT.COM is a CP/M transient
 | ||
|      command; it came with your computer when you bought it.
 | ||
| 
 | ||
| 
 | ||
|    Notes:
 | ||
| 
 | ||
|       Gammon IV is impossible to implement in any C which does not allow
 | ||
|       functions to be passed as parameters to another function -- K & R must
 | ||
|       be followed on this point!
 | ||
|    
 | ||
|       THIS CODE ASSUMES INTEL 8080 CPU.  Inline code simulates an old,
 | ||
|       archaic version of SetJump() and LongJump(), which are not features
 | ||
|       of C/80 3.1.  Use of inline code means you need 8080 compatibility.
 | ||
| 
 | ||
|       By isolating the game-playing algorithm into a separately compiled
 | ||
|       module, I have allowed for the possibility that someone else may come
 | ||
|       up with significant improvements in strategy, AND BE GIVEN CREDIT FOR
 | ||
|       THEM, without having to re-design primitive parts of the program.
 | ||
| 
 | ||
|       The single exception to this rule is the code which decides when the
 | ||
|       computer will offer, accept or reject the doubling cube; that is
 | ||
|       considered primitive, even though the cube is a major factor in human
 | ||
|       strategies when playing for blood or money.  Gammon IV always plays
 | ||
|       for the simple fun of aggravating humans, so cube tactics are neither
 | ||
|       daring nor profound.  Gammon IV does not bluff with the cube; if it
 | ||
|       offers the cube, it is almost sure to win.
 | ||
| 
 | ||
| *****************************************************************************/
 | ||
| 
 | ||
| #define TRUE 1
 | ||
| #define FALSE 0
 | ||
| #define ME 1
 | ||
| #define YU 2
 | ||
| #define YRBAR 0
 | ||
| #define MYBAR 25
 | ||
| #define YRHOME 26
 | ||
| #define MYHOME 27
 | ||
| #define ERROR 999  /* anything well out of range */
 | ||
| #define MYLEVEL 2
 | ||
| 
 | ||
| /* put CRT stuff first so version differences won't affect it */
 | ||
| #asm
 | ||
| ;
 | ||
| ;  Note: Assumes assembly by M80.COM
 | ||
| ;
 | ||
| ;  *========================= CRT Module =========================*
 | ||
| ;  *                                                              *
 | ||
| ;  * User Patch Area:  ALL DB STRINGS MUST TERMINATE WITH A NULL  *
 | ||
| ;  * Use contiguous data area for all console functions, so user  *
 | ||
| ;  * may configure the program for some terminal not a Kaypro 10  *
 | ||
| ;  *                                                              *
 | ||
| ;  * CLEAR SCREEN and GOTOXY are Required Minimum Functions       *
 | ||
| ;  * CRTNIT, CRTXIT, CURSON, CURSOF are Optional and Recommended  *
 | ||
| ;  *                                                              *
 | ||
| ;  * The tokens TK1..TK4 comprise two sets each of playing tokens *
 | ||
| ;  * for the computer & human player.  24 bytes are reserved for  *
 | ||
| ;  * EACH token, so that users may turn on video enhancements --  *
 | ||
| ;  * see the Kaypro '84 recommended settings for examples.        *
 | ||
| ;  *                                                              *
 | ||
| ;  * The tokens TK5..TK8 and the KRTNIT, etc. strings implement   *
 | ||
| ;  * the Control-K command to toggle the Kaypro '84 display.      *
 | ||
| ;  * These should only be patched if you are configuring for two  *
 | ||
| ;  * levels of terminal characteristics, as vis-as-vis the "old"  *
 | ||
| ;  * and "new" Kaypro terminals.  These two levels must use the   *
 | ||
| ;  * same basic protocols for cursor address and clear screen.    *
 | ||
| ;  *                                                              *
 | ||
| ;  * Command Strings must terminate with 0, and the 0 byte CANNOT *
 | ||
| ;  * be sent to the console.  0 is INCLUDED in the reserved area. *
 | ||
| ;  *                                                              *
 | ||
| ;  * GAMMON IV no longer uses any console command function not    *
 | ||
| ;  * specifically mentioned here.  In particular, the clear-to-   *
 | ||
| ;  * end-of-line function is now handled in a more general way.   *
 | ||
| ;  *==============================================================*
 | ||
| ;
 | ||
| ;  Gammon IV.09 (Universal Version) User Patch Area, D.C.OSHEL, 6/15/85
 | ||
| ;----------------------------------------------------------------------------
 | ||
| ;
 | ||
| ; * REQUIRED *
 | ||
| ; reserve 24 bytes apiece for the players' tokens (must terminate with 0)
 | ||
| ; these settings are recommended for Lear Siegler ADM-3A (old Kaypros)
 | ||
| ;
 | ||
| 	 DB 'USER PATCH AREA '
 | ||
| 	 DB 'ALL STRINGS MUST HAVE ZERO TERMINATOR->'
 | ||
| 	 DB 'TOKEN1:'
 | ||
| TK1:	 DB '(',')',0,0,0,0,0,0  	 ; computer's token, ()
 | ||
| 	 DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 | ||
| 	 DB 'TOKEN2:'
 | ||
| TK2:	 DB '[',']',0,0,0,0,0,0  	 ; player's token, []
 | ||
| 	 DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 | ||
| 	 DB 'TOKEN3:'
 | ||
| TK3:	 DB 'q','b',0,0,0,0,0,0  	 ; computer's token (alternate, qb )
 | ||
| 	 DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 | ||
| 	 DB 'TOKEN4:'
 | ||
| TK4:	 DB 'C','3',0,0,0,0,0,0  	 ; player's token (alternate, C3 )
 | ||
| 	 DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 | ||
| ;----------------------------------------------------------------------------
 | ||
| ;
 | ||
| ; * Optional *
 | ||
| ; reserve 8 bytes apiece for the OPTIONAL console function strings
 | ||
| ; patch the first byte to 0 for each function NOT implemented
 | ||
| ;
 | ||
| 	 DB 'CRTNIT:'
 | ||
| CRTNIT:  DB 0,0,0,0,0,0,0,0		 ; crt init, e.g., video mode on
 | ||
| 	 DB 'CRTXIT:'
 | ||
| CRTXIT:  DB 0,0,0,0,0,0,0,0		 ; crt exit, e.g., video mode off
 | ||
| 	 DB 'CURSON:'
 | ||
| CURSON:  DB 0,0,0,0,0,0,0,0		 ; cursor ON  (show cursor)
 | ||
| 	 DB 'CURSOF:'
 | ||
| CURSOF:  DB 0,0,0,0,0,0,0,0		 ; cursor OFF (hide cursor)
 | ||
| ;----------------------------------------------------------------------------
 | ||
| ;
 | ||
| ; * REQUIRED *
 | ||
| ; clear screen and gotoxy (0,0 is top left of screen) are NOT optional
 | ||
| ; default settings are for the Lear Siegler ADM-3A terminal (old Kaypros)
 | ||
| ;
 | ||
| 	 DB 'CLS->'
 | ||
| CLS: 	 DB 26,0,0,0,0,0,0,0		 ; clear screen command
 | ||
| 	 DB 'GOTOXY->'
 | ||
| GOXY:	 DB 27,'=',0,0,0,0,0,0		 ; cursor address prefix
 | ||
| 	 DB 'YB4X BYTE:'
 | ||
| YB4X:	 DB 1	;BYTE 1 = YX: Send Row, Then Col (ADM-3A); 0 = XY: Col, Row
 | ||
| 	 DB 'XOFS BYTE:'
 | ||
| XOFS:	 DB 32	;BYTE offset to add to x in gotoxy sequence (ADM-3A = 20H)
 | ||
| 	 DB 'YOFS BYTE:'
 | ||
| YOFS:	 DB 32	;BYTE offset to add to y in gotoxy sequence (ADM-3A = 20H)
 | ||
| ;
 | ||
| ; these strings must terminate with 0, and 8 bytes are reserved for each
 | ||
| ;
 | ||
| ;
 | ||
| ;----------------------------------------------------------------------------
 | ||
| ;: The following strings implement the Control-K command for Kaypro '84
 | ||
| ;: video able terminals.  
 | ||
| ;----------------------------------------------------------------------------
 | ||
| ;:  
 | ||
| ;: * Kaypro '84 *
 | ||
| ;: recommended tokens for the video able Kaypro '84s (10s, 4'84s, etc)
 | ||
| ;: the second set requires that "video mode" be set in the CRTNIT string
 | ||
| ;: the first set uses Kaypro underline, inverse and low intensity commands
 | ||
| ;: to form the players' stones (24 bytes are reserved for each token)
 | ||
| ;:
 | ||
| 	 DB 'ENHANCED ALTERNATES->'
 | ||
| 	 DB 'TOKEN5:'
 | ||
| TK5:	 DB 27,'B1'	 ; computer's token
 | ||
| 	 DB 27,'B0'
 | ||
| 	 DB 27,'B3'
 | ||
| 	 DB '><'
 | ||
| 	 DB 27,'C3'
 | ||
| 	 DB 27,'C0'
 | ||
| 	 DB 27,'C1'
 | ||
| 	 DB 0,0,0,0
 | ||
| 	 DB 'TOKEN6:'
 | ||
| TK6:	 DB 27,'B3'	 ; player's token
 | ||
| 	 DB 27,'B0'
 | ||
| 	 DB '[]'
 | ||
| 	 DB 27,'C0'
 | ||
| 	 DB 27,'C3'
 | ||
| 	 DB 0,0,0,0,0,0,0,0,0,0
 | ||
| 	 DB 'TOKEN7:'
 | ||
| TK7:	 DB 128,233,129,150,0,0,0,0  	 	; computer's "black chiclet"
 | ||
| 	 DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 | ||
| 	 DB 'TOKEN8:'
 | ||
| TK8:	 DB 128,253,129,190,0,0,0,0  	 	; player's "white chiclet"
 | ||
| 	 DB 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
 | ||
| ;
 | ||
| ;----------------------------------------------------------------------------
 | ||
| ;:
 | ||
| ;: * Kaypro '84 *
 | ||
| ;: recommended settings for Kaypro '84s (strings must terminate with 0)
 | ||
| ;:
 | ||
| 	 DB 'KRTNIT:'
 | ||
| KRTNIT:  DB 27,'B5',0,0,0,0,0		 ; crt init, e.g., video mode on
 | ||
| 	 DB 'KRTXIT:'
 | ||
| KRTXIT:  DB 27,'C5',0,0,0,0,0		 ; crt exit, e.g., video mode off
 | ||
| 	 DB 'KURSON:'
 | ||
| KURSON:  DB 27,'B4',0,0,0,0,0		 ; cursor ON  (show cursor)
 | ||
| 	 DB 'KURSOF:'
 | ||
| KURSOF:  DB 27,'C4',0,0,0,0,0		 ; cursor OFF (hide cursor)
 | ||
| 	 DB '<-END USER PATCH AREA'
 | ||
| ;----------------------------------------------------------------------------
 | ||
| ;	LOF LG
 | ||
| #endasm
 | ||
| 
 | ||
| static int kaypro;
 | ||
| 
 | ||
| get1tkn() {
 | ||
| #asm
 | ||
| 	LXI H,TK1	;load string pointer for token 1
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| get2tkn() {
 | ||
| #asm
 | ||
| 	LXI H,TK2	;load string pointer for token 2
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| get3tkn() {
 | ||
| #asm
 | ||
| 	LXI H,TK3	;load string pointer for token 3
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| get4tkn() {
 | ||
| #asm
 | ||
| 	LXI H,TK4	;load string pointer for token 4
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| get5tkn() {
 | ||
| #asm
 | ||
| 	LXI H,TK5	;load string pointer for token 1
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| get6tkn() {
 | ||
| #asm
 | ||
| 	LXI H,TK6	;load string pointer for token 2
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| get7tkn() {
 | ||
| #asm
 | ||
| 	LXI H,TK7	;load string pointer for token 3
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| get8tkn() {
 | ||
| #asm
 | ||
| 	LXI H,TK8	;load string pointer for token 4
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| getcls() {
 | ||
| #asm
 | ||
| 	LXI H,CLS
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| getcini() {
 | ||
| #asm
 | ||
| 	LXI H,CRTNIT
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| getcxit() {
 | ||
| #asm
 | ||
| 	LXI H,CRTXIT
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| getcof() {
 | ||
| #asm
 | ||
| 	LXI H,CURSOF
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| getcon() {
 | ||
| #asm
 | ||
| 	LXI H,CURSON
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| getkini() {
 | ||
| #asm
 | ||
| 	LXI H,KRTNIT
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| getkxit() {
 | ||
| #asm
 | ||
| 	LXI H,KRTXIT
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| getkof() {
 | ||
| #asm
 | ||
| 	LXI H,KURSOF
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| getkon() {
 | ||
| #asm
 | ||
| 	LXI H,KURSON
 | ||
| #endasm
 | ||
| }
 | ||
| 
 | ||
| gotoxy(x,y) int x,y; {  /* this is solid gold! */
 | ||
| #asm
 | ||
| 	POP H		; get return address
 | ||
| 	POP D		; get Y
 | ||
| 	POP B		; get X
 | ||
| 	PUSH B  	; restore all to keep C happy
 | ||
| 	PUSH D		; ditto
 | ||
| 	PUSH H		; ditto
 | ||
| ;
 | ||
| 	LXI H,GOXY	; blast out the prefix 
 | ||
| 	CALL STOUT
 | ||
| ;
 | ||
| 	LDA YB4X	; sending row, i.e. Y, first?
 | ||
| 	ORA A
 | ||
| 	JZ @X1		; no, do it the other way
 | ||
| ;
 | ||
| 	LDA YOFS	; yes indeed, here's Y
 | ||
| 	ADD E
 | ||
| 	CALL COUT
 | ||
| ;
 | ||
| 	LDA XOFS	; and here's X
 | ||
| 	ADD C
 | ||
| 	CALL COUT
 | ||
| 	RET		; thank you very much, we're done
 | ||
| 	
 | ||
| @X1:	LDA XOFS	; your weird terminal wants X first?  ok
 | ||
| 	ADD C
 | ||
| 	CALL COUT
 | ||
| ;
 | ||
| 	LDA YOFS	; now Y
 | ||
| 	ADD E
 | ||
| 	CALL COUT
 | ||
| 	RET		; tyvm, we done
 | ||
| ;
 | ||
| STOUT:	MOV A,M		; string pointer in HL on entry, trash A, HL
 | ||
| 	ORA A
 | ||
| 	RZ
 | ||
| 	CALL COUT
 | ||
| 	INX H
 | ||
| 	JMP STOUT
 | ||
| ;
 | ||
| COUT:	PUSH H		; char in A on entry, disturb nothing
 | ||
| 	PUSH D
 | ||
| 	PUSH B
 | ||
| 	PUSH PSW
 | ||
| 	MVI C,6
 | ||
| 	MOV E,A
 | ||
| 	CALL 5
 | ||
| 	POP PSW
 | ||
| 	POP B
 | ||
| 	POP D
 | ||
| 	POP H
 | ||
| 	RET
 | ||
| #endasm
 | ||
| /* using this C code instead of inline will slow things down perceptibly */
 | ||
| /*	puts( getprfx() );
 | ||
| 	if  ( ybefore() ) {
 | ||
| 		putc(y + getyofs(),0); putc(x + getxofs(),0);
 | ||
| 	}
 | ||
| 	else {
 | ||
| 		putc(x + getxofs(),0); putc(y + getyofs(),0);
 | ||
| 	}
 | ||
| */
 | ||
| } /* end: gotoxy */
 | ||
| 
 | ||
| 
 | ||
| crtinit()     { 
 | ||
| 	if (kaypro) puts( getkini() );
 | ||
| 	else puts ( getcini() ); 
 | ||
| }  /* here, turn 2 byte graph chars on */
 | ||
| 
 | ||
| crtexit()     { 
 | ||
| 	if (kaypro) puts( getkxit() );
 | ||
| 	else puts ( getcxit() ); 
 | ||
| }  /* and off again on exit...         */
 | ||
| 
 | ||
| clr_screen()  { puts ( getcls() );  }  /* standard */
 | ||
| 
 | ||
| on_cursor()   { 
 | ||
| 	if (kaypro) puts( getkon() );
 | ||
| 	else puts ( getcon() );  
 | ||
| }  /* optional "hide cursor" command   */
 | ||
| 
 | ||
| off_cursor()  { 
 | ||
| 	if (kaypro) puts( getkof() );
 | ||
| 	else puts ( getcof() );  
 | ||
| }  /* optional "show cursor" command   */
 | ||
| 
 | ||
| 
 | ||
| #include "printf.c"
 | ||
| 
 | ||
| extern char *bgversion;
 | ||
| 
 | ||
| char *backtalk[] = {
 | ||
| 	"VILLIERS:  At your service!",
 | ||
| 	"LOUISA:  Delighted!",
 | ||
| 	"TORVE:  Is interesting line of occurrence.  Thurb!",
 | ||
| 	"Copyright (c) 1985 by The Xochitl Sodality Wonders & Marvels Committee",
 | ||
| 	};
 | ||
| 
 | ||
| int list[2][28];  /* two dice, two lists */
 | ||
| 
 | ||
| struct board { 
 | ||
| 	int stones,  /* number of stones on that point */
 | ||
| 	    owner,   /* and whose they are */
 | ||
| 	    x,y,         /*  x and y coordinates of point base */
 | ||
| 	    lastx,lasty, /* last location drawn on this point */
 | ||
| 	    cx,cy;       /* coordinates for column numbers */
 | ||
| 	}
 | ||
| 	point[28], bdsave[28]; /* 24 points, plus 2 bars, 2 homes */
 | ||
| 
 | ||
| 
 | ||
| struct { int cube, whosecube; } doubles;
 | ||
| 
 | ||
| 
 | ||
| struct { int fr,to,flag; } pending;
 | ||
| 
 | ||
| 
 | ||
| int level, dice[2], myscore, yrscore, player, movesleft, cantuse, myturns,
 | ||
|     swapped, tswap, deciding, expert, tone, show, moremsgline, 
 | ||
|     firstmove, helpdisabled, yrdice, lookforit, startcubevalue;
 | ||
| 
 | ||
| char *token1, *token2, chatter[80], buzzard[8];
 | ||
| 
 | ||
| /*======================================================================
 | ||
| 
 | ||
|   OPPONENT -- A little scenario, in which to select level of play
 | ||
| 
 | ||
| ========================================================================*/
 | ||
| 
 | ||
| 
 | ||
| char *chooseplayer() {
 | ||
| int ch; char *q;
 | ||
| 
 | ||
| 	dissemble();
 | ||
| loo:	ch = getkey();
 | ||
| 	switch (ch) {
 | ||
| 		case 'A':
 | ||
| 		case 'V': { level = 0; break; }
 | ||
| 		case 'L':
 | ||
| 		case 'P': { level = 1; break; }
 | ||
| 		case 'T': { level = 2; break; }
 | ||
| 		default:  goto loo;
 | ||
| 	}
 | ||
| 	q = backtalk[ level ];
 | ||
| 	clr_screen(); draw_board(q);
 | ||
| 	return(q);
 | ||
| 
 | ||
| } /* end: chooseplayer */
 | ||
| 
 | ||
| /*====================================================================
 | ||
|                               MAIN
 | ||
| ======================================================================*/
 | ||
| 
 | ||
| main() {
 | ||
| static int ch;
 | ||
| static char *p1 = "P(lay, R(everse, S(wap, A(rrange, N(ew, G(raphic, Q(uit ",
 | ||
| 	    *p2 = "U(se %s dice, O(pponent, X(pert, B(eep, C(ount, Z(ero ",
 | ||
| 	    *myline;
 | ||
| 
 | ||
| 	level = MYLEVEL + 1; /* fetch copyright notice */
 | ||
| 	setup(); 
 | ||
|         hint();
 | ||
| 
 | ||
| 	while (TRUE) {
 | ||
| 		moremsgline = FALSE;  /* show first command line on entry */
 | ||
| 		debug(""); /* erase messages */
 | ||
| 		myline = backtalk[ level ];  /* did level change? */
 | ||
| 		msg( myline );
 | ||
| 		firstmove = TRUE;
 | ||
| 		newboard(); /* note, sets starting cube value to 1 */
 | ||
| 
 | ||
| 		deciding = TRUE;
 | ||
| 		while (deciding) {
 | ||
| 
 | ||
| 		/* display command line */
 | ||
| 
 | ||
| 		off_cursor(); 
 | ||
| 		if (show) { mytotal(); yrtotal(); }
 | ||
| 		else { gotoxy(0,3); puts("   "); gotoxy(0,19); puts("   "); }
 | ||
| 		if (tone) beep();
 | ||
| 		if (expert) msg("Your pleasure? ");
 | ||
| 		else {
 | ||
| 			msg("Select:  ");
 | ||
| 			if (moremsgline) printf(p2,(yrdice? "my": "your")); 
 | ||
| 			else printf(p1); 
 | ||
| 		}
 | ||
| 
 | ||
| 		/* get response and do it */
 | ||
| 
 | ||
| 
 | ||
| 		ch = getkey();
 | ||
| 		switch (ch) {
 | ||
| 			case 'Q': {  /* quit play, exit to CP/M */
 | ||
| 				finishup(); 
 | ||
| 				break; 
 | ||
| 				}
 | ||
| 			case 'A': {  /* arrange stones (or cheat?) */
 | ||
|                                 /* play is suspended, so don't use the long */
 | ||
|                                 /* messages that assist game play */
 | ||
| 				helpdisabled = TRUE;
 | ||
| 				arrange();
 | ||
|                                 update();
 | ||
|                                 debug("");
 | ||
| 				break;
 | ||
| 				}
 | ||
| 			case 'R': {  /* mirror board image */
 | ||
| 				reverse();
 | ||
| 				update();
 | ||
| 				break;
 | ||
| 				}
 | ||
| 			case 'C': {  /* show mytotal, yrtotal counts */
 | ||
| 				show ^= TRUE;
 | ||
| 				break;
 | ||
| 				}
 | ||
| 			case 'S': {  /* SWAP Command - exchange stones */
 | ||
| 				swaptokens();
 | ||
| 				update(); 
 | ||
| 				break;
 | ||
| 				}
 | ||
| 			case 'B': { /* kill the beep */
 | ||
| 				tone ^= TRUE; break;
 | ||
| 				}
 | ||
|                         case 'X': /* expert mode toggle(s) */ 
 | ||
| 			case 27 : {
 | ||
| 				expert ^= TRUE; 
 | ||
| 				break;
 | ||
| 				}
 | ||
| 			case 'D': {  /* use my dice or your dice? */
 | ||
| 			case 'U':
 | ||
| 				yrdice ^= TRUE;
 | ||
| 				break;
 | ||
| 				}
 | ||
| 			case 'O': {  /* change opponents and skill level */
 | ||
| 				myline = chooseplayer();
 | ||
| 				deciding = FALSE;
 | ||
| 				break;
 | ||
| 				}
 | ||
| 			case 'P': {  /* play the game as board is arranged */
 | ||
| 				helpdisabled = FALSE;
 | ||
| 				if (level > MYLEVEL ) {
 | ||
| 					myline = chooseplayer();
 | ||
| 					update();
 | ||
| 				}
 | ||
| 				play(); 
 | ||
| 				break;
 | ||
| 				}
 | ||
|                         case 'G':  /* graphic screen switch, same as: */
 | ||
| 			case 11: { /* control-K, toggle Kaypro '84 display */
 | ||
| 				crtexit();
 | ||
| 				kaypro ^= TRUE;
 | ||
| 				crtinit();
 | ||
| 				swaptokens(); swaptokens();
 | ||
| 				swaptokens(); swaptokens();
 | ||
| 				update();
 | ||
| 				break;
 | ||
| 			}
 | ||
| 			case 'N': {  /* abandon game without quitting */
 | ||
| 				deciding = FALSE; player = 0;
 | ||
| 				break;
 | ||
| 				}
 | ||
| 			case 'Z': { /* zero the score */
 | ||
| 				myscore = yrscore = 0; putscore();
 | ||
| 				break;
 | ||
| 				}
 | ||
| 			default:  { moremsgline ^= TRUE; break; }
 | ||
| 		}}
 | ||
| 	}
 | ||
| } /* end: main */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| /*=======================================================================
 | ||
| 
 | ||
|    PLAY Command - this is the command that initiates the 2-player game
 | ||
| 
 | ||
| =========================================================================*/
 | ||
| 
 | ||
| play() {
 | ||
| 
 | ||
| /* any vars here MUST be STATIC */
 | ||
| 
 | ||
| #asm
 | ||
| 	MOV	B,H	;set up to exit this function by saving caller's
 | ||
| 	LXI	H,0	;stack pointer for use in deeply-nested scope
 | ||
| 	DAD	SP	;WARNING: this function must not use dynamic variables
 | ||
| 	SHLD	_fool	;CAUTION: risky business if called with parameters??
 | ||
| 	MOV	H,B	;HL is restored on general principles, BC is trashed
 | ||
| #endasm
 | ||
| 
 | ||
| 	whofirst(); 
 | ||
| 	taketurns();
 | ||
| 
 | ||
| } /* end: play */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| whofirst() {
 | ||
| int ch, myval, yrval;
 | ||
| 
 | ||
| 	if (yrdice || (player < 0)) {        /* board has been re-arranged */
 | ||
| 		msg("Is it my "); puts(token1);
 | ||
| 		puts(" turn or your "); puts(token2);
 | ||
| 		puts(" turn? ");
 | ||
| 	loo:	ch = toupper(getc(0));
 | ||
| 		if (!(ch == 'M' || ch == 'Y')) goto loo;
 | ||
| 		if (ch == 'M') player = YU;  /* player says Me, of course! */
 | ||
| 		else player = ME;
 | ||
| 		rolldice(player);
 | ||
| 	}
 | ||
| 
 | ||
| 	else if (player == 0) {  /* fresh start, roll the dice */
 | ||
| 		barcube();
 | ||
| 	zoo:	debug("Tossing for first turn...");
 | ||
| 		wipedice();
 | ||
| 		rolldice(ME);
 | ||
| 		myval = dice[0];
 | ||
| 		rolldice(YU);  
 | ||
| 		yrval = dice[0];
 | ||
| 		if (myval == yrval) {
 | ||
| 			if (tone) beep();
 | ||
|                         off_cursor();
 | ||
|                         gotoxy(37,11); puts("[___]"); sleep(3);
 | ||
|                         startcubevalue *= 2;
 | ||
|                         if (startcubevalue > 8) startcubevalue = 8;
 | ||
| 			doubles.cube = startcubevalue;
 | ||
| 			gotoxy(37,11);
 | ||
| 			if (doubles.cube < 16) printf("[ %d ]",doubles.cube);
 | ||
| 			else printf("[%03d]",doubles.cube); 
 | ||
|                         if (startcubevalue < 9) {
 | ||
| 			    msg("Double the cube!");
 | ||
| 			    sleep(20);
 | ||
|                         }
 | ||
| 			goto zoo;
 | ||
| 		}
 | ||
| 		else if (myval < yrval) player = YU;
 | ||
| 		else player = ME;
 | ||
| 		dice[0] = max(myval,yrval); dice[1] = min(myval,yrval);
 | ||
| 	}
 | ||
| 	/* otherwise, continue with last dice rolled as play is resumed */
 | ||
| 
 | ||
| } /* end: whofirst */
 | ||
| 
 | ||
| 
 | ||
| getmove() {  
 | ||
| static int i, ch, temp, happy;
 | ||
| 
 | ||
| 	cantuse = ERROR;  /* important for human player in tellmove */
 | ||
| 	movesleft = 2;
 | ||
| 	if (dice[0] == dice[1]) movesleft += 2;
 | ||
| 	temp = movesleft;		
 | ||
| 
 | ||
| 	getlist(); saveboard(); lookforit = TRUE;
 | ||
| 
 | ||
| 	if ( nomove() ) {
 | ||
| 		debug("All "); 
 | ||
| 		if (player == ME) puts("my"); else puts("your");
 | ||
| 		puts(" moves are blocked!");
 | ||
| 		if (player == YU) sleep(20);
 | ||
| 		return;
 | ||
| 	}
 | ||
| 
 | ||
| 
 | ||
| 	if (player == ME) {
 | ||
| 		/* handle doubles as two consecutive, independent moves */
 | ||
| 		setchat("I move"); debug(chatter);
 | ||
| 		if (movesleft == 4) myturns = 2; else myturns = 1;
 | ||
| 		clrpend();
 | ||
| 		while (myturns > 0) {
 | ||
| 			cantuse = ERROR;
 | ||
| 			movesleft = 2;
 | ||
| 			while (movesleft > 0) { getlist(); mymove(); }
 | ||
| 			myturns--;
 | ||
| 		}
 | ||
| 		strcat(chatter,"\008."); debug(chatter);
 | ||
| 	}
 | ||
| 	else {  /* allow the human to take back a bad board position */
 | ||
| 		happy = FALSE;
 | ||
| 		while (!happy) {
 | ||
| 			while (movesleft > 0) { getlist(); yrmove(); }
 | ||
| 			msg("All ok?  Y/N ");
 | ||
| 			do {
 | ||
| 				ch = getkey();
 | ||
| 			} while (ch != 'N' && (ch != 'Y' && ch != '\n'));
 | ||
| 			if ( ch == 'N' ) {
 | ||
| 				msg("Ok, as it was...");
 | ||
| 				restoreboard();
 | ||
| 				update();
 | ||
| 				movesleft = temp;
 | ||
| 				cantuse = ERROR;
 | ||
| 			}
 | ||
| 			else happy = TRUE;
 | ||
| 			debug("");
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| } /* end: getmove */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| taketurns() { 
 | ||
| 
 | ||
| 	while (TRUE) {  /* NO EXIT!  Only a Win or player ESC can exit */
 | ||
| 
 | ||
| 		getmove();
 | ||
| 		player = other(player);
 | ||
| 		if ( player == ME ) {
 | ||
| 			if ( endgame() ) {
 | ||
| 				if (topstone(ME) < 6 && cubeval()) idouble();
 | ||
| 				else if ( mytotal() < (yrtotal() - 8) ) {
 | ||
| 					idouble();
 | ||
| 				}
 | ||
| 			}
 | ||
| 			else if ( bearoff() ) idouble();
 | ||
| 		}
 | ||
| 		rolldice (player); 
 | ||
| 	}
 | ||
| 
 | ||
| } /* end: taketurns */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| /*=============================================*/
 | ||
| /*     Y O U R   M O V E                       */
 | ||
| /*=============================================*/
 | ||
| 
 | ||
| tellmove() {  /* show what the player's current dice are */
 | ||
| int k,n;
 | ||
| 
 | ||
| 	n = movesleft;
 | ||
| 	debug("You "); 
 | ||
| 	if (!expert) { puts(token2); puts(" "); }
 | ||
| 	puts("have "); 
 | ||
| 	while (n--) {
 | ||
| 		if (dice[0] == dice[1]) k = dice[0];  /* doubles? */
 | ||
| 
 | ||
| 		else if (cantuse == 1) k = dice[0];
 | ||
| 		else if (cantuse == 0) k = dice[1];
 | ||
| 		else if (n == 1) k = dice[1];	/* 2 of 2? */
 | ||
| 		else k = dice[0];		/* 1 of 2? */
 | ||
| 
 | ||
| 		printf("[%d] ",k);
 | ||
| 	}
 | ||
| 	puts("left");
 | ||
| 	if (expert) puts("."); else puts(", moving from high to low.");
 | ||
| 
 | ||
| } /* end: tellmove */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| yrmove() {
 | ||
| 
 | ||
| 	if ( nomove() ) {
 | ||
| 		debug("You have no more moves in this line of play.");
 | ||
| 		movesleft = 0;
 | ||
| 		return;
 | ||
| 	}
 | ||
| 	else {
 | ||
| loo:		tellmove();
 | ||
| 		if (!getyrmove()) {
 | ||
|                      hint(); 
 | ||
|                      goto loo;
 | ||
|                 }
 | ||
| 		debug("");
 | ||
| 	}
 | ||
| 
 | ||
| } /* end: yrmove */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| hint() {
 | ||
|     if (!expert) { 
 | ||
|         debug("HELP, BAR and HOME are useful words in this game.");
 | ||
|         sleep(10);
 | ||
|     }
 | ||
| } /* end: hint */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| nomove() {
 | ||
| int i,j;
 | ||
| 	for (i = 0; i < 2; i++)
 | ||
| 		for (j = 0; j < 28; j++)
 | ||
| 			if (list[i][j] != ERROR) return (FALSE);
 | ||
| 	return (TRUE);
 | ||
| } /* end: nomove */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| getyrmove() {  
 | ||
| int fpoint, tpoint;
 | ||
| 
 | ||
| 
 | ||
| 	firstmove = FALSE; /* I got it, I got it! */
 | ||
| 
 | ||
| 	msg("Move from? ");
 | ||
| 	fpoint = getpt(YRBAR,YRHOME);
 | ||
| 	if ( fpoint == ERROR ||
 | ||
| 	     (list[0][fpoint] == ERROR && list[1][fpoint] == ERROR)) {
 | ||
| 			return (FALSE);
 | ||
| 	}
 | ||
| 	puts(" To? ");
 | ||
| 	tpoint = getpt(YRBAR,YRHOME);
 | ||
| 	if ( tpoint == ERROR || 
 | ||
| 	     (list[0][fpoint] != tpoint && list[1][fpoint] != tpoint)) {
 | ||
| 			return (FALSE);
 | ||
| 	}
 | ||
| 
 | ||
| 	movestone( fpoint, tpoint );
 | ||
| 	if (movesleft < 2) {
 | ||
| 		if (list[0][fpoint] == tpoint) cantuse = 0;
 | ||
| 		else cantuse = 1;
 | ||
| 	}
 | ||
| 	return (TRUE);
 | ||
| 
 | ||
| } /* end: getyrmove */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| /*====== Functions That Make The Selected Move ======*/
 | ||
| 
 | ||
| 
 | ||
| setchat( p ) char *p; {
 | ||
| 	strcpy(chatter,p);
 | ||
| } /* end: setchat */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| putdice( f,t) int f,t; {
 | ||
| static char q[15];
 | ||
| 	if (t == MYHOME) sprintf(q," %d to Home,",25 - f);
 | ||
| 	else if (f == MYBAR) sprintf(q," Bar to %d,",25 - t);
 | ||
| 	else sprintf(q," %d to %d,",25 - f, 25 - t);
 | ||
| 	strcat(chatter,q);
 | ||
| 	debug(chatter);  /* avoid using save_cursor() */
 | ||
| } /* end: putdice */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| lurch( f, t, zlist ) int f, t, zlist; {
 | ||
| 	movestone( f, t );   /* move the stone */
 | ||
| 	putdice(f,t); 	     /* tell user, the action is a bit fast */
 | ||
| 	if (movesleft < 2) cantuse = zlist;
 | ||
| } /* end: lurch */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| /*****************************/
 | ||
| /* must LINK to GAMEPLAN.REL */
 | ||
| /*****************************/
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| /*==========================================================================
 | ||
| 
 | ||
|   GETLIST -- Find the possible moves for any particular throw of the dice
 | ||
| 
 | ||
| ===========================================================================*/
 | ||
| 
 | ||
| 
 | ||
| checkpips( whichlist, ptimon, pips, tops ) 
 | ||
| int whichlist, ptimon, pips, tops; {
 | ||
| static int j,k;
 | ||
| 
 | ||
| 	if ( !ptimon ) {
 | ||
| 		j = whosebar(player); 	/* 0 if you, 25 if me  */
 | ||
| 		k = abs(j - pips);
 | ||
| 	}
 | ||
| 	else if (player == ME) {
 | ||
| 		j = ptimon;
 | ||
| 		k = j - pips;
 | ||
| 		if (k < 1) k = MYHOME;
 | ||
| 	}
 | ||
| 	else {
 | ||
| 		j = 25 - ptimon;
 | ||
| 		k = j + pips;
 | ||
| 		if (k > 24) k = YRHOME;
 | ||
| 	}
 | ||
| 
 | ||
| 	if (point[j].stones > 0 && point[j].owner == player) {
 | ||
| 
 | ||
| 		/* no move to a blocked point */
 | ||
| 		if (point[k].owner != player && point[k].stones > 1)
 | ||
| 			return;
 | ||
| 
 | ||
| 		/* no move home if i can't bear off yet */
 | ||
| 		if (k == whosehome(player) && cantbearoff(j,pips,tops)) 
 | ||
| 			return;
 | ||
| 
 | ||
| 		/* no other move is allowed if i'm on the bar */
 | ||
| 		if (tops == 25 && j != whosebar(player))
 | ||
| 			return;
 | ||
| 
 | ||
| 		/* the move is legal (but maybe not optimal) */
 | ||
| 		list [whichlist] [j] = k;
 | ||
| 	}
 | ||
| 
 | ||
| } /* end: checkpips */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| build( whichlist, pips ) int whichlist, pips; {
 | ||
| int i, tops;
 | ||
| 
 | ||
| 	if (whichlist == cantuse) return;
 | ||
| 	tops = topstone(player);
 | ||
| 	for (i = 0; i < 25; i++) checkpips( whichlist, i, pips, tops );
 | ||
| 
 | ||
| } /* end: build */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| getlist() {  /* find all legal moves using these dice */
 | ||
| static int i, j;
 | ||
| 
 | ||
| 	for (i = 0; i < 2; i++)  /* initialize the lists */
 | ||
| 		for (j = 0; j < 28; j++)
 | ||
| 			list [i] [j] = ERROR;
 | ||
| 
 | ||
| 	build( 0, dice[0] ); /* usually the low die  */
 | ||
| 	build( 1, dice[1] ); /* usually the high die */
 | ||
| 
 | ||
| } /* end: getlist */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| /*==========================================================================
 | ||
| 
 | ||
|   EVALUATE UTILITIES -- Functions for legal and/or best play, telling who's
 | ||
| 			who, who's ahead, who won, etc. etc.
 | ||
| 
 | ||
| ============================================================================*/
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| mytotal() {
 | ||
| int i, cnt;
 | ||
| 
 | ||
| 	cnt = 0;
 | ||
| 	for (i = 0; i < 26; i++) {
 | ||
| 		if (point[i].owner == ME) cnt += point[i].stones * i;
 | ||
| 	}
 | ||
| 	if (show) {
 | ||
| 	gotoxy(0,3); printf("%03d",cnt); 
 | ||
| 	}
 | ||
| 	return (cnt);
 | ||
| 
 | ||
| } /* end: mytotal */
 | ||
| 
 | ||
| 
 | ||
| yrtotal() {
 | ||
| int i, cnt;
 | ||
| 
 | ||
| 	cnt = 0;
 | ||
| 	for (i = 0; i < 26; i++) {
 | ||
| 		if (point[i].owner == YU) cnt += point[i].stones * (25 - i);
 | ||
| 	}
 | ||
| 	if (show) {
 | ||
| 	gotoxy(0,19); printf("%03d",cnt); 
 | ||
| 	}
 | ||
| 	return (cnt);
 | ||
| 
 | ||
| } /* end: yrtotal */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| topstone( who )  int who; {
 | ||
| static int i,j;
 | ||
| 
 | ||
| 	if (point[ whosebar( who ) ].stones > 0) i = 25;
 | ||
| 	else {
 | ||
| 		i = 24;
 | ||
| 		while (i > 0) {
 | ||
| 			if (who == ME) j = i; else j = 25 - i;
 | ||
| 			if (point[j].stones > 0 &&
 | ||
| 			    point[j].owner == who) return (i);
 | ||
| 			--i;
 | ||
| 		}
 | ||
| 	}
 | ||
| 	return (i);  /* return normalized value, 1 - 25, 0 is home */
 | ||
| 
 | ||
| }  /* end: topstone */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| cantbearoff( mypt, pips, tops ) int mypt, pips, tops; {
 | ||
| 
 | ||
| 	/* My destination is Home, but can I do it??? */
 | ||
| 	if (mypt > 6) mypt = 25 - mypt;  /* normalize inner table */
 | ||
| 
 | ||
| 	/* I can't bear off if there's anybody still not in my inner table */
 | ||
| 	if (tops > 6) return (TRUE);
 | ||
| 
 | ||
| 	/* If I'm the highest blot in my own table, I CAN bear off */
 | ||
| 	if (tops == mypt) return (FALSE);
 | ||
| 
 | ||
| 	/* If I'm NOT high, I have to have an exact roll to get away with it */
 | ||
| 	return ( (mypt != pips) );
 | ||
| 
 | ||
| } /* end: cantbearoff */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| whosebar( who ) int who; {
 | ||
| 	return ( (who == ME? MYBAR: YRBAR) );
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| whosehome( who ) int who; {
 | ||
| 	return ( (who == YU? YRHOME: MYHOME) );
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| checkwin() {  
 | ||
| 	if (mytotal() == 0) winner(ME, topstone(YU));
 | ||
| 	if (yrtotal() == 0) winner(YU, topstone(ME));
 | ||
| } /* end: checkwin */
 | ||
| 
 | ||
| 
 | ||
| other( color ) int color; {
 | ||
| 
 | ||
| 	if (color == ME) return (YU); else return (ME);
 | ||
| 
 | ||
| } /* end: otherplayer */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| winner( who, high ) int who, high; {
 | ||
| int gammon; char ch;
 | ||
| 
 | ||
| 	if (tone) beep();
 | ||
| 	if (point[ whosehome( other(who) )].stones > 0) gammon = 1;
 | ||
| 	else gammon = 2;			/* nothing off is a gammon! */
 | ||
| 	if (high == 0) gammon = 1;		/* someone doubled */
 | ||
| 	else if (high > 18) gammon = 3; 	/* backgammon! */
 | ||
| 
 | ||
| 
 | ||
| 	debug("");
 | ||
| 	if (who == ME) puts("I"); else puts("You"); puts(" win"); 
 | ||
| 	switch (gammon) {
 | ||
| 		case 1: { puts("!"); break; }
 | ||
| 		case 2: { puts(" a Gammon!"); break; }
 | ||
| 		case 3: { puts(" a Backgammon!"); break; }
 | ||
| 	}
 | ||
| 	gammon *= doubles.cube;
 | ||
| 	switch (who) {
 | ||
| 		case ME: { myscore += gammon; break; }
 | ||
| 		case YU: { yrscore += gammon; break; }
 | ||
| 	}
 | ||
| 	putscore();
 | ||
| 	msg("Hit ESC to resume play"); 
 | ||
| 	loo:  ch = getc(0); if (ch != 27) goto loo;
 | ||
| 	player = 0;
 | ||
| 	reverse(); deciding = FALSE; jumpjack();
 | ||
| 
 | ||
| } /* end: winner */
 | ||
| 
 | ||
| 
 | ||
| putscore() {
 | ||
| 	gotoxy(39,0); blanks(39); gotoxy(53,0);
 | ||
| 	printf("SCORE:  You %d, Me %d",yrscore,myscore);
 | ||
| } /* end: putscore */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| /*========================================================================
 | ||
| 
 | ||
|   REVERSE Command - allow player to take the opponent's viewpoint of the
 | ||
|                     board layout, mirror the board.  Returns board layout
 | ||
| 		    to the arranged position as seen from opposite side.
 | ||
| 
 | ||
| ==========================================================================*/
 | ||
| 
 | ||
| reverse() {
 | ||
| int cnt1, cnt2, cnt3, cnt4;
 | ||
| 
 | ||
| 	off_cursor();
 | ||
| 	cnt1 = point[MYHOME].stones;  /* save counts for erase */
 | ||
| 	cnt2 = point[YRHOME].stones;
 | ||
| 	cnt3 = point[MYBAR].stones;
 | ||
| 	cnt4 = point[YRBAR].stones;
 | ||
| 
 | ||
| 	putstone( MYHOME, 0, 0);  /* erase old trays before update */
 | ||
| 	putstone( YRHOME, 0, 0);
 | ||
| 
 | ||
| 	halfswap(1); halfswap(13);
 | ||
| 
 | ||
| 	gotoxy(0,11);  puts("    ");  /* erase HOME message */
 | ||
| 	gotoxy(75,11); puts("    ");
 | ||
| 	if (point[1].x < 40) {
 | ||
| 		point[MYHOME].x = point[YRHOME].x = 0;
 | ||
| 	}
 | ||
| 	else {
 | ||
| 		point[MYHOME].x = point[YRHOME].x = 75;
 | ||
| 	}
 | ||
| 
 | ||
| 	point[MYHOME].owner  = point[MYBAR].owner = ME; /* restore counts */
 | ||
| 	point[YRHOME].owner  = point[YRBAR].owner = YU;
 | ||
| 	point[MYHOME].stones = cnt1;
 | ||
| 	point[YRHOME].stones = cnt2;
 | ||
| 	point[MYBAR].stones  = cnt3;
 | ||
| 	point[YRBAR].stones  = cnt4;
 | ||
| 
 | ||
| } /* end: reverse */
 | ||
| 
 | ||
| 
 | ||
| #asm
 | ||
| 	DB 'Gammon IV concept & text graphic rendition by David C. Oshel',0
 | ||
| 	DB 'MidSummer''s Day, June 21, 1985',0
 | ||
| 	DB 'To Whomever Destroys This Notice -- Nothing Shall Happen, Forever',0
 | ||
| #endasm
 | ||
| 
 | ||
| 
 | ||
| halfswap( n ) int n; {
 | ||
| static int i, j, k, o;
 | ||
| 
 | ||
| 	o = n + 6;
 | ||
| 	for (i = n; i < o; i++) {
 | ||
| 		j = ((o * 2) - 1) - i;
 | ||
| 		k = point[i].x;
 | ||
| 		point[i].cx = point[i].x = point[j].x;
 | ||
| 		point[j].cx = point[j].x = k;
 | ||
| 	}
 | ||
| 
 | ||
| } /* end: halfswap */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| getkey() {
 | ||
| int ch;
 | ||
| 	ch = getc(0); /* keybounce? */
 | ||
| 	while ( (ch = toupper(getc(0))) == 0 ) acg(); return (ch);
 | ||
| } /* end: getkey */
 | ||
| 
 | ||
| 
 | ||
| wipedice() {
 | ||
| static char *s = "                  ";
 | ||
| 
 | ||
| 	off_cursor();
 | ||
| 	gotoxy(47,11); puts(s);	 /* erase dice roll messages */
 | ||
| 	gotoxy(12,11); puts(s);	
 | ||
| 
 | ||
| } /* end: wipedice */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| update() {
 | ||
| int i, x, c;
 | ||
| 
 | ||
| 	for (i = 1; i < 25; i++) {
 | ||
| 		gotoxy(point[i].cx,point[i].cy);
 | ||
| 		printf("%2d",25 - i);
 | ||
| 	}
 | ||
| 	if (doubles.whosecube == YU) yrcube(doubles.cube); 
 | ||
| 	else if (doubles.whosecube == ME) mycube(doubles.cube);
 | ||
| 	else barcube();
 | ||
| 	for (i = 0; i < 28; i++) {
 | ||
| 		x = point[i].stones;
 | ||
| 		c = point[i].owner;
 | ||
| 		putstone(i,x,c);
 | ||
| 	}
 | ||
| 	if (point[1].x < 40) x = 0; else x = 75;
 | ||
| 	gotoxy(x,11); puts("HOME");
 | ||
| 
 | ||
| 	putscore(); mytotal(); yrtotal();
 | ||
| 
 | ||
| } /* end: update */
 | ||
| 
 | ||
| 
 | ||
| dissemble() {
 | ||
| 
 | ||
| 	clr_screen();  
 | ||
| 
 | ||
| puts("You are a passenger in the \"Orion\" bound for STAR WELL, a slightly disreputable\n");
 | ||
| puts("planetoid in the Flammarion Rift, where you have a scheduled layover of several\n");
 | ||
| puts("hours.  You enter the Casino there, desperately bored.  Your attention is \n");
 | ||
| puts("immediately drawn to an unusual trio.  Your instinct for good company (they \n");
 | ||
| puts("are playing Backgammon) leads you easily into a round of introductions:\n");
 | ||
| puts("\n");
 | ||
| puts("LOUISA PARINI -- A young woman who gives you the uncomfortable feeling that she\n");
 | ||
| puts("is even younger than she looks.  In fact, she is the offspring of a clan of\n");
 | ||
| puts("noted interstellar jewel thieves and con artists, on her way to a famous\n");
 | ||
| puts("girl's finishing school on Nashua.  She has larceny in her soul, but she is on\n");
 | ||
| puts("holiday.  Do not underestimate her.  (But you will, of course.  You must.)\n");
 | ||
| puts("\n");
 | ||
| puts("ANTHONY VILLIERS -- A mysterious young fop with impeccable manners, and (you\n");
 | ||
| puts("notice) an even more impeccable dueling saber at his side.  There is something\n");
 | ||
| puts("between Louisa and him.  His conversation is light, witty and just slightly\n");
 | ||
| puts("cynical, but you are not wrong to conclude that this is someone you can trust.\n");
 | ||
| puts("\n");
 | ||
| puts("TORVE THE TROG --  This blue-eyed, golden-furred entity is a member of the most\n");
 | ||
| puts("dangerous and unpredictable race in the galaxy (aside from humans).  You are \n");
 | ||
| puts("amazed that this one is allowed to travel. (In fact, Torve's papers were forged\n");
 | ||
| puts("by a member of Louisa's family and procured for him by Villiers).  Torve is\n");
 | ||
| puts("lost in some inner rapture, emitting soft \"Thurb\"-like noises.\n");
 | ||
| puts("\n(Hit any key to continue)");
 | ||
| getkey();
 | ||
| puts("\015You suggest a friendly game of backgammon, at small stakes, and your\n");
 | ||
| puts("companions agree instantly.  Who will be your opponent?  (L, V, or T) ");
 | ||
| 
 | ||
| } /* end: dissemble */
 | ||
| 
 | ||
| 
 | ||
| /*======================================================================
 | ||
| 
 | ||
|   ARRANGE Command:  Move stones around in the playing area.  Play will
 | ||
| 		    commence with this final arrangement.  Notice, this
 | ||
| 		    command allows for cheating because the line input
 | ||
|                     function traps Ctrl-C and executes jumpjack().  Player
 | ||
| 		    returns to the command line with the game frozen, may
 | ||
|                     re-arrange as desired, then resume play.  The Ctrl-C
 | ||
|                     trap will increment play level by 1.  The sufficiently
 | ||
|                     stupid player will not notice, and so may lose anyway.
 | ||
|                     Trap implemented in MYLIB2.C, not here.
 | ||
| 
 | ||
| ========================================================================*/
 | ||
| 
 | ||
| 
 | ||
| arrange() {  /* whoever calls arrange() must also call update() next */
 | ||
| 
 | ||
| /* any vars here MUST be STATIC */
 | ||
| 
 | ||
| #asm
 | ||
| 	MOV	B,H	;set up to exit this function by saving caller's
 | ||
| 	LXI	H,0	;stack pointer for use in deeply-nested scope
 | ||
| 	DAD	SP	;WARNING: this function must not use dynamic variables
 | ||
| 	SHLD	_fool	;CAUTION: risky business if called with parameters??
 | ||
| 	MOV	H,B	;HL is restored on general principles, BC is trashed
 | ||
| #endasm
 | ||
| 
 | ||
|         moveabout();
 | ||
| 
 | ||
| } /* end: arrange */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| moveabout() { 
 | ||
| 
 | ||
| 	player = -1;  /* flag to ask who moves first */
 | ||
| 	if (!expert)
 | ||
| 	   debug("Type BAR or HOME, or the Number of a Point.");
 | ||
| 
 | ||
| 	while ( TRUE ) { /* exit via jumpjack() by typing Control-C   */
 | ||
|            mytotal();
 | ||
|            yrtotal();
 | ||
|            revise();
 | ||
|         }
 | ||
| 
 | ||
| }  /* end: moveabout */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| getpt(b,h) int b,h; {
 | ||
| static char ans[6], *p; int x, d1, d2, look, try;
 | ||
| 
 | ||
| 	on_cursor();
 | ||
| 	gets(ans,5);
 | ||
| 	p = ans;
 | ||
| 	while (*p) *p = toupper(*p++);  /* capitalize string */
 | ||
|  	off_cursor();
 | ||
| 
 | ||
| 	if (!helpdisabled && ((index(ans,"HEL") != -1) || 
 | ||
| 			   (index(ans,"?") != -1))) {
 | ||
| 		x = ERROR;
 | ||
| 		msg("");
 | ||
| 		if (point[ whosebar(player) ].stones > 0) {
 | ||
| 			puts("You're on the Bar, so let's move that one!  BAR ");
 | ||
| 			x = b;
 | ||
| 		}
 | ||
| 		else {
 | ||
| 			puts("Are you ");
 | ||
| 			if (cantuse != 0 && cantuse != 1) puts("REALLY ");
 | ||
| 			puts("blocked?  Try moving From ");
 | ||
| 			look = 24;
 | ||
| 			while (look > 0) {
 | ||
| 				try = list[0][look];
 | ||
| 				if (try == ERROR) try = list[1][look];
 | ||
| 				if ( try != ERROR ) {
 | ||
| 					printf("%d To ",25-look);
 | ||
| 					if (try == YRHOME) printf("HOME");
 | ||
| 					else printf("%d",25-try);
 | ||
| 					look = 0;
 | ||
| 				}
 | ||
| 				look--;
 | ||
| 			}
 | ||
| 			sleep(40);
 | ||
| 		}
 | ||
| 	}
 | ||
| 	else if (index(ans,"B") != -1) x = b;
 | ||
| 	else if (index(ans,"H") != -1) x = h;
 | ||
| 	else {
 | ||
| 		x = atoi( ans );
 | ||
| 		if (x < 1 || x > 24) x = ERROR;
 | ||
| 		else x = 25 - x;  /* translate human to computer view */
 | ||
| 	}
 | ||
| 	return (x);
 | ||
| 
 | ||
| } /* end: getpt */
 | ||
| 
 | ||
| 
 | ||
| whoseit(p,a,b) char *p; int a,b; {
 | ||
| int ch;
 | ||
| 
 | ||
| 	if (point[a].stones > 0 && point[b].stones > 0) {
 | ||
| 		debug("Whose "); puts(p); puts("?  1 = "); 
 | ||
| 		puts(token1);
 | ||
| 		puts("  2 = "); puts(token2); puts(" ");
 | ||
| 	loo:	ch = getkey();
 | ||
| 		if (!(ch == '1' || ch == '2')) goto loo; 
 | ||
| 
 | ||
| 		debug("From "); 
 | ||
| 		if (ch == '1') { ch = a; puts(token1); }
 | ||
| 		else { puts(token2); ch = b; }
 | ||
| 		puts("'s "); puts(p);
 | ||
| 		return ( ch );
 | ||
| 	}
 | ||
| 	else if (point[a].stones > 0) return (a);
 | ||
| 	else if (point[b].stones > 0) return (b);
 | ||
| 	else return (ERROR);
 | ||
| 
 | ||
| } /* end: whoseit */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| revise() {
 | ||
| 	do {
 | ||
| 		setchat("Move a Stone From? ");
 | ||
| 		msg(chatter);
 | ||
| 	}
 | ||
| 	while ( !delightful() );
 | ||
| 
 | ||
| } /* end: revise */
 | ||
| 
 | ||
| 
 | ||
| delightful() {
 | ||
| static int from, to, fcnt, tcnt, fcolor, tcolor; 
 | ||
| 
 | ||
| 	from = getpt(MYBAR,MYHOME);
 | ||
| 
 | ||
| 	sprintf(buzzard,"%d",abs(25 - from));
 | ||
| 
 | ||
| 	if (from == MYBAR) {
 | ||
| 		from = whoseit("bar",MYBAR,YRBAR);
 | ||
| 		strcpy(buzzard,"BAR");
 | ||
| 	}
 | ||
| 
 | ||
| 	if (from == MYHOME) {
 | ||
| 		from = whoseit("home",MYHOME,YRHOME);
 | ||
| 		strcpy(buzzard,"HOME");
 | ||
| 	}
 | ||
| 
 | ||
| 	if (from == ERROR) {
 | ||
|                 debug("Type Control-C to quit.");
 | ||
| 		return( FALSE );
 | ||
| 	}
 | ||
| 
 | ||
|         /*-----------------------------------------------------*/
 | ||
|         /* establish the color of the stones on the from point */
 | ||
|         /*-----------------------------------------------------*/
 | ||
| 	fcolor = point[from].owner;
 | ||
| 
 | ||
|         /*--------------------------------------------------*/
 | ||
|         /* establish the number of stones on the from point */
 | ||
|         /*--------------------------------------------------*/
 | ||
|         fcnt = point[from].stones;
 | ||
| 
 | ||
| 	if (fcnt == 0) {
 | ||
| 		debug("What's the point?"); 
 | ||
|                 return(FALSE);
 | ||
| 	}
 | ||
| 
 | ||
| 	strcat(chatter,buzzard);  /* this avoids use of save_cursor() */
 | ||
| 	strcat(chatter," To? ");
 | ||
| 	msg(chatter);
 | ||
| 
 | ||
| 	to = getpt(MYBAR,MYHOME);
 | ||
| 
 | ||
| 	sprintf(buzzard,"%d",abs(25 - to));
 | ||
| 
 | ||
| 
 | ||
| 	if (to == MYBAR) {
 | ||
| 		if (fcolor == YU) to = YRBAR;
 | ||
| 		strcpy(buzzard,"BAR");
 | ||
| 	}
 | ||
| 
 | ||
| 	if (to == MYHOME) {
 | ||
| 		if (fcolor == YU) to = YRHOME;
 | ||
| 		strcpy(buzzard,"HOME");
 | ||
| 	}
 | ||
| 	
 | ||
| 	if (to == ERROR) {  
 | ||
| 		debug("Type Control-C to quit.");
 | ||
|                 return(FALSE);
 | ||
| 	}
 | ||
| 
 | ||
| 	if (from == to) {
 | ||
| 		debug("Quite easily done!"); 
 | ||
|                 return(FALSE);
 | ||
| 	}
 | ||
| 
 | ||
| 	strcat(chatter,buzzard);
 | ||
| 
 | ||
|         /*---------------------------------------------------*/
 | ||
|         /* establish the color of the stones on the to point */
 | ||
|         /*---------------------------------------------------*/
 | ||
| 	tcolor = point[to].owner;
 | ||
| 
 | ||
|         /*------------------------------------------------*/
 | ||
|         /* establish the number of stones on the to point */
 | ||
|         /*------------------------------------------------*/
 | ||
| 	tcnt = point[to].stones;
 | ||
| 
 | ||
| 	if (fcolor == tcolor || tcolor == 0) {
 | ||
| 		msg(chatter);
 | ||
|                 --fcnt;
 | ||
|                 ++tcnt;
 | ||
| 		putstone(from, fcnt, fcolor);  /* one less */
 | ||
| 		putstone(to,   tcnt, fcolor);  /* one more */
 | ||
| 		debug("");
 | ||
| 		return (TRUE);
 | ||
| 	}
 | ||
| 	else {
 | ||
| 		debug("Evict the other stone");
 | ||
| 		if (tcnt > 1) puts("s");
 | ||
| 		puts(" first!");
 | ||
| 		return (FALSE);
 | ||
| 	}
 | ||
| 
 | ||
| } /* end: delightful */
 | ||
| 
 | ||
| 
 | ||
| /*==========================================================================
 | ||
| 
 | ||
|   INITIALIZATION and NEWBOARD commands -- start of a new game, or cold
 | ||
| 
 | ||
| ===========================================================================*/
 | ||
| 
 | ||
| 
 | ||
| wipeout() {
 | ||
| static int i;
 | ||
| 
 | ||
| 	player = 0;
 | ||
| 	barcube();
 | ||
| 	for (i = 0; i < 28; i++) {
 | ||
| 		point[i].stones = point[i].owner = 0;
 | ||
| 	}
 | ||
| 	update();
 | ||
| 
 | ||
| } /* end: wipeout */
 | ||
| 
 | ||
| 
 | ||
| setup() {
 | ||
| static int i, j, k, copyright;
 | ||
| 
 | ||
| 	myscore = yrscore = player = dice[0] = dice[1] = 0;
 | ||
| 	kaypro = swapped = tswap = expert = helpdisabled = yrdice = FALSE;
 | ||
| 	show = moremsgline = tone = TRUE;
 | ||
| 
 | ||
| 	init_lib();
 | ||
| 	OFFinterrupt();  /* enable jumpjack() on ctl-c */
 | ||
| 	crtinit();         /* Kaypro video mode on (2-byte graphics) */
 | ||
| 	off_cursor();
 | ||
| 	token1 = getAtkn();
 | ||
| 	token2 = getBtkn();
 | ||
| 	copyright = backtalk[ MYLEVEL + 1 ];
 | ||
| 	draw_board( copyright );
 | ||
| 
 | ||
| 	for (i = 0; i < 28; i++) {
 | ||
| 		point[i].stones = point[i].owner = 0;
 | ||
| 		point[i].x = point[i].y = point[i].lastx = point[i].lasty = 0;
 | ||
| 		point[i].cx = point[i].cy = 0;
 | ||
| 	}
 | ||
| 
 | ||
| 	k = 68;
 | ||
| 	for (i = 1; i < 13; i++ ) { /* establish xy coords for the points */
 | ||
| 		j = 25 - i;
 | ||
| 		point[i].cx = point[j].cx = point[i].x = point[j].x = k; 
 | ||
| 		k -= 5;
 | ||
| 		point[i].y = 4;
 | ||
| 		point[j].y = 18;
 | ||
| 		point[i].cy = 2;
 | ||
| 		point[j].cy = 20;
 | ||
| 		if (k == 38) k -= 5; /* skip over bar */
 | ||
| 	}
 | ||
| 
 | ||
| 	point[MYBAR].x  = point[YRBAR].x  = 38;
 | ||
| 	point[MYHOME].x = point[YRHOME].x = 75;
 | ||
| 
 | ||
| 	point[MYBAR].y  = point[MYHOME].y =  5;
 | ||
| 	point[YRBAR].y  = point[YRHOME].y = 17;
 | ||
| 
 | ||
| } /* end: setup */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| newboard() {
 | ||
| static int i;  
 | ||
| 
 | ||
|         startcubevalue = 1;
 | ||
| 	wipedice(); wipeout();
 | ||
| 
 | ||
| 	putstone( MYHOME, 15, ME  );
 | ||
| 	putstone( YRHOME, 15, YU );
 | ||
| 
 | ||
| 	putstone( YRHOME, 13, YU );
 | ||
| 	putstone(  1, 2, YU );
 | ||
| 
 | ||
| 	putstone( YRHOME, 8, YU );
 | ||
| 	putstone( 12, 5, YU );
 | ||
| 
 | ||
| 	putstone( YRHOME, 5, YU );
 | ||
| 	putstone( 17, 3, YU );
 | ||
| 
 | ||
| 	putstone( YRHOME, 0, 0 ); 
 | ||
| 	putstone( 19, 5, YU );
 | ||
| 
 | ||
| 	putstone( MYHOME, 10, ME );
 | ||
| 	putstone(  6, 5, ME  );
 | ||
| 
 | ||
| 	putstone( MYHOME,  7, ME );
 | ||
| 	putstone(  8, 3, ME  );
 | ||
| 
 | ||
| 	putstone( MYHOME,  2, ME );
 | ||
| 	putstone( 13, 5, ME  );
 | ||
| 
 | ||
| 	putstone( MYHOME, 0, 0 );
 | ||
| 	putstone( 24, 2, ME  );
 | ||
| 
 | ||
| } /* end: newboard */
 | ||
| 
 | ||
| 
 | ||
| draw_board( c ) char *c; {
 | ||
| static int line;
 | ||
| static char *m = "     ", *picture[] = {
 | ||
| "The Peelgrunt Game of GAMMON IV%s\n\n\n",
 | ||
| ":=================================o=================================:\n",
 | ||
| ":: ..   \\/   ..   \\/   ..   \\/   |||  ..   \\/   ..   \\/   ..   \\/  ::\n",
 | ||
| "::                               |||                               ::\n",
 | ||
| ":: /\\   ..   /\\   ..   /\\   ..   |||  /\\   ..   /\\   ..   /\\   ..  ::\n"
 | ||
| };
 | ||
| 
 | ||
| 	clr_screen();
 | ||
| 	off_cursor();
 | ||
| 	msg( c );
 | ||
| 	off_cursor();
 | ||
| 	gotoxy(0,0);
 | ||
| 	puts(m); printf(picture[0],bgversion);
 | ||
| 	puts(m); puts(picture[1]);
 | ||
| 	for (line = 0; line < 6; line++) { 
 | ||
| 		puts(m); puts(picture[2]);
 | ||
| 	}
 | ||
| 	puts(m); puts(picture[3]);
 | ||
| 	puts(m); puts(picture[3]);
 | ||
| 	puts(m); puts(picture[3]);
 | ||
| 	for (line = 0; line < 6; line++) {
 | ||
| 		puts(m); puts(picture[4]);
 | ||
| 	}
 | ||
| 	puts(m); puts(picture[1]);
 | ||
| 
 | ||
| } /* end: draw_board */
 | ||
| 
 | ||
| 
 | ||
| /*=========================================================================
 | ||
| 
 | ||
|   DICE Commands:  How to roll the dice
 | ||
| 
 | ||
| ===========================================================================*/
 | ||
| 
 | ||
| 
 | ||
| peek() {
 | ||
| 	if (yrdice) return;  /* you know your own dice, probably...? */
 | ||
| 	if (expert) debug("");
 | ||
| 	else debug("The Dice will Rattle until you Roll.  Now on ");
 | ||
| 	getdice();
 | ||
| 	printf("[%d] [%d] ...",dice[0],dice[1]);
 | ||
| } /* end: peek */
 | ||
| 
 | ||
| 
 | ||
| getonedie() {
 | ||
| 	return ( (abs(acg()) % 6) + 1 );
 | ||
| } /* end: getonedie */
 | ||
| 
 | ||
| 
 | ||
| fixup() {	/* ensure that the low die is in dice[0] */
 | ||
| int d,e;
 | ||
| 	if (player == 0) return;  /* whofirst?  don't mess with the odds */
 | ||
| 	d = min(dice[0],dice[1]);
 | ||
| 	e = max(dice[0],dice[1]);
 | ||
| 	dice[0] = d;
 | ||
| 	dice[1] = e;
 | ||
| } /* end: fixup */
 | ||
| 
 | ||
| 
 | ||
| getdice() {
 | ||
| int ch;
 | ||
| 
 | ||
|         /* if it's MY dice we're using, generate random dice... */
 | ||
| 	if (!yrdice) {
 | ||
| 		dice[0] = getonedie(); 
 | ||
| 		acg();    /* bounce a little for luck */
 | ||
| 		dice[1] = getonedie();
 | ||
| 		fixup();
 | ||
| 		return;
 | ||
| 	}
 | ||
| 
 | ||
|         /* but if it's YOUR dice, then ask about the roll... */
 | ||
| zoo:	msg("<> ");
 | ||
| 	if (player == ME) puts("My"); else puts("Your");
 | ||
| 	puts(" roll: ");
 | ||
| 	if (!expert) puts("\008\008, using your dice: ");
 | ||
| 	puts(" First? ");
 | ||
| loo:	while ( (ch = getc(0)) == 0 );  /* don't bother acg */
 | ||
| 	if (ch == 3) haltgame();
 | ||
| 	ch -= '0';
 | ||
| 	if (ch < 1 || ch > 6) goto loo;
 | ||
| 	putc( ch + '0',0);
 | ||
| 	dice[0] = ch;
 | ||
| 
 | ||
| 	puts("  Second? ");
 | ||
| 	while ( (ch = getc(0)) == 0 );
 | ||
| 	if (ch == 3) haltgame();
 | ||
| 	ch -= '0';
 | ||
| 	if (ch < 1 || ch > 6) goto zoo;
 | ||
| 	putc( ch + '0',0);
 | ||
| 	dice[1] = ch;
 | ||
| 
 | ||
| 	puts("  All Ok? ");
 | ||
| roo:	while ( (ch = toupper(getc(0))) == 0 );  /* don't bother acg */
 | ||
|         if (ch == 3) haltgame();
 | ||
| 	if (ch != 'N' && ch != 'Y') goto zoo;
 | ||
| 	fixup();
 | ||
| 
 | ||
| } /* end: getdice */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| haltgame() {
 | ||
|         player = -1;  /* if play resumes, ask whose roll it is */
 | ||
|         hint();
 | ||
|         jumpjack(); 
 | ||
| 
 | ||
| } /* end: haltgame */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| rolldice( who ) int who; {
 | ||
| int waiting;
 | ||
| 
 | ||
| 	setchat("Your Turn: ");
 | ||
| 	if (!expert) {
 | ||
| 		if (!yrdice) strcat(chatter," P(eek,"); 
 | ||
| 		strcat(chatter," D(ouble, Q(uit, or <AnyKey> to Roll ");
 | ||
| 	}
 | ||
| 	if (player == YU) {  /* not executed if player == 0, i.e., whofirst */
 | ||
| 		waiting = TRUE; 
 | ||
| 		while (waiting) {
 | ||
| 			msg(chatter);
 | ||
| 			switch ( getkey() ) {
 | ||
|                             /* bailout is Ctrl-C instead of ESCape */
 | ||
| 				case  3 : { haltgame(); break; }
 | ||
| 				case 'P': { peek(); break; }
 | ||
| 				case 'D': { udouble(); break; }
 | ||
| 				case 'Q': { winner(ME,topstone(YU)); break; }
 | ||
| 				default : waiting = FALSE;
 | ||
| 		}	}
 | ||
| 		off_cursor(); debug(""); msg("");
 | ||
| 	}
 | ||
| 	highroller(who);
 | ||
| 
 | ||
| } /* end: rolldice */
 | ||
| 
 | ||
| highroller( who ) int who; {  /* parameter is not redundant */
 | ||
| static int y = 11;
 | ||
| static int xme, xyu;
 | ||
| 
 | ||
| 	/* get the values for two dice, either yours or mine */
 | ||
| 
 | ||
| 	getdice();
 | ||
| 
 | ||
| 	/* display the values of the dice in the board area */
 | ||
| 
 | ||
| 	off_cursor();
 | ||
| 	xme = 47; xyu = 12;    /* decide which half to show the values in */ 
 | ||
| 	if (point[1].x > 40) { 
 | ||
| 		xme = 12; 
 | ||
| 		xyu = 47; 
 | ||
| 	}
 | ||
| 	if (player) {
 | ||
| 		gotoxy(xyu,y); blanks(18); /* erase, if not whofirst */
 | ||
| 		gotoxy(xme,y); blanks(18);
 | ||
| 	}
 | ||
| 	if (who == ME) {
 | ||
| 		gotoxy(xme,y); puts("My");
 | ||
| 	}
 | ||
| 	else {
 | ||
| 		gotoxy(xyu,y); puts("Your");
 | ||
| 	}
 | ||
| 	printf(" Roll> [%d] ",dice[0]);
 | ||
| 	if (player) printf("[%d] ",dice[1]); /* whofirst doesn't show this */
 | ||
| 
 | ||
| } /* end: highroller */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| /*========================================================================
 | ||
| 
 | ||
|   MAJOR UTILITIES -- Miscellaneous functions sans which the game will not
 | ||
|                      proceed so well as otherwise..........
 | ||
| 
 | ||
| ========================================================================*/
 | ||
| 
 | ||
| jumpjack() {	/* Much too simple-minded LONGJUMP.  (But it IS simple!)
 | ||
| 		   CAUTION: C/80 has no idea what's going on here! 
 | ||
| 		   The in-line assembly which sets _fool MUST NOT be in
 | ||
| 		   a function which uses dynamic variables, or the stack 
 | ||
| 		   will be disrupted.  See arrange() and play() herein. */
 | ||
| #asm
 | ||
| 	LHLD	_fool	;Retrieve old stack status...
 | ||
| 	SPHL		;...diddle stack pointer
 | ||
| 	RET		;...and execute the ad hoc jump back to outer loop
 | ||
| 
 | ||
| _fool:	DS	2	;stack pointer is saved by doit(), read and used here
 | ||
| 
 | ||
| #endasm
 | ||
| } /* end: jumpjack */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| acg() {  /* additive congruential generator for pseudo-random numbers */
 | ||
| static int arg[] = {
 | ||
| 		4292,	60,	4947,	3972,	4489,
 | ||
| 		1917,	3916,	7579,	3048,	6856,
 | ||
| 		1832,	7589,	1798,	4954,	2880,
 | ||
| 		5142,	5187,	3045,	1529,	3110,
 | ||
| 		4333,	167,	5556,	7237,	5906,
 | ||
| 		5419,	6632,	5833,	3760,	1081,
 | ||
| 		1434,	80,	6212,	344,	7303,
 | ||
| 		3044,	7675,	5420,	457,	3434,
 | ||
| 		2657,	700,	6777,	4436,	620,
 | ||
| 		2129,	629,	3550,	1639,	4546,
 | ||
| 		1220,	6469,	862,	3280,	4664
 | ||
| 	};
 | ||
| static int rp1 = 0, rp2 = 32;
 | ||
| 
 | ||
| 	rp1++; 
 | ||
| 	rp2++;  
 | ||
| 	rp1 %= 55; 
 | ||
| 	rp2 %= 55;
 | ||
| 	arg[rp1] ^= arg[rp2];
 | ||
| 	return ( arg[rp1] );
 | ||
| 	
 | ||
| } /* end: acg */
 | ||
| 
 | ||
| finishup() {
 | ||
| 	crtexit(); on_cursor(); /* restore for user */
 | ||
| 	exit();
 | ||
| }
 | ||
| 
 | ||
| saveboard() {
 | ||
| int i;
 | ||
| 	for (i = 0; i < 28; i++) {
 | ||
| 		bdsave[i].stones = point[i].stones;
 | ||
| 		bdsave[i].owner  = point[i].owner;
 | ||
| 	}
 | ||
| } /* end: saveboard */
 | ||
| 
 | ||
| restoreboard() {
 | ||
| int i;
 | ||
| 	for (i = 0; i < 28; i++) {
 | ||
| 		point[i].stones = bdsave[i].stones;
 | ||
| 		point[i].owner  = bdsave[i].owner;
 | ||
| 	}
 | ||
| } /* end: restoreboard */
 | ||
| 
 | ||
| 
 | ||
| getAtkn() {
 | ||
| 	if (kaypro) return( get5tkn() );
 | ||
| 	else return( get1tkn() );
 | ||
| }
 | ||
| getBtkn() {
 | ||
| 	if (kaypro) return( get6tkn() );
 | ||
| 	else return( get2tkn() );
 | ||
| }
 | ||
| getCtkn() {
 | ||
| 	if (kaypro) return( get7tkn() );
 | ||
| 	else return( get3tkn() );
 | ||
| }
 | ||
| getDtkn() {
 | ||
| 	if (kaypro) return( get8tkn() );
 | ||
| 	else return( get4tkn() );
 | ||
| }
 | ||
| 
 | ||
| swaptokens() {
 | ||
| char *temp;
 | ||
| 
 | ||
| 	swapped ^= TRUE;
 | ||
| 	if (swapped) {
 | ||
| 		temp = token1;
 | ||
| 		token1 = token2;
 | ||
| 		token2 = temp;
 | ||
| 	}
 | ||
| 	else {
 | ||
| 		tswap ^= TRUE;
 | ||
| 		if (tswap) {
 | ||
| 			token1 = getCtkn();
 | ||
| 			token2 = getDtkn();
 | ||
| 		}
 | ||
| 		else {
 | ||
| 			token1 = getAtkn();
 | ||
| 			token2 = getBtkn();
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| } /* end: swaptokens */
 | ||
| 
 | ||
| 
 | ||
| blanks( n ) int n; {
 | ||
| 	while (n--) putc(' ',0);
 | ||
| } /* end: blanks */
 | ||
| 
 | ||
| 
 | ||
| msg(p) char *p; {
 | ||
| 	on_cursor(); 
 | ||
| 	gotoxy(5,23); blanks(74);
 | ||
| 	gotoxy(5,23); puts(p); 
 | ||
| }
 | ||
| 
 | ||
| debug(p) char *p; {
 | ||
| 	on_cursor(); 
 | ||
| 	gotoxy(5,22); blanks(74);
 | ||
| 	gotoxy(5,22); puts(p); return(FALSE);
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| nxtyp(i) int i; {
 | ||
| 	if (i > 9) return ( i - 1 ); else return ( i + 1 );
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| isbar(p) int p; {
 | ||
|    return (( p == MYBAR ) || ( p == YRBAR ));
 | ||
| }
 | ||
| 
 | ||
| ishome(p) int p; {
 | ||
|    return (( p == MYHOME ) || ( p == YRHOME ));
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| putstone( pt, cnt, color ) int pt, cnt, color; {
 | ||
| int i, xp, yp, slack;
 | ||
| char *background, *token;
 | ||
| 
 | ||
| 
 | ||
| 	if (cnt < 1) { /* empty point has neither stones nor owner */
 | ||
|             cnt   = 0; 
 | ||
|             color = 0;  
 | ||
|         }
 | ||
| 
 | ||
| 	point[pt].stones = cnt;    /* number of stones on this point */
 | ||
| 	point[pt].owner  = color;  /* and whose they are */
 | ||
| 
 | ||
|         /* stack stones 5 high in the home tray, 6 high on the points */
 | ||
| 	if (isbar(pt) || ishome(pt)) slack = 5; else slack = 6;
 | ||
| 
 | ||
|         /* locate the base address of the point for animation */
 | ||
| 	xp = point[pt].x;  
 | ||
| 	yp = point[pt].y; 
 | ||
| 
 | ||
| 
 | ||
|         /* decide on the background pattern to be used for empty places */
 | ||
| 	if (pt > 12) background = "/\\  ";  
 | ||
| 	else background = "\\/  ";
 | ||
| 
 | ||
| 	if (point[1].x < 40) {
 | ||
| 		if ((pt % 2) == 1) background = "..  ";
 | ||
| 	}
 | ||
| 	else if ((pt % 2) == 0) background = "..  ";
 | ||
| 
 | ||
| 	if (ishome(pt))    background = "    ";
 | ||
| 	if (isbar(pt))     background = "||| ";
 | ||
| 
 | ||
|         /* get the token pattern to be used */
 | ||
| 	if (color == ME) {
 | ||
| 		token = token1;  
 | ||
| 	}
 | ||
| 	else {
 | ||
| 		token = token2;
 | ||
| 	}
 | ||
| 
 | ||
|         /* draw the entire point with token and background patterns */
 | ||
| 	off_cursor();
 | ||
| 
 | ||
| 	/* first erase all blots from this point (draw the background) */
 | ||
| 		for (i = 0; i < slack; i++) {
 | ||
| 			gotoxy(xp,yp);
 | ||
| 			puts(background);     /* string has point's width */
 | ||
| 			point[pt].lastx = 0;  /* future, not implemented */
 | ||
|                         point[pt].lasty = 0;
 | ||
| 			yp = nxtyp(yp);
 | ||
| 		}
 | ||
| 
 | ||
| 	/* now draw all the blots there are on this point onto the point */
 | ||
| 		for (i = 0; i < cnt; i++) {
 | ||
| 			xp = point[pt].x + (i / slack);
 | ||
| 			if ((i % slack) == 0) yp = point[pt].y;
 | ||
| 			gotoxy(xp, yp);
 | ||
| 			puts(token);
 | ||
| 			point[pt].lastx = xp;  /* future, not implemented */
 | ||
| 			point[pt].lasty = yp;
 | ||
| 			yp = nxtyp(yp);
 | ||
| 		}
 | ||
| 
 | ||
| } /* end: putstone */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| hitblot( from, color ) int from, color; {
 | ||
| static int barpt, addone;
 | ||
| 
 | ||
| 	if (tone) beep();
 | ||
| 	barpt = whosebar( color );
 | ||
| 	putstone(from, 0, 0);
 | ||
| 	addone = point[barpt].stones + 1;
 | ||
| 	putstone( barpt, addone, color );
 | ||
| 
 | ||
| } /* end: hitblot */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| movestone( from, to) int from, to; {
 | ||
| static int opponent, subone, addone;
 | ||
| 
 | ||
| 
 | ||
| 	opponent = other( player );
 | ||
| 	if (point[to].owner == opponent) hitblot(to, opponent);
 | ||
| 
 | ||
| 	subone = point[from].stones - 1;
 | ||
| 	addone = point[to].stones + 1;
 | ||
| 
 | ||
| 	putstone(from, subone, player);
 | ||
| 	putstone(to, addone, player);
 | ||
| 
 | ||
| 	--movesleft;
 | ||
| 	checkwin();  /* never but NEVER let a win go unnoticed! */
 | ||
| 
 | ||
| } /* end: movestone */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| /*========================================================================
 | ||
| 
 | ||
|   CUBE Commands -- commands related to the cube, doubling, etc.
 | ||
| 
 | ||
| =========================================================================*/
 | ||
| 
 | ||
| notyrcube() {
 | ||
| 	gotoxy(75,19); blanks(5);
 | ||
| 	gotoxy(75,20); blanks(5);
 | ||
| } /* end: notyrcube */
 | ||
| 
 | ||
| 
 | ||
| notmycube() {
 | ||
| 	gotoxy(75,2); blanks(5);
 | ||
| 	gotoxy(75,3); blanks(5);
 | ||
| } /* end: notmycube */
 | ||
| 
 | ||
| 
 | ||
| barcube() {
 | ||
|         /* startcubevalue is normally 1, but it may have doubled */
 | ||
|         /* if the opening rolloff for first turn came up doubles */
 | ||
| 	doubles.cube = startcubevalue;
 | ||
| 	doubles.whosecube = 0;
 | ||
| 	notmycube(); notyrcube();
 | ||
|         gotoxy(37,11);
 | ||
|         if (startcubevalue == 1) puts("[BAR]");
 | ||
| 	else if (doubles.cube < 16) printf("[ %d ]",doubles.cube);
 | ||
| 	else printf("[%03d]",doubles.cube); 
 | ||
| } /* end: barcube */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| notbarcube() {
 | ||
| 	gotoxy(37,11); puts(" BAR ");
 | ||
| } /* end: notbarcube */
 | ||
| 
 | ||
| 
 | ||
| mycube(value) int value; {
 | ||
| 	notbarcube(); gotoxy(75,2); puts("CUBE");
 | ||
| 	gotoxy(75,3); 
 | ||
| 	sprintf(buzzard,"[%d]",value); 
 | ||
| 	printf("%-5s",buzzard);
 | ||
| 	doubles.whosecube = ME;
 | ||
| 
 | ||
| } /* end: mycube */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| yrcube(value) int value; {
 | ||
| 	notbarcube; gotoxy(75,19); puts("CUBE");
 | ||
| 	gotoxy(75,20); sprintf(buzzard,"[%d]",value); 
 | ||
| 	printf("%-5s",buzzard);
 | ||
| 	doubles.whosecube = YU;
 | ||
| 
 | ||
| } /* end: yrcube */
 | ||
| 
 | ||
| 
 | ||
| idouble() {
 | ||
| static int ch;
 | ||
| 
 | ||
| 	if (doubles.whosecube == YU) return;  /* not mine, can't double! */
 | ||
| 	if (doubles.cube > 256) return;    /* maximum, don't consider it */
 | ||
| 	notbarcube();
 | ||
| 	if (tone) beep();
 | ||
| 	debug("I double.  Will you accept the cube ");
 | ||
| 	printf("at %d points? ",doubles.cube * 2);
 | ||
| 	loo: 	while ((ch = getc(0)) == 0);
 | ||
| 		if (toupper(ch) == 'Y') {
 | ||
| 			notmycube();
 | ||
| 			doubles.cube *= 2;
 | ||
| 			yrcube(doubles.cube);
 | ||
| 			off_cursor();
 | ||
| 		}
 | ||
| 		else if (toupper(ch) == 'N') winner(ME,0);
 | ||
| 		else goto loo;
 | ||
| 
 | ||
| } /* end: idouble() */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| backgame() {
 | ||
| int max, barred, count, i;
 | ||
| 	if (topstone(YU) < 12 && topstone(ME) > 18) {
 | ||
| 		if (mytotal() < yrtotal() + 4) return (TRUE);
 | ||
| 		max = barred = count = 0;
 | ||
| 		i = 24;
 | ||
| 		while (i > 18) {
 | ||
| 			if (point[i].owner == ME) {
 | ||
| 				max = i;
 | ||
| 				if (point[i].stones > 1) barred++;
 | ||
| 				count += point[i].stones;
 | ||
| 			}
 | ||
| 			i--;
 | ||
| 		}
 | ||
| 		return ((max < topstone(YU) + 1) && (barred > 1 && count < 7));
 | ||
| 	}
 | ||
| 	else return ( mytotal() < yrtotal() + 24 );
 | ||
| 
 | ||
| } /* end: backgame */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| cubeval() {
 | ||
| int ineed, yuneed, yrtop, mytop;
 | ||
| 
 | ||
| 	if (endgame()) {
 | ||
| 
 | ||
| 		/* calculate the number of dice that are required to end */
 | ||
| 		/* the game, with appropriate fudge factors for position */
 | ||
| 
 | ||
| 		yrtop = topstone(YU);
 | ||
| 		mytop = topstone(ME);
 | ||
| 
 | ||
| 		ineed = 15 - point[MYHOME].stones;
 | ||
| 		yuneed = 15 - point[YRHOME].stones;
 | ||
| 
 | ||
| 		if (yrtop < 4 && yuneed < 3) return (FALSE); /* obvious */
 | ||
| 
 | ||
| 		/* topstones still running?  use a different method */
 | ||
| 		if (mytop > 6 || yrtop > 6) {
 | ||
| 			ineed  = 2 * (mytotal() / 8) + 1; /* number of dice */
 | ||
| 			yuneed = 2 * (yrtotal() / 8) + 1;
 | ||
| 		}
 | ||
| 
 | ||
| 		/* count the stones on point 6 twice, they're losers */
 | ||
| 		yuneed += point[6].stones;
 | ||
| 		ineed  += point[6].stones;
 | ||
| 
 | ||
| 		/* you doubled, so you have the roll */
 | ||
| 		yuneed -= 2;
 | ||
| 
 | ||
| 		/* odd number left? */
 | ||
| 		if (ineed  % 2) ineed++;
 | ||
| 		if (yuneed % 2) yuneed++;
 | ||
| 
 | ||
| 		/* is the one point empty? */
 | ||
| 		if (ineed  > 4 && mytop > 3 && point[24].stones == 0) ineed++;
 | ||
| 		if (yuneed > 4 && yrtop > 3 && point[ 1].stones == 0) yuneed++;
 | ||
| 
 | ||
| 		if (mytop < yrtop && ineed < yuneed) return (TRUE);
 | ||
| 		if (yrtop < 5 && yuneed < ineed) return (FALSE);
 | ||
| 		return ( yuneed >= ineed );
 | ||
| 	}
 | ||
| 	else return ( backgame() );
 | ||
| 
 | ||
| } /* end: cubeval */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| testcube() {
 | ||
| 
 | ||
| 	if (cubeval()) {
 | ||
| 		debug("I accept the cube.");
 | ||
| 		notyrcube();
 | ||
| 		doubles.cube *= 2;
 | ||
| 		mycube(doubles.cube);
 | ||
| 	}
 | ||
| 	else winner(YU,0);
 | ||
| 
 | ||
| } /* end: testcube */
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| udouble() {
 | ||
| 
 | ||
| 	if (doubles.whosecube == ME) {
 | ||
| 		if (tone) beep();
 | ||
| 		debug("It's MY cube, dummy!"); 
 | ||
| 	}
 | ||
| 	else testcube();
 | ||
| 
 | ||
| } /* end: udouble */
 | ||
| 
 | ||
| 
 | ||
| #include "mylib2.c"
 | ||
| 
 | ||
|  | 
