[net.sources] Sys V changes to snake2

hansen@pegasus.UUCP (Tony L. Hansen) (02/05/86)

# The diff file was almost as large as the game itself, considering how small
# the game is, so here is snake2 complete with mods to make "-DSYSV" work. The
# timing is done using what I call "halfdelay" mode: the settable timeouts within
# the tty driver.
# 
# 					Tony Hansen
# 					pegasus!hansen
#!/bin/sh
# This is a shar archive.
# The rest of this file is a shell script which will extract:
# snake2.6 README snake2.c Makefile getkey.c quicksleep.c snake2.h snake2.hlp install.com reinstall.com
# Archive created: Tue Feb 4 16:39:56 EST 1986
echo x - snake2.6
sed 's/^X//' > snake2.6 << '~FUNKY STUFF~'
X.TH SNAKE2 6 "University of Rochester"
X.SH NAME
snake2 \- another snake game
X.SH SYNOPSIS
X.B snake2
X.SH DESCRIPTION
Tired of getting eaten by the snake(6) all the time?
Be a snake!!
Eat other users.
Run snake2.
X.PP
You are the snake.
You look like this: ssssssS
X.br
Change your direction by using the number keypad (i.e. 8 is up,
2 is down), using the keys around the 'k' key (for righties) or
the 'd' key (for lefties).
All 8 directions work.
X.PP
Score by eating other folks.
Point values are based on eatee's size.
After 250 points, all point values are doubled.
If you see a capital letter, it means the box has triple point value.
Go for it!
But beware of the root (of all evil).
Its point value is unknown and sometimes does you more harm than good.
X.PP
Ctrl-L will refresh the screen if necessary (like if somebody writes
to you) but won't slow the snake down a mite.
X.SH BUGS
X.PP
You can't go to the bathroom in the middle of this game.
X.SH AUTHOR
Don Libes (seismo!nbs-amrf!libes)
~FUNKY STUFF~
ls -l snake2.6
echo x - README
sed 's/^X//' > README << '~FUNKY STUFF~'
Copyright (C) 1981 by Don Libes.  This software may be freely copied
and distributed for noncommercial purposes provided that this notice
remains intact.  Commercial use of this software requires my prior
written permission.

These files implement the game of snake2.  snake2 is in no way based
on snake(6) from Berkeley except that there is a snake in the picture.

snake2 is actually based upon a game I saw for a couple minutes one
day at Xerox running on a Data General (c. 1980).  I wrote this
without access to that one, so it's no longer very similar and
certainly doesn't have any source in common.

The source for this game is actually useful as an example of how to
do a bunch of not-well-documented things (curses, reading the
keyboard without blocking, <1sec sleeps, updating a file while
avoiding the lost-update problem, keeping a setuid log file, etc)
while still being short.  Additionally, the task of keeping track
of the snake and boxes, is cute, and in fact, was used by one
professor as an assignment for a lower-level programming course.

This game is known to run under 4.2, Eunice and 4.1 (the latter with
the CMU IPC).  Undoubtedly, it will run under any version of UNIX that
can do the following:

1) Check if the user has hit a key without blocking.  All of 4.2,
4.1 and Eunice implement the FIONREAD ioctl which returns the number
of characters unread in the input buffer.  Additionally, some
systems which claim Berkeley enhancements (eg Unisoft) include this.
If that is the case, you don't have to do anything.  Otherwise, just
plug your solution into getkey.c and recompile.

2) Sleep for less than a second.  4.2 has select().  Eunice has VMS
do it.  The 4.1 I used had the CMU IPC which had something very
similar to select.  Some systems (eg Unisoft) implement select in a
way that is not useful here.  You must use something else if your
select does not work like the Berkeley select.  For other systems,
there are device drivers available that are designed simply for this
purpose.  Just plug your solution into quicksleep.c and recompile.

Notes, warnings, trust me...

The actual timeout used is one quarter of a second.  Since the game
does relatively little processing each move (every .25 seconds), it
places virtually no load on the system.

Included are a man page and (for VMS systems) a help file.  Ignore
whichever one is inappropriate for you.

