home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
vol_100
/
186_01
/
robots21.c
< prev
next >
Wrap
Text File
|
1985-08-21
|
17KB
|
651 lines
char copyright[] =
"\n\t Version 2.1, Copyright 1984, 1985 by Tom P Jansing \n\n";
/*
ROBOTS.C - A game for any CRT terminal with an addressable
cursor, with or without graphic features. Written for the
Small-C Version 2B compiler under CP/M-80.
The game is set up for a 24x80 screen, although this can be
changed by adjusting the ROWS and COLS values below, and perhaps
reformatting some of the extra displays. The only other
terminal-dependent function is cursor addressing, handled
by the locate(x,y) function defined just below. This is currently
set up for the popular (ESC) (=) (Y+32) (X+32) scheme found on
Kaypro and Osborne computers, among others.
Just like arcade games, this one gets harder as you get better
at it. If you have trouble however, it gets easier instead.
Gameplay:
You ($) are in the middle of a galaxy populated with stars (*) and
killer robots (%). You can move around, but every time you do, the
robots take a step toward you. To survive, you must lure them into
stars or each other, thereby destroying them. If you outlive the
last robot, you will be "rewarded" with a harder game. For a real
challenge, try winning five rounds in a row with 99 opponents!
History:
Thσ ideß i≤ takeε froφ ß popula≥ versioε a⌠ thσ ╒niversity of Alberta,
which in turn was reportedly inspired by a Basic version in an early
issue of Byte. To my knowledge, this is the first implementation
in C.
By: Tom Jansing April 16, 1985
86 Mission Avenue
St. Albert, Alberta or c/o any Edmonton-area RCP/M.
Canada T8N 1H8
This program may be freely distributed for non-commercial use.
Version 2.1 improvements:
Tightened up the code considerably (COM file is now 10k, not 14)
by using alloc(), substituting a "tiny" printf() for the real one,
and compacting the block letters. Made the relative density of
stars, and therefore the hardness of the game, a patchable variable.
Added a flashier title page, and generally polished up the code.
Aside from being a bit harder (see above), gameplay is basically
unchanged from version 1.
*/
#include stdio.h
#define NOCCARGC /* Tell compiler to skip arg count info */
#define STAR '*' /* On screen and in screen array */
#define SPACE ' ' /* Ditto */
#define HERO '$' /* Shown on screen only */
#define ROBOT '%' /* Ditto */
#define HEADSTRT 6 /* Minimum distance to robots at start */
#define TAKEN -1 /* Used in creating robots */
#define ESC 27 /* Panic button */
#define ROWS 23 /* # of lines on screen - 1 (exactly!) */
#define COLS 78 /* # of columns on screen - 2 */
#define MAXROBS 99 /* Max. # of robots (never >126 of em!) */
#define LENINT 2 /* Length of int variables, in bytes */
char *screen, /* The playing field */
z, /* Temporary variable */
key; /* Player's move */
int stardens = 50; /* Initial star density (relative) */
/* 75=easy; 50=so-so; 25=downright hard */
char junk1[] = "<-RelStarDensity"; /* For patching */
int *rx, /* Robot X positions */
*ry, /* " Y " */
*ralive, /* Dead/alive flags for robots */
hx, hy, /* Hero's X and Y positions */
dx, dy, /* Offset from a robot to 'hero' */
rxx, ryy, /* X and Y of robot under consideration */
dead, /* Status of our hero */
i, j, jj, zx, zy, /* Temporary variables */
robots, /* Initial # of robots this round */
rleft, /* Remaining robots this round */
oldrleft, /* " " last move */
fillfac, /* Screen is (fillfac/32767) full of *s */
fillfac2; /* fillfac = fillfac2(varies) * robots */
/*
** Locate cursor at given position on screen: x=across, y=down.
** ie, locate(0,0); for upper left corner, or
** locate(40,12); for center of a 24x80 screen.
*/
locate(x,y) int x,y; {
putchar(27); /* For Kaypro, Osborne, etc. */
putchar('=');
putchar(y+32);
putchar(x+32);
}
/*
** Keyboard input functions (system-specific):
** kbhit(): Return true if a key has been pressed but not read.
** getkey(): Return, without echoing, a key which presumably has
** already been pressed.
** flushkbd(): Remove any 'typed-ahead' keys already waiting.
*/
char ioret; /* Last key pressed */
kbhit() {
return ((ioret=cpm(6,-1)));
}
getkey() {
return (ioret);
}
flushkbd() {
while (kbhit())
getkey();
}
main() {
screen = alloc(ROWS*COLS);
rx = alloc(MAXROBS*LENINT); /* Get some memory */
ry = alloc(MAXROBS*LENINT);
ralive = alloc(MAXROBS*LENINT);
if (screen==0 || rx==0 || ry==0 || ralive==0) {
printf("Outta memory - TILT!");
exit();
}
hello(); /* Display instructions */
flushkbd(); /* Clear any pending keypresses */
srand(0); /* Seed random generator when key hit */
cls(); /* Clear screen */
/*
** Some initial settings.
*/
fillfac2 = stardens;
dead = NO;
robots = 1; /* Becomes 10 in a moment */
/*
** Main loop.
*/
for (;;) { /* for (ever) */
/*
** Adjust # of stars and robots up or down, depending on success
** or failure of last round. If he keeps losing with 10 robots,
** increase star density (which makes it easier). Or decrease
** the # of stars if he keeps winning with 99 robots. This way,
** nobody can win many rounds in a row against 99.
*/
if (dead)
robots = (robots*7) / 10;
else
robots = (robots*10) / 7;
if (robots < 10) { /* No <10 robots, no >250 star density */
robots = 10;
fillfac2 = (fillfac2+=20)>250 ? 250:fillfac2;
}
if (robots > 99) { /* No >99 robots, no <10 star density */
robots = 99;
fillfac2 = (fillfac2-=20)<10 ? 10:fillfac2;
}
fillfac = fillfac2 * robots; /* Affects # of stars on field */
rleft = robots; /* All of them are left right now */
oldrleft = 0; /* Force initial display of count */
/*
** Fill the screen with randomly placed stars. (APL, eat your heart out!)
*/
for (i=0; i<ROWS*COLS; screen[i++] = rand()<fillfac ? STAR:SPACE)
;
/*
** Start 'hero' at screen center, but not on top of a star.
*/
hx = COLS / 2;
hy = ROWS / 2;
while (screen[subs(hx,hy)]==STAR)
++hx;
/*
** Display screen, scrolling from the bottom for effect.
*/
locate(0,ROWS);
putchar('\n');
for (zy=0; zy<ROWS; ++zy) {
for (zx=0; zx<COLS; putchar(screen[subs(zx++,zy)])) /* One line */
;
putchar('\n');
}
/*
** Create some robots and display them on the screen.
*/
for (j=0; j<robots; ) { /* Note: missing loop increment */
ralive[j] = YES;
rxx = rand() % COLS;
ryy = rand() % ROWS;
if (screen[subs(rxx,ryy)]==SPACE && abs(rxx-hx)+abs(ryy-hy)>HEADSTRT) {
show(rxx,ryy,ROBOT); /* (Above): Not too close to hero */
screen[subs(rxx,ryy)]=TAKEN;
rx[j] = rxx;
ry[j] = ryy;
++j; /* Only if good spot found */
}
}
clrflags(); /* Erase 'taken' flags in screen[] */
showcnt();
showhero();
dead = NO;
flushkbd();
/*
** Repeat the following moves until there are no more robots (or hero).
*/
while (rleft) {
/*
** If a key was hit, process the move. If not, just wait. Note:
** 'else' code could be added to provide time-dependent action.
*/
if (kbhit()) { /* Otherwise, keep looping & waiting*/
key=toupper(getkey()); /* Key hit, so get it. */
/*
** Move our hero as appropriate.
*/
switch (key) {
case ESC: alldone(); /* Get out fast */
case 'S':
case '4': if (spotok(hx-1,hy)) /* Off screen? */
show(hx--,hy,SPACE); /* Nope; move */
else /* No good; ignore */
continue;
break;
case 'F':
case '6': if (spotok(hx+1,hy))
show(hx++,hy,SPACE);
else
continue;
break;
case 'C':
case '2': if (spotok(hx,hy+1))
show(hx,hy++,SPACE);
else
continue;
break;
case 'E':
case '8': if (spotok(hx,hy-1))
show(hx,hy--,SPACE);
else
continue;
break;
case 'D':
case '5': break; /* Stay-put key */
case 'X':
case '1': if (spotok(hx-1,hy+1))
show(hx--,hy++,SPACE);
else
continue;
break;
case 'V':
case '3': if (spotok(hx+1,hy+1))
show(hx++,hy++,SPACE);
else
continue;
break;
case 'W':
case '7': if (spotok(hx-1,hy-1))
show(hx--,hy--,SPACE);
else
continue;
break;
case 'R':
case '9': if (spotok(hx+1,hy-1))
show(hx++,hy--,SPACE);
else
continue;
break;
default: continue; /* Wait for another key */
}
/*
** OK, he moved. Show him in the new spot, and see if he jumped
** right into a robot or star.
*/
showhero();
checkhit();
if (dead)
continue;
/*
** Move the robots. Note: They appear to move simultaneously,
** though they're actually moved one at a time. Because of this,
** allow robots to temporarily 'bump' each other while in transit.
*/
for (j=0; j<robots; ++j) {
if (ralive[j]) {
dx = (rxx=rx[j]) - hx;
dy = (ryy=ry[j]) - hy;
z = screen[subs(rxx,ryy)]; /* This spot just taken? */
if (z<0) {
if (ralive[-1-z])
z = ROBOT;
else
z = SPACE; /* The usual case */
}
if (abs(dx)>abs(dy)) { /* Move vertical or horiz? */
if (dx>0)
show(rxx--,ryy,z); /* Vacate spot; show */
else /* what we stepped on */
show(rxx++,ryy,z);
}
else {
if (dy>0)
show(rxx,ryy--,z);
else
show(rxx,ryy++,z);
}
/*
** Check to see if robot landed on another robot or a star.
*/
z = screen[subs(rxx,ryy)]; /* See what's there now */
screen[subs(rxx,ryy)]= -1-j;/* Mark spot as occupied */
if (z!=SPACE) { /* If star or robot */
--rleft; /* Another 1 bites the dust */
show(rxx,ryy,SPACE); /* Show on screen */
ralive[j] = NO; /* and robot array */
}
else
show(rxx,ryy,ROBOT);
/*
** If this robot landed on another one, kill that one too.
*/
if (z<0) {
z = -1-z; /* Convert to robot index */
if (ralive[z]) { /* Kill the innocent party */
--rleft; /* if it was still alive */
ralive[z] = NO;
}
}
rx[j] = rxx; /* Put THIS robot back into */
ry[j] = ryy; /* the array */
}
} /* Next robot */
/*
** Robots all moved. Show the updated count and our hero again
** (to reset cursor, mainly). More importantly, check for game over.
*/
showcnt();
showhero();
checkhit(); /* Check for collision */
clrflags(); /* Clean up robot droppings */
flushkbd();
} /* (if kbhit) */
} /* (while robots remain) */
/*
** End of round.
*/
if (dead)
youlose();
else
youwin();
} /* End of main loop */
} /* End of main() */
/*
** Return a single subscript from X and Y screen positions. Note: Any
** decent compiler that handles multiple-dimension arrays should
** produce code almost as efficient with this method anyway.
*/
subs(x,y) int x,y; {
return (y*COLS+x);
}
/*
** Display hero on screen in current spot, with cursor on top for visibility.
*/
showhero() {
locate(hx,hy);
putchar(HERO);
putchar('\b');
}
/*
** Show number of robots remaining, but only if number has changed.
*/
showcnt() {
if (rleft != oldrleft) {
locate(0,ROWS);
printf("There are ");
putint(rleft);
printf(" out of ");
putint(robots);
printf(" robots left. ");
oldrleft = rleft;
locate(COLS-12,ROWS);
printf("(ESC=quit)");
}
}
/*
** Display a character on the screen in a specified spot.
*/
show(x,y,c) int x,y; char c; {
locate(x,y);
putchar(c);
}
/*
** Is our hero trying to jump off the screen?
*/
spotok(x,y) int x,y; {
if (x<0 || x>=COLS || y<0 || y>=ROWS)
return (NO);
else
return (YES);
}
/*
** Clear the robots' place markers in the screen array (not to be
** confused with the screen itself).
*/
clrflags() {
for (jj=0; jj<robots; jj++)
screen[subs(rx[jj],ry[jj])] = SPACE;
}
/*
** See if our hero hit a star or robot.
*/
checkhit() {
if (screen[subs(hx,hy)]!=SPACE) { /* Hit star or >1 robots? */
dead = YES; /* That's all, folks! */
rleft = 0; /* Force end of round */
}
else {
for (jj=0; jj<robots; jj++) {
if (ralive[jj] && hx==rx[jj] && hy==ry[jj]) { /* Direct hit? */
dead = YES;
rleft = 0;
}
}
}
}
/*
** Clean up and exit program.
*/
alldone() {
cls();
exit();
}
/*
** Clear screen by scrolling (needs no customizing this way).
*/
cls() {
int n;
for (n=0; n<ROWS+ROWS+2; ++n)
putchar('\n');
}
/*
** Instructions. Split in 2 parts to avoid "literal queue overflow" with CV2.
*/
hello() {
cls();
locate(0,0);
xpand("8 8%5 6%3 8%5 6%3 8%3 7%");
xpand("1\t1 2%4 2%3 2%4 2%3 2%4 2%3 2%4 2%2 2%1 2%1 2%2 2%5 2%");
xpand("1\t1 2%4 2%3 2%4 2%3 2%4 2%3 2%4 2%5 2%5 2%");
xpand("1\t1 7%4 2%4 2%3 7%4 2%4 2%5 2%6 7%");
xpand("1\t1 2%2 2%5 2%4 2%3 2%4 2%3 2%4 2%5 2%9 3 2%");
xpand("1\t1 2%3 2%4 2%4 2%3 2%4 2%3 2%4 2%5 2%5 2%5 2%");
xpand("1\t3%4 3%3 6%3 8%5 6%6 2%6 7%");
printf(copyright); /* Honor system */
hello2();
}
hello2() {
printf("\t\t$ <--\tThis is you. At all costs, avoid the\n");
printf("\t\t% <--\tRobots, which close in on you. Also,\n"); /* K&R: %% */
printf("\t\t* <--\tStars kill anything that touches them.\n\n");
printf("Use the keys shown on the right to move around: -->\t");
printf("W E R\t\t7 8 9\n");
printf("For example, press R or 9 to move to the upper\t\t");
printf("S D F\t or \t4 5 6\n");
printf("right, or D or 5 to stay put.\t\t\t\tX C V\t\t1 2 3\n\n");
printf("You will have between 10 and 99 robots to deal with. ");
printf("Every time you make a\nmove, they close in on you. ");
printf("Try to lure them into stars or each other to\ndestroy them. ");
printf("If you manage to outlive the robots, you will be \"rewarded\"\n");
printf("with a harder game. Beware - this game never ends... ");
printf("never ends... never en\n\n\t\t\t\tInsert Coin: ");
}
you() {
int repeat;
locate(0,ROWS);
putchar('\n');
blanklines(10);
xpand("2\t5 3Y3 3Y4 5O5 3U3 3U6 ");
xpand("2\t6 3Y1 3Y4 7O4 3U3 3U6 ");
xpand("2\t7 5Y4 3O3 3O3 3U3 3U6 ");
for (repeat=0; repeat<3; ++repeat)
xpand("2\t8 3Y5 3O3 3O3 3U3 3U6 ");
xpand("2\t8 3Y6 7O5 7U7 ");
xpand("2\t8 3Y7 5O7 5U8 ");
blanklines(4);
}
youlose() {
you();
xpand("2\t3L9 1 5O6 8S3 9E");
xpand("2\t3L9 7O4 8S4 9E");
xpand("2\t3L8 3O3 3O3 2S9 1 3E6 ");
xpand("2\t3L8 3O3 3O4 7S4 9E");
xpand("2\t3L8 3O3 3O5 7S3 9E");
xpand("2\t3L8 3O3 3O9 1 2S3 3E6 ");
xpand("2\t9L3 7O5 8S3 9E");
xpand("2\t9L4 5O5 8S4 9E");
blanklines(1);
}
youwin() {
you();
xpand("2\t5 2W5 2W3 9I3 3N4 2N6 ");
xpand("2\t5 2W5 2W3 9I3 4N3 2N6 ");
xpand("2\t5 2W5 2W6 3I6 4N3 2N6 ");
xpand("2\t5 2W5 2W6 3I6 2N1 3N1 2N6 ");
xpand("2\t5 2W2 1W2 2W6 3I6 2N1 3N1 2N6 ");
xpand("2\t5 2W1 3W1 2W6 3I6 2N3 4N6 ");
xpand("2\t6 7W4 9I3 2N3 4N6 ");
xpand("2\t7 5W5 9I3 2N4 3N6 ");
blanklines(1);
}
/*
** Print n blank lines at same speed as the block letters.
*/
blanklines(n) int n; {
while(n--)
xpand("2\t9 9 9 9 8 ");
}
/*
** Expand cryptic strings into block letters.
*/
xpand(code) char *code; {
char xbyte, xletter;
while ((xbyte=*code++)) { /* Until end of string */
xbyte -= '0'; /* Convert to binary */
xletter = *code++; /* Get following char */
while (xbyte--)
putchar(xletter);
}
putchar('\n'); /* Newline when done */
}
/*
** Poor man's printf() - takes up less room than the standard version.
** Also, a nifty little routine (courtesy K&R) to print an int value in lieu.
*/
printf(wisdom) char *wisdom; {
while (*wisdom)
putchar(*wisdom++);
}
putint(n) int n; {
int i;
if ((i=n/10)!=0)
putint(i);
putchar(n%10+'0');
}
#include RANDOM.C
#include ABS.C
#include ALLOCMEM.C
#asm
LINK CV2LIB ; LASM opcode - CV2LIB & CV2CALL precompiled.
#endasm