home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Programmer's Library 1.3 / Microsoft-Programers-Library-v1.3.iso / sampcode / c / msc51 / example / life.c < prev    next >
Encoding:
C/C++ Source or Header  |  1988-08-11  |  14.4 KB  |  589 lines

  1. /* life.c 
  2.  *
  3.  * Game of LIFE demonstration program.
  4.  *
  5.  * This program runs a version of the game LIFE that requires little
  6.  * interaction. Yet user is able to stop the game if desired, and edit
  7.  * the cells on the game grid.
  8.  */
  9. #include <stdio.h>
  10. #include <dos.h>
  11.  
  12. #define ROWS 21            /* Dimensions of population matrix */
  13. #define COLS 78
  14. #define SCRNSIZE ((ROWS+2)*(COLS+2)*2)
  15.  
  16. char    mat1[ROWS][COLS];    /* Matrix 1: stores current population */
  17. char    mat2[ROWS][COLS];    /* Matrix 2: stores crowding-numbers */
  18. int    speed;            /* Timing factor (1-10) */
  19. int    cell;            /* Cell character */
  20. int    attrib;            /* Video attribute of each location */
  21. char    far    *videomem;    /* Address of video memory
  22.                      monochrome = b0000, cga = b8000 */
  23. int    adapter;        /* Graphic adapter? cga, ega, mono */
  24. int    more_rounds = 0;    /* Boolean: Continue LIFE? */
  25. int    forever;        /* Boolean: unlimited no. of generations? */
  26. int     pause;                  /* Boolean: pause execution? */
  27. long    repeat;         /* Maximum number of generations to do */
  28. float    density;        /* Density of random distribution (0.0-100.0) */
  29. char    scrnbuf[SCRNSIZE];   /* Screen Buffer area */
  30.  
  31. #define BUFLIMIT (scrnbuf + SCRNSIZE)
  32.  
  33. #define MONO  1                         /* Constant values for adapter */
  34. #define CGA   2                         /*   variable */
  35. #define EGA   3
  36.  
  37. /* Main function.
  38.  *
  39.  * Determine if command-line argument (an integer) is present.
  40.  * If so, run the number of generations given on the command line.
  41.  * Otherwise, run generations indefinitely.
  42.  */
  43. main(argc, argv)
  44. int    argc;
  45. char    **argv;
  46. {
  47.     long    atol();     /* Alpha string to long int conversion */
  48.  
  49.     if (argc == 1)                   /* No command-line argument */
  50.         forever = 1;             /* So run forever */
  51.     else
  52.         repeat = atol(argv[1]);  /* Else, run given number */
  53.     init_life();
  54.     do {
  55.         more_rounds = 0;
  56.         init_buf();
  57.         draw_box(attrib);
  58.         init_mats();
  59.         rand_dist(density/100.0);
  60.         pause = 0;
  61.         while (forever || repeat--) {
  62.             proc_key();
  63.             if (!pause) {
  64.                 if (speed < 10)
  65.                     timer();
  66.                 generation();
  67.             } else
  68.                 if (pause_screen())
  69.                     break;
  70.         }
  71.     } while(more_rounds);
  72.     set_cursor(24,0);
  73. }
  74.  
  75.  
  76. /* Initialize LIFE.
  77.  * 
  78.  * Initialize parameters (static variables) used in the game, including
  79.  * background, cell type, population density, and location of video memory.
  80.  */
  81. init_life()
  82. {
  83.     init_adapter();
  84.     switch (adapter) {
  85.     case MONO:  videomem = (char far *) 0xb0000000;
  86.             speed = 5;
  87.             break;
  88.     case CGA:   videomem = (char far *) 0xb8000000;
  89.             speed = 10;
  90.             break;
  91.     case EGA:   videomem = (char far *) 0xb8000000;
  92.             speed = 5;
  93.             break;
  94.     }
  95.     cell = 254;
  96.     attrib = 11;
  97.     density = 40.0;
  98. }
  99.  
  100. /* Initialize buffer.
  101.  *
  102.  * Initialize the screen buffer array with spaces and the attribute
  103.  * inidicated in the variable attrib.
  104.  */
  105. init_buf()
  106. {
  107.     register char *p = scrnbuf;
  108.  
  109.     while (p < BUFLIMIT) {    
  110.         *p++ = ' ';
  111.         *p++ = attrib;
  112.     }
  113. }
  114.         
  115.  
  116. /* Draw box.
  117.  *
  118.  * Clear screen, and then use extended-ascii line characters to
  119.  * draw the frame (box) for the game of life.
  120.  */
  121. draw_box(attrib_val)
  122. int    attrib_val;    /* Foreground/background attribute of box */
  123. {
  124.     char    *p;        /* pointer into screen buffer */
  125.     int    i, incr;
  126.  
  127.     set_cursor(26,0);
  128.     p = scrnbuf;
  129.  
  130. /* Draw top of box. */
  131.  
  132.     *p = 218;
  133.     p += 2;
  134.     for (i = 0; i < COLS; p += 2, i++)
  135.         *p = 196;
  136.     *p = 191;
  137.     p += 2;
  138.  
  139. /* Draw side of box. */
  140.  
  141.     for (i = 0; i < ROWS; p += 160, i++) {
  142.         incr = (COLS + 1) * 2;
  143.         *p = 179;
  144.         *(p + incr) = 179;
  145.     }
  146.  
  147. /* Draw bottom of box. */
  148.  
  149.     *p = 192;
  150.     p += 2;
  151.     for (i = 0; i < COLS; p += 2, i++)
  152.         *p = 196;
  153.     *p = 217;
  154.     p += 2;
  155.  
  156.     refresh(scrnbuf, videomem);  /* Copy scrnbuf to video memory */
  157.     draw_prompt_box();
  158. }
  159.  
  160. /* Draw Prompt Box.
  161.  *
  162.  * Put main prompt sequence at bottom of the screen.
  163.  */
  164. draw_prompt_box()
  165. {
  166.     set_cursor(23,0);
  167.     printf( "Press key to control execution:  ");
  168.     printf( "F=faster  S=slower  P=pause(restart, quit) ");
  169.     set_cursor(26,0);
  170. }
  171.  
  172. /* Initialize Matrixes.
  173.  *
  174.  * Clear Matrix 1 and Matrix 2, then initialize all the zones
  175.  * (1-9) of Matrix 1.
  176.  *
  177.  * The "zones" are used by the LIFE algorithm to determine method of
  178.  * calculating neighbors. Zones are pertinent to edges and corners:
  179.  *
  180.  *    +-+--------------+-+
  181.  *    |6|      2       |7|
  182.  *    +-+--------------+-+
  183.  *    | |              | |
  184.  *    |4|      1       |5|
  185.  *    | |              | |
  186.  *    +-+--------------+-+
  187.  *    |8|      3       |9|
  188.  *    +-+--------------+-+
  189.  *
  190.  * Zones are recorded in Matrix 1 for ease of computation. If a cell
  191.  * lives, then we add 100 to mark cell's existence.
  192.  */
  193. init_mats()
  194. {
  195.     int i, j;                      /* Loop counters. */
  196.     char *p = (char *) mat1;    /* Pointer into Matrix 1. */
  197.  
  198. /* Initialize zones in Matrix 1 */
  199.  
  200.     clear_mat(mat1, ROWS * COLS);  /* Initialize matrix to all 0's */
  201.     clear_mat(mat2, ROWS * COLS);
  202.  
  203.     *p++ = 6;                           /* Initilialize row 1 */
  204.     for (i = 0; i < COLS-2; i++)       
  205.         *p++ = 2;
  206.     *p++ = 7;
  207.  
  208.     for (j = 0; j < ROWS-2; j++) {     /* Initialize center rows */
  209.         *p++ = 4;
  210.         for (i = 0; i < COLS-2; i++)
  211.             *p++ = 1;
  212.         *p++ = 5;
  213.     }
  214.  
  215.     *p++ = 8;                           /* Initialize bottom row */
  216.     for (i = 0; i < COLS-2; i++)
  217.         *p++ = 3;
  218.     *p++ = 9;
  219.  
  220. }
  221.  
  222.  
  223. /* Random Distribution.
  224.  *
  225.  * Put a random distribution into Matrix 1.
  226.  * Add 100 to each cell that is alive, according to the distribution.
  227.  */
  228. rand_dist(chance)
  229. float    chance;
  230. {
  231.     char    *p;
  232.     char    *bp = (char *) scrnbuf;
  233.     int    rnd;             /* output from rand() */
  234.     int    amt;         /* Amount to exceed for a cell to live */
  235.     int    i, j;            /* Loop counters */
  236.     long    time();         /* Grab time for seed of random sequence */
  237.  
  238.     p = (char *) mat1;
  239.     amt = chance * 32768;
  240.     srand((unsigned) time(0));
  241.     bp += 162;
  242.     for (i = 0; i < ROWS; i++, bp += 4)
  243.         for (j = 0; j < COLS; j++, p++, bp += 2) {
  244.             rnd = rand();
  245.             if (rnd < amt) {
  246.                 *p += 100;
  247.                 *bp = cell;
  248.                 *(bp+1) = attrib;
  249.             }
  250.         }
  251.         
  252.     refresh(scrnbuf, videomem);  /* Put results to the screen */
  253. }
  254.  
  255. /* Timing mechanism.
  256.  *
  257.  * Use the variable "speed" to determine how long to delay before
  258.  * the next generation of life.
  259.  */
  260. timer()
  261. {
  262.     long    period;     /* Count to this number */
  263.     long    i;          /* Loop variable */
  264.     long    j;          /* Dummy variable, to slow down loop */
  265.  
  266.     period = (10 - speed);
  267.     period *= period;
  268.     period *= 700;
  269.     for (i = 0; i < period; i++)
  270.         j = i * 2;
  271. }
  272.  
  273.  
  274. #define NW  (-1-COLS)            /* Directional constants, within    */
  275. #define N   (-COLS)              /*  Matrix 2. For example, NW refers */
  276. #define NE  (1-COLS)             /*  to the upper, left-hand neighbor */
  277. #define E   (1)
  278. #define SE  (1+COLS)
  279. #define S   (COLS)
  280. #define SW  (-1+COLS)
  281. #define W   (-1)
  282. #define LIMIT ((char *)mat1+ROWS*COLS)
  283.  
  284. /* Do one generation of life.
  285.  *
  286.  * The algorithm used here first clears all of Matrix 2, calling
  287.  * an assembly routine for maximum speed. Then, Matrix 1 is scanned.
  288.  * Wherever a living cell is found, the CORRESPONDING NEIGHBOR CELLS
  289.  * IN MATRIX 2 are incremented by 1, and the corresponding cell itself
  290.  * is incremented by 100. If no living cell is found, do nothing.
  291.  * This gives us a fast method for determining neighbor count, which is
  292.  * kept track of in Matrix 2.
  293.  *
  294.  * The "zone" of each cell is checked, and used as a guide for determining
  295.  * neighbors. Nothern neighbors of northernmost row are found in the
  296.  * southernmost row, so that the game has a "boundless" effect...formations
  297.  * that move off one side automatically circle around to the other side.
  298.  *
  299.  * Pass 2 is called to determine what actually lives or dies, based on
  300.  * the neighbor-count of each cell.
  301.  */
  302. generation()
  303. {
  304.     register  char *p1;    /* Pointers into mat1, mat2 */
  305.     register  char *p2;
  306.     int    diff;          /* No. of bytes between mat1 & mat2 */
  307.     int    zone;           /* Which "zone" does cell lie in? */
  308.  
  309.     clear_mat(mat2, ROWS * COLS);
  310.     diff = (char *) mat2 - (char *) mat1;
  311.     for (p1 = (char *) mat1; p1 < LIMIT; p1++) {
  312.         if (*p1 > 100) {                      /* Is cell alive? */
  313.             p2 = p1 + diff;            /* P2 is corresponding cell */
  314.             *p2 += 100;
  315.             zone = (*p1 - 100);        /* Zone is residue of 100 */
  316.             switch (zone) {            /* Take action based on zone */
  317.             case 1:    ++*(p2 + NW);
  318.                 ++*(p2 + N);
  319.                 ++*(p2 + NE);
  320.                 ++*(p2 + E);
  321.                 ++*(p2 + SE);
  322.                 ++*(p2 + S);
  323.                 ++*(p2 + SW);
  324.                 ++*(p2 + W);
  325.                 break;
  326.             case 2:    ++*(p2 + (NW + ROWS * COLS));
  327.                 ++*(p2 + (N + ROWS * COLS));
  328.                 ++*(p2 + (NE + ROWS * COLS));
  329.                 ++*(p2 + E);
  330.                 ++*(p2 + SE);
  331.                 ++*(p2 + S);
  332.                 ++*(p2 + SW);
  333.                 ++*(p2 + W);
  334.                 break;
  335.             case 3:    ++*(p2 + NW);
  336.                 ++*(p2 + N);
  337.                 ++*(p2 + NE);
  338.                 ++*(p2 + E);
  339.                 ++*(p2 + (SE - ROWS * COLS));
  340.                 ++*(p2 + (S - ROWS * COLS));
  341.                 ++*(p2 + (SW - ROWS * COLS));
  342.                 ++*(p2 + W);
  343.                 break;
  344.             case 4:    ++*(p2 + (NW + COLS));
  345.                 ++*(p2 + N);
  346.                 ++*(p2 + NE);
  347.                 ++*(p2 + E);
  348.                 ++*(p2 + SE);
  349.                 ++*(p2 + S);
  350.                 ++*(p2 + (SW + COLS));
  351.                 ++*(p2 + (W + COLS));
  352.                 break;
  353.             case 5:    ++*(p2 + NW);
  354.                 ++*(p2 + N);
  355.                 ++*(p2 + (NE - COLS));
  356.                 ++*(p2 + (E - COLS));
  357.                 ++*(p2 + (SE - COLS));
  358.                 ++*(p2 + S);
  359.                 ++*(p2 + SW);
  360.                 ++*(p2 + W);
  361.                 break;
  362.             case 6:    ++*(p2 + NW + ROWS * COLS + COLS);
  363.                 ++*(p2 + N + ROWS * COLS);
  364.                 ++*(p2 + NE + ROWS * COLS);
  365.                 ++*(p2 + E);
  366.                 ++*(p2 + SE);
  367.                 ++*(p2 + S);
  368.                 ++*(p2 + SW + COLS);
  369.                 ++*(p2 + W + COLS);
  370.                 break;
  371.             case 7:    ++*(p2 + NW + ROWS * COLS);
  372.                 ++*(p2 + N + ROWS * COLS);
  373.                 ++*(p2 + NE + ROWS * COLS - COLS);
  374.                 ++*(p2 + E - COLS);
  375.                 ++*(p2 + SE - COLS);
  376.                 ++*(p2 + S);
  377.                 ++*(p2 + SW);
  378.                 ++*(p2 + W);
  379.                 break;
  380.             case 8:    ++*(p2 + NW + COLS);
  381.                 ++*(p2 + N);
  382.                 ++*(p2 + NE);
  383.                 ++*(p2 + E);
  384.                 ++*(p2 + SE - ROWS * COLS);
  385.                 ++*(p2 + S - ROWS * COLS);
  386.                 ++*(p2 + SW + COLS - ROWS * COLS);
  387.                 ++*(p2 + W + COLS);
  388.                 break;
  389.             case 9:    ++*(p2 + NW);
  390.                 ++*(p2 + N);
  391.                 ++*(p2 + NE - COLS);
  392.                 ++*(p2 + E - COLS);
  393.                 ++*(p2 + SE - ROWS * COLS - COLS);
  394.                 ++*(p2 + S - ROWS * COLS);
  395.                 ++*(p2 + SW - ROWS * COLS);
  396.                 ++*(p2 + W);
  397.                 break;
  398.             default:    break;
  399.             }
  400.         } /* End if. */
  401.     } /* End for. */
  402.     pass2();                     /* Call pass2, for birth & death */
  403.     refresh(scrnbuf, videomem);  /* Write final results to screen */
  404. }
  405.  
  406. /* Do scan of Matrix 2 (pass 2).
  407.  *
  408.  * Scan Matrix 2: a value of 3 indicates a blank cell which should
  409.  * undergo a "birth." A value > 100 (cell was alive) but NOT equal
  410.  * to 102 or 103 means that there is a living cell that must die.
  411.  * Adjust screen buffer and Matrix 1 accordingly.
  412.  */
  413. pass2()
  414. {
  415.     int      i;                  /* Loop variable */
  416.     register char *p2;           /* Pointer into Matrix 2 */
  417.     register int j;              /* Inner-loop variable */
  418.     char     *bp;                /* Pointer into screen buffer */
  419.     char     *top_left;         /* Location of top-right cell within
  420.                                         video buffer */
  421.     int     diff;               /* Distance between Matrixes 1 & 2 */
  422.  
  423.     top_left = scrnbuf + 162;
  424.     p2 = (char *) mat2;
  425.     diff = (char *) mat2 - (char *) mat1;
  426.     for (i = 0; i < ROWS * 160; i += 160)
  427.         for (j = 0; j < COLS; j++, p2++) {
  428.         if (*p2 < 100) {
  429.             if (*p2 == 3) {
  430.                 *(p2 - diff) += 100;
  431.                 bp = top_left + i + (j * 2);
  432.                 *bp = cell;
  433.                 *(bp + 1) = attrib;
  434.             }
  435.         } else
  436.             if (*p2 < 102  ||  *p2 > 103) {
  437.                 *(p2 - diff) -= 100;
  438.                 bp = top_left + i + (j * 2);
  439.                 *bp = ' ';
  440.                 *(bp + 1) = attrib;
  441.             }
  442.         }
  443. }
  444.  
  445. /* Process keystroke.
  446.  *
  447.  * Check keyboard buffer and take action if a keystroke is waiting
  448.  * there in the buffer.
  449.  */
  450. proc_key()
  451. {
  452.     int    key;      /* value of keystroke */
  453.  
  454.     while (get_key(&key)) {
  455.         switch (key) {
  456.             case('p'):
  457.             case('P'): pause = 1;
  458.                    break;
  459.             case('s'):
  460.             case('S'): if (speed > 1)
  461.                    --speed;
  462.                    break;
  463.             case('f'):
  464.             case('F'): if (speed < 10)
  465.                    ++speed;
  466.                    break;
  467.             default:   break;
  468.         }
  469.     }
  470. }
  471.  
  472. /* Pause screen.
  473.  *
  474.  * Print "pause condition" prompt and wait for a meaningful
  475.  * keystroke. Then take action based on this keystroke.
  476.  */
  477. pause_screen()
  478. {
  479.     int    key;      /* value of keystroke */
  480.  
  481.     set_cursor(23,0);
  482.         printf("PAUSE screen controls:  C=continue  Q=quit");
  483.     printf("  S=step  R=restart                 ");
  484.     set_cursor(26,0);
  485.  
  486.     do {
  487.         while (!get_key(&key))
  488.             ;      
  489.     } while (key!='c' && key!='C' && key!='Q' && key!= 'q' &&
  490.                key!='S' && key!='s' && key!='R' && key!='r');
  491.     switch(key) {
  492.     case('C'):
  493.     case('c'):
  494.         draw_prompt_box();
  495.         pause = 0;
  496.         break;
  497.     case('Q'):
  498.     case('q'):
  499.         more_rounds = 0;
  500.         return(1);
  501.         break;
  502.     case('S'):
  503.     case('s'):
  504.         generation();
  505.         repeat--;
  506.         break;
  507.     case('E'):
  508.     case('e'):
  509.         edit();
  510.         break;
  511.     case('R'):
  512.     case('r'):
  513.         more_rounds = 1;
  514.         return(1);
  515.     default:
  516.         break;
  517.     }
  518.     return(0);
  519. }
  520.  
  521. /* Initialize video adapter indicator.
  522.  *
  523.  * Determine whether adapter is monochrome, ega, or cga.
  524.  * Issue set mode BIOS command, using standard mode for color,
  525.  * B&W, or monochrome.
  526.  */
  527. init_adapter()
  528. {
  529.     int mode;                 /* Value returned by BIOS call */
  530.     union REGS regs;
  531.  
  532.     regs.h.ah = 0xF;
  533.     int86(0x10, ®s, ®s);   /* Get video mode, place in AL */
  534.     mode = regs.h.al;
  535.     if (mode == 7)               /* 7 and 15 are MONO modes */
  536.         adapter = MONO;
  537.     else if (mode == 15) {
  538.         adapter = MONO;
  539.         set_mode(7);         /* Set to 7, standard MONO mode */
  540.     } else {
  541.         adapter = is_ega();         /* Test for CGA vs. EGA */
  542.         if (mode >= 8 && mode <=14)
  543.             set_mode(3);
  544.         else switch (mode) {
  545.         case 1:                /* Color */
  546.         case 3:
  547.         case 4:    set_mode(3);        /* 3 is standard color mode */
  548.             break;
  549.         case 0:                /* B & W */
  550.         case 2:
  551.         case 5:
  552.         case 6:    set_mode(2);        /* 2 is standard B & W mode */
  553.             break;
  554.         } /* end switch */
  555.     } /* end else */
  556. }
  557.  
  558.  
  559. is_ega()
  560. {
  561.     union REGS regs;
  562.     char far *ega_byte = (char far *) 0x487;
  563.     int  ega_inactive;
  564.  
  565.     regs.h.ah = 0x12;
  566.     regs.x.cx = 0;
  567.     regs.h.bl = 0x10;
  568.     int86(0x10, ®s, ®s);
  569.     if (regs.x.cx == 0)
  570.         return (CGA);
  571.     ega_inactive = *ega_byte & 0x8;
  572.     if (ega_inactive)
  573.         return (CGA);
  574.     return (EGA);
  575. }
  576.  
  577. set_mode(mode)
  578. int    mode;
  579. {
  580.     union REGS regs;
  581.     
  582.     regs.h.al = (char) mode;
  583.     regs.h.ah = 0;
  584.     int86(0x10, ®s, ®s);
  585.     regs.h.al = 0;
  586.     regs.h.ah = 5;
  587.     int86(0x10, ®s, ®s);
  588. }
  589.