[comp.sources.games] v11i068: r2matic - an automatic Robots 2 player, Part01/01

billr@saab.CNA.TEK.COM (Bill Randle) (11/20/90)

Submitted-by: Mike McGaughey <mmcg@bruce.cs.monash.oz.au>
Posting-number: Volume 11, Issue 68
Archive-name: r2matic/Part01
Environment: curses


[[This is an automatic player for Robots 2, the successor to Robots.
Assuming it gets to level 6 (it only uses random teleports before
then), it will usually get to level 18 or so.

The first lines in the program define where to find the ROBOTS2 game;
you'll have to edit them before you compile R2mat.]]

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 1)."
# Contents:  README R2mat.c
# Wrapped by billr@saab on Fri Nov 16 15:16:25 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(2355 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XThis is an automatic player for Robots 2, the successor to Robots.
XAll of the control code was stolen from the original Robomatic,
Xwhich was written in 1985 by Mauldin, at cmu; the new strategy was hacked
Xtogether (originally) in a day by mmcg@bruce.cs.monash.oz.au.
X
XThe first lines in the program define where to find the ROBOTS2 game;
Xyou'll have to edit them before you compile R2mat.
X
XTo compile: cc R2mat.c -lcurses -ltermcap
X
XBugs:
X    Zero lookahead, sometimes causing it to be incredibly stupid and waste
X        teleports
X    Never safe teleports before level 6, causing a lot of short games (this
X	was a design decision :-).  When it passes level 5, on average,
X	it gets to level 18.
X    Often destroys a perfect position, due to its heuristics for hiding
X    Doesn't play well on lower levels.  Doesn't play particularly well
X    at higher levels either.
X
XFeatures:
X    Provides a free lightshow
X
XTODO:
X    Fix its annoying tendency to move between opposing robots.
X    Add lookahead for important cases.
X
XHow it works:
XIt works by examining the area surrounding the player (radius is 7, as
Xbruce isn't particularly fast), and assigning points for each possible
Xmove depending on the number of robot kills and the amount and position
Xof any junk that would be created.  If sorely pressed, it may use 'a'
Xor 't'.  Has a tendency to wander around until a junkpile comes within
Xits radius of visibility.
X
XFYI: Scores on bruce:
X
XTop Twenty Scores of All Time:
XRank   Score	Name
X   1 132042513   mmcg: eaten on level 55.
X   2 103827914   mmcg: eaten on level 51.
X   3  51333632   mmcg: eaten on level 40.
X   4  47930704   mmcg: eaten on level 40.
X   5  47607698   mmcg: eaten on level 40.
X   6  45832211   jono: eaten on level 39.
X   7  32425000   jono: eaten on level 35.
X   8  26699065   R2Matic (mmcg): eaten on level 33.
X   9  24264938   rob: eaten on level 32.
X  10  23939157   mmcg: eaten on level 32.
X  11  20787812   geoff: eaten on level 30.
X  12  19020831   R2Matic (mmcg): eaten on level 30.
X  13  18617720   jono: eaten on level 29.
X  14  17599671   R2Matic (mmcg): eaten on level 29.
X  15  16429400   cjs: eaten on level 28.
X  16  16375874   mmcg: eaten on level 28.
X  17  15040288   mmcg: eaten on level 27.
X  18  14668288   cjs: eaten on level 27.
X  19  14623588   geoff: eaten on level 27.
X  20  14610109   mmcg: eaten on level 27.
END_OF_FILE
if test 2355 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'R2mat.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'R2mat.c'\"
else
echo shar: Extracting \"'R2mat.c'\" \(32070 characters\)
sed "s/^X//" >'R2mat.c' <<'END_OF_FILE'
X# define NEWROBOT    "robots2"
X# define ROBOT        "/postg/mmcg/r2/robots2"
X
X/* Robo2matic: Play Robots 2, although not as well as the author.
X *
X * Author: Mike McGaughey, July 1990; much of the code is stolen from
X * Rob-O-Matic, by Mauldin.
X *
X * Copyright(C) Mike McGaughey (1990) and Mauldin (1985) [see below].
X *
X * Robots2 is like robots, except the following extra commands are
X * available:
X *    r - random teleport
X *    t - safe teleport (costs)
X *    a - kill all robots adjacent to you (costs)
X *    ? - show all legal moves
X *
X *    There are normal speed robots ('=') and double speed ones ('#').
X * The rule for detecting collisions seems to be: move all robots 1
X * square, look for collisions.  Then move the fast robots another
X * square, again deleting any that collide (i.e. just move all the slow
X * robots, then all the fast ones).
X *
X * Invocation: Robo2matic [-d] [-a]
X *    -d    turns on debugging output
X *    -a    automatic - don't wait for a keypress at the end of the game.
X *
X * BUGS:
X *   decision of when to random teleport is simplistic
X *   Is sometimes incredibly stupid (no lookahead).
X *   Doesn't play as well as the author.
X *
X * This code uses major portions of the following - the basic driver
X * code, the termcap stuff, though the strategy is completely reworked:
X */
X
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/*
X * Tunable heuristic parameters.
X */
X
X#define MAXDIST 7	/* for what radius will we examine the surroundings? */
X			/* increasing his really slows things down! */
X
X#define DEATH_BONUS(dist) (100/(dist))  /* Added bonus for each
X				* bot killed, especially close ones */
X#define FASTBOT_FACTOR    2    /* double it for fastbots */
X#define PUSHED_PENALTY	  10   /* discourage arbitrary pushing of heaps */
X#define AOK_THREAT	-600	/* when to use 'a' - a hack! */
X#define	TO_CENTRE	  2    /* encourage going to centre */
X#define HEAP_NEARNESS(dist)    /* we like to be close to them! */ \
X	    (MAXDIST * MAXDIST / (dist) / (dist))
X#define RANDOMNESS	(MAXDIST/2) /* a sprinkling of the unknown */
X
X#define R_NEAR(dist)	0 /* slight penalties for being too
X					  * close - want to hide behind heaps!*/
X#define FASTBOT_NEAR(dist) (MAXDIST/(dist+1))  /* robots to deal with at a time */
X
X#define MIN_ROBOTS	10	/* min number of close robots to find */
X#define	MIN_HEAPS	10	/* and min close heaps */
X
X# include <curses.h>
X
X# define OPTVAR		"ROBOTOPTS"
X# define OPTIONS	"name=R2Matic"
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 min(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')
Xint TargetLevel = 20;
Xint noinp = 0;
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{ 
X    int   ptc[2], ctp[2];
X    char *rfile=NULL;
X    int ton=0;
X
X    /* Get the options from the command line */
X    while (--argc > 0 && (*++argv)[0] == '-')
X    { 
X        while (*++(*argv))
X        { 
X            switch (**argv)
X            { 
X            case 'd': 
X                debug++;                    
X                break;
X	    case 'a':
X		noinp++; /* automatic */
X		break;
X	    case 't':
X		ton++;
X		break;
X            case 'l':
X                sscanf(++(*argv), "%d", &TargetLevel);        
X                break;
X            default:  
X                printf ("Usage: robomatic [-d]\n");        
X                exit (1);
X            }
X        }
X    }
X
X    /* Open tracing file (if needed) */
X    if (debug || ton)
X    { 
X        if ((trace = fopen ("trace.log", "w")) == NULL)
X        { 
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    { 
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    { 
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    { 
X        close (0);
X        dup (ptc[READ]);
X        close (1);
X        dup (ctp[WRITE]);
X
X        putenv ("TERMCAP", ROBOTTERM);
X        putenv (OPTVAR,  OPTIONS);
X        execl (rfile, rfile, 0);
X        _exit (1);
X    }
X
X    /* Call Robomatic as the Parent Process */
X    else
X    { 
X        robomatic (); 
X    }
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{ 
X    int cmd;
X
X    /* Initialize the Curses package */
X    initscr (); 
X    crmode (); 
X    noecho (); 
X    clear (); 
X    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        { 
X            switch (getchar ())
X            { 
X            case 'd':    
X                debug++; 
X                break;
X            case 'r':    
X                clear (); 
X                refresh (); 
X                drawscreen (); 
X                refresh (); 
X                break;
X            default:    
X                break;
X            }
X        }
X
X        /* Choose a command and send it */
X        cmd = strategy ();
X        if (cmd == 0) { 
X            debug++; 
X            dwait ("command is zero"); 
X        }
X	refresh(); /* in case strategy() is printing debug stuff */
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    if (!noinp) getchar ();
X
X    /* Print termination messages */
X    move (ROWS-1, 0); 
X    clrtoeol (); 
X    refresh ();
X    endwin (); 
X    nocrmode (); 
X    noraw (); 
X    echo ();
X
X    deadrobot ();
X
X    exit (0);
X}
X
X/****************************************************************
X * blankscreen: Fill the screen array with blanks
X ****************************************************************/
X
Xblankscreen ()
X{ 
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{ 
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        { 
X            ch = EOF; 
X            if (trace) fclose (trace); 
X        }
X        else
X        { 
X            ch = buf[0]; 
X            if (trace) { 
X                fputc (ch, trace); 
X                fflush (trace); 
X            } 
X        }
X
X        /* Check for the words "robot food" to see if we died */
X        if (ch == *d) { 
X            if (0 == *++d) { 
X                done++; 
X                playing=0; 
X            } 
X        }
X        else d = "robot food";
X
X        /* Check for the words "scrap heaps" to see if we survived a level */
X        if (ch == *h) { 
X            if (0 == *++h) { 
X                send (';'); 
X            } 
X        }
X        else h = "scrap heaps";
X
X        /* Now figure out what the character means */
X        switch (ch)
X        { 
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            { 
X                deadrobot (); 
X            }
X            else
X            { 
X                row = buf[0] - ' ';
X                col = buf[1] - ' ';
X                if (trace) { 
X                    fprintf (trace, "%c%c", buf[0], buf[1]);
X                    fflush (trace); 
X                }
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            { 
X                fprintf (stderr, "Unknown character '\\%o'\n", ch);
X                kill (child, 9);
X                exit (1);
X            }
X
X            /* Printing character */
X            screen[row][col++] = ch;
X            if (ch == 'I')
X            {  
X                atrow = row; 
X                atcol = col; 
X            }
X            mvaddch (row, col, ch);
X            break;
X        }
X    }
X
X    drawscreen ();
X    move (row, col); 
X    refresh ();
X}
X
Xpstatus(s) char *s;
X{
X    wmove(stdscr, 23, 5);
X    waddstr(stdscr, s);
X    refresh();
X}
X
Xchar pstatbuf[100];
X
X/****************************************************************
X * deadrobot: We died, read our score and print it out
X ****************************************************************/
X
XReadStats(next, curr, level, score, heaps, robots)
Xint *next, *curr, *level, *score, *heaps, *robots;
X{
X    char c;
X    sscanf(screen[ROWS-1], "<%d%c", next, &c);
X    switch(c) {
X    case '>':
X        sscanf(screen[ROWS-1], "<%d> level: %d score: %d heaps: %d robots: %d",
X        next,level,score,heaps,robots);
X        *curr = 0;
X        break;
X    case '+':
X        sscanf(screen[ROWS-1], "<%d+%d> level: %d score: %d heaps: %d robots: %d",
X        next,curr,level,score,heaps,robots);
X        break;
X    default:
X	break;
X    }
X}
X
Xdeadrobot ()
X{ 
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    if (trace) fprintf(trace, "\nDied.\n");
X}
X
X/****************************************************************
X * send: Send a command to the Robots process
X ****************************************************************/
X
Xsend (ch)
Xint ch;
X{ 
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{ 
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 * 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{ 
X    long n;
X    int retc;
X
X    if (noinp)
X	return(0);
X
X    if (retc = ioctl (READ, FIONREAD, &n))
X    { 
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{ 
X    char buf[BUFSIZ];
X    register int c;
X
X    if (trace) {
X	putc('\n', trace);
X	fprintf(trace,f,a1,a2,a3,a4);
X	}
X
X    if (debug)
X    { 
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        { 
X        case 'd':    
X            debug=0; 
X            break;
X        case 'r': 
X            clear (); 
X            refresh (); 
X            drawscreen (); 
X            refresh (); 
X            break;
X        default:    
X            break;
X        }
X        for (c=0; c<COLS; c++)
X        { 
X            mvaddch (0, c, screen[0][c]); 
X        }
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{ 
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{ 
X    register char *namechar, *envchar;
X    register int  i, found;
X
X    found = 0;
X    for (i = 0; environ[i] && !found; i++)
X    { 
X        envchar = environ[i];
X        namechar = name;
X        while (*namechar && (*namechar == *envchar))
X        { 
X            namechar++;
X            envchar++;
X        }
X        found = (*namechar == '\0' && *envchar == '=');
X    }
X    return (found ? i - 1 : -1);
X}
X
Xstatic int  newenv ()
X{ 
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    { 
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{ 
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}
X
X/****************************************************************
X * strategy: Completely reworked, but still heuristic.  Designed to
X * work well with large numbers of robots.  No look ahead; occasionally
X * does stupid things.
X *
X * Generate an array of things within 7 squares of you.
X *
X * For each possible move, work out whether you will be eaten, or
X * eaten on the move after (we have enough information for this),
X * how much more shadow you get from the move, how many nearby robots
X * are destroyed.
X ****************************************************************/
X
X/* Arrays for finding directions and keys to go that way */
Xint deltar[] = {
X    -1, -1, -1,  0,  0,  0,  1,  1,  1};
Xint deltac[] = {
X    -1,  0,  1, -1,  0,  1, -1,  0,  1};
Xchar *keydir = "ykuh.lbjn";
X
X#define DOT_POSN 4    /* counting from 0, of course */
X
XMoveDir(row, col, dir)
Xint *row, *col; 
Xchar dir;
X{
X    int j = 0;
X
X    while (keydir[j] && keydir[j] != dir)
X        j++;
X
X    *row += deltar[j];
X    *col += deltac[j];
X}
X
Xtypedef struct affrobot { /* affected robot struct */
X    char r, c, type, dist;
X} 
Xaffrobot;
X
X/*
X * Nearby robot list, and routine to fill it
X */
X
Xaffrobot CloseRobots[MAXDIST*MAXDIST*4];
X
Xint NumCloseRobots = 0;
X
X#define IsOnScreen(r,c) (((r) < ROWS) && ((r) >= 0) && ((c) < COLS) && \
X((c) >= 0))
X
X/*
X * Structure representing the consequences of various moves.
X * One per move.
X */
X
Xtypedef struct consequences {
X    char dir;
X    char newr; 
X    char newc;
X    char dead;    /* 0 or 1 */
X    char aok;    /* true if an 'a' command is permissable */
X    char followup;    /* can we escape on the subsequent move? */
X    int threat;    /* how threatend are we here - relative int value */
X    int localshadow; /* shadow defined @'s in local square after move */
X}
Xconsequences;
X
Xconsequences moves[9];    /* one for each move */
X
X/*
X * Temp area to work out new local screen configuration after a move.
X * Scrapheaps only; robots are added after they move.
X */
X
Xchar tempscreen[MAXDIST*2+1][MAXDIST*2+1];
X
Xtracetempscreen() /* print it on the trace */
X{
X    int r, c, m=MAXDIST*2+1 ;
X
Xfor (r=(-1); r<=m; r++) {
X	putc('\n', trace);
X	for (c=(-1); c<=m; c++)
X		putc(((r<0 || r==m || c<0 ||
X		    c==m)?'+':(r==MAXDIST&&c==MAXDIST)?'I':tempscreen[r][c]), trace);
X}
X}
X
Xtracescreen() /* print the whole screen on the trace*/
X{
X    int r = (-1); int c=0;
X
X    return;
X
X    while (++r < ROWS) {
X	putc('\n', trace);
X	for (c=0; c<COLS; c++)
X	    putc(screen[r][c], trace);
X	}
X}
X
X/*
X * Routine to build a small shadow screen around the current coordinates
X * showing the location of scrapheaps; also initialises the 'nearest
X * robots' array for the screen.  Does this by spiralling out around the
X * given centre location.  Returns 1 if there is a wall or bot at the
X * centre of the screen.
X */
X
XInitTempRC(atrow, atcol, movedir)
Xint atrow, atcol; 
Xchar movedir;
X{
Xint r = atrow; 
Xint c = atcol; 
Xint d=0; 
Xchar dir='l';
Xint dr, dc=(-1);
X
X/* run around user in a spiral, inserting any robots found into the
X * CloseRobots list and inserting heaps into the temp screen array */
X
XNumCloseRobots = 0;
X
Xwhile (d <= MAXDIST) {
X    tempscreen[r-atrow+MAXDIST][ c-atcol+MAXDIST] = ' ';
X    if (IsOnScreen(r,c)) {
X        switch(screen[r][c]) {
X        case '@':
X            if (d > 0) {
X                tempscreen[r-atrow+MAXDIST][c-atcol+MAXDIST] = '@';
X                break;
X            }
X            /* we are pushing an '@'.  This has a negative bonus. */
X            dr = r; 
X            dc = c;
X            MoveDir(&dr, &dc, movedir);
X            if (screen[dr][dc] != ' ')
X                return(1);    /* cant push it */
X            /* else dr, dc have coords; we place it at end */
X            break;
X        case '=':
X            tempscreen[r-atrow+MAXDIST][c-atcol+MAXDIST] = '.';
X	    goto jjj;
X        case '#':
X            tempscreen[r-atrow+MAXDIST][c-atcol+MAXDIST] = 'o';
X	jjj:
X            if (d == 0) return(1);
X            CloseRobots[NumCloseRobots].type = screen[r][c];
X            CloseRobots[NumCloseRobots].r = (char)r;
X            CloseRobots[NumCloseRobots].c = (char)c;
X            CloseRobots[NumCloseRobots].dist = (char)d;
X            NumCloseRobots++;
X            break;
X        case '|':
X        case '-':
X            if (r == atrow && c == atcol) return(1);
X            tempscreen[r-atrow+MAXDIST][ c-atcol+MAXDIST] = '-';
X            break;
X        default:
X            break;
X        }
X    }
X
X    if (abs(r - atrow) == abs(c - atcol)) /* at a corner */
X        switch(dir) {
X    case 'j': 
X        dir = 'h'; 
X        break;
X    case 'h': 
X        dir = 'k'; 
X        break;
X    case 'k': 
X        dir = 'l'; 
X        break;
X    case 'l': 
X        dir = 'j'; 
X        c++; 
X        d++; 
X        goto skipmv;
X    }
X    MoveDir(&r, &c, dir);
X    skipmv: ;
X}
Xif (trace) {
X    fprintf(trace, "\nChecking direction %c; initial graph:\n",movedir);
X    tracetempscreen();
X    }
Xif (dc > (-1)) { /* lose bonus for the push! */
X    tempscreen[dr-atrow+MAXDIST][ dc-atcol+MAXDIST] = '@';
X    return(2);	/* spec code indicating a pushed heap */
X}
X
Xreturn(0);
X}
X
X/*
X * Move the robots in the 'nearby' list towards a particular location,
X * using the temp screen to detect collisions.  Leave the final locations
X * of all the scrapheaps and robots on the screen.  Set the collision
X * flag if you died.  Modifies the 'nearby' list, and the screen.
X * Returns 1 if you died, but aok is set if an 'a' command there would
X * result in your being OK (caller only takes notice of 'a' for '.' cmds).
X * Dir is in case we are attempting to move onto a scrap heap.
X */
X
X#define NormMove(from, towards)        \
X((towards > from)? (from + 1) : (towards == from) ? from : \
X        (from - 1))
X
XTryTempMove(cons, r, c, dir)
Xconsequences *cons;
Xint r, c; 
Xchar dir;
X{
Xint r2 = 0, c2 = 0;
Xint tr, tc, rnum;
Xint speed = 1;
Xint WasHit = 0;
Xint pushed=0;
X
Xcons->dead = (char) 0;
Xcons->aok = 1;
Xcons->threat = 0;
X
Xdwait("Analysing move %c", dir);
X
Xif ((pushed = InitTempRC(r, c, dir))==1) {
X    cons->dead = 1; 
X    cons->aok=0; 
X    return(1);    /* if you are on heap or bot */
X}
X
Xif (pushed) cons->threat -= PUSHED_PENALTY;
X
X/* Move all robots once, then the fast ones one
X       more.  Return dead if there are any robots 1 square away; aok=0 if
X       any fast robots hit on their second move.
X       */
X
Xwhile (speed < 3) {
X    rnum = (-1);
X    while (++rnum < NumCloseRobots) {
X        if (speed > 1 && CloseRobots[rnum].type == '=') continue;
X
X        r2 = (int)CloseRobots[rnum].r;
X        c2 = (int)CloseRobots[rnum].c;
X        if (tempscreen[r2 - r + MAXDIST][c2 - c + MAXDIST] == '@') {
X	    if (trace) fprintf(trace,
X		"\nRobot (type %c) at (%d,%d) (dist %d) destroyed from behind",
X		CloseRobots[rnum].type,
X		r2, c2, (int)CloseRobots[rnum].dist
X		);
X            cons->threat += DEATH_BONUS((int)CloseRobots[rnum].dist)*
X		(CloseRobots[rnum].type == '='? 1 : FASTBOT_FACTOR);
X            continue; /* robot died */
X        }
X        tempscreen[r2 - r + MAXDIST][c2 - c + MAXDIST] = ' ';    /* remove it so others dont hit it */
X        r2 = NormMove(r2, r);
X        c2 = NormMove(c2, c);
X        CloseRobots[rnum].r = (char) r2;
X        CloseRobots[rnum].c = (char) c2;
X	    if (trace) fprintf(trace,
X		"\nRobot (type %c) now at (%d,%d)",
X		CloseRobots[rnum].type,
X		r2, c2
X		);
X        if (r == r2 && c == c2) {
X	    if (trace) fprintf(trace, ", killing you");
X            cons->dead = 1;
X            if (speed > 1) { 
X                cons->aok = 0; 
X                return(1); 
X            }
X        }
X	tr = r2 - r + MAXDIST;
X	tc = c2 - c + MAXDIST;
X        /* check for collisions, etc */
X        switch (tempscreen[tr][ tc]) {
X        case '=':
X        case '#':    /* in-transit fast robot */
X        case '@':
X	    if (trace)  {fprintf(trace,
X		", crashing into a '%c'! - destroyed",
X		tempscreen[tr][tc]);
X		}
X            cons->threat += (CloseRobots[rnum].type == '='? 1 :
X            FASTBOT_FACTOR) * DEATH_BONUS((char)CloseRobots[rnum].dist);
X            tempscreen[tr][tc] = '@';
X            break;
X        case ' ':
X            tempscreen[tr][ tc] = CloseRobots[rnum].type;
X            break;
X        case '-': 
X	case '.':
X	case 'o':
X            break;
X        default:
X	    break;
X        }
X    } /* robot loop */
X    if (trace) {
X	fprintf(trace, "\nPass %d complete", speed);
X	tracetempscreen();
X	}
X    speed++;
X}
X
Xreturn (int)cons->dead;
X}
X
X/*
X * List of consequences
X */
X
Xconsequences conslist[9]; /* 9 directions */
X
X/*
X * vars for various stats
X */
X
Xint AddTel, CurrTel, CurrLevel, Score, NumHeaps, NumBots;
Xchar TeleOrAok(), TeleOnly();
X
Xstrategy ()
X{ 
Xint dirnum;
Xint numOKdirs = 0;
Xint r, c; /* where to move to */
Xint Threatened; 
Xchar dir;
Xint cp = 0;
X
XReadStats(&AddTel, &CurrTel, &CurrLevel, &Score, &NumHeaps, &NumBots);
X
Xif (trace)
X    tracescreen();
X
X/*  For each possible move, work out the consequences */
X
Xfor (dirnum=0; keydir[dirnum]; dirnum++) {
X    r = atrow; 
X    c = atcol;
X    MoveDir(&r, &c, keydir[dirnum]);
X    if (!TryTempMove(&conslist[dirnum], r, c-1, keydir[dirnum])) {
X	if (trace)
X	    fprintf(trace,"\nDirection %c OK", keydir[dirnum]);
X        AnalyseNewPosn(&conslist[dirnum], r, c);
X        numOKdirs++;
X    }
X    else
X        conslist[dirnum].dead = 1;
X}
X
Xif (!numOKdirs)        /* nowhere to run! */
Xif (conslist[DOT_POSN].aok)
Xreturn(TeleOrAok(&conslist[DOT_POSN]));
Xelse return(TeleOnly(&conslist[DOT_POSN]));
X
X/*
X * Choose the best move, do it.
X */
X
XThreatened = 1e9; 
Xdir = 't';
X
Xfor (dirnum = 0; keydir[dirnum]; dirnum++)
X    if (!conslist[dirnum].dead && conslist[dirnum].threat <= Threatened)
X{ 
X    Threatened = conslist[dirnum].threat, dir = keydir[dirnum];
X}
X
X/* debugging : print current threat */
X{
X    char b[20];
X    sprintf(b, "%9d", Threatened);
X    move(0,0);
X    addstr(b);
X}
X
Xreturn(dir);
X}
X
X/*
X * TeleOrAOK : strategy not worked out yet, never uses 'a'.
X */
X
Xchar TeleOrAok(c)
Xconsequences *c;
X{
X/* debugging : print current threat */
X{
X    char b[20];
X    sprintf(b, "%9d", c->threat);
X    move(0,0);
X    addstr(b);
X}
X    if (c->threat < AOK_THREAT && AddTel + CurrTel > 0)
X	return 'a';
X    else
X	return TeleOnly(c);
X}
X
Xchar TeleOnly(c)
Xconsequences *c;
X{
X    int odds;
X    if (CurrLevel < 6 || AddTel + CurrTel == 0)
X        return 'r';
X    odds = 10000 * NumBots / 20 / 80 * 13; /* conservative */
X
X    if (odds / CurrLevel < 100)
X	return 'r';
X    else
X	return 't';
X}
X/*
X * Look at the temp screen and robots lists, working out which
X * ones threaten you, by how much, and how much shadow you have around you.
X * Shadow is tested for the local screen but cones are computed wrt the whole
X * screen.
X *
X * Weightings:
X *    Shadow: +1 for each square which threatens us, to a max radius of
X *       SHADOWRADIUS (squares beyond this are too distant in time)
X *     Also count a 1/2 width cone for those @'s 2 squares away.
X *    SlowBots: 2 * MAX_RADIUS / (MAX_RADIUS - distance away).
X *    FastBots: 8 * MAX_RADIUS / (MAX_RADIUS - distance away).
X */
X
XAnalyseNewPosn(cons, r, c)
Xconsequences *cons;
Xint r, c;
X{
Xint r2, c2, shadow, s, robots;
Xint roctinfo[8];	/* robots in which directions? */
X
Xif (cons->dead != (char )0) {
X    return;
X    }
X
X/* cons->threat currently contains (positive) points for each robot that died */
X
Xcons->threat *= (-1);
X
X/* we will slightly favour going towards the centre of the screen - this
X   behaviour should tend to amass junkheaps there */
X
Xif (trace) fprintf(trace, "\nRobot death and push points: %d", cons->threat);
X
Xr2 = sgn(abs(r-ROWS/2) - abs(atrow-ROWS/2));
Xcons->threat += r2*TO_CENTRE;
Xc2 = sgn(abs(c-COLS/2) - abs(atcol-COLS/2));
Xcons->threat += c2*TO_CENTRE;
Xif (r2 == 0 && c2 == 0) /* discourage pointless oscillation */
X	cons->threat --;
X
X/* Now we examine the surrounding area.  The most important reason is to
X * add in a factor corresponding to how much trash we have to play with.
X * We don't examine robots - this is the simple version, we only use the
X * death bonus for this.
X */
X
Xrobots = shadow = 0;
X
Xfor (r2 = 0; r2 <= MAXDIST*2; r2++)
X    for (c2 = 0; c2 <= MAXDIST*2; c2++) {
X	int dist = max(abs(r2-MAXDIST),abs(c2-MAXDIST));
X
X	switch (tempscreen[r2][c2]) {
X	case '@':
X	    shadow += HEAP_NEARNESS(dist); /* add heap proximity bonus */
X	    if (r2 == MAXDIST || c2 == MAXDIST)
X		shadow += 3*HEAP_NEARNESS(dist);
X	    break;
X	case '=':	/* slowbot */
X	    robots += R_NEAR(dist);
X	    break;
X	case '#':	/* fastbot */
X	    robots += FASTBOT_NEAR(dist);
X	    break;
X	default:
X	    break;
X	    }
X	}
X
Xif (trace) fprintf(trace, "\nShadow points: %d\nRobot proximity: %d",
X			shadow, robots);
Xcons->threat -= shadow;
X/* if (shadow > MAXDIST*2) */
X	cons->threat += robots; /* away from bots if we have a
X			* good junkpile to head for, otherwise, go get 'em */
X
Xif (trace) {
X    fprintf(trace, "\nDanger factor is %d\n", cons->threat);
X    }
X
Xreturn;
X}
END_OF_FILE
if test 32070 -ne `wc -c <'R2mat.c'`; then
    echo shar: \"'R2mat.c'\" unpacked with wrong size!
fi
# end of 'R2mat.c'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have the archive.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0