home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_100 / 186_01 / robots21.c < prev    next >
Text File  |  1985-08-21  |  17KB  |  651 lines

  1. char    copyright[] =
  2.     "\n\t      Version 2.1, Copyright 1984, 1985 by Tom P Jansing \n\n";
  3. /*
  4.     ROBOTS.C - A game for any CRT terminal with an addressable
  5.     cursor, with or without graphic features.  Written for the
  6.     Small-C Version 2B compiler under CP/M-80.
  7.  
  8.     The game is set up for a 24x80 screen, although this can be
  9.     changed by adjusting the ROWS and COLS values below, and perhaps
  10.     reformatting some of the extra displays.  The only other
  11.     terminal-dependent function is cursor addressing, handled
  12.     by the locate(x,y) function defined just below.  This is currently
  13.     set up for the popular (ESC) (=) (Y+32) (X+32) scheme found on
  14.     Kaypro and Osborne computers, among others.
  15.  
  16.     Just like arcade games, this one gets harder as you get better
  17.     at it.  If you have trouble however, it gets easier instead.
  18.  
  19. Gameplay:
  20.  
  21.     You ($) are in the middle of a galaxy populated with stars (*) and
  22.     killer robots (%).  You can move around, but every time you do, the
  23.     robots take a step toward you.  To survive, you must lure them into
  24.     stars or each other, thereby destroying them.  If you outlive the
  25.     last robot, you will be "rewarded" with a harder game.  For a real
  26.     challenge, try winning five rounds in a row with 99 opponents!
  27.  
  28. History:
  29.  
  30.     Thσ ideß i≤ takeε froφ ß popula≥ versioε a⌠ thσ ╒niversity of Alberta,
  31.     which in turn was reportedly inspired by a Basic version in an early
  32.     issue of Byte.  To my knowledge, this is the first implementation
  33.     in C.
  34.  
  35. By:     Tom Jansing        April 16, 1985
  36.         86 Mission Avenue
  37.     St. Albert, Alberta    or c/o any Edmonton-area RCP/M.
  38.     Canada  T8N 1H8
  39.  
  40.     This program may be freely distributed for non-commercial use.
  41.  
  42. Version 2.1 improvements:
  43.  
  44.     Tightened up the code considerably (COM file is now 10k, not 14)
  45.     by using alloc(), substituting a "tiny" printf() for the real one,
  46.     and compacting the block letters.  Made the relative density of
  47.     stars, and therefore the hardness of the game, a patchable variable.
  48.     Added a flashier title page, and generally polished up the code.
  49.     Aside from being a bit harder (see above), gameplay is basically
  50.     unchanged from version 1.
  51. */
  52.  
  53. #include stdio.h
  54.  
  55. #define NOCCARGC        /* Tell compiler to skip arg count info    */
  56. #define STAR    '*'        /* On screen and in screen array    */
  57. #define SPACE    ' '        /* Ditto                */
  58. #define HERO    '$'        /* Shown on screen only            */
  59. #define ROBOT    '%'        /* Ditto                */
  60. #define HEADSTRT 6        /* Minimum distance to robots at start    */
  61. #define TAKEN    -1        /* Used in creating robots        */
  62. #define ESC    27        /* Panic button                */
  63. #define ROWS    23        /* # of lines on screen - 1 (exactly!)    */
  64. #define COLS    78        /* # of columns on screen - 2        */
  65. #define MAXROBS    99        /* Max. # of robots (never >126 of em!)    */
  66. #define LENINT    2        /* Length of int variables, in bytes    */
  67.  
  68. char    *screen,        /* The playing field            */
  69.     z,            /* Temporary variable            */
  70.     key;            /* Player's move            */
  71.  
  72. int    stardens = 50;        /* Initial star density (relative)    */
  73.                 /* 75=easy; 50=so-so; 25=downright hard    */
  74. char    junk1[] = "<-RelStarDensity";            /* For patching    */
  75.  
  76. int    *rx,            /* Robot X positions            */
  77.     *ry,            /*   "   Y    "            */
  78.     *ralive,        /* Dead/alive flags for robots        */
  79.     hx, hy,            /* Hero's X and Y positions        */
  80.     dx, dy,            /* Offset from a robot to 'hero'    */
  81.     rxx, ryy,        /* X and Y of robot under consideration */
  82.     dead,            /* Status of our hero            */
  83.     i, j, jj, zx, zy,    /* Temporary variables            */
  84.     robots,            /* Initial # of robots this round    */
  85.     rleft,            /* Remaining robots this round        */
  86.     oldrleft,        /*     "       "    last move        */
  87.     fillfac,        /* Screen is (fillfac/32767) full of *s */
  88.     fillfac2;        /* fillfac = fillfac2(varies) * robots    */
  89.  
  90. /*
  91. **  Locate cursor at given position on screen:  x=across, y=down.
  92. **  ie, locate(0,0); for upper left corner, or
  93. **    locate(40,12); for center of a 24x80 screen.
  94. */
  95.  
  96. locate(x,y) int x,y; {
  97.     putchar(27);        /* For Kaypro, Osborne, etc.        */
  98.     putchar('=');
  99.     putchar(y+32);
  100.     putchar(x+32);
  101. }
  102.  
  103. /*
  104. **  Keyboard input functions (system-specific):
  105. **  kbhit():    Return true if a key has been pressed but not read.
  106. **  getkey():    Return, without echoing, a key which presumably has
  107. **        already been pressed.
  108. **  flushkbd():    Remove any 'typed-ahead' keys already waiting.
  109. */
  110.  
  111. char    ioret;                    /* Last key pressed */
  112. kbhit() {
  113.     return ((ioret=cpm(6,-1)));
  114. }
  115.  
  116. getkey() {
  117.     return (ioret);
  118. }
  119.  
  120. flushkbd() {
  121.     while (kbhit())
  122.     getkey();
  123. }
  124.  
  125. main() {
  126.  
  127.     screen =    alloc(ROWS*COLS);
  128.     rx =    alloc(MAXROBS*LENINT);        /* Get some memory */
  129.     ry =    alloc(MAXROBS*LENINT);
  130.     ralive =    alloc(MAXROBS*LENINT);
  131.     if (screen==0 || rx==0 || ry==0 || ralive==0) {
  132.     printf("Outta memory - TILT!");
  133.     exit();
  134.     }
  135.  
  136.     hello();            /* Display instructions            */
  137.     flushkbd();            /* Clear any pending keypresses        */
  138.     srand(0);            /* Seed random generator when key hit    */
  139.     cls();            /* Clear screen                */
  140.  
  141. /*
  142. **  Some initial settings.
  143. */
  144.  
  145.     fillfac2 = stardens;
  146.     dead = NO;
  147.     robots = 1;            /* Becomes 10 in a moment */
  148.  
  149. /*
  150. **  Main loop.
  151. */
  152.  
  153.     for (;;) {            /* for (ever) */
  154.  
  155. /*
  156. **  Adjust # of stars and robots up or down, depending on success
  157. **  or failure of last round.  If he keeps losing with 10 robots,
  158. **  increase star density (which makes it easier).  Or decrease
  159. **  the # of stars if he keeps winning with 99 robots.  This way,
  160. **  nobody can win many rounds in a row against 99.
  161. */
  162.  
  163.     if (dead)
  164.     robots = (robots*7) / 10;
  165.     else
  166.     robots = (robots*10) / 7;
  167.  
  168.     if (robots < 10) {        /* No <10 robots, no >250 star density        */
  169.     robots = 10;
  170.     fillfac2 = (fillfac2+=20)>250 ? 250:fillfac2;
  171.     }
  172.  
  173.     if (robots > 99) {        /* No >99 robots, no <10 star density        */
  174.     robots = 99;
  175.     fillfac2 = (fillfac2-=20)<10 ? 10:fillfac2;
  176.     }
  177.  
  178.     fillfac = fillfac2 * robots;    /* Affects # of stars on field        */
  179.     rleft = robots;            /* All of them are left right now   */
  180.     oldrleft = 0;            /* Force initial display of count   */
  181.  
  182. /*
  183. **  Fill the screen with randomly placed stars.  (APL, eat your heart out!)
  184. */
  185.  
  186.     for (i=0; i<ROWS*COLS; screen[i++] = rand()<fillfac ? STAR:SPACE)
  187.     ;
  188.  
  189. /*
  190. **  Start 'hero' at screen center, but not on top of a star.
  191. */
  192.  
  193.     hx = COLS / 2;
  194.     hy = ROWS / 2;
  195.     while (screen[subs(hx,hy)]==STAR)
  196.     ++hx;
  197.  
  198. /*
  199. **  Display screen, scrolling from the bottom for effect.
  200. */
  201.  
  202.     locate(0,ROWS);
  203.     putchar('\n');
  204.     for (zy=0; zy<ROWS; ++zy) {
  205.     for (zx=0; zx<COLS; putchar(screen[subs(zx++,zy)]))    /* One line */
  206.         ;
  207.     putchar('\n');
  208.     }
  209.  
  210. /*
  211. **  Create some robots and display them on the screen.
  212. */
  213.  
  214.     for (j=0; j<robots; ) {        /* Note: missing loop increment        */
  215.     ralive[j] = YES;
  216.     rxx = rand() % COLS;
  217.     ryy = rand() % ROWS;
  218.     if (screen[subs(rxx,ryy)]==SPACE && abs(rxx-hx)+abs(ryy-hy)>HEADSTRT) {
  219.         show(rxx,ryy,ROBOT);    /* (Above): Not too close to hero   */
  220.         screen[subs(rxx,ryy)]=TAKEN;
  221.         rx[j] = rxx;
  222.         ry[j] = ryy;
  223.         ++j;            /* Only if good spot found        */
  224.     }
  225.     }
  226.     clrflags();                /* Erase 'taken' flags in screen[]  */
  227.  
  228.     showcnt();
  229.     showhero();
  230.     dead = NO;
  231.  
  232.     flushkbd();
  233.  
  234. /*
  235. **  Repeat the following moves until there are no more robots (or hero).
  236. */
  237.  
  238.     while (rleft) {
  239.  
  240. /*
  241. **    If a key was hit, process the move.  If not, just wait.  Note:
  242. **    'else' code could be added to provide time-dependent action.
  243. */
  244.  
  245.     if (kbhit()) {            /* Otherwise, keep looping & waiting*/
  246.         key=toupper(getkey());    /* Key hit, so get it.            */
  247.  
  248. /*
  249. **        Move our hero as appropriate.
  250. */
  251.  
  252.         switch (key) {    
  253.  
  254.         case ESC:    alldone();            /* Get out fast       */
  255.  
  256.         case 'S':
  257.         case '4':    if (spotok(hx-1,hy))        /* Off screen?       */
  258.                 show(hx--,hy,SPACE);    /* Nope; move       */
  259.             else                /* No good; ignore */
  260.                 continue;
  261.             break;
  262.  
  263.         case 'F':
  264.         case '6':    if (spotok(hx+1,hy))
  265.                 show(hx++,hy,SPACE);
  266.             else
  267.                 continue;
  268.             break;
  269.  
  270.         case 'C':
  271.         case '2':    if (spotok(hx,hy+1))
  272.                 show(hx,hy++,SPACE);
  273.             else
  274.                 continue;
  275.             break;
  276.  
  277.         case 'E':
  278.         case '8':    if (spotok(hx,hy-1))
  279.                 show(hx,hy--,SPACE);
  280.             else
  281.                 continue;
  282.             break;
  283.  
  284.         case 'D':
  285.         case '5':    break;                /* Stay-put key    */
  286.  
  287.  
  288.         case 'X':
  289.         case '1':    if (spotok(hx-1,hy+1))
  290.                 show(hx--,hy++,SPACE);
  291.             else
  292.                 continue;
  293.             break;
  294.  
  295.         case 'V':
  296.         case '3':    if (spotok(hx+1,hy+1))
  297.                 show(hx++,hy++,SPACE);
  298.             else
  299.                 continue;
  300.             break;
  301.  
  302.         case 'W':
  303.         case '7':    if (spotok(hx-1,hy-1))
  304.                 show(hx--,hy--,SPACE);
  305.             else
  306.                 continue;
  307.             break;
  308.  
  309.         case 'R':
  310.         case '9':    if (spotok(hx+1,hy-1))
  311.                 show(hx++,hy--,SPACE);
  312.             else
  313.                 continue;
  314.             break;
  315.  
  316.         default:    continue;        /* Wait for another key    */
  317.         }
  318.  
  319. /*
  320. **        OK, he moved.  Show him in the new spot, and see if he jumped
  321. **        right into a robot or star.
  322. */
  323.  
  324.         showhero();
  325.         checkhit();
  326.         if (dead)
  327.         continue;
  328.  
  329. /*
  330. **        Move the robots.  Note: They appear to move simultaneously,
  331. **        though they're actually moved one at a time.  Because of this,
  332. **        allow robots to temporarily 'bump' each other while in transit.
  333. */
  334.  
  335.         for (j=0; j<robots; ++j) {
  336.         if (ralive[j]) {
  337.             dx = (rxx=rx[j]) - hx;
  338.             dy = (ryy=ry[j]) - hy;
  339.  
  340.             z = screen[subs(rxx,ryy)];    /* This spot just taken?    */
  341.             if (z<0) {
  342.             if (ralive[-1-z])
  343.                 z = ROBOT;
  344.             else
  345.                 z = SPACE;        /* The usual case        */
  346.             }
  347.  
  348.             if (abs(dx)>abs(dy)) {    /* Move vertical or horiz?  */
  349.             if (dx>0)
  350.                 show(rxx--,ryy,z);  /* Vacate spot; show  */
  351.             else            /* what we stepped on */
  352.                 show(rxx++,ryy,z);
  353.             }
  354.             else {
  355.             if (dy>0)
  356.                 show(rxx,ryy--,z);
  357.             else
  358.                 show(rxx,ryy++,z);
  359.             }
  360.  
  361. /*
  362. **            Check to see if robot landed on another robot or a star.
  363. */
  364.  
  365.             z = screen[subs(rxx,ryy)];    /* See what's there now     */
  366.             screen[subs(rxx,ryy)]= -1-j;/* Mark spot as occupied    */
  367.             if (z!=SPACE) {        /* If star or robot         */
  368.             --rleft;        /* Another 1 bites the dust */
  369.             show(rxx,ryy,SPACE);    /* Show on screen        */
  370.             ralive[j] = NO;        /*   and robot array        */
  371.             }
  372.             else
  373.             show(rxx,ryy,ROBOT);
  374.  
  375. /*
  376. **            If this robot landed on another one, kill that one too.
  377. */
  378.  
  379.             if (z<0) {
  380.             z = -1-z;        /* Convert to robot index   */
  381.             if (ralive[z]) {    /* Kill the innocent party  */
  382.                 --rleft;        /*   if it was still alive  */
  383.                 ralive[z] = NO;
  384.             }
  385.             }
  386.  
  387.             rx[j] = rxx;        /* Put THIS robot back into */
  388.             ry[j] = ryy;        /*   the array            */
  389.         }
  390.         }                    /* Next robot            */
  391.  
  392. /*
  393. **        Robots all moved.  Show the updated count and our hero again
  394. **        (to reset cursor, mainly).  More importantly, check for game over.
  395. */
  396.  
  397.         showcnt();
  398.         showhero();
  399.  
  400.         checkhit();                /* Check for collision        */
  401.         clrflags();                /* Clean up robot droppings */
  402.         flushkbd();
  403.     }                    /* (if kbhit)            */
  404.     }                        /* (while robots remain)    */
  405.  
  406. /*
  407. **  End of round.
  408. */
  409.  
  410.     if (dead)
  411.     youlose();
  412.     else
  413.     youwin();
  414.  
  415.     }                        /* End of main loop        */
  416. }                        /* End of main()        */
  417.  
  418. /*
  419. **  Return a single subscript from X and Y screen positions.  Note:  Any
  420. **  decent compiler that handles multiple-dimension arrays should 
  421. **  produce code almost as efficient with this method anyway.
  422. */
  423.  
  424. subs(x,y) int x,y; {
  425.     return (y*COLS+x);
  426. }
  427.  
  428. /*
  429. **  Display hero on screen in current spot, with cursor on top for visibility.
  430. */
  431.  
  432. showhero() {
  433.     locate(hx,hy);
  434.     putchar(HERO);
  435.     putchar('\b');
  436. }
  437.  
  438. /*
  439. **  Show number of robots remaining, but only if number has changed.
  440. */
  441.  
  442. showcnt() {
  443.     if (rleft != oldrleft) {
  444.  
  445.     locate(0,ROWS);
  446.     printf("There are ");
  447.     putint(rleft);
  448.     printf(" out of ");
  449.     putint(robots);
  450.     printf(" robots left.   ");
  451.     oldrleft = rleft;
  452.  
  453.     locate(COLS-12,ROWS);
  454.     printf("(ESC=quit)");
  455.     }
  456. }
  457.  
  458. /*
  459. **  Display a character on the screen in a specified spot.
  460. */
  461.  
  462. show(x,y,c) int x,y; char c; {
  463.     locate(x,y);
  464.     putchar(c);
  465. }
  466.  
  467. /*
  468. **  Is our hero trying to jump off the screen?
  469. */
  470.  
  471. spotok(x,y) int x,y; {
  472.     if (x<0 || x>=COLS || y<0 || y>=ROWS)
  473.     return (NO);
  474.     else
  475.     return (YES);
  476. }
  477.  
  478. /*
  479. **  Clear the robots' place markers in the screen array (not to be
  480. **    confused with the screen itself).
  481. */
  482.  
  483. clrflags() {
  484.     for (jj=0; jj<robots; jj++)
  485.     screen[subs(rx[jj],ry[jj])] = SPACE;
  486. }
  487.  
  488. /*
  489. **  See if our hero hit a star or robot.
  490. */
  491.  
  492. checkhit() {
  493.     if (screen[subs(hx,hy)]!=SPACE) {        /* Hit star or >1 robots?   */
  494.     dead = YES;                /* That's all, folks!        */
  495.     rleft = 0;                /* Force end of round        */
  496.     }
  497.     else {
  498.     for (jj=0; jj<robots; jj++) {
  499.         if (ralive[jj] && hx==rx[jj] && hy==ry[jj]) {    /* Direct hit? */
  500.         dead = YES;
  501.         rleft = 0;
  502.         }
  503.     }
  504.     }
  505. }
  506.  
  507. /*
  508. **  Clean up and exit program.
  509. */
  510.  
  511. alldone() {
  512.     cls();
  513.     exit();
  514. }
  515.  
  516. /*
  517. **  Clear screen by scrolling (needs no customizing this way).
  518. */
  519.  
  520. cls() {
  521.     int n;
  522.     for (n=0; n<ROWS+ROWS+2; ++n)
  523.     putchar('\n');
  524. }
  525.  
  526. /*
  527. **  Instructions.  Split in 2 parts to avoid "literal queue overflow" with CV2.
  528. */
  529.  
  530. hello() {
  531.   cls();
  532.   locate(0,0);
  533.   xpand("8 8%5 6%3 8%5 6%3 8%3 7%");
  534.   xpand("1\t1 2%4 2%3 2%4 2%3 2%4 2%3 2%4 2%2 2%1 2%1 2%2 2%5 2%");
  535.   xpand("1\t1 2%4 2%3 2%4 2%3 2%4 2%3 2%4 2%5 2%5 2%");
  536.   xpand("1\t1 7%4 2%4 2%3 7%4 2%4 2%5 2%6 7%");
  537.   xpand("1\t1 2%2 2%5 2%4 2%3 2%4 2%3 2%4 2%5 2%9 3 2%");
  538.   xpand("1\t1 2%3 2%4 2%4 2%3 2%4 2%3 2%4 2%5 2%5 2%5 2%");
  539.   xpand("1\t3%4 3%3 6%3 8%5 6%6 2%6 7%");
  540.   printf(copyright);    /* Honor system */
  541.   hello2();
  542. }
  543.  
  544. hello2() {
  545.   printf("\t\t$  <--\tThis is you.  At all costs, avoid the\n");
  546.   printf("\t\t%  <--\tRobots, which close in on you.  Also,\n");  /* K&R: %% */
  547.   printf("\t\t*  <--\tStars kill anything that touches them.\n\n");
  548.  
  549.   printf("Use the keys shown on the right to move around:   -->\t");
  550.     printf("W E R\t\t7 8 9\n");
  551.   printf("For example, press R or 9 to move to the upper\t\t");
  552.     printf("S D F\t or \t4 5 6\n");
  553.   printf("right, or D or 5 to stay put.\t\t\t\tX C V\t\t1 2 3\n\n");
  554.  
  555.   printf("You will have between 10 and 99 robots to deal with.  ");
  556.   printf("Every time you make a\nmove, they close in on you.  ");
  557.   printf("Try to lure them into stars or each other to\ndestroy them.  ");
  558.   printf("If you manage to outlive the robots, you will be \"rewarded\"\n");
  559.   printf("with a harder game.  Beware - this game never ends...  ");
  560.   printf("never ends...  never en\n\n\t\t\t\tInsert Coin: ");
  561. }
  562.  
  563. you() {
  564.     int repeat;
  565.     locate(0,ROWS);
  566.     putchar('\n');
  567.     blanklines(10);
  568.     xpand("2\t5 3Y3 3Y4 5O5 3U3 3U6 ");
  569.     xpand("2\t6 3Y1 3Y4 7O4 3U3 3U6 ");
  570.     xpand("2\t7 5Y4 3O3 3O3 3U3 3U6 ");
  571.     for (repeat=0; repeat<3; ++repeat)
  572.     xpand("2\t8 3Y5 3O3 3O3 3U3 3U6 ");
  573.     xpand("2\t8 3Y6 7O5 7U7 ");
  574.     xpand("2\t8 3Y7 5O7 5U8 ");
  575.     blanklines(4);
  576.     }
  577.  
  578. youlose() {
  579.     you();
  580.     xpand("2\t3L9 1 5O6 8S3 9E");
  581.     xpand("2\t3L9 7O4 8S4 9E");
  582.     xpand("2\t3L8 3O3 3O3 2S9 1 3E6 ");
  583.     xpand("2\t3L8 3O3 3O4 7S4 9E");
  584.     xpand("2\t3L8 3O3 3O5 7S3 9E");
  585.     xpand("2\t3L8 3O3 3O9 1 2S3 3E6 ");
  586.     xpand("2\t9L3 7O5 8S3 9E");
  587.     xpand("2\t9L4 5O5 8S4 9E");
  588.     blanklines(1);
  589.     }
  590.  
  591. youwin() {
  592.     you();
  593.     xpand("2\t5 2W5 2W3 9I3 3N4 2N6 ");
  594.     xpand("2\t5 2W5 2W3 9I3 4N3 2N6 ");
  595.     xpand("2\t5 2W5 2W6 3I6 4N3 2N6 ");
  596.     xpand("2\t5 2W5 2W6 3I6 2N1 3N1 2N6 ");
  597.     xpand("2\t5 2W2 1W2 2W6 3I6 2N1 3N1 2N6 ");
  598.     xpand("2\t5 2W1 3W1 2W6 3I6 2N3 4N6 ");
  599.     xpand("2\t6 7W4 9I3 2N3 4N6 ");
  600.     xpand("2\t7 5W5 9I3 2N4 3N6 ");
  601.     blanklines(1);
  602.     }
  603.  
  604. /*
  605. **  Print n blank lines at same speed as the block letters.
  606. */
  607.  
  608. blanklines(n) int n; {
  609.     while(n--)
  610.     xpand("2\t9 9 9 9 8 ");
  611.     }
  612.  
  613. /*
  614. **  Expand cryptic strings into block letters.
  615. */
  616.  
  617. xpand(code) char *code; {
  618.     char xbyte, xletter;
  619.     while ((xbyte=*code++)) {        /* Until end of string    */
  620.     xbyte -= '0';            /* Convert to binary    */
  621.     xletter = *code++;        /* Get following char    */
  622.     while (xbyte--)
  623.         putchar(xletter);
  624.     }
  625.     putchar('\n');            /* Newline when done    */
  626. }
  627.  
  628. /*
  629. **  Poor man's printf() - takes up less room than the standard version.
  630. **  Also, a nifty little routine (courtesy K&R) to print an int value in lieu.
  631. */
  632.  
  633. printf(wisdom) char *wisdom; {
  634.     while (*wisdom)
  635.     putchar(*wisdom++);
  636. }
  637.  
  638. putint(n) int n; {
  639.     int i;
  640.     if ((i=n/10)!=0)
  641.     putint(i);
  642.     putchar(n%10+'0');
  643. }
  644.  
  645. #include RANDOM.C
  646. #include ABS.C
  647. #include ALLOCMEM.C
  648. #asm
  649.     LINK    CV2LIB        ; LASM opcode - CV2LIB & CV2CALL precompiled.
  650. #endasm
  651.