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