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