├── .gitignore ├── Dockerfile ├── README.md ├── UNLICENSE ├── docs └── README ├── makefile ├── monster.c ├── movie.c ├── pacdefs.h ├── pacman.6 ├── pacman.c └── util.c /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.o 3 | GPATH 4 | GTAGS 5 | GRTAGS 6 | pacman 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.9 2 | RUN apk add --update git build-base ncurses-dev 3 | 4 | RUN git clone --depth=1 https://github.com/troglobit/pacman.git /root/pacman 5 | WORKDIR /root/pacman 6 | 7 | RUN make 8 | RUN make install DESTDIR=/tmp 9 | 10 | FROM alpine:3.9 11 | RUN apk add --update ncurses 12 | COPY --from=0 /tmp/usr/games/pacman /usr/bin/ 13 | 14 | CMD [ "/usr/bin/pacman" ] 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Pacman 2 | ====== 3 | 4 | This is the original UNIX version of the Pac-Man game. Posted to the 5 | net.sources newsgroup as public domain in 1982 by Mark Horton. 6 | 7 | The games was originally written by Dave Nixon while at AGS Computers 8 | Inc., in July 1981. It was converted to curses in Feb 1982 by Mark 9 | Horton. 10 | 11 | 12 | Screenshots 13 | ----------- 14 | 15 | ``` 16 | ####################################### SCORE: 0 GAME: EASY 17 | # . . . . . . . . ### . . . . . . . . # 18 | # O ### . ##### . ### . ##### . ### O # 19 | # . . . . . . . . . . . . . . . . . . # 20 | # . ### . # . ########### . # . ### . # 21 | # . . . . # . . . ### . . . # . . . . # 22 | ####### . ##### . ### . ##### . ####### 23 | # . # . . . . . . . . # . # 24 | # . # . ### - - ### . # . # 25 | ####### . # . # # . # . ####### 26 | . . . # B I P C # . . . 27 | ####### . # . # # . # . ####### 28 | # . # . ########### . # . # 29 | # . # . . . . . . . . # . # 30 | ####### . # . ########### . # . ####### 31 | # . . . . . . . . ### . . . . . . . . # 32 | # O ### . ##### . ### . ##### . ### O # 33 | # . . # . . . . . @ . . . . . . # . . # 34 | ### . # . # . ########### . # . # . ### 35 | # . . . . # . . . ### . . . # . . . . # 36 | # . ########### . ### . ########### . # 37 | # . . . . . . . . . . . . . . . . . . # gold left = 185 38 | ####################################### delay = 500, refresh = 1 39 | @ @ 40 | 41 | ``` 42 | 43 | 44 | Instructions 45 | ------------ 46 | 47 | Attention: you are in a maze, being chased by monsters! 48 | 49 | There is food scattered uniformly in the maze, marked by ".". 50 | One magic potion is available at each spot marked "O". Each potion will 51 | enable you to eat monsters for a limited duration. It will also 52 | scare them away. When you eat a monster it is regenerated, but this takes 53 | time. You can also regenerate yourself 3 times. Eating all the monsters 54 | results in further treasure appearing magically somewhere in the dungeon, 55 | marked by "$". There is a magic tunnel connecting the center left and 56 | center right parts of the dungeon. The monsters know about it! 57 | 58 | Type: h or s to move left 59 | l or f to move right 60 | k or e to move up 61 | j or c to move down 62 | to halt 63 | q to quit 64 | 65 | Type: 1 easy game 66 | 2 intelligent monsters 67 | 3 very intelligent monsters 68 | 69 | 70 | Download & Build 71 | ---------------- 72 | 73 | The [latest sources][master] are avilable on GitHub. Download and 74 | unpack in the usual manner. 75 | 76 | To build you need ncurses, a C compiler, and make. On Ubuntu or other 77 | Debian GNU/Linux based systems you can get this using: 78 | 79 | sudo apt install build-essential libncurses5-dev 80 | 81 | Other Linux systems have similar packages. On FreeBSD you don't need 82 | anything in particular as long as you can fetch the tarball and have 83 | a working `cc` and `make`. 84 | 85 | FreeBSD users can also download the true original from this mailing 86 | list entry https://marc.info/?l=freebsd-hackers&m=94345990014968&w=2 87 | 88 | [master]: https://github.com/troglobit/pacman/archive/master.tar.gz 89 | -------------------------------------------------------------------------------- /UNLICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /docs/README: -------------------------------------------------------------------------------- 1 | 2 | Pacman has been tested on cbosgd (running 4.1BSD with the fast timer 3 | driver added), and on vanilla 5.0 systems on a VAX and on a 3B. 4 | It is strongly urged that you add the fast timer driver (../ft) since 5 | otherwise the nap mechanism won't work very well. (On 5.0, for 6 | example, it will feel strange every time you hit a key.) 7 | To my knowledge, ft needs to be ported to 5.0, and I don't know of 8 | anyone who has done this. 9 | 10 | Pacman should work on any system that can run this version of curses. 11 | The worst that will happen is that turns will only go one per second, 12 | resulting in a very slow (and boringly easy) game. You need some form 13 | of improved nap to make the game go fast enough to be interesting. 14 | 15 | Recent changes to Pacman make it work reasonably well at 1200 baud on 16 | verbose terminals such as HP's, vt100's, and Ambassadors. However, 17 | it still feels better at high speeds and on terse terminals such as 18 | the mime, concept, blit, and adm3a. ("verbose" and "terse" refer to 19 | the length of the cursor addressing sequence - 3 or 4 characters is 20 | terse, 8 is verbose.) The improvement is to use the draino function, 21 | which needs TIOCOUTQ, which, alas, only exists on 4.1BSD. (But all 22 | the ioctl does is return the number of characters in the output queue, 23 | so it ought to be trivial to add to 5.0.) 24 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | OBJS = pacman.o monster.o util.o movie.o 2 | CFLAGS = -W -Wall -Wextra -g -O2 3 | LDLIBS = -lcurses 4 | 5 | all: pacman 6 | 7 | pacman: $(OBJS) 8 | $(CC) -o $@ $(OBJS) $(LDLIBS) 9 | 10 | pacman.o: pacman.c pacdefs.h 11 | monster.o: monster.c pacdefs.h 12 | util.o: util.c pacdefs.h 13 | movie.o: movie.c pacdefs.h 14 | 15 | install: pacman 16 | @touch /tmp/pacman.log 17 | install -b -D -s pacman $(DESTDIR)/usr/games/pacman 18 | install -b -D pacman.6 $(DESTDIR)/usr/man/man6/pacman.6 19 | install -b -D README.md $(DESTDIR)/usr/share/doc/pacman/README.md 20 | install -b -D /tmp/pacman.log $(DESTDIR)/var/games/pacman.log 21 | @rm /tmp/pacman.log 22 | 23 | clean: 24 | -rm -f *.o 25 | 26 | distclean: clean 27 | -rm -f pacman errs core a.out 28 | -------------------------------------------------------------------------------- /monster.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pacdefs.h" 3 | 4 | extern char 5 | brd[BRDY][BRDX], 6 | display[BRDY][BRDX]; 7 | 8 | extern int 9 | delay, 10 | game, 11 | killflg, 12 | potion, 13 | monst_often, 14 | mflash, rflash, pflash, 15 | rounds; 16 | 17 | extern unsigned 18 | pscore; 19 | 20 | extern struct pac 21 | *pacptr; 22 | 23 | int rscore[MAXMONSTER]; 24 | 25 | struct pac 26 | monst[MAXMONSTER]; 27 | 28 | char monst_names[] = "BIPC"; 29 | char runner_names[] = "bipc"; 30 | char *full_names[] = { 31 | "Blinky", "Inky", "Pinky", "Clyde", 0 32 | }; 33 | 34 | void startmonst(void) 35 | { 36 | struct pac *mptr; 37 | int monstnum; 38 | 39 | for (mptr = &monst[0], monstnum = 0; monstnum < MAXMONSTER; mptr++, monstnum++) 40 | { 41 | if (mptr->stat == START) 42 | { 43 | rscore[monstnum] = 1; 44 | 45 | /* clear home */ 46 | PLOT(mptr->ypos, mptr->xpos, VACANT); 47 | 48 | /* initialize moving monster */ 49 | mptr->ypos = MBEGINY; 50 | mptr->xpos = MBEGINX; 51 | mptr->ydpos = MBEGINY; 52 | mptr->xdpos = MBEGINX; 53 | mptr->stat = RUN; 54 | PLOT(MBEGINY, MBEGINX, mptr->danger ? 55 | monst_names[monstnum] | mflash | COLOR_PAIR (monstnum+1): 56 | runner_names[monstnum] | rflash | COLOR_PAIR (monstnum+1)); 57 | 58 | /* DRIGHT or DLEFT? */ 59 | mptr->dirn = getrand(2) + DLEFT; 60 | break; 61 | } 62 | } 63 | } 64 | 65 | void monster(mnum) 66 | int mnum; 67 | { 68 | struct pac *mptr; 69 | int newx = 0, newy = 0; 70 | int tmpx, tmpy; 71 | 72 | mptr = &monst[mnum]; 73 | 74 | /* remember monster's current position */ 75 | tmpx = mptr->xpos; 76 | tmpy = mptr->ypos; 77 | 78 | /* if we can, let's move a monster */ 79 | if (mptr->stat == RUN) 80 | { 81 | /* get a new direction */ 82 | mptr->dirn = which(mptr, tmpx, tmpy); 83 | switch (mptr->dirn) 84 | { 85 | case DUP: 86 | newy = tmpy + UPINT; 87 | newx = tmpx; 88 | break; 89 | 90 | case DDOWN: 91 | newy = tmpy + DOWNINT; 92 | newx = tmpx; 93 | break; 94 | 95 | case DLEFT: 96 | newx = tmpx + LEFTINT; 97 | newy = tmpy; 98 | if (newx <= 0) 99 | newx = XWRAP; /* wrap around */ 100 | break; 101 | 102 | case DRIGHT: 103 | newx = tmpx + RIGHTINT; 104 | newy = tmpy; 105 | if (newx >= XWRAP) 106 | newx = 0; /* wrap around */ 107 | break; 108 | } 109 | 110 | /* use brd to determine if this was a valid direction */ 111 | switch (brd[newy][newx]) 112 | { 113 | case GOLD: 114 | case VACANT: 115 | case POTION: 116 | case TREASURE: 117 | case CHOICE: 118 | /* set new position */ 119 | mptr->xpos = newx; 120 | mptr->ypos = newy; 121 | 122 | /* run into a pacman? */ 123 | if ((newy == pacptr->ypos) && 124 | (newx == pacptr->xpos)) 125 | { 126 | killflg = dokill(mnum); 127 | }; 128 | if (rounds % monst_often == 0 || killflg == TURKEY) { 129 | PLOT(mptr->ydpos,mptr->xdpos, 130 | display[mptr->ydpos][mptr->xdpos]); 131 | if (mptr->danger == TRUE) 132 | { 133 | PLOT(newy, newx, monst_names[mnum] | mflash | COLOR_PAIR(mnum+1)); 134 | } 135 | else if (killflg != GOTONE) 136 | { 137 | PLOT(newy, newx, runner_names[mnum] | rflash | COLOR_PAIR(mnum+1)); 138 | }; 139 | mptr->ydpos = newy; 140 | mptr->xdpos = newx; 141 | } 142 | break; 143 | 144 | default: 145 | errgen("bad direction"); 146 | break; 147 | }; 148 | } 149 | } 150 | 151 | int which(mptr, x, y) /* which directions are available ? */ 152 | struct pac *mptr; 153 | int x, y; 154 | { 155 | char *brdptr; 156 | int movecnt; 157 | int submovecnt; 158 | int next; 159 | int moves[4]; 160 | int submoves[4]; 161 | int nydirn, nxdirn; 162 | int goodmoves; 163 | int offx, offy; 164 | int tmpdirn; 165 | 166 | /* 167 | * As a general rule: determine the set of all 168 | * possible moves, but select only those moves 169 | * that don't require a monster to backtrack. 170 | */ 171 | movecnt = 0; 172 | brdptr = &(brd[y][x]); 173 | if (((tmpdirn = mptr->dirn) != DDOWN) && 174 | ((next = *(brdptr + (BRDX * UPINT))) != WALL) && 175 | (next != GATE)) 176 | { 177 | moves[movecnt++] = DUP; 178 | }; 179 | if ((tmpdirn != DUP) && 180 | ((next = *(brdptr + (BRDX * DOWNINT))) != WALL) && 181 | (next != GATE)) 182 | { 183 | moves[movecnt++] = DDOWN; 184 | }; 185 | if ((tmpdirn != DRIGHT) && 186 | ((next = *(brdptr + LEFTINT)) != WALL) && 187 | (next != GATE)) 188 | { 189 | moves[movecnt++] = DLEFT; 190 | }; 191 | if ((tmpdirn != DLEFT) && 192 | ((next = *(brdptr + RIGHTINT)) != WALL) && 193 | (next != GATE)) 194 | { 195 | moves[movecnt++] = DRIGHT; 196 | }; 197 | 198 | /* 199 | * If the player requested intelligent monsters and 200 | * the player is scoring high ... 201 | */ 202 | if (game >= 2 && getrand(game == 2 ? 10000 : 1000) < pscore) 203 | { 204 | /* make monsters intelligent */ 205 | if (mptr->danger == FALSE) 206 | { 207 | /* 208 | * Holy Cow!! The pacman is dangerous, 209 | * permit monsters to reverse direction 210 | */ 211 | switch (tmpdirn) 212 | { 213 | case DUP: 214 | if ((*(brdptr + (BRDX * DOWNINT)) != WALL) && 215 | (*(brdptr + (BRDX * DOWNINT)) != GATE)) 216 | { 217 | moves[movecnt++] = DDOWN; 218 | }; 219 | break; 220 | 221 | case DDOWN: 222 | if ((*(brdptr + (BRDX * UPINT)) != WALL) && 223 | (*(brdptr + (BRDX * UPINT)) != GATE)) 224 | { 225 | moves[movecnt++] = DUP; 226 | }; 227 | break; 228 | 229 | case DRIGHT: 230 | if ((*(brdptr + LEFTINT) != WALL) && 231 | (*(brdptr + LEFTINT) != GATE)) 232 | { 233 | moves[movecnt++] = DLEFT; 234 | }; 235 | break; 236 | 237 | case DLEFT: 238 | if ((*(brdptr + RIGHTINT) != WALL) && 239 | (*(brdptr + RIGHTINT) != GATE)) 240 | { 241 | moves[movecnt++] = DRIGHT; 242 | }; 243 | break; 244 | }; 245 | }; 246 | 247 | /* determine the offset from the pacman */ 248 | offx = x - pacptr->xpos; 249 | offy = y - pacptr->ypos; 250 | if (offx > 0) 251 | { 252 | /*need to go left */ 253 | nxdirn = DLEFT; 254 | } 255 | else 256 | { 257 | if (offx < 0) 258 | { 259 | nxdirn = DRIGHT; 260 | } 261 | else 262 | { 263 | /*need to stay here */ 264 | nxdirn = DNULL; 265 | }; 266 | }; 267 | if (offy > 0) 268 | { 269 | /*need to go up */ 270 | nydirn = DUP; 271 | } 272 | else 273 | { 274 | if (offy < 0) 275 | { 276 | /* need to go down */ 277 | nydirn = DDOWN; 278 | } 279 | else 280 | { 281 | /* need to stay here */ 282 | nydirn = DNULL; 283 | }; 284 | }; 285 | goodmoves = 0; 286 | for (submovecnt = 0; submovecnt < movecnt; submovecnt++) 287 | { 288 | if (mptr->danger == TRUE) 289 | { 290 | if ((moves[submovecnt] == nydirn) || 291 | (moves[submovecnt] == nxdirn)) 292 | { 293 | submoves[goodmoves++] = moves[submovecnt]; 294 | }; 295 | } 296 | else 297 | { 298 | if ((moves[submovecnt] != nydirn) && 299 | (moves[submovecnt] != nxdirn)) 300 | { 301 | submoves[goodmoves++] = moves[submovecnt]; 302 | }; 303 | }; 304 | }; 305 | if (goodmoves > 0) 306 | { 307 | return(submoves[getrand(goodmoves)]); 308 | }; 309 | }; 310 | return(moves[getrand(movecnt)]); 311 | } 312 | -------------------------------------------------------------------------------- /movie.c: -------------------------------------------------------------------------------- 1 | /* 2 | * "Movie" routine for Mark Horton's version of PACMAN which uses the 3 | * curses library stuff. You could probably diddle with it a little 4 | * and make it shorter/smaller, however, I haven't seen any document- 5 | * ation on the "new" curses stuff (although we have it), so I don't 6 | * know what I can get away with. 7 | * 8 | * NOTE: This routine was written for a 24 x 80 screen (adm3a, adm5). 9 | * If your screen is different (columns matter, lines don't), 10 | * you will have to change some things. It shouldn't be very 11 | * difficult, but be warned. 12 | * 13 | * Dave Curry (pur-ee!davy) 14 | * 15 | */ 16 | #include "pacdefs.h" 17 | #include 18 | #include 19 | 20 | /* This is the monster we use. Don't take out the extra spaces here, 21 | * they are there so that the thing "cleans up" after itself, since 22 | * I'm too lazy to move all over the place writing out spaces. 23 | */ 24 | char *bigmonster[] = { 25 | " _____ ", 26 | " / \\ ", 27 | " | O O | ", 28 | " v^v^v^v ", 29 | }; 30 | 31 | static void left1(void) 32 | { 33 | SPLOT(10, 0, "_____ \n"); 34 | SPLOT(11, 0, " \\ \n"); 35 | SPLOT(12, 0, " O O | \n"); 36 | SPLOT(13, 0, "^v^v^v \n"); 37 | refresh(); 38 | } 39 | 40 | static void left2(void) 41 | { 42 | SPLOT(10, 0, "____ \n"); 43 | SPLOT(11, 0, " \\ \n"); 44 | SPLOT(12, 0, "O O | \n"); 45 | SPLOT(13, 0, "v^v^v \n"); 46 | refresh(); 47 | } 48 | 49 | static void left3(void) 50 | { 51 | SPLOT(10, 0, "___ \n"); 52 | SPLOT(11, 0, " \\ \n"); 53 | SPLOT(12, 0, " O | \n"); 54 | SPLOT(13, 0, "^v^v \n"); 55 | refresh(); 56 | } 57 | 58 | static void left4(void) 59 | { 60 | SPLOT(10, 0, "__ \n"); 61 | SPLOT(11, 0, " \\ \n"); 62 | SPLOT(12, 0, "O | \n"); 63 | SPLOT(13, 0, "v^v \n"); 64 | refresh(); 65 | } 66 | 67 | static void left5(void) 68 | { 69 | SPLOT(10, 0, "_ \n"); 70 | SPLOT(11, 0, " \\ \n"); 71 | SPLOT(12, 0, " | \n"); 72 | SPLOT(13, 0, "^v \n"); 73 | refresh(); 74 | } 75 | 76 | static void left6(void) 77 | { 78 | SPLOT(10, 0, " \n"); 79 | SPLOT(11, 0, "\\ \n"); 80 | SPLOT(12, 0, "| \n"); 81 | SPLOT(13, 0, "v \n"); 82 | refresh(); 83 | } 84 | 85 | static void left7(void) 86 | { 87 | SPLOT(10, 0, " \n"); 88 | SPLOT(11, 0, " \n"); 89 | SPLOT(12, 0, " \n"); 90 | SPLOT(13, 0, " \n"); 91 | refresh(); 92 | } 93 | 94 | static void right1(void) 95 | { 96 | SPLOT(10, 69, " _____ "); 97 | SPLOT(11, 69, " / \\"); 98 | SPLOT(12, 69, " | O O |"); 99 | SPLOT(13, 69, " v^v^v^v"); 100 | } 101 | 102 | static void right2(void) 103 | { 104 | SPLOT(10, 70, " _____"); 105 | SPLOT(11, 70, " / "); 106 | SPLOT(12, 70, " | O O "); 107 | SPLOT(13, 70, " v^v^v^"); 108 | } 109 | 110 | static void right3(void) 111 | { 112 | SPLOT(10, 71, " ____"); 113 | SPLOT(11, 71, " / "); 114 | SPLOT(12, 71, " | O O"); 115 | SPLOT(13, 71, " v^v^v"); 116 | } 117 | 118 | static void right4(void) 119 | { 120 | SPLOT(10, 72, " ___"); 121 | SPLOT(11, 72, " / "); 122 | SPLOT(12, 72, " | O "); 123 | SPLOT(13, 72, " v^v^"); 124 | } 125 | 126 | static void right5(void) 127 | { 128 | SPLOT(10, 73, " __"); 129 | SPLOT(11, 73, " / "); 130 | SPLOT(12, 73, " | O"); 131 | SPLOT(13, 73, " v^v"); 132 | } 133 | 134 | static void right6(void) 135 | { 136 | SPLOT(10, 74, " _"); 137 | SPLOT(11, 74, " / "); 138 | SPLOT(12, 74, " | "); 139 | SPLOT(13, 74, " v^"); 140 | } 141 | 142 | static void right7(void) 143 | { 144 | SPLOT(10, 75, " "); 145 | SPLOT(11, 75, " /"); 146 | SPLOT(12, 75, " |"); 147 | SPLOT(13, 75, " v"); 148 | } 149 | 150 | static void right8(void) 151 | { 152 | SPLOT(10, 76, " "); 153 | SPLOT(11, 76, " "); 154 | SPLOT(12, 76, " "); 155 | SPLOT(13, 76, " "); 156 | } 157 | 158 | static void right9(void) 159 | { 160 | SPLOT(10, 77, " "); 161 | SPLOT(11, 77, " "); 162 | SPLOT(12, 77, " "); 163 | SPLOT(13, 77, " "); 164 | } 165 | 166 | static void monst7(xxx) 167 | int xxx; 168 | { 169 | SPLOT(10, xxx, " <"); 170 | } 171 | 172 | static void monst6(xxx) 173 | int xxx; 174 | { 175 | SPLOT( 9, xxx, " /"); 176 | SPLOT(10, xxx, " < "); 177 | SPLOT(11, xxx, " \\"); 178 | } 179 | 180 | static void monst5(xxx) 181 | int xxx; 182 | { 183 | SPLOT( 8, xxx, " /"); 184 | SPLOT( 9, xxx, " / "); 185 | SPLOT(10, xxx, " < "); 186 | SPLOT(11, xxx, " \\ "); 187 | SPLOT(12, xxx, " \\"); 188 | } 189 | 190 | static void monst4(xxx) 191 | int xxx; 192 | { 193 | SPLOT( 7, xxx, " /"); 194 | SPLOT( 8, xxx, " / "); 195 | SPLOT( 9, xxx, " / "); 196 | SPLOT(10, xxx, " < "); 197 | SPLOT(11, xxx, " \\ "); 198 | SPLOT(12, xxx, " \\ "); 199 | SPLOT(13, xxx, " \\ "); 200 | } 201 | 202 | static void monst3(xxx) 203 | int xxx; 204 | { 205 | SPLOT(7 , xxx, " / "); 206 | SPLOT(8 , xxx, " / "); 207 | SPLOT(9 , xxx, " / "); 208 | SPLOT(10, xxx, " "); 209 | SPLOT(11, xxx, " \\ "); 210 | SPLOT(12, xxx, " \\ "); 211 | SPLOT(13, xxx, " \\ "); 212 | } 213 | 214 | static void monst2(xxx) 215 | int xxx; 216 | { 217 | SPLOT( 7, xxx, " / "); 218 | SPLOT( 8, xxx, " / "); 219 | SPLOT( 9, xxx, " "); 220 | SPLOT(10, xxx, " "); 221 | SPLOT(11, xxx, " "); 222 | SPLOT(12, xxx, " \\ "); 223 | SPLOT(13, xxx, " \\ "); 224 | } 225 | 226 | static void monst1(xxx) 227 | int xxx; 228 | { 229 | SPLOT( 7, xxx, " / "); 230 | SPLOT( 8, xxx, " "); 231 | SPLOT( 9, xxx, " "); 232 | SPLOT(10, xxx, " "); 233 | SPLOT(11, xxx, " "); 234 | SPLOT(12, xxx, " "); 235 | SPLOT(13, xxx, " \\ "); 236 | } 237 | 238 | void movie(void) 239 | { 240 | int i, j; 241 | 242 | clear(); 243 | refresh(); 244 | 245 | /* 246 | * this loop moves the monster and the small pacman from right to 247 | * left, until it's time to start printing "fractional" monsters 248 | */ 249 | for (i=70; i > (-1); i--) { 250 | for (j=0; j < 4; j++) { 251 | SPLOT((j+10), i, bigmonster[j]); 252 | } 253 | if (i > 20) { 254 | SPLOT(13, (i-20), "> "); 255 | } 256 | else { 257 | SPLOT(13, 1, " "); 258 | } 259 | refresh(); 260 | } 261 | 262 | /* 263 | * left1-left7 are "partial" monster routines, they are also called when 264 | * the monster comes back on from the left (called in opposite order) 265 | */ 266 | left1(); left2(); 267 | left3(); left4(); 268 | left5(); left6(); 269 | left7(); 270 | 271 | sleep(1); 272 | 273 | /* Now we come back on... */ 274 | left7(); left6(); 275 | left5(); left4(); 276 | left3(); left2(); 277 | left1(); 278 | 279 | /* 280 | * Now we start moving the big pacman across. monst1-monst3 are 281 | * routines for drawing pieces of the pacman, until the whole thing 282 | * is on the screen. 283 | */ 284 | for (i=0; i < 70; i++) { 285 | for(j=0; j < 4; j++) { 286 | SPLOT((j+10), i, bigmonster[j]); 287 | } 288 | if (i > 20) { 289 | switch(i) { 290 | case 21: 291 | monst1((i-20)); 292 | break; 293 | case 22: 294 | monst2((i-20)); 295 | break; 296 | case 23: 297 | monst3((i-20)); 298 | break; 299 | default: 300 | monst4((i-20)); 301 | break; 302 | } 303 | } 304 | refresh(); 305 | } 306 | /* 307 | * right1-right9 are partial monster routines, for moving him off to 308 | * the right of the screen. monst4 prints the whole pacman. 309 | */ 310 | right1(); monst4(50); refresh(); 311 | right2(); monst4(51); refresh(); 312 | right3(); monst4(52); refresh(); 313 | right4(); monst4(53); refresh(); 314 | right5(); monst4(54); refresh(); 315 | right6(); monst4(55); refresh(); 316 | right7(); monst4(56); refresh(); 317 | right8(); monst4(57); refresh(); 318 | right9(); monst4(58); refresh(); 319 | 320 | /* Now finish moving the pacman to the end of the screen. */ 321 | for (i=59; i < 74; i++) { 322 | monst4(i); 323 | refresh(); 324 | } 325 | 326 | /* monst5-monst7 print pieces of pacman as he moves off the screen */ 327 | monst5(74); refresh(); 328 | monst6(75); refresh(); 329 | monst7(76); refresh(); 330 | 331 | /* clean up a little bit */ 332 | clear(); 333 | refresh(); 334 | } 335 | -------------------------------------------------------------------------------- /pacdefs.h: -------------------------------------------------------------------------------- 1 | #ifndef PACDEFS_H_ 2 | #define PACDEFS_H_ 3 | 4 | #include 5 | /* dfp #define POS(row,col) fputs(tgoto(vs_cm,(col),(row)),stdout)*/ 6 | /* #define POS(row,col) tputs(tgoto(vs_cm,(col),(row)),1,putch) */ 7 | #define POS(row,col) move(row, col) 8 | /* dfp */ 9 | #define PLOT(A,B,C) POS(A,B);addch(C) 10 | #define SPLOT(A,B,S) POS(A,B);addstr(S) 11 | #define GAME1 '1' 12 | #define GAME2 '2' 13 | #define GAME3 '3' 14 | #define MAXSCORE "/var/games/pacman.log" 15 | #define MSSAVE 5 /* maximum scores saved per game type */ 16 | #define MGTYPE 3 /* Maximum game types */ 17 | #define MAXPAC 3 /* maximum number of pacmen to start */ 18 | #define MAXMONSTER 4 /* max number of monsters */ 19 | #define EMPTY 0 20 | #define FULL 1 21 | #define LEFT 'h' 22 | #define NLEFT 's' 23 | #define RIGHT 'l' 24 | #define NRIGHT 'f' 25 | #define NORTH 'k' /* means UP, but UP defined in vsinit() */ 26 | #define NNORTH 'e' 27 | #define DOWN 'j' 28 | #define NDOWN 'c' 29 | #define HALT ' ' 30 | #define REDRAW '\14' 31 | #define DELETE '\177' 32 | #define ABORT '\34' 33 | #define QUIT 'q' 34 | #define CNTLS '\23' 35 | #define BUF_SIZE 32 36 | #define UPINT (-1) 37 | #define DOWNINT 1 38 | #define LEFTINT (-2) 39 | #define RIGHTINT 2 40 | #define PACMAN '@' 41 | #define MONSTER 'M' 42 | #define RUNNER 'S' 43 | #define TREASURE '$' 44 | #define CHOICE '*' 45 | #define GOLD '.' 46 | #define POTION 'O' 47 | #define VACANT ' ' /* space */ 48 | #define WALL '#' 49 | #define GATE '-' 50 | #define START 0 51 | #define RUN 1 52 | #define FAST 1 53 | #define SLOW 0 54 | #define PSTARTX 18 55 | #define PSTARTY 17 56 | #define MSTARTX 16 /* monster starting position */ 57 | #define MSTARTY 10 /* monster starting position */ 58 | #define MBEGINX 18 /* monster beginning position */ 59 | #define MBEGINY 7 /* monster beginning position */ 60 | #define TRYPOS 13 61 | #define TRXPOS 20 62 | #define GOTONE 1 63 | #define TURKEY (-1) 64 | #define DUP 1 65 | #define DDOWN 4 66 | #define DRIGHT 3 67 | #define DLEFT 2 68 | #define DNULL 0 69 | #define BRDX 40 70 | #define BRDY 23 71 | #define XWRAP 38 72 | 73 | /* Scores */ 74 | #define TREASVAL 50 75 | #define KILLSCORE 200 76 | #define GOLDVAL 10 77 | 78 | #define MSTARTINTVL 10 79 | #define POTINTVL 25 80 | #define BINTVL 10 81 | 82 | #define GOLDCNT 185 83 | #define CUP '|' 84 | #define CDOWN '|' 85 | #define CLEFT '-' 86 | #define CRIGHT '-' 87 | #define PUP 'v' 88 | #define PDOWN '^' 89 | #define PLEFT '>' 90 | #define PRIGHT '<' 91 | 92 | struct pac 93 | { 94 | int xpos; /* real horizontal position */ 95 | int ypos; /* real vertical position */ 96 | int dirn; /* direction of travel */ 97 | int speed; /* FAST/SLOW */ 98 | int danger; /* TRUE if can eat */ 99 | int stat; /* status */ 100 | int xdpos; /* horizontal position currently displayed at */ 101 | int ydpos; /* vertical position currently displayed at */ 102 | }; 103 | 104 | /* monster.c */ 105 | extern void startmonst(void); 106 | extern void monster(int); 107 | extern int which(struct pac *, int, int); 108 | 109 | /* movie.c */ 110 | extern void movie(void); 111 | 112 | /* util.c */ 113 | extern void syncscreen(void); 114 | extern void update(void); 115 | extern void reinit(void); 116 | extern void errgen(char *); 117 | extern int dokill(int); 118 | extern void over(int); 119 | extern void init(void); 120 | extern void pollch(int); 121 | extern unsigned int getrand(int); 122 | 123 | #endif /* PACDEFS_H_ */ 124 | -------------------------------------------------------------------------------- /pacman.6: -------------------------------------------------------------------------------- 1 | .TH PACMAN 6 5/5/82 2 | .SH NAME 3 | pacman \- Food Eating CRT Game 4 | .SH SYNOPSIS 5 | .B pacman 6 | [ 7 | .B \-ehm 8 | ] 9 | [ 10 | .B \-p 11 | ] 12 | [ 13 | .BI \-n cps 14 | ] 15 | .SH DESCRIPTION 16 | .PP 17 | .I Pacman 18 | is the familiar video game where the hero (``pacman'') runs around a maze, 19 | eating food off the floor. 20 | The pacman is controlled with either the hjkl convention, arrow keys, 21 | or a pad centered around the D key, using sefc. 22 | These keys cause an immediate change in direction. 23 | Hitting space or running into a wall causes the pacman to stop. 24 | .PP 25 | There are four monsters running around the maze. 26 | If they run into the pacman, he gets killed. 27 | Getting killed for the third time ends the game. 28 | (Reserve pacmen are shown by @'s in the lower left corner.) 29 | .PP 30 | There are four energizers in the corners of the maze. 31 | Eating an energizer allows the pacman to eat monsters for a limited time. 32 | The number of turns of monster-eating time remaining are shown in the 33 | countdown field as they tick off. 34 | The bell is sounded at 10 turns and for each turn from 4 down to 1. 35 | .PP 36 | After eating four monsters, a treasure will appear just below the monsters pen. 37 | This treasure is worth bonus points, depending on the round. 38 | It will disappear after a certain elapsed time. 39 | .PP 40 | The options control the display format and the difficulty of the game. 41 | Normally, the pacman and monsters are shown highlighted. 42 | On some terminals, at low speeds, this highlighting causes enough extra 43 | output to slow the game down, resulting in delayed response to direction 44 | changes. 45 | (You tend to overshoot your turns.) 46 | The 47 | .B \-p 48 | option can be turned on to disable highlighting, often 49 | resulting in a better game. 50 | .PP 51 | Using the 52 | .B \-n 53 | option you can give pacman an estimate of the number of characters needed 54 | per turn to update the screen. 55 | This estimate, along with your speed, is used to decide how often to 56 | draw the monsters. 57 | The monsters are updated every 58 | .I syncscreen 59 | turns. This value is shown in the lower left corner. 60 | The default, 60 characters, will usually result in a 61 | .I syncscreen 62 | value of 1 63 | in the early rounds and 64 | higher values in subsequent rounds. 65 | A higher value will cause less output and less frequent updates of 66 | monster positions, 67 | and is useful if you have a very dumb terminal or one with very 68 | verbose escape sequences 69 | (such as an ANSI terminal, a VT100, or an HP). 70 | A lower value can be used to keep 71 | .I syncscreen 72 | at 1 in later rounds, on a terminal with very short escape sequences, 73 | such as a mime. 74 | .PP 75 | The 76 | .BR \-e (easy), 77 | .BR \-m (medium), 78 | and 79 | .BR \-h (hard) 80 | options control the difficulty of the game. 81 | The parameter varied is the intelligence of the monsters. 82 | In the easy game, the monsters have no intelligence. 83 | They always move at random. 84 | In the medium game, the monsters gradually get smarter, 85 | as the players score increases. 86 | At 10000 points, the monsters are totally smart. 87 | In the hard game, the monsters get smarter much faster, 88 | reaching total intelligence at 1000 points. 89 | .SH FILES 90 | .DT 91 | /var/games/pacman.log Score file 92 | .SH BUGS 93 | .PP 94 | Large values of 95 | .I cps 96 | do not seem to help when trying to run at 1200 baud on an ANSI 97 | terminal with highlighting. 98 | The 99 | .B \-p 100 | option seems to be necessary in this case. 101 | -------------------------------------------------------------------------------- /pacman.c: -------------------------------------------------------------------------------- 1 | /* 2 | * PACMAN - written by Dave Nixon, AGS Computers Inc., July, 1981. 3 | * Converted for curses Feb 1982 by Mark Horton. 4 | * 5 | * Terminal handling for video games taken from aliens 6 | * the original version of aliens is from 7 | * Fall 1979 Cambridge Jude Miller 8 | * 9 | * Score keeping modified and general terminal handling (termcap routines 10 | * from UCB's ex) added by Rob Coben, BTL, June, 1980. 11 | */ 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include "pacdefs.h" 18 | 19 | /* 20 | * global variables 21 | */ 22 | 23 | extern char 24 | message[]; 25 | 26 | extern char 27 | initbrd[BRDY][BRDX], 28 | display[BRDY][BRDX]; 29 | 30 | extern unsigned 31 | pscore; 32 | 33 | extern struct pac 34 | monst[]; 35 | 36 | extern char monst_names[]; 37 | 38 | int pacsymb = PACMAN, 39 | rounds, /* time keeping mechanism */ 40 | killflg, 41 | delay, 42 | potion, 43 | goldcnt, /* no. of gold pieces remaining */ 44 | game, 45 | monst_often, 46 | monsthere, 47 | boardcount = 1, 48 | wmonst, 49 | potintvl = POTINTVL, 50 | potioncnt, 51 | bcount = 0, 52 | /* attrs: normal monster, edible monster, pacman */ 53 | mflash, rflash, pflash, 54 | showcount, 55 | treascnt = 0; 56 | extern 57 | char *full_names[]; 58 | 59 | struct pac 60 | pac; 61 | 62 | struct pac 63 | pacstart = 64 | { 65 | PSTARTX, 66 | PSTARTY, 67 | DNULL, 68 | SLOW, 69 | FALSE, 70 | 0, 71 | 0, 72 | 0 73 | }; 74 | 75 | struct pac 76 | *pacptr = &pac; 77 | 78 | #define DEFCHPERTURN 60 79 | 80 | static void pacman(void); 81 | 82 | int main(argc, argv) 83 | int argc; 84 | char **argv; 85 | { 86 | struct pac *mptr; 87 | char msgbuf[50]; 88 | char gcnt[10]; 89 | long denom; 90 | int chperturn = DEFCHPERTURN; 91 | int monstcnt; /* monster number */ 92 | int tmp; /* temp variables */ 93 | int pac_cnt; 94 | int tries; 95 | int c; 96 | 97 | game = 0; 98 | 99 | pflash = A_BLINK | COLOR_PAIR (5); 100 | mflash = A_BOLD; 101 | rflash = A_REVERSE; 102 | 103 | while ((c = getopt(argc, argv, "cemhpn:")) != EOF) 104 | switch(c) { 105 | case 'c': 106 | showcount = 1; 107 | break; 108 | case 'e': 109 | game = 1; 110 | break; 111 | case 'm': 112 | game = 2; 113 | break; 114 | case 'h': 115 | game = 3; 116 | break; 117 | case 'p': 118 | mflash = pflash = rflash = 0; 119 | break; 120 | case 'n': 121 | chperturn = atoi(optarg); 122 | break; 123 | default: 124 | fprintf(stderr, "Usage: pacman -emh -p -n#\n"); 125 | exit(1); 126 | } 127 | 128 | init(); /* global init */ 129 | for (pac_cnt = MAXPAC; pac_cnt > 0; pac_cnt--) 130 | { 131 | redraw: 132 | erase(); 133 | potioncnt = 0; 134 | treascnt = 0; 135 | bcount = 0; 136 | potion = FALSE; 137 | SPLOT(0, 45, "SCORE: "); 138 | (void) sprintf(msgbuf, "GAME: %s", game==1 ? "EASY" : 139 | game==2 ? "MEDIUM" : 140 | "HARD"); 141 | SPLOT(0, 65, msgbuf); 142 | SPLOT(21, 45, "food left = "); 143 | (void) sprintf(gcnt, "%6d", goldcnt); 144 | SPLOT(21, 57, gcnt); 145 | 146 | /* 147 | * We update the monsters every monst_often turns, to keep 148 | * the CRT caught up with the computer. The fudge factor 149 | * was calculated from the assumption that each full syncscreen 150 | * outputs chperturn characters. The default is pessimistic 151 | * based on ANSI and HP terminals w/verbose cursor addressing. 152 | */ 153 | denom = ((long) delay) * baudrate(); 154 | monst_often = (chperturn * 10000L + denom - 1) / denom; 155 | if (monst_often < 1) 156 | monst_often = 1; 157 | 158 | if (potion == TRUE) 159 | { 160 | SPLOT(3, 45, "COUNTDOWN: "); 161 | (void) sprintf(message, "%2d", potioncnt); 162 | SPLOT(3, 60, message); 163 | }; 164 | pacsymb = PACMAN; 165 | killflg = FALSE; 166 | (void) sprintf(message, 167 | "delay = %3d, syncscreen = %3d", delay, monst_often); 168 | SPLOT(22, 45, message); 169 | /* 170 | * PLOT maze 171 | */ 172 | for (tmp = 0; tmp < BRDY; tmp++) 173 | { 174 | SPLOT(tmp, 0, &(display[tmp][0])); 175 | }; 176 | /* initialize a pacman */ 177 | pac = pacstart; 178 | PLOT(pacptr->ypos, pacptr->xpos, pacsymb | pflash); 179 | /* display remaining pacmen */ 180 | for (tmp = 0; tmp < pac_cnt - 1; tmp++) 181 | { 182 | PLOT(LINES >=24 ? 23 : 22, (MAXPAC * tmp), PACMAN); 183 | }; 184 | /* 185 | * Init. monsters 186 | */ 187 | for (mptr = &monst[0], monstcnt = 0; monstcnt < MAXMONSTER; mptr++, monstcnt++) 188 | { 189 | mptr->xpos = MSTARTX + (2 * monstcnt); 190 | mptr->ypos = MSTARTY; 191 | mptr->speed = SLOW; 192 | mptr->dirn = DNULL; 193 | mptr->danger = TRUE; 194 | mptr->stat = START; 195 | PLOT(mptr->ypos, mptr->xpos, monst_names[monstcnt] | COLOR_PAIR(monstcnt+1)); 196 | mptr->xdpos = mptr->xpos; 197 | mptr->ydpos = mptr->ypos; 198 | }; 199 | rounds = 0; /* timing mechanism */ 200 | update(); 201 | refresh(); 202 | tries = 0; 203 | while ((pacptr->dirn == DNULL) && (tries++ < 300)) 204 | { 205 | napms(100); 206 | pollch(1); 207 | } 208 | 209 | /* main game loop */ 210 | do 211 | { 212 | if (rounds++ % MSTARTINTVL == 0) 213 | { 214 | startmonst(); 215 | }; 216 | pacman(); 217 | if (killflg == TURKEY) 218 | break; 219 | for (monstcnt = 0; monstcnt < (MAXMONSTER / 2); monstcnt++) 220 | { 221 | monster(monstcnt); /* next monster */ 222 | }; 223 | if (killflg == TURKEY) 224 | break; 225 | if (pacptr->speed == FAST) 226 | { 227 | pacman(); 228 | if (killflg == TURKEY) 229 | break; 230 | }; 231 | for (monstcnt = (MAXMONSTER / 2); monstcnt < MAXMONSTER; monstcnt++) 232 | { 233 | monster(monstcnt); /* next monster */ 234 | }; 235 | if (killflg == TURKEY) 236 | break; 237 | if (potion == TRUE) 238 | { 239 | (void) sprintf(message, "%2d", potioncnt); 240 | SPLOT(3, 60, message); 241 | if (potioncnt == 10 || potioncnt < 5) 242 | beep(); 243 | if (--potioncnt <= 0) 244 | { 245 | SPLOT(3,45," "); 246 | SPLOT(4,45," "); 247 | SPLOT(5,45," "); 248 | potion = FALSE; 249 | pacptr->speed = SLOW; 250 | pacptr->danger = FALSE; 251 | for (monstcnt = 0; monstcnt < MAXMONSTER; monstcnt++) 252 | { 253 | monst[monstcnt].danger = TRUE; 254 | } 255 | } 256 | } 257 | if (bcount && --bcount == 0) { 258 | SPLOT(7,45," "); 259 | } 260 | if (treascnt && --treascnt == 0) { 261 | display[TRYPOS][TRXPOS] = VACANT; 262 | PLOT(TRYPOS, TRXPOS, VACANT); 263 | } 264 | if (rounds % monst_often == 0) 265 | update(); /* score display etc */ 266 | refresh(); 267 | if (goldcnt <= 0) 268 | { 269 | potintvl -= 5; 270 | if (potintvl <= 0) 271 | potintvl = 5; 272 | reinit(); 273 | goto redraw; 274 | }; 275 | } while (killflg != TURKEY); 276 | sprintf(msgbuf, "Oops! %s got you!\n", full_names[wmonst]); 277 | SPLOT(5, 45, msgbuf); 278 | flushinp(); 279 | refresh(); 280 | sleep(2); 281 | } 282 | SPLOT(8, 45, "THE MONSTERS ALWAYS TRIUMPH"); 283 | SPLOT(9, 45, "IN THE END!"); 284 | update(); 285 | refresh(); 286 | over(0); 287 | } 288 | 289 | static void pacman(void) 290 | { 291 | struct pac *mptr; 292 | char msgbuf[50]; 293 | int sqtype = VACANT; 294 | int mcnt; 295 | int tmpx, tmpy; 296 | int bscore; 297 | 298 | refresh(); 299 | 300 | /* pause; this is the main delay on each turn */ 301 | napms(delay); 302 | 303 | /* 304 | * Wait until .1 seconds or less of output in queue. 305 | * This is to make it work better on verbose terminals 306 | * at 1200 baud. 307 | */ 308 | // draino(100); 309 | tcdrain(STDOUT_FILENO); 310 | 311 | /* get command from player, but don't wait */ 312 | pollch(0); 313 | 314 | /* remember current pacman position */ 315 | tmpx = pacptr->xpos; 316 | tmpy = pacptr->ypos; 317 | 318 | /* "eat" any gold */ 319 | /* update display array to reflect what is on terminal */ 320 | display[tmpy][tmpx] = VACANT; 321 | 322 | /* what next? */ 323 | switch (pacptr->dirn) 324 | { 325 | case DUP: 326 | pacsymb = (rounds%2) ? CUP : PUP; 327 | switch (sqtype = display[tmpy + UPINT][tmpx]) 328 | { 329 | case GOLD: 330 | case VACANT: 331 | case CHOICE: 332 | case POTION: 333 | case TREASURE: 334 | 335 | /* erase where the pacman went */ 336 | PLOT(tmpy, tmpx, VACANT); 337 | pacptr->ypos += UPINT; 338 | break; 339 | 340 | default: 341 | pacptr->dirn = DNULL; 342 | pacsymb = PACMAN; 343 | break; 344 | }; 345 | break; 346 | case DDOWN: 347 | pacsymb = (rounds%2) ? CDOWN : PDOWN; 348 | switch (sqtype = display[tmpy + DOWNINT][tmpx]) 349 | { 350 | case GOLD: 351 | case VACANT: 352 | case CHOICE: 353 | case POTION: 354 | case TREASURE: 355 | 356 | /* erase where the pacman went */ 357 | PLOT(tmpy, tmpx, VACANT); 358 | pacptr->ypos += DOWNINT; 359 | break; 360 | 361 | default: 362 | pacptr->dirn = DNULL; 363 | pacsymb = PACMAN; 364 | break; 365 | }; 366 | break; 367 | case DLEFT: 368 | if(tmpx == 0) 369 | { 370 | /* erase where the pacman went */ 371 | PLOT(tmpy, tmpx, VACANT); 372 | pacptr->xpos = XWRAP; 373 | sqtype = VACANT; 374 | break; 375 | }; 376 | pacsymb = (rounds%2) ? CLEFT : PLEFT; 377 | switch (sqtype = display[tmpy][tmpx + LEFTINT]) 378 | { 379 | case GOLD: 380 | case VACANT: 381 | case CHOICE: 382 | case POTION: 383 | case TREASURE: 384 | 385 | /* erase where the pacman went */ 386 | PLOT(tmpy, tmpx, VACANT); 387 | pacptr->xpos += LEFTINT; 388 | break; 389 | 390 | default: 391 | pacptr->dirn = DNULL; 392 | pacsymb = PACMAN; 393 | break; 394 | }; 395 | break; 396 | case DRIGHT: 397 | if(tmpx == XWRAP) 398 | { 399 | /* erase where the pacman went */ 400 | PLOT(tmpy, tmpx, VACANT); 401 | pacptr->xpos = 0; 402 | sqtype = VACANT; 403 | break; 404 | }; 405 | pacsymb = (rounds%2) ? CRIGHT : PRIGHT; 406 | switch (sqtype = display[tmpy][tmpx + RIGHTINT]) 407 | { 408 | case GOLD: 409 | case VACANT: 410 | case CHOICE: 411 | case POTION: 412 | case TREASURE: 413 | 414 | /* erase where the pacman went */ 415 | PLOT(tmpy, tmpx, VACANT); 416 | pacptr->xpos += RIGHTINT; 417 | break; 418 | 419 | default: 420 | pacptr->dirn = DNULL; 421 | pacsymb = PACMAN; 422 | break; 423 | }; 424 | break; 425 | case DNULL: 426 | pacsymb = PACMAN; 427 | break; 428 | } 429 | 430 | /* did the pacman get any points or eat a potion? */ 431 | switch (sqtype) 432 | { 433 | case CHOICE: 434 | case GOLD: 435 | pscore += GOLDVAL; 436 | goldcnt--; 437 | break; 438 | 439 | case TREASURE: 440 | switch (boardcount) { 441 | case 0: 442 | case 1: bscore = 100; break; 443 | case 2: bscore = 200; break; 444 | case 3: case 4: bscore = 500; break; 445 | case 5: case 6: bscore = 700; break; 446 | case 7: case 8: bscore = 1000; break; 447 | default: 448 | case 9: case 10: bscore = 2000; break; 449 | } 450 | pscore += bscore; 451 | sprintf(msgbuf, "BONUS: %4d", bscore); 452 | SPLOT(7, 45, msgbuf); 453 | bcount = BINTVL; 454 | break; 455 | 456 | case POTION: 457 | SPLOT(3, 45, "COUNTDOWN: "); 458 | potion = TRUE; 459 | potioncnt = potintvl; 460 | monsthere = 0; 461 | pacptr->speed = FAST; 462 | pacptr->danger = TRUE; 463 | 464 | /* slow down monsters and make them harmless */ 465 | mptr = &monst[0]; 466 | for (mcnt = 0; mcnt < MAXMONSTER; mcnt++) 467 | { 468 | mptr->speed = SLOW; 469 | mptr->danger = FALSE; 470 | mptr++; 471 | } 472 | break; 473 | } 474 | 475 | /* did the pacman run into a monster? */ 476 | killflg = FALSE; 477 | for (mptr = &monst[0], mcnt = 0; mcnt < MAXMONSTER; mptr++, mcnt++) 478 | { 479 | if ((mptr->xpos==pacptr->xpos) && (mptr->ypos==pacptr->ypos)) 480 | killflg = dokill(mcnt); 481 | }; 482 | if (killflg != TURKEY) 483 | { 484 | PLOT(pacptr->ypos, pacptr->xpos, pacsymb | pflash); 485 | }; 486 | refresh(); 487 | } 488 | -------------------------------------------------------------------------------- /util.c: -------------------------------------------------------------------------------- 1 | #include "pacdefs.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | extern int 14 | delay, 15 | game, 16 | wmonst, 17 | boardcount, 18 | rounds, 19 | monsthere, 20 | potintvl, 21 | treascnt, 22 | bcount, 23 | showcount, 24 | goldcnt; 25 | 26 | extern struct pac 27 | *pacptr; 28 | 29 | extern struct pac 30 | monst[]; 31 | 32 | extern char monst_names[]; 33 | extern char *full_names[]; 34 | 35 | /* 36 | * initbrd is used to re-initialize the display 37 | * array once a new game is started. 38 | */ 39 | char initbrd[BRDY][BRDX] = 40 | { 41 | "#######################################", 42 | "# . . . * . . . . ### . . . . * . . . #", 43 | "# O ### . ##### . ### . ##### . ### O #", 44 | "# * . . * . * . * . . * . * . * . . * #", 45 | "# . ### . # . ########### . # . ### . #", 46 | "# . . . * # . . . ### . . . # * . . . #", 47 | "####### . ##### . ### . ##### . #######", 48 | " # . # . . * . . * . . # . # ", 49 | " # . # . ### - - ### . # . # ", 50 | "####### . # . # # . # . #######", 51 | " * . * # # * . * ", 52 | "####### . # . # # . # . #######", 53 | " # . # . ########### . # . # ", 54 | " # . # * . . . . . . * # . # ", 55 | "####### . # . ########### . # . #######", 56 | "# . . . * . * . . ### . . * . * . . . #", 57 | "# O ### . ##### . ### . ##### . ### O #", 58 | "# . . # * . * . * . . * . * . * # . . #", 59 | "### . # . # . ########### . # . # . ###", 60 | "# . * . . # . . . ### . . . # . . * . #", 61 | "# . ########### . ### . ########### . #", 62 | "# . . . . . . . * . . * . . . . . . . #", 63 | "#######################################", 64 | }; 65 | 66 | /* 67 | * brd is kept for historical reasons. 68 | * It should only be used in the routine "which" 69 | * to determine the next move for a monster or 70 | * in the routine "monster" to determine if it 71 | * was a valid move. Admittedly this is redundant 72 | * and could be replaced by initbrd, but it is kept 73 | * so that someday additional intelligence or 74 | * optimization could be added to the choice of 75 | * the monster's next move. Hence, note the symbol 76 | * CHOICE at most points that a move decision 77 | * logically HAS to be made. 78 | */ 79 | char brd[BRDY][BRDX] = 80 | { 81 | "#######################################", 82 | "# . . . * . . . . ### . . . . * . . . #", 83 | "# O ### . ##### . ### . ##### . ### O #", 84 | "# * . . * . * . * . . * . * . * . . * #", 85 | "# . ### . # . ########### . # . ### . #", 86 | "# . . . * # . . . ### . . . # * . . . #", 87 | "####### . ##### . ### . ##### . #######", 88 | " # . # . . * . . * . . # . # ", 89 | " # . # . ### - - ### . # . # ", 90 | "####### . # . # # . # . #######", 91 | " * . * # # * . * ", 92 | "####### . # . # # . # . #######", 93 | " # . # . ########### . # . # ", 94 | " # . # * . . . . . . * # . # ", 95 | "####### . # . ########### . # . #######", 96 | "# . . . * . * . . ### . . * . * . . . #", 97 | "# O ### . ##### . ### . ##### . ### O #", 98 | "# . . # * . * . * . . * . * . * # . . #", 99 | "### . # . # . ########### . # . # . ###", 100 | "# . * . . # . . . ### . . . # . . * . #", 101 | "# . ########### . ### . ########### . #", 102 | "# . . . . . . . * . . * . . . . . . . #", 103 | "#######################################", 104 | }; 105 | 106 | /* 107 | * display reflects the screen on the player's 108 | * terminal at any point in time. 109 | */ 110 | char display[BRDY][BRDX] = 111 | { 112 | "#######################################", 113 | "# . . . . . . . . ### . . . . . . . . #", 114 | "# O ### . ##### . ### . ##### . ### O #", 115 | "# . . . . . . . . . . . . . . . . . . #", 116 | "# . ### . # . ########### . # . ### . #", 117 | "# . . . . # . . . ### . . . # . . . . #", 118 | "####### . ##### . ### . ##### . #######", 119 | " # . # . . . . . . . . # . # ", 120 | " # . # . ### - - ### . # . # ", 121 | "####### . # . # # . # . #######", 122 | " . . . # # . . . ", 123 | "####### . # . # # . # . #######", 124 | " # . # . ########### . # . # ", 125 | " # . # . . . . . . . . # . # ", 126 | "####### . # . ########### . # . #######", 127 | "# . . . . . . . . ### . . . . . . . . #", 128 | "# O ### . ##### . ### . ##### . ### O #", 129 | "# . . # . . . . . . . . . . . . # . . #", 130 | "### . # . # . ########### . # . # . ###", 131 | "# . . . . # . . . ### . . . # . . . . #", 132 | "# . ########### . ### . ########### . #", 133 | "# . . . . . . . . . . . . . . . . . . #", 134 | "#######################################", 135 | }; 136 | 137 | int incharbuf; 138 | int bufstat; 139 | char message[81], /* temporary message buffer */ 140 | inbuf[2]; 141 | 142 | int ppid, 143 | cpid, 144 | killcnt = 0, 145 | vs_rows, 146 | vs_cols; 147 | 148 | unsigned 149 | pscore; 150 | 151 | long timein; 152 | 153 | struct uscore 154 | { 155 | unsigned score; /* same type as pscore */ 156 | int uid; /* uid of player */ 157 | }; 158 | 159 | struct scorebrd 160 | { 161 | struct uscore entry[MSSAVE]; 162 | } scoresave[MGTYPE]; 163 | 164 | void leave(void); 165 | 166 | void update(void) 167 | { 168 | char str[10]; 169 | 170 | sprintf(str, "%6d", pscore); 171 | SPLOT(0, 52, str); 172 | sprintf(str, "%6d", goldcnt); 173 | SPLOT(21, 57, str); 174 | } 175 | 176 | void reinit(void) 177 | { 178 | int locx, locy; 179 | char tmp; 180 | 181 | if (boardcount % 2 == 0) 182 | movie(); 183 | for (locy = 0; locy < BRDY; locy++) 184 | { 185 | for (locx = 0; locx < BRDX; locx++) 186 | { 187 | tmp = initbrd[locy][locx]; 188 | brd[locy][locx] = tmp; 189 | if ((display[locy][locx] = tmp) == CHOICE) 190 | { 191 | display[locy][locx] = GOLD; 192 | }; 193 | }; 194 | }; 195 | goldcnt = GOLDCNT; 196 | delay = delay * 3 / 4; /* hot it up */ 197 | boardcount++; 198 | } 199 | 200 | void errgen(string) 201 | char *string; 202 | { 203 | SPLOT(23,45,string); 204 | } 205 | 206 | int dokill(mnum) 207 | int mnum; 208 | { 209 | struct pac *mptr; 210 | char msgbuf[50]; 211 | int bscore; 212 | 213 | beep(); 214 | if (monst[mnum].danger == FALSE) 215 | { 216 | if (++killcnt == MAXMONSTER) 217 | { 218 | if (display[TRYPOS][TRXPOS] == GOLD) 219 | { 220 | goldcnt--; 221 | }; 222 | display[TRYPOS][TRXPOS] = TREASURE; 223 | PLOT(TRYPOS, TRXPOS, TREASURE); 224 | killcnt = 0; 225 | treascnt = potintvl; 226 | } 227 | SPLOT(5, 45, "MONSTERS KILLED: "); 228 | (void) sprintf(message, "%1d", killcnt); 229 | SPLOT(5, 62, message); 230 | mptr = (&monst[mnum]); 231 | mptr->ypos = MSTARTY; 232 | mptr->xpos = MSTARTX + (2 * mnum); 233 | mptr->danger = TRUE; 234 | mptr->stat = START; 235 | PLOT(mptr->ypos, mptr->xpos, monst_names[mnum] | COLOR_PAIR(mnum+1)); 236 | monsthere++; 237 | rounds = 1; /* force it to be a while before he comes out */ 238 | switch (monsthere) { 239 | case 1: bscore = KILLSCORE; break; 240 | case 2: bscore = 2 * KILLSCORE; break; 241 | case 3: bscore = 4 * KILLSCORE; break; 242 | case 4: bscore = 8 * KILLSCORE; break; 243 | } 244 | pscore += bscore; 245 | bcount = BINTVL; 246 | sprintf(msgbuf, "BONUS: %4d", bscore); 247 | SPLOT(7, 45, msgbuf); 248 | sprintf(msgbuf, "You got %s!\n", full_names[mnum]); 249 | SPLOT(4, 45, msgbuf); 250 | return(GOTONE); 251 | }; 252 | wmonst = mnum; 253 | return(TURKEY); 254 | } 255 | 256 | /* 257 | * clr -- issues an escape sequence to clear the display 258 | */ 259 | 260 | void clr(void) 261 | { 262 | clear(); 263 | } 264 | 265 | /* 266 | * display initial instructions 267 | */ 268 | 269 | void instruct(void) 270 | { 271 | clr(); 272 | POS(0, 0); 273 | printw("Attention: you are in a maze, being chased by monsters!\n\n"); 274 | printw("There is food scattered uniformly in the maze, marked by \".\".\n"); 275 | printw("One magic potion is available at each spot marked \"O\". Each potion will\n"); 276 | printw("enable you to eat monsters for a limited duration. It will also\n"); 277 | printw("scare them away. When you eat a monster it is regenerated, but this takes\n"); 278 | printw("time. You can also regenerate yourself %d times. Eating all the monsters\n", MAXPAC); 279 | printw("results in further treasure appearing magically somewhere in the dungeon,\n"); 280 | printw("marked by \"$\". There is a magic tunnel connecting the center left and\n"); 281 | printw("center right parts of the dungeon. The monsters know about it!\n\n"); 282 | printw(" Type: h or s to move left\n"); 283 | printw(" l or f to move right\n"); 284 | printw(" k or e to move up\n"); 285 | printw(" j or c to move down\n"); 286 | printw(" to halt \n"); 287 | printw(" q to quit\n\n"); 288 | printw(" Type: 1 easy game\n"); 289 | printw(" 2 intelligent monsters\n"); 290 | printw(" 3 very intelligent monsters\n"); 291 | refresh(); 292 | } 293 | 294 | /* 295 | * over -- game over processing 296 | */ 297 | 298 | void over(int signo) 299 | { 300 | struct passwd *p; 301 | int scorefile = 0; 302 | int line, col; 303 | int i; 304 | 305 | (void)signo; 306 | refresh(); 307 | signal(SIGINT, SIG_IGN); 308 | /* high score to date processing */ 309 | if (game != 0) 310 | { 311 | col = 45; 312 | line = 10; 313 | POS(line++, col); 314 | (void) printw(" ___________________________ "); 315 | POS(line++, col); 316 | (void) printw("| G A M E O V E R |"); 317 | POS(line++, col); 318 | (void) printw("| |"); 319 | POS(line++, col); 320 | (void) printw("| Game type: %6.6s |",game==1?"easy":game==2?"medium":"smart"); 321 | 322 | scorefile = open(MAXSCORE, 2); 323 | if (scorefile != -1) 324 | { 325 | int rc; 326 | 327 | rc = read(scorefile, (char *)scoresave, sizeof(scoresave)); 328 | if (rc == -1) 329 | warn("Failed reading score file %s", MAXSCORE); 330 | 331 | for (i = MSSAVE - 1; i >= 0; i--) { 332 | if (scoresave[game - 1].entry[i].score < pscore) 333 | { 334 | if (i < MSSAVE - 1) 335 | { 336 | scoresave[game - 1].entry[i + 1].score = 337 | scoresave[game - 1].entry[i].score; 338 | scoresave[game - 1].entry[i + 1].uid = 339 | scoresave[game - 1].entry[i].uid; 340 | }; 341 | scoresave[game - 1].entry[i].score = pscore; 342 | scoresave[game - 1].entry[i].uid = getuid(); 343 | }; 344 | }; 345 | lseek(scorefile, 0l, 0); 346 | rc = write(scorefile, (char *)scoresave, sizeof(scoresave)); 347 | if (rc == -1) 348 | warn("Failed writing score file %s", MAXSCORE); 349 | close(scorefile); 350 | POS(line++, col); 351 | (void) printw("| High Scores to date: |"); 352 | for (i = 0; i < MSSAVE; i++) 353 | { 354 | setpwent(); 355 | p = getpwuid(scoresave[game - 1].entry[i].uid); 356 | POS(line++, col); 357 | (void) printw("| Player : %-8s %5u |", p->pw_name, 358 | scoresave[game - 1].entry[i].score); 359 | }; 360 | } 361 | else 362 | { 363 | POS(line++, col); 364 | (void) printw("| |"); 365 | POS(line++, col); 366 | (void) printw("| Please create a 'paclog' |"); 367 | POS(line++, col); 368 | (void) printw("| file. See 'MAXSCORE' in |"); 369 | POS(line++, col); 370 | (void) printw("| 'pacdefs.h'. |"); 371 | }; 372 | POS(line, col); 373 | (void) printw("|___________________________|"); 374 | }; 375 | refresh(); 376 | leave(); 377 | } 378 | 379 | /* 380 | * leave -- flush buffers,kill the Child, reset tty, and delete tempfile 381 | */ 382 | 383 | void leave(void) 384 | { 385 | leaveok(stdscr, FALSE); 386 | POS(23, 0); 387 | refresh(); 388 | endwin(); 389 | exit(0); 390 | } 391 | 392 | /* 393 | * init -- does global initialization and spawns a child process to read 394 | * the input terminal. 395 | */ 396 | 397 | void init(void) 398 | { 399 | int tries = 0; 400 | 401 | errno = 0; 402 | (void) time(&timein); /* get start time */ 403 | srand((unsigned)timein); /* start rand randomly */ 404 | signal(SIGINT, over); 405 | signal(SIGQUIT, over); 406 | 407 | /* Curses init - could probably eliminate much of stuff below */ 408 | initscr(); 409 | if ((start_color()) == OK) 410 | { 411 | init_pair (1, COLOR_YELLOW, COLOR_BLUE); 412 | init_pair (2, COLOR_BLUE, COLOR_YELLOW); 413 | init_pair (3, COLOR_YELLOW, COLOR_GREEN); 414 | init_pair (4, COLOR_MAGENTA, COLOR_CYAN); 415 | init_pair (5, COLOR_YELLOW, COLOR_RED); 416 | } 417 | noecho(); 418 | crmode(); 419 | nonl(); 420 | leaveok(stdscr, TRUE); 421 | keypad(stdscr, TRUE); 422 | nodelay(stdscr, TRUE); 423 | vs_rows = LINES; 424 | vs_cols = COLS; 425 | 426 | if (delay == 0) 427 | delay = 500; /* number of ticks per turn */ 428 | 429 | /* 430 | * New game starts here 431 | */ 432 | if (game == 0) 433 | instruct(); 434 | while ((game == 0) && (tries++ < 300)) 435 | { 436 | napms(100); 437 | pollch(1); 438 | }; 439 | if (tries >= 300) 440 | { 441 | /* I give up. Let's call it quits. */ 442 | leave(); 443 | }; 444 | goldcnt = GOLDCNT; 445 | pscore = 0; 446 | clr(); 447 | } 448 | 449 | /* 450 | * poll -- read characters sent by input subprocess and set global flags 451 | */ 452 | 453 | void pollch(sltime) 454 | int sltime; 455 | { 456 | int stop; 457 | int c; 458 | 459 | (void)sltime; 460 | stop = 0; 461 | readin: 462 | 463 | refresh(); 464 | if (bufstat == EMPTY) { 465 | c = getch(); 466 | if (c < 0) { 467 | bufstat = EMPTY; 468 | } else { 469 | bufstat = FULL; 470 | incharbuf = c; 471 | } 472 | } 473 | 474 | if (bufstat == EMPTY) 475 | { 476 | if (stop) 477 | { 478 | goto readin; 479 | }; 480 | return; 481 | }; 482 | bufstat = EMPTY; 483 | 484 | switch(incharbuf) 485 | { 486 | case LEFT: 487 | case NLEFT: 488 | case KEY_LEFT: 489 | pacptr->dirn = DLEFT; 490 | break; 491 | 492 | case RIGHT: 493 | case NRIGHT: 494 | case KEY_RIGHT: 495 | pacptr->dirn = DRIGHT; 496 | break; 497 | 498 | case NORTH: 499 | case NNORTH: 500 | case KEY_UP: 501 | pacptr->dirn = DUP; 502 | break; 503 | 504 | case DOWN: 505 | case NDOWN: 506 | case KEY_DOWN: 507 | pacptr->dirn = DDOWN; 508 | break; 509 | 510 | case HALT: 511 | case KEY_HOME: 512 | pacptr->dirn = DNULL; 513 | break; 514 | 515 | case REDRAW: 516 | clearok(curscr, TRUE); 517 | // draino(0); 518 | tcdrain(STDOUT_FILENO); 519 | break; 520 | 521 | case ABORT: 522 | case DELETE: 523 | case QUIT: 524 | over(0); 525 | break; 526 | 527 | case 'S': 528 | stop = 1; 529 | goto readin; 530 | 531 | case 'G': 532 | stop = 0; 533 | goto readin; 534 | 535 | case GAME1: 536 | if (game == 0) 537 | game = 1; 538 | break; 539 | 540 | case GAME2: 541 | if (game == 0) 542 | game = 2; 543 | break; 544 | 545 | case GAME3: 546 | if (game == 0) 547 | game = 3; 548 | break; 549 | 550 | default: 551 | goto readin; 552 | } 553 | } 554 | 555 | unsigned int getrand(range) 556 | int range; 557 | { 558 | unsigned int q; 559 | 560 | q = rand(); 561 | return(q % range); 562 | } 563 | 564 | #define FIRSTMSGLINE 13 565 | #define LASTMSGLINE 13 566 | /* 567 | * This function is convenient for debugging pacman. It isn't used elsewhere. 568 | * It's like printf and prints in a window on the right hand side of the screen. 569 | */ 570 | void msgf(fmt, arg1, arg2, arg3, arg4) 571 | char *fmt; 572 | int arg1, arg2, arg3, arg4; 573 | { 574 | char msgbuf[100]; 575 | static char msgline = FIRSTMSGLINE; 576 | 577 | sprintf(msgbuf, fmt, arg1, arg2, arg3, arg4); 578 | SPLOT(msgline, 45, msgbuf); 579 | if (++msgline > LASTMSGLINE) 580 | msgline = FIRSTMSGLINE; 581 | } 582 | --------------------------------------------------------------------------------