diff --git a/Original/backgmmn.c b/Original/backgmmn.c new file mode 100644 index 0000000..ff20aed --- /dev/null +++ b/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/Original/backgmmn.com b/Original/backgmmn.com new file mode 100644 index 0000000..fffa172 Binary files /dev/null and b/Original/backgmmn.com differ diff --git a/Original/backgmmn.lbr b/Original/backgmmn.lbr new file mode 100644 index 0000000..c0e9097 Binary files /dev/null and b/Original/backgmmn.lbr differ diff --git a/Original/backgmmn.sub b/Original/backgmmn.sub new file mode 100644 index 0000000..acb857c --- /dev/null +++ b/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/Original/gameplan.c b/Original/gameplan.c new file mode 100644 index 0000000..5d5b93a --- /dev/null +++ b/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/Original/gameplan.hdr b/Original/gameplan.hdr new file mode 100644 index 0000000..49f8c27 --- /dev/null +++ b/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/Original/mylib2.c b/Original/mylib2.c new file mode 100644 index 0000000..4082da3 --- /dev/null +++ b/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/Original/read.me b/Original/read.me new file mode 100644 index 0000000..148039f --- /dev/null +++ b/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