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