[net.sources.games] Pm - UNIX version of PacMan

ccohesh001@ucdavis.UUCP (0058) (11/06/85)

SYNOPSIS
	pm - computer version of PacMan

INTENDED ENVIRONMENT
	BSD 4.2 VAX 11/750

TESTED ENVIRONMENTS
	BSD 4.2 VAX 11/750
	BSD 4.1 VAX 11/750
	BSD 2.9 PDP 11/70
	BSD 2.8 PDP 11/70

DESCRIPTION
	Pm is a UNIX version of the popular arcade game PacMan.  This
	differs drastically from the version distributed with BSD 4.1.

NOTES
	This implementation uses the Curses library.  It is currently
	being distributed binary only.  A source distribution will
	follow after sufficient testing proves there to be few or no
	bugs present.

	Please send all bug reports to:
		
		{dual,lll-crg,ucbvax}!ucdavis!deneb!ccohesh001

	PacMan is being distributed due to the encouragement and enthusiasm
	of those who have already played it.

WHEREAMI
	Pm is available via anonymous ftp from lll-crg.ARPA.  The binary
	and other related support files are stored under a tar archive
	named "pm.tar".

AUTHOR
	Peter Costantinidis, Jr.
-- 
-- Peter Costantinidis, Jr.
-- ucdavis!ccohesh001@ucb-vax.arpa		(ARPA)
-- ...!{ucbvax,lll-crg,dual}!ucdavis!ccohesh001	(UUCP)

ccohesh001@ucdavis.UUCP (Peter Costantinidis, Jr.) (12/14/85)

SYNOPSIS
	pm - UNIX version of PacMan

INTENDED ENVIRONMENT
	BSD 4.[12] VAX 11/750
	BSD 2.[89] PDP 11/70

TESTED ENVIRONMENTS
	BSD 4.[12] VAX 11/750
	BSD 2.[89] PDP 11/70

DESCRIPTION
	Pm is a UNIX version of the popular arcade game PacMan.  This
	differs drastically from the version distributed with BSD 4.1.
	PacMan is being distributed due to the encouragement and enthusiasm
	of those who have already played it.

BUGS
	Please send all bug reports to:
		
		{dual,lll-crg,ucbvax}!ucdavis!deneb!ccohesh001

WHEREAMI
	net.sources.games: 2 part posting

AUTHOR
	Peter Costantinidis, Jr., University of California at Davis
-- 
-- Peter Costantinidis, Jr.
-- ucdavis!deneb!ccohesh001@ucb-vax.arpa		(ARPA)
-- ...!{ucbvax,lll-crg,dual}!ucdavis!deneb!ccohesh001	(UUCP)

ccohesh001@ucdavis.UUCP (Peter Costantinidis, Jr.) (12/14/85)

