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