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