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