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