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