/* Program: LIFE Author : Kim Moser Date : 08/88 Descrip: IBM PC implementation of John Conway's LIFE simulation. Note: comments beginning with '@@@' indicate equivalent Modula-2 code. Requires IBM PC or compatible with color card. */ #include /************************************************************************** Screen I/O **************************************************************************/ /* Used exclusively by function writeat(): */ #define SCREEN 0x0B8000000 /* Assumes color card; change to 0x0B000 if using monochrome */ #define WIDTH 80 #define HEIGHT 25 /* Width and height of screen [in characters]; may be changed if video card supports more rows and/or columns */ /* Defined types */ typedef char scrnbyte; /* Can be treated as an unsigned short int */ /* @@@ scrnbyte = CHAR; */ typedef struct scrnchar { scrnbyte ch, at; /* Character and its attribute [on screen] */ }; /* @@@ scrnchar = RECORD ch, at: scrnbyte; END; */ /* Color definitions [assumed to be unsigned integers] */ #define BLACK 0 #define BLUE 1 #define GREEN 2 #define CYAN 3 #define RED 4 #define MAGENTA 5 #define BROWN 6 #define LIGHTGREY 7 #define DARKGREY 8 #define LIGHTBLYE 9 #define LIGHTGREEN 10 #define LIGHTCYAN 11 #define LIGHTRED 12 #define LIGHTMAGENTA 13 #define YELLOW 14 #define BRIGHTWHITE 15 /* Don't mess with these: */ #define BITSINCELL 2 /* Each cell takes 2 bits */ #define CELLSINBLOCK 4 /* 4 cells in each CellBlock */ #define BITSINBLOCK 8 /* Each CellBlock takes 8 bits */ #define ANDMASK 3 /* 00000011: When ANDed [bit-wise] with a CellBlock, will yield value of rightmost cell] */ #define DEAD 0 #define LIVE 1 #define WALL 2 /* You may change these defaults: */ #define FIELDWIDTH 70 #define FIELDHEIGHT 70 /* Other vars: */ /* You may change these defaults: */ static char livech = '*'; /* Screen character to represent live cells */ static scrnbyte livefg = BRIGHTWHITE; /* Live cell foreground color */ static scrnbyte livebg = BLACK; /* Live dell background color */ static char deadch = '.'; /* Screen character to represent dead cells */ static scrnbyte deadfg = BLUE; /* Dead cell foreground color */ static scrnbyte deadbg = BLACK; /* Dead cell background color */ static char wallch = 'W'; /* Screen character to represent walls */ static scrnbyte wallfg = GREEN; /* Wall foreground color */ static scrnbyte wallbg = BLUE; /* Wall background color */ static char borderch = 'B'; /* Screen character to represent out-of-field */ static scrnbyte borderfg = RED; static scrnbyte borderbg = YELLOW; /* Cell types: */ typedef unsigned short int cell; typedef struct cellblock { /* These fields MUST fit evenly into the record, and must end on a byte boundary: */ cell cell1, cell2, cell3, cell4 : BITSINCELL; }; typedef struct cellblock field [FIELDHEIGHT] [(FIELDWIDTH/CELLSINBLOCK)+1]; /* @@@ field = ARRAY [0..(FIELDHEIGHT-1)] [0..((FIELDWIDTH-1) DIV CELLSINBLOCK)+1] OF cellblock */ /* Vars for this 'module' */ static scrnbyte fgd = BRIGHTWHITE; /* Default foreground color */ static scrnbyte bgd = BLACK; /* Default background color */ static scrnbyte att = BRIGHTWHITE; /* Create default attr byte */ /* Functions for this 'module': */ setcolor( f, b ) /* Creates attribute byte 'att' from 'fgd' and 'bgd' colors */ int f, b; { /* Are we setting colors that have already been set? */ if ( (fgd == f) || (bgd == b) ) return; /* Set foreground and background colors: */ fgd = f; bgd = b; /* Make attribute byte: */ att = (bgd << 4) + fgd; } writeat( x, y, s ) /* Write string 's' at screen location 'x','y' (1,1 = top left) */ int x,y; char *s; { int i; int c; struct scrnchar *scrn; /* Points to char/attribute */ /* @@@ scrn = POINTER TO scrnchar; */ scrn = (struct scrnchar far *) (SCREEN + xytoadr(x,y)); /* Must be FAR because screen memory is in a different data segment */ /* @@@ scrn = VAL( scrnchar, SCREEN + xytoadr(x,y) ); */ for ( i=0; s[i] != '\0'; i++ ) { scrn->ch = s[i]; /* Write character on screen */ (scrn++)->at = att; /* Write attribute and point to next char on screen */ } } int xytoadr( x, y ) int x, y; /* Returns x,y (screen coords) converted to offset from top left corner, according to screen width and height #defined */ { return ( (y-1)*WIDTH*sizeof(struct scrnchar) + (x-1)*sizeof(struct scrnchar) ); } /*************************************************************************/ cell cellat( f, x, y ) /* Depending on value of cell at (x,y) [offset from (0,0)], returns either LIVE if alive, DEAD if dead, or WALL if wall. */ field f; int x, y; { struct cellblock cb; cb = (f[y*CELLSINBLOCK*BITSINCELL][x/CELLSINBLOCK]); /* 'cb' now contains CellBlock which contains cell we want */ switch(x%CELLSINBLOCK) { case 0: return (cell) cb.cell1; case 1: return (cell) cb.cell2; case 2: return (cell) cb.cell3; case 3: return (cell) cb.cell4; default: printf( "cellat(): internal error: invalid field requested: %d", x ); } /* i = (unsigned short int) c; 'i' now contains CellBlock containing cell we want -- but with the advantage that it can be manipulated as an "unsigned short int" Right-justify bit-field representing cell we want: (i >>= (BITSINBLOCK-(((x%CELLSINBLOCK)+1)*BITSINCELL))); Isolate bit-field representing cell we want: i &= ANDMASK; If this value is neither LIVE, nor DEAD, nor WALL, then something is very wrong. */ } setcellat( f, x, y, c ) /* Sets cell in field 'f' at coordinates (x,y) [offset from (0,0)] to 'ce' [which had better be LIVE, DEAD, or WALL] */ field f; int x, y; cell c; { struct cellblock *cb; /* @@@ cb = POINTER TO cellblock */ if ( (c != DEAD) && (c != LIVE) && (c != WALL) ) { /* This IF clause can be removed since all client procedures check parameters for proper values before calling setcellat(). */ printf( "setcellat(): internal error: invalid cell value passed: %d", c ); } cb = &(f[y*CELLSINBLOCK*BITSINCELL][x/CELLSINBLOCK]); /* 'cb' now points to CellBlock which contains cell we want */ switch(x%CELLSINBLOCK) { case 0: cb->cell1 = c; break; case 1: cb->cell2 = c; break; case 2: cb->cell3 = c; break; case 3: cb->cell4 = c; break; default: printf( "setcellat(): internal error: invalid field requested: %d", x ); } } clearfield( f ) /* Clears field 'f' [i.e. sets all cells to DEAD] */ field f; { int x, y; /* Indexes into 'f' */ for (y=0; y= FIELDWIDTH) { ch = borderch; fore = borderfg; back = borderbg; /* printf( "showfield(): internal error: invalid cell returned by cellat()." ); */ break; } else { switch (cellat(f,a,b)) { case LIVE: ch = livech; fore = livefg; back = livebg; break; case DEAD: ch = deadch; fore = deadfg; back = deadbg; break; case WALL: ch = wallch; fore = wallfg; back = wallbg; break; default: ch = borderch; fore = borderfg; back = borderbg; /* printf( "showfield(): internal error: invalid cell returned by cellat()." ); */ break; } } setcolor( fore, back ); s[0] = ch; s[1] = '\0'; writeat( i, j, s ); a++; /* Point to next column of cells */ } printf( "%d", j ); } } field mainfield; main() { clearfield( mainfield ); showfield( mainfield, 0, 0, 1, 1, 70, 10 ); }