diff --git a/Backgammon/Compiler/Compiling.md b/Backgammon/Compiler/Compiling.md new file mode 100644 index 0000000..da5f0c9 --- /dev/null +++ b/Backgammon/Compiler/Compiling.md @@ -0,0 +1,62 @@ +# Compiling Gammon IV + +## Requirements + +To compile this program, you need the following: + +* The Software Toolworks' C/80 v3.1 compiler for CP/M +* Microsoft MACRO-80 assembler (M80.com) +* Microsoft LINK-80 linker (L80.COM) + +All of these tools can also be found here: +http://www.retroarchive.org/cpm/lang/lang.htm + +## Configuration + +The C compiler needs to be configured in order to be able to compile Gammon IV. + +Just use CCONFIG.COM to make the following settings: + + 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 + 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 + +I assume that all of the following files are placed on a hard disk or "big" floppy disk. +If you are compiling this game on a old system with limited disk space, please look into backgmmn.c, where you can find tips on how to distribute the files onto several disks. + +The following files are needed: + BACKGMMN.C, GAMEPLAN.C, GAMEPLAN.HDR, MYLIB2.C, + +From the C compiler: + PRINTF.C, STDLIB.REL, CLIBRARY.REL, C.COM + +Assembler/Linker: + L80.COM, M80.COM + +To use the submit scripts, you also need ``SUBMIT.COM`` from your CP/M installation. + +Using ``SUBMIT BGMAKE``, all steps of compiling, assembling and linking will run. + +Using ``SUBMIT BGCLEAN``, all compiled files and intermediary files will be deleted. + +If you don't want to use the script or just want to compile some parts, these steps are taken in the script: + + c -m backgmmn + m80 =backgmmn + c -m gameplan + m80 =gameplan + l80 backgmmn,gameplan,stdlib/s,clibrary/s,backgmmn/n/e + diff --git a/Backgammon/Compiler/bgclean.sub b/Backgammon/Compiler/bgclean.sub new file mode 100644 index 0000000..0172b3f --- /dev/null +++ b/Backgammon/Compiler/bgclean.sub @@ -0,0 +1,8 @@ +era *.bak +era *.mac +era backgmmn.rel +era gameplan.rel +era backgmmn.com +era backgmmn.mac +era gameplan.mac + diff --git a/Backgammon/Compiler/bgmake.sub b/Backgammon/Compiler/bgmake.sub new file mode 100644 index 0000000..b8b595a --- /dev/null +++ b/Backgammon/Compiler/bgmake.sub @@ -0,0 +1,13 @@ +era *.bak +era *.mac +era backgmmn.rel +era gameplan.rel +era backgmmn.com +c -m backgmmn +m80 =backgmmn +era backgmmn.mac +c -m gameplan +m80 =gameplan +era gameplan.mac +l80 backgmmn,gameplan,stdlib/s,clibrary/s,backgmmn/n/e + diff --git a/Backgammon/Compiler/c80v31.zip b/Backgammon/Compiler/c80v31.zip new file mode 100644 index 0000000..e43d1fc Binary files /dev/null and b/Backgammon/Compiler/c80v31.zip differ diff --git a/Backgammon/Compiler/l80.com b/Backgammon/Compiler/l80.com new file mode 100644 index 0000000..264e3b5 Binary files /dev/null and b/Backgammon/Compiler/l80.com differ diff --git a/Backgammon/Compiler/m80.com b/Backgammon/Compiler/m80.com new file mode 100644 index 0000000..d546065 Binary files /dev/null and b/Backgammon/Compiler/m80.com differ diff --git a/Backgammon/Original/backgmmn.c b/Backgammon/Original/backgmmn.c new file mode 100644 index 0000000..ff20aed --- /dev/null +++ b/Backgammon/Original/backgmmn.c @@ -0,0 +1,2278 @@ +/* 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 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" + + \ No newline at end of file diff --git a/Backgammon/Original/backgmmn.com b/Backgammon/Original/backgmmn.com new file mode 100644 index 0000000..fffa172 Binary files /dev/null and b/Backgammon/Original/backgmmn.com differ diff --git a/Backgammon/Original/backgmmn.lbr b/Backgammon/Original/backgmmn.lbr new file mode 100644 index 0000000..c0e9097 Binary files /dev/null and b/Backgammon/Original/backgmmn.lbr differ diff --git a/Backgammon/Original/backgmmn.sub b/Backgammon/Original/backgmmn.sub new file mode 100644 index 0000000..acb857c --- /dev/null +++ b/Backgammon/Original/backgmmn.sub @@ -0,0 +1,14 @@ +era *.bak +era *.mac +era backgmmn.rel +era gameplan.rel +era backgmmn.com +b:c -m backgmmn +b:m80 =backgmmn +era backgmmn.mac +b:c -m gameplan +b:m80 =gameplan +era gameplan.mac +b:l80 backgmmn,gameplan,stdlib/s,clibrary/s,backgmmn/n/e +backgmmn + \ No newline at end of file diff --git a/Backgammon/Original/gameplan.c b/Backgammon/Original/gameplan.c new file mode 100644 index 0000000..5d5b93a --- /dev/null +++ b/Backgammon/Original/gameplan.c @@ -0,0 +1,866 @@ + +/* Gammon IV, by David C. Oshel, Ames, Iowa */ + +/*--------------------------------------------------------------------*/ +/* GAMEPLAN.C -- separately compiled module, contains the tactics and */ +/* strategy for making the computer's move in Gammon IV */ +/* BACKGMMN.REL calls MyMove(), MyMove() calls topstone */ +/* and movestone and a few others. Uses the globals in */ +/* GAMEPLAN.HDR quite heavily. Bgversion declared here */ +/*--------------------------------------------------------------------*/ + +#include "gameplan.hdr" /* contains external, global declarations */ + +/*-------------------------------------------------------------------------*/ +/* */ +/* Bgversion is an external reference in the main module. Use this string */ +/* to take credit for a decent gameplaying algorithm if you find one! */ +/* */ +char *bgversion = ".20 -April 1, 1986- by David C. Oshel"; + /*....v....1....v....2....v....3....v...*/ + /* just 38 characters for version info! */ +/* */ +/*-------------------------------------------------------------------------*/ + + +/*=============================================*/ +/* M Y M O V E */ +/*=============================================*/ + + +naked() { /* am I leaving too many blots? */ +static int i, clink; + i = 24; clink = 0; + while (i) { + if (point[i].stones == 1 && point[i].owner == ME) clink++; + i--; + } + return (clink > 2); +} /* end: naked */ + + +yourfolly() { /* look for lotsa blots in your inner table */ +static int i, clink; + i = 18; clink = 0; + while (i < 25) { + if (point[i].owner == YU && point[i].stones == 1) + clink++; + i++; + } + return (clink >= 3); +} /* end: yourfolly */ + + +goodboard() { /* look for four made points near my inner table */ +static int i, clank, clink; + i = 9; clank = 0; + while (i > 3) { + if (point[i].owner == ME && point[i].stones > 1) clank++; + i--; + } + if (clank > 4) return (TRUE); /* bar is nearly blocked */ + + i = 6; clank = clink = 0; + while (i) { + if (point[i].owner != ME) + ; + else if (point[i].stones == 1) clink++; else clank++; + i--; + } + return (clank > 3 && clink < 2); +} /* end: goodboard */ + + +bearoff() { + return (topstone(ME) < 7); +} /* end: bearoff */ + + +scanahead( from ) int from; { +static int count; + + count = 0; + while (--from > 0) { + ++count; + if ( point[ from ].owner == YU ) return (count); + } + return (7); + +} /* end: scanahead */ + + +endgame() { /* Is no strategy required from here on in? */ + return ( (25 - topstone(YU)) > topstone(ME) ); +} /* end: endgame */ + + +/*------------------------------------------------------------*/ +/* MATCHUP */ +/* */ +/* 2-stone functions that force the choice of the next move. */ +/* These are the HEART and SOUL of this backgammon algorithm! */ +/*------------------------------------------------------------*/ + +setpend( from, to ) int from, to; { + pending.fr = from; + pending.to = to; + pending.flag = TRUE; +} /* end: setpend */ + + +clrpend() { + pending.flag = FALSE; +} /* end: clrpend */ + + +natural(f1,t1,f2,t2) int f1,t1,f2,t2; { + clrpend(); + if (point[t2].stones == 1 && t1 == f2) setpend(f2,t2); + return (pending.flag); +} /* end: natural */ + + +matchup( test4 ) int (* test4)(); { +static int i, j, ti, tj; + + if ( pending.flag ) return (FALSE); /* this is probably redundant */ + + for (i = 1; i < 26; i++) { + ti = list[0][i]; + if ( ti == ERROR ) goto zoo; + for (j = 1; j < 26; j++) { + tj = list[1][j]; + if ( tj == ERROR ) goto voo; + if ( (* test4)( i, ti, j, tj ) ) { + lurch( i, ti, 0); + return (TRUE); + } + voo: ; + } + zoo: ; + } + return (FALSE); + +} /* end: matchup */ + + +matchhi( test4 ) int (* test4)(); { +static int i, j, ti, tj; + + if ( pending.flag ) return (FALSE); /* this is probably redundant */ + + for (i = 1; i < 26; i++) { + ti = list[1][i]; + if ( ti == ERROR ) goto zoo; + for (j = 1; j < 26; j++) { + tj = list[0][j]; + if ( tj == ERROR ) goto voo; + if ( (* test4)( i, ti, j, tj ) ) { + lurch( i, ti, 1); + return (TRUE); + } + voo: ; + } + zoo: ; + } + return (FALSE); + +} /* end: matchhi */ + + + +/*--------------------------------------------------------*/ +/* CLOCKWISE and COUNTERCLOCK */ +/* */ +/* the rest of these are single-stone decisions based on */ +/* rules of thumb, board-scanning functions */ +/*--------------------------------------------------------*/ + +plainstupid( from ) int from; { /* don't break a safe point */ + return (from < 13 && (point[from].stones == 2 && scanahead(from) < 7)); +} /* end: plainstupid */ + + +unwise( innertablept ) int innertablept; { + /* if it's a hit, just for god's sake don't put him on the bar!! */ + if ( innertablept < 7 ) { + if (point[ innertablept ].owner == YU || + point[ YRBAR ].stones > 0) + return (TRUE); + } + return(FALSE); +} /* end: unwise */ + + + +covermine( from, to ) int from, to; { + if ( from < 8 ) return(FALSE); + return ( (point[ to ].stones == 1) && (point[ to ].owner == ME) ); +} /* end: covermine */ + + +idareyou( from, to ) int from, to; { + if (unwise( to )) return (FALSE); + if ( (point[ from ].stones != 2) + && (point[ to ].stones < 2) + && (scanahead( to ) > 6) ) return ( TRUE ); + else return (FALSE); +} /* end: idareyou */ + + +hitandrun( from, to ) int from, to; { + return ( point[ to ].owner == YU ); +} /* end: hitandrun */ + + +dbuild( from, to ) int from, to; { +static int diceleft; + diceleft = (myturns? 2 + movesleft: movesleft); + if (diceleft > 1) { + /* can't possibly be only one stone on from point */ + /* or kamikaze would have covered it on last move */ + return ( point[to].stones == 0 ); + } + return (FALSE); +} /* end: dbuild */ + + +kamikaze( from, to ) int from, to; { +/* cover my distant blot, or razzle-dazzle 'em with the long doubles hit */ +static int j, k, diceleft; + + k = from; + j = from - to; + diceleft = myturns * movesleft; /* NB: 2*2 == 2+2, "fourtunately" */ + while ( diceleft-- ) { /* predicting where doubles land is easy! */ + k -= j; + if (k < 1) return (FALSE); /* out of bounds */ + if ( point[ k ].stones == 0 ) continue; /* simplify */ + if ( point[ k ].stones == 1 ) /* found my blot or yours? */ + return (TRUE); + else if ( point[k].owner == YU ) /* found your blockade? */ + return (FALSE); + else continue; /* found my safe point, so ignore it */ + } + return (FALSE); + +} /* end: kamikaze */ + + +hittite( from, to ) int from, to; { + return (hitandrun(from,to) && to > 9); +} /* end: hittite */ + + +safehit( from, to ) int from, to; { + return (hittite(from,to) && idareyou(from,to)); +} /* end: safehit */ + + +foolsdemise( from, to ) int from, to; { + /* annihilate orphaned blots in enemy's inner, my outer table */ + return (to > 17 && point[to].owner == YU); +} /* end: foolsdemise */ + + +landonme( from, to ) int from, to; { + if ( plainstupid(from) ) return (FALSE); + if ( loneranger(from,to) ) { + if (from < 19 && to > 6) return(TRUE); + } + else return ( point[ to ].owner == ME && point[to].stones < 4); +} /* end: landonme */ + + + +/* these evaluations have meaning only in the endgame */ + + +nobackgammon( from, to ) int from, to; { /* endgame */ + return (from > 19); +} /* end: nobackgammon */ + + +crosstable( from, to ) int from, to; { + /* always move a table ahead if possible, in the endgame */ + if (from < 7) return (FALSE); + if (from > 18 && to <= 18) return (TRUE); + if (from > 12 && to <= 12) return (TRUE); + if (from > 6 && to <= 6) return (TRUE); + return (FALSE); +} /* end: crosstable */ + + +fiftytworule( from, to ) int from, to; { /* endgame */ +static int p; + if (from < 7) return (FALSE); /* not in inner table! */ + p = from % 6; + if (p == 0) return (TRUE); /* improve the six */ + if (p != 5) return ( (to % 6) < 3 ); /* best improve under five */ +} /* end: fiftytworule */ + + + + +/* these evaluations are universally applicable, last resort moves */ + + +gohome( from, to ) int from, to; { /* always go home if you can */ + return (to == MYHOME); +} /* end: gohome */ + + +scatter( from, to ) int from, to; { /* scatter, esp. in the endgame */ + if (plainstupid(from) || unwise(to)) return (FALSE); + return ( point[ from ].stones > 1 && point[ to ].stones == 0 ); +} /* end: scatter */ + + +runnerup( from, to ) int from, to; { + if (from < 10 || from > 18) return (FALSE); + return (TRUE); +} /* end: runnerup */ + + +loneranger( from, to ) int from, to; { + return( point[ from ].stones == 1 ); +} /* end: loneranger */ + + +run( dummy1, dummy2 ) int dummy1, dummy2; { /* MUST move something! */ + return (TRUE); +} /* end: run */ + + + + + +/* clockwise and counterclock make a 1-stone choice on rules of thumb */ + + +counterclock( test ) int (* test)(); { +static int i,j; + + for (i = 0; i < 2; i++) { + for (j = 1; j < 25; j++) { + if ( list[i][j] == ERROR ) continue; + if ( (* test)( j, list[i][j] ) ) { + lurch( j, list[i][j], i); + return ( TRUE ); + } } } + return (FALSE); + +} /* end: counterclock */ + + +clockwise( test ) int (* test)(); { +static int i,j; + + for (i = 0; i < 2; i++) { + for (j = 25; j > 0; j--) { + if ( list[i][j] == ERROR ) continue; + if ( (* test)( j, list[i][j] ) ) { + lurch( j, list[i][j], i); + return ( TRUE ); + } } } + return (FALSE); + +} /* end: clockwise */ + + + + + +/*-------------------------------------------*/ +/* Make Prime */ +/*-------------------------------------------*/ + +static int prmchk; + + +buildprime( f1,t1,f2,t2 ) int f1,t1,f2,t2; { + clrpend(); + /* check for the doubles bug */ + if ((dice[0] == dice[1]) && (point[f1].stones < 2)) return(FALSE); + + /* look for the combination */ + if ( t1 == prmchk && t2 == prmchk) setpend(f2,t2); + + /* stick like glue to a made point, but doubles may move forward */ + if (dice[0] != dice[1]) { + if ((f2 < 8) && (point[f2].stones == 2)) clrpend(); + if ((f1 < 8) && (point[f1].stones == 2)) clrpend(); + } + + return(pending.flag); + +} /* end: buildprime */ + + +makeprime() { +static int i, tab[] = { ERROR,1,2,3,20,22,24,9,4,6,8,5,7 }; + i = 12; + while (i) { + prmchk = tab[i]; + i--; + if ( point[ prmchk ].stones > 1 ) continue; + else if ( matchup( buildprime ) ) return(TRUE); + } + return(FALSE); +} /* end: makeprime */ + + +coverprime( from, to ) int from, to; { + return (((to == prmchk) && + (point[prmchk].owner == ME)) && + (point[from].stones != 2)); +} /* coverprime */ + + +cleanup() { +static int i, tab[] = { ERROR,1,2,3,20,22,24,9,4,6,8,5,7 }; + i = 12; + while (i) { + prmchk = tab[i]; + i--; + if ( point[ prmchk ].stones != 1 ) continue; + else if ( counterclock( coverprime ) ) return(TRUE); + } + return(FALSE); +} /* end: cleanup */ + + +/*-------------------------------------*/ +/* Walking Prime */ +/*-------------------------------------*/ + +swivelhips( from, to ) int from, to; { + return ( from > prmchk ); +} /* end: swivelhips */ + + +slink( from, to ) int from, to; { + return( (from > prmchk) && (point[to].stones == 1) ); +} /* end: slink */ + + +weasel() { + if ( clockwise( slink ) ) + return(TRUE); + if ( counterclock( swivelhips ) ) + return(TRUE); + if ( clockwise( run ) ) + return(TRUE); +} /* end: weasel */ + +ihaveprime( from ) int from; { +static int i, to, ez; + ez = 0; + for (i = 0; i < 6; i++) { + to = from - i; + if ((point[to].owner == ME) && (point[to].stones > 1)) ez++; + } + return (ez > 4); +} /* end: ihaveprime */ + + +walkingprime() { +/* looks for the walking prime anywhere in the front tables */ +/* then tries to bring up a runner from behind the prime, */ +/* ensuring that a back stone WILL move before a front one */ +static int i; + i = 12; + while (i > 5) { + if ( ihaveprime(i) ) { + prmchk = i; + if ( weasel() ) return (TRUE); + } + i--; + } + return(FALSE); +} /* end: walkingprime */ + + +/*---------- Book Moves ----------*/ +/* only valid if my move is first */ +/*--------------------------------*/ + +zip(a,b,c,d) int a,b,c,d; { + lurch(a,b,0); + lurch(c,d,0); + movesleft = 0; return( TRUE ); +} /* end: zip */ + + +zoom( a,b,c,d,e,f,g,h ) int a,b,c,d,e,f,g,h; { + myturns = 0; zip(a,b,c,d); zip(e,f,g,h); return( TRUE ); +} /* end: zoom */ + + +book() { +int a,b; + if (!firstmove) return (FALSE); + firstmove = FALSE; + a = min(dice[0],dice[1]); + b = max(dice[0],dice[1]); + switch (level) { + case 0: { return ( book0(a,b) ); break; } + case 1: { return ( book1(a,b) ); break; } + case 2: { return ( book2(a,b) ); break; } + } +} +book0( a,b ) int a, b; { + switch (a) { + case 1: { switch (b) { + case 1: return ( zoom(8,7,8,7,6,5,6,5) ); + case 2: return ( zip(24,23,13,11) ); + case 3: return ( zip(8,5,6,5) ); + case 4: return ( zip(24,23,13,9) ); + case 5: return ( zip(24,23,13,8) ); + case 6: return ( zip(13,7,8,7) ); + } + break; } + case 2: { switch (b) { + case 2: return ( zoom(6,4,6,4,13,11,13,11) ); + case 3: return ( zip(13,11,13,10) ); + case 4: return ( zip(8,4,6,4) ); + case 5: return ( zip(13,8,13,11) ); + case 6: return ( zip(24,18,13,11) ); + } + break; } + case 3: { switch (b) { + case 3: return ( zoom(13,10,13,10,10,7,10,7) ); + case 4: return ( zip(13,10,13,9) ); + case 5: return ( zip(13,10,13,8) ); + case 6: return ( zip(24,18,13,10) ); + } + break; } + case 4: { switch (b) { + case 4: return ( zoom(13,9,13,9,24,20,24,20) ); + case 5: return ( zip(13,8,13,9) ); + case 6: return ( zip(24,18,18,14) ); + } + break; } + case 5: { switch (b) { + case 5: return ( zoom(13,8,13,8,8,3,8,3) ); + case 6: return ( zip(24,18,18,13) ); + } + break; } + case 6: { return ( zoom(13,7,13,7,24,18,24,18) ); + break; } + } + +} /* end: book0 */ +book1( a,b ) int a, b; { /* mostly follows Becker */ + switch (a) { + case 1: { switch (b) { + case 1: return ( zoom(8,7,8,7,6,5,6,5) ); + case 2: return ( zip(13,11,6,5) ); + case 3: return ( zip(8,5,6,5) ); + case 4: return ( zip(13,9,6,5) ); + case 5: return ( zip(13,8,6,5) ); + case 6: return ( zip(13,7,8,7) ); + } + break; } + case 2: { switch (b) { + case 2: return ( zoom(6,4,6,4,13,11,13,11) ); + case 3: return ( zip(13,11,13,10) ); + case 4: return ( zip(8,4,6,4) ); + case 5: return ( zip(13,8,13,11) ); + case 6: return ( zip(13,7,7,5) ); + } + break; } + case 3: { switch (b) { + case 3: return ( zoom(13,10,13,10,8,5,8,5) ); + case 4: return ( zip(13,10,13,9) ); + case 5: return ( zip(13,8,8,5) ); + case 6: return ( zip(13,7,13,10) ); + } + break; } + case 4: { switch (b) { + case 4: return ( zoom(13,9,13,9,9,5,9,5) ); + case 5: return ( zip(13,8,13,9) ); + case 6: return ( zip(13,7,13,9) ); + } + break; } + case 5: { switch (b) { + case 5: return ( zoom(13,8,13,8,8,3,8,3) ); + case 6: return ( zip(13,7,13,8) ); + } + break; } + case 6: { return ( zoom(13,7,13,7,24,18,24,18) ); + break; } + } + +} /* end: book1 */ +book2( a,b ) int a, b; { /* mostly follows Becker */ + switch (a) { + case 1: { switch (b) { + case 1: return ( zoom(8,7,8,7,6,5,6,5) ); + case 2: return ( zip(13,11,24,23) ); + case 3: return ( zip(8,5,6,5) ); + case 4: return ( zip(13,9,24,23) ); + case 5: return ( zip(13,8,24,23) ); + case 6: return ( zip(13,7,8,7) ); + } + break; } + case 2: { switch (b) { + case 2: return ( zoom(6,4,6,4,24,23,24,23) ); + case 3: return ( zip(13,11,13,10) ); + case 4: return ( zip(8,4,6,4) ); + case 5: return ( zip(13,8,13,11) ); + case 6: return ( zip(13,7,13,11) ); + } + break; } + case 3: { switch (b) { + case 3: return ( zoom(13,10,13,10,10,7,10,7) ); + case 4: return ( zip(13,10,13,9) ); + case 5: return ( zip(13,8,8,5) ); + case 6: return ( zip(13,7,13,10) ); + } + break; } + case 4: { switch (b) { + case 4: return ( zoom(13,9,13,9,9,5,9,5) ); + case 5: return ( zip(13,8,13,9) ); + case 6: return ( zip(13,7,13,9) ); + } + break; } + case 5: { switch (b) { + case 5: return ( zoom(13,8,13,8,8,3,8,3) ); + case 6: return ( zip(13,7,13,8) ); + } + break; } + case 6: { return ( zoom(13,7,13,7,24,18,24,18) ); + break; } + } + +} /* end: book2 */ + + +/*====== MyMove ======*/ + +torve() { + if ( makeprime() ) { /* this will use doubles, if it can */ + return; + } + else if ( walkingprime() ) { /* i have six prime points, so run!!! */ + return; + } + else if ( dice[0] == dice[1] ) { /* this is too easy! */ + if ( counterclock( kamikaze ) ) + return; + if ( counterclock( dbuild ) ) /* claim new turf */ + return; + if ( clockwise( run ) ) + return; + } + else if ( cleanup() ) { /* cover my single blot on prime points */ + return; + } + else if ( bearoff() ) { /* I'm ready, but you're in the back game! */ + if ( counterclock( gohome ) ) + return; + if ( clockwise( run ) ) + return; + } + else { + if ( clockwise( hitandrun ) ) + return; + if ( matchup( natural ) ) + return; + if ( clockwise( landonme ) ) + return; + if ( counterclock( runnerup ) ) + return; + if ( clockwise( scatter ) ) + return; + if ( clockwise( run ) ) + return; + } + +} /* end: torve */ + + + +villiers() { + if ( makeprime() ) { /* this will use doubles, if it can */ + return; + } + else if ( walkingprime() ) { /* i have six prime points, so run!!! */ + return; + } + else if ( dice[0] == dice[1] ) { /* this is too easy! */ + if ( counterclock( kamikaze ) ) + return; + if ( counterclock( dbuild ) ) /* claim new turf */ + return; + if ( clockwise( run ) ) + return; + } + else if ( cleanup() ) { /* cover my single blot on prime points */ + return; + } + else if ( bearoff() ) { /* I'm ready, but you're in the back game! */ + if ( counterclock( gohome ) ) + return; + if ( clockwise( run ) ) + return; + } + else { + if ( clockwise( foolsdemise ) ) + return; + if ( clockwise( idareyou ) ) + return; + if ( counterclock( covermine ) ) + return; + if ( matchup( natural ) ) + return; + if ( clockwise( landonme ) ) + return; + if ( clockwise( runnerup ) ) + return; + if ( clockwise( scatter ) ) + return; + if ( clockwise( run ) ) + return; + } + +} /* end: villiers */ + + + +louisa() { + if ( makeprime() ) { /* this will use doubles, if it can */ + return; + } + else if ( walkingprime() ) { /* i have six prime points, so run!!! */ + return; + } + else if ( dice[0] == dice[1] ) { /* this is too easy! */ + if ( counterclock( kamikaze ) ) + return; + if ( counterclock( dbuild ) ) /* claim new turf */ + return; + if ( clockwise( run ) ) + return; + } + else if ( cleanup() ) { /* cover my single blot on prime points */ + return; + } + else if ( bearoff() ) { /* I'm ready, but you're in the back game! */ + if ( counterclock( gohome ) ) + return; + if ( clockwise( run ) ) + return; + } + else if ( (!naked() && goodboard()) || yourfolly() ) { + if ( clockwise( hitandrun ) ) + return; + if ( matchup( natural ) ) + return; + if ( clockwise( landonme ) ) + return; + if ( counterclock( runnerup ) ) + return; + if ( clockwise( scatter ) ) + return; + if ( clockwise( run ) ) + return; + } + else { + if ( clockwise( foolsdemise ) ) + return; + if ( clockwise( idareyou ) ) + return; + if ( counterclock( covermine ) ) + return; + if ( matchup( natural ) ) + return; + if ( clockwise( landonme ) ) + return; + if ( clockwise( runnerup ) ) + return; + if ( clockwise( scatter ) ) + return; + if ( clockwise( run ) ) + return; + } +} /* end: louisa */ + + +mymove() { +int i, d; + + if ( nomove() ) { + if (lookforit && (dice[0] != dice[1])) { + lookforit = FALSE; + puts("\008... "); + switch (level) { + case 0: { puts("Blocked!"); break; } + case 1: { puts("Well, no!"); break; } + case 2: { puts("Thurb!"); break; } + } + sleep(10); + restoreboard(); + update(); + + /* put the high die in list zero */ + d = dice[0]; dice[0] = dice[1]; dice[1] = d; + + cantuse = ERROR; movesleft = 2; myturns = 1; + switch (level) { + case 0: { setchat("I move"); break; } + case 1: { setchat("Let's try"); break; } + case 2: { setchat("Move is"); break; } + } + debug(chatter); + prmchk = 12; + weasel(); + /* the rules say, use both dice if you can, or */ + /* the highest if one or the other but not both */ + } + else { + lookforit = TRUE; + strcat(chatter," and now I'm blocked "); + myturns = movesleft = 0; + } } + else if ( book() ) { + return; + } + else if ( pending.flag ) { + lurch( pending.fr, pending.to, 1 ); + clrpend(); + } + else if ( endgame() ) { /* very solid tactics here!! */ + if ( clockwise( gohome ) ) + return; + if ( clockwise( nobackgammon ) ) /* no excuse! */ + return; + if ( clockwise( crosstable ) ) + return; + if ( clockwise( fiftytworule ) ) + return; + if ( clockwise( scatter ) ) + return; + if ( clockwise( run ) ) + return; + } + else if ( point[ MYBAR ].stones > 0 ) { /* I'm on the bar! */ + if ( clockwise( hitandrun ) ) /* wreak havoc, please */ + return; + if ( clockwise( run ) ) /* note: uses low die first */ + return; + } + else switch (level) { + case 0: { villiers(); break; } + case 1: { louisa(); break; } + case 2: { torve(); break; } + } + + +} /* end: mymove */ + +/*------------------------------*/ +/* end of the GAMEPLAN.C module */ +/*------------------------------*/ + \ No newline at end of file diff --git a/Backgammon/Original/gameplan.hdr b/Backgammon/Original/gameplan.hdr new file mode 100644 index 0000000..49f8c27 --- /dev/null +++ b/Backgammon/Original/gameplan.hdr @@ -0,0 +1,42 @@ +/* GAMEPLAN.HDR */ + +/*================================================================*/ +/* change nothing in this file */ +/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ + +#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 +#define MYLEVEL 2 + +extern int list[2][28]; /* two dice, two lists */ + +extern 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 */ + +extern struct { int cube, whosecube; } doubles; + +extern struct { int fr,to,flag; } pending; + +extern int level, dice[2], myscore, yrscore, player, movesleft, cantuse, + myturns, swapped, tswap, deciding, expert, tone, show, moremsgline, + firstmove, helpdisabled, yrdice, lookforit, startcubevalue; + +extern char *chatter[80]; + +/*----------------------------------------------*/ +/* end of header -- change NOTHING in this file */ +/*----------------------------------------------*/ + \ No newline at end of file diff --git a/Backgammon/Original/mylib2.c b/Backgammon/Original/mylib2.c new file mode 100644 index 0000000..4082da3 --- /dev/null +++ b/Backgammon/Original/mylib2.c @@ -0,0 +1,541 @@ +/* +MYLIB2.C + +These routines are CONDITIONALLY compiled; i.e., only as needed. + +---------------------------------------------------------------------- +Incorporates special mods used by my Backgammon game, BACKGMMN.C etc. +---------------------------------------------------------------------- + +A set of common I/O functions that seem to turn up a lot in my programs, +including terminal functions for Kaypro 10, 4'84, 2X etc. + +Uses Software Toolworks' C/80 3.1 compiler. Place #include "mylib2.c" at the +end of your source file for correct CONDITIONAL COMPILATION. + +David C. Oshel +1219 Harding Ave. +Ames, Iowa 50010 + +Last modified: March 25, 1986 + +----------------------------------------------------------------------------- +** WARNING ** These routines use direct console IO, bdos function 6! + + YOU MUST CALL INIT_LIB() BEFORE USING THESE ROUTINES! + + ======= UTILITIES ======= + + * init_lib() - CALL THIS FIRST, OR THE RESULT WILL BE VERY STRANGE! + * + * puts(p) - unformatted print, e.g., puts("Hello, sailor!\n"); + * gets(p,max) - printable input only, no prompt character + * + * ask(p) - demand Yes or No response to question p + * random() - effective random 16-bit integer IFF gets() is used + * sleep(n) - sleep n/10ths of a second, roughly (from C80.LIB) + * rollup() - roll up 23 lines of screen + * ONscript() - printer echo ON for output via puts, chrout + * OFFscript() - printer echo OFF for output via puts, chrout + * ONinterrupt() - Ctl-C, Ctl-B cause program exit + * OFFinterrupt() - Ctl-C, Ctl-B cause comedy + * hide_input(p,max) - like gets, but used when entering passwords + * chrout(c) - if scripting, echo output also to LST: + * putscreen(p) - like puts, but always and only to screen + + + ======= KAYPRO 10 TERMINAL/VIDEO FUNCTIONS ======= + + * gotoxy(x,y) - 0,0 is top left, horz <= 79 precedes vert <= 24, + * where 0,24 is on the 25th, status, line. + * beep() - terminal bell + * home() - home cursor, do not clear screen + * clr_screen() - home and clear + * + * shadow_box(h,v,x1,y1,x2,y2) - like box, but with shadow, calls box + * box(tlx,tly,brx,bry) - draw a line box, coords: topleft XY, bottomright XY + * note that box calls ldraw(x1,y1,x2,y2), below + * + * clr_lend() - clear from cursor to end of line + * clr_send() - clear from cursor to end of screen + * rev_vid(), + * nor_vid() - reverse field + * dim_vid(), + * bri_vid() - low/high intensity + * on_blink(), + * off_blink() - blinking chars + * ul_start(), + * ul_stop() - start/stop underline + * save_cursor(), + * retn_cursor() - remember/restore current cursor location + * ins_line(), + * del_line() - insert/delete screen text line + * on_cursor(), + * off_cursor() - hide/show cursor + * vm_on(), + * vm_off() - "Video Mode" commands + * pixel(x,y) - draw pixel at x,y (video coords, x <= 159, y <= 99) + * no_pixel(x,y) - erase pixel at x,y + * ldraw(x1,y1,x2,y2) - draw/ erase graphics line, see discussion for box + * lwipe(x1,y1,x2,y2) - range for video coordinates as for pixel +*/ + + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + + +/* hide this here so's not to worry about it elsewhere */ +/* "printf.c" collides with one of these, can't remember which */ +/* puts() takes longer to write, but executes faster */ +extern char Cmode, IOpread[4], IOpwrit[4], IOpeof[4]; + +/* make these known only to what follows */ +static int MYbstout, MYscrtp, MYretnirp; /* odd names mark semi-private */ +static unsigned RNDloc; /* effective random location, bumped by gets() */ + /* and scrambled when the LCG random() is called */ + /* makes a decent algorithm for interactive games */ + + +#ifneed init_lib +init_lib() { + + MYretnirp = fopen("LST:","w"); + OFFscript(); + ONinterrupt(); + Cmode = 0; + IOpread[0] = 6; IOpwrit[0] = 6; + +} /* end: init_lib */ +#endif + + + + +#ifneed random +random() { /* depends on effective random location spun by gets() */ + + RNDloc = 2053 * RNDloc + 13849; + return (RNDloc); +} +#endif + + + +#ifneed ONscript +ONscript() { + + MYscrtp = TRUE; + +} +#endif +#ifneed OFFscript +OFFscript() { + + MYscrtp = FALSE; + +} +#endif + + +#ifneed ONinterrupt +ONinterrupt() { + + MYbstout = TRUE; + +} +#endif +#ifneed OFFinterrupt +OFFinterrupt() { + + MYbstout = FALSE; + +} +#endif + + +#ifneed ask +ask(p) char *p; { + +char ch, resp[2]; + +loo: puts(p); + gets(resp,1); + ch = toupper( *resp ); + if ( !( ch == 'Y' || ch == 'N' )) { + puts("Please answer the question, Yes or No.\n"); + goto loo; + } + return (ch == 'Y'); + +} /* end: ask */ +#endif + + +#ifneed rollup +rollup() { + +int i; + + for (i=0; i<23; i++) puts("\n"); + +} /* end: rollup */ +#endif + + +#ifneed sleep +sleep( n ) int n; { /* sleep for n/10 seconds, 0 <= n < 256 */ + + n; /* get n into HL */ +#asm + MOV B,L ;delay B/10ths of a second +__DL0: MVI A,100 ;100 milliseconds, 1/10 second +__DL1: MVI C,249 ;1 millisecond per unit of A at 4 MHz +__DL2: DCR C ;Leventhal, Z80 Assembly Language Programming + JNZ __DL2 + DCR A + JNZ __DL1 + DCR B + JNZ __DL0 ;on exit, HL has FALSE if n was 0, else TRUE +#endasm +} /* end: sleep */ +#endif + + +/*========================================*/ +/* GETS(p, maxinput) */ +/* Local getline function with special *---* WARNING: */ +/* input handling, 1 <= len <= maxinput *---* Execute INIT_LIB() first !! */ +/* Updates effective random, RNDloc, */ +/* Forces input from CONSOLE only! */ +/*========================================*/ + +#ifneed gets +gets(p,maxinput) char *p; int maxinput; { + +/* This function depends on BDOS Function #6. Init_lib() sets Cmode=0 and + IOpread[0]=6 and IOpwrit[0]=6 (courtesy of and peculiar to C/80 3.1) + YOU must ensure that the target string is long enough to collect the + entire maximum input allowed and specified, INCLUDING FINAL NULL! */ + +static int len; +static char ch; + + len = -1; + if (maxinput < 1 || maxinput > 127) maxinput = 79; + + /*--------------------------------*/ + /* SPECIAL ROUTINE FOR BACKGAMMON */ + /*--------------------------------*/ + +loo: while ( !(ch = getc(0)) ) acg(); /* keep the game lively */ + + if (len < 0) len = 0; /* don't destroy prompt by backing up */ + if (ch == '\n') { /* end of line? don't store newline */ + *p = '\0'; /* mark it with a B for baby and me */ + /* chrout('\n'); */ /* but DON'T echo newline */ + return ( len ); /* <--- HERE IS THE FUNCTION EXIT! */ + } + else if (ch == '\b' || ch == 0x7F) { /* backspace? rubout? */ + if (len--) { /* where's the prompt? */ + puts("\008 \008"); /* we're ok, echo erase */ + p--; /* delete from string */ + } + } + + /*--------------------------------*/ + /* SPECIAL ROUTINE FOR BACKGAMMON */ + /*--------------------------------*/ + + else if (ch == '\003') { /* user bailout key is Ctrl-C, not ESC */ + if (MYbstout) exit(); + else { + haltgame(); /* sets whofirst flag and does jumpjack() */ + } + } + + else if (ch == '\025' || ch == '\030') { /* Ctl-U, Ctl-X */ + while (len--) { + p--; + puts("\008 \008"); + } + } + else if (len == maxinput) { /* test specials before testing len */ + chrout('\007'); + } + else if (ch > 31 && ch < 127) { /* printable char? */ + chrout(ch); /* yes, echo it */ + *p++ = ch; /* collect it */ + len++; /* keep track of it */ + } + else { /* control chars? */ + chrout('\007'); + } + goto loo; + +} /* end: gets */ +#endif + + + + +#ifneed hide_input +hide_input(s,len) char *s; int len; { + +/* receive at most len chars in s buffer, + terminate string with zero, + but echo each char with 1, 2, or 3 meaningless dots */ + +char ch; int num; + + if ((len < 1) || (len > 127)) len = 127; + num = 0; + for (;;) { /* forever */ + while ((ch = getc(0)) == 0) /* bdos 6 does not wait, so we do */ + ; + if ((ch == '\r') || (ch == '\n') || (num++ > len)) { + /* not sure what the CR key actually is to bdos 6 & C/80 */ + *s++ = '\0'; + return; /* this way out */ + } + if ((num % 2) == 0) putc('.',0); /* deception, illusion */ + if ((num % 5) == 0) putc('.',0); + putc('.',0); + *s++ = ch; + } + +} /* end: hide_input */ +#endif + + + + +/*------------------------ kpro stuff -------------------------*/ + +#ifneed shadow_box +/* like box, but with horizontal & vertical displacement for shadow */ +shadow_box(h,v,x1,y1,x2,y2) int h,v,x1,y1,x2,y2; +{ + box(x1+h,y1+v,x2+h,y2+v); /* draw the shadow */ + box(x1,y1,x2,y2); /* draw the box */ + ldraw(x1+h,y1+v,x1,y1); /* draw the corners */ + ldraw(x2+h,y2+v,x2,y2); + ldraw(x2+h,y1+v,x2,y1); + ldraw(x1+h,y2+v,x1,y2); +} +#endif + + +#ifneed box +/* parameters are topleft X,Y and bottomright X,Y + X ranges from 0 to 159, Y ranges from 0 to 99, top left is 0,0 + */ +box(x1,y1,x2,y2) int x1,y1,x2,y2; { + ldraw(x1,y1,x1,y2); + ldraw(x1,y2,x2,y2); /* appears to draw the box anticlockwise */ + ldraw(x2,y1,x2,y2); + ldraw(x1,y1,x2,y1); +} +#endif + + + +#ifneed gotoxy +gotoxy (xpos,ypos) int xpos,ypos; { /* 0,0 is top left corner */ + putscreen("\033="); + putc(ypos+' ',0); + putc(xpos+' ',0); + } +#endif + + +#ifneed beep +beep() { putc(7,0); } /* send bell character */ +#endif + +#ifneed home +home() { putc(30,0); } /* home cursor to top left */ +#endif + +#ifneed clr_screen +clr_screen() { putc(26,0); } /* home and erase screen */ +#endif + + +#ifneed clr_lend +clr_lend() { putc(24,0); } /* clear to end of line */ +#endif + +#ifneed clr_send +clr_send() { putc(23,0); } /* clear to end of screen */ +#endif + + + +#ifneed rev_vid +rev_vid() { putscreen ("\033B0"); } /* reverse background */ +#endif + +#ifneed nor_vid +nor_vid() { putscreen ("\033C0"); } +#endif + + + +#ifneed dim_vid +dim_vid() { putscreen ("\033B1"); } /* low intensity */ +#endif + +#ifneed bri_vid +bri_vid() { putscreen ("\033C1"); } +#endif + + + +#ifneed on_blink +on_blink() { putscreen ("\033B2"); } /* blinking characters */ +#endif + +#ifneed off_blink +off_blink() { putscreen ("\033C2"); } +#endif + + + +#ifneed ul_start +ul_start() { putscreen ("\033B3"); } /* underline */ +#endif + +#ifneed ul_stop +ul_stop() { putscreen ("\033C3"); } +#endif + + +#ifneed save_cursor +save_cursor() { putscreen ("\033B6"); } /* remember cursor position */ +#endif +#ifneed retn_cursor +retn_cursor() { putscreen ("\033C6"); } /* return to remembered pos */ +#endif + + +#ifneed on_status +on_status() { putscreen ("\033B7"); } /* status line preservation on */ +#endif +#ifneed off_status +off_status() { putscreen ("\033C7"); } +#endif + + +#ifneed ins_line +ins_line() { /* insert text line */ + putscreen("\033R"); + } +#endif +#ifneed del_line +del_line() { /* delete text line */ + putscreen("\033E"); + } +#endif + + +#ifneed on_cursor +on_cursor() { putscreen ("\033B4"); } /* (in)visible cursor */ +#endif +#ifneed off_cursor +off_cursor() { putscreen ("\033C4"); } +#endif + + + +/* Video Mode ON/OFF: video WORD, 8 bit video if 15 and 7 are both high */ +/* VM-ON 10000001 11111111 VM-OFF */ +/* ^video ^x^video */ +/* otherwise, video BYTE, high bit 7 interprets bits 0-6 as screen dots */ +/* 11111111 */ +/* ^video */ +/* e.g., */ +/* Non-VideoMode VideoMode */ +/* xx 1 11:0 where % is 1 01:0 1 11:0 xx */ +/* xx 3 11 2 the video 3 00 2 3 11 2 xx */ +/* xx 5 11 4 flag bit, 5 00 4 5 11 4 xx */ +/* x 7:%1 6 x is pixel 7:%0 6 7:%1 6 xx */ +/* ^ ^ ^ */ +/* to set the pixels, first do a gotoxy to character screen position */ +/* this mode is faster than Pixel ON/OFF if values are drawn from table */ +#ifneed vm_on +vm_on() { putscreen ("\033B5"); } /* video mode on */ +#endif +#ifneed vm_off +vm_off() { putscreen ("\033C5"); } +#endif + + +#ifneed pixel +pixel(x,y) int x,y; { /* x <= 159, y <= 99 */ + putscreen("\033*"); + putc(y+' ',0); putc(x+' ',0); +} +#endif + +#ifneed no_pixel +no_pixel(x,y) int x,y; { /* x <= 159, y <= 99 */ + putscreen("\033 "); + putc(y+' ',0); putc(x+' ',0); +} +#endif + + +#ifneed ldraw +/* use x1 <= x2, y1 <= y2, order is significant (Kaypro bug?) */ +ldraw(x1,y1,x2,y2) int x1,x2,y1,y2; { /* x <= 159, y <= 99 */ + putscreen("\033L"); + putc(y1+' ',0); putc(x1+' ',0); + putc(y2+' ',0); putc(x2+' ',0); +} +#endif + + +#ifneed lwipe +lwipe(x1,y1,x2,y2) int x1,x2,y1,y2; { /* x <= 159, y <= 99 */ + putscreen("\033D"); + putc(y1+' ',0); putc(x1+' ',0); + putc(y2+' ',0); putc(x2+' ',0); +} +#endif + + +#ifneed putscreen +putscreen(p) char *p; { + + while (*p) putc(*p++,0); + +} /* end: putscreen */ +#endif + + +#ifneed puts +puts(p) char *p; { + + while (*p) chrout(*p++); + +} /* end: puts */ +#endif + + +#ifneed chrout +chrout(c) char c; { /* SPECIAL FOR SCRIPT OPTION WITH LST: */ + + putc(c,0); + if ( MYscrtp ) putc(c,MYretnirp); + +} /* end: chrout */ +#endif + +/* end: MYLIB.C */ + + \ No newline at end of file diff --git a/Backgammon/Original/read.me b/Backgammon/Original/read.me new file mode 100644 index 0000000..148039f --- /dev/null +++ b/Backgammon/Original/read.me @@ -0,0 +1,56 @@ + +GAMNIV20.LBR +------------ +Gammon IV, Version 2.0, April 1, 1986. Full-screen backgammon for CP/M. + +This is the April Fool's Day (1986) edition of Gammon IV, the first major +update in many months. + +BACKGMMN.COM is the compiled and ready-to-run object file. It requires +either an ADM-3A compatible terminal ("old" Kaypro) or a screen with video +characteristics exactly like the Kaypro 2X, 4'84 or 10. However, this +version has the same CRT Module as formerly, including a clearly-marked user +patch area for non-Kaypro terminals. + +Improvements for Version 2.0 include: + +a) Several bugs and poltergeists, especially in the Arrange command, have + been completely exorcised. No bugs have been found in the Play command + for months; but, as before, Gammon IV will not invariably recognize a + forced move, either in your play or in its own. Gammon IV knows and + obeys all other official rules of backgammon. + +b) There are no "hidden" commands anymore. All commands are now shown in + the command lines, including X(pert and G(raphic. The G(raphic command + turns on the Kaypro video-able display. + +c) Input error handling now documents the BAR, HOME and HELP commands, so + first-time players do not need to read anything else in order to play + the game successfully. The X(pert mode dispenses with these messages, + so X(pert actually does mean expert, now! + +d) The "break" command is Control-C again, instead of ESCAPE. It was + all too easy to hit ESC instead of 1 on the Kaypro keyboard, leading to + an inadvertently paused game. + +e) The sanction against cheating (your opponent became Torve) has been + removed. + +f) If the cube is doubled during the opening rolloff to see who goes + first, and the cube value reaches 8, additional pairs on the dice + have no further effect. The cube's opening value will not go above 8. + +The computer's three styles of play have not been changed. Judging from the +feedback I've received, Gammon IV plays acceptably well at the current +levels. All information needed to re-compile the program is included in the +BACKGMMN.C file, including the details of C/80 3.1 configuration. The +compile and link steps are governed by BACKGMMN.SUB. + +This program is in the public domain, so distribute it freely. + +Enjoy! + +David C. Oshel +1219 Harding Ave. +Ames, Iowa 50010 + \ No newline at end of file diff --git a/Backgammon/README.md b/Backgammon/README.md new file mode 100644 index 0000000..af00ada --- /dev/null +++ b/Backgammon/README.md @@ -0,0 +1,51 @@ +# Gammon IV (VT100 ANSI edition) + +By: + David C. Oshel + 1219 Harding Ave. + Ames, Iowa 50010 + +This is a patched version of "Gammon IV, Version 2.0, April 1, 1986", a full-screen backgammon for CP/M. + +I patched the game to use VT100 compatible escape sequences (including ANSI color). + +Therefore, I removed most of the "user patch area" and hardcoded the VT100 sequences in the C code, +replaced the asm code of gotoxy() with C code and removed the Kaypro graphics routines +(and the "G(raphics" command) as it is not needed for my VT100 version. + +``BACKGMMN.COM`` is the compiled and ready-to-run game. + +Have fun playing! + +Anna Christina Naß + +## Compiling the game + +See [Compiler/Compiling.md](Compiler/Compiling.md) for information on compiling the game on CP/M. + +## From the original Version 2.0 READ.ME: + +Improvements for Version 2.0 include: + +a) Several bugs and poltergeists, especially in the Arrange command, have been completely exorcised. No bugs have been found in the Play command for months; but, as before, Gammon IV will not invariably recognize a forced move, either in your play or in its own. Gammon IV knows and obeys all other official rules of backgammon. + +b) There are no "hidden" commands anymore. All commands are now shown in the command lines, including X(pert. + +c) Input error handling now documents the BAR, HOME and HELP commands, so first-time players do not need to read anything else in order to play the game successfully. The X(pert mode dispenses with these messages, so X(pert actually does mean expert, now! + +d) The "break" command is Control-C again, instead of ESCAPE. It was all too easy to hit ESC instead of 1 on the Kaypro keyboard, leading to an inadvertently paused game. + +e) The sanction against cheating (your opponent became Torve) has been removed. + +f) If the cube is doubled during the opening rolloff to see who goes first, and the cube value reaches 8, additional pairs on the dice have no further effect. The cube's opening value will not go above 8. + +The computer's three styles of play have not been changed. Judging from the feedback I've received, Gammon IV plays acceptably well at the current levels. All information needed to re-compile the program is included in the BACKGMMN.C file, including the details of C/80 3.1 configuration. The compile and link steps are governed by BACKGMMN.SUB. + +This program is in the public domain, so distribute it freely. + +Enjoy! + + David C. Oshel + 1219 Harding Ave. + Ames, Iowa 50010 + diff --git a/Backgammon/backgmmn.c b/Backgammon/backgmmn.c new file mode 100644 index 0000000..d3a49ac --- /dev/null +++ b/Backgammon/backgmmn.c @@ -0,0 +1,1968 @@ +/* backgmmn.c */ + +/*************************************************************************** + + GAMMON IV, Version 2 + VT100 version by Anna Christina Nass + + 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 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.) + + 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: + + This version uses VT100-compatible ANSI escape sequences which work + on a RC2014 computer with Marco Maccaferri's VT100 VGA terminal module. + + 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 * +; * * +; * 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. * +; * * +; * 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. * +; *==============================================================* +; +; * REQUIRED * +; reserve 24 bytes apiece for the players' tokens (must terminate with 0) +; + DB 'USER PATCH AREA ' + DB 'ALL STRINGS MUST HAVE ZERO TERMINATOR->' + DB 'TOKEN1:' +TK1: DB 27,'[','3','1','m' ; computer's token, () + DB '(',')' + DB 27,'[','0','m' + DB 0,0,0,0,0,0,0,0,0,0,0,0,0 + DB 'TOKEN2:' +TK2: DB 27,'[','3','4','m' ; player's token, [] + DB '[',']' + DB 27,'[','0','m' + DB 0,0,0,0,0,0,0,0,0,0,0,0,0 + DB 'TOKEN3:' +TK3: DB 27,'[','3','1','m' ; computer's alternate token + DB 174,175 + DB 27,'[','0','m' + DB 0,0,0,0,0,0,0,0,0,0,0,0,0 + DB 'TOKEN4:' +TK4: DB 27,'[','3','4','m' ; player's alternate token + DB 222,221 + DB 27,'[','0','m' + DB 0,0,0,0,0,0,0,0,0,0,0,0,0 +; +;---------------------------------------------------------------------------- +; +; * Optional * + 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) +;---------------------------------------------------------------------------- + DB '<-END USER PATCH AREA' +;---------------------------------------------------------------------------- +; LOF LG +#endasm + +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 +} + +getcof() { +#asm + LXI H,CURSOF +#endasm +} + +getcon() { +#asm + LXI H,CURSON +#endasm +} + +gotoxy(x,y) int x,y; { + /* this outputs the VT100 Escape sequence for cursor positioning */ + char s[7]; + putc( 27, 0 ); putc( '[', 0 ); + puts( itoa(y+1,s) ); + putc( ';', 0 ); + puts( itoa(x+1,s) ); + putc( 'H', 0 ); +} /* end: gotoxy */ + +clr_screen() { + putc( 27, 0 ); puts( "[2J" ); + putc( 27, 0 ); putc( 'H', 0); +} /* standard */ + +on_cursor() { + puts ( getcon() ); +} /* optional "hide cursor" command */ + +off_cursor() { + 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, 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 '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; + swapped = tswap = expert = helpdisabled = yrdice = tone = FALSE; + show = moremsgline = TRUE; + + init_lib(); + OFFinterrupt(); /* enable jumpjack() on ctl-c */ + off_cursor(); + token1 = get1tkn(); + token2 = get2tkn(); + 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 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() { + 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 */ + + +swaptokens() { +char *temp; + + swapped ^= TRUE; + if (swapped) { + temp = token1; + token1 = token2; + token2 = temp; + } + else { + tswap ^= TRUE; + if (tswap) { + token1 = get3tkn(); + token2 = get4tkn(); + } + else { + token1 = get1tkn(); + token2 = get2tkn(); + } + } + +} /* 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" + diff --git a/Backgammon/backgmmn.com b/Backgammon/backgmmn.com new file mode 100644 index 0000000..c474be1 Binary files /dev/null and b/Backgammon/backgmmn.com differ diff --git a/Backgammon/gameplan.c b/Backgammon/gameplan.c new file mode 100644 index 0000000..5d5b93a --- /dev/null +++ b/Backgammon/gameplan.c @@ -0,0 +1,866 @@ + +/* Gammon IV, by David C. Oshel, Ames, Iowa */ + +/*--------------------------------------------------------------------*/ +/* GAMEPLAN.C -- separately compiled module, contains the tactics and */ +/* strategy for making the computer's move in Gammon IV */ +/* BACKGMMN.REL calls MyMove(), MyMove() calls topstone */ +/* and movestone and a few others. Uses the globals in */ +/* GAMEPLAN.HDR quite heavily. Bgversion declared here */ +/*--------------------------------------------------------------------*/ + +#include "gameplan.hdr" /* contains external, global declarations */ + +/*-------------------------------------------------------------------------*/ +/* */ +/* Bgversion is an external reference in the main module. Use this string */ +/* to take credit for a decent gameplaying algorithm if you find one! */ +/* */ +char *bgversion = ".20 -April 1, 1986- by David C. Oshel"; + /*....v....1....v....2....v....3....v...*/ + /* just 38 characters for version info! */ +/* */ +/*-------------------------------------------------------------------------*/ + + +/*=============================================*/ +/* M Y M O V E */ +/*=============================================*/ + + +naked() { /* am I leaving too many blots? */ +static int i, clink; + i = 24; clink = 0; + while (i) { + if (point[i].stones == 1 && point[i].owner == ME) clink++; + i--; + } + return (clink > 2); +} /* end: naked */ + + +yourfolly() { /* look for lotsa blots in your inner table */ +static int i, clink; + i = 18; clink = 0; + while (i < 25) { + if (point[i].owner == YU && point[i].stones == 1) + clink++; + i++; + } + return (clink >= 3); +} /* end: yourfolly */ + + +goodboard() { /* look for four made points near my inner table */ +static int i, clank, clink; + i = 9; clank = 0; + while (i > 3) { + if (point[i].owner == ME && point[i].stones > 1) clank++; + i--; + } + if (clank > 4) return (TRUE); /* bar is nearly blocked */ + + i = 6; clank = clink = 0; + while (i) { + if (point[i].owner != ME) + ; + else if (point[i].stones == 1) clink++; else clank++; + i--; + } + return (clank > 3 && clink < 2); +} /* end: goodboard */ + + +bearoff() { + return (topstone(ME) < 7); +} /* end: bearoff */ + + +scanahead( from ) int from; { +static int count; + + count = 0; + while (--from > 0) { + ++count; + if ( point[ from ].owner == YU ) return (count); + } + return (7); + +} /* end: scanahead */ + + +endgame() { /* Is no strategy required from here on in? */ + return ( (25 - topstone(YU)) > topstone(ME) ); +} /* end: endgame */ + + +/*------------------------------------------------------------*/ +/* MATCHUP */ +/* */ +/* 2-stone functions that force the choice of the next move. */ +/* These are the HEART and SOUL of this backgammon algorithm! */ +/*------------------------------------------------------------*/ + +setpend( from, to ) int from, to; { + pending.fr = from; + pending.to = to; + pending.flag = TRUE; +} /* end: setpend */ + + +clrpend() { + pending.flag = FALSE; +} /* end: clrpend */ + + +natural(f1,t1,f2,t2) int f1,t1,f2,t2; { + clrpend(); + if (point[t2].stones == 1 && t1 == f2) setpend(f2,t2); + return (pending.flag); +} /* end: natural */ + + +matchup( test4 ) int (* test4)(); { +static int i, j, ti, tj; + + if ( pending.flag ) return (FALSE); /* this is probably redundant */ + + for (i = 1; i < 26; i++) { + ti = list[0][i]; + if ( ti == ERROR ) goto zoo; + for (j = 1; j < 26; j++) { + tj = list[1][j]; + if ( tj == ERROR ) goto voo; + if ( (* test4)( i, ti, j, tj ) ) { + lurch( i, ti, 0); + return (TRUE); + } + voo: ; + } + zoo: ; + } + return (FALSE); + +} /* end: matchup */ + + +matchhi( test4 ) int (* test4)(); { +static int i, j, ti, tj; + + if ( pending.flag ) return (FALSE); /* this is probably redundant */ + + for (i = 1; i < 26; i++) { + ti = list[1][i]; + if ( ti == ERROR ) goto zoo; + for (j = 1; j < 26; j++) { + tj = list[0][j]; + if ( tj == ERROR ) goto voo; + if ( (* test4)( i, ti, j, tj ) ) { + lurch( i, ti, 1); + return (TRUE); + } + voo: ; + } + zoo: ; + } + return (FALSE); + +} /* end: matchhi */ + + + +/*--------------------------------------------------------*/ +/* CLOCKWISE and COUNTERCLOCK */ +/* */ +/* the rest of these are single-stone decisions based on */ +/* rules of thumb, board-scanning functions */ +/*--------------------------------------------------------*/ + +plainstupid( from ) int from; { /* don't break a safe point */ + return (from < 13 && (point[from].stones == 2 && scanahead(from) < 7)); +} /* end: plainstupid */ + + +unwise( innertablept ) int innertablept; { + /* if it's a hit, just for god's sake don't put him on the bar!! */ + if ( innertablept < 7 ) { + if (point[ innertablept ].owner == YU || + point[ YRBAR ].stones > 0) + return (TRUE); + } + return(FALSE); +} /* end: unwise */ + + + +covermine( from, to ) int from, to; { + if ( from < 8 ) return(FALSE); + return ( (point[ to ].stones == 1) && (point[ to ].owner == ME) ); +} /* end: covermine */ + + +idareyou( from, to ) int from, to; { + if (unwise( to )) return (FALSE); + if ( (point[ from ].stones != 2) + && (point[ to ].stones < 2) + && (scanahead( to ) > 6) ) return ( TRUE ); + else return (FALSE); +} /* end: idareyou */ + + +hitandrun( from, to ) int from, to; { + return ( point[ to ].owner == YU ); +} /* end: hitandrun */ + + +dbuild( from, to ) int from, to; { +static int diceleft; + diceleft = (myturns? 2 + movesleft: movesleft); + if (diceleft > 1) { + /* can't possibly be only one stone on from point */ + /* or kamikaze would have covered it on last move */ + return ( point[to].stones == 0 ); + } + return (FALSE); +} /* end: dbuild */ + + +kamikaze( from, to ) int from, to; { +/* cover my distant blot, or razzle-dazzle 'em with the long doubles hit */ +static int j, k, diceleft; + + k = from; + j = from - to; + diceleft = myturns * movesleft; /* NB: 2*2 == 2+2, "fourtunately" */ + while ( diceleft-- ) { /* predicting where doubles land is easy! */ + k -= j; + if (k < 1) return (FALSE); /* out of bounds */ + if ( point[ k ].stones == 0 ) continue; /* simplify */ + if ( point[ k ].stones == 1 ) /* found my blot or yours? */ + return (TRUE); + else if ( point[k].owner == YU ) /* found your blockade? */ + return (FALSE); + else continue; /* found my safe point, so ignore it */ + } + return (FALSE); + +} /* end: kamikaze */ + + +hittite( from, to ) int from, to; { + return (hitandrun(from,to) && to > 9); +} /* end: hittite */ + + +safehit( from, to ) int from, to; { + return (hittite(from,to) && idareyou(from,to)); +} /* end: safehit */ + + +foolsdemise( from, to ) int from, to; { + /* annihilate orphaned blots in enemy's inner, my outer table */ + return (to > 17 && point[to].owner == YU); +} /* end: foolsdemise */ + + +landonme( from, to ) int from, to; { + if ( plainstupid(from) ) return (FALSE); + if ( loneranger(from,to) ) { + if (from < 19 && to > 6) return(TRUE); + } + else return ( point[ to ].owner == ME && point[to].stones < 4); +} /* end: landonme */ + + + +/* these evaluations have meaning only in the endgame */ + + +nobackgammon( from, to ) int from, to; { /* endgame */ + return (from > 19); +} /* end: nobackgammon */ + + +crosstable( from, to ) int from, to; { + /* always move a table ahead if possible, in the endgame */ + if (from < 7) return (FALSE); + if (from > 18 && to <= 18) return (TRUE); + if (from > 12 && to <= 12) return (TRUE); + if (from > 6 && to <= 6) return (TRUE); + return (FALSE); +} /* end: crosstable */ + + +fiftytworule( from, to ) int from, to; { /* endgame */ +static int p; + if (from < 7) return (FALSE); /* not in inner table! */ + p = from % 6; + if (p == 0) return (TRUE); /* improve the six */ + if (p != 5) return ( (to % 6) < 3 ); /* best improve under five */ +} /* end: fiftytworule */ + + + + +/* these evaluations are universally applicable, last resort moves */ + + +gohome( from, to ) int from, to; { /* always go home if you can */ + return (to == MYHOME); +} /* end: gohome */ + + +scatter( from, to ) int from, to; { /* scatter, esp. in the endgame */ + if (plainstupid(from) || unwise(to)) return (FALSE); + return ( point[ from ].stones > 1 && point[ to ].stones == 0 ); +} /* end: scatter */ + + +runnerup( from, to ) int from, to; { + if (from < 10 || from > 18) return (FALSE); + return (TRUE); +} /* end: runnerup */ + + +loneranger( from, to ) int from, to; { + return( point[ from ].stones == 1 ); +} /* end: loneranger */ + + +run( dummy1, dummy2 ) int dummy1, dummy2; { /* MUST move something! */ + return (TRUE); +} /* end: run */ + + + + + +/* clockwise and counterclock make a 1-stone choice on rules of thumb */ + + +counterclock( test ) int (* test)(); { +static int i,j; + + for (i = 0; i < 2; i++) { + for (j = 1; j < 25; j++) { + if ( list[i][j] == ERROR ) continue; + if ( (* test)( j, list[i][j] ) ) { + lurch( j, list[i][j], i); + return ( TRUE ); + } } } + return (FALSE); + +} /* end: counterclock */ + + +clockwise( test ) int (* test)(); { +static int i,j; + + for (i = 0; i < 2; i++) { + for (j = 25; j > 0; j--) { + if ( list[i][j] == ERROR ) continue; + if ( (* test)( j, list[i][j] ) ) { + lurch( j, list[i][j], i); + return ( TRUE ); + } } } + return (FALSE); + +} /* end: clockwise */ + + + + + +/*-------------------------------------------*/ +/* Make Prime */ +/*-------------------------------------------*/ + +static int prmchk; + + +buildprime( f1,t1,f2,t2 ) int f1,t1,f2,t2; { + clrpend(); + /* check for the doubles bug */ + if ((dice[0] == dice[1]) && (point[f1].stones < 2)) return(FALSE); + + /* look for the combination */ + if ( t1 == prmchk && t2 == prmchk) setpend(f2,t2); + + /* stick like glue to a made point, but doubles may move forward */ + if (dice[0] != dice[1]) { + if ((f2 < 8) && (point[f2].stones == 2)) clrpend(); + if ((f1 < 8) && (point[f1].stones == 2)) clrpend(); + } + + return(pending.flag); + +} /* end: buildprime */ + + +makeprime() { +static int i, tab[] = { ERROR,1,2,3,20,22,24,9,4,6,8,5,7 }; + i = 12; + while (i) { + prmchk = tab[i]; + i--; + if ( point[ prmchk ].stones > 1 ) continue; + else if ( matchup( buildprime ) ) return(TRUE); + } + return(FALSE); +} /* end: makeprime */ + + +coverprime( from, to ) int from, to; { + return (((to == prmchk) && + (point[prmchk].owner == ME)) && + (point[from].stones != 2)); +} /* coverprime */ + + +cleanup() { +static int i, tab[] = { ERROR,1,2,3,20,22,24,9,4,6,8,5,7 }; + i = 12; + while (i) { + prmchk = tab[i]; + i--; + if ( point[ prmchk ].stones != 1 ) continue; + else if ( counterclock( coverprime ) ) return(TRUE); + } + return(FALSE); +} /* end: cleanup */ + + +/*-------------------------------------*/ +/* Walking Prime */ +/*-------------------------------------*/ + +swivelhips( from, to ) int from, to; { + return ( from > prmchk ); +} /* end: swivelhips */ + + +slink( from, to ) int from, to; { + return( (from > prmchk) && (point[to].stones == 1) ); +} /* end: slink */ + + +weasel() { + if ( clockwise( slink ) ) + return(TRUE); + if ( counterclock( swivelhips ) ) + return(TRUE); + if ( clockwise( run ) ) + return(TRUE); +} /* end: weasel */ + +ihaveprime( from ) int from; { +static int i, to, ez; + ez = 0; + for (i = 0; i < 6; i++) { + to = from - i; + if ((point[to].owner == ME) && (point[to].stones > 1)) ez++; + } + return (ez > 4); +} /* end: ihaveprime */ + + +walkingprime() { +/* looks for the walking prime anywhere in the front tables */ +/* then tries to bring up a runner from behind the prime, */ +/* ensuring that a back stone WILL move before a front one */ +static int i; + i = 12; + while (i > 5) { + if ( ihaveprime(i) ) { + prmchk = i; + if ( weasel() ) return (TRUE); + } + i--; + } + return(FALSE); +} /* end: walkingprime */ + + +/*---------- Book Moves ----------*/ +/* only valid if my move is first */ +/*--------------------------------*/ + +zip(a,b,c,d) int a,b,c,d; { + lurch(a,b,0); + lurch(c,d,0); + movesleft = 0; return( TRUE ); +} /* end: zip */ + + +zoom( a,b,c,d,e,f,g,h ) int a,b,c,d,e,f,g,h; { + myturns = 0; zip(a,b,c,d); zip(e,f,g,h); return( TRUE ); +} /* end: zoom */ + + +book() { +int a,b; + if (!firstmove) return (FALSE); + firstmove = FALSE; + a = min(dice[0],dice[1]); + b = max(dice[0],dice[1]); + switch (level) { + case 0: { return ( book0(a,b) ); break; } + case 1: { return ( book1(a,b) ); break; } + case 2: { return ( book2(a,b) ); break; } + } +} +book0( a,b ) int a, b; { + switch (a) { + case 1: { switch (b) { + case 1: return ( zoom(8,7,8,7,6,5,6,5) ); + case 2: return ( zip(24,23,13,11) ); + case 3: return ( zip(8,5,6,5) ); + case 4: return ( zip(24,23,13,9) ); + case 5: return ( zip(24,23,13,8) ); + case 6: return ( zip(13,7,8,7) ); + } + break; } + case 2: { switch (b) { + case 2: return ( zoom(6,4,6,4,13,11,13,11) ); + case 3: return ( zip(13,11,13,10) ); + case 4: return ( zip(8,4,6,4) ); + case 5: return ( zip(13,8,13,11) ); + case 6: return ( zip(24,18,13,11) ); + } + break; } + case 3: { switch (b) { + case 3: return ( zoom(13,10,13,10,10,7,10,7) ); + case 4: return ( zip(13,10,13,9) ); + case 5: return ( zip(13,10,13,8) ); + case 6: return ( zip(24,18,13,10) ); + } + break; } + case 4: { switch (b) { + case 4: return ( zoom(13,9,13,9,24,20,24,20) ); + case 5: return ( zip(13,8,13,9) ); + case 6: return ( zip(24,18,18,14) ); + } + break; } + case 5: { switch (b) { + case 5: return ( zoom(13,8,13,8,8,3,8,3) ); + case 6: return ( zip(24,18,18,13) ); + } + break; } + case 6: { return ( zoom(13,7,13,7,24,18,24,18) ); + break; } + } + +} /* end: book0 */ +book1( a,b ) int a, b; { /* mostly follows Becker */ + switch (a) { + case 1: { switch (b) { + case 1: return ( zoom(8,7,8,7,6,5,6,5) ); + case 2: return ( zip(13,11,6,5) ); + case 3: return ( zip(8,5,6,5) ); + case 4: return ( zip(13,9,6,5) ); + case 5: return ( zip(13,8,6,5) ); + case 6: return ( zip(13,7,8,7) ); + } + break; } + case 2: { switch (b) { + case 2: return ( zoom(6,4,6,4,13,11,13,11) ); + case 3: return ( zip(13,11,13,10) ); + case 4: return ( zip(8,4,6,4) ); + case 5: return ( zip(13,8,13,11) ); + case 6: return ( zip(13,7,7,5) ); + } + break; } + case 3: { switch (b) { + case 3: return ( zoom(13,10,13,10,8,5,8,5) ); + case 4: return ( zip(13,10,13,9) ); + case 5: return ( zip(13,8,8,5) ); + case 6: return ( zip(13,7,13,10) ); + } + break; } + case 4: { switch (b) { + case 4: return ( zoom(13,9,13,9,9,5,9,5) ); + case 5: return ( zip(13,8,13,9) ); + case 6: return ( zip(13,7,13,9) ); + } + break; } + case 5: { switch (b) { + case 5: return ( zoom(13,8,13,8,8,3,8,3) ); + case 6: return ( zip(13,7,13,8) ); + } + break; } + case 6: { return ( zoom(13,7,13,7,24,18,24,18) ); + break; } + } + +} /* end: book1 */ +book2( a,b ) int a, b; { /* mostly follows Becker */ + switch (a) { + case 1: { switch (b) { + case 1: return ( zoom(8,7,8,7,6,5,6,5) ); + case 2: return ( zip(13,11,24,23) ); + case 3: return ( zip(8,5,6,5) ); + case 4: return ( zip(13,9,24,23) ); + case 5: return ( zip(13,8,24,23) ); + case 6: return ( zip(13,7,8,7) ); + } + break; } + case 2: { switch (b) { + case 2: return ( zoom(6,4,6,4,24,23,24,23) ); + case 3: return ( zip(13,11,13,10) ); + case 4: return ( zip(8,4,6,4) ); + case 5: return ( zip(13,8,13,11) ); + case 6: return ( zip(13,7,13,11) ); + } + break; } + case 3: { switch (b) { + case 3: return ( zoom(13,10,13,10,10,7,10,7) ); + case 4: return ( zip(13,10,13,9) ); + case 5: return ( zip(13,8,8,5) ); + case 6: return ( zip(13,7,13,10) ); + } + break; } + case 4: { switch (b) { + case 4: return ( zoom(13,9,13,9,9,5,9,5) ); + case 5: return ( zip(13,8,13,9) ); + case 6: return ( zip(13,7,13,9) ); + } + break; } + case 5: { switch (b) { + case 5: return ( zoom(13,8,13,8,8,3,8,3) ); + case 6: return ( zip(13,7,13,8) ); + } + break; } + case 6: { return ( zoom(13,7,13,7,24,18,24,18) ); + break; } + } + +} /* end: book2 */ + + +/*====== MyMove ======*/ + +torve() { + if ( makeprime() ) { /* this will use doubles, if it can */ + return; + } + else if ( walkingprime() ) { /* i have six prime points, so run!!! */ + return; + } + else if ( dice[0] == dice[1] ) { /* this is too easy! */ + if ( counterclock( kamikaze ) ) + return; + if ( counterclock( dbuild ) ) /* claim new turf */ + return; + if ( clockwise( run ) ) + return; + } + else if ( cleanup() ) { /* cover my single blot on prime points */ + return; + } + else if ( bearoff() ) { /* I'm ready, but you're in the back game! */ + if ( counterclock( gohome ) ) + return; + if ( clockwise( run ) ) + return; + } + else { + if ( clockwise( hitandrun ) ) + return; + if ( matchup( natural ) ) + return; + if ( clockwise( landonme ) ) + return; + if ( counterclock( runnerup ) ) + return; + if ( clockwise( scatter ) ) + return; + if ( clockwise( run ) ) + return; + } + +} /* end: torve */ + + + +villiers() { + if ( makeprime() ) { /* this will use doubles, if it can */ + return; + } + else if ( walkingprime() ) { /* i have six prime points, so run!!! */ + return; + } + else if ( dice[0] == dice[1] ) { /* this is too easy! */ + if ( counterclock( kamikaze ) ) + return; + if ( counterclock( dbuild ) ) /* claim new turf */ + return; + if ( clockwise( run ) ) + return; + } + else if ( cleanup() ) { /* cover my single blot on prime points */ + return; + } + else if ( bearoff() ) { /* I'm ready, but you're in the back game! */ + if ( counterclock( gohome ) ) + return; + if ( clockwise( run ) ) + return; + } + else { + if ( clockwise( foolsdemise ) ) + return; + if ( clockwise( idareyou ) ) + return; + if ( counterclock( covermine ) ) + return; + if ( matchup( natural ) ) + return; + if ( clockwise( landonme ) ) + return; + if ( clockwise( runnerup ) ) + return; + if ( clockwise( scatter ) ) + return; + if ( clockwise( run ) ) + return; + } + +} /* end: villiers */ + + + +louisa() { + if ( makeprime() ) { /* this will use doubles, if it can */ + return; + } + else if ( walkingprime() ) { /* i have six prime points, so run!!! */ + return; + } + else if ( dice[0] == dice[1] ) { /* this is too easy! */ + if ( counterclock( kamikaze ) ) + return; + if ( counterclock( dbuild ) ) /* claim new turf */ + return; + if ( clockwise( run ) ) + return; + } + else if ( cleanup() ) { /* cover my single blot on prime points */ + return; + } + else if ( bearoff() ) { /* I'm ready, but you're in the back game! */ + if ( counterclock( gohome ) ) + return; + if ( clockwise( run ) ) + return; + } + else if ( (!naked() && goodboard()) || yourfolly() ) { + if ( clockwise( hitandrun ) ) + return; + if ( matchup( natural ) ) + return; + if ( clockwise( landonme ) ) + return; + if ( counterclock( runnerup ) ) + return; + if ( clockwise( scatter ) ) + return; + if ( clockwise( run ) ) + return; + } + else { + if ( clockwise( foolsdemise ) ) + return; + if ( clockwise( idareyou ) ) + return; + if ( counterclock( covermine ) ) + return; + if ( matchup( natural ) ) + return; + if ( clockwise( landonme ) ) + return; + if ( clockwise( runnerup ) ) + return; + if ( clockwise( scatter ) ) + return; + if ( clockwise( run ) ) + return; + } +} /* end: louisa */ + + +mymove() { +int i, d; + + if ( nomove() ) { + if (lookforit && (dice[0] != dice[1])) { + lookforit = FALSE; + puts("\008... "); + switch (level) { + case 0: { puts("Blocked!"); break; } + case 1: { puts("Well, no!"); break; } + case 2: { puts("Thurb!"); break; } + } + sleep(10); + restoreboard(); + update(); + + /* put the high die in list zero */ + d = dice[0]; dice[0] = dice[1]; dice[1] = d; + + cantuse = ERROR; movesleft = 2; myturns = 1; + switch (level) { + case 0: { setchat("I move"); break; } + case 1: { setchat("Let's try"); break; } + case 2: { setchat("Move is"); break; } + } + debug(chatter); + prmchk = 12; + weasel(); + /* the rules say, use both dice if you can, or */ + /* the highest if one or the other but not both */ + } + else { + lookforit = TRUE; + strcat(chatter," and now I'm blocked "); + myturns = movesleft = 0; + } } + else if ( book() ) { + return; + } + else if ( pending.flag ) { + lurch( pending.fr, pending.to, 1 ); + clrpend(); + } + else if ( endgame() ) { /* very solid tactics here!! */ + if ( clockwise( gohome ) ) + return; + if ( clockwise( nobackgammon ) ) /* no excuse! */ + return; + if ( clockwise( crosstable ) ) + return; + if ( clockwise( fiftytworule ) ) + return; + if ( clockwise( scatter ) ) + return; + if ( clockwise( run ) ) + return; + } + else if ( point[ MYBAR ].stones > 0 ) { /* I'm on the bar! */ + if ( clockwise( hitandrun ) ) /* wreak havoc, please */ + return; + if ( clockwise( run ) ) /* note: uses low die first */ + return; + } + else switch (level) { + case 0: { villiers(); break; } + case 1: { louisa(); break; } + case 2: { torve(); break; } + } + + +} /* end: mymove */ + +/*------------------------------*/ +/* end of the GAMEPLAN.C module */ +/*------------------------------*/ + \ No newline at end of file diff --git a/Backgammon/gameplan.hdr b/Backgammon/gameplan.hdr new file mode 100644 index 0000000..49f8c27 --- /dev/null +++ b/Backgammon/gameplan.hdr @@ -0,0 +1,42 @@ +/* GAMEPLAN.HDR */ + +/*================================================================*/ +/* change nothing in this file */ +/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ + +#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 +#define MYLEVEL 2 + +extern int list[2][28]; /* two dice, two lists */ + +extern 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 */ + +extern struct { int cube, whosecube; } doubles; + +extern struct { int fr,to,flag; } pending; + +extern int level, dice[2], myscore, yrscore, player, movesleft, cantuse, + myturns, swapped, tswap, deciding, expert, tone, show, moremsgline, + firstmove, helpdisabled, yrdice, lookforit, startcubevalue; + +extern char *chatter[80]; + +/*----------------------------------------------*/ +/* end of header -- change NOTHING in this file */ +/*----------------------------------------------*/ + \ No newline at end of file diff --git a/Backgammon/mylib2.c b/Backgammon/mylib2.c new file mode 100644 index 0000000..4082da3 --- /dev/null +++ b/Backgammon/mylib2.c @@ -0,0 +1,541 @@ +/* +MYLIB2.C + +These routines are CONDITIONALLY compiled; i.e., only as needed. + +---------------------------------------------------------------------- +Incorporates special mods used by my Backgammon game, BACKGMMN.C etc. +---------------------------------------------------------------------- + +A set of common I/O functions that seem to turn up a lot in my programs, +including terminal functions for Kaypro 10, 4'84, 2X etc. + +Uses Software Toolworks' C/80 3.1 compiler. Place #include "mylib2.c" at the +end of your source file for correct CONDITIONAL COMPILATION. + +David C. Oshel +1219 Harding Ave. +Ames, Iowa 50010 + +Last modified: March 25, 1986 + +----------------------------------------------------------------------------- +** WARNING ** These routines use direct console IO, bdos function 6! + + YOU MUST CALL INIT_LIB() BEFORE USING THESE ROUTINES! + + ======= UTILITIES ======= + + * init_lib() - CALL THIS FIRST, OR THE RESULT WILL BE VERY STRANGE! + * + * puts(p) - unformatted print, e.g., puts("Hello, sailor!\n"); + * gets(p,max) - printable input only, no prompt character + * + * ask(p) - demand Yes or No response to question p + * random() - effective random 16-bit integer IFF gets() is used + * sleep(n) - sleep n/10ths of a second, roughly (from C80.LIB) + * rollup() - roll up 23 lines of screen + * ONscript() - printer echo ON for output via puts, chrout + * OFFscript() - printer echo OFF for output via puts, chrout + * ONinterrupt() - Ctl-C, Ctl-B cause program exit + * OFFinterrupt() - Ctl-C, Ctl-B cause comedy + * hide_input(p,max) - like gets, but used when entering passwords + * chrout(c) - if scripting, echo output also to LST: + * putscreen(p) - like puts, but always and only to screen + + + ======= KAYPRO 10 TERMINAL/VIDEO FUNCTIONS ======= + + * gotoxy(x,y) - 0,0 is top left, horz <= 79 precedes vert <= 24, + * where 0,24 is on the 25th, status, line. + * beep() - terminal bell + * home() - home cursor, do not clear screen + * clr_screen() - home and clear + * + * shadow_box(h,v,x1,y1,x2,y2) - like box, but with shadow, calls box + * box(tlx,tly,brx,bry) - draw a line box, coords: topleft XY, bottomright XY + * note that box calls ldraw(x1,y1,x2,y2), below + * + * clr_lend() - clear from cursor to end of line + * clr_send() - clear from cursor to end of screen + * rev_vid(), + * nor_vid() - reverse field + * dim_vid(), + * bri_vid() - low/high intensity + * on_blink(), + * off_blink() - blinking chars + * ul_start(), + * ul_stop() - start/stop underline + * save_cursor(), + * retn_cursor() - remember/restore current cursor location + * ins_line(), + * del_line() - insert/delete screen text line + * on_cursor(), + * off_cursor() - hide/show cursor + * vm_on(), + * vm_off() - "Video Mode" commands + * pixel(x,y) - draw pixel at x,y (video coords, x <= 159, y <= 99) + * no_pixel(x,y) - erase pixel at x,y + * ldraw(x1,y1,x2,y2) - draw/ erase graphics line, see discussion for box + * lwipe(x1,y1,x2,y2) - range for video coordinates as for pixel +*/ + + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + + +/* hide this here so's not to worry about it elsewhere */ +/* "printf.c" collides with one of these, can't remember which */ +/* puts() takes longer to write, but executes faster */ +extern char Cmode, IOpread[4], IOpwrit[4], IOpeof[4]; + +/* make these known only to what follows */ +static int MYbstout, MYscrtp, MYretnirp; /* odd names mark semi-private */ +static unsigned RNDloc; /* effective random location, bumped by gets() */ + /* and scrambled when the LCG random() is called */ + /* makes a decent algorithm for interactive games */ + + +#ifneed init_lib +init_lib() { + + MYretnirp = fopen("LST:","w"); + OFFscript(); + ONinterrupt(); + Cmode = 0; + IOpread[0] = 6; IOpwrit[0] = 6; + +} /* end: init_lib */ +#endif + + + + +#ifneed random +random() { /* depends on effective random location spun by gets() */ + + RNDloc = 2053 * RNDloc + 13849; + return (RNDloc); +} +#endif + + + +#ifneed ONscript +ONscript() { + + MYscrtp = TRUE; + +} +#endif +#ifneed OFFscript +OFFscript() { + + MYscrtp = FALSE; + +} +#endif + + +#ifneed ONinterrupt +ONinterrupt() { + + MYbstout = TRUE; + +} +#endif +#ifneed OFFinterrupt +OFFinterrupt() { + + MYbstout = FALSE; + +} +#endif + + +#ifneed ask +ask(p) char *p; { + +char ch, resp[2]; + +loo: puts(p); + gets(resp,1); + ch = toupper( *resp ); + if ( !( ch == 'Y' || ch == 'N' )) { + puts("Please answer the question, Yes or No.\n"); + goto loo; + } + return (ch == 'Y'); + +} /* end: ask */ +#endif + + +#ifneed rollup +rollup() { + +int i; + + for (i=0; i<23; i++) puts("\n"); + +} /* end: rollup */ +#endif + + +#ifneed sleep +sleep( n ) int n; { /* sleep for n/10 seconds, 0 <= n < 256 */ + + n; /* get n into HL */ +#asm + MOV B,L ;delay B/10ths of a second +__DL0: MVI A,100 ;100 milliseconds, 1/10 second +__DL1: MVI C,249 ;1 millisecond per unit of A at 4 MHz +__DL2: DCR C ;Leventhal, Z80 Assembly Language Programming + JNZ __DL2 + DCR A + JNZ __DL1 + DCR B + JNZ __DL0 ;on exit, HL has FALSE if n was 0, else TRUE +#endasm +} /* end: sleep */ +#endif + + +/*========================================*/ +/* GETS(p, maxinput) */ +/* Local getline function with special *---* WARNING: */ +/* input handling, 1 <= len <= maxinput *---* Execute INIT_LIB() first !! */ +/* Updates effective random, RNDloc, */ +/* Forces input from CONSOLE only! */ +/*========================================*/ + +#ifneed gets +gets(p,maxinput) char *p; int maxinput; { + +/* This function depends on BDOS Function #6. Init_lib() sets Cmode=0 and + IOpread[0]=6 and IOpwrit[0]=6 (courtesy of and peculiar to C/80 3.1) + YOU must ensure that the target string is long enough to collect the + entire maximum input allowed and specified, INCLUDING FINAL NULL! */ + +static int len; +static char ch; + + len = -1; + if (maxinput < 1 || maxinput > 127) maxinput = 79; + + /*--------------------------------*/ + /* SPECIAL ROUTINE FOR BACKGAMMON */ + /*--------------------------------*/ + +loo: while ( !(ch = getc(0)) ) acg(); /* keep the game lively */ + + if (len < 0) len = 0; /* don't destroy prompt by backing up */ + if (ch == '\n') { /* end of line? don't store newline */ + *p = '\0'; /* mark it with a B for baby and me */ + /* chrout('\n'); */ /* but DON'T echo newline */ + return ( len ); /* <--- HERE IS THE FUNCTION EXIT! */ + } + else if (ch == '\b' || ch == 0x7F) { /* backspace? rubout? */ + if (len--) { /* where's the prompt? */ + puts("\008 \008"); /* we're ok, echo erase */ + p--; /* delete from string */ + } + } + + /*--------------------------------*/ + /* SPECIAL ROUTINE FOR BACKGAMMON */ + /*--------------------------------*/ + + else if (ch == '\003') { /* user bailout key is Ctrl-C, not ESC */ + if (MYbstout) exit(); + else { + haltgame(); /* sets whofirst flag and does jumpjack() */ + } + } + + else if (ch == '\025' || ch == '\030') { /* Ctl-U, Ctl-X */ + while (len--) { + p--; + puts("\008 \008"); + } + } + else if (len == maxinput) { /* test specials before testing len */ + chrout('\007'); + } + else if (ch > 31 && ch < 127) { /* printable char? */ + chrout(ch); /* yes, echo it */ + *p++ = ch; /* collect it */ + len++; /* keep track of it */ + } + else { /* control chars? */ + chrout('\007'); + } + goto loo; + +} /* end: gets */ +#endif + + + + +#ifneed hide_input +hide_input(s,len) char *s; int len; { + +/* receive at most len chars in s buffer, + terminate string with zero, + but echo each char with 1, 2, or 3 meaningless dots */ + +char ch; int num; + + if ((len < 1) || (len > 127)) len = 127; + num = 0; + for (;;) { /* forever */ + while ((ch = getc(0)) == 0) /* bdos 6 does not wait, so we do */ + ; + if ((ch == '\r') || (ch == '\n') || (num++ > len)) { + /* not sure what the CR key actually is to bdos 6 & C/80 */ + *s++ = '\0'; + return; /* this way out */ + } + if ((num % 2) == 0) putc('.',0); /* deception, illusion */ + if ((num % 5) == 0) putc('.',0); + putc('.',0); + *s++ = ch; + } + +} /* end: hide_input */ +#endif + + + + +/*------------------------ kpro stuff -------------------------*/ + +#ifneed shadow_box +/* like box, but with horizontal & vertical displacement for shadow */ +shadow_box(h,v,x1,y1,x2,y2) int h,v,x1,y1,x2,y2; +{ + box(x1+h,y1+v,x2+h,y2+v); /* draw the shadow */ + box(x1,y1,x2,y2); /* draw the box */ + ldraw(x1+h,y1+v,x1,y1); /* draw the corners */ + ldraw(x2+h,y2+v,x2,y2); + ldraw(x2+h,y1+v,x2,y1); + ldraw(x1+h,y2+v,x1,y2); +} +#endif + + +#ifneed box +/* parameters are topleft X,Y and bottomright X,Y + X ranges from 0 to 159, Y ranges from 0 to 99, top left is 0,0 + */ +box(x1,y1,x2,y2) int x1,y1,x2,y2; { + ldraw(x1,y1,x1,y2); + ldraw(x1,y2,x2,y2); /* appears to draw the box anticlockwise */ + ldraw(x2,y1,x2,y2); + ldraw(x1,y1,x2,y1); +} +#endif + + + +#ifneed gotoxy +gotoxy (xpos,ypos) int xpos,ypos; { /* 0,0 is top left corner */ + putscreen("\033="); + putc(ypos+' ',0); + putc(xpos+' ',0); + } +#endif + + +#ifneed beep +beep() { putc(7,0); } /* send bell character */ +#endif + +#ifneed home +home() { putc(30,0); } /* home cursor to top left */ +#endif + +#ifneed clr_screen +clr_screen() { putc(26,0); } /* home and erase screen */ +#endif + + +#ifneed clr_lend +clr_lend() { putc(24,0); } /* clear to end of line */ +#endif + +#ifneed clr_send +clr_send() { putc(23,0); } /* clear to end of screen */ +#endif + + + +#ifneed rev_vid +rev_vid() { putscreen ("\033B0"); } /* reverse background */ +#endif + +#ifneed nor_vid +nor_vid() { putscreen ("\033C0"); } +#endif + + + +#ifneed dim_vid +dim_vid() { putscreen ("\033B1"); } /* low intensity */ +#endif + +#ifneed bri_vid +bri_vid() { putscreen ("\033C1"); } +#endif + + + +#ifneed on_blink +on_blink() { putscreen ("\033B2"); } /* blinking characters */ +#endif + +#ifneed off_blink +off_blink() { putscreen ("\033C2"); } +#endif + + + +#ifneed ul_start +ul_start() { putscreen ("\033B3"); } /* underline */ +#endif + +#ifneed ul_stop +ul_stop() { putscreen ("\033C3"); } +#endif + + +#ifneed save_cursor +save_cursor() { putscreen ("\033B6"); } /* remember cursor position */ +#endif +#ifneed retn_cursor +retn_cursor() { putscreen ("\033C6"); } /* return to remembered pos */ +#endif + + +#ifneed on_status +on_status() { putscreen ("\033B7"); } /* status line preservation on */ +#endif +#ifneed off_status +off_status() { putscreen ("\033C7"); } +#endif + + +#ifneed ins_line +ins_line() { /* insert text line */ + putscreen("\033R"); + } +#endif +#ifneed del_line +del_line() { /* delete text line */ + putscreen("\033E"); + } +#endif + + +#ifneed on_cursor +on_cursor() { putscreen ("\033B4"); } /* (in)visible cursor */ +#endif +#ifneed off_cursor +off_cursor() { putscreen ("\033C4"); } +#endif + + + +/* Video Mode ON/OFF: video WORD, 8 bit video if 15 and 7 are both high */ +/* VM-ON 10000001 11111111 VM-OFF */ +/* ^video ^x^video */ +/* otherwise, video BYTE, high bit 7 interprets bits 0-6 as screen dots */ +/* 11111111 */ +/* ^video */ +/* e.g., */ +/* Non-VideoMode VideoMode */ +/* xx 1 11:0 where % is 1 01:0 1 11:0 xx */ +/* xx 3 11 2 the video 3 00 2 3 11 2 xx */ +/* xx 5 11 4 flag bit, 5 00 4 5 11 4 xx */ +/* x 7:%1 6 x is pixel 7:%0 6 7:%1 6 xx */ +/* ^ ^ ^ */ +/* to set the pixels, first do a gotoxy to character screen position */ +/* this mode is faster than Pixel ON/OFF if values are drawn from table */ +#ifneed vm_on +vm_on() { putscreen ("\033B5"); } /* video mode on */ +#endif +#ifneed vm_off +vm_off() { putscreen ("\033C5"); } +#endif + + +#ifneed pixel +pixel(x,y) int x,y; { /* x <= 159, y <= 99 */ + putscreen("\033*"); + putc(y+' ',0); putc(x+' ',0); +} +#endif + +#ifneed no_pixel +no_pixel(x,y) int x,y; { /* x <= 159, y <= 99 */ + putscreen("\033 "); + putc(y+' ',0); putc(x+' ',0); +} +#endif + + +#ifneed ldraw +/* use x1 <= x2, y1 <= y2, order is significant (Kaypro bug?) */ +ldraw(x1,y1,x2,y2) int x1,x2,y1,y2; { /* x <= 159, y <= 99 */ + putscreen("\033L"); + putc(y1+' ',0); putc(x1+' ',0); + putc(y2+' ',0); putc(x2+' ',0); +} +#endif + + +#ifneed lwipe +lwipe(x1,y1,x2,y2) int x1,x2,y1,y2; { /* x <= 159, y <= 99 */ + putscreen("\033D"); + putc(y1+' ',0); putc(x1+' ',0); + putc(y2+' ',0); putc(x2+' ',0); +} +#endif + + +#ifneed putscreen +putscreen(p) char *p; { + + while (*p) putc(*p++,0); + +} /* end: putscreen */ +#endif + + +#ifneed puts +puts(p) char *p; { + + while (*p) chrout(*p++); + +} /* end: puts */ +#endif + + +#ifneed chrout +chrout(c) char c; { /* SPECIAL FOR SCRIPT OPTION WITH LST: */ + + putc(c,0); + if ( MYscrtp ) putc(c,MYretnirp); + +} /* end: chrout */ +#endif + +/* end: MYLIB.C */ + + \ No newline at end of file diff --git a/README.md b/README.md index 3b2b5da..978732d 100644 --- a/README.md +++ b/README.md @@ -8,12 +8,6 @@ These games use VT100 either per default or were adopted to using them. Whenever possible, I will provide working binaries (i.e. .COM files) and also the sources or original files that I found online. -## Backgammon - -by David C. Oshel; C/asm sources available; I've adopted the code to VT100. - -See this repository: https://git.imzadi.de/acn/backgammon-vt100 - ## Games in this repository * [2048](2048/) @@ -34,6 +28,7 @@ See this repository: https://git.imzadi.de/acn/backgammon-vt100 * [Robot Chase](RobotChase/) * [MazezaM](MazezaM/) * [Pac](Pac/) +* [Backgammon](Backgammon/) ## More Games on the Interwebs