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