home *** CD-ROM | disk | FTP | other *** search
- /* life.c
- *
- * Game of LIFE demonstration program.
- *
- * This program runs a version of the game LIFE that requires little
- * interaction. Yet user is able to stop the game if desired, and edit
- * the cells on the game grid.
- */
- #include <stdio.h>
- #include <dos.h>
-
- #define ROWS 21 /* Dimensions of population matrix */
- #define COLS 78
- #define SCRNSIZE ((ROWS+2)*(COLS+2)*2)
-
- char mat1[ROWS][COLS]; /* Matrix 1: stores current population */
- char mat2[ROWS][COLS]; /* Matrix 2: stores crowding-numbers */
- int speed; /* Timing factor (1-10) */
- int cell; /* Cell character */
- int attrib; /* Video attribute of each location */
- char far *videomem; /* Address of video memory
- monochrome = b0000, cga = b8000 */
- int adapter; /* Graphic adapter? cga, ega, mono */
- int more_rounds = 0; /* Boolean: Continue LIFE? */
- int forever; /* Boolean: unlimited no. of generations? */
- int pause; /* Boolean: pause execution? */
- long repeat; /* Maximum number of generations to do */
- float density; /* Density of random distribution (0.0-100.0) */
- char scrnbuf[SCRNSIZE]; /* Screen Buffer area */
-
- #define BUFLIMIT (scrnbuf + SCRNSIZE)
-
- #define MONO 1 /* Constant values for adapter */
- #define CGA 2 /* variable */
- #define EGA 3
-
- /* Main function.
- *
- * Determine if command-line argument (an integer) is present.
- * If so, run the number of generations given on the command line.
- * Otherwise, run generations indefinitely.
- */
- main(argc, argv)
- int argc;
- char **argv;
- {
- long atol(); /* Alpha string to long int conversion */
-
- if (argc == 1) /* No command-line argument */
- forever = 1; /* So run forever */
- else
- repeat = atol(argv[1]); /* Else, run given number */
- init_life();
- do {
- more_rounds = 0;
- init_buf();
- draw_box(attrib);
- init_mats();
- rand_dist(density/100.0);
- pause = 0;
- while (forever || repeat--) {
- proc_key();
- if (!pause) {
- if (speed < 10)
- timer();
- generation();
- } else
- if (pause_screen())
- break;
- }
- } while(more_rounds);
- set_cursor(24,0);
- }
-
-
- /* Initialize LIFE.
- *
- * Initialize parameters (static variables) used in the game, including
- * background, cell type, population density, and location of video memory.
- */
- init_life()
- {
- init_adapter();
- switch (adapter) {
- case MONO: videomem = (char far *) 0xb0000000;
- speed = 5;
- break;
- case CGA: videomem = (char far *) 0xb8000000;
- speed = 10;
- break;
- case EGA: videomem = (char far *) 0xb8000000;
- speed = 5;
- break;
- }
- cell = 254;
- attrib = 11;
- density = 40.0;
- }
-
- /* Initialize buffer.
- *
- * Initialize the screen buffer array with spaces and the attribute
- * inidicated in the variable attrib.
- */
- init_buf()
- {
- register char *p = scrnbuf;
-
- while (p < BUFLIMIT) {
- *p++ = ' ';
- *p++ = attrib;
- }
- }
-
-
- /* Draw box.
- *
- * Clear screen, and then use extended-ascii line characters to
- * draw the frame (box) for the game of life.
- */
- draw_box(attrib_val)
- int attrib_val; /* Foreground/background attribute of box */
- {
- char *p; /* pointer into screen buffer */
- int i, incr;
-
- set_cursor(26,0);
- p = scrnbuf;
-
- /* Draw top of box. */
-
- *p = 218;
- p += 2;
- for (i = 0; i < COLS; p += 2, i++)
- *p = 196;
- *p = 191;
- p += 2;
-
- /* Draw side of box. */
-
- for (i = 0; i < ROWS; p += 160, i++) {
- incr = (COLS + 1) * 2;
- *p = 179;
- *(p + incr) = 179;
- }
-
- /* Draw bottom of box. */
-
- *p = 192;
- p += 2;
- for (i = 0; i < COLS; p += 2, i++)
- *p = 196;
- *p = 217;
- p += 2;
-
- refresh(scrnbuf, videomem); /* Copy scrnbuf to video memory */
- draw_prompt_box();
- }
-
- /* Draw Prompt Box.
- *
- * Put main prompt sequence at bottom of the screen.
- */
- draw_prompt_box()
- {
- set_cursor(23,0);
- printf( "Press key to control execution: ");
- printf( "F=faster S=slower P=pause(restart, quit) ");
- set_cursor(26,0);
- }
-
- /* Initialize Matrixes.
- *
- * Clear Matrix 1 and Matrix 2, then initialize all the zones
- * (1-9) of Matrix 1.
- *
- * The "zones" are used by the LIFE algorithm to determine method of
- * calculating neighbors. Zones are pertinent to edges and corners:
- *
- * +-+--------------+-+
- * |6| 2 |7|
- * +-+--------------+-+
- * | | | |
- * |4| 1 |5|
- * | | | |
- * +-+--------------+-+
- * |8| 3 |9|
- * +-+--------------+-+
- *
- * Zones are recorded in Matrix 1 for ease of computation. If a cell
- * lives, then we add 100 to mark cell's existence.
- */
- init_mats()
- {
- int i, j; /* Loop counters. */
- char *p = (char *) mat1; /* Pointer into Matrix 1. */
-
- /* Initialize zones in Matrix 1 */
-
- clear_mat(mat1, ROWS * COLS); /* Initialize matrix to all 0's */
- clear_mat(mat2, ROWS * COLS);
-
- *p++ = 6; /* Initilialize row 1 */
- for (i = 0; i < COLS-2; i++)
- *p++ = 2;
- *p++ = 7;
-
- for (j = 0; j < ROWS-2; j++) { /* Initialize center rows */
- *p++ = 4;
- for (i = 0; i < COLS-2; i++)
- *p++ = 1;
- *p++ = 5;
- }
-
- *p++ = 8; /* Initialize bottom row */
- for (i = 0; i < COLS-2; i++)
- *p++ = 3;
- *p++ = 9;
-
- }
-
-
- /* Random Distribution.
- *
- * Put a random distribution into Matrix 1.
- * Add 100 to each cell that is alive, according to the distribution.
- */
- rand_dist(chance)
- float chance;
- {
- char *p;
- char *bp = (char *) scrnbuf;
- int rnd; /* output from rand() */
- int amt; /* Amount to exceed for a cell to live */
- int i, j; /* Loop counters */
- long time(); /* Grab time for seed of random sequence */
-
- p = (char *) mat1;
- amt = chance * 32768;
- srand((unsigned) time(0));
- bp += 162;
- for (i = 0; i < ROWS; i++, bp += 4)
- for (j = 0; j < COLS; j++, p++, bp += 2) {
- rnd = rand();
- if (rnd < amt) {
- *p += 100;
- *bp = cell;
- *(bp+1) = attrib;
- }
- }
-
- refresh(scrnbuf, videomem); /* Put results to the screen */
- }
-
- /* Timing mechanism.
- *
- * Use the variable "speed" to determine how long to delay before
- * the next generation of life.
- */
- timer()
- {
- long period; /* Count to this number */
- long i; /* Loop variable */
- long j; /* Dummy variable, to slow down loop */
-
- period = (10 - speed);
- period *= period;
- period *= 700;
- for (i = 0; i < period; i++)
- j = i * 2;
- }
-
-
- #define NW (-1-COLS) /* Directional constants, within */
- #define N (-COLS) /* Matrix 2. For example, NW refers */
- #define NE (1-COLS) /* to the upper, left-hand neighbor */
- #define E (1)
- #define SE (1+COLS)
- #define S (COLS)
- #define SW (-1+COLS)
- #define W (-1)
- #define LIMIT ((char *)mat1+ROWS*COLS)
-
- /* Do one generation of life.
- *
- * The algorithm used here first clears all of Matrix 2, calling
- * an assembly routine for maximum speed. Then, Matrix 1 is scanned.
- * Wherever a living cell is found, the CORRESPONDING NEIGHBOR CELLS
- * IN MATRIX 2 are incremented by 1, and the corresponding cell itself
- * is incremented by 100. If no living cell is found, do nothing.
- * This gives us a fast method for determining neighbor count, which is
- * kept track of in Matrix 2.
- *
- * The "zone" of each cell is checked, and used as a guide for determining
- * neighbors. Nothern neighbors of northernmost row are found in the
- * southernmost row, so that the game has a "boundless" effect...formations
- * that move off one side automatically circle around to the other side.
- *
- * Pass 2 is called to determine what actually lives or dies, based on
- * the neighbor-count of each cell.
- */
- generation()
- {
- register char *p1; /* Pointers into mat1, mat2 */
- register char *p2;
- int diff; /* No. of bytes between mat1 & mat2 */
- int zone; /* Which "zone" does cell lie in? */
-
- clear_mat(mat2, ROWS * COLS);
- diff = (char *) mat2 - (char *) mat1;
- for (p1 = (char *) mat1; p1 < LIMIT; p1++) {
- if (*p1 > 100) { /* Is cell alive? */
- p2 = p1 + diff; /* P2 is corresponding cell */
- *p2 += 100;
- zone = (*p1 - 100); /* Zone is residue of 100 */
- switch (zone) { /* Take action based on zone */
- case 1: ++*(p2 + NW);
- ++*(p2 + N);
- ++*(p2 + NE);
- ++*(p2 + E);
- ++*(p2 + SE);
- ++*(p2 + S);
- ++*(p2 + SW);
- ++*(p2 + W);
- break;
- case 2: ++*(p2 + (NW + ROWS * COLS));
- ++*(p2 + (N + ROWS * COLS));
- ++*(p2 + (NE + ROWS * COLS));
- ++*(p2 + E);
- ++*(p2 + SE);
- ++*(p2 + S);
- ++*(p2 + SW);
- ++*(p2 + W);
- break;
- case 3: ++*(p2 + NW);
- ++*(p2 + N);
- ++*(p2 + NE);
- ++*(p2 + E);
- ++*(p2 + (SE - ROWS * COLS));
- ++*(p2 + (S - ROWS * COLS));
- ++*(p2 + (SW - ROWS * COLS));
- ++*(p2 + W);
- break;
- case 4: ++*(p2 + (NW + COLS));
- ++*(p2 + N);
- ++*(p2 + NE);
- ++*(p2 + E);
- ++*(p2 + SE);
- ++*(p2 + S);
- ++*(p2 + (SW + COLS));
- ++*(p2 + (W + COLS));
- break;
- case 5: ++*(p2 + NW);
- ++*(p2 + N);
- ++*(p2 + (NE - COLS));
- ++*(p2 + (E - COLS));
- ++*(p2 + (SE - COLS));
- ++*(p2 + S);
- ++*(p2 + SW);
- ++*(p2 + W);
- break;
- case 6: ++*(p2 + NW + ROWS * COLS + COLS);
- ++*(p2 + N + ROWS * COLS);
- ++*(p2 + NE + ROWS * COLS);
- ++*(p2 + E);
- ++*(p2 + SE);
- ++*(p2 + S);
- ++*(p2 + SW + COLS);
- ++*(p2 + W + COLS);
- break;
- case 7: ++*(p2 + NW + ROWS * COLS);
- ++*(p2 + N + ROWS * COLS);
- ++*(p2 + NE + ROWS * COLS - COLS);
- ++*(p2 + E - COLS);
- ++*(p2 + SE - COLS);
- ++*(p2 + S);
- ++*(p2 + SW);
- ++*(p2 + W);
- break;
- case 8: ++*(p2 + NW + COLS);
- ++*(p2 + N);
- ++*(p2 + NE);
- ++*(p2 + E);
- ++*(p2 + SE - ROWS * COLS);
- ++*(p2 + S - ROWS * COLS);
- ++*(p2 + SW + COLS - ROWS * COLS);
- ++*(p2 + W + COLS);
- break;
- case 9: ++*(p2 + NW);
- ++*(p2 + N);
- ++*(p2 + NE - COLS);
- ++*(p2 + E - COLS);
- ++*(p2 + SE - ROWS * COLS - COLS);
- ++*(p2 + S - ROWS * COLS);
- ++*(p2 + SW - ROWS * COLS);
- ++*(p2 + W);
- break;
- default: break;
- }
- } /* End if. */
- } /* End for. */
- pass2(); /* Call pass2, for birth & death */
- refresh(scrnbuf, videomem); /* Write final results to screen */
- }
-
- /* Do scan of Matrix 2 (pass 2).
- *
- * Scan Matrix 2: a value of 3 indicates a blank cell which should
- * undergo a "birth." A value > 100 (cell was alive) but NOT equal
- * to 102 or 103 means that there is a living cell that must die.
- * Adjust screen buffer and Matrix 1 accordingly.
- */
- pass2()
- {
- int i; /* Loop variable */
- register char *p2; /* Pointer into Matrix 2 */
- register int j; /* Inner-loop variable */
- char *bp; /* Pointer into screen buffer */
- char *top_left; /* Location of top-right cell within
- video buffer */
- int diff; /* Distance between Matrixes 1 & 2 */
-
- top_left = scrnbuf + 162;
- p2 = (char *) mat2;
- diff = (char *) mat2 - (char *) mat1;
- for (i = 0; i < ROWS * 160; i += 160)
- for (j = 0; j < COLS; j++, p2++) {
- if (*p2 < 100) {
- if (*p2 == 3) {
- *(p2 - diff) += 100;
- bp = top_left + i + (j * 2);
- *bp = cell;
- *(bp + 1) = attrib;
- }
- } else
- if (*p2 < 102 || *p2 > 103) {
- *(p2 - diff) -= 100;
- bp = top_left + i + (j * 2);
- *bp = ' ';
- *(bp + 1) = attrib;
- }
- }
- }
-
- /* Process keystroke.
- *
- * Check keyboard buffer and take action if a keystroke is waiting
- * there in the buffer.
- */
- proc_key()
- {
- int key; /* value of keystroke */
-
- while (get_key(&key)) {
- switch (key) {
- case('p'):
- case('P'): pause = 1;
- break;
- case('s'):
- case('S'): if (speed > 1)
- --speed;
- break;
- case('f'):
- case('F'): if (speed < 10)
- ++speed;
- break;
- default: break;
- }
- }
- }
-
- /* Pause screen.
- *
- * Print "pause condition" prompt and wait for a meaningful
- * keystroke. Then take action based on this keystroke.
- */
- pause_screen()
- {
- int key; /* value of keystroke */
-
- set_cursor(23,0);
- printf("PAUSE screen controls: C=continue Q=quit");
- printf(" S=step R=restart ");
- set_cursor(26,0);
-
- do {
- while (!get_key(&key))
- ;
- } while (key!='c' && key!='C' && key!='Q' && key!= 'q' &&
- key!='S' && key!='s' && key!='R' && key!='r');
- switch(key) {
- case('C'):
- case('c'):
- draw_prompt_box();
- pause = 0;
- break;
- case('Q'):
- case('q'):
- more_rounds = 0;
- return(1);
- break;
- case('S'):
- case('s'):
- generation();
- repeat--;
- break;
- case('E'):
- case('e'):
- edit();
- break;
- case('R'):
- case('r'):
- more_rounds = 1;
- return(1);
- default:
- break;
- }
- return(0);
- }
-
- /* Initialize video adapter indicator.
- *
- * Determine whether adapter is monochrome, ega, or cga.
- * Issue set mode BIOS command, using standard mode for color,
- * B&W, or monochrome.
- */
- init_adapter()
- {
- int mode; /* Value returned by BIOS call */
- union REGS regs;
-
- regs.h.ah = 0xF;
- int86(0x10, ®s, ®s); /* Get video mode, place in AL */
- mode = regs.h.al;
- if (mode == 7) /* 7 and 15 are MONO modes */
- adapter = MONO;
- else if (mode == 15) {
- adapter = MONO;
- set_mode(7); /* Set to 7, standard MONO mode */
- } else {
- adapter = is_ega(); /* Test for CGA vs. EGA */
- if (mode >= 8 && mode <=14)
- set_mode(3);
- else switch (mode) {
- case 1: /* Color */
- case 3:
- case 4: set_mode(3); /* 3 is standard color mode */
- break;
- case 0: /* B & W */
- case 2:
- case 5:
- case 6: set_mode(2); /* 2 is standard B & W mode */
- break;
- } /* end switch */
- } /* end else */
- }
-
-
- is_ega()
- {
- union REGS regs;
- char far *ega_byte = (char far *) 0x487;
- int ega_inactive;
-
- regs.h.ah = 0x12;
- regs.x.cx = 0;
- regs.h.bl = 0x10;
- int86(0x10, ®s, ®s);
- if (regs.x.cx == 0)
- return (CGA);
- ega_inactive = *ega_byte & 0x8;
- if (ega_inactive)
- return (CGA);
- return (EGA);
- }
-
- set_mode(mode)
- int mode;
- {
- union REGS regs;
-
- regs.h.al = (char) mode;
- regs.h.ah = 0;
- int86(0x10, ®s, ®s);
- regs.h.al = 0;
- regs.h.ah = 5;
- int86(0x10, ®s, ®s);
- }
-