xref: /openbsd/games/snake/snake.c (revision 898184e3)
1 /*	$OpenBSD: snake.c,v 1.14 2009/11/13 21:50:12 deraadt Exp $	*/
2 /*	$NetBSD: snake.c,v 1.8 1995/04/29 00:06:41 mycroft Exp $	*/
3 
4 /*
5  * Copyright (c) 1980, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 /*
34  * snake - crt hack game.
35  *
36  * You move around the screen with arrow keys trying to pick up money
37  * without getting eaten by the snake.  hjkl work as in vi in place of
38  * arrow keys.  You can leave at the exit any time.
39  *
40  * compile as follows:
41  *	cc -O snake.c move.c -o snake -lm -lcurses
42  */
43 
44 #include <sys/param.h>
45 #include <sys/types.h>
46 #include <sys/ioctl.h>
47 
48 #include <curses.h>
49 #include <err.h>
50 #include <fcntl.h>
51 #include <math.h>
52 #include <pwd.h>
53 #include <signal.h>
54 #include <stdio.h>
55 #include <stdlib.h>
56 #include <string.h>
57 #include <termios.h>
58 #include <time.h>
59 #include <unistd.h>
60 
61 #include "pathnames.h"
62 
63 #ifdef	DEBUG
64 #define	cashvalue	(loot-penalty)/25
65 #else
66 #define	cashvalue	chunk*(loot-penalty)/25
67 #endif
68 
69 struct point {
70 	int col, line;
71 };
72 
73 #define	same(s1, s2)	((s1)->line == (s2)->line && (s1)->col == (s2)->col)
74 
75 #define PENALTY	10	/* % penalty for invoking spacewarp     */
76 
77 #define ME		'I'
78 #define SNAKEHEAD	'S'
79 #define SNAKETAIL	's'
80 #define TREASURE	'$'
81 #define GOAL		'#'
82 
83 #define TOPN	3	/* top scores to print if you lose */
84 
85 #define pchar(point, c)	mvaddch((point)->line + 1, (point)->col + 1, (c))
86 /* Can't use terminal timing to do delay, in light of X */
87 #define delay(t)	usleep((t) * 50000)
88 /* Delay units are 1/20 s */
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 #ifdef LOGGING
101 FILE	*logfile;
102 #endif
103 
104 int	lcnt, ccnt;	/* user's idea of screen size */
105 int	chunk;		/* amount of money given at a time */
106 
107 void	snscore(int, int);
108 
109 void	chase(struct point *, struct point *);
110 int	chk(struct point *);
111 void	drawbox(void);
112 void	length(int);
113 void	mainloop(void);
114 int	post(int, int);
115 int	pushsnake(void);
116 void	setup(void);
117 void	snrand(struct point *);
118 void	snap(void);
119 void	spacewarp(int);
120 void	stop(int);
121 int	stretch(struct point *);
122 void	surround(struct point *);
123 void	suspend(void);
124 void	win(struct point *);
125 void	winnings(int);
126 
127 #ifdef LOGGING
128 void	logit(char *);
129 #endif
130 
131 int	wantstop;
132 
133 int
134 main(int argc, char *argv[])
135 {
136 	int	ch, i;
137 	struct sigaction sa;
138 	gid_t	gid;
139 
140 	/* don't create the score file if it doesn't exist. */
141 	rawscores = open(_PATH_RAWSCORES, O_RDWR, 0664);
142 #ifdef LOGGING
143 	logfile = fopen(_PATH_LOGFILE, "a");
144 #endif
145 
146 	/* revoke privs */
147 	gid = getgid();
148 	setresgid(gid, gid, gid);
149 
150 	while ((ch = getopt(argc, argv, "hl:stw:")) != -1)
151 		switch ((char)ch) {
152 		case 'w':	/* width */
153 			ccnt = atoi(optarg);
154 			break;
155 		case 'l':	/* length */
156 			lcnt = atoi(optarg);
157 			break;
158 		case 's': /* score */
159 			snscore(rawscores, 0);
160 			exit(0);
161 			break;
162 		case 't': /* slow terminal */
163 			fast = 0;
164 			break;
165 		case '?':
166 		case 'h':
167 		default:
168 			fputs("usage: snake [-st] [-l length] [-w width]\n",
169 			    stderr);
170 			exit(1);
171 		}
172 
173 	srandomdev();
174 	penalty = loot = 0;
175 	initscr();
176 #ifdef KEY_LEFT
177 	keypad(stdscr, TRUE);
178 #endif
179 	nonl();
180 	cbreak();
181 	noecho();
182 
183 	if (!lcnt || lcnt > LINES - 2)
184 		lcnt = LINES - 2;
185 	if (!ccnt || ccnt > COLS - 3)
186 		ccnt = COLS - 3;
187 
188 	i = MIN(lcnt, ccnt);
189 	if (i < 4) {
190 		endwin();
191 		errx(1, "screen too small for a fair game.");
192 	}
193 	/*
194 	 * chunk is the amount of money the user gets for each $.
195 	 * The formula below tries to be fair for various screen sizes.
196 	 * We only pay attention to the smaller of the 2 edges, since
197 	 * that seems to be the bottleneck.
198 	 * This formula is a hyperbola which includes the following points:
199 	 *	(24, $25)	(original scoring algorithm)
200 	 *	(12, $40)	(experimentally derived by the "feel")
201 	 *	(48, $15)	(a guess)
202 	 * This will give a 4x4 screen $99/shot.  We don't allow anything
203 	 * smaller than 4x4 because there is a 3x3 game where you can win
204 	 * an infinite amount of money.
205 	 */
206 	if (i < 12)
207 		i = 12;	/* otherwise it isn't fair */
208 	/*
209 	 * Compensate for border.  This really changes the game since
210 	 * the screen is two squares smaller but we want the default
211 	 * to be $25, and the high scores on small screens were a bit
212 	 * much anyway.
213 	 */
214 	i += 2;
215 	chunk = (675.0 / (i + 6)) + 2.5;	/* min screen edge */
216 
217 	memset(&sa, 0, sizeof sa);
218 	sigemptyset(&sa.sa_mask);
219 	sa.sa_handler = stop;
220 	sigaction(SIGINT, &sa, NULL);
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 	/* NOT REACHED */
232 	return(0);
233 }
234 
235 /* Main command loop */
236 void
237 mainloop(void)
238 {
239 	int	k;
240 	int	c, lastc = 0;
241 	int repeat = 1;
242 
243 	for (;;) {
244 		if (wantstop) {
245 			endwin();
246 			length(moves);
247 			exit(0);
248 		}
249 
250 		/* Highlight you, not left & above */
251 		move(you.line + 1, you.col + 1);
252 		refresh();
253 		if (((c = getch()) <= '9') && (c >= '0')) {
254 			repeat = c - '0';
255 			while (((c = getch()) <= '9') && (c >= '0'))
256 				repeat = 10 * repeat + (c - '0');
257 		} else {
258 			if (c != '.')
259 				repeat = 1;
260 		}
261 		if (c == '.')
262 			c = lastc;
263 		if (!fast)
264 			flushinp();
265 		lastc = c;
266 
267 		switch (c) {
268 		case CTRL('z'):
269 			suspend();
270 			continue;
271 		case '\044':
272 		case 'x':
273 		case 0177:	/* del or end of file */
274 		case ERR:
275 			endwin();
276 			length(moves);
277 #ifdef LOGGING
278 			logit("quit");
279 #endif
280 			exit(0);
281 		case CTRL('l'):
282 			setup();
283 			winnings(cashvalue);
284 			continue;
285 		case 'p':
286 		case 'd':
287 			snap();
288 			continue;
289 		case 'w':
290 			spacewarp(0);
291 			continue;
292 		case 'A':
293 			repeat = you.col;
294 			c = 'h';
295 			break;
296 		case 'H':
297 		case 'S':
298 			repeat = you.col - money.col;
299 			c = 'h';
300 			break;
301 		case 'T':
302 			repeat = you.line;
303 			c = 'k';
304 			break;
305 		case 'K':
306 		case 'E':
307 			repeat = you.line - money.line;
308 			c = 'k';
309 			break;
310 		case 'P':
311 			repeat = ccnt - 1 - you.col;
312 			c = 'l';
313 			break;
314 		case 'L':
315 		case 'F':
316 			repeat = money.col - you.col;
317 			c = 'l';
318 			break;
319 		case 'B':
320 			repeat = lcnt - 1 - you.line;
321 			c = 'j';
322 			break;
323 		case 'J':
324 		case 'C':
325 			repeat = money.line - you.line;
326 			c = 'j';
327 			break;
328 		}
329 		for (k = 1; k <= repeat; k++) {
330 			moves++;
331 			switch (c) {
332 			case 's':
333 			case 'h':
334 #ifdef KEY_LEFT
335 			case KEY_LEFT:
336 #endif
337 			case '\b':
338 				if (you.col > 0) {
339 					if ((fast) || (k == 1))
340 						pchar(&you, ' ');
341 					you.col--;
342 					if ((fast) || (k == repeat) ||
343 					    (you.col == 0))
344 						pchar(&you, ME);
345 				}
346 				break;
347 			case 'f':
348 			case 'l':
349 #ifdef KEY_RIGHT
350 			case KEY_RIGHT:
351 #endif
352 			case ' ':
353 				if (you.col < ccnt - 1) {
354 					if ((fast) || (k == 1))
355 						pchar(&you, ' ');
356 					you.col++;
357 					if ((fast) || (k == repeat) ||
358 					    (you.col == ccnt - 1))
359 						pchar(&you, ME);
360 				}
361 				break;
362 			case CTRL('p'):
363 			case 'e':
364 			case 'k':
365 #ifdef KEY_UP
366 			case KEY_UP:
367 #endif
368 			case 'i':
369 				if (you.line > 0) {
370 					if ((fast) || (k == 1))
371 						pchar(&you, ' ');
372 					you.line--;
373 					if ((fast) || (k == repeat) ||
374 					    (you.line == 0))
375 						pchar(&you, ME);
376 				}
377 				break;
378 			case CTRL('n'):
379 			case 'c':
380 			case 'j':
381 #ifdef KEY_DOWN
382 			case KEY_DOWN:
383 #endif
384 			case '\n':
385 			case '\r':
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;		 Previously, snake missed a turn! */
412 			}
413 			if (same(&you, &finish)) {
414 				win(&finish);
415 				flushinp();
416 				endwin();
417 				printf("You have won with $%d.\n", cashvalue);
418 				fflush(stdout);
419 #ifdef LOGGING
420 				logit("won");
421 #endif
422 				length(moves);
423 				post(cashvalue, 1);
424 				close(rawscores);
425 				exit(0);
426 			}
427 			if (pushsnake())
428 				break;
429 		}
430 	}
431 }
432 
433 /* set up the board */
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	oldbest = 0;
500 	uid_t	uid = getuid();
501 
502 	/* I want to printf() the scores for terms that clear on endwin(),
503 	 * but this routine also gets called with flag == 0 to see if
504 	 * the snake should wink.  If (flag) then we're at game end and
505 	 * can printf.
506 	 */
507 	if (rawscores == -1) {
508 		if (flag)
509 			warnx("Can't open score file %s", _PATH_RAWSCORES);
510 		return(1);
511 	}
512 	/* Figure out what happened in the past */
513 	lseek(rawscores, uid * sizeof(short), SEEK_SET);
514 	read(rawscores, &oldbest, sizeof(short));
515 	if (!flag)
516 		return (score > oldbest ? 1 : 0);
517 
518 	/* Update this jokers best */
519 	if (score > oldbest) {
520 		lseek(rawscores, uid * sizeof(short), SEEK_SET);
521 		write(rawscores, &score, sizeof(short));
522 		printf("\nYou bettered your previous best of $%d\n", oldbest);
523 	} else
524 		printf("\nYour best to date is $%d\n", oldbest);
525 
526 	fsync(rawscores);
527 	/* See if we have a new champ */
528 	snscore(rawscores, TOPN);
529 	return(1);
530 }
531 
532 const int	mx[8] = { 0, 1, 1, 1, 0,-1,-1,-1};
533 const int	my[8] = {-1,-1, 0, 1, 1, 1, 0,-1};
534 const float	absv[8] = {1, 1.4, 1, 1.4, 1, 1.4, 1, 1.4};
535 int	oldw = 0;
536 
537 void
538 chase(struct point *np, struct point *sp)
539 {
540 	/* this algorithm has bugs; otherwise the snake would get too good */
541 	struct point d;
542 	int	w, i, wt[8];
543 	double	v1, v2, vp, max;
544 
545 	d.col = you.col-sp->col;
546 	d.line = you.line-sp->line;
547 	v1 = sqrt((double)(d.col * d.col + d.line * d.line) );
548 	w  = 0;
549 	max = 0;
550 	for (i = 0; i < 8; i++) {
551 		vp = d.col * mx[i] + d.line * my[i];
552 		v2 = absv[i];
553 		if (v1 > 0)
554 			vp = ((double)vp) / (v1 * v2);
555 		else
556 			vp = 1.0;
557 		if (vp > max) {
558 			max = vp;
559 			w = i;
560 		}
561 	}
562 	for (i = 0; i < 8; i++) {
563 		d.col = sp->col + mx[i];
564 		d.line = sp->line + my[i];
565 		wt[i] = 0;
566 		if (d.col < 0 || d.col >= ccnt || d.line < 0 || d.line >= lcnt)
567 			continue;
568 		/*
569 		 * Change to allow snake to eat you if you're on the money;
570 		 * otherwise, you can just crouch there until the snake goes
571 		 * away.  Not positive it's right.
572 		 *
573 		 * if (d.line == 0 && d.col < 5) continue;
574 		 */
575 		if (same(&d, &money) || same(&d,&finish))
576 			continue;
577 		wt[i] = (i == w ? loot/10 : 1);
578 		if (i == oldw)
579 			wt[i] += loot/20;
580 	}
581 	for (w = i = 0; i < 8; i++)
582 		w += wt[i];
583 	vp = ((random() >> 6) & 01777) % w;
584 	for (i = 0; i < 8; i++)
585 		if (vp < wt[i])
586 			break;
587 		else
588 			vp -= wt[i];
589 	if (i == 8) {
590 		printw("failure\n");
591 		i = 0;
592 		while (wt[i] == 0)
593 			i++;
594 	}
595 	oldw = w = i;
596 	np->col = sp->col + mx[w];
597 	np->line = sp->line + my[w];
598 }
599 
600 void
601 spacewarp(int w)
602 {
603 	struct point p;
604 	int	j;
605 	const char  *str;
606 
607 	snrand(&you);
608 	p.col = COLS / 2 - 8;
609 	p.line = LINES / 2 - 1;
610 	if (p.col < 0)
611 		p.col = 0;
612 	if (p.line < 0)
613 		p.line = 0;
614 	if (w) {
615 		str = "BONUS!!!";
616 		loot = loot - penalty;
617 		penalty = 0;
618 	} else {
619 		str = "SPACE WARP!!!";
620 		penalty += loot / PENALTY;
621 	}
622 	for (j = 0; j < 3; j++) {
623 		erase();
624 		refresh();
625 		delay(5);
626 		mvaddstr(p.line + 1, p.col + 1, str);
627 		refresh();
628 		delay(10);
629 	}
630 	setup();
631 	winnings(cashvalue);
632 }
633 
634 void
635 snap(void)
636 {
637 
638 	/* I don't see the graphical purpose of the next block of code.
639 	 * It just makes no sense.
640 	 *
641 	 * struct point p;
642 	 *
643 	 * if (you.line < 3)
644 	 *	pchar(point(&p, you.col, 0), '-');
645 	 * if (you.line > lcnt - 4)
646 	 *	pchar(point(&p, you.col, lcnt - 1), '_');
647 	 * if(you.col < 10)
648 	 *	pchar(point(&p, 0, you.line), '(');
649 	 * if(you.col > ccnt-10)
650 	 *	pchar(point(&p, ccnt-1, you.line), ')');
651 	 */
652 	if (!stretch(&money))
653 		if (!stretch(&finish)) {
654 			pchar(&you, '?');
655 			refresh();
656 			delay(10);
657 			pchar(&you, ME);
658 		}
659 	/* Again, I don't see the point of the following either.
660 	 *
661 	 * if (you.line < 3) {
662 	 * 	point(&p, you.col, 0);
663 	 * 	chk(&p);
664 	 * }
665 	 * if (you.line > lcnt - 4) {
666 	 * 	point(&p, you.col, lcnt - 1);
667 	 * 	chk(&p);
668 	 * }
669 	 * if (you.col < 10) {
670 	 * 	point(&p, 0, you.line);
671 	 * 	chk(&p);
672 	 * }
673 	 * if (you.col > ccnt-10) {
674 	 * 	point(&p, ccnt - 1, you.line);
675 	 * 	chk(&p);
676 	 * }
677 	 */
678 	refresh();
679 }
680 
681 int
682 stretch(struct point *ps)
683 {
684 	struct point p;
685 
686 	p.col = you.col;
687 	p.line = you.line;
688 	if ((abs(ps->col - you.col) < (ccnt / 12)) && (you.line != ps->line)) {
689 		if (you.line < ps->line) {
690 			for (p.line = you.line + 1; p.line <= ps->line; p.line++)
691 				pchar(&p, 'v');
692 			refresh();
693 			delay(10);
694 			for (; p.line > you.line; p.line--)
695 				chk(&p);
696 		} else {
697 			for (p.line = you.line - 1; p.line >= ps->line; p.line--)
698 				pchar(&p, '^');
699 			refresh();
700 			delay(10);
701 			for (; p.line < you.line; p.line++)
702 				chk(&p);
703 		}
704 		return(1);
705 	} else if ((abs(ps->line - you.line) < (lcnt / 7)) && (you.col != ps->col)) {
706 		p.line = you.line;
707 		if (you.col < ps->col) {
708 			for (p.col = you.col + 1; p.col <= ps->col; p.col++)
709 				pchar(&p, '>');
710 			refresh();
711 			delay(10);
712 			for (; p.col > you.col; p.col--)
713 				chk(&p);
714 		} else {
715 			for (p.col = you.col - 1; p.col >= ps->col; p.col--)
716 				pchar(&p, '<');
717 			refresh();
718 			delay(10);
719 			for (; p.col < you.col; p.col++)
720 				chk(&p);
721 		}
722 		return(1);
723 	}
724 	return(0);
725 }
726 
727 void
728 surround(struct point *ps)
729 {
730 	int	j;
731 
732 	if (ps->col == 0)
733 		ps->col++;
734 	if (ps->line == 0)
735 		ps->line++;
736 	if (ps->line == LINES - 1)
737 		ps->line--;
738 	if (ps->col == COLS - 1)
739 		ps->col--;
740 	mvaddstr(ps->line, ps->col, "/*\\");
741 	mvaddstr(ps->line + 1, ps->col, "* *");
742 	mvaddstr(ps->line + 2, ps->col, "\\*/");
743 	for (j = 0; j < 20; j++) {
744 		pchar(ps, '@');
745 		refresh();
746 		delay(1);
747 		pchar(ps, ' ');
748 		refresh();
749 		delay(1);
750 	}
751 	if (post(cashvalue, 0)) {
752 		mvaddstr(ps->line, ps->col, "   ");
753 		mvaddstr(ps->line + 1, ps->col, "o.o");
754 		mvaddstr(ps->line + 2, ps->col, "\\_/");
755 		refresh();
756 		delay(6);
757 		mvaddstr(ps->line, ps->col, "   ");
758 		mvaddstr(ps->line + 1, ps->col, "o.-");
759 		mvaddstr(ps->line + 2, ps->col, "\\_/");
760 		refresh();
761 		delay(6);
762 	}
763 	mvaddstr(ps->line, ps->col, "   ");
764 	mvaddstr(ps->line + 1, ps->col, "o.o");
765 	mvaddstr(ps->line + 2, ps->col, "\\_/");
766 	refresh();
767 	delay(6);
768 }
769 
770 void
771 win(struct point *ps)
772 {
773 	struct point x;
774 	int	j, k;
775 	int	boxsize;	/* actually diameter of box, not radius */
776 
777 	boxsize = (fast ? 10 : 4);
778 	x.col = ps->col;
779 	x.line = ps->line;
780 	for (j = 1; j < boxsize; j++) {
781 		for (k = 0; k < j; k++) {
782 			pchar(&x, '#');
783 			x.line--;
784 		}
785 		for (k = 0; k < j; k++) {
786 			pchar(&x, '#');
787 			x.col++;
788 		}
789 		j++;
790 		for (k = 0; k < j; k++) {
791 			pchar(&x, '#');
792 			x.line++;
793 		}
794 		for (k = 0; k < j; k++) {
795 			pchar(&x, '#');
796 			x.col--;
797 		}
798 		refresh();
799 		delay(1);
800 	}
801 }
802 
803 int
804 pushsnake(void)
805 {
806 	int	i, bonus;
807 	int	issame = 0;
808 	struct point tmp;
809 
810 	/*
811 	 * My manual says times doesn't return a value.  Furthermore, the
812 	 * snake should get his turn every time no matter if the user is
813 	 * on a fast terminal with typematic keys or not.
814 	 * So I have taken the call to times out.
815 	 */
816 	for (i = 4; i >= 0; i--)
817 		if (same(&snake[i], &snake[5]))
818 			issame++;
819 	if (!issame)
820 		pchar(&snake[5], ' ');
821 	/* Need the following to catch you if you step on the snake's tail */
822 	tmp.col = snake[5].col;
823 	tmp.line = snake[5].line;
824 	for (i = 4; i >= 0; i--)
825 		snake[i + 1] = snake[i];
826 	chase(&snake[0], &snake[1]);
827 	pchar(&snake[1], SNAKETAIL);
828 	pchar(&snake[0], SNAKEHEAD);
829 	for (i = 0; i < 6; i++) {
830 		if ((same(&snake[i], &you)) || (same(&tmp, &you))) {
831 			surround(&you);
832 			i = (cashvalue) % 10;
833 			bonus = ((random() >> 8) & 0377) % 10;
834 			mvprintw(lcnt + 1, 0, "%d\n", bonus);
835 			refresh();
836 			delay(30);
837 			if (bonus == i) {
838 				spacewarp(1);
839 #ifdef LOGGING
840 				logit("bonus");
841 #endif
842 				flushinp();
843 				return(1);
844 			}
845 			flushinp();
846 			endwin();
847 			if (loot >= penalty) {
848 				printf("\nYou and your $%d have been eaten\n", cashvalue);
849 			} else {
850 				printf("\nThe snake ate you.  You owe $%d.\n", -cashvalue);
851 			}
852 #ifdef LOGGING
853 			logit("eaten");
854 #endif
855 			length(moves);
856 			snscore(rawscores, TOPN);
857 			close(rawscores);
858 			exit(0);
859 		}
860 	}
861 	return(0);
862 }
863 
864 int
865 chk(struct point *sp)
866 {
867 	int	j;
868 
869 	if (same(sp, &money)) {
870 		pchar(sp, TREASURE);
871 		return(2);
872 	}
873 	if (same(sp, &finish)) {
874 		pchar(sp, GOAL);
875 		return(3);
876 	}
877 	if (same(sp, &snake[0])) {
878 		pchar(sp, SNAKEHEAD);
879 		return(4);
880 	}
881 	for (j = 1; j < 6; j++) {
882 		if (same(sp, &snake[j])) {
883 			pchar(sp, SNAKETAIL);
884 			return(4);
885 		}
886 	}
887 	if ((sp->col < 4) && (sp->line == 0)) {
888 		winnings(cashvalue);
889 		if ((you.line == 0) && (you.col < 4))
890 			pchar(&you, ME);
891 		return(5);
892 	}
893 	if (same(sp, &you)) {
894 		pchar(sp, ME);
895 		return(1);
896 	}
897 	pchar(sp, ' ');
898 	return(0);
899 }
900 
901 void
902 winnings(int won)
903 {
904 	if (won > 0)
905 		mvprintw(1, 1, "$%d  ", won);
906 }
907 
908 void
909 stop(int dummy)
910 {
911 	wantstop = 1;
912 }
913 
914 void
915 suspend(void)
916 {
917 	endwin();
918 	kill(getpid(), SIGTSTP);
919 	refresh();
920 	winnings(cashvalue);
921 }
922 
923 void
924 length(int num)
925 {
926 	printf("You made %d moves.\n", num);
927 }
928 
929 #ifdef LOGGING
930 void
931 logit(char *msg)
932 {
933 	time_t t;
934 
935 	if (logfile != NULL) {
936 		time(&t);
937 		fprintf(logfile, "%s $%d %dx%d %s %s",
938 		    getlogin(), cashvalue, lcnt, ccnt, msg, ctime(&t));
939 		fflush(logfile);
940 	}
941 }
942 #endif
943