1 /* $OpenBSD: wump.c,v 1.34 2018/12/20 09:55:44 schwarze Exp $ */
2
3 /*
4 * Copyright (c) 1989, 1993
5 * The Regents of the University of California. All rights reserved.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Dave Taylor, of Intuitive Systems.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36 /*
37 * A no longer new version of the age-old favorite Hunt-The-Wumpus game that
38 * has been a part of the BSD distribution for longer than us old folk
39 * would care to remember.
40 */
41
42 #include <sys/wait.h>
43
44 #include <err.h>
45 #include <fcntl.h>
46 #include <paths.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51
52 #include "pathnames.h"
53
54 /* some defines to spec out what our wumpus cave should look like */
55
56 /* #define MAX_ARROW_SHOT_DISTANCE 6 */ /* +1 for '0' stopper */
57 #define MAX_LINKS_IN_ROOM 25 /* a complex cave */
58
59 #define MAX_ROOMS_IN_CAVE 250
60 #define ROOMS_IN_CAVE 20
61 #define MIN_ROOMS_IN_CAVE 10
62
63 #define LINKS_IN_ROOM 3
64 #define NUMBER_OF_ARROWS 5
65 #define PIT_COUNT 3
66 #define BAT_COUNT 3
67
68 #define EASY 1 /* levels of play */
69 #define HARD 2
70
71 /* some macro definitions for cleaner output */
72
73 #define plural(n) (n == 1 ? "" : "s")
74
75 /* simple cave data structure; +1 so we can index from '1' not '0' */
76 struct room_record {
77 int tunnel[MAX_LINKS_IN_ROOM];
78 int has_a_pit, has_a_bat;
79 } cave[MAX_ROOMS_IN_CAVE+1];
80
81 /*
82 * global variables so we can keep track of where the player is, how
83 * many arrows they still have, where el wumpo is, and so on...
84 */
85 int player_loc = -1; /* player location */
86 int wumpus_loc = -1; /* The Bad Guy location */
87 int level = EASY; /* level of play */
88 int arrows_left; /* arrows unshot */
89 int oldstyle = 0; /* dodecahedral cave? */
90
91 #ifdef DEBUG
92 int debug = 0;
93 #endif
94
95 int pit_num = -1; /* # pits in cave */
96 int bat_num = -1; /* # bats */
97 int room_num = ROOMS_IN_CAVE; /* # rooms in cave */
98 int link_num = LINKS_IN_ROOM; /* links per room */
99 int arrow_num = NUMBER_OF_ARROWS; /* arrow inventory */
100
101 char answer[20]; /* user input */
102
103 int bats_nearby(void);
104 void cave_init(void);
105 void clear_things_in_cave(void);
106 void display_room_stats(void);
107 void dodecahedral_cave_init(void);
108 int gcd(int, int);
109 int getans(const char *);
110 void initialize_things_in_cave(void);
111 void instructions(void);
112 int int_compare(const void *, const void *);
113 /* void jump(int); */
114 void kill_wump(void);
115 int main(int, char **);
116 int move_to(const char *);
117 void move_wump(void);
118 void no_arrows(void);
119 void pit_kill(void);
120 void pit_kill_bat(void);
121 int pit_nearby(void);
122 void pit_survive(void);
123 int shoot(char *);
124 void shoot_self(void);
125 int take_action(void);
126 __dead void usage(void);
127 void wump_kill(void);
128 void wump_bat_kill(void);
129 void wump_walk_kill(void);
130 int wump_nearby(void);
131
132
133 int
main(int argc,char * argv[])134 main(int argc, char *argv[])
135 {
136 int c;
137
138 if (pledge("stdio rpath proc exec", NULL) == -1)
139 err(1, "pledge");
140
141 #ifdef DEBUG
142 while ((c = getopt(argc, argv, "a:b:hop:r:t:d")) != -1)
143 #else
144 while ((c = getopt(argc, argv, "a:b:hop:r:t:")) != -1)
145 #endif
146 switch (c) {
147 case 'a':
148 arrow_num = atoi(optarg);
149 break;
150 case 'b':
151 bat_num = atoi(optarg);
152 break;
153 #ifdef DEBUG
154 case 'd':
155 debug = 1;
156 break;
157 #endif
158 case 'h':
159 level = HARD;
160 break;
161 case 'o':
162 oldstyle = 1;
163 break;
164 case 'p':
165 pit_num = atoi(optarg);
166 break;
167 case 'r':
168 room_num = atoi(optarg);
169 if (room_num < MIN_ROOMS_IN_CAVE)
170 errx(1,
171 "no self-respecting wumpus would live in such a small cave!");
172 if (room_num > MAX_ROOMS_IN_CAVE)
173 errx(1,
174 "even wumpii can't furnish caves that large!");
175 break;
176 case 't':
177 link_num = atoi(optarg);
178 if (link_num < 2)
179 errx(1,
180 "wumpii like extra doors in their caves!");
181 break;
182 default:
183 usage();
184 }
185
186 if (oldstyle) {
187 room_num = 20;
188 link_num = 3;
189 /* Original game had exactly 2 bats and 2 pits */
190 if (bat_num < 0)
191 bat_num = 2;
192 if (pit_num < 0)
193 pit_num = 2;
194 } else {
195 if (bat_num < 0)
196 bat_num = BAT_COUNT;
197 if (pit_num < 0)
198 pit_num = PIT_COUNT;
199 }
200
201 if (link_num > MAX_LINKS_IN_ROOM ||
202 link_num > room_num - (room_num / 4))
203 errx(1,
204 "too many tunnels! The cave collapsed!\n(Fortunately, the wumpus escaped!)");
205
206 if (level == HARD) {
207 if (room_num / 2 - bat_num)
208 bat_num += arc4random_uniform(room_num / 2 - bat_num);
209 if (room_num / 2 - pit_num)
210 pit_num += arc4random_uniform(room_num / 2 - pit_num);
211 }
212
213 /* Leave at least two rooms free--one for the player to start in, and
214 * potentially one for the wumpus.
215 */
216 if (bat_num > room_num / 2 - 1)
217 errx(1,
218 "the wumpus refused to enter the cave, claiming it was too crowded!");
219
220 if (pit_num > room_num / 2 - 1)
221 errx(1,
222 "the wumpus refused to enter the cave, claiming it was too dangerous!");
223
224 instructions();
225
226 if (pledge("stdio", NULL) == -1)
227 err(1, "pledge");
228
229 if (oldstyle)
230 dodecahedral_cave_init();
231 else
232 cave_init();
233
234 /* and we're OFF! da dum, da dum, da dum, da dum... */
235 (void)printf(
236 "\nYou're in a cave with %d rooms and %d tunnels leading from each room.\n\
237 There are %d bat%s and %d pit%s scattered throughout the cave, and your\n\
238 quiver holds %d custom super anti-evil Wumpus arrows. Good luck.\n",
239 room_num, link_num, bat_num, plural(bat_num), pit_num,
240 plural(pit_num), arrow_num);
241
242 for (;;) {
243 initialize_things_in_cave();
244 arrows_left = arrow_num;
245 do {
246 display_room_stats();
247 (void)printf("Move or shoot? (m-s) ");
248 (void)fflush(stdout);
249 (void)fpurge(stdin);
250 if (!fgets(answer, sizeof(answer), stdin))
251 break;
252 } while (!take_action());
253 (void)fpurge(stdin);
254
255 if (!getans("\nCare to play another game? (y-n) ")) {
256 (void)printf("\n");
257 return 0;
258 }
259 clear_things_in_cave();
260 if (!getans("In the same cave? (y-n) ")) {
261 if (oldstyle)
262 dodecahedral_cave_init();
263 else
264 cave_init();
265 }
266 }
267 }
268
269 void
display_room_stats(void)270 display_room_stats(void)
271 {
272 int i;
273
274 /*
275 * Routine will explain what's going on with the current room, as well
276 * as describe whether there are pits, bats, & wumpii nearby. It's
277 * all pretty mindless, really.
278 */
279 (void)printf(
280 "\nYou are in room %d of the cave, and have %d arrow%s left.\n",
281 player_loc, arrows_left, plural(arrows_left));
282
283 if (bats_nearby())
284 (void)printf("*rustle* *rustle* (must be bats nearby)\n");
285 if (pit_nearby())
286 (void)printf("*whoosh* (I feel a draft from some pits).\n");
287 if (wump_nearby())
288 (void)printf("*sniff* (I can smell the evil Wumpus nearby!)\n");
289
290 (void)printf("There are tunnels to rooms %d, ",
291 cave[player_loc].tunnel[0]);
292
293 for (i = 1; i < link_num - 1; i++)
294 /* if (cave[player_loc].tunnel[i] <= room_num) */
295 (void)printf("%d, ", cave[player_loc].tunnel[i]);
296 (void)printf("and %d.\n", cave[player_loc].tunnel[link_num - 1]);
297 }
298
299 int
take_action(void)300 take_action(void)
301 {
302 /*
303 * Do the action specified by the player, either 'm'ove, 's'hoot
304 * or something exceptionally bizarre and strange! Returns 1
305 * iff the player died during this turn, otherwise returns 0.
306 */
307 switch (*answer) {
308 case 'M':
309 case 'm': /* move */
310 return(move_to(answer + 1));
311 case 'S':
312 case 's': /* shoot */
313 return(shoot(answer + 1));
314 case 'Q':
315 case 'q':
316 case 'x':
317 exit(0);
318 case '\n':
319 return(0);
320 }
321 if (arc4random_uniform(15) == 1)
322 (void)printf("Que pasa?\n");
323 else
324 (void)printf("I don't understand!\n");
325 return(0);
326 }
327
328 int
move_to(const char * room_number)329 move_to(const char *room_number)
330 {
331 int i, just_moved_by_bats, next_room, tunnel_available;
332
333 /*
334 * This is responsible for moving the player into another room in the
335 * cave as per their directions. If room_number is a null string,
336 * then we'll prompt the user for the next room to go into. Once
337 * we've moved into the room, we'll check for things like bats, pits,
338 * and so on. This routine returns 1 if something occurs that kills
339 * the player and 0 otherwise...
340 */
341 tunnel_available = just_moved_by_bats = 0;
342 next_room = atoi(room_number);
343
344 /* crap for magic tunnels */
345 /* if (next_room == room_num + 1 &&
346 * cave[player_loc].tunnel[link_num-1] != next_room)
347 * ++next_room;
348 */
349 while (next_room < 1 || next_room > room_num /* + 1 */) {
350 if (next_room < 0 && next_room != -1)
351 (void)printf("Sorry, but we're constrained to a semi-Euclidean cave!\n");
352 if (next_room > room_num /* + 1 */)
353 (void)printf("What? The cave surely isn't quite that big!\n");
354 /* if (next_room == room_num + 1 &&
355 * cave[player_loc].tunnel[link_num-1] != next_room) {
356 * (void)printf("What? The cave isn't that big!\n");
357 * ++next_room;
358 * }
359 */ (void)printf("To which room do you wish to move? ");
360 (void)fflush(stdout);
361 if (!fgets(answer, sizeof(answer), stdin))
362 return(1);
363 next_room = atoi(answer);
364 }
365
366 /* now let's see if we can move to that room or not */
367 tunnel_available = 0;
368 for (i = 0; i < link_num; i++)
369 if (cave[player_loc].tunnel[i] == next_room)
370 tunnel_available = 1;
371
372 if (!tunnel_available) {
373 (void)printf("*Oof!* (You hit the wall)\n");
374 if (arc4random_uniform(6) == 1) {
375 (void)printf("Your colorful comments awaken the wumpus!\n");
376 move_wump();
377 if (wumpus_loc == player_loc) {
378 wump_walk_kill();
379 return(1);
380 }
381 }
382 return(0);
383 }
384
385 /* now let's move into that room and check it out for dangers */
386 /* if (next_room == room_num + 1)
387 * jump(next_room = arc4random_uniform(room_num) + 1);
388 */
389 player_loc = next_room;
390 for (;;) {
391 if (next_room == wumpus_loc) { /* uh oh... */
392 if (just_moved_by_bats)
393 wump_bat_kill();
394 else
395 wump_kill();
396 return(1);
397 }
398 if (cave[next_room].has_a_pit) {
399 if (arc4random_uniform(12) < 2) {
400 pit_survive();
401 return(0);
402 } else {
403 if (just_moved_by_bats)
404 pit_kill_bat();
405 else
406 pit_kill();
407 return(1);
408 }
409 }
410
411 if (cave[next_room].has_a_bat) {
412 (void)printf(
413 "*flap* *flap* *flap* (humongous bats pick you up and move you%s!)\n",
414 just_moved_by_bats ? " again": "");
415 next_room = player_loc =
416 arc4random_uniform(room_num) + 1;
417 just_moved_by_bats = 1;
418 }
419
420 else
421 break;
422 }
423 return(0);
424 }
425
426 int
shoot(char * room_list)427 shoot(char *room_list)
428 {
429 int chance, next, roomcnt;
430 int j, arrow_location, link, ok;
431 char *p;
432
433 /*
434 * Implement shooting arrows. Arrows are shot by the player indicating
435 * a space-separated list of rooms that the arrow should pass through;
436 * if any of the rooms they specify are not accessible via tunnel from
437 * the room the arrow is in, it will instead fly randomly into another
438 * room. If the player hits the wumpus, this routine will indicate
439 * such. If it misses, this routine may *move* the wumpus one room.
440 * If it's the last arrow, then the player dies... Returns 1 if the
441 * player has won or died, 0 if nothing has happened.
442 */
443 arrow_location = player_loc;
444 for (roomcnt = 1;; ++roomcnt, room_list = NULL) {
445 if (!(p = strtok(room_list, " \t\n"))) {
446 if (roomcnt == 1) {
447 (void)printf("Enter a list of rooms to shoot into:\n");
448 (void)fflush(stdout);
449 if (!(p = strtok(fgets(answer, sizeof(answer), stdin),
450 " \t\n"))) {
451 (void)printf(
452 "The arrow falls to the ground at your feet.\n");
453 return(0);
454 }
455 } else
456 break;
457 }
458 if (roomcnt > 5) {
459 (void)printf(
460 "The arrow wavers in its flight and can go no further than room %d!\n",
461 arrow_location);
462 break;
463 }
464
465 next = atoi(p);
466 if (next == 0)
467 break; /* Old wumpus used room 0 as the terminator */
468
469 chance = arc4random_uniform(10);
470 if (roomcnt == 4 && chance < 2) {
471 (void)printf(
472 "Your finger slips on the bowstring! *twaaaaaang*\n\
473 The arrow is weakly shot and can go no further than room %d!\n",arrow_location);
474 break;
475 } else if (roomcnt == 5 && chance < 6) {
476 (void)printf(
477 "The arrow wavers in its flight and can go no further than room %d!\n",
478 arrow_location);
479 break;
480 }
481
482 for (j = 0, ok = 0; j < link_num; j++)
483 if (cave[arrow_location].tunnel[j] == next)
484 ok = 1;
485
486 if (ok) {
487 /* if (next > room_num) {
488 * (void)printf(
489 * "A faint gleam tells you the arrow has gone through a magic tunnel!\n");
490 * arrow_location =
491 * arc4random_uniform(room_num) + 1;
492 * } else
493 */ arrow_location = next;
494 } else {
495 link = (arc4random_uniform(link_num));
496 if (cave[arrow_location].tunnel[link] == player_loc)
497 (void)printf(
498 "*thunk* The arrow can't find a way from %d to %d and flies back into\n\
499 your room!\n",
500 arrow_location, next);
501 /* else if (cave[arrow_location].tunnel[link] > room_num)
502 * (void)printf(
503 *"*thunk* The arrow flies randomly into a magic tunnel, thence into\n\
504 *room %d!\n",
505 * cave[arrow_location].tunnel[link]);
506 */ else
507 (void)printf(
508 "*thunk* The arrow can't find a way from %d to %d and flies randomly\n\
509 into room %d!\n", arrow_location, next, cave[arrow_location].tunnel[link]);
510
511 arrow_location = cave[arrow_location].tunnel[link];
512 }
513
514 /*
515 * now we've gotten into the new room let us see if El Wumpo is
516 * in the same room ... if so we've a HIT and the player WON!
517 */
518 if (arrow_location == wumpus_loc) {
519 kill_wump();
520 return(1);
521 }
522
523 if (arrow_location == player_loc) {
524 shoot_self();
525 return(1);
526 }
527 }
528
529 if (!--arrows_left) {
530 no_arrows();
531 return(1);
532 }
533
534 {
535 /* each time you shoot, it's more likely the wumpus moves */
536 static int lastchance = 2;
537
538 lastchance += 2;
539 if (arc4random_uniform(level == EASY ? 12 : 9) < lastchance) {
540 move_wump();
541 if (wumpus_loc == player_loc) {
542 wump_walk_kill();
543 /* Reset for next game */
544 lastchance = arc4random_uniform(3);
545 return(1);
546 }
547
548 }
549 }
550 (void)printf("The arrow hit nothing.\n");
551 return(0);
552 }
553
554 int
gcd(int a,int b)555 gcd(int a, int b)
556 {
557 int r;
558
559 if (!(r = (a % b)))
560 return(b);
561 return(gcd(b, r));
562 }
563
564 void
cave_init(void)565 cave_init(void)
566 {
567 int i, j, k, link;
568 int delta;
569
570 /*
571 * This does most of the interesting work in this program actually!
572 * In this routine we'll initialize the Wumpus cave to have all rooms
573 * linking to all others by stepping through our data structure once,
574 * recording all forward links and backwards links too. The parallel
575 * "linkcount" data structure ensures that no room ends up with more
576 * than three links, regardless of the quality of the random number
577 * generator that we're using.
578 */
579
580 /* Note that throughout the source there are commented-out vestigial
581 * remains of the 'magic tunnel', which was a tunnel to room
582 * room_num +1. It was necessary if all paths were two-way and
583 * there was an odd number of rooms, each with an odd number of
584 * exits. It's being kept in case cave_init ever gets reworked into
585 * something more traditional.
586 */
587
588 /* initialize the cave first off. */
589 for (i = 1; i <= room_num; ++i)
590 for (j = 0; j < link_num ; ++j)
591 cave[i].tunnel[j] = -1;
592
593 /* choose a random 'hop' delta for our guaranteed link.
594 * To keep the cave connected, require greatest common
595 * divisor of (delta + 1) and room_num to be 1
596 */
597 do {
598 delta = arc4random_uniform(room_num - 1) + 1;
599 } while (gcd(room_num, delta + 1) != 1);
600
601 for (i = 1; i <= room_num; ++i) {
602 link = ((i + delta) % room_num) + 1; /* connection */
603 cave[i].tunnel[0] = link; /* forw link */
604 cave[link].tunnel[1] = i; /* back link */
605 }
606 /* now fill in the rest of the cave with random connections.
607 * This is a departure from historical versions of wumpus.
608 */
609 for (i = 1; i <= room_num; i++)
610 for (j = 2; j < link_num ; j++) {
611 if (cave[i].tunnel[j] != -1)
612 continue;
613 try_again: link = arc4random_uniform(room_num) + 1;
614 /* skip duplicates */
615 for (k = 0; k < j; k++)
616 if (cave[i].tunnel[k] == link)
617 goto try_again;
618 /* don't let a room connect to itself */
619 if (link == i)
620 goto try_again;
621 cave[i].tunnel[j] = link;
622 if (arc4random() % 2 == 1)
623 continue;
624 for (k = 0; k < link_num; ++k) {
625 /* if duplicate, skip it */
626 if (cave[link].tunnel[k] == i)
627 k = link_num;
628 else {
629 /* if open link, use it, force exit */
630 if (cave[link].tunnel[k] == -1) {
631 cave[link].tunnel[k] = i;
632 k = link_num;
633 }
634 }
635 }
636 }
637 /*
638 * now that we're done, sort the tunnels in each of the rooms to
639 * make it easier on the intrepid adventurer.
640 */
641 for (i = 1; i <= room_num; ++i)
642 qsort(cave[i].tunnel, (u_int)link_num,
643 sizeof(cave[i].tunnel[0]), int_compare);
644
645 #ifdef DEBUG
646 if (debug)
647 for (i = 1; i <= room_num; ++i) {
648 (void)printf("<room %d has tunnels to ", i);
649 for (j = 0; j < link_num; ++j)
650 (void)printf("%d ", cave[i].tunnel[j]);
651 (void)printf(">\n");
652 }
653 #endif
654 }
655
656 void
dodecahedral_cave_init(void)657 dodecahedral_cave_init(void)
658 {
659 int vert[20][3] = {
660 {1, 4, 7},
661 {0, 2, 9},
662 {1, 3, 11},
663 {2, 4, 13},
664 {0, 3, 5},
665 {4, 6, 14},
666 {5, 7, 16},
667 {0, 6, 8},
668 {7, 9, 17},
669 {1, 8, 10},
670 {9, 11, 18},
671 {2, 10, 12},
672 {11, 13, 19},
673 {3, 12, 14},
674 {5, 13, 15},
675 {14, 16, 19},
676 {6, 15, 17},
677 {8, 16, 18},
678 {10, 17, 19},
679 {12, 15, 18},
680 };
681 int loc[20];
682 int i, j, temp;
683
684 if (room_num != 20 || link_num != 3)
685 errx(1, "wrong parameters for dodecahedron");
686 for (i = 0; i < 20; i++)
687 loc[i] = i;
688 for (i = 0; i < 20; i++) {
689 j = arc4random_uniform(20 - i);
690 if (j) {
691 temp = loc[i];
692 loc[i] = loc[i + j];
693 loc[i + j] = temp;
694 }
695 }
696 /* cave is offset by 1 */
697 for (i = 0; i < 20; i++) {
698 for (j = 0; j < 3; j++)
699 cave[loc[i] + 1].tunnel[j] = loc[vert[i][j]] + 1;
700 }
701
702 /*
703 * now that we're done, sort the tunnels in each of the rooms to
704 * make it easier on the intrepid adventurer.
705 */
706 for (i = 1; i <= room_num; ++i)
707 qsort(cave[i].tunnel, (u_int)link_num,
708 sizeof(cave[i].tunnel[0]), int_compare);
709
710 #ifdef DEBUG
711 if (debug)
712 for (i = 1; i <= room_num; ++i) {
713 (void)printf("<room %d has tunnels to ", i);
714 for (j = 0; j < link_num; ++j)
715 (void)printf("%d ", cave[i].tunnel[j]);
716 (void)printf(">\n");
717 }
718 #endif
719 }
720
721 void
clear_things_in_cave(void)722 clear_things_in_cave(void)
723 {
724 int i;
725
726 /*
727 * remove bats and pits from the current cave in preparation for us
728 * adding new ones via the initialize_things_in_cave() routines.
729 */
730 for (i = 1; i <= room_num; ++i)
731 cave[i].has_a_bat = cave[i].has_a_pit = 0;
732 }
733
734 void
initialize_things_in_cave(void)735 initialize_things_in_cave(void)
736 {
737 int i, loc;
738
739 /* place some bats, pits, the wumpus, and the player. */
740 for (i = 0; i < bat_num; ++i) {
741 do {
742 loc = arc4random_uniform(room_num) + 1;
743 } while (cave[loc].has_a_bat);
744 cave[loc].has_a_bat = 1;
745 #ifdef DEBUG
746 if (debug)
747 (void)printf("<bat in room %d>\n", loc);
748 #endif
749 }
750
751 for (i = 0; i < pit_num; ++i) {
752 do {
753 loc = arc4random_uniform(room_num) + 1;
754 } while (cave[loc].has_a_pit || cave[loc].has_a_bat);
755 /* Above used to be &&; || makes sense but so does just
756 * checking cave[loc].has_a_pit */
757 cave[loc].has_a_pit = 1;
758 #ifdef DEBUG
759 if (debug)
760 (void)printf("<pit in room %d>\n", loc);
761 #endif
762 }
763
764 wumpus_loc = arc4random_uniform(room_num) + 1;
765 #ifdef DEBUG
766 if (debug)
767 (void)printf("<wumpus in room %d>\n", wumpus_loc);
768 #endif
769
770 do {
771 player_loc = arc4random_uniform(room_num) + 1;
772 } while (player_loc == wumpus_loc || cave[player_loc].has_a_pit ||
773 cave[player_loc].has_a_bat);
774 /* Replaced (level == HARD ?
775 * (link_num / room_num < 0.4 ? wump_nearby() : 0) : 0)
776 * with bat/pit checks in initial room. If this is kept there is
777 * a slight chance that no room satisfies all four conditions.
778 */
779 }
780
781 int
getans(const char * prompt)782 getans(const char *prompt)
783 {
784 char buf[20];
785
786 /*
787 * simple routine to ask the yes/no question specified until the user
788 * answers yes or no, then return 1 if they said 'yes' and 0 if they
789 * answered 'no'.
790 */
791 for (;;) {
792 (void)printf("%s", prompt);
793 (void)fflush(stdout);
794 if (!fgets(buf, sizeof(buf), stdin))
795 return(0);
796 if (*buf == 'N' || *buf == 'n')
797 return(0);
798 if (*buf == 'Y' || *buf == 'y')
799 return(1);
800 (void)printf(
801 "I don't understand your answer; please enter 'y' or 'n'!\n");
802 }
803 }
804
805 int
bats_nearby(void)806 bats_nearby(void)
807 {
808 int i;
809
810 /* check for bats in the immediate vicinity */
811 for (i = 0; i < link_num; ++i)
812 if (cave[cave[player_loc].tunnel[i]].has_a_bat)
813 return(1);
814 return(0);
815 }
816
817 int
pit_nearby(void)818 pit_nearby(void)
819 {
820 int i;
821
822 /* check for pits in the immediate vicinity */
823 for (i = 0; i < link_num; ++i)
824 if (cave[cave[player_loc].tunnel[i]].has_a_pit)
825 return(1);
826 return(0);
827 }
828
829 int
wump_nearby(void)830 wump_nearby(void)
831 {
832 int i, j;
833
834 /* check for a wumpus within TWO caves of where we are */
835 for (i = 0; i < link_num; ++i) {
836 if (cave[player_loc].tunnel[i] == wumpus_loc)
837 return(1);
838 for (j = 0; j < link_num; ++j)
839 if (cave[cave[player_loc].tunnel[i]].tunnel[j] ==
840 wumpus_loc)
841 return(1);
842 }
843 return(0);
844 }
845
846 void
move_wump(void)847 move_wump(void)
848 {
849 wumpus_loc = cave[wumpus_loc].tunnel[arc4random_uniform(link_num)];
850 #ifdef DEBUG
851 if (debug)
852 (void)printf("Wumpus moved to room %d\n",wumpus_loc);
853 #endif
854 }
855
856 int
int_compare(const void * a,const void * b)857 int_compare(const void *a, const void *b)
858 {
859 return(*(const int *)a < *(const int *)b ? -1 : 1);
860 }
861
862 void
instructions(void)863 instructions(void)
864 {
865 const char *pager;
866 pid_t pid;
867 int status;
868 int fd;
869
870 /*
871 * read the instructions file, if needed, and show the user how to
872 * play this game!
873 */
874 if (!getans("Instructions? (y-n) "))
875 return;
876
877 if ((fd = open(_PATH_WUMPINFO, O_RDONLY)) == -1) {
878 (void)printf(
879 "Sorry, but the instruction file seems to have disappeared in a\n\
880 puff of greasy black smoke! (poof)\n");
881 return;
882 }
883
884 if (!isatty(1))
885 pager = "/bin/cat";
886 else {
887 if (!(pager = getenv("PAGER")) || (*pager == 0))
888 pager = _PATH_PAGER;
889 }
890 switch (pid = fork()) {
891 case 0: /* child */
892 if (dup2(fd, 0) == -1)
893 err(1, "dup2");
894 (void)execl(_PATH_BSHELL, "sh", "-c", pager, (char *)NULL);
895 err(1, "exec sh -c %s", pager);
896 /* NOT REACHED */
897 case -1:
898 err(1, "fork");
899 /* NOT REACHED */
900 default:
901 (void)waitpid(pid, &status, 0);
902 close(fd);
903 break;
904 }
905 }
906
907 void
usage(void)908 usage(void)
909 {
910 (void)fprintf(stderr,
911 "usage: %s [-ho] [-a arrows] [-b bats] [-p pits] "
912 "[-r rooms] [-t tunnels]\n", getprogname());
913 exit(1);
914 }
915
916 /* messages */
917 void
wump_kill(void)918 wump_kill(void)
919 {
920 (void)printf(
921 "*ROAR* *chomp* *snurfle* *chomp*!\n\
922 Much to the delight of the Wumpus, you walk right into his mouth,\n\
923 making you one of the easiest dinners he's ever had! For you, however,\n\
924 it's a rather unpleasant death. The only good thing is that it's been\n\
925 so long since the evil Wumpus cleaned his teeth that you immediately\n\
926 pass out from the stench!\n");
927 }
928
929 void
wump_walk_kill(void)930 wump_walk_kill(void)
931 {
932 (void)printf(
933 "Oh dear. All the commotion has managed to awaken the evil Wumpus, who\n\
934 has chosen to walk into this very room! Your eyes open wide as they behold\n\
935 the great sucker-footed bulk that is the Wumpus; the mouth of the Wumpus\n\
936 also opens wide as the evil beast beholds dinner.\n\
937 *ROAR* *chomp* *snurfle* *chomp*!\n");
938 }
939
940 void
wump_bat_kill(void)941 wump_bat_kill(void)
942 {
943 (void)printf(
944 "Flap, flap. The bats fly you right into the room with the evil Wumpus!\n\
945 The Wumpus, seeing a fine dinner flying overhead, takes a swipe at you,\n\
946 and the bats, not wanting to serve as hors d'oeuvres, drop their\n\
947 soon-to-be-dead weight and take off in the way that only bats flying out\n\
948 of a very bad place can. As you fall towards the large, sharp, and very\n\
949 foul-smelling teeth of the Wumpus, you think, \"Man, this is going to hurt.\"\n\
950 It does.\n");
951 }
952
953 void
kill_wump(void)954 kill_wump(void)
955 {
956 (void)printf(
957 "*thwock!* *groan* *crash*\n\n\
958 A horrible roar fills the cave, and you realize, with a smile, that you\n\
959 have slain the evil Wumpus and won the game! You don't want to tarry for\n\
960 long, however, because not only is the Wumpus famous, but the stench of\n\
961 dead Wumpus is also quite well known--a stench powerful enough to slay the\n\
962 mightiest adventurer at a single whiff!!\n");
963 }
964
965 void
no_arrows(void)966 no_arrows(void)
967 {
968 (void)printf(
969 "\nYou turn and look at your quiver, and realize with a sinking feeling\n\
970 that you've just shot your last arrow (figuratively, too). Sensing this\n\
971 with its psychic powers, the evil Wumpus rampages through the cave, finds\n\
972 you, and with a mighty *ROAR* eats you alive!\n");
973 }
974
975 void
shoot_self(void)976 shoot_self(void)
977 {
978 (void)printf(
979 "\n*Thwack!* A sudden piercing feeling informs you that your wild arrow\n\
980 has ricocheted back and wedged in your side, causing extreme agony. The\n\
981 evil Wumpus, with its psychic powers, realizes this and immediately rushes\n\
982 to your side, not to help, alas, but to EAT YOU!\n\
983 (*CHOMP*)\n");
984 }
985
986 /*
987 * void
988 * jump(int where)
989 * {
990 * (void)printf(
991 * "\nWith a jaunty step you enter the magic tunnel. As you do, you\n\
992 * notice that the walls are shimmering and glowing. Suddenly you feel\n\
993 * a very curious, warm sensation and find yourself in room %d!!\n", where);
994 * }
995 */
996
997 void
pit_kill(void)998 pit_kill(void)
999 {
1000 (void)printf(
1001 "*AAAUUUUGGGGGHHHHHhhhhhhhhhh...*\n\
1002 The whistling sound and updraft as you walked into this room of the\n\
1003 cave apparently weren't enough to clue you in to the presence of the\n\
1004 bottomless pit. You have a lot of time to reflect on this error as\n\
1005 you fall many miles to the core of the earth. Look on the bright side;\n\
1006 you can at least find out if Jules Verne was right...\n");
1007 }
1008
1009 void
pit_kill_bat(void)1010 pit_kill_bat(void)
1011 {
1012 (void)printf(
1013 "*AAAUUUUGGGGGHHHHHhhhhhhhhhh...*\n\
1014 It appears the bats have decided to drop you into a bottomless pit. At\n\
1015 least, that's what the whistling sound and updraft would suggest. Look on\n\
1016 the bright side; you can at least find out if Jules Verne was right...\n");
1017 }
1018
1019 void
pit_survive(void)1020 pit_survive(void)
1021 {
1022 (void)printf(
1023 "Without conscious thought you grab for the side of the cave and manage\n\
1024 to grasp onto a rocky outcrop. Beneath your feet stretches the limitless\n\
1025 depths of a bottomless pit! Rock crumbles beneath your feet!\n");
1026 }
1027