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
40static 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
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/* note t must be < 20 due to restrictions of usleep() */
101#define delay(t)	usleep(t * 50000);
102
103static struct point you;
104static struct point money;
105static struct point finish;
106static struct point snake[6];
107
108static int loot, penalty;
109static int moves;
110static int fast = 1;
111
112static int rawscores;
113static FILE *logfile;
114
115static int lcnt, ccnt;		/* user's idea of screen size */
116static int chunk;		/* amount of money given at a time */
117
118static void chase(struct point *, struct point *);
119static int chk(const struct point *);
120static void drawbox(void);
121static void flushi(void);
122static void length(int);
123static void logit(const char *);
124static void mainloop(void) __dead;
125static struct point *point(struct point *, int, int);
126static int post(int, int);
127static int pushsnake(void);
128static void setup(void);
129static void snap(void);
130static void snrand(struct point *);
131static void spacewarp(int);
132static void stop(int) __dead;
133static int stretch(const struct point *);
134static void surround(struct point *);
135static void suspend(void);
136static void win(const struct point *);
137static void winnings(int);
138
139int
140main(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
250static struct point *
251point(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 */
259static void
260mainloop(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 */
448static void
449setup(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
465static void
466drawbox(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
480static void
481snrand(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
509static int
510post(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 */
583static void
584flushi(void)
585{
586	tcflush(0, TCIFLUSH);
587}
588
589static const int mx[8] = {
590	0, 1, 1, 1, 0, -1, -1, -1
591};
592static const int my[8] = {
593	-1, -1, 0, 1, 1, 1, 0, -1
594};
595static const float absv[8] = {
596	1, 1.4, 1, 1.4, 1, 1.4, 1, 1.4
597};
598static int oldw = 0;
599
600static void
601chase(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
661static void
662spacewarp(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
694static void
695snap(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
741static int
742stretch(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
788static void
789surround(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
831static void
832win(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
863static int
864pushsnake(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
921static int
922chk(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
958static void
959winnings(int won)
960{
961	if (won > 0) {
962		mvprintw(1, 1, "$%d", won);
963	}
964}
965
966static void
967stop(int dummy __unused)
968{
969	signal(SIGINT, SIG_IGN);
970	endwin();
971	length(moves);
972	exit(0);
973}
974
975static void
976suspend(void)
977{
978	endwin();
979	kill(getpid(), SIGTSTP);
980	refresh();
981	winnings(cashvalue);
982}
983
984static void
985length(int num)
986{
987	printf("You made %d moves.\n", num);
988}
989
990static void
991logit(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