home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / qc / qc20 / life.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-11-21  |  34.2 KB  |  1,165 lines

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