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