2 * wumpus.c --- a faithful translation of the classic "Hunt The Wumpus" game.
4 * Translator: Eric S. Raymond <esr@snark.thyrsus.com>
5 * Version: $Id: wumpus.c,v 1.4 1996/05/17 17:30:35 esr Exp esr $
7 * The BASIC source is that posted by Magnus Olsson in USENET article
8 * <9207071854.AA21847@thep.lu.se>: he wrote
10 * >Below is the source code for _one_ (rather simple) Wumpus version,
11 * >which I found in the PC-BLUE collection on SIMTEL20. I believe this is
12 * >pretty much the same version that was published in David Ahl's "101
13 * >Basic Computer Games" (or, possibly, in the sequel).
15 * I have staunchly resisted the temptation to "improve" this game. It
16 * is functionally identical to the BASIC version (source for which
17 * appears in the comments). I fixed some typos in the help text.
19 * Language hackers may be interested to know that he most difficult thing
20 * about the translation was tracking the details required to translate from
21 * 1-origin to 0-origin array indexing.
23 * The only enhancement is a an -s command-line switch for setting the
26 * So, pretend for a little while that your workstation is an ASR-33 and
27 * limber up your fingers for a trip to nostalgia-land...
29 * SPDX-License-Identifier: BSD-2-Clause
30 * SPDX-FileCopyrightText: (C) Eric S. Raymond <esr@thyrsus.com>
37 #include <sys/socket.h>
41 /* 5 REM *** HUNT THE WUMPUS *** */
46 static int j, k, arrows, scratchloc;
47 static char inp[BUFSIZ]; /* common input buffer */
56 static int loc[LOCS], save[LOCS]; /* locations */
63 /* 80 REM *** SET UP CAVE (DODECAHEDRAL NODE LIST) *** */
65 /* 90 FOR J=1 TO 20 */
70 /* 115 DATA 2,5,8,1,3,10,2,4,12,3,5,14,1,4,6 */
71 /* 120 DATA 5,7,15,6,8,17,1,7,9,8,10,18,2,9,11 */
72 /* 125 DATA 10,12,19,3,11,13,12,14,20,4,13,15,6,14,16 */
73 /* 130 DATA 15,17,20,7,16,18,9,17,19,11,18,20,13,16,19 */
74 static int cave[20][3] = {
75 {1, 4, 7}, {0, 2, 9}, {1, 3, 11}, {2, 4, 13}, {0, 3, 5},
76 {4, 6, 14}, {5, 7, 16}, {0, 6, 8}, {7, 9, 17}, {1, 8, 10},
77 {9, 11, 18}, {2, 10, 12}, {11, 13, 19}, {3, 12, 14}, {5, 13, 15},
78 {14, 16, 19}, {6, 15, 17}, {8, 16, 18}, {10, 17, 19}, {12, 15, 18},
81 /* 135 DEF FNA(X)=INT(20*RND(1))+1 */
82 #define FNA() (rand() % 20)
84 /* 140 DEF FNB(X)=INT(3*RND(1))+1 */
85 #define FNB() (rand() % 3)
87 /* 145 DEF FNC(X)=INT(4*RND(1))+1 */
88 #define FNC() (rand() % 4)
93 (void)printf("%s\n?", prompt);
94 if (fgets(inp, sizeof(inp), stdin))
105 (void)printf("%s\n?", prompt);
106 if (fgets(inp, sizeof(inp), stdin))
107 return (toupper(inp[0]));
114 void print_instructions() {
115 /* 375 REM *** INSTRUCTIONS *** */
116 /* 380 PRINT "WELCOME TO 'HUNT THE WUMPUS'" */
117 puts("WELCOME TO 'HUNT THE WUMPUS'");
118 /* 385 PRINT " THE WUMPUS LIVES IN A CAVE OF 20 ROOMS. EACH ROOM"
120 puts(" THE WUMPUS LIVES IN A CAVE OF 20 ROOMS. EACH ROOM");
121 /* 390 PRINT "HAS 3 TUNNELS LEADING TO OTHER ROOMS. (LOOK AT A"
123 puts("HAS 3 TUNNELS LEADING TO OTHER ROOMS. (LOOK AT A");
124 /* 395 PRINT "DODECAHEDRON TO SEE HOW THIS WORKS-IF YOU DON'T KNOW"
126 puts("DODECAHEDRON TO SEE HOW THIS WORKS-IF YOU DON'T KNOW");
127 /* 400 PRINT "WHAT A DODECAHEDRON IS, ASK SOMEONE)" */
128 puts("WHAT A DODECAHEDRON IS, ASK SOMEONE)");
131 /* 410 PRINT " HAZARDS:" */
133 /* 415 PRINT " BOTTOMLESS PITS - TWO ROOMS HAVE BOTTOMLESS PITS IN THEM
135 puts(" BOTTOMLESS PITS - TWO ROOMS HAVE BOTTOMLESS PITS IN THEM");
136 /* 420 PRINT " IF YOU GO THERE, YOU FALL INTO THE PIT (& LOSE!)"
138 puts(" IF YOU GO THERE, YOU FALL INTO THE PIT (& LOSE!)");
139 /* 425 PRINT " SUPER BATS - TWO OTHER ROOMS HAVE SUPER BATS. IF YOU"
141 puts(" SUPER BATS - TWO OTHER ROOMS HAVE SUPER BATS. IF YOU");
142 /* 430 PRINT " GO THERE, A BAT GRABS YOU AND TAKES YOU TO SOME
144 puts(" GO THERE, A BAT GRABS YOU AND TAKES YOU TO SOME OTHER");
145 /* 435 PRINT " ROOM AT RANDOM. (WHICH MAY BE TROUBLESOME)"
147 puts(" ROOM AT RANDOM. (WHICH MAY BE TROUBLESOME)");
148 /* 440 INPUT "TYPE AN E THEN RETURN ";W9 */
149 (void)getlet("TYPE AN E THEN RETURN ");
150 /* 445 PRINT " WUMPUS:" */
152 /* 450 PRINT " THE WUMPUS IS NOT BOTHERED BY HAZARDS (HE HAS SUCKER"
154 puts(" THE WUMPUS IS NOT BOTHERED BY HAZARDS (HE HAS SUCKER");
155 /* 455 PRINT " FEET AND IS TOO BIG FOR A BAT TO LIFT). USUALLY"
157 puts(" FEET AND IS TOO BIG FOR A BAT TO LIFT). USUALLY");
158 /* 460 PRINT " HE IS ASLEEP. TWO THINGS WAKE HIM UP: YOU SHOOTING AN"
160 puts(" HE IS ASLEEP. TWO THINGS WAKE HIM UP: YOU SHOOTING AN");
161 /* 465 PRINT "ARROW OR YOU ENTERING HIS ROOM." */
162 puts("ARROW OR YOU ENTERING HIS ROOM.");
163 /* 470 PRINT " IF THE WUMPUS WAKES HE MOVES (P=.75) ONE ROOM"
165 puts(" IF THE WUMPUS WAKES HE MOVES (P=.75) ONE ROOM");
166 /* 475 PRINT " OR STAYS STILL (P=.25). AFTER THAT, IF HE IS WHERE YOU"
168 puts(" OR STAYS STILL (P=.25). AFTER THAT, IF HE IS WHERE YOU");
169 /* 480 PRINT " ARE, HE EATS YOU UP AND YOU LOSE!" */
170 puts(" ARE, HE EATS YOU UP AND YOU LOSE!");
173 /* 490 PRINT " YOU:" */
175 /* 495 PRINT " EACH TURN YOU MAY MOVE OR SHOOT A CROOKED ARROW"
177 puts(" EACH TURN YOU MAY MOVE OR SHOOT A CROOKED ARROW");
178 /* 500 PRINT " MOVING: YOU CAN MOVE ONE ROOM (THRU ONE TUNNEL)"
180 puts(" MOVING: YOU CAN MOVE ONE ROOM (THRU ONE TUNNEL)");
181 /* 505 PRINT " ARROWS: YOU HAVE 5 ARROWS. YOU LOSE WHEN YOU RUN OUT
183 puts(" ARROWS: YOU HAVE 5 ARROWS. YOU LOSE WHEN YOU RUN OUT");
184 /* 510 PRINT " EACH ARROW CAN GO FROM 1 TO 5 ROOMS. YOU AIM BY
186 puts(" EACH ARROW CAN GO FROM 1 TO 5 ROOMS. YOU AIM BY TELLING");
187 /* 515 PRINT " THE COMPUTER THE ROOM#S YOU WANT THE ARROW TO GO TO."
189 puts(" THE COMPUTER THE ROOM#S YOU WANT THE ARROW TO GO TO.");
190 /* 520 PRINT " IF THE ARROW CAN'T GO THAT WAY (IF NO TUNNEL) IT
192 puts(" IF THE ARROW CAN'T GO THAT WAY (IF NO TUNNEL) IT MOVES");
193 /* 525 PRINT " AT RANDOM TO THE NEXT ROOM." */
194 puts(" AT RANDOM TO THE NEXT ROOM.");
195 /* 530 PRINT " IF THE ARROW HITS THE WUMPUS, YOU WIN."
197 puts(" IF THE ARROW HITS THE WUMPUS, YOU WIN.");
198 /* 535 PRINT " IF THE ARROW HITS YOU, YOU LOSE." */
199 puts(" IF THE ARROW HITS YOU, YOU LOSE.");
200 /* 540 INPUT "TYPE AN E THEN RETURN ";W9 */
201 (void)getlet("TYPE AN E THEN RETURN ");
202 /* 545 PRINT " WARNINGS:" */
204 /* 550 PRINT " WHEN YOU ARE ONE ROOM AWAY FROM A WUMPUS OR HAZARD,"
206 puts(" WHEN YOU ARE ONE ROOM AWAY FROM A WUMPUS OR HAZARD,");
207 /* 555 PRINT " THE COMPUTER SAYS:" */
208 puts(" THE COMPUTER SAYS:");
209 /* 560 PRINT " WUMPUS: 'I SMELL A WUMPUS'" */
210 puts(" WUMPUS: 'I SMELL A WUMPUS'");
211 /* 565 PRINT " BAT : 'BATS NEARBY'" */
212 puts(" BAT : 'BATS NEARBY'");
213 /* 570 PRINT " PIT : 'I FEEL A DRAFT'" */
214 puts(" PIT : 'I FEEL A DRAFT'");
220 void check_hazards() {
221 /* 585 REM *** PRINT LOCATION & HAZARD WARNINGS *** */
225 /* 595 FOR J=2 TO 6 */
226 /* 600 FOR K=1 TO 3 */
227 /* 605 IF S(L(1),K)<>L(J) THEN 640 */
228 /* 610 ON J-1 GOTO 615,625,625,635,635 */
229 /* 615 PRINT "I SMELL A WUMPUS!" */
231 /* 625 PRINT "I FEEL A DRAFT" */
233 /* 635 PRINT "BATS NEARBY!" */
236 for (j = WUMPUS; j < LOCS; j++) {
237 for (k = 0; k < 3; k++) {
238 if (cave[loc[YOU]][k] != loc[j]) {
243 (void)puts("I SMELL A WUMPUS!");
244 } else if (j == PIT1 || j == PIT2) {
245 (void)puts("I FEEL A DRAFT");
246 } else if (j == BATS1 || j == BATS2) {
247 (void)puts("BATS NEARBY!");
252 /* 650 PRINT "YOU ARE IN ROOM "L(1) */
253 (void)printf("YOU ARE IN ROOM %d\n", loc[YOU] + 1);
255 /* 655 PRINT "TUNNELS LEAD TO "S(L,1);S(L,2);S(L,3) */
256 (void)printf("TUNNELS LEAD TO %d %d %d\n", cave[loc[YOU]][0] + 1,
257 cave[loc[YOU]][1] + 1, cave[loc[YOU]][2] + 1);
265 int move_or_shoot() {
268 /* 670 REM *** CHOOSE OPTION *** */
271 /* 675 PRINT "SHOOT OR MOVE (S-M)"; */
273 c = getlet("SHOOT OR MOVE (S-M)");
275 /* 685 IF I$<>"S" THEN 700 */
278 /* 700 IF I$<>"M" THEN 675 */
283 } else if (c == 'M') {
291 extern void check_shot(), move_wumpus();
294 /* 715 REM *** ARROW ROUTINE *** */
298 /* 725 REM *** PATH OF ARROW *** */
300 /* 735 PRINT "NO. OF ROOMS (1-5)"; */
302 j9 = getnum("NO. OF ROOMS (1-5)");
304 /* 745 IF J9<1 THEN 735 */
305 /* 750 IF J9>5 THEN 735 */
306 if (j9 < 1 || j9 > 5) {
310 /* 755 FOR K=1 TO J9 */
311 for (k = 0; k < j9; k++) {
312 /* 760 PRINT "ROOM #"; */
314 path[k] = getnum("ROOM #") - 1;
316 /* 770 IF K<=2 THEN 790 */
321 /* 775 IF P(K)<>P(K-2) THEN 790 */
322 if (path[k] != path[k - 2]) {
326 /* 780 PRINT "ARROWS AREN'T THAT CROOKED - TRY ANOTHER ROOM"
328 (void)puts("ARROWS AREN'T THAT CROOKED - TRY ANOTHER ROOM");
335 /* 795 REM *** SHOOT ARROW *** */
337 scratchloc = loc[YOU];
339 /* 805 FOR K=1 TO J9 */
340 for (k = 0; k < j9; k++) {
344 (void)printf("Location is %d, looking for tunnel to room %d\n",
345 scratchloc + 1, path[k] + 1);
348 /* 810 FOR K1=1 TO 3 */
349 for (k1 = 0; k1 < 3; k1++) {
350 /* 815 IF S(L,K1)=P(K) THEN 895 */
351 if (cave[scratchloc][k1] == path[k]) {
353 * This is the only bit of the translation I'm
354 * not sure about. It requires the trajectory
355 * of the arrow to be a path. Without it, all
356 * rooms on the trajectory would be required by
357 * the above to be adjacent to the player,
358 * making for a trivial game --- just move to
359 * where you smell a wumpus and shoot into all
360 * adjacent passages! However, I can't find an
361 * equivalent in the BASIC.
363 scratchloc = path[k];
366 (void)printf("Found tunnel to room %d\n",
370 /* this simulates logic at 895 in the BASIC code
373 if (finished != NOT) {
381 /* 825 REM *** NO TUNNEL FOR ARROW *** */
382 /* 830 L=S(L,FNB(1)) */
383 scratchloc = cave[scratchloc][FNB()];
386 (void)printf("No tunnel for room %d, new location is %d\n",
387 path[k] + 1, scratchloc + 1);
392 if (finished != NOT) {
400 if (finished == NOT) {
401 /* 845 PRINT "MISSED" */
402 (void)puts("MISSED");
405 scratchloc = loc[YOU];
407 /* 855 REM *** MOVE WUMPUS *** */
411 /* 865 REM *** AMMO CHECK *** */
413 /* 875 IF A>0 THEN 885 */
424 /* 890 REM *** SEE IF ARROW IS AT L(1) OR AT L(2) */
427 /* 900 IF L<>L(2) THEN 920 */
428 /* 905 PRINT "AHA! YOU GOT THE WUMPUS!" */
431 if (scratchloc == loc[WUMPUS]) {
432 (void)puts("AHA! YOU GOT THE WUMPUS!");
436 /* 920 IF L<>L(1) THEN 840 */
437 /* 925 PRINT "OUCH! ARROW GOT YOU!" */
439 else if (scratchloc == loc[YOU]) {
440 (void)puts("OUCH! ARROW GOT YOU!");
446 /* 935 REM *** MOVE WUMPUS ROUTINE *** */
450 /* 945 IF K=4 THEN 955 */
451 /* 950 L(2)=S(L(2),K) */
453 loc[WUMPUS] = cave[loc[WUMPUS]][k];
457 (void)printf("Wumpus location is now room %d\n", loc[WUMPUS] + 1);
460 /* 955 IF L(2)<>L THEN 970 */
461 if (loc[WUMPUS] != loc[YOU]) {
465 /* 960 PRINT "TSK TSK TSK - WUMPUS GOT YOU!" */
466 (void)puts("TSK TSK TSK - WUMPUS GOT YOU!");
475 /* 975 REM *** MOVE ROUTINE *** */
480 /* 985 PRINT "WHERE TO"; */
482 scratchloc = getnum("WHERE TO");
484 /* 995 IF L<1 THEN 985 */
485 /* 1000 IF L>20 THEN 985 */
486 if (scratchloc < 1 || scratchloc > 20) {
491 /* 1005 FOR K=1 TO 3 */
492 for (k = 0; k < 3; k++) {
493 /* 1010 REM *** CHECK IF LEGAL MOVE *** */
494 /* 1015 IF S(L(1),K)=L THEN 1045 */
495 if (cave[loc[YOU]][k] == scratchloc) {
502 /* 1025 IF L=L(1) THEN 1045 */
503 if (scratchloc != loc[YOU]) {
504 /* 1030 PRINT "NOT POSSIBLE -"; */
505 (void)puts("NOT POSSIBLE -");
512 /* 1040 REM *** CHECK FOR HAZARDS *** */
514 loc[YOU] = scratchloc;
516 if (scratchloc == loc[WUMPUS]) {
517 /* 1050 REM *** WUMPUS *** */
518 /* 1055 IF L<>L(2) THEN 1090 */
519 /* 1060 PRINT "... OOPS! BUMPED A WUMPUS!" */
520 /* 1065 REM *** MOVE WUMPUS *** */
522 /* 1075 IF F=0 THEN 1090 */
524 (void)puts("... OOPS! BUMPED A WUMPUS!");
529 /* Fall through since Wumpus could have been in a pit or bat
532 if (scratchloc == loc[PIT1] || scratchloc == loc[PIT2]) {
533 /* 1085 REM *** PIT *** */
534 /* 1090 IF L=L(3) THEN 1100 */
535 /* 1095 IF L<>L(4) THEN 1120 */
536 /* 1100 PRINT "YYYYIIIIEEEE . . . FELL IN PIT" */
539 (void)puts("YYYYIIIIEEEE . . . FELL IN PIT");
543 if (scratchloc == loc[BATS1] || scratchloc == loc[BATS2]) {
544 /* 1115 REM *** BATS *** */
545 /* 1120 IF L=L(5) THEN 1130 */
546 /* 1125 IF L<>L(6) THEN 1145 */
547 /* 1130 PRINT "ZAP--SUPER BAT SNATCH! ELSEWHEREVILLE FOR YOU!"
553 (void)puts("ZAP--SUPER BAT SNATCH! ELSEWHEREVILLE FOR YOU!");
554 scratchloc = loc[YOU] = FNA();
565 if (argc >= 2 && strcmp(argv[1], "-s") == 0) {
566 srand(atoi(argv[2]));
568 srand((int)time((long *)0));
571 /* 15 PRINT "INSTRUCTIONS (Y-N)"; */
573 c = getlet("INSTRUCTIONS (Y-N)");
575 /* 25 IF I$="N" THEN 35 */
579 print_instructions();
581 /* 150 REM *** LOCATE L ARRAY ITEMS *** */
582 /* 155 REM *** 1-YOU, 2-WUMPUS, 3&4-PITS, 5&6-BATS *** */
586 /* 170 FOR J=1 TO 6 */
587 /* 175 L(J)=FNA(0) */
590 for (j = 0; j < LOCS; j++) {
591 loc[j] = save[j] = FNA();
594 /* 190 REM *** CHECK FOR CROSSOVERS (IE L(1)=L(2), ETC) ***
596 /* 195 FOR J=1 TO 6 */
597 /* 200 FOR K=1 TO 6 */
598 /* 205 IF J=K THEN 215 */
599 /* 210 IF L(J)=L(K) THEN 170 */
602 for (j = 0; j < LOCS; j++) {
603 for (k = 0; k < LOCS; k++) {
606 } else if (loc[j] == loc[k]) {
612 /* 225 REM *** SET NO. OF ARROWS *** */
617 scratchloc = loc[YOU];
619 /* 240 REM *** RUN THE GAME *** */
620 /* 245 PRINT "HUNT THE WUMPUS" */
621 (void)puts("HUNT THE WUMPUS");
624 (void)printf("Wumpus is at %d, pits at %d & %d, bats at %d & %d\n",
625 loc[WUMPUS] + 1, loc[PIT1] + 1, loc[PIT2] + 1,
626 loc[BATS1] + 1, loc[BATS2] + 1);
630 /* 250 REM *** HAZARD WARNING AND LOCATION *** */
634 /* 260 REM *** MOVE OR SHOOT *** */
636 /* 270 ON O GOTO 280,300 */
637 if (move_or_shoot()) {
638 /* 275 REM *** SHOOT *** */
642 /* 285 IF F=0 THEN 255 */
643 if (finished == NOT) {
649 /* 295 REM *** MOVE *** */
653 /* 305 IF F=0 THEN 255 */
654 if (finished == NOT) {
659 /* 310 IF F>0 THEN 335 */
660 if (finished == LOSE) {
661 /* 315 REM *** LOSE *** */
662 /* 320 PRINT "HA HA HA - YOU LOSE!" */
664 (void)puts("HA HA HA - YOU LOSE!");
666 /* 330 REM *** WIN *** */
667 /* 335 PRINT "HEE HEE HEE - THE WUMPUS'LL GET YOU NEXT TIME!!"
669 (void)puts("HEE HEE HEE - THE WUMPUS'LL GET YOU NEXT TIME!!");
672 /* 340 FOR J=1 TO 6 */
675 for (j = YOU; j < LOCS; j++) {
679 /* 355 PRINT "SAME SETUP (Y-N)"; */
681 c = getlet("SAME SETUP (Y-N)");
683 /* 365 IF I$<>"Y"THEN 170 */
692 /* wumpus.c ends here */