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