home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 7 / 07.iso / c / c040 / 1.ddi / SAMPLES / LIFE.C$ / LIFE.bin
Encoding:
Text File  |  1990-02-27  |  34.0 KB  |  1,150 lines

  1. /* LIFE.C - Game of LIFE demonstration program.
  2.  *
  3.  * If GRAPHICS.LIB was not included in your run-time libraries during
  4.  * setup, you must you must add it to the LIFE.MAK program list or
  5.  * give it on the QCL command line.
  6.  *
  7.  * The game of life was invented in 1970 by mathematics professor John
  8.  * Horton Conway. The object is to create and study the life forms that
  9.  * evolve from patterns entered on the board (usually a video screen,
  10.  * though life can also be played with pencil and paper).
  11.  *
  12.  * The game of life is based on the following laws:
  13.  *
  14.  *     1. Law of Survival - If a living cell has either two or or three
  15.  *        neighbors, it survives.
  16.  *
  17.  *     2. Law of Death - A living cell with more than three neighbors
  18.  *        dies of overcrowding. A living cell with less than two
  19.  *        neighboers dies of isolation.
  20.  *
  21.  *     3. Law of Birth - A dead cell with exactly three neighbors is born
  22.  *        in the next generation.
  23.  *
  24.  * These simple laws result in complex interactions. For example,
  25.  * try entering the following patterns:
  26.  *
  27.  *      ■■       ■             ■        ■■                  ■    ■
  28.  *     ■ ■         ■            ■      ■■       ■■■■■     ■■ ■■■■ ■■
  29.  *       ■      ■■  ■■■     ■   ■       ■       ■   ■       ■    ■
  30.  */
  31.  
  32. #include <string.h>
  33. #include <stdlib.h>
  34. #include <stdio.h>
  35. #include <conio.h>
  36. #include <ctype.h>
  37. #include <time.h>
  38. #include <graph.h>
  39. #include "tools.h"
  40.  
  41. /* Dimensions of population matrix for largest possible screen--
  42.  * VGA 80 * 50.
  43.  */
  44. #define MAXROWS 46
  45. #define MAXCOLS 78
  46. #define MAXSCRN ((MAXROWS + 2) * (MAXCOLS + 2))
  47.  
  48. #define SROWS    (cfg.mrows + 2)            /* Screen rows        */
  49. #define SCOLS    (cfg.mcols + 2)            /* Screen columns     */
  50. #define SBUFSIZE (SROWS * SCOLS)            /* Screen buffer size */
  51. #define MBUFSIZE (cfg.mrows * cfg.mcols)    /* Matrix buffer size */
  52.  
  53. #define BEEP 7
  54.  
  55. /* Action for change_cell */
  56. enum CELLCHANGE { OFF, ON, TOGGLE };
  57.  
  58. /* Cell type specifies the symbol and attribute of a cell. The two
  59.  * most common cells, life and death, are initialized.
  60.  */
  61. typedef struct LIFECELL
  62. {
  63.     unsigned char  symbol;
  64.     unsigned char  atrib;
  65. } CELL;
  66. CELL life =
  67. {   '■',
  68.     SETATRIB( _TBRIGHTWHITE, _TBLACK )
  69. };
  70. CELL death =
  71. {   ' ',
  72.     SETATRIB( _TBRIGHTWHITE, _TBLACK )
  73. };
  74.  
  75. /* Structure for overall attributes of life, initialized for mono   */
  76. struct LIFECONFIG
  77. {
  78.     double      density;        /* Percent of random distribution   */
  79.     int         rescan;         /* CGA rescan flag                  */
  80.     int         _far *videomem; /* Address of video memory          */
  81.     char        boxatrib;       /* Attribute for frame              */
  82.     char        helpatrib;      /* Attribute for prompt line        */
  83.     unsigned    mrows;          /* Matrix rows                      */
  84.     unsigned    mcols;          /* Matrix columns                   */
  85.     unsigned    cursor;         /* Cursor begin and end lines       */
  86. } cfg =
  87. {
  88.     0.40,
  89.     FALSE,
  90.     (int _far *)0xb0000000,
  91.     SETATRIB( _TWHITE, _TBLACK ),
  92.     SETATRIB( _TBLACK, _TWHITE ),
  93.     21,
  94.     78
  95. };
  96.  
  97. /* Global variables */
  98. char    mat1[MAXROWS][MAXCOLS]; /* Matrix 1: stores current population  */
  99. char    mat2[MAXROWS][MAXCOLS]; /* Matrix 2: stores crowding-numbers    */
  100. int     cell;                   /* Cell character                       */
  101. char    attrib;                 /* Video attribute of each location     */
  102. int     forever;                /* Unlimited number of generations?     */
  103. long    repeat;                 /* Maximum number of generations to do  */
  104. CELL    _near scrnbuf[MAXSCRN]; /* Screen Buffer area                   */
  105.  
  106. /* Key codes */
  107. #define HM      0x0147
  108. #define UA      0x0148
  109. #define PU      0x0149
  110. #define RA      0x014d
  111. #define PD      0x0151
  112. #define DA      0x0150
  113. #define ED      0x014f
  114. #define LA      0x014b
  115. #define SH_HM   0x0247
  116. #define SH_UA   0x0248
  117. #define SH_PU   0x0249
  118. #define SH_RA   0x024d
  119. #define SH_PD   0x0251
  120. #define SH_DA   0x0250
  121. #define SH_ED   0x024f
  122. #define SH_LA   0x024b
  123. #define INS     0x0152
  124. #define DEL     0x0153
  125. #define ENTER   13
  126. #define ESC     27
  127.  
  128. /* String prompts */
  129. char run_str[] =
  130. "RUN:  F=Faster  S=Slower  O=Options  Q=Quit";
  131.  
  132. char edit_str[] =
  133. "EDIT:  ARROWs=Move  SHIFT-ARROWs=Move/toggle  SPACE=Toggle  ENTER=Done  C=Clear";
  134.  
  135. char pause_str[] =
  136. "PAUSE:  G=Go  C=Clear  Q=Quit  S=Step  E=Edit  N=New random  R=Read  W=Write";
  137.  
  138. char file_str[] = "Enter file name: ";
  139.  
  140. char ferr_str[] = "File access failure - press a key to continue . . .";
  141.  
  142. char dense_str[] = "Invalid density - press a key to continue . . .";
  143.  
  144. /* Function prototypes */
  145. void run_mode( void );
  146. int  pause_mode( void );
  147. void edit_mode( void );
  148. void init_life( void );
  149. void init_buf( void );
  150. void draw_box( void );
  151. void init_mats( void );
  152. void rand_dist( double chance );
  153. void generation( void );
  154. void pass2( void );
  155. void change_cell( int action, int row, int col );
  156. int  read_life( void );
  157. int  write_life( void );
  158. int  show_prompt( char *prompt, char response[] );
  159. void refresh( CELL _near inbuffer[], int _far *outbuffer );
  160.  
  161. /* main - Runs the game of life.
  162.  *
  163.  * Params: argc - the number of command-line arguments
  164.  *         argv - array of command-line strings
  165.  *
  166.  * Return: 0
  167.  *
  168.  * Uses:   repeat
  169.  */
  170. void main( int argc, char **argv )
  171. {
  172.  
  173.     /* Initialize video and matrixes. Draw frame. */
  174.     init_life();
  175.     init_buf();
  176.     draw_box();
  177.     init_mats();
  178.  
  179.     /* If no command-line argument, run forever. Otherwise, repeat the number
  180.      * of times specified in command line. 0 in command line means start
  181.      * by editing, not randomizing.
  182.      */
  183.     if( argc > 1 )
  184.     {
  185.         repeat = atol( argv[1] );
  186.         forever = FALSE;
  187.     }
  188.     else
  189.     {
  190.         repeat = TRUE;
  191.         forever = TRUE;
  192.     }
  193.     if ( !repeat )
  194.     {
  195.         forever = TRUE;
  196.         edit_mode();
  197.     }
  198.     else
  199.         rand_dist( cfg.density );
  200.  
  201.     /* Run life. */
  202.     run_mode();
  203.  
  204.     /* Restore and quit. */
  205.     _settextcursor( cfg.cursor );
  206.     _displaycursor( _GCURSORON );
  207.     _setvideomode( _DEFAULTMODE );
  208.     exit( FALSE );
  209. }
  210.  
  211. /* run_mode - Runs life, checking between generations for keystrokes.
  212.  * When a keystroke is received, take appropriate action.
  213.  *
  214.  * Params: None
  215.  *
  216.  * Return: None
  217.  *
  218.  * Uses:   repeat, forever, run_str
  219.  */
  220. void run_mode()
  221. {
  222.     unsigned key;
  223.     static clock_t speed = 100;   /* Delay in microseconds (less than 1000) */
  224.  
  225.     /* Display command prompt and check keys while running. */
  226.     show_prompt( run_str, "" );
  227.     while( forever || repeat-- )
  228.     {
  229.         delay( speed );
  230.         generation();
  231.         if( key = getkey( NO_WAIT ) )
  232.         {
  233.             key = toupper( key );
  234.             switch( key )
  235.             {
  236.                 case 'O':       /* Do pause mode action */
  237.                     if( !pause_mode() )
  238.                         return;
  239.                     break;
  240.                 case 'F':       /* Faster */
  241.                     if( speed )
  242.                         speed -= 100;
  243.                     break;
  244.                 case 'S':       /* Slower */
  245.                     if( speed < 1000 )
  246.                         speed += 100;
  247.                     break;
  248.                 case 'Q':       /* Terminate */
  249.                 case ESC:
  250.                     return;
  251.             }
  252.         }
  253.     }
  254. }
  255.  
  256. /* pause_mode - Gets a pause mode keystroke and takes appropriate action.
  257.  *
  258.  * Params: None
  259.  *
  260.  * Return: FALSE if quit, TRUE if any other command
  261.  *
  262.  * Uses:   cfg and various message strings
  263.  */
  264. int pause_mode()
  265. {
  266.     int      i, pause = TRUE;
  267.     char     tmp[80];
  268.     unsigned key;
  269.  
  270.     show_prompt( pause_str, "" );
  271.     while( pause )
  272.     {
  273.         key = getkey( WAIT );
  274.         switch( toupper( key ) )
  275.         {
  276.  
  277.             case 'C':              /* Clear life arena              */
  278.                 init_buf();
  279.                 draw_box();
  280.                 init_mats();
  281.                 break;
  282.             case 'G':              /* Go - restart life             */
  283.                 pause = FALSE;
  284.                 break;
  285.             case 'E':              /* Edit - edit current pattern   */
  286.                 edit_mode();
  287.                 break;
  288.             case 'Q':              /* Quit - end game               */
  289.             case ESC:
  290.                 return FALSE;
  291.             case 'S':              /* Step - do one generation      */
  292.                 generation();
  293.                 repeat--;
  294.                 break;
  295.             case 'N':              /* New - randomize again         */
  296.                 sprintf( tmp, "Current density: %.f  Enter new: ",
  297.                          cfg.density * 100 );
  298.                 show_prompt( tmp, tmp );
  299.                 i = atoi( tmp );
  300.                 if ( (i < 1) || (i > 100) )
  301.                 {
  302.                     show_prompt( dense_str, "" );
  303.                     putch( BEEP );
  304.                     getch();
  305.                     show_prompt( pause_str, "" );
  306.                     break;
  307.                 }
  308.                 /* Clear screen and set new. */
  309.                 init_buf();
  310.                 draw_box();
  311.                 init_mats();
  312.                 show_prompt( pause_str, "" );
  313.                 rand_dist( cfg.density = (double)(i / 100.0) );
  314.                 break;
  315.             case 'R':              /* Get a new pattern from file   */
  316.                 if( !read_life() )
  317.                 {
  318.                     show_prompt( ferr_str, "" );
  319.                     putch( BEEP );
  320.                     getch();
  321.                 }
  322.                 show_prompt( pause_str, "" );
  323.                 break;
  324.             case 'W':              /* Write current pattern to file */
  325.                 if( !write_life() )
  326.                 {
  327.                     show_prompt( ferr_str, "" );
  328.                     putch( BEEP );
  329.                     getch();
  330.                 }
  331.                 show_prompt( pause_str, "" );
  332.                 break;
  333.         }
  334.     }
  335.     /* Restore run prompt. */
  336.     show_prompt( run_str, "" );
  337.     return TRUE;
  338. }
  339.  
  340. /* edit_mode - Repeatedly accepts editing keystrokes and takes
  341.  * appropriate action.
  342.  *
  343.  * Params: None
  344.  *
  345.  * Return: None
  346.  *
  347.  * Uses:   repeat, cfg, edit_str, pause_str
  348.  */
  349. void edit_mode()
  350. {
  351.     int more = TRUE;
  352.     unsigned key;
  353.     unsigned curs_row = cfg.mrows / 2, curs_col = cfg.mcols / 2;
  354.  
  355.     /* Update prompt, turn on cursor, and center cursor. */
  356.     show_prompt( edit_str, "" );
  357.     _displaycursor ( _GCURSORON );
  358.     _settextposition( curs_row + 2, curs_col + 2 );
  359.  
  360.     do
  361.     {
  362.         key = getkey( WAIT );
  363.         switch( key )
  364.         {
  365.             case SH_HM:         /* Move northwest */
  366.             case HM:
  367.                 if( (curs_col > 0) && (curs_row > 0) )
  368.                 {
  369.                     curs_col--;
  370.                     curs_row--;
  371.                 }
  372.                 break;
  373.             case SH_UA:         /* Move north */
  374.             case UA:
  375.             case 'k':
  376.                 if( curs_row > 0 )
  377.                     curs_row--;
  378.                 break;
  379.             case SH_PU:         /* Move northeast */
  380.             case PU:
  381.                 if( (curs_col < cfg.mcols - 1) && (curs_row > 0) )
  382.                 {
  383.                     curs_col++;
  384.                     curs_row--;
  385.                 }
  386.                 break;
  387.             case SH_RA:         /* Move east */
  388.             case RA:
  389.             case 'l':
  390.                 if( curs_col < cfg.mcols - 1)
  391.                     curs_col++;
  392.                 break;
  393.             case SH_PD:         /* Move southeast */
  394.             case PD:
  395.                 if( (curs_col < cfg.mcols - 1) && (curs_row < cfg.mrows - 1) )
  396.                 {
  397.                     curs_col++;
  398.                     curs_row++;
  399.                 }
  400.                 break;
  401.             case SH_DA:         /* Move south */
  402.             case DA:
  403.             case 'j':
  404.                 if( curs_row < cfg.mrows - 1)
  405.                     curs_row++;
  406.                 break;
  407.             case SH_ED:         /* Move southwest */
  408.             case ED:
  409.                 if( (curs_col > 0 ) && (curs_row < cfg.mrows - 1) )
  410.                 {
  411.                     curs_col--;
  412.                     curs_row++;
  413.                 }
  414.                 break;
  415.             case SH_LA:         /* Move west */
  416.             case LA:
  417.             case 'h':
  418.                 if( curs_col > 0 )
  419.                     curs_col--;
  420.                 break;
  421.             case ' ':           /* Toggle current cell */
  422.                 change_cell( TOGGLE, curs_row, curs_col );
  423.                 break;
  424.             case INS:           /* Turn current cell on */
  425.                 change_cell( ON, curs_row, curs_col );
  426.                 break;
  427.             case DEL:           /* Turn current cell off */
  428.                 change_cell( OFF, curs_row, curs_col );
  429.                 break;
  430.             case 'C':           /* Clear cells */
  431.             case 'c':
  432.                 init_buf();
  433.                 draw_box();
  434.                 init_mats();
  435.                 break;
  436.             case 'D':           /* Done - accept editing changes */
  437.             case 'd':
  438.             case ENTER:
  439.                 more = FALSE;
  440.                 break;
  441.             default:            /* Ignore unknown keys */
  442.                 break;
  443.         }
  444.         /* If shift was down, toggle key. */
  445.         if( (key >> 8) == 2 )
  446.             change_cell( TOGGLE, curs_row, curs_col );
  447.  
  448.         /* Update cursor position. */
  449.         _settextposition( curs_row + 2, curs_col + 2 );
  450.  
  451.     } while( more );
  452.  
  453.     /* Turn off cursor and restore pause prompt. */
  454.     _displaycursor (_GCURSOROFF );
  455.     show_prompt( pause_str, "" );
  456. }
  457.  
  458. /* init_life - Initializes the screen mode, rows, and cursor status.
  459.  * Sets global screen, configuration, and life variables.
  460.  *
  461.  * Params: None
  462.  *
  463.  * Return: None
  464.  *
  465.  * Uses:   Sets the following:
  466.  *           cfg.rescan - flag for CGA retrace handling
  467.  *              .cursor - cusor shape
  468.  *              .mrows - maximum rows
  469.  *              .videomem - pointer to screen buffer
  470.  *              .boxatrib - foreground and background colors of frame
  471.  *              .helpatrib - colors of help line
  472.  *           life.atrib - colors of live cells
  473.  *           death.atrib - colors of dead cells
  474.  */
  475. void init_life()
  476. {
  477.     struct videoconfig vc;
  478.  
  479.     /* Save starting cursor and set block cursor. Then turn it off. */
  480.     cfg.cursor = _settextcursor( SETCURSOR( 0, 7 ) );
  481.     _displaycursor( _GCURSOROFF );
  482.  
  483.     /* Set the highest best possible number of rows. */
  484.     cfg.mrows = _settextrows( _MAXTEXTROWS ) - 4;
  485.  
  486.     /* Get configuration and set variables based on adapter. */
  487.     _getvideoconfig( &vc );
  488.     if( vc.adapter == _CGA )
  489.         cfg.rescan = TRUE;
  490.     if( vc.numxpixels || (vc.numtextcols != 80) )
  491.     {
  492.         _outtext( "Start LIFE in 80-column text mode" );
  493.         exit( TRUE );
  494.     }
  495.  
  496.     /* Set variables based on mode. Use default for mono. Reset video
  497.      * address for graphics adapters. Reset attributes for color text.
  498.      */
  499.     if( vc.mode != _TEXTMONO )
  500.     {
  501.         cfg.videomem = (int _far *)0xb8000000;
  502.         if( vc.mode == _TEXTC80 )
  503.         {
  504.             cfg.boxatrib = SETATRIB( _TBRIGHTWHITE, _TBLUE );
  505.             life.atrib = death.atrib = SETATRIB( _TWHITE, _TBLUE );
  506.             cfg.helpatrib = SETATRIB( _TWHITE, _TBLACK );
  507.         }
  508.     }
  509. }
  510.  
  511. /* init_buf - Initialize screen buffer dead cells.
  512.  *
  513.  * Params: None
  514.  *
  515.  * Return: None
  516.  *
  517.  * Uses:   scrnbuf, cfg
  518.  */
  519. void init_buf()
  520. {
  521.     register CELL *p = scrnbuf;
  522.  
  523.     while( p < scrnbuf + SBUFSIZE )
  524.         *p++ = death;
  525. }
  526.  
  527.  
  528. /* draw_box - Write extended-ascii line characters around the frame (box)
  529.  * of the screen buffer. Then write the modified buffer to the screen.
  530.  *
  531.  * Params: None
  532.  *
  533.  * Return: None
  534.  *
  535.  * Uses:   scrnbuf, cfg
  536.  */
  537. void draw_box()
  538. {
  539.     register unsigned char *p = (char *)scrnbuf;   /* Pointer into buffer */
  540.     unsigned i, incr;
  541.  
  542.     /* Draw top of box. */
  543.     *p = '┌';
  544.     p +=2;
  545.     for( i = 0; i < cfg.mcols; p += 2, i++ )
  546.         *p = '─';
  547.     *p = '┐';
  548.     p += 2;
  549.  
  550.     /* Draw side of box. */
  551.     incr = (SCOLS - 1) * 2;
  552.     for( i = 0; i < cfg.mrows; p += (SCOLS * 2), i++ )
  553.     {
  554.         *p = '│';
  555.         *(p + incr) = '│';
  556.     }
  557.  
  558.     /* Draw bottom of box. */
  559.     *p = '└';
  560.     p += 2;
  561.     for( i = 0; i < cfg.mcols; p += 2, i++)
  562.         *p = '─';
  563.     *p = '┘';
  564.  
  565.     /* Copy modified screen buffer to video memory. */
  566.     refresh( scrnbuf, cfg.videomem );
  567. }
  568.  
  569. /* init_mats - Initializes life matrixes. Clears matrix 1 and matrix 2,
  570.  * then initialize all the zones (1-9) of matrix 1.
  571.  *
  572.  * The "zones" are used by the LIFE algorithm to determine the method
  573.  * of calculating neighbors. Zones are pertinent to edges and corners:
  574.  *
  575.  *    +-+--------------+-+
  576.  *    |6|      2       |7|
  577.  *    +-+--------------+-+
  578.  *    | |              | |
  579.  *    |4|      1       |5|
  580.  *    | |              | |
  581.  *    +-+--------------+-+
  582.  *    |8|      3       |9|
  583.  *    +-+--------------+-+
  584.  *
  585.  * Zones are recorded in matrix 1 for ease of computation. If a cell
  586.  * lives, then add 100 to flag cell's existence.
  587.  *
  588.  * Params: None
  589.  *
  590.  * Return: None
  591.  *
  592.  * Uses:   scrnbuf, cfg
  593.  */
  594. void init_mats()
  595. {
  596.     unsigned i, j;              /* Loop counters         */
  597.     char *p = (char *)mat1;     /* Pointer into matrix 1 */
  598.  
  599.     /* Initialize zones in matrix 1 to 0. */
  600.     memset( mat1, 0, cfg.mrows * cfg.mcols );
  601.     memset( mat2, 0, cfg.mrows * cfg.mcols );
  602.  
  603.     /* Initilialize row 1 to zones 6, 2, and 7. */
  604.     *p++ = 6;
  605.     for( i = 0; i < (cfg.mcols - 2); i++)
  606.         *p++ = 2;
  607.     *p++ = 7;
  608.  
  609.     /* Initialize center rows to zones 4, 1, and 5. */
  610.     for( j = 0; j < (cfg.mrows - 2); j++ )
  611.     {
  612.         *p++ = 4;
  613.         for( i = 0; i < (cfg.mcols - 2); i++ )
  614.             *p++ = 1;
  615.         *p++ = 5;
  616.     }
  617.  
  618.     /* Initialize bottom row to zones 8, 3, and 9. */
  619.     *p++ = 8;
  620.     for( i = 0; i < (cfg.mcols - 2); i++ )
  621.         *p++ = 3;
  622.     *p++ = 9;
  623. }
  624.  
  625. /* rand_dist - Initializes a random distribution of cells. The cells
  626.  * are updated both in matrix 1 and in the screen buffer. If a cell has
  627.  * a random value greater than the calculated distribution, 100 is added
  628.  * to its value in matrix 1, and it is written into the screen buffer.
  629.  *
  630.  * Params: chance - the percentage of randomness
  631.  *
  632.  * Return: None
  633.  *
  634.  * Uses:   scrnbuf, cfg
  635.  */
  636. void rand_dist( double chance )
  637. {
  638.     char    *p = (char *)mat1;      /* Pointer to matrix 1      */
  639.     register CELL *bp = scrnbuf;    /* Pointer to screen buffer */
  640.     unsigned i, j;                  /* Loop counters            */
  641.     int      amt, rnd;
  642.  
  643.     amt = (int)(chance * 32768);    /* Amount to exceed for a cell to live  */
  644.     srand( (unsigned)time( NULL ) );/* Randomize seed                       */
  645.     bp += SCOLS + 1;                /* Start at first non-frame cell        */
  646.  
  647.     /* Assign life or death to each cell. */
  648.     for( i = 0; i < cfg.mrows; i++, bp += 2 )
  649.     {
  650.         for( j = 0; j < cfg.mcols; j++, p++, bp++ )
  651.         {
  652.             rnd = rand();
  653.             if( rnd < amt )
  654.             {
  655.                 *p += 100;
  656.                 *bp = life;
  657.             }
  658.         }
  659.     }
  660.  
  661.     /* Put results on the screen. */
  662.     refresh( scrnbuf, cfg.videomem );
  663. }
  664.  
  665. #define NW  (-1-cfg.mcols)        /* Directional constants, within     */
  666. #define N   (-cfg.mcols)          /*  matrix 2. For example, NW refers */
  667. #define NE  (1-cfg.mcols)         /*  to the upper, left-hand neighbor */
  668. #define E   (1)
  669. #define SE  (1+cfg.mcols)
  670. #define S   (cfg.mcols)
  671. #define SW  (-1+cfg.mcols)
  672. #define W   (-1)
  673.  
  674. /* generation - Do one generation of life. First matrix 2 is cleared, then
  675.  * matrix 1 is scanned. Wherever a living cell is found, the CORRESPONDING
  676.  * NEIGHBOR CELLS IN MATRIX 2 are incremented by 1, and the corresponding
  677.  * cell itself is incremented by 100. If the cell is not living, do nothing.
  678.  * This provides a fast method of determining neighbor count, which is
  679.  * kept track of in matrix 2.
  680.  *
  681.  * The "zone" of each cell is checked, and used as a guide for determining
  682.  * neighbors. Nothern neighbors of northernmost row are found in the
  683.  * southernmost row, so that the game has a "boundless" effect...formations
  684.  * that move off one side automatically circle around to the other side.
  685.  *
  686.  * Pass 2 is called to determine what actually lives or dies, based on
  687.  * the neighbor-count of each cell.
  688.  *
  689.  * Params: None
  690.  *
  691.  * Return: None
  692.  *
  693.  * Uses:   scrnbuf, cfg
  694.  */
  695. void generation()
  696. {
  697.     register  char *p1;    /* Pointers into matrixes 1 and 2 */
  698.     register  char *p2;
  699.     int     diff;          /* Bytes between matrixes 1 and 2 */
  700.     int     zone;          /* Zone of each cell              */
  701.     int     msize = MBUFSIZE;
  702.  
  703.     /* Clear matrix 2 and calculate distance between zones 1 and 2. */
  704.     memset( mat2, 0, msize );
  705.     diff = (char *)mat2 - (char *)mat1;
  706.  
  707.     /* For each cell . . . */
  708.     for( p1 = (char *)mat1; p1 < (char *)mat1 + msize; p1++ )
  709.     {
  710.         /* If matrix 1 cell is alive . . . */
  711.         if( *p1 > 100 )
  712.         {
  713.             /* Point to matrix 2 cell and update it. */
  714.             p2 = p1 + diff;
  715.             *p2 += 100;
  716.  
  717.             /* Get the zone and update the neighbors accordingly. */
  718.             zone = (*p1 - 100);
  719.             switch( zone )
  720.             {
  721.                 case 1:
  722.                     ++*(p2 + NW);
  723.                     ++*(p2 + N);
  724.                     ++*(p2 + NE);
  725.                     ++*(p2 + E);
  726.                     ++*(p2 + SE);
  727.                     ++*(p2 + S);
  728.                     ++*(p2 + SW);
  729.                     ++*(p2 + W);
  730.                     break;
  731.                 case 2:
  732.                     ++*(p2 + NW + msize);
  733.                     ++*(p2 + N + msize);
  734.                     ++*(p2 + NE + msize);
  735.                     ++*(p2 + E);
  736.                     ++*(p2 + SE);
  737.                     ++*(p2 + S);
  738.                     ++*(p2 + SW);
  739.                     ++*(p2 + W);
  740.                     break;
  741.                 case 3:
  742.                     ++*(p2 + NW);
  743.                     ++*(p2 + N);
  744.                     ++*(p2 + NE);
  745.                     ++*(p2 + E);
  746.                     ++*(p2 + SE - msize);
  747.                     ++*(p2 + S - msize);
  748.                     ++*(p2 + SW - msize);
  749.                     ++*(p2 + W);
  750.                     break;
  751.                 case 4:
  752.                     ++*(p2 + NW + cfg.mcols);
  753.                     ++*(p2 + N);
  754.                     ++*(p2 + NE);
  755.                     ++*(p2 + E);
  756.                     ++*(p2 + SE);
  757.                     ++*(p2 + S);
  758.                     ++*(p2 + SW + cfg.mcols);
  759.                     ++*(p2 + W + cfg.mcols);
  760.                     break;
  761.                 case 5:
  762.                     ++*(p2 + NW);
  763.                     ++*(p2 + N);
  764.                     ++*(p2 + NE - cfg.mcols);
  765.                     ++*(p2 + E - cfg.mcols);
  766.                     ++*(p2 + SE - cfg.mcols);
  767.                     ++*(p2 + S);
  768.                     ++*(p2 + SW);
  769.                     ++*(p2 + W);
  770.                     break;
  771.                 case 6:
  772.                     ++*(p2 + NW + msize + cfg.mcols);
  773.                     ++*(p2 + N + msize);
  774.                     ++*(p2 + NE + msize);
  775.                     ++*(p2 + E);
  776.                     ++*(p2 + SE);
  777.                     ++*(p2 + S);
  778.                     ++*(p2 + SW + cfg.mcols);
  779.                     ++*(p2 + W + cfg.mcols);
  780.                     break;
  781.                 case 7:
  782.                     ++*(p2 + NW + msize);
  783.                     ++*(p2 + N + msize);
  784.                     ++*(p2 + NE + msize - cfg.mcols);
  785.                     ++*(p2 + E - cfg.mcols);
  786.                     ++*(p2 + SE - cfg.mcols);
  787.                     ++*(p2 + S);
  788.                     ++*(p2 + SW);
  789.                     ++*(p2 + W);
  790.                     break;
  791.                 case 8:
  792.                     ++*(p2 + NW + cfg.mcols);
  793.                     ++*(p2 + N);
  794.                     ++*(p2 + NE);
  795.                     ++*(p2 + E);
  796.                     ++*(p2 + SE - msize);
  797.                     ++*(p2 + S - msize);
  798.                     ++*(p2 + SW + cfg.mcols - msize);
  799.                     ++*(p2 + W + cfg.mcols);
  800.                     break;
  801.                 case 9:
  802.                     ++*(p2 + NW);
  803.                     ++*(p2 + N);
  804.                     ++*(p2 + NE - cfg.mcols);
  805.                     ++*(p2 + E - cfg.mcols);
  806.                     ++*(p2 + SE - msize - cfg.mcols);
  807.                     ++*(p2 + S - msize);
  808.                     ++*(p2 + SW - msize);
  809.                     ++*(p2 + W);
  810.                     break;
  811.                 default:
  812.                     break;
  813.             }
  814.         } /* End if */
  815.     } /* End for */
  816.  
  817.     /* Call pass2 to calculate birth or death of each cell. */
  818.     pass2();
  819. }
  820.  
  821. /* pass2 - Scan matrix 2 and update matrix 1 according to the following:
  822.  *
  823.  *    Matrix 2 value        Matrix 1 result
  824.  *    --------------        ----------------------
  825.  *          3               Dead cell becomes live
  826.  *          102, 103        No change
  827.  *          other > 100     Live cell becomes dead
  828.  *          other < 100     No change
  829.  *
  830.  * Params: None
  831.  *
  832.  * Return: None
  833.  *
  834.  * Uses:   scrnbuf, cfg
  835.  */
  836. void pass2()
  837. {
  838.     register char *p2= (char *)mat2;/* Pointer into matrix 2        */
  839.     CELL     *bp = scrnbuf;         /* Pointer into screen buffer   */
  840.     unsigned i, j;                  /* Loop variables               */
  841.     unsigned diff;                  /* Distance between matrixes    */
  842.  
  843.     /* Skip frame to first cell and calculate distance between matrixes. */
  844.     bp += SCOLS + 1;
  845.     diff = (char *)mat2 - (char *)mat1;
  846.  
  847.     /* Outer loop counts rows. */
  848.     for( i = 0; i < cfg.mrows; i++, bp += 2 )
  849.     {
  850.         /* Next loop counts columns. */
  851.         for( j = 0; j < cfg.mcols; j++, p2++, bp++ )
  852.         {
  853.             /* Write live cell if 3. */
  854.             if( *p2 < 100 )
  855.             {
  856.                 if( *p2 == 3 )
  857.                 {
  858.                     *(p2 - diff) += 100;
  859.                     *bp = life;
  860.                 }
  861.             }
  862.             else
  863.             /* Dead cell if above 100, but not 102 or 103. */
  864.             {
  865.                 if( (*p2 < 102) || (*p2 > 103) )
  866.                 {
  867.                     *(p2 - diff) -= 100;
  868.                     *bp = death;
  869.                 }
  870.             }
  871.         }
  872.     }
  873.  
  874.     /* Put results on the screen. */
  875.     refresh( scrnbuf, cfg.videomem );
  876. }
  877.  
  878. /* change_cell - Set the state of a specified cell. The cell may be turned
  879.  * on, off, or toggled. Update the status in matrix 1 and in the screen
  880.  * buffer.
  881.  *
  882.  * Params: action - OFF, ON, or TOGGLE
  883.  *         row
  884.  *         col
  885.  *
  886.  * Return: None
  887.  *
  888.  * Uses:   scrnbuf, cfg
  889.  */
  890. void change_cell( int action, int row, int col )
  891. {
  892.     register CELL *sp = scrnbuf;
  893.  
  894.     /* Skip frame to first cell. */
  895.     sp += SCOLS + 1;
  896.     sp += row * SCOLS;
  897.     sp += col;
  898.  
  899.     /* Set cell state. */
  900.     switch( action )
  901.     {
  902.         case OFF:
  903.             mat1[row][col] -= 100;
  904.             *sp = death;
  905.             break;
  906.         case ON:
  907.             mat1[row][col] += 100;
  908.             *sp = life;
  909.             break;
  910.         case TOGGLE:
  911.             if( mat1[row][col] > 100 )
  912.             {
  913.                 mat1[row][col] -= 100;
  914.                 *sp = death;
  915.             }
  916.             else
  917.             {
  918.                 mat1[row][col] += 100;
  919.                 *sp = life;
  920.             }
  921.             break;
  922.     }
  923.  
  924.     /* Show result on screen. */
  925.     refresh( scrnbuf, cfg.videomem );
  926. }
  927.  
  928. /* show_prompt - Displays a specified prompt line on the bottom of the
  929.  * screen. If a non-null buffer is passed for a response, waits for the
  930.  * user to enter a string.
  931.  *
  932.  * Params: prompt - prompt or help string
  933.  *         response - buffer for string response (if NULL, no response)
  934.  *
  935.  * Return: TRUE if no response expected or valid received, FALSE if
  936.  *         invalid response
  937.  */
  938. int show_prompt( char *prompt, char response[] )
  939. {
  940.     char tmp[81];
  941.  
  942.     /* Clear old prompt, and write new. */
  943.     memset( tmp, ' ', 80 );
  944.     tmp[80] = 0;
  945.     _settextcolor( cfg.helpatrib & 0x00ff );
  946.     _setbkcolor( (long)(cfg.helpatrib >> 4) );
  947.     _settextposition( SROWS + 1, 1 );
  948.     _outtext( tmp );
  949.     _settextposition( SROWS + 1, 1 );
  950.     _outtext( prompt );
  951.  
  952.     /* If a response buffer was passed, get a response for it. */
  953.     if ( *response )
  954.     {
  955.         _displaycursor( _GCURSORON );
  956.         tmp[0] = 80;
  957.         cgets( tmp );
  958.         strcpy( response, tmp + 2 );
  959.  
  960.         _displaycursor( _GCURSOROFF );
  961.         if( *response )
  962.             return TRUE;
  963.         else
  964.             return FALSE;
  965.     }
  966.     else
  967.         return TRUE;
  968. }
  969.  
  970. /* read_life - Reads a life pattern from a file. The pattern is bit decoded
  971.  * (life for bit set, death for bit clear). If the file contains more cells
  972.  * than the screen, extra cells are ignored. For example, this happens if
  973.  * a pattern was saved in 25-line mode, but read in 43-line mode.
  974.  *
  975.  * Params: None
  976.  *
  977.  * Return: TRUE if successful, FALSE if unsuccessful
  978.  *
  979.  * Uses:   scrnbuf, cfg
  980.  */
  981. int read_life()
  982. {
  983.     CELL *scrnp = scrnbuf;          /* Pointer into screen buffer   */
  984.     char *matp = (char *)mat1;      /* Pointer into matrix 1        */
  985.     unsigned char mbyte, fbyte;     /* Mask byte and read byte      */
  986.     unsigned i, j, k;               /* Loop counters                */
  987.     int  more = TRUE;               /* Continuation flag            */
  988.     FILE *filep;                    /* Ptr to file struct           */
  989.     char fname[81];                 /* File name buffer             */
  990.  
  991.     /* Fail if prompt or file open fails. */
  992.     if( !show_prompt( file_str, fname ) )
  993.         return FALSE;
  994.     if( !(filep = fopen( fname, "rb" )) )
  995.         return FALSE;
  996.  
  997.     /* Initialize buffer, screen, and pointers. */
  998.     init_buf();
  999.     init_mats();
  1000.     draw_box();
  1001.     scrnp += SCOLS + 1;
  1002.  
  1003.     /* Initialize mask byte and read first byte. */
  1004.     mbyte = 0x80;
  1005.     fread( (void *)&fbyte, 1, 1, filep );
  1006.  
  1007.     /* Count rows . . . */
  1008.     for( i = 0, k = 0; (i < cfg.mrows) && more; i++, scrnp += 2 )
  1009.     {
  1010.         /* Count columns . . . */
  1011.         for( j = 0; (j < cfg.mcols) && more; j++, scrnp++, matp++)
  1012.         {
  1013.             /* If bit is on, make cell live. */
  1014.             if( mbyte & fbyte )
  1015.             {
  1016.                 *matp += 100;
  1017.                 *scrnp = life;
  1018.             }
  1019.  
  1020.             /* Adjust mask and read another byte if necessary. */
  1021.             mbyte >>= 1;
  1022.             if( ++k > 7 )
  1023.             {
  1024.                 k = 0;
  1025.                 mbyte = 0x80;
  1026.  
  1027.                 /* Quit if there are no more bytes in file. */
  1028.                 if( !fread( (void *)&fbyte, 1, 1, filep ) )
  1029.                     more = FALSE;
  1030.             }
  1031.         }
  1032.     }
  1033.  
  1034.     /* Show on screen and close file. */
  1035.     refresh( scrnbuf, cfg.videomem );
  1036.     fclose( filep );
  1037.     return TRUE;
  1038. }
  1039.  
  1040. /* write_life - Writes a life pattern to a file. The pattern is bit encoded
  1041.  * (life for bit set, death for bit clear).
  1042.  *
  1043.  * Params: None
  1044.  *
  1045.  * Return: TRUE if successful, FALSE if unsuccessful
  1046.  *
  1047.  * Uses:   scrnbuf, cfg
  1048.  */
  1049. int write_life()
  1050. {
  1051.     char *matp = (char *)mat1;      /* Pointer into matrix 1    */
  1052.     unsigned mbyte, fbyte;          /* Mask byte and read byte  */
  1053.     unsigned i, j, k;               /* Loop counters            */
  1054.     FILE *filep;                    /* Ptr to file struct       */
  1055.     char fname[81];                 /* File name buffer         */
  1056.  
  1057.     /* Fail if prompt or file open fails. */
  1058.     if( !show_prompt( file_str, fname ) )
  1059.         return FALSE;
  1060.     if( !(filep = fopen( fname, "wb" )) )
  1061.         return FALSE;
  1062.  
  1063.     /* Initialize mask and read bytes. */
  1064.     mbyte = 0x80;
  1065.     fbyte = k = 0;
  1066.  
  1067.     /* Count rows . . . */
  1068.     for( i = 0; i < cfg.mrows; i++)
  1069.     {
  1070.         /* Count columns . . . */
  1071.         for( j = 0; j < cfg.mcols; j++, matp++)
  1072.         {
  1073.             /* If cell is live, turn bit on. */
  1074.             if( *matp > 100 )
  1075.                 fbyte += mbyte;
  1076.  
  1077.             /* Adjust mask and write another byte if necessary. */
  1078.             mbyte >>= 1;
  1079.             if( ++k > 7 )
  1080.             {
  1081.                 fwrite( (void *)&fbyte, 1, 1, filep );
  1082.                 fbyte = k = 0;
  1083.                 mbyte = 0x80;
  1084.             }
  1085.         }
  1086.     }
  1087.  
  1088.     /* Make sure last byte is written, then close file. */
  1089.     if( k > 0 )
  1090.         fwrite( (void *)&fbyte, 1, 1, filep );
  1091.     fclose( filep );
  1092.     return TRUE;
  1093. }
  1094.  
  1095. /* refresh - Writes buffer containing cells to the actual video screen
  1096.  * buffer. If CGA, adjust for rescan while copying. Otherwise, copy
  1097.  * directly. The CGA variation can only be done fast enough in assembly.
  1098.  *
  1099.  * Params: inbuffer - internal buffer containing cells
  1100.  *         outbuffer - pointer to hardware video memory
  1101.  *
  1102.  * Return: None
  1103.  *
  1104.  * Uses:   cfg
  1105.  */
  1106. void refresh( CELL _near inbuffer[], int _far *outbuffer )
  1107. {
  1108.     int ssize = SBUFSIZE;
  1109.  
  1110.     _asm
  1111.     {
  1112.         mov  si, inbuffer   ; Load src = screen buffer
  1113.         les  di, outbuffer  ; Load dest = video memory
  1114.         mov  cx, ssize      ; rows * columns
  1115.         cld                 ; DF = 0 (direction flag)
  1116.  
  1117.         cmp  cfg.rescan, FALSE  ; If not CGA, don't check rescan
  1118.         je   notcga
  1119.  
  1120.         mov  dx, 03DAh
  1121. wait0:
  1122.         sti
  1123.         nop
  1124.         cli
  1125.         lodsw               ; Load character and save in BX
  1126.         mov  bx, ax
  1127.  
  1128. wait1:  in   al, dx         ; Wait till horizontal active
  1129.         shr  al, 1
  1130.         jc   wait1
  1131.         cli
  1132.  
  1133. wait2:  in   al, dx         ; Wait till horizontal inactive (retrace)
  1134.         shr  al, 1
  1135.         jnc  wait2
  1136.  
  1137.         mov  ax, bx         ; Restore character and
  1138.         stosw               ;   move to video memory
  1139.         sti
  1140.  
  1141.         loop wait0          ; Next
  1142.         jmp SHORT getout    ; Done for CGA
  1143.  
  1144. notcga:                     ; Non-CGA version
  1145.         rep  movsw          ; Copy the whole screen with no waiting
  1146. getout:
  1147.  
  1148.     }
  1149. }
  1150.