xref: /dragonfly/games/snake/snake/snake.c (revision 279dd846)
1 /*-
2  * Copyright (c) 1980, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
16  *
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
27  * SUCH DAMAGE.
28  *
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  */
33 
34 /*
35  * snake - crt hack game.
36  *
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.
40  *
41  * compile as follows:
42  *	cc -O snake.c move.c -o snake -lm -ltermlib
43  */
44 
45 #include <sys/param.h>
46 
47 #include <curses.h>
48 #include <fcntl.h>
49 #include <pwd.h>
50 #include <time.h>
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include <sys/types.h>
54 #include <err.h>
55 #include <math.h>
56 #include <signal.h>
57 #include <stdio.h>
58 #include <string.h>
59 #include <termios.h>
60 
61 #include "pathnames.h"
62 
63 #define cashvalue	chunk*(loot-penalty)/25
64 
65 struct point {
66 	int col, line;
67 };
68 
69 #define	same(s1, s2)	((s1)->line == (s2)->line && (s1)->col == (s2)->col)
70 
71 #define PENALTY  10		/* % penalty for invoking spacewarp */
72 
73 #define EOT	'\004'
74 #define LF	'\n'
75 #define DEL	'\177'
76 
77 #define ME		'I'
78 #define SNAKEHEAD	'S'
79 #define SNAKETAIL	's'
80 #define TREASURE	'$'
81 #define GOAL		'#'
82 
83 #ifndef MIN
84 #define MIN(a, b) ((a) < (b) ? (a) : (b))
85 #endif
86 
87 #define pchar(point, c)	mvaddch((point)->line + 1, (point)->col + 1, (c))
88 #define delay(t)	usleep(t * 50000);
89 
90 struct point you;
91 struct point money;
92 struct point finish;
93 struct point snake[6];
94 
95 int loot, penalty;
96 int moves;
97 int fast = 1;
98 
99 int rawscores;
100 FILE *logfile;
101 
102 int lcnt, ccnt;		/* user's idea of screen size */
103 int chunk;		/* amount of money given at a time */
104 
105 void chase(struct point *, struct point *);
106 int chk(const struct point *);
107 void drawbox(void);
108 void flushi(void);
109 void home(void);
110 void length(int);
111 void logit(const char *);
112 void mainloop(void) __attribute__((__noreturn__));
113 struct point *point(struct point *, int, int);
114 int post(int, int);
115 int pushsnake(void);
116 void right(const struct point *);
117 void setup(void);
118 void snap(void);
119 void snrand(struct point *);
120 void spacewarp(int);
121 void stop(int) __attribute__((__noreturn__));
122 int stretch(const struct point *);
123 void surround(struct point *);
124 void suspend(void);
125 void win(const struct point *);
126 void winnings(int);
127 
128 int
129 main(int argc, char **argv)
130 {
131 	int ch, i;
132 	time_t tv;
133 
134 	/* Open score files then revoke setgid privileges */
135 	rawscores = open(_PATH_RAWSCORES, O_RDWR|O_CREAT, 0664);
136 	if (rawscores < 0) {
137 		warn("open %s", _PATH_RAWSCORES);
138 		sleep(2);
139 	} else if (rawscores < 3)
140 		exit(1);
141 	logfile = fopen(_PATH_LOGFILE, "a");
142 	if (logfile == NULL) {
143 		warn("fopen %s", _PATH_LOGFILE);
144 		sleep(2);
145 	}
146 	setgid(getgid());
147 
148 	time(&tv);
149 
150 	while ((ch = getopt(argc, argv, "l:w:t")) != -1)
151 		switch ((char) ch) {
152 #ifdef DEBUG
153 		case 'd':
154 			tv = atol(optarg);
155 			break;
156 #endif
157 		case 'w':	/* width */
158 			ccnt = atoi(optarg);
159 			break;
160 		case 'l':	/* length */
161 			lcnt = atoi(optarg);
162 			break;
163 		case 't':
164 			fast = 0;
165 			break;
166 		case '?':
167 		default:
168 #ifdef DEBUG
169 			fputs("usage: snake [-d seed] [-w width] [-l length] [-t]\n", stderr);
170 #else
171 			fputs("usage: snake [-w width] [-l length] [-t]\n", stderr);
172 #endif
173 			exit(1);
174 		}
175 
176 	srandom((int) tv);
177 
178 	penalty = loot = 0;
179 	initscr();
180 	cbreak();
181 	noecho();
182 #ifdef KEY_LEFT
183 	keypad(stdscr, TRUE);
184 #endif
185 	if (!lcnt || lcnt > LINES - 2)
186 		lcnt = LINES - 2;
187 	if (!ccnt || ccnt > COLS - 2)
188 		ccnt = COLS - 2;
189 
190 	i = MIN(lcnt, ccnt);
191 	if (i < 4) {
192 		endwin();
193 		errx(1, "screen too small for a fair game.");
194 	}
195 
196 	/*
197 	 * chunk is the amount of money the user gets for each $.
198 	 * The formula below tries to be fair for various screen sizes.
199 	 * We only pay attention to the smaller of the 2 edges, since
200 	 * that seems to be the bottleneck.
201 	 * This formula is a hyperbola which includes the following points:
202 	 *	(24, $25)	(original scoring algorithm)
203 	 *	(12, $40)	(experimentally derived by the "feel")
204 	 *	(48, $15)	(a guess)
205 	 * This will give a 4x4 screen $99/shot.  We don't allow anything
206 	 * smaller than 4x4 because there is a 3x3 game where you can win
207 	 * an infinite amount of money.
208 	 */
209 	if (i < 12)		/* otherwise it isn't fair */
210 		i = 12;
211 	/*
212 	 * Compensate for border.  This really changes the game since
213 	 * the screen is two squares smaller but we want the default
214 	 * to be $25, and the high scores on small screens were a bit
215 	 * much anyway.
216 	 */
217 	i += 2;
218 	chunk = (675.0 / (i + 6)) + 2.5;	/* min screen edge */
219 
220 	signal(SIGINT, stop);
221 
222 	snrand(&finish);
223 	snrand(&you);
224 	snrand(&money);
225 	snrand(&snake[0]);
226 
227 	for (i = 1; i < 6; i++)
228 		chase(&snake[i], &snake[i - 1]);
229 	setup();
230 	mainloop();
231 	/* NOTREACHED */
232 	return (0);
233 }
234 
235 struct point *
236 point(struct point *ps, int x, int y)
237 {
238 	ps->col = x;
239 	ps->line = y;
240 	return (ps);
241 }
242 
243 /* Main command loop */
244 void
245 mainloop(void)
246 {
247 	int     k;
248 	int     repeat = 1;
249 	int	lastc = 0;
250 
251 	for (;;) {
252 		int     c;
253 
254 		/* Highlight you, not left & above */
255 		move(you.line + 1, you.col + 1);
256 		refresh();
257 		if (((c = getch()) <= '9') && (c >= '0')) {
258 			repeat = c - '0';
259 			while (((c = getch()) <= '9') && (c >= '0'))
260 				repeat = 10 * repeat + (c - '0');
261 		} else {
262 			if (c != '.')
263 				repeat = 1;
264 		}
265 		if (c == '.') {
266 			c = lastc;
267 		}
268 		if (!fast)
269 			flushi();
270 		lastc = c;
271 		switch (c) {
272 		case CTRL('z'):
273 			suspend();
274 			continue;
275 		case EOT:
276 		case 'x':
277 		case 0177:	/* del or end of file */
278 			endwin();
279 			length(moves);
280 			logit("quit");
281 			exit(0);
282 		case CTRL('l'):
283 			setup();
284 			winnings(cashvalue);
285 			continue;
286 		case 'p':
287 		case 'd':
288 			snap();
289 			continue;
290 		case 'w':
291 			spacewarp(0);
292 			continue;
293 		case 'A':
294 			repeat = you.col;
295 			c = 'h';
296 			break;
297 		case 'H':
298 		case 'S':
299 			repeat = you.col - money.col;
300 			c = 'h';
301 			break;
302 		case 'T':
303 			repeat = you.line;
304 			c = 'k';
305 			break;
306 		case 'K':
307 		case 'E':
308 			repeat = you.line - money.line;
309 			c = 'k';
310 			break;
311 		case 'P':
312 			repeat = ccnt - 1 - you.col;
313 			c = 'l';
314 			break;
315 		case 'L':
316 		case 'F':
317 			repeat = money.col - you.col;
318 			c = 'l';
319 			break;
320 		case 'B':
321 			repeat = lcnt - 1 - you.line;
322 			c = 'j';
323 			break;
324 		case 'J':
325 		case 'C':
326 			repeat = money.line - you.line;
327 			c = 'j';
328 			break;
329 		}
330 		for (k = 1; k <= repeat; k++) {
331 			moves++;
332 			switch (c) {
333 			case 's':
334 			case 'h':
335 #ifdef KEY_LEFT
336 			case KEY_LEFT:
337 #endif
338 			case '\b':
339 				if (you.col > 0) {
340 					if ((fast) || (k == 1))
341 						pchar(&you, ' ');
342 					you.col--;
343 					if ((fast) || (k == repeat) ||
344 					    (you.col == 0))
345 						pchar(&you, ME);
346 				}
347 				break;
348 			case 'f':
349 			case 'l':
350 #ifdef KEY_RIGHT
351 			case KEY_RIGHT:
352 #endif
353 			case ' ':
354 				if (you.col < ccnt - 1) {
355 					if ((fast) || (k == 1))
356 						pchar(&you, ' ');
357 					you.col++;
358 					if ((fast) || (k == repeat) ||
359 					    (you.col == ccnt - 1))
360 						pchar(&you, ME);
361 				}
362 				break;
363 			case CTRL('p'):
364 			case 'e':
365 			case 'k':
366 #ifdef KEY_UP
367 			case KEY_UP:
368 #endif
369 			case 'i':
370 				if (you.line > 0) {
371 					if ((fast) || (k == 1))
372 						pchar(&you, ' ');
373 					you.line--;
374 					if ((fast) || (k == repeat) ||
375 					    (you.line == 0))
376 						pchar(&you, ME);
377 				}
378 				break;
379 			case CTRL('n'):
380 			case 'c':
381 			case 'j':
382 #ifdef KEY_DOWN
383 			case KEY_DOWN:
384 #endif
385 			case LF:
386 			case 'm':
387 				if (you.line + 1 < lcnt) {
388 					if ((fast) || (k == 1))
389 						pchar(&you, ' ');
390 					you.line++;
391 					if ((fast) || (k == repeat) ||
392 					    (you.line == lcnt - 1))
393 						pchar(&you, ME);
394 				}
395 				break;
396 			}
397 
398 			if (same(&you, &money)) {
399 				loot += 25;
400 				if (k < repeat)
401 					pchar(&you, ' ');
402 				do {
403 					snrand(&money);
404 				} while ((money.col == finish.col &&
405 				    money.line == finish.line) ||
406 				    (money.col < 5 && money.line == 0) ||
407 				    (money.col == you.col &&
408 					money.line == you.line));
409 				pchar(&money, TREASURE);
410 				winnings(cashvalue);
411 				continue;
412 			}
413 			if (same(&you, &finish)) {
414 				win(&finish);
415 				flushi();
416 				endwin();
417 				printf("You have won with $%d.\n", cashvalue);
418 				fflush(stdout);
419 				logit("won");
420 				post(cashvalue, 1);
421 				length(moves);
422 				exit(0);
423 			}
424 			if (pushsnake())
425 				break;
426 		}
427 	}
428 }
429 
430 /*
431  * setup the board
432  */
433 void
434 setup(void)
435 {
436 	int i;
437 
438 	erase();
439 	pchar(&you, ME);
440 	pchar(&finish, GOAL);
441 	pchar(&money, TREASURE);
442 	for (i = 1; i < 6; i++) {
443 		pchar(&snake[i], SNAKETAIL);
444 	}
445 	pchar(&snake[0], SNAKEHEAD);
446 	drawbox();
447 	refresh();
448 }
449 
450 void
451 drawbox(void)
452 {
453 	int i;
454 
455 	for (i = 1; i <= ccnt; i++) {
456 		mvaddch(0, i, '-');
457 		mvaddch(lcnt + 1, i, '-');
458 	}
459 	for (i = 0; i <= lcnt + 1; i++) {
460 		mvaddch(i, 0, '|');
461 		mvaddch(i, ccnt + 1, '|');
462 	}
463 }
464 
465 void
466 snrand(struct point *sp)
467 {
468 	struct point p;
469 	int i;
470 
471 	for (;;) {
472 		p.col = random() % ccnt;
473 		p.line = random() % lcnt;
474 
475 		/* make sure it's not on top of something else */
476 		if (p.line == 0 && p.col < 5)
477 			continue;
478 		if (same(&p, &you))
479 			continue;
480 		if (same(&p, &money))
481 			continue;
482 		if (same(&p, &finish))
483 			continue;
484 		for (i = 0; i < 6; i++)
485 			if (same(&p, &snake[i]))
486 				break;
487 		if (i < 6)
488 			continue;
489 		break;
490 	}
491 	*sp = p;
492 }
493 
494 int
495 post(int iscore, int flag)
496 {
497 	short	score = iscore;
498 	short	uid;
499 	short	oldbest = 0;
500 	short	allbwho = 0, allbscore = 0;
501 	struct	passwd *p;
502 
503 	/* I want to printf() the scores for terms that clear on cook(),
504 	 * but this routine also gets called with flag == 0 to see if
505 	 * the snake should wink.  If (flag) then we're at game end and
506 	 * can printf.
507 	 */
508 	/*
509 	 * Neg uid, 0, and 1 cannot have scores recorded.
510 	 */
511 	if ((uid = getuid()) <= 1) {
512 		if (flag)
513 			printf("No saved scores for uid %d.\n", uid);
514 		return (1);
515 	}
516 	if (rawscores < 0) {
517 		/* Error reported earlier */
518 		return (1);
519 	}
520 	/* Figure out what happened in the past */
521 	read(rawscores, &allbscore, sizeof(short));
522 	read(rawscores, &allbwho, sizeof(short));
523 	lseek(rawscores, ((off_t)uid)*sizeof(short), SEEK_SET);
524 	read(rawscores, &oldbest, sizeof(short));
525 	if (!flag) {
526 		lseek(rawscores, 0, SEEK_SET);
527 		return (score > oldbest ? 1 : 0);
528 	}
529 
530 	/* Update this jokers best */
531 	if (score > oldbest) {
532 		lseek(rawscores, ((off_t)uid)*sizeof(short), SEEK_SET);
533 		write(rawscores, &score, sizeof(short));
534 		printf("You bettered your previous best of $%d\n", oldbest);
535 	} else
536 		printf("Your best to date is $%d\n", oldbest);
537 
538 	/* See if we have a new champ */
539 	p = getpwuid(allbwho);
540 	if (score > allbscore) {
541 		lseek(rawscores, 0, SEEK_SET);
542 		write(rawscores, &score, sizeof(short));
543 		write(rawscores, &uid, sizeof(short));
544 		if (allbwho) {
545 			if (p)
546 				printf("You beat %s's old record of $%d!\n",
547 				    p->pw_name, allbscore);
548 			else
549 				printf("You beat (%d)'s old record of $%d!\n",
550 				       (int)allbwho, allbscore);
551 		}
552 		else
553 			printf("You set a new record!\n");
554 	} else if (p)
555 		printf("The highest is %s with $%d\n", p->pw_name, allbscore);
556 	else
557 		printf("The highest is (%d) with $%d\n", (int)allbwho,
558 		    allbscore);
559 	lseek(rawscores, 0, SEEK_SET);
560 	return (1);
561 }
562 
563 /*
564  * Flush typeahead to keep from buffering a bunch of chars and then
565  * overshooting.  This loses horribly at 9600 baud, but works nicely
566  * if the terminal gets behind.
567  */
568 void
569 flushi(void)
570 {
571 	tcflush(0, TCIFLUSH);
572 }
573 
574 int mx[8] = {
575 	0, 1, 1, 1, 0, -1, -1, -1
576 };
577 int my[8] = {
578 	-1, -1, 0, 1, 1, 1, 0, -1
579 };
580 float absv[8] = {
581 	1, 1.4, 1, 1.4, 1, 1.4, 1, 1.4
582 };
583 int oldw = 0;
584 
585 void
586 chase(struct point *np, struct point *sp)
587 {
588 	/* this algorithm has bugs; otherwise the snake would get too good */
589 	struct point d;
590 	int w, i, wt[8];
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));
594 	w = 0;
595 	max = 0;
596 	for (i = 0; i < 8; i++) {
597 		vp = d.col * mx[i] + d.line * my[i];
598 		v2 = absv[i];
599 		if (v1 > 0)
600 			vp = ((double)vp) / (v1 * v2);
601 		else
602 			vp = 1.0;
603 		if (vp > max) {
604 			max = vp;
605 			w = i;
606 		}
607 	}
608 	for (i = 0; i < 8; i++) {
609 		point(&d, sp->col + mx[i], sp->line + my[i]);
610 		wt[i] = 0;
611 		if (d.col < 0 || d.col >= ccnt || d.line < 0 || d.line >= lcnt)
612 			continue;
613 		/*
614 		 * Change to allow snake to eat you if you're on the money,
615 		 * otherwise, you can just crouch there until the snake goes
616 		 * away.  Not positive it's right.
617 		 *
618 		 * if (d.line == 0 && d.col < 5) continue;
619 		 */
620 		if (same(&d, &money))
621 			continue;
622 		if (same(&d, &finish))
623 			continue;
624 		wt[i] = i == w ? loot / 10 : 1;
625 		if (i == oldw)
626 			wt[i] += loot / 20;
627 	}
628 	for (w = i = 0; i < 8; i++)
629 		w += wt[i];
630 	vp = ((random() >> 6) & 01777) % w;
631 	for (i = 0; i < 8; i++)
632 		if (vp < wt[i])
633 			break;
634 		else
635 			vp -= wt[i];
636 	if (i == 8) {
637 		printw("failure\n");
638 		i = 0;
639 		while (wt[i] == 0)
640 			i++;
641 	}
642 	oldw = w = i;
643 	point(np, sp->col + mx[w], sp->line + my[w]);
644 }
645 
646 void
647 spacewarp(int w)
648 {
649 	struct point p;
650 	int j;
651 	const char   *str;
652 
653 	snrand(&you);
654 	point(&p, COLS / 2 - 8, LINES / 2 - 1);
655 	if (p.col < 0)
656 		p.col = 0;
657 	if (p.line < 0)
658 		p.line = 0;
659 	if (w) {
660 		str = "BONUS!!!";
661 		loot = loot - penalty;
662 		penalty = 0;
663 	} else {
664 		str = "SPACE WARP!!!";
665 		penalty += loot / PENALTY;
666 	}
667 	for (j = 0; j < 3; j++) {
668 		erase();
669 		refresh();
670 		delay(5);
671 		mvaddstr(p.line + 1, p.col + 1, str);
672 		refresh();
673 		delay(10);
674 	}
675 	setup();
676 	winnings(cashvalue);
677 }
678 
679 void
680 snap(void)
681 {
682 	if (!stretch(&money))
683 		if (!stretch(&finish)) {
684 			pchar(&you, '?');
685 			refresh();
686 			delay(10);
687 			pchar(&you, ME);
688 		}
689 	refresh();
690 }
691 
692 int
693 stretch(const struct point *ps)
694 {
695 	struct point p;
696 
697 	point(&p, you.col, you.line);
698 	if ((abs(ps->col - you.col) < (ccnt / 12)) && (you.line != ps->line)) {
699 		if (you.line < ps->line) {
700 			for (p.line = you.line + 1; p.line <= ps->line; p.line++)
701 				pchar(&p, 'v');
702 			refresh();
703 			delay(10);
704 			for (; p.line > you.line; p.line--)
705 				chk(&p);
706 		} else {
707 			for (p.line = you.line - 1; p.line >= ps->line; p.line--)
708 				pchar(&p, '^');
709 			refresh();
710 			delay(10);
711 			for (; p.line < you.line; p.line++)
712 				chk(&p);
713 		}
714 		return (1);
715 	} else
716 		if ((abs(ps->line - you.line) < (lcnt / 7))
717 		    && (you.col != ps->col)) {
718 			p.line = you.line;
719 			if (you.col < ps->col) {
720 				for (p.col = you.col + 1; p.col <= ps->col; p.col++)
721 					pchar(&p, '>');
722 				refresh();
723 				delay(10);
724 				for (; p.col > you.col; p.col--)
725 					chk(&p);
726 			} else {
727 				for (p.col = you.col - 1; p.col >= ps->col; p.col--)
728 					pchar(&p, '<');
729 				refresh();
730 				delay(10);
731 				for (; p.col < you.col; p.col++)
732 					chk(&p);
733 			}
734 			return (1);
735 		}
736 	return (0);
737 }
738 
739 void
740 surround(struct point *ps)
741 {
742 	int j;
743 
744 	if (ps->col == 0)
745 		ps->col++;
746 	if (ps->line == 0)
747 		ps->line++;
748 	if (ps->line == LINES - 1)
749 		ps->line--;
750 	if (ps->col == COLS - 1)
751 		ps->col--;
752 	mvaddstr(ps->line, ps->col, "/*\\");
753 	mvaddstr(ps->line + 1, ps->col, "* *");
754 	mvaddstr(ps->line + 2, ps->col, "\\*/");
755 	for (j = 0; j < 20; j++) {
756 		pchar(ps, '@');
757 		refresh();
758 		delay(1);
759 		pchar(ps, ' ');
760 		refresh();
761 		delay(1);
762 	}
763 	if (post(cashvalue, 0)) {
764 		mvaddstr(ps->line, ps->col, "   ");
765 		mvaddstr(ps->line + 1, ps->col, "o.o");
766 		mvaddstr(ps->line + 2, ps->col, "\\_/");
767 		refresh();
768 		delay(6);
769 		mvaddstr(ps->line, ps->col, "   ");
770 		mvaddstr(ps->line + 1, ps->col, "o.-");
771 		mvaddstr(ps->line + 2, ps->col, "\\_/");
772 		refresh();
773 		delay(6);
774 	}
775 	mvaddstr(ps->line, ps->col, "   ");
776 	mvaddstr(ps->line + 1, ps->col, "o.o");
777 	mvaddstr(ps->line + 2, ps->col, "\\_/");
778 	refresh();
779 	delay(6);
780 }
781 
782 void
783 win(const struct point *ps)
784 {
785 	struct point x;
786 	int j, k;
787 	int boxsize;	/* actually diameter of box, not radius */
788 
789 	boxsize = fast ? 10 : 4;
790 	point(&x, ps->col, ps->line);
791 	for (j = 1; j < boxsize; j++) {
792 		for (k = 0; k < j; k++) {
793 			pchar(&x, '#');
794 			x.line--;
795 		}
796 		for (k = 0; k < j; k++) {
797 			pchar(&x, '#');
798 			x.col++;
799 		}
800 		j++;
801 		for (k = 0; k < j; k++) {
802 			pchar(&x, '#');
803 			x.line++;
804 		}
805 		for (k = 0; k < j; k++) {
806 			pchar(&x, '#');
807 			x.col--;
808 		}
809 		refresh();
810 		delay(1);
811 	}
812 }
813 
814 int
815 pushsnake(void)
816 {
817 	int i, bonus;
818 	int issame = 0;
819 	struct point tmp;
820 
821 	/*
822 	 * My manual says times doesn't return a value.  Furthermore, the
823 	 * snake should get his turn every time no matter if the user is
824 	 * on a fast terminal with typematic keys or not.
825 	 * So I have taken the call to times out.
826 	 */
827 	for (i = 4; i >= 0; i--)
828 		if (same(&snake[i], &snake[5]))
829 			issame++;
830 	if (!issame)
831 		pchar(&snake[5], ' ');
832 	/* Need the following to catch you if you step on the snake's tail */
833 	tmp.col = snake[5].col;
834 	tmp.line = snake[5].line;
835 	for (i = 4; i >= 0; i--)
836 		snake[i + 1] = snake[i];
837 	chase(&snake[0], &snake[1]);
838 	pchar(&snake[1], SNAKETAIL);
839 	pchar(&snake[0], SNAKEHEAD);
840 	for (i = 0; i < 6; i++) {
841 		if (same(&snake[i], &you) || same(&tmp, &you)) {
842 			surround(&you);
843 			i = (cashvalue) % 10;
844 			bonus = ((random() >> 8) & 0377) % 10;
845 			mvprintw(lcnt + 1, 0, "%d\n", bonus);
846 			refresh();
847 			delay(30);
848 			if (bonus == i) {
849 				spacewarp(1);
850 				logit("bonus");
851 				flushi();
852 				return (1);
853 			}
854 			flushi();
855 			endwin();
856 			if (loot >= penalty) {
857 				printf("\nYou and your $%d have been eaten\n",
858 				    cashvalue);
859 			} else {
860 				printf("\nThe snake ate you.  You owe $%d.\n",
861 				    -cashvalue);
862 			}
863 			logit("eaten");
864 			length(moves);
865 			exit(0);
866 		}
867 	}
868 	return (0);
869 }
870 
871 int
872 chk(const struct point *sp)
873 {
874 	int j;
875 
876 	if (same(sp, &money)) {
877 		pchar(sp, TREASURE);
878 		return (2);
879 	}
880 	if (same(sp, &finish)) {
881 		pchar(sp, GOAL);
882 		return (3);
883 	}
884 	if (same(sp, &snake[0])) {
885 		pchar(sp, SNAKEHEAD);
886 		return (4);
887 	}
888 	for (j = 1; j < 6; j++) {
889 		if (same(sp, &snake[j])) {
890 			pchar(sp, SNAKETAIL);
891 			return (4);
892 		}
893 	}
894 	if ((sp->col < 4) && (sp->line == 0)) {
895 		winnings(cashvalue);
896 		if ((you.line == 0) && (you.col < 4))
897 			pchar(&you, ME);
898 		return (5);
899 	}
900 	if (same(sp, &you)) {
901 		pchar(sp, ME);
902 		return (1);
903 	}
904 	pchar(sp, ' ');
905 	return (0);
906 }
907 
908 void
909 winnings(int won)
910 {
911 	if (won > 0) {
912 		mvprintw(1, 1, "$%d", won);
913 	}
914 }
915 
916 void
917 stop(int dummy __unused)
918 {
919 	signal(SIGINT, SIG_IGN);
920 	endwin();
921 	length(moves);
922 	exit(0);
923 }
924 
925 void
926 suspend(void)
927 {
928 	endwin();
929 	kill(getpid(), SIGTSTP);
930 	refresh();
931 	winnings(cashvalue);
932 }
933 
934 void
935 length(int num)
936 {
937 	printf("You made %d moves.\n", num);
938 }
939 
940 void
941 logit(const char *msg)
942 {
943 	time_t t;
944 
945 	if (logfile != NULL) {
946 		time(&t);
947 		fprintf(logfile, "%s $%d %dx%d %s %s",
948 		    getlogin(), cashvalue, lcnt, ccnt, msg, ctime(&t));
949 		fflush(logfile);
950 	}
951 }
952