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