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