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 $
32 * $DragonFly: src/games/snake/snake/snake.c,v 1.4 2006/09/03 23:47:56 pavalos Exp $
36 * snake - crt hack game.
38 * You move around the screen with arrow keys trying to pick up money
39 * without getting eaten by the snake. hjkl work as in vi in place of
40 * arrow keys. You can leave at the exit any time.
43 * cc -O snake.c move.c -o snake -lm -ltermlib
46 #include <sys/param.h>
54 #include <sys/types.h>
63 #include "pathnames.h"
65 #define cashvalue chunk*(loot-penalty)/25
71 #define same(s1, s2) ((s1)->line == (s2)->line && (s1)->col == (s2)->col)
73 #define PENALTY 10 /* % penalty for invoking spacewarp */
86 #define MIN(a, b) ((a) < (b) ? (a) : (b))
89 #define pchar(point, c) mvaddch((point)->line + 1, (point)->col + 1, (c))
90 #define delay(t) usleep(t * 50000);
95 struct point snake[6];
104 int lcnt, ccnt; /* user's idea of screen size */
105 int chunk; /* amount of money given at a time */
107 void chase(struct point *, struct point *);
108 int chk(const struct point *);
113 void logit(const char *);
114 int main(int, char **);
115 void mainloop(void) __attribute__((__noreturn__));
116 struct point *point(struct point *, int, int);
119 void right(const struct point *);
122 void snrand(struct point *);
124 void stop(int) __attribute__((__noreturn__));
125 int stretch(const struct point *);
126 void surround(struct point *);
128 void win(const struct point *);
132 main(int argc, char **argv)
137 /* Open score files then revoke setgid privileges */
138 rawscores = open(_PATH_RAWSCORES, O_RDWR|O_CREAT, 0664);
140 warn("open %s", _PATH_RAWSCORES);
142 } else if (rawscores < 3)
144 logfile = fopen(_PATH_LOGFILE, "a");
145 if (logfile == NULL) {
146 warn("fopen %s", _PATH_LOGFILE);
153 while ((ch = getopt(argc, argv, "l:w:t")) != -1)
160 case 'w': /* width */
163 case 'l': /* length */
172 fputs("usage: snake [-d seed] [-w width] [-l length] [-t]\n", stderr);
174 fputs("usage: snake [-w width] [-l length] [-t]\n", stderr);
186 keypad(stdscr, TRUE);
188 if (!lcnt || lcnt > LINES - 2)
190 if (!ccnt || ccnt > COLS - 2)
196 errx(1, "screen too small for a fair game.");
200 * chunk is the amount of money the user gets for each $.
201 * The formula below tries to be fair for various screen sizes.
202 * We only pay attention to the smaller of the 2 edges, since
203 * that seems to be the bottleneck.
204 * This formula is a hyperbola which includes the following points:
205 * (24, $25) (original scoring algorithm)
206 * (12, $40) (experimentally derived by the "feel")
207 * (48, $15) (a guess)
208 * This will give a 4x4 screen $99/shot. We don't allow anything
209 * smaller than 4x4 because there is a 3x3 game where you can win
210 * an infinite amount of money.
212 if (i < 12) /* otherwise it isn't fair */
215 * Compensate for border. This really changes the game since
216 * the screen is two squares smaller but we want the default
217 * to be $25, and the high scores on small screens were a bit
221 chunk = (675.0 / (i + 6)) + 2.5; /* min screen edge */
223 signal(SIGINT, stop);
230 for (i = 1; i < 6; i++)
231 chase(&snake[i], &snake[i - 1]);
239 point(struct point *ps, int x, int y)
246 /* Main command loop */
257 /* Highlight you, not left & above */
258 move(you.line + 1, you.col + 1);
260 if (((c = getch()) <= '9') && (c >= '0')) {
262 while (((c = getch()) <= '9') && (c >= '0'))
263 repeat = 10 * repeat + (c - '0');
280 case 0177: /* del or end of file */
302 repeat = you.col - money.col;
311 repeat = you.line - money.line;
315 repeat = ccnt - 1 - you.col;
320 repeat = money.col - you.col;
324 repeat = lcnt - 1 - you.line;
329 repeat = money.line - you.line;
333 for (k = 1; k <= repeat; k++) {
343 if ((fast) || (k == 1))
346 if ((fast) || (k == repeat) ||
357 if (you.col < ccnt - 1) {
358 if ((fast) || (k == 1))
361 if ((fast) || (k == repeat) ||
362 (you.col == ccnt - 1))
374 if ((fast) || (k == 1))
377 if ((fast) || (k == repeat) ||
390 if (you.line + 1 < lcnt) {
391 if ((fast) || (k == 1))
394 if ((fast) || (k == repeat) ||
395 (you.line == lcnt - 1))
401 if (same(&you, &money)) {
407 } while ((money.col == finish.col &&
408 money.line == finish.line) ||
409 (money.col < 5 && money.line == 0) ||
410 (money.col == you.col &&
411 money.line == you.line));
412 pchar(&money, TREASURE);
416 if (same(&you, &finish)) {
420 printf("You have won with $%d.\n", cashvalue);
443 pchar(&finish, GOAL);
444 pchar(&money, TREASURE);
445 for (i = 1; i < 6; i++) {
446 pchar(&snake[i], SNAKETAIL);
448 pchar(&snake[0], SNAKEHEAD);
458 for (i = 1; i <= ccnt; i++) {
460 mvaddch(lcnt + 1, i, '-');
462 for (i = 0; i <= lcnt + 1; i++) {
464 mvaddch(i, ccnt + 1, '|');
469 snrand(struct point *sp)
475 p.col = random() % ccnt;
476 p.line = random() % lcnt;
478 /* make sure it's not on top of something else */
479 if (p.line == 0 && p.col < 5)
483 if (same(&p, &money))
485 if (same(&p, &finish))
487 for (i = 0; i < 6; i++)
488 if (same(&p, &snake[i]))
498 post(int iscore, int flag)
500 short score = iscore;
503 short allbwho = 0, allbscore = 0;
506 /* I want to printf() the scores for terms that clear on cook(),
507 * but this routine also gets called with flag == 0 to see if
508 * the snake should wink. If (flag) then we're at game end and
512 * Neg uid, 0, and 1 cannot have scores recorded.
514 if ((uid = getuid()) <= 1) {
516 printf("No saved scores for uid %d.\n", uid);
520 /* Error reported earlier */
523 /* Figure out what happened in the past */
524 read(rawscores, &allbscore, sizeof(short));
525 read(rawscores, &allbwho, sizeof(short));
526 lseek(rawscores, ((off_t)uid)*sizeof(short), SEEK_SET);
527 read(rawscores, &oldbest, sizeof(short));
529 lseek(rawscores, 0, SEEK_SET);
530 return (score > oldbest ? 1 : 0);
533 /* Update this jokers best */
534 if (score > oldbest) {
535 lseek(rawscores, ((off_t)uid)*sizeof(short), SEEK_SET);
536 write(rawscores, &score, sizeof(short));
537 printf("You bettered your previous best of $%d\n", oldbest);
539 printf("Your best to date is $%d\n", oldbest);
541 /* See if we have a new champ */
542 p = getpwuid(allbwho);
543 if (score > allbscore) {
544 lseek(rawscores, 0, SEEK_SET);
545 write(rawscores, &score, sizeof(short));
546 write(rawscores, &uid, sizeof(short));
549 printf("You beat %s's old record of $%d!\n",
550 p->pw_name, allbscore);
552 printf("You beat (%d)'s old record of $%d!\n",
553 (int)allbwho, allbscore);
556 printf("You set a new record!\n");
558 printf("The highest is %s with $%d\n", p->pw_name, allbscore);
560 printf("The highest is (%d) with $%d\n", (int)allbwho,
562 lseek(rawscores, 0, SEEK_SET);
567 * Flush typeahead to keep from buffering a bunch of chars and then
568 * overshooting. This loses horribly at 9600 baud, but works nicely
569 * if the terminal gets behind.
574 tcflush(0, TCIFLUSH);
578 0, 1, 1, 1, 0, -1, -1, -1
581 -1, -1, 0, 1, 1, 1, 0, -1
584 1, 1.4, 1, 1.4, 1, 1.4, 1, 1.4
589 chase(struct point *np, struct point *sp)
591 /* this algorithm has bugs; otherwise the snake would get too good */
594 double v1, v2, vp, max;
595 point(&d, you.col - sp->col, you.line - sp->line);
596 v1 = sqrt((double)(d.col * d.col + d.line * d.line));
599 for (i = 0; i < 8; i++) {
600 vp = d.col * mx[i] + d.line * my[i];
603 vp = ((double)vp) / (v1 * v2);
611 for (i = 0; i < 8; i++) {
612 point(&d, sp->col + mx[i], sp->line + my[i]);
614 if (d.col < 0 || d.col >= ccnt || d.line < 0 || d.line >= lcnt)
617 * Change to allow snake to eat you if you're on the money,
618 * otherwise, you can just crouch there until the snake goes
619 * away. Not positive it's right.
621 * if (d.line == 0 && d.col < 5) continue;
623 if (same(&d, &money))
625 if (same(&d, &finish))
627 wt[i] = i == w ? loot / 10 : 1;
631 for (w = i = 0; i < 8; i++)
633 vp = ((random() >> 6) & 01777) % w;
634 for (i = 0; i < 8; i++)
646 point(np, sp->col + mx[w], sp->line + my[w]);
657 point(&p, COLS / 2 - 8, LINES / 2 - 1);
664 loot = loot - penalty;
667 str = "SPACE WARP!!!";
668 penalty += loot / PENALTY;
670 for (j = 0; j < 3; j++) {
674 mvaddstr(p.line + 1, p.col + 1, str);
685 if (!stretch(&money))
686 if (!stretch(&finish)) {
696 stretch(const struct point *ps)
700 point(&p, you.col, you.line);
701 if ((abs(ps->col - you.col) < (ccnt / 12)) && (you.line != ps->line)) {
702 if (you.line < ps->line) {
703 for (p.line = you.line + 1; p.line <= ps->line; p.line++)
707 for (; p.line > you.line; p.line--)
710 for (p.line = you.line - 1; p.line >= ps->line; p.line--)
714 for (; p.line < you.line; p.line++)
719 if ((abs(ps->line - you.line) < (lcnt / 7))
720 && (you.col != ps->col)) {
722 if (you.col < ps->col) {
723 for (p.col = you.col + 1; p.col <= ps->col; p.col++)
727 for (; p.col > you.col; p.col--)
730 for (p.col = you.col - 1; p.col >= ps->col; p.col--)
734 for (; p.col < you.col; p.col++)
743 surround(struct point *ps)
751 if (ps->line == LINES - 1)
753 if (ps->col == COLS - 1)
755 mvaddstr(ps->line, ps->col, "/*\\");
756 mvaddstr(ps->line + 1, ps->col, "* *");
757 mvaddstr(ps->line + 2, ps->col, "\\*/");
758 for (j = 0; j < 20; j++) {
766 if (post(cashvalue, 0)) {
767 mvaddstr(ps->line, ps->col, " ");
768 mvaddstr(ps->line + 1, ps->col, "o.o");
769 mvaddstr(ps->line + 2, ps->col, "\\_/");
772 mvaddstr(ps->line, ps->col, " ");
773 mvaddstr(ps->line + 1, ps->col, "o.-");
774 mvaddstr(ps->line + 2, ps->col, "\\_/");
778 mvaddstr(ps->line, ps->col, " ");
779 mvaddstr(ps->line + 1, ps->col, "o.o");
780 mvaddstr(ps->line + 2, ps->col, "\\_/");
786 win(const struct point *ps)
790 int boxsize; /* actually diameter of box, not radius */
792 boxsize = fast ? 10 : 4;
793 point(&x, ps->col, ps->line);
794 for (j = 1; j < boxsize; j++) {
795 for (k = 0; k < j; k++) {
799 for (k = 0; k < j; k++) {
804 for (k = 0; k < j; k++) {
808 for (k = 0; k < j; k++) {
825 * My manual says times doesn't return a value. Furthermore, the
826 * snake should get his turn every time no matter if the user is
827 * on a fast terminal with typematic keys or not.
828 * So I have taken the call to times out.
830 for (i = 4; i >= 0; i--)
831 if (same(&snake[i], &snake[5]))
834 pchar(&snake[5], ' ');
835 /* Need the following to catch you if you step on the snake's tail */
836 tmp.col = snake[5].col;
837 tmp.line = snake[5].line;
838 for (i = 4; i >= 0; i--)
839 snake[i + 1] = snake[i];
840 chase(&snake[0], &snake[1]);
841 pchar(&snake[1], SNAKETAIL);
842 pchar(&snake[0], SNAKEHEAD);
843 for (i = 0; i < 6; i++) {
844 if (same(&snake[i], &you) || same(&tmp, &you)) {
846 i = (cashvalue) % 10;
847 bonus = ((random() >> 8) & 0377) % 10;
848 mvprintw(lcnt + 1, 0, "%d\n", bonus);
859 if (loot >= penalty) {
860 printf("\nYou and your $%d have been eaten\n",
863 printf("\nThe snake ate you. You owe $%d.\n",
875 chk(const struct point *sp)
879 if (same(sp, &money)) {
883 if (same(sp, &finish)) {
887 if (same(sp, &snake[0])) {
888 pchar(sp, SNAKEHEAD);
891 for (j = 1; j < 6; j++) {
892 if (same(sp, &snake[j])) {
893 pchar(sp, SNAKETAIL);
897 if ((sp->col < 4) && (sp->line == 0)) {
899 if ((you.line == 0) && (you.col < 4))
903 if (same(sp, &you)) {
915 mvprintw(1, 1, "$%d", won);
920 stop(int dummy __unused)
922 signal(SIGINT, SIG_IGN);
932 kill(getpid(), SIGTSTP);
940 printf("You made %d moves.\n", num);
944 logit(const char *msg)
948 if (logfile != NULL) {
950 fprintf(logfile, "%s $%d %dx%d %s %s",
951 getlogin(), cashvalue, lcnt, ccnt, msg, ctime(&t));