[net.sources.games] Rob-O-Matic: Plays the Robots Game

mlm@cad.cs.cmu.edu.ARPA (Michael Mauldin) (10/14/85)

#!/bin/sh
#
#	 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
#	 @ Here is your new automatic Robots player, Rob-O-Matic! @
#	 @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
# 
#    To unpack this program, run this message through 'unshar', or
#    remove the mail headers and pipe the body through the "sh" command.
#    Then "make" should build the program for you.
# 
#	 	 A tradition of fine game playing programs,
#	 	from the People who brought you Rog-O-Matic.
# 
# Michael L. Mauldin (Fuzzy)		Department of Computer Science
# Mauldin@CAD.CS.CMU.EDU		Carnegie-Mellon University
# (412) 578-3065			Pittsburgh, PA  15213
#
echo 'Start of Rob-O-Matic Distribution:'
echo 'x - robomatic.6'
sed 's/^X//' > robomatic.6 << '/'
X.TH ROBOMATIC 6 10/14/85
X.UC 4
X.SH NAME
Xrobomatic \- Automatically Playing a Game of Logic
X.SH SYNOPSIS
X.I robomatic
X[
X.I -d
X]
X.SH DESCRIPTION
X.PP
X.I Rob-O-Matic
XPlays the "Robots" game written by 
XAllan Black of Strathclyde University, Glasgow.
X.SH OPTIONS
X.TP
X.B -d
XTurns on debugging.
X.SH FILES
X.PP
XLooks for the Robots executable in the current directory, then in two
Xplaces specified at compile-time (one of which is /usr/games).
X.SH SEE ALSO
X.PP
Xrobots(6)
X.SH BUGS
X.PP
XDoesn't play an optimum game.
X.SH HISTORY
X.TP
X14-Oct-85 Michael Mauldin (mlm) at Carnegie-Mellon University
XCreated.
/
echo 'x - robomatic.c'
sed 's/^X//' > robomatic.c << '/'
X/* robomatic.c: Rob-O-Matic I (CMU) Mon Oct 14 11:51:01 EDT 1985 - mlm */
X/*****************************************************************
X *
X * Rob-O-Matic: Play the robots game
X * Copyright (C) 1985 by Mauldin
X * The right is granted to any person, university, or company 
X * to copy, modify, or distribute (for free) this file, providing
X * that this notice is not removed.
X *
X * HISTORY
X * 11-Oct-85  Michael Mauldin (mlm) at Carnegie-Mellon University
X *	Created.
X *
X *****************************************************************/
X
X# include <curses.h>
X
X# define NEWROBOT	"/usr/mlm/bin/robots"
X# define ROBOT		"/usr/games/robots"
X
X# define READ		0
X# define WRITE		1
X# define abs(A)		((A)>0?(A):-(A))
X# define max(A,B)	((A)>(B)?(A):(B))
X# define sgn(A)		((A)==0?0:((A)>0?1:-1))
X
X/* Define the Rob-O-Matic pseudo-terminal */
X
X# 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:"
X# define ctrl(C) ((C)&037)
X# define BL ctrl('G')
X# define BS ctrl('H')
X# define CE ctrl('E')
X# define CL ctrl('L')
X# define CM ctrl('F')
X# define CR ctrl('M')
X# define DO ctrl('B')
X# define LF ctrl('J')
X# define ND ctrl('C')
X# define TA ctrl('I')
X# define UP ctrl('A')
X
Xint   child;
Xint   frobot, trobot;
Xint   debug=0;
XFILE *trace=NULL;
X/****************************************************************
X * Main routine
X ****************************************************************/
X
Xmain (argc, argv)
Xint   argc;
Xchar *argv[];
X
X{ int   ptc[2], ctp[2];
X  char *rfile=NULL;
X
X  /* Get the options from the command line */
X  while (--argc > 0 && (*++argv)[0] == '-')
X  { while (*++(*argv))
X    { switch (**argv)
X      { case 'd': debug++;					break;
X        default:  printf ("Usage: robomatic [-d]\n");		exit (1);
X      }
X    }
X  }
X
X  /* Open tracing file (if needed) */
X  if (debug)
X  { if ((trace = fopen ("trace.log", "w")) == NULL)
X    { perror ("trace.log");
X      exit (1);
X    }
X  }
X
X  /* Find an executable of the robots game */
X  if (access ("robots", 1) == 0)	rfile = "robots";
X  else if (access (NEWROBOT, 1) == 0)	rfile = NEWROBOT;
X  else if (access (ROBOT, 1) == 0)	rfile = ROBOT;
X  else
X  { perror ("robots");
X    exit (1);
X  }
X
X  /* Get two pipes to attach to the Robots process */
X  if ((pipe (ptc) < 0) || (pipe (ctp) < 0))
X  { fprintf (stderr, "Cannot get pipes!\n");
X    exit (1);
X  }
X
X  trobot = ptc[WRITE];
X  frobot = ctp[READ];
X
X  /* Now fork a child process, update the TERMCAP, and exec the Robots */
X  if ((child = fork ()) == 0)
X  { close (0);
X    dup (ptc[READ]);
X    close (1);
X    dup (ctp[WRITE]);
X
X    putenv ("TERMCAP", ROBOTTERM);
X    execl (rfile, rfile, 0);
X    _exit (1);
X  }
X
X  /* Call Robomatic as the Parent Process */
X  else
X  { robomatic (); }
X}
X
X/****************************************************************
X * Robomatic: Play Robots.  Read the scrren, choose a move, and send it
X ****************************************************************/
X
X# define ROWS	24
X# define COLS	80
X
Xchar screen[ROWS][COLS];
Xint playing=1;
Xint row=0, col=0, atrow= -1, atcol= -1;
X
Xrobomatic ()
X{ int cmd;
X  
X  /* Initialize the Curses package */
X  initscr (); crmode (); noecho (); clear (); refresh ();
X
X  /* Clearn the screen array */
X  blankscreen ();
X
X  /* Read the first screen of output */
X  send (';');	/* Cause robots to send a bell when ready to read cmd */
X  getrobot ();	/* Read screen updates until a bell */
X
X  /* Main loop, send a command and then read the screen */
X  while (playing)
X  { 
X    /* If the user types anything, execute his commands */
X    while (charsavail (stdin))
X    { switch (getchar ())
X      { case 'd':	debug++; 
X			break;
X        case 'r':	clear (); refresh (); drawscreen (); refresh (); 
X			break;
X	default:	break;
X      }
X    }
X
X    /* Choose a command and send it */
X    cmd = strategy ();
X    if (cmd == 0) { debug++; dwait ("command is zero"); }
X    dwait ("Sending command '%c'.", cmd);
X    send (cmd);
X
X    /* Send the semicolon and read screen updates until a bell */
X    send (';');
X    getrobot ();
X  }
X
X  /* Clear the scoreboard on some Robots */
X  send ('\n');
X
X  /* Now wait for the user to type a character before finishing */
X  refresh ();
X  getchar ();
X
X  /* Print termination messages */
X  move (ROWS-1, 0); clrtoeol (); refresh ();
X  endwin (); nocrmode (); noraw (); echo ();
X
X  deadrobot ();
X
X  exit (0);
X}
X
X/****************************************************************
X * blankscreen: Fill the screen array with blanks
X ****************************************************************/
X
Xblankscreen ()
X{ register int i, j;
X
X  for (i=0; i<ROWS; i++)
X    for (j=0; j<COLS; j++)
X      screen[i][j] = ' ';
X}
X
X/****************************************************************
X * getrobot: Read the Robot screen, stop when you read a bell
X ****************************************************************/
X
Xgetrobot ()
X{ int   r, c, ch, done=0;
X  register int i, j;
X  char *d, *h, buf[BUFSIZ];
X
X  d = "robot food";			/* FSM to check for death */
X  h = "scrap heaps";			/* FSM to check for new level */
X
X  while (!done)
X  {
X    /* Read a character from the Robots process */
X    if (read (frobot, buf, 1) < 1)
X    { ch = EOF; if (trace) fclose (trace); }
X    else
X    { ch = buf[0]; if (trace) { fputc (ch, trace); fflush (trace); } }
X
X    /* Check for the words "robot food" to see if we died */
X    if (ch == *d) { if (0 == *++d) { done++; playing=0; } }
X    else d = "robot food";
X
X    /* Check for the words "scrap heaps" to see if we survived a level */
X    if (ch == *h) { if (0 == *++h) { send (';'); } }
X    else h = "scrap heaps";
X
X    /* Now figure out what the character means */
X    switch (ch)
X    { case BL:	/* Bell */
X        done++;
X        break;
X
X      case BS: /* BackSpace */
X        col--;
X        break;
X
X      case CE: /* Clear to end of line */
X        for (i = col; i < COLS; i++)
X          screen[row][i] = ' ';
X
X        move (row, col);
X        clrtoeol ();
X	break;
X
X      case CL: /* Clear Screen */
X        clear ();
X        blankscreen ();
X	row = col = 0;
X        break;
X
X      case CM:	/* Cursor motion command */
X        if (read (frobot, buf, 2) < 2)
X	{ deadrobot (); }
X	else
X	{ row = buf[0] - ' ';
X	  col = buf[1] - ' ';
X          if (trace) { fprintf (trace, "%c%c", buf[0], buf[1]);
X		       fflush (trace); }
X	}
X        break;
X
X      case CR:	/* Carriage Return */
X        col = 0;
X        break;
X
X      case DO:	/* Cursor down */
X        row++;
X        break;
X
X      case LF:	/* Line Feed */
X        row++;
X        col = 0;
X        break;
X
X      case ND:	/* Non-destructive space (cursor right) */
X        col++;
X        break;
X
X      case TA:	/* Tab */
X        col = 8 * (1 + col / 8);
X        break;
X
X      case EOF:	/* End of file */
X	playing = 0;
X	return;
X        break;
X
X      case UP:	/* Cursor up */
X        row--;
X        break;
X
X      default: /* Other */
X
X	/* Control character */
X        if (ch < ' ')
X        { fprintf (stderr, "Unknown character '\\%o'\n", ch);
X	  kill (child, 9);
X	  exit (1);
X        }
X
X	/* Printing character */
X	if (ch == 'I')
X	{  atrow = row; atcol = col; }
X        mvaddch (row, col, ch);
X        screen[row][col++] = ch;
X        break;
X    }
X  }
X
X  drawscreen ();
X  move (row, col); 
X  refresh ();
X}
X
X/****************************************************************
X * deadrobot: We died, read our score and print it out
X ****************************************************************/
X
Xdeadrobot ()
X{ int level=0, score=0;
X  char junk[BUFSIZ];
X
X  sscanf (screen[ROWS-1], "%s level: %d score: %d", junk, &level, &score);
X  printf ("Died on level %d with a score of %d.\n", level, score);
X}
X
X/****************************************************************
X * send: Send a command to the Robots process
X ****************************************************************/
X
Xsend (ch)
Xint ch;
X{ char buf[BUFSIZ];
X
X  *buf = ch;
X  write (trobot, buf, 1);
X}
X
X/****************************************************************
X * drawscreen: Draw the screen array on the user's terminal
X ****************************************************************/
X
Xdrawscreen ()
X{ register int r, c;
X
X  for (r=0; r<ROWS; r++)
X    for (c=0; c<COLS; c++)
X      mvaddch (r, c, screen[r][c]);
X}
X
X/****************************************************************
X * strategy: Five basic rules
X *
X * 1. Find the nearest enemy robot.
X * 2. Can you hide behind a scrap heap and cause him to crash?
X * 3. Can you find another robot close to the first and move in.
X *    such a way so as to cause them to collide?
X * 4. Can you make any safe move?  Choose randomly.
X * 5. Teleport.
X ****************************************************************/
X
X/* Arrays for finding directions and keys to go that way */
Xint deltar[] = {-1, -1, -1,  0,  0,  0,  1,  1,  1};
Xint deltac[] = {-1,  0,  1, -1,  0,  1, -1,  0,  1};
Xchar *keydir = "ykuh.lbjn";
X
Xstrategy ()
X{ register int k, r, c, ch, cmd;
X  int tr=0, tc=0, hr=0, hc=0;
X
X  /* Find closest robot and find a heap to block him with */
X  if (closest (&tr, &tc, '=') )
X  { if (findheap (tr, tc, &hr, &hc) || findcollide (tr, tc, &hr, &hc))
X    { if (cmd = makemove (hr, hc)) return (cmd); }  
X  }
X
X  /* Make a random move */
X  cmd = 't';
X  for (k=0; k<9; k++)
X  { r = atrow + deltar[k];
X    c = atcol + deltac[k];
X    ch = keydir[k];
X    if (safe (r, c))
X    { if (cmd=='t') cmd = ch;
X      else if (time(0) & 1) cmd = ch;
X    }
X  }
X
X  return (cmd);
X}
X
X/****************************************************************
X * safe: Is it safe to move to a given square
X ****************************************************************/
X
Xsafe (r, c)
Xregister int r, c;
X{
X  return ((screen[r][c] == ' ' || screen[r][c] == 'I') &&
X	   screen[r-1][c-1] != '=' && screen[r-1][  c] != '=' &&
X	   screen[r-1][c+1] != '=' && screen[  r][c-1] != '=' &&
X	   screen[  r][c+1] != '=' && screen[r+1][c-1] != '=' &&
X	   screen[r+1][  c] != '=' && screen[r+1][c+1] != '=');
X}
X
X/****************************************************************
X * find the closest object of a given type
X ****************************************************************/
X
Xclosest (rp, cp, type)
Xint *rp, *cp, type;
X{ register int dist=999, newdist, tr=0, tc=0, r, c;
X
X  /* Find closest robot */
X  for (r=1; r<ROWS-1; r++)
X  { for (c=1; c<COLS; c++)
X    { if (screen[r][c] == type)
X      { newdist = distance (atrow, atcol, r, c);
X	if (newdist < dist)
X	{ tr=r; tc=c; dist=newdist; }
X      }
X    }
X  }
X
X  *rp=tr; *cp=tc;
X
X  dwait ("Closest %s is at (%d,%d).",
X         type == '=' ? "robot" : "scrap heap", tr, tc);
X
X  return (dist < 999);
X}
X
X/****************************************************************
X * distance: Calculate the distance between two points
X ****************************************************************/
X
Xdistance (r1, c1, r2, c2)
Xint r1, c1, r2, c2;
X{ register int dr, dc;
X
X  if ((dr = r2-r1) < 0) dr = -dr;	/* Absolute row distance */
X  if ((dc = c2-c1) < 0) dc = -dc;	/* Absolute col distance */
X  return (max(dr,dc));			/* Max norm */
X}
X
X/****************************************************************
X * findheap: Find a heap to block a given robot
X ****************************************************************/
X
Xfindheap (tr, tc, pr, pc)
Xint tr, tc, *pr, *pc;
X{ int r, c;
X
X  /* Check closest heap for blocking first */
X  if (closest (&r, &c, '@') && willblock (r, c, tr, tc, pr, pc))
X  { dwait ("Heading for nearby safe square at (%d,%d).", *pr, *pc);
X    return (1);
X  }
X
X  /* Look through all heaps on the screen to find a block */
X  for (r=1; r<ROWS-1; r++)
X  { for (c=1; c<COLS; c++)
X    { if (screen[r][c] == '@' && willblock (r, c, tr, tc, pr, pc))
X      { dwait ("Heading for safe square at (%d,%d).", *pr, *pc);
X        return (1);
X      }
X    }
X  }
X
X  /* Lose */  
X  return (0);
X}
X
X/****************************************************************
X * willblock: returns true if the heap at (hr,hc) will block the enemy robot
X * located at (tr,tc).  The square to move to is returned in (pr, pc)
X ****************************************************************/
X
Xwillblock (hr, hc, tr, tc, pr, pc)
Xint hr, hc, tr, tc, *pr, *pc;
X{ register int dr, dc, sr,sc;
X
X  /* First find the "shadow" behind the block from the enemy robot */
X  dr = hr-tr; dc = hc-tc;
X
X  if (abs (dr) == abs (dc))
X  { sr = hr+sgn (dr); sc = hc+sgn (dc); }
X  else if (abs (dr) > abs (dc))
X  { sr = hr+sgn (dr); sc = hc; }
X  else
X  { sr = hr; sc = hc+sgn (dc); }
X
X  /* If we are closer to the shadow square than the robot is, win */
X  if (distance (sr, sc, tr, tc) > distance (hr, hc, atrow, atcol))
X  { *pr = sr; *pc = sc; return (1); }
X  
X  /* Lose */
X  return (0);
X}
X
X/****************************************************************
X * findcollide: Find a robot to collide with a given robot.  The target
X * square will be the nearest safe square to the collision point.
X ****************************************************************/
X
Xfindcollide (tr, tc, pr, pc)
Xint tr, tc, *pr, *pc;
X{ int r=0, c=0, d, cr1, cc1, cr2, cc2;
X
X  /* Search from target robot for another on the same row or column */
X  for (d=1; d<COLS; d++)
X  { if (tc+d < COLS && screen[tr][tc+d] == '=')
X    { r = tr; c = tc+d; break; }
X
X    if (tc-d > 0 && screen[tr][tc-d] == '=')
X    { r = tr; c = tc-d; break; }
X
X    if (tr+d < LINES-1 && screen[tr+d][tc] == '=')
X    { r = tr+d; c = tc; break; }
X
X    if (tr-d > 0 && screen[tr-d][tc] == '=')
X    { r = tr-d; c = tc; break; }
X  }
X
X  if (r || c)
X  { dwait ("possible collision between (%d,%d) and (%d,%d)", tr, tc, r, c); }
X  
X  /* Check for two on same row */
X  if (r == tr && r != atrow)
X  { /* Check collision up */
X    cr1 = tr - (d+1)/2 - 1;
X    cr2 = tr + (d+1)/2 + 1;
X    cc1 = cc2 = (tc + c) / 2;
X    if (distance (atrow, atcol, cr1, cc1) < distance (atrow, atcol, cr2, cc2))
X    { *pr = cr1; *pc = cc1; }
X    else
X    { *pr = cr2; *pc = cc2; }
X
X    if (*pr < 1 || *pr >= ROWS || *pc < 1 || *pc > COLS) return (0);
X
X    dwait ("predicting safe spot near collision (%d,%d)", *pr, *pc);
X    return (1);
X  }
X  
X  /* Check for two on same column */
X  if (c == tc && c != atcol)
X  { /* Check collision left */
X    cr1 = cr2 = (tr + r) / 2;
X    cc1 = tc - (d+1)/2 - 1;
X    cc2 = tc + (d+1)/2 + 1;
X    if (distance (atrow, atcol, cr1, cc1) < distance (atrow, atcol, cr2, cc2))
X    { *pr = cr1; *pc = cc1; }
X    else
X    { *pr = cr2; *pc = cc2; }
X
X    if (*pr < 1 || *pr >= ROWS || *pc < 1 || *pc > COLS) return (0);
X
X    dwait ("predicting safe spot near collision (%d,%d)", *pr, *pc);
X    return (1);
X  }
X  
X  /* Fail */
X  return (0);
X}
X
X/****************************************************************
X * makemove: Return the direction to move toward a given square.  This
X * routine checks to make sure the move is a safe one.  Returns 0 if no
X * move is safe, and the character if one is found.
X ****************************************************************/
X
Xmakemove (r, c)
X{ int dr, dc;
X
X  dr = sgn (r-atrow);  dc = sgn (c-atcol);
X
X  if (dr && dc)
X  { if (safe (atrow+dr, atcol+dc)) return (keydir[3*dr + dc + 4]);
X    if (safe (atrow,    atcol+dc)) return (keydir[   0 + dc + 4]);
X    if (safe (atrow+dr, atcol   )) return (keydir[3*dr +  0 + 4]);
X  }
X  else if (dr == 0)
X  { if (safe (atrow,    atcol+dc)) return (keydir[ 0 + dc + 4]);
X    if (safe (atrow+1,  atcol+dc)) return (keydir[ 3 + dc + 4]);
X    if (safe (atrow-1,  atcol   )) return (keydir[-3 +  0 + 4]);
X  }
X  else if (dc == 0)
X  { if (safe (atrow+dr,  atcol  )) return (keydir[3*dr + 0 + 4]);
X    if (safe (atrow,     atcol+1)) return (keydir[3*dr + 1 + 4]);
X    if (safe (atrow+dr,  atcol-1)) return (keydir[3*dr - 1 + 4]);
X  }
X
X  dwait ("makemove: cannot move to (%d,%d) safely.", r, c);
X  return (0);
X}
X
X/*****************************************************************
X * charsavail: How many characters are there at the terminal? If any
X * characters are found, 'noterm' is reset, since there is obviously
X * a terminal around if the user is typing at us.
X *****************************************************************/
X
Xcharsavail ()
X{ long n;
X  int retc;
X  
X  if (retc = ioctl (READ, FIONREAD, &n))
X  { fprintf (stderr, "Ioctl returns %d, n=%ld.\n", retc, n);
X    n=0;
X  }
X
X  return ((int) n);
X}
X
X/****************************************************************
X * dwait: Debugging message
X ****************************************************************/
X
Xdwait (f, a1, a2, a3, a4)
Xchar *f;
Xint a1, a2, a3, a4;
X{ char buf[BUFSIZ];
X  register int c;
X
X  if (debug)
X  { mvprintw (0, 0, "[%d,%d] ", atrow, atcol);
X    printw (f, a1, a2, a3, a4);
X    addch (' ');
X    move (row, col);
X    refresh ();
X    switch (getchar ())
X    { case 'd':	debug=0; break;
X      case 'r': clear (); refresh (); drawscreen (); refresh (); break;
X      default:	break;
X    }
X    for (c=0; c<COLS; c++)
X    { mvaddch (0, c, screen[0][c]); }
X  }
X}
X
X/*
X *  putenv  --  put value into environment
X *
X *  Usage:  i = putenv (name,value)
X *	int i;
X *	char *name, *value;
X *
X *  Putenv associates "value" with the environment parameter "name".
X *  If "value" is 0, then "name" will be deleted from the environment.
X *  Putenv returns 0 normally, -1 on error (not enough core for malloc).
X *
X *  Putenv may need to add a new name into the environment, or to
X *  associate a value longer than the current value with a particular
X *  name.  So, to make life simpler, putenv() copies your entire
X *  environment into the heap (i.e. malloc()) from the stack
X *  (i.e. where it resides when your process is initiated) the first
X *  time you call it.
X *
X *  HISTORY
X * 14-Oct-85 Michael Mauldin (mlm) at Carnegie-Mellon University
X *      Ripped out of CMU lib for Rob-O-Matic portability
X * 20-Nov-79  Steven Shafer (sas) at Carnegie-Mellon University
X *	Created for VAX.  Too bad Bell Labs didn't provide this.  It's
X *	unfortunate that you have to copy the whole environment onto the
X *	heap, but the bookkeeping-and-not-so-much-copying approach turns
X *	out to be much hairier.  So, I decided to do the simple thing,
X *	copying the entire environment onto the heap the first time you
X *	call putenv(), then doing realloc() uniformly later on.
X *	Note that "putenv(name,getenv(name))" is a no-op; that's the reason
X *	for the use of a 0 pointer to tell putenv() to delete an entry.
X *
X */
X
X#define EXTRASIZE 5		/* increment to add to env. size */
X
Xchar *index (), *malloc (), *realloc ();
Xint   strlen ();
X
Xstatic int  envsize = -1;	/* current size of environment */
Xextern char **environ;		/* the global which is your env. */
X
Xstatic int  findenv ();		/* look for a name in the env. */
Xstatic int  newenv ();		/* copy env. from stack to heap */
Xstatic int  moreenv ();		/* incr. size of env. */
X
Xint   putenv (name, value)
Xchar *name, *value;
X{ register int  i, j;
X  register char *p;
X
X  if (envsize < 0)
X  {				/* first time putenv called */
X    if (newenv () < 0)		/* copy env. to heap */
X      return (-1);
X  }
X
X  i = findenv (name);		/* look for name in environment */
X
X  if (value)
X  {				/* put value into environment */
X    if (i < 0)
X    {				/* name must be added */
X      for (i = 0; environ[i]; i++);
X      if (i >= (envsize - 1))
X      {				/* need new slot */
X	if (moreenv () < 0)
X	  return (-1);
X      }
X      p = malloc (strlen (name) + strlen (value) + 2);
X      if (p == 0)		/* not enough core */
X	return (-1);
X      environ[i + 1] = 0;	/* new end of env. */
X    }
X    else
X    {				/* name already in env. */
X      p = realloc (environ[i],
X	  strlen (name) + strlen (value) + 2);
X      if (p == 0)
X	return (-1);
X    }
X    sprintf (p, "%s=%s", name, value);/* copy into env. */
X    environ[i] = p;
X  }
X  else
X  {				/* delete name from environment */
X    if (i >= 0)
X    {				/* name is currently in env. */
X      free (environ[i]);
X      for (j = i; environ[j]; j++);
X      environ[i] = environ[j - 1];
X      environ[j - 1] = 0;
X    }
X  }
X
X  return (0);
X}
X
Xstatic int  findenv (name)
Xchar *name;
X{ register char *namechar, *envchar;
X  register int  i, found;
X
X  found = 0;
X  for (i = 0; environ[i] && !found; i++)
X  { envchar = environ[i];
X    namechar = name;
X    while (*namechar && (*namechar == *envchar))
X    { namechar++;
X      envchar++;
X    }
X    found = (*namechar == '\0' && *envchar == '=');
X  }
X  return (found ? i - 1 : -1);
X}
X
Xstatic int  newenv ()
X{ register char **env, *elem;
X  register int  i, esize;
X
X  for (i = 0; environ[i]; i++);
X  esize = i + EXTRASIZE + 1;
X  env = (char **) malloc (esize * sizeof (elem));
X  if (env == 0)
X    return (-1);
X
X  for (i = 0; environ[i]; i++)
X  { elem = malloc (strlen (environ[i]) + 1);
X    if (elem == 0)
X      return (-1);
X    env[i] = elem;
X    strcpy (elem, environ[i]);
X  }
X
X  env[i] = 0;
X  environ = env;
X  envsize = esize;
X  return (0);
X}
X
Xstatic int  moreenv ()
X{ register int  esize;
X  register char **env;
X
X  esize = envsize + EXTRASIZE;
X  env = (char **) realloc (environ, esize * sizeof (*env));
X  if (env == 0)
X    return (-1);
X  environ = env;
X  envsize = esize;
X  return (0);
X}
/
echo 'x - Makefile'
sed 's/^X//' > Makefile << '/'
Xrobomatic:	robomatic.c
X	cc -O -o robomatic robomatic.c -lcurses -ltermcap
/
echo 'Rob-O-Matic Distribution complete.'
exit