/* * wumpus.c - mdb loadable module that provides ::wumpus dcmd. Solaris. * * This provides a new debugger command for the Solaris modular debugger, mdb. * The command is ::wumpus, which plays the classic game "Hunt the Wumpus". * * The game is true to one of the original versions. The source is from * Eric S Raymond's wumpus-1.3 (http://freshmeat.net/projects/wumpus-classic, * his homepage is http://www.catb.org/~esr). * * 24-Sep-2005, ver 0.70 (first release!) * * INSTALL: * cc, * cc -v -xstrconst -K pic -D_KERNEL -c wumpus.c * cc -v -xstrconst -K pic -G -o wumpus.so wumpus.o * cp -i wumpus.so /usr/lib/mdb/proc * gcc, * gcc -v -fPIC -D_KERNEL -c wumpus.c * gcc -v -G -o wumpus.so wumpus.o * cp -i wumpus.so /usr/lib/mdb/proc * * USAGE: * $ mdb * > ::load wumpus * > ::wumpus * INSTRUCTIONS (Y-N) * ? * * THANKS: Gary Riseborough, mdb module writing hints. * * PORTIONS: Copyright (c) 2005 Brendan Gregg. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * (http://www.gnu.org/copyleft/gpl.html) * * 24-Sep-2005 Brendan Gregg Created this. */ #include #include #include #include #include #include int toupper(int c); /* * HUNT THE WUMPUS * * This source is from Eric S Raymond's wumpus-1.3. I've adjusted some * statements to work better with mdb, and trimmed many comment lines which * contained an original BASIC version. (The BASIC code did serve to explain * the unusual style, in particular numerous goto statements). * * Since this code is the bulk of this program, I've followed the style * elsewhere (indentation, e.t.c.). */ static int path[5]; static int j, k, arrows, scratchloc; static char inp[BUFSIZ]; /* common input buffer */ #define YOU 0 #define WUMPUS 1 #define PIT1 2 #define PIT2 3 #define BATS1 4 #define BATS2 5 #define LOCS 6 static int loc[LOCS], save[LOCS]; /* locations */ #define NOT 0 #define WIN 1 #define LOSE -1 static int finished; static int cave[20][3] = { {1,4,7}, {0,2,9}, {1,3,11}, {2,4,13}, {0,3,5}, {4,6,14}, {5,7,16}, {0,6,8}, {7,9,17}, {1,8,10}, {9,11,18}, {2,10,12}, {11,13,19}, {3,12,14}, {5,13,15}, {14,16,19}, {6,15,17}, {8,16,18}, {10,17,19}, {12,15,18}, }; #define FNA() (rand() % 20) #define FNB() (rand() % 3) #define FNC() (rand() % 4) int getnum(prompt) char *prompt; { /* * the following printf should be mdb_printf to obey the mdb API more * closely, however mdb_printf sends output to a pager which prevents * printing of prompts. */ (void) printf("%s\n?", prompt); /* we use fgets as there is no mdb_fgets or suitable alternative */ if (fgets(inp, sizeof(inp), stdin)) return(atoi(inp)); else { mdb_printf("\n"); exit(1); } } int getlet(prompt) char *prompt; { (void) printf("%s\n?", prompt); if (fgets(inp, sizeof(inp), stdin)) return(toupper(inp[0])); else { mdb_printf("\n"); exit(1); } } void print_instructions() { char ebuf[BUFSIZ]; mdb_printf("WELCOME TO 'HUNT THE WUMPUS'\n"); mdb_printf(" THE WUMPUS LIVES IN A CAVE OF 20 ROOMS. EACH ROOM\n"); mdb_printf("HAS 3 TUNNELS LEADING TO OTHER ROOMS. (LOOK AT A\n"); mdb_printf("DODECAHEDRON TO SEE HOW THIS WORKS-IF YOU DON'T KNOW\n"); mdb_printf("WHAT A DODECAHEDRON IS, ASK SOMEONE)\n"); mdb_printf("\n"); mdb_printf(" HAZARDS:\n"); mdb_printf(" BOTTOMLESS PITS - TWO ROOMS HAVE BOTTOMLESS PITS IN THEM\n"); mdb_printf(" IF YOU GO THERE, YOU FALL INTO THE PIT (& LOSE!)\n"); mdb_printf(" SUPER BATS - TWO OTHER ROOMS HAVE SUPER BATS. IF YOU\n"); mdb_printf(" GO THERE, A BAT GRABS YOU AND TAKES YOU TO SOME OTHER\n"); mdb_printf(" ROOM AT RANDOM. (WHICH MAY BE TROUBLESOME)\n"); (void) getlet("TYPE AN E THEN RETURN "); mdb_printf(" WUMPUS:\n"); mdb_printf(" THE WUMPUS IS NOT BOTHERED BY HAZARDS (HE HAS SUCKER\n"); mdb_printf(" FEET AND IS TOO BIG FOR A BAT TO LIFT). USUALLY\n"); mdb_printf(" HE IS ASLEEP. TWO THINGS WAKE HIM UP: YOU SHOOTING AN\n"); mdb_printf("ARROW OR YOU ENTERING HIS ROOM.\n"); mdb_printf(" IF THE WUMPUS WAKES HE MOVES (P=.75) ONE ROOM\n"); mdb_printf(" OR STAYS STILL (P=.25). AFTER THAT, IF HE IS WHERE YOU\n"); mdb_printf(" ARE, HE EATS YOU UP AND YOU LOSE!\n"); mdb_printf("\n"); mdb_printf(" YOU:\n"); mdb_printf(" EACH TURN YOU MAY MOVE OR SHOOT A CROOKED ARROW\n"); mdb_printf(" MOVING: YOU CAN MOVE ONE ROOM (THRU ONE TUNNEL)\n"); mdb_printf(" ARROWS: YOU HAVE 5 ARROWS. YOU LOSE WHEN YOU RUN OUT\n"); mdb_printf(" EACH ARROW CAN GO FROM 1 TO 5 ROOMS. YOU AIM BY TELLING\n"); mdb_printf(" THE COMPUTER THE ROOM#S YOU WANT THE ARROW TO GO TO.\n"); mdb_printf(" IF THE ARROW CAN'T GO THAT WAY (IF NO TUNNEL) IT MOVES\n"); mdb_printf(" AT RANDOM TO THE NEXT ROOM.\n"); mdb_printf(" IF THE ARROW HITS THE WUMPUS, YOU WIN.\n"); mdb_printf(" IF THE ARROW HITS YOU, YOU LOSE.\n"); (void) getlet("TYPE AN E THEN RETURN "); mdb_printf(" WARNINGS:\n"); mdb_printf(" WHEN YOU ARE ONE ROOM AWAY FROM A WUMPUS OR HAZARD,\n"); mdb_printf(" THE COMPUTER SAYS:\n"); mdb_printf(" WUMPUS: 'I SMELL A WUMPUS'\n"); mdb_printf(" BAT : 'BATS NEARBY'\n"); mdb_printf(" PIT : 'I FEEL A DRAFT'\n"); mdb_printf("\n"); } void check_hazards() { (void) mdb_printf("\n"); for (k = 0; k < 3; k++) { int room = cave[loc[YOU]][k]; if (room == loc[WUMPUS]) (void) mdb_printf("I SMELL A WUMPUS!\n"); else if (room == loc[PIT1] || room == loc[PIT2]) (void) mdb_printf("I FEEL A DRAFT\n"); else if (room == loc[BATS1] || room == loc[BATS2]) (void) mdb_printf("BATS NEARBY!\n"); } (void) mdb_printf("YOU ARE IN ROOM %d\n", loc[YOU]+1); (void) mdb_printf("TUNNELS LEAD TO %d %d %d\n", cave[loc[YOU]][0]+1, cave[loc[YOU]][1]+1, cave[loc[YOU]][2]+1); (void) mdb_printf("\n"); } int move_or_shoot() { int c; badin: c = getlet("SHOOT OR MOVE (S-M)"); if (c == 'S') return(1); else if (c == 'M') return(0); else goto badin; } void shoot() { extern void check_shot(), move_wumpus(); int j9; finished = NOT; badrange: j9 = getnum("NO. OF ROOMS (1-5)"); if (j9 < 1 || j9 > 5) goto badrange; for (k = 0; k < j9; k++) { path[k] = getnum("ROOM #") - 1; if (k <= 1) continue; if (path[k] != path[k - 2]) continue; (void) mdb_printf("ARROWS AREN'T THAT CROOKED - TRY ANOTHER ROOM\n"); k--; } scratchloc = loc[YOU]; for (k = 0; k < j9; k++) { int k1; for (k1 = 0; k1 < 3; k1++) { if (cave[scratchloc][k1] == path[k]) { /* * This is the only bit of the translation I'm not sure * about. It requires the trajectory of the arrow to * be a path. Without it, all rooms on the trajectory * would be required by the above to be adjacent to the * player, making for a trivial game --- just move to where * you smell a wumpus and shoot into all adjacent passages! * However, I can't find an equivalent in the BASIC. * (ESR) */ scratchloc = path[k]; /* this simulates logic at 895 in the BASIC code */ check_shot(); if (finished != NOT) return; } } scratchloc = cave[scratchloc][FNB()]; check_shot(); } ammo: if (finished == NOT) { (void) mdb_printf("MISSED\n"); scratchloc = loc[YOU]; move_wumpus(); if (--arrows <= 0) finished = LOSE; } } void check_shot() { if (scratchloc == loc[WUMPUS]) { (void) mdb_printf("AHA! YOU GOT THE WUMPUS!\n"); finished = WIN; } else if (scratchloc == loc[YOU]) { (void) mdb_printf("OUCH! ARROW GOT YOU!\n"); finished = LOSE; } } void move_wumpus() { k = FNC(); if (k < 3) loc[WUMPUS] = cave[loc[WUMPUS]][k]; if (loc[WUMPUS] != loc[YOU]) return; (void) mdb_printf("TSK TSK TSK - WUMPUS GOT YOU!\n"); finished = LOSE; } void move_player() { finished = NOT; badmove: scratchloc = getnum("WHERE TO"); if (scratchloc < 1 || scratchloc > 20) goto badmove; scratchloc--; for (k = 0; k < 3; k++) { if (cave[loc[YOU]][k] == scratchloc) goto goodmove; } if (scratchloc != loc[YOU]) { (void) mdb_printf("NOT POSSIBLE -\n"); goto badmove; } goodmove: loc[YOU] = scratchloc; if (scratchloc == loc[WUMPUS]) { (void) mdb_printf("... OOPS! BUMPED A WUMPUS!\n"); move_wumpus(); } else if (scratchloc == loc[PIT1] || scratchloc == loc[PIT2]) { (void) mdb_printf("YYYYIIIIEEEE . . . FELL IN PIT\n"); finished = LOSE; } else if (scratchloc == loc[BATS1] || scratchloc == loc[BATS2]) { (void) mdb_printf("ZAP--SUPER BAT SNATCH! ELSEWHEREVILLE FOR YOU!\n"); scratchloc = loc[YOU] = FNA(); goto goodmove; } } /* * mdb ::wumpus dcmd */ static int wumpus (uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) { int c; srand((int)time((long *) 0)); c = getlet("INSTRUCTIONS (Y-N)"); if (c == 'Y') print_instructions(); badlocs: for (j = 0; j < LOCS; j++) loc[j] = save[j] = FNA(); for (j = 0; j < LOCS; j++) for (k = 0; k < LOCS; k++) if (j == k) continue; else if (loc[j] == loc[k]) goto badlocs; newgame: arrows = 5; scratchloc = loc[YOU]; (void) mdb_printf("HUNT THE WUMPUS\n"); #ifdef DEBUG (void) mdb_printf("Wumpus is at %d, pits at %d & %d, bats at %d & %d\n", loc[WUMPUS]+1, loc[PIT1]+1, loc[PIT2]+1, loc[BATS1]+1, loc[BATS2]+1); #endif nextmove: check_hazards(); if (move_or_shoot()) { shoot(); if (finished == NOT) goto nextmove; } else { move_player(); if (finished == NOT) goto nextmove; } if (finished == LOSE) { (void) mdb_printf("HA HA HA - YOU LOSE!\n"); } else { (void) mdb_printf("HEE HEE HEE - THE WUMPUS'LL GET YOU NEXT TIME!!\n"); } for (j = YOU; j < LOCS; j++) loc[j] = save[j]; /* * I've added the question "ANOTHER GAME (Y-N)" to provide a way * to quit back to mdb (ok, Ctrl-C actually works too). * (BDG) */ c = getlet("ANOTHER GAME (Y-N)"); if (c == 'Y') { c = getlet("SAME SETUP (Y-N)"); if (c != 'Y') goto badlocs; else goto newgame; } return(DCMD_OK); } /* * Other mdb odds and ends */ static const mdb_dcmd_t dcmds[] = { { "wumpus", "hunt_the_wumpus", "Play Hunt the Wumpus", wumpus }, { NULL } }; static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, NULL }; const mdb_modinfo_t *_mdb_init ( void ) { return(&modinfo); }