2 * Copyright (c) 1980, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
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 $
35 * snake - crt hack game.
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.
42 * cc -O snake.c move.c -o snake -lm -ltermlib
45 #include <sys/param.h>
53 #include <sys/types.h>
61 #include "pathnames.h"
63 #define cashvalue chunk*(loot-penalty)/25
69 #define same(s1, s2) ((s1)->line == (s2)->line && (s1)->col == (s2)->col)
71 #define PENALTY 10 /* % penalty for invoking spacewarp */
84 #define MIN(a, b) ((a) < (b) ? (a) : (b))
87 #define pchar(point, c) mvaddch((point)->line + 1, (point)->col + 1, (c))
88 #define delay(t) usleep(t * 50000);
93 struct point snake[6];
102 int lcnt, ccnt; /* user's idea of screen size */
103 int chunk; /* amount of money given at a time */
105 void chase(struct point *, struct point *);
106 int chk(const struct point *);
111 void logit(const char *);
112 int main(int, char **);
113 void mainloop(void) __attribute__((__noreturn__));
114 struct point *point(struct point *, int, int);
117 void right(const struct point *);
120 void snrand(struct point *);
122 void stop(int) __attribute__((__noreturn__));
123 int stretch(const struct point *);
124 void surround(struct point *);
126 void win(const struct point *);
130 main(int argc, char **argv)
135 /* Open score files then revoke setgid privileges */
136 rawscores = open(_PATH_RAWSCORES, O_RDWR|O_CREAT, 0664);
138 warn("open %s", _PATH_RAWSCORES);
140 } else if (rawscores < 3)
142 logfile = fopen(_PATH_LOGFILE, "a");
143 if (logfile == NULL) {
144 warn("fopen %s", _PATH_LOGFILE);
151 while ((ch = getopt(argc, argv, "l:w:t")) != -1)
158 case 'w': /* width */
161 case 'l': /* length */
170 fputs("usage: snake [-d seed] [-w width] [-l length] [-t]\n", stderr);
172 fputs("usage: snake [-w width] [-l length] [-t]\n", stderr);
184 keypad(stdscr, TRUE);
186 if (!lcnt || lcnt > LINES - 2)
188 if (!ccnt || ccnt > COLS - 2)
194 errx(1, "screen too small for a fair game.");
198 * chunk is the amount of money the user gets for each $.
199 * The formula below tries to be fair for various screen sizes.
200 * We only pay attention to the smaller of the 2 edges, since
201 * that seems to be the bottleneck.
202 * This formula is a hyperbola which includes the following points:
203 * (24, $25) (original scoring algorithm)
204 * (12, $40) (experimentally derived by the "feel")
205 * (48, $15) (a guess)
206 * This will give a 4x4 screen $99/shot. We don't allow anything
207 * smaller than 4x4 because there is a 3x3 game where you can win
208 * an infinite amount of money.
210 if (i < 12) /* otherwise it isn't fair */
213 * Compensate for border. This really changes the game since
214 * the screen is two squares smaller but we want the default
215 * to be $25, and the high scores on small screens were a bit
219 chunk = (675.0 / (i + 6)) + 2.5; /* min screen edge */
221 signal(SIGINT, stop);
228 for (i = 1; i < 6; i++)
229 chase(&snake[i], &snake[i - 1]);
237 point(struct point *ps, int x, int y)
244 /* Main command loop */
255 /* Highlight you, not left & above */
256 move(you.line + 1, you.col + 1);
258 if (((c = getch()) <= '9') && (c >= '0')) {
260 while (((c = getch()) <= '9') && (c >= '0'))
261 repeat = 10 * repeat + (c - '0');
278 case 0177: /* del or end of file */
300 repeat = you.col - money.col;
309 repeat = you.line - money.line;
313 repeat = ccnt - 1 - you.col;
318 repeat = money.col - you.col;
322 repeat = lcnt - 1 - you.line;
327 repeat = money.line - you.line;
331 for (k = 1; k <= repeat; k++) {
341 if ((fast) || (k == 1))
344 if ((fast) || (k == repeat) ||
355 if (you.col < ccnt - 1) {
356 if ((fast) || (k == 1))
359 if ((fast) || (k == repeat) ||
360 (you.col == ccnt - 1))
372 if ((fast) || (k == 1))
375 if ((fast) || (k == repeat) ||
388 if (you.line + 1 < lcnt) {
389 if ((fast) || (k == 1))
392 if ((fast) || (k == repeat) ||
393 (you.line == lcnt - 1))
399 if (same(&you, &money)) {
405 } while ((money.col == finish.col &&
406 money.line == finish.line) ||
407 (money.col < 5 && money.line == 0) ||
408 (money.col == you.col &&
409 money.line == you.line));
410 pchar(&money, TREASURE);
414 if (same(&you, &finish)) {
418 printf("You have won with $%d.\n", cashvalue);
441 pchar(&finish, GOAL);
442 pchar(&money, TREASURE);
443 for (i = 1; i < 6; i++) {
444 pchar(&snake[i], SNAKETAIL);
446 pchar(&snake[0], SNAKEHEAD);
456 for (i = 1; i <= ccnt; i++) {
458 mvaddch(lcnt + 1, i, '-');
460 for (i = 0; i <= lcnt + 1; i++) {
462 mvaddch(i, ccnt + 1, '|');
467 snrand(struct point *sp)
473 p.col = random() % ccnt;
474 p.line = random() % lcnt;
476 /* make sure it's not on top of something else */
477 if (p.line == 0 && p.col < 5)
481 if (same(&p, &money))
483 if (same(&p, &finish))
485 for (i = 0; i < 6; i++)
486 if (same(&p, &snake[i]))
496 post(int iscore, int flag)
498 short score = iscore;
501 short allbwho = 0, allbscore = 0;
504 /* I want to printf() the scores for terms that clear on cook(),
505 * but this routine also gets called with flag == 0 to see if
506 * the snake should wink. If (flag) then we're at game end and
510 * Neg uid, 0, and 1 cannot have scores recorded.
512 if ((uid = getuid()) <= 1) {
514 printf("No saved scores for uid %d.\n", uid);
518 /* Error reported earlier */
521 /* Figure out what happened in the past */
522 read(rawscores, &allbscore, sizeof(short));
523 read(rawscores, &allbwho, sizeof(short));
524 lseek(rawscores, ((off_t)uid)*sizeof(short), SEEK_SET);
525 read(rawscores, &oldbest, sizeof(short));
527 lseek(rawscores, 0, SEEK_SET);
528 return (score > oldbest ? 1 : 0);
531 /* Update this jokers best */
532 if (score > oldbest) {
533 lseek(rawscores, ((off_t)uid)*sizeof(short), SEEK_SET);
534 write(rawscores, &score, sizeof(short));
535 printf("You bettered your previous best of $%d\n", oldbest);
537 printf("Your best to date is $%d\n", oldbest);
539 /* See if we have a new champ */
540 p = getpwuid(allbwho);
541 if (score > allbscore) {
542 lseek(rawscores, 0, SEEK_SET);
543 write(rawscores, &score, sizeof(short));
544 write(rawscores, &uid, sizeof(short));
547 printf("You beat %s's old record of $%d!\n",
548 p->pw_name, allbscore);
550 printf("You beat (%d)'s old record of $%d!\n",
551 (int)allbwho, allbscore);
554 printf("You set a new record!\n");
556 printf("The highest is %s with $%d\n", p->pw_name, allbscore);
558 printf("The highest is (%d) with $%d\n", (int)allbwho,
560 lseek(rawscores, 0, SEEK_SET);
565 * Flush typeahead to keep from buffering a bunch of chars and then
566 * overshooting. This loses horribly at 9600 baud, but works nicely
567 * if the terminal gets behind.
572 tcflush(0, TCIFLUSH);
576 0, 1, 1, 1, 0, -1, -1, -1
579 -1, -1, 0, 1, 1, 1, 0, -1
582 1, 1.4, 1, 1.4, 1, 1.4, 1, 1.4
587 chase(struct point *np, struct point *sp)
589 /* this algorithm has bugs; otherwise the snake would get too good */
592 double v1, v2, vp, max;
593 point(&d, you.col - sp->col, you.line - sp->line);
594 v1 = sqrt((double)(d.col * d.col + d.line * d.line));
597 for (i = 0; i < 8; i++) {
598 vp = d.col * mx[i] + d.line * my[i];
601 vp = ((double)vp) / (v1 * v2);
609 for (i = 0; i < 8; i++) {
610 point(&d, sp->col + mx[i], sp->line + my[i]);
612 if (d.col < 0 || d.col >= ccnt || d.line < 0 || d.line >= lcnt)
615 * Change to allow snake to eat you if you're on the money,
616 * otherwise, you can just crouch there until the snake goes
617 * away. Not positive it's right.
619 * if (d.line == 0 && d.col < 5) continue;
621 if (same(&d, &money))
623 if (same(&d, &finish))
625 wt[i] = i == w ? loot / 10 : 1;
629 for (w = i = 0; i < 8; i++)
631 vp = ((random() >> 6) & 01777) % w;
632 for (i = 0; i < 8; i++)
644 point(np, sp->col + mx[w], sp->line + my[w]);
655 point(&p, COLS / 2 - 8, LINES / 2 - 1);
662 loot = loot - penalty;
665 str = "SPACE WARP!!!";
666 penalty += loot / PENALTY;
668 for (j = 0; j < 3; j++) {
672 mvaddstr(p.line + 1, p.col + 1, str);
683 if (!stretch(&money))
684 if (!stretch(&finish)) {
694 stretch(const struct point *ps)
698 point(&p, you.col, you.line);
699 if ((abs(ps->col - you.col) < (ccnt / 12)) && (you.line != ps->line)) {
700 if (you.line < ps->line) {
701 for (p.line = you.line + 1; p.line <= ps->line; p.line++)
705 for (; p.line > you.line; p.line--)
708 for (p.line = you.line - 1; p.line >= ps->line; p.line--)
712 for (; p.line < you.line; p.line++)
717 if ((abs(ps->line - you.line) < (lcnt / 7))
718 && (you.col != ps->col)) {
720 if (you.col < ps->col) {
721 for (p.col = you.col + 1; p.col <= ps->col; p.col++)
725 for (; p.col > you.col; p.col--)
728 for (p.col = you.col - 1; p.col >= ps->col; p.col--)
732 for (; p.col < you.col; p.col++)
741 surround(struct point *ps)
749 if (ps->line == LINES - 1)
751 if (ps->col == COLS - 1)
753 mvaddstr(ps->line, ps->col, "/*\\");
754 mvaddstr(ps->line + 1, ps->col, "* *");
755 mvaddstr(ps->line + 2, ps->col, "\\*/");
756 for (j = 0; j < 20; j++) {
764 if (post(cashvalue, 0)) {
765 mvaddstr(ps->line, ps->col, " ");
766 mvaddstr(ps->line + 1, ps->col, "o.o");
767 mvaddstr(ps->line + 2, ps->col, "\\_/");
770 mvaddstr(ps->line, ps->col, " ");
771 mvaddstr(ps->line + 1, ps->col, "o.-");
772 mvaddstr(ps->line + 2, ps->col, "\\_/");
776 mvaddstr(ps->line, ps->col, " ");
777 mvaddstr(ps->line + 1, ps->col, "o.o");
778 mvaddstr(ps->line + 2, ps->col, "\\_/");
784 win(const struct point *ps)
788 int boxsize; /* actually diameter of box, not radius */
790 boxsize = fast ? 10 : 4;
791 point(&x, ps->col, ps->line);
792 for (j = 1; j < boxsize; j++) {
793 for (k = 0; k < j; k++) {
797 for (k = 0; k < j; k++) {
802 for (k = 0; k < j; k++) {
806 for (k = 0; k < j; k++) {
823 * My manual says times doesn't return a value. Furthermore, the
824 * snake should get his turn every time no matter if the user is
825 * on a fast terminal with typematic keys or not.
826 * So I have taken the call to times out.
828 for (i = 4; i >= 0; i--)
829 if (same(&snake[i], &snake[5]))
832 pchar(&snake[5], ' ');
833 /* Need the following to catch you if you step on the snake's tail */
834 tmp.col = snake[5].col;
835 tmp.line = snake[5].line;
836 for (i = 4; i >= 0; i--)
837 snake[i + 1] = snake[i];
838 chase(&snake[0], &snake[1]);
839 pchar(&snake[1], SNAKETAIL);
840 pchar(&snake[0], SNAKEHEAD);
841 for (i = 0; i < 6; i++) {
842 if (same(&snake[i], &you) || same(&tmp, &you)) {
844 i = (cashvalue) % 10;
845 bonus = ((random() >> 8) & 0377) % 10;
846 mvprintw(lcnt + 1, 0, "%d\n", bonus);
857 if (loot >= penalty) {
858 printf("\nYou and your $%d have been eaten\n",
861 printf("\nThe snake ate you. You owe $%d.\n",
873 chk(const struct point *sp)
877 if (same(sp, &money)) {
881 if (same(sp, &finish)) {
885 if (same(sp, &snake[0])) {
886 pchar(sp, SNAKEHEAD);
889 for (j = 1; j < 6; j++) {
890 if (same(sp, &snake[j])) {
891 pchar(sp, SNAKETAIL);
895 if ((sp->col < 4) && (sp->line == 0)) {
897 if ((you.line == 0) && (you.col < 4))
901 if (same(sp, &you)) {
913 mvprintw(1, 1, "$%d", won);
918 stop(int dummy __unused)
920 signal(SIGINT, SIG_IGN);
930 kill(getpid(), SIGTSTP);
938 printf("You made %d moves.\n", num);
942 logit(const char *msg)
946 if (logfile != NULL) {
948 fprintf(logfile, "%s $%d %dx%d %s %s",
949 getlogin(), cashvalue, lcnt, ccnt, msg, ctime(&t));