#!/bin/sh
: "This is a shell archive, meaning:                              "
: "1. Remove everything above the #! /bin/sh line.                "
: "2. Save the resulting test in a file.                          "
: "3. Execute the file with /bin/sh (not csh) to create the files:"
: "	READ_ME"
: "	Makefile"
: "	Makefile.b"
: "	TODO"
: "	pm.h"
: "	pm.6"
: "	shar"
: "	pm.c"
: "	random.c"
: "	rip.c"
: "	score.c"
: "	screen.c"
: "	timing.c"
: "	warning.c"
: "This archive created:  Fri Dec 13 13:04:40 PST 1985 "
echo file: READ_ME
sed 's/^X//' >READ_ME << 'END-of-READ_ME'
XThis is the first source distribution of the game "pm".  This game should
Xrun under Version 7, BSD4.2, BSD4.3, BSD2.8, BSD2.9.  A BSD4.3 version has
Xnot been tested, but binary distribution bug reports have indicated some
Xproblems in "flsbuf.c" that I think are now corrected.
X
XThis version still does busy looping.  An implementation of msleep() was
Xtried that used setitimer() for sleeping, but this did not work satisfactorly.
XIf someone else is able to make a better version of msleep(), I would
Xappreciate it if they could send me the source.  However, thus far I have
Xfound the current msleep() to be the most portable between different
Xoperating system versions (it is the original UNIX Version 7 version).
XNote the define "MYTIMER" turns on the busy looping and off the setitimer(2).
X
X
XWed Oct 30 00:23:20 PST 1985
X
XThis is the first binary distribution of the game "pm", a UNIX version
Xof the arcade game Pacman.  A source distribution will be made within
Xthe next couple of months via netnews.  Please send all bug reports to
Xme (...!ucbvax!ucdavis!deneb!ccohesh001) so that they can be incorporated
Xinto the first source distribution.
X
XThis binary distribution will only run under UNIX 4.2BSD.  A binary for
XUNIX 2.9BSD can be provided upon request.
X
XSince I am the main source of distribution, please mail all suggestions
Xor comments to me.
X
X			Peter Costantinidis, Jr.
X			...!ucbvax!ucdavis!deneb!ccohesh001
X
X
XThe following may not make much sense unless you have a source distribution:
X
XMon Aug 30 11:18:09 PDT 1982
X
XLook an pending(), it's sole purpose is to tell if the user has
Xtyped anything, and if so, then read it, if not, then go on.  If
Xyou don't have a similar function, then you are screwed!!!
X
XThe purpose of delay() is to slow the CPU down so that the terminal can
Xkeep up.  I load in my own copy of flsbuf (stdio) that counts the number
Xof characters that are printed.  I make sure that the CPU waits long
Xenough to make sure that they all got printed (a simple function of the
Xbaud rate will tell you how long you need to wait).
X
XIf you want a list of commands, type in a '?'.  If you want
Xthe Wizard's commands, look in the define file.  If you want the
XWizard's password, decrypt the one in the define file or make your own.
XIf (argv[0] == "tester") then you are wizard automatically (this
Xshould probably be kept secret).
X
XPm "remembers" the uids of everyone who has played.  These are maintained
Xin the pm_user file by setting the bit corresponding to the uid.  If a
Xplayer has not played before, they are given an opening message (printed
Xout by the inappropriately named function directions()), and will then
Xnever see it again.
X
XIf you don't have curses, then give up!
X
XOne problem encountered during the development of this program was curses.
XThe one I was using had problems and you may notice code that might not be
Xuseful any longer (assuming your version of curses doesn't have these bugs).
X
XThe game looks best if the `maze' ('#' characters) are in inverse video.
XIf your termcap defines standout to be high intensity, the board would
Xprobably look best if you tweeked the code and made the maze normal (i.e.
Xnot in standout) and everything else in high intensity (standout).
X
XI would like to thank Rick Heli, a member of UCD's Computer Center staff
Xfor his help in the documentation of this program.
END-of-READ_ME
echo file: Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
X#
X# Makefile:	makefile for pm
X#
X#	Tunables:
X#		PM_ROLL -	pathname of score file
X#		PM_USER -	pathname of user log file
X#		OS_VERS -	operating system version
X#				(BSD29, BSD41, BSD42, BSD43)
X#		SEND	-	pathname of binary export directory
X#
X#	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X#
X
X#
X# Files
X#
XHDRS	=	pm.h
XOBJS	=	config.o flsbuf.o init.o make_moves.o\
X		misc.o monsters.o msg.o pm.o random.o\
X		rip.o score.o screen.o timing.o warning.o
XSRCS1	=	config.c flsbuf.c init.c make_moves.c\
X		misc.c monsters.c msg.c
XSRCS2	=	pm.c random.c rip.c score.c screen.c timing.c warning.c
XSRCS	=	$(SRCS1) $(SRCS2)
X#
X# Configuration section
X#
XPM_ROLL	=	/usfa/g01030/ccohesh001/pm_roll
XPM_USER	=	/usfa/g01030/ccohesh001/pm_user
XOS_VERS =	BSD42
XDEFS	=	-DPM_ROLL=\"$(PM_ROLL)\" -DPM_USER=\"$(PM_USER)\" \
X		-D$(OS_VERS) -DWIZARD_UID=16649
XDEST	=	/usfa/g01030/ccohesh001/xhesh
X#
X# Flags section
X#
XCFLAGS	=	-O $(DEFS)
XLDFLAGS	=	-o tester
XLINTARGS=	-ahxc $(DEFS) -DLINT
XCRLIB	=	-lcurses
XTERMLIB	=	-ltermlib
X#
X# Misc.
X#
XPMLIB	=	pm.a
XPMDOC	=	pm.6
XBEXPORT	=	READ_ME Makefile.b TODO $(PMLIB) $(PMDOC) config.c
XALL	=	READ_ME Makefile Makefile.b TODO $(HDRS) $(SRCS) $(PMDOC) shar
XREST	=	READ_ME Makefile Makefile.b TODO $(HDRS) $(PMDOC) shar
XEX1	=	$(REST) $(SRCS2)
XEX2	=	$(SRCS1)
X
Xtester:		$(PMLIB)
X		-mv tester tester.old
X		cc $(LDFLAGS) $(PMLIB) $(CRLIB) $(TERMLIB)
X		chmod 04711 tester
X
Xinstall:	pm pm_roll pm_user clean
X
X$(PMLIB):	$(OBJS)
X		ar rv $@ $?
X		ranlib $@
Xclean:
X		rm $(OBJS) *.old
X
Xpm:		tester
X		-mv pm pm.old; mv tester pm
X#		@strip pm
X		>tester
X
X$(OBJS):	$(HDRS)
X
Xtags:		$(HDRS) $(SRCS)
X		ctags -u $?
X		sort tags -o tags
X
Xpm_roll:
X		> $(PM_ROLL)
X
Xpm_user:
X		> $(PM_USER)
X
XPrint:		$(HDRS) $(SRCS)
X		pr -l60 -w132 $? | lpr -d "-tpm"
X		-touch Print
X
XP_all:
X		pr -l60 -w132 Tags $(HDRS) $(SRCS) | lpr -mc -d "-tpm"
X		-touch Print
X
Xlint:
X		lint $(LINTARGS) $(SRCS) -lcurses
X
Xcount:
X		wc $(HDRS) $(SRCS)
X
Xbexport:	$(PMLIB)
X		cp $(BEXPORT) $(DEST)
X
Xexport:
X		/bin/sh shar $(EX1) > pm1.shar
X		/bin/sh shar $(EX2) > pm2.shar
X
Xcrypt:		pm.tar
X		crypt < pm.tar > pm.tar.c
X
XSave:		$(HDRS) $(SRCS)
X		cp $? Makefile xsave
X
XCxref:		$(SRCS) $(HDRS)
X		cxref $(SRCS) $(HDRS) > $@
END-of-Makefile
echo file: Makefile.b
sed 's/^X//' >Makefile.b << 'END-of-Makefile.b'
X#
X# Makefile.b -	makefile for binary distribution
X#
X#	The following macros must be redefined for your installation.
X#
X#	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X#
XGROUP	=	sorcerer
XOWNER	=	wizard
XBINDEST	=	/usr/games
XLIBDEST	=	/usr/games/lib
XWIZARD_UID=	666
X#
X#	The following need not be changed.
X#
X
XOBJS	=	config.o
XSRCS	=	config.c
XOS_VERS =	BSD42
XPM_ROLL	=	$(LIBDEST)/pm_roll
XPM_USER	=	$(LIBDEST)/pm_user
XDEFS	=	-DPM_ROLL=\"$(PM_ROLL)\" -DPM_USER=\"$(PM_USER)\" \
X		-D$(OS_VERS) -DWIZARD_UID=$(WIZARD_UID)
XCFLAGS	=	-O $(DEFS)
XLDFLAGS	=	-o pm
XCRLIB	=	-lcurses
XTERMLIB	=	-ltermlib
XPMLIB	=	pm.a
X
Xall:		pm
X
Xinstall:	all pm_roll pm_user
X		cp pm $(BINDEST)
X		chgrp $(GROUP) $(BINDEST)/pm
X		chgrp $(GROUP) $(PM_ROLL) $(PM_USER)
X		chown $(OWNER) $(BINDEST)/pm
X		chown $(OWNER) $(PM_ROLL) $(PM_USER)
X		chmod 4711 $(BINDEST)/pm
X
Xpm:		$(OBJS)
X		ranlib $(PMLIB)
X		cc $(LDFLAGS) $(OBJS) $(PMLIB) $(CRLIB) $(TERMLIB)
X
X$(OBJS):	Makefile.b
X
XMakefile.b:
X
Xpm_roll:
X		> $(PM_ROLL)
Xpm_user:
X		> $(PM_USER)
X
Xclean:
X		rm -f pm config.o
X
Xremove:		clean
X		rm -f $(PM_ROLL) $(PM_USER)
END-of-Makefile.b
echo file: TODO
sed 's/^X//' >TODO << 'END-of-TODO'
X- Make the fruit appear and go away
X- Fix the bug that sometimes causes wierd characters floating around
X  (something to do with the tunnel, the monster gets its mo_name mixed up)
X  (Note: haven't seen this one in quite a while, it might be fixed.)
X- Possibly combine pm_roll and pm_user into one file
X- Test on BSD4.3.
END-of-TODO
echo file: pm.h
sed 's/^X//' >pm.h << 'END-of-pm.h'
X/*
X** pm.h -	all pm source files include this
X**
X**	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X*/
X
X/*
X** defines that control things
X*/
X#define	PATTERNS		/* let there be patterns!!!		*/
X#define	FASTER			/* let the monsters move fast (monsters.c) */
X#define	MAX_UPDATE		/* more refreshes			*/
X#ifdef	BSD42|BSD43|BSD29
X#	define	BAD_OVERLAY	/* bug in curses (overlay.c)		*/
X#endif
X#define	MYTIMER			/* quite reliable			*/
X
X#define	BYTE_SIZE	8	/* # of bits in a byte			*/
X
X
X#include <curses.h>
X#include <ctype.h>
X#include <sys/types.h>
X#include <sys/timeb.h>
X#ifdef	BSD42|BSD43
X#	include <sys/time.h>
X#else
X#	include <time.h>
X#endif
X#ifdef	MYTIMER
X#	undef	ITIMER_REAL
X#endif
X
X/*
X** bound defines
X*/
X#define	TOP		1
X#define	LEFT		0
X#define	RIGHT		52
X
X/*
X** function status returns
X*/
X#define	ERROR		-1
X#define	STOP		-2
X#define	QUIT		-3
X#define	FINE		-4
X#define	BAD		-5
X
X/*
X** input defines
X*/
X#define	MUP		'k'		/* move up			*/
X#define	MDOWN		'j'		/* move down			*/
X#define	MLEFT		'h'		/* move left			*/
X#define	MRIGHT		'l'		/* move right			*/
X#define	MSTOP		' '		/* stop movement		*/
X#define	MKP_UP		'8'		/* key pad move up		*/
X#define	MKP_DOWN	'2'		/* key pad move down		*/
X#define	MKP_LEFT	'4'		/* key pad move left		*/
X#define	MKP_RIGHT	'6'		/* key pad move right		*/
X#define	MKP_STOP	'5'		/* key pad stop movement	*/
X#define	MQUIT		'q'		/* quit game			*/
X#define	MREDRAW		CTRL(L)		/* redraw the screen		*/
X#define	MSHELL		'!'		/* shell escape			*/
X#define	MHELP		'?'		/* give list of commands	*/
X#define	MFAST		'f'		/* toggle looping in make_move	*/
X#define	MQUIET		'b'		/* toggle beeping		*/
X#define	MPAUSE		'p'		/* hang on getchar()		*/
X#define	MHUH		CTRL(R)		/* reprint last message		*/
X#define	MNULL		'\0'
X/*
X** special input defines (cheating)
X*/
X#define	MWIZARD		CTRL(P)		/* request to become wizard	*/
X#define	MSTATUS		's'		/* print debugging info		*/
X#define	MMONS		'm'		/* print monster  ""    ""	*/
X#define	MPM		'@'		/* request for more pm's	*/
X#define	MUP_LVL		CTRL(U)		/* up a level			*/
X#define	MDN_LVL		CTRL(D)		/* down a level			*/
X#define	MEAT		CTRL(E)		/* make them eatable		*/
X#define	MMEAN		CTRL(M)		/* make them non-eatable	*/
X#define	MSLOW		'r'		/* change null padding		*/
X
X/*
X** wizardly defines
X*/
X#define	W_PASSWD	"mTEsRrd1.h/"	/* any guesses?			*/
X#define	SALT		"mT"		/* look familiar anyone?	*/
X
X/*
X** various characters on the screen
X** the underscores mean the "submissive" ghost associated with *
X*/
X#define	PM		'@'
X#define HARPO		'H'
X#define _HARPO		'h'
X#define GROUCHO		'G'
X#define _GROUCHO	'g'
X#define ZEPPO		'Z'
X#define _ZEPPO		'z'
X#define CHICO		'C'
X#define _CHICO		'c'
X#define	BLOCK		'#'
X#define	DOT		'.'
X#define	TUNNEL		'-'
X#define	ENERGY		'*'
X#define	EMPTY		' '
X#define	DOOR		'='
X
X/*
X** monster attributes
X*/
X#define	FAST		001		/* three different (relative) speeds */
X#define	MED		002
X#define	SLOW		004
X#define	SMART		010		/* three different (relative) smarts */
X#define	NORMAL		020
X#define	DUMB		040
X
X/*
X** to keep lint quiet
X** used primarily for the "Hit return to continue"'s
X*/
X#ifdef	LINT
X#	define	trash(x)	_trash_ = x
X#else
X#	define	trash(x)	x
X#endif
X
X/*
X** miscellaneous definition
X*/
X#define	BELL		'\07'
X#define	SPEED		15
X#define	MIN_BAUD	1200		/* must alter bauds[] if this value is
X					** changed */
X#define	EAT_PAUSE	((unss) 20)	/* used when pm is eaten */
X#define	MAX_DIRS	4
X#define	MAX_LEVEL	13
X#define	MAX_ENERGY	4
X#define	MAX_DOTS	208
X#define	MAX_MONS	4
X#define	MAX_PMS		4
X#define	MAX_BLINKS	30		/* number of warning blinks	*/
X#define	MAX_CNT		50		/* kind of useless, (see get_move) */
X#define	TUNN_TIME	2
X#define TUNN_ROW	11
X#define	DOOR_COL	26		/* column where door to monsters is */
X#define	V_DOT		10
X#define	V_ENERGY	50
X#define	ALARM_TIME	10		/* messages get erased every 10 sec */
X#define	BONUS		10000		/* get a free pm with 10000 score*/
X#define	CMASK		0177		/* used to strip garbage from what
X					** inch() returns (when in stand...)
X					*/
X/*
X** useful defines to make core more terse
X*/
X#define	when		break; case
X#define otherwise	break; default
X
X#ifdef	PATTERNS
X#	define	SEED		(uid+level)	/* thier uid */
X#else
X#	define	SEED		get_seed()	/* thier uid */
X#endif
X
X/*
X** stuff to do with the score routines
X*/
X#define	DEFAULT_SH	"/bin/sh"	/* default shell */
X#define	MAX_SCORES	10		/*  maximum scores on pm roll */
X#define	SCR_SIZE	(sizeof(score))
X#define	NAME_SIZE	50		/* biggest name allowed on roll	*/
X#define	MODE		0644		/* the mode of the PM_ROLL	*/
X			/* you devil */
X#define	MAX_BYTES	4096		/* byte size of user file	*/
X#define	MAX_USERS	(MAX_BYTES*BYTE_SIZE)/* number of uid's (bit size of
X					** PM_USER)			*/
X
X/*
X** flag stuff for the above
X*/
X#define	FL_DIE		0		/* indicates he died		*/
X#define	FL_QUIT		1		/* indicates he quit		*/
X
X/*
X** misc definitions
X*/
X#define	unss	unsigned short
X
X/*
X** structure definitions
X*/
Xtypedef	struct
X{
X	int	x, y;
X} coord;
X
Xtypedef	struct				/* monster description		*/
X{
X	coord	mo_pos;			/* where it is at		*/
X	char	mo_inch;		/* what it is on top of		*/
X	bool	mo_run,			/* TRUE if it is eatable	*/
X		mo_tunn,		/* TRUE if it is in a tunnel	*/
X		mo_eaten,		/* TRUE if eaten		*/
X		mo_inside;		/* TRUE if inside		*/
X	char	mo_ch,			/* current move			*/
X		mo_name;		/* name (letter) of monster	*/
X	int	mo_cnt,			/* how many times to do mo_ch	*/
X		mo_extunn,		/* how long left in tunnel	*/
X		mo_attrib;		/* monsters characteristics	*/
X} mons;
X
Xtypedef	struct
X{
X	int	sc_uid;			/* player's uid			*/
X	long	sc_score;		/* player's score		*/
X	int	sc_level;		/* how deep the player went	*/
X	int	sc_flags;		/* misc. info			*/
X	char	sc_name[NAME_SIZE],	/* player's name		*/
X		sc_mons;		/* monster's name		*/
X} score;
X
X/*
X** psuedo functions
X*/
X/*
X** need to strip out the garbage that inch() returns, i only want
X** the single character, and i think that it gets messed up when
X** that character is in background
X*/
X#define	INCH()		(inch() & CMASK)
X#define	abs(x)		(x < 0 ? - x : x)
X#define	MVADDCH(p, c)	mvaddch(p.y, p.x, c)
X#define	TF(x)		(x ? "True" : "False")
X#define	DIST()		rnd(4, 15)
X#define	IS_FRUIT(c)	(c == fr_ch)	/* ...			*/
X#define	SLOWER()	slow(FALSE)
X#ifndef	CTRL
X#	define	CTRL(ch)	('ch' & '\037')
X#endif
X#define	AT(pos1, pos2)	(((pos1)->x == (pos2)->x) && ((pos1)->y == (pos2)->y))
X#define	OUTOFTUNN(pos)	(((pos)->y != 0) && ((pos)->y != 52))
X#define	beep()		putchar(BELL)
X#define	RN		(((seed = (seed * 11109) + 13849) & 0xfff) >> 1)
X#define	randomize(i)	seed = i
X#define	flush()		raw(),noraw()
X#define	m_erase(mon)	mvaddch(mon.mo_pos.y, mon.mo_pos.x, mon.mo_inch)
X#define	draw()		ftime(&_tp), refresh(), delay()
X
Xextern	coord	pm_pos;
Xextern	struct	timeb	_tp;
Xextern	int	pm_tunn, pm_extunn, d_left, e_left, level, fr_val,
X		fruit_val[], pms_left, pm_bonus, pm_eaten, pm_run,
X		mons_eaten, mons_val[], eat_times[], timer, was_wiz, is_wiz,
X		timeit, quiet, fast, uid, seed, bauds[], wizard_uid;
Xextern	char	*argv0, fruit[], fruit_eaten[], fr_ch, ch, oldch,
X		newch, moves[], *mesg, **environ, *pm_user, *pm_roll,
X		baud;
X#ifdef	LINT
Xextern	char	_trash_;
X#endif
Xextern	long	thescore, hi_score, demon, move_cntr, chcnt;
Xextern	mons	ghosts[];
Xextern	mons	*h, *g, *c, *z;
Xextern	char	_putchar(), *crypt(), *strcpy();
Xextern	int	getuid();
Xextern	off_t	lseek();
X
X/*
X** local functions
X*/
Xextern	void	add_fruit(), aggressive(), chg_lvl(), check_scrs(),
X		commands(), delay(), die(), directions(),
X		doadd(), draw_screen(), eat_pm(), init(),
X		m_eat_pm(),
X		mons_init(), m_move(), msg(), msg_erase(), msleep(),
X		mv_mon(), new_screen(), old_screen(), p_barriers(), p_dots(),
X		p_energizers(), p_fruits(), p_info(), p_monsters(),
X		p_pm(), p_pms(), p_scores(), place_m(), pm_eat_m(),
X		pmers(),
X		print_scrs(), quit_it(), quitit(),
X		re_msg(), redraw(), scores(), scrcpy(),
X		shell(), slow(), slowness(), status(), strucpy(),
X		submissive(), tombstone(), trap(), usage(), warning();
Xextern	int	_mv_mon(), can_see(), chk_pm_user(), dir_int(), get_move(),
X		is_mons(), is_safe(), m_is_safe(),
X		make_moves(), moveit(), move_to(), pending(), rnd();
Xextern	char	gen_mv(), *get_pass(), int_dir(), lturn(), *mons_str(),
X		opposite(), *punctrl(), rturn(), to_baud(), toletter(),
X		tunn_look();
Xextern	long	get_hi_scr();
X
Xextern	mons	*wh_mons();
X
X#ifdef	PATTERNS
Xextern	int	get_seed();
X#endif
END-of-pm.h
echo file: pm.6
sed 's/^X//' >pm.6 << 'END-of-pm.6'
X.TH PM UCD "16 October 1985"
X.SH NAME
Xpm \- computer version of PacMan
X.SH SYNOPSIS
X.B pm
X[
X.B -s
X] [
X.B -p
X] [
X.B -Bn
X]
X.SH OPTIONS
X.TP
X.B \-s
XPrint out the list of scores.
X.TP
X.B \-Bn
XSpecify the baud rate which you are using.
X.TP
X.B \-p
XPrint list of players.
X.SH DESCRIPTION
X.I Pm
Xis a UNIX version of the popular arcade game PacMan.
XThe player, represented by the `@' symbol, attempts to score as many
Xpoints as possible.  He does this by eating the small dots on the
Xscreeen, the energizers, the monsters and fruit.  Each item provides
Xa number of points as follows:
X.sp
X.nf
X.ta 3i
XDot(.)	10
XEnergizer(*)	50
XMonster	varies
XFruit(%,&,0,etc.)	varies
X.fi
X.LP
XOf course it would be simple if this was all there was to do. 
XUnfortunately, the pacman is chased by Groucho, Zeppo, Harpo and Chico
Xwhich are represented by G, Z, H and C respectively.  If they ever
Xrun into the pacman, he is eaten and loses a life (you begin the game
Xwith three).  However, pacman can retaliate by eating an energizer.
XWhen this happens, the characters representing the monsters become
Xlower case and pacman will be able to eat them.  But beware, the
Xopportunity to do this is short.
X.LP
XThe points gained for eating monsters vary based on the number consecutively
Xconsumed.  This is probably related to the fact that the monsters
Xtend to move more quickly as the game continues.
XIn addition, fruit is worth more as the pacman continues.
X.PP
X.TP
X.I List of Commands
X.nf
X.ta 3i
X!	exec a shell
Xb	toggle beeping
Xf	toggle speed
Xh	move left
Xj	move down
Xk	move up
Xl	move right
Xp	pause
Xq	quit
XSPACE	stop
X.fi
X.LP
XPlayers will discover several interesting features in the game.  For
Xone, the monsters will become faster and more aggressive as the game
Xgoes on.  For another, players may notice that the monsters have their
Xown personalities; it has been postulated that this is related to
Xthe favorite machines of the author.
X.LP
XNormally the game does not generate beeps, but the player can cause
X.I pm 
Xto beep whenever the pacman eats anything with the
X.B b
Xcommand.  In addition, the game
Xpauses after each cycle.  These pauses may be shortened with the 
X.B f
Xcommand.
X.SH FILES
X*/pm_roll	list of the top ten
X.br
X*/pm_user	list of who plays
X.SH AUTHOR
XPeter Costantinidis, Jr., University of California, Davis
X.SH DIAGNOSTICS
XOccasionally complains when strange things seem to occur in the tunnel.
X.SH BUGS
X.I Pm
Xis at less than its best at terminal speeds of less than 9600 Baud.
XIn addition, the algorithm
X.I pm
Xuses to simulate real time is very cpu intensive, and thus the game is
Xexpensive to play.
END-of-pm.6
echo file: shar
sed 's/^X//' >shar << 'END-of-shar'
X#!/bin/sh
Xecho "#!/bin/sh"
Xecho ": \"This is a shell archive, meaning:                              \""
Xecho ": \"1. Remove everything above the #! /bin/sh line.                \""
Xecho ": \"2. Save the resulting test in a file.                          \""
Xecho ": \"3. Execute the file with /bin/sh (not csh) to create the files:\""
Xfor i
Xdo
X	echo ": \"	$i\""
Xdone
Xecho -n ": \"This archive created:  "
Xecho `date` \"
Xfor i
Xdo
X	echo "echo file: $i"
X	echo "sed 's/^X//' >$i << 'END-of-$i'"
X	sed 's/^/X/' $i
X	echo "END-of-$i"
Xdone
Xecho exit
END-of-shar
echo file: pm.c
sed 's/^X//' >pm.c << 'END-of-pm.c'
X/*
X** pm.c -	main: argument parsing, main control loop and usage messages
X**
X**	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X*/
X#include "pm.h"
X
Xmain (argc, argv)
Xreg	int	argc;
Xreg	char	**argv;
X{
X	argv0 = *argv++, argc--;
X	if (argc == 1)
X	{
X		if (*((*argv)++) != '-')
X			usage();
X		switch (**argv)
X		{
X			case 'p':
X				pmers();
X				exit(0);
X			case 's':
X				check_scrs();
X				exit(0);
X			case 'B':
X				baud = to_baud(++*argv);
X				break;
X			default:
X				usage();
X		}
X	}
X	else if (argc)
X		usage();
X	if (!strcmp("tester", argv0))
X		was_wiz = is_wiz = TRUE;
X#ifdef	NOFULLPATH
X	/*
X	** at one time the author was worried about the whole world finding
X	** out this games existed.  to keep its location a little private
X	** he tried to discourage full pathnames to the game from showing
X	** up on a ps(1) output by forcing argv[0] to be "pm".
X	** the following code checked for this condition.
X	*/
X	else if (getuid() != wizard_uid && strcmp("pm", argv0))
X	{
X		fprintf(stderr, "That is a Big No NO!!!");
X		setuid(getuid());
X		if (getuid() != 0)
X			kill(0, 9);	/* blast them out of the water */
X	}
X#endif
X	init();
X	slow(TRUE);
X	while (TRUE)
X	{
X		SLOWER();
X		demon++;
X		if (make_moves())
X			break;
X		if (timer > 0)
X			timer--;
X		/*
X		** we know we need a new board drawn when we are
X		** out of dots and energizers
X		*/
X		p_scores();
X		if (!e_left && !d_left)
X		{	/* no more dots left	*/
X			chg_lvl(1);
X			continue;
X		}
X		if (!timer && !pm_run)	/* energizers ran out	*/
X		{
X			aggressive();
X			pm_run = TRUE;
X			continue;
X		}
X		if ((timer < MAX_BLINKS) && !(timer % 4))
X			warning();		/* warn every four moves*/
X		m_move();			/* move the monsters	*/
X	}
X	quitit();
X	exit(0);
X}
X
X/*
X** usage()	- print a usage message and exit
X*/
Xvoid	usage ()
X{
X	fprintf(stderr, "Usage: %s [-s] [-p] [-Bn]\n", argv0);
X	exit(1);
X}
END-of-pm.c
echo file: random.c
sed 's/^X//' >random.c << 'END-of-random.c'
X/*
X** random.c -	yet another random number generator and random seed maker
X**
X**	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X*/
X
X#include "pm.h"
X
X/*
X** rnd()	- return a number between a and b, inclusive
X*/
Xint	rnd (a, b)
Xreg	int	a, b;
X{
X	return((RN % (abs(b - a) + 1)) + a);
X}
X
X#ifndef	PATTERNS
X/*
X** get_seed()	- returns a seed for the random number generator
X**		  dependent upon the time and date
X*/
Xint	get_seed ()
X{
X	reg	int	seed;
X	reg 	struct	tm	*timestruct;
X	auto	long	clock;
X	extern	long	time();
X	extern	struct	tm	*localtime();
X
X	clock = time(0);
X	timestruct = localtime(&clock);
X	seed = timestruct->tm_sec  +
X	       timestruct->tm_min  +
X	       timestruct->tm_hour +
X	       timestruct->tm_mday +
X	       timestruct->tm_mon  +
X	       timestruct->tm_year +
X	       timestruct->tm_yday;
X	return((int) ((seed + clock) % 32767));
X}
X#endif
END-of-random.c
echo file: rip.c
sed 's/^X//' >rip.c << 'END-of-rip.c'
X/*
X** rip.c -	code dealing with the end of the game
X**
X**	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X*/
X
X#include "pm.h"
X
X/*
X** die()	- pm has died forever...
X*/
Xvoid	die (mon)
Xreg	char	mon;
X{
X	echo();
X	nocrmode();
X	_puts(CL);
X	tombstone(thescore, mon);
X	printf("[Press return to continue]");
X	getchar();
X	scores(mon, FL_DIE);
X	exit(0);
X}
X
X/*
X** quitit()	- called when they quit
X*/
Xvoid	quitit ()
X{
X	clear();
X	move(LINES - 1, 0);
X	draw();
X	echo();
X	nocrmode();
X	printf("[Press return to continue]");
X	getchar();
X	scores(NULL, FL_QUIT);
X	exit(0);
X}
X
X/*
X** tombstone()	- print a pretty little pm
X*/
Xstatic	char	*stone[] =
X{	"                       @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
X	"                     @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
X	"                   @@@@@@                            @@@@@",
X	"                  @@@@@          @@@@@@@@@@@@@         @@@@",
X	"                 @@@@@        @@@@@@@@@@@@@@@@@@@       @@@@",
X	"                @@@@@       @@@@@            @@@@@      @@@@",
X	"               @@@@@        @@@@   Eaten by    @@@      @@@@@",
X	"               @@@@@        @@@@               @@@      @@@@@",
X	"              @@@@@         @@@                @@@      @@@@",
X	"              @@@@         @@@@                @@@      @@@@",
X	"             @@@@@          @@@                @@@      @@@@",
X	"             @@@@@          @@@@               @@@     @@@@@",
X	"             @@@@@          @@@@               @@@    @@@@@",
X	"              @@@@          @@@@@            @@@@@ @@@@@@",
X	"              @@@@@           @@@@@@@@@@@@@@@@@@@@@@@@",
X	"               @@@@@              @@@@@@@@@@@@  @@@@",
X	"               @@@@@",
X	"                @@@@@",
X	"                 @@@@@",
X	"                  @@@@@",
X	"                   @@@@@@",
X	"                    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
X	"                      @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
X	0
X};
Xvoid	tombstone (scr, monster)
Xlong	scr;
Xreg	char	monster;
X{
X	reg	char	**s = stone;
X
X	clear();
X	move(0, 0);
X	while (*s)
X		printw("%s\n", *s++);
X	move(9, 33);
X	printw("%9s", mons_str(monster));
X	move(18, 30);
X	printw("After getting %ld points.", scr);
X	move(LINES - 1, 0);
X	draw();
X}
END-of-rip.c
echo file: score.c
sed 's/^X//' >score.c << 'END-of-score.c'
X/*
X** score.c -	code dealing with maintaining the score file and the user log
X**
X**	Note:	the score file is not encrypted
X**
X**	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X*/
X#include "pm.h"
X#include <pwd.h>
X
Xextern	int	fwrite(), fread();
X
X/*
X** check_scrs()	- print out the pm roll (only for score enquiries)
X*/
Xvoid	check_scrs ()
X{
X	reg	int	i;
X	auto	score	scrs[MAX_SCORES];
X
X	if (rwscore(0, scrs))
X	{
X		printf("No scores recorded at this time\n");
X		return;
X	}
X	printf("\nTop Ten Players:\n");
X	printf("Rank\tScore\tName\n");
X	for (i = 0; i < MAX_SCORES && scrs[i].sc_uid != -1; i++)
X	{
X		print_scrs(&scrs[i], i + 1);
X		printf(".\n");
X	}
X}
X
X/*
X** get_hi_scr()	- get the highest score from the score file
X*/
Xlong	get_hi_scr ()
X{
X	auto	score	scrs[MAX_SCORES];
X
X	if (rwscore(0, scrs))
X		return(0L);
X	if (scrs[0].sc_uid == uid)
X	{	/* taunt the high scorer */
X		printf("\nWelcome back %s,\n", scrs[0].sc_name);
X		printf("do you think that you can do better than\n");
X		printf("last time?  Just a second while a think\n");
X		printf("of a better strategy!\n");
X		sleep(1);
X#ifdef	PATTERNS
X		/*
X		** they seem to have this pattern mastered, so
X		** lets be tricky and give them a different pattern
X		** (i.e. change the seed)
X		*/
X		randomize(getpid());
X#endif
X	}
X	return(scrs[0].sc_score);
X}
X
X#ifdef	PM_USER
X/*
X** chk_pm_user() -	check the user file
X**			return non-zero on error
X*/
Xint	chk_pm_user ()
X{
X	reg	int	i;
X	reg	int	bit, byte;
X	auto	char	buf[MAX_BYTES];
X
X	if (!pm_user ||
X	    uid > (unsigned) MAX_USERS)	/* wraps on 16 bit machines */
X		return(FALSE);
X	byte = uid / BYTE_SIZE;
X	bit = 1 << (uid % BYTE_SIZE);
X	for (i=0; i<MAX_BYTES; i++)
X		buf[i] = '\0';
X	if (rwuser(0, buf))
X		fprintf(stderr, "%s: creating user file\n", argv0);
X	if (!(buf[byte] & bit)) /* have they played before? */
X	{
X		directions(); 			/* give directions and */
X		buf[byte] |= bit;
X		if (rwuser(1, buf))
X		{
X			fprintf(stderr, "%s: cannot update user file\n", argv0);
X			return(TRUE);
X		}
X	}
X	return(FALSE);
X}
X#endif
X
X/*
X** print_scrs()	- print out the score record
X*/
Xvoid	print_scrs (scr, rank)
Xreg	score	*scr;
Xreg	int	rank;
X{
X	static	char	*reason[] =
X	{
X		"eaten",
X		"quit",
X	};
X
X	if (scr->sc_uid < 0)
X		return;
X	printf("%d\t%6ld\t%s: %s after %d screens",
X		rank, scr->sc_score, scr->sc_name,
X		reason[scr->sc_flags], scr->sc_level+1);
X	if (scr->sc_flags == FL_DIE)
X		printf(" by %s", mons_str(scr->sc_mons));
X}
X
X/*
X** scores()	- print the list of scores and conditionally add
X**		  the new one
X*/
Xvoid	scores (mon, flags)
Xchar	mon;
Xint	flags;
X{
X	reg	int	i;
X	auto	score	scrs[MAX_SCORES];
X
X	if (rwscore(0, &(scrs[0])))
X	{
X		fprintf(stderr, "%s: creating the score file\n", argv0);
X		for (i=0; i<MAX_SCORES; i++)
X			scrs[i].sc_uid = -1;	/* indicates an invalid score */
X	}
X	if (madeit(thescore, scrs))
X	{
X		auto	score	newscr;
X		auto	char	buf[BUFSIZ];
X
X		printf("You made it on the pm roll with ");
X		printf("your score of %ld!\n", thescore);
X		printf("Your name please: ");
X		if (!gets(buf))
X			printf("Sorry, that is not acceptable!\n");
X		newscr.sc_score = thescore;
X		newscr.sc_uid = uid;
X		newscr.sc_level = level;
X		newscr.sc_mons = mon;
X		newscr.sc_flags = flags;
X		strucpy(newscr.sc_name, buf, NAME_SIZE);
X		newscr.sc_name[NAME_SIZE - 1] = NULL;
X		/*
X		** find where to insert their score
X		*/
X		for (i = 0; i < MAX_SCORES && scrs[i].sc_uid != -1; i++)
X		{
X			auto	score	scr;
X
X			if (thescore < scrs[i].sc_score)
X				continue;
X			scrcpy(&scr, &(scrs[i]));
X			scrcpy(&(scrs[i]), &newscr);
X			for (++i; i < MAX_SCORES; i++)
X			{
X				auto	score	tmp;
X
X				scrcpy(&tmp,&(scrs[i]));
X				scrcpy(&(scrs[i]),&scr);
X				scrcpy(&scr,&tmp);
X			}
X			break;
X		}
X		if (scrs[i].sc_uid == -1)	/* add at end of roll */
X			scrcpy(&(scrs[i]), &newscr);
X		if (rwscore(1, scrs))
X			fprintf(stderr, "%s: cannot write score file\n", argv0);
X	}
X	check_scrs();
X}
X
X/*
X** madeit()	- return true if the given score will make on the given roll
X*/
Xstatic	int	madeit (l, scrs)
Xreg	long	l;
Xreg	score	*scrs;
X{
X	reg	int	i;
X
X	if (!l || was_wiz)
X		return(FALSE);
X	for (i=0; i<MAX_SCORES; i++, scrs++)
X		if (scrs->sc_uid == -1)
X			return(TRUE);
X		else if (l > scrs->sc_score)
X			return(TRUE);
X	return(FALSE);
X}
X
X/*
X** pmers()	- called by main() to print the list of players
X*/
Xvoid	pmers ()
X{
X#ifdef	PM_USER
X	auto	char	buf[MAX_BYTES];
X	reg	int	j;
X
X	if (rwuser(0, buf))
X	{
X		perror(pm_user);
X		exit(1);
X	}
X	for (j = 0; j < MAX_BYTES; j++)
X	{
X		reg	int	i;
X
X		if (!buf[j])
X			continue;
X		for (i = 0; i < BYTE_SIZE; i++)
X		{
X			auto	int	puid;
X			struct	passwd	*pw;
X			extern	struct passwd *getpwuid();
X
X			if (!((char) (1 << i) & buf[j]))
X				continue;
X			if (pw = getpwuid(puid = ((j * BYTE_SIZE) + i)))
X				printf("%s\n", pw->pw_name);
X			else
X				fprintf(stderr, "%s: getpwuid(%d) error\n",
X					argv0, puid);
X		}
X	}
X#else
X	printf("%s: there is no pm user file\n", argv0);
X#endif
X}
X
X/*
X** rwscore()	- read/write the contents of the pm_roll file
X**		- if flag is true, write, else read
X**		- return non-zero on error
X*/
Xstatic	int	rwscore (flag, scrs)
Xreg	int	flag;
Xreg	score	*scrs;
X{
X	reg	FILE	*fp;
X	reg	int	(*func)();
X
X	if (!(fp = fopen(pm_roll, flag ? "w" : "r")))
X		return(TRUE);
X	func = flag ? fwrite : fread;
X	if ((*func)(scrs, sizeof(*scrs), MAX_SCORES, fp) != MAX_SCORES)
X	{
X		perror(pm_roll);
X		fclose(fp);
X		return(TRUE);
X	}
X	fclose(fp);
X	return(FALSE);
X}
X
X#ifdef	PM_USER
X/*
X** rwuser()	- read/write the contents of the pm_user file
X**		- if flag is true, write, else read
X**		- return non-zero on error
X*/
Xstatic	int	rwuser (flag, buf)
Xreg	int	flag;
Xreg	char	*buf;
X{
X	reg	FILE	*fp;
X	reg	int	(*func)();
X
X	if (!(fp = fopen(pm_user, flag ? "w" : "r")))
X		return(TRUE);
X	func = flag ? fwrite : fread;
X	if ((*func)(buf, sizeof(*buf), MAX_BYTES, fp) != MAX_BYTES)
X	{
X		perror(pm_user);
X		fclose(fp);
X		return(TRUE);
X	}
X	fclose(fp);
X	return(FALSE);
X}
X#endif
END-of-score.c
echo file: screen.c
sed 's/^X//' >screen.c << 'END-of-screen.c'
X/*
X** screen.c -	code dealing with display (most of what is written to the
X**		screen during the course of a game is done here)
X**
X**	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X*/
X
X#include "pm.h"
X
X/*
X** p_draw_screen()- draw the board
X**		- this ordering is kind of important.  the dots must
X**		  be printed first and then the barriers in standout
X**		  so that if the terminal uses "inverse video" for
X**		  the so entry in /etc/termcap (or wherever) the
X**		  paths through the maze are not in inverse video.
X**		- also, whenever the screen is cleared and dots()
X**		  called, barriers() must be called again, or else
X**		  dots() will overwrite the barriers.
X**		- the SLOWER() is so that the monsters don't move
X**		  until the screen is totally redrawn
X*/
Xvoid	draw_screen ()
X{
X	alarm(0);	/* make sure the alarm is off */
X	msg("");
X	randomize(SEED);
X	clear();
X	p_dots();	/* the dots must be printed first */
X	standout();
X	p_barriers();
X	standend();
X	p_scores();
X	p_energizers();
X	p_pm();
X	p_pms();
X	p_monsters();
X	p_fruits();
X	draw();
X	sleep(1);
X}
X
X/*
X** new_screen()	- called when a screen has been cleaned out
X**		- the SLOWER() is so that the monsters don't move
X**		  until the screen is totally redrawn
X*/
Xvoid	new_screen ()
X{
X	reg	int	i;
X
X	alarm(0);	/* make sure the alarm is off */
X	msg("");
X	randomize(SEED);
X	flush();
X	mvaddch(pm_pos.y, pm_pos.x, EMPTY);
X	aggressive();
X	for (i = 0; i < 4; i++)
X		m_erase(ghosts[i]);
X	draw();
X	p_scores();
X	p_dots();
X	standout();
X	p_barriers();
X	standend();
X	p_energizers();
X	p_pm();
X	p_monsters();
X	p_fruits();
X	draw();
X	sleep(2);
X}
X
X/*
X** old_screen()- called when pm is eaten
X*/
Xvoid	old_screen ()
X{
X	reg	int	i;
X
X	alarm(0);	/* make sure the alarm is off */
X	msg("");
X	flush();
X	mvaddch(pm_pos.y, pm_pos.x, EMPTY);
X	aggressive();
X	for (i = 0; i < MAX_MONS; i++)
X		m_erase(ghosts[i]);
X	draw();
X	p_scores();
X	p_pms();
X	p_pm();
X	p_monsters();
X	draw();
X	sleep(1);
X}
X
X/*
X** redraw()	- redraw the screen
X*/
Xvoid	redraw ()
X{
X	alarm(0);	/* make sure the alarm is off */
X	clearok(stdscr, TRUE);
X	msg("");
X	draw();
X	sleep(1);
X}
X
X/*
X** p_scores()	- print the score and high score if changed
X**		- check to see if they get another pm
X**		- must remember to print both scores when the
X**		  game is first started
X*/
Xvoid	p_scores ()
X{
X	static	long	_score = -1L;
X
X	if (thescore == _score)
X		return;	/* the posted score is accurate */
X	if ((thescore > BONUS) && pm_bonus)
X	{
X		pm_bonus = FALSE;
X		pms_left++;
X		p_pms();
X	}
X	move(0, 23);
X	printw("%06ld", thescore);
X	if (thescore > hi_score || _score == -1L)
X	{
X		move(0, 47);
X		printw("%06ld",
X			(thescore>hi_score) ? (hi_score=thescore) : hi_score);
X	}
X	_score = thescore;
X}
X
X/*
X** p_fruits()	- place the fruit and sets its value
X*/
Xvoid	p_fruits ()
X{
X	reg	int	lvl = (level >= MAX_LEVEL ? MAX_LEVEL-1 : level);
X
X	fr_ch = fruit[lvl];
X	fr_val = fruit_val[lvl];
X	move(13, 26);
X	addch(fr_ch);
X}
X
X/*
X** add_fruit()	- add eaten fruit to fruit list
X**		- have to shift things over some
X*/
Xvoid	add_fruit (fr)
Xreg	char	fr;
X{
X	reg	int	i;
X
X	for (i = 6; i > 0; i--)
X		fruit_eaten[i * 2] = fruit_eaten[(i - 1) * 2];
X	fruit_eaten[0] = fr;
X	move(0, 55);
X	printw("%s", fruit_eaten);
X}
X
X/*
X** p_pm()	- place the pm in its starting position
X*/
Xvoid	p_pm ()
X{
X	pm_tunn = FALSE;
X	pm_pos.x = 26;
X	pm_pos.y = 17;
X	move(pm_pos.y, pm_pos.x);
X	addch(PM);
X}
X
X/*
X** p_pms()	- place the spare pm's
X*/
Xvoid	p_pms ()
X{
X	reg	int	i;
X
X	for (i = 1; i < MAX_PMS; i++)
X	{
X		move(0, (2 * (i - 1)));	
X		if (i >= pms_left)
X			addch(EMPTY);
X		else
X			addch(PM);
X	}
X}
X
X/*
X** p_energizers() - put in the energizers
X*/
Xvoid	p_energizers ()
X{
X	move(4, 51);
X	addch(ENERGY);
X	move(4, 1);
X	addch(ENERGY);
X	move(17, 1);
X	addch(ENERGY);
X	move(17, 51);
X	addch(ENERGY);
X	e_left = MAX_ENERGY;
X}
X
Xstatic	char	*_board[] =
X{
X	"#####################################################\n",
X	"#                       #####                       #\n",
X	"# ######### ########### ##### ########### ######### #\n",
X	"# ######### ########### ##### ########### ######### #\n",
X	"#                                                   #\n",
X	"# ######### ##### ################# ##### ######### #\n",
X	"#           #####       #####       #####           #\n",
X	"########### ########### ##### ########### ###########\n",
X	"#         # #####                   ##### #         #\n",
X	"########### ##### ######## ######## ##### ###########\n",
X	"                  #               #                  \n",
X	"########### ##### ################# ##### ###########\n",
X	"#         # #####                   ##### #         #\n",
X	"########### ##### ################# ##### ###########\n",
X	"#                       #####                       #\n",
X	"# ######### ########### ##### ########### ######### #\n",
X	"#     #####                               #####     #\n",
X	"##### ##### ##### ################# ##### ##### #####\n",
X	"#           #####       #####       #####           #\n",
X	"# ##################### ##### ##################### #\n",
X	"# ##################### ##### ##################### #\n",
X	"#                                                   #\n",
X	"#####################################################\n",
X	0
X};
X
X/*
X** p_barriers()	- fills in the board
X*/
Xvoid	p_barriers ()
X{
X	static	int	once = TRUE;
X	static	WINDOW	*tmp;
X
X	if (once)
X	{
X		reg	char	**str = _board;
X
X		if ((tmp = newwin(0, 0, 0, 0)) == ERR)
X		{
X			move(0, 0);
X			printw("barriers(): newwin() error");
X			draw();
X			quit_it();
X		}
X		wmove(tmp, TOP, 0);
X		while (*str)
X			waddstr(tmp, *str++);
X		once = FALSE;
X	}
X	overlay(tmp, stdscr);
X}
X
Xstatic	char	*_dots[] =
X{
X	"                                                     \n",
X	" . . . . . . . . . . . .     . . . . . . . . . . . . \n",
X	" .         .           .     .           .         . \n",
X	" .         .           .     .           .         . \n",
X	" . . . . . . . . . . . . . . . . . . . . . . . . . . \n",
X	" .         .     .                 .     .         . \n",
X	" . . . . . .     . . . .     . . . .     . . . . . . \n",
X	"           .                             .           \n",
X	"           .                             .           \n",
X	"           .              =              .           \n",
X	"-          .                             .          -\n",
X	"           .                             .           \n",
X	"           .                             .           \n",
X	"           .                             .           \n",
X	" . . . . . . . . . . . .     . . . . . . . . . . . . \n",
X	" .         .           .     .           .         . \n",
X	" . . .     . . . . . . .     . . . . . . .     . . . \n",
X	"     .     .     .                 .     .     .     \n",
X	" . . . . . .     . . . .     . . . .     . . . . . . \n",
X	" .                     .     .                     . \n",
X	" .                     .     .                     . \n",
X	" . . . . . . . . . . . . . . . . . . . . . . . . . . \n",
X	"                                                     \n",
X	0
X};
X
X/*
X** p_dots()	- fills in the board
X*/
Xvoid	p_dots ()
X{
X	reg	char	**str = _dots;
X
X	d_left = MAX_DOTS;
X	move(TOP, 0);
X	while (*str)
X		addstr(*str++);
X}
END-of-screen.c
echo file: timing.c
sed 's/^X//' >timing.c << 'END-of-timing.c'
X/*
X** timing.c -	functions dealing with the "smooth" running of the game
X**		it is important not for the game to get ahead of the screen
X**		and the necessary `slowing down' of the game is done here
X**
X**	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X*/
X
X#include <signal.h>
X#include "pm.h"
X
X#define	BPBYTE	10	/* bits sent to termnal per character (byte) */
X#define	PAWS(x)	(unss) ((1000 * x * BPBYTE) / bauds[baud])
X#define	TIMEBS(x,y) (((unss) ((x)->time - (y)->time) * 1000) + \
X			(x)->millitm - (y)->millitm)
X/*
X** delay()	- coordinate with tty speed
X**     /                1000 ms.                            \
X**     | ------------------------------------ * delta(chars) | == delay in ms.
X**     \   (baud bits/sec)/(BPBYTE bits/char)               /
X*/
Xvoid	delay ()
X{
X	unss	mlen, u;	/* how long it took to print */
X	auto	struct	timeb	tp;
X
X	ftime(&tp);
X	mlen = TIMEBS(&tp, &_tp);
X	if ((u = PAWS(chcnt)) >= mlen)
X		msleep(u - mlen);
X	chcnt = 0l;
X}
X
X/*
X** msleep()	- sleep the specified number of milliseconds
X*/
Xvoid	msleep (u)
Xreg	unss	u;
X{
X#ifdef	ITIMER_REAL
X	static	struct	itimerval	oitv, itv =
X	{
X		{	0L, 0L },
X		{	0L, 0L }
X	};
X	extern	int	wakeup();
X
X#ifndef	LINT
X	signal(SIGALRM, wakeup);
X#endif
X	itv.it_value.tv_sec = u / 1000;
X	itv.it_value.tv_usec = u % 1000;
X	if (setitimer(ITIMER_REAL, &itv, &oitv))
X	{
X		fprintf(stderr, "%s: setitimer() error\n", argv0);
X		perror("setitimer");
X		quitit();
X	}
X	pause();
X#else
X	auto	struct	timeb	tp, tp2;
X
X	ftime(&tp);
X	while (TRUE)
X	{
X		ftime(&tp2);
X		if (TIMEBS(&tp2, &tp) >= u)
X			return;
X	}
X#endif
X}
X
X#ifdef	ITIMER_REAL
X/*
X** wakeup()	- someplace to go when the alarm goes off
X*/
Xstatic	int	wakeup ()
X{
X	return(0);
X}
X#endif
X
Xstatic	int	rates[] =	/* these were `tuned' after much playing */
X{
X/*        0    1    2    3    4    5    6    7    8    9	*/
X	430, 395, 380, 350, 340, 310, 325, 330, 325, 300,
X/*       10   11   12   13   14   15   16   17   18   19	*/
X	305, 325, 360, 320, 290, 260, 220, 190, 140, 120,
X/*       20   21   22   23   24   25   26   27   28   29	*/
X	 95,  70,  45,  20, 100, 200,  50,  99, 200, 300,
X/*       30   31   32   33   34   35   36   37   38   39	*/
X	  5,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X/*       40   41   42   43   44   45   46   47   48   49	*/
X	  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X/*       50   51   52   53   54   55   56   57   58   59	*/
X	  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
X};
X
X/*
X** slow()	- make the game go faster as they go deeper
X**		- assuming that they WILL NOT GET DEEPER THAN
X**		  50 LEVELS!!!!  if they are, then they have been
X**		  tieing up the computer long enough and should stop
X**		  playing anyways
X*/
Xvoid	slow (flag)
Xreg	int	flag;
X{
X	reg	int	ms;
X	static	struct	timeb	tp;
X	auto	struct	timeb	tp2;
X
X	if (flag)
X	{
X		ftime(&tp);
X		return;
X	}
X	if (level > 59)
X		quitit();
X	ftime(&tp2);
X	ms = (int) TIMEBS(&tp2, &tp);
X	if (fast)
X	{
X		if (ms < rates[level]/2)
X			msleep((rates[level]/2) - ms);
X	}
X	else
X	{
X		if (ms < rates[level])
X			msleep(rates[level] - ms);
X	}
X	ftime(&tp);
X}
X
X/*
X** slowness()	- sets delay padding for number of nulls to be printed
X**		  each loop
X*/
Xvoid	slowness ()
X{
X	auto	char	buf[BUFSIZ];
X
X	_puts(CL);
X	echo();
X	nocrmode();
X	printf("old delay: %d, new delay: ", rates[level]);
X	if (!gets(buf))
X		msg("EOF in slowness");
X	if (buf[0])
X		if (sscanf(buf, "%d", &(rates[level])) == EOF)
X			msg("EOF2 in slowness");
X	noecho();
X	crmode();
X	redraw();
X}
X
X/*
X** to_baud()	- convert the given string to an appropriate baud define
X*/
Xchar	to_baud (s)
Xreg	char	*s;
X{
X	reg	int	i;
X	static	char	*sbauds[] =
X	{
X		"0", "50", "75", "110", "134", "150",
X		"200", "300", "600", "1200", "1800", "2400",
X		"4800", "9600", "EXTA", "EXTB",
X		0
X	};
X
X	for (i = 0; sbauds[i]; i++)
X		if (!strcmp(sbauds[i], s))
X			return((char) i);
X	return('\0');
X}
END-of-timing.c
echo file: warning.c
sed 's/^X//' >warning.c << 'END-of-warning.c'
X/*
X** warning.c -	code dealing with the energizers and them being eaten and
X**		wearing out and informing the player his time is ending
X**
X**	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X*/
X
X#include "pm.h"
X
X/*
X** warning()	- warn pm that energizers are about to wear off
X*/
Xvoid	warning ()
X{
X	reg	int	i;
X
X	for (i = 0; i < MAX_MONS; i++)
X	{
X		if (!ghosts[i].mo_run)
X			continue;
X		if (!ghosts[i].mo_tunn)
X			mvaddch(ghosts[i].mo_pos.y, ghosts[i].mo_pos.x,
X				toupper(ghosts[i].mo_name));
X	}
X	draw();
X	/*
X	msleep(10l);
X	*/
X	for (i = 0; i < MAX_MONS; i++)
X	{
X		if (!ghosts[i].mo_run)
X			continue;
X		if (!ghosts[i].mo_tunn)
X			mvaddch(ghosts[i].mo_pos.y, ghosts[i].mo_pos.x,
X				ghosts[i].mo_name);
X	}
X	draw();
X}
X
X/*
X** aggressive()	- perform all the house keeping when the enegizers
X**		  wear off the pm
X*/
Xvoid	aggressive ()
X{
X	reg	int	i;
X
X	mons_eaten = -1;
X	timer = 0;			/* reset the timer	*/
X	for (i = 0; i < MAX_MONS; i++)
X	{
X		ghosts[i].mo_run = FALSE;
X		if (islower(ghosts[i].mo_name))
X			ghosts[i].mo_name = toupper(ghosts[i].mo_name);
X		else
X			continue;
X		if (!ghosts[i].mo_tunn)
X			mvaddch(ghosts[i].mo_pos.y, ghosts[i].mo_pos.x, ghosts[i].mo_name);
X	}
X}
X
X/*
X** submissive()	- make the ghosts eatable
X*/
Xvoid	submissive ()
X{
X	reg	int	i;
X
X	if (level >= MAX_LEVEL)
X		timer = eat_times[MAX_LEVEL - 1];
X	else
X		timer = eat_times[level];
X	pm_run = FALSE;
X	mons_eaten = -1;
X	for (i = 0; i < MAX_MONS; i++)
X	{
X		ghosts[i].mo_run = TRUE;
X		if (isupper(ghosts[i].mo_name))
X			ghosts[i].mo_name = tolower(ghosts[i].mo_name);
X		else
X			continue;
X		if (!ghosts[i].mo_tunn)
X			mvaddch(ghosts[i].mo_pos.y, ghosts[i].mo_pos.x, ghosts[i].mo_name);
X	}
X}
END-of-warning.c
exit
-- 
-- Peter Costantinidis, Jr.
-- ucdavis!deneb!ccohesh001@ucb-vax.arpa		(ARPA)
-- ...!{ucbvax,lll-crg,dual}!ucdavis!deneb!ccohesh001	(UUCP)