The game should be installed setuid with the owner set to "snake" or
"daemon" or something similarly innocuous, to prevent tampering with
the score file.  Since Eunice does not implement setuid(), Eunice
sites must install it with sysprv.  Frightening, isn't it?

If you have to modify the game to run under your system, please send
me back the modifications.  Thanks.

Don Libes
National Bureau of Standards
Met. Bldg, Rm B229
Gaithersburg, MD  20899
(301) 921-2171
{seismo,umcp-cs}!nbs-amrf!libes
~FUNKY STUFF~
ls -l README
echo x - snake2.c
sed 's/^X//' > snake2.c << '~FUNKY STUFF~'
/* snake2
 *
 * Copyright (C) 1981 by Don Libes.  This software may be freely copied
 * and distributed for noncommercial purposes provided that this notice
 * remains intact.  Commercial use of this software requires my prior
 * written permission.
 *
 * HISTORY
 * 04-Feb-86	Tony Hansen at pegasus!hansen
 *	Modified to support System V
 *
 * 20-Sep-85  libes at National Bureau of Standards
 *      Modified to support BSD 4.2 and Eunice (BSD 4.1)
 *	Modified to run on any size screen.
 *
 * 07-Aug-81  don at University of Rochester
 *	Fixed lack of rollover bug.  
 *	ioctl(FIONREAD,...) gives # of NEW unread chars added to input
 *	buffer since last call, not just # of unread chars in buffer.
 *
 * 18-Jul-81  don at University of Rochester
 *	Ring structure for snake body was being corrupted.  It seems C's
 *	idea on how to mod negative numbers is backwards.  Hard to tell
 *	if this is a bug, because reference manual is fuzzy on this.
 *
 * 16-Jul-81  don at University of Rochester
 *	Created after playing similar game on DG at Xerox.
 *	Different scoring.  Diagonal moves allowed.  Boxes are people
 *	rather than just points.  Various other hacks.  See man file.
 *	cc snake2.c -lcurses -ltermcap -lipc
 */

#include <sys/types.h>
#include <signal.h>
#include <curses.h>
#include <ctype.h>
#include <pwd.h>

#include "snake2.h"

#define MAXUSERS	255		/* A large number */
#define MAXLEN		((LINES-2)*(COLS-2))	/* Maximum length of snake */
#define MAXBOXES	6
#define SKILL		(12-6)		/* How often we grow */
#ifdef SYSV
#define bell()		beep()
#else
#define bell()		putchar('\07')	/* Ring my chimes */
#endif

time_t time();				/* For random factor */

int *snakex, *snakey;			/* position of snake */
int sptr;				/* ptr to snake in its array */
int length;				/* length of snake */
int turn = 0;				/* number of turns we've had */
int points = 0;				/* Points by eating */
int diry = 1;				/* direction we are moving */
int dirx = 1;				/* initially diagonally and down */

/* Data structures for boxes */
struct {
	int y;
	int x;
	int inuse;
	char *name;
	int length;
	int points;			/* point value of box */
} boxes[MAXBOXES];

char names[MAXUSERS][USERNAME_LENGTH+1];
int numnames = 0;			/* number of names in /etc/passwd */

/* Data structures for log */
struct logtype {
	char name[USERNAME_LENGTH+1];
	int length;
	int points;
	int rows, columns;
} log[MAXUSERS];
long our_offset;			/* position of our name in log file */

/* scoring algorithm */
#define score(length,points,rows,columns)	((points)+(length))

/* parameters for lseek */
#define L_SET	0
#define L_INCR	1
#define L_XTND	2

char *getlogin(), *whoami;
char *strncpy(), *malloc();
int sortlog();
FILE *fopen();
FILE *lf;
int reluid;				/* uid relative to log file */
int usersknown;				/* number of users in log */
int userknown = FALSE;				/* if user in log file */
int maxlen;

WINDOW *tmpscr;				/* save screen for clear */
WINDOW *newwin();

