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