xref: /dragonfly/games/snake/snake/snake.c (revision cecb9aae)
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 int main(int, char **);
113 void mainloop(void) __attribute__((__noreturn__));
114 struct point *point(struct point *, int, int);
115 int post(int, int);
116 int pushsnake(void);
117 void right(const struct point *);
118 void setup(void);
119 void snap(void);
120 void snrand(struct point *);
121 void spacewarp(int);
122 void stop(int) __attribute__((__noreturn__));
123 int stretch(const struct point *);
124 void surround(struct point *);
125 void suspend(void);
126 void win(const struct point *);
127 void winnings(int);
128 
129 int
130 main(int argc, char **argv)
131 {
132 	int ch, i;
133 	time_t tv;
134 
135 	/* Open score files then revoke setgid privileges */
136 	rawscores = open(_PATH_RAWSCORES, O_RDWR|O_CREAT, 0664);
137 	if (rawscores < 0) {
138 		warn("open %s", _PATH_RAWSCORES);
139 		sleep(2);
140 	} else if (rawscores < 3)
141 		exit(1);
142 	logfile = fopen(_PATH_LOGFILE, "a");
143 	if (logfile == NULL) {
144 		warn("fopen %s", _PATH_LOGFILE);
145 		sleep(2);
146 	}
147 	setgid(getgid());
148 
149 	time(&tv);
150 
151 	while ((ch = getopt(argc, argv, "l:w:t")) != -1)
152 		switch ((char) ch) {
153 #ifdef DEBUG
154 		case 'd':
155 			tv = atol(optarg);
156 			break;
157 #endif
158 		case 'w':	/* width */
159 			ccnt = atoi(optarg);
160 			break;
161 		case 'l':	/* length */
162 			lcnt = atoi(optarg);
163 			break;
164 		case 't':
165 			fast = 0;
166 			break;
167 		case '?':
168 		default:
169 #ifdef DEBUG
170 			fputs("usage: snake [-d seed] [-w width] [-l length] [-t]\n", stderr);
171 #else
172 			fputs("usage: snake [-w width] [-l length] [-t]\n", stderr);
173 #endif
174 			exit(1);
175 		}
176 
177 	srandom((int) tv);
178 
179 	penalty = loot = 0;
180 	initscr();
181 	cbreak();
182 	noecho();
183 #ifdef KEY_LEFT
184 	keypad(stdscr, TRUE);
185 #endif
186 	if (!lcnt || lcnt > LINES - 2)
187 		lcnt = LINES - 2;
188 	if (!ccnt || ccnt > COLS - 2)
189 		ccnt = COLS - 2;
190 
191 	i = MIN(lcnt, ccnt);
192 	if (i < 4) {
193 		endwin();
194 		errx(1, "screen too small for a fair game.");
195 	}
196 
197 	/*
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.
209 	 */
210 	if (i < 12)		/* otherwise it isn't fair */
211 		i = 12;
212 	/*
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
216 	 * much anyway.
217 	 */
218 	i += 2;
219 	chunk = (675.0 / (i + 6)) + 2.5;	/* min screen edge */
220 
221 	signal(SIGINT, stop);
222 
223 	snrand(&finish);
224 	snrand(&you);
225 	snrand(&money);
226 	snrand(&snake[0]);
227 
228 	for (i = 1; i < 6; i++)
229 		chase(&snake[i], &snake[i - 1]);
230 	setup();
231 	mainloop();
232 	/* NOTREACHED */
233 	return (0);
234 }
235 
236 struct point *
237 point(struct point *ps, int x, int y)
238 {
239 	ps->col = x;
240 	ps->line = y;
241 	return (ps);
242 }
243 
244 /* Main command loop */
245 void
246 mainloop(void)
247 {
248 	int     k;
249 	int     repeat = 1;
250 	int	lastc = 0;
251 
252 	for (;;) {
253 		int     c;
254 
255 		/* Highlight you, not left & above */
256 		move(you.line + 1, you.col + 1);
257 		refresh();
258 		if (((c = getch()) <= '9') && (c >= '0')) {
259 			repeat = c - '0';
260 			while (((c = getch()) <= '9') && (c >= '0'))
261 				repeat = 10 * repeat + (c - '0');
262 		} else {
263 			if (c != '.')
264 				repeat = 1;
265 		}
266 		if (c == '.') {
267 			c = lastc;
268 		}
269 		if (!fast)
270 			flushi();
271 		lastc = c;
272 		switch (c) {
273 		case CTRL('z'):
274 			suspend();
275 			continue;
276 		case EOT:
277 		case 'x':
278 		case 0177:	/* del or end of file */
279 			endwin();
280 			length(moves);
281 			logit("quit");
282 			exit(0);
283 		case CTRL('l'):
284 			setup();
285 			winnings(cashvalue);
286 			continue;
287 		case 'p':
288 		case 'd':
289 			snap();
290 			continue;
291 		case 'w':
292 			spacewarp(0);
293 			continue;
294 		case 'A':
295 			repeat = you.col;
296 			c = 'h';
297 			break;
298 		case 'H':
299 		case 'S':
300 			repeat = you.col - money.col;
301 			c = 'h';
302 			break;
303 		case 'T':
304 			repeat = you.line;
305 			c = 'k';
306 			break;
307 		case 'K':
308 		case 'E':
309 			repeat = you.line - money.line;
310 			c = 'k';
311 			break;
312 		case 'P':
313 			repeat = ccnt - 1 - you.col;
314 			c = 'l';
315 			break;
316 		case 'L':
317 		case 'F':
318 			repeat = money.col - you.col;
319 			c = 'l';
320 			break;
321 		case 'B':
322 			repeat = lcnt - 1 - you.line;
323 			c = 'j';
324 			break;
325 		case 'J':
326 		case 'C':
327 			repeat = money.line - you.line;
328 			c = 'j';
329 			break;
330 		}
331 		for (k = 1; k <= repeat; k++) {
332 			moves++;
333 			switch (c) {
334 			case 's':
335 			case 'h':
336 #ifdef KEY_LEFT
337 			case KEY_LEFT:
338 #endif
339 			case '\b':
340 				if (you.col > 0) {
341 					if ((fast) || (k == 1))
342 						pchar(&you, ' ');
343 					you.col--;
344 					if ((fast) || (k == repeat) ||
345 					    (you.col == 0))
346 						pchar(&you, ME);
347 				}
348 				break;
349 			case 'f':
350 			case 'l':
351 #ifdef KEY_RIGHT
352 			case KEY_RIGHT:
353 #endif
354 			case ' ':
355 				if (you.col < ccnt - 1) {
356 					if ((fast) || (k == 1))
357 						pchar(&you, ' ');
358 					you.col++;
359 					if ((fast) || (k == repeat) ||
360 					    (you.col == ccnt - 1))
361 						pchar(&you, ME);
362 				}
363 				break;
364 			case CTRL('p'):
365 			case 'e':
366 			case 'k':
367 #ifdef KEY_UP
368 			case KEY_UP:
369 #endif
370 			case 'i':
371 				if (you.line > 0) {
372 					if ((fast) || (k == 1))
373 						pchar(&you, ' ');
374 					you.line--;
375 					if ((fast) || (k == repeat) ||
376 					    (you.line == 0))
377 						pchar(&you, ME);
378 				}
379 				break;
380 			case CTRL('n'):
381 			case 'c':
382 			case 'j':
383 #ifdef KEY_DOWN
384 			case KEY_DOWN:
385 #endif
386 			case LF:
387 			case 'm':
388 				if (you.line + 1 < lcnt) {
389 					if ((fast) || (k == 1))
390 						pchar(&you, ' ');
391 					you.line++;
392 					if ((fast) || (k == repeat) ||
393 					    (you.line == lcnt - 1))
394 						pchar(&you, ME);
395 				}
396 				break;
397 			}
398 
399 			if (same(&you, &money)) {
400 				loot += 25;
401 				if (k < repeat)
402 					pchar(&you, ' ');
403 				do {
404 					snrand(&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);
411 				winnings(cashvalue);
412 				continue;
413 			}
414 			if (same(&you, &finish)) {
415 				win(&finish);
416 				flushi();
417 				endwin();
418 				printf("You have won with $%d.\n", cashvalue);
419 				fflush(stdout);
420 				logit("won");
421 				post(cashvalue, 1);
422 				length(moves);
423 				exit(0);
424 			}
425 			if (pushsnake())
426 				break;
427 		}
428 	}
429 }
430 
431 /*
432  * setup the board
433  */
434 void
435 setup(void)
436 {
437 	int i;
438 
439 	erase();
440 	pchar(&you, ME);
441 	pchar(&finish, GOAL);
442 	pchar(&money, TREASURE);
443 	for (i = 1; i < 6; i++) {
444 		pchar(&snake[i], SNAKETAIL);
445 	}
446 	pchar(&snake[0], SNAKEHEAD);
447 	drawbox();
448 	refresh();
449 }
450 
451 void
452 drawbox(void)
453 {
454 	int i;
455 
456 	for (i = 1; i <= ccnt; i++) {
457 		mvaddch(0, i, '-');
458 		mvaddch(lcnt + 1, i, '-');
459 	}
460 	for (i = 0; i <= lcnt + 1; i++) {
461 		mvaddch(i, 0, '|');
462 		mvaddch(i, ccnt + 1, '|');
463 	}
464 }
465 
466 void
467 snrand(struct point *sp)
468 {
469 	struct point p;
470 	int i;
471 
472 	for (;;) {
473 		p.col = random() % ccnt;
474 		p.line = random() % lcnt;
475 
476 		/* make sure it's not on top of something else */
477 		if (p.line == 0 && p.col < 5)
478 			continue;
479 		if (same(&p, &you))
480 			continue;
481 		if (same(&p, &money))
482 			continue;
483 		if (same(&p, &finish))
484 			continue;
485 		for (i = 0; i < 6; i++)
486 			if (same(&p, &snake[i]))
487 				break;
488 		if (i < 6)
489 			continue;
490 		break;
491 	}
492 	*sp = p;
493 }
494 
495 int
496 post(int iscore, int flag)
497 {
498 	short	score = iscore;
499 	short	uid;
500 	short	oldbest = 0;
501 	short	allbwho = 0, allbscore = 0;
502 	struct	passwd *p;
503 
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
507 	 * can printf.
508 	 */
509 	/*
510 	 * Neg uid, 0, and 1 cannot have scores recorded.
511 	 */
512 	if ((uid = getuid()) <= 1) {
513 		if (flag)
514 			printf("No saved scores for uid %d.\n", uid);
515 		return (1);
516 	}
517 	if (rawscores < 0) {
518 		/* Error reported earlier */
519 		return (1);
520 	}
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));
526 	if (!flag) {
527 		lseek(rawscores, 0, SEEK_SET);
528 		return (score > oldbest ? 1 : 0);
529 	}
530 
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);
536 	} else
537 		printf("Your best to date is $%d\n", oldbest);
538 
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));
545 		if (allbwho) {
546 			if (p)
547 				printf("You beat %s's old record of $%d!\n",
548 				    p->pw_name, allbscore);
549 			else
550 				printf("You beat (%d)'s old record of $%d!\n",
551 				       (int)allbwho, allbscore);
552 		}
553 		else
554 			printf("You set a new record!\n");
555 	} else if (p)
556 		printf("The highest is %s with $%d\n", p->pw_name, allbscore);
557 	else
558 		printf("The highest is (%d) with $%d\n", (int)allbwho,
559 		    allbscore);
560 	lseek(rawscores, 0, SEEK_SET);
561 	return (1);
562 }
563 
564 /*
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.
568  */
569 void
570 flushi(void)
571 {
572 	tcflush(0, TCIFLUSH);
573 }
574 
575 int mx[8] = {
576 	0, 1, 1, 1, 0, -1, -1, -1
577 };
578 int my[8] = {
579 	-1, -1, 0, 1, 1, 1, 0, -1
580 };
581 float absv[8] = {
582 	1, 1.4, 1, 1.4, 1, 1.4, 1, 1.4
583 };
584 int oldw = 0;
585 
586 void
587 chase(struct point *np, struct point *sp)
588 {
589 	/* this algorithm has bugs; otherwise the snake would get too good */
590 	struct point d;
591 	int w, i, wt[8];
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));
595 	w = 0;
596 	max = 0;
597 	for (i = 0; i < 8; i++) {
598 		vp = d.col * mx[i] + d.line * my[i];
599 		v2 = absv[i];
600 		if (v1 > 0)
601 			vp = ((double)vp) / (v1 * v2);
602 		else
603 			vp = 1.0;
604 		if (vp > max) {
605 			max = vp;
606 			w = i;
607 		}
608 	}
609 	for (i = 0; i < 8; i++) {
610 		point(&d, sp->col + mx[i], sp->line + my[i]);
611 		wt[i] = 0;
612 		if (d.col < 0 || d.col >= ccnt || d.line < 0 || d.line >= lcnt)
613 			continue;
614 		/*
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.
618 		 *
619 		 * if (d.line == 0 && d.col < 5) continue;
620 		 */
621 		if (same(&d, &money))
622 			continue;
623 		if (same(&d, &finish))
624 			continue;
625 		wt[i] = i == w ? loot / 10 : 1;
626 		if (i == oldw)
627 			wt[i] += loot / 20;
628 	}
629 	for (w = i = 0; i < 8; i++)
630 		w += wt[i];
631 	vp = ((random() >> 6) & 01777) % w;
632 	for (i = 0; i < 8; i++)
633 		if (vp < wt[i])
634 			break;
635 		else
636 			vp -= wt[i];
637 	if (i == 8) {
638 		printw("failure\n");
639 		i = 0;
640 		while (wt[i] == 0)
641 			i++;
642 	}
643 	oldw = w = i;
644 	point(np, sp->col + mx[w], sp->line + my[w]);
645 }
646 
647 void
648 spacewarp(int w)
649 {
650 	struct point p;
651 	int j;
652 	const char   *str;
653 
654 	snrand(&you);
655 	point(&p, COLS / 2 - 8, LINES / 2 - 1);
656 	if (p.col < 0)
657 		p.col = 0;
658 	if (p.line < 0)
659 		p.line = 0;
660 	if (w) {
661 		str = "BONUS!!!";
662 		loot = loot - penalty;
663 		penalty = 0;
664 	} else {
665 		str = "SPACE WARP!!!";
666 		penalty += loot / PENALTY;
667 	}
668 	for (j = 0; j < 3; j++) {
669 		erase();
670 		refresh();
671 		delay(5);
672 		mvaddstr(p.line + 1, p.col + 1, str);
673 		refresh();
674 		delay(10);
675 	}
676 	setup();
677 	winnings(cashvalue);
678 }
679 
680 void
681 snap(void)
682 {
683 	if (!stretch(&money))
684 		if (!stretch(&finish)) {
685 			pchar(&you, '?');
686 			refresh();
687 			delay(10);
688 			pchar(&you, ME);
689 		}
690 	refresh();
691 }
692 
693 int
694 stretch(const struct point *ps)
695 {
696 	struct point p;
697 
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++)
702 				pchar(&p, 'v');
703 			refresh();
704 			delay(10);
705 			for (; p.line > you.line; p.line--)
706 				chk(&p);
707 		} else {
708 			for (p.line = you.line - 1; p.line >= ps->line; p.line--)
709 				pchar(&p, '^');
710 			refresh();
711 			delay(10);
712 			for (; p.line < you.line; p.line++)
713 				chk(&p);
714 		}
715 		return (1);
716 	} else
717 		if ((abs(ps->line - you.line) < (lcnt / 7))
718 		    && (you.col != ps->col)) {
719 			p.line = you.line;
720 			if (you.col < ps->col) {
721 				for (p.col = you.col + 1; p.col <= ps->col; p.col++)
722 					pchar(&p, '>');
723 				refresh();
724 				delay(10);
725 				for (; p.col > you.col; p.col--)
726 					chk(&p);
727 			} else {
728 				for (p.col = you.col - 1; p.col >= ps->col; p.col--)
729 					pchar(&p, '<');
730 				refresh();
731 				delay(10);
732 				for (; p.col < you.col; p.col++)
733 					chk(&p);
734 			}
735 			return (1);
736 		}
737 	return (0);
738 }
739 
740 void
741 surround(struct point *ps)
742 {
743 	int j;
744 
745 	if (ps->col == 0)
746 		ps->col++;
747 	if (ps->line == 0)
748 		ps->line++;
749 	if (ps->line == LINES - 1)
750 		ps->line--;
751 	if (ps->col == COLS - 1)
752 		ps->col--;
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++) {
757 		pchar(ps, '@');
758 		refresh();
759 		delay(1);
760 		pchar(ps, ' ');
761 		refresh();
762 		delay(1);
763 	}
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, "\\_/");
768 		refresh();
769 		delay(6);
770 		mvaddstr(ps->line, ps->col, "   ");
771 		mvaddstr(ps->line + 1, ps->col, "o.-");
772 		mvaddstr(ps->line + 2, ps->col, "\\_/");
773 		refresh();
774 		delay(6);
775 	}
776 	mvaddstr(ps->line, ps->col, "   ");
777 	mvaddstr(ps->line + 1, ps->col, "o.o");
778 	mvaddstr(ps->line + 2, ps->col, "\\_/");
779 	refresh();
780 	delay(6);
781 }
782 
783 void
784 win(const struct point *ps)
785 {
786 	struct point x;
787 	int j, k;
788 	int boxsize;	/* actually diameter of box, not radius */
789 
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++) {
794 			pchar(&x, '#');
795 			x.line--;
796 		}
797 		for (k = 0; k < j; k++) {
798 			pchar(&x, '#');
799 			x.col++;
800 		}
801 		j++;
802 		for (k = 0; k < j; k++) {
803 			pchar(&x, '#');
804 			x.line++;
805 		}
806 		for (k = 0; k < j; k++) {
807 			pchar(&x, '#');
808 			x.col--;
809 		}
810 		refresh();
811 		delay(1);
812 	}
813 }
814 
815 int
816 pushsnake(void)
817 {
818 	int i, bonus;
819 	int issame = 0;
820 	struct point tmp;
821 
822 	/*
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.
827 	 */
828 	for (i = 4; i >= 0; i--)
829 		if (same(&snake[i], &snake[5]))
830 			issame++;
831 	if (!issame)
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)) {
843 			surround(&you);
844 			i = (cashvalue) % 10;
845 			bonus = ((random() >> 8) & 0377) % 10;
846 			mvprintw(lcnt + 1, 0, "%d\n", bonus);
847 			refresh();
848 			delay(30);
849 			if (bonus == i) {
850 				spacewarp(1);
851 				logit("bonus");
852 				flushi();
853 				return (1);
854 			}
855 			flushi();
856 			endwin();
857 			if (loot >= penalty) {
858 				printf("\nYou and your $%d have been eaten\n",
859 				    cashvalue);
860 			} else {
861 				printf("\nThe snake ate you.  You owe $%d.\n",
862 				    -cashvalue);
863 			}
864 			logit("eaten");
865 			length(moves);
866 			exit(0);
867 		}
868 	}
869 	return (0);
870 }
871 
872 int
873 chk(const struct point *sp)
874 {
875 	int j;
876 
877 	if (same(sp, &money)) {
878 		pchar(sp, TREASURE);
879 		return (2);
880 	}
881 	if (same(sp, &finish)) {
882 		pchar(sp, GOAL);
883 		return (3);
884 	}
885 	if (same(sp, &snake[0])) {
886 		pchar(sp, SNAKEHEAD);
887 		return (4);
888 	}
889 	for (j = 1; j < 6; j++) {
890 		if (same(sp, &snake[j])) {
891 			pchar(sp, SNAKETAIL);
892 			return (4);
893 		}
894 	}
895 	if ((sp->col < 4) && (sp->line == 0)) {
896 		winnings(cashvalue);
897 		if ((you.line == 0) && (you.col < 4))
898 			pchar(&you, ME);
899 		return (5);
900 	}
901 	if (same(sp, &you)) {
902 		pchar(sp, ME);
903 		return (1);
904 	}
905 	pchar(sp, ' ');
906 	return (0);
907 }
908 
909 void
910 winnings(int won)
911 {
912 	if (won > 0) {
913 		mvprintw(1, 1, "$%d", won);
914 	}
915 }
916 
917 void
918 stop(int dummy __unused)
919 {
920 	signal(SIGINT, SIG_IGN);
921 	endwin();
922 	length(moves);
923 	exit(0);
924 }
925 
926 void
927 suspend(void)
928 {
929 	endwin();
930 	kill(getpid(), SIGTSTP);
931 	refresh();
932 	winnings(cashvalue);
933 }
934 
935 void
936 length(int num)
937 {
938 	printf("You made %d moves.\n", num);
939 }
940 
941 void
942 logit(const char *msg)
943 {
944 	time_t t;
945 
946 	if (logfile != NULL) {
947 		time(&t);
948 		fprintf(logfile, "%s $%d %dx%d %s %s",
949 		    getlogin(), cashvalue, lcnt, ccnt, msg, ctime(&t));
950 		fflush(logfile);
951 	}
952 }
953