main()
{
	init();
	while (TRUE) {
		if (!snake()) break;	/* get user input */
#ifndef SYSV
		quick_sleep(TIMEOUT);
#endif
		ranbox();		/* randomly box */
		ranbox2();		/* randomly unbox */
	}
	quit();
}

intr()
{
	cleanup();
	exit(0);
}

init()
{
	int i = 0;
	extern struct passwd *getpwent(), *getpwuid();
	struct passwd *pwent;
	long last_offset;	/* keep track of offsets in log file */

#ifdef EUNICE
	/* if invoked from raw VMS, getlogin() screws up by returning */
	/* an empty string rather than a null string */
	if (!((whoami = getlogin()) && whoami[0])) {
#else
	if (!(whoami = getlogin())) {
#endif
		/* can't just copy ptr from getpwuid since this is */
		/* static data which will be reused when we read in */
		/* /etc/passwd! */
		whoami = malloc(USERNAME_LENGTH+1);
		strncpy(whoami,getpwuid(getuid())->pw_name, USERNAME_LENGTH);
	}
	setuid(geteuid());

	initscr();
	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		signal(SIGINT, intr);
	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
		signal(SIGQUIT, intr);
	tmpscr = newwin(LINES,COLS,0,0);
	box(stdscr,'|','-');
	noecho();			/* Don't let directions echo! */
#ifdef SYSV
	cbreak();
	makehalfdelay();
#else
	raw();
#endif
	refresh();

	maxlen = MAXLEN;
	snakex = (int *)malloc(maxlen*sizeof(int));
	snakey = (int *)malloc(maxlen*sizeof(int));
	snakex[0] = snakey[0] = 2;
	move(2,2);
	addch('s');
	snakey[1] = 3; snakex[1] = 3;
	move(3,3);
	addch('s');
	length = 2;
	sptr = 1;

	srand((int) time((time_t *)0));
	/* read in names */
	while ((pwent = getpwent()) && numnames < MAXUSERS) {
		strncpy(names[numnames++],pwent->pw_name, USERNAME_LENGTH);
	}
	if (!(lf = fopen(LOG,"r+"))) {
		cleanup();
		printf("can't read/update score file (%s)\n",LOG);
		exit(0);
	}
	last_offset = our_offset = 0L;
	while (5 == fscanf(lf,"%s%d%d%d%d",log[i].name,&log[i].length,
					&log[i].points,
					&log[i].rows,
					&log[i].columns)) {
		if (!userknown) {
			if (!strcmp(whoami,log[i].name)) {
				our_offset = last_offset;
				reluid = i;
				userknown = TRUE;
				/* Give'm something to shoot for */
				move(LINES-1,50);
				printw("Personal high score: %d",
					score(log[i].length,log[i].points,
						log[i].rows,log[i].columns));
			} else last_offset = ftell(lf);
		}
		i++;
	}

	if (!userknown) {	/* New entry for this user */
		our_offset = last_offset;
		strcpy(log[i].name,whoami);
		log[i].length = 0;
		log[i].points = 0;
		log[i].rows = LINES;
		log[i].columns = COLS;
		reluid = i;
		userknown = TRUE;

		/* make new entry in log file */
		fseek(lf,0L,L_XTND);	/* EOF */
		fprintf(lf,"%*s%8d%8d%8d%8d\n",USERNAME_LENGTH,
					log[i].name,
					log[i].length,
					log[i].points,
					log[i].rows,
					log[i].columns);
		i++;
	}
	fclose(lf);
	usersknown = i;
}

quit()
{
	int i = 0;

	if (score(length,points,LINES,COLS) >
		score(log[reluid].length,
			log[reluid].points,
			log[reluid].rows,
			log[reluid].columns)) { /* new personal high score! */

		log[reluid].length = length;
		log[reluid].points = points;
		log[reluid].rows = LINES;
		log[reluid].columns = COLS;

		if (!(lf = fopen(LOG,"r+"))) {
			cleanup();
			printf("can't update score file (%s)\n",LOG);
			exit(0);
		}
		fseek(lf,our_offset+1,L_SET);
		fprintf(lf,"%*s%8d%8d%8d%8d\n",USERNAME_LENGTH,
			whoami,length,points,LINES,COLS);
		fclose(lf);
	}
	/* the 36 here is just the width of the stripe we are painting */
	/* i.e. it equals the width of the printw */
	move(1,(COLS-36)/2);
	printw("%*s%8s%8s%8s",USERNAME_LENGTH,
		"Who","Length","Points","Score");
	/* print out top LINES-4 scorers (right on top of game) */
	qsort(log,usersknown,sizeof(struct logtype),sortlog);
	for (i=0;i<(LINES-4) && i<usersknown;i++) {
		move(2+i,(COLS-36)/2);
		printw("%*s%8d%8d%8d",USERNAME_LENGTH,
			log[i].name,log[i].length,log[i].points,
			score(log[i].points,log[i].length,
				log[i].rows,log[i].columns));
	}
	cleanup();
}
	
snake()
{
	int key;			/* last key struck */

	turn++;

	if (-1 != (key = getkey())) {
		switch (key) {
		case 'i': case '8': case 'e':
			if (diry == 1 && dirx == 0) bell();
			else {
				diry = -1;
				dirx = 0;
			}
			break;
		case 'l': case '6': case 'f':
			if (diry == 0 && dirx == -1) bell();
			else {
				diry = 0;
				dirx = 1;
			}
			break;
		case ',': case '2': case 'c':
			if (diry == -1 && dirx == 0) bell();
			else {
				diry = 1;
				dirx = 0;
			}
			break;
		case 'j': case '4': case 's':
			if (diry == 0 && dirx == 1) bell();
			else {
				diry = 0;
				dirx = -1;
			}
			break;
		case '9': case 'o': case 'r':
			if (diry == 1 && dirx == -1) bell();
			else {
				diry = -1;
				dirx = 1;
			}
			break;
		case '3': case '.': case 'v':
			if (diry == -1 && dirx == -1) bell();
			else {
				diry = 1;
				dirx = 1;
			}
			break;
		case '1': case 'm': case 'x':
			if (diry == -1 && dirx == 1) bell(); 
			else {
				diry = 1;
				dirx = -1;
			}
			break;
		case '7': case 'u': case 'w':
			if (diry == 1 && dirx == 1) bell();
			else {
				diry = -1;
				dirx = -1;
			}
			break;
		case 003:	/* Ctrl-C */
		case 004:	/* Ctrl-D */
		case 0177:	/* Rubout */
		case 034:	/* FS	  */
		case 'q': case 'Q':
			return(0);
			break;
		case 014:	/* Ctrl-L */
			/* Really refresh screen */
			wclear(tmpscr);
			overlay(stdscr,tmpscr);
			clear();
			refresh();
			overlay(tmpscr,stdscr);
			break;
		}
	}
	return movesnake(diry,dirx);
}

movesnake(y,x)    /* check if snake hits money or edge of window, too */
{
	int c;
	int newsptr;	/* temp snake pointer */
	newsptr = (1+sptr)%maxlen;

	/* keep snake at right length */
	if (turn%SKILL) {
		move(snakey[(maxlen+1+sptr-length)%maxlen],
		     snakex[(maxlen+1+sptr-length)%maxlen]);
		addch(' ');
	} else {
		length++;
		move(0,15);
		printw("Length = %d",length);
		move(0,33);
		printw("Points = %d",points);
		move(0,51);
		printw("Score = %d",score(length,points,LINES,COLS));
	}

	/* move head of snake one step further */
	snakey[newsptr] = snakey[sptr]+y;
	snakex[newsptr] = snakex[sptr]+x;

	/* check for a hit! */
	move(snakey[newsptr],snakex[newsptr]);
	if (snakey[newsptr] == 0 || snakey[newsptr] == LINES-1 ||
		snakex[newsptr] == 0 || snakex[newsptr] == COLS-1) {
		move(LINES-2,2);
		addstr("You hit the edge, you snake!");
		return(0);
	}
	c = inch();
	if (c != ' ') { /* musta hit sumptin, duh... */
		int i;
		for (i=0;i<MAXBOXES;i++) {
			if (!boxes[i].inuse) continue;
			if (boxes[i].x <= snakex[newsptr] &&
			    boxes[i].x + boxes[i].length > snakex[newsptr] &&
			    boxes[i].y <= snakey[newsptr] &&
			    boxes[i].y + boxes[i].length > snakey[newsptr]) {
				/* hit box i */
				delbox(i);
				/* double points after 250 */
				if (points+length>=250)
					points += 2*boxes[i].points;
				else points += boxes[i].points;
				move(0,33);
				printw("Points = %d",points);
				move(0,51);
				printw("Score = %d",points+length);
				break;
			}
		}
		if (i == MAXBOXES) {
			move(LINES-2,2);
			addstr("You hit yourself, you snake!");
			return(0);
		}
	}

	move(snakey[sptr],snakex[sptr]);
	addch('s');
	move(snakey[newsptr],snakex[newsptr]);
	addch('S');

	sptr = newsptr;
	refresh();
	return(TRUE);
}

ranbox() /* randomly draw a box */
{
	int i, j, y, x, y2, x2, s;
	int length;

	i = rand()%MAXBOXES;
	if (boxes[i].inuse) return;
	j = rand()%numnames;
	length = strlen(names[j]);
	y = 1 + rand()%((LINES-1)-length);
	x = 1 + rand()%((COLS-1)-length);
	/* check if area is clear */
	for(y2=y;y2<y+length;y2++) {
		for (x2=x;x2<x+length;x2++) {
			move(y2,x2);
			if (' ' != inch()) return;
		}
	}
	boxes[i].inuse = TRUE;
	boxes[i].x = x;
	boxes[i].y = y;
	boxes[i].name = names[j];
	boxes[i].length = length;
	if (!(s = rand()%9)) boxes[i].points = length*3;
		else boxes[i].points = length;
	if (names[j] == names[0] ) { /* root! */
		boxes[i].points = (rand()%40) - 20;
	}
	/* draw box */
	/* top */
	move(y,x);
	addstr(names[j]);
	/* show triple value by capitalizing first letter */
	if ((!s) && (islower(names[j][0]))) {
		move(y,x);
		addch(names[j][0]-('a'-'A'));
	}
	for (y2=y+1;y2<y+length;y2++) {
		/* left side */
		move(y2,x);
		addch(names[j][y2-y]);
		/* right side */
		move(y2,x+length-1);
		addch(names[j][length-(y2-y)-1]);
	}
	/* bottom */
	for (x2=x+1;x2<x+length-1;x2++) {
		move(y+length-1,x2);
		addch(names[j][length-(x2-x)-1]);
	}
}

ranbox2() /* randomly delete a box */
{
	/* about every 10 times try and delete a randomly selected box */
	if (!(rand()%10)) {
		delbox(rand()%MAXBOXES);
	}
}

delbox(i) /* delete a box */
int i;
{
	int x2, y2, x, y;
	int length;

	if (!boxes[i].inuse) return;
	x = boxes[i].x;
	y = boxes[i].y;
	length = boxes[i].length;
	boxes[i].inuse = FALSE;
	/* delete box (this should probably be written as one loop) */
	move(y,x);
	/* delete top of box */
	for (x2=x;x2<x+length;x2++) {
		/* to */
		move(y,x2);
		addch(' ');
	}
	/* sides */
	for (y2=y+1;y2<y+length;y2++) {
		move(y2,x);
		addch(' ');
		move(y2,x+length-1);
		addch(' ');
	}
	/* bottom */
	for (x2=x+1;x2<x+length-1;x2++) {
		move(y+length-1,x2);
		addch(' ');
	}
}

sortlog(foo,bar)	/* sort function for log */
struct logtype *foo;
struct logtype *bar;
{
	int x, y;

	x = score(foo->length,foo->points,foo->rows,foo->columns);
	y = score(bar->length,bar->points,bar->rows,bar->columns);
	if (x == y) return(0);
	else if (x > y) return(-1);
	else return(1);
}

cleanup()
{
	/* move UNIX prompt to bottom line (from where?) */
	move(LINES-1,0);
	echo();
	noraw();
	refresh();
	endwin();
}
~FUNKY STUFF~
ls -l snake2.c
echo x - Makefile
sed 's/^X//' > Makefile << '~FUNKY STUFF~'
LOG = /usr/games/lib/snake2.scores

# choose system type from one of -DEUNICE, -DBSD42, -DBSD41, -DSYSV
UNIX = -DSYSV
#UNIX = -DBSD42

# choose timeout mechanism from one of
# -DBSD_SELECT, -DCMU_IPC, -DVMS or nothing for SYSV
TIMER = 
#TIMER = -DBSD_SELECT

CFLAGS = -O $(UNIX) $(TIMER) -DLOG=\"$(LOG)\"

LIBS = -lcurses
#LIBS = -lcurses -ltermcap
#LIBS = -lcurses -ltermlib

snake2: snake2.o quicksleep.o getkey.o
	cc -s -o snake2 snake2.o quicksleep.o getkey.o $(LIBS)

install:
	strip snake2
	cat /dev/null >> $(LOG)
	chmod 644 $(LOG)
	/etc/chown daemon snake2 $(LOG)
	chmod 4755 snake2
	mv snake2 /usr/local/bin

snake2.o: snake2.h
getkey.o: snake2.h
~FUNKY STUFF~
ls -l Makefile
echo x - getkey.c
sed 's/^X//' > getkey.c << '~FUNKY STUFF~'
/* getkey.c - Don Libes
getkey() - return the last key struck by the user or -1.
If user has struct multiple keys, ignore all but the last.
*/

#include "snake2.h"

#include <stdio.h>
#if BSD42 || BSD41 || EUNICE
#include <sys/ioctl.h>
#endif
#if SYSV
#include <sys/termio.h>
#endif

#ifdef FIONREAD
int
getkey()
{
	long chars = 0;

	if (-1 == ioctl(fileno(stdin),FIONREAD,&chars)) {
		cleanup();
		perror("ioctl in getkey");
		exit(0);
	}
	
	if (chars == 0) return(-1);

	while (chars-- > 1) getchar();

	return(getchar() & 0x7f);		/* strip parity */
}
#else
makehalfdelay()
{
	struct termio curtty;
	ioctl(1, TCGETA, &curtty);
	curtty.c_cc[VMIN] = 0;
	curtty.c_cc[VTIME] = TIMEOUT;
	ioctl(1, TCSETAW, &curtty);
}

int
getkey()
{
	return getchar();
}
#endif FIONREAD
~FUNKY STUFF~
ls -l getkey.c
echo x - quicksleep.c
sed 's/^X//' > quicksleep.c << '~FUNKY STUFF~'
/* quicksleep.c - Don Libes
quick_sleep() - sleep for less than a second
*/

#ifdef CMU_IPC
quick_sleep(timeout)
int timeout;		/* milliseconds */
{
	int sop[8];
	sop[0] = 1;

	ipcmessagewait(sop,timeout);	/* huh, message?  what message? */
#endif

#ifdef UNISOFT_SELECT
/* My documentation says that this doesn't work yet, but someday it might */
quick_sleep(timeout)
int timeout;		/* milliseconds */
{
	(void) select(20, 0, 0, timeout);
}
#endif

#ifdef BSD_SELECT
#include <sys/time.h>
quick_sleep(timeout)
int timeout;		/* microseconds */
{
	struct timeval t;

	t.tv_sec = timeout / 1000000;
	t.tv_usec = timeout % 1000000;
	(void) select(32, 0, 0, 0, &t);
}
#endif

#ifdef VMS
quick_sleep(timeout)
char *timeout;
{
	int rc;
	char *vms_timeout[8];

	struct {
		int length;
		char *string;
	} timeout_desc;
	timeout_desc.string = timeout;
	timeout_desc.length = strlen(timeout);

	if (1 != (rc = sys$bintim(&timeout_desc,vms_timeout))) {
		printf("$bintim() = %x\n",rc);
		cleanup();
		exit(0);
	}
 	if (1 != (rc = sys$schdwk(0,0,vms_timeout,0))) {
		printf("$schdwk() = %x\n",rc);
		cleanup();
		exit(0);
	}
	sys$hiber(0);
}
#endif
~FUNKY STUFF~
ls -l quicksleep.c
echo x - snake2.h
sed 's/^X//' > snake2.h << '~FUNKY STUFF~'
/* TIMEOUT should be one quarter of a second or 250 milliseconds */
#ifdef VMS
#define TIMEOUT "000 00:00:00.25"
#endif
#ifdef BSD_SELECT
#define TIMEOUT 250000		/* microseconds */
#endif
#ifdef UNISOFT_SELECT
#define TIMEOUT 250		/* milliseconds */
#endif
#ifdef CMU_IPC
#define TIMEOUT 250		/* milliseconds */
#endif
#ifdef SYSV
#define TIMEOUT 3		/* 10's of seconds */
#endif

#ifdef EUNICE
#define USERNAME_LENGTH	12
#else
#define USERNAME_LENGTH 8
#endif
~FUNKY STUFF~
ls -l snake2.h
echo x - snake2.hlp
sed 's/^X//' > snake2.hlp << '~FUNKY STUFF~'
1 SNAKE2
 Be a snake!!  Eat other users.  To run, type: snake2

 You are the snake.  You look like this: ssssssS

 Change your direction by using the number keypad (i.e.  8 is up, 2 is
 down), using the keys around the 'k' key (for righties) or the 'd' key
 (for lefties).  All 8 directions work.

 Score by eating other folks.  Point values are based on eatee's size.
 After 250 points, all point values are doubled.  If you see a capital
 letter, it means the box has triple point value.  Go for it!  But
 beware the root (of all evil).  Its point value is unknown and
 sometimes does you more harm than good.

 Ctrl-L will refresh the screen if necessary (like if somebody writes to
 you) but won't slow the snake down a mite.
2 author
 Don Libes   (seismo!nbs-amrf!libes)
2 bugs
 You can't go to the bathroom in the middle of this game.
2 keyboard
 Certain programs (such as EDT) that use the keypad, sometimes leave them
 generating different character sequences than normal (i.e. the '6' key
 generates a <esc>[z rather than a '6').  If you really want to use the
 keypad and the game is ignoring you, try resetting the terminal.
2 terminal
 If you run snake2 directly from VMS, it will assume you are on a vt100
 compatible terminal.  If you are not on a vt100 lookalike, get into
 Eunice first and set your terminal type.

 If you are on a vt100 and the initial box is funny looking (i.e.  the
 left border is missing), the terminal is in "no auto wrap" mode, which
 means that when the cursor gets to column 80, its sits there dropping
 characters rather than doing a crlf and continuing.  Turn off this
 braindamaged feature, by putting your terminal into "auto wrap" mode.
~FUNKY STUFF~
ls -l snake2.hlp
echo x - install.com
sed 's/^X//' > install.com << '~FUNKY STUFF~'
$! install snake2 with sysprv
$! sysprv is necessary to update the score file
$!
$ pushp sysprv,cmkrnl,cmexe
$ mc install
eun_usr:[usr.local.bin]snake2./shared/header/priv=sysprv
$ popp
~FUNKY STUFF~
ls -l install.com
echo x - reinstall.com
sed 's/^X//' > reinstall.com << '~FUNKY STUFF~'
$ ! install new version of snake2 - Don Libes
$
$! make it /notraceback so it can be installed
$ trpatch :== $eun_usr:[usr.eun]trpatch trpatch
$ trpatch snake2.
$!
$ pushp sysprv,cmkrnl,cmexe
$ copy snake2. eun_usr:[usr.local.bin]
$ mc install
eun_usr:[usr.local.bin]snake2./replace
$ popp
~FUNKY STUFF~
ls -l reinstall.com
# The following exit is to ensure that extra garbage 
# after the end of the shar file will be ignored.
exit 0