ccohesh001@ucdavis.UUCP (Peter Costantinidis, Jr.) (12/14/85)

#!/bin/sh
: "This is a shell archive, meaning:                              "
: "1. Remove everything above the #! /bin/sh line.                "
: "2. Save the resulting test in a file.                          "
: "3. Execute the file with /bin/sh (not csh) to create the files:"
: "	config.c"
: "	flsbuf.c"
: "	init.c"
: "	make_moves.c"
: "	misc.c"
: "	monsters.c"
: "	msg.c"
: "This archive created:  Fri Dec 13 13:05:41 PST 1985 "
echo file: config.c
sed 's/^X//' >config.c << 'END-of-config.c'
X/*
X** config.c -	installation dependent parameters
X**
X**	PM_ROLL:	full pathname of score file
X**
X**	PM_USER:	full pathname of user log file.  used to keep
X**			track of who has played
X**
X**	WIZARD_UID:	if argv[0] == "tester" and getuid() = WIZARD_UID
X**			then game runs in diagnostic mode where special
X**			commands take effect
X**
X**	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X*/
Xchar	*pm_roll = PM_ROLL,	/* score file				*/
X#ifdef	PM_USER
X	*pm_user = PM_USER;	/* user file				*/
X#else
X	*pm_user = NULL;	/* no user file				*/
X#endif
X#ifdef	NOFULLPATH
Xint	wizard_uid = WIZARD_UID;
X#endif
END-of-config.c
echo file: flsbuf.c
sed 's/^X//' >flsbuf.c << 'END-of-flsbuf.c'
X/*LINTLIBRARY*/
X#include <stdio.h>
X
X/*
X**  It is necessary to count the number of characters that are printed.
X**  It was decided that the easiest way to do this would be to increment
X**  a counter variable in _flsbuf().
X**
X**	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X*/
Xextern	char	*malloc();
Xextern	long	chcnt;		/* counter */
X
Xint
X_flsbuf(c, iop)
X	unsigned c;
X	register FILE *iop;
X{
X	register char *base;
X	register n = 1, rn;
X	extern char _sobuf[];
X
X	if (iop->_flag & _IORW) {
X		iop->_flag |= _IOWRT;
X		iop->_flag &= ~_IOEOF;
X	}
X
Xtryagain:
X	if (iop->_flag & _IONBF) {
X		iop->_cnt = 0;
X		if (1 != write(fileno(iop), (char *) &c, 1))
X		{
X			iop->_flag |= _IOERR;
X			return(EOF);
X		}
X		chcnt++;	/* counter incremented */
X		return(c);
X	} 
X	if ((base = iop->_base) == NULL) {
X		if (iop == stdout) {
X			if (isatty(fileno(stdout))) {
X				iop->_flag |= _IONBF;
X				goto tryagain;
X			}
X			iop->_base = _sobuf;
X			iop->_ptr = _sobuf;
X			goto tryagain;
X		}
X		if ((iop->_base = base = malloc(BUFSIZ)) == NULL) {
X			iop->_flag |= _IONBF;
X			goto tryagain;
X		}
X		iop->_flag |= _IOMYBUF;
X		rn = n = 0;
X	} else if((rn = n = iop->_ptr - base) > 0) {
X		iop->_ptr = base;
X		n = write(fileno(iop), base, n);
X		chcnt += (long) n;	/* counter incremented */
X	}
X	iop->_cnt = BUFSIZ - 1;
X	*base++ = c;
X	iop->_ptr = base;
X	if (rn != n) {
X		iop->_flag |= _IOERR;
X		return(EOF);
X	}
X	return(c);
X}
X
X#ifndef	LINT
Xint
Xfflush(iop)
X	register FILE *iop;
X{
X	register char *base;
X	register n;
X
X	if ((iop->_flag & (_IONBF|_IOWRT)) == _IOWRT
X	 && (base = iop->_base) != NULL && (n = iop->_ptr - base) > 0) {
X		iop->_ptr = base;
X		iop->_cnt = BUFSIZ;
X		if (write(fileno(iop), base, n) != n) {
X			iop->_flag |= _IOERR;
X			return(EOF);
X		}
X	}
X	return(0);
X}
X
X/*
X * Flush buffers on exit
X */
X
X#ifndef	BSD43
X_cleanup()
X{
X	register FILE *iop;
X	extern FILE *_lastbuf;
X
X	for(iop = _iob; iop < _lastbuf; iop++)
X		fclose(iop);
X}
X#endif
X
Xint
Xfclose(iop)
X	register FILE *iop;
X{
X	register r;
X
X	r = EOF;
X	if (iop->_flag & (_IOREAD|_IOWRT|_IORW)
X	    && (iop->_flag & _IOSTRG) == 0) {
X		r = fflush(iop);
X		if (close(fileno(iop)) < 0)
X			r = EOF;
X		if (iop->_flag & _IOMYBUF)
X			free(iop->_base);
X		if (iop->_flag & (_IOMYBUF|_IONBF))
X			iop->_base = NULL;
X	}
X	iop->_flag &=
X		~(_IOREAD|_IOWRT|_IONBF|_IOMYBUF|_IOERR|_IOEOF|_IOSTRG|_IORW);
X	iop->_cnt = 0;
X	return(r);
X}
X#endif
END-of-flsbuf.c
echo file: init.c
sed 's/^X//' >init.c << 'END-of-init.c'
X/*
X** init.c -	game initializations
X**
X**	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X*/
X#include "pm.h"
X
Xcoord	pm_pos;
Xstruct	timeb	_tp;		/* used for timing			*/
Xlong	thescore = 0L,		/* player's score			*/
X	hi_score = 0L, 		/* high score so far			*/
X	move_cntr = 0L,		/* # of moves made by player		*/
X	chcnt = 0L, 		/* character count			*/
X	demon = 0L;		/* # of loops game made (psuedo time)	*/
Xchar	fr_ch,			/* fruit character			*/
X	ch = ' ',		/* current move of pm			*/
X	oldch = '\0',		/* old (temporary) move			*/
X	newch = '\0',		/* new move (future)			*/
X	baud = '\0',		/* output baud rate of terminal		*/
X	*argv0,			/* argv[0]				*/
X	*mesg;			/* pointer to last message		*/
Xint	timeit = FALSE,		/* printing loop/move counter?		*/
X	quiet = TRUE,		/* bells and whistles			*/
X	fast = FALSE,		/* skip senseless looping		*/
X	timer = 0,		/* duration timer for energizers	*/
X	level = 0,		/* level (board) number			*/
X	seed,			/* rnd num seed				*/
X	fr_val,			/* fruit value 				*/
X	d_left = MAX_DOTS,	/* number of dots left on board		*/
X	e_left = MAX_ENERGY,	/* number of energizers left on board	*/
X	mons_eaten = -1,	/* number of monsters eaten (<= 4)	*/
X	pm_eaten = FALSE,	/* got eaten				*/
X	pms_left = 3,		/* pm's left (you start with three)	*/
X	pm_bonus = TRUE,	/* can get a bonus pm			*/
X	pm_run = TRUE,		/* TRUE if eatable			*/
X	pm_tunn = FALSE,	/*  "   if in tunnel			*/
X	pm_extunn,		/* how long left in tunnel		*/
X	is_wiz = FALSE,		/* TRUE if currently wizard		*/
X	was_wiz = FALSE,	/* TRUE if ever was wizard		*/
X	uid;			/* user's uid				*/
Xmons	ghosts[MAX_MONS],	/* array of monsters			*/
X	*h, *g, *c, *z;		/* pointers into array of monsters	*/
Xchar	fruit[] = "%&00++$$~~^^_",
X	fruit_eaten[15] = "              ",
X	moves[] = "hjkl";
Xint	fruit_val[] =	/* the values of each succeeding fruit */
X{
X	100, 300, 500, 500, 700, 700, 1000, 1000, 2000, 2000, 3000, 3000, 5000
X};
Xint	mons_val[] =	/* the values of the monsters when eaten */
X{
X	200, 400, 800, 1600
X};
Xint	eat_times[] =	/* the duration of the power pill */
X{
X	130, 130, 120, 115, 110, 100, 95, 85, 75, 70, 65, 55, 45
X};
Xint	bauds[] =
X{
X/* 0  1  2   3   4   5   6   7   8     9    10    11    12    13     14   15*/
X/* 0 50 75 110 134 150 200 300 600  1200  1800  2400  4800  9600   EXTA EXTB*/
X   0, 0, 0,  0,  0,  0,  0,  0,  0, 1200, 1800, 2400, 4800, 9600, 19200,   0
X};
X
X/*
X** init() -	perform necessary intializations
X*/
Xvoid	init ()
X{
X	if ((uid = getuid()) < 0)
X	{
X		fprintf(stderr, "Who the hell are you???\n");
X		exit(1);
X	}
X	randomize(SEED);
X#ifdef	PM_USER
X	if (chk_pm_user())	/* check user log file */
X		fprintf(stderr, "%s: Can not make entry into user log file\n",
X			argv0);
X#endif
X	hi_score = get_hi_scr();
X	if (initscr() == ERR)
X	{
X		fprintf(stderr, "initscr() error\n");
X		perror(argv0);
X		exit(1);
X	}
X	if (!baud)	/* if baud, then we are trying to simulate another */
X	{
X		if (!bauds[_tty.sg_ospeed])
X		{
X			fprintf(stderr, "pm: baud rate must be at least %d(%d)\n", MIN_BAUD, _tty.sg_ospeed);
X			endwin();
X			exit(1);
X		}
X		baud = _tty.sg_ospeed;
X	}
X	trap(0);
X	h = &ghosts[0];
X	g = &ghosts[1];
X	c = &ghosts[2];
X	z = &ghosts[3];
X	mons_init();
X	crmode();
X	noecho();
X	mesg = NULL;
X	draw_screen();
X}
X
X/*
X** mons_init()	- initialize the monsters
X**		- MUST be called before monsters()!!!
X**	Note:	I am sure that there was a reason why I did not statically
X**		initialize these structures.  When I remember the reason
X**		I will mention it here at a later date.
X*/
Xvoid	mons_init ()
X{
X	reg	int	i;
X
X	h->mo_attrib = SMART | SLOW;
X	g->mo_attrib = SMART | FAST;
X	c->mo_attrib = NORMAL | FAST;
X	z->mo_attrib = DUMB | MED;
X	h->mo_name = HARPO;
X	g->mo_name = GROUCHO;
X	c->mo_name = CHICO;
X	z->mo_name = ZEPPO;
X	for (i = 0; i < 4; i++)
X		m_init(&ghosts[i]);
X	g->mo_inside = FALSE;
X}
X
X/*
X** m_init()	- initialize a single monster
X**		- this function is called from p_monsters() (every time a
X**		  new screen is entered)
X*/
Xm_init (m)
Xreg	mons	*m;
X{
X	m->mo_inch = EMPTY;
X	m->mo_run = FALSE;
X	m->mo_tunn = FALSE;
X	m->mo_eaten = FALSE;
X	m->mo_inside = TRUE;
X	m->mo_ch = ' ';
X	m->mo_cnt = 0;
X	m->mo_extunn = 0;
X}
END-of-init.c
echo file: make_moves.c
sed 's/^X//' >make_moves.c << 'END-of-make_moves.c'
X/*
X** make_moves -	code relating to player movement
X**
X**	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X*/
X#include "pm.h"
X
X/*
X** make_moves -	`ch' is the global variable designating the move of the
X**		player.  perhaps it would have been better to pass this
X**		character to this function instead of using a global.
X*/
Xint	make_moves ()
X{
X	reg	char	what;
X	reg	int	quit = FALSE;
X	auto	coord	tmp_pos;
X
X	if (pm_tunn)
X	{	/* in tunnel	*/
X		if (pm_extunn--)
X		{	/* still in tunnel, move over a square	*/
X			switch (ch)
X			{
X				when MLEFT:
X					if (--pm_pos.x < LEFT)
X						pm_pos.x = RIGHT;
X				when MRIGHT:
X					if (++pm_pos.x > RIGHT)
X						pm_pos.x = LEFT;
X				otherwise:
X					msg("case error in tunnel");
X			}
X			/*
X			** check for monsters here!!!!
X			** to see if they have run into any in the tunnel
X			*/
XAbove:	/* sorry about this! */
X			switch (what = tunn_look(&pm_pos))
X			{
X				case EMPTY:
X					return(msg("In and out of tunn"),quit);
X				case TUNNEL:
X					return(quit);
X				case PM:	/* nothing here but me! */
X					return(quit);
X			}
X			if (!is_mons(what))
X				return(msg("found: %s in tunn", punctrl(what)),quit);
X			if (islower(what))  /* we have caught a monster	*/
X				return(pm_eat_m(what), quit);
X			if (isupper(what))
X			{
X				pm_eaten = TRUE;
X				m_eat_pm(wh_mons(what));
X				return(quit);
X			}
X			msg("What was that???");
X		}
X		else
X		{
X			pm_tunn = FALSE;
X			tmp_pos.x = pm_pos.x;
X			tmp_pos.y = pm_pos.y;
X			newch = ch;
X			goto here;
X		}
X	}
X	else
X		mvaddch(pm_pos.y, pm_pos.x, ' ');
X	if (pending())
X	{
X		if (isupper(newch = getchar()))
X			newch = tolower(newch);
X		else if (isdigit(newch))
X			newch = toletter(newch);
X		move_cntr++;
X	}
X	if (newch)
X	{
X		oldch = ch;
X		ch = newch;
X	}
X	if (!ch)
X		return(quit);
Xtop:
X	tmp_pos.x = pm_pos.x;
X	tmp_pos.y = pm_pos.y;
X	switch (moveit(ch, &tmp_pos))
X	{
X		when FINE:
X		when ERROR:
X			ch = oldch;
X			oldch = '\0';
X			newch = '\0';
X			goto top;
X		when QUIT:
X			quit = TRUE;
X	}
Xhere:
X	if (is_safe(&tmp_pos))
X		pm_pos.x = tmp_pos.x, pm_pos.y = tmp_pos.y;
X	else if (pm_eaten)
X		return(0);
X	else if (pm_tunn)
X		goto Above;
X	else
X	{
X#ifdef	USELESS
X/*
X** maybe the next `if' statement should have been an `else if',
X** but until that has been ascertained, this one is useless
X*/
X		if ((newch == ch) && oldch)
X		{
X			ch = oldch;
X			oldch = '\0';
X			goto top;
X		}
X#endif
X		if (oldch)
X		{
X			ch = oldch;
X			oldch = '\0';
X			goto top;
X		}
X	}
X	/*
X	** redraw pm and leave cursor there if not in tunnel
X	*/
X	if (!pm_tunn)
X	{
X		move(pm_pos.y, pm_pos.x);
X		addch(PM);
X		move(pm_pos.y, pm_pos.x);
X	}
X	draw();
X	return(quit);
X}
X
X/*
X** pending()	- return TRUE if the user wants service, else return FALSE
X**		- i realize that this function could be simpler (ex. just
X**		  return((int) l)), but i wanted to minimize lint's complaints
X*/
Xint	pending ()
X{
X#ifdef	FIONREAD
X	auto	long	l;
X
X	if (ioctl(0, FIONREAD, &l) == -1)
X		return(FALSE);
X	return(l > 0 ? TRUE : FALSE);
X#else
X	return(1);
X#endif
X}
X
X/*
X** is_safe()	- returns TRUE if location is safe
X**		- also assumes that move will be made regardless
X**		  of safeness
X*/
Xint	is_safe (where)
Xreg	coord	*where;
X{
X	reg	char	what;
X
X	move(where->y, where->x);
X	what = INCH();
X	move(pm_pos.y, pm_pos.x);
X	switch (what)
X	{
X		case BLOCK:
X		case DOOR:
X			return(FALSE);
X		case TUNNEL:
X			pm_tunn = TRUE;
X			pm_extunn = TUNN_TIME;
X			return(TRUE);
X		case DOT:
X			thescore += V_DOT;
X			d_left--;
X			if (!quiet)
X				beep();
X			return(TRUE);
X		case ENERGY:
X			thescore += V_ENERGY;
X			e_left--;
X			if (!quiet)
X				beep();
X			submissive();
X			return(TRUE);
X		case EMPTY:
X			return(TRUE);
X		case PM:
X			msg("I'm going skitzo");
X			return(FALSE);
X	}
X	if (IS_FRUIT(what))		/* check to see if it is a fruit */
X	{
X		thescore += fr_val;
X		if (!quiet)
X			beep();
X		fr_val = 0;		/* shows fruit has been eaten	*/
X		add_fruit(fr_ch);
X		return(TRUE);
X	}
X#ifdef	DEBUG
X	if (!is_mons(what))
X		return(msg("found a %s in @ 226", punctrl(what)), FALSE);
X#endif
X	if (islower(what))		/* we have caught a monster	*/
X	{
X#ifdef	DEBUG
X		if (pm_run)		/* remove message later on###	*/
X			msg("Eatable, but not running");
X		/*
X		** may need this
X		pm_pos.x = where->x;
X		pm_pos.y = where->y;
X		*/
X#endif
X		return(pm_eat_m(what), TRUE);
X	}
X	pm_eaten = TRUE;
X	pm_pos.x = where->x;
X	pm_pos.y = where->y;
X	m_eat_pm(wh_mons(what));
X	return(FALSE);
X}
X
X/*
X** pm_eat_m()	- the pm ate the m!!!
X**		- the variable flag is used to indicate that the
X**		  monsters (including the one eaten) must become
X**		  submissive (after the eaten one has been initialized)
X*/
Xvoid	pm_eat_m (who)
Xreg	char	who;
X{
X	reg	mons	*m;
X	reg	int	flag = FALSE;
X
X	thescore += mons_val[++mons_eaten];
X	if (mons_eaten == 3)
X	{	/* all the monsters are eaten, reset the timer	*/
X		timer = 0;
X		mons_eaten = -1;
X	}
X	if (!(m = wh_mons(who)))
X	{
X		msg("Lost monster in pm_eat_m()");	
X		return;
X	}
X	switch (m->mo_inch)	/* check what was underneath him*/
X	{
X		when DOT:
X			thescore += V_DOT;
X			d_left--;
X			if (!quiet)
X				beep();
X		when ENERGY:
X			flag = TRUE;
X			thescore += V_ENERGY;
X			e_left--;
X			if (!quiet)
X				beep();
X	}
X	m->mo_name = toupper(who);
X	m_init(m);
X	place_m(m);
X	m->mo_eaten = TRUE;
X	if (flag)
X		submissive();
X}
X
X/*
X** moveit()	- evaluate move and return status
X*/
Xmoveit (what, where)
Xreg	char	what;
Xreg	coord	*where;
X{
X	switch (what)
X	{
X		case MUP:
X			return(where->y--, FINE);
X		case MDOWN:
X			return(where->y++, FINE);
X		case MLEFT:
X			return(where->x--, FINE);
X		case MRIGHT:
X			return(where->x++, FINE);
X		case MSTOP:
X			return(STOP);
X		case MQUIT:
X			return(QUIT);
X		case MREDRAW:
X			return(redraw(), ERROR);
X		case MSHELL:
X			return(shell(), ERROR);
X		case MHELP:
X			return(commands(), ERROR);
X		case MFAST:
X			return(fast = !fast, ERROR);
X		case MQUIET:
X			return(quiet = !quiet, ERROR);
X		case MPAUSE:
X			/*
X			** they are not allowed to pause to examine
X			** the board (for potential moves), so clear
X			** the screen (and go to the bottom) while
X			** they are paused
X			*/
X			if (is_wiz) /* wizard is an exception! */
X				return(trash(getchar()), ERROR);
X			_puts(CL);
X			move(LINES - 1, 0);
X			draw();
X			printf("[Hit return when ready] ");
X			trash(getchar());
X			redraw();
X			return(ERROR);
X		case MHUH:
X			return(re_msg(), ERROR);
X		case MWIZARD:
X			if (is_wiz)
X			{
X				msg("");
X				is_wiz = FALSE;
X				return(ERROR);
X			}
X			msg("Wizard's Password: ");
X			if (!strcmp(W_PASSWD, crypt(get_pass(), SALT)))
X			{
X				was_wiz = TRUE;
X				is_wiz = TRUE;
X				if (getuid() != WIZARD_UID)
X					msg("Are you trying to cheat?");
X				else
X					msg("Hi wiz!");
X			}
X			else
X				msg("Who are you kidding?");
X			return(ERROR);
X		default:
X			if (!is_wiz)
X				return(ERROR);
X	}
X	/*
X	** since they are wizard, lets try some of these
X	*/
X	switch (what)
X	{
X		when MPM:
X			pms_left++, p_pms();
X		when MSLOW:
X			slowness();
X		when MSTATUS:
X			status();
X		when MMONS:
X			p_info(getchar());
X		when MUP_LVL:
X			chg_lvl(1);
X		when MDN_LVL:
X			chg_lvl(-1);
X		when MEAT:
X			submissive();
X		when MMEAN:
X			aggressive();
X	}
X	return(ERROR);
X}
X
X/*
X** commands()	- print a list of the users commands
X**		- erase the screen by hand
X*/
Xvoid	commands ()
X{
X	static	char	*cmds[] =
X	{	"---------------------------------------------------",
X		"|         Movement:         |   Misc:             |",
X		"---------------------------------------------------",
X		"|                           |                     |",
X		"|             k             |    !       shell    |",
X		"|             ^             |    q       quit     |",
X		"|             ^             | <SPACE>    stop     |",
X		"|             ^             |    f       faster   |",
X		"|             ^             |    b       quiet    |",
X		"|   h < < < < * > > > > l   |    p       pause    |",
X		"|             v             |                     |",
X		"|             v             |                     |",
X		"|             v             |                     |",
X		"|             v             |                     |",
X		"|             j             |                     |",
X		"|                           |                     |",
X		"---------------------------------------------------",
X		"\nHit return to continue",
X		0
X	};
X	reg	char	**s = cmds;
X
X	_puts(CL);
X	while (*s)
X		printf("%s\n", *s++);
X	trash(getchar());
X	chcnt = 0l;
X	redraw();
X}
X
X/*
X** status()	- print out a bunch of debugging info
X*/
Xvoid	status ()
X{
X	alarm(0);
X	_puts(CL);
X	move(0, 0);
X	printf("        Diagnostics\n\n");
X	printf("Fruit:             %c\n", fr_ch);
X	printf("Fruit value:       %d\n", fr_val);
X	printf("Level:             %d\n", level);
X	printf("Moves:             %ld\n", move_cntr);
X	printf("Time:              %ld\n", demon);
X	printf("Timeit:            %s\n", (timeit ? "Yes" : "No"));
X	printf("Fast:              %s\n", (fast ? "Yes" : "No"));
X	printf("Beeping:           %s\n", (quiet ? "No" : "Yes"));
X	printf("Dots left:         %d\n", d_left);
X	printf("Energizers left:   %d\n", e_left);
X	printf("Pm's left:         %d\n", pms_left);
X	printf("Time left:         %d\n", timer);
X	printf("Score:             %ld\n", thescore);
X	printf("Pos.:              (%d, %d)\n", pm_pos.x, pm_pos.y);
X	printf("Tunn.:             %s\n",TF(pm_tunn));
X	printf("Baud:              %d\n", bauds[baud]);
X	printf("Screen dimension   %d x %d\n", LINES, COLS);
X	printf("High score:        %ld\n", hi_score);
X	printf("Max's:             %d,%d\n", stdscr->_maxy, stdscr->_maxx);
X	printf("\n");
X	printf("\nHit return to continue\n");
X	trash(getchar());
X	chcnt = 0L;
X	redraw();
X}
END-of-make_moves.c
echo file: misc.c
sed 's/^X//' >misc.c << 'END-of-misc.c'
X/*
X** misc.c -	miscellaneous functions
X**
X**	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X*/
X#include <signal.h>
X#include "pm.h"
X
X
X/*
X** chg_lvl()	- change the level by the desired ammount
X*/
Xvoid	chg_lvl (delta)
Xreg	int	delta;
X{
X	level += delta;
X	if (level < 0)
X		level = 0;
X	ch = ' ';
X	oldch = ' ';
X	newch = ' ';
X	pm_run = TRUE;
X	sleep(1);
X	new_screen();
X}
X
X/*
X** scrcpy()	- copy score structures
X**		- there are more efficient ways todo this, but lint doesn't
X**		  like any of the ones i came up with
X*/
Xvoid	scrcpy (to, from)
Xreg	score	*to, *from;
X{
X	to->sc_uid = from->sc_uid;
X	to->sc_score = from->sc_score;
X	to->sc_level = from->sc_level;
X	to->sc_flags = from->sc_flags;
X	to->sc_mons = from->sc_mons;
X	strncpy(to->sc_name, from->sc_name, sizeof(from->sc_name));
X	return;
X}
X
X/*
X** dir_int()	- changes a char direction indicator to a int
X*/
Xint	dir_int (dir)
Xreg	char	dir;
X{
X	switch (dir)
X	{
X		case MUP:
X			return(0);
X		case MDOWN:
X			return(1);
X		case MLEFT:
X			return(2);
X		case MRIGHT:
X			return(3);
X		default:
X			return(-1);
X	}
X	/*NOTREACHED*/
X}
X
Xstatic	char	*dirs[] =
X{
X	"\n\n\t\tWelcome to the game of pm\n",
X	"Just a few words of information and caution to",
X	"beginning pm players.  This game is very expensive!!!",
X	"It is so expensive that your usercode may not last",
X	"more than a few medium length games.  The arcade",
X	"equivalent of this costs a quarter per game, no",
X	"matter how long it lasts.  With pm it is the other way",
X	"around, the longer you play the more expensive it gets!",
X	"If you are unfamilar with the commands for movement, I",
X	"suggest that you try getting the hang of them by playing",
X	"other games (such as rogue, snake, or tank) that use",
X	"similar commands.  It could get very expensive getting",
X	"the hang of moving around by learning on pm.",
X	"The higher your baud rate, the better the game performs.",
X	"9600 is a pretty good speed to run at.",
X	"For a summary of valid commands, type in a '?'.",
X	"\nHappy packing!!!",
X	"\n[Hit return to continue] ",
X	0
X};
X
X/*
X** directions()	- print out any opening messages to beginners
X*/
Xvoid	directions ()
X{
X	reg	char	**s = dirs;
X
X	while (*s)
X		printf("%s\n", *s++);
X	trash(getchar());
X}
X
X/*
X** get_pass()	- read in the password
X**		- only read in 8 characters!
X*/
Xchar	*get_pass ()
X{
X	static	char	buf[9];
X	reg	int	i;
X
X	nocrmode();
X	for (i = 0; i < 9; i++)
X		buf[i] = '\0';
X	for (i = 0; i < 8; i++)
X	{
X		reg	char	in;
X
X		if ((in = getchar()) == '\n')
X			break;
X		buf[i] = in;
X	}
X	crmode();
X	return(buf);
X}
X
X/*
X** int_dir()	- changes an int to a char direction indicator
X**		- the % insures that it is in range
X*/
Xchar	int_dir (dir)
Xreg	int	dir;
X{
X	static	char	_dirs[] =
X	{
X		MUP, MDOWN, MLEFT, MRIGHT, 0
X	};
X
X	return(_dirs[dir % MAX_DIRS]);
X}
X
X/*
X** lturn()	- return the direction to the left, relative to
X**		  the given direction
X*/
Xchar	lturn (dir)
Xreg	char	dir;
X{
X	switch (dir)
X	{
X		case MUP:
X			return(MLEFT);
X		case MDOWN:
X			return(MRIGHT);
X		case MLEFT:
X			return(MDOWN);
X		case MRIGHT:
X			return(MUP);
X		default:
X			return(MSTOP);
X	}
X	/*NOTREACHED*/
X}
X
X/*
X** mons_str()	- return the (full) name of the given monster
X*/
Xchar	*mons_str (mon)
Xreg	char	mon;
X{
X	switch (mon)
X	{	case HARPO:
X			return("Harpo");
X		case GROUCHO:
X			return("Groucho");
X		case CHICO:
X			return("Chico");
X		case ZEPPO:
X			return("Zeppo");
X		default:
X			return("Anonymous");
X	}
X	/*NOTREACHED*/
X}
X
X/*
X** opposite()	- return the direction opposite to that specified
X*/
Xchar	opposite (dir)
Xreg	char	dir;
X{
X	switch (dir)
X	{
X		case MUP:
X			return(MDOWN);
X		case MDOWN:
X			return(MUP);
X		case MLEFT:
X			return(MRIGHT);
X		case MRIGHT:
X			return(MLEFT);
X		default:
X			return(MSTOP);
X	}
X}
X
X/*
X** rturn()	- return the direction to the right, relative to
X**		  the given direction
X*/
Xchar	rturn (dir)
Xreg	char	dir;
X{
X	switch (dir)
X	{
X		case MUP:
X			return(MRIGHT);
X		case MDOWN:
X			return(MLEFT);
X		case MLEFT:
X			return(MUP);
X		case MRIGHT:
X			return(MDOWN);
X		default:
X			return(MSTOP);
X	}
X}
X
X/*
X** quit_it()	- stop the game
X*/
Xvoid	quit_it ()
X{
X	echo();
X	nocrmode();
X	endwin();
X	exit(0);
X}
X
X/*
X** shell()	- set thier uid to thier realuid and give them a shell
X*/
Xvoid	shell ()
X{
X	reg	char	*sh;
X	reg	int	pid;
X	extern	char	*getenv();
X
X	echo();
X	nocrmode();
X	_puts(CL);		/* clear screen		*/
X	move(LINES - 1, 0);	/* and go to the bottom */
X	draw();
X	if ((sh = getenv("SHELL")) == NULL) /* check for a preferred shell */
X		sh = DEFAULT_SH;
X	if ((pid = fork()) == -1)
X	{
X		fprintf(stderr, "fork failed, by by!\n");
X		quit_it();
X	}
X	if (!pid) /* if child */
X	{
X		if (setuid(uid) == -1) /* incase we are running setuid */
X			exit((fprintf(stderr, "Can't setuid(%d)\n", uid), 1));
X		if (setgid(getgid()) == -1) /* incase we are running setgid */
X			exit((fprintf(stderr,"Can't setgid(%d)\n",getgid()),1));
X#ifndef	LINT
X		signal(SIGINT, SIG_DFL);
X		signal(SIGQUIT, SIG_DFL);
X#endif
X		execle(sh, "shell", "-i", 0, environ);
X		perror("pm");
X		exit(1);
X	}
X#ifndef	LINT
X	signal(SIGINT, SIG_IGN);
X	signal(SIGQUIT, SIG_IGN);
X#endif
X	wait(0);
X	trap(0);		/* reset signals		*/
X	noecho();
X	crmode();
X	printf("[Press return to continue]");
X	trash(getchar());
X	redraw();
X}
X
X/*
X** toletter()	- translate the numeric move (key pad) to a letter move
X**		- this is to facilitate the use of keypads if the
X**		  terminal is so equipped
X**		- return NULL for invalid
X*/
Xchar	toletter (in)
Xreg	char	in;
X{
X	static	char	mvs[] =
X	{
X		NULL, NULL, MDOWN, NULL, MLEFT, MSTOP, MRIGHT, NULL, MUP, NULL
X	};
X
X	return(mvs[(in - '0') % 9]);
X}
X
X/*
X** trap()	- catches signals
X**		- flag is zero for the initial call, non-zero
X**		  when an interrupt is recieved
X*/
Xvoid	trap (flag)
Xreg	int	flag;
X{
X	if (!flag)
X	{
X#ifndef	LINT
X		signal(SIGINT, trap);
X		signal(SIGHUP, trap);
X#endif
X		return;
X	}
X#ifndef	LINT
X	signal(SIGINT, SIG_IGN);
X	signal(SIGHUP, SIG_IGN);
X#endif
X	clear();
X	draw();
X	quit_it();
X}
X
X/*
X** tunn_look()	- return what is at the given location in the tunnel
X*/
Xchar	tunn_look (pos)
Xreg	coord	*pos;
X{
X	reg	int	i;
X
X	for (i = 0; i < MAX_MONS; i++)
X		if (AT(pos, &ghosts[i].mo_pos))
X			return(ghosts[i].mo_name);
X	if (AT(pos, &pm_pos))
X		return(PM);
X	move(pos->y, pos->x);
X	return(INCH());
X}
X
X#ifdef	BAD_OVERLAY
X/*
X** overlay() -	a bug exist[s,ed] in the curses function overlay() on the
X**		4.2 machine on which this program was updated.  this file
X**		is included in case this bug is not just local to this
X**		system.
X**
X*/
X
X/*
X** In the outer `for' loop the "<" should have been a "<=".
X*/
X# define	min(a,b)	(a < b ? a : b)
X# define	max(a,b)	(a > b ? a : b)
X
X/*
X *	This routine writes win1 on win2 non-destructively.
X *
X * 11/5/82 (Berkeley) @(#)overlay.c	1.4
X */
Xoverlay(win1, win2)
Xreg WINDOW	*win1, *win2; {
X
X	reg char	*sp, *end;
X	reg int		x, y, endy, endx, starty, startx;
X
X# ifdef DEBUG
X	fprintf(outf, "OVERLAY(%0.2o, %0.2o);\n", win1, win2);
X# endif
X	starty = max(win1->_begy, win2->_begy) - win1->_begy;
X	startx = max(win1->_begx, win2->_begx) - win1->_begx;
X	endy = min(win1->_maxy, win2->_maxy) - win1->_begy - 1;
X	endx = min(win1->_maxx, win2->_maxx) - win1->_begx - 1;
X/*
X** this is what was erroneously here:
X**
X**	for (y = starty; y < endy; y++) {
X**
X** below you will find the correct code (s/</<=/)
X*/
X	for (y = starty; y <= endy; y++) {
X		end = &win1->_y[y][endx];
X		x = startx + win1->_begx;
X		for (sp = &win1->_y[y][startx]; sp <= end; sp++) {
X			if (!isspace(*sp))
X				mvwaddch(win2, y + win1->_begy, x, *sp);
X			x++;
X		}
X	}
X}
X#endif
END-of-misc.c
echo file: monsters.c
sed 's/^X//' >monsters.c << 'END-of-monsters.c'
X/*
X** monsters.c -
X**  This file contains the necessary functions
X**  to deal with the monsters and how they move.
X**  If the flag variable is TRUE, then they are after
X**  you, else you are after them.
X**
X**
X**	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X*/
X#include "pm.h"
X
X/*
X** harpo	- will try his best to get you, but is slow
X**		- smart, slow
X**
X** groucho  	- is always behind you, it is hard to shake him
X**		- smart, fast
X**
X** chico	- he's fast
X**		- fast
X**
X** zeppo	- terribly shy and will actually run away from you
X**		- medium, dumb
X*/
X
X/*
X** p_monsters()	- place and initialize the monsters for new screens
X*/
Xvoid	p_monsters ()
X{
X	reg	int	i;
X
X	for (i = 0; i < MAX_MONS; i++)
X		m_init(&ghosts[i]);
X	h->mo_pos.x = 26;
X	h->mo_pos.y = 11;
X	g->mo_pos.x = 26;
X	g->mo_pos.y = 9;
X	c->mo_pos.x = 28;
X	c->mo_pos.y = 11;
X	z->mo_pos.x = 24;
X	z->mo_pos.y = 11;
X	h->mo_name = HARPO;
X	g->mo_name = GROUCHO;
X	c->mo_name = CHICO;
X	z->mo_name = ZEPPO;
X	g->mo_inside = FALSE;
X	for (i = 0; i < MAX_MONS; i++)
X	{
X		move(ghosts[i].mo_pos.y, ghosts[i].mo_pos.x);
X		addch(ghosts[i].mo_name);
X	}
X}
X
X/*
X** m_move()	- move the monsters
X**		- if they are eatable, then skip them half the time
X**		- if they are inside, skip them a third of the time
X*/
Xvoid	m_move ()
X{
X	reg	int	i;
X
X	for (i = 0; i < MAX_MONS; i++)
X	{
X		if (ghosts[i].mo_run && (demon % 3l))
X			continue;
X		mv_mon(&ghosts[i]);
X		if (pm_eaten)
X			return;
X	}
X}
X
X/*
X** mv_mon()	- make the given monster move
X**		- maybe change the "mod 5"'s to randomness
X**		- intelligence is simulated by choosing the best
X**		  possible move, and then occasionally doing some-
X**		  thing random.  occasionally is defined relative
X**		  to the intelligence of the monster!
X**		- when a monster is in the tunnel, it continues
X**		  moving in its original direction
X**		- returns FALSE if pm is eaten (everything must stop!)
X*/
Xvoid	mv_mon (m)
Xreg	mons	*m;
X{
X#ifdef	FASTER
X	reg	int	once = FALSE;
X#endif
X
X#ifdef	FASTER
Xtop:
X#endif
X	if (!get_move(m))
X	{
X		m->mo_ch = moves[rnd(0, 3)];
X		m->mo_cnt = 0;
X	}
X	/*
X	** take care of slow monsters
X	*/
X	if ((!(demon % SPEED)) &&
X	    (m->mo_attrib & SLOW) &&
X	    !m->mo_run && !m->mo_tunn)
X		return;
X#ifdef	FASTER
X	/*
X	** take care of fast monsters
X	*/
X	if (!(demon % SPEED) &&
X	    (m->mo_attrib & FAST) &&
X	    !once && !m->mo_run && !m->mo_tunn)
X		once = TRUE;
X	else if (once)
X		once = FALSE;
X#endif
X	/*
X	** take care of dumb monsters
X	** -	25% of time they move randomly, and if they are inside,
X	**	then 50% of the time they move wrong
X	*/
X	if ((m->mo_attrib & DUMB) && !m->mo_tunn)
X	{
X		if (rnd(1,100) < 25)
X		{
X			if ((m->mo_ch = gen_mv(m)) != MSTOP)
X				m->mo_cnt = DIST();
X			else
X				m->mo_cnt = 0;
X		}
X		else if (m->mo_inside && (rnd(1, 100) < 45))
X		{
X			m->mo_ch = opposite(m->mo_ch);
X			m->mo_cnt = 2;
X		}
X	}
X	/*
X	** take care of medium monsters
X	*/
X	else if ((m->mo_attrib & NORMAL) && !m->mo_tunn)
X	{
X		if (rnd(1,100) < 10)
X		{
X			if ((m->mo_ch = gen_mv(m)) != MSTOP)
X				m->mo_cnt = DIST();
X			else
X				m->mo_cnt = 0;
X		}
X		else if (m->mo_inside && (rnd(1, 100) < 37))
X		{
X			m->mo_ch = opposite(m->mo_ch);
X			m->mo_cnt = 2;
X		}
X		else if ((rnd(1, 100) < 15) && (m->mo_ch != MSTOP))
X			m->mo_cnt = rnd(2, 4);
X	}
X	else if (m->mo_attrib & SMART)
X	{
X		if (m->mo_inside && (rnd(1, 100) < 27))
X		{
X			m->mo_ch = opposite(m->mo_ch);
X			m->mo_cnt = 2;
X		}
X	}
X	if (!_mv_mon(m))
X		return;
X#ifdef	FASTER
X	if (once)
X		goto top;
X#endif
X}
X
X/*
X** p_info()	- prints information about the given monster
X*/
Xvoid	p_info (name)
Xreg	char	name;
X{
X	reg	mons	*m;
X
X	m = wh_mons(name);
X	if (m == NULL)
X		msg("No such monster: %s", punctrl(name));
X	_puts(CL);
X	fprintf(stderr, "Name:          %c\n", m->mo_name);
X	fprintf(stderr, "Place:         (%d,%d)\n", m->mo_pos.x, m->mo_pos.y);
X	fprintf(stderr, "Inch:          %c\n", m->mo_inch);
X	fprintf(stderr, "Run:           %s\n", TF(m->mo_run));
X	fprintf(stderr, "Tunn:          %s\n", TF(m->mo_tunn));
X	fprintf(stderr, "Eaten:         %s\n", TF(m->mo_eaten));
X	fprintf(stderr, "Inside:        %s\n", TF(m->mo_inside));
X	fprintf(stderr, "Move:          %c\n", m->mo_ch);
X	fprintf(stderr, "Count:         %d\n", m->mo_cnt);
X	fprintf(stderr, "Attrib:        %o\n\n", m->mo_attrib);
X	trash(getchar());
X	redraw();
X}
X
X/*
X** _mv_mon()	- lower level routine to move a monster
X**		- checks for barriers and stuff...
X**		- returns FALSE if pm is eaten (everything must stop!)
X**		- if in tunnel, it will only move half as fast
X*/
X_mv_mon (m)
Xreg	mons	*m;
X{
X	reg	char	what;
X	auto	coord	pos;
XAbove:
X	if (m->mo_tunn)
X	{
X		if (!(demon % 4))
X		{
X			pos.x = m->mo_pos.x;
X			pos.y = m->mo_pos.y;
X			switch (m->mo_ch)
X			{
X				when MLEFT:
X					if (--pos.x < LEFT)
X						pos.x = RIGHT;
X				when MRIGHT:
X					if (++pos.x > RIGHT)
X						pos.x = LEFT;
X				otherwise:
X					msg("Bad move in tunn");
X			}
X		}
X		else	/* not moving this time around	*/
X			return(TRUE);
X		switch (what = tunn_look(&pos))
X		{
X			case PM:
X				m->mo_pos.x = pos.x;
X				m->mo_pos.y = pos.y;
X				if (OUTOFTUNN(&m->mo_pos))
X					m->mo_tunn = FALSE;
X				if (m->mo_run)
X				{
X					pm_eat_m(m->mo_name);
X					return(FALSE);
X				}
X				m_eat_pm(m);
X				return(FALSE);
X			case EMPTY:
X				if (!OUTOFTUNN(&pos))
X					msg("Lost out of tunn");
X				m->mo_tunn = FALSE;
X				goto ok;
X			case TUNNEL:
X				m->mo_pos.x = pos.x;
X				m->mo_pos.y = pos.y;
X				m->mo_inch = what;
X				return(TRUE);
X			default:
X				/*
X				** ran into another monster?
X				*/
X				m->mo_ch = opposite(m->mo_ch);
X				msg("Ran into another monster");
X				return(TRUE);
X		}
X	}
X	else
X	{
X		if (move_to(m->mo_ch, &m->mo_pos, &pos) == -1)
X			return(-1);
X		move(pos.y, pos.x);
X		what = INCH();
X	}
X	switch (what)
X	{
X		when PM:
X			mvaddch(m->mo_pos.y, m->mo_pos.x, m->mo_inch);
X			draw();
X			m->mo_pos.x = pos.x;
X			m->mo_pos.y = pos.y;
X			if (m->mo_run)
X				return(pm_eat_m(m->mo_name), FALSE);
X			mvaddch(pos.y, pos.x, m->mo_name);
X			draw();
X			m->mo_inch = EMPTY;
X			m_eat_pm(m);
X			return(FALSE);
X		when BLOCK:
X			m->mo_cnt = 0;
X		when DOT:
X		case ENERGY:
X		case EMPTY:
Xok:
X			mvaddch(m->mo_pos.y, m->mo_pos.x, m->mo_inch);
X#ifdef	MAX_UPDATE
X			draw();
X#endif
X			mvaddch(pos.y, pos.x, m->mo_name);
X			m->mo_pos.x = pos.x;
X			m->mo_pos.y = pos.y;
X			m->mo_inch = what;
X		when DOOR:
X			if (!m->mo_inside)	/*  can only exit, not enter */
X			{
X				m->mo_cnt = 0;
X				break;
X			}
X			mvaddch(m->mo_pos.y, m->mo_pos.x, m->mo_inch);
X#ifdef	MAX_UPDATE
X			draw();
X#endif
X			mvaddch(pos.y, pos.x, m->mo_name);
X			m->mo_pos.x = pos.x;
X			m->mo_pos.y = pos.y;
X			m->mo_inch = what;
X			m->mo_inside = FALSE;
X		when TUNNEL:
X			if (tunn_look(&pos) != TUNNEL)
X				break;
X			mvaddch(m->mo_pos.y, m->mo_pos.x, m->mo_inch);
X			m->mo_inch = TUNNEL;
X			m->mo_tunn = TRUE;
X			m->mo_pos.x = pos.x;
X			m->mo_pos.y = pos.y;
X			goto Above;
X		otherwise:
X			if (IS_FRUIT(what))
X				goto ok;
X			if (!is_mons(what))
X			{
X				msg("_mv_mon(): default");
X				break;
X			}
X			m->mo_cnt = 0;
X	}
X	draw();
X	return(TRUE);
X}
X
X/*
X** get_move()	- find a smart move for the given monster
X**		- return FALSE if none was determined
X*/
Xint	get_move (m)
Xreg	mons	*m;
X{
X	reg	int	i, dist = 0;
X	auto	char	tmp;
X
X	if (m->mo_cnt > 0)
X		m->mo_cnt--;
X	/*
X	** must always move up when on the door!!!
X	*/
X	if (m->mo_inch == DOOR)
X	{
X		m->mo_ch = MUP;
X		m->mo_cnt = 0;
X		return(TRUE);
X	}
X	if (m->mo_tunn)
X	{	/* if in tunnel, keep moving	*/
X#ifdef	DEBUG
X		if (!m->mo_ch)
X			msg("m's in tunn, & mo_ch is null");
X#endif
X		return(TRUE);
X	}
X	if (m->mo_inside)
X	{
X		/*
X		** still inside thier little cave (box)
X		** make them move to space under the door if they
X		** are not there already
X		*/
X		if (m->mo_pos.x > DOOR_COL)
X		{
X			m->mo_ch = MLEFT;
X			m->mo_cnt = 1;
X			return(TRUE);
X		}
X		if (m->mo_pos.x < DOOR_COL)
X		{
X			m->mo_ch = MRIGHT;
X			m->mo_cnt = 1;
X			return(TRUE);
X		}
X		/*
X		** they're under the door, try to move up!
X		*/
X		m->mo_ch = MUP;
X		m->mo_cnt = 1;
X		return(TRUE);
X	}
X	tmp = m->mo_ch;
X	for (i = 0; i < 4; i++)
X	{
X		m->mo_ch = int_dir(i);
X		if (dist = can_see(m, &pm_pos))
X			break;
X	}
X	if (m->mo_run && dist)	/* running away from him		*/
X	{
X		m->mo_cnt = 0;
X		if (m_is_safe(m, opposite(m->mo_ch)))
X			return(m->mo_ch = opposite(m->mo_ch), TRUE);
X		m->mo_ch = tmp;
X		if ((m->mo_ch = gen_mv(m)) != MSTOP)
X			return(TRUE);
X		m->mo_ch = (rnd(0, 1) ? lturn(m->mo_ch) : rturn(m->mo_ch));
X		return(FALSE);
X	}
X	if (dist)
X		return(m->mo_cnt = dist, TRUE);
X	m->mo_ch = tmp;
X	if (m->mo_cnt)	/* check for predetermination		*/
X		return(TRUE);
X	m->mo_cnt = 0;
X	if ((m->mo_ch = gen_mv(m)) != MSTOP)
X		return(TRUE);
X	m->mo_ch = tmp;
X	if (m_is_safe(m, m->mo_ch))
X		return(TRUE);
X	return(FALSE);
X}
X
X/*
X** gen_mv()	- generate a move when no hueristics are available
X**		- turn 80% of the time, 50% of that left, 50% right
X**		- 20% of the time just keep going
X*/
Xchar	gen_mv (m)
Xreg	mons	*m;
X{
X	if (rnd(0, 99)<40)
X	{
X		if (m_is_safe(m, rturn(m->mo_ch)))
X			return(rturn(m->mo_ch));
X		if (m_is_safe(m, lturn(m->mo_ch)))
X			return(lturn(m->mo_ch));
X		return(MSTOP);
X	}
X	if (rnd(0, 59) < 40)
X	{
X		if (m_is_safe(m, lturn(m->mo_ch)))
X			return(lturn(m->mo_ch));
X		if (m_is_safe(m, rturn(m->mo_ch)))
X			return(rturn(m->mo_ch));
X		return(MSTOP);
X	}
X	if (m_is_safe(m, m->mo_ch))
X		return(m->mo_ch);
X	if (rnd(0, 1))
X	{
X		if (m_is_safe(m, rturn(m->mo_ch)))
X			return(rturn(m->mo_ch));
X		if (m_is_safe(m, lturn(m->mo_ch)))
X			return(lturn(m->mo_ch));
X		return(MSTOP);
X	}
X	if (m_is_safe(m, lturn(m->mo_ch)))
X		return(lturn(m->mo_ch));
X	if (m_is_safe(m, rturn(m->mo_ch)))
X		return(rturn(m->mo_ch));
X	return(MSTOP);
X}
X
X/*
X** m_is_safe()	- return TRUE if monster can move in the indicated
X**		  direction
X*/
Xint	m_is_safe (m, dir)
Xreg	mons	*m;
Xreg	char	dir;
X{
X	reg	char	what;
X	auto	coord	pos;
X
X	if (move_to(dir, &m->mo_pos, &pos))
X		return(FALSE);
X	move(pos.y, pos.x);
X	switch (what = INCH())
X	{
X		case HARPO:
X		case _HARPO:
X		case GROUCHO:
X		case _GROUCHO:
X		case CHICO:
X		case _CHICO:
X		case ZEPPO:
X		case _ZEPPO:
X		case BLOCK:
X		case DOOR:
X			return(FALSE);
X		case DOT:
X		case ENERGY:
X		case EMPTY:
X		case TUNNEL:
X			return(TRUE);
X		case PM:
X			if (m->mo_run)
X				return(FALSE);
X			return(TRUE);
X		default:
X			if (IS_FRUIT(what))
X				return(TRUE);
X			msg("m_is_safe default");
X			return(FALSE);
X	}
X}
X
X/*
X** can_see()	- returns distance the monster is from the pm if
X**		  the pm can be seen
X**		- look in the direction the monster is facing
X**		- special case for when pm is in tunnel, then the
X**		  monster must be facing the tunnel
X*/
Xint	can_see (m, pos)
Xreg	mons	*m;
Xreg	coord	*pos;
X{
X	auto	int	dist = 0;
X	auto	coord	mv;
X
X	switch (m->mo_ch)
X	{
X		when MUP:
X			if (m->mo_pos.x != pos->x)	/* not in line	*/
X				return(FALSE);
X			if ((m->mo_pos.y == TUNN_ROW) && pm_tunn)
X				break;
X			if (m->mo_pos.y < pos->y)	/* not facing	*/
X				return(FALSE);
X		when MDOWN:
X			if (m->mo_pos.x != pos->x)	/* not in line	*/
X				return(FALSE);
X			if ((m->mo_pos.y == TUNN_ROW) && pm_tunn)
X				break;
X			if (m->mo_pos.y > pos->y)	/* not facing	*/
X				return(FALSE);
X		when MLEFT:
X			if (m->mo_pos.y != pos->y)	/* not in line	*/
X				return(FALSE);
X			if ((m->mo_pos.y == TUNN_ROW) && pm_tunn)
X				break;
X			if (m->mo_pos.x < pos->x)	/* not facing	*/
X				return(FALSE);
X		when MRIGHT:
X			if (m->mo_pos.y != pos->y)	/* not in line	*/
X				return(FALSE);
X			if ((m->mo_pos.y == TUNN_ROW) && pm_tunn)
X				break;
X			if (m->mo_pos.x > pos->x)	/* not facing	*/
X				return(FALSE);
X		otherwise:
X			msg("default in can_see: \%03o", m->mo_ch);
X			return(FALSE);
X	}
X	/*
X	** at this point, they are in direct line and
X	** facing in the right direction, we need to
X	** see if anything is in the way
X	*/
X	mv.x = m->mo_pos.x;
X	mv.y = m->mo_pos.y;
X	do
X	{
X		reg	char	what;
X			
X		if (move_to(m->mo_ch, &mv, &mv) == -1)
X			return(msg("move_to=-1"), FALSE);
X		move(mv.y, mv.x);
X		switch (what = INCH())
X		{
X			case PM:
X			case HARPO:
X			case _HARPO:
X			case GROUCHO:
X			case _GROUCHO:
X			case CHICO:
X			case _CHICO:
X			case ZEPPO:
X			case _ZEPPO:
X			case DOT:
X			case ENERGY:
X			case DOOR:
X			case EMPTY:
X				break;
X			case TUNNEL:
X				if (pm_tunn)
X					return(dist);
X				else
X					msg("can_see(): what???");
X			case BLOCK:
X				return(0);
X			default:
X				if (IS_FRUIT(what))	/* its the fruit */
X					break;
X				msg("can_see(): case (2)");
X				return(0);
X		}
X		dist++;
X	} while ((mv.x != pos->x) || (mv.y != pos->y));
X	return(dist);
X}
X
X/*
X** move_to()	-
X*/
Xint	move_to (dir, pos1, pos2)
Xreg	int	dir;			/* declared int to pacify lint	*/
Xreg	coord	*pos1, *pos2;
X{
X	static	int	offset[2][4] =
X	{	{0, 0, -1, 1},
X		{-1, 1, 0, 0}
X	};
X
X	if ((dir = dir_int((char) dir)) == -1)
X		return(-1);
X	pos2->x = pos1->x + offset[0][dir];
X	pos2->y = pos1->y + offset[1][dir];
X	return(0);
X}
X
X/*
X** wh_mons()	- return pointer to ch's monster struct
X*/
Xmons	*wh_mons (mch)
Xreg	char	mch;
X{
X	switch (mch)
X	{
X		case HARPO:
X		case _HARPO:
X			return(h);
X		case GROUCHO:
X		case _GROUCHO:
X			return(g);
X		case CHICO:
X		case _CHICO:
X			return(c);
X		case ZEPPO:
X		case _ZEPPO:
X			return(z);
X		default:
X			msg("Unknown monster!!!");
X			return((mons *) NULL);
X	}
X}
X
X/*
X** m_eat_pm()	- a monster has eaten the pm
X*/
Xvoid	m_eat_pm (m)
Xreg	mons	*m;
X{
X	eat_pm();
X	sleep(1);
X	if (!--pms_left)	/* if no more pm's then quit immediately */
X		die(m->mo_name);
X	flush();
X	old_screen();
X	pm_eaten = FALSE;
X	ch = ' ';
X	oldch = '\0';
X	newch = '\0';
X}
X
X/*
X** eat_pm()	- make the pm look eaten
X*/
Xvoid	eat_pm ()
X{
X	reg	int	i;
X
X	if (pm_tunn)
X		return;
X	for (i = 0; i < 5; i++)
X	{
X		mvaddch(pm_pos.y, pm_pos.x, 'O');
X		draw();
X		msleep(EAT_PAUSE);
X		mvaddch(pm_pos.y, pm_pos.x, '=');
X		draw();
X		msleep(EAT_PAUSE);
X		mvaddch(pm_pos.y, pm_pos.x, 'O');
X		draw();
X		msleep(EAT_PAUSE);
X		mvaddch(pm_pos.y, pm_pos.x, '=');
X		draw();
X		msleep(EAT_PAUSE);
X	}
X	mvaddch(pm_pos.y, pm_pos.x, EMPTY);
X	draw();
X	sleep(2);
X}
X
X/*
X** is_mons()	- return TRUE if ch is a monster
X*/
Xint	is_mons (whoru)
Xreg	char	whoru;
X{
X	switch (whoru)
X	{
X		case HARPO:
X		case _HARPO:
X		case GROUCHO:
X		case _GROUCHO:
X		case CHICO:
X		case _CHICO:
X		case ZEPPO:
X		case _ZEPPO:
X			return(TRUE);
X	}
X	return(FALSE);
X}
X
X/*
X** place_m()	- put a monster back in its box
X**		- an infinite loop can occur if the box is full!
X**		- the box should never be full!!!
X*/
Xvoid	place_m (m)
Xreg	mons	*m;
X{
X	reg	int	xx;
X
X	m->mo_pos.y = 11;
X	while (TRUE)
X	{
X		move(m->mo_pos.y, (xx = rnd(19, 33)));
X		if (INCH() == EMPTY)
X			break;
X	}
X	m->mo_pos.x = xx;
X	move(m->mo_pos.y, m->mo_pos.x);
X	addch(m->mo_name);
X}
END-of-monsters.c
echo file: msg.c
sed 's/^X//' >msg.c << 'END-of-msg.c'
X/*
X** msg.c -	code dealing with the printing of messages
X**
X**	[pm by Peter Costantinidis, Jr. @ University of California at Davis]
X*/
X#include <signal.h>
X#include "pm.h"
X
X/*
X** strucpy()	- copy string using punctrl for things
X**		- ctrl chars count double
X*/
Xvoid	strucpy (s1, s2, len)
Xreg	char	*s1, *s2;
Xreg	int	len;
X{
X	reg	char	*sp;
X
X	while (len-- && *s2)
X	{
X		if ((*s2 < ' ') && !len--) /* if len = 0, then no room */
X			return;
X		strcpy(s1, sp = punctrl(*s2++));
X		s1 += strlen(sp);
X	}
X	*s1 = '\0';
X}
X
X/*
X** msg:
X**	Display a message on the screen.
X*/
Xstatic char msgbuf[BUFSIZ];
X
X/*VARARGS1*/
Xvoid	msg (fmt, args)
Xchar	*fmt;
Xint	args;
X{
X	alarm(0);
X	/*
X	** if the string is "", just clear the line
X	*/
X	if (*fmt == '\0')
X	{
X		move(5, 55);
X		clrtoeol();
X		return;
X	}
X	/*
X	** otherwise print the message and flush it out
X	*/
X	doadd(fmt, &args);
X	move(5, 55);
X	addstr(msgbuf);
X	clrtoeol();
X	refresh();
X	/*
X	** set off an alarm to erase it
X	*/
X#ifndef	LINT
X	signal(SIGALRM, msg_erase);
X#endif
X	alarm(ALARM_TIME);
X}
X
X/*
X** msg_erase()	- erase the msg line
X*/
Xvoid	msg_erase ()
X{
X	alarm(0);
X	move(5, 55);
X	addstr("                       ");
X}
X
Xvoid	doadd (fmt, args)
Xchar	**fmt;
Xint	***args;
X{
X	static	FILE	junk;
X
X	/*
X	** Do the printf into buf
X	*/
X	junk._flag = _IOWRT + _IOSTRG;
X	junk._ptr = &msgbuf[0];
X	junk._cnt = 32767;
X	_doprnt(fmt, args, &junk);
X	putc('\0', &junk);
X}
X
X/*
X** re_msg()	- reprint the last message
X*/
Xvoid	re_msg ()
X{
X	msg(msgbuf);
X}
X
X/*
X** punctrl()	- print a readable version of a certain character
X**
X**	Note:	Due to the inconsistent availability of a function to perform
X**		this, my own version has been built in and used in place of
X**		any pre-existing function.  I believe that this particular
X**		version suts down on data space considerably from the versions
X**		I have found on the Berkley systems.
X*/
Xchar	*punctrl (chr)
Xchar	chr;
X{
X	static	char	*str = "^ ";
X
X	chr &= 0177;
X	if (chr >= ' ' && chr <= '~')
X	{
X		static	char	*str1 = " ";
X
X		*str1 = chr;
X		return(str1);
X	}
X	if (chr == CTRL(?))
X		return("^?");
X	*(str+1) = chr + '@';
X	return(str);
X}
END-of-msg.c
exit
-- 
-- Peter Costantinidis, Jr.
-- ucdavis!deneb!ccohesh001@ucb-vax.arpa		(ARPA)
-- ...!{ucbvax,lll-crg,dual}!ucdavis!deneb!ccohesh001	(UUCP)