[net.sources.games] System III/V PacMan

safern@aecom.UUCP (Eric Safern) (01/10/86)

Hello,
	I have received enough requests for this program to warrant, in 
my judgement, a new posting.  I will do this next week as soon as I test
Pacman on a system V machine.

-- 
	  Eric Safern
	  ...{ihnp4,rocky2,philabs,esquire,cucard,pegasus,spike}!aecom!safern

safern@aecom2.UUCP (Eric Safern) (03/14/86)

Hello,

        The following is a public domain, System V based  version
of  pacman.  It is based upon a Berkeley version of the same game
posted a few months ago.  I  promised  to  post  this  in  mid  -
January,  when  I  finished it, but for the past two months I was
unable to post due to problems involving  a  transfer  to  a  new
computer.   Anyway, here it is, in two parts.  It has been tested
on Interactive System  III,  System  V/68  and  ATT  System  5.2.
Hopefully it will work an all system V and III machines, and with 
a  minimum  of  work, on 4.X Berkeley.  The last I'm not too sure
about.  I never tried it on a  Berkeley  system,  so  I  couldn't
really say.  

        There are still some small problems.  Firstly,  sometimes
when  someone plays for the first time, it sits there for five or
ten seconds before accepting commands.  This only happens once in 
a while, and only when the person gets put in the user  file  for
the  first  time.   I have had some problems with turning on echo
for the shell escape.  I fixed this on system III by changing the 
curses library routine echo until it worked.  I could not do this 
on system V because I couldn't get  at  the  sources  for  curses
under  system  V.   Finally,  I  elected  to  not  post  the file
"flsbuf.c" included in  the  earlier  posting  of  pm,  since  it
contains   proprietary   unix   source   for  which  I  signed  a
non-disclosure  agreement.   The  code  in   flsbuf.c   contained
_flsbuf(c,  iop) from the stdio library, with a small change made
to count the number of characters  printed.   This  was  done  to
allow  for  a  delay  after printing characters, to make the game
smoother.  If you compile the game and find it too `choppy', take 
the routine _flsbuf from the library, place it in  flsbuf.c,  and
try  to  modify it to increment chcnt for each character printed.
If you don't know how  to  do  this,  find  an  original  posting
containing  the  proprietary code.  You may have some problems on
system V, with the  linker  complaining  about  multiply  defined
procedures.   I  just  left  flsbuf.c  out entirely, and it works
fine.  Don't forget to move  tester  to  pm  before  you  put  it
somewhere  permanently,  as  it  is  in  root mode if the game is
called tester.  

        Thanks to the original author, Peter  Costantinidis,  Jr.
who  wrote  the  Berkeley  version.   This is a pretty good game,
quite well written.  The biggest problem I  had  with  it  (other
than the numerous berkeley dependencies) was the non-intelligence 
of  the  monsters.  They are designed to only spot you if you are
in their `line of sight'.  If you simply stay out of  their  way,
they  will  simply  wander aimlessly.  It seems to me that in the
arcade version, they know where you are on  the  board,  line  of
sight  or  not.   Please  send  me  a  copy  of  any  bug  fixes,
suggestions, etc.  that you may have.  
-- 
	  Eric Safern
	  ...{ihnp4,rocky2,philabs,esquire,cucard,pegasus,spike}!aecom!safern

safern@aecom2.UUCP (Eric Safern) (03/14/86)

