wump.c revision 1.17 1 /* $NetBSD: wump.c,v 1.17 2005/02/15 12:56:20 jsm 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 #include <sys/cdefs.h>
37 #ifndef lint
38 __COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
39 The Regents of the University of California. All rights reserved.\n");
40 #endif /* not lint */
41
42 #ifndef lint
43 #if 0
44 static char sccsid[] = "@(#)wump.c 8.1 (Berkeley) 5/31/93";
45 #else
46 __RCSID("$NetBSD: wump.c,v 1.17 2005/02/15 12:56:20 jsm Exp $");
47 #endif
48 #endif /* not lint */
49
50 /*
51 * A very new version of the age old favorite Hunt-The-Wumpus game that has
52 * been a part of the BSD distribution of Unix for longer than us old folk
53 * would care to remember.
54 */
55
56 #include <err.h>
57 #include <sys/types.h>
58 #include <sys/file.h>
59 #include <sys/wait.h>
60 #include <stdio.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <time.h>
64 #include <unistd.h>
65 #include "pathnames.h"
66
67 /* some defines to spec out what our wumpus cave should look like */
68
69 #define MAX_ARROW_SHOT_DISTANCE 6 /* +1 for '0' stopper */
70 #define MAX_LINKS_IN_ROOM 25 /* a complex cave */
71
72 #define MAX_ROOMS_IN_CAVE 250
73 #define ROOMS_IN_CAVE 20
74 #define MIN_ROOMS_IN_CAVE 10
75
76 #define LINKS_IN_ROOM 3
77 #define NUMBER_OF_ARROWS 5
78 #define PIT_COUNT 3
79 #define BAT_COUNT 3
80
81 #define EASY 1 /* levels of play */
82 #define HARD 2
83
84 /* some macro definitions for cleaner output */
85
86 #define plural(n) (n == 1 ? "" : "s")
87
88 /* simple cave data structure; +1 so we can index from '1' not '0' */
89 struct room_record {
90 int tunnel[MAX_LINKS_IN_ROOM];
91 int has_a_pit, has_a_bat;
92 } cave[MAX_ROOMS_IN_CAVE+1];
93
94 /*
95 * global variables so we can keep track of where the player is, how
96 * many arrows they still have, where el wumpo is, and so on...
97 */
98 int player_loc = -1; /* player location */
99 int wumpus_loc = -1; /* The Bad Guy location */
100 int level = EASY; /* level of play */
101 int arrows_left; /* arrows unshot */
102
103 #ifdef DEBUG
104 int debug = 0;
105 #endif
106
107 int pit_num = PIT_COUNT; /* # pits in cave */
108 int bat_num = BAT_COUNT; /* # bats */
109 int room_num = ROOMS_IN_CAVE; /* # rooms in cave */
110 int link_num = LINKS_IN_ROOM; /* links per room */
111 int arrow_num = NUMBER_OF_ARROWS; /* arrow inventory */
112
113 char answer[20]; /* user input */
114
115 int bats_nearby(void);
116 void cave_init(void);
117 void clear_things_in_cave(void);
118 void display_room_stats(void);
119 int gcd(int, int);
120 int getans(const char *);
121 void initialize_things_in_cave(void);
122 void instructions(void);
123 int int_compare(const void *, const void *);
124 void jump(int);
125 void kill_wump(void);
126 int main(int, char **);
127 int move_to(const char *);
128 void move_wump(void);
129 void no_arrows(void);
130 void pit_kill(void);
131 int pit_nearby(void);
132 void pit_survive(void);
133 int shoot(char *);
134 void shoot_self(void);
135 int take_action(void);
136 void usage(void) __attribute__((__noreturn__));
137 void wump_kill(void);
138 int wump_nearby(void);
139
140 int
141 main(argc, argv)
142 int argc;
143 char **argv;
144 {
145 int c;
146
147 /* Revoke setgid privileges */
148 setgid(getgid());
149
150 #ifdef DEBUG
151 while ((c = getopt(argc, argv, "a:b:hp:r:t:d")) != -1)
152 #else
153 while ((c = getopt(argc, argv, "a:b:hp:r:t:")) != -1)
154 #endif
155 switch (c) {
156 case 'a':
157 arrow_num = atoi(optarg);
158 break;
159 case 'b':
160 bat_num = atoi(optarg);
161 break;
162 #ifdef DEBUG
163 case 'd':
164 debug = 1;
165 break;
166 #endif
167 case 'h':
168 level = HARD;
169 break;
170 case 'p':
171 pit_num = atoi(optarg);
172 break;
173 case 'r':
174 room_num = atoi(optarg);
175 if (room_num < MIN_ROOMS_IN_CAVE) {
176 (void)fprintf(stderr,
177 "No self-respecting wumpus would live in such a small cave!\n");
178 exit(1);
179 }
180 if (room_num > MAX_ROOMS_IN_CAVE) {
181 (void)fprintf(stderr,
182 "Even wumpii can't furnish caves that large!\n");
183 exit(1);
184 }
185 break;
186 case 't':
187 link_num = atoi(optarg);
188 if (link_num < 2) {
189 (void)fprintf(stderr,
190 "Wumpii like extra doors in their caves!\n");
191 exit(1);
192 }
193 break;
194 case '?':
195 default:
196 usage();
197 }
198
199 if (link_num > MAX_LINKS_IN_ROOM ||
200 link_num > room_num - (room_num / 4)) {
201 (void)fprintf(stderr,
202 "Too many tunnels! The cave collapsed!\n(Fortunately, the wumpus escaped!)\n");
203 exit(1);
204 }
205
206 if (level == HARD) {
207 bat_num += ((random() % (room_num / 2)) + 1);
208 pit_num += ((random() % (room_num / 2)) + 1);
209 }
210
211 if (bat_num > room_num / 2) {
212 (void)fprintf(stderr,
213 "The wumpus refused to enter the cave, claiming it was too crowded!\n");
214 exit(1);
215 }
216
217 if (pit_num > room_num / 2) {
218 (void)fprintf(stderr,
219 "The wumpus refused to enter the cave, claiming it was too dangerous!\n");
220 exit(1);
221 }
222
223 instructions();
224 cave_init();
225
226 /* and we're OFF! da dum, da dum, da dum, da dum... */
227 (void)printf(
228 "\nYou're in a cave with %d rooms and %d tunnels leading from each room.\n\
229 There are %d bat%s and %d pit%s scattered throughout the cave, and your\n\
230 quiver holds %d custom super anti-evil Wumpus arrows. Good luck.\n",
231 room_num, link_num, bat_num, plural(bat_num), pit_num,
232 plural(pit_num), arrow_num);
233
234 for (;;) {
235 initialize_things_in_cave();
236 arrows_left = arrow_num;
237 do {
238 display_room_stats();
239 (void)printf("Move or shoot? (m-s) ");
240 (void)fflush(stdout);
241 if (!fgets(answer, sizeof(answer), stdin))
242 break;
243 } while (!take_action());
244
245 if (!getans("\nCare to play another game? (y-n) "))
246 exit(0);
247 if (getans("In the same cave? (y-n) "))
248 clear_things_in_cave();
249 else
250 cave_init();
251 }
252 /* NOTREACHED */
253 return (0);
254 }
255
256 void
257 display_room_stats()
258 {
259 int i;
260
261 /*
262 * Routine will explain what's going on with the current room, as well
263 * as describe whether there are pits, bats, & wumpii nearby. It's
264 * all pretty mindless, really.
265 */
266 (void)printf(
267 "\nYou are in room %d of the cave, and have %d arrow%s left.\n",
268 player_loc, arrows_left, plural(arrows_left));
269
270 if (bats_nearby())
271 (void)printf("*rustle* *rustle* (must be bats nearby)\n");
272 if (pit_nearby())
273 (void)printf("*whoosh* (I feel a draft from some pits).\n");
274 if (wump_nearby())
275 (void)printf("*sniff* (I can smell the evil Wumpus nearby!)\n");
276
277 (void)printf("There are tunnels to rooms %d, ",
278 cave[player_loc].tunnel[0]);
279
280 for (i = 1; i < link_num - 1; i++)
281 if (cave[player_loc].tunnel[i] <= room_num)
282 (void)printf("%d, ", cave[player_loc].tunnel[i]);
283 (void)printf("and %d.\n", cave[player_loc].tunnel[link_num - 1]);
284 }
285
286 int
287 take_action()
288 {
289 /*
290 * Do the action specified by the player, either 'm'ove, 's'hoot
291 * or something exceptionally bizarre and strange! Returns 1
292 * iff the player died during this turn, otherwise returns 0.
293 */
294 switch (*answer) {
295 case 'M':
296 case 'm': /* move */
297 return(move_to(answer + 1));
298 case 'S':
299 case 's': /* shoot */
300 return(shoot(answer + 1));
301 case 'Q':
302 case 'q':
303 case 'x':
304 exit(0);
305 case '\n':
306 return(0);
307 }
308 if (random() % 15 == 1)
309 (void)printf("Que pasa?\n");
310 else
311 (void)printf("I don't understand!\n");
312 return(0);
313 }
314
315 int
316 move_to(room_number)
317 const char *room_number;
318 {
319 int i, just_moved_by_bats, next_room, tunnel_available;
320
321 /*
322 * This is responsible for moving the player into another room in the
323 * cave as per their directions. If room_number is a null string,
324 * then we'll prompt the user for the next room to go into. Once
325 * we've moved into the room, we'll check for things like bats, pits,
326 * and so on. This routine returns 1 if something occurs that kills
327 * the player and 0 otherwise...
328 */
329 tunnel_available = just_moved_by_bats = 0;
330 next_room = atoi(room_number);
331
332 /* crap for magic tunnels */
333 if (next_room == room_num + 1 &&
334 cave[player_loc].tunnel[link_num-1] != next_room)
335 ++next_room;
336
337 while (next_room < 1 || next_room > room_num + 1) {
338 if (next_room < 0 && next_room != -1)
339 (void)printf("Sorry, but we're constrained to a semi-Euclidean cave!\n");
340 if (next_room > room_num + 1)
341 (void)printf("What? The cave surely isn't quite that big!\n");
342 if (next_room == room_num + 1 &&
343 cave[player_loc].tunnel[link_num-1] != next_room) {
344 (void)printf("What? The cave isn't that big!\n");
345 ++next_room;
346 }
347 (void)printf("To which room do you wish to move? ");
348 (void)fflush(stdout);
349 if (!fgets(answer, sizeof(answer), stdin))
350 return(1);
351 next_room = atoi(answer);
352 }
353
354 /* now let's see if we can move to that room or not */
355 tunnel_available = 0;
356 for (i = 0; i < link_num; i++)
357 if (cave[player_loc].tunnel[i] == next_room)
358 tunnel_available = 1;
359
360 if (!tunnel_available) {
361 (void)printf("*Oof!* (You hit the wall)\n");
362 if (random() % 6 == 1) {
363 (void)printf("Your colorful comments awaken the wumpus!\n");
364 move_wump();
365 if (wumpus_loc == player_loc) {
366 wump_kill();
367 return(1);
368 }
369 }
370 return(0);
371 }
372
373 /* now let's move into that room and check it out for dangers */
374 if (next_room == room_num + 1)
375 jump(next_room = (random() % room_num) + 1);
376
377 player_loc = next_room;
378 for (;;) {
379 if (next_room == wumpus_loc) { /* uh oh... */
380 wump_kill();
381 return(1);
382 }
383 if (cave[next_room].has_a_pit) {
384 if (random() % 12 < 2) {
385 pit_survive();
386 return(0);
387 } else {
388 pit_kill();
389 return(1);
390 }
391 }
392
393 if (cave[next_room].has_a_bat) {
394 (void)printf(
395 "*flap* *flap* *flap* (humongous bats pick you up and move you%s!)\n",
396 just_moved_by_bats ? " again": "");
397 next_room = player_loc = (random() % room_num) + 1;
398 just_moved_by_bats = 1;
399 }
400
401 else
402 break;
403 }
404 return(0);
405 }
406
407 int
408 shoot(room_list)
409 char *room_list;
410 {
411 int chance, next, roomcnt;
412 int j, arrow_location, link, ok;
413 char *p;
414
415 /*
416 * Implement shooting arrows. Arrows are shot by the player indicating
417 * a space-separated list of rooms that the arrow should pass through;
418 * if any of the rooms they specify are not accessible via tunnel from
419 * the room the arrow is in, it will instead fly randomly into another
420 * room. If the player hits the wumpus, this routine will indicate
421 * such. If it misses, this routine will *move* the wumpus one room.
422 * If it's the last arrow, the player then dies... Returns 1 if the
423 * player has won or died, 0 if nothing has happened.
424 */
425 arrow_location = player_loc;
426 for (roomcnt = 1;; ++roomcnt, room_list = NULL) {
427 if (!(p = strtok(room_list, " \t\n"))) {
428 if (roomcnt == 1) {
429 (void)printf(
430 "The arrow falls to the ground at your feet!\n");
431 return(0);
432 } else
433 break;
434 }
435 if (roomcnt > 5) {
436 (void)printf(
437 "The arrow wavers in its flight and and can go no further!\n");
438 break;
439 }
440 next = atoi(p);
441 for (j = 0, ok = 0; j < link_num; j++)
442 if (cave[arrow_location].tunnel[j] == next)
443 ok = 1;
444
445 if (ok) {
446 if (next > room_num) {
447 (void)printf(
448 "A faint gleam tells you the arrow has gone through a magic tunnel!\n");
449 arrow_location = (random() % room_num) + 1;
450 } else
451 arrow_location = next;
452 } else {
453 link = (random() % link_num);
454 if (link == player_loc)
455 (void)printf(
456 "*thunk* The arrow can't find a way from %d to %d and flys back into\n\
457 your room!\n",
458 arrow_location, next);
459 else if (cave[arrow_location].tunnel[link] > room_num)
460 (void)printf(
461 "*thunk* The arrow flys randomly into a magic tunnel, thence into\n\
462 room %d!\n",
463 cave[arrow_location].tunnel[link]);
464 else
465 (void)printf(
466 "*thunk* The arrow can't find a way from %d to %d and flys randomly\n\
467 into room %d!\n",
468 arrow_location, next,
469 cave[arrow_location].tunnel[link]);
470 arrow_location = cave[arrow_location].tunnel[link];
471 break;
472 }
473 chance = random() % 10;
474 if (roomcnt == 3 && chance < 2) {
475 (void)printf(
476 "Your bowstring breaks! *twaaaaaang*\n\
477 The arrow is weakly shot and can go no further!\n");
478 break;
479 } else if (roomcnt == 4 && chance < 6) {
480 (void)printf(
481 "The arrow wavers in its flight and and can go no further!\n");
482 break;
483 }
484 }
485
486 /*
487 * now we've gotten into the new room let us see if El Wumpo is
488 * in the same room ... if so we've a HIT and the player WON!
489 */
490 if (arrow_location == wumpus_loc) {
491 kill_wump();
492 return(1);
493 }
494
495 if (arrow_location == player_loc) {
496 shoot_self();
497 return(1);
498 }
499
500 if (!--arrows_left) {
501 no_arrows();
502 return(1);
503 }
504
505 {
506 /* each time you shoot, it's more likely the wumpus moves */
507 static int lastchance = 2;
508
509 if (random() % level == EASY ? 12 : 9 < (lastchance += 2)) {
510 move_wump();
511 if (wumpus_loc == player_loc)
512 wump_kill();
513 lastchance = random() % 3;
514
515 }
516 }
517 return(0);
518 }
519
520 int
521 gcd(a, b)
522 int a, b;
523 {
524 int r;
525
526 r = a % b;
527 if (r == 0)
528 return (b);
529 return (gcd(b, r));
530 }
531
532 void
533 cave_init()
534 {
535 int i, j, k, link;
536 int delta;
537
538 /*
539 * This does most of the interesting work in this program actually!
540 * In this routine we'll initialize the Wumpus cave to have all rooms
541 * linking to all others by stepping through our data structure once,
542 * recording all forward links and backwards links too. The parallel
543 * "linkcount" data structure ensures that no room ends up with more
544 * than three links, regardless of the quality of the random number
545 * generator that we're using.
546 */
547 srandom((int)time((time_t *)0));
548
549 /* initialize the cave first off. */
550 for (i = 1; i <= room_num; ++i)
551 for (j = 0; j < link_num ; ++j)
552 cave[i].tunnel[j] = -1;
553
554 /*
555 * Choose a random 'hop' delta for our guaranteed link.
556 * To keep the cave connected, we need the greatest common divisor
557 * of (delta + 1) and room_num to be 1.
558 */
559 do {
560 delta = (random() % (room_num - 1)) + 1;
561 } while (gcd(room_num, delta + 1) != 1);
562
563 for (i = 1; i <= room_num; ++i) {
564 link = ((i + delta) % room_num) + 1; /* connection */
565 cave[i].tunnel[0] = link; /* forw link */
566 cave[link].tunnel[1] = i; /* back link */
567 }
568 /* now fill in the rest of the cave with random connections */
569 for (i = 1; i <= room_num; i++)
570 for (j = 2; j < link_num ; j++) {
571 if (cave[i].tunnel[j] != -1)
572 continue;
573 try_again: link = (random() % room_num) + 1;
574 /* skip duplicates */
575 for (k = 0; k < j; k++)
576 if (cave[i].tunnel[k] == link)
577 goto try_again;
578 cave[i].tunnel[j] = link;
579 if (random() % 2 == 1)
580 continue;
581 for (k = 0; k < link_num; ++k) {
582 /* if duplicate, skip it */
583 if (cave[link].tunnel[k] == i)
584 k = link_num;
585
586 /* if open link, use it, force exit */
587 if (cave[link].tunnel[k] == -1) {
588 cave[link].tunnel[k] = i;
589 k = link_num;
590 }
591 }
592 }
593 /*
594 * now that we're done, sort the tunnels in each of the rooms to
595 * make it easier on the intrepid adventurer.
596 */
597 for (i = 1; i <= room_num; ++i)
598 qsort(cave[i].tunnel, (u_int)link_num,
599 sizeof(cave[i].tunnel[0]), int_compare);
600
601 #ifdef DEBUG
602 if (debug)
603 for (i = 1; i <= room_num; ++i) {
604 (void)printf("<room %d has tunnels to ", i);
605 for (j = 0; j < link_num; ++j)
606 (void)printf("%d ", cave[i].tunnel[j]);
607 (void)printf(">\n");
608 }
609 #endif
610 }
611
612 void
613 clear_things_in_cave()
614 {
615 int i;
616
617 /*
618 * remove bats and pits from the current cave in preparation for us
619 * adding new ones via the initialize_things_in_cave() routines.
620 */
621 for (i = 1; i <= room_num; ++i)
622 cave[i].has_a_bat = cave[i].has_a_pit = 0;
623 }
624
625 void
626 initialize_things_in_cave()
627 {
628 int i, loc;
629
630 /* place some bats, pits, the wumpus, and the player. */
631 for (i = 0; i < bat_num; ++i) {
632 do {
633 loc = (random() % room_num) + 1;
634 } while (cave[loc].has_a_bat);
635 cave[loc].has_a_bat = 1;
636 #ifdef DEBUG
637 if (debug)
638 (void)printf("<bat in room %d>\n", loc);
639 #endif
640 }
641
642 for (i = 0; i < pit_num; ++i) {
643 do {
644 loc = (random() % room_num) + 1;
645 } while (cave[loc].has_a_pit && cave[loc].has_a_bat);
646 cave[loc].has_a_pit = 1;
647 #ifdef DEBUG
648 if (debug)
649 (void)printf("<pit in room %d>\n", loc);
650 #endif
651 }
652
653 wumpus_loc = (random() % room_num) + 1;
654 #ifdef DEBUG
655 if (debug)
656 (void)printf("<wumpus in room %d>\n", loc);
657 #endif
658
659 do {
660 player_loc = (random() % room_num) + 1;
661 } while (player_loc == wumpus_loc || (level == HARD ?
662 (link_num / room_num < 0.4 ? wump_nearby() : 0) : 0));
663 }
664
665 int
666 getans(prompt)
667 const char *prompt;
668 {
669 char buf[20];
670
671 /*
672 * simple routine to ask the yes/no question specified until the user
673 * answers yes or no, then return 1 if they said 'yes' and 0 if they
674 * answered 'no'.
675 */
676 for (;;) {
677 (void)printf("%s", prompt);
678 (void)fflush(stdout);
679 if (!fgets(buf, sizeof(buf), stdin))
680 return(0);
681 if (*buf == 'N' || *buf == 'n')
682 return(0);
683 if (*buf == 'Y' || *buf == 'y')
684 return(1);
685 (void)printf(
686 "I don't understand your answer; please enter 'y' or 'n'!\n");
687 }
688 /* NOTREACHED */
689 }
690
691 int
692 bats_nearby()
693 {
694 int i;
695
696 /* check for bats in the immediate vicinity */
697 for (i = 0; i < link_num; ++i)
698 if (cave[cave[player_loc].tunnel[i]].has_a_bat)
699 return(1);
700 return(0);
701 }
702
703 int
704 pit_nearby()
705 {
706 int i;
707
708 /* check for pits in the immediate vicinity */
709 for (i = 0; i < link_num; ++i)
710 if (cave[cave[player_loc].tunnel[i]].has_a_pit)
711 return(1);
712 return(0);
713 }
714
715 int
716 wump_nearby()
717 {
718 int i, j;
719
720 /* check for a wumpus within TWO caves of where we are */
721 for (i = 0; i < link_num; ++i) {
722 if (cave[player_loc].tunnel[i] == wumpus_loc)
723 return(1);
724 for (j = 0; j < link_num; ++j)
725 if (cave[cave[player_loc].tunnel[i]].tunnel[j] ==
726 wumpus_loc)
727 return(1);
728 }
729 return(0);
730 }
731
732 void
733 move_wump()
734 {
735 wumpus_loc = cave[wumpus_loc].tunnel[random() % link_num];
736 }
737
738 int
739 int_compare(a, b)
740 const void *a, *b;
741 {
742 return(*(const int *)a < *(const int *)b ? -1 : 1);
743 }
744
745 void
746 instructions()
747 {
748 const char *pager;
749 pid_t pid;
750 int status;
751 int fd;
752
753 /*
754 * read the instructions file, if needed, and show the user how to
755 * play this game!
756 */
757 if (!getans("Instructions? (y-n) "))
758 return;
759
760 if (access(_PATH_WUMPINFO, R_OK)) {
761 (void)printf(
762 "Sorry, but the instruction file seems to have disappeared in a\n\
763 puff of greasy black smoke! (poof)\n");
764 return;
765 }
766
767 if (!isatty(STDOUT_FILENO))
768 pager = "cat";
769 else {
770 if (!(pager = getenv("PAGER")) || (*pager == 0))
771 pager = _PATH_PAGER;
772 }
773 switch (pid = fork()) {
774 case 0: /* child */
775 if ((fd = open(_PATH_WUMPINFO, O_RDONLY)) == -1)
776 err(1, "open %s", _PATH_WUMPINFO);
777 if (dup2(fd, STDIN_FILENO) == -1)
778 err(1, "dup2");
779 (void)execl("/bin/sh", "sh", "-c", pager, (char *) NULL);
780 err(1, "exec sh -c %s", pager);
781 case -1:
782 err(1, "fork");
783 default:
784 (void)waitpid(pid, &status, 0);
785 break;
786 }
787 }
788
789 void
790 usage()
791 {
792 (void)fprintf(stderr,
793 "usage: wump [-h] [-a arrows] [-b bats] [-p pits] [-r rooms] [-t tunnels]\n");
794 exit(1);
795 }
796
797 /* messages */
798
799 void
800 wump_kill()
801 {
802 (void)printf(
803 "*ROAR* *chomp* *snurfle* *chomp*!\n\
804 Much to the delight of the Wumpus, you walked right into his mouth,\n\
805 making you one of the easiest dinners he's ever had! For you, however,\n\
806 it's a rather unpleasant death. The only good thing is that it's been\n\
807 so long since the evil Wumpus cleaned his teeth that you immediately\n\
808 passed out from the stench!\n");
809 }
810
811 void
812 kill_wump()
813 {
814 (void)printf(
815 "*thwock!* *groan* *crash*\n\n\
816 A horrible roar fills the cave, and you realize, with a smile, that you\n\
817 have slain the evil Wumpus and won the game! You don't want to tarry for\n\
818 long, however, because not only is the Wumpus famous, but the stench of\n\
819 dead Wumpus is also quite well known, a stench plenty enough to slay the\n\
820 mightiest adventurer at a single whiff!!\n");
821 }
822
823 void
824 no_arrows()
825 {
826 (void)printf(
827 "\nYou turn and look at your quiver, and realize with a sinking feeling\n\
828 that you've just shot your last arrow (figuratively, too). Sensing this\n\
829 with its psychic powers, the evil Wumpus rampagees through the cave, finds\n\
830 you, and with a mighty *ROAR* eats you alive!\n");
831 }
832
833 void
834 shoot_self()
835 {
836 (void)printf(
837 "\n*Thwack!* A sudden piercing feeling informs you that the ricochet\n\
838 of your wild arrow has resulted in it wedging in your side, causing\n\
839 extreme agony. The evil Wumpus, with its psychic powers, realizes this\n\
840 and immediately rushes to your side, not to help, alas, but to EAT YOU!\n\
841 (*CHOMP*)\n");
842 }
843
844 void
845 jump(where)
846 int where;
847 {
848 (void)printf(
849 "\nWith a jaunty step you enter the magic tunnel. As you do, you\n\
850 notice that the walls are shimmering and glowing. Suddenly you feel\n\
851 a very curious, warm sensation and find yourself in room %d!!\n", where);
852 }
853
854 void
855 pit_kill()
856 {
857 (void)printf(
858 "*AAAUUUUGGGGGHHHHHhhhhhhhhhh...*\n\
859 The whistling sound and updraft as you walked into this room of the\n\
860 cave apparently wasn't enough to clue you in to the presence of the\n\
861 bottomless pit. You have a lot of time to reflect on this error as\n\
862 you fall many miles to the core of the earth. Look on the bright side;\n\
863 you can at least find out if Jules Verne was right...\n");
864 }
865
866 void
867 pit_survive()
868 {
869 (void)printf(
870 "Without conscious thought you grab for the side of the cave and manage\n\
871 to grasp onto a rocky outcrop. Beneath your feet stretches the limitless\n\
872 depths of a bottomless pit! Rock crumbles beneath your feet!\n");
873 }
874