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