#!/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 Mar 14 01:19:33 EST 1986 "
echo file: READ_ME
sed 's/^X//' >READ_ME << 'END-of-READ_ME'
XThis distribution is a System III/V version of pacman.
XAll comments, etc. concerning this game should be directed to
X
XEric Safern
X...{ihnp4,rocky2,philabs,esquire,cucard,pegasus}!aecom!safern
X
XHere is the readme from earlier distributions:
X
X
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, SYSIII, SYSV)
X#              SEND    -       pathname of binary export directory
X#
X#      Please note: on system 5.2 and any systems whose curses does not
X#      use termcap, you must comment out TERMLIB.
X#
X#      [pm by Peter Costantinidis, Jr. @ University of California at Davis]
X#
X
X#
X# Files
X#
XHDRS    =       pm.h
XOBJS    =       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#
X# Configuration section
X#
XPM_ROLL	=       /y1/safern/bin/lib/pm_roll
XPM_USER =       /y1/safern/bin/lib/pm_user
XOS_VERS =       SYSV
XDEFS	=       -DPM_ROLL=\"$(PM_ROLL)\" -DPM_USER=\"$(PM_USER)\" \
X		-DWIZARD_UID=283
XDEST	=       /y3/safern/bin/lib/xhesh
X
X#
X# Flags section
X#
XCFLAGS  =       -O -D$(OS_VERS)
XLDFLAGS =       -o tester
XLINTARGS =      -ahxc $(DEFS) -DLINT
XCRLIB	=       -lcurses
XTERMLIB	=       -ltermcap     # on ATT 5.2, this line must be commented out.
X
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:         config.o $(OBJS)
X		-mv tester tester.old
X		cc $(LDFLAGS) $(OBJS) config.o $(CRLIB) $(TERMLIB)
X		chmod 04711 tester
X
Xconfig.o:       $(HDRS)
X		cc $(CFLAGS) $(DEFS) -c config.c
X
Xinstall:	pm pm_roll pm_user clean
X
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 $? | print
X		-touch Print
X
XP_all:
X		pr $(HDRS) $(SRCS) | print
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
X#if BSD42|BSD43|BSD29
X# define       BAD_OVERLAY             /* bug in curses (overlay.c)            */
X#endif
X
X#if SYSIII|SYSV
X# define       ECHOBUG                 /* bug in curses (ttyctl.c)             */
X#endif			/* if you haven't got this problem, remove this */
X
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
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/*
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        "LuWBlzHTlqQ0g"
X#define SALT            "Lu"
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#if SYSV|SYSIII
X# define       EAT_PAUSE       20      /* used when pm is eaten */
X#else
X# define EAT_PAUSE  ((unss) 20)        /* used when pm is eaten */
X#endif
X
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 source more terse
X*/
X#define        when            break; case
X#define otherwise      break; default
X
X#ifdef PATTERNS
X# define       SEED            (uid+level)     /* their uid */
X#else
X# define       SEED            get_seed()      /* their 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#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/*
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
X#ifdef SYSV
X# define       doclear()       refresh(), wclear(cls), wrefresh(cls);
X#else
X# define       doclear()       _puts(CL);
X#endif
X
X#ifdef ECHOBUG
X# define       Echo()          Necho()
Xextern void    Necho();
X#else
X# define       Echo()          echo()
X#endif
X
X#if SYSV|SYSIII
X# include <fcntl.h>
Xextern  int oldfl, baud;
Xextern  long   _tp;
Xextern  struct  tbuffer garbage;
X# define       draw()          _tp = times(&garbage), refresh(), delay()
X#else
Xextern  int bauds[];    /* this is a character array on SYS3/V */
Xextern  char baud;
Xextern  struct  timeb   _tp;
X# define       draw()          ftime(&_tp), refresh(), delay()
X#endif
X
X#ifdef SYSV
Xextern WINDOW  *cls;            /* to clear the screen */
X#endif
Xextern coord   pm_pos;
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, wizard_uid;
Xextern char    *argv0, fruit[], fruit_eaten[], fr_ch, ch, oldch,
X               newch, moves[], *mesg, **environ, *pm_user, *pm_roll;
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
XSystem III, V port by Eric Safern, Yeshiva University, N.Y.
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 ": \"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       for (;;)
X       {
X	       slow();
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
X#if SYSV|SYSIII
X       fcntl(0, F_SETFL, oldfl);
X#endif
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       doclear();
X       tombstone(thescore, mon);
X       printf("[Press return to continue]");
X#if SYSV|SYSIII
X       fcntl(0, F_SETFL, oldfl);
X#endif
X       endwin();
X       trash(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       refresh();
X#if SYSV|SYSIII
X       fcntl(0, F_SETFL, oldfl);
X#endif
X       printf("[Press return to continue]");
X       endwin();
X       trash(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
X#ifdef SYSIII
X# include <sys/ioctl.h>
X#endif SYSIII
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 I 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/*
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
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 screen%c",
X               rank, scr->sc_score, scr->sc_name,
X               reason[scr->sc_flags], scr->sc_level+1, (scr->sc_level ? 's' : ' '));
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("\nYou made it on the pm roll with ");
X               printf("your score of %ld!\n", thescore);
X               printf("Your name please: ");
X#if SYSV|SYSIII                                        /* doesn't seem to help much */
X               ioctl(fileno(stdin), TCFLSH, 0);        /* flush input */
X#endif
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       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}
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/*
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}
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**
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#if !SYSV && !SYSIII
X       flush();        /* this totally destroys crmode on System III/V */
X#endif
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#if !SYSV && !SYSIII
X       flush();        /* this totally destroys crmode on System III/V */
X#endif
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)) == (WINDOW *) 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)  ((1000 * x * BPBYTE) / baud)
X#define TIMEBS(x,y) ((x - y) * 100 / 6)
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	int     u;
X	auto    long tp;
X
X	u = PAWS(chcnt);
X	for(;;) {
X		tp = times(&garbage);
X		if (TIMEBS(tp, _tp) >= u) {
X			chcnt = 0L;
X			return;
X		}
X	}
X}
X
Xstatic	int	rates[] =	/* these were `tuned' after much playing */
X{
X/*        0    1    2    3    4    5    6    7    8    9	*/
X	320, 265, 220, 210, 200, 190, 180, 170, 160, 150,
X/*       10   11   12   13   14   15   16   17   18   19	*/
X	140, 130, 120, 110, 100, 100, 100,  90,  90,  80,
X/*       20   21   22   23   24   25   26   27   28   29	*/
X	 95,  70,  45,  20, 100, 150,  50,  99, 100, 100,
X/*       30   31   32   33   34   35   36   37   38   39	*/
X	  5,   0,   0,   0,   0,  20,   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**                60 LEVELS!!!!  if they are, then they have been
X**                tying up the computer long enough and should stop
X**                playing anyway
X*/
Xstatic  long    tp;
X
Xvoid    initslow()
X{
X	tp = times(&garbage);
X}
X
Xvoid    slow()
X{
X	reg     int     ms, num;
X	auto    long    tp2;
X
X	if (level > 59)
X		quitit();
X	num = (fast ? rates[level]/4 : rates[level]/2);
X	for(;;) {
X		tp2 = times(&garbage);
X		if (TIMEBS(tp2, tp) >= num) {
X			tp = tp2;
X			return;
X		}
X	}
X}
X
X/*
X** slowness()   - sets delay in rates
X**
X*/
Xvoid	slowness ()
X{
X	auto	char	buf[BUFSIZ];
X
X	doclear();
X	nocrmode();
X	printf("old delay: %d, new delay: ", rates[level]);
X#if SYSV|SYSIII
X	fcntl(0, F_SETFL, oldfl);
X#endif
X	Echo();        /* defined to echo() on machines without bug */
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#if SYSV|SYSIII
X	fcntl(0, F_SETFL, O_NDELAY);
X#endif
X	noecho();
X	crmode();
X	redraw();
X}
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
-- 
	  Eric Safern
	  ...{ihnp4,rocky2,philabs,esquire,cucard,pegasus,spike}!aecom!safern

safern@aecom2.UUCP (Eric Safern) (03/14/86)

#!/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 Mar 14 01:19:51 EST 1986 "
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 WIZARD_UID
Xint    wizard_uid = WIZARD_UID;
X#else
Xint    wizard_uid = -1;
X#endif
END-of-config.c
echo file: flsbuf.c
sed 's/^X//' >flsbuf.c << 'END-of-flsbuf.c'
X/* empty file */
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
X#include "pm.h"
X
X#if SYSV|SYSIII
X
X# ifdef SYSIII
X# include <sys/ioctl.h>
X# endif SYSIII
X
X# ifdef SYSV
XWINDOW *cls;
X# endif SYSV
X
Xstruct termio tty;
Xunss   btmp = 0;               /* temporary variable for symbolic baud */
Xint    oldfl, baud = 0;
Xlong   _tp;                    /* used for timing                      */
Xstruct tbuffer {
X       long utime;
X       long stime;
X       long cutime;
X       long cstime;
X} garbage;
X#else
Xstruct timeb   _tp;            /* used for timing                      */
X#endif
Xcoord  pm_pos;
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
X#if !SYSV && !SYSIII
X       baud = '\0',            /* output baud rate of terminal         */
X#endif
X
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       100, 95, 90, 75, 90, 85, 85, 85, 75, 70, 65, 55, 45
X};
X
X#if !SYSV && !SYSIII
Xint    bauds[] =
X{
X   0, 0, 0,  0,  0,  0,  0,  0,  0, 1200, 1800, 2400, 4800, 9600, 19200,   0
X};
X#endif
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       initslow();       /* set the time for the first call to slow */
X
X       if (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
X#if SYSV|SYSIII
X       oldfl = fcntl(0, F_GETFL);
X       fcntl(0, F_SETFL, O_NDELAY);
X#endif
X
X       hi_score = get_hi_scr();
X       if (initscr() == (WINDOW *) ERR)
X       {
X               fprintf(stderr, "initscr() error\n");
X               perror(argv0);
X
X#if SYSV|SYSIII
X              fcntl(0, F_SETFL, oldfl);
X#endif
X
X               exit(1);
X       }
X# ifdef SYSV
X       cls = newwin(0, 0, 0, 0);
X# endif SYSV
X       if (!baud)      /* if baud, then we are trying to simulate another */
X       {
X
X#if SYSV|SYSIII
X              if ((ioctl(fileno(stdout), TCGETA, &tty)) == -1) {
X                       perror("pm: ");
X                       endwin();
X                       fcntl(0, F_SETFL, oldfl);
X                       exit(1);
X              }
X              switch(btmp = (tty.c_cflag & CBAUD)) {
X                      case B0:
X                      case B50:
X                      case B75:
X                      case B110:
X                      case B134:
X                      case B150:
X                      case B200:
X                      case B300:
X                      case B600:
X                               fprintf(stderr, "pm: baud rate must be at least %d\n", MIN_BAUD);
X                               endwin();
X                               fcntl(0, F_SETFL, oldfl);
X                               exit(1);
X                      case B1200:
X                               baud = 1200;
X                               break;
X                      case B1800:
X                               baud = 1800;
X                               break;
X                      case B2400:
X                               baud = 2400;
X                               break;
X                      case B4800:
X                               baud = 4800;
X                               break;
X                      case B9600:
X                               baud = 9600;
X                               break;
X              }
X
X#else
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#endif
X
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#ifdef SYSIII
X# include <sys/ioctl.h>
X#endif SYSIII
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 !SYSV && !SYSIII
X              newch = getchar();
X#endif
X               if (isupper(newch))
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 *               on SYSTEM V, actually does a read into newch if possible
X */
Xint    pending ()
X{
X
X#if !SYSV && !SYSIII
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(read(0, &newch, 1));
X
X/*
X *      This must be done elsewhere, otherwise the read hangs.
X *      if this is done, the read returns a zero if nothing pending,
X *      if there is a char, it's put in ch and a one is returned.
X *      oldfl=(fcntl(0, F_GETFL));
X *      fcntl(0, F_SETFL, O_NDELAY);
X *
X *      Also, in die, include this:
X *      fcntl(0, F_SETFL, oldfl);
X */
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       if (!quiet)
X              beep();
X       switch (m->mo_inch)     /* check what was underneath him*/
X       {
X               when DOT:
X                       thescore += V_DOT;
X                       d_left--;
X               when ENERGY:
X                       flag = TRUE;
X                       thescore += V_ENERGY;
X                       e_left--;
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 SYSV|SYSIII
X		       fcntl(0, F_SETFL, oldfl);
X#endif
X		       if (is_wiz) { /* wizard is an exception! */
X                              trash(getchar());
X#if SYSV|SYSIII
X                              fcntl(0, F_SETFL, O_NDELAY);
X#endif
X                              return(ERROR);
X		       }
X                       move(LINES - 1, 0);
X                       draw();
X		       doclear();
X                       printf("[Press return to continue]");
X                       trash(getchar());
X                       redraw();
X#if SYSV|SYSIII
X       		       fcntl(0, F_SETFL, O_NDELAY);
X#endif
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#if SYSV|SYSIII
X       	               fcntl(0, F_SETFL, oldfl);
X#endif
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#if SYSV|SYSIII
X       		       fcntl(0, F_SETFL, O_NDELAY);
X#endif
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
X#if SYSV|SYSIII
X                      fcntl(0, F_SETFL, oldfl),
X                      p_info(getchar()),
X                      fcntl(0, F_SETFL, O_NDELAY);
X#else
X                      p_info(getchar()),
X#endif
X
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              NULL
X       };
X       reg     char    **s = cmds;
X
X       doclear();
X       while (*s)
X               printf("%s\n", *s++);
X       printf("[Press return to continue]");
X#if SYSV|SYSIII
X       fcntl(0, F_SETFL, oldfl);
X       trash(getchar());
X       fcntl(0, F_SETFL, O_NDELAY);
X#else
X       trash(getchar());
X#endif
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       doclear();
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", 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("\n[Press return to continue]");
X
X#if SYSV|SYSIII
X       fcntl(0, F_SETFL, oldfl);
X       trash(getchar());
X       fcntl(0, F_SETFL, O_NDELAY);
X#else
X       trash(getchar());
X#endif
X
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** 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       NULL
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       printf("[Press return to continue] ");
X       trash(getchar());        /* no fcntl needed here */
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       endwin();
X#if SYSV|SYSIII
X       fcntl(0, F_SETFL, oldfl);
X#endif
X       exit(0);
X}
X
X/*
X** shell()     - set their uid to their real uid and give them a shell
X*/
Xvoid   shell ()
X{
X       reg     char    *sh;
X       reg     int     pid;
X       extern  char    *getenv();
X
X       doclear();
X#if SYSV|SYSIII
X       fcntl(0, F_SETFL, oldfl);
X#endif
X       Echo();                 /* defined to echo() on machines without bug */
X       nocrmode();
X       if ((sh = getenv("SHELL")) == NULL) /* check for a preferred shell */
X               sh = DEFAULT_SH;
X       move(LINES - 1, 0);     /* and go to the bottom */
X       draw();
X       if ((pid = fork()) == -1)
X       {
X	       fprintf(stderr, "fork failed, bye bye\n");
X               quit_it();
X       }
X       if (!pid) /* if child */
X       {
X	       if (setuid(uid) == -1)  /* in case we are running setuid */
X                       exit((fprintf(stderr, "Can't setuid(%d)\n", uid), 1));
X	       if (setgid(getgid()) == -1)  /* in case 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#if SYSV|SYSIII
X	fcntl(0, F_SETFL, O_NDELAY);
X#endif
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       doclear();
X       refresh();
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
X
Xvoid Necho()
X{
X#ifdef ECHOBUG
X# ifdef SYSIII
X	_tty.c_lflag |= (ECHO|ECHOE|ECHOK|ECHONL);
X	_echoit = TRUE;
X	Stty(_tty_ch, &_tty);
X# else
X# ifdef SYSV
X	echo();
X# endif
X#else
X	echo();
X#endif
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.5", "150",
X		"200", "300", "600", "1200", "1800", "2400",
X		"4800", "9600",
X		NULL
X	};
X
X	for (i = 0; sbauds[i]; i++)
X		if (!strcmp(sbauds[i], s))
X			return(atoi(s));
X	return('\0');
X}
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 % 4L))
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       doclear();
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       printf("[Press return to continue]");
X       trash(getchar());        /* no need to add fcntl here */
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 their 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 heuristics 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#if !SYSV && !SYSIII
X       flush();         /* this totally destroys crmode on System V */
X#endif
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       sleep(2);
X       mvaddch(pm_pos.y, pm_pos.x, EMPTY);
X       draw();
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#ifdef SYSV
X#      define _IOSTRG _IOLBF
X#endif
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*/
X
X#ifdef SYSV
Xstatic unsigned char msgbuf[BUFSIZ];
X#else
Xstatic char msgbuf[BUFSIZ];
X#endif
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
-- 
	  Eric Safern
	  ...{ihnp4,rocky2,philabs,esquire,cucard,pegasus,spike}!aecom!safern