chris@eneevax.UUCP (01/23/84)
Here is source code for a modified version of the game ``chase''. While bored one afternoon I tried it and got annoyed at the various bugs, so I rewrote it. I've incorporated a suggestion from Linton Floyd that makes the game considerably more challenging. This program requires the Maryland Window Library, and includes a random number generator written in Vax assembly code (no, I didn't write it; I got it from someone who got it from someone else). However, if you have the C library rand() routine you can write a randint() function easily enough -- see the comments in the Makefile. Oh yes, the best part: the "-r" option lets you use the Rogue motion keys. Chris : Run this shell script with "sh" not "csh" PATH=:/bin:/usr/bin:/usr/ucb export PATH all=FALSE if [ $1x = -ax ]; then all=TRUE fi /bin/echo 'Extracting Makefile' sed 's/^X//' <<'//go.sysin dd *' >Makefile # Makefile for chase # # rand.s is for Vaxen. The only routines I use from it are srand and randint. # randint(n) returns an integer in the range [0..n-1], and can be done on a # 32 bit machine with # (int) ((double) n * ((double) rand () / 2147483648.)) # or for 16 bit machines # (int) ((double) n * ((double) rand () / 32768.)) # assuming that rand() returns a positive integer. # # -- 23 Jan 1983 Chris Torek OBJS= chase.o dochase.o gen.o initwin.o key.o move.o rand.o CFLAGS= -O chase: $(OBJS) cc -o chase $(OBJS) -lwinlib -ljobs -ltermlib chase.o: chase.c chase.h dochase.o: dochase.c chase.h gen.o: gen.c chase.h initwin.o: initwin.c chase.h key.o: key.c chase.h move.o: move.c chase.h rand.o: rand.s //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 Makefile /bin/echo -n ' '; /bin/ls -ld Makefile fi /bin/echo 'Extracting chase.c' sed 's/^X//' <<'//go.sysin dd *' >chase.c #ifndef lint static char rcsid[] = "$Header: /usr/enee/chris/bin/src/chase/RCS/chase.c,v 1.2 84/01/23 04:05:55 chris Rel $"; #endif X/* * $Header: /usr/enee/chris/bin/src/chase/RCS/chase.c,v 1.2 84/01/23 04:05:55 chris Rel $ * * $Log: chase.c,v $ * Revision 1.2 84/01/23 04:05:55 chris * "fields"->"fences" * * Revision 1.1 84/01/23 03:58:01 chris * Initial revision * */ #include "chase.h" #include <ctype.h> main (argc, argv) register int argc; register char **argv; { static char askr[] = "How many robots? "; static char askef[] = "How many electric fences? "; int i; printf (" Welcome to Chase\n"); while (argc > 1 && argv[1][0] == '-') { register char *s; argc--; argv++; s = &argv[0][1]; while (*s) switch (*s++) { case 'r': WantRogueStyle++; break; default: fprintf (stderr, "Usage: chase [ -r ] [ robots [ fences ]]\n"); exit (1); } } if (argc > 1) GetNum (argv[1], &Robots, 1, MaxRobots, askr); else GetNum ((char *) 0, &Robots, 1, MaxRobots, askr); if (argc > 2) GetNum (argv[2], &EFences, 0, MaxEFences, askef); else GetNum ((char *) 0, &EFences, 0, MaxEFences, askef); GetKey = WantRogueStyle ? RogueGetKey : OldGetKey; InitWin (); srand (time ((long *) 0) + getpid ()); i = randint ((Robots + 1) >> 1) + (Robots >> 2); Gen (i, HRobotChar); Gen (Robots - i, RobotChar); Gen (EFences, EFenceChar); PlaceSelf (); DoChase (); } X/* Get a number between min and max, from optional string str or keyboard */ GetNum (str, num, min, max, prompt) char *str; register int *num; int min, max; char *prompt; { static char *remember; static char buf[100]; for (;;) { if (str) { *num = atoi (str); str = 0; remember = 0; } else { register char *s; if (remember) s = remember; else { printf (prompt); fgets (buf, sizeof buf, stdin); s = buf; } while (isspace (*s)) s++; *num = 0; while (isdigit (*s)) *num = *num * 10 + *s++ - '0'; while (isspace (*s)) s++; remember = *s ? s : 0; } if (*num >= min && *num <= max) return; printf ("Must be at least %d and at most %d\n", min, max); } } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 chase.c /bin/echo -n ' '; /bin/ls -ld chase.c fi /bin/echo 'Extracting chase.h' sed 's/^X//' <<'//go.sysin dd *' >chase.h X/* * $Header: /usr/enee/chris/bin/src/chase/RCS/chase.h,v 1.1 84/01/23 03:58:11 chris Rel $ * chase -- robot hunt game * * $Log: chase.h,v $ * Revision 1.1 84/01/23 03:58:11 chris * Initial revision * */ #include <stdio.h> #include <local/window.h> #define FieldRows 24 #define FieldCols 60 #define UsageRows 22 #define UsageCols 18 #define MinRows (FieldRows > UsageRows ? FieldRows : UsageRows) #define MinCols (FieldCols + UsageCols + 1) #define FieldX (FieldCols-2) #define FieldY (FieldRows-2) #define MaxRobots 500 #define MaxEFences 300 #define MaxTries 4000 /* for placement; see Gen, PlaceSelf */ #define SelfChar 'I' /* You */ #define RobotChar '=' /* A robot */ #define HRobotChar '#' /* Another type of robot */ #define EFenceChar 'X' /* An electric fence */ #define JunkChar '@' /* A junk heap */ Win *Field; /* The playing field */ Win *Usage; /* Description of keys */ int Rows; /* Real screen rows */ int Cols; /* Real screen cols */ int Robots; /* Number of robots on screen */ int EFences; /* Number of E-Fences */ Pos SelfAt; /* Self's position in field */ int (*GetKey) (); /* Keyboard reader */ int OldGetKey (); /* Old-chase-style */ int RogueGetKey (); /* Rogue style */ int WantRogueStyle; /* True => want rogue-style keys */ #define KUL 0 /* Key: Up & Left */ #define KU 1 /* Key: Up */ #define KUR 2 /* Key: Up & Right */ #define KL 3 /* Key: Left */ #define KDOT 4 /* Key: stay */ #define KR 5 /* Key: Right */ #define KDL 6 /* Key: Down & Left */ #define KD 7 /* Key: Down */ #define KDR 8 /* Key: Down & Right */ #define KTele 9 /* Key: Teleport */ #define KStand 10 /* Key: Stand */ #define KQuit 11 /* Key: Quit */ #define KRedraw 12 /* Key: Redraw */ #define KBad 13 /* Key: Bad (others) */ //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 chase.h /bin/echo -n ' '; /bin/ls -ld chase.h fi /bin/echo 'Extracting dochase.c' sed 's/^X//' <<'//go.sysin dd *' >dochase.c X/* * $Header: /usr/enee/chris/bin/src/chase/RCS/dochase.c,v 1.1 84/01/23 03:58:18 chris Rel $ * * $Log: dochase.c,v $ * Revision 1.1 84/01/23 03:58:18 chris * Initial revision * */ #include "chase.h" DoChase () { register int c; WSetRealCursor++; for (;;) { WRCurRow = SelfAt.row + 1; WRCurCol = SelfAt.col + 1; Wrefresh (0); if (Robots == 0) End ("You have defeated all the robots!"); switch ((*GetKey) ()) { case EOF: case KQuit: Wexit (0); case KUL: Move (-1, -1); break; case KU: Move (-1, 0); break; case KUR: Move (-1, 1); break; case KL: Move (0, -1); break; case KDOT: Move (0, 0); break; case KR: Move (0, 1); break; case KDL: Move (1, -1); break; case KD: Move (1, 0); break; case KDR: Move (1, 1); break; case KTele: Teleport (); break; case KStand: for (;;) { Move (0, 0); Wrefresh (0); if (Robots == 0) break; } break; case KRedraw: ScreenGarbaged++; break; default: Ding (); break; } } } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 dochase.c /bin/echo -n ' '; /bin/ls -ld dochase.c fi /bin/echo 'Extracting gen.c' sed 's/^X//' <<'//go.sysin dd *' >gen.c X/* * $Header: /usr/enee/chris/bin/src/chase/RCS/gen.c,v 1.1 84/01/23 03:58:32 chris Rel $ * * $Log: gen.c,v $ * Revision 1.1 84/01/23 03:58:32 chris * Initial revision * */ #include "chase.h" X/* Generate positions for n things displayed with c */ Gen (n, c) register int n; int c; { register int row, col, tries; while (--n >= 0) { for (tries = MaxTries; --tries >= 0;) { row = randint (FieldY); col = randint (FieldX); WAcursor (Field, row, col); if (Wread (Field, 1, 0) != ' ') continue; WAcursor (Field, row, col); Wputc (c, Field); goto ok; } Wcleanup (); printf ("Unable to place %c after %d tries!\n", c, MaxTries); exit (1); ok:; } } PlaceSelf () { register int tries; for (tries = MaxTries; --tries >= 0;) { SelfAt.row = randint (FieldY - 2) + 1; SelfAt.col = randint (FieldX - 2) + 1; WAcursor (Field, SelfAt.row - 1, SelfAt.col - 1); if (Wread (Field, 1, 0) != ' ' || Wread (Field, 1, 0) != ' ' || Wread (Field, 1, 0) != ' ') continue; WAcursor (Field, SelfAt.row, SelfAt.col - 1); if (Wread (Field, 1, 0) != ' ' || Wread (Field, 1, 0) != ' ' || Wread (Field, 1, 0) != ' ') continue; WAcursor (Field, SelfAt.row + 1, SelfAt.col - 1); if (Wread (Field, 1, 0) != ' ' || Wread (Field, 1, 0) != ' ' || Wread (Field, 1, 0) != ' ') continue; WAcursor (Field, SelfAt.row, SelfAt.col); Wputc (SelfChar, Field); WAcursor (Field, SelfAt.row, SelfAt.col); return; } Wcleanup (); printf ("Unable to place self after %d tries!\n", MaxTries); exit (1); } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 gen.c /bin/echo -n ' '; /bin/ls -ld gen.c fi /bin/echo 'Extracting initwin.c' sed 's/^X//' <<'//go.sysin dd *' >initwin.c X/* * $Header: /usr/enee/chris/bin/src/chase/RCS/initwin.c,v 1.1 84/01/23 03:58:54 chris Rel $ * * $Log: initwin.c,v $ * Revision 1.1 84/01/23 03:58:54 chris * Initial revision * */ #include "chase.h" X/* Initialize the window (display) system */ InitWin () { if (Winit (0, 0)) { printf ("Sorry, but this program won't run on your terminal.\n"); exit (1); } Wscreensize (&Rows, &Cols); if (Rows < MinRows || Cols < MinCols) { Wcleanup (); printf ("Sorry, your screen is too small.\n"); exit (1); } Field = Wopen (0, 0, 0, FieldCols, FieldRows, 0, 0); Usage = Wopen (1, FieldCols+1, 0, UsageCols, UsageRows, 0, 0); if (Field == 0 || Usage == 0) { Wcleanup (); printf ("Something is wrong! I can't open the window.\n"); exit (1); } Woncursor (Field, 0); Wframe (Field); Wwrap (Field, 0); Woncursor (Usage, 0); Wnewline (Usage, 1); Wwrap (Usage, 0); Wputs (" Move Control:\n", Usage); if (WantRogueStyle) { Wputs (" y k u\n", Usage); Wputs (" \\|/\n", Usage); Wputs (" h-.-l\n", Usage); Wputs (" /|\\\n", Usage); Wputs (" b j n\n", Usage); } else { Wputs (" y u i\n", Usage); Wputs (" \\|/\n", Usage); Wputs (" h-j-k\n", Usage); Wputs (" /|\\\n", Usage); Wputs (" n m ,\n", Usage); } Wputs ("\n\n\tCommands:\n", Usage); Wputs (" t - Teleport\n", Usage); Wputs (" s - Last stand\n", Usage); Wputs (" q - Quit\n", Usage); Wputs (" ^L - Redraw\n", Usage); Wputs ("\n\n\tKey\n", Usage); Wputs (" I - You\n = - Robot\n # - Robot\n X - Electric\n Fence\n @ - Junk heap", Usage); } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 initwin.c /bin/echo -n ' '; /bin/ls -ld initwin.c fi /bin/echo 'Extracting key.c' sed 's/^X//' <<'//go.sysin dd *' >key.c X/* * $Header: /usr/enee/chris/bin/src/chase/RCS/key.c,v 1.1 84/01/23 03:59:06 chris Rel $ * * $Log: key.c,v $ * Revision 1.1 84/01/23 03:59:06 chris * Initial revision * */ #include "chase.h" X/* Get a key type, old chase style keyboard */ OldGetKey () { switch (getchar ()) { case EOF: return EOF; case 'y': case 'Y': return KUL; case 'u': case 'U': return KU; case 'i': case 'I': return KUR; case 'h': case 'H': return KL; case 'j': case 'J': return KDOT; case 'k': case 'K': return KR; case 'n': case 'N': return KDL; case 'm': case 'M': return KD; case ',': return KDR; case 't': case 'T': return KTele; case 's': case 'S': return KStand; case 'q': case 'Q': return KQuit; case 014: return KRedraw; default: return KBad; } } X/* Get a key type, new Rogue style */ RogueGetKey () { switch (getchar ()) { case EOF: return EOF; case 'y': case 'Y': return KUL; case 'k': case 'K': return KU; case 'u': case 'U': return KUR; case 'h': case 'H': return KL; case '.': return KDOT; case 'l': case 'L': return KR; case 'b': case 'B': return KDL; case 'j': case 'J': return KD; case 'n': case 'N': return KDR; case 't': case 'T': return KTele; case 's': case 'S': return KStand; case 'q': case 'Q': return KQuit; case 014: return KRedraw; default: return KBad; } } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 key.c /bin/echo -n ' '; /bin/ls -ld key.c fi /bin/echo 'Extracting move.c' sed 's/^X//' <<'//go.sysin dd *' >move.c X/* * $Header: /usr/enee/chris/bin/src/chase/RCS/move.c,v 1.2 84/01/23 04:06:21 chris Rel $ * * $Log: move.c,v $ * Revision 1.2 84/01/23 04:06:21 chris * "field"->"fence" * * Revision 1.1 84/01/23 03:59:13 chris * Initial revision * */ #include "chase.h" X/* Bad character found in field window */ BadChar (c, y, x) int c, y, x; { Wcleanup (); printf ("Odd character '%c' found at (%d, %d)\n", c, y, x); exit (1); } X/* Sign function */ #define sgn(x) ((x) < 0 ? -1 : (x) ? 1 : 0) X/* Absolute value */ #define abs(x) ((x) < 0 ? -(x) : (x)) X/* Move self, then robots */ Move (DelY, DelX) int DelY, DelX; { register int c, y, x; register char *np; int DoMunch; static char new[FieldY * FieldX]; static int beenhere; static int minX, minY, maxX, maxY; /* Field-robot-scan optimization */ /* Move self */ if (DelY || DelX) { if (SelfAt.row + DelY < 0 || SelfAt.row + DelY >= FieldY || SelfAt.col + DelX < 0 || SelfAt.col + DelX >= FieldX) { Ding (); return; } WAcursor (Field, SelfAt.row, SelfAt.col); Wputc (' ', Field); /* Blank out self */ WAcursor (Field, SelfAt.row += DelY, SelfAt.col += DelX); switch (c = Wread (Field, 1, 0)) { case HRobotChar: case RobotChar: Munch (); case EFenceChar: End ("You ran into an electric fence and are now dead."); case JunkChar: ForcedStand (); return; case ' ': break; default: BadChar (c, SelfAt.row, SelfAt.col); } WAcursor (Field, SelfAt.row, SelfAt.col); Wputc (SelfChar, Field); WAcursor (Field, SelfAt.row, SelfAt.col); } WRCurRow = SelfAt.row + 1; WRCurCol = SelfAt.col + 1; Wrefresh (0); /* Move robots */ if (beenhere) { for (np = new + sizeof new; np > new;) *--np = 0; } else maxX = FieldX - 1, maxY = FieldY - 1, beenhere++; for (y = minY; y <= maxY; y++) { WAcursor (Field, y, minX); for (x = minX; x <= maxX; x++) { register int ny, nx; int nc; c = Wread (Field, 1, 0); if (c != RobotChar && c != HRobotChar) continue; WAcursor (Field, y, x); Wputc (' ', Field); if (c == RobotChar) { ny = y + (y < SelfAt.row ? 1 : y == SelfAt.row ? 0 : -1); nx = x + (x < SelfAt.col ? 1 : x == SelfAt.col ? 0 : -1); } else { ny = SelfAt.row - y; nx = SelfAt.col - x; if (abs (ny) < abs (nx)) nx = x + sgn (nx), ny = y; else if (abs (nx) < abs (ny)) nx = x, ny = y + sgn (ny); else nx = x + sgn (nx), ny = y + sgn (ny); } WAcursor (Field, ny, nx); if ((nc = Wread (Field, 1, 0)) == EFenceChar || nc == JunkChar) Robots--; /* Destroy one robot */ else { np = &new[ny * FieldX + nx]; if (*np == 0) *np = c; else if (*np != JunkChar) *np = JunkChar, Robots -= 2; else Robots--; } WAcursor (Field, y, x + 1); } } np = new; DoMunch = 0; minX = FieldX; minY = FieldY; maxX = 0; maxY = 0; for (y = 0; y < FieldY; y++) { for (x = 0; x < FieldX; x++) { if ((c = *np++) == 0) continue; if (y < minY) minY = y; if (y > maxY) maxY = y; if (x < minX) minX = x; if (x > maxX) maxX = x; if (y == SelfAt.row && x == SelfAt.col) DoMunch++; WAcursor (Field, y, x); Wputc (c, Field); } } if (DoMunch) Munch (); } X/* End of game routine */ End (message) char *message; { Wrefresh (1); Wcleanup (); printf ("%s\n", message); exit (0); } X/* Get munched by a robot */ Munch () { Wauxcursor (Field, SelfAt.row + 1, SelfAt.col + 1); Waputs ("*MUNCH*", WINVERSE, Field); End ("You have been captured and eaten by robots."); } X/* Teleport */ Teleport () { register int y, x, tries; if (randint ((Robots / 10) + 10) == 0) Munch (); WAcursor (Field, SelfAt.row, SelfAt.col); Wputc (' ', Field); for (tries = MaxTries; --tries >= 0;) { y = randint (FieldY); x = randint (FieldX); WAcursor (Field, y, x); if (Wread (Field, 1, 0) != ' ') continue; WAcursor (Field, SelfAt.row = y, SelfAt.col = x); Wputc (SelfChar, Field); Move (0, 0); return; } Wcleanup (); printf ("Unable to place self after %d tries!\n", MaxTries); exit (1); } ForcedStand () { Win *msg; WAcursor (Field, SelfAt.row, SelfAt.col); Wputc (SelfChar, Field); WAcursor (Field, SelfAt.row, SelfAt.col); WRCurRow = SelfAt.row + 1; WRCurCol = SelfAt.col + 1; msg = Wopen (0, 1, FieldRows - 1, FieldCols - 2, 1, 0, 0); if (msg) { Woncursor (msg, 0); Wsetmode (msg, WBLINK); Wputs ("You are trapped in a junk pile!", msg); } Wrefresh (0); for (;;) { Move (0, 0); Wrefresh (0); if (Robots == 0) return; } } //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 move.c /bin/echo -n ' '; /bin/ls -ld move.c fi /bin/echo 'Extracting rand.s' sed 's/^X//' <<'//go.sysin dd *' >rand.s .data .align 2 _randx: .long 1 .text .align 2 .globl _srand # set the random seed _srand: # srand(seed) int seed; .word 0 movl _randx,r0 movl 4(ap),_randx ret .align 2 .globl _rand # give a 31 bit random positive integer _rand: # int rand() .word 0 jsb rand bicl2 $1,r0 rotl $-1,r0,r0 ret .align 2 .globl _randint # return a number in [0..n-1] _randint: # int randint(n) int n; .word 0 jsb rand emul 4(ap),r0,$0,r2 tstl r0 jgeq L1 addl3 4(ap),r3,r0 ret L1: movl r3,r0 ret .align 2 # compute the next 32 bit random number rand: mull3 $505360173,_randx,r0 addl2 $907633385,r0 movl r0,_randx rsb .align 2 .globl _flat # give a random double in [0.,1.) _flat: # double flat() .word 0 jsb rand movl r0,r2 movf $0f1.0,r0 extzv $25,$7,r2,r3 insv r3,$0,$7,r0 extzv $9,$16,r2,r3 insv r3,$16,$16,r0 extzv $0,$9,r2,r1 subd2 $0d1.0,r0 ret //go.sysin dd * made=TRUE if [ $made = TRUE ]; then /bin/chmod 644 rand.s /bin/echo -n ' '; /bin/ls -ld rand.s fi -- Chris Torek, Dept of CS, Univeristy of Maryland, College Park, MD ...!umcp-cs!chris chris%umcp-cs@CSNet-Relay