c5c68f713bb74295820e69de2618b46225e4f0d5
[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  * $DragonFly: src/games/snake/snake/snake.c,v 1.4 2006/09/03 23:47:56 pavalos Exp $
33  */
34
35 /*
36  * snake - crt hack game.
37  *
38  * You move around the screen with arrow keys trying to pick up money
39  * without getting eaten by the snake.  hjkl work as in vi in place of
40  * arrow keys.  You can leave at the exit any time.
41  *
42  * compile as follows:
43  *      cc -O snake.c move.c -o snake -lm -ltermlib
44  */
45
46 #include <sys/param.h>
47
48 #include <curses.h>
49 #include <fcntl.h>
50 #include <pwd.h>
51 #include <time.h>
52 #include <stdlib.h>
53 #include <unistd.h>
54 #include <sys/types.h>
55 #include <err.h>
56 #include <math.h>
57 #include <signal.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <termios.h>
62
63 #include "pathnames.h"
64
65 #define cashvalue       chunk*(loot-penalty)/25
66
67 struct point {
68         int col, line;
69 };
70
71 #define same(s1, s2)    ((s1)->line == (s2)->line && (s1)->col == (s2)->col)
72
73 #define PENALTY  10             /* % penalty for invoking spacewarp */
74
75 #define EOT     '\004'
76 #define LF      '\n'
77 #define DEL     '\177'
78
79 #define ME              'I'
80 #define SNAKEHEAD       'S'
81 #define SNAKETAIL       's'
82 #define TREASURE        '$'
83 #define GOAL            '#'
84
85 #ifndef MIN
86 #define MIN(a, b) ((a) < (b) ? (a) : (b))
87 #endif
88
89 #define pchar(point, c) mvaddch((point)->line + 1, (point)->col + 1, (c))
90 #define delay(t)        usleep(t * 50000);
91
92 struct point you;
93 struct point money;
94 struct point finish;
95 struct point snake[6];
96
97 int loot, penalty;
98 int moves;
99 int fast = 1;
100
101 int rawscores;
102 FILE *logfile;
103
104 int lcnt, ccnt;         /* user's idea of screen size */
105 int chunk;              /* amount of money given at a time */
106
107 void chase(struct point *, struct point *);
108 int chk(const struct point *);
109 void drawbox(void);
110 void flushi(void);
111 void home(void);
112 void length(int);
113 void logit(const char *);
114 int main(int, char **);
115 void mainloop(void) __attribute__((__noreturn__));
116 struct point *point(struct point *, int, int);
117 int post(int, int);
118 int pushsnake(void);
119 void right(const struct point *);
120 void setup(void);
121 void snap(void);
122 void snrand(struct point *);
123 void spacewarp(int);
124 void stop(int) __attribute__((__noreturn__));
125 int stretch(const struct point *);
126 void surround(struct point *);
127 void suspend(void);
128 void win(const struct point *);
129 void winnings(int);
130
131 int
132 main(int argc, char **argv)
133 {
134         int ch, i;
135         time_t tv;
136
137         /* Open score files then revoke setgid privileges */
138         rawscores = open(_PATH_RAWSCORES, O_RDWR|O_CREAT, 0664);
139         if (rawscores < 0) {
140                 warn("open %s", _PATH_RAWSCORES);
141                 sleep(2);
142         } else if (rawscores < 3)
143                 exit(1);
144         logfile = fopen(_PATH_LOGFILE, "a");
145         if (logfile == NULL) {
146                 warn("fopen %s", _PATH_LOGFILE);
147                 sleep(2);
148         }
149         setgid(getgid());
150
151         time(&tv);
152
153         while ((ch = getopt(argc, argv, "l:w:t")) != -1)
154                 switch ((char) ch) {
155 #ifdef DEBUG
156                 case 'd':
157                         tv = atol(optarg);
158                         break;
159 #endif
160                 case 'w':       /* width */
161                         ccnt = atoi(optarg);
162                         break;
163                 case 'l':       /* length */
164                         lcnt = atoi(optarg);
165                         break;
166                 case 't':
167                         fast = 0;
168                         break;
169                 case '?':
170                 default:
171 #ifdef DEBUG
172                         fputs("usage: snake [-d seed] [-w width] [-l length] [-t]\n", stderr);
173 #else
174                         fputs("usage: snake [-w width] [-l length] [-t]\n", stderr);
175 #endif
176                         exit(1);
177                 }
178
179         srandom((int) tv);
180
181         penalty = loot = 0;
182         initscr();
183         cbreak();
184         noecho();
185 #ifdef KEY_LEFT
186         keypad(stdscr, TRUE);
187 #endif
188         if (!lcnt || lcnt > LINES - 2)
189                 lcnt = LINES - 2;
190         if (!ccnt || ccnt > COLS - 2)
191                 ccnt = COLS - 2;
192
193         i = MIN(lcnt, ccnt);
194         if (i < 4) {
195                 endwin();
196                 errx(1, "screen too small for a fair game.");
197         }
198
199         /*
200          * chunk is the amount of money the user gets for each $.
201          * The formula below tries to be fair for various screen sizes.
202          * We only pay attention to the smaller of the 2 edges, since
203          * that seems to be the bottleneck.
204          * This formula is a hyperbola which includes the following points:
205          *      (24, $25)       (original scoring algorithm)
206          *      (12, $40)       (experimentally derived by the "feel")
207          *      (48, $15)       (a guess)
208          * This will give a 4x4 screen $99/shot.  We don't allow anything
209          * smaller than 4x4 because there is a 3x3 game where you can win
210          * an infinite amount of money.
211          */
212         if (i < 12)             /* otherwise it isn't fair */
213                 i = 12;
214         /*
215          * Compensate for border.  This really changes the game since
216          * the screen is two squares smaller but we want the default
217          * to be $25, and the high scores on small screens were a bit
218          * much anyway.
219          */
220         i += 2;
221         chunk = (675.0 / (i + 6)) + 2.5;        /* min screen edge */
222
223         signal(SIGINT, stop);
224
225         snrand(&finish);
226         snrand(&you);
227         snrand(&money);
228         snrand(&snake[0]);
229
230         for (i = 1; i < 6; i++)
231                 chase(&snake[i], &snake[i - 1]);
232         setup();
233         mainloop();
234         /* NOTREACHED */
235         return (0);
236 }
237
238 struct point *
239 point(struct point *ps, int x, int y)
240 {
241         ps->col = x;
242         ps->line = y;
243         return (ps);
244 }
245
246 /* Main command loop */
247 void
248 mainloop(void)
249 {
250         int     k;
251         int     repeat = 1;
252         int     lastc = 0;
253
254         for (;;) {
255                 int     c;
256
257                 /* Highlight you, not left & above */
258                 move(you.line + 1, you.col + 1);
259                 refresh();
260                 if (((c = getch()) <= '9') && (c >= '0')) {
261                         repeat = c - '0';
262                         while (((c = getch()) <= '9') && (c >= '0'))
263                                 repeat = 10 * repeat + (c - '0');
264                 } else {
265                         if (c != '.')
266                                 repeat = 1;
267                 }
268                 if (c == '.') {
269                         c = lastc;
270                 }
271                 if (!fast)
272                         flushi();
273                 lastc = c;
274                 switch (c) {
275                 case CTRL('z'):
276                         suspend();
277                         continue;
278                 case EOT:
279                 case 'x':
280                 case 0177:      /* del or end of file */
281                         endwin();
282                         length(moves);
283                         logit("quit");
284                         exit(0);
285                 case CTRL('l'):
286                         setup();
287                         winnings(cashvalue);
288                         continue;
289                 case 'p':
290                 case 'd':
291                         snap();
292                         continue;
293                 case 'w':
294                         spacewarp(0);
295                         continue;
296                 case 'A':
297                         repeat = you.col;
298                         c = 'h';
299                         break;
300                 case 'H':
301                 case 'S':
302                         repeat = you.col - money.col;
303                         c = 'h';
304                         break;
305                 case 'T':
306                         repeat = you.line;
307                         c = 'k';
308                         break;
309                 case 'K':
310                 case 'E':
311                         repeat = you.line - money.line;
312                         c = 'k';
313                         break;
314                 case 'P':
315                         repeat = ccnt - 1 - you.col;
316                         c = 'l';
317                         break;
318                 case 'L':
319                 case 'F':
320                         repeat = money.col - you.col;
321                         c = 'l';
322                         break;
323                 case 'B':
324                         repeat = lcnt - 1 - you.line;
325                         c = 'j';
326                         break;
327                 case 'J':
328                 case 'C':
329                         repeat = money.line - you.line;
330                         c = 'j';
331                         break;
332                 }
333                 for (k = 1; k <= repeat; k++) {
334                         moves++;
335                         switch (c) {
336                         case 's':
337                         case 'h':
338 #ifdef KEY_LEFT
339                         case KEY_LEFT:
340 #endif
341                         case '\b':
342                                 if (you.col > 0) {
343                                         if ((fast) || (k == 1))
344                                                 pchar(&you, ' ');
345                                         you.col--;
346                                         if ((fast) || (k == repeat) ||
347                                             (you.col == 0))
348                                                 pchar(&you, ME);
349                                 }
350                                 break;
351                         case 'f':
352                         case 'l':
353 #ifdef KEY_RIGHT
354                         case KEY_RIGHT:
355 #endif
356                         case ' ':
357                                 if (you.col < ccnt - 1) {
358                                         if ((fast) || (k == 1))
359                                                 pchar(&you, ' ');
360                                         you.col++;
361                                         if ((fast) || (k == repeat) ||
362                                             (you.col == ccnt - 1))
363                                                 pchar(&you, ME);
364                                 }
365                                 break;
366                         case CTRL('p'):
367                         case 'e':
368                         case 'k':
369 #ifdef KEY_UP
370                         case KEY_UP:
371 #endif
372                         case 'i':
373                                 if (you.line > 0) {
374                                         if ((fast) || (k == 1))
375                                                 pchar(&you, ' ');
376                                         you.line--;
377                                         if ((fast) || (k == repeat) ||
378                                             (you.line == 0))
379                                                 pchar(&you, ME);
380                                 }
381                                 break;
382                         case CTRL('n'):
383                         case 'c':
384                         case 'j':
385 #ifdef KEY_DOWN
386                         case KEY_DOWN:
387 #endif
388                         case LF:
389                         case 'm':
390                                 if (you.line + 1 < lcnt) {
391                                         if ((fast) || (k == 1))
392                                                 pchar(&you, ' ');
393                                         you.line++;
394                                         if ((fast) || (k == repeat) ||
395                                             (you.line == lcnt - 1))
396                                                 pchar(&you, ME);
397                                 }
398                                 break;
399                         }
400
401                         if (same(&you, &money)) {
402                                 loot += 25;
403                                 if (k < repeat)
404                                         pchar(&you, ' ');
405                                 do {
406                                         snrand(&money);
407                                 } while ((money.col == finish.col &&
408                                     money.line == finish.line) ||
409                                     (money.col < 5 && money.line == 0) ||
410                                     (money.col == you.col &&
411                                         money.line == you.line));
412                                 pchar(&money, TREASURE);
413                                 winnings(cashvalue);
414                                 continue;
415                         }
416                         if (same(&you, &finish)) {
417                                 win(&finish);
418                                 flushi();
419                                 endwin();
420                                 printf("You have won with $%d.\n", cashvalue);
421                                 fflush(stdout);
422                                 logit("won");
423                                 post(cashvalue, 1);
424                                 length(moves);
425                                 exit(0);
426                         }
427                         if (pushsnake())
428                                 break;
429                 }
430         }
431 }
432
433 /*
434  * setup the board
435  */
436 void
437 setup(void)
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(void)
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 void
469 snrand(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 int
498 post(int iscore, int 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), SEEK_SET);
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), SEEK_SET);
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 void
572 flushi(void)
573 {
574         tcflush(0, TCIFLUSH);
575 }
576
577 int mx[8] = {
578         0, 1, 1, 1, 0, -1, -1, -1
579 };
580 int my[8] = {
581         -1, -1, 0, 1, 1, 1, 0, -1
582 };
583 float absv[8] = {
584         1, 1.4, 1, 1.4, 1, 1.4, 1, 1.4
585 };
586 int oldw = 0;
587
588 void
589 chase(struct point *np, struct point *sp)
590 {
591         /* this algorithm has bugs; otherwise the snake would get too good */
592         struct point d;
593         int w, i, wt[8];
594         double v1, v2, vp, max;
595         point(&d, you.col - sp->col, you.line - sp->line);
596         v1 = sqrt((double)(d.col * d.col + d.line * d.line));
597         w = 0;
598         max = 0;
599         for (i = 0; i < 8; i++) {
600                 vp = d.col * mx[i] + d.line * my[i];
601                 v2 = absv[i];
602                 if (v1 > 0)
603                         vp = ((double)vp) / (v1 * v2);
604                 else
605                         vp = 1.0;
606                 if (vp > max) {
607                         max = vp;
608                         w = i;
609                 }
610         }
611         for (i = 0; i < 8; i++) {
612                 point(&d, sp->col + mx[i], sp->line + my[i]);
613                 wt[i] = 0;
614                 if (d.col < 0 || d.col >= ccnt || d.line < 0 || d.line >= lcnt)
615                         continue;
616                 /*
617                  * Change to allow snake to eat you if you're on the money,
618                  * otherwise, you can just crouch there until the snake goes
619                  * away.  Not positive it's right.
620                  *
621                  * if (d.line == 0 && d.col < 5) continue;
622                  */
623                 if (same(&d, &money))
624                         continue;
625                 if (same(&d, &finish))
626                         continue;
627                 wt[i] = i == w ? loot / 10 : 1;
628                 if (i == oldw)
629                         wt[i] += loot / 20;
630         }
631         for (w = i = 0; i < 8; i++)
632                 w += wt[i];
633         vp = ((random() >> 6) & 01777) % w;
634         for (i = 0; i < 8; i++)
635                 if (vp < wt[i])
636                         break;
637                 else
638                         vp -= wt[i];
639         if (i == 8) {
640                 printw("failure\n");
641                 i = 0;
642                 while (wt[i] == 0)
643                         i++;
644         }
645         oldw = w = i;
646         point(np, sp->col + mx[w], sp->line + my[w]);
647 }
648
649 void
650 spacewarp(int w)
651 {
652         struct point p;
653         int j;
654         const char   *str;
655
656         snrand(&you);
657         point(&p, COLS / 2 - 8, LINES / 2 - 1);
658         if (p.col < 0)
659                 p.col = 0;
660         if (p.line < 0)
661                 p.line = 0;
662         if (w) {
663                 str = "BONUS!!!";
664                 loot = loot - penalty;
665                 penalty = 0;
666         } else {
667                 str = "SPACE WARP!!!";
668                 penalty += loot / PENALTY;
669         }
670         for (j = 0; j < 3; j++) {
671                 erase();
672                 refresh();
673                 delay(5);
674                 mvaddstr(p.line + 1, p.col + 1, str);
675                 refresh();
676                 delay(10);
677         }
678         setup();
679         winnings(cashvalue);
680 }
681
682 void
683 snap(void)
684 {
685         if (!stretch(&money))
686                 if (!stretch(&finish)) {
687                         pchar(&you, '?');
688                         refresh();
689                         delay(10);
690                         pchar(&you, ME);
691                 }
692         refresh();
693 }
694
695 int
696 stretch(const struct point *ps)
697 {
698         struct point p;
699
700         point(&p, you.col, you.line);
701         if ((abs(ps->col - you.col) < (ccnt / 12)) && (you.line != ps->line)) {
702                 if (you.line < ps->line) {
703                         for (p.line = you.line + 1; p.line <= ps->line; p.line++)
704                                 pchar(&p, 'v');
705                         refresh();
706                         delay(10);
707                         for (; p.line > you.line; p.line--)
708                                 chk(&p);
709                 } else {
710                         for (p.line = you.line - 1; p.line >= ps->line; p.line--)
711                                 pchar(&p, '^');
712                         refresh();
713                         delay(10);
714                         for (; p.line < you.line; p.line++)
715                                 chk(&p);
716                 }
717                 return (1);
718         } else
719                 if ((abs(ps->line - you.line) < (lcnt / 7))
720                     && (you.col != ps->col)) {
721                         p.line = you.line;
722                         if (you.col < ps->col) {
723                                 for (p.col = you.col + 1; p.col <= ps->col; p.col++)
724                                         pchar(&p, '>');
725                                 refresh();
726                                 delay(10);
727                                 for (; p.col > you.col; p.col--)
728                                         chk(&p);
729                         } else {
730                                 for (p.col = you.col - 1; p.col >= ps->col; p.col--)
731                                         pchar(&p, '<');
732                                 refresh();
733                                 delay(10);
734                                 for (; p.col < you.col; p.col++)
735                                         chk(&p);
736                         }
737                         return (1);
738                 }
739         return (0);
740 }
741
742 void
743 surround(struct point *ps)
744 {
745         int j;
746
747         if (ps->col == 0)
748                 ps->col++;
749         if (ps->line == 0)
750                 ps->line++;
751         if (ps->line == LINES - 1)
752                 ps->line--;
753         if (ps->col == COLS - 1)
754                 ps->col--;
755         mvaddstr(ps->line, ps->col, "/*\\");
756         mvaddstr(ps->line + 1, ps->col, "* *");
757         mvaddstr(ps->line + 2, ps->col, "\\*/");
758         for (j = 0; j < 20; j++) {
759                 pchar(ps, '@');
760                 refresh();
761                 delay(1);
762                 pchar(ps, ' ');
763                 refresh();
764                 delay(1);
765         }
766         if (post(cashvalue, 0)) {
767                 mvaddstr(ps->line, ps->col, "   ");
768                 mvaddstr(ps->line + 1, ps->col, "o.o");
769                 mvaddstr(ps->line + 2, ps->col, "\\_/");
770                 refresh();
771                 delay(6);
772                 mvaddstr(ps->line, ps->col, "   ");
773                 mvaddstr(ps->line + 1, ps->col, "o.-");
774                 mvaddstr(ps->line + 2, ps->col, "\\_/");
775                 refresh();
776                 delay(6);
777         }
778         mvaddstr(ps->line, ps->col, "   ");
779         mvaddstr(ps->line + 1, ps->col, "o.o");
780         mvaddstr(ps->line + 2, ps->col, "\\_/");
781         refresh();
782         delay(6);
783 }
784
785 void
786 win(const struct point *ps)
787 {
788         struct point x;
789         int j, k;
790         int boxsize;    /* actually diameter of box, not radius */
791
792         boxsize = fast ? 10 : 4;
793         point(&x, ps->col, ps->line);
794         for (j = 1; j < boxsize; j++) {
795                 for (k = 0; k < j; k++) {
796                         pchar(&x, '#');
797                         x.line--;
798                 }
799                 for (k = 0; k < j; k++) {
800                         pchar(&x, '#');
801                         x.col++;
802                 }
803                 j++;
804                 for (k = 0; k < j; k++) {
805                         pchar(&x, '#');
806                         x.line++;
807                 }
808                 for (k = 0; k < j; k++) {
809                         pchar(&x, '#');
810                         x.col--;
811                 }
812                 refresh();
813                 delay(1);
814         }
815 }
816
817 int
818 pushsnake(void)
819 {
820         int i, bonus;
821         int issame = 0;
822         struct point tmp;
823
824         /*
825          * My manual says times doesn't return a value.  Furthermore, the
826          * snake should get his turn every time no matter if the user is
827          * on a fast terminal with typematic keys or not.
828          * So I have taken the call to times out.
829          */
830         for (i = 4; i >= 0; i--)
831                 if (same(&snake[i], &snake[5]))
832                         issame++;
833         if (!issame)
834                 pchar(&snake[5], ' ');
835         /* Need the following to catch you if you step on the snake's tail */
836         tmp.col = snake[5].col;
837         tmp.line = snake[5].line;
838         for (i = 4; i >= 0; i--)
839                 snake[i + 1] = snake[i];
840         chase(&snake[0], &snake[1]);
841         pchar(&snake[1], SNAKETAIL);
842         pchar(&snake[0], SNAKEHEAD);
843         for (i = 0; i < 6; i++) {
844                 if (same(&snake[i], &you) || same(&tmp, &you)) {
845                         surround(&you);
846                         i = (cashvalue) % 10;
847                         bonus = ((random() >> 8) & 0377) % 10;
848                         mvprintw(lcnt + 1, 0, "%d\n", bonus);
849                         refresh();
850                         delay(30);
851                         if (bonus == i) {
852                                 spacewarp(1);
853                                 logit("bonus");
854                                 flushi();
855                                 return (1);
856                         }
857                         flushi();
858                         endwin();
859                         if (loot >= penalty) {
860                                 printf("\nYou and your $%d have been eaten\n",
861                                     cashvalue);
862                         } else {
863                                 printf("\nThe snake ate you.  You owe $%d.\n",
864                                     -cashvalue);
865                         }
866                         logit("eaten");
867                         length(moves);
868                         exit(0);
869                 }
870         }
871         return (0);
872 }
873
874 int
875 chk(const struct point *sp)
876 {
877         int j;
878
879         if (same(sp, &money)) {
880                 pchar(sp, TREASURE);
881                 return (2);
882         }
883         if (same(sp, &finish)) {
884                 pchar(sp, GOAL);
885                 return (3);
886         }
887         if (same(sp, &snake[0])) {
888                 pchar(sp, SNAKEHEAD);
889                 return (4);
890         }
891         for (j = 1; j < 6; j++) {
892                 if (same(sp, &snake[j])) {
893                         pchar(sp, SNAKETAIL);
894                         return (4);
895                 }
896         }
897         if ((sp->col < 4) && (sp->line == 0)) {
898                 winnings(cashvalue);
899                 if ((you.line == 0) && (you.col < 4))
900                         pchar(&you, ME);
901                 return (5);
902         }
903         if (same(sp, &you)) {
904                 pchar(sp, ME);
905                 return (1);
906         }
907         pchar(sp, ' ');
908         return (0);
909 }
910
911 void
912 winnings(int won)
913 {
914         if (won > 0) {
915                 mvprintw(1, 1, "$%d", won);
916         }
917 }
918
919 void
920 stop(int dummy __unused)
921 {
922         signal(SIGINT, SIG_IGN);
923         endwin();
924         length(moves);
925         exit(0);
926 }
927
928 void
929 suspend(void)
930 {
931         endwin();
932         kill(getpid(), SIGTSTP);
933         refresh();
934         winnings(cashvalue);
935 }
936
937 void
938 length(int num)
939 {
940         printf("You made %d moves.\n", num);
941 }
942
943 void
944 logit(const char *msg)
945 {
946         time_t t;
947
948         if (logfile != NULL) {
949                 time(&t);
950                 fprintf(logfile, "%s $%d %dx%d %s %s",
951                     getlogin(), cashvalue, lcnt, ccnt, msg, ctime(&t));
952                 fflush(logfile);
953         }
954 }