Sync with NetBSD. Many fixes to include:
[dragonfly.git] / games / snake / snake / snake.c
1 /*
2  * Copyright (c) 1980, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *      This product includes software developed by the University of
16  *      California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * @(#) Copyright (c) 1980, 1993 The Regents of the University of California.  All rights reserved.
34  * @(#)snake.c  8.2 (Berkeley) 1/7/94
35  * $FreeBSD: src/games/snake/snake/snake.c,v 1.11.2.1 2000/08/17 06:21:44 jhb Exp $
36  * $DragonFly: src/games/snake/snake/snake.c,v 1.3 2006/09/03 23:23:10 pavalos Exp $
37  */
38
39 /*
40  * snake - crt hack game.
41  *
42  * You move around the screen with arrow keys trying to pick up money
43  * without getting eaten by the snake.  hjkl work as in vi in place of
44  * arrow keys.  You can leave at the exit any time.
45  *
46  * compile as follows:
47  *      cc -O snake.c move.c -o snake -lm -ltermlib
48  */
49
50 #include <sys/param.h>
51
52 #include <curses.h>
53 #include <fcntl.h>
54 #include <pwd.h>
55 #include <time.h>
56 #include <stdlib.h>
57 #include <unistd.h>
58 #include <sys/types.h>
59 #include <err.h>
60 #include <math.h>
61 #include <signal.h>
62 #include <stdio.h>
63 #include <stdlib.h>
64 #include <string.h>
65 #include <termios.h>
66
67 #include "pathnames.h"
68
69 #define cashvalue       chunk*(loot-penalty)/25
70
71 struct point {
72         int col, line;
73 };
74
75 #define same(s1, s2)    ((s1)->line == (s2)->line && (s1)->col == (s2)->col)
76
77 #define PENALTY  10     /* % penalty for invoking spacewarp     */
78
79 #define EOT     '\004'
80 #define LF      '\n'
81 #define DEL     '\177'
82
83 #define ME              'I'
84 #define SNAKEHEAD       'S'
85 #define SNAKETAIL       's'
86 #define TREASURE        '$'
87 #define GOAL            '#'
88
89 #ifndef MIN
90 #define MIN(a, b) ((a) < (b) ? (a) : (b))
91 #endif
92
93 #define pchar(point, c) mvaddch((point)->line + 1, (point)->col + 1, (c))
94 #define delay(t)        usleep(t * 50000);
95
96 struct point you;
97 struct point money;
98 struct point finish;
99 struct point snake[6];
100
101 int loot, penalty;
102 int moves;
103 int     fast = 1;
104
105 int rawscores;
106 FILE *logfile;
107
108 int     lcnt, ccnt;     /* user's idea of screen size */
109 int     chunk;          /* amount of money given at a time */
110
111 void            chase(struct point *, struct point *);
112 int             chk(const struct point *);
113 void            drawbox(void);
114 void            flushi(void);
115 void            home(void);
116 void            length(int);
117 void            logit(const char *);
118 int             main(int, char **);
119 void            mainloop(void) __attribute__((__noreturn__));
120 struct point   *point(struct point *, int, int);
121 int             post(int, int);
122 int             pushsnake(void);
123 void            right(const struct point *);
124 void            setup(void);
125 void            snap(void);
126 void            snrand(struct point *);
127 void            spacewarp(int);
128 void            stop(int) __attribute__((__noreturn__));
129 int             stretch(const struct point *);
130 void            surround(struct point *);
131 void            suspend(void);
132 void            win(const struct point *);
133 void            winnings(int);
134
135 main(argc,argv)
136 int argc;
137 char **argv;
138 {
139         int ch, i;
140         time_t tv;
141
142         /* Open score files then revoke setgid privileges */
143         rawscores = open(_PATH_RAWSCORES, O_RDWR|O_CREAT, 0664);
144         if (rawscores < 0) {
145                 warn("open %s", _PATH_RAWSCORES);
146                 sleep(2);
147         } else if (rawscores < 3)
148                 exit(1);
149         logfile = fopen(_PATH_LOGFILE, "a");
150         if (logfile == NULL) {
151                 warn("fopen %s", _PATH_LOGFILE);
152                 sleep(2);
153         }
154         setgid(getgid());
155
156         (void) time(&tv);
157
158         while ((ch = getopt(argc, argv, "l:w:t")) != -1)
159                 switch ((char) ch) {
160 #ifdef DEBUG
161                 case 'd':
162                         tv = atol(optarg);
163                         break;
164 #endif
165                 case 'w':       /* width */
166                         ccnt = atoi(optarg);
167                         break;
168                 case 'l':       /* length */
169                         lcnt = atoi(optarg);
170                         break;
171                 case 't':
172                         fast = 0;
173                         break;
174                 case '?':
175                 default:
176 #ifdef DEBUG
177                         fputs("usage: snake [-d seed] [-w width] [-l length] [-t]\n", stderr);
178 #else
179                         fputs("usage: snake [-w width] [-l length] [-t]\n", stderr);
180 #endif
181                         exit(1);
182                 }
183
184         srandom((int) tv);
185
186         penalty = loot = 0;
187         initscr();
188         cbreak();
189         noecho();
190 #ifdef KEY_LEFT
191         keypad(stdscr, TRUE);
192 #endif
193         if (!lcnt || lcnt > LINES - 2)
194                 lcnt = LINES - 2;
195         if (!ccnt || ccnt > COLS - 2)
196                 ccnt = COLS - 2;
197
198         i = MIN(lcnt, ccnt);
199         if (i < 4) {
200                 endwin();
201                 errx(1, "screen too small for a fair game.");
202         }
203
204         /*
205          * chunk is the amount of money the user gets for each $.
206          * The formula below tries to be fair for various screen sizes.
207          * We only pay attention to the smaller of the 2 edges, since
208          * that seems to be the bottleneck.
209          * This formula is a hyperbola which includes the following points:
210          *      (24, $25)       (original scoring algorithm)
211          *      (12, $40)       (experimentally derived by the "feel")
212          *      (48, $15)       (a guess)
213          * This will give a 4x4 screen $99/shot.  We don't allow anything
214          * smaller than 4x4 because there is a 3x3 game where you can win
215          * an infinite amount of money.
216          */
217         if (i < 12) i = 12;     /* otherwise it isn't fair */
218         /*
219          * Compensate for border.  This really changes the game since
220          * the screen is two squares smaller but we want the default
221          * to be $25, and the high scores on small screens were a bit
222          * much anyway.
223          */
224         i += 2;
225         chunk = (675.0 / (i+6)) + 2.5;  /* min screen edge */
226
227         signal (SIGINT, stop);
228
229         snrand(&finish);
230         snrand(&you);
231         snrand(&money);
232         snrand(&snake[0]);
233
234         for(i=1;i<6;i++)
235                 chase (&snake[i], &snake[i-1]);
236         setup();
237         mainloop();
238         /* NOTREACHED */
239         return (0);
240 }
241
242 struct point *
243 point(ps, x, y)
244         struct point *ps;
245         int     x, y;
246 {
247         ps->col = x;
248         ps->line = y;
249         return (ps);
250 }
251
252 /* Main command loop */
253 void
254 mainloop()
255 {
256         int     k;
257         int     repeat = 1;
258         int     lastc = 0;
259
260         for (;;) {
261                 int     c;
262
263                 /* Highlight you, not left & above */
264                 move(you.line + 1, you.col + 1);
265                 refresh();
266                 if (((c = getch()) <= '9') && (c >= '0')) {
267                         repeat = c - '0';
268                         while (((c = getch()) <= '9') && (c >= '0'))
269                                 repeat = 10 * repeat + (c - '0');
270                 } else {
271                         if (c != '.') repeat = 1;
272                 }
273                 if (c == '.') {
274                         c = lastc;
275                 }
276                 if (!fast) flushi();
277                 lastc = c;
278                 switch (c){
279                 case CTRL('z'):
280                         suspend();
281                         continue;
282                 case EOT:
283                 case 'x':
284                 case 0177:      /* del or end of file */
285                         endwin();
286                         length(moves);
287                         logit("quit");
288                         exit(0);
289                 case CTRL('l'):
290                         setup();
291                         winnings(cashvalue);
292                         continue;
293                 case 'p':
294                 case 'd':
295                         snap();
296                         continue;
297                 case 'w':
298                         spacewarp(0);
299                         continue;
300                 case 'A':
301                         repeat = you.col;
302                         c = 'h';
303                         break;
304                 case 'H':
305                 case 'S':
306                         repeat = you.col - money.col;
307                         c = 'h';
308                         break;
309                 case 'T':
310                         repeat = you.line;
311                         c = 'k';
312                         break;
313                 case 'K':
314                 case 'E':
315                         repeat = you.line - money.line;
316                         c = 'k';
317                         break;
318                 case 'P':
319                         repeat = ccnt - 1 - you.col;
320                         c = 'l';
321                         break;
322                 case 'L':
323                 case 'F':
324                         repeat = money.col - you.col;
325                         c = 'l';
326                         break;
327                 case 'B':
328                         repeat = lcnt - 1 - you.line;
329                         c = 'j';
330                         break;
331                 case 'J':
332                 case 'C':
333                         repeat = money.line - you.line;
334                         c = 'j';
335                         break;
336                 }
337                 for(k=1;k<=repeat;k++){
338                         moves++;
339                         switch(c) {
340                         case 's':
341                         case 'h':
342 #ifdef KEY_LEFT
343                         case KEY_LEFT:
344 #endif
345                         case '\b':
346                                 if (you.col >0) {
347                                         if((fast)||(k == 1))
348                                                 pchar(&you,' ');
349                                         you.col--;
350                                         if((fast) || (k == repeat) ||
351                                            (you.col == 0))
352                                                 pchar(&you,ME);
353                                 }
354                                 break;
355                         case 'f':
356                         case 'l':
357 #ifdef KEY_RIGHT
358                         case KEY_RIGHT:
359 #endif
360                         case ' ':
361                                 if (you.col < ccnt-1) {
362                                         if((fast)||(k == 1))
363                                                 pchar(&you,' ');
364                                         you.col++;
365                                         if((fast) || (k == repeat) ||
366                                            (you.col == ccnt-1))
367                                                 pchar(&you,ME);
368                                 }
369                                 break;
370                         case CTRL('p'):
371                         case 'e':
372                         case 'k':
373 #ifdef KEY_UP
374                         case KEY_UP:
375 #endif
376                         case 'i':
377                                 if (you.line > 0) {
378                                         if((fast)||(k == 1))
379                                                 pchar(&you,' ');
380                                         you.line--;
381                                         if((fast) || (k == repeat) ||
382                                           (you.line == 0))
383                                                 pchar(&you,ME);
384                                 }
385                                 break;
386                         case CTRL('n'):
387                         case 'c':
388                         case 'j':
389 #ifdef KEY_DOWN
390                         case KEY_DOWN:
391 #endif
392                         case LF:
393                         case 'm':
394                                 if (you.line+1 < lcnt) {
395                                         if((fast)||(k == 1))
396                                                 pchar(&you,' ');
397                                         you.line++;
398                                         if((fast) || (k == repeat) ||
399                                           (you.line == lcnt-1))
400                                                 pchar(&you,ME);
401                                 }
402                                 break;
403                         }
404
405                         if (same(&you,&money))
406                         {
407                                 loot += 25;
408                                 if(k < repeat)
409                                         pchar(&you,' ');
410                                 do {
411                                         snrand(&money);
412                                 } while (money.col == finish.col && money.line == finish.line ||
413                                          money.col < 5 && money.line == 0 ||
414                                          money.col == you.col && money.line == you.line);
415                                 pchar(&money,TREASURE);
416                                 winnings(cashvalue);
417                                 continue;
418                         }
419                         if (same(&you,&finish))
420                         {
421                                 win(&finish);
422                                 flushi();
423                                 endwin();
424                                 printf("You have won with $%d.\n", cashvalue);
425                                 fflush(stdout);
426                                 logit("won");
427                                 post(cashvalue,1);
428                                 length(moves);
429                                 exit(0);
430                         }
431                         if (pushsnake())break;
432                 }
433         }
434 }
435
436 setup(){        /*
437                  * setup the board
438                  */
439         int i;
440
441         erase();
442         pchar(&you,ME);
443         pchar(&finish,GOAL);
444         pchar(&money,TREASURE);
445         for(i=1; i<6; i++) {
446                 pchar(&snake[i],SNAKETAIL);
447         }
448         pchar(&snake[0], SNAKEHEAD);
449         drawbox();
450         refresh();
451 }
452
453 void
454 drawbox()
455 {
456         int i;
457
458         for (i = 1; i <= ccnt; i++) {
459                 mvaddch(0, i, '-');
460                 mvaddch(lcnt + 1, i, '-');
461         }
462         for (i = 0; i <= lcnt + 1; i++) {
463                 mvaddch(i, 0, '|');
464                 mvaddch(i, ccnt + 1, '|');
465         }
466 }
467
468 snrand(sp)
469 struct point *sp;
470 {
471         struct point p;
472         int i;
473
474         for (;;) {
475                 p.col = random() % ccnt;
476                 p.line = random() % lcnt;
477
478                 /* make sure it's not on top of something else */
479                 if (p.line == 0 && p.col < 5)
480                         continue;
481                 if (same(&p, &you))
482                         continue;
483                 if (same(&p, &money))
484                         continue;
485                 if (same(&p, &finish))
486                         continue;
487                 for (i = 0; i < 6; i++)
488                         if (same(&p, &snake[i]))
489                                 break;
490                 if (i < 6)
491                         continue;
492                 break;
493         }
494         *sp = p;
495 }
496
497 post(iscore, flag)
498 int     iscore, flag;
499 {
500         short   score = iscore;
501         short   uid;
502         short   oldbest=0;
503         short   allbwho=0, allbscore=0;
504         struct  passwd *p;
505
506         /* I want to printf() the scores for terms that clear on cook(),
507          * but this routine also gets called with flag == 0 to see if
508          * the snake should wink.  If (flag) then we're at game end and
509          * can printf.
510          */
511         /*
512          * Neg uid, 0, and 1 cannot have scores recorded.
513          */
514         if ((uid = getuid()) <= 1) {
515                 if (flag)
516                         printf("No saved scores for uid %d.\n", uid);
517                 return(1);
518         }
519         if (rawscores < 0) {
520                 /* Error reported earlier */
521                 return(1);
522         }
523         /* Figure out what happened in the past */
524         read(rawscores, &allbscore, sizeof(short));
525         read(rawscores, &allbwho, sizeof(short));
526         lseek(rawscores, ((off_t)uid)*sizeof(short), 0);
527         read(rawscores, &oldbest, sizeof(short));
528         if (!flag) {
529                 lseek(rawscores, 0, SEEK_SET);
530                 return (score > oldbest ? 1 : 0);
531         }
532
533         /* Update this jokers best */
534         if (score > oldbest) {
535                 lseek(rawscores, ((off_t)uid)*sizeof(short), 0);
536                 write(rawscores, &score, sizeof(short));
537                 printf("You bettered your previous best of $%d\n", oldbest);
538         } else
539                 printf("Your best to date is $%d\n", oldbest);
540
541         /* See if we have a new champ */
542         p = getpwuid(allbwho);
543         if (score > allbscore) {
544                 lseek(rawscores, 0, SEEK_SET);
545                 write(rawscores, &score, sizeof(short));
546                 write(rawscores, &uid, sizeof(short));
547                 if (allbwho) {
548                         if (p)
549                                 printf("You beat %s's old record of $%d!\n",
550                             p->pw_name, allbscore);
551                 else
552                                 printf("You beat (%d)'s old record of $%d!\n",
553                                        (int)allbwho, allbscore);
554                 }
555                 else
556                         printf("You set a new record!\n");
557         } else if (p)
558                 printf("The highest is %s with $%d\n", p->pw_name, allbscore);
559         else
560                 printf("The highest is (%d) with $%d\n", (int)allbwho,
561                     allbscore);
562         lseek(rawscores, 0, SEEK_SET);
563         return (1);
564 }
565
566 /*
567  * Flush typeahead to keep from buffering a bunch of chars and then
568  * overshooting.  This loses horribly at 9600 baud, but works nicely
569  * if the terminal gets behind.
570  */
571 flushi()
572 {
573         tcflush(0, TCIFLUSH);
574 }
575 int mx [8] = {
576         0, 1, 1, 1, 0,-1,-1,-1};
577 int my [8] = {
578         -1,-1, 0, 1, 1, 1, 0,-1};
579 float absv[8]= {
580         1, 1.4, 1, 1.4, 1, 1.4, 1, 1.4
581 };
582 int oldw=0;
583 chase (np, sp)
584 struct point *sp, *np;
585 {
586         /* this algorithm has bugs; otherwise the
587            snake would get too good */
588         struct point d;
589         int w, i, wt[8];
590         double v1, v2, vp, max;
591         point(&d,you.col-sp->col,you.line-sp->line);
592         v1 = sqrt( (double) (d.col*d.col + d.line*d.line) );
593         w=0;
594         max=0;
595         for(i=0; i<8; i++)
596         {
597                 vp = d.col*mx[i] + d.line*my[i];
598                 v2 = absv[i];
599                 if (v1>0)
600                         vp = ((double)vp)/(v1*v2);
601                 else vp=1.0;
602                 if (vp>max)
603                 {
604                         max=vp;
605                         w=i;
606                 }
607         }
608         for(i=0; i<8; i++)
609         {
610                 point(&d,sp->col+mx[i],sp->line+my[i]);
611                 wt[i]=0;
612                 if (d.col<0 || d.col>=ccnt || d.line<0 || d.line>=lcnt)
613                         continue;
614                 /*
615                  * Change to allow snake to eat you if you're on the money,
616                  * otherwise, you can just crouch there until the snake goes
617                  * away.  Not positive it's right.
618                  *
619                  * if (d.line == 0 && d.col < 5) continue;
620                  */
621                 if (same(&d,&money)) continue;
622                 if (same(&d,&finish)) continue;
623                 wt[i]= i==w ? loot/10 : 1;
624                 if (i==oldw) wt [i] += loot/20;
625         }
626         for(w=i=0; i<8; i++)
627                 w+= wt[i];
628         vp = ((random() >> 6) & 01777) % w;
629         for(i=0; i<8; i++)
630                 if (vp <wt[i])
631                         break;
632                 else
633                         vp -= wt[i];
634         if (i==8) {
635                 printw("failure\n");
636                 i=0;
637                 while (wt[i]==0) i++;
638         }
639         oldw=w=i;
640         point(np,sp->col+mx[w],sp->line+my[w]);
641 }
642
643 spacewarp(w)
644 int w;{
645         struct point p;
646         int j;
647         const char   *str;
648
649         snrand(&you);
650         point(&p, COLS / 2 - 8, LINES / 2 - 1);
651         if (p.col < 0)
652                 p.col = 0;
653         if (p.line < 0)
654                 p.line = 0;
655         if (w) {
656                 str = "BONUS!!!";
657                 loot = loot - penalty;
658                 penalty = 0;
659         } else {
660                 str = "SPACE WARP!!!";
661                 penalty += loot/PENALTY;
662         }
663         for(j=0;j<3;j++){
664                 erase();
665                 refresh();
666                 delay(5);
667                 mvaddstr(p.line + 1, p.col + 1, str);
668                 refresh();
669                 delay(10);
670         }
671         setup();
672         winnings(cashvalue);
673 }
674
675 snap()
676 {
677 #if 0 /* This code doesn't really make sense.  */
678         struct point p;
679
680         if (you.line < 3) {
681                 mvaddch(1, you.col + 1, '-');
682         }
683         if (you.line > lcnt - 4) {
684                 mvaddch(lcnt, you.col + 1, '_');
685         }
686         if (you.col < 10) {
687                 mvaddch(you.line + 1, 1, '(');
688         }
689         if (you.col > ccnt - 10) {
690                 mvaddch(you.line + 1, ccnt, ')');
691         }
692 #endif
693         if (!stretch(&money))
694                 if (!stretch(&finish)) {
695                         pchar(&you, '?');
696                         refresh();
697                         delay(10);
698                         pchar(&you, ME);
699                 }
700 #if 0
701         if (you.line < 3) {
702                 point(&p, you.col, 0);
703                 chk(&p);
704         }
705         if(you.line > lcnt-4){
706                 point(&p,you.col,lcnt-1);
707                 chk(&p);
708         }
709         if(you.col < 10){
710                 point(&p,0,you.line);
711                 chk(&p);
712         }
713         if(you.col > ccnt-10){
714                 point(&p,ccnt-1,you.line);
715                 chk(&p);
716         }
717 #endif
718         refresh();
719 }
720
721 stretch(ps)
722 const struct point *ps;{
723         struct point p;
724
725         point(&p,you.col,you.line);
726         if ((abs(ps->col - you.col) < (ccnt / 12)) && (you.line != ps->line)) {
727                 if(you.line < ps->line){
728                         for (p.line = you.line+1;p.line <= ps->line;p.line++)
729                                 pchar(&p,'v');
730                         refresh();
731                         delay(10);
732                         for (;p.line > you.line;p.line--)
733                                 chk(&p);
734                 } else {
735                         for (p.line = you.line-1;p.line >= ps->line;p.line--)
736                                 pchar(&p,'^');
737                         refresh();
738                         delay(10);
739                         for (;p.line < you.line;p.line++)
740                                 chk(&p);
741                 }
742                 return(1);
743         } else
744                 if ((abs(ps->line - you.line) < (lcnt/7))
745                     && (you.col != ps->col)) {
746                 p.line = you.line;
747                 if(you.col < ps->col){
748                         for (p.col = you.col+1;p.col <= ps->col;p.col++)
749                                 pchar(&p,'>');
750                         refresh();
751                         delay(10);
752                         for (;p.col > you.col;p.col--)
753                                 chk(&p);
754                 } else {
755                         for (p.col = you.col-1;p.col >= ps->col;p.col--)
756                                 pchar(&p,'<');
757                         refresh();
758                         delay(10);
759                         for (;p.col < you.col;p.col++)
760                                 chk(&p);
761                 }
762                 return(1);
763         }
764         return(0);
765 }
766
767 surround(ps)
768 struct point *ps;{
769         int j;
770
771         if(ps->col == 0)ps->col++;
772         if(ps->line == 0)ps->line++;
773         if(ps->line == LINES -1)ps->line--;
774         if(ps->col == COLS -1)ps->col--;
775         mvaddstr(ps->line, ps->col, "/*\\");
776         mvaddstr(ps->line + 1, ps->col, "* *");
777         mvaddstr(ps->line + 2, ps->col, "\\*/");
778         for (j=0;j<20;j++){
779                 pchar(ps,'@');
780                 refresh();
781                 delay(1);
782                 pchar(ps,' ');
783                 refresh();
784                 delay(1);
785         }
786         if (post(cashvalue,0)) {
787                 mvaddstr(ps->line, ps->col, "   ");
788                 mvaddstr(ps->line + 1, ps->col, "o.o");
789                 mvaddstr(ps->line + 2, ps->col, "\\_/");
790                 refresh();
791                 delay(6);
792                 mvaddstr(ps->line, ps->col, "   ");
793                 mvaddstr(ps->line + 1, ps->col, "o.-");
794                 mvaddstr(ps->line + 2, ps->col, "\\_/");
795                 refresh();
796                 delay(6);
797         }
798         mvaddstr(ps->line, ps->col, "   ");
799         mvaddstr(ps->line + 1, ps->col, "o.o");
800         mvaddstr(ps->line + 2, ps->col, "\\_/");
801         refresh();
802         delay(6);
803 }
804
805 win(ps)
806 const struct point *ps;
807 {
808         struct point x;
809         int j,k;
810         int boxsize;    /* actually diameter of box, not radius */
811
812         boxsize = fast ? 10 : 4;
813         point(&x,ps->col,ps->line);
814         for(j=1;j<boxsize;j++){
815                 for(k=0;k<j;k++){
816                         pchar(&x,'#');
817                         x.line--;
818                 }
819                 for(k=0;k<j;k++){
820                         pchar(&x,'#');
821                         x.col++;
822                 }
823                 j++;
824                 for(k=0;k<j;k++){
825                         pchar(&x,'#');
826                         x.line++;
827                 }
828                 for(k=0;k<j;k++){
829                         pchar(&x,'#');
830                         x.col--;
831                 }
832                 refresh();
833                 delay(1);
834         }
835 }
836
837 pushsnake()
838 {
839         int i, bonus;
840         int issame = 0;
841         struct point tmp;
842
843         /*
844          * My manual says times doesn't return a value.  Furthermore, the
845          * snake should get his turn every time no matter if the user is
846          * on a fast terminal with typematic keys or not.
847          * So I have taken the call to times out.
848          */
849         for(i=4; i>=0; i--)
850                 if (same(&snake[i], &snake[5]))
851                         issame++;
852         if (!issame)
853                 pchar(&snake[5],' ');
854         /* Need the following to catch you if you step on the snake's tail */
855         tmp.col = snake[5].col;
856         tmp.line = snake[5].line;
857         for(i=4; i>=0; i--)
858                 snake[i+1]= snake[i];
859         chase(&snake[0], &snake[1]);
860         pchar(&snake[1],SNAKETAIL);
861         pchar(&snake[0],SNAKEHEAD);
862         for(i=0; i<6; i++)
863         {
864                 if (same(&snake[i],&you) || same(&tmp, &you))
865                 {
866                         surround(&you);
867                         i = (cashvalue) % 10;
868                         bonus = ((random() >> 8) & 0377) % 10;
869                         mvprintw(lcnt + 1, 0, "%d\n", bonus);
870                         refresh();
871                         delay(30);
872                         if (bonus == i) {
873                                 spacewarp(1);
874                                 logit("bonus");
875                                 flushi();
876                                 return(1);
877                         }
878                         flushi();
879                         endwin();
880                         if ( loot >= penalty ){
881                                 printf("\nYou and your $%d have been eaten\n",
882                                     cashvalue);
883                         } else {
884                                 printf("\nThe snake ate you.  You owe $%d.\n",
885                                     -cashvalue);
886                         }
887                         logit("eaten");
888                         length(moves);
889                         exit(0);
890                 }
891         }
892         return(0);
893 }
894
895 chk(sp)
896 const struct point *sp;
897 {
898         int j;
899
900         if (same(sp,&money)) {
901                 pchar(sp,TREASURE);
902                 return(2);
903         }
904         if (same(sp,&finish)) {
905                 pchar(sp,GOAL);
906                 return(3);
907         }
908         if (same(sp,&snake[0])) {
909                 pchar(sp,SNAKEHEAD);
910                 return(4);
911         }
912         for(j=1;j<6;j++){
913                 if(same(sp,&snake[j])){
914                         pchar(sp,SNAKETAIL);
915                         return(4);
916                 }
917         }
918         if ((sp->col < 4) && (sp->line == 0)){
919                 winnings(cashvalue);
920                 if((you.line == 0) && (you.col < 4)) pchar(&you,ME);
921                 return(5);
922         }
923         if (same(sp,&you)) {
924                 pchar(sp,ME);
925                 return(1);
926         }
927         pchar(sp,' ');
928         return(0);
929 }
930 winnings(won)
931 int won;
932 {
933         if (won > 0) {
934                 mvprintw(1, 1, "$%d", won);
935         }
936 }
937
938 void
939 stop(dummy){
940         signal(SIGINT,SIG_IGN);
941         endwin();
942         length(moves);
943         exit(0);
944 }
945
946 suspend()
947 {
948         endwin();
949         kill(getpid(), SIGTSTP);
950         refresh();
951         winnings(cashvalue);
952 }
953
954 length(num)
955 int num;
956 {
957         printf("You made %d moves.\n", num);
958 }
959
960 logit(msg)
961 const char *msg;
962 {
963         time_t t;
964
965         if (logfile != NULL) {
966                 time(&t);
967                 fprintf(logfile, "%s $%d %dx%d %s %s",
968                     getlogin(), cashvalue, lcnt, ccnt, msg, ctime(&t));
969                 fflush(logfile);
970         }
971 }