xref: /netbsd/games/snake/snake/snake.c (revision bf9ec67e)
1 /*	$NetBSD: snake.c,v 1.17 2001/08/31 07:17:02 jsm 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. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *	This product includes software developed by the University of
18  *	California, Berkeley and its contributors.
19  * 4. Neither the name of the University nor the names of its contributors
20  *    may be used to endorse or promote products derived from this software
21  *    without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  */
35 
36 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1980, 1993\n\
39 	The Regents of the University of California.  All rights reserved.\n");
40 #endif				/* not lint */
41 
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)snake.c	8.2 (Berkeley) 1/7/94";
45 #else
46 __RCSID("$NetBSD: snake.c,v 1.17 2001/08/31 07:17:02 jsm Exp $");
47 #endif
48 #endif				/* not lint */
49 
50 /*
51  * snake - crt hack game.
52  *
53  * You move around the screen with arrow keys trying to pick up money
54  * without getting eaten by the snake.  hjkl work as in vi in place of
55  * arrow keys.  You can leave at the exit any time.
56  *
57  * compile as follows:
58  *	cc -O snake.c move.c -o snake -lm -ltermlib
59  */
60 
61 #include <sys/param.h>
62 
63 #include <curses.h>
64 #include <fcntl.h>
65 #include <pwd.h>
66 #include <stdlib.h>
67 #include <time.h>
68 #include <unistd.h>
69 #include <sys/types.h>
70 #include <err.h>
71 #include <math.h>
72 #include <signal.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <termios.h>
77 
78 #include "pathnames.h"
79 
80 #define cashvalue	chunk*(loot-penalty)/25
81 
82 struct point {
83 	int col, line;
84 };
85 
86 #define	same(s1, s2)	((s1)->line == (s2)->line && (s1)->col == (s2)->col)
87 
88 #define PENALTY  10		/* % penalty for invoking spacewarp	 */
89 
90 #define EOT	'\004'
91 #define LF	'\n'
92 #define DEL	'\177'
93 
94 #define ME		'I'
95 #define SNAKEHEAD	'S'
96 #define SNAKETAIL	's'
97 #define TREASURE	'$'
98 #define GOAL		'#'
99 
100 #ifndef MIN
101 #define MIN(a, b) ((a) < (b) ? (a) : (b))
102 #endif
103 
104 #define pchar(point, c)	mvaddch((point)->line + 1, (point)->col + 1, (c))
105 #define delay(t)	usleep(t * 50000);
106 
107 struct point you;
108 struct point money;
109 struct point finish;
110 struct point snake[6];
111 
112 int     loot, penalty;
113 int     moves;
114 int     fast = 1;
115 
116 int rawscores;
117 FILE *logfile;
118 
119 int	lcnt, ccnt;	/* user's idea of screen size */
120 int	chunk;		/* amount of money given at a time */
121 
122 void		chase __P((struct point *, struct point *));
123 int		chk __P((const struct point *));
124 void		drawbox __P((void));
125 void		flushi __P((void));
126 void		home __P((void));
127 void		length __P((int));
128 void		logit __P((const char *));
129 int		main __P((int, char **));
130 void		mainloop __P((void)) __attribute__((__noreturn__));
131 struct point   *point __P((struct point *, int, int));
132 int		post __P((int, int));
133 int		pushsnake __P((void));
134 void		right __P((const struct point *));
135 void		setup __P((void));
136 void		snap __P((void));
137 void		snrand __P((struct point *));
138 void		spacewarp __P((int));
139 void		stop __P((int)) __attribute__((__noreturn__));
140 int		stretch __P((const struct point *));
141 void		surround __P((struct point *));
142 void		suspend __P((void));
143 void		win __P((const struct point *));
144 void		winnings __P((int));
145 
146 int
147 main(argc, argv)
148 	int     argc;
149 	char  **argv;
150 {
151 	int     ch, i;
152 	time_t tv;
153 
154 	/* Open score files then revoke setgid privileges */
155 	rawscores = open(_PATH_RAWSCORES, O_RDWR|O_CREAT, 0664);
156 	if (rawscores < 0) {
157 		warn("open %s", _PATH_RAWSCORES);
158 		sleep(2);
159 	} else if (rawscores < 3)
160 		exit(1);
161 	logfile = fopen(_PATH_LOGFILE, "a");
162 	if (logfile == NULL) {
163 		warn("fopen %s", _PATH_LOGFILE);
164 		sleep(2);
165 	}
166 	setgid(getgid());
167 
168 	(void) time(&tv);
169 
170 	while ((ch = getopt(argc, argv, "l:w:t")) != -1)
171 		switch ((char) ch) {
172 #if 0
173 		case 'd':
174 			tv = atol(optarg);
175 			break;
176 #endif
177 		case 'w':	/* width */
178 			ccnt = atoi(optarg);
179 			break;
180 		case 'l':	/* length */
181 			lcnt = atoi(optarg);
182 			break;
183 		case 't':
184 			fast = 0;
185 			break;
186 		case '?':
187 		default:
188 			fputs("usage: snake [-d seed] [-w width] [-l length] [-t]\n", stderr);
189 			exit(1);
190 		}
191 
192 	srandom((int) tv);
193 
194 	penalty = loot = 0;
195 	initscr();
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 struct point *
251 point(ps, x, y)
252 	struct point *ps;
253 	int     x, y;
254 {
255 	ps->col = x;
256 	ps->line = y;
257 	return (ps);
258 }
259 
260 /* Main command loop */
261 void
262 mainloop()
263 {
264 	int     k;
265 	int     repeat = 1;
266 	int	lastc = 0;
267 
268 	for (;;) {
269 		int     c;
270 
271 		/* Highlight you, not left & above */
272 		move(you.line + 1, you.col + 1);
273 		refresh();
274 		if (((c = getch()) <= '9') && (c >= '0')) {
275 			repeat = c - '0';
276 			while (((c = getch()) <= '9') && (c >= '0'))
277 				repeat = 10 * repeat + (c - '0');
278 		} else {
279 			if (c != '.')
280 				repeat = 1;
281 		}
282 		if (c == '.') {
283 			c = lastc;
284 		}
285 		if (!fast)
286 			flushi();
287 		lastc = c;
288 		switch (c) {
289 		case CTRL('z'):
290 			suspend();
291 			continue;
292 		case EOT:
293 		case 'x':
294 		case 0177:	/* del or end of file */
295 			endwin();
296 			length(moves);
297 			logit("quit");
298 			exit(0);
299 		case CTRL('l'):
300 			setup();
301 			winnings(cashvalue);
302 			continue;
303 		case 'p':
304 		case 'd':
305 			snap();
306 			continue;
307 		case 'w':
308 			spacewarp(0);
309 			continue;
310 		case 'A':
311 			repeat = you.col;
312 			c = 'h';
313 			break;
314 		case 'H':
315 		case 'S':
316 			repeat = you.col - money.col;
317 			c = 'h';
318 			break;
319 		case 'T':
320 			repeat = you.line;
321 			c = 'k';
322 			break;
323 		case 'K':
324 		case 'E':
325 			repeat = you.line - money.line;
326 			c = 'k';
327 			break;
328 		case 'P':
329 			repeat = ccnt - 1 - you.col;
330 			c = 'l';
331 			break;
332 		case 'L':
333 		case 'F':
334 			repeat = money.col - you.col;
335 			c = 'l';
336 			break;
337 		case 'B':
338 			repeat = lcnt - 1 - you.line;
339 			c = 'j';
340 			break;
341 		case 'J':
342 		case 'C':
343 			repeat = money.line - you.line;
344 			c = 'j';
345 			break;
346 		}
347 		for (k = 1; k <= repeat; k++) {
348 			moves++;
349 			switch (c) {
350 			case 's':
351 			case 'h':
352 #ifdef KEY_LEFT
353 			case KEY_LEFT:
354 #endif
355 			case '\b':
356 				if (you.col > 0) {
357 					if ((fast) || (k == 1))
358 						pchar(&you, ' ');
359 					you.col--;
360 					if ((fast) || (k == repeat) ||
361 					    (you.col == 0))
362 						pchar(&you, ME);
363 				}
364 				break;
365 			case 'f':
366 			case 'l':
367 #ifdef KEY_RIGHT
368 			case KEY_RIGHT:
369 #endif
370 			case ' ':
371 				if (you.col < ccnt - 1) {
372 					if ((fast) || (k == 1))
373 						pchar(&you, ' ');
374 					you.col++;
375 					if ((fast) || (k == repeat) ||
376 					    (you.col == ccnt - 1))
377 						pchar(&you, ME);
378 				}
379 				break;
380 			case CTRL('p'):
381 			case 'e':
382 			case 'k':
383 #ifdef KEY_UP
384 			case KEY_UP:
385 #endif
386 			case 'i':
387 				if (you.line > 0) {
388 					if ((fast) || (k == 1))
389 						pchar(&you, ' ');
390 					you.line--;
391 					if ((fast) || (k == repeat) ||
392 					    (you.line == 0))
393 						pchar(&you, ME);
394 				}
395 				break;
396 			case CTRL('n'):
397 			case 'c':
398 			case 'j':
399 #ifdef KEY_DOWN
400 			case KEY_DOWN:
401 #endif
402 			case LF:
403 			case 'm':
404 				if (you.line + 1 < lcnt) {
405 					if ((fast) || (k == 1))
406 						pchar(&you, ' ');
407 					you.line++;
408 					if ((fast) || (k == repeat) ||
409 					    (you.line == lcnt - 1))
410 						pchar(&you, ME);
411 				}
412 				break;
413 			}
414 
415 			if (same(&you, &money)) {
416 				loot += 25;
417 				if (k < repeat)
418 					pchar(&you, ' ');
419 				do {
420 					snrand(&money);
421 				} while ((money.col == finish.col &&
422 					money.line == finish.line) ||
423 				    (money.col < 5 && money.line == 0) ||
424 				    (money.col == you.col &&
425 					money.line == you.line));
426 				pchar(&money, TREASURE);
427 				winnings(cashvalue);
428 				continue;
429 			}
430 			if (same(&you, &finish)) {
431 				win(&finish);
432 				flushi();
433 				endwin();
434 				printf("You have won with $%d.\n", cashvalue);
435 				fflush(stdout);
436 				logit("won");
437 				post(cashvalue, 1);
438 				length(moves);
439 				exit(0);
440 			}
441 			if (pushsnake())
442 				break;
443 		}
444 	}
445 }
446 
447 /*
448  * setup the board
449  */
450 void
451 setup()
452 {
453 	int     i;
454 
455 	erase();
456 	pchar(&you, ME);
457 	pchar(&finish, GOAL);
458 	pchar(&money, TREASURE);
459 	for (i = 1; i < 6; i++) {
460 		pchar(&snake[i], SNAKETAIL);
461 	}
462 	pchar(&snake[0], SNAKEHEAD);
463 	drawbox();
464 	refresh();
465 }
466 
467 void
468 drawbox()
469 {
470 	int i;
471 
472 	for (i = 1; i <= ccnt; i++) {
473 		mvaddch(0, i, '-');
474 		mvaddch(lcnt + 1, i, '-');
475 	}
476 	for (i = 0; i <= lcnt + 1; i++) {
477 		mvaddch(i, 0, '|');
478 		mvaddch(i, ccnt + 1, '|');
479 	}
480 }
481 
482 void
483 snrand(sp)
484 	struct point *sp;
485 {
486 	struct point p;
487 	int i;
488 
489 	for (;;) {
490 		p.col = random() % ccnt;
491 		p.line = random() % lcnt;
492 
493 		/* make sure it's not on top of something else */
494 		if (p.line == 0 && p.col < 5)
495 			continue;
496 		if (same(&p, &you))
497 			continue;
498 		if (same(&p, &money))
499 			continue;
500 		if (same(&p, &finish))
501 			continue;
502 		for (i = 0; i < 6; i++)
503 			if (same(&p, &snake[i]))
504 				break;
505 		if (i < 6)
506 			continue;
507 		break;
508 	}
509 	*sp = p;
510 }
511 
512 int
513 post(iscore, flag)
514 	int     iscore, flag;
515 {
516 	short   score = iscore;
517 	short   uid;
518 	short   oldbest = 0;
519 	short   allbwho = 0, allbscore = 0;
520 	struct passwd *p;
521 
522 	/* I want to printf() the scores for terms that clear on cook(),
523 	 * but this routine also gets called with flag == 0 to see if
524 	 * the snake should wink.  If (flag) then we're at game end and
525 	 * can printf.
526 	 */
527 	/*
528 	 * Neg uid, 0, and 1 cannot have scores recorded.
529 	 */
530 	if ((uid = getuid()) <= 1) {
531 		if (flag)
532 			printf("No saved scores for uid %d.\n", uid);
533 		return (1);
534 	}
535 	if (rawscores < 0) {
536 		/* Error reported earlier */
537 		return (1);
538 	}
539 	/* Figure out what happened in the past */
540 	read(rawscores, &allbscore, sizeof(short));
541 	read(rawscores, &allbwho, sizeof(short));
542 	lseek(rawscores, uid * sizeof(short), SEEK_SET);
543 	read(rawscores, &oldbest, sizeof(short));
544 	if (!flag) {
545 		lseek(rawscores, 0, SEEK_SET);
546 		return (score > oldbest ? 1 : 0);
547 	}
548 
549 	/* Update this jokers best */
550 	if (score > oldbest) {
551 		lseek(rawscores, uid * sizeof(short), SEEK_SET);
552 		write(rawscores, &score, sizeof(short));
553 		printf("You bettered your previous best of $%d\n", oldbest);
554 	} else
555 		printf("Your best to date is $%d\n", oldbest);
556 
557 	/* See if we have a new champ */
558 	p = getpwuid(allbwho);
559 	if (p == NULL || score > allbscore) {
560 		lseek(rawscores, 0, SEEK_SET);
561 		write(rawscores, &score, sizeof(short));
562 		write(rawscores, &uid, sizeof(short));
563 		if (allbwho)
564 			printf("You beat %s's old record of $%d!\n",
565 			       p->pw_name, allbscore);
566 		else
567 			printf("You set a new record!\n");
568 	} else
569 		printf("The highest is %s with $%d\n", p->pw_name, allbscore);
570 	lseek(rawscores, 0, SEEK_SET);
571 	return (1);
572 }
573 
574 /*
575  * Flush typeahead to keep from buffering a bunch of chars and then
576  * overshooting.  This loses horribly at 9600 baud, but works nicely
577  * if the terminal gets behind.
578  */
579 void
580 flushi()
581 {
582 	tcflush(0, TCIFLUSH);
583 }
584 
585 const int     mx[8] = {
586 	0, 1, 1, 1, 0, -1, -1, -1
587 };
588 const int     my[8] = {
589 	-1, -1, 0, 1, 1, 1, 0, -1
590 };
591 const float   absv[8] = {
592 	1, 1.4, 1, 1.4, 1, 1.4, 1, 1.4
593 };
594 int     oldw = 0;
595 
596 void
597 chase(np, sp)
598 	struct point *sp, *np;
599 {
600 	/* this algorithm has bugs; otherwise the snake would get too good */
601 	struct point d;
602 	int     w, i, wt[8];
603 	double  v1, v2, vp, max;
604 	point(&d, you.col - sp->col, you.line - sp->line);
605 	v1 = sqrt((double) (d.col * d.col + d.line * d.line));
606 	w = 0;
607 	max = 0;
608 	for (i = 0; i < 8; i++) {
609 		vp = d.col * mx[i] + d.line * my[i];
610 		v2 = absv[i];
611 		if (v1 > 0)
612 			vp = ((double) vp) / (v1 * v2);
613 		else
614 			vp = 1.0;
615 		if (vp > max) {
616 			max = vp;
617 			w = i;
618 		}
619 	}
620 	for (i = 0; i < 8; i++) {
621 		point(&d, sp->col + mx[i], sp->line + my[i]);
622 		wt[i] = 0;
623 		if (d.col < 0 || d.col >= ccnt || d.line < 0 || d.line >= lcnt)
624 			continue;
625 		/*
626 		 * Change to allow snake to eat you if you're on the money,
627 		 * otherwise, you can just crouch there until the snake goes
628 		 * away.  Not positive it's right.
629 		 *
630 		 * if (d.line == 0 && d.col < 5) continue;
631 		 */
632 		if (same(&d, &money))
633 			continue;
634 		if (same(&d, &finish))
635 			continue;
636 		wt[i] = i == w ? loot / 10 : 1;
637 		if (i == oldw)
638 			wt[i] += loot / 20;
639 	}
640 	for (w = i = 0; i < 8; i++)
641 		w += wt[i];
642 	vp = ((random() >> 6) & 01777) % w;
643 	for (i = 0; i < 8; i++)
644 		if (vp < wt[i])
645 			break;
646 		else
647 			vp -= wt[i];
648 	if (i == 8) {
649 		printw("failure\n");
650 		i = 0;
651 		while (wt[i] == 0)
652 			i++;
653 	}
654 	oldw = w = i;
655 	point(np, sp->col + mx[w], sp->line + my[w]);
656 }
657 
658 void
659 spacewarp(w)
660 	int     w;
661 {
662 	struct point p;
663 	int     j;
664 	const char   *str;
665 
666 	snrand(&you);
667 	point(&p, COLS / 2 - 8, LINES / 2 - 1);
668 	if (p.col < 0)
669 		p.col = 0;
670 	if (p.line < 0)
671 		p.line = 0;
672 	if (w) {
673 		str = "BONUS!!!";
674 		loot = loot - penalty;
675 		penalty = 0;
676 	} else {
677 		str = "SPACE WARP!!!";
678 		penalty += loot / PENALTY;
679 	}
680 	for (j = 0; j < 3; j++) {
681 		erase();
682 		refresh();
683 		delay(5);
684 		mvaddstr(p.line + 1, p.col + 1, str);
685 		refresh();
686 		delay(10);
687 	}
688 	setup();
689 	winnings(cashvalue);
690 }
691 
692 void
693 snap()
694 {
695 #if 0 /* This code doesn't really make sense.  */
696 	struct point p;
697 
698 	if (you.line < 3) {
699 		mvaddch(1, you.col + 1, '-');
700 	}
701 	if (you.line > lcnt - 4) {
702 		mvaddch(lcnt, you.col + 1, '_');
703 	}
704 	if (you.col < 10) {
705 		mvaddch(you.line + 1, 1, '(');
706 	}
707 	if (you.col > ccnt - 10) {
708 		mvaddch(you.line + 1, ccnt, ')');
709 	}
710 #endif
711 	if (!stretch(&money))
712 		if (!stretch(&finish)) {
713 			pchar(&you, '?');
714 			refresh();
715 			delay(10);
716 			pchar(&you, ME);
717 		}
718 #if 0
719 	if (you.line < 3) {
720 		point(&p, you.col, 0);
721 		chk(&p);
722 	}
723 	if (you.line > lcnt - 4) {
724 		point(&p, you.col, lcnt - 1);
725 		chk(&p);
726 	}
727 	if (you.col < 10) {
728 		point(&p, 0, you.line);
729 		chk(&p);
730 	}
731 	if (you.col > ccnt - 10) {
732 		point(&p, ccnt - 1, you.line);
733 		chk(&p);
734 	}
735 #endif
736 	refresh();
737 }
738 
739 int
740 stretch(ps)
741 	const struct point *ps;
742 {
743 	struct point p;
744 
745 	point(&p, you.col, you.line);
746 	if ((abs(ps->col - you.col) < (ccnt / 12)) && (you.line != ps->line)) {
747 		if (you.line < ps->line) {
748 			for (p.line = you.line + 1; p.line <= ps->line; p.line++)
749 				pchar(&p, 'v');
750 			refresh();
751 			delay(10);
752 			for (; p.line > you.line; p.line--)
753 				chk(&p);
754 		} else {
755 			for (p.line = you.line - 1; p.line >= ps->line; p.line--)
756 				pchar(&p, '^');
757 			refresh();
758 			delay(10);
759 			for (; p.line < you.line; p.line++)
760 				chk(&p);
761 		}
762 		return (1);
763 	} else
764 		if ((abs(ps->line - you.line) < (lcnt/7))
765 		    && (you.col != ps->col)) {
766 			p.line = you.line;
767 			if (you.col < ps->col) {
768 				for (p.col = you.col + 1; p.col <= ps->col; p.col++)
769 					pchar(&p, '>');
770 				refresh();
771 				delay(10);
772 				for (; p.col > you.col; p.col--)
773 					chk(&p);
774 			} else {
775 				for (p.col = you.col - 1; p.col >= ps->col; p.col--)
776 					pchar(&p, '<');
777 				refresh();
778 				delay(10);
779 				for (; p.col < you.col; p.col++)
780 					chk(&p);
781 			}
782 			return (1);
783 		}
784 	return (0);
785 }
786 
787 void
788 surround(ps)
789 	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 void
832 win(ps)
833 	const struct point *ps;
834 {
835 	struct point x;
836 	int     j, k;
837 	int     boxsize;	/* actually diameter of box, not radius */
838 
839 	boxsize = fast ? 10 : 4;
840 	point(&x, ps->col, ps->line);
841 	for (j = 1; j < boxsize; j++) {
842 		for (k = 0; k < j; k++) {
843 			pchar(&x, '#');
844 			x.line--;
845 		}
846 		for (k = 0; k < j; k++) {
847 			pchar(&x, '#');
848 			x.col++;
849 		}
850 		j++;
851 		for (k = 0; k < j; k++) {
852 			pchar(&x, '#');
853 			x.line++;
854 		}
855 		for (k = 0; k < j; k++) {
856 			pchar(&x, '#');
857 			x.col--;
858 		}
859 		refresh();
860 		delay(1);
861 	}
862 }
863 
864 int
865 pushsnake()
866 {
867 	int     i, bonus;
868 	int     issame = 0;
869 	struct point tmp;
870 
871 	/*
872 	 * My manual says times doesn't return a value.  Furthermore, the
873 	 * snake should get his turn every time no matter if the user is
874 	 * on a fast terminal with typematic keys or not.
875 	 * So I have taken the call to times out.
876 	 */
877 	for (i = 4; i >= 0; i--)
878 		if (same(&snake[i], &snake[5]))
879 			issame++;
880 	if (!issame)
881 		pchar(&snake[5], ' ');
882 	/* Need the following to catch you if you step on the snake's tail */
883 	tmp.col = snake[5].col;
884 	tmp.line = snake[5].line;
885 	for (i = 4; i >= 0; i--)
886 		snake[i + 1] = snake[i];
887 	chase(&snake[0], &snake[1]);
888 	pchar(&snake[1], SNAKETAIL);
889 	pchar(&snake[0], SNAKEHEAD);
890 	for (i = 0; i < 6; i++) {
891 		if (same(&snake[i], &you) || same(&tmp, &you)) {
892 			surround(&you);
893 			i = (cashvalue) % 10;
894 			bonus = ((random() >> 8) & 0377) % 10;
895 			mvprintw(lcnt + 1, 0, "%d\n", bonus);
896 			refresh();
897 			delay(30);
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 int
922 chk(sp)
923 	const struct point *sp;
924 {
925 	int     j;
926 
927 	if (same(sp, &money)) {
928 		pchar(sp, TREASURE);
929 		return (2);
930 	}
931 	if (same(sp, &finish)) {
932 		pchar(sp, GOAL);
933 		return (3);
934 	}
935 	if (same(sp, &snake[0])) {
936 		pchar(sp, SNAKEHEAD);
937 		return (4);
938 	}
939 	for (j = 1; j < 6; j++) {
940 		if (same(sp, &snake[j])) {
941 			pchar(sp, SNAKETAIL);
942 			return (4);
943 		}
944 	}
945 	if ((sp->col < 4) && (sp->line == 0)) {
946 		winnings(cashvalue);
947 		if ((you.line == 0) && (you.col < 4))
948 			pchar(&you, ME);
949 		return (5);
950 	}
951 	if (same(sp, &you)) {
952 		pchar(sp, ME);
953 		return (1);
954 	}
955 	pchar(sp, ' ');
956 	return (0);
957 }
958 
959 void
960 winnings(won)
961 	int     won;
962 {
963 	if (won > 0) {
964 		mvprintw(1, 1, "$%d", won);
965 	}
966 }
967 
968 void
969 stop(dummy)
970 	int dummy __attribute__((__unused__));
971 {
972 	signal(SIGINT, SIG_IGN);
973 	endwin();
974 	length(moves);
975 	exit(0);
976 }
977 
978 void
979 suspend()
980 {
981 	endwin();
982 	kill(getpid(), SIGTSTP);
983 	refresh();
984 	winnings(cashvalue);
985 }
986 
987 void
988 length(num)
989 	int     num;
990 {
991 	printf("You made %d moves.\n", num);
992 }
993 
994 void
995 logit(msg)
996 	const char   *msg;
997 {
998 	time_t  t;
999 
1000 	if (logfile != NULL) {
1001 		time(&t);
1002 		fprintf(logfile, "%s $%d %dx%d %s %s",
1003 		    getlogin(), cashvalue, lcnt, ccnt, msg, ctime(&t));
1004 		fflush(logfile);
1005 	}
1006 }
1007