pacman/util.c
mark
mark
Sun Apr 4 15:21:03 AEST 1982
#include "pacdefs.h"
#include <signal.h>
#include <pwd.h>
extern char
*mktemp();
extern int
delay,
errno,
wmonst,
boardcount,
rounds,
monsthere,
potintvl,
treascnt,
goldcnt;
extern long
time();
extern struct pac
*pacptr;
extern struct pac
monst[];
extern char monst_names[];
extern char *full_names[];
/*
* initbrd is used to re-initialize the display
* array once a new game is started.
*/
char initbrd[BRDY][BRDX] =
{
"#######################################",
"# . . . * . . . . ### . . . . * . . . #",
"# O ### . ##### . ### . ##### . ### O #",
"# * . . * . * . * . . * . * . * . . * #",
"# . ### . # . ########### . # . ### . #",
"# . . . * # . . . ### . . . # * . . . #",
"####### . ##### . ### . ##### . #######",
" # . # . . * . . * . . # . # ",
" # . # . ### - - ### . # . # ",
"####### . # . # # . # . #######",
" * . * # # * . * ",
"####### . # . # # . # . #######",
" # . # . ########### . # . # ",
" # . # * . . . . . . * # . # ",
"####### . # . ########### . # . #######",
"# . . . * . * . . ### . . * . * . . . #",
"# O ### . ##### . ### . ##### . ### O #",
"# . . # * . * . * . . * . * . * # . . #",
"### . # . # . ########### . # . # . ###",
"# . * . . # . . . ### . . . # . . * . #",
"# . ########### . ### . ########### . #",
"# . . . . . . . * . . * . . . . . . . #",
"#######################################",
};
/*
* brd is kept for historical reasons.
* It should only be used in the routine "which"
* to determine the next move for a monster or
* in the routine "monster" to determine if it
* was a valid move. Admittedly this is redundant
* and could be replaced by initbrd, but it is kept
* so that someday additional intelligence or
* optimization could be added to the choice of
* the monster's next move. Hence, note the symbol
* CHOICE at most points that a move decision
* logically HAS to be made.
*/
char brd[BRDY][BRDX] =
{
"#######################################",
"# . . . * . . . . ### . . . . * . . . #",
"# O ### . ##### . ### . ##### . ### O #",
"# * . . * . * . * . . * . * . * . . * #",
"# . ### . # . ########### . # . ### . #",
"# . . . * # . . . ### . . . # * . . . #",
"####### . ##### . ### . ##### . #######",
" # . # . . * . . * . . # . # ",
" # . # . ### - - ### . # . # ",
"####### . # . # # . # . #######",
" * . * # # * . * ",
"####### . # . # # . # . #######",
" # . # . ########### . # . # ",
" # . # * . . . . . . * # . # ",
"####### . # . ########### . # . #######",
"# . . . * . * . . ### . . * . * . . . #",
"# O ### . ##### . ### . ##### . ### O #",
"# . . # * . * . * . . * . * . * # . . #",
"### . # . # . ########### . # . # . ###",
"# . * . . # . . . ### . . . # . . * . #",
"# . ########### . ### . ########### . #",
"# . . . . . . . * . . * . . . . . . . #",
"#######################################",
};
/*
* display reflects the screen on the player's
* terminal at any point in time.
*/
char display[BRDY][BRDX] =
{
"#######################################",
"# . . . . . . . . ### . . . . . . . . #",
"# O ### . ##### . ### . ##### . ### O #",
"# . . . . . . . . . . . . . . . . . . #",
"# . ### . # . ########### . # . ### . #",
"# . . . . # . . . ### . . . # . . . . #",
"####### . ##### . ### . ##### . #######",
" # . # . . . . . . . . # . # ",
" # . # . ### - - ### . # . # ",
"####### . # . # # . # . #######",
" . . . # # . . . ",
"####### . # . # # . # . #######",
" # . # . ########### . # . # ",
" # . # . . . . . . . . # . # ",
"####### . # . ########### . # . #######",
"# . . . . . . . . ### . . . . . . . . #",
"# O ### . ##### . ### . ##### . ### O #",
"# . . # . . . . . . . . . . . . # . . #",
"### . # . # . ########### . # . # . ###",
"# . . . . # . . . ### . . . # . . . . #",
"# . ########### . ### . ########### . #",
"# . . . . . . . . . . . . . . . . . . #",
"#######################################",
};
char combuf[BUFSIZ],
message[81], /* temporary message buffer */
inbuf[2];
int ppid,
cpid,
game,
killcnt = 0,
vs_rows,
vs_cols;
unsigned
pscore;
long timein;
struct uscore
{
unsigned score; /* same type as pscore */
int uid; /* uid of player */
};
struct scorebrd
{
struct uscore entry[MSSAVE];
} scoresave[MGTYPE] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
update()
{
char str[10];
sprintf(str, "%6d", pscore);
SPLOT(0, 52, str);
sprintf(str, "%6d", goldcnt);
SPLOT(21, 57, str);
}
reinit()
{
register int locx, locy;
register char tmp;
if (boardcount % 2 == 0)
movie();
for (locy = 0; locy < BRDY; locy++)
{
for (locx = 0; locx < BRDX; locx++)
{
tmp = initbrd[locy][locx];
brd[locy][locx] = tmp;
if ((display[locy][locx] = tmp) == CHOICE)
{
display[locy][locx] = GOLD;
};
};
};
goldcnt = GOLDCNT;
delay = delay * 3 / 4; /* hot it up */
boardcount++;
}
errgen(string)
char *string;
{
SPLOT(23,45,string);
}
dokill(mnum)
int mnum;
{
register struct pac *mptr;
char msgbuf[50];
beep();
if (monst[mnum].danger == FALSE)
{
if (++killcnt == MAXMONSTER)
{
if (display[TRYPOS][TRXPOS] == GOLD)
{
goldcnt--;
};
display[TRYPOS][TRXPOS] = TREASURE;
PLOT(TRYPOS, TRXPOS, TREASURE);
killcnt = 0;
treascnt = potintvl;
}
SPLOT(5, 45, "MONSTERS KILLED: ");
(void) sprintf(message, "%1d", killcnt);
SPLOT(5, 62, message);
mptr = (&monst[mnum]);
mptr->ypos = MSTARTY;
mptr->xpos = MSTARTX + (2 * mnum);
mptr->danger = TRUE;
mptr->stat = START;
PLOT(mptr->ypos, mptr->xpos, monst_names[mnum]);
monsthere++;
rounds = 1; /* force it to be a while before he comes out */
switch (monsthere) {
case 1: pscore += KILLSCORE; break;
case 2: pscore += 2 * KILLSCORE; break;
case 3: pscore += 4 * KILLSCORE; break;
case 4: pscore += 8 * KILLSCORE; break;
}
sprintf(msgbuf, "You got %s!\n", full_names[mnum]);
SPLOT(4, 45, msgbuf);
return(GOTONE);
};
wmonst = mnum;
return(TURKEY);
}
/*
/* clr -- issues an escape sequence to clear the display
*/
clr()
{
clear();
}
printw(fmt, p1, p2, p3, p4)
char *fmt;
int p1, p2, p3, p4;
{
static char buf[100];
sprintf(buf, fmt, p1, p2, p3, p4);
addstr(buf);
}
/*
* display initial instructions
*/
instruct()
{
clr();
POS(0, 0);
printw("Attention: you are in a maze, being chased by monsters!\n\n");
printw("There is food scattered uniformly in the maze, marked by \".\".\n");
printw("One magic potion is available at each spot marked \"O\". Each potion will\n");
printw("enable you to eat monsters for a limited duration. It will also\n");
printw("scare them away. When you eat a monster it is regenerated, but this takes\n");
printw("time. You can also regenerate yourself %d times. Eating all the monsters\n", MAXPAC);
printw("results in further treasure appearing magically somewhere in the dungeon,\n");
printw("marked by \"$\". There is a magic tunnel connecting the center left and\n");
printw("center right parts of the dungeon. The monsters know about it!\n\n");
printw(" Type: h or s to move left\n");
printw(" l or f to move right\n");
printw(" k or e to move up\n");
printw(" j or c to move down\n");
printw(" <space> to halt \n");
printw(" q to quit\n\n");
printw(" Type: 1 easy game\n");
printw(" 2 intelligent monsters\n");
printw(" 3 very intelligent monsters\n");
refresh();
}
/*
* over -- game over processing
*/
over()
{
register int i;
register int line, col;
int scorefile = 0;
struct passwd *getpwuid(), *p;
refresh();
signal(SIGINT, SIG_IGN);
/* clr(); */
/* high score to date processing */
if (game != 0)
{
col = 45;
line = 10;
POS(line++, col);
(void) printw(" ___________________________ ");
POS(line++, col);
(void) printw("| G A M E O V E R |");
POS(line++, col);
(void) printw("| |");
POS(line++, col);
(void) printw("| Game type: %6.6s |",game==1?"easy":game==2?"medium":"smart");
if ((scorefile = open(MAXSCORE, 2)) != -1)
{
read(scorefile, (char *)scoresave, sizeof(scoresave));
for (i = MSSAVE - 1; i >= 0; i--) {
if (scoresave[game - 1].entry[i].score < pscore)
{
if (i < MSSAVE - 1)
{
scoresave[game - 1].entry[i + 1].score =
scoresave[game - 1].entry[i].score;
scoresave[game - 1].entry[i + 1].uid =
scoresave[game - 1].entry[i].uid;
};
scoresave[game - 1].entry[i].score = pscore;
scoresave[game - 1].entry[i].uid = getuid();
};
};
lseek(scorefile, 0l, 0);
write(scorefile, (char *)scoresave, sizeof(scoresave));
close(scorefile);
POS(line++, col);
(void) printw("| High Scores to date: |");
for (i = 0; i < MSSAVE; i++)
{
setpwent();
p = getpwuid(scoresave[game - 1].entry[i].uid);
POS(line++, col);
(void) printw("| Player : %-8s %5u |", p->pw_name,
scoresave[game - 1].entry[i].score);
};
}
else
{
/* clr(); */
POS(line++, col);
(void) printw("| |");
POS(line++, col);
(void) printw("| Please create a 'paclog' |");
POS(line++, col);
(void) printw("| file. See 'MAXSCORE' in |");
POS(line++, col);
(void) printw("| 'pacdefs.h'. |");
};
POS(line, col);
(void) printw("|___________________________|");
};
refresh();
leave();
}
/*
* leave -- flush buffers,kill the Child, reset tty, and delete tempfile
*/
leave()
{
POS(23, 0);
refresh();
endwin();
exit(0);
}
/*
* init -- does global initialization and spawns a child process to read
* the input terminal.
*/
init()
{
register int tries = 0;
static int lastchar = DELETE;
extern short ospeed; /* baud rate for crt (for tputs()) */
int over();
#ifdef USG
struct termio t;
#endif
errno = 0;
(void) time(&timein); /* get start time */
srand((unsigned)timein); /* start rand randomly */
signal(SIGINT, over);
signal(SIGQUIT, over);
/* Curses init - could probably eliminate much of stuff below */
initscr();
noecho();
crmode();
nonl();
leaveok(stdscr, TRUE);
vs_rows = LINES;
vs_cols = COLS;
#ifdef USG
ioctl(0, TCGETA, &t);
t.c_cc[VTIME] = 0;
t.c_cc[VMIN] = 0;
ioctl(0, TCSETA, &t);
#endif
if (delay == 0)
delay = 500; /* number of ticks per turn */
/*
* New game starts here
*/
if (game == 0)
instruct();
while ((game == 0) && (tries++ < 300))
{
napms(100);
poll(1);
};
if (tries >= 300)
{
/* I give up. Let's call it quits. */
leave();
};
goldcnt = GOLDCNT;
pscore = 0;
clr();
}
/*
* poll -- read characters sent by input subprocess and set global flags
*/
poll(sltime)
{
int stop;
register int charcnt;
int junk;
stop = 0;
readin:
refresh();
/* Check for input we've seen but not processed */
if (combuf[1] == FULL)
charcnt = 1;
else
charcnt = 0;
#ifdef FIONREAD
/* Check for typeahead on 4BSD systems. */
if (charcnt <= 0) {
ioctl(0, FIONREAD, &junk);
if (junk)
charcnt = read(0, combuf, 1);
}
#else
/* Have to try a read if nothing else worked. */
if (charcnt <= 0)
charcnt = read(0, combuf, 1);
#endif
switch (charcnt)
{
case 0:
combuf[1] = EMPTY;
break;
case -1:
errgen("READ ERROR IN POLL");
abort();
default:
combuf[0] = combuf[charcnt-1];
combuf[1] = FULL;
break;
};
if (combuf[1] == EMPTY)
{
if (stop)
{
goto readin;
};
return;
};
combuf[1] = EMPTY;
switch(combuf[0] & 0177)
{
case LEFT:
pacptr->dirn = DLEFT;
break;
case RIGHT:
pacptr->dirn = DRIGHT;
break;
case NORTH:
case NNORTH:
pacptr->dirn = DUP;
break;
case DOWN:
case NDOWN:
pacptr->dirn = DDOWN;
break;
case HALT:
pacptr->dirn = DNULL;
break;
case REDRAW:
clearok(curscr, TRUE);
break;
case ABORT:
case DELETE:
case QUIT:
over();
break;
case CNTLS:
stop = 1;
goto readin;
case GAME1:
game = 1;
break;
case GAME2:
game = 2;
break;
case GAME3:
game = 3;
break;
default:
goto readin;
}
}
getrand(range)
int range;
{
register unsigned int q;
q = rand();
return(q % range);
}
/*
* This function is convenient for debugging pacman. It isn't used elsewhere.
* It's like printf and prints in a window on the right hand side of the screen.
*/
msgf(fmt, arg1, arg2, arg3, arg4)
char *fmt;
int arg1, arg2, arg3, arg4;
{
char msgbuf[100];
static char msgline = 13;
sprintf(msgbuf, fmt, arg1, arg2, arg3, arg4);
SPLOT(msgline, 45, msgbuf);
if (msgline++ > 20)
msgline = 13;
}
/*
* napms. Sleep for ms milliseconds. We don't expect a particularly good
* resolution - 60ths of a second is normal, 10ths might even be good enough,
* but the rest of the program thinks in ms because the unit of resolution
* varies from system to system. (In some countries, it's 50ths, for example.)
*
* If you're thinking of replacing this with a call to sleep, or by outputting
* some pad characters, forget it. You won't get a decent game. You absolutely
* HAVE to have a fraction-of-a-second sleep. Sleeping for a full second will
* make the game seem really slow. Outputting pad characters will cause the
* keyboard to be ahead of the display by whatever buffering the tty driver
* does, typically about one second. This causes you to have to anticipate
* your moves and also makes response dependent on system load - in general
* the game will be crummy.
*
* Here are some reasonable ways to get a good nap.
*
* (1) Use the select (dselect?) system call in Berkeley 4.2BSD.
*
* (2) Use the 1/10th second resolution wait in the UNIX 3.0 tty driver.
* (This is untested - rumor has it that this feature does not work
* as advertised, and there might also be problems with the user hitting
* a key too soon.)
*
* (3) Install the ft (fast timer) device in your kernel.
* This is a psuedo-device to which an ioctl will wait n ticks
* and then send you an alarm.
*
* (4) Install the nap system call in your kernel.
* This system call does a timeout for the requested number of ticks.
*/
#ifdef SELECT
napms(ms)
int ms;
{
select(0, 0, 0, ms);
}
#endif
#if FTIOCSET || NAPSYSCALL
/*
* Pause for ms milliseconds. Convert to ticks and wait that long.
* the constant 6 is HZ/10, change it to 5 for 50 HZ systems.
* Call nap, which is either defined below or a system call.
*/
napms(ms)
int ms;
{
int ticks = ms * 6 / 100;
if (ticks <= 0)
ticks = 1;
nap(ticks); /* call either the code below or nap system call */
}
#endif
#ifdef FTIOCSET
/*
* This uses the ft device.
* The following code is adapted from the sleep code in libc.
* It uses the "fast timer" device posted to USENET in Feb 1982.
* nap is like sleep but the units are ticks (e.g. 1/60ths of
* seconds in the USA).
*/
#include <setjmp.h>
static jmp_buf jmp;
static int ftfd;
nap(n)
unsigned n;
{
int napx();
unsigned altime;
int (*alsig)() = SIG_DFL;
char *ftname;
struct requestbuf {
short time;
short signo;
} rb;
if (ftfd <= 0) {
ftname = "/dev/ft0";
while (ftfd <= 0) {
ftfd = open(ftname, 0);
if (ftfd <= 0)
ftname[7] ++;
}
}
if (n==0)
return;
altime = alarm(1000); /* time to maneuver */
if (setjmp(jmp)) {
signal(SIGALRM, alsig);
alarm(altime);
return;
}
if (altime) {
if (altime > n)
altime -= n;
else {
n = altime;
altime = 1;
}
}
alsig = signal(SIGALRM, napx);
rb.time = n;
rb.signo = SIGALRM;
ioctl(ftfd, FTIOCSET, &rb);
for(;;)
pause();
/*NOTREACHED*/
}
static
napx()
{
longjmp(jmp, 1);
}
#endif
#ifdef USG
#define IDLETTY "/dev/idletty"
/*
* Do it with the timer in the tty driver. Resolution is only 1/10th
* of a second. Problem is, if the user types something while we're
* sleeping, we wake up immediately, and have no way to tell how long
* we should sleep again. So we're sneaky and use a tty which we are
* pretty sure nobody is using.
*
* This requires some care. If you choose a tty that is a dialup or
* which otherwise can show carrier, it will hang and you won't get
* any response from the keyboard. You can use /dev/tty if you have
* no such tty, but response will feel funny as described above.
*/
napms(ms)
int ms;
{
struct termio t, ot;
static int ttyfd;
int n, tenths;
char c;
if (ttyfd == 0)
ttyfd = open(IDLETTY, 2);
if (ttyfd < 0) {
fprintf(stderr, "Need read/write permission on %s\r\n",
IDLETTY);
leave();
}
tenths = (ms+50) / 100;
ioctl(ttyfd, TCGETA, &t);
ot = t;
t.c_cflag &= ~ICANON;
t.c_cc[VMIN] = 0;
t.c_cc[VTIME] = tenths;
ioctl(ttyfd, TCSETA, &t);
n = read(ttyfd, &c, 1);
if (n > 0) {
combuf[0] = c;
combuf[1] = FULL;
}
ioctl(ttyfd, TCSETA, &ot);
}
#endif
#ifndef A_BLINK
/* Simulations for the old curses */
flushinp()
{
#ifdef USG
ioctl(0, TCFLSH, 0);
#else
ioctl(0, TIOCFLUSH, 0);
#endif
}
beep()
{
putchar('\7');
fflush(stdout);
}
baudrate()
{
int baud;
#ifdef USG
baud = _tty.c_cflag & CBAUD;
#else
baud = _tty.sg_ospeed;
#endif
switch (baud) {
case B110: return 110;
case B300: return 300;
case B1200: return 1200;
case B2400: return 2400;
case B4800: return 4800;
case B9600: return 9600;
case EXTA: return 19200;
}
return 9600; /* Guess */
}
#endif
More information about the Comp.sources.unix
mailing list