[comp.sources.atari.st] v01i096: leolife -- Game of Life

koreth@ssyx.ucsc.edu (Steven Grimm) (01/21/89)

Submitted-by: leo@philmds.dts.philips.nl (Leo de Wit)
Posting-number: Volume 1, Issue 96
Archive-name: leolife

[The binaries have been posted to the binaries group. -sg]

: This is a shar archive.  Extract with sh, not csh.
: This archive ends with exit, so do not worry about trailing junk.
echo 'Extracting life.c'
sed 's/^X//' > life.c << '+ END-OF-FILE life.c'
X/******************************************************************************
X *                                                                            *
X *                     COPYRIGHT (C) 1988    L.J.M. de Wit                    *
X *                     ALL RIGHTS RESERVED                                    *
X *                                                                            *
X *                     life.c   version 1.0 of 11 Nov 1988                    *
X *                                                                            *
X * This software may be used and distributed freely if not used commercially  *
X * and the originator (me) is mentioned.                                      *
X *                                                                            *
X ******************************************************************************
X *
X * NAME
X *    life   - the game of life (graphics demo if you want)
X *
X * SYNTAX
X *    life.ttp [-d|-D] [-w wait] [01-pattern ...]
X *
X * DESCRIPTION
X *    This program barely needs comments. 'Generations' are created on a grid,
X *    on a step by step basis. The criterium used for a cell to survive
X *    (resp. die), is that it has between BOT (3) and TOP (4) neighbours
X *    (resp. has not).
X *
X *    The program runs only in high and medium resolution modes (so everyone
X *    should be able to run it); not supporting low res is entirely due to
X *    laziness on my part 8-), but since no colours are involved (yet) it
X *    seemed an unnecessary addition.
X *
X *    For both resolutions supported you can choose a coarse grid (25 x 40)
X *    and a fine one (50 x 80); default is coarse, and the -d or -D (double)
X *    option selects the fine grid.
X *
X *    The -w or -W option lets you specify an amount of time to wait between
X *    generations (default is nowait).
X *
X *    Any remaining arguments are taken to be the initial patterns to be placed
X *    on the grid, a '0' representing an empty cell, a '1' a filled one. These
X *    patterns are placed on subsequent rows, near the middle of the screen.
X *    Some pattern sets generate repetitive populations, other die away, still
X *    other generate nice ever-changing patterns. Experiment. Symmetric sets
X *    are (possibly) favorite; this means using symmetric patterns, with the
X *    first matching the last, the second the last but one, etc., and using an
X *    odd number of patterns in the coarse grid case and an even number of
X *    patterns in the fine grid case. You could alternative use point symmetry
X *    instead of line symmetry; figure out for yourself how to arrange this.
X *    If there are no remaining arguments after any flags, a random pattern is
X *    generated.
X *
X *    To finish the game, 'press any key ...'
X *
X * EXAMPLE
X *    life -d -w 20 011110 110011 101101 001100 101101 110011 011110
X *
X *    will have a fine grid, a wait pause of 20, and an initial pattern set
X *    that looks like:
X *                                  ****
X *                                 **  **
X *                                 * ** *
X *                                   **
X *                                 * ** *
X *                                 **  **
X *                                  ****
X * DECISIONS
X *    Screen access is done directly, for speed reasons (we must be able to
X *    print several hundreds of characters, several times per second, if we
X *    want screen updates to appear - almost - immediate).
X *    On the other hand, C proved to be quite fast enough, so there was no
X *    need to resort to assembler.
X *    Each resolution and each mode (coarse/fine) demands it own treatment,
X *    so four separate (functional identical) functions were written, one
X *    for each of them.
X *
X *    By using a private startup routine (not included) instead of the
X *    compiler supplied one, the size of the program could be kept
X *    considerably smaller (not using stdio).
X */
X
X#include <osbind.h>
X
X#define FALSE 0
X#define TRUE  1
X
X#define CPUTC(c) bios(3,2,c)              /* Write char c to console */
X#define SC_24 0xffffff                    /* Mask 24 bits */
X#define BOT 3                             /* Min. # of neighbours to survive */
X#define TOP 4                             /* Max. # of neighbours to survive */
X
X#define B_P_ROW  1280                     /* Bytes per screen row */
X#define B_P_DROW  640                     /* Bytes per half screen row */
X#define W_P_ROW   640                     /* Words per screen row */
X#define W_P_DROW  320                     /* Words per half screen row */
X#define L_P_ROW   320                     /* Longs per screen row */
X#define L_P_DROW  160                     /* Longs per half screen row */
X
X#define B_P_HLIN   80                     /* Bytes per hires line */
X#define W_P_HLIN   40                     /* Words per hires line */
X#define L_P_MLIN   40                     /* Longs per meres line */
X
X#define STDROWS    25                     /* Default # of grid rows */
X#define STDCOLS    40                     /* Default # of grid columns */
X#define MAXARRSIZ (STDROWS*STDCOLS*4)     /* Max.gridsize for either gridtype */
X
X#define BROW(bp,r) (*(bp) + Cols * (r))   /* Start of row r in block *bp   */
X
X/* create some unsigned types */
Xtypedef unsigned char  uchar;
Xtypedef unsigned short ushort;
Xtypedef unsigned long  ulong;
X
Xtypedef uchar grid[MAXARRSIZ];            /* A 'grid of cells' type */
X
Xstatic grid *oldp, *newp;                 /* Pointers to old and new grid */
X
Xstatic short Rows;                        /* Actual # of rows in grid */
Xstatic short Cols;                        /* Actual # of columns in grid */
X
Xstatic ulong *start;                      /* Points to logical screen start */
X
Xint _mneed = 60000;                       /* For Lattice C: restricts the   */
X                                          /* memory used by the program, so */
X                                          /* that Mallocs will find enough  */
X                                          /* memory left.                   */
X
Xstatic ushort dsized = FALSE;             /* Boolean: fine grid used? */
X
Xstatic void init_grid(),                  /* Initiate one grid        */
X            calc_grid(),                  /* Calculate next grid      */
X            show_s1grid(),                /* Show coarse grid med res */
X            show_d1grid(),                /* Show fine grid med res   */
X            show_s2grid(),                /* Show coarse grid hi res  */
X            show_d2grid();                /* Show fine grid hi res    */
X
Xmain(argc,argv)
Xint argc;
Xchar **argv;
X{
X   short cntr = 0;                        /* # of generations                */
X   grid *swap;                            /* Temporary to change grid ptrs   */
X   register ulong *upn, *upo, uv;         /* For clearing grids initially    */
X   register short cnt;                    /* Counting clear bytes            */
X   long i, waittime = 0;                  /* Counting wait loop              */
X   void (*showfunc)();                    /* The display function being used */
X   static void (*sfuncs[])() = {          /* Possible display functions      */
X      (void (*)())0,(void (*)())0,        /* Low res not implemented         */
X      show_s1grid, show_d1grid,           /* Med res coarse and fine         */
X      show_s2grid, show_d2grid            /* Hi res coarse and fine          */
X   };
X
X   start   = (ulong *)Logbase();          /* Cur. log. screen will be used   */
X   for (--argc, argv++;
X           argc > 0 && **argv == '-';
X           --argc, argv++) {              /* Handle flag arguments           */
X      if (argv[0][1] == 'd' || argv[0][1] == 'D') {   /* Double, i.e. fine   */
X         dsized = TRUE;
X      } else if (argv[0][1] == 'w' || argv[0][1] == 'W') {  /* Wait count    */
X         if (--argc <= 0) {
X            Cconws("life: usage: life.ttp [-d|-D] [-w|-W wait] [01-pattern...]\r\n");
X            Crawcin();
X            Pterm(1);
X         }
X         waittime = atoi(*++argv);
X         if (waittime < 0 || waittime > 10000) {
X            Cconws("life: wait must be between 0 and 10000\r\n");
X            Crawcin();
X            Pterm(1);
X         }
X      } else {                            /* Illegal flag                    */
X         Cconws("life: usage: life.ttp [-d|-D] [-w|-W wait] [01-pattern...]\r\n");
X         Crawcin();
X         Pterm(1);
X      }
X   }
X
X   showfunc = sfuncs[2 * Getrez() + dsized]; /* Select display function      */
X   if (showfunc == (void (*)())0) {       /* Low resolution not provided for */
X      Cconws("life: must be medium/high resolution, sorry ...\r\n");
X      Crawcin();
X      Pterm(1);
X   }
X
X   Cconws("\33E\33f\33Y,1created 1988       * LIFE *       by Leo de Wit");
X   for (i = 1 << 18; --i >= 0; ) ;        /* Wait some time                  */
X   CPUTC('\33'); CPUTC('l');              /* Clear current line              */
X
X   if (dsized) {                          /* Fine grid chosen ...            */
X      Rows = STDROWS * 2;                 /* Twice as many rows ...          */
X      Cols = STDCOLS * 2;                 /* Twice as many cols ...          */
X   } else {                               /* Coarse grid chosen              */
X      Rows = STDROWS;                     /* Std. # of rows                  */
X      Cols = STDCOLS;                     /* Std. # of cols                  */
X   }
X
X   newp = (grid *)Malloc(sizeof(grid));   /* Allocate one grid               */
X   oldp = (grid *)Malloc(sizeof(grid));   /* And another                     */
X   if (newp == (grid *)-1 || oldp == (grid *)-1) {
X      Cconws("life: memory allocation failed\r\n");
X      Crawcin();
X      Pterm(1);
X   }
X   uv = 0;                                /* Now clear them both ...         */
X   upn = (ulong *)newp;
X   upo = (ulong *)oldp;
X   for (cnt = sizeof(grid)/sizeof(ulong); --cnt >= 0; ) {
X      *upn++ = *upo++ = uv;
X   }
X
X   init_grid(newp,argc,argv);             /* Initiate grid *newp             */
X   waittime <<= 10;                       /* Scale waittime reasonably       */
X
X   for (;;) {
X      (*showfunc)(newp,oldp);             /* Write to logical screen         */
X      if (((cntr++ & 0x7) == 0) && Bconstat(2)) { /* Test constat once in 8  */
X         break;
X      }
X      swap = newp; newp = oldp; oldp = swap; /* Exchange grid pointers       */
X      calc_grid(newp,oldp);               /* Calculate new grid from old     */
X      for (i = waittime; --i >= 0; ) ;    /* This is the wait loop           */
X   }
X
X   Bconin(2);                             /* Read key pressed from queue     */
X}
X
Xstatic void init_grid(bp,argc,argv)       /* Initiate grid *bp               */
Xgrid *bp;
Xint argc;
Xchar **argv;
X{
X   short len, maxlen = 0, j, i, r0, c0;
X
X   if (argc == 0) {                       /* No parameters: random pattern   */
X      short maxi = (Rows == STDROWS) ? 16 : 64; /* # of filled cells         */
X
X      for (i = 0; i < maxi; i++) {
X         short ranrow = Rows/3+((unsigned)Random())*Rows/(3*SC_24);
X         short rancol = Cols/3+((unsigned)Random())*Cols/(3*SC_24);
X
X         BROW(bp,ranrow)[rancol] = '\1';
X      }
X   } else {                               /* Pattern parameters              */
X      for (i = 0; i < argc; i++) {        /* Find longest string length      */
X         if ((len = strlen(argv[i])) > maxlen) {
X            maxlen = len;
X         }
X      }
X      r0 = (Rows - argc)/2;               /* Start row                       */
X      c0 = (Cols - maxlen)/2;             /* Start column                    */
X      for (i = 0; i < argc; i++) {        /* Handle each string              */
X         len = strlen(argv[i]);
X         for (j = 0; j < len; j++) {      /* Set each cell                   */
X            BROW(bp,r0+i)[c0+j] = argv[i][j] & 1;
X         }
X      }
X   }
X}
X
Xstatic void calc_grid(newp,oldp)          /* Calculate new grid from old     */
Xgrid *newp, *oldp;
X{
X   register short r, c;                   /* Current row, col                */
X   register uchar *cp,                    /* Ptr into new grid               */
X                  *ocp;                   /* Ptr into old grid               */
X
X   for (r = Rows; --r >= 0; ) {           /* Zero each row of grid           */
X      cp = BROW(newp,r+1);
X      for (c = Cols; --c >= 0; ) {        /* Zero each cell of row           */
X         *--cp = '\0';
X      }
X   }
X   for (r = Rows - 1; --r >= 1; ) {       /* Handle each row of old          */
X      ocp = BROW(oldp,r+1);
X      --ocp;
X      for (c = Cols - 1; --c >= 1; ) {    /* Handle each cell of old row     */
X         if (*--ocp) {                    /* If it was filled ...            */
X            cp = &BROW(newp,r-1)[c-1];    /* Incr. each neighbour's count    */
X            (*cp++)++; (*cp++)++; (*cp++)++; /* Previous row in new grid     */
X            cp += Cols - 3;
X            (*cp++)++; (*cp++)++; (*cp++)++; /* Same row in new grid         */
X            cp += Cols - 3;
X            (*cp++)++; (*cp++)++; (*cp++)++; /* Next row in new grid         */
X         }
X      }
X   }
X   for (r = Rows; --r >= 0; ) {           /* Handle each row of new grid     */
X      cp = BROW(newp,r+1);
X      for (c = Cols; --cp, --c >= 0; ) {  /* Handle each cell of row         */
X         *cp = (*cp >= BOT && *cp <= TOP);/* Set it according to accum. cnt  */
X      }
X   }
X}
X
Xstatic void show_s1grid(newp,oldp)        /* Display coarse grid med res     */
Xgrid *newp, *oldp;
X{
X   register short r, c;                   /* Current row & column            */
X   register ulong val;                    /* Value to fill with              */
X   register ulong step = L_P_MLIN;        /* Step to next screen line        */
X   register uchar *cp,                    /* Ptr into new grid               */
X                  *ocp;                   /* Ptr into old grid               */
X   register ulong *sa,                    /* Current screen address          */
X                  *basad;                 /* Screen address of end of row    */
X
X   basad = start + (Rows * L_P_ROW);
X   for (r = Rows; basad -= L_P_ROW, --r >= 0; ) {  /* Handle each row        */
X      cp = BROW(newp,r+1);
X      ocp = BROW(oldp,r+1);
X      for (c = Cols; --c >= 0; ) {        /* Handle each column              */
X         if (*--cp != *--ocp) {
X            val = -*cp;                   /* 0xffffffff : 0                  */
X            sa = basad + c;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val;
X         }
X      }
X   }
X}
X
Xstatic void show_d1grid(newp,oldp)        /* Display fine grid med res       */
Xgrid *newp, *oldp;
X{
X   register short r, c;                   /* Current row & column            */
X   register ulong val;                    /* Value to fill with              */
X   register ulong step = L_P_MLIN;        /* Step to next screen line        */
X   register ushort *sp,                   /* Ptr into new grid               */
X                  *osp;                   /* Ptr into old grid               */
X   register ulong *sa,                    /* Current screen address          */
X                  *basad;                 /* Screen address of end of row    */
X
X   basad = start + (Rows * L_P_DROW);
X   for (r = Rows; basad -= L_P_DROW, --r >= 0; ) {  /* Handle each row      */
X      sp = (ushort *)BROW(newp,r+1);
X      osp = (ushort *)BROW(oldp,r+1);
X      for (c = Cols/2; --c >= 0; ) {      /* Handle each column, two a time  */
X         if (*--sp != *--osp) {
X            static ulong values[] = {0, 0xff00ff, 0xff00ff00, 0xffffffff};
X
X            val = values[(*sp & 1) + (*sp >> 7)];
X            sa = basad + c;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val;
X         }
X      }
X   }
X}
X
Xstatic void show_s2grid(newp,oldp)        /* Display coarse grid hi res      */
Xgrid *newp, *oldp;
X{
X   register short r, c;                   /* Current row & column            */
X   register ushort val;                   /* Value to fill with              */
X   register ulong step = W_P_HLIN;        /* Step to next screen line        */
X   register uchar *cp,                    /* Ptr into new grid               */
X                  *ocp;                   /* Ptr into old grid               */
X   register ushort *sa,                   /* Current screen address          */
X                   *basad;                /* Screen address of end of row    */
X
X   basad = ((ushort *)start) + (Rows * W_P_ROW);
X   for (r = Rows; basad -= W_P_ROW, --r >= 0; ) {  /* Handle each row        */
X      cp = BROW(newp,r+1);
X      ocp = BROW(oldp,r+1);
X      for (c = Cols; --c >= 0; ) {        /* Handle each column              */
X         if (*--cp != *--ocp) {
X            val = -*cp;                   /* 0xffffffff : 0                  */
X            sa = basad + c;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val;
X         }
X      }
X   }
X}
X
Xstatic void show_d2grid(newp,oldp)        /* Display fine grid hi res        */
Xgrid *newp, *oldp;
X{
X   register short r, c;                   /* Current row & column            */
X   register uchar val;                    /* Value to fill with              */
X   register ulong step = B_P_HLIN;        /* Step to next screen line        */
X   register uchar *cp,                    /* Ptr into new grid               */
X                  *ocp;                   /* Ptr into old grid               */
X   register uchar *sa,                    /* Current screen address          */
X                  *basad;                 /* Screen address of end of row    */
X
X   basad = ((uchar *)start) + (Rows * B_P_DROW);
X   for (r = Rows; basad -= B_P_DROW, --r >= 0; ) {  /* Handle each row        */
X      cp = BROW(newp,r+1);
X      ocp = BROW(oldp,r+1);
X      for (c = Cols; --c >= 0; ) {        /* Handle each column              */
X         if (*--cp != *--ocp) {
X            val = -*cp;                   /* 0xff : 0                        */
X            sa = basad + c;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val; sa += step;
X            *sa = val;
X         }
X      }
X   }
X}
+ END-OF-FILE life.c
chmod 'u=rw,g=r,o=' 'life.c'
echo 'SENT: -rw-r-----  1 leo         19008 Jan 20 18:29 life.c'
echo -n 'RCVD: '
/bin/ls -l life.c
exit 0