/* 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 #define FALSE 0; #define TRUE 1; /************************************************************************** Screen I/O identifiers **************************************************************************/ #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 */ /* 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 #define UPKEY 72 #define DOWNKEY 80 #define LEFTKEY 75 #define RIGHTKEY 77 #define ESCKEY 27 #define F1KEY 59 #define F2KEY 60 #define F3KEY 61 #define SH_F1KEY 84 #define SH_F2KEY 85 #define SH_F3KEY 86 #define CTRL_F1KEY 94 #define CTRL_F2KEY 95 #define CTRL_F3KEY 96 /* Types for screen IO: */ typedef char scrnbyte; /* Can be considered an unsigned short int */ /* @@@ scrnbyte = CHAR; */ typedef struct scrnchar { scrnbyte ch, at; /* Character and its attribute [on screen] */ }; /* @@@ scrnchar = RECORD ch, at: scrnbyte; END; */ typedef unsigned int scrncoord; typedef int cellcoord; typedef unsigned short int direction; typedef unsigned short int color; /* Screen IO vars */ static scrnbyte fgd = BRIGHTWHITE; /* Default foreground color */ static scrnbyte bgd = BLACK; /* Default background color */ static scrnbyte att = BRIGHTWHITE; /* Create default attr byte */ /************************************************************************** LIFE identifiers **************************************************************************/ /* Definitions specific to LIFE: */ #define DEAD 0 #define LIVE 1 #define WALL 2 #define BORDER 3 /* Status: */ #define GROWING 0 #define EDITING 1 /* You may change these defaults: */ #define FIELDWIDTH 100 #define FIELDHEIGHT 100 /* Other vars: */ unsigned int status; unsigned int generation; static cellcoord cellX=0, cellY=0; /* Cell coordinates of editor cursor */ /* Status box coords [you may change these defaults]: */ static scrncoord statx1 = 1; static scrncoord staty1 = 22; static scrncoord statx2 = 80; static scrncoord staty2 = 25; /* Cell types: */ typedef unsigned short int cell; typedef struct cellblock { cell cell0; }; typedef struct cellblock field [FIELDHEIGHT] [FIELDWIDTH]; /* You may change these defaults: */ static char livech = '*'; /* Screen character to represent live cells */ static color livefg = BRIGHTWHITE; /* Live cell foreground color */ static color livebg = BLACK; /* Live dell background color */ static char deadch = '.'; /* Screen character to represent dead cells */ static color deadfg = BLUE; /* Dead cell foreground color */ static color deadbg = BLACK; /* Dead cell background color */ static char wallch = 'W'; /* Screen character to represent walls */ static color wallfg = GREEN; /* Wall foreground color */ static scrnbyte wallbg = BLUE; /* Wall background color */ static char borderch = 'B'; /* Screen character to represent out-of-field */ static color borderfg = RED; static color borderbg = YELLOW; /* Functions for this 'module': */ static scrncoord curx=1, cury=1; goxy( x, y ) scrncoord x, y; /* Sets imaginary cursor coords to (x,y) */ { extern scrncoord curx, cury; curx = x; cury = y; } setcolor( f, b ) /* Creates attribute byte 'att' from 'fgd' and 'bgd' colors */ color f, b; { /* Are we setting colors that have already been set? */ if ( (fgd != f) || (bgd != b) ) { /* Set foreground and background colors: */ fgd = f; bgd = b; /* Make attribute byte: */ att = (bgd << 4) + fgd; } } scrncoord xytoadr( x, y ) scrncoord x, y; /* Returns offset from (0,0), of location (x,y) on screen, according to screen width and height #defined */ { return (scrncoord) ( (y-1)*WIDTH*sizeof(struct scrnchar) + (x-1)*sizeof(struct scrnchar) ); } writeat( x, y, s ) /* Write string 's' at screen location 'x','y' (1,1 = top left) */ scrncoord x,y; char s[]; { int i; /* Index into 's' */ struct scrnchar far *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 */ } } writeatch( x, y, c ) /* Write character 'c' at screen location 'x','y' (1,1 = top left). Note: c must be passed as 'c', NOT "c", because it is a char. */ scrncoord x,y; unsigned char c; { char s[2]; s[0] = c; s[1] = '\0'; writeat( x, y, s ); /* struct scrnchar far *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) ); */ scrn->ch = c; /* Write character on screen */ (scrn)->at = att; /* Write attribute */ */ } writestr( s ) /* Writes string 's' at (curx,cury) */ char s[]; { extern scrncoord curx, cury; writeat( curx, cury, s ); } writech( c ) /* Writes char 'c' at (curx,cury) */ unsigned char c; { extern scrncoord curx, cury; writeatch( curx, cury, c ); } fillarea( x1, y1, x2, y2, f, b, c ) /* Fills area of screen bounded by (x1,y1) and (x2,y2) [offset from (1,1)] with char 'c' using foreground and background colors 'f' and 'b' */ scrncoord x1, y1, x2, y2; color f, b; char c; { scrncoord x, y; /* Local row, col counters */ setcolor( f, b ); for ( y=y1; y<=y2; y++ ) { /* Fill all requested rows */ for ( x=x1; x<=x2; x++ ) { /* Fill all requested columns of current row */ writeatch( x, y, c ); } } } char *inttostr( x, s ) /* Sets 's' to character string representation of 'x' [assumes 's' is long enough to hold up to 7 chars ["-32767\0"]. Includes minus sign ('-') if negative, but omits plus sign ('+') if positive. Returns pointer to beginning of converted string. */ int x; char s[]; { unsigned int i = 0; /* Index into 's' */ unsigned short int d; /* Each digit [takes values 0..9] */ unsigned int place; /* Counts down from 10000 to 1 */ int firstnonzero = FALSE; /* Set to TRUE when first non-zero char found */ if (x<0) { x = -x; /* Make positive */ s[i++] = '-'; } /* Assume 'x' can be no larger than 32767 */ for (place=10000; place>=1; place /= 10) { if (x>=place) { /* Should digit be '1'..'9'? */ d = x / place; /* Result will be 1..9 */ x -= (d*place); /* Remainder */ s[i++] = '0' + d; /* ASCII '0'..'9' */ firstnonzero = TRUE; } else { /* Zero in this place, but add it only if a non-zero was previously printed: */ if ( (firstnonzero) || (place==1) ) s[i++] = '0'; else s[i++] = ' '; } } s[i] = '\0'; /* Terminate string properly */ return &(s[0]); } /*************************************************************************/ 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, WALL if wall, or BORDER if out of range. */ field f; cellcoord x, y; { struct cellblock cb; if ( (x>=FIELDWIDTH) || (x<0) || (y>=FIELDHEIGHT) || (y<0) ) return BORDER; else { cb = f[y][x]; /* 'cb' now contains CellBlock which contains cell we want */ return cb.cell0; } /* 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' [assumed to be LIVE, DEAD, or WALL] */ field f; cellcoord x, y; cell c; { /* @@@ cb = POINTER TO cellblock */ if ( (c != DEAD) && (c != LIVE) && (c != WALL) ) { printf( "setcellat(): internal error: invalid cell value passed: %u", c ); } f[x][y].cell0 = c; /* cb = &(f[y][x]); /* 'cb' now points to CellBlock which contains cell we want */ cb->cell0 = c; /* Set the cell */ */ } clearfield( f ) /* Clears field 'f' [i.e. sets all cells to DEAD] */ field f; { cellcoord x, y; /* Indexes into 'f' */ for (y=0; y= 0) { cellY--; /* If won't go past first editor row... */ if (crsrY-1 >= y1) { /* Redraw cell under cursor: */ showfield( f, cellX, cellY+1, crsrX, crsrY--, crsrX, crsrY ); } else { /* Will go past first editor row, so scroll down: */ showfield( f, x, --y, x1, y1, x2, y2 ); } } updatestatus(); break; case DOWNKEY: /* If won't go past last row of cells in memory... */ if (cellY+1 < FIELDHEIGHT) { cellY++; /* If won't go past last editor row... */ if (crsrY+1 <= y2) { /* Redraw cell under cursor: */ showfield( f, cellX, cellY-1, crsrX, crsrY++, crsrX, crsrY ); } else { /* Will go past last editor row, so scroll up: */ showfield( f, x, ++y, x1, y1, x2, y2 ); } } updatestatus(); break; case LEFTKEY: /* If won't go past first column of cells in memory... */ if (cellX-1 >= 0) { cellX--; /* If won't go past first editor column... */ if (crsrX-1 >= x1) { /* Redraw cell under cursor: */ showfield( f, cellX+1, cellY, crsrX--, crsrY, crsrX, crsrY ); } else { /* Will go past first editor column, so scroll right: */ showfield( f, --x, y, x1, y1, x2, y2 ); } } updatestatus(); break; case RIGHTKEY: /* If won't go past last column of cells in memory... */ if (cellX+1 < FIELDWIDTH) { cellX++; /* If won't go past last editor column... */ if (crsrX+1 <= x2) { /* Redraw cell under cursor: */ showfield( f, cellX-1, cellY, crsrX++, crsrY, crsrX, crsrY ); } else { /* Will go past last editor column, so scroll left: */ showfield( f, ++x, y, x1, y1, x2, y2 ); } } updatestatus(); break; case F1KEY: /* Change live char */ break; case F2KEY: /* Change dead char */ break; case F3KEY: /* Change wall char */ break; case F4KEY: /* Change border char */ break; case SH_F1KEY: /* Change live fg */ break; case SH_F2KEY: /* Change dead fg */ break; case SH_F3KEY: /* Change wall fg */ break; case SH_F4KEY: /* Change border fg */ break; case CTRL_F1KEY: /* Change live bg */ break; case CTRL_F2KEY: /* Change dead bg */ break; case CTRL_F3KEY: /* Change wall bg */ break; case CTRL_F4KEY: /* Change border bg */ break; default: /* Ignore illegal extended keys */ break; } case 'A': case 'a': pen = livech; setcolor( livefg, livebg ); break; case 'D': case 'd': pen = deadch; setcolor( deadfg, deadbg ); break; case 'W': case 'w': pen = wallch; setcolor( wallfg, wallbg ); break; } } while (ch != ESCKEY); /* @@@ UNTIL (ch = ESCKEY) */ } initstatus() /* Writes status line */ { extern scrncoord statx1, staty1, statx2, staty2; fillarea( statx1, staty1, statx2, staty2, BRIGHTWHITE, BLUE, ' ' ); setcolor( BRIGHTWHITE, BLUE ); writeat( statx1+1, staty1, "Status:" ); } edstatus() /* Draws initial status line for edit mode [does not fill in variables] */ { extern scrncoord statx1, staty1, statx2, staty2; setcolor( YELLOW, BLUE ); writeat( statx1+9, staty1, "EDITING" ); setcolor( BRIGHTWHITE, BLUE ); writeat( statx1+18, staty1, "Generation: (Reset: Next: Previous: )" ); writeat( statx1+1, staty1+1, "Live: Dead: Wall: Border: Move:" ); writeat( statx1+1, staty1+2, "Char: / / Fgnd: / / Bgnd: / / Done:" ); writeat( statx1+1, staty1+3, "Current cell: ," ); /* Write fields that won't change: */ setcolor( YELLOW, BLUE ); writeat( statx1+45, staty1, "ENTER" ); writeat( statx1+58, staty1, "+" ); writeat( statx1+71, staty1, "-" ); writeat( statx1+49, staty1+1, "Arrows PgUp PgDn Home End" ); writeat( statx1+7, staty1+2, "F1..F4" ); writeat( statx1+23, staty1+2, "SHIFT-F1..F4" ); writeat( statx1+45, staty1+2, "CTRL-F1..F4" ); writeat( statx1+66, staty1+2, "ESC" ); } updatestatus() /* Updates status line depending on 'status' */ { extern scrncoord statx1, staty1, statx2, staty2; extern unsigned int generation; extern cellcoord cellX, cellY; char s[255]; /* Temporary string */ switch (status) { case EDITING: setcolor( RED, BLUE ); writeat( statx1+15, staty1+3, inttostr( cellX, s ) ); writeat( statx1+22, staty1+3, inttostr( cellY, s ) ); setcolor( livefg, livebg ); writeatch( statx1+7, staty1+1, livech ); setcolor( deadfg, deadbg ); writeatch( statx1+17, staty1+1, deadch ); setcolor( wallfg, wallbg ); writeatch( statx1+27, staty1+1, wallch ); setcolor( borderfg, borderbg ); writeatch( statx1+39, staty1+1, borderch ); case GROWING: setcolor( RED, BLUE ); writeat( statx1+31, staty1, inttostr(generation, s) ); break; default: printf( "updatestatus(): internal error: unknown status code: %d", status ); break; } } field mainfield; char ch; int kim = 31542; test( f, x, y, c ) field f; cellcoord x, y; cell c; { printf( "%u\n", c ); } main() { clearfield( mainfield ); setcellat( mainfield, 3, 5, LIVE ); showfield( mainfield, 3,3, 1,5,70,15 ); status = EDITING; initstatus(); edstatus(); edit( mainfield, 3,3, 1, 5, 70, 15 ); }