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