|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T r
Length: 20861 (0x517d)
Types: TextFile
Names: »robomatic.c«
└─⟦b20c6495f⟧ Bits:30007238 EUUGD18: Wien-båndet, efterår 1987
└─⟦this⟧ »EUUGD18/General/Robomatic/robomatic.c«
/* robomatic.c: Rob-O-Matic I (CMU) Mon Oct 14 11:51:01 EDT 1985 - mlm */
/*****************************************************************
*
* Rob-O-Matic: Play the robots game
* Copyright (C) 1985 by Mauldin
* The right is granted to any person, university, or company
* to copy, modify, or distribute (for free) this file, providing
* that this notice is not removed.
*
* HISTORY
* 11-Oct-85 Michael Mauldin (mlm) at Carnegie-Mellon University
* Created.
*
*****************************************************************/
# include <curses.h>
# define NEWROBOT "/usr/mlm/bin/robots"
# define ROBOT "/usr/games/newrobots"
# define READ 0
# define WRITE 1
# define abs(A) ((A)>0?(A):-(A))
# define max(A,B) ((A)>(B)?(A):(B))
# define sgn(A) ((A)==0?0:((A)>0?1:-1))
/* Define the Rob-O-Matic pseudo-terminal */
# define ROBOTTERM "rb|rterm:am:bs:ce=^E:cl=^L:cm=^F%+ %+ :co#80:li#24:pt:ta=^I:up=^A:do=^B:nd=^C:db:xn:"
# define ctrl(C) ((C)&037)
# define BL ctrl('G')
# define BS ctrl('H')
# define CE ctrl('E')
# define CL ctrl('L')
# define CM ctrl('F')
# define CR ctrl('M')
# define DO ctrl('B')
# define LF ctrl('J')
# define ND ctrl('C')
# define TA ctrl('I')
# define UP ctrl('A')
int child;
int frobot, trobot;
int debug=0;
FILE *trace=NULL;
/****************************************************************
* Main routine
****************************************************************/
main (argc, argv)
int argc;
char *argv[];
{ int ptc[2], ctp[2];
char *rfile=NULL;
/* Get the options from the command line */
while (--argc > 0 && (*++argv)[0] == '-')
{ while (*++(*argv))
{ switch (**argv)
{ case 'd': debug++; break;
default: printf ("Usage: robomatic [-d]\n"); exit (1);
}
}
}
/* Open tracing file (if needed) */
if (debug)
{ if ((trace = fopen ("trace.log", "w")) == NULL)
{ perror ("trace.log");
exit (1);
}
}
/* Find an executable of the robots game */
if (access ("newrobots", 1) == 0) rfile = "newrobots";
else if (access (NEWROBOT, 1) == 0) rfile = NEWROBOT;
else if (access (ROBOT, 1) == 0) rfile = ROBOT;
else
{ perror ("newrobots");
exit (1);
}
/* Get two pipes to attach to the Robots process */
if ((pipe (ptc) < 0) || (pipe (ctp) < 0))
{ fprintf (stderr, "Cannot get pipes!\n");
exit (1);
}
trobot = ptc[WRITE];
frobot = ctp[READ];
/* Now fork a child process, update the TERMCAP, and exec the Robots */
if ((child = fork ()) == 0)
{ close (0);
dup (ptc[READ]);
close (1);
dup (ctp[WRITE]);
putenv ("TERMCAP", ROBOTTERM);
execl (rfile, rfile, 0);
_exit (1);
}
/* Call Robomatic as the Parent Process */
else
{ robomatic (); }
}
/****************************************************************
* Robomatic: Play Robots. Read the scrren, choose a move, and send it
****************************************************************/
# define ROWS 24
# define COLS 80
char screen[ROWS][COLS];
int playing=1;
int row=0, col=0, atrow= -1, atcol= -1;
robomatic ()
{ int cmd;
/* Initialize the Curses package */
initscr (); crmode (); noecho (); clear (); refresh ();
/* Clearn the screen array */
blankscreen ();
/* Read the first screen of output */
send (';'); /* Cause robots to send a bell when ready to read cmd */
getrobot (); /* Read screen updates until a bell */
/* Main loop, send a command and then read the screen */
while (playing)
{
/* If the user types anything, execute his commands */
while (charsavail (stdin))
{ switch (getchar ())
{ case 'd': debug++;
break;
case 'r': clear (); refresh (); drawscreen (); refresh ();
break;
default: break;
}
}
/* Choose a command and send it */
cmd = strategy ();
if (cmd == 0) { debug++; dwait ("command is zero"); }
dwait ("Sending command '%c'.", cmd);
send (cmd);
/* Send the semicolon and read screen updates until a bell */
send (';');
getrobot ();
}
/* Clear the scoreboard on some Robots */
send ('\n');
/* Now wait for the user to type a character before finishing */
refresh ();
getchar ();
/* Print termination messages */
move (ROWS-1, 0); clrtoeol (); refresh ();
endwin (); nocrmode (); noraw (); echo ();
deadrobot ();
exit (0);
}
/****************************************************************
* blankscreen: Fill the screen array with blanks
****************************************************************/
blankscreen ()
{ register int i, j;
for (i=0; i<ROWS; i++)
for (j=0; j<COLS; j++)
screen[i][j] = ' ';
}
/****************************************************************
* getrobot: Read the Robot screen, stop when you read a bell
****************************************************************/
getrobot ()
{ int r, c, ch, done=0;
register int i, j;
char *d, *h, buf[BUFSIZ];
d = "robot food"; /* FSM to check for death */
h = "scrap heaps"; /* FSM to check for new level */
while (!done)
{
/* Read a character from the Robots process */
if (read (frobot, buf, 1) < 1)
{ ch = EOF; if (trace) fclose (trace); }
else
{ ch = buf[0]; if (trace) { fputc (ch, trace); fflush (trace); } }
/* Check for the words "robot food" to see if we died */
if (ch == *d) { if (0 == *++d) { done++; playing=0; } }
else d = "robot food";
/* Check for the words "scrap heaps" to see if we survived a level */
if (ch == *h) { if (0 == *++h) { send (';'); } }
else h = "scrap heaps";
/* Now figure out what the character means */
switch (ch)
{ case BL: /* Bell */
done++;
break;
case BS: /* BackSpace */
col--;
break;
case CE: /* Clear to end of line */
for (i = col; i < COLS; i++)
screen[row][i] = ' ';
move (row, col);
clrtoeol ();
break;
case CL: /* Clear Screen */
clear ();
blankscreen ();
row = col = 0;
break;
case CM: /* Cursor motion command */
if (read (frobot, buf, 2) < 2)
{ deadrobot (); }
else
{ row = buf[0] - ' ';
col = buf[1] - ' ';
if (trace) { fprintf (trace, "%c%c", buf[0], buf[1]);
fflush (trace); }
}
break;
case CR: /* Carriage Return */
col = 0;
break;
case DO: /* Cursor down */
row++;
break;
case LF: /* Line Feed */
row++;
col = 0;
break;
case ND: /* Non-destructive space (cursor right) */
col++;
break;
case TA: /* Tab */
col = 8 * (1 + col / 8);
break;
case EOF: /* End of file */
playing = 0;
return;
break;
case UP: /* Cursor up */
row--;
break;
default: /* Other */
/* Control character */
if (ch < ' ')
{ fprintf (stderr, "Unknown character '\\%o'\n", ch);
kill (child, 9);
exit (1);
}
/* Printing character */
if (ch == 'I')
{ atrow = row; atcol = col; }
mvaddch (row, col, ch);
screen[row][col++] = ch;
break;
}
}
drawscreen ();
move (row, col);
refresh ();
}
/****************************************************************
* deadrobot: We died, read our score and print it out
****************************************************************/
deadrobot ()
{ int level=0, score=0;
char junk[BUFSIZ];
sscanf (screen[ROWS-1], "%s level: %d score: %d", junk, &level, &score);
printf ("Died on level %d with a score of %d.\n", level, score);
}
/****************************************************************
* send: Send a command to the Robots process
****************************************************************/
send (ch)
int ch;
{ char buf[BUFSIZ];
*buf = ch;
write (trobot, buf, 1);
}
/****************************************************************
* drawscreen: Draw the screen array on the user's terminal
****************************************************************/
drawscreen ()
{ register int r, c;
for (r=0; r<ROWS; r++)
for (c=0; c<COLS; c++)
mvaddch (r, c, screen[r][c]);
}
/****************************************************************
* strategy: Five basic rules
*
* 1. Find the nearest enemy robot.
* 2. Can you hide behind a scrap heap and cause him to crash?
* 3. Can you find another robot close to the first and move in.
* such a way so as to cause them to collide?
* 4. Can you make any safe move? Choose randomly.
* 5. Teleport.
****************************************************************/
/* Arrays for finding directions and keys to go that way */
int deltar[] = {-1, -1, -1, 0, 0, 0, 1, 1, 1};
int deltac[] = {-1, 0, 1, -1, 0, 1, -1, 0, 1};
char *keydir = "ykuh.lbjn";
strategy ()
{ register int k, r, c, ch, cmd;
int tr=0, tc=0, hr=0, hc=0;
/* Find closest robot and find a heap to block him with */
if (closest (&tr, &tc, '=') )
{ if (findheap (tr, tc, &hr, &hc) || findcollide (tr, tc, &hr, &hc))
{ if (cmd = makemove (hr, hc)) return (cmd); }
}
/* Make a random move */
cmd = 't';
for (k=0; k<9; k++)
{ r = atrow + deltar[k];
c = atcol + deltac[k];
ch = keydir[k];
if (safe (r, c))
{ if (cmd=='t') cmd = ch;
else if (time(0) & 1) cmd = ch;
}
}
return (cmd);
}
/****************************************************************
* safe: Is it safe to move to a given square
****************************************************************/
safe (r, c)
register int r, c;
{
return ((screen[r][c] == ' ' || screen[r][c] == 'I') &&
screen[r-1][c-1] != '=' && screen[r-1][ c] != '=' &&
screen[r-1][c+1] != '=' && screen[ r][c-1] != '=' &&
screen[ r][c+1] != '=' && screen[r+1][c-1] != '=' &&
screen[r+1][ c] != '=' && screen[r+1][c+1] != '=');
}
/****************************************************************
* find the closest object of a given type
****************************************************************/
closest (rp, cp, type)
int *rp, *cp, type;
{ register int dist=999, newdist, tr=0, tc=0, r, c;
/* Find closest robot */
for (r=1; r<ROWS-1; r++)
{ for (c=1; c<COLS; c++)
{ if (screen[r][c] == type)
{ newdist = distance (atrow, atcol, r, c);
if (newdist < dist)
{ tr=r; tc=c; dist=newdist; }
}
}
}
*rp=tr; *cp=tc;
dwait ("Closest %s is at (%d,%d).",
type == '=' ? "robot" : "scrap heap", tr, tc);
return (dist < 999);
}
/****************************************************************
* distance: Calculate the distance between two points
****************************************************************/
distance (r1, c1, r2, c2)
int r1, c1, r2, c2;
{ register int dr, dc;
if ((dr = r2-r1) < 0) dr = -dr; /* Absolute row distance */
if ((dc = c2-c1) < 0) dc = -dc; /* Absolute col distance */
return (max(dr,dc)); /* Max norm */
}
/****************************************************************
* findheap: Find a heap to block a given robot
****************************************************************/
findheap (tr, tc, pr, pc)
int tr, tc, *pr, *pc;
{ int r, c;
/* Check closest heap for blocking first */
if (closest (&r, &c, '@') && willblock (r, c, tr, tc, pr, pc))
{ dwait ("Heading for nearby safe square at (%d,%d).", *pr, *pc);
return (1);
}
/* Look through all heaps on the screen to find a block */
for (r=1; r<ROWS-1; r++)
{ for (c=1; c<COLS; c++)
{ if (screen[r][c] == '@' && willblock (r, c, tr, tc, pr, pc))
{ dwait ("Heading for safe square at (%d,%d).", *pr, *pc);
return (1);
}
}
}
/* Lose */
return (0);
}
/****************************************************************
* willblock: returns true if the heap at (hr,hc) will block the enemy robot
* located at (tr,tc). The square to move to is returned in (pr, pc)
****************************************************************/
willblock (hr, hc, tr, tc, pr, pc)
int hr, hc, tr, tc, *pr, *pc;
{ register int dr, dc, sr,sc;
/* First find the "shadow" behind the block from the enemy robot */
dr = hr-tr; dc = hc-tc;
if (abs (dr) == abs (dc))
{ sr = hr+sgn (dr); sc = hc+sgn (dc); }
else if (abs (dr) > abs (dc))
{ sr = hr+sgn (dr); sc = hc; }
else
{ sr = hr; sc = hc+sgn (dc); }
/* If we are closer to the shadow square than the robot is, win */
if (distance (sr, sc, tr, tc) > distance (hr, hc, atrow, atcol))
{ *pr = sr; *pc = sc; return (1); }
/* Lose */
return (0);
}
/****************************************************************
* findcollide: Find a robot to collide with a given robot. The target
* square will be the nearest safe square to the collision point.
****************************************************************/
findcollide (tr, tc, pr, pc)
int tr, tc, *pr, *pc;
{ int r=0, c=0, d, cr1, cc1, cr2, cc2;
/* Search from target robot for another on the same row or column */
for (d=1; d<COLS; d++)
{ if (tc+d < COLS && screen[tr][tc+d] == '=')
{ r = tr; c = tc+d; break; }
if (tc-d > 0 && screen[tr][tc-d] == '=')
{ r = tr; c = tc-d; break; }
if (tr+d < LINES-1 && screen[tr+d][tc] == '=')
{ r = tr+d; c = tc; break; }
if (tr-d > 0 && screen[tr-d][tc] == '=')
{ r = tr-d; c = tc; break; }
}
if (r || c)
{ dwait ("possible collision between (%d,%d) and (%d,%d)", tr, tc, r, c); }
/* Check for two on same row */
if (r == tr && r != atrow)
{ /* Check collision up */
cr1 = tr - (d+1)/2 - 1;
cr2 = tr + (d+1)/2 + 1;
cc1 = cc2 = (tc + c) / 2;
if (distance (atrow, atcol, cr1, cc1) < distance (atrow, atcol, cr2, cc2))
{ *pr = cr1; *pc = cc1; }
else
{ *pr = cr2; *pc = cc2; }
if (*pr < 1 || *pr >= ROWS || *pc < 1 || *pc > COLS) return (0);
dwait ("predicting safe spot near collision (%d,%d)", *pr, *pc);
return (1);
}
/* Check for two on same column */
if (c == tc && c != atcol)
{ /* Check collision left */
cr1 = cr2 = (tr + r) / 2;
cc1 = tc - (d+1)/2 - 1;
cc2 = tc + (d+1)/2 + 1;
if (distance (atrow, atcol, cr1, cc1) < distance (atrow, atcol, cr2, cc2))
{ *pr = cr1; *pc = cc1; }
else
{ *pr = cr2; *pc = cc2; }
if (*pr < 1 || *pr >= ROWS || *pc < 1 || *pc > COLS) return (0);
dwait ("predicting safe spot near collision (%d,%d)", *pr, *pc);
return (1);
}
/* Fail */
return (0);
}
/****************************************************************
* makemove: Return the direction to move toward a given square. This
* routine checks to make sure the move is a safe one. Returns 0 if no
* move is safe, and the character if one is found.
****************************************************************/
makemove (r, c)
{ int dr, dc;
dr = sgn (r-atrow); dc = sgn (c-atcol);
if (dr && dc)
{ if (safe (atrow+dr, atcol+dc)) return (keydir[3*dr + dc + 4]);
if (safe (atrow, atcol+dc)) return (keydir[ 0 + dc + 4]);
if (safe (atrow+dr, atcol )) return (keydir[3*dr + 0 + 4]);
}
else if (dr == 0)
{ if (safe (atrow, atcol+dc)) return (keydir[ 0 + dc + 4]);
if (safe (atrow+1, atcol+dc)) return (keydir[ 3 + dc + 4]);
if (safe (atrow-1, atcol )) return (keydir[-3 + 0 + 4]);
}
else if (dc == 0)
{ if (safe (atrow+dr, atcol )) return (keydir[3*dr + 0 + 4]);
if (safe (atrow, atcol+1)) return (keydir[3*dr + 1 + 4]);
if (safe (atrow+dr, atcol-1)) return (keydir[3*dr - 1 + 4]);
}
dwait ("makemove: cannot move to (%d,%d) safely.", r, c);
return (0);
}
/*****************************************************************
* charsavail: How many characters are there at the terminal? If any
* characters are found, 'noterm' is reset, since there is obviously
* a terminal around if the user is typing at us.
*****************************************************************/
charsavail ()
{ long n;
int retc;
if (retc = ioctl (READ, FIONREAD, &n))
{ fprintf (stderr, "Ioctl returns %d, n=%ld.\n", retc, n);
n=0;
}
return ((int) n);
}
/****************************************************************
* dwait: Debugging message
****************************************************************/
dwait (f, a1, a2, a3, a4)
char *f;
int a1, a2, a3, a4;
{ char buf[BUFSIZ];
register int c;
if (debug)
{ mvprintw (0, 0, "[%d,%d] ", atrow, atcol);
printw (f, a1, a2, a3, a4);
addch (' ');
move (row, col);
refresh ();
switch (getchar ())
{ case 'd': debug=0; break;
case 'r': clear (); refresh (); drawscreen (); refresh (); break;
default: break;
}
for (c=0; c<COLS; c++)
{ mvaddch (0, c, screen[0][c]); }
}
}
/*
* putenv -- put value into environment
*
* Usage: i = putenv (name,value)
* int i;
* char *name, *value;
*
* Putenv associates "value" with the environment parameter "name".
* If "value" is 0, then "name" will be deleted from the environment.
* Putenv returns 0 normally, -1 on error (not enough core for malloc).
*
* Putenv may need to add a new name into the environment, or to
* associate a value longer than the current value with a particular
* name. So, to make life simpler, putenv() copies your entire
* environment into the heap (i.e. malloc()) from the stack
* (i.e. where it resides when your process is initiated) the first
* time you call it.
*
* HISTORY
* 14-Oct-85 Michael Mauldin (mlm) at Carnegie-Mellon University
* Ripped out of CMU lib for Rob-O-Matic portability
* 20-Nov-79 Steven Shafer (sas) at Carnegie-Mellon University
* Created for VAX. Too bad Bell Labs didn't provide this. It's
* unfortunate that you have to copy the whole environment onto the
* heap, but the bookkeeping-and-not-so-much-copying approach turns
* out to be much hairier. So, I decided to do the simple thing,
* copying the entire environment onto the heap the first time you
* call putenv(), then doing realloc() uniformly later on.
* Note that "putenv(name,getenv(name))" is a no-op; that's the reason
* for the use of a 0 pointer to tell putenv() to delete an entry.
*
*/
#define EXTRASIZE 5 /* increment to add to env. size */
char *index (), *malloc (), *realloc ();
int strlen ();
static int envsize = -1; /* current size of environment */
extern char **environ; /* the global which is your env. */
static int findenv (); /* look for a name in the env. */
static int newenv (); /* copy env. from stack to heap */
static int moreenv (); /* incr. size of env. */
int putenv (name, value)
char *name, *value;
{ register int i, j;
register char *p;
if (envsize < 0)
{ /* first time putenv called */
if (newenv () < 0) /* copy env. to heap */
return (-1);
}
i = findenv (name); /* look for name in environment */
if (value)
{ /* put value into environment */
if (i < 0)
{ /* name must be added */
for (i = 0; environ[i]; i++);
if (i >= (envsize - 1))
{ /* need new slot */
if (moreenv () < 0)
return (-1);
}
p = malloc (strlen (name) + strlen (value) + 2);
if (p == 0) /* not enough core */
return (-1);
environ[i + 1] = 0; /* new end of env. */
}
else
{ /* name already in env. */
p = realloc (environ[i],
strlen (name) + strlen (value) + 2);
if (p == 0)
return (-1);
}
sprintf (p, "%s=%s", name, value);/* copy into env. */
environ[i] = p;
}
else
{ /* delete name from environment */
if (i >= 0)
{ /* name is currently in env. */
free (environ[i]);
for (j = i; environ[j]; j++);
environ[i] = environ[j - 1];
environ[j - 1] = 0;
}
}
return (0);
}
static int findenv (name)
char *name;
{ register char *namechar, *envchar;
register int i, found;
found = 0;
for (i = 0; environ[i] && !found; i++)
{ envchar = environ[i];
namechar = name;
while (*namechar && (*namechar == *envchar))
{ namechar++;
envchar++;
}
found = (*namechar == '\0' && *envchar == '=');
}
return (found ? i - 1 : -1);
}
static int newenv ()
{ register char **env, *elem;
register int i, esize;
for (i = 0; environ[i]; i++);
esize = i + EXTRASIZE + 1;
env = (char **) malloc (esize * sizeof (elem));
if (env == 0)
return (-1);
for (i = 0; environ[i]; i++)
{ elem = malloc (strlen (environ[i]) + 1);
if (elem == 0)
return (-1);
env[i] = elem;
strcpy (elem, environ[i]);
}
env[i] = 0;
environ = env;
envsize = esize;
return (0);
}
static int moreenv ()
{ register int esize;
register char **env;
esize = envsize + EXTRASIZE;
env = (char **) realloc (environ, esize * sizeof (*env));
if (env == 0)
return (-1);
environ = env;
envsize = esize;
return (0);
}