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