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. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * @(#) Copyright (c) 1980, 1993 The Regents of the University of California. All rights reserved.
34 * @(#)snake.c 8.2 (Berkeley) 1/7/94
35 * $FreeBSD: src/games/snake/snake/snake.c,v 1.11.2.1 2000/08/17 06:21:44 jhb Exp $
36 * $DragonFly: src/games/snake/snake/snake.c,v 1.4 2006/09/03 23:47:56 pavalos Exp $
40 * snake - crt hack game.
42 * You move around the screen with arrow keys trying to pick up money
43 * without getting eaten by the snake. hjkl work as in vi in place of
44 * arrow keys. You can leave at the exit any time.
47 * cc -O snake.c move.c -o snake -lm -ltermlib
50 #include <sys/param.h>
58 #include <sys/types.h>
67 #include "pathnames.h"
69 #define cashvalue chunk*(loot-penalty)/25
75 #define same(s1, s2) ((s1)->line == (s2)->line && (s1)->col == (s2)->col)
77 #define PENALTY 10 /* % penalty for invoking spacewarp */
90 #define MIN(a, b) ((a) < (b) ? (a) : (b))
93 #define pchar(point, c) mvaddch((point)->line + 1, (point)->col + 1, (c))
94 #define delay(t) usleep(t * 50000);
99 struct point snake
[6];
108 int lcnt
, ccnt
; /* user's idea of screen size */
109 int chunk
; /* amount of money given at a time */
111 void chase(struct point
*, struct point
*);
112 int chk(const struct point
*);
117 void logit(const char *);
118 int main(int, char **);
119 void mainloop(void) __attribute__((__noreturn__
));
120 struct point
*point(struct point
*, int, int);
123 void right(const struct point
*);
126 void snrand(struct point
*);
128 void stop(int) __attribute__((__noreturn__
));
129 int stretch(const struct point
*);
130 void surround(struct point
*);
132 void win(const struct point
*);
136 main(int argc
, char **argv
)
141 /* Open score files then revoke setgid privileges */
142 rawscores
= open(_PATH_RAWSCORES
, O_RDWR
|O_CREAT
, 0664);
144 warn("open %s", _PATH_RAWSCORES
);
146 } else if (rawscores
< 3)
148 logfile
= fopen(_PATH_LOGFILE
, "a");
149 if (logfile
== NULL
) {
150 warn("fopen %s", _PATH_LOGFILE
);
157 while ((ch
= getopt(argc
, argv
, "l:w:t")) != -1)
164 case 'w': /* width */
167 case 'l': /* length */
176 fputs("usage: snake [-d seed] [-w width] [-l length] [-t]\n", stderr
);
178 fputs("usage: snake [-w width] [-l length] [-t]\n", stderr
);
190 keypad(stdscr
, TRUE
);
192 if (!lcnt
|| lcnt
> LINES
- 2)
194 if (!ccnt
|| ccnt
> COLS
- 2)
200 errx(1, "screen too small for a fair game.");
204 * chunk is the amount of money the user gets for each $.
205 * The formula below tries to be fair for various screen sizes.
206 * We only pay attention to the smaller of the 2 edges, since
207 * that seems to be the bottleneck.
208 * This formula is a hyperbola which includes the following points:
209 * (24, $25) (original scoring algorithm)
210 * (12, $40) (experimentally derived by the "feel")
211 * (48, $15) (a guess)
212 * This will give a 4x4 screen $99/shot. We don't allow anything
213 * smaller than 4x4 because there is a 3x3 game where you can win
214 * an infinite amount of money.
216 if (i
< 12) i
= 12; /* otherwise it isn't fair */
218 * Compensate for border. This really changes the game since
219 * the screen is two squares smaller but we want the default
220 * to be $25, and the high scores on small screens were a bit
224 chunk
= (675.0 / (i
+6)) + 2.5; /* min screen edge */
226 signal (SIGINT
, stop
);
234 chase (&snake
[i
], &snake
[i
-1]);
242 point(struct point
*ps
, int x
, int y
)
249 /* Main command loop */
260 /* Highlight you, not left & above */
261 move(you
.line
+ 1, you
.col
+ 1);
263 if (((c
= getch()) <= '9') && (c
>= '0')) {
265 while (((c
= getch()) <= '9') && (c
>= '0'))
266 repeat
= 10 * repeat
+ (c
- '0');
268 if (c
!= '.') repeat
= 1;
281 case 0177: /* del or end of file */
303 repeat
= you
.col
- money
.col
;
312 repeat
= you
.line
- money
.line
;
316 repeat
= ccnt
- 1 - you
.col
;
321 repeat
= money
.col
- you
.col
;
325 repeat
= lcnt
- 1 - you
.line
;
330 repeat
= money
.line
- you
.line
;
334 for(k
=1;k
<=repeat
;k
++){
347 if((fast
) || (k
== repeat
) ||
358 if (you
.col
< ccnt
-1) {
362 if((fast
) || (k
== repeat
) ||
378 if((fast
) || (k
== repeat
) ||
391 if (you
.line
+1 < lcnt
) {
395 if((fast
) || (k
== repeat
) ||
396 (you
.line
== lcnt
-1))
402 if (same(&you
,&money
))
409 } while ((money
.col
== finish
.col
&& money
.line
== finish
.line
) ||
410 (money
.col
< 5 && money
.line
== 0) ||
411 (money
.col
== you
.col
&& money
.line
== you
.line
));
412 pchar(&money
,TREASURE
);
416 if (same(&you
,&finish
))
421 printf("You have won with $%d.\n", cashvalue
);
428 if (pushsnake())break;
443 pchar(&money
,TREASURE
);
445 pchar(&snake
[i
],SNAKETAIL
);
447 pchar(&snake
[0], SNAKEHEAD
);
457 for (i
= 1; i
<= ccnt
; i
++) {
459 mvaddch(lcnt
+ 1, i
, '-');
461 for (i
= 0; i
<= lcnt
+ 1; i
++) {
463 mvaddch(i
, ccnt
+ 1, '|');
468 snrand(struct point
*sp
)
474 p
.col
= random() % ccnt
;
475 p
.line
= random() % lcnt
;
477 /* make sure it's not on top of something else */
478 if (p
.line
== 0 && p
.col
< 5)
482 if (same(&p
, &money
))
484 if (same(&p
, &finish
))
486 for (i
= 0; i
< 6; i
++)
487 if (same(&p
, &snake
[i
]))
497 post(int iscore
, int flag
)
499 short score
= iscore
;
502 short allbwho
=0, allbscore
=0;
505 /* I want to printf() the scores for terms that clear on cook(),
506 * but this routine also gets called with flag == 0 to see if
507 * the snake should wink. If (flag) then we're at game end and
511 * Neg uid, 0, and 1 cannot have scores recorded.
513 if ((uid
= getuid()) <= 1) {
515 printf("No saved scores for uid %d.\n", uid
);
519 /* Error reported earlier */
522 /* Figure out what happened in the past */
523 read(rawscores
, &allbscore
, sizeof(short));
524 read(rawscores
, &allbwho
, sizeof(short));
525 lseek(rawscores
, ((off_t
)uid
)*sizeof(short), 0);
526 read(rawscores
, &oldbest
, sizeof(short));
528 lseek(rawscores
, 0, SEEK_SET
);
529 return (score
> oldbest
? 1 : 0);
532 /* Update this jokers best */
533 if (score
> oldbest
) {
534 lseek(rawscores
, ((off_t
)uid
)*sizeof(short), 0);
535 write(rawscores
, &score
, sizeof(short));
536 printf("You bettered your previous best of $%d\n", oldbest
);
538 printf("Your best to date is $%d\n", oldbest
);
540 /* See if we have a new champ */
541 p
= getpwuid(allbwho
);
542 if (score
> allbscore
) {
543 lseek(rawscores
, 0, SEEK_SET
);
544 write(rawscores
, &score
, sizeof(short));
545 write(rawscores
, &uid
, sizeof(short));
548 printf("You beat %s's old record of $%d!\n",
549 p
->pw_name
, allbscore
);
551 printf("You beat (%d)'s old record of $%d!\n",
552 (int)allbwho
, allbscore
);
555 printf("You set a new record!\n");
557 printf("The highest is %s with $%d\n", p
->pw_name
, allbscore
);
559 printf("The highest is (%d) with $%d\n", (int)allbwho
,
561 lseek(rawscores
, 0, SEEK_SET
);
566 * Flush typeahead to keep from buffering a bunch of chars and then
567 * overshooting. This loses horribly at 9600 baud, but works nicely
568 * if the terminal gets behind.
573 tcflush(0, TCIFLUSH
);
576 0, 1, 1, 1, 0,-1,-1,-1};
578 -1,-1, 0, 1, 1, 1, 0,-1};
580 1, 1.4, 1, 1.4, 1, 1.4, 1, 1.4
585 chase(struct point
*np
, struct point
*sp
)
587 /* this algorithm has bugs; otherwise the
588 snake would get too good */
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
) );
598 vp
= d
.col
*mx
[i
] + d
.line
*my
[i
];
601 vp
= ((double)vp
)/(v1
*v2
);
611 point(&d
,sp
->col
+mx
[i
],sp
->line
+my
[i
]);
613 if (d
.col
<0 || d
.col
>=ccnt
|| d
.line
<0 || d
.line
>=lcnt
)
616 * Change to allow snake to eat you if you're on the money,
617 * otherwise, you can just crouch there until the snake goes
618 * away. Not positive it's right.
620 * if (d.line == 0 && d.col < 5) continue;
622 if (same(&d
,&money
)) continue;
623 if (same(&d
,&finish
)) continue;
624 wt
[i
]= i
==w
? loot
/10 : 1;
625 if (i
==oldw
) wt
[i
] += loot
/20;
629 vp
= ((random() >> 6) & 01777) % w
;
638 while (wt
[i
]==0) i
++;
641 point(np
,sp
->col
+mx
[w
],sp
->line
+my
[w
]);
652 point(&p
, COLS
/ 2 - 8, LINES
/ 2 - 1);
659 loot
= loot
- penalty
;
662 str
= "SPACE WARP!!!";
663 penalty
+= loot
/PENALTY
;
669 mvaddstr(p
.line
+ 1, p
.col
+ 1, str
);
680 if (!stretch(&money
))
681 if (!stretch(&finish
)) {
691 stretch(const struct point
*ps
)
695 point(&p
,you
.col
,you
.line
);
696 if ((abs(ps
->col
- you
.col
) < (ccnt
/ 12)) && (you
.line
!= ps
->line
)) {
697 if(you
.line
< ps
->line
){
698 for (p
.line
= you
.line
+1;p
.line
<= ps
->line
;p
.line
++)
702 for (;p
.line
> you
.line
;p
.line
--)
705 for (p
.line
= you
.line
-1;p
.line
>= ps
->line
;p
.line
--)
709 for (;p
.line
< you
.line
;p
.line
++)
714 if ((abs(ps
->line
- you
.line
) < (lcnt
/7))
715 && (you
.col
!= ps
->col
)) {
717 if(you
.col
< ps
->col
){
718 for (p
.col
= you
.col
+1;p
.col
<= ps
->col
;p
.col
++)
722 for (;p
.col
> you
.col
;p
.col
--)
725 for (p
.col
= you
.col
-1;p
.col
>= ps
->col
;p
.col
--)
729 for (;p
.col
< you
.col
;p
.col
++)
738 surround(struct point
*ps
)
742 if(ps
->col
== 0)ps
->col
++;
743 if(ps
->line
== 0)ps
->line
++;
744 if(ps
->line
== LINES
-1)ps
->line
--;
745 if(ps
->col
== COLS
-1)ps
->col
--;
746 mvaddstr(ps
->line
, ps
->col
, "/*\\");
747 mvaddstr(ps
->line
+ 1, ps
->col
, "* *");
748 mvaddstr(ps
->line
+ 2, ps
->col
, "\\*/");
757 if (post(cashvalue
,0)) {
758 mvaddstr(ps
->line
, ps
->col
, " ");
759 mvaddstr(ps
->line
+ 1, ps
->col
, "o.o");
760 mvaddstr(ps
->line
+ 2, ps
->col
, "\\_/");
763 mvaddstr(ps
->line
, ps
->col
, " ");
764 mvaddstr(ps
->line
+ 1, ps
->col
, "o.-");
765 mvaddstr(ps
->line
+ 2, ps
->col
, "\\_/");
769 mvaddstr(ps
->line
, ps
->col
, " ");
770 mvaddstr(ps
->line
+ 1, ps
->col
, "o.o");
771 mvaddstr(ps
->line
+ 2, ps
->col
, "\\_/");
777 win(const struct point
*ps
)
781 int boxsize
; /* actually diameter of box, not radius */
783 boxsize
= fast
? 10 : 4;
784 point(&x
,ps
->col
,ps
->line
);
785 for(j
=1;j
<boxsize
;j
++){
816 * My manual says times doesn't return a value. Furthermore, the
817 * snake should get his turn every time no matter if the user is
818 * on a fast terminal with typematic keys or not.
819 * So I have taken the call to times out.
822 if (same(&snake
[i
], &snake
[5]))
825 pchar(&snake
[5],' ');
826 /* Need the following to catch you if you step on the snake's tail */
827 tmp
.col
= snake
[5].col
;
828 tmp
.line
= snake
[5].line
;
830 snake
[i
+1]= snake
[i
];
831 chase(&snake
[0], &snake
[1]);
832 pchar(&snake
[1],SNAKETAIL
);
833 pchar(&snake
[0],SNAKEHEAD
);
836 if (same(&snake
[i
],&you
) || same(&tmp
, &you
))
839 i
= (cashvalue
) % 10;
840 bonus
= ((random() >> 8) & 0377) % 10;
841 mvprintw(lcnt
+ 1, 0, "%d\n", bonus
);
852 if ( loot
>= penalty
){
853 printf("\nYou and your $%d have been eaten\n",
856 printf("\nThe snake ate you. You owe $%d.\n",
868 chk(const struct point
*sp
)
872 if (same(sp
,&money
)) {
876 if (same(sp
,&finish
)) {
880 if (same(sp
,&snake
[0])) {
885 if(same(sp
,&snake
[j
])){
890 if ((sp
->col
< 4) && (sp
->line
== 0)){
892 if((you
.line
== 0) && (you
.col
< 4)) pchar(&you
,ME
);
907 mvprintw(1, 1, "$%d", won
);
912 stop(__unused
int dummy
)
914 signal(SIGINT
,SIG_IGN
);
924 kill(getpid(), SIGTSTP
);
932 printf("You made %d moves.\n", num
);
936 logit(const char *msg
)
940 if (logfile
!= NULL
) {
942 fprintf(logfile
, "%s $%d %dx%d %s %s",
943 getlogin(), cashvalue
, lcnt
, ccnt
, msg
, ctime(&t
));